changeset 31763:96dffe08f3f7

merge of '2873917d9d4d30d86018ef5da25d8076c9c402ab' and 'c0b910c136c2999321b3ee2969b3e2c60abd11b8'
author Mark Doliner <mark@kingant.net>
date Tue, 08 Mar 2011 02:27:47 +0000
parents d2962c2d7791 (current diff) 2e0cff411cc9 (diff)
children 04576947c4e0 18ca4a90842f
files libpurple/protocols/jabber/jabber.c
diffstat 39 files changed, 1265 insertions(+), 390 deletions(-) [+]
line wrap: on
line diff
--- a/COPYRIGHT	Tue Mar 08 02:27:25 2011 +0000
+++ b/COPYRIGHT	Tue Mar 08 02:27:47 2011 +0000
@@ -49,6 +49,7 @@
 Carlos Bederian
 Dave Bell
 Igor Belyi
+David Benjamin
 Brian Bernas
 Paul Betts
 Runa Bhattacharjee
@@ -98,6 +99,7 @@
 Markos Chandras
 Matthew Chapman
 Christophe Chapuis
+Tirtha Chatterjee
 Patrick Cheung
 Ka-Hing Cheung
 Sadrul Habib Chowdhury
@@ -399,6 +401,7 @@
 Eric Polino <aluink@gmail.com>
 Ari Pollak
 Stephen Pope
+Cristi Posoiu
 Nathan Poznick
 Jory A. Pratt
 David Preece
--- a/ChangeLog	Tue Mar 08 02:27:25 2011 +0000
+++ b/ChangeLog	Tue Mar 08 02:27:47 2011 +0000
@@ -3,6 +3,17 @@
 version 2.7.11 (??/??/????):
 	General:
 	* Our bundled libgadu should now build on HP-UX.
