changeset 31626:11e629e6c23d

propagate from branch 'im.pidgin.pidgin' (head 075c2902b90abb6349a6b689e26fa0ecf720ca04) to branch 'im.pidgin.pidgin.mxit' (head 3d58b1f843fc2aebf9411756956d07ae96951623)
author andrew.victor@mxit.com
date Tue, 26 Apr 2011 13:43:24 +0000
parents b671728e6ee9 (current diff) 000aac6e42fe (diff)
children 64da22357346
files ChangeLog
diffstat 16 files changed, 1070 insertions(+), 269 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Apr 19 05:05:25 2011 +0000
+++ b/ChangeLog	Tue Apr 26 13:43:24 2011 +0000
@@ -64,6 +64,18 @@
 	IRC:
 	* Add "authserv" service command.  (tomos) (#13337)
 
+	MXit:
+	* Support for an Invite Message when adding a buddy.
+	* Fixed bug in splitting-up of messages that contain a lot of links.
+	* Fixed crash caused by timer not being disabled on disconnect.
+	  (introduced in 2.7.11)
+	* Clearing of the conversation window now works.
+	* When receiving an invite you can display the sender's profile
+	  information, avatar image, invite message.
+	* The Change PIN option was moved into separate action.
+	* New profile attributes added and shown.
+	* Update to protocol v6.3.
+
 	XMPP:
 	* Remember the previously entered user directory when searching.
 	  (Keith Moyer) (#12451)
--- a/libpurple/protocols/mxit/actions.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/actions.c	Tue Apr 26 13:43:24 2011 +0000
@@ -41,48 +41,21 @@
  *  @param gc		The connection object
  *  @param fields	The fields from the request pop-up
  */
-static void mxit_cb_set_profile( PurpleConnection* gc, PurpleRequestFields* fields )
+static void mxit_profile_cb( PurpleConnection* gc, PurpleRequestFields* fields )
 {
 	struct MXitSession*		session	= (struct MXitSession*) gc->proto_data;
 	PurpleRequestField*		field	= NULL;
-	const char*				pin		= NULL;
-	const char*				pin2	= NULL;
 	const char*				name	= NULL;
 	const char*				bday	= NULL;
 	const char*				err		= NULL;
-	int						len;
-	int						i;
 
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_set_profile\n" );
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_profile_cb\n" );
 
 	if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
 		purple_debug_error( MXIT_PLUGIN_ID, "Unable to update profile; account offline.\n" );
 		return;
 	}
 
-	/* validate pin */
-	pin = purple_request_fields_get_string( fields, "pin" );
-	if ( !pin ) {
-		err = _( "The PIN you entered is invalid." );
-		goto out;
-	}
-	len = strlen( pin );
-	if ( ( len < 4 ) || ( len > 10 ) ) {
-		err = _( "The PIN you entered has an invalid length [4-10]." );
-		goto out;
-	}
-	for ( i = 0; i < len; i++ ) {
-		if ( !g_ascii_isdigit( pin[i] ) ) {
-			err = _( "The PIN is invalid. It should only consist of digits [0-9]." );
-			goto out;
-		}
-	}
-	pin2 = purple_request_fields_get_string( fields, "pin2" );
-	if ( ( !pin2 ) || ( strcmp( pin, pin2 ) != 0 ) ) {
-		err = _( "The two PINs you entered do not match." );
-		goto out;
-	}
-
 	/* validate name */
 	name = purple_request_fields_get_string( fields, "name" );
 	if ( ( !name ) || ( strlen( name ) < 3 ) ) {
@@ -104,12 +77,6 @@
 		char				attrib[512];
 		unsigned int		acount		= 0;
 
-		/* all good, so we can now update the profile */
-
-		/* update pin */
-		purple_account_set_password( session->acc, pin );
-		g_free( session->encpwd );
-		session->encpwd = mxit_encrypt_password( session );
 
 		/* update name */
 		g_strlcpy( profile->nickname, name, sizeof( profile->nickname ) );
@@ -117,12 +84,12 @@
 		g_string_append( attributes, attrib );
 		acount++;
 
-		/* update hidden */
-		field = purple_request_fields_get_field( fields, "hidden" );
-		profile->hidden = purple_request_field_bool_get_value( field );
-		g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_HIDENUMBER, CP_PROFILE_TYPE_BOOL, ( profile->hidden ) ? "1" : "0" );
-		g_string_append( attributes, attrib );
-		acount++;
+		/* force hidden if disabled */
+		if ( profile->hidden == FALSE ) {
+			g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_HIDENUMBER, CP_PROFILE_TYPE_BOOL, "1" );
+			g_string_append( attributes, attrib );
+			acount++;
+		}
 
 		/* update birthday */
 		g_strlcpy( profile->birthday, bday, sizeof( profile->birthday ) );
@@ -186,8 +153,43 @@
 		g_string_append( attributes, attrib );
 		acount++;
 
+		/* update about me */
+		name = purple_request_fields_get_string( fields, "aboutme" );
+		if ( !name )
+			profile->aboutme[0] = '\0';
+		else
+			g_strlcpy( profile->aboutme, name, sizeof( profile->aboutme ) );
+		g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_ABOUTME, CP_PROFILE_TYPE_UTF8, profile->aboutme );
+		g_string_append( attributes, attrib );
+		acount++;
+
+		/* update where am i */
+		name = purple_request_fields_get_string( fields, "whereami" );
+		if ( !name)
+			profile->whereami[0] = '\0';
+		else
+			g_strlcpy( profile->whereami, name, sizeof( profile->whereami ) );
+		g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_WHEREAMI, CP_PROFILE_TYPE_UTF8, profile->whereami );
+		g_string_append( attributes, attrib );
+		acount++;
+
+		/* update flags */
+		field = purple_request_fields_get_field( fields, "searchable" );
+		if ( purple_request_field_bool_get_value( field ) )		/* is searchable -> clear not-searchable flag */
+			profile->flags &= ~CP_PROF_NOT_SEARCHABLE;
+		else
+			profile->flags |= CP_PROF_NOT_SEARCHABLE;
+		field = purple_request_fields_get_field( fields, "suggestable" );
+		if ( purple_request_field_bool_get_value( field ) )		/* is suggestable -> clear not-suggestable flag */
+			profile->flags &= ~CP_PROF_NOT_SUGGESTABLE;
+		else
+			profile->flags |= CP_PROF_NOT_SUGGESTABLE;
+		g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%i", CP_PROFILE_FLAGS, CP_PROFILE_TYPE_LONG, profile->flags);
+		g_string_append( attributes, attrib );
+		acount++;
+
 		/* send the profile update to MXit */
