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