+	* Fix some instances of file transfers never completing. (Cristi Posoiu)
+	  (#12472)
+
+	Pidgin:
+	* Sort by Status no longer causes buddies to move around when you
+	  click them.
+	* Fix embedding in the system tray on older GTK+ releases (such as on
+	  CentOS 5.5 and older Fedora).
+	* No longer require libstartup-notification for startup notification
+	  support.  GTK+ has included support for years, so use it instead. (David
+	  Benjamin) (#13245)
 
 	AIM:
 	* Fix a bug where some buddies from your buddy list might not show up.
@@ -15,8 +26,14 @@
 	XMPP:
 	* Fix building on platforms with an older glib (inadvertantly broken in
 	  2.7.10).  (#13329)
-	* Don't treat the on-join status storms as 'new arrivals'.  (#a14527)
-	  (Thijs Alkemade)
+	* Don't treat the on-join status storms as 'new arrivals'.  (Thijs
+	  Alkemade) (#a14527)
+	* Extend the /join command to support room JIDs, enabling you to join
+	  a room on any server.  (Solarius, Matěj Cepl, Tirtha 'wyuka'
+	  Chatterjee) (#4526)
+	* Add support for receiving a limited amount of history when joining a
+	  room (not currently supported by Pidgin and Finch).  (Thijs Alkemade)
+	  (#10986, #a14219)
 
 version 2.7.10 (02/06/2011):
 	General:
@@ -323,6 +340,40 @@
 	* Bonjour support now requires Apple Bonjour Print Services version
 	  2.0.0 or newer (http://support.apple.com/kb/dl999).
 
+	libpurple:
+	* Fall back to an ordinary request if a UI does not support showing a
+	  request with an icon.  Fixes receiving MSN file transfer requests
+	  including a thumbnail in Finch.
+
+	Pidgin:
+	* Add support for the Gadu-Gadu protocol in the gevolution plugin to
+	  provide Evolution integration with contacts with GG IDs. (#10709)
+	* Remap the "Set User Mood" shortcut to Control-D, which does not
+	  conflict with the previous shortcut for Get Buddy Info on the
+	  selected buddy.
+	* Add a plugin action menu (under Tools) for the Voice and Video
+	  Settings plugin.
+
+	Finch:
+	* Add support for drop-down account options (like the SILC cipher
+	  and HMAC options or the QQ protocol version).
+
+	XMPP:
+	* Unify the connection security-related settings into one dropdown.
+	* Fix a crash when multiple accounts are simultaneously performing
+	  SASL authentication when built with Cyrus SASL support.  (thanks
+	  to Jan Kaluza) (#11560)
+	* Restore the ability to connect to XMPP servers that do not offer
+	  Stream ID. (#12331)
+	* Added support for using Google's relay servers when making voice and
+	  video calls to Google clients.
+
+	Yahoo/Yahoo JAPAN:
+	* Stop doing unnecessary lookups of certain alias information.  This
+	  solves deadlocks when a given Yahoo account has a ridiculously large
+	  (>500 buddies) list and may improve login speed for those on slow
+	  connections. (#12532)
+
 version 2.7.3 (08/10/2010):
 	General:
 	* Use silent build rules for automake >1.11. You can enable verbose
--- a/ChangeLog.API	Tue Mar 08 02:27:25 2011 +0000
+++ b/ChangeLog.API	Tue Mar 08 02:27:47 2011 +0000
@@ -1,6 +1,15 @@
 Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul
 
 version 2.7.11 (??/??/????):
+	* libpurple:
+		Added:
+		* Four entries in the GHashTable passed when joining
+		  an XMPP chat room which allow the UI to request a limited
+		  amount of history.  See XEP-0045 7.1.16 for details; the
+		  entries are named history_maxchars, history_maxstanzas,
+		  history_seconds, and history_since.  history_since must be
+		  interpretable by purple_str_to_time, and the prpl takes care
+		  of formatting the time properly.
 	* Perl:
 		Added:
 		* Purple::find_conversation_with_account
--- a/configure.ac	Tue Mar 08 02:27:25 2011 +0000
+++ b/configure.ac	Tue Mar 08 02:27:47 2011 +0000
@@ -541,27 +541,6 @@
 	fi
 
 	dnl #######################################################################
-	dnl # Check for startup notification
-	dnl #######################################################################
-	if test "x$enable_startup_notification" = "xyes"; then
-		PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [
-			AC_MSG_RESULT(no)
-			enable_startup_notification="no"
-			if test "x$force_deps" = "xyes" ; then
-				AC_MSG_ERROR([
-Startup notification development headers not found.
-Use --disable-startup-notification if you do not need it.
-])
-			fi])
-
-		if test "x$enable_startup_notification" = "xyes"; then
-			AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.])
-			AC_SUBST(STARTUP_NOTIFICATION_CFLAGS)
-			AC_SUBST(STARTUP_NOTIFICATION_LIBS)
-		fi
-	fi
-
-	dnl #######################################################################
 	dnl # Check for GtkSpell
 	dnl #######################################################################
 	if test "x$enable_gtkspell" = "xyes" ; then
--- a/libpurple/ft.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/ft.c	Tue Mar 08 02:27:47 2011 +0000
@@ -1100,9 +1100,11 @@
 		r = write(xfer->fd, buffer, s);
 		if (r < 0 && errno == EAGAIN)
 			r = 0;
-		if ((purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer))
-			purple_xfer_set_completed(xfer, TRUE);
 	}
+	if (r >= 0 && (purple_xfer_get_bytes_sent(xfer)+r) >= purple_xfer_get_size(xfer) &&
+		!purple_xfer_is_completed(xfer))
+		purple_xfer_set_completed(xfer, TRUE);
+	
 
 	return r;
 }
--- a/libpurple/protocols/jabber/chat.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/jabber/chat.c	Tue Mar 08 02:27:47 2011 +0000
@@ -281,6 +281,14 @@
 
 	char *jid;
 
+	char *history_maxchars;
+	char *history_maxstanzas;
+	char *history_seconds;
+	char *history_since;
+
+	struct tm history_since_datetime;
+	const char *history_since_string = NULL;
+
 	chat = jabber_chat_new(js, room, server, handle, password, data);
 	if (chat == NULL)
 		return NULL;
@@ -297,6 +305,22 @@
 	xmlnode_set_attrib(presence, "to", jid);
 	g_free(jid);
 
+	history_maxchars   = g_hash_table_lookup(data, "history_maxchars");
+	history_maxstanzas = g_hash_table_lookup(data, "history_maxstanzas");
+	history_seconds    = g_hash_table_lookup(data, "history_seconds");
+	history_since      = g_hash_table_lookup(data, "history_since");
+
+	if (history_since) {
+		if (purple_str_to_time(history_since, TRUE, &history_since_datetime, NULL, NULL) != 0) {
+			history_since_string = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", &history_since_datetime);
+		} else {
+			history_since_string = NULL;
+
+			purple_debug_error("jabber", "Invalid date format for history_since"
+			                             " while requesting history: %s", history_since);
+		}
+	}
+
 	x = xmlnode_new_child(presence, "x");
 	xmlnode_set_namespace(x, "http://jabber.org/protocol/muc");
 
@@ -305,6 +329,27 @@
 		xmlnode_insert_data(p, password, -1);
 	}
 
+	if ((history_maxchars && *history_maxchars)
+	    || (history_maxstanzas && *history_maxstanzas)
+	    || (history_seconds && *history_seconds)
+	    || (history_since_string && *history_since_string)) {
+
+		xmlnode *history = xmlnode_new_child(x, "history");
+
+		if (history_maxchars && *history_maxchars) {
+			xmlnode_set_attrib(history, "maxchars", history_maxchars);
+		}
+		if (history_maxstanzas && *history_maxstanzas) {
+			xmlnode_set_attrib(history, "maxstanzas", history_maxstanzas);
+		}
+		if (history_seconds && *history_seconds) {
+			xmlnode_set_attrib(history, "seconds", history_seconds);
+		}
+		if (history_since_string && *history_since_string) {
+			xmlnode_set_attrib(history, "since", history_since_string);
+		}
+	}
+
 	jabber_send(js, presence);
 	xmlnode_free(presence);
 
--- a/libpurple/protocols/jabber/jabber.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Tue Mar 08 02:27:47 2011 +0000
@@ -3008,21 +3008,44 @@
 {
 	JabberChat *chat = jabber_chat_find_by_conv(conv);
 	GHashTable *components;
-
-	if(!chat || !args || !args[0])
+	JabberID *jid;
+	const char *room = NULL, *server = NULL, *handle = NULL;
+
+	if (!chat || !args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
 	components = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
 
-	g_hash_table_replace(components, "room", args[0]);
-	g_hash_table_replace(components, "server", chat->server);
-	g_hash_table_replace(components, "handle", chat->handle);
-	if(args[1])
-		g_hash_table_replace(components, "password", args[1]);
+	jid = jabber_id_new(args[0]);
+	if (jid) {
+		room   = jid->node;
+		server = jid->domain;
+		handle = jid->resource ? jid->resource : chat->handle;
+	} else {
+		/* If jabber_id_new failed, the user may have just passed in
+		 * a room name.  For backward compatibility, handle that here.
+		 */
+		if (strchr(args[0], '@')) {
+			*error = g_strdup(_("Invalid XMPP ID"));
+			return PURPLE_CMD_RET_FAILED;
+		}
+
+		room   = args[0];
+		server = chat->server;
+		handle = chat->handle;
+	}
+
+	g_hash_table_insert(components, "room", (gpointer)room);
+	g_hash_table_insert(components, "server", (gpointer)server);
+	g_hash_table_insert(components, "handle", (gpointer)handle);
+
+	if (args[1])
+		g_hash_table_insert(components, "password", args[1]);
 
 	jabber_chat_join(purple_conversation_get_gc(conv), components);
 
 	g_hash_table_destroy(components);
+	jabber_id_free(jid);
 	return PURPLE_CMD_RET_OK;
 }
 
@@ -3650,6 +3673,7 @@
 	                  PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber",
 	                  jabber_cmd_chat_join,
 	                  _("join: &lt;room&gt; [password]:  Join a chat on this server."),
+	                  /* _("join: &lt;room[@server]&gt; [password]:  Join a chat."), */
 	                  NULL);
 	commands = g_slist_prepend(commands, GUINT_TO_POINTER(id));
 
--- a/libpurple/protocols/jabber/jutil.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/jabber/jutil.c	Tue Mar 08 02:27:47 2011 +0000
@@ -501,12 +501,9 @@
 jabber_id_free(JabberID *jid)
 {
 	if(jid) {
-		if(jid->node)
-			g_free(jid->node);
-		if(jid->domain)
-			g_free(jid->domain);
-		if(jid->resource)
-			g_free(jid->resource);
+		g_free(jid->node);
+		g_free(jid->domain);
+		g_free(jid->resource);
 		g_free(jid);
 	}
 }
--- a/libpurple/protocols/jabber/message.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/jabber/message.c	Tue Mar 08 02:27:47 2011 +0000
@@ -1140,6 +1140,12 @@
 	if(!who || !msg)
 		return 0;
 
+	if (purple_debug_is_verbose()) {
+		/* TODO: Maybe we need purple_debug_is_really_verbose? :) */
+		purple_debug_misc("jabber", "jabber_message_send_im: who='%s'\n"
+		                            "\tmsg='%s'\n", who, msg);
+	}
+
 	resource = jabber_get_resource(who);
 
 	jb = jabber_buddy_find(gc->proto_data, who, TRUE);
--- a/libpurple/protocols/msn/contact.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/contact.c	Tue Mar 08 02:27:47 2011 +0000
@@ -363,10 +363,9 @@
 	char *type;
 	char *member_id;
 	MsnUser *user;
-	xmlnode *annotation, *display;
+	xmlnode *annotation;
 	guint nid = MSN_NETWORK_UNKNOWN;
 	char *invite = NULL;
-	char *display_text;
 
 	passport = xmlnode_get_data(xmlnode_get_child(member, node));
 	if (!msn_email_is_valid(passport)) {
@@ -376,13 +375,8 @@
 
 	type = xmlnode_get_data(xmlnode_get_child(member, "Type"));
 	member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId"));
-	if ((display = xmlnode_get_child(member, "DisplayName"))) {
-		display_text = xmlnode_get_data(display);
-	} else {
-		display_text = NULL;
-	}
 
-	user = msn_userlist_find_add_user(session->userlist, passport, display_text);
+	user = msn_userlist_find_add_user(session->userlist, passport, NULL);
 
 	for (annotation = xmlnode_get_child(member, "Annotations/Annotation");
 	     annotation;
@@ -423,7 +417,6 @@
 	g_free(type);
 	g_free(member_id);
 	g_free(invite);
-	g_free(display_text);
 }
 
 static void
--- a/libpurple/protocols/msn/msn.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/msn.c	Tue Mar 08 02:27:47 2011 +0000
@@ -110,11 +110,9 @@
 
 	g_return_val_if_fail(str != NULL, NULL);
 
-	g_snprintf(buf, sizeof(buf), "%s%s", str,
-			   (strchr(str, '@') ? "" : "@hotmail.com"));
-
-	tmp = g_utf8_strdown(buf, -1);
-	strncpy(buf, tmp, sizeof(buf));
+	tmp = g_strchomp(g_utf8_strdown(str, -1));
+	g_snprintf(buf, sizeof(buf), "%s%s", tmp,
+			   (strchr(tmp, '@') ? "" : "@hotmail.com"));
 	g_free(tmp);
 
 	return buf;
--- a/libpurple/protocols/msn/p2p.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/p2p.c	Tue Mar 08 02:27:47 2011 +0000
@@ -23,72 +23,139 @@
  */
 
 #include "internal.h"
+#include "debug.h"
 
 #include "p2p.h"
 #include "msnutils.h"
 
 MsnP2PInfo *
-msn_p2p_info_new(void)
+msn_p2p_info_new(MsnP2PVersion version)
 {
-	return g_new0(MsnP2PInfo, 1);
+	MsnP2PInfo *info = g_new0(MsnP2PInfo, 1);
+	info->version = version;
+
+	switch (version) {
+		case MSN_P2P_VERSION_ONE:
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", version);
+			g_free(info);
+			info = NULL;
+	}
+
+	return info;
 }
 
 MsnP2PInfo *
 msn_p2p_info_dup(MsnP2PInfo *info)
 {
 	MsnP2PInfo *new_info = g_new0(MsnP2PInfo, 1);
-	*new_info = *info;
+
+	new_info->version = info->version;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+		case MSN_P2P_VERSION_TWO:
+			*new_info = *info;
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+			g_free(new_info);
+			new_info = NULL;
+	}
+
 	return new_info;
 }
 
 void
 msn_p2p_info_free(MsnP2PInfo *info)
 {
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
 	g_free(info);
 }
 
 size_t
 msn_p2p_header_from_wire(MsnP2PInfo *info, const char *wire)
 {
-	MsnP2PHeader *header;
+	size_t len;
 
-	header = &info->header;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE: {
+			MsnP2PHeader *header = &info->header.v1;
 
-	header->session_id = msn_pop32le(wire);
-	header->id         = msn_pop32le(wire);
-	header->offset     = msn_pop64le(wire);
-	header->total_size = msn_pop64le(wire);
-	header->length     = msn_pop32le(wire);
-	header->flags      = msn_pop32le(wire);
-	header->ack_id     = msn_pop32le(wire);
-	header->ack_sub_id = msn_pop32le(wire);
-	header->ack_size   = msn_pop64le(wire);
+			header->session_id = msn_pop32le(wire);
+			header->id         = msn_pop32le(wire);
+			header->offset     = msn_pop64le(wire);
+			header->total_size = msn_pop64le(wire);
+			header->length     = msn_pop32le(wire);
+			header->flags      = msn_pop32le(wire);
+			header->ack_id     = msn_pop32le(wire);
+			header->ack_sub_id = msn_pop32le(wire);
+			header->ack_size   = msn_pop64le(wire);
 
-	return P2P_PACKET_HEADER_SIZE;
+			len = P2P_PACKET_HEADER_SIZE;
+			break;
+		}
+
+		case MSN_P2P_VERSION_TWO:
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return len;
 }
 
 char *
 msn_p2p_header_to_wire(MsnP2PInfo *info, size_t *len)
 {
-	MsnP2PHeader *header;
-	char *wire;
+	char *wire = NULL;
 	char *tmp;
 
-	header = &info->header;
-	tmp = wire = g_new(char, P2P_PACKET_HEADER_SIZE);
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE: {
+			MsnP2PHeader *header = &info->header.v1;
+			tmp = wire = g_new(char, P2P_PACKET_HEADER_SIZE);
+
+			msn_push32le(tmp, header->session_id);
+			msn_push32le(tmp, header->id);
+			msn_push64le(tmp, header->offset);
+			msn_push64le(tmp, header->total_size);
+			msn_push32le(tmp, header->length);
+			msn_push32le(tmp, header->flags);
+			msn_push32le(tmp, header->ack_id);
+			msn_push32le(tmp, header->ack_sub_id);
+			msn_push64le(tmp, header->ack_size);
 
-	msn_push32le(tmp, header->session_id);
-	msn_push32le(tmp, header->id);
-	msn_push64le(tmp, header->offset);
-	msn_push64le(tmp, header->total_size);
-	msn_push32le(tmp, header->length);
-	msn_push32le(tmp, header->flags);
-	msn_push32le(tmp, header->ack_id);
-	msn_push32le(tmp, header->ack_sub_id);
-	msn_push64le(tmp, header->ack_size);
+			if (len)
+				*len = P2P_PACKET_HEADER_SIZE;
+
+			break;
+		}
 
-	if (len)
-		*len = P2P_PACKET_HEADER_SIZE;
+		case MSN_P2P_VERSION_TWO:
+			if (len)
+				*len = 0;
+
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 
 	return wire;
 
@@ -127,15 +194,30 @@
 void
 msn_p2p_info_to_string(MsnP2PInfo *info, GString *str)
 {
-	g_string_append_printf(str, "Session ID: %u\r\n", info->header.session_id);
-	g_string_append_printf(str, "ID:         %u\r\n", info->header.id);
-	g_string_append_printf(str, "Offset:     %" G_GUINT64_FORMAT "\r\n", info->header.offset);
-	g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", info->header.total_size);
-	g_string_append_printf(str, "Length:     %u\r\n", info->header.length);
-	g_string_append_printf(str, "Flags:      0x%x\r\n", info->header.flags);
-	g_string_append_printf(str, "ACK ID:     %u\r\n", info->header.ack_id);
-	g_string_append_printf(str, "SUB ID:     %u\r\n", info->header.ack_sub_id);
-	g_string_append_printf(str, "ACK Size:   %" G_GUINT64_FORMAT "\r\n", info->header.ack_size);
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE: {
+			MsnP2PHeader *header = &info->header.v1;
+			g_string_append_printf(str, "Session ID: %u\r\n", header->session_id);
+			g_string_append_printf(str, "ID:         %u\r\n", header->id);
+			g_string_append_printf(str, "Offset:     %" G_GUINT64_FORMAT "\r\n", header->offset);
+			g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", header->total_size);
+			g_string_append_printf(str, "Length:     %u\r\n", header->length);
+			g_string_append_printf(str, "Flags:      0x%x\r\n", header->flags);
+			g_string_append_printf(str, "ACK ID:     %u\r\n", header->ack_id);
+			g_string_append_printf(str, "SUB ID:     %u\r\n", header->ack_sub_id);
+			g_string_append_printf(str, "ACK Size:   %" G_GUINT64_FORMAT "\r\n", header->ack_size);
+
+			break;
+		}
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
 	g_string_append_printf(str, "Footer:     0x%08X\r\n", info->footer.value);
 }
 
@@ -150,67 +232,232 @@
 gboolean
 msn_p2p_info_is_valid(MsnP2PInfo *info)
 {
-	return info->header.total_size >= info->header.length;
+	gboolean valid = FALSE;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			valid = info->header.v1.total_size >= info->header.v1.length;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return valid;
 }
 
 gboolean
 msn_p2p_info_is_final(MsnP2PInfo *info)
 {
-	return info->header.offset + info->header.length >= info->header.total_size;
+	gboolean final = FALSE;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			final = info->header.v1.offset + info->header.v1.length >= info->header.v1.total_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return final;
 }
 
 guint32
 msn_p2p_info_get_session_id(MsnP2PInfo *info)
 {
-	return info->header.session_id;
+	guint32 session_id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			session_id = info->header.v1.session_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return session_id;
 }
 
 guint32
 msn_p2p_info_get_id(MsnP2PInfo *info)
 {
-	return info->header.id;
+	guint32 id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			id = info->header.v1.id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return id;
 }
 
 guint64
 msn_p2p_info_get_offset(MsnP2PInfo *info)
 {
-	return info->header.offset;
+	guint64 offset = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			offset = info->header.v1.offset;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return offset;
 }
 
 guint64
 msn_p2p_info_get_total_size(MsnP2PInfo *info)
 {
-	return info->header.total_size;
+	guint64 total_size = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			total_size = info->header.v1.total_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return total_size;
 }
 
 guint32
 msn_p2p_info_get_length(MsnP2PInfo *info)
 {
-	return info->header.length;
+	guint32 length = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			length = info->header.v1.length;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return length;
 }
 
 guint32
 msn_p2p_info_get_flags(MsnP2PInfo *info)
 {
-	return info->header.flags;
+	guint32 flags = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			flags = info->header.v1.flags;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return flags;
 }
 
 guint32
 msn_p2p_info_get_ack_id(MsnP2PInfo *info)
 {
-	return info->header.ack_id;
+	guint32 ack_id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			ack_id = info->header.v1.ack_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return ack_id;
 }
 
 guint32
 msn_p2p_info_get_ack_sub_id(MsnP2PInfo *info)
 {
-	return info->header.ack_sub_id;
+	guint32 ack_sub_id = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			ack_sub_id = info->header.v1.ack_sub_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return ack_sub_id;
 }
 
 guint64
 msn_p2p_info_get_ack_size(MsnP2PInfo *info)
 {
-	return info->header.ack_size;
+	guint64 ack_size = 0;
+
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			ack_size = info->header.v1.ack_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
+	return ack_size;
 }
 
 guint32
@@ -222,55 +469,156 @@
 void
 msn_p2p_info_set_session_id(MsnP2PInfo *info, guint32 session_id)
 {
-	info->header.session_id = session_id;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.session_id = session_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
 }
 
 void
 msn_p2p_info_set_id(MsnP2PInfo *info, guint32 id)
 {
-	info->header.id = id;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.id = id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
+
 }
 
 void
 msn_p2p_info_set_offset(MsnP2PInfo *info, guint64 offset)
 {
-	info->header.offset = offset;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.offset = offset;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
 msn_p2p_info_set_total_size(MsnP2PInfo *info, guint64 total_size)
 {
-	info->header.total_size = total_size;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.total_size = total_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
 msn_p2p_info_set_length(MsnP2PInfo *info, guint32 length)
 {
-	info->header.length = length;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.length = length;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
 msn_p2p_info_set_flags(MsnP2PInfo *info, guint32 flags)
 {
-	info->header.flags = flags;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.flags = flags;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
 msn_p2p_info_set_ack_id(MsnP2PInfo *info, guint32 ack_id)
 {
-	info->header.ack_id = ack_id;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.ack_id = ack_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
 msn_p2p_info_set_ack_sub_id(MsnP2PInfo *info, guint32 ack_sub_id)
 {
-	info->header.ack_sub_id = ack_sub_id;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.ack_sub_id = ack_sub_id;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
 msn_p2p_info_set_ack_size(MsnP2PInfo *info, guint64 ack_size)
 {
-	info->header.ack_size = ack_size;
+	switch (info->version) {
+		case MSN_P2P_VERSION_ONE:
+			info->header.v1.ack_size = ack_size;
+			break;
+
+		case MSN_P2P_VERSION_TWO:
+			/* Nothing to do! */
+			break;
+
+		default:
+			purple_debug_error("msn", "Invalid P2P Info version: %d\n", info->version);
+	}
 }
 
 void
--- a/libpurple/protocols/msn/p2p.h	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/p2p.h	Tue Mar 08 02:27:47 2011 +0000
@@ -58,8 +58,18 @@
 } MsnP2PFooter;
 #define P2P_PACKET_FOOTER_SIZE (1 * 4)
 
+typedef enum
+{
+	MSN_P2P_VERSION_ONE = 0,
+	MSN_P2P_VERSION_TWO = 1,
+} MsnP2PVersion;
+
 typedef struct {
-	MsnP2PHeader header;
+	MsnP2PVersion version;
+	union {
+		MsnP2PHeader v1;
+		MsnP2Pv2Header v2;
+	} header;
 	MsnP2PFooter footer;
 } MsnP2PInfo;
 
@@ -94,7 +104,7 @@
 } MsnP2PAppId;
 
 MsnP2PInfo *
-msn_p2p_info_new(void);
+msn_p2p_info_new(MsnP2PVersion version);
 
 MsnP2PInfo *
 msn_p2p_info_dup(MsnP2PInfo *info);
--- a/libpurple/protocols/msn/slpmsg.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/slpmsg.c	Tue Mar 08 02:27:47 2011 +0000
@@ -48,7 +48,7 @@
 	else
 		slpmsg->slplink = NULL;
 
-	slpmsg->p2p_info = msn_p2p_info_new();
+	slpmsg->p2p_info = msn_p2p_info_new(MSN_P2P_VERSION_ONE);
 
 	return slpmsg;
 }
--- a/libpurple/protocols/msn/slpmsg_part.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/slpmsg_part.c	Tue Mar 08 02:27:47 2011 +0000
@@ -54,7 +54,7 @@
 	}
 
 	part = msn_slpmsgpart_new(NULL);
-	part->info = msn_p2p_info_new();
+	part->info = msn_p2p_info_new(MSN_P2P_VERSION_ONE);
 
 	/* Extract the binary SLP header */
 	len = msn_p2p_header_from_wire(part->info, data);
--- a/libpurple/protocols/msn/switchboard.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/switchboard.c	Tue Mar 08 02:27:47 2011 +0000
@@ -265,12 +265,6 @@
 		return;
 	}
 
-	/* Don't add ourselves either... */
-	if (g_str_equal(passport, purple_account_get_username(account))) {
-		g_free(passport);
-		return;
-	}
-
 	if (!msnuser) {
 		purple_debug_info("msn","User %s is not on our list.\n", passport);
 		msnuser = msn_user_new(userlist, passport, NULL);
--- a/libpurple/protocols/msn/user.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/msn/user.c	Tue Mar 08 02:27:47 2011 +0000
@@ -223,7 +223,10 @@
 {
 	g_return_val_if_fail(user != NULL, FALSE);
 
-	if (user->friendly_name && name && (!strcmp(user->friendly_name, name) ||
+	if (!name)
+		return FALSE;
+
+	if (user->friendly_name && (!strcmp(user->friendly_name, name) ||
 				!strcmp(user->passport, name)))
 		return FALSE;
 
--- a/libpurple/protocols/mxit/Makefile.am	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/Makefile.am	Tue Mar 08 02:27:47 2011 +0000
@@ -33,7 +33,9 @@
 	roster.c \
 	roster.h \
 	splashscreen.c \
-	splashscreen.h
+	splashscreen.h \
+	voicevideo.c \
+	voicevideo.h
 
 
 AM_CFLAGS = $(st)
--- a/libpurple/protocols/mxit/Makefile.mingw	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/Makefile.mingw	Tue Mar 08 02:27:47 2011 +0000
@@ -51,7 +51,8 @@
 			profile.c \
 			protocol.c \
 			roster.c \
-			splashscreen.c
+			splashscreen.c \
+			voicevideo.c
 
 OBJECTS = $(C_SRC:%.c=%.o)
 
--- a/libpurple/protocols/mxit/actions.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/actions.c	Tue Mar 08 02:27:47 2011 +0000
@@ -314,12 +314,11 @@
 {
 	char	version[256];
 
-	g_snprintf( version, sizeof( version ), "MXit libPurple Plugin v%s\n"
+	g_snprintf( version, sizeof( version ), 
 											"MXit Client Protocol v%i.%i\n\n"
 											"Author:\nPieter Loubser\n\n"
 											"Contributors:\nAndrew Victor\n\n"
 											"Testers:\nBraeme Le Roux\n\n",
-											MXIT_PLUGIN_VERSION,
 											( MXIT_CP_PROTO_VESION / 10 ), ( MXIT_CP_PROTO_VESION % 10 ) );
 
 	mxit_popup( PURPLE_NOTIFY_MSG_INFO, _( "About" ), version );
--- a/libpurple/protocols/mxit/chunk.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/chunk.c	Tue Mar 08 02:27:47 2011 +0000
@@ -406,10 +406,9 @@
  *  @param chunkdata		Chunked-data buffer
  *  @param mxitId			The username who's avatar to download
  *  @param avatarId			The Id of the avatar image (as string)
- *  @param imgsize			The resolution of the avatar image
  *  @return					The number of bytes encoded in the buffer
  */
-int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId, unsigned int imgsize )
+int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId )
 {
 	int			pos = 0;
 
@@ -432,7 +431,7 @@
 	pos += add_int16( &chunkdata[pos], 1 );
 
 	/* image size [4 bytes] */
-	pos += add_int32( &chunkdata[pos], imgsize );
+	pos += add_int32( &chunkdata[pos], MXIT_AVATAR_SIZE );
 
 	return pos;
 }
--- a/libpurple/protocols/mxit/chunk.h	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/chunk.h	Tue Mar 08 02:27:47 2011 +0000
@@ -152,7 +152,7 @@
 int mxit_chunk_create_get( char* chunkdata, const char* fileid, int filesize, int offset );
 int mxit_chunk_create_received( char* chunkdata, const char* fileid, unsigned char status );
 int mxit_chunk_create_set_avatar( char* chunkdata, const unsigned char* data, int datalen );
-int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId, unsigned int imgsize );
+int mxit_chunk_create_get_avatar( char* chunkdata, const char* mxitId, const char* avatarId );
 
 /* Decode chunk */
 void mxit_chunk_parse_offer( char* chunkdata, int datalen, struct offerfile_chunk* offer );
--- a/libpurple/protocols/mxit/formcmds.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/formcmds.c	Tue Mar 08 02:27:47 2011 +0000
@@ -47,7 +47,11 @@
 	MXIT_CMD_REPLY,				/* Reply (reply) */
 	MXIT_CMD_PLATREQ,			/* Platform Request (platreq) */
 	MXIT_CMD_SELECTCONTACT,		/* Select Contact (selc) */
-	MXIT_CMD_IMAGE				/* Inline image (img) */
+	MXIT_CMD_IMAGE,				/* Inline image (img) */
+	MXIT_CMD_SCREENCONFIG,		/* Chat-screen config (csc) */
+	MXIT_CMD_SCREENINFO,		/* Chat-screen info (csi) */
+	MXIT_CMD_IMAGESTRIP,		/* Image Strip (is) */
+	MXIT_CMD_TABLE				/* Table (tbl) */
 } MXitCommandType;
 
 
@@ -87,7 +91,7 @@
 		goto done;
 	}
 
-	/* lets first see if we dont have the inline image already in cache */
+	/* lets first see if we don't have the inline image already in cache */
 	if (g_hash_table_lookup(iireq->mx->session->iimages, iireq->url)) {
 		/* inline image found in the cache, so we just ignore this reply */
 		goto done;
@@ -149,8 +153,16 @@
 			else if (strcmp(type, "selc") == 0)				/* select contact */
 				return MXIT_CMD_SELECTCONTACT;
 		}
-		else if (strcmp(op, "img") == 0)
-				return MXIT_CMD_IMAGE;
+		else if (strcmp(op, "img") == 0)					/* inline image */
+			return MXIT_CMD_IMAGE;
+		else if (strcmp(op, "csc") == 0)					/* chat-screen config */
+			return MXIT_CMD_SCREENCONFIG;
+		else if (strcmp(op, "csi") == 0)					/* chat-screen info */
+			return MXIT_CMD_SCREENINFO;
+		else if (strcmp(op, "is") == 0)						/* image-strip */
+			return MXIT_CMD_IMAGESTRIP;
+		else if (strcmp(op, "tbl") == 0)					/* table */
+			return MXIT_CMD_TABLE;
 	}
 
 	return MXIT_CMD_UNKNOWN;
@@ -333,7 +345,7 @@
 			g_string_append_printf(msg, "%s%s>", MXIT_II_TAG, iireq->url);
 			mx->got_img = TRUE;
 
-			/* lets first see if we dont have the inline image already in cache */
+			/* lets first see if we don't have the inline image already in cache */
 			if (g_hash_table_lookup(mx->session->iimages, iireq->url)) {
 				/* inline image found in the cache, so we do not have to request it from the web */
 				g_free(iireq);
--- a/libpurple/protocols/mxit/login.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/login.c	Tue Mar 08 02:27:47 2011 +0000
@@ -84,7 +84,7 @@
 	session->iimages = g_hash_table_new( g_str_hash, g_str_equal );
 	session->rx_state = RX_STATE_RLEN;
 	session->http_interval = MXIT_HTTP_POLL_MIN;
-	session->http_last_poll = time( NULL );
+	session->http_last_poll = mxit_now_milli();
 
 	return session;
 }
@@ -106,7 +106,7 @@
 	purple_connection_update_progress( session->con, _( "Logging In..." ), 2, 4 );
 
 	/* create a timer to send a ping packet if the connection is idle */
-	session->last_tx = time( NULL );
+	session->last_tx = mxit_now_milli();
 
 	/* encrypt the user password */
 	session->encpwd = mxit_encrypt_password( session );
@@ -141,9 +141,10 @@
 	}
 
 	/* This timer might already exist if we're registering a new account */
-	if ( session->q_timer == 0 )
+	if ( session->q_timer == 0 ) {
 		/* start the tx queue manager timer */
-		session->q_timer = purple_timeout_add_seconds( 2, mxit_manage_queue, session );
+		session->q_timer = purple_timeout_add_seconds( 2, mxit_manage_queue_slow, session );
+	}
 }
 
 
@@ -238,7 +239,7 @@
 	/* nickname */
 	str = purple_request_fields_get_string( fields, "nickname" );
 	if ( ( !str ) || ( strlen( str ) < 3 ) ) {
-		err = _( "The Display Name you entered is invalid." );
+		err = _( "The Display Name you entered is too short." );
 		goto out;
 	}
 	g_strlcpy( profile->nickname, str, sizeof( profile->nickname ) );
@@ -546,8 +547,8 @@
 	/* get state */
 	state = purple_account_get_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
 
-	url = g_strdup_printf( "%s?type=getpid&sessionid=%s&login=%s&ver=%s&clientid=%s&cat=%s&chalresp=%s&cc=%s&loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li",
-			session->logindata->wapserver, session->logindata->sessionid, purple_url_encode( session->acc->username ), MXIT_CP_RELEASE, MXIT_CLIENT_ID, MXIT_CP_ARCH,
+	url = g_strdup_printf( "%s?type=getpid&sessionid=%s&login=%s&ver=%i.%i.%i&clientid=%s&cat=%s&chalresp=%s&cc=%s&loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li",
+			session->logindata->wapserver, session->logindata->sessionid, purple_url_encode( session->acc->username ), PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CLIENT_ID, MXIT_CP_ARCH,
 			captcha_resp, session->logindata->cc, session->logindata->locale, ( state == MXIT_STATE_REGISTER1 ) ? 0 : 1, MXIT_CP_PLATFORM, MXIT_CP_OS,
 			MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) );
 	url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo2, session );
@@ -762,6 +763,12 @@
 {
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_reconnect\n" );
 
+	/* remove the input cb function */
+	if ( session->con->inpa ) {
+		purple_input_remove( session->con->inpa );
+		session->con->inpa = 0;
+	}
+
 	/* close existing connection */
 	session->flags &= ~MXIT_FLAG_CONNECTED;
 	purple_proxy_connect_cancel_with_handle( session->con );
--- a/libpurple/protocols/mxit/mxit.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/mxit.c	Tue Mar 08 02:27:47 2011 +0000
@@ -37,6 +37,7 @@
 #include	"filexfer.h"
 #include	"actions.h"
 #include	"multimx.h"
+#include	"voicevideo.h"
 
 
 #ifdef	MXIT_LINK_CLICK
@@ -524,7 +525,7 @@
 	if ( session->http )
 		return;
 
-	if ( session->last_tx <= time( NULL ) - MXIT_PING_INTERVAL ) {
+	if ( session->last_tx <= ( mxit_now_milli() - ( MXIT_PING_INTERVAL * 1000 ) ) ) {
 		/*
 		 * this connection has been idle for too long, better ping
 		 * the server before it kills our connection.
@@ -559,6 +560,8 @@
  */
 static void mxit_get_info( PurpleConnection *gc, const char *who )
 {
+	PurpleBuddy*			buddy;
+	struct contact*			contact;
 	struct MXitSession*		session			= (struct MXitSession*) gc->proto_data;
 	const char*				profilelist[]	= { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME,
 												CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_REGCOUNTRY, CP_PROFILE_LASTSEEN,
@@ -566,6 +569,22 @@
 
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_info: '%s'\n", who );
 
+	/* find the buddy information for this contact (reference: "libpurple/blist.h") */
+	buddy = purple_find_buddy( session->acc, who );
+	if ( !buddy ) {
+		purple_debug_warning( MXIT_PLUGIN_ID, "mxit_get_info: unable to find the buddy '%s'\n", who );
+		return;
+	}
+
+	contact = purple_buddy_get_protocol_data( buddy );
+	if ( !contact )
+		return;
+
+	/* only MXit users have profiles */
+	if ( contact->type != MXIT_TYPE_MXIT ) {
+		mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "No profile available" ), _( "This contact does not have a profile." ) );
+		return;
+	}
 
 	/* send profile request */
 	mxit_send_extprofile_request( session, who, ARRAY_SIZE( profilelist ), profilelist );
@@ -588,7 +607,33 @@
 
 
 /*------------------------------------------------------------------------
- * Buddy list menu.
+ * Re-Invite was selected from the buddy-list menu.
+ *
+ *  @param node		The entry in the buddy list.
+ *  @param ignored	(not used)
+ */
+static void mxit_reinvite( PurpleBlistNode *node, gpointer ignored )
+{
+	PurpleBuddy*		buddy;
+	struct contact*		contact;
+	PurpleConnection*	gc;
+	struct MXitSession*	session;
+
+	buddy = (PurpleBuddy *)node;
+	gc = purple_account_get_connection( purple_buddy_get_account( buddy ) );
+	session = gc->proto_data;
+
+	contact = purple_buddy_get_protocol_data( (PurpleBuddy*) node );
+	if ( !contact )
+		return;
+
+	/* send a new invite */
+	mxit_send_invite( session, contact->username, contact->alias, contact->groupname );
+}
+
+
+/*------------------------------------------------------------------------
+ * Buddy-list menu.
  *
  *  @param node		The entry in the buddy list.
  */
@@ -597,6 +642,7 @@
 	PurpleBuddy*		buddy;
 	struct contact*		contact;
 	GList*				m = NULL;
+	PurpleMenuAction*	act;
 
 	if ( !PURPLE_BLIST_NODE_IS_BUDDY( node ) )
 		return NULL;
@@ -606,6 +652,12 @@
 	if ( !contact )
 		return NULL;
 
+	if ( ( contact->subtype == MXIT_SUBTYPE_DELETED ) || ( contact->subtype == MXIT_SUBTYPE_REJECTED ) || ( contact->subtype == MXIT_SUBTYPE_NONE ) ) {
+		/* contact is in Deleted, Rejected or None state */
+		act = purple_menu_action_new( _( "Re-Invite" ), PURPLE_CALLBACK( mxit_reinvite ), NULL, NULL );
+		m = g_list_append(m, act);
+	}
+
 	return m;
 }
 
@@ -686,8 +738,8 @@
 	NULL,					/* attention_types */
 	sizeof( PurplePluginProtocolInfo ),		/* struct_size */
 	mxit_get_text_table,	/* get_account_text_table */
-	NULL,					/* initiate_media */
-	NULL,					/* get_media_caps */
+	mxit_media_initiate,	/* initiate_media */
+	mxit_media_caps,		/* get_media_caps */
 	mxit_get_moods,			/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL					/* get_public_alias */
@@ -706,7 +758,7 @@
 
 	MXIT_PLUGIN_ID,										/* plugin id (must be unique) */
 	MXIT_PLUGIN_NAME,									/* plugin name (this will be displayed in the UI) */
-	MXIT_PLUGIN_VERSION,								/* version of the plugin */
+	DISPLAY_VERSION,									/* version of the plugin */
 
 	MXIT_PLUGIN_SUMMARY,								/* short summary of the plugin */
 	MXIT_PLUGIN_DESC,									/* description of the plugin (can be long) */
--- a/libpurple/protocols/mxit/mxit.h	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/mxit.h	Tue Mar 08 02:27:47 2011 +0000
@@ -63,13 +63,12 @@
 /* Plugin details */
 #define		MXIT_PLUGIN_ID				"prpl-loubserp-mxit"
 #define		MXIT_PLUGIN_NAME			"MXit"
-#define		MXIT_PLUGIN_VERSION			"2.4.0"
 #define		MXIT_PLUGIN_EMAIL			"Pieter Loubser <libpurple@mxit.com>"
 #define		MXIT_PLUGIN_WWW				"http://www.mxit.com"
 #define		MXIT_PLUGIN_SUMMARY			"MXit Protocol Plugin"
 #define		MXIT_PLUGIN_DESC			"MXit"
 
-#define		MXIT_HTTP_USERAGENT			"libpurple-"MXIT_PLUGIN_VERSION
+#define		MXIT_HTTP_USERAGENT			"libpurple-"DISPLAY_VERSION
 
 
 /* default connection settings */
@@ -111,7 +110,6 @@
 /* define this to enable the link clicking support */
 #define		MXIT_LINK_CLICK
 
-
 #ifdef		MXIT_LINK_CLICK
 #define		MXIT_LINK_PREFIX			"gopher://"
 #define		MXIT_LINK_KEY				"MXIT"
@@ -137,10 +135,13 @@
 	unsigned int		http_seqno;					/* HTTP request sequence number */
 	guint				http_timer_id;				/* timer resource id (pidgin) */
 	int					http_interval;				/* poll inverval */
-	time_t				http_last_poll;				/* the last time a poll has been sent */
+	gint64				http_last_poll;				/* the last time a poll has been sent */
 	guint				http_handler;				/* HTTP connection handler */
 	void*				http_out_req;				/* HTTP outstanding request */
 
+	/* other servers */
+	char				voip_server[HOST_NAME_MAX];	/* voice/video server */
+
 	/* client */
 	struct login_data*	logindata;
 	char*				encpwd;						/* encrypted password */
@@ -159,7 +160,7 @@
 
 	/* transmit */
 	struct tx_queue		queue;						/* transmit packet queue (FIFO mode) */
-	time_t				last_tx;					/* timestamp of last packet sent */
+	gint64				last_tx;					/* timestamp of last packet sent */
 	int					outack;						/* outstanding ack packet */
 	guint				q_timer;					/* timer handler for managing queue */
 
@@ -169,7 +170,7 @@
 	unsigned int		rx_i;						/* receive buffer current index */
 	int					rx_res;						/* amount of bytes still outstanding for the current packet */
 	char				rx_state;					/* current receiver state */
-	time_t				last_rx;					/* timestamp of last packet received */
+	gint64				last_rx;					/* timestamp of last packet received */
 	GList*				active_chats;				/* list of all our contacts we received messages from (active chats) */
 
 	/* groupchat */
--- a/libpurple/protocols/mxit/profile.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/profile.c	Tue Mar 08 02:27:47 2011 +0000
@@ -108,10 +108,10 @@
  */
 static const char* datetime( gint64 msecs )
 {
-    time_t secs = msecs / 1000;
+	time_t secs = msecs / 1000;
 
-    struct tm t;
-    localtime_r( &secs, &t );
+	struct tm t;
+	localtime_r( &secs, &t );
 
 	return purple_utf8_strftime( "%d-%m-%Y %H:%M:%S", &t );
 }
@@ -140,13 +140,10 @@
 	purple_notify_user_info_add_pair( info, _( "Display Name" ), profile->nickname );
 	purple_notify_user_info_add_pair( info, _( "Birthday" ), profile->birthday );
 	purple_notify_user_info_add_pair( info, _( "Gender" ), profile->male ? _( "Male" ) : _( "Female" ) );
-//	purple_notify_user_info_add_pair( info, _( "Hidden Number" ), profile->hidden ? _( "Yes" ) : _( "No" ) );
 
 	/* optional information */
-//	purple_notify_user_info_add_pair( info, _( "Title" ), profile->title );
 	purple_notify_user_info_add_pair( info, _( "First Name" ), profile->firstname );
 	purple_notify_user_info_add_pair( info, _( "Last Name" ), profile->lastname );
-//	purple_notify_user_info_add_pair( info, _( "Email" ), profile->email );
 	purple_notify_user_info_add_pair( info, _( "Country" ), profile->regcountry );
 
 	purple_notify_user_info_add_section_break( info );
--- a/libpurple/protocols/mxit/protocol.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/protocol.c	Tue Mar 08 02:27:47 2011 +0000
@@ -37,6 +37,7 @@
 #include	"login.h"
 #include	"formcmds.h"
 #include	"http.h"
+#include	"voicevideo.h"
 
 
 #define		MXIT_MS_OFFSET		3
@@ -45,6 +46,18 @@
 #define		CP_REC_TERM			( ( session->http ) ? CP_HTTP_REC_TERM : CP_SOCK_REC_TERM )
 
 
+/*------------------------------------------------------------------------
+ * return the current timestamp in milliseconds
+ */
+gint64 mxit_now_milli( void )
+{
+	GTimeVal	now;
+
+	g_get_current_time( &now );
+
+	return ( ( now.tv_sec * 1000 ) + ( now.tv_usec / 1000 ) );
+}
+
 
 /*------------------------------------------------------------------------
  * Display a notification popup message to the user.
@@ -411,7 +424,7 @@
 	}
 
 	/* update the timestamp of the last-transmitted packet */
-	session->last_tx = time( NULL );
+	session->last_tx = mxit_now_milli();
 
 	/*
 	 * we need to remember that we are still waiting for the ACK from
@@ -474,17 +487,13 @@
 	packet->datalen = datalen;
 
 
-	/*
-	 * shortcut: first check if there are any commands still outstanding.
-	 * if not, then we might as well just write this packet directly and
-	 * skip the whole queueing thing
-	 */
-	if ( session->outack == 0 ) {
-		/* no outstanding ACKs, so we might as well write it directly */
+	/* shortcut */
+	if ( ( session->queue.count == 0 ) && ( session->outack == 0 ) ) {
+		/* the queue is empty and there are no outstanding acks so we can write it directly */
 		mxit_send_packet( session, packet );
 	}
 	else {
-		/* ACK still outstanding, so we need to queue this request until we have the ACK */
+		/* we need to queue this packet */
 
 		if ( ( packet->cmd == CP_CMD_PING ) || ( packet->cmd == CP_CMD_POLL ) ) {
 			/* we do NOT queue HTTP poll nor socket ping packets */
@@ -503,42 +512,89 @@
 
 
 /*------------------------------------------------------------------------
- * Callback to manage the packet send queue (send next packet, timeout's, etc).
+ * Manage the packet send queue (send next packet, timeout's, etc).
  *
  *  @param session		The MXit session object
  */
-gboolean mxit_manage_queue( gpointer user_data )
+static void mxit_manage_queue( struct MXitSession* session )
 {
-	struct MXitSession* session		= (struct MXitSession*) user_data;
 	struct tx_packet*	packet		= NULL;
+	gint64				now			= mxit_now_milli();
 
 	if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) {
 		/* we are not connected, so ignore the queue */
-		return TRUE;
+		return;
 	}
 	else if ( session->outack > 0 ) {
 		/* we are still waiting for an outstanding ACK from the MXit server */
-		if ( session->last_tx <= time( NULL ) - MXIT_ACK_TIMEOUT ) {
+		if ( session->last_tx <= mxit_now_milli() - ( MXIT_ACK_TIMEOUT * 1000 ) ) {
 			/* ack timeout! so we close the connection here */
 			purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%i'\n", session->outack );
 			purple_connection_error( session->con, _( "Timeout while waiting for a response from the MXit server." ) );
 		}
-		return TRUE;
+		return;
+	}
+
+	/* 
+	 * the mxit server has flood detection and it prevents you from sending messages to fast.
+	 * this is a self defense mechanism, a very annoying feature. so the client must ensure that
+	 * it does not send messages too fast otherwise mxit will ignore the user for 30 seconds.
+	 * this is what we are trying to avoid here..
+	 */
+	if ( session->last_tx > ( now - MXIT_TX_DELAY ) ) {
+		/* we need to wait a little before sending the next packet, so schedule a wakeup call */
+		gint64 tdiff = now - ( session->last_tx );
+		guint delay = ( MXIT_TX_DELAY - tdiff ) + 9;
+		if ( delay <= 0 )
+			delay = MXIT_TX_DELAY;
+		purple_timeout_add( delay, mxit_manage_queue_fast, session );
+	}
+	else {
+		/* get the next packet from the queue to send */
+		packet = pop_tx_packet( session );
+		if ( packet != NULL ) {
+			/* there was a packet waiting to be sent to the server, now is the time to do something about it */
+
+			/* send the packet to MXit server */
+			mxit_send_packet( session, packet );
+		}
 	}
-
-	packet = pop_tx_packet( session );
-	if ( packet != NULL ) {
-		/* there was a packet waiting to be sent to the server, now is the time to do something about it */
-
-		/* send the packet to MXit server */
-		mxit_send_packet( session, packet );
-	}
-
+}
+
+
+/*------------------------------------------------------------------------
+ * Slow callback to manage the packet send queue.
+ *
+ *  @param session		The MXit session object
+ */
+gboolean mxit_manage_queue_slow( gpointer user_data )
+{
+	struct MXitSession* session		= (struct MXitSession*) user_data;
+
+	mxit_manage_queue( session );
+
+	/* continue running */
 	return TRUE;
 }
 
 
 /*------------------------------------------------------------------------
+ * Fast callback to manage the packet send queue.
+ *
+ *  @param session		The MXit session object
+ */
+gboolean mxit_manage_queue_fast( gpointer user_data )
+{
+	struct MXitSession* session		= (struct MXitSession*) user_data;
+
+	mxit_manage_queue( session );
+
+	/* stop running */
+	return FALSE;
+}
+
+
+/*------------------------------------------------------------------------
  * Callback to manage HTTP server polling (HTTP connections ONLY)
  *
  *  @param session		The MXit session object
@@ -547,9 +603,9 @@
 {
 	struct MXitSession* session		= (struct MXitSession*) user_data;
 	gboolean			poll		= FALSE;
-	time_t				now			= time( NULL );
+	gint64				now			= mxit_now_milli();
 	int					polldiff;
-	int					rxdiff;
+	gint64				rxdiff;
 
 	if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) {
 		/* we only poll if we are actually logged in */
@@ -579,7 +635,7 @@
 
 	if ( poll ) {
 		/* send poll request */
-		session->http_last_poll = time( NULL );
+		session->http_last_poll = mxit_now_milli();
 		mxit_send_poll( session );
 	}
 
@@ -638,21 +694,36 @@
 	const char*			locale;
 	char				data[CP_MAX_PACKET];
 	int					datalen;
+	char*				clientVersion;
+	unsigned int		features	= MXIT_CP_FEATURES;
 
 	locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE );
 
+	/* Voice and Video supported */
+	if (mxit_audio_enabled() && mxit_video_enabled())
+		features |= (MXIT_CF_VOICE | MXIT_CF_VIDEO);
+	else if (mxit_audio_enabled())
+		features |= MXIT_CF_VOICE;
+
+	/* generate client version string (eg, P-2.7.10-Y-PURPLE) */
+	clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM );
+
 	/* convert the packet to a byte stream */
 	datalen = snprintf( data, sizeof( data ),
 								"ms=%s%c%s%c%i%c%s%c"		/* "ms"=password\1version\1maxreplyLen\1name\1 */
 								"%s%c%i%c%s%c%s%c"			/* dateOfBirth\1gender\1location\1capabilities\1 */
-								"%s%c%i%c%s%c%s",			/* dc\1features\1dialingcode\1locale */
-								session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM,
+								"%s%c%i%c%s%c%s"			/* dc\1features\1dialingcode\1locale */
+								"%c%i%c%i",					/* \1protocolVer\1lastRosterUpdate */	
+								session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM,
 								profile->birthday, CP_FLD_TERM, ( profile->male ) ? 1 : 0, CP_FLD_TERM, MXIT_DEFAULT_LOC, CP_FLD_TERM, MXIT_CP_CAP, CP_FLD_TERM,
-								session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale
+								session->distcode, CP_FLD_TERM, features, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale,
+								CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0
 	);
 
 	/* queue packet for transmission */
 	mxit_queue_packet( session, data, datalen, CP_CMD_REGISTER );
+
+	g_free( clientVersion );
 }
 
 
@@ -663,21 +734,32 @@
  */
 void mxit_send_login( struct MXitSession* session )
 {
-	const char*	splashId;
-	const char*	locale;
-	char		data[CP_MAX_PACKET];
-	int			datalen;
+	const char*		splashId;
+	const char*		locale;
+	char			data[CP_MAX_PACKET];
+	int				datalen;
+	char*			clientVersion;
+	unsigned int	features	= MXIT_CP_FEATURES;
 
 	locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE );
 
+	/* Voice and Video supported */
+	if (mxit_audio_enabled() && mxit_video_enabled())
+		features |= (MXIT_CF_VOICE | MXIT_CF_VIDEO);
+	else if (mxit_audio_enabled())
+		features |= MXIT_CF_VOICE;
+
+	/* generate client version string (eg, P-2.7.10-Y-PURPLE) */
+	clientVersion = g_strdup_printf( "%c-%i.%i.%i-%s-%s", MXIT_CP_DISTCODE, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_MICRO_VERSION, MXIT_CP_ARCH, MXIT_CP_PLATFORM );
+
 	/* convert the packet to a byte stream */
 	datalen = snprintf( data, sizeof( data ),
 								"ms=%s%c%s%c%i%c"			/* "ms"=password\1version\1getContacts\1 */
 								"%s%c%s%c%i%c"				/* capabilities\1dc\1features\1 */
 								"%s%c%s%c"					/* dialingcode\1locale\1 */
 								"%i%c%i%c%i",				/* maxReplyLen\1protocolVer\1lastRosterUpdate */
-								session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, 1, CP_FLD_TERM,
-								MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM,
+								session->encpwd, CP_FLD_TERM, clientVersion, CP_FLD_TERM, 1, CP_FLD_TERM,
+								MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, features, CP_FLD_TERM,
 								session->dialcode, CP_FLD_TERM, locale, CP_FLD_TERM,
 								CP_MAX_FILESIZE, CP_FLD_TERM, MXIT_CP_PROTO_VESION, CP_FLD_TERM, 0
 	);
@@ -689,6 +771,8 @@
 
 	/* queue packet for transmission */
 	mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN );
+
+	g_free( clientVersion );
 }
 
 
@@ -732,7 +816,7 @@
  *  @param session		The MXit session object
  *  @param username		Username who's profile is being requested (NULL = our own)
  *  @param nr_attribs	Number of attributes being requested
- *  @param attributes	The names of the attributes
+ *  @param attribute	The names of the attributes
  */
 void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] )
 {
@@ -742,7 +826,8 @@
 
 	datalen = snprintf( data, sizeof( data ),
 								"ms=%s%c%i",		/* "ms="mxitid\1nr_attributes */
-								(username ? username : ""), CP_FLD_TERM, nr_attrib);
+								( username ? username : "" ), CP_FLD_TERM, nr_attrib
+	);
 
 	/* add attributes */
 	for ( i = 0; i < nr_attrib; i++ )
@@ -790,6 +875,63 @@
 
 
 /*------------------------------------------------------------------------
+ * Send packet to request list of suggested friends.
+ *
+ *  @param session		The MXit session object
+ *  @param max			Maximum number of results to return
+ *  @param nr_attribs	Number of attributes being requested
+ *  @param attribute	The names of the attributes  
+ */
+void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] )
+{
+	char			data[CP_MAX_PACKET];
+	int				datalen;
+	unsigned int	i;
+
+	/* convert the packet to a byte stream */
+	datalen = snprintf( data, sizeof( data ),
+								"ms=%i%c%s%c%i%c%i",		/* inputType \1 input \ 1 maxSuggestions \1 numAttributes \1 name0 ... \1 nameN */
+								CP_SUGGEST_FRIENDS, CP_FLD_TERM, "", CP_FLD_TERM, max, CP_FLD_TERM, nr_attrib );
+
+	/* add attributes */
+	for ( i = 0; i < nr_attrib; i++ )
+		datalen += sprintf(	data + datalen, "%c%s", CP_FLD_TERM, attribute[i] );
+
+	/* queue packet for transmission */
+	mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS );
+}
+
+
+/*------------------------------------------------------------------------
+ * Send packet to perform a search for users.
+ *
+ *  @param session		The MXit session object
+ *  @param max			Maximum number of results to return
+ *  @param text			The search text
+ *  @param nr_attribs	Number of attributes being requested
+ *  @param attribute	The names of the attributes
+ */
+void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] )
+{
+	char			data[CP_MAX_PACKET];
+	int				datalen;
+	unsigned int	i;
+
+	/* convert the packet to a byte stream */
+	datalen = snprintf( data, sizeof( data ),
+								"ms=%i%c%s%c%i%c%i",		/* inputType \1 input \ 1 maxSuggestions \1 numAttributes \1 name0 ... \1 nameN */
+								CP_SUGGEST_SEARCH, CP_FLD_TERM, text, CP_FLD_TERM, max, CP_FLD_TERM, nr_attrib );
+
+	/* add attributes */
+	for ( i = 0; i < nr_attrib; i++ )
+		datalen += sprintf(	data + datalen, "%c%s", CP_FLD_TERM, attribute[i] );
+
+	/* queue packet for transmission */
+	mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS );
+}
+
+
+/*------------------------------------------------------------------------
  * Send a presence update packet to the MXit server.
  *
  *  @param session		The MXit session object
@@ -1039,7 +1181,6 @@
  *  @param nr_usernames	Number of users being invited
  *  @param usernames	The usernames of the users being invited
  */