-		mxit_send_extprofile_update( session, session->encpwd, acount, attributes->str );
+		mxit_send_extprofile_update( session, NULL, acount, attributes->str );
 		g_string_free( attributes, TRUE );
 	}
 	else {
@@ -202,17 +204,16 @@
  *
  *  @param action	The action object
  */
-static void mxit_cb_action_profile( PurplePluginAction* action )
+static void mxit_profile_action( PurplePluginAction* action )
 {
 	PurpleConnection*			gc		= (PurpleConnection*) action->context;
 	struct MXitSession*			session	= (struct MXitSession*) gc->proto_data;
 	struct MXitProfile*			profile	= session->profile;
 
 	PurpleRequestFields*		fields	= NULL;
-	PurpleRequestFieldGroup*	group	= NULL;
 	PurpleRequestField*			field	= NULL;
 
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_action_profile\n" );
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_profile_action\n" );
 
 	/* ensure that we actually have the user's profile information */
 	if ( !profile ) {
@@ -222,69 +223,174 @@
 	}
 
 	fields = purple_request_fields_new();
-	group = purple_request_field_group_new( NULL );
-	purple_request_fields_add_group( fields, group );
+
+	/* Public information - what other users can see */
+	{
+		PurpleRequestFieldGroup* public_group = purple_request_field_group_new( "Public information" );
+
+		/* display name */
+		field = purple_request_field_string_new( "name", _( "Display Name" ), profile->nickname, FALSE );
+		purple_request_field_group_add_field( public_group, field );
+
+		/* birthday */
+		field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE );
+		purple_request_field_group_add_field( public_group, field );
+		if ( profile->flags & CP_PROF_DOBLOCKED )
+			purple_request_field_string_set_editable( field, FALSE );
+
+		/* gender */
+		field = purple_request_field_choice_new( "male", _( "Gender" ), ( profile->male ) ? 1 : 0 );
+		purple_request_field_choice_add( field, _( "Female" ) );		/* 0 */
+		purple_request_field_choice_add( field, _( "Male" ) );			/* 1 */
+		purple_request_field_group_add_field( public_group, field );
+
+		/* first name */
+		field = purple_request_field_string_new( "firstname", _( "First Name" ), profile->firstname, FALSE );
+		purple_request_field_group_add_field( public_group, field );
+
+		/* last name */
+		field = purple_request_field_string_new( "lastname", _( "Last Name" ), profile->lastname, FALSE );
+		purple_request_field_group_add_field( public_group, field );
+
+		/* about me */
+		field = purple_request_field_string_new( "aboutme", _( "About Me" ), profile->aboutme, FALSE);
+		purple_request_field_group_add_field( public_group, field );
+
+		/* where I live */
+		field = purple_request_field_string_new( "whereami", _( "Where I Live" ), profile->whereami, FALSE);
+		purple_request_field_group_add_field( public_group, field );
+
+		purple_request_fields_add_group( fields, public_group );
+	}
+
+	/* Private information - what only MXit can see */
+	{
+		PurpleRequestFieldGroup* private_group = purple_request_field_group_new( "Private information" );
+
+		/* title */
+		field = purple_request_field_string_new( "title", _( "Title" ), profile->title, FALSE );
+		purple_request_field_group_add_field( private_group, field );
+
+		/* email */
+		field = purple_request_field_string_new( "email", _( "Email" ), profile->email, FALSE );
+		purple_request_field_group_add_field( private_group, field );
+
+		/* mobile number */
+		field = purple_request_field_string_new( "mobilenumber", _( "Mobile Number" ), profile->mobilenr, FALSE );
+		purple_request_field_group_add_field( private_group, field );
+
+		/* is searchable */
+		field = purple_request_field_bool_new( "searchable", _( "Can be searched" ), ( ( profile->flags & CP_PROF_NOT_SEARCHABLE ) == 0) );
+		purple_request_field_group_add_field( private_group, field );
+
+		/* is suggestable */
+		field = purple_request_field_bool_new( "suggestable", _( "Can be suggested" ), ( ( profile->flags & CP_PROF_NOT_SUGGESTABLE ) == 0 ) );
+		purple_request_field_group_add_field( private_group, field );
+
+		purple_request_fields_add_group( fields, private_group );
+	}
+
+	/* (reference: "libpurple/request.h") */
+	purple_request_fields( gc, _( "Profile" ), _( "Update your MXit Profile" ), NULL, fields, _( "Set" ),
+			G_CALLBACK( mxit_profile_cb ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc );
+}
+
 
-#if	0
-	/* UID (read-only) */
-	if ( session->uid ) {
-		field = purple_request_field_string_new( "mxitid", _( "Your UID" ), session->uid, FALSE );
-		purple_request_field_string_set_editable( field, FALSE );
-		purple_request_field_group_add_field( group, field );
+/*------------------------------------------------------------------------
+ * The user has selected to change their PIN.
+ *
+ *  @param gc		The connection object
+ *  @param fields	The fields from the request pop-up
+ */
+static void mxit_change_pin_cb( PurpleConnection* gc, PurpleRequestFields* fields )
+{
+	struct MXitSession*		session	= (struct MXitSession*) gc->proto_data;
+	const char*				pin		= NULL;
+	const char*				pin2	= NULL;
+	const char*				err		= NULL;
+	int						len;
+	int						i;
+
+	if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
+		purple_debug_error( MXIT_PLUGIN_ID, "Unable to update PIN; account offline.\n" );
+		return;
+	}
+
+	/* validate pin */
+	pin = purple_request_fields_get_string( fields, "pin" );
+	if ( !pin ) {
+		err = _( "The PIN you entered is invalid." );
+		goto out;
+	}
+	len = strlen( pin );
+	if ( ( len < 4 ) || ( len > 10 ) ) {
+		err = _( "The PIN you entered has an invalid length [4-10]." );
+		goto out;
+	}
+	for ( i = 0; i < len; i++ ) {
+		if ( !g_ascii_isdigit( pin[i] ) ) {
+			err = _( "The PIN is invalid. It should only consist of digits [0-9]." );
+			goto out;
+		}
 	}
-#endif
+	pin2 = purple_request_fields_get_string( fields, "pin2" );
+	if ( ( !pin2 ) || ( strcmp( pin, pin2 ) != 0 ) ) {
+		err = _( "The two PINs you entered do not match." );
+		goto out;
+	}
+
+out:
+	if ( !err ) {
+		/* update PIN in account */
+		purple_account_set_password( session->acc, pin );
+
+		/* update session object */
+		g_free( session->encpwd );
+		session->encpwd = mxit_encrypt_password( session );
+
+		/* send the update request to MXit */
+		mxit_send_extprofile_update( session, session->encpwd, 0, NULL );
+	}
+	else {
+		/* show error to user */
+		mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "PIN Update Error" ), err );
+	}
+}
+
+
+/*------------------------------------------------------------------------
+ * Enable the user to change their PIN.
+ *
+ *  @param action	The action object
+ */
+static void mxit_change_pin_action( PurplePluginAction* action )
+{
+	PurpleConnection*			gc		= (PurpleConnection*) action->context;
+	struct MXitSession*			session	= (struct MXitSession*) gc->proto_data;
+
+	PurpleRequestFields*		fields	= NULL;
+	PurpleRequestFieldGroup*	group	= NULL;
+	PurpleRequestField*			field	= NULL;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_change_pin_action\n" );
+
+	fields = purple_request_fields_new();
+	group = purple_request_field_group_new(NULL);
+	purple_request_fields_add_group(fields, group);
 
 	/* pin */
 	field = purple_request_field_string_new( "pin", _( "PIN" ), session->acc->password, FALSE );
 	purple_request_field_string_set_masked( field, TRUE );
 	purple_request_field_group_add_field( group, field );
+
+	/* verify pin */
 	field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), session->acc->password, FALSE );
 	purple_request_field_string_set_masked( field, TRUE );
 	purple_request_field_group_add_field( group, field );
 
-	/* display name */
-	field = purple_request_field_string_new( "name", _( "Display Name" ), profile->nickname, FALSE );
-	purple_request_field_group_add_field( group, field );
-
-	/* birthday */
-	field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE );
-	purple_request_field_group_add_field( group, field );
-	if ( profile->flags & CP_PROF_DOBLOCKED )
-		purple_request_field_string_set_editable( field, FALSE );
-
-	/* gender */
-	field = purple_request_field_choice_new( "male", _( "Gender" ), ( profile->male ) ? 1 : 0 );
-	purple_request_field_choice_add( field, _( "Female" ) );		/* 0 */
-	purple_request_field_choice_add( field, _( "Male" ) );			/* 1 */
-	purple_request_field_group_add_field( group, field );
-
-	/* hidden */
-	field = purple_request_field_bool_new( "hidden", _( "Hide my number" ), profile->hidden );
-	purple_request_field_group_add_field( group, field );
-
-	/* title */
-	field = purple_request_field_string_new( "title", _( "Title" ), profile->title, FALSE );
-	purple_request_field_group_add_field( group, field );
-
-	/* first name */
-	field = purple_request_field_string_new( "firstname", _( "First Name" ), profile->firstname, FALSE );
-	purple_request_field_group_add_field( group, field );
-
-	/* last name */
-	field = purple_request_field_string_new( "lastname", _( "Last Name" ), profile->lastname, FALSE );
-	purple_request_field_group_add_field( group, field );
-
-	/* email */
-	field = purple_request_field_string_new( "email", _( "Email" ), profile->email, FALSE );
-	purple_request_field_group_add_field( group, field );
-
-	/* mobile number */
-	field = purple_request_field_string_new( "mobilenumber", _( "Mobile Number" ), profile->mobilenr, FALSE );
-	purple_request_field_group_add_field( group, field );
-
 	/* (reference: "libpurple/request.h") */
