libpurple/protocols/mxit/client.c

changeset 38887
826f5da7b56c
parent 38886
c1fb4e53da4e
parent 38341
3da74e727e78
child 38888
8bcae3a0b165
--- a/libpurple/protocols/mxit/client.c	Sun May 28 13:26:27 2017 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2968 +0,0 @@
-/*
- *					MXit Protocol libPurple Plugin
- *
- *			-- MXit client protocol implementation --
- *
- *				Pieter Loubser	<libpurple@mxit.com>
- *
- *			(C) Copyright 2009	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	"internal.h"
-#include	"debug.h"
-#include	"version.h"
-
-#include	"client.h"
-#include	"mxit.h"
-#include	"roster.h"
-#include	"chunk.h"
-#include	"filexfer.h"
-#include	"markup.h"
-#include	"multimx.h"
-#include	"splashscreen.h"
-#include	"login.h"
-#include	"formcmds.h"
-#include	"http.h"
-#include	"cipher.h"
-
-
-#define		MXIT_MS_OFFSET		3
-
-/* configure the right record terminator char to use */
-#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.
- *
- *  @param type			The type of notification:
- *		- info:		PURPLE_NOTIFY_MSG_INFO
- *		- warning:	PURPLE_NOTIFY_MSG_WARNING
- *		- error:	PURPLE_NOTIFY_MSG_ERROR
- *  @param heading		Heading text
- *  @param message		Message text
- */
-void mxit_popup( int type, const char* heading, const char* message )
-{
-	/* (reference: "libpurple/notify.h") */
-	purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL, NULL );
-}
-
-
-/*------------------------------------------------------------------------
- * For compatibility with legacy clients, all usernames are sent from MXit with a domain
- *  appended.  For MXit contacts, this domain is set to "@m".  This function strips
- *  those fake domains.
- *
- *  @param username		The username of the contact
- */
-void mxit_strip_domain( char* username )
-{
-	if ( g_str_has_suffix( username, "@m" ) )
-		username[ strlen( username ) - 2 ] = '\0';
-}
-
-
-/*------------------------------------------------------------------------
- * Dump a byte buffer to the console for debugging purposes.
- *
- *  @param buf			The data
- *  @param len			The data length
- */
-void dump_bytes( struct MXitSession* session, const char* buf, int len )
-{
-	char*	msg	= g_malloc0( len + 1 );
-	int		i;
-
-	for ( i = 0; i < len; i++ ) {
-		char ch	= buf[i];
-
-		if ( ch == CP_REC_TERM )		/* record terminator */
-			msg[i] = '!';
-		else if ( ch == CP_FLD_TERM )	/* field terminator */
-			msg[i] = '^';
-		else if ( ch == CP_PKT_TERM )	/* packet terminator */
-			msg[i] = '@';
-		else if ( ( ch < 0x20 ) || ( ch > 0x7E ) )		/* non-printable character */
-			msg[i] = '_';
-		else
-			msg[i] = ch;
-	}
-
-	purple_debug_info( MXIT_PLUGIN_ID, "DUMP: '%s'\n", msg );
-
-	g_free( msg );
-}
-
-
-/*------------------------------------------------------------------------
- * Determine if we have an active chat with a specific contact
- *
- *  @param session		The MXit session object
- *  @param who			The contact name
- *  @return				Return true if we have an active chat with the contact
- */
-gboolean find_active_chat( const GList* chats, const char* who )
-{
-	const GList*	list	= chats;
-	const char*		chat	= NULL;
-
-	while ( list ) {
-		chat = (const char*) list->data;
-
-		if ( strcmp( chat, who ) == 0 )
-			return TRUE;
-
-		list = g_list_next( list );
-	}
-
-	return FALSE;
-}
-
-
-/*------------------------------------------------------------------------
- * scnprintf
- *
- *	@param string		The destination buffer.
- *	@param size			The maximum size of the destination buffer.
- * 	@param format		The format string
- *	@param ...			The parameters to the format string.
- *	@return				The number of characters actually stored in the buffer.
- */
-static int scnprintf( gchar* string, size_t size, const char *format, ... )
-{
-	va_list args;
-	guint i;
-
-	va_start( args, format );
-	i = g_vsnprintf( string, size, format, args );
-	va_end( args );
-
-	if ( i < size )
-		return i;
-	else if ( size > 0 )		/* destination buffer too short - return number of characters actually inserted */
-		return size - 1;
-	else
-		return 0;
-}
-
-
-
-/*========================================================================================================================
- * Low-level Packet transmission
- */
-
-/*------------------------------------------------------------------------
- * Remove next packet from transmission queue.
- *
- *  @param session		The MXit session object
- *  @return				The next packet for transmission (or NULL)
- */
-static struct tx_packet* pop_tx_packet( struct MXitSession* session )
-{
-	struct tx_packet*	packet	= NULL;
-
-	if ( session->queue.count > 0 ) {
-		/* dequeue the next packet */
-		packet = session->queue.packets[session->queue.rd_i];
-		session->queue.packets[session->queue.rd_i] = NULL;
-		session->queue.rd_i = ( session->queue.rd_i + 1 ) % MAX_QUEUE_SIZE;
-		session->queue.count--;
-	}
-
-	return packet;
-}
-
-
-/*------------------------------------------------------------------------
- * Add packet to transmission queue.
- *
- *  @param session		The MXit session object
- *  @param packet		The packet to transmit
- *  @return				Return TRUE if packet was enqueue, or FALSE if queue is full.
- */
-static gboolean push_tx_packet( struct MXitSession* session, struct tx_packet* packet )
-{
-	if ( session->queue.count < MAX_QUEUE_SIZE ) {
-		/* enqueue packet */
-		session->queue.packets[session->queue.wr_i] = packet;
-		session->queue.wr_i = ( session->queue.wr_i + 1 ) % MAX_QUEUE_SIZE;
-		session->queue.count++;
-		return TRUE;
-	}
-	else
-		return FALSE;		/* queue is full */
-}
-
-
-/*------------------------------------------------------------------------
- * Deallocate transmission packet.
- *
- *  @param packet		The packet to deallocate.
- */
-static void free_tx_packet( struct tx_packet* packet )
-{
-	g_free( packet->data );
-	g_free( packet );
-	packet = NULL;
-}
-
-
-/*------------------------------------------------------------------------
- * Flush all the packets from the tx queue and release the resources.
- *
- *  @param session		The MXit session object
- */
-static void flush_queue( struct MXitSession* session )
-{
-	struct tx_packet*	packet;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "flushing the tx queue\n" );
-
-	while ( (packet = pop_tx_packet( session ) ) != NULL )
-		free_tx_packet( packet );
-}
-
-
-/*------------------------------------------------------------------------
- * TX Step 3: Write the packet data to the TCP connection.
- *
- *  @param fd			The file descriptor
- *  @param pktdata		The packet data
- *  @param pktlen		The length of the packet data
- *  @return				Return -1 on error, otherwise 0
- */
-static int mxit_write_sock_packet( int fd, const char* pktdata, int pktlen )
-{
-	int		written;
-	int		res;
-
-	written = 0;
-	while ( written < pktlen ) {
-		res = write( fd, &pktdata[written], pktlen - written );
-		if ( res <= 0 ) {
-			/* error on socket */
-			if ( errno == EAGAIN )
-				continue;
-
-			purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to MXit server (%i)\n", res );
-			return -1;
-		}
-		written += res;
-	}
-
-	return 0;
-}
-
-
-/**
- * Callback called for handling a HTTP GET response
- *
- * @param http_conn http api object (see http.h)
- * @param response  http api object (see http.h)
- * @param _session  The MXit session object
- */
-static void
-mxit_cb_http_rx(PurpleHttpConnection *http_conn, PurpleHttpResponse *response,
-	gpointer _session)
-{
-	struct MXitSession *session = _session;
-	const gchar *got_data;
-	size_t got_len;
-
-	if (!purple_http_response_is_successful(response)) {
-		purple_debug_error(MXIT_PLUGIN_ID, "HTTP response error (%s)\n",
-			purple_http_response_get_error(response));
-		return;
-	}
-
-	/* convert the HTTP result */
-	got_data = purple_http_response_get_data(response, &got_len);
-	memcpy(session->rx_dbuf, got_data, got_len);
-	session->rx_i = got_len;
-
-	mxit_parse_packet(session);
-}
-
-
-/**
- * TX Step 3: Write the packet data to the HTTP connection (GET style).
- *
- * @param session The MXit session object
- * @param packet  The packet data
- */
-static void
-mxit_write_http_get(struct MXitSession* session, struct tx_packet* packet)
-{
-	PurpleHttpRequest *req;
-	char *part = NULL;
-
-	if (packet->datalen > 0) {
-		char *tmp;
-
-		tmp = g_strndup(packet->data, packet->datalen);
-		part = g_strdup(purple_url_encode(tmp));
-		g_free(tmp);
-	}
-
-	req = purple_http_request_new(NULL);
-	purple_http_request_set_url_printf(req, "%s?%s%s", session->http_server,
-		purple_url_encode(packet->header), part ? part : "");
-	purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
-	purple_http_connection_set_add(session->async_http_reqs,
-		purple_http_request(session->con, req, mxit_cb_http_rx,
-			session));
-	purple_http_request_unref(req);
-
-	g_free(part);
-}
-
-
-/**
- * TX Step 3: Write the packet data to the HTTP connection (POST style).
- *
- * @param session The MXit session object
- * @param packet  The packet data
- */
-static void
-mxit_write_http_post(struct MXitSession* session, struct tx_packet* packet)
-{
-	PurpleHttpRequest *req;
-
-	/* strip off the last '&' from the header */
-	packet->header[packet->headerlen - 1] = '\0';
-	packet->headerlen--;
-
-	req = purple_http_request_new(NULL);
-	purple_http_request_set_url_printf(req, "%s?%s", session->http_server,
-		purple_url_encode(packet->header));
-	purple_http_request_set_method(req, "POST");
-	purple_http_request_header_set(req, "User-Agent", MXIT_HTTP_USERAGENT);
-	purple_http_request_header_set(req, "Content-Type",
-		"application/octet-stream");
-	purple_http_request_set_contents(req, packet->data + MXIT_MS_OFFSET,
-		packet->datalen - MXIT_MS_OFFSET);
-	purple_http_connection_set_add(session->async_http_reqs,
-		purple_http_request(session->con, req, mxit_cb_http_rx,
-			session));
-	purple_http_request_unref(req);
-}
-
-
-/*------------------------------------------------------------------------
- * TX Step 2: Handle the transmission of the packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param packet		The packet to transmit
- */
-static void mxit_send_packet( struct MXitSession* session, struct tx_packet* packet )
-{
-	int		res;
-
-	if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) {
-		/* we are not connected so ignore all packets to be send */
-		purple_debug_error( MXIT_PLUGIN_ID, "Dropping TX packet (we are not connected)\n" );
-		return;
-	}
-
-	purple_debug_info( MXIT_PLUGIN_ID, "Packet send CMD:%i (%i)\n", packet->cmd, packet->headerlen + packet->datalen );
-#ifdef	DEBUG_PROTOCOL
-	dump_bytes( session, packet->header, packet->headerlen );
-	dump_bytes( session, packet->data, packet->datalen );
-#endif
-
-	if ( !session->http ) {
-		/* socket connection */
-		char		data[packet->datalen + packet->headerlen];
-		int			datalen;
-
-		/* create raw data buffer */
-		memcpy( data, packet->header, packet->headerlen );
-		memcpy( data + packet->headerlen, packet->data, packet->datalen );
-		datalen = packet->headerlen + packet->datalen;
-
-		res = mxit_write_sock_packet( session->fd, data, datalen );
-		if ( res < 0 ) {
-			/* we must have lost the connection, so terminate it so that we can reconnect */
-			purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "We have lost the connection to MXit. Please reconnect." ) );
-		}
-	}
-	else {
-		/* http connection */
-
-		if ( packet->cmd == CP_CMD_MEDIA ) {
-			/* multimedia packets must be send with a HTTP POST */
-			mxit_write_http_post( session, packet );
-		}
-		else {
-			mxit_write_http_get( session, packet );
-		}
-	}
-
-	/* update the timestamp of the last-transmitted packet */
-	session->last_tx = mxit_now_milli();
-
-	/*
-	 * we need to remember that we are still waiting for the ACK from
-	 * the server on this request
-	 */
-	session->outack = packet->cmd;
-
-	/* free up the packet resources */
-	free_tx_packet( packet );
-}
-
-
-/*------------------------------------------------------------------------
- * TX Step 1: Create a new Tx packet and queue it for sending.
- *
- *  @param session		The MXit session object
- *  @param data			The packet data (payload)
- *  @param datalen		The length of the packet data
- *  @param cmd			The MXit command for this packet
- */
-static void mxit_queue_packet( struct MXitSession* session, const char* data, int datalen, int cmd )
-{
-	struct tx_packet*	packet;
-	char				header[256];
-	int					hlen;
-
-	/* create a packet for sending */
-	packet = g_new0( struct tx_packet, 1 );
-	packet->data = g_malloc0( datalen );
-	packet->cmd = cmd;
-	packet->headerlen = 0;
-
-	/* create generic packet header */
-	hlen = scnprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM );	/* client mxitid */
-
-	if ( session->http ) {
-		/* http connection only */
-		hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "s=" );
-		if ( session->http_sesid > 0 ) {
-			hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_sesid, CP_FLD_TERM );	/* http session id */
-		}
-		session->http_seqno++;
-		hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "%u%c", session->http_seqno, CP_REC_TERM );		/* http request sequence id */
-	}
-
-	hlen += scnprintf( header + hlen, sizeof( header ) - hlen, "cm=%i%c", cmd, CP_REC_TERM ); 						/* packet command */
-
-	if ( !session->http ) {
-		/* socket connection only */
-		packet->headerlen = scnprintf( packet->header, sizeof( packet->header ), "ln=%i%c", ( datalen + hlen ), CP_REC_TERM );		/* packet length */
-	}
-
-	/* copy the header to packet */
-	memcpy( packet->header + packet->headerlen, header, hlen );
-	packet->headerlen += hlen;
-
-	/* copy payload to packet */
-	if ( datalen > 0 )
-		memcpy( packet->data, data, datalen );
-	packet->datalen = datalen;
-
-
-	/* 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 {
-		/* 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 */
-			free_tx_packet( packet );
-			return;
-		}
-
-		purple_debug_info( MXIT_PLUGIN_ID, "queueing packet for later sending cmd=%i\n", cmd );
-		if ( !push_tx_packet( session, packet ) ) {
-			/* packet could not be queued for transmission */
-			mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Message Send Error" ), _( "Unable to process your request at this time" ) );
-			free_tx_packet( packet );
-		}
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Manage the packet send queue (send next packet, timeout's, etc).
- *
- *  @param session		The MXit session object
- */
-static void mxit_manage_queue( struct MXitSession* session )
-{
-	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;
-	}
-	else if ( session->outack > 0 ) {
-		/* we are still waiting for an outstanding ACK from the MXit server */
-		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, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Timeout while waiting for a response from the MXit server." ) );
-		}
-		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->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 );
-			}
-		}
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * 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;
-
-	session->q_fast_timer_id = 0;
-	mxit_manage_queue( session );
-
-	/* stop running */
-	return FALSE;
-}
-
-
-/*------------------------------------------------------------------------
- * Callback to manage HTTP server polling (HTTP connections ONLY)
- *
- *  @param session		The MXit session object
- */
-gboolean mxit_manage_polling( gpointer user_data )
-{
-	struct MXitSession* session		= (struct MXitSession*) user_data;
-	gboolean			poll		= FALSE;
-	gint64				now			= mxit_now_milli();
-	gint64				rxdiff;
-
-	if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) {
-		/* we only poll if we are actually logged in */
-		return TRUE;
-	}
-
-	/* calculate the time differences */
-	rxdiff = now - session->last_rx;
-
-	if ( rxdiff < MXIT_HTTP_POLL_MIN ) {
-		/* we received some reply a few moments ago, so reset the poll interval */
-		session->http_interval = MXIT_HTTP_POLL_MIN;
-	}
-	else if ( session->http_last_poll < ( now - session->http_interval ) ) {
-		/* time to poll again */
-		poll = TRUE;
-
-		/* back-off some more with the polling */
-		session->http_interval = session->http_interval + ( session->http_interval / 2 );
-		if ( session->http_interval > MXIT_HTTP_POLL_MAX )
-			session->http_interval = MXIT_HTTP_POLL_MAX;
-	}
-
-	/* debugging */
-	//purple_debug_info( MXIT_PLUGIN_ID, "POLL TIMER: %i (%i)\n", session->http_interval, rxdiff );
-
-	if ( poll ) {
-		/* send poll request */
-		session->http_last_poll = mxit_now_milli();
-		mxit_send_poll( session );
-	}
-
-	return TRUE;
-}
-
-
-/*========================================================================================================================
- * Send MXit operations.
- */
-
-/*------------------------------------------------------------------------
- * Send a ping/keepalive packet to MXit server.
- *
- *  @param session		The MXit session object
- */
-void mxit_send_ping( struct MXitSession* session )
-{
-	/* queue packet for transmission */
-	mxit_queue_packet( session, NULL, 0, CP_CMD_PING );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a poll request to the HTTP server (HTTP connections ONLY).
- *
- *  @param session		The MXit session object
- */
-void mxit_send_poll( struct MXitSession* session )
-{
-	/* queue packet for transmission */
-	mxit_queue_packet( session, NULL, 0, CP_CMD_POLL );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a logout packet to the MXit server.
- *
- *  @param session		The MXit session object
- */
-void mxit_send_logout( struct MXitSession* session )
-{
-	/* queue packet for transmission */
-	mxit_queue_packet( session, NULL, 0, CP_CMD_LOGOUT );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a register packet to the MXit server.
- *
- *  @param session		The MXit session object
- */
-void mxit_send_register( struct MXitSession* session )
-{
-	struct MXitProfile*	profile		= session->profile;
-	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 );
-
-	/* 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 = scnprintf( 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 */
-								"%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,
-								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 );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a login packet to the MXit server.
- *
- *  @param session		The MXit session object
- */
-void mxit_send_login( struct MXitSession* session )
-{
-	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 );
-
-	/* 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 = scnprintf( 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, 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
-	);
-
-	/* include "custom resource" information */
-	splashId = splash_current( session );
-	if ( splashId != NULL )
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%ccr=%s", CP_REC_TERM, splashId );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN );
-
-	g_free( clientVersion );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a chat message packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param to			The username of the recipient
- *  @param msg			The message text
- */
-void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup, gboolean is_command )
-{
-	char		data[CP_MAX_PACKET];
-	char*		markuped_msg;
-	int			datalen;
-	int			msgtype = ( is_command ? CP_MSGTYPE_COMMAND : CP_MSGTYPE_NORMAL );
-
-	/* first we need to convert the markup from libPurple to MXit format */
-	if ( parse_markup )
-		markuped_msg = mxit_convert_markup_tx( msg, &msgtype );
-	else
-		markuped_msg = g_strdup( msg );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%s%c%i%c%i",		/* "ms"=jid\1msg\1type\1flags */
-								to, CP_FLD_TERM, markuped_msg, CP_FLD_TERM, msgtype, CP_FLD_TERM, CP_MSG_MARKUP | CP_MSG_EMOTICON
-	);
-
-	/* free the resources */
-	g_free( markuped_msg );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_TX_MSG );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a extended profile request packet to the MXit server.
- *
- *  @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 attribute	The names of the attributes
- */
-void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] )
-{
-	char			data[CP_MAX_PACKET];
-	int				datalen;
-	unsigned int	i;
-
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%i",		/* "ms="mxitid\1nr_attributes */
-								( username ? username : "" ), CP_FLD_TERM, nr_attrib
-	);
-
-	/* add attributes */
-	for ( i = 0; i < nr_attrib; i++ )
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, attribute[i] );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_GET );
-}
-
-
-/*------------------------------------------------------------------------
- * Send an update profile packet to the MXit server.
- *
- *  @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 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					= NULL;
-	int				datalen;
-	unsigned int	i;
-
-	if ( attributes )
-		parts = g_strsplit( attributes, "\01", 1 + ( nr_attrib * 3 ) );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%i",	/* "ms"=password\1nr_attibutes  */
-								( password ) ? password : "", CP_FLD_TERM, nr_attrib
-	);
-
-	/* add attributes */
-	for ( i = 1; i < nr_attrib * 3; i+=3 ) {
-		if ( parts == NULL || parts[i] == NULL || parts[i + 1] == NULL || parts[i + 2] == NULL ) {
-			purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile update attributes = '%s' - nbr=%u\n", attributes, nr_attrib );
-			g_strfreev( parts );
-			return;
-		}
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen,
-								"%c%s%c%s%c%s",		/* \1name\1type\1value  */
-								CP_FLD_TERM, parts[i], CP_FLD_TERM, parts[i + 1], CP_FLD_TERM, parts[i + 2] );
-	}
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_SET );
-
-	/* freeup the memory */
-	g_strfreev( parts );
-}
-
-
-/*------------------------------------------------------------------------
- * 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 = scnprintf( data, sizeof( data ),
-								"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++ )
-		datalen += scnprintf( data + datalen, sizeof( 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 = scnprintf( data, sizeof( data ),
-								"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++ )
-		datalen += scnprintf( data + datalen, sizeof( 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
- *  @param presence		The presence (as per MXit types)
- *  @param statusmsg	The status message (can be NULL)
- */
-void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%i%c",					/* "ms"=show\1status */
-								presence, CP_FLD_TERM
-	);
-
-	/* append status message (if one is set) */
-	if ( statusmsg )
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%s", statusmsg );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_STATUS );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a mood update packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param mood			The mood (as per MXit types)
- */
-void mxit_send_mood( struct MXitSession* session, int mood )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%i",	/* "ms"=mood */
-								mood
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MOOD );
-}
-
-
-/*------------------------------------------------------------------------
- * Send an invite contact packet to the MXit server.
- *
- *  @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, 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 = scnprintf( data, sizeof( data ),
-								"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,
-								( message ? message : "" ), CP_FLD_TERM,
-								( mxitid ? 0 : 1 )
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_INVITE );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a remove contact packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param username		The username of the contact being removed
- */
-void mxit_send_remove( struct MXitSession* session, const char* username )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s",	/* "ms"=username */
-								username
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_REMOVE );
-}
-
-
-/*------------------------------------------------------------------------
- * Send an accept subscription (invite) packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param username		The username of the contact being accepted
- *  @param alias		Our alias for the contact
- */
-void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%s%c%s",	/* "ms"=username\1group\1alias */
-								username, CP_FLD_TERM, "", CP_FLD_TERM, alias
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_ALLOW );
-}
-
-
-/*------------------------------------------------------------------------
- * Send an deny subscription (invite) packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param username		The username of the contact being denied
- *  @param reason		The message describing the reason for the rejection (can be NULL).
- */
-void mxit_send_deny_sub( struct MXitSession* session, const char* username, const char* reason )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s",	/* "ms"=username */
-								username
-	);
-
-	/* append reason (if one is set) */
-	if ( reason )
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, reason );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_DENY );
-}
-
-
-/*------------------------------------------------------------------------
- * Send an update contact packet to the MXit server.
- *
- *  @param session		The MXit session object
- *  @param username		The username of the contact being denied
- *  @param alias		Our alias for the contact
- *  @param groupname	Group in which contact should be stored.
- */
-void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%s%c%s",	/* "ms"=groupname\1username\1alias */
-								groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_UPDATE );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a splash-screen click event packet.
- *
- *  @param session		The MXit session object
- *  @param splashid		The identifier of the splash-screen
- */
-void mxit_send_splashclick( struct MXitSession* session, const char* splashid )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s",	/* "ms"=splashId */
-								splashid
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_SPLASHCLICK );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a message event packet.
- *
- *  @param session		The MXit session object
- *  @param to           The username of the original sender (ie, recipient of the event)
- *  @param id			The identifier of the event (received in message)
- *  @param event		Identified the type of event
- */
-void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_msgevent: to=%s id=%s event=%i\n", to, id, event );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%s%c%i",		/* "ms"=contactAddress \1 id \1 event */
-								to, CP_FLD_TERM, id, CP_FLD_TERM, event
-	);
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MSGEVENT );
-}
-
-
-/*------------------------------------------------------------------------
- * Send packet to create a MultiMX room.
- *
- *  @param session		The MXit session object
- *  @param groupname	Name of the room to create
- *  @param nr_usernames	Number of users in initial invite
- *  @param usernames	The usernames of the users in the initial invite
- */
-void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] )
-{
-	char		data[CP_MAX_PACKET];
-	int			datalen;
-	int			i;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%i",	/* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */
-								groupname, CP_FLD_TERM, nr_usernames
-	);
-
-	/* add usernames */
-	for ( i = 0; i < nr_usernames; i++ )
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_CREATE );
-}
-
-
-/*------------------------------------------------------------------------
- * Send packet to invite users to existing MultiMX room.
- *
- *  @param session		The MXit session object
- *  @param roomid		The unique RoomID for the MultiMx room.
- *  @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];
-	int			datalen;
-	int			i;
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ),
-								"ms=%s%c%i",	/* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */
-								roomid, CP_FLD_TERM, nr_usernames
-	);
-
-	/* add usernames */
-	for ( i = 0; i < nr_usernames; i++ )
-		datalen += scnprintf( data + datalen, sizeof( data ) - datalen, "%c%s", CP_FLD_TERM, usernames[i] );
-
-	/* queue packet for transmission */
-	mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_INVITE );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a "send file direct" multimedia packet.
- *
- *  @param session		The MXit session object
- *  @param username		The username of the recipient
- *  @param filename		The name of the file being sent
- *  @param buf			The content of the file
- *  @param buflen		The length of the file contents
- */
-void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, size_t buflen )
-{
-	char				data[CP_MAX_PACKET];
-	int					datalen		= 0;
-	gchar*				chunk;
-	size_t				chunksize;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "SENDING FILE '%s' of %zu bytes to user '%s'\n", filename, buflen, username );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ), "ms=" );
-
-	/* map chunk header over data buffer */
-	chunk = &data[datalen];
-
-	/* encode chunk */
-	chunksize = mxit_chunk_create_senddirect( chunk_data( chunk ), username, filename, buf, buflen );
-	set_chunk_type( chunk, CP_CHUNK_DIRECT_SND );
-	set_chunk_length( chunk, chunksize );
-	datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
-
-	/* send the byte stream to the mxit server */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a "reject file" multimedia packet.
- *
- *  @param session		The MXit session object
- *  @param fileid		A unique ID that identifies this file
- */
-void mxit_send_file_reject( struct MXitSession* session, const char* fileid )
-{
-	char				data[CP_MAX_PACKET];
-	int					datalen		= 0;
-	gchar*				chunk;
-	size_t				chunksize;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_reject\n" );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ), "ms=" );
-
-	/* map chunk header over data buffer */
-	chunk = &data[datalen];
-
-	/* encode chunk */
-	chunksize = mxit_chunk_create_reject( chunk_data( chunk ), fileid );
-	set_chunk_type( chunk, CP_CHUNK_REJECT );
-	set_chunk_length( chunk, chunksize );
-	datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
-
-	/* send the byte stream to the mxit server */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a "get file" multimedia packet.
- *
- *  @param session		The MXit session object
- *  @param fileid		A unique ID that identifies this file
- *  @param filesize		The number of bytes to retrieve
- *  @param offset		Offset in file at which to start retrieving
- */
-void mxit_send_file_accept( struct MXitSession* session, const char* fileid, size_t filesize, size_t offset )
-{
-	char				data[CP_MAX_PACKET];
-	int					datalen		= 0;
-	gchar*				chunk;
-	size_t				chunksize;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_accept\n" );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ), "ms=" );
-
-	/* map chunk header over data buffer */
-	chunk = &data[datalen];
-
-	/* encode chunk */
-	chunksize = mxit_chunk_create_get( chunk_data(chunk), fileid, filesize, offset );
-	set_chunk_type( chunk, CP_CHUNK_GET );
-	set_chunk_length( chunk, chunksize );
-	datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
-
-	/* send the byte stream to the mxit server */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a "received file" multimedia packet.
- *
- *  @param session		The MXit session object
- *  @param status		The status of the file-transfer
- */
-void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status )
-{
-	char				data[CP_MAX_PACKET];
-	int					datalen		= 0;
-	gchar*				chunk;
-	size_t				chunksize;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_received\n" );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ), "ms=" );
-
-	/* map chunk header over data buffer */
-	chunk = &data[datalen];
-
-	/* encode chunk */
-	chunksize = mxit_chunk_create_received( chunk_data(chunk), fileid, status );
-	set_chunk_type( chunk, CP_CHUNK_RECEIVED );
-	set_chunk_length( chunk, chunksize );
-	datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
-
-	/* send the byte stream to the mxit server */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a "set avatar" multimedia packet.
- *
- *  @param session		The MXit session object
- *  @param data			The avatar data
- *  @param buflen		The length of the avatar data
- */
-void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, size_t avatarlen )
-{
-	char				data[CP_MAX_PACKET];
-	int					datalen		= 0;
-	gchar*				chunk;
-	size_t				chunksize;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_avatar: %zu bytes\n", avatarlen );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ), "ms=" );
-
-	/* map chunk header over data buffer */
-	chunk = &data[datalen];
-
-	/* encode chunk */
-	chunksize = mxit_chunk_create_set_avatar( chunk_data(chunk), avatar, avatarlen );
-	set_chunk_type( chunk, CP_CHUNK_SET_AVATAR );
-	set_chunk_length( chunk, chunksize );
-	datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
-
-	/* send the byte stream to the mxit server */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
-}
-
-
-/*------------------------------------------------------------------------
- * Send a "get avatar" multimedia packet.
- *
- *  @param session		The MXit session object
- *  @param mxitId		The username who's avatar to request
- *  @param avatarId		The id of the avatar image (as string)
- *  @param data			The avatar data
- *  @param buflen		The length of the avatar data
- */
-void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId )
-{
-	char				data[CP_MAX_PACKET];
-	int					datalen		= 0;
-	gchar*				chunk;
-	size_t				chunksize;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_avatar: %s\n", mxitId );
-
-	/* convert the packet to a byte stream */
-	datalen = scnprintf( data, sizeof( data ), "ms=" );
-
-	/* map chunk header over data buffer */
-	chunk = &data[datalen];
-
-	/* encode chunk */
-	chunksize = mxit_chunk_create_get_avatar( chunk_data(chunk), mxitId, avatarId );
-	set_chunk_type( chunk, CP_CHUNK_GET_AVATAR );
-	set_chunk_length( chunk, chunksize );
-	datalen += MXIT_CHUNK_HEADER_SIZE + chunksize;
-
-	/* send the byte stream to the mxit server */
-	mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA );
-}
-
-
-/*------------------------------------------------------------------------
- * Process a login message 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_login( struct MXitSession* session, struct record** records, int rcount )
-{
-	PurpleStatus*	status;
-	int				presence;
-	const char*		statusmsg;
-	const char*		profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME,
-									CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL,
-									CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_RELATIONSHIP, CP_PROFILE_FLAGS };
-
-	purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
-
-	/* we were not yet logged in so we need to complete the login sequence here */
-	session->flags |= MXIT_FLAG_LOGGEDIN;
-	purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 );
-	purple_connection_set_state( session->con, PURPLE_CONNECTION_CONNECTED );
-
-	/* save extra info if this is a HTTP connection */
-	if ( session->http ) {
-		/* save the http server to use for this session */
-		g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) );
-
-		/* save the session id */
-		session->http_sesid = atoi( records[0]->fields[0]->data );
-	}
-
-	/* extract UserId (from protocol 5.9) */
-	if ( records[1]->fcount >= 9 )
-		session->uid = g_strdup( records[1]->fields[8]->data );
-
-	/* display the current splash-screen */
-	if ( splash_popup_enabled( session ) )
-		splash_display( session );
-
-	/* update presence status */
-	status = purple_account_get_active_status( session->acc );
-	presence = mxit_convert_presence( purple_status_get_id( status ) );
-	statusmsg = purple_status_get_attr_string( status, "message" );
-
-	if ( ( presence != MXIT_PRESENCE_ONLINE ) || ( statusmsg ) ) {
-		/* when logging into MXit, your default presence is online. but with the UI, one can change
-		 * the presence to whatever. in the case where its changed to a different presence setting
-		 * we need to send an update to the server, otherwise the user's presence will be out of
-		 * sync between the UI and MXit.
-		 */
-		char* statusmsg1 = purple_markup_strip_html( statusmsg );
-		char* statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG );
-
-		mxit_send_presence( session, presence, statusmsg2 );
-
-		g_free( statusmsg1 );
-		g_free( statusmsg2 );
-	}
-
-	/* retrieve our MXit profile */
-	mxit_send_extprofile_request( session, NULL, ARRAY_SIZE( profilelist ), profilelist );
-}
-
-
-/*------------------------------------------------------------------------
- * Process a received message 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_message( struct MXitSession* session, struct record** records, int rcount )
-{
-	struct RXMsgData*	mx			= NULL;
-	char*				message		= NULL;
-	char*				sender		= NULL;
-	int					msglen		= 0;
-	int					msgflags	= 0;
-	int					msgtype		= 0;
-
-	if ( ( rcount == 1 ) || ( records[0]->fcount < 2 ) || ( records[1]->fcount == 0 ) || ( records[1]->fields[0]->len == 0 ) ) {
-		/* packet contains no message or an empty message */
-		return;
-	}
-
-	message = records[1]->fields[0]->data;
-	msglen = strlen( message );
-
-	/* strip off dummy domain */
-	sender = records[0]->fields[0]->data;
-	mxit_strip_domain( sender );
-
-#ifdef	DEBUG_PROTOCOL
-	purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", sender );
-#endif
-
-	/* decode message flags (if any) */
-	if ( records[0]->fcount >= 5 )
-		msgflags = atoi( records[0]->fields[4]->data );
-	msgtype = atoi( records[0]->fields[2]->data );
-
-	if ( msgflags & CP_MSG_PWD_ENCRYPTED ) {
-		/* this is a password encrypted message. we do not currently support those so ignore it */
-		PurpleBuddy*	buddy;
-		const char*		name;
-		char			msg[128];
-
-		buddy = purple_blist_find_buddy( session->acc, sender );
-		if ( buddy )
-			name = purple_buddy_get_alias( buddy );
-		else
-			name = sender;
-		g_snprintf( msg, sizeof( msg ), _( "%s sent you an encrypted message, but it is not supported on this client." ), name );
-		mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), msg );
-		return;
-	}
-	else if ( msgflags & CP_MSG_TL_ENCRYPTED ) {
-		/* This is a transport-layer encrypted message. We don't support
-		 * it anymore, because original client doesn't look like it was. */
-		purple_serv_got_im(session->con, sender,
-			_("An encrypted message was received which could not be decrypted."),
-			PURPLE_MESSAGE_ERROR, time(NULL));
-		return;
-	}
-
-	if ( msgflags & CP_MSG_NOTIFY_DELIVERY ) {
-		/* delivery notification is requested */
-		if ( records[0]->fcount >= 4 )
-			mxit_send_msgevent( session, sender, records[0]->fields[3]->data, CP_MSGEVENT_DELIVERED );
-	}
-
-	/* create and initialise new markup struct */
-	mx = g_new0( struct RXMsgData, 1 );
-	mx->msg = g_string_sized_new( msglen );
-	mx->session = session;
-	mx->from = g_strdup( sender );
-	mx->timestamp = atoi( records[0]->fields[1]->data );
-	mx->got_img = FALSE;
-	mx->chatid = -1;
-	mx->img_count = 0;
-
-	/* update list of active chats */
-	if ( !find_active_chat( session->active_chats, mx->from ) ) {
-		session->active_chats = g_list_append( session->active_chats, g_strdup( mx->from ) );
-	}
-
-	if ( is_multimx_contact( session, mx->from ) ) {
-		/* this is a MultiMx chatroom message */
-		multimx_message_received( mx, message, msglen, msgtype, msgflags );
-	}
-	else {
-		mxit_parse_markup( mx, message, msglen, msgtype, msgflags );
-	}
-
-	/* we are now done parsing the message */
-	mx->converted = TRUE;
-	if ( mx->img_count == 0 ) {
-		/* we have all the data we need for this message to be displayed now. */
-		mxit_show_message( mx );
-	}
-	else {
-		/* this means there are still images outstanding for this message and
-		 * still need to wait for them before we can display the message.
-		 * so the image received callback function will eventually display
-		 * the message. */
-	}
-
-	/* cleanup */
-	if ( msgflags & CP_MSG_TL_ENCRYPTED )
-		g_free( message );
-}
-
-
-/*------------------------------------------------------------------------
- * Process a received subscription request 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_new_sub( struct MXitSession* session, struct record** records, int rcount )
-{
-	struct contact*		contact;
-	struct record*		rec;
-	int					i;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_new_sub (%i recs)\n", rcount );
-
-	for ( i = 0; i < rcount; i++ ) {
-		rec = records[i];
-
-		if ( rec->fcount < 4 ) {
-			purple_debug_error( MXIT_PLUGIN_ID, "BAD SUBSCRIPTION RECORD! %i fields\n", rec->fcount );
-			break;
-		}
-
-		/* build up a new contact info struct */
-		contact = g_new0( struct contact, 1 );
-
-		g_strlcpy( contact->username, rec->fields[0]->data, sizeof( contact->username ) );
-		mxit_strip_domain( contact->username );				/* remove dummy domain */
-		g_strlcpy( contact->alias, rec->fields[1]->data, sizeof( contact->alias ) );
-		contact->type = atoi( rec->fields[2]->data );
-
-		if ( rec->fcount >= 5 ) {
-			/* there is a personal invite message attached */
-			if ( ( rec->fields[4]->data ) && ( *rec->fields[4]->data ) )
-				contact->msg = strdup( rec->fields[4]->data );
-		}
-
-		/* handle the subscription */
-		if ( contact-> type == MXIT_TYPE_MULTIMX ) {		/* subscription to a MultiMX room */
-			char* creator = NULL;
-
-			if ( rec->fcount >= 6 )
-				creator = rec->fields[5]->data;
-
-			multimx_invite( session, contact, creator );
-		}
-		else
-			mxit_new_subscription( session, contact );
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Parse the received presence value, and ensure that it is supported.
- *
- *  @param value		The received presence value.
- *  @return				A valid presence value.
- */
-static short mxit_parse_presence( const char* value )
-{
-	short presence = atoi( value );
-
-	/* ensure that the presence value is valid */
-	switch ( presence ) {
-		case MXIT_PRESENCE_OFFLINE :
-		case MXIT_PRESENCE_ONLINE :
-		case MXIT_PRESENCE_AWAY :
-		case MXIT_PRESENCE_DND :
-			return presence;
-
-		default :
-			return MXIT_PRESENCE_ONLINE;
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Parse the received mood value, and ensure that it is supported.
- *
- *  @param value		The received mood value.
- *  @return				A valid mood value.
- */
-static short mxit_parse_mood( const char* value )
-{
-	short mood = atoi( value );
-
-	/* ensure that the mood value is valid */
-	if ( ( mood >= MXIT_MOOD_NONE ) && ( mood <= MXIT_MOOD_STRESSED ) )
-		return mood;
-
-	return MXIT_MOOD_NONE;
-}
-
-
-/*------------------------------------------------------------------------
- * Process a received contact update 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_contact( struct MXitSession* session, struct record** records, int rcount )
-{
-	struct contact*		contact	= NULL;
-	struct record*		rec;
-	int					i;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_contact (%i recs)\n", rcount );
-
-	for ( i = 0; i < rcount; i++ ) {
-		rec = records[i];
-
-		if ( rec->fcount < 6 ) {
-			purple_debug_error( MXIT_PLUGIN_ID, "BAD CONTACT RECORD! %i fields\n", rec->fcount );
-			break;
-		}
-
-		/* build up a new contact info struct */
-		contact = g_new0( struct contact, 1 );
-
-		g_strlcpy( contact->groupname, rec->fields[0]->data, sizeof( contact->groupname ) );
-		g_strlcpy( contact->username, rec->fields[1]->data, sizeof( contact->username ) );
-		mxit_strip_domain( contact->username );				/* remove dummy domain */
-		g_strlcpy( contact->alias, rec->fields[2]->data, sizeof( contact->alias ) );
-
-		contact->presence = mxit_parse_presence( rec->fields[3]->data );
-		contact->type = atoi( rec->fields[4]->data );
-		contact->mood = mxit_parse_mood( rec->fields[5]->data );
-
-		if ( rec->fcount > 6 ) {
-			/* added in protocol 5.9 - flags & subtype */
-			contact->flags = atoi( rec->fields[6]->data );
-			contact->subtype = rec->fields[7]->data[0];
-		}
-		if ( rec->fcount > 8 ) {
-			/* added in protocol 6.0 - reject message */
-			contact->msg = g_strdup( rec->fields[8]->data );
-		}
-
-		/* add the contact to the buddy list */
-		if ( contact-> type == MXIT_TYPE_MULTIMX )			/* contact is a MultiMX room */
-			multimx_created( session, contact );
-		else
-			mxit_update_contact( session, contact );
-	}
-
-	if ( !( session->flags & MXIT_FLAG_FIRSTROSTER ) ) {
-		session->flags |= MXIT_FLAG_FIRSTROSTER;
-		mxit_update_blist( session );
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Process a received presence update 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_presence( struct MXitSession* session, struct record** records, int rcount )
-{
-	int					i;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount );
-
-	for ( i = 0; i < rcount; 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 );
-			break;
-		}
-
-		/*
-		 * The format of the record is:
-		 * 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, mxit_parse_presence( rec->fields[1]->data ), mxit_parse_mood( rec->fields[2]->data ),
-				rec->fields[3]->data, rec->fields[4]->data, flags );
-		mxit_update_buddy_avatar( session, rec->fields[0]->data, rec->fields[5]->data );
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Process a received extended profile 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_extprofile( struct MXitSession* session, struct record** records, int rcount )
-{
-	const char*				mxitId		= records[0]->fields[0]->data;
-	struct MXitProfile*		profile		= NULL;
-	int						count;
-	int						i;
-	const char*				avatarId	= NULL;
-	char*					statusMsg	= NULL;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_extprofile: profile for '%s'\n", mxitId );
-
-	if ( ( records[0]->fields[0]->len == 0 ) || ( session->uid && ( strcmp( session->uid, records[0]->fields[0]->data ) == 0 ) ) ) {
-		/* No UserId or Our UserId provided, so this must be our own profile information */
-		if ( session->profile == NULL )
-			session->profile = g_new0( struct MXitProfile, 1 );
-		profile = session->profile;
-	}
-	else {
-		/* is a buddy's profile */
-		profile = g_new0( struct MXitProfile, 1 );
-	}
-
-	/* set the count for attributes */
-	count = atoi( records[0]->fields[1]->data );
-
-	/* ensure the packet has the correct number of fields */
-	if ( records[0]->fcount < ( 2 + ( count * 3 ) ) ) {
-		purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in extprofile response. fields=%i records=%i", records[0]->fcount, count );
-		return;
-	}
-
-	for ( i = 0; i < count; i++ ) {
-		char* fname;
-		char* fvalue;
-		char* fstatus;
-		int f = ( i * 3 ) + 2;
-
-		fname = records[0]->fields[f]->data;		/* field name */
-		fvalue = records[0]->fields[f + 1]->data;	/* field value */
-		fstatus = records[0]->fields[f + 2]->data;	/* field status */
-
-		/* first check the status on the returned attribute */
-		if ( fstatus[0] != '0' ) {
-			/* error: attribute requested was NOT found */
-			purple_debug_error( MXIT_PLUGIN_ID, "Bad profile status on attribute '%s' \n", fname );
-			continue;
-		}
-
-		if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) {
-			/* birthdate */
-			if ( records[0]->fields[f + 1]->len > 10 ) {
-				fvalue[10] = '\0';
-				records[0]->fields[f + 1]->len = 10;
-			}
-			memcpy( profile->birthday, fvalue, records[0]->fields[f + 1]->len );
-		}
-		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_STATUS, fname ) == 0 ) {
-			/* status message - just keep a reference to the value */
-			statusMsg = g_markup_escape_text( fvalue, -1 );
-		}
-		else if ( strcmp( CP_PROFILE_AVATAR, fname ) == 0 ) {
-			/* avatar id - just keep a reference to the value */
-			avatarId = fvalue;
-		}
-		else if ( strcmp( CP_PROFILE_TITLE, fname ) == 0 ) {
-			/* title */
-			g_strlcpy( profile->title, fvalue, sizeof( profile->title ) );
-		}
-		else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) {
-			/* first name */
-			g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) );
-		}
-		else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) {
-			/* last name */
-			g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) );
-		}
-		else if ( strcmp( CP_PROFILE_EMAIL, fname ) == 0 ) {
-			/* email address */
-			g_strlcpy( profile->email, fvalue, sizeof( profile->email ) );
-		}
-		else if ( strcmp( CP_PROFILE_MOBILENR, fname ) == 0 ) {
-			/* mobile number */
-			g_strlcpy( profile->mobilenr, fvalue, sizeof( profile->mobilenr ) );
-		}
-		else if ( strcmp( CP_PROFILE_REGCOUNTRY, fname ) == 0 ) {
-			/* registered country */
-			g_strlcpy( profile->regcountry, fvalue, sizeof( profile->regcountry ) );
-		}
-		else if ( strcmp( CP_PROFILE_FLAGS, fname ) == 0 ) {
-			/* profile flags */
-			profile->flags = g_ascii_strtoll( fvalue, NULL, 10 );
-		}
-		else if ( strcmp( CP_PROFILE_LASTSEEN, fname ) == 0 ) {
-			/* last seen online */
-			profile->lastonline = g_ascii_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 if ( strcmp( CP_PROFILE_RELATIONSHIP, fname ) == 0) {
-			/* relatinship status */
-			profile->relationship = strtol( fvalue, NULL, 10 );
-		}
-		else {
-			/* invalid profile attribute */
-			purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname );
-		}
-	}
-
-	if ( profile != session->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 ) && ( *statusMsg ) ) {
-				/* update the status message */
-				g_free(contact->statusMsg);
-				contact->statusMsg = strdup( statusMsg );
-			}
-			else
-				contact->statusMsg = NULL;
-			g_free(contact->profile);
-			contact->profile = profile;
-			if ( ( avatarId ) && ( *avatarId ) ) {
-				/* avatar must be requested for this invite before we can display it */
-				mxit_get_avatar( session, mxitId, 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 );
-
-			if ( ( statusMsg ) && ( *statusMsg ) ) {
-				/* update the status message */
-				PurpleBuddy*		buddy	= NULL;
-
-				buddy = purple_blist_find_buddy( session->acc, mxitId );
-				if ( buddy ) {
-					contact = purple_buddy_get_protocol_data( buddy );
-					if ( contact ) {
-						g_free(contact->statusMsg);
-						contact->statusMsg = strdup( statusMsg );
-					}
-				}
-			}
-
-			/* show the profile */
-			mxit_show_profile( session, mxitId, profile );
-			g_free( profile );
-		}
-	}
-
-	g_free( statusMsg );
-}
-
-
-/*------------------------------------------------------------------------
- * 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
-	 */
-
-	/* ensure that record[0] contacts the minumum number of fields */
-	if ( records[0]->fcount < 4 ) {
-		purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in suggest contacts response. fields=%i", records[0]->fcount );
-		return;
-	}
-
-	/* 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 );
-
-	/* ensure that record[0] contains the specified number of attributes */
-	if ( records[0]->fcount < ( 4 + count ) ) {
-		purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in suggest contacts response. fields=%i attributes=%i", records[0]->fcount, count );
-		return;
-	}
-
-	for ( i = 1; i < rcount; i ++ ) {
-		struct record*		rec		= records[i];
-		struct MXitProfile*	profile	= g_new0( struct MXitProfile, 1 );
-		int j;
-
-		/* ensure that each result contains the specified number of attributes */
-		if ( rec->fcount != ( 2 + count ) ) {
-			purple_debug_error( MXIT_PLUGIN_ID, "Insufficient number of fields in suggest contacts response. fields=%i attributes=%i", rec->fcount, count );
-			g_free( profile );
-			continue;
-		}
-
-		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_FIRSTNAME, fname ) == 0 ) {
-				/* first name */
-				g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) );
-			}
-			else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) {
-				/* last name */
-				g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) );
-			}
-			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 );
-}
-
-/*------------------------------------------------------------------------
- * Process a received message event 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_msgevent( struct MXitSession* session, struct record** records, int rcount )
-{
-	int event;
-
-	/*
-	 * contactAddress \1 dateTime \1 id \1 event
-	 */
-
-	/* strip off dummy domain */
-	mxit_strip_domain( records[0]->fields[0]->data );
-
-	event = atoi( records[0]->fields[3]->data );
-
-	switch ( event ) {
-		case CP_MSGEVENT_TYPING :							/* user is typing */
-		case CP_MSGEVENT_ANGRY :							/* user is typing angrily */
-			purple_serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_IM_TYPING );
-			break;
-
-		case CP_MSGEVENT_STOPPED :							/* user has stopped typing */
-			purple_serv_got_typing_stopped( session->con, records[0]->fields[0]->data );
-			break;
-
-		case CP_MSGEVENT_ERASING :							/* user is erasing text */
-		case CP_MSGEVENT_DELIVERED :						/* message was delivered */
-		case CP_MSGEVENT_DISPLAYED :						/* message was viewed */
-			/* these are currently not supported by libPurple */
-			break;
-
-		default:
-			purple_debug_error( MXIT_PLUGIN_ID, "Unknown message event received (%i)\n", event );
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Process a received multimedia 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_media( struct MXitSession* session, struct record** records, int rcount )
-{
-	guint	chunktype;
-	guint32	chunksize;
-	gchar*	chunkdata;
-
-	/* received packet is too short to even contain a chunk header */
-	if ( records[0]->fields[0]->len < MXIT_CHUNK_HEADER_SIZE )
-		return;
-
-	/* decode the chunk header */
-	chunktype = chunk_type( records[0]->fields[0]->data );
-	chunksize = chunk_length( records[0]->fields[0]->data );
-	chunkdata = chunk_data( records[0]->fields[0]->data );
-
-	/* check chunk size against length of received data */
-	if ( MXIT_CHUNK_HEADER_SIZE + chunksize > records[0]->fields[0]->len )
-		return;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_media (%i records) (%i type) (%i bytes)\n", rcount, chunktype, chunksize );
-
-	/* supported chunked data types */
-	switch ( chunktype ) {
-		case CP_CHUNK_CUSTOM :				/* custom resource */
-			{
-				struct cr_chunk chunk;
-
-				/* decode the chunked data */
-				if ( mxit_chunk_parse_cr( chunkdata, chunksize, &chunk ) ) {
-
-					purple_debug_info( MXIT_PLUGIN_ID, "chunk info id=%s handle=%s op=%i\n", chunk.id, chunk.handle, chunk.operation );
-
-					/* this is a splash-screen operation */
-					if ( strcmp( chunk.handle, HANDLE_SPLASH2 ) == 0 ) {
-						if ( chunk.operation == CR_OP_UPDATE ) {		/* update the splash-screen */
-							struct splash_chunk *splash = chunk.resources->data;			// TODO: Fix - assuming 1st resource is splash
-							gboolean clickable = ( g_list_length( chunk.resources ) > 1 );	// TODO: Fix - if 2 resources, then is clickable
-
-							if ( splash != NULL )
-								splash_update( session, chunk.id, splash->data, splash->datalen, clickable );
-						}
-						else if ( chunk.operation == CR_OP_REMOVE )		/* remove the splash-screen */
-							splash_remove( session );
-					}
-
-					/* cleanup custom resources */
-					g_list_foreach( chunk.resources, (GFunc)g_free, NULL );
-				}
-			}
-			break;
-
-		case CP_CHUNK_OFFER :				/* file offer */
-			{
-				struct offerfile_chunk chunk;
-
-				/* decode the chunked data */
-				if ( mxit_chunk_parse_offer( chunkdata, chunksize, &chunk ) ) {
-					/* process the offer */
-					mxit_xfer_rx_offer( session, chunk.username, chunk.filename, chunk.filesize, chunk.fileid );
-				}
-			}
-			break;
-
-		case CP_CHUNK_GET :					/* get file response */
-			{
-				struct getfile_chunk chunk;
-
-				/* decode the chunked data */
-				if ( mxit_chunk_parse_get( chunkdata, chunksize, &chunk ) ) {
-					/* process the getfile */
-					mxit_xfer_rx_file( session, chunk.fileid, chunk.data, chunk.length );
-				}
-			}
-			break;
-
-		case CP_CHUNK_GET_AVATAR :			/* get avatars */
-			{
-				struct getavatar_chunk chunk;
-				struct contact* contact = NULL;
-
-				/* decode the chunked data */
-				if ( mxit_chunk_parse_get_avatar( chunkdata, chunksize, &chunk ) ) {
-					/* update avatar image */
-					purple_debug_info( MXIT_PLUGIN_ID, "updating avatar for contact '%s'\n", chunk.mxitid );
-
-					contact = get_mxit_invite_contact( session, chunk.mxitid );
-					if ( contact ) {
-						/* this is an invite (add image to the internal image store) */
-						if (contact->image)
-							g_object_unref(contact->image);
-						contact->image = purple_image_new_from_data(
-							g_memdup(chunk.data, chunk.length), chunk.length);
-						/* show the profile */
-						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;
-
-		case CP_CHUNK_SET_AVATAR :
-			/* this is a reply packet to a set avatar request. no action is required */
-			break;
-
-		case CP_CHUNK_REJECT :
-			/* this is a reply packet to a reject file request. no action is required */
-			break;
-
-		case CP_CHUNK_DIRECT_SND :
-			/* this is a ack for a file send. */
-			{
-				struct sendfile_chunk chunk;
-
-				if ( mxit_chunk_parse_sendfile( chunkdata, chunksize, &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 :
-			/* this is a ack for a file received. no action is required */
-			break;
-
-		default :
-			purple_debug_error( MXIT_PLUGIN_ID, "Unsupported chunked data packet type received (%i)\n", chunktype );
-			break;
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Handle a redirect sent from the MXit server.
- *
- *  @param session		The MXit session object
- *  @param url			The redirect information
- */
-static void mxit_perform_redirect( struct MXitSession* session, const char* url )
-{
-	gchar**		parts;
-	gchar**		host;
-	int			type;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s\n", url );
-
-	/* tokenize the URL string */
-	parts = g_strsplit( url, ";", 0 );
-
-	/* Part 1: protocol://host:port */
-	host = g_strsplit( parts[0], ":", 4 );
-	if ( strcmp( host[0], "socket" ) == 0 ) {
-		/* redirect to a MXit socket proxy */
-		g_strlcpy( session->server, &host[1][2], sizeof( session->server ) );
-		session->port = atoi( host[2] );
-	}
-	else {
-		purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Cannot perform redirect using the specified protocol" ) );
-		goto redirect_fail;
-	}
-
-	/* Part 2: type of redirect */
-	type = atoi( parts[1] );
-	if ( type == CP_REDIRECT_PERMANENT ) {
-		/* permanent redirect, so save new MXit server and port */
-		purple_account_set_string( session->acc, MXIT_CONFIG_SERVER_ADDR, session->server );
-		purple_account_set_int( session->acc, MXIT_CONFIG_SERVER_PORT, session->port );
-	}
-
-	/* Part 3: message (optional) */
-	if ( parts[2] != NULL )
-		purple_connection_notice( session->con, parts[2] );
-
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s redirect to %s:%i\n",
-			( type == CP_REDIRECT_PERMANENT ) ? "Permanent" : "Temporary", session->server, session->port );
-
-	/* perform the re-connect to the new MXit server */
-	mxit_reconnect( session );
-
-redirect_fail:
-	g_strfreev( parts );
-	g_strfreev( host );
-}
-
-
-/*------------------------------------------------------------------------
- * Process a success response received from the MXit server.
- *
- *  @param session		The MXit session object
- *  @param packet		The received packet
- */
-static int process_success_response( struct MXitSession* session, struct rx_packet* packet )
-{
-	/* ignore ping/poll packets */
-	if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) )
-		session->last_rx = mxit_now_milli();
-
-	/*
-	 * when we pass the packet records to the next level for parsing
-	 * we minus 3 records because 1) the first record is the packet
-	 * type 2) packet reply status 3) the last record is bogus
-	 */
-
-	/* packet command */
-	switch ( packet->cmd ) {
-
-		case CP_CMD_REGISTER :
-				/* fall through, when registeration successful, MXit will auto login */
-		case CP_CMD_LOGIN :
-				/* login response */
-				if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) {
-					mxit_parse_cmd_login( session, &packet->records[2], packet->rcount - 3 );
-				}
-				break;
-
-		case CP_CMD_LOGOUT :
-				/* logout response */
-				session->flags &= ~MXIT_FLAG_LOGGEDIN;
-				purple_account_disconnect( session->acc );
-
-				/* note:
-				 * we do not prompt the user here for a reconnect, because this could be the user
-				 * logging in with his phone. so we just disconnect the account otherwise
-				 * mxit will start to bounce between the phone and pidgin. also could be a valid
-				 * disconnect selected by the user.
-				 */
-				return -1;
-
-		case CP_CMD_CONTACT :
-				/* contact update */
-				mxit_parse_cmd_contact( session, &packet->records[2], packet->rcount - 3 );
-				break;
-
-		case CP_CMD_PRESENCE :
-				/* presence update */
-				mxit_parse_cmd_presence( session, &packet->records[2], packet->rcount - 3 );
-				break;
-
-		case CP_CMD_RX_MSG :
-				/* incoming message (no bogus record) */
-				mxit_parse_cmd_message( session, &packet->records[2], packet->rcount - 2 );
-				break;
-
-		case CP_CMD_NEW_SUB :
-				/* new subscription request */
-				mxit_parse_cmd_new_sub( session, &packet->records[2], packet->rcount - 3 );
-				break;
-
-		case CP_CMD_MEDIA :
-				/* multi-media message */
-				mxit_parse_cmd_media( session, &packet->records[2], packet->rcount - 2 );
-				break;
-
-		case CP_CMD_EXTPROFILE_GET :
-				/* profile update */
-				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_GOT_MSGEVENT :
-				/* received message event */
-				mxit_parse_cmd_msgevent( session, &packet->records[2], packet->rcount - 2 );
-				break;
-
-		case CP_CMD_MOOD :
-				/* mood update */
-		case CP_CMD_UPDATE :
-				/* update contact information */
-		case CP_CMD_ALLOW :
-				/* allow subscription ack */
-		case CP_CMD_DENY :
-				/* deny subscription ack */
-		case CP_CMD_INVITE :
-				/* invite contact ack */
-		case CP_CMD_REMOVE :
-				/* remove contact ack */
-		case CP_CMD_TX_MSG :
-				/* outgoing message ack */
-		case CP_CMD_STATUS :
-				/* presence update ack */
-		case CP_CMD_GRPCHAT_CREATE :
-				/* create groupchat */
-		case CP_CMD_GRPCHAT_INVITE :
-				/* groupchat invite */
-		case CP_CMD_PING :
-				/* ping reply */
-		case CP_CMD_POLL :
-				/* 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 :
-				/* event message */
-				break;
-
-		default :
-			/* unknown packet */
-			purple_debug_error( MXIT_PLUGIN_ID, "Received unknown client packet (cmd = %i)\n", packet->cmd );
-	}
-
-	return 0;
-}
-
-
-/*------------------------------------------------------------------------
- * Process an error response received from the MXit server.
- *
- *  @param session		The MXit session object
- *  @param packet		The received packet
- */
-static int process_error_response( struct MXitSession* session, struct rx_packet* packet )
-{
-	char			errmsg[256];
-	const char*		errdesc;
-
-	/* set the error description to be shown to the user */
-	if ( packet->errmsg )
-		errdesc = packet->errmsg;
-	else
-		errdesc = _( "An internal MXit server error occurred." );
-
-	purple_debug_info( MXIT_PLUGIN_ID, "Error Reply %i:%s\n", packet->errcode, errdesc );
-
-	if ( packet->errcode == MXIT_ERRCODE_LOGGEDOUT ) {
-		/* we are not currently logged in, so we need to reconnect */
-		purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( errdesc ) );
-	}
-
-	/* packet command */
-	switch ( packet->cmd ) {
-
-		case CP_CMD_REGISTER :
-		case CP_CMD_LOGIN :
-				if ( packet->errcode == MXIT_ERRCODE_REDIRECT ) {
-					mxit_perform_redirect( session, packet->errmsg );
-					return 0;
-				}
-				else {
-					g_snprintf( errmsg, sizeof( errmsg ), _( "Login error: %s (%i)" ), errdesc, packet->errcode );
-					purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, errmsg );
-					return -1;
-				}
-		case CP_CMD_LOGOUT :
-				g_snprintf( errmsg, sizeof( errmsg ), _( "Logout error: %s (%i)" ), errdesc, packet->errcode );
-				purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) );
-				return -1;
-		case CP_CMD_CONTACT :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_RX_MSG :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_TX_MSG :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Sending Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_STATUS :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Status Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_MOOD :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Mood Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_KICK :
-				/*
-				 * the MXit server sends this packet if we were idle for too long.
-				 * to stop the server from closing this connection we need to resend
-				 * the login packet.
-				 */
-				mxit_send_login( session );
-				break;
-		case CP_CMD_INVITE :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Invitation Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_REMOVE :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Removal Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_ALLOW :
-		case CP_CMD_DENY :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Subscription Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_UPDATE :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Update Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_MEDIA :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "File Transfer Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_GRPCHAT_CREATE :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Cannot create MultiMx room" ), _( errdesc ) );
-				break;
-		case CP_CMD_GRPCHAT_INVITE :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "MultiMx Invitation Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_EXTPROFILE_GET :
-		case CP_CMD_EXTPROFILE_SET :
-				mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Profile Error" ), _( errdesc ) );
-				break;
-		case CP_CMD_SPLASHCLICK :
-		case CP_CMD_MSGEVENT :
-				/* ignore error */
-				break;
-		case CP_CMD_PING :
-		case CP_CMD_POLL :
-				break;
-		default :
-				mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Error" ), _( errdesc ) );
-				break;
-	}
-
-	return 0;
-}
-
-
-/*========================================================================================================================
- * Low-level Packet receive
- */
-
-#ifdef	DEBUG_PROTOCOL
-/*------------------------------------------------------------------------
- * Dump a received packet structure.
- *
- *  @param p			The received packet
- */
-static void dump_packet( struct rx_packet* p )
-{
-	struct record*		r	= NULL;
-	struct field*		f	= NULL;
-	int					i;
-	int					j;
-
-	purple_debug_info( MXIT_PLUGIN_ID, "PACKET DUMP: (%i records)\n", p->rcount );
-
-	for ( i = 0; i < p->rcount; i++ ) {
-		r = p->records[i];
-		purple_debug_info( MXIT_PLUGIN_ID, "RECORD: (%i fields)\n", r->fcount );
-
-		for ( j = 0; j < r->fcount; j++ ) {
-			f = r->fields[j];
-			purple_debug_info( MXIT_PLUGIN_ID, "\tFIELD: (len=%zu) '%s' \n", f->len, f->data );
-		}
-	}
-}
-#endif
-
-
-/*------------------------------------------------------------------------
- * Free up memory used by a packet structure.
- *
- *  @param p			The received packet
- */
-static void free_rx_packet( struct rx_packet* p )
-{
-	struct record*		r	= NULL;
-	struct field*		f	= NULL;
-	int					i;
-	int					j;
-
-	for ( i = 0; i < p->rcount; i++ ) {
-		r = p->records[i];
-
-		for ( j = 0; j < r->fcount; j++ ) {
-			g_free( f );
-		}
-		g_free( r->fields );
-		g_free( r );
-	}
-	g_free( p->records );
-}
-
-
-/*------------------------------------------------------------------------
- * Add a new field to a record.
- *
- *  @param r			Parent record object
- *  @return				The newly created field
- */
-static struct field* add_field( struct record* r )
-{
-	struct field*	field;
-
-	field = g_new0( struct field, 1 );
-
-	r->fields = g_realloc( r->fields, sizeof( struct field* ) * ( r->fcount + 1 ) );
-	r->fields[r->fcount] = field;
-	r->fcount++;
-
-	return field;
-}
-
-
-/*------------------------------------------------------------------------
- * Add a new record to a packet.
- *
- *  @param p			The packet object
- *  @return				The newly created record
- */
-static struct record* add_record( struct rx_packet* p )
-{
-	struct record*	rec;
-
-	rec = g_new0( struct record, 1 );
-
-	p->records = g_realloc( p->records, sizeof( struct record* ) * ( p->rcount + 1 ) );
-	p->records[p->rcount] = rec;
-	p->rcount++;
-
-	return rec;
-}
-
-
-/*------------------------------------------------------------------------
- * Parse the received byte stream into a proper client protocol packet.
- *
- *  @param session		The MXit session object
- *  @return				Success (0) or Failure (!0)
- */
-int mxit_parse_packet( struct MXitSession* session )
-{
-	struct rx_packet	packet;
-	struct record*		rec;
-	struct field*		field;
-	gboolean			pbreak;
-	unsigned int		i;
-	int					res	= 0;
-
-#ifdef	DEBUG_PROTOCOL
-	purple_debug_info( MXIT_PLUGIN_ID, "Received packet (%i bytes)\n", session->rx_i );
-	dump_bytes( session, session->rx_dbuf, session->rx_i );
-#endif
-
-	i = 0;
-	while ( i < session->rx_i ) {
-
-		/* create first record and field */
-		rec = NULL;
-		field = NULL;
-		memset( &packet, 0x00, sizeof( struct rx_packet ) );
-		rec = add_record( &packet );
-		pbreak = FALSE;
-
-		/* break up the received packet into fields and records for easy parsing */
-		while ( ( i < session->rx_i ) && ( !pbreak ) ) {
-
-			switch ( session->rx_dbuf[i] ) {
-				case CP_SOCK_REC_TERM :
-						/* new record */
-						if ( packet.rcount == 1 ) {
-							/* packet command */
-							if ( packet.records[0]->fcount > 0 )
-								packet.cmd = atoi( packet.records[0]->fields[0]->data );
-						}
-						else if ( packet.rcount == 2 ) {
-							/* special case: binary multimedia packets should not be parsed here */
-							if ( packet.cmd == CP_CMD_MEDIA ) {
-								/* add the chunked to new record */
-								rec = add_record( &packet );
-								field = add_field( rec );
-								field->data = &session->rx_dbuf[i + 1];
-								field->len = session->rx_i - i;
-								/* now skip the binary data */
-								res = chunk_length( field->data );
-								/* determine if we have more packets */
-								if ( res + 6 + i < session->rx_i ) {
-									/* we have more than one packet in this stream */
-									i += res + 6;
-									pbreak = TRUE;
-								}
-								else {
-									i = session->rx_i;
-								}
-							}
-						}
-						else if ( !field ) {
-							field = add_field( rec );
-							field->data = &session->rx_dbuf[i];
-						}
-						session->rx_dbuf[i] = '\0';
-						rec = add_record( &packet );
-						field = NULL;
-
-						break;
-				case CP_FLD_TERM :
-						/* new field */
-						session->rx_dbuf[i] = '\0';
-						if ( !field ) {
-							field = add_field( rec );
-							field->data = &session->rx_dbuf[i];
-						}
-						field = NULL;
-						break;
-				case CP_PKT_TERM :
-						/* packet is done! */
-						session->rx_dbuf[i] = '\0';
-						pbreak = TRUE;
-						break;
-				default :
-						/* skip non special characters */
-						if ( !field ) {
-							field = add_field( rec );
-							field->data = &session->rx_dbuf[i];
-						}
-						field->len++;
-						break;
-			}
-
-			i++;
-		}
-
-		if ( packet.rcount < 2 ) {
-			/* bad packet */
-			purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "Invalid packet received from MXit." ) );
-			free_rx_packet( &packet );
-			continue;
-		}
-
-		session->rx_dbuf[session->rx_i] = '\0';
-		packet.errcode = atoi( packet.records[1]->fields[0]->data );
-
-		purple_debug_info( MXIT_PLUGIN_ID, "Packet received CMD:%i (%i)\n", packet.cmd, packet.errcode );
-#ifdef	DEBUG_PROTOCOL
-		/* debug */
-		dump_packet( &packet );
-#endif
-
-		/* reset the out ack */
-		if ( session->outack == packet.cmd ) {
-			/* outstanding ack received from mxit server */
-			session->outack = 0;
-		}
-
-		/* check packet status */
-		if ( packet.errcode != MXIT_ERRCODE_SUCCESS ) {
-			/* error reply! */
-			if ( ( packet.records[1]->fcount > 1 ) && ( packet.records[1]->fields[1]->data ) )
-				packet.errmsg = packet.records[1]->fields[1]->data;
-			else
-				packet.errmsg = NULL;
-
-			res = process_error_response( session, &packet );
-		}
-		else {
-			/* success reply! */
-			res = process_success_response( session, &packet );
-		}
-
-		/* free up the packet resources */
-		free_rx_packet( &packet );
-	}
-
-	if ( session->outack == 0 )
-			mxit_manage_queue( session );
-
-	return res;
-}
-
-
-/*------------------------------------------------------------------------
- * Callback when data is received from the MXit server.
- *
- *  @param user_data		The MXit session object
- *  @param source			The file-descriptor on which data was received
- *  @param cond				Condition which caused the callback (PURPLE_INPUT_READ)
- */
-void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond )
-{
-	struct MXitSession*	session		= (struct MXitSession*) user_data;
-	char				ch;
-	int					res;
-	int					len;
-
-	if ( session->rx_state == RX_STATE_RLEN ) {
-		/* we are reading in the packet length */
-		len = read( session->fd, &ch, 1 );
-		if ( len < 0 ) {
-			/* connection error */
-			purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x01)" ) );
-			return;
-		}
-		else if ( len == 0 ) {
-			/* connection closed */
-			purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x02)" ) );
-			return;
-		}
-		else {
-			/* byte read */
-			if ( ch == CP_REC_TERM ) {
-				/* the end of the length record found */
-				session->rx_lbuf[session->rx_i] = '\0';
-				session->rx_res = atoi( &session->rx_lbuf[3] );
-				if ( ( session->rx_res <= 0 ) || ( session->rx_res > CP_MAX_PACKET ) ) {
-					purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x03)" ) );
-					return;
-				}
-				session->rx_state = RX_STATE_DATA;
-				session->rx_i = 0;
-			}
-			else {
-				/* still part of the packet length record */
-				session->rx_lbuf[session->rx_i] = ch;
-				session->rx_i++;
-				if ( session->rx_i >= sizeof( session->rx_lbuf ) ) {
-					/* malformed packet length record (too long) */
-					purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x04)" ) );
-					return;
-				}
-			}
-		}
-	}
-	else if ( session->rx_state == RX_STATE_DATA ) {
-		/* we are reading in the packet data */
-		len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res );
-		if ( len < 0 ) {
-			/* connection error */
-			purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x05)" ) );
-			return;
-		}
-		else if ( len == 0 ) {
-			/* connection closed */
-			purple_connection_error( session->con, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _( "A connection error occurred to MXit. (read stage 0x06)" ) );
-			return;
-		}
-		else {
-			/* data read */
-			session->rx_i += len;
-			session->rx_res -= len;
-
-			if ( session->rx_res == 0 ) {
-				/* ok, so now we have read in the whole packet */
-				session->rx_state = RX_STATE_PROC;
-			}
-		}
-	}
-
-	if ( session->rx_state == RX_STATE_PROC ) {
-		/* we have a full packet, which we now need to process */
-		res = mxit_parse_packet( session );
-
-		if ( res == 0 ) {
-			/* we are still logged in */
-			session->rx_state = RX_STATE_RLEN;
-			session->rx_res = 0;
-			session->rx_i = 0;
-		}
-	}
-}
-
-
-/*------------------------------------------------------------------------
- * Log the user off MXit and close the connection
- *
- *  @param session		The MXit session object
- */
-void mxit_close_connection( struct MXitSession* session )
-{
-	purple_debug_info( MXIT_PLUGIN_ID, "mxit_close_connection\n" );
-
-	if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) {
-		/* we are already closed */
-		return;
-	}
-	else if ( session->flags & MXIT_FLAG_LOGGEDIN ) {
-		/* we are currently logged in so we need to send a logout packet */
-		if ( !session->http ) {
-			mxit_send_logout( session );
-		}
-		session->flags &= ~MXIT_FLAG_LOGGEDIN;
-	}
-	session->flags &= ~MXIT_FLAG_CONNECTED;
-
-	/* cancel all outstanding async calls */
-	purple_http_connection_set_destroy(session->async_http_reqs);
-	session->async_http_reqs = NULL;
-
-	/* remove the input cb function */
-	if ( session->inpa ) {
-		purple_input_remove( session->inpa );
-		session->inpa = 0;
-	}
-
-	/* remove HTTP poll timer */
-	if ( session->http_timer_id > 0 )
-		purple_timeout_remove( session->http_timer_id );
-
-	/* 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 ) {
-		struct multimx* multimx = (struct multimx *) session->rooms->data;
-
-		session->rooms = g_list_remove( session->rooms, multimx );
-
-		free( multimx );
-	}
-	g_list_free( session->rooms );
-	session->rooms = NULL;
-
-	/* remove all rx chats names */
-	while ( session->active_chats != NULL ) {
-		char* chat = (char*) session->active_chats->data;
-
-		session->active_chats = g_list_remove( session->active_chats, chat );
-
-		g_free( chat );
-	}
-	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 );
-
-		g_free(contact->msg);
-		g_free(contact->statusMsg);
-		g_free(contact->profile);
-		if (contact->image)
-			g_object_unref(contact->image);
-		g_free( contact );
-	}
-	g_list_free( session->invites );
-	session->invites = NULL;
-
-	free( session->profile );
-	mxit_free_emoticon_cache( session );
-	g_free( session->uid );
-	g_free( session->encpwd );
-	session->encpwd = NULL;
-
-	/* flush all the commands still in the queue */
-	flush_queue( session );
-}
-

mercurial