changeset 24766:ce108a92fa4e

merge of 'b70000f3bf5c6b7f30121fb749d72ac869b58ae9' and 'cd296a8ef14b597780af9c7d28af18a4913c252c'
author Elliott Sales de Andrade <qulogic@pidgin.im>
date Tue, 16 Dec 2008 02:25:08 +0000
parents c1c464583f8c (diff) 25667ca518d6 (current diff)
children 295464ae2d2a f8dbd57cf635
files
diffstat 8 files changed, 792 insertions(+), 764 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Dec 16 02:21:33 2008 +0000
+++ b/ChangeLog	Tue Dec 16 02:25:08 2008 +0000
@@ -4,26 +4,29 @@
 	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)
-	* 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)
+	* 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)
@@ -32,46 +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 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.
+	* 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.
+	* 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)
+	* 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 <metadata/> 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 <metadata/> 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)
 
@@ -81,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.
--- a/libpurple/protocols/myspace/message.c	Tue Dec 16 02:21:33 2008 +0000
+++ b/libpurple/protocols/myspace/message.c	Tue Dec 16 02:25:08 2008 +0000
@@ -19,18 +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 MsimMessage *msim_msg_append_dynamic_name(MsimMessage *msg, gchar *name, MsimMessageType type, gpointer 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;
@@ -54,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;
@@ -98,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;
@@ -127,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.
@@ -190,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 *);
 
@@ -211,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 *);
 
@@ -238,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("<MsimMessage: empty>");
+	} else {
+		debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element,
+				"\n", "<MsimMessage: \n", "\n/MsimMessage>");
+	}
+
+	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;
 
@@ -254,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;
@@ -364,7 +591,8 @@
 		*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().
  */
@@ -384,7 +612,8 @@
 	return new;
 }
 
-/** Free the data of a message element.
+/**
+ * Free the data of a message element.
  *
  * @param elem The MsimMessageElement *
  *
@@ -415,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;
@@ -427,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.
@@ -435,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;
@@ -453,8 +704,10 @@
 	g_free(elem);
 }
 
-/** Free a complete message. */
-void 
+/**
+ * Free a complete message.
+ */
+void
 msim_msg_free(MsimMessage *msg)
 {
 	if (!msg) {
@@ -470,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("<MsimMessage: empty>");
-	} else {
-		debug_str = msim_msg_pack_using(msg, msim_msg_debug_string_element, 
-				"\n", "<MsimMessage: \n", "\n/MsimMessage>");
-	}
-
-	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.
@@ -895,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;
@@ -946,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().
  */
@@ -959,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);
@@ -971,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.
@@ -979,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;
@@ -1000,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
@@ -1016,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. */
@@ -1031,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.
  *
@@ -1096,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)
 {
@@ -1144,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;
 
@@ -1155,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);
@@ -1177,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
@@ -1184,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;
@@ -1196,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;
@@ -1206,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;
@@ -1214,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
@@ -1236,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)
 {
@@ -1250,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)
 {
@@ -1311,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;
@@ -1348,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
@@ -1376,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; */
 
@@ -1386,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);
+}
--- a/libpurple/protocols/myspace/message.h	Tue Dec 16 02:21:33 2008 +0000
+++ b/libpurple/protocols/myspace/message.h	Tue Dec 16 02:25:08 2008 +0000
@@ -24,17 +24,20 @@
 
 #include <glib.h>
 
+#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 */
--- a/libpurple/protocols/myspace/myspace.c	Tue Dec 16 02:21:33 2008 +0000
+++ b/libpurple/protocols/myspace/myspace.c	Tue Dec 16 02:25:08 2008 +0000
@@ -2598,7 +2598,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,
--- a/libpurple/protocols/myspace/session.h	Tue Dec 16 02:21:33 2008 +0000
+++ b/libpurple/protocols/myspace/session.h	Tue Dec 16 02:25:08 2008 +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
 
--- a/libpurple/protocols/myspace/user.c	Tue Dec 16 02:21:33 2008 +0000
+++ b/libpurple/protocols/myspace/user.c	Tue Dec 16 02:25:08 2008 +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("<a href=\"http://myspace.com/%d\">%s</a>",
-				user->id, _("View web profile"));
+		if (user->buddy != NULL)
+			profile = g_strdup_printf("<a href=\"http://myspace.com/%s\">%s</a>",
+					purple_buddy_get_name(user->buddy), _("View web profile"));
+		else
+			profile = g_strdup_printf("<a href=\"http://myspace.com/%d\">%s</a>",
+					user->id, _("View web profile"));
 		purple_notify_user_info_add_pair(user_info, NULL, profile);
 		g_free(profile);
 	}