-	purple_request_fields( gc, _( "Profile" ), _( "Update your Profile" ), _( "Here you can update your MXit profile" ), fields, _( "Set" ),
-			G_CALLBACK( mxit_cb_set_profile ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc );
+	purple_request_fields( gc, _( "Change PIN" ), _( "Change MXit PIN" ), NULL, fields, _( "Set" ),
+			G_CALLBACK( mxit_change_pin_cb ), _( "Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, gc );
 }
 
 
@@ -293,7 +399,7 @@
  *
  *  @param action	The action object
  */
-static void mxit_cb_action_splash( PurplePluginAction* action )
+static void mxit_splash_action( PurplePluginAction* action )
 {
 	PurpleConnection*		gc		= (PurpleConnection*) action->context;
 	struct MXitSession*		session	= (struct MXitSession*) gc->proto_data;
@@ -310,11 +416,11 @@
  *
  *  @param action	The action object
  */
-static void mxit_cb_action_about( PurplePluginAction* action )
+static void mxit_about_action( PurplePluginAction* action )
 {
 	char	version[256];
 
-	g_snprintf( version, sizeof( version ), 
+	g_snprintf( version, sizeof( version ),
 											"MXit Client Protocol v%i.%i\n\n"
 											"Author:\nPieter Loubser\n\n"
 											"Contributors:\nAndrew Victor\n\n"
@@ -326,6 +432,61 @@
 
 
 /*------------------------------------------------------------------------
+ * Request list of suggested friends.
+ *
+ *  @param action	The action object
+ */
+static void mxit_suggested_friends_action( PurplePluginAction* action )
+{
+	PurpleConnection*		gc				= (PurpleConnection*) action->context;
+	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_STATUS, CP_PROFILE_AVATAR,
+				CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME };
+
+	mxit_send_suggest_friends( session, MXIT_SEARCHRESULTS_MAX, ARRAY_SIZE( profilelist ), profilelist );
+}
+
+
+/*------------------------------------------------------------------------
+ * Perform contact search.
+ *
+ *  @param action	The action object
+ */
+static void mxit_user_search_cb( PurpleConnection *gc, const char *input )
+{
+	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_STATUS, CP_PROFILE_AVATAR,
+				CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME };
+
+	mxit_send_suggest_search( session, MXIT_SEARCHRESULTS_MAX, input, ARRAY_SIZE( profilelist ), profilelist );
+}
+
+
+/*------------------------------------------------------------------------
+ * Display the search input form.
+ *
+ *  @param action	The action object
+ */
+static void mxit_user_search_action( PurplePluginAction* action )
+{
+	PurpleConnection*		gc				= (PurpleConnection*) action->context;
+
+	purple_request_input( gc, _( "Search for user" ),
+		_( "Search for a MXit contact" ),
+		_( "Type search information" ),
+		NULL, FALSE, FALSE, NULL,
+		_("_Search"), G_CALLBACK( mxit_user_search_cb ),
+		_("_Cancel"), NULL,
+		purple_connection_get_account( gc ), NULL, NULL,
+		gc);
+}
+
+
+/*------------------------------------------------------------------------
  * Associate actions with the MXit plugin.
  *
  *  @param plugin	The MXit protocol plugin
@@ -338,17 +499,28 @@
 	GList*					m		= NULL;
 
 	/* display / change profile */
-	action = purple_plugin_action_new( _( "Change Profile..." ), mxit_cb_action_profile );
+	action = purple_plugin_action_new( _( "Change Profile..." ), mxit_profile_action );
+	m = g_list_append( m, action );
+
+	/* change PIN */
+	action = purple_plugin_action_new( _( "Change PIN..." ), mxit_change_pin_action );
+	m = g_list_append( m, action );
+
+	/* suggested friends */
+	action = purple_plugin_action_new( _( "Suggested friends..." ), mxit_suggested_friends_action );
+	m = g_list_append( m, action );
+
+	/* search for contacts */
+	action = purple_plugin_action_new( _( "Search for contacts..." ), mxit_user_search_action );
 	m = g_list_append( m, action );
 
 	/* display splash-screen */
-	action = purple_plugin_action_new( _( "View Splash..." ), mxit_cb_action_splash );
+	action = purple_plugin_action_new( _( "View Splash..." ), mxit_splash_action );
 	m = g_list_append( m, action );
 
 	/* display plugin version */
-	action = purple_plugin_action_new( _( "About..." ), mxit_cb_action_about );
+	action = purple_plugin_action_new( _( "About..." ), mxit_about_action );
 	m = g_list_append( m, action );
 
 	return m;
 }
-
--- a/libpurple/protocols/mxit/chunk.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/chunk.c	Tue Apr 26 13:43:24 2011 +0000
@@ -224,6 +224,7 @@
  *
  *  @param chunkdata		The chunked-data buffer
  *  @param str				A pointer to extracted string.  Must be g_free()'d.
+ *  @param maxstrlen		Maximum size of destination buffer.
  *  @return					The number of bytes consumed
  */
 static int get_utf8_string( const char* chunkdata, char* str, int maxstrlen )
@@ -465,10 +466,10 @@
 	pos += get_int32( &chunkdata[pos], &(offer->filesize) );
 
 	/* filename [UTF-8] */
-	pos += get_utf8_string( &chunkdata[pos], offer->filename, sizeof( offer->filename) );
+	pos += get_utf8_string( &chunkdata[pos], offer->filename, sizeof( offer->filename ) );
 
 	/* mime type [UTF-8] */
-	/* not used by libPurple */
+	pos += get_utf8_string( &chunkdata[pos], offer->mimetype, sizeof( offer->mimetype ) );
 
 	/* timestamp [8 bytes] */
 	/* not used by libPurple */
@@ -606,6 +607,37 @@
 
 
 /*------------------------------------------------------------------------
+ * Parse a received "send file direct" response chunk.  (Chunk 10)
+ *
+ *  @param chunkdata		Chunked data buffer
+ *  @param datalen			The length of the chunked data
+ *  @param sendfile			Decoded sendfile information
+ */
+void mxit_chunk_parse_sendfile( char* chunkdata, int datalen, struct sendfile_chunk* sendfile )
+{
+	int			pos		= 0;
+	short		entries	= 0;
+
+	purple_debug_info( MXIT_PLUGIN_ID, "mxit_chunk_parse_sendfile (%i bytes)\n", datalen );
+
+	/* number of entries [2 bytes] */
+	pos += get_int16( &chunkdata[pos], &entries );
+
+	if ( entries < 1 )		/* no data */
+		return;
+
+	/* contactAddress [UTF-8 string] */
+	pos += get_utf8_string( &chunkdata[pos], sendfile->username, sizeof( sendfile->username ) );
+
+	/* status [4 bytes] */
+	pos += get_int32( &chunkdata[pos], &(sendfile->status) );
+
+	/* status message [UTF-8 string] */
+	pos += get_utf8_string( &chunkdata[pos], sendfile->statusmsg, sizeof( sendfile->statusmsg ) );
+}
+
+
+/*------------------------------------------------------------------------
  * Parse a received "get avatar" response chunk.  (Chunk 14)
  *
  *  @param chunkdata		Chunked data buffer
--- a/libpurple/protocols/mxit/chunk.h	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/chunk.h	Tue Apr 26 13:43:24 2011 +0000
@@ -99,14 +99,20 @@
 	return &chunkheader[MXIT_CHUNK_HEADER_SIZE];
 }
 
-
+/*
+ * Offer File chunk (6).
+ */
 struct offerfile_chunk {
 	char	fileid[MXIT_CHUNK_FILEID_LEN];
 	char	username[MXIT_CP_MAX_JID_LEN + 1];
 	int		filesize;
 	char	filename[FILENAME_MAX];
+	char	mimetype[64];
 };
 
+/*
+ * Get File chunk (8) response.
+ */
 struct getfile_chunk {
 	char	fileid[MXIT_CHUNK_FILEID_LEN];
 	int		offset;
@@ -115,6 +121,9 @@
 	char*	data;
 };
 
+/*
+ * Custom Resource chunk (1).
+ */
 struct cr_chunk {
 	char	id[64];
 	char	handle[64];
@@ -122,6 +131,9 @@
 	GList*	resources;
 };
 
+/*
+ * Splash Image chunk (2)
+ */
 struct splash_chunk {
 	char	anchor;
 	char	showtime;
@@ -130,10 +142,16 @@
 	int		datalen;
 };
 
+/*
+ * Splash Click Through chunk (3)
+ */
 struct splash_click_chunk {
 	char	reserved[1];
 };
 
+/*
+ * Get Avatar chunk (14) response.
+ */
 struct getavatar_chunk {
 	char	mxitid[50];
 	char	avatarid[64];
@@ -146,6 +164,15 @@
 	char*	data;
 };
 
+/*
+ * Send File Direct chunk (10) response.
+ */
+struct sendfile_chunk {
+	char	username[MXIT_CP_MAX_JID_LEN + 1];
+	int		status;
+	char	statusmsg[1024];
+};
+
 /* Encode chunk */
 int mxit_chunk_create_senddirect( char* chunkdata, const char* username, const char* filename, const unsigned char* data, int datalen );
 int mxit_chunk_create_reject( char* chunkdata, const char* fileid );
@@ -158,6 +185,7 @@
 void mxit_chunk_parse_offer( char* chunkdata, int datalen, struct offerfile_chunk* offer );
 void mxit_chunk_parse_get( char* chunkdata, int datalen, struct getfile_chunk* getfile );
 void mxit_chunk_parse_cr( char* chunkdata, int datalen, struct cr_chunk* cr );
+void mxit_chunk_parse_sendfile( char* chunkdata, int datalen, struct sendfile_chunk* sendfile );
 void mxit_chunk_parse_get_avatar( char* chunkdata, int datalen, struct getavatar_chunk* avatar );
 
 #endif		/* _MXIT_CHUNK_H_ */
--- a/libpurple/protocols/mxit/formcmds.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/formcmds.c	Tue Apr 26 13:43:24 2011 +0000
@@ -54,6 +54,15 @@
 	MXIT_CMD_TABLE				/* Table (tbl) */
 } MXitCommandType;
 
+/* Chat-screen behaviours (bhvr) */
+#define SCREEN_NO_HEADINGS		0x01
+#define SCREEN_FULLSCREEN		0x02
+#define SCREEN_AUTOCLEAR		0x04
+#define SCREEN_NO_AUDIO			0x08
+#define SCREEN_NO_MSGPREFIX		0x10
+#define SCREEN_NOTIFY			0x20
+#define SCREEN_PROGRESSBAR		0x40
+
 
 /*
  * object for an inline image request with an URL
@@ -238,9 +247,7 @@
 	clearmsgscreen = g_hash_table_lookup(hash, "clearmsgscreen");
 	if ( (clearmsgscreen) && (strcmp(clearmsgscreen, "true") == 0) ) {
 		/* this is a command to clear the chat screen */
-		purple_debug_info(MXIT_PLUGIN_ID, "Clear the screen\n");
-
-		purple_conversation_clear_message_history(conv);			// TODO: This doesn't actually clear the screen.
+		purple_conversation_clear_message_history(conv);
 	}
 }
 
