diff -r 821b2c2d6ca7 -r ba0ded418e6a libpurple/protocols/myspace/myspace.c --- a/libpurple/protocols/myspace/myspace.c Mon Apr 30 04:30:00 2007 +0000 +++ b/libpurple/protocols/myspace/myspace.c Mon Apr 30 04:34:04 2007 +0000 @@ -125,6 +125,75 @@ */ return "myspace"; } + +/** + * Unescape a protocol message. + * + * @param msg The message to unescape. + * + * @return The unescaped message. Caller must g_free(). + * + * Messages should be unescaped after being received, as part of + * the parsing process. + */ +static gchar *msim_unescape(const gchar *msg) +{ + /* TODO: make more elegant, refactor with msim_escape */ + gchar *tmp, *ret; + + tmp = str_replace(msg, "/1", "/"); + ret = str_replace(tmp, "/2", "\\"); + g_free(tmp); + return ret; +} + +/** + * Escape a protocol message. + * + * @param msg The message to escape. + * + * @return The escaped message. Caller must g_free(). + * + * Messages should be escaped before sending. + */ +static gchar *msim_escape(const gchar *msg) +{ + /* TODO: make more elegant, refactor with msim_unescape */ + gchar *tmp, *ret; + + tmp = str_replace(msg, "/", "/1"); + ret = str_replace(tmp, "\\", "/2"); + g_free(tmp); + + return ret; +} + +/** + * Replace 'old' with 'new' in 'str'. + * + * @param str The original string. + * @param old The substring of 'str' to replace. + * @param new The replacement for 'old' within 'str'. + * + * @return A _new_ string, based on 'str', with 'old' replaced + * by 'new'. Must be g_free()'d by caller. + * + * This string replace method is based on + * http://mail.gnome.org/archives/gtk-app-devel-list/2000-July/msg00201.html + * + */ +static gchar *str_replace(const gchar* str, const gchar *old, const gchar *new) +{ + char **items; + char *ret; + + items = g_strsplit(str, old, -1); + ret = g_strjoinv(new, items); + g_free(items); + return ret; +} + + /** * Parse a MySpaceIM protocol message into a hash table. * @@ -172,7 +241,10 @@ #endif if (i % 2) { - value = token; + /* Odd-numbered ordinal is a value */ + + /* Note: returns a new string */ + value = msim_unescape(token); /* Check if key already exists */ if (g_hash_table_lookup(table, key) == NULL) @@ -180,7 +252,11 @@ #if MSIM_DEBUG_PARSE purple_debug_info("msim", "insert: |%s|=|%s|\n", key, value); #endif - g_hash_table_insert(table, g_strdup(key), g_strdup(value)); + /* Insert - strdup 'key' because it will be g_strfreev'd (as 'tokens'), + * but do not strdup 'value' because it was newly allocated by + * msim_unescape(). + */ + g_hash_table_insert(table, g_strdup(key), value); } else { /* TODO: Some dictionaries have multiple values for the same * key. Should append to a GList to handle this case. */ @@ -189,6 +265,7 @@ value); } } else { + /* Even numbered index is a key name */ key = token; } } @@ -277,28 +354,41 @@ * Send an arbitrary protocol message. * * @param session - * @param msg The textual, encoded message to send. + * @param msg The textual, packed message to send. + * + * @return TRUE if succeeded, FALSE if not. * * Note: this does not send instant messages. For that, see msim_send_im. */ -static void msim_send(MsimSession *session, const gchar *msg) +static gboolean msim_send(MsimSession *session, const gchar *msg) { - int ret; + int total_bytes_sent, total_bytes; - g_return_if_fail(MSIM_SESSION_VALID(session)); - g_return_if_fail(msg != NULL); - + g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE); + g_return_val_if_fail(msg != NULL, FALSE); + purple_debug_info("msim", "msim_send: writing <%s>\n", msg); - ret = send(session->fd, msg, strlen(msg), 0); + /* Loop until all data is sent, or a failure occurs. */ + total_bytes_sent = 0; + total_bytes = strlen(msg); + do + { + int bytes_sent; + + bytes_sent = send(session->fd, msg + total_bytes_sent, + total_bytes - total_bytes_sent, 0); - if (ret != strlen(msg)) - { - purple_debug_info("msim", - "msim_send(%s): strlen=%d, but only wrote %s\n", - msg, strlen(msg), ret); - /* TODO: better error -or- TODO: send all, loop unless ret=-1 */ - } + if (bytes_sent < 0) + { + purple_debug_info("msim", "msim_send(%s): send() failed: %s\n", + msg, g_strerror(errno)); + return FALSE; + } + total_bytes_sent += bytes_sent; + + } while(total_bytes_sent < total_bytes); + return TRUE; } /** @@ -388,6 +478,8 @@ g_free(nc); /* Reply */ + /* TODO: escape values. A response_str with a / in it (\ is not in base64) will + * cause a login failure! / must be encoded as /1. */ buf = g_strdup_printf("\\login2\\%d\\username\\%s\\response\\%s\\clientver\\%d\\reconn\\%d\\status\\%d\\id\\1\\final\\", 196610, account->username, response_str, MSIM_CLIENT_VERSION, 0, 100); @@ -612,10 +704,17 @@ g_return_val_if_fail(msim_is_userid(userid) == TRUE, 0); g_return_val_if_fail(message != NULL, 0); + /* XXX: delete after escape each value */ + message = msim_escape(message); + /* TODO: constants for bm types */ + /* TODO: escape values */ msg_string = g_strdup_printf("\\bm\\121\\sesskey\\%s\\t\\%s\\cv\\%d\\msg\\%s\\final\\", session->sesskey, userid, MSIM_CLIENT_VERSION, message); + /* XXX: delete after escape each value */ + g_free(message); + purple_debug_info("msim", "going to write: %s\n", msg_string); msim_send(session, msg_string); @@ -1362,6 +1461,7 @@ lid = 7; } + /* TODO: escape values */ msg_string = g_strdup_printf("\\persist\\1\\sesskey\\%s\\cmd\\1\\dsn\\%d\\uid\\%s\\lid\\%d\\rid\\%d\\body\\%s=%s\\final\\", session->sesskey, dsn, session->userid, lid, rid, field_name, user);