-
 void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] )
 {
 	char		data[CP_MAX_PACKET];
@@ -1271,7 +1412,7 @@
 	/* map chunk header over data buffer */
 	chunk = &data[datalen];
 
-	size = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId, MXIT_AVATAR_SIZE );
+	size = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId );
 	if ( size < 0 ) {
 		purple_debug_error( MXIT_PLUGIN_ID, "Error creating get avatar chunk (%i)\n", size );
 		return;
@@ -1322,6 +1463,10 @@
 	if ( records[1]->fcount >= 9 )
 		session->uid = g_strdup( records[1]->fields[8]->data );
 
+	/* extract VoIP server (from protocol 6.2) */
+	if ( records[1]->fcount >= 11 )
+		g_strlcpy( session->voip_server, records[1]->fields[10]->data, sizeof( session->voip_server ) );
+
 	/* display the current splash-screen */
 	if ( splash_popup_enabled( session ) )
 		splash_display( session );
@@ -1567,13 +1712,13 @@
  */
 static void mxit_parse_cmd_presence( struct MXitSession* session, struct record** records, int rcount )
 {
-	struct record*		rec;
 	int					i;
 
 	purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount );
 
 	for ( i = 0; i < rcount; i++ ) {
-		rec = records[i];
+		struct record*	rec		= records[i];
+		int				flags	= 0;
 
 		if ( rec->fcount < 6 ) {
 			purple_debug_error( MXIT_PLUGIN_ID, "BAD PRESENCE RECORD! %i fields\n", rec->fcount );
@@ -1582,12 +1727,15 @@
 
 		/*
 		 * The format of the record is:
-		 * contactAddressN\1presenceN\1moodN\1customMoodN\1statusMsgN\1avatarIdN
+		 * contactAddressN \1 presenceN \1 moodN \1 customMoodN \1 statusMsgN \1 avatarIdN [ \1 flagsN ]
 		 */
 		mxit_strip_domain( rec->fields[0]->data );		/* contactAddress */
 
+		if ( rec->fcount >= 7 )		/* flags field is included */
+			flags = atoi( rec->fields[6]->data );
+
 		mxit_update_buddy_presence( session, rec->fields[0]->data, atoi( rec->fields[1]->data ), atoi( rec->fields[2]->data ),
-				rec->fields[3]->data, rec->fields[4]->data );
+				rec->fields[3]->data, rec->fields[4]->data, flags );
 		mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data );
 	}
 }