@@ -283,6 +290,7 @@
 
 /*------------------------------------------------------------------------
  * Process a PlatformRequest MXit command.
+ *  [::op=cmd|type=platreq|selmsg=Upgrade MXit|dest=http%3a//m.mxit.com|id=12345:]
  *
  *  @param hash			The MXit command <key,value> map
  *  @param msg			The message to display (as generated so far)
@@ -371,6 +379,173 @@
 
 
 /*------------------------------------------------------------------------
+ * Process an Imagestrip MXit command.
+ *  [::op=is|nm=status|dat=iVBORw0KGgoAAAA%3d%3d|v=63398792426788|fw=8|fh=8|layer=0:]
+ *
+ *  @param from			The sender of the message.
+ *  @param hash			The MXit command <key,value> map
+ */
+static void command_imagestrip(struct MXitSession* session, const char* from, GHashTable* hash)
+{
+	const char* name;
+	const char* validator;
+	const char* tmp;
+	int width, height, layer;
+
+	purple_debug_info(MXIT_PLUGIN_ID, "ImageStrip received from %s\n", from);
+
+	/* image strip name */
+	name = g_hash_table_lookup(hash, "nm");
+
+	/* validator */
+	validator = g_hash_table_lookup(hash, "v");
+
+	/* image data */
+	tmp = g_hash_table_lookup(hash, "dat");
+	if (tmp) {
+		guchar*		rawimg;
+		gsize		rawimglen;
+		char*		dir;
+		char*		filename;
+
+		/* base64 decode the image data */
+		rawimg = purple_base64_decode(tmp, &rawimglen);
+
+		/* save it to a file */
+		dir = g_strdup_printf("%s/mxit/imagestrips", purple_user_dir());
+		purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR);		/* ensure directory exists */
+
+		filename = g_strdup_printf("%s/%s-%s-%s.png", dir, from, name, validator);
+		purple_util_write_data_to_file_absolute(filename, (char*) rawimg, rawimglen);
+
+		g_free(dir);
+		g_free(filename);
+	}
+
+	tmp = g_hash_table_lookup(hash, "fw");
+	width = atoi(tmp);
+
+	tmp = g_hash_table_lookup(hash, "fh");
+	height = atoi(tmp);
+
+	tmp = g_hash_table_lookup(hash, "layer");
+	layer = atoi(tmp);
+
+	purple_debug_info(MXIT_PLUGIN_ID, "ImageStrip %s from %s: [w=%i h=%i l=%i validator=%s]\n", name, from, width, height, layer, validator);
+}
+
+
+/*------------------------------------------------------------------------
+ * Process a Chat-Screen-Info MXit command.
+ *  [::op=csi:]
+ *
+ *  @param session		The MXit session object
+ *  @param from			The sender of the message.
+ */
+static void command_screeninfo(struct MXitSession* session, const char* from)
+{
+	char* response;
+
+	purple_debug_info(MXIT_PLUGIN_ID, "Chat Screen Info received from %s\n", from);
+
+	// TODO: Determine width, height, colors of chat-screen.
+
+	response = g_strdup_printf("::type=csi|res=bhvr,0;w,%i;h,%i;col,0.ffffffff,29.ff000000:", 300, 400);
+
+	/* send response back to MXit */
+    mxit_send_message( session, from, response, FALSE, TRUE );
+
+	g_free(response);
+}
+
+
+/*------------------------------------------------------------------------
+ * Process a Chat-Screen-Configure MXit command.
+ *  [::op=csc|bhvr=|menu=<menu>|col=<colors>:]
+ *  where:
+ *   menu ::= <menuitem> { ";" <menuitem> }
+ *     menuitem ::= { type "," <text> "," <name> "," <meta> }
+ *   colors ::= <color> { ";" <color> }
+ *     color ::= <colorid> "," <ARGB hex color>   
+ *
+ *  @param session		The MXit session object
+ *  @param from			The sender of the message.
+ *  @param hash			The MXit command <key,value> map
+ */
+static void command_screenconfig(struct MXitSession* session, const char* from, GHashTable* hash)
+{
+	const char* tmp;
+
+	purple_debug_info(MXIT_PLUGIN_ID, "Chat Screen Configure received from %s\n", from);
+
+	/* Behaviour */
+	tmp = g_hash_table_lookup(hash, "bhvr");
+	if (tmp) {
+		purple_debug_info(MXIT_PLUGIN_ID, "  behaviour = %s\n", tmp);
+		// TODO: Re-configure conversation screen.
+	}
+
+	/* Menu */
+	tmp = g_hash_table_lookup(hash, "menu");
+	if (tmp) {
+		purple_debug_info(MXIT_PLUGIN_ID, "  menu = %s\n", tmp);
+		// TODO: Implement conversation-specific sub-menu.
+	}
+
+	/* Colours */
+	tmp = g_hash_table_lookup(hash, "col");
+	if (tmp) {
+		purple_debug_info(MXIT_PLUGIN_ID, "  colours = %s\n", tmp);
+		// TODO: Re-configuration conversation colors.
+	}
+}
+
+
+/*------------------------------------------------------------------------
+ * Process a Table Markup MXit command.
+ *
+ *  @param mx			The received message data object
+ *  @param hash			The MXit command <key,value> map
+ */
+static void command_table(struct RXMsgData* mx, GHashTable* hash)
+{
+	const char* tmp;
+	const char* name;
+	int mode;
+	int nr_columns = 0, nr_rows = 0;
+	gchar** coldata;
+	int i, j;
+
+	/* table name */
+	name = g_hash_table_lookup(hash, "nm");
+
+	/* number of columns */
+	tmp = g_hash_table_lookup(hash, "col");
+	nr_columns = atoi(tmp);	
+
+	/* number of rows */
+	tmp = g_hash_table_lookup(hash, "row");
+	nr_rows = atoi(tmp);
+
+	/* mode */
+	tmp = g_hash_table_lookup(hash, "mode");
+	mode = atoi(tmp);
+
+	/* table data */
+	tmp = g_hash_table_lookup(hash, "d");
+	coldata = g_strsplit(tmp, "~", 0);			/* split into entries for each row & column */
+
+	purple_debug_info(MXIT_PLUGIN_ID, "Table %s from %s: [cols=%i rows=%i mode=%i]\n", name, mx->from, nr_columns, nr_rows, mode);
+
+	for (i = 0; i < nr_rows; i++) {
+		for (j = 0; j < nr_columns; j++) {
+			purple_debug_info(MXIT_PLUGIN_ID, " Row %i Column %i = %s\n", i, j, coldata[i*nr_columns + j]);
+		}
+	}
+}
+
+
+/*------------------------------------------------------------------------
  * Process a received MXit Command message.
  *
  *  @param mx				The received message data object
@@ -412,6 +587,18 @@
 				case MXIT_CMD_IMAGE :
 					command_image(mx, hash, mx->msg);
 					break;
+				case MXIT_CMD_SCREENCONFIG :
+					command_screenconfig(mx->session, mx->from, hash);
+					break;
+				case MXIT_CMD_SCREENINFO :
+					command_screeninfo(mx->session, mx->from);
+					break;
+				case MXIT_CMD_IMAGESTRIP :
+					command_imagestrip(mx->session, mx->from, hash);
+					break;
+				case MXIT_CMD_TABLE :
+					command_table(mx, hash);
+					break;
 				default :
 					/* command unknown, or not currently supported */
 					break;
--- a/libpurple/protocols/mxit/login.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/login.c	Tue Apr 26 13:43:24 2011 +0000
@@ -141,9 +141,9 @@
 	}
 
 	/* This timer might already exist if we're registering a new account */
-	if ( session->q_timer == 0 ) {
+	if ( session->q_slow_timer_id == 0 ) {
 		/* start the tx queue manager timer */
-		session->q_timer = purple_timeout_add_seconds( 2, mxit_manage_queue_slow, session );
+		session->q_slow_timer_id = purple_timeout_add_seconds( 2, mxit_manage_queue_slow, session );
 	}
 }
 
--- a/libpurple/protocols/mxit/markup.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/markup.c	Tue Apr 26 13:43:24 2011 +0000
@@ -235,7 +235,6 @@
  */
 static void mxit_show_split_message( struct RXMsgData* mx )
 {
-	const char*		cont	= "<font color=\"#999999\">continuing...</font>\n";
 	GString*		msg		= NULL;
 	char*			ch		= NULL;
 	int				pos		= 0;
@@ -245,7 +244,6 @@
 	int				l_gt	= 0;
 	int				stop	= 0;
 	int				tags	= 0;
-	int				segs	= 0;
 	gboolean		intag	= FALSE;
 
 	/*
@@ -319,21 +317,20 @@
 				stop--;
 			}
 
-			/* build the string */
-			if ( segs )
-				g_string_prepend( msg, cont );
-
 			/* push message to pidgin */
 			serv_got_im( mx->session->con, mx->from, msg->str, mx->flags, mx->timestamp );
 			g_string_free( msg, TRUE );
 			msg = NULL;
 
+			/* next part need this flag set */
+			mx->flags |= PURPLE_MESSAGE_RAW;
+
 			tags = 0;
-			segs++;
 			start = stop + 1;
+			pos = start;
 		}
-
-		pos++;
+		else
+			pos++;
 	}
 
 	if ( start != pos ) {
@@ -343,8 +340,6 @@
 		ch[pos] = '\0';
 		msg = g_string_new( &ch[start] );
 		ch[pos] = '\n';
-		if ( segs )
-			g_string_prepend( msg, cont );
 
 		/* push message to pidgin */
 		serv_got_im( mx->session->con, mx->from, msg->str, mx->flags, mx->timestamp );
@@ -1095,10 +1090,15 @@
 				}
 				else if ( purple_str_has_prefix( &message[i], "<font size=" ) ) {
 					/* font size */
+					int fontsize;
+
 					tag = g_new0( struct tag, 1 );
 					tag->type = MXIT_TAG_SIZE;
 					tagstack = g_list_prepend( tagstack, tag );
 					// TODO: implement size control
+					if ( sscanf( &message[i+12], "%i", &fontsize ) ) {
+						purple_debug_info( MXIT_PLUGIN_ID, "Font size set to %i\n", fontsize );
+					}
 				}
 				else if ( purple_str_has_prefix( &message[i], "<font color=" ) ) {
 					/* font colour */
@@ -1151,6 +1151,17 @@
 				g_string_append_c( mx, message[i] );		/* character to escape */
 				break;
 
+			case '.' : /* might be a MXit font size change, or custom emoticon */
+				if ( i + 1 < len ) {
+					if ( ( message[i+1] == '+' ) || ( message[i+1] == '-' ) )
+						g_string_append( mx, "\\." );		/* escape "." */
+					else
+						g_string_append_c( mx, '.' );
+				}
+				else
+					g_string_append_c( mx, '.' );
+				break;
+
 			default:
 				g_string_append_c( mx, message[i] );
 				break;
--- a/libpurple/protocols/mxit/multimx.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/multimx.c	Tue Apr 26 13:43:24 2011 +0000
@@ -177,22 +177,13 @@
 /*------------------------------------------------------------------------
  * Another user has join the GroupChat, add them to the member-list.
  *
- *  @param session		The MXit session object
- *  @param multimx		The MultiMX room object
+ *  @param convo		The Conversation object
  *  @param nickname		The nickname of the user who joined the room
  */
-static void member_added(struct MXitSession* session, struct multimx* multimx, const char* nickname)
+static void member_added(PurpleConversation* convo, const char* nickname)
 {
-	PurpleConversation *convo;
-
 	purple_debug_info(MXIT_PLUGIN_ID, "member_added: '%s'\n", nickname);
 
-	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, session->acc);
-	if (convo == NULL) {
-		purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
-		return;
-	}
-
 	purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), nickname, NULL, PURPLE_CBFLAGS_NONE, TRUE);
 }
 
@@ -200,79 +191,61 @@
 /*------------------------------------------------------------------------
  * Another user has left the GroupChat, remove them from the member-list.
  *
- *  @param session		The MXit session object
- *  @param multimx		The MultiMX room object
+ *  @param convo		The Conversation object
  *  @param nickname		The nickname of the user who left the room
  */
-static void member_removed(struct MXitSession* session, struct multimx* multimx, const char* nickname)
+static void member_removed(PurpleConversation* convo, const char* nickname)
 {
-	PurpleConversation *convo;
-
 	purple_debug_info(MXIT_PLUGIN_ID, "member_removed: '%s'\n", nickname);
 
-	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, session->acc);
-	if (convo == NULL) {
-		purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
-		return;
-	}
-
 	purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nickname, NULL);
 }
 
 
 /*------------------------------------------------------------------------
- * A user was kicked from the GroupChat.
+ * A user was kicked from the GroupChat, remove them from the member-list.
  *
- *  @param session		The MXit session object
- *  @param multimx		The MultiMX room object
+ *  @param convo		The Conversation object
  *  @param nickname		The nickname of the user who was kicked
  */