--- a/libpurple/protocols/myspace/user.h	Tue Dec 16 02:21:33 2008 +0000
+++ b/libpurple/protocols/myspace/user.h	Tue Dec 16 02:25:08 2008 +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;
--- a/po/de.po	Tue Dec 16 02:21:33 2008 +0000
+++ b/po/de.po	Tue Dec 16 02:25:08 2008 +0000
@@ -7,13 +7,14 @@
 #
 # This file is distributed under the same license as the Pidgin package.
 #
+# Jochen Kemnade <jochenkemnade@web.de>, 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 <bjoern@cs.tu-berlin.de>\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 <jochenkemnade@web.de>\n"
 "Language-Team: German <de@li.org>\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 "<b>Removed buddy %u.</b>"
 msgstr "<b>Buddy %u entfernt</b>"
 
-#, fuzzy, c-format
+#, c-format
 msgid "<b>New buddy %u joined.</b>"
-msgstr "Buddy entfernen"
+msgstr "<b>Neuer Buddy %u ist beigetreten.</b>"
 
 #, c-format
 msgid "Unknown-%d"
@@ -7494,17 +7493,17 @@
 msgid "Select icon..."
 msgstr "Icon wählen..."
 
-#, fuzzy, c-format
+#, c-format
 msgid "<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"
-msgstr "<b>Anmeldezeit</b>: %s<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Anmeldezeit</b>: %d-%d-%d, %d:%d:%d<br>\n"
+
+#, c-format
 msgid "<b>Total Online Buddies</b>: %d<br>\n"
-msgstr "<b>Aktuell online:</b> %d<br>\n"
-
-#, fuzzy, c-format
+msgstr "<b>Insgesamt online</b>: %d<br>\n"
+
+#, c-format
 msgid "<b>Last Refresh</b>: %d-%d-%d, %d:%d:%d<br>\n"
-msgstr "<b>Letzte Aktualisierung</b>: %s<br>\n"
+msgstr "<b>Letzte Aktualisierung</b>: %d-%d-%d, %d:%d:%d<br>\n"
 
 #, c-format
 msgid "<b>Server</b>: %s<br>\n"
@@ -7557,21 +7556,19 @@
 msgstr "<p><b>Original-Autor</b>:<br>\n"
 
 msgid "<p><b>Code Contributors</b>:<br>\n"
-msgstr ""
-
-#, fuzzy
+msgstr "<p><b>Code-Mitwirkende</b>:<br>\n"
+
 msgid "<p><b>Lovely Patch Writers</b>:<br>\n"
-msgstr "<b>Letzte Aktualisierung</b>: %s<br>\n"
-
-#, fuzzy
+msgstr "<p><b>Wunderbare Patch-Schreiber</b>:<br>\n"
+
 msgid "<p><b>Acknowledgement</b>:<br>\n"
-msgstr "<b>Gesendet</b>: %lu<br>\n"
+msgstr "<p><b>Bestätigung</b>:<br>\n"
 
 msgid "<p><i>And, all the boys in the backroom...</i><br>\n"
-msgstr ""
+msgstr "<p><i>Und all die Jungs im Hinterzimmer...</i><br>\n"
 
 msgid "<i>Feel free to join us!</i> :)"
-msgstr ""
+msgstr "<i>Treten Sie uns bei, wenn Sie mögen!</i> :)"
 
 #, 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 ""
 "<span size='larger' weight='bold'>Welcome to %s!</span>\n"
 "\n"
@@ -10389,9 +10387,9 @@
 "<span size='larger' weight='bold'>Willkommen bei %s!</span>\n"
 "\n"
 "Sie haben keine IM-Konten konfiguriert. Um sich mit %s zu verbinden, drücken "
-"Sie unten auf den <b>Hinzufügen</b>-Button und konfigurieren Sie Ihr erstes "
-"Konto. Wenn Sie mehrere IM-Konten mit %s benutzen wollen, drücken Sie erneut "
-"auf <b>Hinzufügen</b> um sie einzurichten.\n"
+"Sie unten auf den <b>Hinzufügen...</b>-Button und konfigurieren Sie Ihr "
+"erstes Konto. Wenn Sie mehrere IM-Konten mit %s benutzen wollen, drücken Sie "
+"erneut auf <b>Hinzufügen...</b> um sie einzurichten.\n"
 "\n"
 "Sie können später über <b>Konten->Konten verwalten</b> im Buddy-"
 "Listenfenster zu diesem Dialog zurückkehren und Konten hinzufügen, "