@@ -1908,7 +2056,7 @@
 {
 	/* ignore ping/poll packets */
 	if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) )
-		session->last_rx = time( NULL );
+		session->last_rx = mxit_now_milli();
 
 	/*
 	 * when we pass the packet records to the next level for parsing
--- a/libpurple/protocols/mxit/protocol.h	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/protocol.h	Tue Mar 08 02:27:47 2011 +0000
@@ -75,6 +75,8 @@
 #define		MXIT_CF_NO_AVATARS		0x200000
 #define		MXIT_CF_GAMING			0x400000
 #define		MXIT_CF_GAMING_UPDATE	0x800000
+#define		MXIT_CF_VOICE			0x1000000
+#define		MXIT_CF_VIDEO			0x2000000
 
 /* Client features supported by this implementation */
 #define		MXIT_CP_FEATURES		( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 )
@@ -82,14 +84,13 @@
 
 #define		MXIT_PING_INTERVAL		( 5 * 60 )				/* ping the server after X seconds of being idle (5 minutes) */
 #define		MXIT_ACK_TIMEOUT		( 30 )					/* timeout after waiting X seconds for an ack from the server (30 seconds) */
+#define		MXIT_TX_DELAY			( 100 )					/* delay between sending consecutive packets (100 ms) */
 
 /* MXit client version */