-static void member_kicked(struct MXitSession* session, struct multimx* multimx, const char* nickname)
+static void member_kicked(PurpleConversation* convo, const char* nickname)
 {
-	PurpleConversation *convo;
-
 	purple_debug_info(MXIT_PLUGIN_ID, "member_kicked: '%s'\n", nickname);
 
-	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, session->acc);
-	if (convo == NULL) {
-		purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
-		return;
-	}
+	purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nickname, _("was kicked"));
+}
+
 
-	/* who was kicked? - compare to our original nickname */
-	if (purple_utf8_strcasecmp(nickname, multimx->nickname) == 0)
-	{
-		/* you were kicked */
-		purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "MXit", _("You have been kicked from this MultiMX."), PURPLE_MESSAGE_SYSTEM, time(NULL));
-		purple_conv_chat_clear_users(PURPLE_CONV_CHAT(convo));
-		serv_got_chat_left(session->con, multimx->chatid);
-	}
-	else
-		purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), nickname, _("was kicked"));
+/*------------------------------------------------------------------------
+ * You were kicked from the GroupChat.
+ *
+ *  @param convo		The Conversation object
+ *  @param session		The MXit session object
+ *  @param multimx		The MultiMX room object
+ */
+static void you_kicked(PurpleConversation* convo, struct MXitSession* session, struct multimx* multimx)
+{
+	purple_debug_info(MXIT_PLUGIN_ID, "you_kicked\n");
+
+	purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "MXit", _("You have been kicked from this MultiMX."), PURPLE_MESSAGE_SYSTEM, time(NULL));
+	purple_conv_chat_clear_users(PURPLE_CONV_CHAT(convo));
+	serv_got_chat_left(session->con, multimx->chatid);
 }
 
 
 /*------------------------------------------------------------------------
  * Update the full GroupChat member list.
  *
- *  @param session		The MXit session object
- *  @param multimx		The MultiMX room object
+ *  @param convo		The Conversation object
  *  @param data			The nicknames of the users in the room (separated by \n)
  */
-static void member_update(struct MXitSession* session, struct multimx* multimx, char* data)
+static void member_update(PurpleConversation* convo, char* data)
 {
-	PurpleConversation *convo;
 	gchar** userlist;
 	int i = 0;
 
 	purple_debug_info(MXIT_PLUGIN_ID, "member_update: '%s'\n", data);
 
-	convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, session->acc);
-	if (convo == NULL) {
-		purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
-		return;
-	}
-
 	/* Clear list */
 	purple_conv_chat_clear_users(PURPLE_CONV_CHAT(convo));
 
@@ -402,27 +375,38 @@
 		/* Must be a service message */
 		char* ofs;
 
+		PurpleConversation* convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, multimx->roomname, mx->session->acc);
+		if (convo == NULL) {
+			purple_debug_error(MXIT_PLUGIN_ID, "Conversation '%s' not found\n", multimx->roomname);
+			return;
+		}
+
 		/* Determine if somebody has joined or left - update member-list */
 		if ((ofs = strstr(msg, " has joined")) != NULL) {
 			/* Somebody has joined */
 			*ofs = '\0';
-			member_added(mx->session, multimx, msg);
+			member_added(convo, msg);
 			mx->processed = TRUE;
 		}
 		else if ((ofs = strstr(msg, " has left")) != NULL) {
 			/* Somebody has left */
 			*ofs = '\0';
-			member_removed(mx->session, multimx, msg);
+			member_removed(convo, msg);
 			mx->processed = TRUE;
 		}
 		else if ((ofs = strstr(msg, " has been kicked")) != NULL) {
 			/* Somebody has been kicked */
 			*ofs = '\0';
-			member_kicked(mx->session, multimx, msg);
+			member_kicked(convo, msg);
+			mx->processed = TRUE;
+		}
+		else if (strcmp(msg, "You have been kicked.") == 0) {
+			/* You have been kicked */
+			you_kicked(convo, mx->session, multimx);
 			mx->processed = TRUE;
 		}
 		else if (g_str_has_prefix(msg, "The following users are in this MultiMx:") == TRUE) {
-			member_update(mx->session, multimx, msg + strlen("The following users are in this MultiMx:") + 1);
+			member_update(convo, msg + strlen("The following users are in this MultiMx:") + 1);
 			mx->processed = TRUE;
 		}
 		else {
--- a/libpurple/protocols/mxit/mxit.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/mxit.c	Tue Apr 26 13:43:24 2011 +0000
@@ -565,25 +565,23 @@
 	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,
-												CP_PROFILE_STATUS, CP_PROFILE_AVATAR };
+												CP_PROFILE_STATUS, CP_PROFILE_AVATAR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME };
 
 	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;
-	}
+	if ( buddy ) {
+		/* user is in our contact-list, so it's not an invite */
+		contact = purple_buddy_get_protocol_data( buddy );
+		if ( !contact )
+			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;
+		/* 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 */
@@ -628,7 +626,7 @@
 		return;
 
 	/* send a new invite */
-	mxit_send_invite( session, contact->username, contact->alias, contact->groupname );
+	mxit_send_invite( session, contact->username, TRUE, contact->alias, contact->groupname, NULL );
 }
 
 
