# HG changeset patch # User Richard Laager # Date 1230932892 0 # Node ID f8dbd57cf635c7bf7c02ce141218251bb453c593 # Parent ce108a92fa4e2e29b048d263f9fc79f856546ca7# Parent ca01403251f1e08fb279819f02333ca8e7211552 explicit merge of '448583a763dcf343f1a4392bdcdcc4c4f01f3aed' and 'df350a99631d730a3d67255d916ee24e97f0f621' diff -r ca01403251f1 -r f8dbd57cf635 COPYRIGHT --- a/COPYRIGHT Fri Jan 02 21:47:52 2009 +0000 +++ b/COPYRIGHT Fri Jan 02 21:48:12 2009 +0000 @@ -272,6 +272,7 @@ David Mohr Andrew Molloy Michael Monreal +Laurent Montaron Marco Monteiro Benjamin Moody John Moody diff -r ca01403251f1 -r f8dbd57cf635 ChangeLog --- a/ChangeLog Fri Jan 02 21:47:52 2009 +0000 +++ b/ChangeLog Fri Jan 02 21:48:12 2009 +0000 @@ -1,25 +1,32 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.5.3 (??/??/????): +version 2.5.3 (12/??/2008): libpurple: * Corrected maximum message lengths for Yahoo! * The Buddy State Notification plugin no longer prints duplicate - notifications when the same buddy is in multiple groups (Florian Quèze) - * The Buddy State Notification plugin no longer turns JID's, MSN Passport - ID's, etc. into links (Florian Quèze) - * Fix a crash in SIMPLE when a malformed message is received. - * purple-remote now has a "getstatusmessage" command to retrieve the text - of the current status message. + notifications when the same buddy is in multiple groups (Florian + Quèze) + * The Buddy State Notification plugin no longer turns JID's, MSN + Passport ID's, etc. into links (Florian Quèze) + * purple-remote now has a "getstatusmessage" command to retrieve + the text of the current status message. * Various fixes to the nullprpl (Paul Aurich) * Fix a crash when accessing the roomlist for an account that's not connected (Paul Aurich) - * Fix a crash in purple_accounts_delete that happens when this function is - called before the buddy list is initialized (Florian Quèze) - * Fix use of av_len in perl bindings to fix some off-by-one bugs (Paul - Aurich) - * On ICQ, advertise the ICQ 6 typing capability. This should fix the - reports of typing notifications not working with third-party clients - (Jaromír Karmazín) + * Fix a crash in purple_accounts_delete that happens when this + function is called before the buddy list is initialized + (Florian Quèze) + * Fix use of av_len in perl bindings to fix some off-by-one bugs + (Paul Aurich) + * On ICQ, advertise the ICQ 6 typing capability. This should fix + the reports of typing notifications not working with third-party + clients (Jaromír Karmazín) + * Many QQ fixes and improvements, including the ability to connect + using QQ2008 protocol and sending/receiving of long messages. + * Fix a crash with DNS SRV lookups (Florian Quèze) + * Fix insanely long idle times for Sametime 7.5 buddies by assuming + 0 idle time if the idle timestamp is in the future (Laurent + Montaron) Gadu-Gadu: * Fix some problems with Gadu-Gadu buddy icons (Adam Strzelecki) @@ -28,37 +35,47 @@ Strzelecki) MSN: - * Fix an error with offline messages by shipping the *new* "Microsoft - Secure Server Authority" and the "Microsoft Internet Authority" - certificates. These are now always installed even when using - --with-system-ssl-certs because most system certs are not up to date. - * The Games and Office media can now be set and displayed (in addition - to the previous Music media). The Media status text now shows the - album, if possible. + * Fix an error with offline messages by shipping the *new* + "Microsoft Secure Server Authority" and the "Microsoft Internet + Authority" certificates. These are now always installed even when + using --with-system-ssl-certs because most systems don't ship + those intermediate certificates. + * The Games and Office media can now be set and displayed (in + addition to the previous Music media). The Media status text now + shows the album, if possible. * Messages sent from a mobile device while you were offline are now correctly received. + * Server transfers after you've been connected for a long time + should now be handled correctly. + * Many other fixes and code cleanup. + + SIMPLE: + * Fix a crash when a malformed message is received. + * Don't allow connecting accounts if no server name has been + specified (Florian Quèze) XMPP: - * Fix the namespace URL we look for in PEP reply stanzas to match the URL - used in the 'get' requests (Paul Aurich) + * Fix the namespace URL we look for in PEP reply stanzas to match + the URL used in the 'get' requests (Paul Aurich) * Resources can be set to the local machine's hostname by using __HOSTNAME__ as the resource string (Jonathan Sailor) * Resources can now be left blank, causing the server to generate a resource for us where supported (Jonathan Sailor) * Resources now default to no value * Quit trying to get user info for MUC's (Paul Aurich) - * Send "client-accepts-full-bind-result" attribute during SASL login. - This will fix Google Talk login failures if the user configures the - wrong domain for his/her account. - * Support new element to indicate no XEP-0084 User Avatar - (Paul Aurich) - * Fix SHA1 avatar checksum errors that occur when one of the bytes in a - checksum begins with 0 (Paul Aurich) - + * Send "client-accepts-full-bind-result" attribute during SASL + login. This will fix Google Talk login failures if the user + configures the wrong domain for his/her account. + * Support new element to indicate no XEP-0084 User + Avatar (Paul Aurich) + * Fix SHA1 avatar checksum errors that occur when one of the bytes + in a checksum begins with 0 (Paul Aurich) + Zephyr: * Enable auto-reply, to emulate 'zaway' (Toby Schaffer) - * Fix a crash when an account is configured to use tzc but tzc is not - installed or the configured tzc command is invalid (Michael Terry) + * Fix a crash when an account is configured to use tzc but tzc is + not installed or the configured tzc command is invalid (Michael + Terry) * Fix a 10 second delay waiting on tzc if it is not installed or the configured command is invalid (Michael Terry) @@ -68,13 +85,13 @@ previously changed that pref, add a line like this to ~/.purple/gtkrc-2.0 (where 500 is the timeout (in ms) you want): gtk-tooltip-timeout = 500 - To completely disable tooltips (e.g. if you had an old tooltip_delay - of zero), add this to ~/.purple/gtkrc-2.0: + To completely disable tooltips (e.g. if you had an old + tooltip_delay of zero), add this to ~/.purple/gtkrc-2.0: gtk-enable-tooltips = 0 * Moved the release notification dialog to a mini-dialog in the buddylist. (Thanks to Casey Ho) - * Fix a crash when closing an authorization minidialog with the X then - immediately going offline (Paul Aurich) + * Fix a crash when closing an authorization minidialog with the X + then immediately going offline (Paul Aurich) Finch: * Allow binding meta+arrow keys for actions. diff -r ca01403251f1 -r f8dbd57cf635 libpurple/dnssrv.c --- a/libpurple/dnssrv.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/dnssrv.c Fri Jan 02 21:48:12 2009 +0000 @@ -336,6 +336,12 @@ static gboolean initialized = FALSE; #endif + if (!protocol || !*protocol || !transport || !*transport || !domain || !*domain) { + purple_debug_error("dnssrv", "Wrong arguments\n"); + cb(NULL, 0, extradata); + g_return_val_if_reached(NULL); + } + query = g_strdup_printf("_%s._%s.%s", protocol, transport, domain); purple_debug_info("dnssrv","querying SRV record for %s\n", query); diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/msn/nexus.c --- a/libpurple/protocols/msn/nexus.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/msn/nexus.c Fri Jan 02 21:48:12 2009 +0000 @@ -434,6 +434,9 @@ #endif char *decrypted_data; + if (resp == NULL) + return; + purple_debug_info("msn", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]); enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken"); diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/msn/oim.c --- a/libpurple/protocols/msn/oim.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/msn/oim.c Fri Jan 02 21:48:12 2009 +0000 @@ -616,7 +616,7 @@ /* Match number to user's mobile number, FROM is a phone number if the other side pages you using your phone number */ - if (!strncmp(passport, "tel:+", 5)) { + if (!strncmp(from, "tel:+", 5)) { MsnUser *user = msn_userlist_find_user_with_mobile_phone( rdata->oim->session->userlist, from + 4); @@ -630,13 +630,16 @@ msn_message_get_attr(message, "boundary")); tokens = g_strsplit(message->body, boundary, 0); - for (part = tokens; *part != NULL; part++) { + /* tokens+1 to skip the "This is a multipart message..." text */ + for (part = tokens+1; *part != NULL; part++) { MsnMessage *multipart; + const char *type; multipart = msn_message_new(MSN_MSG_UNKNOWN); msn_message_parse_payload(multipart, *part, strlen(*part), MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM); - if (!strcmp(msn_message_get_content_type(multipart), "text/plain")) { + type = msn_message_get_content_type(multipart); + if (type && !strcmp(type, "text/plain")) { decode_msg = (char *)purple_base64_decode(multipart->body, &body_len); msn_message_destroy(multipart); break; diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/msn/session.c Fri Jan 02 21:48:12 2009 +0000 @@ -453,25 +453,23 @@ PurpleConnection *gc; PurpleStoredImage *img; - if (session->logged_in) - return; - - account = session->account; - gc = purple_account_get_connection(account); + if (!session->logged_in) { + account = session->account; + gc = purple_account_get_connection(account); - img = purple_buddy_icons_find_account_icon(session->account); - /* TODO: Do we really want to call this if img is NULL? */ - msn_user_set_buddy_icon(session->user, img); - if (img != NULL) - purple_imgstore_unref(img); + img = purple_buddy_icons_find_account_icon(session->account); + /* TODO: Do we really want to call this if img is NULL? */ + msn_user_set_buddy_icon(session->user, img); + if (img != NULL) + purple_imgstore_unref(img); - session->logged_in = TRUE; + session->logged_in = TRUE; + purple_connection_set_state(gc, PURPLE_CONNECTED); + + /* Sync users */ + msn_session_sync_users(session); + } msn_change_status(session); - - purple_connection_set_state(gc, PURPLE_CONNECTED); - - /* Sync users */ - msn_session_sync_users(session); } diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/myspace/message.c --- a/libpurple/protocols/myspace/message.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/myspace/message.c Fri Jan 02 21:48:12 2009 +0000 @@ -19,17 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "message.h" #include "myspace.h" -#include "message.h" -static void msim_msg_free_element(gpointer data, gpointer user_data); static void msim_msg_debug_string_element(gpointer data, gpointer user_data); -static gchar *msim_msg_pack_using(MsimMessage *msg, GFunc gf, const gchar *sep, const gchar *begin, const gchar *end); -static GList *msim_msg_get_node(MsimMessage *msg, const gchar *name); -static MsimMessage *msim_msg_new_v(gchar *first_key, va_list argp); -/* Escape codes and associated replacement text, used for protocol message - * escaping and unescaping. */ +/** + * Escape codes and associated replacement text, used for protocol message + * escaping and unescaping. + */ static struct MSIM_ESCAPE_REPLACEMENT { gchar *code; gchar text; @@ -53,7 +51,7 @@ guint msg_len; gs = g_string_new(""); - msg_len = strlen(msg); + msg_len = strlen(msg); for (i = 0; i < msg_len; ++i) { struct MSIM_ESCAPE_REPLACEMENT *replacement; @@ -97,7 +95,7 @@ guint msg_len; gs = g_string_new(""); - msg_len = strlen(msg); + msg_len = strlen(msg); for (i = 0; i < msg_len; ++i) { struct MSIM_ESCAPE_REPLACEMENT *replacement; @@ -126,27 +124,8 @@ return g_string_free(gs, FALSE); } -/** Create a new MsimMessage. - * - * @param first_key The first key in the sequence, or NULL for an empty message. - * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. - * - * See msim_msg_append() documentation for details on types. - */ -MsimMessage * -msim_msg_new(gchar *first_key, ...) -{ - va_list argp; - - if (first_key) { - va_start(argp, first_key); - return msim_msg_new_v(first_key, argp); - } else { - return NULL; - } -} - -/** Create a new message from va_list and its first argument. +/** + * Create a new message from va_list and its first argument. * * @param first_key The first argument (a key), or NULL to take all arguments * from argp. @@ -189,11 +168,11 @@ /* Interpret variadic arguments. */ switch (type) { - case MSIM_TYPE_INTEGER: - case MSIM_TYPE_BOOLEAN: + case MSIM_TYPE_INTEGER: + case MSIM_TYPE_BOOLEAN: msg = msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int))); break; - + case MSIM_TYPE_STRING: value = va_arg(argp, char *); @@ -210,7 +189,7 @@ /* msim_msg_free() will free this GString the caller created. */ msg = msim_msg_append(msg, key, type, gs); break; - + case MSIM_TYPE_LIST: gl = va_arg(argp, GList *); @@ -237,9 +216,310 @@ return msg; } -/** Perform a deep copy on a GList * of gchar * strings. Free with msim_msg_list_free(). */ -GList * -msim_msg_list_copy(GList *old) +/** + * Create a new MsimMessage. + * + * @param first_key The first key in the sequence, or NULL for an empty message. + * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. + * + * See msim_msg_append() documentation for details on types. + */ +MsimMessage * +msim_msg_new(gchar *first_key, ...) +{ + va_list argp; + + if (first_key) { + va_start(argp, first_key); + return msim_msg_new_v(first_key, argp); + } else { + return NULL; + } +} + +/** + * Pack a string using the given GFunc and seperator. + * Used by msim_msg_dump() and msim_msg_pack(). + */ +static gchar * +msim_msg_pack_using(MsimMessage *msg, + GFunc gf, + const gchar *sep, + const gchar *begin, const gchar *end) +{ + int num_items; + gchar **strings; + gchar **strings_tmp; + gchar *joined; + gchar *final; + int i; + + g_return_val_if_fail(msg != NULL, NULL); + + num_items = g_list_length(msg); + + /* Add one for NULL terminator for g_strjoinv(). */ + strings = (gchar **)g_new0(gchar *, num_items + 1); + + strings_tmp = strings; + g_list_foreach(msg, gf, &strings_tmp); + + joined = g_strjoinv(sep, strings); + final = g_strconcat(begin, joined, end, NULL); + g_free(joined); + + /* Clean up. */ + for (i = 0; i < num_items; ++i) { + g_free(strings[i]); + } + + g_free(strings); + + return final; +} + +/** + * Return a human-readable string of the message. + * + * @return A new gchar *, must be g_free()'d. + */ +static gchar * +msim_msg_dump_to_str(MsimMessage *msg) +{ + gchar *debug_str; + + if (!msg) { + debug_str = g_strdup(""); + } else { + debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element, + "\n", ""); + } + + return debug_str; +} + +/** + * Store a human-readable string describing the element. + * + * @param data Pointer to an MsimMessageElement. + * @param user_data + */ +static void +msim_msg_debug_string_element(gpointer data, gpointer user_data) +{ + MsimMessageElement *elem; + gchar *string; + GString *gs; + gchar *binary; + gchar ***items; /* wow, a pointer to a pointer to a pointer */ + + gchar *s; + GList *gl; + guint i; + + elem = (MsimMessageElement *)data; + items = user_data; + + switch (elem->type) { + case MSIM_TYPE_INTEGER: + string = g_strdup_printf("%s(integer): %d", elem->name, + GPOINTER_TO_UINT(elem->data)); + break; + + case MSIM_TYPE_RAW: + string = g_strdup_printf("%s(raw): %s", elem->name, + elem->data ? (gchar *)elem->data : "(NULL)"); + break; + + case MSIM_TYPE_STRING: + string = g_strdup_printf("%s(string): %s", elem->name, + elem->data ? (gchar *)elem->data : "(NULL)"); + break; + + case MSIM_TYPE_BINARY: + gs = (GString *)elem->data; + binary = purple_base64_encode((guchar*)gs->str, gs->len); + string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary); + g_free(binary); + break; + + case MSIM_TYPE_BOOLEAN: + string = g_strdup_printf("%s(boolean): %s", elem->name, + elem->data ? "TRUE" : "FALSE"); + break; + + case MSIM_TYPE_DICTIONARY: + if (!elem->data) { + s = g_strdup("(NULL)"); + } else { + s = msim_msg_dump_to_str((MsimMessage *)elem->data); + } + + if (!s) { + s = g_strdup("(NULL, couldn't msim_msg_dump_to_str)"); + } + + string = g_strdup_printf("%s(dict): %s", elem->name, s); + + g_free(s); + break; + + case MSIM_TYPE_LIST: + gs = g_string_new(""); + g_string_append_printf(gs, "%s(list): \n", elem->name); + + i = 0; + for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) { + g_string_append_printf(gs, " %d. %s\n", i, (gchar *)(gl->data)); + ++i; + } + + string = g_string_free(gs, FALSE); + break; + + default: + string = g_strdup_printf("%s(unknown type %d", + elem->name ? elem->name : "(NULL)", elem->type); + break; + } + + **items = string; + ++(*items); +} + +/** + * Search for and return the node in msg, matching name, or NULL. + * + * @param msg Message to search within. + * @param name Field name to search for. + * + * @return The GList * node for the MsimMessageElement with the given name, or NULL if not found or name is NULL. + * + * For internal use - users probably want to use msim_msg_get() to + * access the MsimMessageElement *, instead of the GList * container. + * + */ +static GList * +msim_msg_get_node(MsimMessage *msg, const gchar *name) +{ + GList *node; + + if (!name || !msg) { + return NULL; + } + + /* Linear search for the given name. O(n) but n is small. */ + for (node = msg; node != NULL; node = g_list_next(node)) { + MsimMessageElement *elem; + + elem = (MsimMessageElement *)node->data; + + g_return_val_if_fail(elem != NULL, NULL); + g_return_val_if_fail(elem->name != NULL, NULL); + + if (strcmp(elem->name, name) == 0) { + return node; + } + } + return NULL; +} + +/** + * Create a new MsimMessageElement * - must be g_free()'d. + * + * For internal use; users probably want msim_msg_append() or msim_msg_insert_before(). + * + * @param dynamic_name Whether 'name' should be freed when the message is destroyed. + */ +static MsimMessageElement * +msim_msg_element_new(const gchar *name, MsimMessageType type, gpointer data, gboolean dynamic_name) +{ + MsimMessageElement *elem; + + elem = g_new0(MsimMessageElement, 1); + + elem->name = name; + elem->dynamic_name = dynamic_name; + elem->type = type; + elem->data = data; + + return elem; +} + +/** + * Append a new element to a message. + * + * @param name Textual name of element (static string, neither copied nor freed). + * @param type An MSIM_TYPE_* code. + * @param data Pointer to data, see below. + * + * @return The new message - must be assigned to as with GList*. For example: + * + * msg = msim_msg_append(msg, ...) + * + * The data parameter depends on the type given: + * + * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x). + * + * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE. + * + * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed. + * + * * MSIM_TYPE_RAW: gchar *. The data WILL BE FREED - use g_strdup() if needed. + * + * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data AND GString will be freed. + * + * * MSIM_TYPE_DICTIONARY: An MsimMessage *. Freed when message is destroyed. + * + * * MSIM_TYPE_LIST: GList * of gchar *. Again, everything will be freed. + * + * */ +MsimMessage * +msim_msg_append(MsimMessage *msg, const gchar *name, + MsimMessageType type, gpointer data) +{ + return g_list_append(msg, msim_msg_element_new(name, type, data, FALSE)); +} + +/** + * Append a new element, but with a dynamically-allocated name. + * Exactly the same as msim_msg_append(), except 'name' will be freed when + * the message is destroyed. Normally, it isn't, because a static string is given. + */ +static MsimMessage * +msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name, + MsimMessageType type, gpointer data) +{ + return g_list_append(msg, msim_msg_element_new(name, type, data, TRUE)); +} + +/** + * Insert a new element into a message, before the given element name. + * + * @param name_before Name of the element to insert the new element before. If + * could not be found or NULL, new element will be inserted at end. + * + * See msim_msg_append() for usage of other parameters, and an important note about return value. + */ +MsimMessage * +msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, + const gchar *name, MsimMessageType type, gpointer data) +{ + MsimMessageElement *new_elem; + GList *node_before; + + new_elem = msim_msg_element_new(name, type, data, FALSE); + + node_before = msim_msg_get_node(msg, name_before); + + return g_list_insert_before(msg, node_before, new_elem); +} + +/** + * Perform a deep copy on a GList * of gchar * strings. Free with msim_msg_list_free(). + */ +static GList * +msim_msg_list_copy(const GList *old) { GList *new_list; @@ -253,65 +533,13 @@ return new_list; } -/** Free a GList * of MsimMessageElement *'s. */ -void -msim_msg_list_free(GList *l) -{ - - for (; l != NULL; l = g_list_next(l)) { - MsimMessageElement *elem; - - elem = (MsimMessageElement *)l->data; - - /* Note that name is almost never dynamically allocated elsewhere; - * it is usually a static string, but not in lists. So cast it. */ - g_free((gchar *)elem->name); - g_free(elem->data); - g_free(elem); - } - g_list_free(l); -} - -/** Parse a |-separated string into a new GList. Free with msim_msg_list_free(). */ -GList * -msim_msg_list_parse(const gchar *raw) -{ - gchar **array; - GList *list; - guint i; - - array = g_strsplit(raw, "|", 0); - list = NULL; - - /* TODO: escape/unescape /3 <-> | within list elements */ - - for (i = 0; array[i] != NULL; ++i) { - MsimMessageElement *elem; - - /* Freed in msim_msg_list_free() */ - elem = g_new0(MsimMessageElement, 1); - - /* Give the element a name for debugging purposes. - * Not supposed to be looked up by this name; instead, - * lookup the elements by indexing the array. */ - elem->name = g_strdup_printf("(list item #%d)", i); - elem->type = MSIM_TYPE_RAW; - elem->data = g_strdup(array[i]); - - list = g_list_append(list, elem); - } - - g_strfreev(array); - - return list; -} - -/** Clone an individual element. +/** + * Clone an individual element. * * @param data MsimMessageElement * to clone. * @param user_data Pointer to MsimMessage * to add cloned element to. */ -static void +static void msim_msg_clone_element(gpointer data, gpointer user_data) { MsimMessageElement *elem; @@ -357,10 +585,14 @@ /* Append cloned data. Note that the 'name' field is a static string, so it * never needs to be copied nor freed. */ - *new = msim_msg_append(*new, elem->name, elem->type, new_data); + if (elem->dynamic_name) + *new = msim_msg_append_dynamic_name(*new, g_strdup(elem->name), elem->type, new_data); + else + *new = msim_msg_append(*new, elem->name, elem->type, new_data); } -/** Clone an existing MsimMessage. +/** + * Clone an existing MsimMessage. * * @return Cloned message; caller should free with msim_msg_free(). */ @@ -380,7 +612,8 @@ return new; } -/** Free the data of a message element. +/** + * Free the data of a message element. * * @param elem The MsimMessageElement * * @@ -411,7 +644,7 @@ case MSIM_TYPE_DICTIONARY: msim_msg_free((MsimMessage *)elem->data); break; - + case MSIM_TYPE_LIST: g_list_free((GList *)elem->data); break; @@ -423,7 +656,29 @@ } } -/** Free an individual message element. +/** + * Free a GList * of MsimMessageElement *'s. + */ +void +msim_msg_list_free(GList *l) +{ + + for (; l != NULL; l = g_list_next(l)) { + MsimMessageElement *elem; + + elem = (MsimMessageElement *)l->data; + + /* Note that name is almost never dynamically allocated elsewhere; + * it is usually a static string, but not in lists. So cast it. */ + g_free((gchar *)elem->name); + g_free(elem->data); + g_free(elem); + } + g_list_free(l); +} + +/** + * Free an individual message element. * * @param data MsimMessageElement * to free. * @param user_data Not used; required to match g_list_foreach() callback prototype. @@ -431,7 +686,7 @@ * Frees both the element data and the element itself. * Also frees the name if dynamic_name is TRUE. */ -static void +static void msim_msg_free_element(gpointer data, gpointer user_data) { MsimMessageElement *elem; @@ -449,8 +704,10 @@ g_free(elem); } -/** Free a complete message. */ -void +/** + * Free a complete message. + */ +void msim_msg_free(MsimMessage *msg) { if (!msg) { @@ -466,422 +723,8 @@ g_list_free(msg); } -/** Send an existing MsimMessage. */ -gboolean -msim_msg_send(MsimSession *session, MsimMessage *msg) -{ - gchar *raw; - gboolean success; - - raw = msim_msg_pack(msg); - g_return_val_if_fail(raw != NULL, FALSE); - success = msim_send_raw(session, raw); - g_free(raw); - - msim_msg_dump("msim_msg_send()ing %s\n", msg); - - return success; -} - /** - * - * Send a message to the server, whose contents is specified using - * variable arguments. - * - * @param session - * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. - * - * This function exists for coding convenience: it allows a message to be created - * and sent in one line of code. Internally it calls msim_msg_send(). - * - * IMPORTANT: See msim_msg_append() documentation for details on element types. - * - */ -gboolean -msim_send(MsimSession *session, ...) -{ - gboolean success; - MsimMessage *msg; - va_list argp; - - va_start(argp, session); - msg = msim_msg_new_v(NULL, argp); - - /* Actually send the message. */ - success = msim_msg_send(session, msg); - - /* Cleanup. */ - msim_msg_free(msg); - - return success; -} - -/** Create a new MsimMessageElement * - must be g_free()'d. - * - * For internal use; users probably want msim_msg_append() or msim_msg_insert_before(). - * - * @param dynamic_name Whether 'name' should be freed when the message is destroyed. - */ -static MsimMessageElement * -msim_msg_element_new(const gchar *name, MsimMessageType type, gpointer data, gboolean dynamic_name) -{ - MsimMessageElement *elem; - - elem = g_new0(MsimMessageElement, 1); - - elem->name = name; - elem->dynamic_name = dynamic_name; - elem->type = type; - elem->data = data; - - return elem; -} - - -/** Append a new element to a message. - * - * @param name Textual name of element (static string, neither copied nor freed). - * @param type An MSIM_TYPE_* code. - * @param data Pointer to data, see below. - * - * @return The new message - must be assigned to as with GList*. For example: - * - * msg = msim_msg_append(msg, ...) - * - * The data parameter depends on the type given: - * - * * MSIM_TYPE_INTEGER: Use GUINT_TO_POINTER(x). - * - * * MSIM_TYPE_BINARY: Same as integer, non-zero is TRUE and zero is FALSE. - * - * * MSIM_TYPE_STRING: gchar *. The data WILL BE FREED - use g_strdup() if needed. - * - * * MSIM_TYPE_RAW: gchar *. The data WILL BE FREED - use g_strdup() if needed. - * - * * MSIM_TYPE_BINARY: g_string_new_len(data, length). The data AND GString will be freed. - * - * * MSIM_TYPE_DICTIONARY: An MsimMessage *. Freed when message is destroyed. - * - * * MSIM_TYPE_LIST: GList * of gchar *. Again, everything will be freed. - * - * */ -MsimMessage * -msim_msg_append(MsimMessage *msg, const gchar *name, - MsimMessageType type, gpointer data) -{ - return g_list_append(msg, msim_msg_element_new(name, type, data, FALSE)); -} - -/** Append a new element, but with a dynamically-allocated name. - * Exactly the same as msim_msg_append(), except 'name' will be freed when - * the message is destroyed. Normally, it isn't, because a static string is given. - */ -static MsimMessage * -msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name, - MsimMessageType type, gpointer data) -{ - return g_list_append(msg, msim_msg_element_new(name, type, data, TRUE)); -} - -/** Insert a new element into a message, before the given element name. - * - * @param name_before Name of the element to insert the new element before. If - * could not be found or NULL, new element will be inserted at end. - * - * See msim_msg_append() for usage of other parameters, and an important note about return value. - */ -MsimMessage * -msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, - const gchar *name, MsimMessageType type, gpointer data) -{ - MsimMessageElement *new_elem; - GList *node_before; - - new_elem = msim_msg_element_new(name, type, data, FALSE); - - node_before = msim_msg_get_node(msg, name_before); - - return g_list_insert_before(msg, node_before, new_elem); -} - -/** Pack a string using the given GFunc and seperator. - * Used by msim_msg_dump() and msim_msg_pack(). - */ -gchar * -msim_msg_pack_using(MsimMessage *msg, - GFunc gf, - const gchar *sep, - const gchar *begin, const gchar *end) -{ - int num_items; - gchar **strings; - gchar **strings_tmp; - gchar *joined; - gchar *final; - int i; - - g_return_val_if_fail(msg != NULL, NULL); - - num_items = g_list_length(msg); - - /* Add one for NULL terminator for g_strjoinv(). */ - strings = (gchar **)g_new0(gchar *, num_items + 1); - - strings_tmp = strings; - g_list_foreach(msg, gf, &strings_tmp); - - joined = g_strjoinv(sep, strings); - final = g_strconcat(begin, joined, end, NULL); - g_free(joined); - - /* Clean up. */ - for (i = 0; i < num_items; ++i) { - g_free(strings[i]); - } - - g_free(strings); - - return final; -} -/** Store a human-readable string describing the element. - * - * @param data Pointer to an MsimMessageElement. - * @param user_data - */ -static void -msim_msg_debug_string_element(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - gchar *string; - GString *gs; - gchar *binary; - gchar ***items; /* wow, a pointer to a pointer to a pointer */ - - gchar *s; - GList *gl; - guint i; - - elem = (MsimMessageElement *)data; - items = user_data; - - switch (elem->type) { - case MSIM_TYPE_INTEGER: - string = g_strdup_printf("%s(integer): %d", elem->name, - GPOINTER_TO_UINT(elem->data)); - break; - - case MSIM_TYPE_RAW: - string = g_strdup_printf("%s(raw): %s", elem->name, - elem->data ? (gchar *)elem->data : "(NULL)"); - break; - - case MSIM_TYPE_STRING: - string = g_strdup_printf("%s(string): %s", elem->name, - elem->data ? (gchar *)elem->data : "(NULL)"); - break; - - case MSIM_TYPE_BINARY: - gs = (GString *)elem->data; - binary = purple_base64_encode((guchar*)gs->str, gs->len); - string = g_strdup_printf("%s(binary, %d bytes): %s", elem->name, (int)gs->len, binary); - g_free(binary); - break; - - case MSIM_TYPE_BOOLEAN: - string = g_strdup_printf("%s(boolean): %s", elem->name, - elem->data ? "TRUE" : "FALSE"); - break; - - case MSIM_TYPE_DICTIONARY: - if (!elem->data) { - s = g_strdup("(NULL)"); - } else { - s = msim_msg_dump_to_str((MsimMessage *)elem->data); - } - - if (!s) { - s = g_strdup("(NULL, couldn't msim_msg_dump_to_str)"); - } - - string = g_strdup_printf("%s(dict): %s", elem->name, s); - - g_free(s); - break; - - case MSIM_TYPE_LIST: - gs = g_string_new(""); - g_string_append_printf(gs, "%s(list): \n", elem->name); - - i = 0; - for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) { - g_string_append_printf(gs, " %d. %s\n", i, (gchar *)(gl->data)); - ++i; - } - - string = g_string_free(gs, FALSE); - break; - - default: - string = g_strdup_printf("%s(unknown type %d", - elem->name ? elem->name : "(NULL)", elem->type); - break; - } - - **items = string; - ++(*items); -} - -/** Print a human-readable string of the message to Purple's debug log. - * - * @param fmt_string A static string, in which '%s' will be replaced. - */ -void -msim_msg_dump(const gchar *fmt_string, MsimMessage *msg) -{ - gchar *debug_str; - - g_return_if_fail(fmt_string != NULL); - - debug_str = msim_msg_dump_to_str(msg); - - g_return_if_fail(debug_str != NULL); - - purple_debug_info("msim", fmt_string, debug_str); - - g_free(debug_str); -} - -/** Return a human-readable string of the message. - * - * @return A new gchar *, must be g_free()'d. - */ -gchar * -msim_msg_dump_to_str(MsimMessage *msg) -{ - gchar *debug_str; - - if (!msg) { - debug_str = g_strdup(""); - } else { - debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element, - "\n", ""); - } - - return debug_str; -} - -/** Return a message element data as a new string for a raw protocol message, converting from other types (integer, etc.) if necessary. - * - * @return const gchar * The data as a string, or NULL. Caller must g_free(). - * - * Returns a string suitable for inclusion in a raw protocol message, not necessarily - * optimal for human consumption. For example, strings are escaped. Use - * msim_msg_get_string() if you want a string, which in some cases is same as this. - */ -gchar * -msim_msg_pack_element_data(MsimMessageElement *elem) -{ - GString *gs; - GList *gl; - - g_return_val_if_fail(elem != NULL, NULL); - - switch (elem->type) { - case MSIM_TYPE_INTEGER: - return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data)); - - case MSIM_TYPE_RAW: - /* Not un-escaped - this is a raw element, already escaped if necessary. */ - return (gchar *)g_strdup((gchar *)elem->data); - - case MSIM_TYPE_STRING: - /* Strings get escaped. msim_escape() creates a new string. */ - g_return_val_if_fail(elem->data != NULL, NULL); - return elem->data ? msim_escape((gchar *)elem->data) : - g_strdup("(NULL)"); - - case MSIM_TYPE_BINARY: - gs = (GString *)elem->data; - /* Do not escape! */ - return purple_base64_encode((guchar *)gs->str, gs->len); - - case MSIM_TYPE_BOOLEAN: - /* Not used by messages in the wire protocol * -- see msim_msg_pack_element. - * Only used by dictionaries, see msim_msg_pack_element_dict. */ - return elem->data ? g_strdup("On") : g_strdup("Off"); - - case MSIM_TYPE_DICTIONARY: - return msim_msg_pack_dict((MsimMessage *)elem->data); - - case MSIM_TYPE_LIST: - /* Pack using a|b|c|d|... */ - gs = g_string_new(""); - - for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) { - g_string_append_printf(gs, "%s", (gchar*)(gl->data)); - - /* All but last element is separated by a bar. */ - if (g_list_next(gl)) - g_string_append(gs, "|"); - } - - return g_string_free(gs, FALSE); - - default: - purple_debug_info("msim", "field %s, unknown type %d\n", - elem->name ? elem->name : "(NULL)", - elem->type); - return NULL; - } -} - -/** Pack an element into its protcol representation inside a dictionary. - * - * See msim_msg_pack_element(). - */ -static void -msim_msg_pack_element_dict(gpointer data, gpointer user_data) -{ - MsimMessageElement *elem; - gchar *string, *data_string, ***items; - - elem = (MsimMessageElement *)data; - items = (gchar ***)user_data; - - /* Exclude elements beginning with '_' from packed protocol messages. */ - if (elem->name[0] == '_') { - return; - } - - data_string = msim_msg_pack_element_data(elem); - - g_return_if_fail(data_string != NULL); - - switch (elem->type) { - /* These types are represented by key name/value pairs (converted above). */ - case MSIM_TYPE_INTEGER: - case MSIM_TYPE_RAW: - case MSIM_TYPE_STRING: - case MSIM_TYPE_BINARY: - case MSIM_TYPE_DICTIONARY: - case MSIM_TYPE_LIST: - case MSIM_TYPE_BOOLEAN: /* Boolean is On or Off */ - string = g_strconcat(elem->name, "=", data_string, NULL); - break; - - default: - g_free(data_string); - g_return_if_fail(FALSE); - break; - } - - g_free(data_string); - - **items = string; - ++(*items); -} - -/** Pack an element into its protocol representation. + * Pack an element into its protocol representation. * * @param data Pointer to an MsimMessageElement. * @param user_data Pointer to a gchar ** array of string items. @@ -891,7 +734,7 @@ * is responsible for creating array to correct dimensions, and * freeing each string element of the array added by this function. */ -static void +static void msim_msg_pack_element(gpointer data, gpointer user_data) { MsimMessageElement *elem; @@ -942,8 +785,55 @@ ++(*items); } +/** + * Pack an element into its protcol representation inside a dictionary. + * + * See msim_msg_pack_element(). + */ +static void +msim_msg_pack_element_dict(gpointer data, gpointer user_data) +{ + MsimMessageElement *elem; + gchar *string, *data_string, ***items; -/** Return a packed string of a message suitable for sending over the wire. + elem = (MsimMessageElement *)data; + items = (gchar ***)user_data; + + /* Exclude elements beginning with '_' from packed protocol messages. */ + if (elem->name[0] == '_') { + return; + } + + data_string = msim_msg_pack_element_data(elem); + + g_return_if_fail(data_string != NULL); + + switch (elem->type) { + /* These types are represented by key name/value pairs (converted above). */ + case MSIM_TYPE_INTEGER: + case MSIM_TYPE_RAW: + case MSIM_TYPE_STRING: + case MSIM_TYPE_BINARY: + case MSIM_TYPE_DICTIONARY: + case MSIM_TYPE_LIST: + case MSIM_TYPE_BOOLEAN: /* Boolean is On or Off */ + string = g_strconcat(elem->name, "=", data_string, NULL); + break; + + default: + g_free(data_string); + g_return_if_fail(FALSE); + break; + } + + g_free(data_string); + + **items = string; + ++(*items); +} + +/** + * Return a packed string of a message suitable for sending over the wire. * * @return A string. Caller must g_free(). */ @@ -955,11 +845,12 @@ return msim_msg_pack_using(msg, msim_msg_pack_element, "\\", "\\", "\\final\\"); } -/** Return a packed string of a dictionary, suitable for embedding in MSIM_TYPE_DICTIONARY. +/** + * Return a packed string of a dictionary, suitable for embedding in MSIM_TYPE_DICTIONARY. * * @return A string; caller must g_free(). */ -gchar * +static gchar * msim_msg_pack_dict(MsimMessage *msg) { g_return_val_if_fail(msg != NULL, NULL); @@ -967,7 +858,146 @@ return msim_msg_pack_using(msg, msim_msg_pack_element_dict, "\034", "", ""); } -/** +/** + * Send an existing MsimMessage. + */ +gboolean +msim_msg_send(MsimSession *session, MsimMessage *msg) +{ + gchar *raw; + gboolean success; + + raw = msim_msg_pack(msg); + g_return_val_if_fail(raw != NULL, FALSE); + success = msim_send_raw(session, raw); + g_free(raw); + + msim_msg_dump("msim_msg_send()ing %s\n", msg); + + return success; +} + +/** + * Return a message element data as a new string for a raw protocol message, + * converting from other types (integer, etc.) if necessary. + * + * @return const gchar * The data as a string, or NULL. Caller must g_free(). + * + * Returns a string suitable for inclusion in a raw protocol message, not necessarily + * optimal for human consumption. For example, strings are escaped. Use + * msim_msg_get_string() if you want a string, which in some cases is same as this. + */ +gchar * +msim_msg_pack_element_data(MsimMessageElement *elem) +{ + GString *gs; + GList *gl; + + g_return_val_if_fail(elem != NULL, NULL); + + switch (elem->type) { + case MSIM_TYPE_INTEGER: + return g_strdup_printf("%d", GPOINTER_TO_UINT(elem->data)); + + case MSIM_TYPE_RAW: + /* Not un-escaped - this is a raw element, already escaped if necessary. */ + return (gchar *)g_strdup((gchar *)elem->data); + + case MSIM_TYPE_STRING: + /* Strings get escaped. msim_escape() creates a new string. */ + g_return_val_if_fail(elem->data != NULL, NULL); + return elem->data ? msim_escape((gchar *)elem->data) : + g_strdup("(NULL)"); + + case MSIM_TYPE_BINARY: + gs = (GString *)elem->data; + /* Do not escape! */ + return purple_base64_encode((guchar *)gs->str, gs->len); + + case MSIM_TYPE_BOOLEAN: + /* Not used by messages in the wire protocol * -- see msim_msg_pack_element. + * Only used by dictionaries, see msim_msg_pack_element_dict. */ + return elem->data ? g_strdup("On") : g_strdup("Off"); + + case MSIM_TYPE_DICTIONARY: + return msim_msg_pack_dict((MsimMessage *)elem->data); + + case MSIM_TYPE_LIST: + /* Pack using a|b|c|d|... */ + gs = g_string_new(""); + + for (gl = (GList *)elem->data; gl != NULL; gl = g_list_next(gl)) { + g_string_append_printf(gs, "%s", (gchar*)(gl->data)); + + /* All but last element is separated by a bar. */ + if (g_list_next(gl)) + g_string_append(gs, "|"); + } + + return g_string_free(gs, FALSE); + + default: + purple_debug_info("msim", "field %s, unknown type %d\n", + elem->name ? elem->name : "(NULL)", + elem->type); + return NULL; + } +} + +/** + * Send a message to the server, whose contents is specified using + * variable arguments. + * + * @param session + * @param ... A sequence of gchar* key/type/value triplets, terminated with NULL. + * + * This function exists for coding convenience: it allows a message to be created + * and sent in one line of code. Internally it calls msim_msg_send(). + * + * IMPORTANT: See msim_msg_append() documentation for details on element types. + * + */ +gboolean +msim_send(MsimSession *session, ...) +{ + gboolean success; + MsimMessage *msg; + va_list argp; + + va_start(argp, session); + msg = msim_msg_new_v(NULL, argp); + + /* Actually send the message. */ + success = msim_msg_send(session, msg); + + /* Cleanup. */ + msim_msg_free(msg); + + return success; +} + +/** + * Print a human-readable string of the message to Purple's debug log. + * + * @param fmt_string A static string, in which '%s' will be replaced. + */ +void +msim_msg_dump(const gchar *fmt_string, MsimMessage *msg) +{ + gchar *debug_str; + + g_return_if_fail(fmt_string != NULL); + + debug_str = msim_msg_dump_to_str(msg); + + g_return_if_fail(debug_str != NULL); + + purple_debug_info("msim", fmt_string, debug_str); + + g_free(debug_str); +} + +/** * Parse a raw protocol message string into a MsimMessage *. * * @param raw The raw message string to parse, will be g_free()'d. @@ -975,7 +1005,7 @@ * @return MsimMessage *. Caller should msim_msg_free() when done. */ MsimMessage * -msim_parse(gchar *raw) +msim_parse(const gchar *raw) { MsimMessage *msg; gchar *token; @@ -996,13 +1026,12 @@ "missing initial backslash: <%s>\n", raw); /* XXX: Should we try to recover, and read to first backslash? */ - g_free(raw); return NULL; } msg = msim_msg_new(FALSE); - for (tokens = g_strsplit(raw + 1, "\\", 0), i = 0; + for (tokens = g_strsplit(raw + 1, "\\", 0), i = 0; (token = tokens[i]); i++) { #ifdef MSIM_DEBUG_PARSE @@ -1012,7 +1041,7 @@ /* Odd-numbered ordinal is a value. */ value = token; - + /* Incoming protocol messages get tagged as MSIM_TYPE_RAW, which * represents an untyped piece of data. msim_msg_get_* will * convert to appropriate types for caller, and handle unescaping if needed. */ @@ -1027,49 +1056,11 @@ } g_strfreev(tokens); - /* Can free now since all data was copied to hash key/values */ - g_free(raw); - return msg; } -/** Search for and return the node in msg, matching name, or NULL. - * - * @param msg Message to search within. - * @param name Field name to search for. - * - * @return The GList * node for the MsimMessageElement with the given name, or NULL if not found or name is NULL. - * - * For internal use - users probably want to use msim_msg_get() to - * access the MsimMessageElement *, instead of the GList * container. - * - */ -static GList * -msim_msg_get_node(MsimMessage *msg, const gchar *name) -{ - GList *node; - - if (!name || !msg) { - return NULL; - } - - /* Linear search for the given name. O(n) but n is small. */ - for (node = msg; node != NULL; node = g_list_next(node)) { - MsimMessageElement *elem; - - elem = (MsimMessageElement *)node->data; - - g_return_val_if_fail(elem != NULL, NULL); - g_return_val_if_fail(elem->name != NULL, NULL); - - if (strcmp(elem->name, name) == 0) { - return node; - } - } - return NULL; -} - -/** Return the first MsimMessageElement * with given name in the MsimMessage *. +/** + * Return the first MsimMessageElement * with given name in the MsimMessage *. * * @param name Name to search for. * @@ -1092,30 +1083,6 @@ } } -/** Return the data of an element of a given name, as a string. - * - * @param name Name of element. - * - * @return gchar * The data as a string, or NULL if not found. - * Caller must g_free(). - * - * Note that msim_msg_pack_element_data() is similar, but returns a string - * for inclusion into a raw protocol string (escaped and everything). - * This function unescapes the string for you, if needed. - */ -gchar * -msim_msg_get_string(MsimMessage *msg, const gchar *name) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - if (!elem) { - return NULL; - } - - return msim_msg_get_string_from_element(elem); -} - gchar * msim_msg_get_string_from_element(MsimMessageElement *elem) { @@ -1140,9 +1107,20 @@ } } -/** Return an element as a new list. Caller frees with msim_msg_list_free(). */ -GList * -msim_msg_get_list(MsimMessage *msg, const gchar *name) +/** + * Return the data of an element of a given name, as a string. + * + * @param name Name of element. + * + * @return gchar * The data as a string, or NULL if not found. + * Caller must g_free(). + * + * Note that msim_msg_pack_element_data() is similar, but returns a string + * for inclusion into a raw protocol string (escaped and everything). + * This function unescapes the string for you, if needed. + */ +gchar * +msim_msg_get_string(MsimMessage *msg, const gchar *name) { MsimMessageElement *elem; @@ -1151,10 +1129,46 @@ return NULL; } - return msim_msg_get_list_from_element(elem); + return msim_msg_get_string_from_element(elem); } -GList * +/** + * Parse a |-separated string into a new GList. Free with msim_msg_list_free(). + */ +static GList * +msim_msg_list_parse(const gchar *raw) +{ + gchar **array; + GList *list; + guint i; + + array = g_strsplit(raw, "|", 0); + list = NULL; + + /* TODO: escape/unescape /3 <-> | within list elements */ + + for (i = 0; array[i] != NULL; ++i) { + MsimMessageElement *elem; + + /* Freed in msim_msg_list_free() */ + elem = g_new0(MsimMessageElement, 1); + + /* Give the element a name for debugging purposes. + * Not supposed to be looked up by this name; instead, + * lookup the elements by indexing the array. */ + elem->name = g_strdup_printf("(list item #%d)", i); + elem->type = MSIM_TYPE_RAW; + elem->data = g_strdup(array[i]); + + list = g_list_append(list, elem); + } + + g_strfreev(array); + + return list; +} + +static GList * msim_msg_get_list_from_element(MsimMessageElement *elem) { g_return_val_if_fail(elem != NULL, NULL); @@ -1173,6 +1187,22 @@ } /** + * Return an element as a new list. Caller frees with msim_msg_list_free(). + */ +GList * +msim_msg_get_list(MsimMessage *msg, const gchar *name) +{ + MsimMessageElement *elem; + + elem = msim_msg_get(msg, name); + if (!elem) { + return NULL; + } + + return msim_msg_get_list_from_element(elem); +} + +/** * Parse a \x1c-separated "dictionary" of key=value pairs into a hash table. * * @param raw The text of the dictionary to parse. Often the @@ -1180,8 +1210,8 @@ * * @return A new MsimMessage *. Must msim_msg_free() when done. */ -MsimMessage * -msim_msg_dictionary_parse(gchar *raw) +static MsimMessage * +msim_msg_dictionary_parse(const gchar *raw) { MsimMessage *dict; gchar *item; @@ -1192,8 +1222,8 @@ g_return_val_if_fail(raw != NULL, NULL); dict = msim_msg_new(NULL); - - for (items = g_strsplit(raw, "\x1c", 0), i = 0; + + for (items = g_strsplit(raw, "\x1c", 0), i = 0; (item = items[i]); i++) { gchar *key, *value; @@ -1202,7 +1232,7 @@ key = elements[0]; if (!key) { - purple_debug_info("msim", "msim_msg_parse_dictionary(%s): null key\n", + purple_debug_info("msim", "msim_msg_dictionary_parse(%s): null key\n", raw); g_strfreev(elements); break; @@ -1210,14 +1240,14 @@ value = elements[1]; if (!value) { - purple_debug_info("msim", "msim_msg_parse_dictionary(%s): null value\n", + purple_debug_info("msim", "msim_msg_dictionary_prase(%s): null value\n", raw); g_strfreev(elements); break; } #ifdef MSIM_DEBUG_PARSE - purple_debug_info("msim_msg_parse_dictionary","-- %s: %s\n", key ? key : "(NULL)", + purple_debug_info("msim_msg_dictionary_parse","-- %s: %s\n", key ? key : "(NULL)", value ? value : "(NULL)"); #endif /* Append with _dynamic_name since g_strdup(key) is dynamic, and @@ -1232,7 +1262,27 @@ return dict; } -/** Return an element as a new dictionary. Caller frees with msim_msg_free(). */ +static MsimMessage * +msim_msg_get_dictionary_from_element(MsimMessageElement *elem) +{ + g_return_val_if_fail(elem != NULL, NULL); + switch (elem->type) { + case MSIM_TYPE_DICTIONARY: + return msim_msg_clone((MsimMessage *)elem->data); + + case MSIM_TYPE_RAW: + return msim_msg_dictionary_parse(elem->data); + + default: + purple_debug_info("msim_msg_get_dictionary", "type %d unknown, name %s\n", + elem->type, elem->name ? elem->name : "(NULL)"); + return NULL; + } +} + +/** + * Return an element as a new dictionary. Caller frees with msim_msg_free(). + */ MsimMessage * msim_msg_get_dictionary(MsimMessage *msg, const gchar *name) { @@ -1246,49 +1296,6 @@ return msim_msg_get_dictionary_from_element(elem); } -MsimMessage * -msim_msg_get_dictionary_from_element(MsimMessageElement *elem) -{ - g_return_val_if_fail(elem != NULL, NULL); - switch (elem->type) { - case MSIM_TYPE_DICTIONARY: - return msim_msg_clone((MsimMessage *)elem->data); - - case MSIM_TYPE_RAW: - return msim_msg_dictionary_parse((gchar *)elem->data); - - default: - purple_debug_info("msim_msg_get_dictionary", "type %d unknown, name %s\n", - elem->type, elem->name ? elem->name : "(NULL)"); - return NULL; - } -} - -/** Return the data of an element of a given name, as an unsigned integer. - * - * @param name Name of element. - * - * @return guint Numeric representation of data, or 0 if could not be converted / not found. - * - * Useful to obtain an element's data if you know it should be an integer, - * even if it is not stored as an MSIM_TYPE_INTEGER. MSIM_TYPE_STRING will - * be converted handled correctly, for example. - */ -guint -msim_msg_get_integer(MsimMessage *msg, const gchar *name) -{ - MsimMessageElement *elem; - - elem = msim_msg_get(msg, name); - - if (!elem) { - return 0; - } - - return msim_msg_get_integer_from_element(elem); -} - - guint msim_msg_get_integer_from_element(MsimMessageElement *elem) { @@ -1307,29 +1314,32 @@ } } -/** Return the data of an element of a given name, as a binary GString. +/** + * Return the data of an element of a given name, as an unsigned integer. * - * @param binary_data A pointer to a new pointer, which will be filled in with the binary data. CALLER MUST g_free(). + * @param name Name of element. * - * @param binary_length A pointer to an integer, which will be set to the binary data length. + * @return guint Numeric representation of data, or 0 if could not be converted / not found. * - * @return TRUE if successful, FALSE if not. + * Useful to obtain an element's data if you know it should be an integer, + * even if it is not stored as an MSIM_TYPE_INTEGER. MSIM_TYPE_STRING will + * be converted handled correctly, for example. */ -gboolean -msim_msg_get_binary(MsimMessage *msg, const gchar *name, - gchar **binary_data, gsize *binary_length) +guint +msim_msg_get_integer(MsimMessage *msg, const gchar *name) { MsimMessageElement *elem; - + elem = msim_msg_get(msg, name); + if (!elem) { - return FALSE; + return 0; } - return msim_msg_get_binary_from_element(elem, binary_data, binary_length); + return msim_msg_get_integer_from_element(elem); } -gboolean +static gboolean msim_msg_get_binary_from_element(MsimMessageElement *elem, gchar **binary_data, gsize *binary_length) { GString *gs; @@ -1344,7 +1354,7 @@ * by msimprpl code for things like instant messages - stuff that should be * escaped if needed). DWIM. */ - + /* Previously, incoming messages were stored as MSIM_TYPE_STRING. * This was fine for integers and strings, since they can easily be * converted in msim_get_*, as desirable. However, it does not work @@ -1372,7 +1382,7 @@ /* Rejected because if it isn't already a GString, have to g_new0 it and - * then caller has to ALSO free the GString! + * then caller has to ALSO free the GString! * * return (GString *)elem->data; */ @@ -1382,3 +1392,26 @@ return FALSE; } } + +/** + * Return the data of an element of a given name, as a binary GString. + * + * @param binary_data A pointer to a new pointer, which will be filled in with the binary data. CALLER MUST g_free(). + * + * @param binary_length A pointer to an integer, which will be set to the binary data length. + * + * @return TRUE if successful, FALSE if not. + */ +gboolean +msim_msg_get_binary(MsimMessage *msg, const gchar *name, + gchar **binary_data, gsize *binary_length) +{ + MsimMessageElement *elem; + + elem = msim_msg_get(msg, name); + if (!elem) { + return FALSE; + } + + return msim_msg_get_binary_from_element(elem, binary_data, binary_length); +} diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/myspace/message.h --- a/libpurple/protocols/myspace/message.h Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/myspace/message.h Fri Jan 02 21:48:12 2009 +0000 @@ -24,17 +24,20 @@ #include +#define MsimMessage GList /* #define instead of typedef to avoid casting */ +typedef gchar MsimMessageType; +typedef struct _MsimMessageElement MsimMessageElement; + +#include "session.h" + /* Types */ -#define MsimMessage GList /* #define instead of typedef to avoid casting */ -typedef struct _MsimMessageElement +struct _MsimMessageElement { const gchar *name; /**< Textual name of element. */ gboolean dynamic_name; /**< TRUE if 'name' is a dynamic string to be freed, not static. */ guint type; /**< MSIM_TYPE_* code. */ gpointer data; /**< Pointer to data, or GUINT_TO_POINTER for int/bool. */ -} MsimMessageElement; - -typedef gchar MsimMessageType; +}; #define msim_msg_get_next_element_node(msg) ((MsimMessage *)(msg->next)) @@ -58,20 +61,13 @@ void msim_msg_free(MsimMessage *msg); MsimMessage *msim_msg_append(MsimMessage *msg, const gchar *name, MsimMessageType type, gpointer data); MsimMessage *msim_msg_insert_before(MsimMessage *msg, const gchar *name_before, const gchar *name, MsimMessageType type, gpointer data); -gchar *msim_msg_dump_to_str(MsimMessage *msg); gchar *msim_msg_pack_element_data(MsimMessageElement *elem); void msim_msg_dump(const char *fmt_string, MsimMessage *msg); gchar *msim_msg_pack(MsimMessage *msg); -gchar *msim_msg_pack_dict(MsimMessage *msg); -GList *msim_msg_list_copy(GList *old); void msim_msg_list_free(GList *l); -GList *msim_msg_list_parse(const gchar *raw); -/* Defined in myspace.h */ -struct _MsimSession; - -/* Based on http://permalink.gmane.org/gmane.comp.parsers.sparse/695 +/* Based on http://permalink.gmane.org/gmane.comp.parsers.sparse/695 * Define macros for useful gcc attributes. */ #ifdef __GNUC__ #define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) @@ -87,7 +83,7 @@ #define FORMAT_ATTR(pos) #define NORETURN_ATTR #define SENTINEL_ATTR -#endif +#endif /* Cause gcc to emit "a missing sentinel in function call" if forgot * to write NULL as last, terminating parameter. */ @@ -95,8 +91,7 @@ gboolean msim_msg_send(struct _MsimSession *session, MsimMessage *msg); -MsimMessage *msim_parse(gchar *raw); -MsimMessage *msim_msg_dictionary_parse(gchar *raw); +MsimMessage *msim_parse(const gchar *raw); MsimMessageElement *msim_msg_get(MsimMessage *msg, const gchar *name); @@ -109,10 +104,6 @@ /* Retrieve data by element (MsimMessageElement *), returned from msim_msg_get() */ gchar *msim_msg_get_string_from_element(MsimMessageElement *elem); -GList *msim_msg_get_list_from_element(MsimMessageElement *elem); -MsimMessage *msim_msg_get_dictionary_from_element(MsimMessageElement *elem); guint msim_msg_get_integer_from_element(MsimMessageElement *elem); -gboolean msim_msg_get_binary_from_element(MsimMessageElement *elem, - gchar **binary_data, gsize *binary_length); #endif /* _MYSPACE_MESSAGE_H */ diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/myspace/myspace.c --- a/libpurple/protocols/myspace/myspace.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/myspace/myspace.c Fri Jan 02 21:48:12 2009 +0000 @@ -2604,7 +2604,7 @@ purple_debug_info("msim", "in loop: buf=<%s>\n", session->rxbuf); #endif *end = 0; - msg = msim_parse(g_strdup(session->rxbuf)); + msg = msim_parse(session->rxbuf); if (!msg) { purple_debug_info("msim", "msim_input_cb: couldn't parse rxbuf\n"); purple_connection_error_reason (gc, diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/myspace/session.h --- a/libpurple/protocols/myspace/session.h Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/myspace/session.h Fri Jan 02 21:48:12 2009 +0000 @@ -20,6 +20,8 @@ #ifndef _MYSPACE_SESSION_H #define _MYSPACE_SESSION_H +#include "account.h" + /* Random number in every MsimSession, to ensure it is valid. */ #define MSIM_SESSION_STRUCT_MAGIC 0xe4a6752b diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/myspace/user.c --- a/libpurple/protocols/myspace/user.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/myspace/user.c Fri Jan 02 21:48:12 2009 +0000 @@ -174,8 +174,12 @@ /* TODO: link to username, if available */ char *profile; purple_notify_user_info_add_section_break(user_info); - profile = g_strdup_printf("%s", - user->id, _("View web profile")); + if (user->buddy != NULL) + profile = g_strdup_printf("%s", + purple_buddy_get_name(user->buddy), _("View web profile")); + else + profile = g_strdup_printf("%s", + user->id, _("View web profile")); purple_notify_user_info_add_pair(user_info, NULL, profile); g_free(profile); } diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/myspace/user.h --- a/libpurple/protocols/myspace/user.h Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/myspace/user.h Fri Jan 02 21:48:12 2009 +0000 @@ -25,6 +25,7 @@ typedef struct _MsimUser { PurpleBuddy *buddy; + /* Note: id is also &buddy->node (set_blist_node_int), when buddy is non-NULL */ int id; guint client_cv; gchar *client_info; @@ -34,7 +35,6 @@ guint total_friends; gchar *headline; gchar *display_name; - /* Note: uid is in &buddy->node (set_blist_node_int), since it never changes */ gchar *username; gchar *band_name, *song_name; gchar *image_url; diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/sametime/sametime.c --- a/libpurple/protocols/sametime/sametime.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/sametime/sametime.c Fri Jan 02 21:48:12 2009 +0000 @@ -514,6 +514,11 @@ idle_len = time(NULL) - idle; ugly_idle_len = ((time(NULL) * 1000) - idle) / 1000; + if(idle > ugly_idle_len) + ugly_idle_len = 0; + else + ugly_idle_len = (ugly_idle_len - idle) / 1000; + /* what's the deal here? Well, good clients are smart enough to publish their idle time by using an attribute to indicate that diff -r ca01403251f1 -r f8dbd57cf635 libpurple/protocols/simple/simple.c --- a/libpurple/protocols/simple/simple.c Fri Jan 02 21:47:52 2009 +0000 +++ b/libpurple/protocols/simple/simple.c Fri Jan 02 21:48:12 2009 +0000 @@ -1948,6 +1948,13 @@ sip->txbuf = purple_circ_buffer_new(0); userserver = g_strsplit(username, "@", 2); + if (userserver[1] == NULL || userserver[1][0] == '\0') { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, + _("SIP connect server not specified")); + return; + } + purple_connection_set_display_name(gc, userserver[0]); sip->username = g_strdup(userserver[0]); sip->servername = g_strdup(userserver[1]); diff -r ca01403251f1 -r f8dbd57cf635 po/de.po --- a/po/de.po Fri Jan 02 21:47:52 2009 +0000 +++ b/po/de.po Fri Jan 02 21:48:12 2009 +0000 @@ -7,13 +7,14 @@ # # This file is distributed under the same license as the Pidgin package. # +# Jochen Kemnade , 2008. msgid "" msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-12-14 22:58+0100\n" -"PO-Revision-Date: 2008-12-14 22:58+0100\n" -"Last-Translator: Björn Voigt \n" +"POT-Creation-Date: 2008-12-15 10:46+0100\n" +"PO-Revision-Date: 2008-12-15 10:46+0100\n" +"Last-Translator: Jochen Kemnade \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2906,10 +2907,10 @@ msgstr "Hostname oder Portnummer ihres Proxys sind falsch angegeben." msgid "Token Error" -msgstr "Kürzel-Fehler" +msgstr "Token-Fehler" msgid "Unable to fetch the token.\n" -msgstr "Kann das Kürzel nicht abholen.\n" +msgstr "Kann das Token nicht abholen.\n" msgid "Save Buddylist..." msgstr "Buddy-Liste speichern..." @@ -2961,10 +2962,10 @@ msgstr "Passwort (nochmal)" msgid "Enter current token" -msgstr "Geben Sie das aktuelle Kürzel ein" +msgstr "Geben Sie das aktuelle Token ein" msgid "Current token" -msgstr "Aktuelles Kürzel" +msgstr "Aktuelles Token" msgid "Register New Gadu-Gadu Account" msgstr "Registrierung eines neuen Gadu-Gadu-Kontos" @@ -5007,9 +5008,8 @@ msgid "Game Title" msgstr "Spieltitel" -#, fuzzy msgid "Office Title" -msgstr "Titel anpassen" +msgstr "Dienststellenbezeichnung" msgid "Set Friendly Name..." msgstr "Setze Spitzname..." @@ -7229,9 +7229,8 @@ msgid "Zodiac" msgstr "Sternzeichen" -#, fuzzy msgid "Blood" -msgstr "Blockiert" +msgstr "Blutgruppe" msgid "True" msgstr "Wahr" @@ -7453,9 +7452,9 @@ msgid "Removed buddy %u." msgstr "Buddy %u entfernt" -#, fuzzy, c-format +#, c-format msgid "New buddy %u joined." -msgstr "Buddy entfernen" +msgstr "Neuer Buddy %u ist beigetreten." #, c-format msgid "Unknown-%d" @@ -7494,17 +7493,17 @@ msgid "Select icon..." msgstr "Icon wählen..." -#, fuzzy, c-format +#, c-format msgid "Login time: %d-%d-%d, %d:%d:%d
\n" -msgstr "Anmeldezeit: %s
\n" - -#, fuzzy, c-format +msgstr "Anmeldezeit: %d-%d-%d, %d:%d:%d
\n" + +#, c-format msgid "Total Online Buddies: %d
\n" -msgstr "Aktuell online: %d
\n" - -#, fuzzy, c-format +msgstr "Insgesamt online: %d
\n" + +#, c-format msgid "Last Refresh: %d-%d-%d, %d:%d:%d
\n" -msgstr "Letzte Aktualisierung: %s
\n" +msgstr "Letzte Aktualisierung: %d-%d-%d, %d:%d:%d
\n" #, c-format msgid "Server: %s
\n" @@ -7557,21 +7556,19 @@ msgstr "