-#define		MXIT_CP_DISTCODE		"P"						/* client distribution code (magic, do not touch!) */
-#define		MXIT_CP_RELEASE			"5.9.0"					/* client version */
+#define		MXIT_CP_DISTCODE		'P'						/* client distribution code (magic, do not touch!) */
 #define		MXIT_CP_ARCH			"Y"						/* client architecture series (Y not for Yoda but for PC-client) */
 #define		MXIT_CLIENT_ID			"LP"					/* client ID as specified by MXit */
 #define		MXIT_CP_PLATFORM		"PURPLE"				/* client platform */
-#define		MXIT_CP_VERSION			MXIT_CP_DISTCODE"-"MXIT_CP_RELEASE"-"MXIT_CP_ARCH"-"MXIT_CP_PLATFORM
 #define		MXIT_CP_PROTO_VESION	60						/* client protocol version */
 
 /* set operating system name */
@@ -107,7 +108,7 @@
 #define		MXIT_CP_CAP				"utf8=true;cid="MXIT_CLIENT_ID
 
 /* Client settings */
-#define		MAX_QUEUE_SIZE			( 1 << 4 )				/* tx queue size (16 packets) */
+#define		MAX_QUEUE_SIZE			( 1 << 5 )				/* tx queue size (32 packets) */
 #define		MXIT_POPUP_WIN_NAME		"MXit Notification"		/* popup window name */
 #define		MXIT_MAX_ATTRIBS		10						/* maximum profile attributes supported */
 #define		MXIT_DEFAULT_LOCALE		"en"					/* default locale setting */
