--- 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 ); -} -