![]() | ![]() | ![]() | GNetwork Library Manual | ![]() |
---|
Source Code — The code used in the tutorial.
#ifndef __FLP_CLIENT_H__ #define __FLP_CLIENT_H__ #include <libgtcpsocket/gtcp-connection.h> /* Used by the "flp-status" property */ typedef enum /* < prefix=FLP_STATUS > */ { FLP_STATUS_CLOSING = -3, FLP_STATUS_CLOSED = -2, FLP_STATUS_OPENING = -1, FLP_STATUS_READY = 0, FLP_STATUS_RECIEVING = 1 } FLPStatusType; /* Used by the "error" signal */ typedef enum /* < prefix=FLP_ERROR > */ { FLP_ERROR_UNKNOWN, FLP_ERROR_BAD_USERPASS, FLP_ERROR_NOT_FOUND, FLP_ERROR_BAD_PERMISSIONS, FLP_ERROR_NOTRANSFER } FLPErrorType; typedef struct _FLPClient FLPClient; typedef struct _FLPClientClass FLPClientClass; struct _FLPClient { GTcpConnection parent; /* Read-Write Object Properties */ FLPStatusType flp_status; gchar *username; gchar *passwd; /* Read-only Object Properties */ gchar *filename; gulong filesize; gulong offset; /* Other Data */ gulong filesize_recieved; gint fd; }; struct _FLPClientClass { GTcpConnectionClass parent_class; /* Protocol Signals */ void (*connect_complete) (FLPClient *client, gboolean connected); void (*quit) (FLPClient *client, gboolean requested); /* Incoming Data Signals */ void (*error) (FLPClient *client, FLPErrorType error); void (*download_start) (FLPClient *client, const gchar *filename, gsize filesize, gsize offset); void (*download_done) (FLPClient *client, const gchar *filename, gboolean completed); }; GType flp_client_get_type (void); #endif /* __FLP_CLIENT_H__ */
#include "flp-client.h" /* For open() */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* For close() */ #include <unistd.h> /* Property IDs */ enum { PROP_0, FLP_STATUS, USERNAME, PASSWD, FILENAME, FILESIZE, OFFSET }; /* Signal ID Location */ enum { CONNECT_COMPLETE, QUIT, ERROR, DOWNLOAD_START, DOWNLOAD_DONE, LAST_SIGNAL }; /* Cached Signal IDs */ static gint flp_signals[LAST_SIGNAL] = {0}; /* Parent Class (GTcpConnectionClass) */ static gpointer parent_class = NULL; /* FLPClient Object Callbacks */ static void flp_client_quit (FLPClient *client, gboolean requested) { if (client->fd != -1) { g_signal_emit (client, flp_signals[DOWNLOAD_DONE], 0, client->filename, FALSE); } } static void flp_client_error (FLPClient *client, FLPErrorType error) { if (client->fd != -1) { g_signal_emit (client, flp_signals[DOWNLOAD_DONE], 0, client->filename, FALSE); } } static void flp_client_download_done (FLPClient *client, const gchar *filename, gboolean completed) { /* The destination file should always be open at this point. */ g_assert (client->fd != -1); close (client->fd); /* Reset the object members */ client->fd = -1; client->filesize_recieved = 0; /* Reset the object properties */ /* Freeze the "notify" signal until we're done. */ g_object_freeze_notify (G_OBJECT (client)); client->offset = 0; g_object_notify (G_OBJECT (client), "offset"); client->filesize = 0; g_object_notify (G_OBJECT (client), "filesize"); g_free (client->filename); client->filename = NULL; g_object_notify (G_OBJECT (client), "filename"); /* We're done, so thaw the "notify" signal. */ g_object_thaw_notify (G_OBJECT (client)); } static void flp_client_download_start (FLPClient *client, const gchar *filename, gsize filesize, gsize offset) { gchar *path; /* The client should not have an open file already */ g_assert (client->fd == -1); /* Put the file in ~/Downloads/<filename> */ path = g_build_filename (g_get_home_dir (), "Downloads", filename, NULL); /* Actually open the file */ client->fd = open (path, (O_CREAT | O_NONBLOCK | O_APPEND)); /* Free the path */ g_free (path); } /* GTcpConnection Callbacks */ static void flp_client_connect_done (GTcpConnection *conn, GTcpConnectionStatus status) { if (GTCP_CONNECTION_CLASS (parent_class)->connect_done != NULL) (*GTCP_CONNECTION_CLASS (parent_class)->connect_done) (conn, status); /* If we're connected... */ if (status == GTCP_CONNECTION_CONNECTED) { gchar *data = NULL; FLPClient *client = FLP_CLIENT (conn); /* Set the "flp-status" property */ client->status = FLP_CLIENT_LOGGING_IN; g_object_notify (G_OBJECT (conn), "flp-status"); /* Construct the login string */ if (client->username != NULL) { if (client->password != NULL) { data = g_strdup_printf ("LOGIN %s %s\n", client->username, client->password); } else { data = g_strdup_printf ("LOGIN %s %s\n", client->username); } } else { data = g_strdup ("LOGIN\n"); } /* Send the login string */ gtcp_connection_send (conn, data, -1); /* Free the login string */ g_free (data); } } static void flp_client_closed (GTcpConnection *conn, gboolean requested) { if (GTCP_CONNECTION_CLASS (parent_class)->closed != NULL) (*GTCP_CONNECTION_CLASS (parent_class)->closed) (conn, requested); /* If the user requested the quit, then the quit function should have set the status appropriately. */ g_signal_emit (conn, signals[QUIT], 0, (FLP_CLIENT (conn)->status == FLP_CLIENT_QUITTING)); } static void flp_client_recv (GTcpConnection *conn, gconstpointer data, gsize length) { FLPClient *client = FLP_CLIENT (conn); gint line_end_pos = -1; if (GTCP_CONNECTION_CLASS (parent_class)->recv != NULL) (*GTCP_CONNECTION_CLASS (parent_class)->recv) (conn, data, length); /* First, split the raw incoming data into lines */ lines = g_strsplit (data, "\n", -1); if (lines == NULL) return; for (i = 0; lines[i] != NULL; i++) { gchar *msg = NULL; /* verify that we've got a message, and not data for a file. We can do this by checking to see if the following sscanf parse works. If it does, msg will be filled with the message type string (and any other characters up to the newline). */ if (sscanf (lines[i], "FLP %a[^\n]", &msg) == 1) { FLPErrorType error_code; gboolean error_msg = FALSE; /* Find the proper message type */ /* Login successful */ if (g_ascii_strcasecmp (msg, "LOGGED_IN") == 0) { /* Then, emit the "connect-complete" signal */ g_signal_emit (client, signals[CONNECT_COMPLETE], 0, TRUE); } /* Starting to recieve a file */ else if (g_ascii_strcasencmp (msg, "STARTGET", 8) == 0) { gchar *filename = NULL; gulong size = 0; gint sscanf_retval; /* First, get the filename and size */ sscanf_retval = sscanf (msg + 9, "%as %ul", &filename, &size); /* Set the "filename" property */ g_free (client->filename); client->filename = g_strdup (filename); g_free (filename); g_object_notify (G_OBJECT (client), "filename"); /* Set the "filesize" property */ client->size = size; g_object_notify (G_OBJECT (client), "filesize"); /* Reset the bytes recieved for this file */ client->bytes_recieved = 0; client->offset = 0; g_object_notify (G_OBJECT (client), "offset"); /* Emit the "download-start" signal */ g_signal_emit (client, flp_signals[DOWNLOAD_START], 0, client->filename, client->filesize, client->offset); } /* Done recieving a file */ else if (g_ascii_strcasecmp (msg, "DONEGET") == 0) { gboolean complete; complete = (client->bytes_recieved == client->filesize); g_signal_emit (client, flp_signals[DOWNLOAD_DONE], 0, client->filename, client->filesize, complete); } /* Error Types */ else if (g_ascii_strcasecmp (msg, "BADUSERPASS") == 0) { error_msg = TRUE; error_code = FLP_ERROR_BAD_USERPASS; } else if (g_ascii_strcasecmp (msg, "NOTFOUND") == 0) { error_msg = TRUE; error_code = FLP_ERROR_NOT_FOUND; } else if (g_ascii_strcasecmp (msg, "BADPERMS") == 0) { error_msg = TRUE; error_code = FLP_ERROR_BAD_PERMISSIONS; } else if (g_ascii_strcasecmp (msg, "NOTRANSFER") == 0) { error_msg = TRUE; error_code = FLP_ERROR_NOTRANSFER; } /* *msg == "UNKNOWN" or some random bad data. */ else { error_msg = TRUE; error_code = FLP_ERROR_UNKNOWN; } if (error_msg) g_signal_emit (client, signals[ERROR], 0, error_code); } /* We don't have a message, this is data for an incoming file */ else { gsize line_size = strlen (lines[i]); client->filesize_recieved += line_size; write (client->fd, lines[i], line_size); } g_free (msg); } /* GObject Callbacks */ static void flp_client_finalize (GObject *object) { FlpClient *client = FLP_CLIENT (object); g_free (client->filename); if (client->fd != -1) close (client->fd); if (G_OBJECT_CLASS (parent_class)->finalize != NULL) (*G_OBJECT_CLASS (parent_class)->finalize) (object); } static void flp_client_set_property (GObject * object, guint property, const GValue * value, GParamSpec * param_spec) { FlpClient *client = FLP_CLIENT (object); switch (property) { case USERNAME: g_return_if_fail (client->flp_status <= GIRC_CLIENT_CLOSED); g_free (client->username); client->username = g_value_dup_string (value); g_object_notify (object, "username"); break; case PASSWORD: g_return_if_fail (client->flp_status <= GIRC_CLIENT_CLOSED); g_free (client->password); client->password = g_value_dup_string (value); g_object_notify (object, "password"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property, param_spec); break; } } static void flp_client_get_property (GObject * object, guint property, GValue * value, GParamSpec * param_spec) { FLPClient *client = FLP_CLIENT (object); switch (property) { case FLP_STATUS: g_value_set_enum (value, client->flp_status); break; case USERNAME: g_value_set_string (value, client->username); break; case PASSWORD: g_value_set_string (value, client->password); break; case FILENAME: g_value_set_string (value, client->filename); break; case FILESIZE: g_value_set_ulong (value, client->filesize); break; case OFFSET: g_value_set_ulong (value, client->offset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property, param_spec); break; } } /* GType Functions */ static void flp_client_class_init (FLPClientClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); GTcpConnectionClass *tcp_class = GTCP_CONNECTION_CLASS (class); parent_class = g_type_class_peek_parent (class); object_class->finalize = flp_client_finalize; object_class->set_property = flp_client_set_property; object_class->get_property = flp_client_get_property; tcp_class->closed = flp_client_closed; tcp_class->connect_done = flp_client_connect_done; tcp_class->recv = flp_client_recv; class->quit = flp_client_quit; class->error = flp_client_error; class->download_start = flp_client_download_start; class->download_done = flp_client_download_done; /* Signals */ flp_signals[CONNECT_COMPLETE] = g_signal_new ("connect-complete", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (FLPClientClass, connect_complete), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); flp_signals[QUIT] = g_signal_new ("quit", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (FLPClientClass, quit), NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); flp_signals[ERROR] = g_signal_new ("error", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (FLPClientClass, error), NULL, NULL, g_cclosure_marshal_VOID__ENUM, G_TYPE_NONE, 1, FLP_TYPE_ERROR_TYPE); flp_signals[DOWNLOAD_START] = g_signal_new ("download-start", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (FLPClientClass, download_start), NULL, NULL, _flp_marshal_VOID__STRING_ULONG_ULONG, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_ULONG, G_TYPE_ULONG); flp_signals[DOWNLOAD_DONE] = g_signal_new ("download-done", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (FLPClientClass, download_done), NULL, NULL, _flp_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); /* Properties */ g_object_class_install_property (object_class, FLP_STATUS, // Property ID g_param_spec_enum ("flp-status", // Property Name _("Protocol Status"), // Short Description _("The status of the connection."), // Long Description FLP_TYPE_STATUS_TYPE, // Enumerated GType FLP_STATUS_CLOSED, // Default Value G_PARAM_READABLE)); // Property Flags g_object_class_install_property (object_class, USERNAME, g_param_spec_string ("username", _("User name"), _("The username used to authenticate " "connections."), NULL, // Default Value (G_PARAM_READWRITE | G_PARAM_CONSTRUCT))); g_object_class_install_property (object_class, PASSWORD, g_param_spec_string ("password", _("Password"), _("The password used to authenticate " "connections."), NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT))); g_object_class_install_property (object_class, FILENAME, g_param_spec_string ("filename", _("Transfer Filename"), _("The filename currently being transferred."), NULL, G_PARAM_READABLE)); g_object_class_install_property (object_class, FILESIZE, g_param_spec_ulong ("filesize", _("Transfer Filename"), _("The size of the file currently being " "transferred."), 0, // Minimum Value G_MAXULONG, // Maximum Value 0, // Default Value G_PARAM_READABLE)); g_object_class_install_property (object_class, OFFSET, g_param_spec_ulong ("offset", _("Transfer Offset"), _("The byte position of the current download."), 0, G_MAXULONG, 0, G_PARAM_READABLE)); } static void flp_client_instance_init (FlpClient *client) { /* Initialize the file descriptor to invalid */ client->fd = -1; } /* PUBLIC API */ GType flp_client_get_type (void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof (FLPClientClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) flp_client_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (FLPClient), 0, (GInstanceInitFunc) flp_client_instance_init, NULL }; type = g_type_register_static (GTCP_TYPE_CONNECTION, "FLPClient", &info, 0); } return type; }
<< Object-Specific Code | Server Application Tutorial >> |