@@ -125,6 +126,7 @@
 #define		CP_CMD_TX_MSG			0x000A					/* (10) send new message */
 #define		CP_CMD_REGISTER			0x000B					/* (11) register */
 //#define	CP_CMD_PROFILE_SET		0x000C					/* (12) set profile (DEPRECATED see CP_CMD_EXTPROFILE_SET) */
+#define		CP_CMD_SUGGESTCONTACTS	0x000D					/* (13) suggest contacts */
 #define		CP_CMD_POLL				0x0011					/* (17) poll the HTTP server for an update */
 //#define	CP_CMD_PROFILE_GET		0x001A					/* (26) get profile (DEPRECATED see CP_CMD_EXTPROFILE_GET) */
 #define		CP_CMD_MEDIA			0x001B					/* (27) get multimedia message */
@@ -202,6 +204,12 @@
 /* profile flags */
 #define		CP_PROF_DOBLOCKED		0x40					/* date-of-birth cannot be changed */
 
+/* suggestion types */
+#define		CP_SUGGEST_ADDRESSBOOK	0						/* address book search */
+#define		CP_SUGGEST_FRIENDS		1						/* suggested friends */
+#define		CP_SUGGEST_SEARCH		2						/* free-text search */
+#define		CP_SUGGEST_MXITID		3						/* MXitId search */
+
 /* define this to enable protocol debugging (very verbose logging) */
 #define		DEBUG_PROTOCOL
 
@@ -277,7 +285,8 @@
 gboolean find_active_chat( const GList* chats, const char* who );
 
 void mxit_cb_rx( gpointer data, gint source, PurpleInputCondition cond );
-gboolean mxit_manage_queue( gpointer user_data );
+gboolean mxit_manage_queue_slow( gpointer user_data );
+gboolean mxit_manage_queue_fast( gpointer user_data );
 gboolean mxit_manage_polling( gpointer user_data );
 
 void mxit_send_register( struct MXitSession* session );
@@ -293,6 +302,9 @@
 void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes );
 void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] );
 
+void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] );
+void mxit_send_suggest_search( struct MXitSession* session, int max, const char* text, unsigned int nr_attrib, const char* attribute[] );
+
 void mxit_send_invite( struct MXitSession* session, const char* username, const char* alias, const char* groupname );
 void mxit_send_remove( struct MXitSession* session, const char* username );
 void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias );
@@ -314,6 +326,7 @@
 int mxit_parse_packet( struct MXitSession* session );
 void dump_bytes( struct MXitSession* session, const char* buf, int len );
 void mxit_close_connection( struct MXitSession* session );
+gint64 mxit_now_milli( void );
 
 
 #endif		/* _MXIT_PROTO_H_ */
--- a/libpurple/protocols/mxit/roster.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/roster.c	Tue Mar 08 02:27:47 2011 +0000
@@ -443,8 +443,9 @@
  *  @param mood			The new mood for the contact
  *  @param customMood	The custom mood identifier
  *  @param statusMsg	This is the contact's status message
+ *  @param flags		The contact's presence flags.
  */
-void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg )
+void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg, int flags )
 {
 	PurpleBuddy*		buddy	= NULL;
 	struct contact*		contact	= NULL;
@@ -470,6 +471,7 @@
 
 	contact->presence = presence;
 	contact->mood = mood;
+	contact->capabilities = flags;
 
 	/* validate mood */
 	if (( contact->mood < MXIT_MOOD_NONE ) || ( contact->mood > MXIT_MOOD_STRESSED ))
--- a/libpurple/protocols/mxit/roster.h	Tue Mar 08 02:27:25 2011 +0000
+++ b/libpurple/protocols/mxit/roster.h	Tue Mar 08 02:27:47 2011 +0000
@@ -79,6 +79,11 @@
 #define		MXIT_CFLAG_FOCUS_SEND_BLANK	0x20000
 
 
+/* MXit presence flags */
+#define		MXIT_PFLAG_VOICE			0x1
+#define		MXIT_PFLAG_VIDEO			0x2
+
+
 /* Subscription types */
 #define		MXIT_SUBTYPE_BOTH			'B'
 #define		MXIT_SUBTYPE_PENDING		'P'
@@ -108,6 +113,7 @@
 	short		mood;								/* contact current mood */
 	int			flags;								/* contact flags */
 	short		presence;							/* presence state */
+	int			capabilities;						/* contact capabilities */
 	short		subtype;							/* subscription type */
 
 	char*		msg;								/* invite/rejection message */
@@ -129,7 +135,7 @@
 
 /* MXit Protocol callbacks */
 void mxit_update_contact( struct MXitSession* session, struct contact* contact );
-void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg );
+void mxit_update_buddy_presence( struct MXitSession* session, const char* username, short presence, short mood, const char* customMood, const char* statusMsg, int flags );
 void mxit_update_buddy_avatar( struct MXitSession* session, const char* username, const char* avatarId );
 void mxit_new_subscription( struct MXitSession* session, struct contact* contact );
 void mxit_update_blist( struct MXitSession* session );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/mxit/voicevideo.c	Tue Mar 08 02:27:47 2011 +0000
