# HG changeset patch # User Mark Doliner # Date 1313962037 0 # Node ID 57d43a9a4e7ef7c23af268cc192c3ee478a3a532 # Parent dc3ea8f6381aaf3770dc69ace798c52ec1aaf4d5# Parent 93cb9f408df4ad23f96c912f29e2fbf624462c23 merge of '01dbf4adfbd244ad73e7c0a6850a5bcc9d0b9582' and '7d17d6b03c98b91ec1f90c5f17f7c923973f2e2c' diff -r dc3ea8f6381a -r 57d43a9a4e7e ChangeLog --- a/ChangeLog Sun Aug 21 18:43:00 2011 +0000 +++ b/ChangeLog Sun Aug 21 21:27:17 2011 +0000 @@ -6,6 +6,14 @@ * Don't try to format ICQ usernames entered as email addresses. Gets rid of an "Unable to format username" error at login. (#13883) + MXit: + * Remove all reference to Hidden Number. + * Fix decoding of font-size changes in the markup of received messages. + * Ignore new invites to join a GroupChat if you're already joined, or + still have a pending invite. + * The buddy's name was not centered vertically in the buddy-list if they + did not have a status-message or mood set. + version 2.10.0 (08/18/2011): Pidgin: * Make the max size of incoming smileys a pref instead of hardcoding it. diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/actions.c --- a/libpurple/protocols/mxit/actions.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/actions.c Sun Aug 21 21:27:17 2011 +0000 @@ -84,13 +84,6 @@ g_string_append( attributes, attrib ); acount++; - /* force hidden if disabled */ - if ( profile->hidden == FALSE ) { - g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_HIDENUMBER, CP_PROFILE_TYPE_BOOL, "1" ); - g_string_append( attributes, attrib ); - acount++; - } - /* update birthday */ g_strlcpy( profile->birthday, bday, sizeof( profile->birthday ) ); g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%s", CP_PROFILE_BIRTHDATE, CP_PROFILE_TYPE_UTF8, profile->birthday ); @@ -165,7 +158,7 @@ /* update where am i */ name = purple_request_fields_get_string( fields, "whereami" ); - if ( !name) + if ( !name ) profile->whereami[0] = '\0'; else g_strlcpy( profile->whereami, name, sizeof( profile->whereami ) ); @@ -375,8 +368,8 @@ purple_debug_info( MXIT_PLUGIN_ID, "mxit_change_pin_action\n" ); fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); + group = purple_request_field_group_new( NULL ); + purple_request_fields_add_group( fields, group ); /* pin */ field = purple_request_field_string_new( "pin", _( "PIN" ), session->acc->password, FALSE ); @@ -479,10 +472,10 @@ _( "Search for a MXit contact" ), _( "Type search information" ), NULL, FALSE, FALSE, NULL, - _("_Search"), G_CALLBACK( mxit_user_search_cb ), - _("_Cancel"), NULL, + _( "_Search" ), G_CALLBACK( mxit_user_search_cb ), + _( "_Cancel" ), NULL, purple_connection_get_account( gc ), NULL, NULL, - gc); + gc ); } diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/cipher.c --- a/libpurple/protocols/mxit/cipher.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/cipher.c Sun Aug 21 21:27:17 2011 +0000 @@ -1,7 +1,7 @@ /* * MXit Protocol libPurple Plugin * - * -- user password encryption -- + * -- encryption -- * * Pieter Loubser * @@ -31,30 +31,69 @@ #include "aes.h" -/* password encryption */ +/* encryption */ #define INITIAL_KEY "6170383452343567" #define SECRET_HEADER "" +#define ENCRYPT_HEADER "" + + +/*------------------------------------------------------------------------ + * Add ISO10126 Padding to the data. + * + * @param data The data to pad. + */ +static void padding_add( GString* data ) +{ + unsigned int blocks = ( data->len / 16 ) + 1; + unsigned int padding = ( blocks * 16 ) - data->len; + + g_string_set_size( data, blocks * 16 ); + data->str[data->len - 1] = padding; +} /*------------------------------------------------------------------------ - * Pad the secret data using ISO10126 Padding. + * Remove ISO10126 Padding from the data. * - * @param secret The data to pad (caller must ensure buffer has enough space for padding) - * @return The total number of 128-bit blocks used + * @param data The data from which to remove padding. */ -static int pad_secret_data( char* secret ) +static void padding_remove( GString* data ) { - int blocks = 0; - int passlen; - int padding; + unsigned int padding; + + if ( data->len == 0 ) + return; + + padding = data->str[data->len - 1]; + g_string_truncate( data, data->len - padding ); +} + - passlen = strlen( secret ); - blocks = ( passlen / 16 ) + 1; - padding = ( blocks * 16 ) - passlen; - secret[passlen] = 0x50; - secret[(blocks * 16) - 1] = padding; +/*------------------------------------------------------------------------ + * Generate the Transport-Layer crypto key. + * (Note: this function is not-thread safe) + * + * @param session The MXit Session object + * @return The transport-layer crypto key. + */ +static char* transport_layer_key( struct MXitSession* session ) +{ + static char key[16 + 1]; + int passlen = strlen( session->acc->password ); - return blocks; + /* initialize with initial key */ + g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); + + /* client key (8 bytes) */ + memcpy( key, session->clientkey, strlen( session->clientkey ) ); + + /* add last 8 characters of the PIN (no padding if less characters) */ + if ( passlen <= 8 ) + memcpy( key + 8, session->acc->password, passlen ); + else + memcpy( key + 8, session->acc->password + ( passlen - 8 ), 8 ); + + return key; } @@ -67,42 +106,131 @@ */ char* mxit_encrypt_password( struct MXitSession* session ) { - char key[64]; + char key[16 + 1]; char exkey[512]; - char pass[64]; + GString* pass = NULL; char encrypted[64]; char* base64; - int blocks; - int size; int i; purple_debug_info( MXIT_PLUGIN_ID, "mxit_encrypt_password\n" ); memset( encrypted, 0x00, sizeof( encrypted ) ); - memset( exkey, 0x00, sizeof( exkey ) ); - memset( pass, 0x58, sizeof( pass ) ); - pass[sizeof( pass ) - 1] = '\0'; - /* build the custom AES encryption key */ + /* build the AES encryption key */ g_strlcpy( key, INITIAL_KEY, sizeof( key ) ); memcpy( key, session->clientkey, strlen( session->clientkey ) ); ExpandKey( (unsigned char*) key, (unsigned char*) exkey ); - /* build the custom data to be encrypted */ - g_strlcpy( pass, SECRET_HEADER, sizeof( pass ) ); - strcat( pass, session->acc->password ); + /* build the secret data to be encrypted: SECRET_HEADER + password */ + pass = g_string_new( SECRET_HEADER ); + g_string_append( pass, session->acc->password ); + padding_add( pass ); /* add ISO10126 padding */ - /* pad the secret data */ - blocks = pad_secret_data( pass ); - size = blocks * 16; - - /* now encrypt the password. we encrypt each block separately (ECB mode) */ - for ( i = 0; i < size; i += 16 ) - Encrypt( (unsigned char*) pass + i, (unsigned char*) exkey, (unsigned char*) encrypted + i ); + /* now encrypt the secret. we encrypt each block separately (ECB mode) */ + for ( i = 0; i < pass->len; i += 16 ) + Encrypt( (unsigned char*) pass->str + i, (unsigned char*) exkey, (unsigned char*) encrypted + i ); /* now base64 encode the encrypted password */ - base64 = purple_base64_encode( (unsigned char*) encrypted, size ); + base64 = purple_base64_encode( (unsigned char*) encrypted, pass->len ); + + g_string_free( pass, TRUE ); return base64; } + +/*------------------------------------------------------------------------ + * Decrypt a message using transport-layer encryption. + * + * @param session The MXit session object + * @param message The encrypted message data (is base64-encoded). + * @return The decrypted message. Must be g_free'd when no longer needed. + */ +char* mxit_decrypt_message( struct MXitSession* session, char* message ) +{ + guchar* raw_message; + gsize raw_len; + char exkey[512]; + GString* decoded = NULL; + int i; + + /* remove optional header: */ + if ( strncmp( message, ENCRYPT_HEADER, strlen( ENCRYPT_HEADER ) ) == 0 ) + message += strlen( ENCRYPT_HEADER ); + + /* base64 decode the message */ + raw_message = purple_base64_decode( message, &raw_len ); + + /* build the AES key */ + ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey ); + + /* AES decrypt each block */ + decoded = g_string_sized_new( raw_len ); + for ( i = 0; i < raw_len; i += 16 ) { + char block[16]; + + Decrypt( (unsigned char*) raw_message + i, (unsigned char*) exkey, (unsigned char*) block ); + g_string_append_len( decoded, block, 16 ); + } + g_free( raw_message ); + + /* check that the decrypted message starts with header: */ + if ( strncmp( decoded->str, SECRET_HEADER, strlen( SECRET_HEADER ) != 0 ) ) { + g_string_free( decoded, TRUE ); + return NULL; /* message could not be decrypted */ + } + + /* remove ISO10126 padding */ + padding_remove( decoded ); + + /* remove encryption header */ + g_string_erase( decoded, 0, strlen( SECRET_HEADER ) ); + + return g_string_free( decoded, FALSE ); +} + + +/*------------------------------------------------------------------------ + * Encrypt a message using transport-layer encryption. + * + * @param session The MXit session object + * @param message The message data. + * @return The encrypted message. Must be g_free'd when no longer needed. + */ +char* mxit_encrypt_message( struct MXitSession* session, char* message ) +{ + GString* raw_message = NULL; + char exkey[512]; + GString* encoded = NULL; + gchar* base64; + int i; + + purple_debug_info( MXIT_PLUGIN_ID, "encrypt message: '%s'\n", message ); + + /* append encryption header to message data */ + raw_message = g_string_new( SECRET_HEADER ); + g_string_append( raw_message, message ); + padding_add( raw_message ); /* add ISO10126 padding */ + + /* build the AES key */ + ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey ); + + /* AES encrypt each block */ + encoded = g_string_sized_new( raw_message->len ); + for ( i = 0; i < raw_message->len; i += 16 ) { + char block[16]; + + Encrypt( (unsigned char*) raw_message->str + i, (unsigned char*) exkey, (unsigned char*) block ); + g_string_append_len( encoded, block, 16 ); + } + g_string_free( raw_message, TRUE ); + + /* base64 encode the encrypted message */ + base64 = purple_base64_encode( (unsigned char *) encoded->str, encoded->len ); + g_string_free( encoded, TRUE ); + + purple_debug_info( MXIT_PLUGIN_ID, "encrypted message: '%s'\n", base64 ); + + return base64; +} diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/cipher.h --- a/libpurple/protocols/mxit/cipher.h Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/cipher.h Sun Aug 21 21:27:17 2011 +0000 @@ -1,7 +1,7 @@ /* * MXit Protocol libPurple Plugin * - * -- user password encryption -- + * -- encryption -- * * Pieter Loubser * @@ -32,5 +32,7 @@ char* mxit_encrypt_password( struct MXitSession* session ); +char* mxit_decrypt_message( struct MXitSession* session, char* message ); +char* mxit_encrypt_message( struct MXitSession* session, char* message ); #endif /* _MXIT_CIPHER_H_ */ diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/formcmds.c --- a/libpurple/protocols/mxit/formcmds.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/formcmds.c Sun Aug 21 21:27:17 2011 +0000 @@ -253,8 +253,8 @@ /*------------------------------------------------------------------------ * Process a Reply MXit command. - * [::op=cmd|type=reply|replymsg=back|selmsg=b) Back|id=12345:] - * [::op=cmd|nm=rep|type=reply|replymsg=back|selmsg=b) Back|id=12345:] + * [::op=cmd|type=reply|replymsg=back|selmsg=b) Back|displaymsg=Processing|id=12345:] + * [::op=cmd|nm=rep|type=reply|replymsg=back|selmsg=b) Back|displaymsg=Processing|id=12345:] * * @param mx The received message data object * @param hash The MXit command map @@ -265,22 +265,26 @@ char* selmsg; char* nm; - selmsg = g_hash_table_lookup(hash, "selmsg"); /* find the selection message */ - replymsg = g_hash_table_lookup(hash, "replymsg"); /* find the reply message */ + selmsg = g_hash_table_lookup(hash, "selmsg"); /* selection message */ + replymsg = g_hash_table_lookup(hash, "replymsg"); /* reply message */ nm = g_hash_table_lookup(hash, "nm"); /* name parameter */ - if ((selmsg) && (replymsg) && (nm)) { + + if ((selmsg == NULL) || (replymsg == NULL)) + return; /* these parameters are required */ + + if (nm) { /* indicates response must be a structured response */ gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1); - gchar* replycmd = g_strdup_printf("::type=reply|nm=%s|res=%s|err=0:", nm, replymsg); + gchar* replycmd = g_strdup_printf("type=reply|nm=%s|res=%s|err=0", nm, replymsg); - mxit_add_html_link( mx, replycmd, seltext ); + mxit_add_html_link( mx, replycmd, TRUE, seltext ); g_free(seltext); g_free(replycmd); } - else if ((selmsg) && (replymsg)) { + else { gchar* seltext = g_markup_escape_text(purple_url_decode(selmsg), -1); - mxit_add_html_link( mx, purple_url_decode(replymsg), seltext ); + mxit_add_html_link( mx, purple_url_decode(replymsg), FALSE, seltext ); g_free(seltext); } @@ -317,6 +321,7 @@ /*------------------------------------------------------------------------ * Process an inline image MXit command. + * [::op=img|dat=ASDF23408asdflkj2309flkjsadf%3d%3d|algn=1|w=120|h=12|t=100|replymsg=text:] * * @param mx The received message data object * @param hash The MXit command map @@ -372,7 +377,7 @@ reply = g_hash_table_lookup(hash, "replymsg"); if (reply) { g_string_append_printf(msg, "\n"); - mxit_add_html_link(mx, reply, _( "click here" )); + mxit_add_html_link(mx, reply, FALSE, _( "click here" )); } } diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/markup.c --- a/libpurple/protocols/mxit/markup.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/markup.c Sun Aug 21 21:27:17 2011 +0000 @@ -124,10 +124,11 @@ * Adds a link to a message * * @param mx The Markup message object - * @param linkname This is the what will be returned when the link gets clicked - * @param displayname This is the name for the link which will be displayed in the UI + * @param replydata This is the what will be returned when the link gets clicked + * @param isStructured Indicates that the reply is a structured reply + * @param displaytext This is the text for the link which will be displayed in the UI */ -void mxit_add_html_link( struct RXMsgData* mx, const char* linkname, const char* displayname ) +void mxit_add_html_link( struct RXMsgData* mx, const char* replydata, gboolean isStructured, const char* displaytext ) { #ifdef MXIT_LINK_CLICK char retstr[256]; @@ -135,15 +136,24 @@ char link[256]; int len; - len = g_snprintf( retstr, sizeof( retstr ), "%s|%s|%s|%s|%s", MXIT_LINK_KEY, purple_account_get_username( mx->session->acc ), - purple_account_get_protocol_id( mx->session->acc ), mx->from, linkname ); + /* + * The link content is encoded as follows: + * MXIT_LINK_KEY | ACCOUNT_USER | ACCOUNT_PROTO | REPLY_TO | REPLY_FORMAT | REPLY_DATA + */ + len = g_snprintf( retstr, sizeof( retstr ), "%s|%s|%s|%s|%i|%s", + MXIT_LINK_KEY, + purple_account_get_username( mx->session->acc ), + purple_account_get_protocol_id( mx->session->acc ), + mx->from, + isStructured ? 1 : 0, + replydata ); retstr64 = purple_base64_encode( (const unsigned char*) retstr, len ); g_snprintf( link, sizeof( link ), "%s%s", MXIT_LINK_PREFIX, retstr64 ); g_free( retstr64 ); - g_string_append_printf( mx->msg, "%s", link, displayname ); + g_string_append_printf( mx->msg, "%s", link, displaytext ); #else - g_string_append_printf( mx->msg, "%s", linkname ); + g_string_append_printf( mx->msg, "%s", replydata ); #endif } @@ -735,6 +745,7 @@ gboolean tag_bold = FALSE; gboolean tag_under = FALSE; gboolean tag_italic = FALSE; + int font_size = 0; #ifdef MXIT_DEBUG_MARKUP purple_debug_info( MXIT_PLUGIN_ID, "Markup RX (original): '%s'\n", message ); @@ -823,7 +834,7 @@ if ( ch ) { /* end found */ *ch = '\0'; - mxit_add_html_link( mx, &message[i + 1], &message[i + 1] ); + mxit_add_html_link( mx, &message[i + 1], FALSE, &message[i + 1] ); *ch = '$'; i += ( ch - &message[i + 1] ) + 1; } @@ -862,59 +873,54 @@ } break; case '.' : - if ( !( msgflags & CP_MSG_EMOTICON ) ) { - g_string_append_c( mx->msg, message[i] ); - break; - } - else if ( i + 1 >= len ) { + if ( i + 1 >= len ) { /* message too short */ g_string_append_c( mx->msg, '.' ); break; } - switch ( message[i+1] ) { - case '+' : - /* increment text size */ - g_string_append( mx->msg, "" ); - i++; - break; - case '-' : - /* decrement text size */ - g_string_append( mx->msg, "" ); - i++; - break; - case '{' : - /* custom emoticon */ - if ( i + 2 >= len ) { - /* message too short */ - g_string_append_c( mx->msg, '.' ); - break; - } + if ( ( msgflags & CP_MSG_EMOTICON ) && ( message[i+1] == '{' ) ) { + /* custom emoticon */ + if ( i + 2 >= len ) { + /* message too short */ + g_string_append_c( mx->msg, '.' ); + break; + } + + parse_emoticon_str( &message[i+2], tmpstr1 ); + if ( tmpstr1[0] != '\0' ) { + mx->got_img = TRUE; + + if ( g_hash_table_lookup( mx->session->iimages, tmpstr1 ) ) { + /* emoticon found in the cache, so we do not have to request it from the WAPsite */ + } + else { + /* request emoticon from the WAPsite */ + mx->img_count++; + emoticon_request( mx, tmpstr1 ); + } - parse_emoticon_str( &message[i+2], tmpstr1 ); - if ( tmpstr1[0] != '\0' ) { - mx->got_img = TRUE; + g_string_append_printf( mx->msg, MXIT_II_TAG"%s>", tmpstr1 ); + i += strlen( tmpstr1 ) + 2; + } + else + g_string_append_c( mx->msg, '.' ); + } + else if ( ( msgflags & CP_MSG_MARKUP ) && ( message[i+1] == '+' ) ) { + /* increment text size */ + font_size++; + g_string_append_printf( mx->msg, "", font_size ); + i++; + } + else if ( ( msgflags & CP_MSG_MARKUP ) && ( message[i+1] == '-' ) ) { + /* decrement text size */ + font_size--; + g_string_append_printf( mx->msg, "", font_size ); + i++; + } + else + g_string_append_c( mx->msg, '.' ); - if ( g_hash_table_lookup( mx->session->iimages, tmpstr1 ) ) { - /* emoticon found in the cache, so we do not have to request it from the WAPsite */ - } - else { - /* request emoticon from the WAPsite */ - mx->img_count++; - emoticon_request( mx, tmpstr1 ); - } - - g_string_append_printf( mx->msg, MXIT_II_TAG"%s>", tmpstr1 ); - i += strlen( tmpstr1 ) + 2; - } - else - g_string_append_c( mx->msg, '.' ); - - break; - default : - g_string_append_c( mx->msg, '.' ); - break; - } break; case '\\' : if ( i + 1 >= len ) { diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/markup.h --- a/libpurple/protocols/mxit/markup.h Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/markup.h Sun Aug 21 21:27:17 2011 +0000 @@ -31,7 +31,7 @@ void mxit_parse_markup( struct RXMsgData* mx, char* message, int len, short msgtype, int msgflags ); char* mxit_convert_markup_tx( const char* message, int* msgtype ); -void mxit_add_html_link( struct RXMsgData* mx, const char* linkname, const char* displayname ); +void mxit_add_html_link( struct RXMsgData* mx, const char* replydata, gboolean isStructured, const char* displaytext ); void mxit_show_message( struct RXMsgData* mx ); void mxit_free_emoticon_cache( struct MXitSession* session ); diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/multimx.c --- a/libpurple/protocols/mxit/multimx.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/multimx.c Sun Aug 21 21:27:17 2011 +0000 @@ -277,7 +277,11 @@ GHashTable *components; struct multimx* multimx = NULL; - purple_debug_info(MXIT_PLUGIN_ID, "Groupchat invite to '%s' by '%s'\n", contact->alias, creator); + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat invite to '%s' (roomid='%s') by '%s'\n", contact->alias, contact->username, creator); + + /* Check if the room already exists (ie, already joined or invite pending) */ + if (find_room_by_username(session, contact->username) != NULL) + return; /* Create a new room */ multimx = room_create(session, contact->username, contact->alias, STATE_INVITED); @@ -307,7 +311,7 @@ multimx = find_room_by_username(session, contact->username); if (multimx == NULL) { multimx = room_create(session, contact->username, contact->alias, TRUE); - } + } else if (multimx->state == STATE_INVITED) { /* After successfully accepting an invitation */ multimx->state = STATE_JOINED; diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/mxit.c --- a/libpurple/protocols/mxit/mxit.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/mxit.c Sun Aug 21 21:27:17 2011 +0000 @@ -75,10 +75,10 @@ link = (gchar*) purple_base64_decode( link64 + strlen( MXIT_LINK_PREFIX ), &len ); purple_debug_info( MXIT_PLUGIN_ID, "Clicked Link: '%s'\n", link ); - parts = g_strsplit( link, "|", 5 ); + parts = g_strsplit( link, "|", 6 ); /* check if this is a valid mxit link */ - if ( ( !parts ) || ( !parts[0] ) || ( !parts[1] ) || ( !parts[2] ) || ( !parts[3] ) || ( !parts[4] ) ) { + if ( ( !parts ) || ( !parts[0] ) || ( !parts[1] ) || ( !parts[2] ) || ( !parts[3] ) || ( !parts[4] ) || ( !parts[5] ) ) { /* this is not for us */ goto skip; } @@ -96,10 +96,10 @@ goto skip; /* determine if it's a command-response to send */ - is_command = g_str_has_prefix( parts[4], "::type=reply|" ); + is_command = ( atoi( parts[4] ) == 1 ); /* send click message back to MXit */ - mxit_send_message( con->proto_data, parts[3], parts[4], FALSE, is_command ); + mxit_send_message( con->proto_data, parts[3], parts[5], FALSE, is_command ); g_free( link ); link = NULL; @@ -129,7 +129,7 @@ /*------------------------------------------------------------------------ * Register MXit to receive URI click notifications from the UI */ -void mxit_register_uri_handler(void) +void mxit_register_uri_handler( void ) { not_link_ref_count++; if ( not_link_ref_count == 1 ) { @@ -198,7 +198,7 @@ if ( !buddy ) return; - contact = purple_buddy_get_protocol_data(buddy); + contact = purple_buddy_get_protocol_data( buddy ); if ( !contact ) return; @@ -214,7 +214,7 @@ case MXIT_TYPE_INFO : tmp = g_strdup_printf("%s\n", _( "Loading menu..." )); serv_got_im( session->con, who, tmp, PURPLE_MESSAGE_NOTIFY, time( NULL ) ); - g_free(tmp); + g_free( tmp ); mxit_send_message( session, who, " ", FALSE, FALSE ); default : break; @@ -268,7 +268,7 @@ */ static const char* mxit_list_emblem( PurpleBuddy* buddy ) { - struct contact* contact = purple_buddy_get_protocol_data(buddy); + struct contact* contact = purple_buddy_get_protocol_data( buddy ); if ( !contact ) return NULL; @@ -309,19 +309,18 @@ */ char* mxit_status_text( PurpleBuddy* buddy ) { - struct contact* contact = purple_buddy_get_protocol_data(buddy); + char* text = NULL; + struct contact* contact = purple_buddy_get_protocol_data( buddy ); if ( !contact ) return NULL; - if ( contact->statusMsg ) { - /* status message */ - return g_strdup( contact-> statusMsg ); - } - else { - /* mood */ - return g_strdup( mxit_convert_mood_to_name( contact->mood ) ); - } + if ( contact->statusMsg ) /* status message */ + text = g_strdup( contact-> statusMsg ); + else if ( contact->mood != MXIT_MOOD_NONE ) /* mood */ + text = g_strdup( mxit_convert_mood_to_name( contact->mood ) ); + + return text; } @@ -334,7 +333,7 @@ */ static void mxit_tooltip( PurpleBuddy* buddy, PurpleNotifyUserInfo* info, gboolean full ) { - struct contact* contact = purple_buddy_get_protocol_data(buddy); + struct contact* contact = purple_buddy_get_protocol_data( buddy ); if ( !contact ) return; @@ -358,10 +357,6 @@ /* rejection message */ if ( ( contact->subtype == MXIT_SUBTYPE_REJECTED ) && ( contact->msg != NULL ) ) purple_notify_user_info_add_pair( info, _( "Rejection Message" ), contact->msg ); - - /* hidden number */ - if ( contact->flags & MXIT_CFLAG_HIDDEN ) - purple_notify_user_info_add_pair( info, _( "Hidden Number" ), _( "Yes" ) ); } @@ -429,7 +424,7 @@ char* statusmsg2; /* Handle mood changes */ - if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) { + if ( purple_status_type_get_primitive(purple_status_get_type( status ) ) == PURPLE_STATUS_MOOD ) { const char* moodid = purple_status_get_attr_string( status, PURPLE_MOOD_NAME ); int mood; @@ -492,7 +487,7 @@ purple_debug_info( MXIT_PLUGIN_ID, "mxit_free_buddy\n" ); - contact = purple_buddy_get_protocol_data(buddy); + contact = purple_buddy_get_protocol_data( buddy ); if ( contact ) { if ( contact->statusMsg ) g_free( contact->statusMsg ); @@ -503,7 +498,7 @@ g_free( contact ); } - purple_buddy_set_protocol_data(buddy, NULL); + purple_buddy_set_protocol_data( buddy, NULL ); } @@ -612,12 +607,11 @@ */ static void mxit_reinvite( PurpleBlistNode *node, gpointer ignored ) { - PurpleBuddy* buddy; - struct contact* contact; + PurpleBuddy* buddy = (PurpleBuddy *) node; PurpleConnection* gc; struct MXitSession* session; + struct contact* contact; - buddy = (PurpleBuddy *)node; gc = purple_account_get_connection( purple_buddy_get_account( buddy ) ); session = gc->proto_data; @@ -653,12 +647,24 @@ if ( ( contact->subtype == MXIT_SUBTYPE_DELETED ) || ( contact->subtype == MXIT_SUBTYPE_REJECTED ) || ( contact->subtype == MXIT_SUBTYPE_NONE ) ) { /* contact is in Deleted, Rejected or None state */ act = purple_menu_action_new( _( "Re-Invite" ), PURPLE_CALLBACK( mxit_reinvite ), NULL, NULL ); - m = g_list_append(m, act); + m = g_list_append( m, act ); } return m; } + +/*------------------------------------------------------------------------ + * Return Chat-room default settings. + * + * @return Chat defaults list + */ +static GHashTable *mxit_chat_info_defaults( PurpleConnection *gc, const char *chat_name ) +{ + return g_hash_table_new_full( g_str_hash, g_str_equal, NULL, g_free ); +} + + /*========================================================================================================================*/ static PurplePluginProtocolInfo proto_info = { @@ -680,7 +686,7 @@ mxit_status_types, /* status types [roster.c] */ mxit_blist_menu, /* blist_node_menu */ mxit_chat_info, /* chat_info [multimx.c] */ - NULL, /* chat_info_defaults */ + mxit_chat_info_defaults,/* chat_info_defaults */ mxit_login, /* login [login.c] */ mxit_close, /* close */ mxit_send_im, /* send_im */ diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/mxit.h --- a/libpurple/protocols/mxit/mxit.h Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/mxit.h Sun Aug 21 21:27:17 2011 +0000 @@ -191,7 +191,7 @@ void mxit_enable_signals( struct MXitSession* session ); #ifdef MXIT_LINK_CLICK -void mxit_register_uri_handler(void); +void mxit_register_uri_handler( void ); #endif diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/profile.c --- a/libpurple/protocols/mxit/profile.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/profile.c Sun Aug 21 21:27:17 2011 +0000 @@ -119,12 +119,12 @@ return 0; /* current time */ - t = time(NULL); + t = time( NULL ); localtime_r( &t, &now ); /* decode hdate */ memset( &bdate, 0, sizeof( struct tm ) ); - purple_str_to_time(date, FALSE, &bdate, NULL, NULL); + purple_str_to_time( date, FALSE, &bdate, NULL, NULL ); /* calculate difference */ age = now.tm_year - bdate.tm_year; @@ -172,7 +172,7 @@ if ( buddy ) { purple_notify_user_info_add_pair( info, _( "Alias" ), purple_buddy_get_alias( buddy ) ); purple_notify_user_info_add_section_break( info ); - contact = purple_buddy_get_protocol_data(buddy); + contact = purple_buddy_get_protocol_data( buddy ); } purple_notify_user_info_add_pair( info, _( "Display Name" ), profile->nickname ); @@ -215,9 +215,6 @@ /* subscription type */ purple_notify_user_info_add_pair( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) ); - - /* hidden number */ - purple_notify_user_info_add_pair( info, _( "Hidden Number" ), ( contact->flags & MXIT_CFLAG_HIDDEN ) ? _( "Yes" ) : _( "No" ) ); } else { /* this is an invite */ @@ -284,6 +281,7 @@ /* define columns */ column = purple_notify_searchresults_column_new( _( "UserId" ) ); + purple_notify_searchresult_column_set_visible( column, FALSE ); purple_notify_searchresults_column_add( results, column ); column = purple_notify_searchresults_column_new( _( "Display Name" ) ); purple_notify_searchresults_column_add( results, column ); @@ -298,7 +296,7 @@ column = purple_notify_searchresults_column_new( _( "Where I live" ) ); purple_notify_searchresults_column_add( results, column ); - while (entries != NULL) { + while ( entries != NULL ) { struct MXitProfile* profile = ( struct MXitProfile *) entries->data; GList* row; gchar* tmp = purple_base64_encode( (unsigned char *) profile->userid, strlen( profile->userid ) ); @@ -328,5 +326,5 @@ purple_notify_searchresults( session->con, NULL, text, NULL, results, NULL, NULL ); - g_free( text); + g_free( text ); } diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/profile.h --- a/libpurple/protocols/mxit/profile.h Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/profile.h Sun Aug 21 21:27:17 2011 +0000 @@ -50,7 +50,6 @@ int flags; /* user's profile flags */ gint64 lastonline; /* user's last-online timestamp */ - gboolean hidden; /* set if the user's mxitid should remain hidden */ }; struct MXitSession; diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/protocol.c --- a/libpurple/protocols/mxit/protocol.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/protocol.c Sun Aug 21 21:27:17 2011 +0000 @@ -86,7 +86,7 @@ void mxit_strip_domain( char* username ) { if ( g_str_has_suffix( username, "@m" ) ) - username[ strlen(username) - 2 ] = '\0'; + username[ strlen( username ) - 2 ] = '\0'; } @@ -704,9 +704,9 @@ locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); /* Voice and Video supported */ - if (mxit_audio_enabled() && mxit_video_enabled()) - features |= (MXIT_CF_VOICE | MXIT_CF_VIDEO); - else if (mxit_audio_enabled()) + if ( mxit_audio_enabled() && mxit_video_enabled() ) + features |= ( MXIT_CF_VOICE | MXIT_CF_VIDEO ); + else if ( mxit_audio_enabled() ) features |= MXIT_CF_VOICE; /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ @@ -748,9 +748,9 @@ locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); /* Voice and Video supported */ - if (mxit_audio_enabled() && mxit_video_enabled()) - features |= (MXIT_CF_VOICE | MXIT_CF_VIDEO); - else if (mxit_audio_enabled()) + if ( mxit_audio_enabled() && mxit_video_enabled() ) + features |= ( MXIT_CF_VOICE | MXIT_CF_VIDEO ); + else if ( mxit_audio_enabled() ) features |= MXIT_CF_VOICE; /* generate client version string (eg, P-2.7.10-Y-PURPLE) */ @@ -835,7 +835,7 @@ /* add attributes */ for ( i = 0; i < nr_attrib; i++ ) - datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_GET ); @@ -868,7 +868,7 @@ /* add attributes */ for ( i = 1; i < nr_attrib * 3; i+=3 ) - datalen += sprintf( data + datalen, "%c%s%c%s%c%s", /* \1name\1type\1value */ + datalen += sprintf( 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 */ @@ -900,7 +900,7 @@ /* add attributes */ for ( i = 0; i < nr_attrib; i++ ) - datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); @@ -929,7 +929,7 @@ /* add attributes */ for ( i = 0; i < nr_attrib; i++ ) - datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_SUGGESTCONTACTS ); @@ -1175,7 +1175,7 @@ /* add usernames */ for ( i = 0; i < nr_usernames; i++ ) - datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, usernames[i] ); + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, usernames[i] ); /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_CREATE ); @@ -1204,7 +1204,7 @@ /* add usernames */ for ( i = 0; i < nr_usernames; i++ ) - datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, usernames[i] ); + datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, usernames[i] ); /* queue packet for transmission */ mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_INVITE ); @@ -1448,7 +1448,7 @@ PurpleStatus* status; int presence; const char* statusmsg; - const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_HIDENUMBER, CP_PROFILE_FULLNAME, + 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_FLAGS }; @@ -1516,6 +1516,7 @@ { struct RXMsgData* mx = NULL; char* message = NULL; + char* sender = NULL; int msglen = 0; int msgflags = 0; int msgtype = 0; @@ -1529,10 +1530,11 @@ msglen = strlen( message ); /* strip off dummy domain */ - mxit_strip_domain( records[0]->fields[0]->data ); + sender = records[0]->fields[0]->data; + mxit_strip_domain( sender ); #ifdef DEBUG_PROTOCOL - purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", records[0]->fields[0]->data ); + purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", sender ); #endif /* decode message flags (if any) */ @@ -1540,33 +1542,42 @@ msgflags = atoi( records[0]->fields[4]->data ); msgtype = atoi( records[0]->fields[2]->data ); - if ( msgflags & CP_MSG_ENCRYPTED ) { - /* this is an encrypted message. we do not currently support those so ignore it */ + 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_find_buddy( session->acc, records[0]->fields[0]->data ); + buddy = purple_find_buddy( session->acc, sender ); if ( buddy ) name = purple_buddy_get_alias( buddy ); else - name = records[0]->fields[0]->data; + 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. */ + message = mxit_decrypt_message( session, message ); + if ( !message ) { + /* could not be decrypted */ + 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, records[0]->fields[0]->data, records[0]->fields[3]->data, CP_MSGEVENT_DELIVERED ); + 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( records[0]->fields[0]->data ); + mx->from = g_strdup( sender ); mx->timestamp = atoi( records[0]->fields[1]->data ); mx->got_img = FALSE; mx->chatid = -1; @@ -1597,6 +1608,10 @@ * so the image received callback function will eventually display * the message. */ } + + /* cleanup */ + if ( msgflags & CP_MSG_TL_ENCRYPTED ) + g_free( message ); } @@ -1810,10 +1825,6 @@ /* gender */ profile->male = ( fvalue[0] == '1' ); } - else if ( strcmp( CP_PROFILE_HIDENUMBER, fname ) == 0 ) { - /* hide number */ - profile->hidden = ( fvalue[0] == '1' ); - } else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) { /* nickname */ g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) ); @@ -2270,7 +2281,7 @@ case CP_CMD_PRESENCE : /* presence update */ - mxit_parse_cmd_presence(session, &packet->records[2], packet->rcount - 3 ); + mxit_parse_cmd_presence( session, &packet->records[2], packet->rcount - 3 ); break; case CP_CMD_RX_MSG : diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/protocol.h --- a/libpurple/protocols/mxit/protocol.h Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/protocol.h Sun Aug 21 21:27:17 2011 +0000 @@ -155,7 +155,10 @@ /* message flags */ #define CP_MSG_NOTIFY_DELIVERY 0x0002 /* request delivery notification */ #define CP_MSG_NOTIFY_READ 0x0004 /* request read notification */ -#define CP_MSG_ENCRYPTED 0x0010 /* message is encrypted */ +#define CP_MSG_PWD_ENCRYPTED 0x0010 /* message is password encrypted */ +#define CP_MSG_TL_ENCRYPTED 0x0020 /* message is transport encrypted */ +#define CP_MSG_RPLY_PWD_ENCRYPT 0x0040 /* reply should be password encrypted */ +#define CP_MSG_RPLY_TL_ENCRYPT 0x0080 /* reply should be transport encrypted */ #define CP_MSG_MARKUP 0x0200 /* message may contain markup */ #define CP_MSG_EMOTICON 0x0400 /* message may contain custom emoticons */ @@ -179,7 +182,7 @@ /* extended profile attribute fields */ #define CP_PROFILE_BIRTHDATE "birthdate" /* Birthdate (String - ISO 8601 format) */ #define CP_PROFILE_GENDER "gender" /* Gender (Boolean - 0=female, 1=male) */ -#define CP_PROFILE_HIDENUMBER "hidenumber" /* Hide Number (Boolean - 0=false, 1=true) */ +// #define CP_PROFILE_HIDENUMBER "hidenumber" /* Hide Number (Boolean - 0=false, 1=true) (DEPRECATED) */ #define CP_PROFILE_FULLNAME "fullname" /* Fullname (UTF8 String) */ #define CP_PROFILE_STATUS "statusmsg" /* Status Message (UTF8 String) */ #define CP_PROFILE_PREVSTATUS "prevstatusmsgs" /* Previous Status Messages (UTF8 String) */ diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/roster.c --- a/libpurple/protocols/mxit/roster.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/roster.c Sun Aug 21 21:27:17 2011 +0000 @@ -82,9 +82,9 @@ } /* add Mood option */ - type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD, "mood", NULL, FALSE, TRUE, TRUE, + type = purple_status_type_new_with_attrs( PURPLE_STATUS_MOOD, "mood", NULL, FALSE, TRUE, TRUE, PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new( PURPLE_TYPE_STRING ), - NULL); + NULL ); statuslist = g_list_append( statuslist, type ); return statuslist; @@ -135,21 +135,21 @@ /* moods (reference: libpurple/status.h) */ static PurpleMood mxit_moods[] = { - {"angry", N_("Angry"), NULL}, - {"excited", N_("Excited"), NULL}, - {"grumpy", N_("Grumpy"), NULL}, - {"happy", N_("Happy"), NULL}, - {"in_love", N_("In love"), NULL}, - {"invincible", N_("Invincible"), NULL}, - {"sad", N_("Sad"), NULL}, - {"hot", N_("Hot"), NULL}, - {"sick", N_("Sick"), NULL}, - {"sleepy", N_("Sleepy"), NULL}, - {"bored", N_("Bored"), NULL}, - {"cold", N_("Cold"), NULL}, - {"confused", N_("Confused"), NULL}, - {"hungry", N_("Hungry"), NULL}, - {"stressed", N_("Stressed"), NULL}, + { "angry", N_( "Angry" ), NULL }, + { "excited", N_( "Excited" ), NULL }, + { "grumpy", N_( "Grumpy" ), NULL }, + { "happy", N_( "Happy" ), NULL }, + { "in_love", N_( "In love" ), NULL }, + { "invincible", N_( "Invincible" ), NULL }, + { "sad", N_( "Sad" ), NULL }, + { "hot", N_( "Hot" ), NULL }, + { "sick", N_( "Sick" ), NULL }, + { "sleepy", N_( "Sleepy" ), NULL }, + { "bored", N_( "Bored" ), NULL }, + { "cold", N_( "Cold" ), NULL }, + { "confused", N_( "Confused" ), NULL }, + { "hungry", N_( "Hungry" ), NULL }, + { "stressed", N_( "Stressed" ), NULL }, /* Mark the last record. */ { NULL, NULL, NULL } }; diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/roster.h --- a/libpurple/protocols/mxit/roster.h Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/roster.h Sun Aug 21 21:27:17 2011 +0000 @@ -74,7 +74,7 @@ /* MXit contact flags */ -#define MXIT_CFLAG_HIDDEN 0x02 +//#define MXIT_CFLAG_HIDDEN 0x02 /* (DEPRECATED) */ #define MXIT_CFLAG_GATEWAY 0x04 #define MXIT_CFLAG_FOCUS_SEND_BLANK 0x20000 @@ -96,7 +96,7 @@ /* client protocol constants */ #define MXIT_CP_MAX_JID_LEN 64 #define MXIT_CP_MAX_GROUP_LEN 32 -#define MXIT_CP_MAX_ALIAS_LEN 48 +#define MXIT_CP_MAX_ALIAS_LEN 100 #define MXIT_DEFAULT_GROUP "MXit" @@ -106,8 +106,8 @@ */ struct contact { char username[MXIT_CP_MAX_JID_LEN+1]; /* unique contact name (with domain) */ - char alias[MXIT_CP_MAX_GROUP_LEN+1]; /* contact alias (what will be seen) */ - char groupname[MXIT_CP_MAX_ALIAS_LEN+1]; /* contact group name */ + char alias[MXIT_CP_MAX_ALIAS_LEN+1]; /* contact alias (what will be seen) */ + char groupname[MXIT_CP_MAX_GROUP_LEN+1]; /* contact group name */ short type; /* contact type */ short mood; /* contact current mood */ diff -r dc3ea8f6381a -r 57d43a9a4e7e libpurple/protocols/mxit/voicevideo.c --- a/libpurple/protocols/mxit/voicevideo.c Sun Aug 21 18:43:00 2011 +0000 +++ b/libpurple/protocols/mxit/voicevideo.c Sun Aug 21 21:27:17 2011 +0000 @@ -95,7 +95,7 @@ /* and only when they're online */ if (contact->presence == MXIT_PRESENCE_OFFLINE) - return MXIT_PRESENCE_OFFLINE; + return PURPLE_MEDIA_CAPS_NONE; /* they support voice-only */ if (contact->capabilities & MXIT_PFLAG_VOICE)