changeset 16747:496855295bd7

Use MsimMessage via msim_send(), instead of msim_send_raw(). This is a more object-oriented approach that simplifies sending of messages. Support for: - Several field types used in the protocol (integer, string, binary). - Transparently escape strings and base64 binary data as necessary. - Multiple values per key are supported (unlike GHashTable), required for WebChallenge message. - Order is preserved (unlike GHashTable). Also required for WebChallenge. Note that currently MsimMessage is only used for sending messages. -
author Jeffrey Connelly <jaconnel@calpoly.edu>
date Wed, 23 May 2007 04:18:05 +0000
parents 52357f6392a4
children da7621e799b7 8a7238fb7905
files libpurple/protocols/myspace/myspace.c libpurple/protocols/myspace/myspace.h
diffstat 2 files changed, 147 insertions(+), 262 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/myspace/myspace.c	Tue May 22 06:03:18 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Wed May 23 04:18:05 2007 +0000
@@ -6,7 +6,7 @@
  *
  * Based on Purple's "C Plugin HOWTO" hello world example.
  *
- * Code also drawn from myspace:
+ * Code also drawn from mockprpl:
  *  http://snarfed.org/space/purple+mock+protocol+plugin
  *  Copyright (C) 2004-2007, Ryan Barrett <mockprpl@ryanb.org>
  *
@@ -33,37 +33,13 @@
 
 #define PURPLE_PLUGIN
 
-#include <string.h>
-#include <errno.h>	/* for EAGAIN */
-#include <stdarg.h>
-
-#include <glib.h>
-
-#ifdef _WIN32
-#include "win32dep.h"
-#else
-/* For recv() and send(); needed to match Win32 */
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif
-
-#include "internal.h"
-
-#include "notify.h"
-#include "plugin.h"
-#include "accountopt.h"
-#include "version.h"
-#include "cipher.h"     /* for SHA-1 */
-#include "util.h"       /* for base64 */
-#include "debug.h"      /* for purple_debug_info */
-
 #include "myspace.h"
 #include "message.h"
 
 /** 
  * Load the plugin.
  */
-static gboolean msim_load(PurplePlugin *plugin)
+gboolean msim_load(PurplePlugin *plugin)
 {
 #ifdef MSIM_USE_PURPLE_RC4
 	/* If compiled to use RC4 from libpurple, check if it is really there. */
@@ -74,7 +50,7 @@
 				_("The RC4 cipher could not be found"),
 				_("Recompile without MSIM_USE_PURPLE_RC4, or upgrade "
 					"to a libpurple with RC4 support (>= 2.0.1). MySpaceIM "
-					"plugin will not be loaded.");
+					"plugin will not be loaded."));
 		return FALSE;
 	}
 #endif
@@ -86,7 +62,7 @@
  *
  * @return GList of status types.
  */