@@ -664,7 +662,7 @@
 /*========================================================================================================================*/
 
 static PurplePluginProtocolInfo proto_info = {
-	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE,			/* options */
+	OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE | OPT_PROTO_INVITE_MESSAGE,			/* options */
 	NULL,					/* user_splits */
 	NULL,					/* protocol_options */
 	{						/* icon_spec */
@@ -692,7 +690,7 @@
 	mxit_set_status,		/* set_status */
 	NULL,					/* set_idle */
 	NULL,					/* change_passwd */
-	mxit_add_buddy,			/* add_buddy				[roster.c] */
+	NULL,					/* add_buddy				[roster.c] */
 	NULL,					/* add_buddies */
 	mxit_remove_buddy,		/* remove_buddy				[roster.c] */
 	NULL,					/* remove_buddies */
@@ -743,7 +741,7 @@
 	mxit_get_moods,			/* get_moods */
 	NULL,					/* set_public_alias */
 	NULL,					/* get_public_alias */
-	NULL,					/* add_buddy_with_invite */
+	mxit_add_buddy,			/* add_buddy_with_invite */
 	NULL					/* add_buddies_with_invite */
 };
 
--- a/libpurple/protocols/mxit/mxit.h	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/mxit.h	Tue Apr 26 13:43:24 2011 +0000
@@ -107,6 +107,10 @@
 #define		MXIT_FLAG_FIRSTROSTER		0x04		/* set to true once the first roster update has been received and processed */
 
 
+/* Maximum number of search results */
+#define		MXIT_SEARCHRESULTS_MAX		30
+
+
 /* define this to enable the link clicking support */
 #define		MXIT_LINK_CLICK
 
@@ -162,7 +166,8 @@
 	struct tx_queue		queue;						/* transmit packet queue (FIFO mode) */
 	gint64				last_tx;					/* timestamp of last packet sent */
 	int					outack;						/* outstanding ack packet */
-	guint				q_timer;					/* timer handler for managing queue */
+	guint				q_slow_timer_id;			/* timer handle for slow tx queue */
+	guint				q_fast_timer_id;			/* timer handle for fast tx queue */
 
 	/* receive */
 	char				rx_lbuf[16];				/* receive byte buffer (socket packet length) */
@@ -172,6 +177,7 @@
 	char				rx_state;					/* current receiver state */
 	gint64				last_rx;					/* timestamp of last packet received */
 	GList*				active_chats;				/* list of all our contacts we received messages from (active chats) */
+	GList*				invites;					/* list of all the invites that we have received */
 
 	/* groupchat */
 	GList*				rooms;						/* active groupchat rooms */
--- a/libpurple/protocols/mxit/profile.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/profile.c	Tue Apr 26 13:43:24 2011 +0000
@@ -23,6 +23,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
+#define		_XOPEN_SOURCE
+#include	<time.h>
+
 #include    "internal.h"
 #include	"purple.h"
 
@@ -34,7 +37,7 @@
 /*------------------------------------------------------------------------
  * Returns true if it is a valid date.
  *
- * @param bday		Date-of-Birth string
+ * @param bday		Date-of-Birth string (YYYY-MM-DD)
  * @return			TRUE if valid, else FALSE
  */
 gboolean validateDate( const char* bday )
@@ -101,6 +104,40 @@
 
 
 /*------------------------------------------------------------------------
+ * Calculate an Age from the date-of-birth.
+ *
+ * @param date		Date-of-Birth string (YYYY-MM-DD)
+ * @return			The age
+ */
+static int calculateAge( const char* date )
+{
+	time_t t;
+	struct tm now, bdate;
+	int age;
+
+	if ( ( !date ) || ( strlen( date ) == 0 ) )
+		return 0;
+
+	/* current time */
+	t = time(NULL);
+	localtime_r( &t, &now );
+
+	/* decode hdate */
+	memset( &bdate, 0, sizeof( struct tm ) );
+	strptime( date, "%Y-%m-%d", &bdate );
+
+	/* calculate difference */
+	age = now.tm_year - bdate.tm_year;
+	if ( now.tm_mon < bdate.tm_mon )		/* is before month of birth */
+		age--;
+	else if ( (now.tm_mon == bdate.tm_mon ) && ( now.tm_mday < bdate.tm_mday ) )	/* before birthday in current month */
+		age--;
+
+	return age;
+}
+
+
+/*------------------------------------------------------------------------
  * Returns timestamp field in date & time format (DD-MM-YYYY HH:MM:SS)
  *
  * @param msecs		The timestamps (milliseconds since epoch)
@@ -120,9 +157,9 @@
 /*------------------------------------------------------------------------
  * Display the profile information.
  *
- *  @param session		The MXit session object
- *  @param username		The username who's profile information this is
- *  @param profile		The profile
+ * @param session		The MXit session object
+ * @param username		The username who's profile information this is
+ * @param profile		The profile
  */
 void mxit_show_profile( struct MXitSession* session, const char* username, struct MXitProfile* profile )
 {
@@ -146,6 +183,11 @@
 	purple_notify_user_info_add_pair( info, _( "Last Name" ), profile->lastname );
 	purple_notify_user_info_add_pair( info, _( "Country" ), profile->regcountry );
 
+	if ( strlen( profile->aboutme ) > 0 )
+		purple_notify_user_info_add_pair( info, _( "About Me" ), profile->aboutme );
+	if ( strlen( profile->whereami ) > 0 )
+		purple_notify_user_info_add_pair( info, _( "Where I Live" ), profile->whereami );
+
 	purple_notify_user_info_add_section_break( info );
 
 	if ( contact ) {
@@ -172,7 +214,105 @@
 		/* hidden number */
 		purple_notify_user_info_add_pair( info, _( "Hidden Number" ), ( contact->flags & MXIT_CFLAG_HIDDEN ) ? _( "Yes" ) : _( "No" ) );
 	}
+	else {
+		/* this is an invite */
+		contact = get_mxit_invite_contact( session, username );
+		if ( contact ) {
+			/* invite found */
+
+			if ( contact->msg )
+				purple_notify_user_info_add_pair( info, _( "Invite Message" ), contact->msg );
+
+			if ( contact->imgid ) {
+				/* this invite has a avatar */
+				char* img_text;
+				img_text = g_strdup_printf( "<img id='%d'>", contact->imgid );
+				purple_notify_user_info_add_pair( info, _( "Photo" ), img_text );
+			}
+
+			if ( contact->statusMsg )
+				purple_notify_user_info_add_pair( info, _( "Status Message" ), contact->statusMsg );
+		}
+	}
 
 	purple_notify_userinfo( session->con, username, info, NULL, NULL );
 	purple_notify_user_info_destroy( info );
 }
+
+
+/*------------------------------------------------------------------------
+ * Display the profiles of search results.
+ *
+ * @param gc			The connection object
+ * @param row			The selected row from search-results
+ * @param user_data		NULL (unused)
+ */
+static void mxit_search_results_add_cb( PurpleConnection *gc, GList *row, gpointer user_data )
+{
+	/* display add buddy dialog */
+	purple_blist_request_add_buddy( purple_connection_get_account( gc ), g_list_nth_data( row, 0 ), NULL, g_list_nth_data( row, 1 ) );
+}
+
+
+/*------------------------------------------------------------------------
+ * Display the profiles of search results.
+ *
+ * @param session		The MXit session object
+ * @param searchType	The type of search (CP_SUGGEST_*)
+ * @param maxResults	The maximum number of results
+ * @param entries		The list of profile entries
+ */
+void mxit_show_search_results( struct MXitSession* session, int searchType, int maxResults, GList* entries )
+{
+	PurpleNotifySearchResults*	results;
+	PurpleNotifySearchColumn*	column;
+	gchar*						text;
+
+	if ( !entries ) {
+		mxit_popup( PURPLE_NOTIFY_MSG_INFO, _( "No results" ), _( "No contacts found." ) );
+		return;
+	}
+
+	results = purple_notify_searchresults_new();
+	if ( !results )
+		return;
+
+	/* define columns */
+	column = purple_notify_searchresults_column_new( _( "UserId" ) );
+	purple_notify_searchresults_column_add( results, column );
+	column = purple_notify_searchresults_column_new( _( "Display Name" ) );
+	purple_notify_searchresults_column_add( results, column );
+	column = purple_notify_searchresults_column_new( _( "Gender" ) );
+	purple_notify_searchresults_column_add( results, column );
+	column = purple_notify_searchresults_column_new( _( "Age" ) );
+	purple_notify_searchresults_column_add( results, column );
+	column = purple_notify_searchresults_column_new( _( "Where I live" ) );
+	purple_notify_searchresults_column_add( results, column );
+
+	while (entries != NULL) {
+		struct MXitProfile* profile	= ( struct MXitProfile *) entries->data;
+		GList*	row;
+
+		/* column values */
+		row = g_list_append( NULL, g_strdup( profile->userid ) );
+		row = g_list_append( row, g_strdup( profile->nickname ) );
+		row = g_list_append( row, g_strdup( profile->male ? "Male" : "Female" ) );
+		row = g_list_append( row, g_strdup_printf( "%i", calculateAge( profile->birthday ) ) );
+		row = g_list_append( row, g_strdup( profile->whereami ) );
+
+		purple_notify_searchresults_row_add( results, row );
+		entries = g_list_next( entries );
+	}
+
+	/* button */
+	purple_notify_searchresults_button_add( results, PURPLE_NOTIFY_BUTTON_INVITE, mxit_search_results_add_cb );
+
+	if ( searchType == CP_SUGGEST_FRIENDS )
+		text = g_strdup_printf( _( "You have %i suggested friends." ), maxResults );
+	else
+		text = g_strdup_printf( _( "We found %i contacts that match your search." ), maxResults );
+
+	purple_notify_searchresults( session->con, NULL, text, NULL, results, NULL, NULL );
+
+	g_free( text);
+}
--- a/libpurple/protocols/mxit/profile.h	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/profile.h	Tue Apr 26 13:43:24 2011 +0000
@@ -32,26 +32,30 @@
 struct MXitProfile {
 	/* required */
 	char		loginname[64];						/* name user uses to log into MXit with (aka 'mxitid') */
-	char		nickname[64];						/* user's own display name (aka 'nickname', aka 'fullname', aka 'alias') in MXit */
+	char		userid[51];							/* internal UserId (only in search results) */
+	char		nickname[101];						/* user's own display name (aka 'display name', aka 'fullname', aka 'alias') in MXit */
 	char		birthday[16];						/* user's birthday "YYYY-MM-DD" */
 	gboolean	male;								/* true if the user's gender is male (otherwise female) */
 	char		pin[16];							/* user's password */
 
 	/* optional */
-	char		title[32];							/* user's title */
-	char		firstname[64];						/* user's first name */
-	char		lastname[64];						/* user's last name (aka 'surname') */
-	char		email[64];							/* user's email address */
+	char		title[21];							/* user's title */
+	char		firstname[51];						/* user's first name */
+	char		lastname[51];						/* user's last name (aka 'surname') */
+	char		email[201];							/* user's email address */
 	char		mobilenr[21];						/* user's mobile number */
 	char		regcountry[3];						/* user's registered country code */
-	gint64		flags;								/* user's profile flags */
+	char		whereami[51];						/* where am I / where I live */
+	char		aboutme[513];						/* about me */
+
+	int			flags;								/* user's profile flags */
 	gint64		lastonline;							/* user's last-online timestamp */
-
-	gboolean	hidden;								/* set if the user's msisdn should remain hidden */
+	gboolean	hidden;								/* set if the user's mxitid should remain hidden */
 };
 
 struct MXitSession;
 void mxit_show_profile( struct MXitSession* session, const char* username, struct MXitProfile* profile );
+void mxit_show_search_results( struct MXitSession* session, int searchType, int maxResults, GList* entries );
 
 gboolean validateDate( const char* bday );
 
--- a/libpurple/protocols/mxit/protocol.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/protocol.c	Tue Apr 26 13:43:24 2011 +0000
@@ -535,28 +535,31 @@
 		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 );
+	if ( session->q_fast_timer_id == 0 ) {
+		/* the fast timer has not been set yet */
+		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;
+			session->q_fast_timer_id = 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 );
+			}
 		}
 	}
 }
@@ -587,6 +590,7 @@
 {
 	struct MXitSession* session		= (struct MXitSession*) user_data;
 
+	session->q_fast_timer_id = 0;
 	mxit_manage_queue( session );
 
 	/* stop running */
@@ -713,7 +717,7 @@
 								"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 */
-								"%c%i%c%i",					/* \1protocolVer\1lastRosterUpdate */	
+								"%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, features, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale,
@@ -844,16 +848,17 @@
  *  @param session		The MXit session object
  *  @param password		The new password to be used for logging in (optional)
  *	@param nr_attrib	The number of attributes
- *	@param attributes	String containing the attributes and settings seperated by '0x01'
+ *	@param attributes	String containing the attribute-name, attribute-type and value (seperated by '\01')
  */
 void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes )
 {
 	char			data[CP_MAX_PACKET];
-	gchar**			parts;
+	gchar**			parts					= NULL;
 	int				datalen;
 	unsigned int	i;
 
-	parts = g_strsplit( attributes, "\01", ( MXIT_MAX_ATTRIBS * 3 ) );
+	if ( attributes )
+		parts = g_strsplit( attributes, "\01", 1 + ( nr_attrib * 3 ) );
 
 	/* convert the packet to a byte stream */
 	datalen = snprintf( data, sizeof( data ),
@@ -880,7 +885,7 @@
  *  @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  
+ *  @param attribute	The names of the attributes
  */
 void mxit_send_suggest_friends( struct MXitSession* session, int max, unsigned int nr_attrib, const char* attribute[] )
 {
@@ -890,8 +895,8 @@
 
 	/* 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 );
+								"ms=%i%c%s%c%i%c%i%c%i",	/* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */
+								CP_SUGGEST_FRIENDS, CP_FLD_TERM, "", CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib );
 
 	/* add attributes */
 	for ( i = 0; i < nr_attrib; i++ )
@@ -919,8 +924,8 @@
 
 	/* 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 );
+								"ms=%i%c%s%c%i%c%i%c%i",	/* inputType \1 input \1 maxSuggestions \1 startIndex \1 numAttributes \1 name0 \1 name1 ... \1 nameN */
+								CP_SUGGEST_SEARCH, CP_FLD_TERM, text, CP_FLD_TERM, max, CP_FLD_TERM, 0, CP_FLD_TERM, nr_attrib );
 
 	/* add attributes */
 	for ( i = 0; i < nr_attrib; i++ )
@@ -985,19 +990,23 @@
  *
  *  @param session		The MXit session object
  *  @param username		The username of the contact being invited
+ *  @param mxitid		Indicates the username is a MXitId.
  *  @param alias		Our alias for the contact
  *  @param groupname	Group in which contact should be stored.
+ *  @param message		Invite message
  */
-void mxit_send_invite( struct MXitSession* session, const char* username, const char* alias, const char* groupname )
+void mxit_send_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message )
 {
 	char		data[CP_MAX_PACKET];
 	int			datalen;
 
 	/* convert the packet to a byte stream */
 	datalen = snprintf( data, sizeof( data ),
-								"ms=%s%c%s%c%s%c%i%c%s",	/* "ms"=group\1username\1alias\1type\1msg */
+								"ms=%s%c%s%c%s%c%i%c%s%c%i",	/* "ms"=group \1 username \1 alias \1 type \1 msg \1 isuserid */
 								groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias,
-								CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM, ""
+								CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM,
+								( message ? message : "" ), CP_FLD_TERM,
+								( mxitid ? 0 : 1 )
 	);
 
 	/* queue packet for transmission */
@@ -1441,7 +1450,7 @@
 	const char*		statusmsg;
 	const char*		profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_HIDENUMBER, CP_PROFILE_FULLNAME,
 									CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL,
-									CP_PROFILE_MOBILENR, CP_PROFILE_FLAGS };
+									CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_FLAGS };
 
 	purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
 
