Mercurial > pidgin
diff libpurple/protocols/mxit/multimx.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 | 5f80ab7ac183 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/mxit/multimx.c Sun Nov 08 23:55:56 2009 +0000 @@ -0,0 +1,597 @@ +/* + * MXit Protocol libPurple Plugin + * + * -- MultiMx GroupChat -- + * + * Andrew Victor <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 <string.h> +#include <errno.h> + +#include "purple.h" +#include "prpl.h" + +#include "protocol.h" +#include "mxit.h" +#include "multimx.h" +#include "markup.h" + + +#if 0 +static void multimx_dump(struct multimx* multimx) +{ + purple_debug_info(MXIT_PLUGIN_ID, "MultiMX:\n"); + purple_debug_info(MXIT_PLUGIN_ID, " Chat ID: %i\n", multimx->chatid); + purple_debug_info(MXIT_PLUGIN_ID, " Username: %s\n", multimx->roomid); + purple_debug_info(MXIT_PLUGIN_ID, " Alias: %s\n", multimx->roomname); + purple_debug_info(MXIT_PLUGIN_ID, " State: %i\n", multimx->state); +} +#endif + + +/*------------------------------------------------------------------------ + * Find a MultiMx session based on libpurple chatID. + * + * @param session The MXit session object + * @param id The libpurple group-chat ID + * @return The MultiMX room object (or NULL if not found) + */ +static struct multimx* find_room_by_id(struct MXitSession* session, int id) +{ + GList* x = session->rooms; + + while (x != NULL) { + struct multimx* multimx = (struct multimx *) x->data; + + if (multimx->chatid == id) + return multimx; + + x = g_list_next(x); + } + + return NULL; +} + + +/*------------------------------------------------------------------------ + * Find a MultiMx session based on Alias + * + * @param session The MXit session object + * @param roomname The UI room-name + * @return The MultiMX room object (or NULL if not found) + */ +static struct multimx* find_room_by_alias(struct MXitSession* session, const char* roomname) +{ + GList* x = session->rooms; + + while (x != NULL) { + struct multimx* multimx = (struct multimx *) x->data; + + if (!strcmp(multimx->roomname, roomname)) + return multimx; + + x = g_list_next(x); + } + + return NULL; +} + + +/*------------------------------------------------------------------------ + * Find a MultiMx session based on Username (MXit RoomId) + * + * @param session The MXit session object + * @param username The MXit RoomID (MultiMX contact username) + * @return The MultiMX room object (or NULL if not found) + */ +static struct multimx* find_room_by_username(struct MXitSession* session, const char* username) +{ + GList* x = session->rooms; + + while (x != NULL) { + struct multimx* multimx = (struct multimx *) x->data; + + if (!strcmp(multimx->roomid, username)) + return multimx; + + x = g_list_next(x); + } + + return NULL; +} + + +/*------------------------------------------------------------------------ + * Create a GroupChat room, and add to list of rooms. + * + * @param session The MXit session object + * @param roomid The MXit RoomID (MultiMX contact username) + * @param roomname The UI room-name + * @param state The initial state of the room (see multimx.h) + * @return The MultiMX room object + */ +static struct multimx* room_create(struct MXitSession* session, const char* roomid, const char* roomname, short state) +{ + struct multimx* multimx = NULL; + static int groupchatID = 1; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat create - roomid='%s' roomname='%s'\n", roomid, roomname); + + /* Create a new GroupChat */ + multimx = g_new0(struct multimx, 1); + + /* Initialize groupchat */ + g_strlcpy(multimx->roomid, roomid, sizeof(multimx->roomid)); + g_strlcpy(multimx->roomname, roomname, sizeof(multimx->roomname)); + multimx->chatid = groupchatID++; + multimx->state = state; + + /* Add to GroupChat list */ + session->rooms = g_list_append(session->rooms, multimx); + + return multimx; +} + + +/*------------------------------------------------------------------------ + * Free the Groupchat room. + * + * @param session The MXit session object + * @param multimx The MultiMX room object to deallocate + */ +static void room_remove(struct MXitSession* session, struct multimx* multimx) +{ + /* Remove from GroupChat list */ + session->rooms = g_list_remove(session->rooms, multimx); + + /* Deallocate it */ + free (multimx); + multimx = NULL; +} + + +/*------------------------------------------------------------------------ + * Another user has join the GroupChat, add them to the member-list. + * + * @param session The MXit session object + * @param multimx The MultiMX room object + * @param nickname The nickname of the user who joined the room + */ +static void member_added(struct MXitSession* session, struct multimx* multimx, const char* nickname) +{ + PurpleConversation *convo; + + purple_debug_info(MXIT_PLUGIN_ID, "member_added: '%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_add_user(PURPLE_CONV_CHAT(convo), nickname, NULL, PURPLE_CBFLAGS_NONE, TRUE); +} + + +/*------------------------------------------------------------------------ + * 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 nickname The nickname of the user who left the room + */ +static void member_removed(struct MXitSession* session, struct multimx* multimx, 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); +} + + +/*------------------------------------------------------------------------ + * Update the full GroupChat member list. + * + * @param session The MXit session object + * @param multimx The MultiMX room 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) +{ + 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)); + + /* Add each member */ + data = g_strstrip(data); /* string leading & trailing whitespace */ + userlist = g_strsplit(data, "\n", 0); /* tokenize string */ + while (userlist[i] != NULL) { + purple_debug_info(MXIT_PLUGIN_ID, "member_update - adding: '%s'\n", userlist[i]); + purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), userlist[i], NULL, PURPLE_CBFLAGS_NONE, FALSE); + i++; + } + g_strfreev(userlist); +} + + +/* ------------------------------------------------------------------------------------------------- + * Calls from MXit Protocol layer + * ------------------------------------------------------------------------------------------------- */ + +/*------------------------------------------------------------------------ + * Received a Subscription Request to a MultiMX room. + * + * @param session The MXit session object + * @param contact The invited MultiMX room's contact information + * @param creator The nickname of the room's creator / invitor + */ +void multimx_invite(struct MXitSession* session, struct contact* contact, const char* creator) +{ + GHashTable *components; + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat invite to '%s' by '%s'\n", contact->alias, creator); + + /* Create a new room */ + multimx = room_create(session, contact->username, contact->alias, STATE_INVITED); + + components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + g_hash_table_insert(components, g_strdup("room"), g_strdup(contact->alias)); + + /* Call libpurple - will trigger either 'mxit_chat_join' or 'mxit_chat_reject' */ + serv_got_chat_invite(session->con, contact->alias, creator, NULL, components); +} + + +/*------------------------------------------------------------------------ + * MultiMX room has been added to the roster. + * + * @param session The MXit session object + * @param contact The MultiMX room's contact information + */ +void multimx_created(struct MXitSession* session, struct contact* contact) +{ + PurpleConnection *gc = session->con; + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat '%s' created as '%s'\n", contact->alias, contact->username); + + /* Find matching MultiMX group */ + multimx = find_room_by_username(session, contact->username); + if (multimx == NULL) { + multimx = room_create(session, contact->username, contact->alias, TRUE); + } + else if (multimx->state == STATE_INVITED) { + /* After successfully accepting an invitation */ + multimx->state = STATE_JOINED; + } + + /* Call libpurple - will trigger 'mxit_chat_join' */ + serv_got_joined_chat(gc, multimx->chatid, multimx->roomname); + + /* Send ".list" command to GroupChat server to retrieve current member-list */ + mxit_send_message(session, multimx->roomid, ".list", FALSE); +} + + +/*------------------------------------------------------------------------ + * Is this username a MultiMX contact? + * + * @param session The MXit session object + * @param username The username of the contact + * @return TRUE if this contacts matches the RoomID of a MultiMX room. + */ +gboolean is_multimx_contact(struct MXitSession* session, const char* username) +{ + /* Check for username in list of open rooms */ + return (find_room_by_username(session, username) != NULL); +} + + +/*------------------------------------------------------------------------ + * Received a message from a MultiMX room. + * + */ +void multimx_message_received(struct RXMsgData* mx, char* msg, int msglen, short msgtype, int msgflags) +{ + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat message received: %s\n", msg); + + /* Find matching multimx group */ + multimx = find_room_by_username(mx->session, mx->from); + if (multimx == NULL) { + purple_debug_error(MXIT_PLUGIN_ID, "Groupchat '%s' not found\n", mx->from); + return; + } + + /* Determine if system message or a message from a contact */ + if (msg[0] == '<') { + /* Message contains embedded nickname - must be from contact */ + unsigned int i; + + for (i = 1; i < strlen(msg); i++) { /* search for end of nickname */ + if (msg[i] == '>') { + msg[i] = '\0'; + g_free(mx->from); + mx->from = g_strdup(&msg[1]); + msg = &msg[i+2]; /* skip '>' and newline */ + break; + } + } + + /* now do markup processing on the message */ + mx->chatid = multimx->chatid; + mxit_parse_markup(mx, msg, strlen(msg), msgtype, msgflags); + } + else { + /* Must be a service message */ + char* ofs; + + /* 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); + mx->processed = TRUE; + } + else if ((ofs = strstr(msg, " has left")) != NULL) { + /* Somebody has left */ + *ofs = '\0'; + member_removed(mx->session, multimx, msg); + 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); + mx->processed = TRUE; + } + else { + /* Display server message in chat window */ + serv_got_chat_in(mx->session->con, multimx->chatid, "MXit", PURPLE_MESSAGE_SYSTEM, msg, mx->timestamp); + mx->processed = TRUE; + } + } +} + + + +/* ------------------------------------------------------------------------------------------------- + * Callbacks from libpurple + * ------------------------------------------------------------------------------------------------- */ + +/*------------------------------------------------------------------------ + * User has selected "Add Chat" from the main menu. + * + * @param gc The connection object + * @return A list of chat configuration values + */ +GList* mxit_chat_info(PurpleConnection *gc) +{ + GList *m = NULL; + struct proto_chat_entry *pce; + + /* Configuration option: Room Name */ + pce = g_new0(struct proto_chat_entry, 1); + pce->label = "_Room Name:"; + pce->identifier = "room"; + pce->required = TRUE; + m = g_list_append(m, pce); + + return m; +} + + +/*------------------------------------------------------------------------ + * User has joined a chatroom, either because they are creating it or they + * accepted an invite. + * + * @param gc The connection object + * @param components The list of chat configuration values + */ +void mxit_chat_join(PurpleConnection *gc, GHashTable *components) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + const char* roomname = NULL; + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "mxit_chat_join\n"); + + /* Determine if groupchat already exists */ + roomname = g_hash_table_lookup(components, "room"); + multimx = find_room_by_alias(session, roomname); + + if (multimx != NULL) { + /* The room information already exists */ + + if (multimx->state == STATE_INVITED) { + /* Invite is pending */ + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat %i accept sent\n", multimx->chatid); + + /* Send Subscription Accept to MXit */ + mxit_send_allow_sub(session, multimx->roomid, multimx->roomname); + } + else { + /* Join existing room */ + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat %i rejoined\n", multimx->chatid); + + serv_got_joined_chat(gc, multimx->chatid, multimx->roomname); + } + } + else { + /* Send Groupchat Create to MXit */ + mxit_send_groupchat_create(session, roomname, 0, NULL); + } +} + + +/*------------------------------------------------------------------------ + * User has rejected an invite to join a MultiMX room. + * + * @param gc The connection object + * @param components The list of chat configuration values + */ +void mxit_chat_reject(PurpleConnection *gc, GHashTable* components) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + const char* roomname = NULL; + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "mxit_chat_reject\n"); + + roomname = g_hash_table_lookup(components, "room"); + multimx = find_room_by_alias(session, roomname); + if (multimx == NULL) { + purple_debug_error(MXIT_PLUGIN_ID, "Groupchat '%s' not found\n", roomname); + return; + } + + /* Send Subscription Reject to MXit */ + mxit_send_deny_sub(session, multimx->roomid); + + /* Remove from our list of rooms */ + room_remove(session, multimx); +} + + +/*------------------------------------------------------------------------ + * Return name of chatroom (on mouse hover) + * + * @param components The list of chat configuration values. + * @return The name of the chat room + */ +char* mxit_chat_name(GHashTable *components) +{ + return g_strdup(g_hash_table_lookup(components, "room")); +} + + +/*------------------------------------------------------------------------ + * User has selected to invite somebody to a chatroom. + * + * @param gc The connection object + * @param id The chat room ID + * @param msg The invitation message entered by the user + * @param name The username of the person to invite + */ +void mxit_chat_invite(PurpleConnection *gc, int id, const char *msg, const char *username) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat invite to '%s'\n", username); + + /* Find matching MultiMX group */ + multimx = find_room_by_id(session, id); + if (multimx == NULL) { + purple_debug_error(MXIT_PLUGIN_ID, "Could not find groupchat %i\n", id); + return; + } + + /* Send invite to MXit */ + mxit_send_groupchat_invite(session, multimx->roomid, 1, &username); +} + + +/*------------------------------------------------------------------------ + * User as closed the chat window, and the chatroom is not marked as persistent. + * + * @param gc The connection object + * @param id The chat room ID + */ +void mxit_chat_leave(PurpleConnection *gc, int id) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + struct multimx* multimx = NULL; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat %i leave\n", id); + + /* Find matching multimx group */ + multimx = find_room_by_id(session, id); + if (multimx == NULL) { + purple_debug_error(MXIT_PLUGIN_ID, "Could not find groupchat %i\n", id); + return; + } + + /* Send Remove Groupchat to MXit */ + mxit_send_remove(session, multimx->roomid); + + /* Remove from our list of rooms */ + room_remove(session, multimx); +} + + +/*------------------------------------------------------------------------ + * User has entered a message in a chatroom window, send it to the MXit server. + * + * @param gc The connection object + * @param id The chat room ID + * @param message The sent message data + * @param flags The message flags + * @return Indicates success / failure + */ +int mxit_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) +{ + struct MXitSession* session = (struct MXitSession*) gc->proto_data; + struct multimx* multimx = NULL; + const char* nickname; + + purple_debug_info(MXIT_PLUGIN_ID, "Groupchat %i message send: '%s'\n", id, message); + + /* Find matching MultiMX group */ + multimx = find_room_by_id(session, id); + if (multimx == NULL) { + purple_debug_error(MXIT_PLUGIN_ID, "Could not find groupchat %i\n", id); + return -1; + } + + /* Send packet to MXit */ + mxit_send_message(session, multimx->roomid, message, TRUE); + + /* Determine our nickname to display */ + if (session->profile && (session->profile->nickname[0] != '\0')) /* default is profile name (since that's what everybody else sees) */ + nickname = session->profile->nickname; + else + nickname = purple_account_get_alias(purple_connection_get_account(gc)); /* local alias */ + + /* Display message in chat window */ + serv_got_chat_in(gc, id, nickname, flags, message, time(NULL)); + + return 0; +} +