-static GList *msim_status_types(PurpleAccount *acct)
+GList *msim_status_types(PurpleAccount *acct)
 {
     GList *types;
     PurpleStatusType *type;
@@ -134,7 +110,7 @@
  *
  * @return The base icon name string.
  */
-static const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
+const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
 {
     /* Use a MySpace icon submitted by hbons submitted one at
      * http://developer.pidgin.im/wiki/MySpaceIM. */
@@ -146,7 +122,7 @@
  *
  * @return The unescaped message. Caller must g_free().
  */
-static gchar *msim_unescape(const gchar *msg)
+gchar *msim_unescape(const gchar *msg)
 {
 	/* TODO: make more elegant, refactor with msim_escape */
 	gchar *tmp, *ret;
@@ -162,7 +138,7 @@
  *
  * @return The escaped message. Caller must g_free().
  */
-static gchar *msim_escape(const gchar *msg)
+gchar *msim_escape(const gchar *msg)
 {
 	/* TODO: make more elegant, refactor with msim_unescape */
 	gchar *tmp, *ret;
@@ -188,7 +164,7 @@
  * 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)
+gchar *str_replace(const gchar* str, const gchar *old, const gchar *new)
 {
 	char **items;
 	char *ret;
@@ -209,7 +185,7 @@
  * @return Hash table of message. Caller should destroy with
  *              g_hash_table_destroy() when done.
  */
-static GHashTable* msim_parse(gchar* msg)
+GHashTable* msim_parse(gchar* msg)
 {
     GHashTable *table;
     gchar *token;
@@ -292,7 +268,7 @@
  *
  * @return Hash table of the keys and values. Must g_hash_table_destroy() when done.
  */
-static GHashTable *msim_parse_body(const gchar *body_str)
+GHashTable *msim_parse_body(const gchar *body_str)
 {
     GHashTable *table;
     gchar *item;
@@ -351,7 +327,7 @@
 
 
 #ifdef MSIM_DEBUG_MSG
-static void print_hash_item(gpointer key, gpointer value, gpointer user_data)
+void print_hash_item(gpointer key, gpointer value, gpointer user_data)
 {
     purple_debug_info("msim", "%s=%s\n", (char*)key, (char*)value);
 }
@@ -366,11 +342,11 @@
  * @return TRUE if succeeded, FALSE if not.
  *
  */
-static gboolean msim_send_raw(MsimSession *session, const gchar *msg)
+gboolean msim_send_raw(MsimSession *session, const gchar *msg)
 {
 	int total_bytes_sent, total_bytes;
     
-	purple_debug_info("msim", "msim_send: writing <%s>\n", msg);
+	purple_debug_info("msim", "msim_send_raw: writing <%s>\n", msg);
 
     g_return_val_if_fail(MSIM_SESSION_VALID(session), FALSE);
     g_return_val_if_fail(msg != NULL, FALSE);
@@ -398,101 +374,12 @@
 	return TRUE;
 }
 
-/** Send an existing MsimMessage.
- */
-
-/* TODO: move to message.c */
-static gboolean msim_msg_send(MsimSession *session, MsimMessage *msg)
-{
-	gchar *raw;
-	gboolean success;
-	
-	raw = msim_msg_pack(msg);
-	success = msim_send_raw(session, raw);
-	g_free(raw);
-	
-	return success;
-}
-
-/**
- *
- * Send a message to the server, whose contents is specified using 
- * variable arguments.
- *
- * @param session
- * @param ... A sequence of gchar* key/value pairs, terminated with NULL. 
- *
- * IMPORTANT: The key/value pair strings are not copied. The 'key' strings 
- * are not freed, but the 'value' pair is. Use g_strdup() to pass a static
- * string as a value. This function operates this way so that you can easily
- * use a call to g_strdup_printf() for the 'value' strings and not have to
- * worry about freeing the memory.
- *
- * It bears repeating: THE VALUE STRINGS WILL BE FREED. Copy if static.
- *
- * This function exists for coding convenience: a message can be created
- * and sent in one line of code. Internally it calls msim_msg_send().
- *
- */
-
-static gboolean msim_send(MsimSession *session, ...)
-{
-	va_list argp;
-	gchar *key, *value;
-	MsimMessageType type;
-	gboolean success;
-	MsimMessage *msg;
-    
-	msg = msim_msg_new();
-
-	/* Parse key, value pairs until NULL. */
-	va_start(argp, session);
-	do
-	{
-		key = va_arg(argp, gchar *);
-		if (!key)
-		{
-			break;
-		}
-
-		type = va_arg(argp, int);
-
-		switch (type)
-		{
-			case MSIM_TYPE_INTEGER: 
-				msim_msg_append(msg, key, type, GUINT_TO_POINTER(va_arg(argp, int)));
-				break;
-				
-			case MSIM_TYPE_STRING:
-				value = va_arg(argp, char *);
-
-				g_return_val_if_fail(value != NULL, FALSE);
-
-				msim_msg_append(msg, key, type, value);
-				break;
-
-			default:
-				purple_debug_info("msim", "msim_send: unknown type %d\n", type);
-				break;
-		}
-	} while(key && value);
-
-	/* Actually send the message. */
-	success = msim_msg_send(session, msg);
-
-	/* Cleanup. */
-	va_end(argp);	
-	msim_msg_free(msg);
-
-	return success;
-}
-
 /** 
  * Start logging in to the MSIM servers.
  * 
  * @param acct Account information to use to login.
  */
-static void msim_login(PurpleAccount *acct)
+void msim_login(PurpleAccount *acct)
 {
     PurpleConnection *gc;
     const char *host;
@@ -536,14 +423,14 @@
  *
  * @return 0, since the 'table' parameter is no longer needed.
  */
-static int msim_login_challenge(MsimSession *session, GHashTable *table) 
+int msim_login_challenge(MsimSession *session, GHashTable *table) 
 {
     PurpleAccount *account;
     gchar *nc_str;
     guchar *nc;
-    gchar *response_str;
+    gchar *response;
     gsize nc_len;
-    gchar *buf;
+	guint response_len;
 
     g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
     g_return_val_if_fail(table != NULL, 0);
@@ -568,43 +455,20 @@
 
     purple_connection_update_progress(session->gc, _("Logging in"), 2, 4);
 
-    response_str = msim_compute_login_response(nc, account->username, account->password);
+    response = msim_compute_login_response(nc, account->username, account->password, &response_len);
 
     g_free(nc);
 
-    /* Reply */
-	/* \status\100\id\1\login2\196610\username\msimprpl@xyzzy.cjb.net\clientver\673\reconn\0\response\+6M+DQhovMxwdt1ceIervus9O5K+X9BR02w6B4+4+Zrg66pWrSzX94ER8efSb8Ju9kye2MnDdpTDwybziACy2mQFWIB9Mf5/1Tdlr6kAtJA==\final\ 
-	 this fails. what is it?
-	 - escaping? no, removing msim_escape() still causes login failure. should verify.
-	 - message order? should login2 come first?
-	 - something else?
-	 */
-#if 0
 	msim_send(session, 
-			"login2", g_strdup_printf("%d", MSIM_AUTH_ALGORITHM),
-			"username", g_strdup(account->username),
-			"response", g_strdup(response_str),
-			"clientver", g_strdup_printf("%d", MSIM_CLIENT_VERSION),
-			"reconn", g_strdup_printf("%d", 0),
-			"status", g_strdup_printf("%d", 100),
-			"id", g_strdup_printf("%d", 1),
+			"login2", MSIM_TYPE_INTEGER, MSIM_AUTH_ALGORITHM,
+			"username", MSIM_TYPE_STRING, g_strdup(account->username),
+			/* GString and gchar * response will be freed in msim_msg_free() in msim_send(). */
+			"response", MSIM_TYPE_BINARY, g_string_new_len(response, response_len),
+			"clientver", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
+			"reconn", MSIM_TYPE_INTEGER, 0,
+			"status", MSIM_TYPE_INTEGER, 100,
+			"id", MSIM_TYPE_INTEGER, 1,
 			NULL);
-#else
-
-	/* TODO: use msim_send. But, response_str must NOT be escaped. */
-	/* \login2\196610\username\msimprpl@xyzzy.cjb.net\response\nseVXvvrwgQsv7FUAbHJLMP8YPEGKHftwN+Z0zCjmxOTOc0/nVPQWZ5Znv5i6kh26XfZlqNzvoPqaXNbXL6TsSZpU/guAAg0o6XBA1e/Sw==\clientver\673\reconn\0\status\100\id\1\final\ - works*/
-	buf = g_strdup_printf("\\login2\\%d\\username\\%s\\response\\%s\\clientver\\%d\\reconn\\%d\\status\\%d\\id\\1\\final\\",
-				MSIM_AUTH_ALGORITHM, account->username, response_str, MSIM_CLIENT_VERSION, 0, 100);
-
-    
-    purple_debug_info("msim", "response=<%s>\n", buf);
-
-    msim_send_raw(session, buf);
-
-    g_free(buf);
-#endif
-
-    g_free(response_str);
 
     return 0;
 }
@@ -723,12 +587,13 @@
  * @param nonce The base64 encoded nonce ('nc') field from the server.
  * @param email User's email address (used as login name).
  * @param password User's cleartext password.
+ * @param response_len Will be written with response length.
  *
- * @return Encoded login challenge response, ready to send to the server. Must be g_free()'d
+ * @return Binary login challenge response, ready to send to the server. Must be g_free()'d
  *         when finished.
  */
-static gchar* msim_compute_login_response(guchar nonce[2*NONCE_SIZE], 
-		gchar* email, gchar* password)
+gchar* msim_compute_login_response(guchar nonce[2*NONCE_SIZE], 
+		gchar* email, gchar* password, guint* response_len)
 {
     PurpleCipherContext *key_context;
     PurpleCipher *sha1;
@@ -743,7 +608,6 @@
     gchar* password_utf16le;
     guchar* data;
 	guchar* data_out;
-    gchar* response;
 	size_t data_len, data_out_len;
 	gsize conv_bytes_read, conv_bytes_written;
 	GError* conv_error;
@@ -841,16 +705,13 @@
 
 	g_assert(data_out_len == data_len);
 
-    response = purple_base64_encode(data_out, data_out_len);
-#ifdef MSIM_USE_PURPLE_RC4
-	g_free(data_out);
+#ifdef MSIM_DEBUG_LOGIN_CHALLENGE
+    purple_debug_info("msim", "response=<%s>\n", data_out);
 #endif
 
-#ifdef MSIM_DEBUG_LOGIN_CHALLENGE
-    purple_debug_info("msim", "response=<%s>\n", response);
-#endif
+	*response_len = data_out_len;
 
-    return response;
+    return (gchar*)data_out;
 }
 
 /**
@@ -873,7 +734,7 @@
  * instant message. If a userid is specified directly, this function is called
  * immediately here.
  */
-static int msim_send_im(PurpleConnection *gc, const char *who,
+int msim_send_im(PurpleConnection *gc, const char *who,
                             const char *message, PurpleMessageFlags flags)
 {
     MsimSession *session;
@@ -941,31 +802,20 @@
  * @return 0, since the 'table' parameter is no longer needed.
  *
  */
-static int msim_send_im_by_userid(MsimSession *session, const gchar *userid, const gchar *message, PurpleMessageFlags flags)
+int msim_send_im_by_userid(MsimSession *session, const gchar *userid, const gchar *message, PurpleMessageFlags flags)
 {
-    gchar *msg_string;
-	gchar *escaped_message;
-
     g_return_val_if_fail(MSIM_SESSION_VALID(session), 0);
     g_return_val_if_fail(userid != NULL, 0);
     g_return_val_if_fail(msim_is_userid(userid) == TRUE, 0);
     g_return_val_if_fail(message != NULL, 0);
 
-	/* TODO: Remove this code and use MsimMessage after it is implemented and escapes strings. */
-	escaped_message = msim_escape(message);
-
-	/* TODO: escape values */
-    msg_string = g_strdup_printf("\\bm\\%d\\sesskey\\%s\\t\\%s\\cv\\%d\\msg\\%s\\final\\",
-            MSIM_BM_INSTANT, session->sesskey, userid, MSIM_CLIENT_VERSION, message);
-
-	/* TODO: Remove this code and use MsimMessage after it is implemented and escapes strings. */
-	g_free(escaped_message);
-
-    purple_debug_info("msim", "going to write: %s\n", msg_string);
-
-    msim_send_raw(session, msg_string);
-
-    /* TODO: notify Purple that we sent the IM. */
+	msim_send(session, 
+			"bm", MSIM_TYPE_INTEGER, MSIM_BM_INSTANT,
+			"sesskey", MSIM_TYPE_STRING, g_strdup(session->sesskey),
+			"t", MSIM_TYPE_STRING, g_strdup(userid),
+			"cv", MSIM_TYPE_INTEGER, MSIM_CLIENT_VERSION,
+			"msg", MSIM_TYPE_STRING, g_strdup(message),
+			NULL);	
 
     /* Not needed since sending messages to yourself is allowed by MSIM! */
     /*if (strcmp(from_username, who) == 0)
@@ -986,7 +836,7 @@
  * @param data A send_im_cb_struct* of information on the IM to send.
  *
  */
-static void msim_send_im_by_userid_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
+void msim_send_im_by_userid_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
 {
     send_im_cb_struct *s;
     gchar *userid;
@@ -1016,7 +866,7 @@
  * @param userinfo Message from server on user's info, containing UserName.
  * @param data A gchar* of the incoming instant message's text.
  */
-static void msim_incoming_im_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
+void msim_incoming_im_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
 {
     gchar *msg;
     gchar *username;
@@ -1045,7 +895,7 @@
  *
  * @return 0, since table can be freed.
  */
-static int msim_incoming_im(MsimSession *session, GHashTable *table)
+int msim_incoming_im(MsimSession *session, GHashTable *table)
 {
     gchar *userid;
     gchar *msg;
@@ -1076,7 +926,7 @@
  * @return The return value of the function used to process the message, or -1 if
  * called with invalid parameters.
  */
-static int msim_process(PurpleConnection *gc, GHashTable *table)
+int msim_process(PurpleConnection *gc, GHashTable *table)
 {
     MsimSession *session;
 
@@ -1153,7 +1003,7 @@
  *
  * @return 0, since the 'table' field is no longer needed.
  */
-static int msim_process_reply(MsimSession *session, GHashTable *table)
+int msim_process_reply(MsimSession *session, GHashTable *table)
 {
     gchar *rid_str;
 
@@ -1223,7 +1073,7 @@
  *
  * @return 0, since 'table' can be freed.
  */
-static int msim_error(MsimSession *session, GHashTable *table)
+int msim_error(MsimSession *session, GHashTable *table)
 {
     gchar *err, *errmsg, *full_errmsg;
 
@@ -1255,7 +1105,7 @@
 
 #if 0
 /* Not sure about this */
-static void msim_status_now(gchar *who, gpointer data)
+void msim_status_now(gchar *who, gpointer data)
 {
     printf("msim_status_now: %s\n", who);
 }
@@ -1269,7 +1119,7 @@
  * @param data gchar* status string.
  *
  */
-static void msim_status_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
+void msim_status_cb(MsimSession *session, GHashTable *userinfo, gpointer data)
 {
     PurpleBuddyList *blist;
     PurpleBuddy *buddy;
@@ -1356,7 +1206,7 @@
  *
  * @return 0, since 'table' can be freed.
  */
-static int msim_status(MsimSession *session, GHashTable *table)
+int msim_status(MsimSession *session, GHashTable *table)
 {
     gchar *status_str;
     gchar *userid;
@@ -1401,7 +1251,7 @@
  *
  * Reads the input, and dispatches calls msim_process to handle it.
  */
-static void msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond)
+void msim_input_cb(gpointer gc_uncasted, gint source, PurpleInputCondition cond)
 {
     PurpleConnection *gc;
     PurpleAccount *account;
@@ -1523,7 +1373,7 @@
  * @param source File descriptor.
  * @param error_message
  */
-static void msim_connect_cb(gpointer data, gint source, const gchar *error_message)
+void msim_connect_cb(gpointer data, gint source, const gchar *error_message)
 {
     PurpleConnection *gc;
     MsimSession *session;
@@ -1555,7 +1405,7 @@
  *
  * @return Pointer to a new session. Free with msim_session_destroy.
  */
-static MsimSession *msim_session_new(PurpleAccount *acct)
+MsimSession *msim_session_new(PurpleAccount *acct)
 {
     MsimSession *session;
 
@@ -1585,7 +1435,7 @@
  *
  * @param session The session to destroy.
  */
-static void msim_session_destroy(MsimSession *session)
+void msim_session_destroy(MsimSession *session)
 {
     g_return_if_fail(MSIM_SESSION_VALID(session));
 
@@ -1605,7 +1455,7 @@
  * 
  * @param gc The connection.
  */
-static void msim_close(PurpleConnection *gc)
+void msim_close(PurpleConnection *gc)
 {
     g_return_if_fail(gc != NULL);
     
@@ -1620,7 +1470,7 @@
  *
  * @return TRUE if is userid, FALSE if not.
  */
-static inline gboolean msim_is_userid(const gchar *user)
+gboolean msim_is_userid(const gchar *user)
 {
     g_return_val_if_fail(user != NULL, FALSE);
 
@@ -1639,7 +1489,7 @@
  * between a user represented by an email address from
  * other forms of identification.
  */ 
-static inline gboolean msim_is_email(const gchar *user)
+gboolean msim_is_email(const gchar *user)
 {
     g_return_val_if_fail(user != NULL, FALSE);
 
@@ -1655,10 +1505,9 @@
  * @param cb Callback, called with user information when available.
  * @param data An arbitray data pointer passed to the callback.
  */
-static void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
+void msim_lookup_user(MsimSession *session, const gchar *user, MSIM_USER_LOOKUP_CB cb, gpointer data)
 {
     gchar *field_name;
-    gchar *msg_string;
     guint rid, cmd, dsn, lid;
 
     g_return_if_fail(MSIM_SESSION_VALID(session));
@@ -1706,11 +1555,18 @@
         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);
 
-    msim_send_raw(session, msg_string);
+	msim_send(session,
+			"persist", MSIM_TYPE_INTEGER, 1,
+			"sesskey", MSIM_TYPE_STRING, g_strdup(session->sesskey),
+			"cmd", MSIM_TYPE_INTEGER, 1,
+			"dsn", MSIM_TYPE_INTEGER, dsn,
+			"uid", MSIM_TYPE_STRING, g_strdup(session->userid),
+			"lid", MSIM_TYPE_INTEGER, lid,
+			"rid", MSIM_TYPE_INTEGER, rid,
+			/* TODO: dictionary field type */
+			"body", MSIM_TYPE_STRING, g_strdup_printf("%s=%s", field_name, user),
+			NULL);
 } 
 
 
@@ -1722,7 +1578,7 @@
  * @return Status text, or NULL if error.
  *
  */
-static char *msim_status_text(PurpleBuddy *buddy)
+char *msim_status_text(PurpleBuddy *buddy)
 {
     MsimSession *session;
     GHashTable *userinfo;
@@ -1754,7 +1610,7 @@
  * @param full TRUE if should obtain full tooltip text.
  *
  */
-static void msim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
+void msim_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full)
 {
     g_return_if_fail(buddy != NULL);
     g_return_if_fail(user_info != NULL);
@@ -1786,7 +1642,7 @@
 }
 
 /** Callbacks called by Purple, to access this plugin. */
-static PurplePluginProtocolInfo prpl_info =
+PurplePluginProtocolInfo prpl_info =
 {
     OPT_PROTO_MAIL_CHECK,/* options - TODO: myspace will notify of mail */
     NULL,              /* user_splits */
@@ -1859,7 +1715,7 @@
 
 
 /** Based on MSN's plugin info comments. */
-static PurplePluginInfo info =
+PurplePluginInfo info =
 {
     PURPLE_PLUGIN_MAGIC,                                
     PURPLE_MAJOR_VERSION,
@@ -1898,18 +1754,19 @@
 
 #include "message.h"
 
-static void init_plugin(PurplePlugin *plugin) 
+void init_plugin(PurplePlugin *plugin) 
 {
 	PurpleAccountOption *option;
 #ifdef _TEST_MSIM_MSG
 	MsimMessage *msg = msim_msg_new();
-	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v1");
+	msg = msim_msg_append(msg, "bx", MSIM_TYPE_BINARY, g_string_new_len(g_strdup("XXX"), 3));
+	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v1"));
 	msg = msim_msg_append(msg, "k1", MSIM_TYPE_INTEGER, 42);
-	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v43");
-	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v5");
-	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, "v7");
+	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v43"));
+	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v52/xxx\\yyy"));
+	msg = msim_msg_append(msg, "k1", MSIM_TYPE_STRING, g_strdup("v7"));
+	purple_debug_info("msim", "msg=%s\n", msim_msg_debug_string(msg));
 	purple_debug_info("msim", "msg=%s\n", msim_msg_pack(msg));
-	purple_debug_info("msim", "msg=%s\n", msim_msg_debug_string(msg));
 	msim_msg_free(msg);
 	exit(0);
 #endif
--- a/libpurple/protocols/myspace/myspace.h	Tue May 22 06:03:18 2007 +0000
+++ b/libpurple/protocols/myspace/myspace.h	Wed May 23 04:18:05 2007 +0000
@@ -22,6 +22,32 @@
 #ifndef _MYSPACE_MYSPACE_H
 #define _MYSPACE_MYSPACE_H
 
+/* Other includes */
+#include <string.h>
+#include <errno.h>	/* for EAGAIN */
+#include <stdarg.h>
+
+#include <glib.h>
+
+#ifdef _WIN32
+#include "win32dep.h"
+#else
+/* For recv() and send(); needed to match Win32 */
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+#include "internal.h"
+
+#include "notify.h"
+#include "plugin.h"
+#include "accountopt.h"
+#include "version.h"
+#include "cipher.h"     /* for SHA-1 */
+#include "util.h"       /* for base64 */
+#include "debug.h"      /* for purple_debug_info */
+
+
 /* Conditional compilation options */
 
 /* Debugging options */
@@ -112,66 +138,68 @@
 
 
 /* Functions */
-static void init_plugin(PurplePlugin *plugin);
-static GList *msim_status_types(PurpleAccount *acct);
-static const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy);
+gboolean msim_load(PurplePlugin *plugin);
+GList *msim_status_types(PurpleAccount *acct);
+const gchar *msim_list_icon(PurpleAccount *acct, PurpleBuddy *buddy);
 
 /* TODO: move these three functions to message.c/h */
-static gchar *msim_unescape(const gchar *msg);
-static gchar *msim_escape(const gchar *msg);
-static gchar *str_replace(const gchar* str, const gchar *old, const gchar *new);
+gchar *msim_unescape(const gchar *msg);
+gchar *msim_escape(const gchar *msg);
+gchar *str_replace(const gchar* str, const gchar *old, const gchar *new);
 
-static GHashTable *msim_parse(gchar* msg);
-static GHashTable* msim_parse_body(const gchar *body_str);
+GHashTable *msim_parse(gchar* msg);
+GHashTable* msim_parse_body(const gchar *body_str);
 
-static void print_hash_item(gpointer key, gpointer value, gpointer user_data);
-static gboolean msim_send_raw(MsimSession *session, const gchar *msg);
-static gchar *msim_pack(GHashTable *table);
-static gboolean msim_sendh(MsimSession *session, GHashTable *table);
-static gboolean msim_send(MsimSession *session, ...);
+void print_hash_item(gpointer key, gpointer value, gpointer user_data);
+gboolean msim_send_raw(MsimSession *session, const gchar *msg);
+gchar *msim_pack(GHashTable *table);
+gboolean msim_sendh(MsimSession *session, GHashTable *table);
+gboolean msim_send(MsimSession *session, ...);
 
-static void msim_login(PurpleAccount *acct);
-static int msim_login_challenge(MsimSession *session, GHashTable *table);
-static gchar* msim_compute_login_response(guchar nonce[2*NONCE_SIZE],
-		        gchar* email, gchar* password);
+void msim_login(PurpleAccount *acct);
+int msim_login_challenge(MsimSession *session, GHashTable *table);
+gchar* msim_compute_login_response(guchar nonce[2*NONCE_SIZE],
+		        gchar* email, gchar* password, guint *response_len);
 
-static int msim_send_im(PurpleConnection *gc, const char *who,
+int msim_send_im(PurpleConnection *gc, const char *who,
 		const char *message, PurpleMessageFlags flags);
-static int msim_send_im_by_userid(MsimSession *session, const gchar *userid, 
+int msim_send_im_by_userid(MsimSession *session, const gchar *userid, 
 		const gchar *message, PurpleMessageFlags flags);
-static void msim_send_im_by_userid_cb(MsimSession *session, 
+void msim_send_im_by_userid_cb(MsimSession *session, 
 		GHashTable *userinfo, gpointer data);
-static void msim_incoming_im_cb(MsimSession *session, GHashTable *userinfo, 
+void msim_incoming_im_cb(MsimSession *session, GHashTable *userinfo, 
 		gpointer data);
-static int msim_incoming_im(MsimSession *session, GHashTable *table);
+int msim_incoming_im(MsimSession *session, GHashTable *table);
 
-static int msim_process_reply(MsimSession *session, GHashTable *table);
-static int msim_process(PurpleConnection *gc, GHashTable *table);
+int msim_process_reply(MsimSession *session, GHashTable *table);
+int msim_process(PurpleConnection *gc, GHashTable *table);
 
-static int msim_error(MsimSession *session, GHashTable *table);
-static void msim_status_cb(MsimSession *session, GHashTable *userinfo, 
+int msim_error(MsimSession *session, GHashTable *table);
+void msim_status_cb(MsimSession *session, GHashTable *userinfo, 
 		gpointer data);
-static int msim_status(MsimSession *session, GHashTable *table);
-static void msim_input_cb(gpointer gc_uncasted, gint source, 
+int msim_status(MsimSession *session, GHashTable *table);
+void msim_input_cb(gpointer gc_uncasted, gint source, 
 		PurpleInputCondition cond);
-static void msim_connect_cb(gpointer data, gint source, 
+void msim_connect_cb(gpointer data, gint source, 
 		const gchar *error_message);
 
-static MsimSession *msim_session_new(PurpleAccount *acct);
-static void msim_session_destroy(MsimSession *session);
+MsimSession *msim_session_new(PurpleAccount *acct);
+void msim_session_destroy(MsimSession *session);
 
-static void msim_close(PurpleConnection *gc);
+void msim_close(PurpleConnection *gc);
 
-static inline gboolean msim_is_userid(const gchar *user);
-static inline gboolean msim_is_email(const gchar *user);
+gboolean msim_is_userid(const gchar *user);
+gboolean msim_is_email(const gchar *user);
 
-static void msim_lookup_user(MsimSession *session, const gchar *user, 
+void msim_lookup_user(MsimSession *session, const gchar *user, 
 		MSIM_USER_LOOKUP_CB cb, gpointer data);
 
-static char *msim_status_text(PurpleBuddy *buddy);
-static void msim_tooltip_text(PurpleBuddy *buddy, 
+char *msim_status_text(PurpleBuddy *buddy);
+void msim_tooltip_text(PurpleBuddy *buddy, 
 		PurpleNotifyUserInfo *user_info, gboolean full);
 
+void init_plugin(PurplePlugin *plugin);
+
 #ifndef MSIM_USE_PURPLE_RC4
 /* 
    Unix SMB/CIFS implementation.