@@ -0,0 +1,154 @@
+/*
+ *					MXit Protocol libPurple Plugin
+ *
+ *						-- voice & video --
+ *
+ *				Andrew Victor	<libpurple@mxit.com>
+ *
+ *			(C) Copyright 2010	MXit Lifestyle (Pty) Ltd.
+ *				<http://www.mxitlifestyle.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#include "purple.h"
+#include "mxit.h"
+#include "roster.h"
+#include "voicevideo.h"
+
+#if defined(USE_VV) && defined(MXIT_DEV_VV)
+
+#warning "MXit VV support enabled."
+
+/*------------------------------------------------------------------------
+ * Does this client support Voice?
+ */
+gboolean mxit_audio_enabled(void)
+{
+    PurpleMediaManager *manager = purple_media_manager_get();
+    PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager);
+
+    return (caps & PURPLE_MEDIA_CAPS_AUDIO);
+}
+
+/*------------------------------------------------------------------------
+ * Does this client support Voice and Video?
+ */
+gboolean mxit_video_enabled(void)
+{
+    PurpleMediaManager *manager = purple_media_manager_get();
+    PurpleMediaCaps caps = purple_media_manager_get_ui_caps(manager);
+
+    return (caps & PURPLE_MEDIA_CAPS_VIDEO);
+}
+
+/*------------------------------------------------------------------------
+ * Return the list of media capabilities this contact supports.
+ *
+ *  @param account		The MXit account object
+ *  @param who			The username of the contact.
+ *  @return				The media capabilities supported
+ */
+PurpleMediaCaps mxit_media_caps(PurpleAccount *account, const char *who)
+{
+	struct MXitSession*	session	= purple_account_get_connection(account)->proto_data;
+	PurpleBuddy*		buddy;
+	struct contact*		contact;
+	PurpleMediaCaps		capa	= PURPLE_MEDIA_CAPS_NONE;
+
+	purple_debug_info(MXIT_PLUGIN_ID, "mxit_media_caps: buddy '%s'\n", who);
+
+	/* We need to have a voice/video server */
+	if (strlen(session->voip_server) == 0)
+		return PURPLE_MEDIA_CAPS_NONE;
+
+	/* find the buddy information for this contact (reference: "libpurple/blist.h") */
+	buddy = purple_find_buddy(account, who);
+	if (!buddy) {
+		purple_debug_warning(MXIT_PLUGIN_ID, "mxit_media_caps: unable to find the buddy '%s'\n", who);
+		return PURPLE_MEDIA_CAPS_NONE;
+	}
+
+	contact = purple_buddy_get_protocol_data(buddy);
+	if (!contact)
+		return PURPLE_MEDIA_CAPS_NONE;
+
+	/* can only communicate with MXit users */
+	if (contact->type != MXIT_TYPE_MXIT)
+		return PURPLE_MEDIA_CAPS_NONE;
+
+	/* and only with contacts in the 'Both' subscription state */
+	if (contact->subtype != MXIT_SUBTYPE_BOTH)
+		return PURPLE_MEDIA_CAPS_NONE;
+
+	/* and only when they're online */
+	if (contact->presence == MXIT_PRESENCE_OFFLINE)
+		return MXIT_PRESENCE_OFFLINE;
+
+	/* they support voice-only */
+	if (contact->capabilities & MXIT_PFLAG_VOICE)
+		capa |= PURPLE_MEDIA_CAPS_AUDIO;
+
+	/* they support voice-and-video */
+	if (contact->capabilities & MXIT_PFLAG_VIDEO)
+		capa |= (PURPLE_MEDIA_CAPS_AUDIO | PURPLE_MEDIA_CAPS_VIDEO | PURPLE_MEDIA_CAPS_AUDIO_VIDEO);
+
+	return capa;
+}
+
+
+/*------------------------------------------------------------------------
+ * Initiate a voice/video session with a contact.
+ *
+ *  @param account		The MXit account object
+ *  @param who			The username of the contact.
+ *  @param type			The type of media session to initiate
+ *  @return				TRUE if session was initiated
+ */
+gboolean mxit_media_initiate(PurpleAccount *account, const char *who, PurpleMediaSessionType type)
+{
+	purple_debug_info(MXIT_PLUGIN_ID, "mxit_media_initiate: buddy '%s'\n", who);
+
+	return FALSE;
+}
+
+#else
+
+/*
+ * Voice and Video not supported.
+ */
+
+gboolean mxit_audio_enabled(void)
+{
+    return FALSE;
+}
+
+gboolean mxit_video_enabled(void)
+{
+	return FALSE;
+}
+
+PurpleMediaCaps mxit_media_caps(PurpleAccount *account, const char *who)
+{
+	return PURPLE_MEDIA_CAPS_NONE;
+}
+
+gboolean mxit_media_initiate(PurpleAccount *account, const char *who, PurpleMediaSessionType type)
+{
+	return FALSE;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/mxit/voicevideo.h	Tue Mar 08 02:27:47 2011 +0000
@@ -0,0 +1,41 @@
+/*
+ *					MXit Protocol libPurple Plugin
+ *
+ *						-- voice & video --
+ *
+ *				Andrew Victor	<libpurple@mxit.com>
+ *
+ *			(C) Copyright 2010	MXit Lifestyle (Pty) Ltd.
+ *				<http://www.mxitlifestyle.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+
+#ifndef		_MXIT_VOICEVICEO_H_
+#define		_MXIT_VOICEVIDEO_H_
+
+#include	"media.h"
+
+
+#undef		MXIT_DEV_VV
+
+
+gboolean mxit_audio_enabled(void);
+gboolean mxit_video_enabled(void);
+PurpleMediaCaps mxit_media_caps(PurpleAccount* account, const char* who);
+gboolean mxit_media_initiate(PurpleAccount* account, const char* who, PurpleMediaSessionType type);
+
+
+#endif		/* _MXIT_VOICEVIDEO_H_ */
--- a/pidgin/Makefile.am	Tue Mar 08 02:27:25 2011 +0000
+++ b/pidgin/Makefile.am	Tue Mar 08 02:27:47 2011 +0000
@@ -156,7 +156,6 @@
 	$(SM_LIBS) \
 	$(INTLLIBS) \
 	$(GTKSPELL_LIBS) \
-	$(STARTUP_NOTIFICATION_LIBS) \
 	$(LIBXML_LIBS) \
 	$(GTK_LIBS) \
 	$(top_builddir)/libpurple/libpurple.la
@@ -180,7 +179,6 @@
 	$(GTK_CFLAGS) \
 	$(DBUS_CFLAGS) \
 	$(GTKSPELL_CFLAGS) \
-	$(STARTUP_NOTIFICATION_CFLAGS) \
 	$(LIBXML_CFLAGS) \
 	$(INTGG_CFLAGS)
 endif  # ENABLE_GTK
--- a/pidgin/gtkblist.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/pidgin/gtkblist.c	Tue Mar 08 02:27:47 2011 +0000
@@ -7743,7 +7743,7 @@
 		gtk_tree_store_append(gtkblist->treemodel, iter, &groupiter);
 		return;
 	} else {
-		sort_method_none(node, blist, groupiter, cur, iter);
+		sort_method_alphabetical(node, blist, groupiter, cur, iter);
 		return;
 	}
 
--- a/pidgin/gtkdialogs.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/pidgin/gtkdialogs.c	Tue Mar 08 02:27:47 2011 +0000
@@ -650,14 +650,6 @@
 else
 	g_string_append(str, "    <b>Perl:</b> Disabled<br/>");
 
-#ifndef _WIN32
-#ifdef HAVE_STARTUP_NOTIFICATION
-	g_string_append(str, "    <b>Startup Notification:</b> Enabled<br/>");
-#else
-	g_string_append(str, "    <b>Startup Notification:</b> Disabled<br/>");
-#endif
-#endif
-
 if (purple_plugins_find_with_id("core-tcl") != NULL) {
 	g_string_append(str, "    <b>Tcl:</b> Enabled<br/>");
 #ifdef HAVE_TK
--- a/pidgin/gtkdocklet-gtk.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/pidgin/gtkdocklet-gtk.c	Tue Mar 08 02:27:47 2011 +0000
@@ -47,19 +47,37 @@
 static gboolean
 docklet_gtk_embed_timeout_cb(gpointer data)
 {
-	/* The docklet was not embedded within the timeout.
-	 * Remove it as a visibility manager, but leave the plugin
-	 * loaded so that it can embed automatically if/when a notification
-	 * area becomes available.
-	 */
-	purple_debug_info("docklet", "failed to embed within timeout\n");
-	pidgin_docklet_remove();
-	purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+#if !GTK_CHECK_VERSION(2,12,0)
+	if (gtk_status_icon_is_embedded(docklet)) {
+		/* Older GTK+ (<2.12) don't implement the embedded signal, but the
+		   information is still accessable through the above function. */
+		purple_debug_info("docklet", "embedded\n");
 
+		pidgin_docklet_embedded();
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", TRUE);
+	}
+	else
+#endif
+	{
+		/* The docklet was not embedded within the timeout.
+		 * Remove it as a visibility manager, but leave the plugin
+		 * loaded so that it can embed automatically if/when a notification
+		 * area becomes available.
+		 */
+		purple_debug_info("docklet", "failed to embed within timeout\n");
+		pidgin_docklet_remove();
+		purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded", FALSE);
+	}
+
+#if GTK_CHECK_VERSION(2,12,0)
 	embed_timeout = 0;
 	return FALSE;
+#else
+	return TRUE;
+#endif
 }
 
+#if GTK_CHECK_VERSION(2,12,0)
 static gboolean
 docklet_gtk_embedded_cb(GtkWidget *widget, gpointer data)
 {
@@ -82,6 +100,7 @@
 
 	return TRUE;
 }
+#endif
 
 static void
 docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data)
@@ -206,7 +225,9 @@
 
 	g_signal_connect(G_OBJECT(docklet), "activate", G_CALLBACK(docklet_gtk_status_activated_cb), NULL);
 	g_signal_connect(G_OBJECT(docklet), "popup-menu", G_CALLBACK(docklet_gtk_status_clicked_cb), NULL);
+#if GTK_CHECK_VERSION(2,12,0)
 	g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL);
+#endif
 	g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL);
 
 	gtk_status_icon_set_visible(docklet, TRUE);
@@ -226,11 +247,15 @@
 	 */
 	if (!recreate) {
 		pidgin_docklet_embedded();
+#if GTK_CHECK_VERSION(2,12,0)
 		if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/gtk/embedded")) {
 			embed_timeout = purple_timeout_add_seconds(LONG_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
 		} else {
 			embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
 		}
+#else
+		embed_timeout = purple_timeout_add_seconds(SHORT_EMBED_TIMEOUT, docklet_gtk_embed_timeout_cb, NULL);
+#endif
 	}
 
 	purple_debug_info("docklet", "GTK+ created\n");
--- a/pidgin/gtkmain.c	Tue Mar 08 02:27:25 2011 +0000
+++ b/pidgin/gtkmain.c	Tue Mar 08 02:27:47 2011 +0000
@@ -76,18 +76,6 @@
 
 #include <getopt.h>
 