@@ -1459,7 +1468,7 @@
 		session->http_sesid = atoi( records[0]->fields[0]->data );
 	}
 
-	/* extract MXitId (from protocol 5.9) */
+	/* extract UserId (from protocol 5.9) */
 	if ( records[1]->fcount >= 9 )
 		session->uid = g_strdup( records[1]->fields[8]->data );
 
@@ -1624,10 +1633,9 @@
 
 		if ( rec->fcount >= 5 ) {
 			/* there is a personal invite message attached */
-			contact->msg = strdup( rec->fields[4]->data );
+			if ( ( rec->fields[4]->data ) && ( strlen( rec->fields[4]->data ) > 0 ) )
+				contact->msg = strdup( rec->fields[4]->data );
 		}
-		else
-			contact->msg = NULL;
 
 		/* handle the subscription */
 		if ( contact-> type == MXIT_TYPE_MULTIMX ) {		/* subscription to a MultiMX room */
@@ -1850,6 +1858,14 @@
 			/* last seen online */
 			profile->lastonline = strtoll( fvalue, NULL, 10 );
 		}
+		else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) {
+			/* where am I */
+			g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) );
+		}
+		else if ( strcmp( CP_PROFILE_ABOUTME, fname ) == 0) {
+			/* about me */
+			g_strlcpy( profile->aboutme, fvalue, sizeof( profile->aboutme ) );
+		}
 		else {
 			/* invalid profile attribute */
 			purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname );
@@ -1857,18 +1873,127 @@
 	}
 
 	if ( profile != session->profile ) {
-		/* update avatar (if necessary) */
-		if ( avatarId )
-			mxit_update_buddy_avatar( session, mxitId, avatarId );
-
-		/* if this is not our profile, just display it */
-		mxit_show_profile( session, mxitId, profile );
-		g_free( profile );
+		/* not our own profile */
+		struct contact*		contact		= NULL;
+
+		contact = get_mxit_invite_contact( session, mxitId );
+		if ( contact ) {
+			/* this is an invite, so update its profile info */
+			if ( ( statusMsg ) && ( strlen( statusMsg ) > 0 ) ) {
+				/* update the status message */
+				if ( contact->statusMsg )
+					g_free( contact->statusMsg );
+				contact->statusMsg = strdup( statusMsg );
+			}
+			else
+				contact->statusMsg = NULL;
+			if ( contact->profile )
+				g_free( contact->profile );
+			contact->profile = profile;
+			if ( ( avatarId ) && ( strlen( avatarId ) > 0 ) ) {
+				/* avatar must be requested for this invite before we can display it */
+				mxit_get_avatar( session, mxitId, avatarId );
+				if ( contact->avatarId )
+					g_free( contact->avatarId );
+				contact->avatarId = strdup( avatarId );
+			}
+			else {
+				/* display what we have */
+				contact->avatarId = NULL;
+				mxit_show_profile( session, mxitId, profile );
+			}
+		}
+		else {
+			/* this is a contact */
+			if ( avatarId )
+				mxit_update_buddy_avatar( session, mxitId, avatarId );
+			mxit_show_profile( session, mxitId, profile );
+			g_free( profile );
+		}
 	}
 }
 
 
 /*------------------------------------------------------------------------
+ * Process a received suggest-contacts packet.
+ *
+ *  @param session		The MXit session object
+ *  @param records		The packet's data records
+ *  @param rcount		The number of data records
+ */
+static void mxit_parse_cmd_suggestcontacts( struct MXitSession* session, struct record** records, int rcount )
+{
+	GList* entries = NULL;
+	int searchType;
+	int maxResults;
+	int count;
+	int i;
+
+	/*
+	 * searchType \1 numSuggestions \1 total \1 numAttributes \1 name0 \1 name1 \1 ... \1 nameN \0
+	 * userid \1 contactType \1 value0 \1 value1 ... valueN \0
+	 * ...
+	 * userid \1 contactType \1 value0 \1 value1 ... valueN
+	 */
+
+	/* the type of results */
+	searchType = atoi( records[0]->fields[0]->data );
+
+	/* the maximum number of results */
+	maxResults = atoi( records[0]->fields[2]->data );
+
+	/* set the count for attributes */
+	count = atoi( records[0]->fields[3]->data );
+
+	for ( i = 1; i < rcount; i ++ ) {
+		struct record*		rec		= records[i];
+		struct MXitProfile*	profile	= g_new0( struct MXitProfile, 1 );
+		int j;
+
+		g_strlcpy( profile->userid, rec->fields[0]->data, sizeof( profile->userid ) );
+		// TODO: ContactType - User or Service
+
+		for ( j = 0; j < count; j++ ) {
+			char* fname;
+			char* fvalue = "";
+
+			fname = records[0]->fields[4 + j]->data;		/* field name */
+			if ( records[i]->fcount > ( 2 + j ) )
+				fvalue = records[i]->fields[2 + j]->data;	/* field value */
+
+			purple_debug_info( MXIT_PLUGIN_ID, " %s: field='%s' value='%s'\n", profile->userid, fname, fvalue );
+
+			if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) {
+				/* birthdate */
+				g_strlcpy( profile->birthday, fvalue, sizeof( profile->birthday ) );
+			}
+			else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) {
+				/* gender */
+				profile->male = ( fvalue[0] == '1' );
+			}
+			else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) {
+				/* nickname */
+				g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) );
+			}
+			else if ( strcmp( CP_PROFILE_WHEREAMI, fname ) == 0 ) {
+				/* where am I */
+				g_strlcpy( profile->whereami, fvalue, sizeof( profile->whereami ) );
+			}
+			/* ignore other attibutes */
+		}
+
+		entries = g_list_append( entries, profile );
+	}
+
+	/* display */
+	mxit_show_search_results( session, searchType, maxResults, entries );
+
+	/* cleanup */
+	g_list_foreach( entries, (GFunc)g_free, NULL );
+}
+
+
+/*------------------------------------------------------------------------
  * Return the length of a multimedia chunk
  *
  * @return		The actual chunk data length in bytes
@@ -1960,17 +2085,27 @@
 		case CP_CHUNK_GET_AVATAR :			/* get avatars */
 			{
 				struct getavatar_chunk chunk;
+				struct contact* contact = NULL;
 
 				/* decode the chunked data */
-				memset( &chunk, 0, sizeof ( struct getavatar_chunk ) );
+				memset( &chunk, 0, sizeof( struct getavatar_chunk ) );
 				mxit_chunk_parse_get_avatar( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk );
 
 				/* update avatar image */
 				if ( chunk.data ) {
 					purple_debug_info( MXIT_PLUGIN_ID, "updating avatar for contact '%s'\n", chunk.mxitid );
-					purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length), chunk.length, chunk.avatarid );
+
+					contact = get_mxit_invite_contact( session, chunk.mxitid );
+					if ( contact ) {
+						/* this is an invite (add image to the internal image store) */
+						contact->imgid = purple_imgstore_add_with_id( chunk.data, chunk.length, NULL );
+						mxit_show_profile( session, chunk.mxitid, contact->profile );
+					}
+					else {
+						/* this is a contact's avatar, so update it */
+						purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length), chunk.length, chunk.avatarid );
+					}
 				}
-
 			}
 			break;
 
@@ -1979,7 +2114,18 @@
 			break;
 
 		case CP_CHUNK_DIRECT_SND :
-			/* this is a ack for a file send. no action is required */
+			/* this is a ack for a file send. */
+			{
+				struct sendfile_chunk chunk;
+
+				memset( &chunk, 0, sizeof( struct sendfile_chunk ) );
+				mxit_chunk_parse_sendfile( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk );
+
+				purple_debug_info( MXIT_PLUGIN_ID, "file-send send to '%s' [status=%i message='%s']\n", chunk.username, chunk.status, chunk.statusmsg );
+
+				if ( chunk.status != 0 )	/* not success */
+					mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "File Send Failed" ), chunk.statusmsg );
+			}
 			break;
 
 		case CP_CHUNK_RECEIVED :
@@ -2119,6 +2265,11 @@
 				mxit_parse_cmd_extprofile( session, &packet->records[2], packet->rcount - 2 );
 				break;
 
+		case CP_CMD_SUGGESTCONTACTS :
+				/* suggest contacts */
+				mxit_parse_cmd_suggestcontacts( session, &packet->records[2], packet->rcount - 2 );
+				break;
+
 		case CP_CMD_MOOD :
 				/* mood update */
 		case CP_CMD_UPDATE :