Original-Autor:
\n" msgid "

Code Contributors:
\n" -msgstr "" - -#, fuzzy +msgstr "

Code-Mitwirkende:
\n" + msgid "

Lovely Patch Writers:
\n" -msgstr "Letzte Aktualisierung: %s
\n" - -#, fuzzy +msgstr "

Wunderbare Patch-Schreiber:
\n" + msgid "

Acknowledgement:
\n" -msgstr "Gesendet: %lu
\n" +msgstr "

Bestätigung:
\n" msgid "

And, all the boys in the backroom...
\n" -msgstr "" +msgstr "

Und all die Jungs im Hinterzimmer...
\n" msgid "Feel free to join us! :)" -msgstr "" +msgstr "Treten Sie uns bei, wenn Sie mögen! :)" #, c-format msgid "About OpenQ %s" @@ -7640,7 +7637,7 @@ #, c-format msgid "Failed requesting token, 0x%02X" -msgstr "" +msgstr "Fehler beim Anfordern des Tokens, 0x%02X" #, c-format msgid "Invalid token len, %d" @@ -7648,7 +7645,7 @@ #. extend redirect used in QQ2006 msgid "Redirect_EX is not currently supported" -msgstr "" +msgstr "Redirect_EX wird im Moment nicht unterstützt" #. need activation #. need activation @@ -7712,13 +7709,11 @@ msgid "Connection lost" msgstr "Verbindung verloren" -#, fuzzy msgid "Getting server" -msgstr "Benutzer-Info setzen..." - -#, fuzzy +msgstr "Hole server" + msgid "Requesting token" -msgstr "Anfragekürzel" +msgstr "Fordere Token an" msgid "Couldn't resolve host" msgstr "Kann den Hostnamen nicht auflösen" @@ -9269,6 +9264,9 @@ msgid "SIP usernames may not contain whitespaces or @ symbols" msgstr "SIP-Benutzernamen dürfen keine Leerzeichen oder @-Symbole enthalten" +msgid "SIP connect server not specified" +msgstr "SIP-Verbindungsserver nicht angegeben" + #. *< type #. *< ui_requirement #. *< flags @@ -10374,7 +10372,7 @@ msgid "Protocol" msgstr "Protokoll" -#, fuzzy, c-format +#, c-format msgid "" "Welcome to %s!\n" "\n" @@ -10389,9 +10387,9 @@ "Willkommen bei %s!\n" "\n" "Sie haben keine IM-Konten konfiguriert. Um sich mit %s zu verbinden, drücken " -"Sie unten auf den Hinzufügen-Button und konfigurieren Sie Ihr erstes " -"Konto. Wenn Sie mehrere IM-Konten mit %s benutzen wollen, drücken Sie erneut " -"auf Hinzufügen um sie einzurichten.\n" +"Sie unten auf den Hinzufügen...-Button und konfigurieren Sie Ihr " +"erstes Konto. Wenn Sie mehrere IM-Konten mit %s benutzen wollen, drücken Sie " +"erneut auf Hinzufügen... um sie einzurichten.\n" "\n" "Sie können später über Konten->Konten verwalten im Buddy-" "Listenfenster zu diesem Dialog zurückkehren und Konten hinzufügen, "