-#ifdef HAVE_STARTUP_NOTIFICATION
-# define SN_API_NOT_YET_FROZEN
-# include <libsn/sn-launchee.h>
-# include <gdk/gdkx.h>
-#endif
-
-
-
-#ifdef HAVE_STARTUP_NOTIFICATION
-static SnLauncheeContext *sn_context = NULL;
-static SnDisplay *sn_display = NULL;
-#endif
 
 #ifdef HAVE_SIGNAL_H
 
@@ -472,42 +460,6 @@
 	g_free(text);
 }
 
-#ifdef HAVE_STARTUP_NOTIFICATION
-static void
-sn_error_trap_push(SnDisplay *display, Display *xdisplay)
-{
-	gdk_error_trap_push();
-}
-
-static void
-sn_error_trap_pop(SnDisplay *display, Display *xdisplay)
-{
-	gdk_error_trap_pop();
-}
-
-static void
-startup_notification_complete(void)
-{
-	Display *xdisplay;
-
-	xdisplay = GDK_DISPLAY();
-	sn_display = sn_display_new(xdisplay,
-								sn_error_trap_push,
-								sn_error_trap_pop);
-	sn_context =
-		sn_launchee_context_new_from_environment(sn_display,
-												 DefaultScreen(xdisplay));
-
-	if (sn_context != NULL)
-	{
-		sn_launchee_context_complete(sn_context);
-		sn_launchee_context_unref(sn_context);
-
-		sn_display_unref(sn_display);
-	}
-}
-#endif /* HAVE_STARTUP_NOTIFICATION */
-
 /* FUCKING GET ME A TOWEL! */
 #ifdef _WIN32
 /* suppress gcc "no previous prototype" warning */
@@ -876,6 +828,7 @@
 		dbus_connection_send_with_reply_and_block(conn, message, -1, NULL);
 		dbus_message_unref(message);
 #endif
+		gdk_notify_startup_complete();
 		purple_core_quit();
 		g_printerr(_("Exiting because another libpurple client is already running.\n"));
 #ifdef HAVE_SIGNAL_H
@@ -967,9 +920,10 @@
 		g_list_free(active_accounts);
 	}
 
-#ifdef HAVE_STARTUP_NOTIFICATION
-	startup_notification_complete();
-#endif
+	/* GTK clears the notification for us when opening the first window,
+	 * but we may have launched with only a status icon, so clear the it
+	 * just in case. */
+	gdk_notify_startup_complete();
 
 #ifdef _WIN32
 	winpidgin_post_init();
--- a/po/de.po	Tue Mar 08 02:27:25 2011 +0000
+++ b/po/de.po	Tue Mar 08 02:27:47 2011 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-02-06 14:22+0100\n"
-"PO-Revision-Date: 2011-02-06 14:22+0100\n"
+"POT-Creation-Date: 2011-03-06 12:25+0100\n"
+"PO-Revision-Date: 2011-03-06 12:25+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: German <de@li.org>\n"
 "Language: de\n"
@@ -3239,9 +3239,7 @@
 msgstr "UIN"
 
 #. first name
-#. purple_notify_user_info_add_pair( info, _( "Hidden Number" ), profile->hidden ? _( "Yes" ) : _( "No" ) );
 #. optional information
-#. purple_notify_user_info_add_pair( info, _( "Title" ), profile->title );
 msgid "First Name"
 msgstr "Vorname"
 
@@ -4010,7 +4008,6 @@
 msgid "Postal Code"
 msgstr "Postleitzahl"
 
-#. purple_notify_user_info_add_pair( info, _( "Email" ), profile->email );
 msgid "Country"
 msgstr "Land"
 
@@ -6148,6 +6145,9 @@
 msgid "Connecting..."
 msgstr "Verbinde..."
 
+msgid "The Display Name you entered is too short."
+msgstr "Der eingegebene Anzeigename ist zu kurz."
+
 msgid "The PIN you entered has an invalid length [7-10]."
 msgstr "Die eingegebene PIN hat eine ungültige Länge [7-10]."
 
@@ -6227,33 +6227,6 @@
 msgid "Retrieving User Information..."
 msgstr "Abrufen der Benutzerinformationen..."
 
-msgid "Loading menu..."
-msgstr "Lade das Menü..."
-
-msgid "Status Message"
-msgstr "Status-Nachricht"
-
-msgid "Rejection Message"
-msgstr "Ablehnungsnachricht"
-
-#. hidden number
-msgid "Hidden Number"
-msgstr "Versteckte Nummer"
-
-msgid "Your MXit ID..."
-msgstr "Ihre MXit-ID..."
-
-#. Configuration options
-#. WAP server (reference: "libpurple/accountopt.h")
-msgid "WAP Server"
-msgstr "WAP-Server"
-
-msgid "Connect via HTTP"
-msgstr "Über HTTP verbinden"
-
-msgid "Enable splash-screen popup"
-msgstr "Startbildschirm-Popup aktivieren"
-
 #. you were kicked
 msgid "You have been kicked from this MultiMX."
 msgstr "Sie wurden von MultiMX hinausgeworfen."
@@ -6268,6 +6241,43 @@
 msgid "You have invited"
 msgstr "Sie haben eingeladen"
 
+msgid "Loading menu..."
+msgstr "Lade das Menü..."
+
+msgid "Status Message"
+msgstr "Status-Nachricht"
+
+msgid "Rejection Message"
+msgstr "Ablehnungsnachricht"
+
+#. hidden number
+msgid "Hidden Number"
+msgstr "Versteckte Nummer"
+
+msgid "No profile available"
+msgstr "Kein Profil verfügbar"
+
+msgid "This contact does not have a profile."
+msgstr "Dieser Kontakt hat kein Profil."
+
+msgid "Your MXit ID..."
+msgstr "Ihre MXit-ID..."
+
+#. contact is in Deleted, Rejected or None state
+msgid "Re-Invite"
+msgstr "Erneut einladen"
+
+#. Configuration options
+#. WAP server (reference: "libpurple/accountopt.h")
+msgid "WAP Server"
+msgstr "WAP-Server"
+
+msgid "Connect via HTTP"
+msgstr "Über HTTP verbinden"
+
+msgid "Enable splash-screen popup"
+msgstr "Startbildschirm-Popup aktivieren"
+
 msgid "Last Online"
 msgstr "Zuletzt online"
 
@@ -7571,6 +7581,14 @@
 msgid "You have been disconnected from chat room %s."
 msgstr "Die Verbindung zum Raum %s wurde unterbrochen."
 
+msgid "The new formatting is invalid."
+msgstr "Die neue Formatierung ist ungültig."
+
+msgid "Username formatting can change only capitalization and whitespace."
+msgstr ""
+"Benutzernamen-Formatierung kann nur die Groß-/Kleinschreibung und "
+"Leerzeichen ändern."
+
 msgid "Pop-Up Message"
 msgstr "Pop-Up Nachricht"
 
@@ -7846,14 +7864,6 @@
 msgid "ICQ Privacy Options"
 msgstr "ICQ Privatsphärenoptionen"
 
-msgid "The new formatting is invalid."
-msgstr "Die neue Formatierung ist ungültig."
-
-msgid "Username formatting can change only capitalization and whitespace."
-msgstr ""
-"Benutzernamen-Formatierung kann nur die Groß-/Kleinschreibung und "
-"Leerzeichen ändern."
-
 msgid "Change Address To:"
 msgstr "Ändere die Adresse zu:"
 
@@ -7961,75 +7971,6 @@
 "ist notwendig für IM-Bilder. Da Ihre IP-Adresse verwendet wird, kann dies "
 "eine Verletzung der Privatsphäre bedeuten."
 
-msgid "Invalid SNAC"
-msgstr "Ungültiger SNAC"
-
-msgid "Server rate limit exceeded"
-msgstr "Server-Datenrate überschritten"
-
-msgid "Client rate limit exceeded"
-msgstr "Client-Datenrate überschritten"
-
-msgid "Service unavailable"
-msgstr "Dienst nicht verfügbar"
-
-msgid "Service not defined"
-msgstr "Dienst nicht definiert"
-
-msgid "Obsolete SNAC"
-msgstr "Obsoleter SNAC"
-
-msgid "Not supported by host"
-msgstr "Nicht unterstützt vom Host"
-
-msgid "Not supported by client"
-msgstr "Nicht unterstützt vom Client"
-
-msgid "Refused by client"
-msgstr "Abgelehnt vom Client"
-
-msgid "Reply too big"
-msgstr "Antwort zu groß"
-
-msgid "Responses lost"
-msgstr "Antworten verloren"
-
-msgid "Request denied"
-msgstr "Anfrage verweigert"
-
-msgid "Busted SNAC payload"
-msgstr "Ruinierte SNAC-Daten"
-
-msgid "Insufficient rights"
-msgstr "Ungenügende Rechte"
-
-msgid "In local permit/deny"
-msgstr "In lokaler erlaubt/verboten-Liste"
-
-msgid "Warning level too high (sender)"
-msgstr "Warnstufe zu hoch (Absender)"
-
-msgid "Warning level too high (receiver)"
-msgstr "Warnstufe zu hoch (Empfänger)"
-
-msgid "User temporarily unavailable"
-msgstr "Benutzer ist temporär nicht verfügbar"
-
-msgid "No match"
-msgstr "Keine Übereinstimmung"
-
-msgid "List overflow"
-msgstr "Listenüberlauf"
-
-msgid "Request ambiguous"
-msgstr "Anfrage ist nicht eindeutig"
-
-msgid "Queue full"
-msgstr "Warteschlange voll"
-
-msgid "Not while on AOL"
-msgstr "Nicht solange bei AOL angemeldet"
-
 #. Label
 msgid "Buddy Icon"
 msgstr "Buddy-Icon"
@@ -8148,6 +8089,75 @@
 msgid "Capabilities"
 msgstr "Fähigkeiten"
 
+msgid "Invalid SNAC"
+msgstr "Ungültiger SNAC"
+
+msgid "Server rate limit exceeded"
+msgstr "Server-Datenrate überschritten"
+
+msgid "Client rate limit exceeded"
+msgstr "Client-Datenrate überschritten"
+
+msgid "Service unavailable"
+msgstr "Dienst nicht verfügbar"
+
+msgid "Service not defined"
+msgstr "Dienst nicht definiert"
+
+msgid "Obsolete SNAC"
+msgstr "Obsoleter SNAC"
+
+msgid "Not supported by host"
+msgstr "Nicht unterstützt vom Host"
+
+msgid "Not supported by client"
+msgstr "Nicht unterstützt vom Client"
+
+msgid "Refused by client"
+msgstr "Abgelehnt vom Client"
+
+msgid "Reply too big"
+msgstr "Antwort zu groß"
+
+msgid "Responses lost"
+msgstr "Antworten verloren"
+
+msgid "Request denied"
+msgstr "Anfrage verweigert"
+
+msgid "Busted SNAC payload"
+msgstr "Ruinierte SNAC-Daten"
+
+msgid "Insufficient rights"
+msgstr "Ungenügende Rechte"
+
+msgid "In local permit/deny"
+msgstr "In lokaler erlaubt/verboten-Liste"
+
+msgid "Warning level too high (sender)"
+msgstr "Warnstufe zu hoch (Absender)"
+
+msgid "Warning level too high (receiver)"
+msgstr "Warnstufe zu hoch (Empfänger)"
+
+msgid "User temporarily unavailable"
+msgstr "Benutzer ist temporär nicht verfügbar"
+
+msgid "No match"
+msgstr "Keine Übereinstimmung"
+
+msgid "List overflow"
+msgstr "Listenüberlauf"
+
+msgid "Request ambiguous"
+msgstr "Anfrage ist nicht eindeutig"
+
+msgid "Queue full"
+msgstr "Warteschlange voll"
+
+msgid "Not while on AOL"
+msgstr "Nicht solange bei AOL angemeldet"
+
 #. Translators: This string is a menu option that, if selected, will cause
 #. you to appear online to the chosen user even when your status is set to
 #. Invisible.