@@ -2145,6 +2296,7 @@
 				/* HTTP poll reply */
 		case CP_CMD_EXTPROFILE_SET :
 				/* profile update */
+				// TODO: Protocol 6.2 indicates status for each attribute, and current value.
 		case CP_CMD_SPLASHCLICK :
 				/* splash-screen clickthrough */
 		case CP_CMD_MSGEVENT :
@@ -2637,9 +2789,13 @@
 	if ( session->http_timer_id > 0 )
 		purple_timeout_remove( session->http_timer_id );
 
-	/* remove queue manager timer */
-	if ( session->q_timer > 0 )
-		purple_timeout_remove( session->q_timer );
+	/* remove slow queue manager timer */
+	if ( session->q_slow_timer_id > 0 )
+		purple_timeout_remove( session->q_slow_timer_id );
+
+	/* remove fast queue manager timer */
+	if ( session->q_fast_timer_id > 0 )
+		purple_timeout_remove( session->q_fast_timer_id );
 
 	/* remove all groupchat rooms */
 	while ( session->rooms != NULL ) {
@@ -2663,6 +2819,23 @@
 	g_list_free( session->active_chats );
 	session->active_chats = NULL;
 
+	/* clear the internal invites */
+	while ( session->invites != NULL ) {
+		struct contact* contact = (struct contact*) session->invites->data;
+
+		session->invites = g_list_remove( session->invites, contact );
+
+		if ( contact->msg )
+			g_free( contact->msg );
+		if ( contact->statusMsg )
+			g_free( contact->statusMsg );
+		if ( contact->profile )
+			g_free( contact->profile );
+		g_free( contact );
+	}
+	g_list_free( session->invites );
+	session->invites = NULL;
+
 	/* free profile information */
 	if ( session->profile )
 		free( session->profile );
--- a/libpurple/protocols/mxit/protocol.h	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/protocol.h	Tue Apr 26 13:43:24 2011 +0000
@@ -91,7 +91,7 @@
 #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_PROTO_VESION	60						/* client protocol version */
+#define		MXIT_CP_PROTO_VESION	63						/* client protocol version */
 
 /* set operating system name */
 #if defined( __APPLE__ )
@@ -110,7 +110,6 @@
 /* Client settings */
 #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 */
 #define		MXIT_DEFAULT_LOC		"planetpurple"			/* the default location for registration */
 
@@ -194,14 +193,19 @@
 #define		CP_PROFILE_REGCOUNTRY	"registeredcountry"		/* Registered Country Code (UTF8 String) */
 #define		CP_PROFILE_FLAGS		"flags"					/* Profile flags (Bitset) */
 #define		CP_PROFILE_LASTSEEN		"lastseen"				/* Last-Online timestamp */
+#define		CP_PROFILE_WHEREAMI		"whereami"				/* Where am I / Where I live */
+#define		CP_PROFILE_ABOUTME		"aboutme"				/* About me */
 
 /* extended profile field types */
-#define		CP_PROFILE_TYPE_BOOL	0x02					/* boolean profile attribute type */
-#define		CP_PROFILE_TYPE_INT		0x05					/* integer profile attribute type */
-#define		CP_PROFILE_TYPE_UTF8	0x0A					/* UTF8 string profile attribute type */
-#define		CP_PROFILE_TYPE_DATE	0x0B					/* date-time profile attribute type */
+#define		CP_PROFILE_TYPE_BOOL	0x02					/* boolean (0 or 1) */
+#define		CP_PROFILE_TYPE_INT		0x05					/* integer (32-bit) */
+#define		CP_PROFILE_TYPE_LONG	0x06					/* long (64-bit) */
+#define		CP_PROFILE_TYPE_UTF8	0x0A					/* UTF8 string */
+#define		CP_PROFILE_TYPE_DATE	0x0B					/* date-time (ISO 8601 format) */
 
 /* profile flags */
+#define		CP_PROF_NOT_SEARCHABLE	0x02					/* user cannot be searched for */
+#define		CP_PROF_NOT_SUGGESTABLE	0x08					/* user cannot be suggested as friend */
 #define		CP_PROF_DOBLOCKED		0x40					/* date-of-birth cannot be changed */
 
 /* suggestion types */
@@ -305,7 +309,7 @@
 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_invite( struct MXitSession* session, const char* username, gboolean mxitid, const char* alias, const char* groupname, const char* message );
 void mxit_send_remove( struct MXitSession* session, const char* username );
 void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias );
 void mxit_send_deny_sub( struct MXitSession* session, const char* username );
--- a/libpurple/protocols/mxit/roster.c	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/roster.c	Tue Apr 26 13:43:24 2011 +0000
@@ -564,8 +564,8 @@
 		buddy = g_slist_nth_data( list, i );
 
 		if ( !purple_buddy_get_protocol_data( buddy ) ) {
-			const gchar *alias = purple_buddy_get_alias( buddy );
-			const gchar *name = purple_buddy_get_name( buddy );
+			const gchar* alias = purple_buddy_get_alias( buddy );
+			const gchar* name = purple_buddy_get_name( buddy );
 
 			/* this buddy should be removed, because we did not receive him in our roster update from MXit */
 			purple_debug_info( MXIT_PLUGIN_ID, "Removed 'old' buddy from the blist '%s' (%s)\n", alias, name );
@@ -592,9 +592,16 @@
 	/* send a allow subscription packet to MXit */
 	mxit_send_allow_sub( invite->session, invite->contact->username, invite->contact->alias );
 
+	/* remove the invite from our internal invites list */
+	invite->session->invites = g_list_remove( invite->session->invites, invite->contact );
+
 	/* freeup invite object */
 	if ( invite->contact->msg )
 		g_free( invite->contact->msg );
+	if ( invite->contact->statusMsg )
+		g_free( invite->contact->statusMsg );
+	if ( invite->contact->profile )
+		g_free( invite->contact->profile );
 	g_free( invite->contact );
 	g_free( invite );
 }
@@ -614,9 +621,16 @@
 	/* send a deny subscription packet to MXit */
 	mxit_send_deny_sub( invite->session, invite->contact->username );
 
+	/* remove the invite from our internal invites list */
+	invite->session->invites = g_list_remove( invite->session->invites, invite->contact );
+
 	/* freeup invite object */
 	if ( invite->contact->msg )
 		g_free( invite->contact->msg );
+	if ( invite->contact->statusMsg )
+		g_free( invite->contact->statusMsg );
+	if ( invite->contact->profile )
+		g_free( invite->contact->profile );
 	g_free( invite->contact );
 	g_free( invite );
 }
@@ -639,12 +653,42 @@
 	invite->session = session;
 	invite->contact = contact;
 
+	/* add the invite to our internal invites list */
+	invite->session->invites = g_list_append( invite->session->invites, invite->contact );
+
 	/* (reference: "libpurple/account.h") */
 	purple_account_request_authorization( session->acc, contact->username, NULL, contact->alias, contact->msg, FALSE, mxit_cb_buddy_auth, mxit_cb_buddy_deny, invite );
 }
 
 
 /*------------------------------------------------------------------------
+ * Return the contact object for a mxit invite
+ *
+ *  @param session		The MXit session object
+ *  @param username		The username of the contact
+ *  @return				The contact object for the inviting user
+ */
+struct contact* get_mxit_invite_contact( struct MXitSession* session, const char* username )
+{
+	struct contact*		con		= NULL;
+	struct contact*		match	= NULL;
+	int					i;
+
+	/* run through all the invites and try and find the match */
+	for ( i = 0; i < g_list_length( session->invites ); i++ ) {
+		con = g_list_nth_data( session->invites, i );
+		if ( strcmp( con->username, username ) == 0 ) {
+			/* invite found */
+			match = con;
+			break;
+		}
+	}
+
+	return match;
+}
+
+
+/*------------------------------------------------------------------------
  * Return TRUE if this is a MXit Chatroom contact.
  *
  *  @param session		The MXit session object
@@ -680,8 +724,9 @@
  *  @param gc		The connection object
  *  @param buddy	The new buddy
  *  @param group	The group of the new buddy
+ *  @param message	The invite message
  */
-void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group )
+void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group, const char* message )
 {
 	struct MXitSession*	session	= (struct MXitSession*) gc->proto_data;
 	GSList*				list	= NULL;
@@ -702,7 +747,7 @@
 		 * you accept an invite.  so in that case the user is already
 		 * in our blist and ready to be chatted to.
 		 */
-		mxit_send_invite( session, buddy_name, buddy_alias, group_name );
+		mxit_send_invite( session, buddy_name, TRUE, buddy_alias, group_name, message );
 	}
 	else {
 		purple_debug_info( MXIT_PLUGIN_ID, "mxit_add_buddy (scenario 2) (list:%i)\n", g_slist_length( list ) );
--- a/libpurple/protocols/mxit/roster.h	Tue Apr 19 05:05:25 2011 +0000
+++ b/libpurple/protocols/mxit/roster.h	Tue Apr 26 13:43:24 2011 +0000
@@ -121,6 +121,10 @@
 	char		customMood[16];						/* custom mood */
 	char*		statusMsg;							/* status message */
 	char*		avatarId;							/* avatarId */
+
+	/* invites only */
+	void*		profile;							/* user's profile (if available) */
+	int			imgid;								/* avatar image id in the imgstore */
 };
 
 /* Presence / Status */
@@ -140,9 +144,10 @@
 void mxit_new_subscription( struct MXitSession* session, struct contact* contact );
 void mxit_update_blist( struct MXitSession* session );
 gboolean is_mxit_chatroom_contact( struct MXitSession* session, const char* username );
+struct contact* get_mxit_invite_contact( struct MXitSession* session, const char* username );
 
 /* libPurple callbacks */
-void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group );
+void mxit_add_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group, const char* message );
 void mxit_remove_buddy( PurpleConnection* gc, PurpleBuddy* buddy, PurpleGroup* group );
 void mxit_buddy_alias( PurpleConnection* gc, const char* who, const char* alias );
 void mxit_buddy_group( PurpleConnection* gc, const char* who, const char* old_group, const char* new_group );