Mercurial > pidgin
diff libpurple/protocols/mxit/mxit.c @ 28526:69aa4660401a
Initial addition of the MXit protocol plugin, provided by the MXit folks
themselves.
author | John Bailey <rekkanoryo@rekkanoryo.org> |
---|---|
date | Sun, 08 Nov 2009 23:55:56 +0000 |
parents | |
children | 0fd6d016c474 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/mxit.c Sun Nov 08 23:55:56 2009 +0000 @@ -0,0 +1,694 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- MXit libPurple plugin API -- + * + * Pieter Loubser <libpurple@mxit.com> + * + * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd. + * <http://www.mxitlifestyle.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include <glib.h> +#include <stdio.h> +#include <string.h> + +#include "purple.h" +#include "notify.h" +#include "plugin.h" +#include "version.h" + +#include "mxit.h" +#include "protocol.h" +#include "login.h" +#include "roster.h" +#include "chunk.h" +#include "filexfer.h" +#include "actions.h" +#include "multimx.h" + + +#ifdef MXIT_LINK_CLICK + + +/* pidgin callback function pointers for URI click interception */ +static void *(*mxit_pidgin_uri_cb)(const char *uri); +static PurpleNotifyUiOps* mxit_nots_override_original; +static PurpleNotifyUiOps mxit_nots_override; +static int not_link_ref_count = 0; + + +/*------------------------------------------------------------------------ + * Handle an URI clicked on the UI + * + * @param link the link name which has been clicked + */ +static void* mxit_link_click( const char* link64 ) +{ + PurpleAccount* account; + PurpleConnection* con; + gchar** parts = NULL; + gchar* link = NULL; + unsigned int len; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_link_click (%s)\n", link64 ); + + if ( g_ascii_strncasecmp( link64, MXIT_LINK_PREFIX, strlen( MXIT_LINK_PREFIX ) ) != 0 ) { + /* this is not for us */ + goto skip; + } + + /* decode the base64 payload */ + 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 ); + + /* check if this is a valid mxit link */ + if ( ( !parts ) || ( !parts[0] ) || ( !parts[1] ) || ( !parts[2] ) || ( !parts[3] ) || ( !parts[4] ) ) { + /* this is not for us */ + goto skip; + } + else if ( g_ascii_strcasecmp( parts[0], MXIT_LINK_KEY ) != 0 ) { + /* this is not for us */ + goto skip; + } + + /* find the account */ + account = purple_accounts_find( parts[1], parts[2] ); + if ( !account ) + goto skip; + con = purple_account_get_connection( account ); + + /* send click message back to MXit */ + mxit_send_message( con->proto_data, parts[3], parts[4], FALSE ); + + g_free( link ); + link = NULL; + g_strfreev( parts ); + parts = NULL; + + return (void*) link64; + +skip: + /* this is not an internal mxit link */ + + if ( link ) + g_free( link ); + link = NULL; + + if ( parts ) + g_strfreev( parts ); + parts = NULL; + + if ( mxit_pidgin_uri_cb ) + return mxit_pidgin_uri_cb( link64 ); + else + return (void*) link64; +} + + +/*------------------------------------------------------------------------ + * Register MXit to receive URI click notifications from the UI + */ +void mxit_register_uri_handler() +{ + not_link_ref_count++; + if ( not_link_ref_count == 1 ) { + /* make copy of notifications */ + mxit_nots_override_original = purple_notify_get_ui_ops(); + memcpy( &mxit_nots_override, mxit_nots_override_original, sizeof( PurpleNotifyUiOps ) ); + + /* save previously configured callback function pointer */ + mxit_pidgin_uri_cb = mxit_nots_override.notify_uri; + + /* override the URI function call with MXit's own one */ + mxit_nots_override.notify_uri = mxit_link_click; + purple_notify_set_ui_ops( &mxit_nots_override ); + } +} + + +/*------------------------------------------------------------------------ + * Unegister MXit from receiving URI click notifications from the UI + */ +static void mxit_unregister_uri_handler() +{ + not_link_ref_count--; + if ( not_link_ref_count == 0 ) { + /* restore the notifications to its original state */ + purple_notify_set_ui_ops( mxit_nots_override_original ); + } +} + +#endif + + +/*------------------------------------------------------------------------ + * This gets called when a new chat conversation is opened by the user + * + * @param conv The conversation object + * @param session The MXit session object + */ +static void mxit_cb_chat_created( PurpleConversation* conv, struct MXitSession* session ) +{ + PurpleConnection* gc; + struct contact* contact; + PurpleBuddy* buddy; + const char* who; + + gc = purple_conversation_get_gc( conv ); + if ( session->con != gc ) { + /* not our conversation */ + return; + } + else if ( purple_conversation_get_type( conv ) != PURPLE_CONV_TYPE_IM ) { + /* wrong type of conversation */ + return; + } + + /* get the contact name */ + who = purple_conversation_get_name( conv ); + if ( !who ) + return; + + purple_debug_info( MXIT_PLUGIN_ID, "Conversation started with '%s'\n", who ); + + /* find the buddy object */ + buddy = purple_find_buddy( session->acc, who ); + if ( ( !buddy ) || ( !buddy->proto_data ) ) + return; + + /* we ignore all conversations with which we have chatted with in this session */ + if ( find_active_chat( session->active_chats, who ) ) + return; + + /* determite if this buddy is a MXit service */ + contact = buddy->proto_data; + switch ( contact->type ) { + case MXIT_TYPE_BOT : + case MXIT_TYPE_CHATROOM : + case MXIT_TYPE_GALLERY : + case MXIT_TYPE_INFO : + serv_got_im( session->con, who, "<font color=\"#999999\">Loading menu...</font>\n", PURPLE_MESSAGE_NOTIFY, time( NULL ) ); + mxit_send_message( session, who, " ", FALSE ); + default : + break; + } +} + + +/*------------------------------------------------------------------------ + * Enable some signals to handled by our plugin + * + * @param session The MXit session object + */ +void mxit_enable_signals( struct MXitSession* session ) +{ + /* enable the signal when a new conversation is opened by the user */ + purple_signal_connect_priority( purple_conversations_get_handle(), "conversation-created", session, PURPLE_CALLBACK( mxit_cb_chat_created ), + session, PURPLE_SIGNAL_PRIORITY_HIGHEST ); +} + + +/*------------------------------------------------------------------------ + * Disable some signals handled by our plugin + * + * @param session The MXit session object + */ +static void mxit_disable_signals( struct MXitSession* session ) +{ + /* disable the signal when a new conversation is opened by the user */ + purple_signal_disconnect( purple_conversations_get_handle(), "conversation-created", session, PURPLE_CALLBACK( mxit_cb_chat_created ) ); +} + + +/*------------------------------------------------------------------------ + * Return the base icon name. + * + * @param account The MXit account object + * @param buddy The buddy + * @return The icon name (excluding extension) + */ +static const char* mxit_list_icon( PurpleAccount* account, PurpleBuddy* buddy ) +{ + return "mxit"; +} + + +/*------------------------------------------------------------------------ + * Return the emblem icon name. + * + * @param buddy The buddy + * @return The icon name (excluding extension) + */ +static const char* mxit_list_emblem( PurpleBuddy* buddy ) +{ + struct contact* contact = buddy->proto_data; + + if ( !contact ) + return NULL; + + switch ( contact-> type ) { + case MXIT_TYPE_JABBER : /* external contacts via MXit */ + case MXIT_TYPE_MSN : + case MXIT_TYPE_YAHOO : + case MXIT_TYPE_ICQ : + case MXIT_TYPE_AIM : + case MXIT_TYPE_QQ : + case MXIT_TYPE_WV : + return "external"; + + case MXIT_TYPE_BOT : /* MXit services */ + case MXIT_TYPE_GALLERY : + case MXIT_TYPE_INFO : + return "bot"; + + case MXIT_TYPE_CHATROOM : /* MXit group chat services */ + case MXIT_TYPE_MULTIMX : + default: + return NULL; + } +} + + +/*------------------------------------------------------------------------ + * Return short string representing buddy's status for display on buddy list. + * Returns status message (if one is set), or otherwise the mood. + * + * @param buddy The buddy. + * @return The status text + */ +char* mxit_status_text( PurpleBuddy* buddy ) +{ + struct contact* contact = buddy->proto_data; + + 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 ) ); + } +} + + +/*------------------------------------------------------------------------ + * Return UI tooltip information for a buddy when hovering in buddy list. + * + * @param buddy The buddy + * @param info The tooltip info being returned + * @param full Return full or summarized information + */ +static void mxit_tooltip( PurpleBuddy* buddy, PurpleNotifyUserInfo* info, gboolean full ) +{ + struct contact* contact = buddy->proto_data; + + if ( !contact ) + return; + + /* status (reference: "libpurple/notify.h") */ + if ( contact->presence != MXIT_PRESENCE_OFFLINE ) + purple_notify_user_info_add_pair( info, _( "Status" ), mxit_convert_presence_to_name( contact->presence ) ); + + /* status message */ + if ( contact->statusMsg ) + purple_notify_user_info_add_pair( info, _( "Status Message" ), contact->statusMsg ); + + /* mood */ + if ( contact->mood != MXIT_MOOD_NONE ) + purple_notify_user_info_add_pair( info, _( "Mood" ), mxit_convert_mood_to_name( contact->mood ) ); + + /* subscription type */ + if ( contact->subtype != 0 ) + purple_notify_user_info_add_pair( info, _( "Subscription" ), mxit_convert_subtype_to_name( contact->subtype ) ); + + /* hidden number */ + if ( contact->flags & MXIT_CFLAG_HIDDEN ) + purple_notify_user_info_add_pair( info, _( "Hidden Number" ), "Yes" ); +} + + +/*------------------------------------------------------------------------ + * Initiate the logout sequence, close the connection and clear the session data. + * + * @param gc The connection object + */ +static void mxit_close( PurpleConnection* gc ) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + + /* disable signals */ + mxit_disable_signals( session ); + + /* close the connection */ + mxit_close_connection( session ); + +#ifdef MXIT_LINK_CLICK + /* unregister for uri click notification */ + mxit_unregister_uri_handler(); +#endif + + purple_debug_info( MXIT_PLUGIN_ID, "Releasing the session object..\n" ); + + /* free the session memory */ + g_free( session ); + session = NULL; +} + + +/*------------------------------------------------------------------------ + * Send a message to a contact + * + * @param gc The connection object + * @param who The username of the recipient + * @param message The message text + * @param flags Message flags (defined in conversation.h) + * @return Positive value (success, and echo to conversation window) + Zero (success, no echo) + Negative value (error) + */ +static int mxit_send_im( PurpleConnection* gc, const char* who, const char* message, PurpleMessageFlags flags ) +{ + purple_debug_info( MXIT_PLUGIN_ID, "Sending message '%s' to buddy '%s'\n", message, who ); + + mxit_send_message( gc->proto_data, who, message, TRUE ); + + return 1; /* echo to conversation window */ +} + + +/*------------------------------------------------------------------------ + * The user changed their current presence state. + * + * @param account The MXit account object + * @param status The new status (libPurple status type) + */ +static void mxit_set_status( PurpleAccount* account, PurpleStatus* status ) +{ + struct MXitSession* session = purple_account_get_connection( account )->proto_data; + const char* statusid; + int presence; + char* statusmsg1; + char* statusmsg2; + + /* get the status id (reference: "libpurple/status.h") */ + statusid = purple_status_get_id( status ); + + /* convert the purple status to a mxit status */ + presence = mxit_convert_presence( statusid ); + if ( presence < 0 ) { + /* error, status not found */ + purple_debug_info( MXIT_PLUGIN_ID, "Presence status NOT found! (id = %s)\n", statusid ); + return; + } + + statusmsg1 = purple_markup_strip_html( purple_status_get_attr_string( status, "message" ) ); + statusmsg2 = g_strndup( statusmsg1, CP_MAX_STATUS_MSG ); + purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_status: '%s'\n", statusmsg2 ); + + /* update presence state */ + mxit_send_presence( session, presence, statusmsg2 ); + + g_free( statusmsg1 ); + g_free( statusmsg2 ); +} + + +/*------------------------------------------------------------------------ + * MXit supports messages to offline contacts. + * + * @param buddy The buddy + */ +static gboolean mxit_offline_message( const PurpleBuddy *buddy ) +{ + return TRUE; +} + + +/*------------------------------------------------------------------------ + * Free the resources used to store a buddy. + * + * @param buddy The buddy + */ +static void mxit_free_buddy( PurpleBuddy* buddy ) +{ + struct contact* contact; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_free_buddy\n" ); + + contact = buddy->proto_data; + if ( contact ) { + if ( contact->statusMsg ) + g_free( contact->statusMsg ); + if ( contact->avatarId ) + g_free( contact->avatarId ); + g_free( contact ); + } + buddy->proto_data = NULL; +} + + +/*------------------------------------------------------------------------ + * Periodic task called every KEEPALIVE_INTERVAL (30 sec) to to maintain + * idle connections, timeouts and the transmission queue to the MXit server. + * + * @param gc The connection object + */ +static void mxit_keepalive( PurpleConnection *gc ) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + + /* if not logged in, there is nothing to do */ + if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) + return; + + /* pinging is only for socket connections (HTTP does polling) */ + if ( session->http ) + return; + + if ( session->last_tx <= time( NULL ) - MXIT_PING_INTERVAL ) { + /* + * this connection has been idle for too long, better ping + * the server before it kills our connection. + */ + mxit_send_ping( session ); + } +} + + +/*------------------------------------------------------------------------ + * Set or clear our Buddy icon. + * + * @param gc The connection object + * @param img The buddy icon data + */ +static void mxit_set_buddy_icon( PurpleConnection *gc, PurpleStoredImage *img ) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + + if ( img == NULL ) + mxit_set_avatar( session, NULL, 0 ); + else + mxit_set_avatar( session, purple_imgstore_get_data( img ), purple_imgstore_get_size( img ) ); +} + + +/*------------------------------------------------------------------------ + * Request profile information for another MXit contact. + * + * @param gc The connection object + * @param who The username of the contact. + */ +static void mxit_get_info( PurpleConnection *gc, const char *who ) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + 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 }; + + purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_info: '%s'\n", who ); + + + /* send profile request */ + mxit_send_extprofile_request( session, who, ARRAY_SIZE( profilelist ), profilelist ); +} + + +/*------------------------------------------------------------------------ + * Return a list of labels to be used by Pidgin for assisting the user. + */ +static GHashTable* mxit_get_text_table( PurpleAccount* acc ) +{ + GHashTable* table; + + table = g_hash_table_new( g_str_hash, g_str_equal ); + + g_hash_table_insert( table, "login_label", _( "Your Mobile Number..." ) ); + + return table; +} + +/*========================================================================================================================*/ + +static PurplePluginProtocolInfo proto_info = { + OPT_PROTO_REGISTER_NOSCREENNAME | OPT_PROTO_UNIQUE_CHATNAME | OPT_PROTO_IM_IMAGE, /* options */ + NULL, /* user_splits */ + NULL, /* protocol_options */ + { /* icon_spec */ + "png", /* format */ + 32, 32, /* min width & height */ + MXIT_AVATAR_SIZE, /* max width */ + MXIT_AVATAR_SIZE, /* max height */ + 100000, /* max filezize */ + PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY /* scaling rules */ + }, + mxit_list_icon, /* list_icon */ + mxit_list_emblem, /* list_emblem */ + mxit_status_text, /* status_text */ + mxit_tooltip, /* tooltip_text */ + mxit_status_types, /* status types [roster.c] */ + NULL, /* blist_node_menu */ + mxit_chat_info, /* chat_info [multimx.c] */ + NULL, /* chat_info_defaults */ + mxit_login, /* login [login.c] */ + mxit_close, /* close */ + mxit_send_im, /* send_im */ + NULL, /* set_info */ + NULL, /* send_typing */ + mxit_get_info, /* get_info */ + mxit_set_status, /* set_status */ + NULL, /* set_idle */ + NULL, /* change_passwd */ + mxit_add_buddy, /* add_buddy [roster.c] */ + NULL, /* add_buddies */ + mxit_remove_buddy, /* remove_buddy [roster.c] */ + NULL, /* remove_buddies */ + NULL, /* add_permit */ + NULL, /* add_deny */ + NULL, /* rem_permit */ + NULL, /* rem_deny */ + NULL, /* set_permit_deny */ + mxit_chat_join, /* join_chat [multimx.c] */ + mxit_chat_reject, /* reject chat invite [multimx.c] */ + mxit_chat_name, /* get_chat_name [multimx.c] */ + mxit_chat_invite, /* chat_invite [multimx.c] */ + mxit_chat_leave, /* chat_leave [multimx.c] */ + NULL, /* chat_whisper */ + mxit_chat_send, /* chat_send [multimx.c] */ + mxit_keepalive, /* keepalive */ + mxit_register, /* register_user */ + NULL, /* get_cb_info */ + NULL, /* get_cb_away */ + mxit_buddy_alias, /* alias_buddy [roster.c] */ + mxit_buddy_group, /* group_buddy [roster.c] */ + mxit_rename_group, /* rename_group [roster.c] */ + mxit_free_buddy, /* buddy_free */ + NULL, /* convo_closed */ + NULL, /* normalize */ + mxit_set_buddy_icon, /* set_buddy_icon */ + NULL, /* remove_group */ // TODO: Add function to move all contacts out of this group (cmd=30 - remove group)? + NULL, /* get_cb_real_name */ + NULL, /* set_chat_topic */ + NULL, /* find_blist_chat */ + NULL, /* roomlist_get_list */ + NULL, /* roomlist_cancel */ + NULL, /* roomlist_expand_category */ + mxit_xfer_enabled, /* can_receive_file [filexfer.c] */ + mxit_xfer_tx, /* send_file [filexfer.c */ + mxit_xfer_new, /* new_xfer [filexfer.c] */ + mxit_offline_message, /* offline_message */ + NULL, /* whiteboard_prpl_ops */ + NULL, /* send_raw */ + NULL, /* roomlist_room_serialize */ + NULL, /* unregister_user */ + NULL, /* send_attention */ + NULL, /* attention_types */ + sizeof( PurplePluginProtocolInfo ), /* struct_size */ + mxit_get_text_table, /* get_account_text_table */ + NULL, + NULL +}; + + +static PurplePluginInfo plugin_info = { + PURPLE_PLUGIN_MAGIC, /* purple magic, this must always be PURPLE_PLUGIN_MAGIC */ + PURPLE_MAJOR_VERSION, /* libpurple version */ + PURPLE_MINOR_VERSION, /* libpurple version */ + PURPLE_PLUGIN_PROTOCOL, /* plugin type (connecting to another network) */ + NULL, /* UI requirement (NULL for core plugin) */ + 0, /* plugin flags (zero is default) */ + NULL, /* plugin dependencies (set this value to NULL no matter what) */ + PURPLE_PRIORITY_DEFAULT, /* libpurple priority */ + + MXIT_PLUGIN_ID, /* plugin id (must be unique) */ + MXIT_PLUGIN_NAME, /* plugin name (this will be displayed in the UI) */ + MXIT_PLUGIN_VERSION, /* version of the plugin */ + + MXIT_PLUGIN_SUMMARY, /* short summary of the plugin */ + MXIT_PLUGIN_DESC, /* description of the plugin (can be long) */ + MXIT_PLUGIN_EMAIL, /* plugin author name and email address */ + MXIT_PLUGIN_WWW, /* plugin website (to find new versions and reporting of bugs) */ + + NULL, /* function pointer for loading the plugin */ + NULL, /* function pointer for unloading the plugin */ + NULL, /* function pointer for destroying the plugin */ + + NULL, /* pointer to an UI-specific struct */ + &proto_info, /* pointer to either a PurplePluginLoaderInfo or PurplePluginProtocolInfo struct */ + NULL, /* pointer to a PurplePluginUiInfo struct */ + mxit_actions, /* function pointer where you can define plugin-actions */ + + /* padding */ + NULL, /* pointer reserved for future use */ + NULL, /* pointer reserved for future use */ + NULL, /* pointer reserved for future use */ + NULL /* pointer reserved for future use */ +}; + + +/*------------------------------------------------------------------------ + * Initialising the MXit plugin. + * + * @param plugin The plugin object + */ +static void init_plugin( PurplePlugin* plugin ) +{ + PurpleAccountOption* option; + + purple_debug_info( MXIT_PLUGIN_ID, "Loading MXit libPurple plugin...\n" ); + + /* Configuration options */ + + /* WAP server (reference: "libpurple/accountopt.h") */ + option = purple_account_option_string_new( _( "WAP Server" ), MXIT_CONFIG_WAPSERVER, DEFAULT_WAPSITE ); + proto_info.protocol_options = g_list_append( proto_info.protocol_options, option ); + + option = purple_account_option_bool_new( _( "Connect via HTTP" ), MXIT_CONFIG_USE_HTTP, FALSE ); + proto_info.protocol_options = g_list_append( proto_info.protocol_options, option ); + + option = purple_account_option_bool_new( _( "Enable splash-screen popup" ), MXIT_CONFIG_SPLASHPOPUP, FALSE ); + proto_info.protocol_options = g_list_append( proto_info.protocol_options, option ); + + g_assert( sizeof( struct raw_chunk ) == 5 ); +} + +PURPLE_INIT_PLUGIN( mxit, init_plugin, plugin_info ); +