# HG changeset patch # User masca@cpw.pidgin.im # Date 1284231805 0 # Node ID 6469c68fa0939c2b2624bcee48e2505e98711f49 # Parent c49697f075cfd162743d3f3b1c8c4251611fbb29# Parent 9af193ee13b7bac83ed49e6315df86d4ea581ca5 propagate from branch 'im.pidgin.pidgin' (head fabc09bf724818b9b50e1c41d4afd6549f298c05) to branch 'im.pidgin.cpw.qulogic.msnp16' (head d8e8a3b3ec17b199432993002327e4ecf156d12b) diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/Makefile.am --- a/libpurple/protocols/msn/Makefile.am Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/Makefile.am Sat Sep 11 19:03:25 2010 +0000 @@ -12,8 +12,6 @@ command.h \ contact.c\ contact.h\ - dialog.c \ - dialog.h \ directconn.c \ directconn.h \ error.c \ @@ -56,8 +54,6 @@ state.h \ switchboard.c \ switchboard.h \ - sync.c \ - sync.h \ table.c \ table.h \ transaction.c \ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/cmdproc.c --- a/libpurple/protocols/msn/cmdproc.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Sat Sep 11 19:03:25 2010 +0000 @@ -122,7 +122,8 @@ return; } - msn_history_add(cmdproc->history, trans); + if (trans->saveable) + msn_history_add(cmdproc->history, trans); data = msn_transaction_to_string(trans); @@ -155,75 +156,6 @@ } void -msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command, - const char *format, ...) -{ - MsnServConn *servconn; - char *data; - char *params = NULL; - va_list arg; - size_t len; - - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(command != NULL); - - servconn = cmdproc->servconn; - - if (!servconn->connected) - return; - - if (format != NULL) - { - va_start(arg, format); - params = g_strdup_vprintf(format, arg); - va_end(arg); - } - - if (params != NULL) - data = g_strdup_printf("%s %s\r\n", command, params); - else - data = g_strdup_printf("%s\r\n", command); - - g_free(params); - - len = strlen(data); - - show_debug_cmd(cmdproc, FALSE, data); - - msn_servconn_write(servconn, data, len); - - g_free(data); -} - -void -msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command, - const char *format, ...) -{ - MsnTransaction *trans; - va_list arg; - - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(command != NULL); - - if (!cmdproc->servconn->connected) - return; - - trans = g_new0(MsnTransaction, 1); - - trans->cmdproc = cmdproc; - trans->command = g_strdup(command); - - if (format != NULL) - { - va_start(arg, format); - trans->params = g_strdup_vprintf(format, arg); - va_end(arg); - } - - msn_cmdproc_send_trans(cmdproc, trans); -} - -void msn_cmdproc_process_payload(MsnCmdProc *cmdproc, char *payload, int payload_len) { @@ -243,58 +175,71 @@ msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { MsnMsgTypeCb cb; - const char *messageId = NULL; + const char *message_id = NULL; /* Multi-part messages */ - if ((messageId = msn_message_get_attr(msg, "Message-ID")) != NULL) { - const char *chunk_text = msn_message_get_attr(msg, "Chunks"); + message_id = msn_message_get_header_value(msg, "Message-ID"); + if (message_id != NULL) { + /* This is the first in a series of chunks */ + + const char *chunk_text = msn_message_get_header_value(msg, "Chunks"); guint chunk; if (chunk_text != NULL) { chunk = strtol(chunk_text, NULL, 10); - /* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent + /* 1024 chunks of ~1300 bytes is ~1MB, which seems OK to prevent some random client causing pidgin to hog a ton of memory. Probably should figure out the maximum that the official client actually supports, though. */ if (chunk > 0 && chunk < 1024) { msg->total_chunks = chunk; msg->received_chunks = 1; - g_hash_table_insert(cmdproc->multiparts, (gpointer)messageId, msn_message_ref(msg)); - purple_debug_info("msn", "Received chunked message, messageId: '%s', total chunks: %d\n", - messageId, chunk); + g_hash_table_insert(cmdproc->multiparts, (gpointer)message_id, msn_message_ref(msg)); + purple_debug_info("msn", "Received chunked message, message_id: '%s', total chunks: %d\n", + message_id, chunk); } else { - purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", messageId, chunk); + purple_debug_error("msn", "MessageId '%s' has too many chunks: %d\n", message_id, chunk); } return; } else { - chunk_text = msn_message_get_attr(msg, "Chunk"); + chunk_text = msn_message_get_header_value(msg, "Chunk"); if (chunk_text != NULL) { - MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, messageId); + /* This is one chunk in a series of chunks */ + + MsnMessage *first = g_hash_table_lookup(cmdproc->multiparts, message_id); chunk = strtol(chunk_text, NULL, 10); if (first == NULL) { purple_debug_error("msn", - "Unable to find first chunk of messageId '%s' to correspond with chunk %d.\n", - messageId, chunk+1); - } else if (first->received_chunks == chunk) { - /* Chunk is from 1 to total-1 (doesn't count first one) */ - purple_debug_info("msn", "Received chunk %d of %d, messageId: '%s'\n", - chunk+1, first->total_chunks, messageId); - first->body = g_realloc(first->body, first->body_len + msg->body_len); - memcpy(first->body + first->body_len, msg->body, msg->body_len); - first->body_len += msg->body_len; - first->received_chunks++; - if (first->received_chunks != first->total_chunks) - return; - else - /* We're done! Send it along... The caller takes care of - freeing the old one. */ - msg = first; - } else { - /* TODO: Can you legitimately receive chunks out of order? */ - g_hash_table_remove(cmdproc->multiparts, messageId); + "Unable to find first chunk of message_id '%s' to correspond with chunk %d.\n", + message_id, chunk + 1); + } else if (first->received_chunks != chunk) { + /* + * We received an out of order chunk number (i.e. not the + * next one in the sequence). Not sure if this can happen + * legitimately, but we definitely don't handle it right + * now. + */ + g_hash_table_remove(cmdproc->multiparts, message_id); return; } + + /* Chunk is from 1 to total-1 (doesn't count first one) */ + purple_debug_info("msn", "Received chunk %d of %d, message_id: '%s'\n", + chunk + 1, first->total_chunks, message_id); + first->body = g_realloc(first->body, first->body_len + msg->body_len); + memcpy(first->body + first->body_len, msg->body, msg->body_len); + first->body_len += msg->body_len; + first->received_chunks++; + if (first->received_chunks != first->total_chunks) + /* We're waiting for more chunks */ + return; + + /* + * We have all the chunks for this message, great! Send + * it along... The caller takes care of freeing the old one. + */ + msg = first; } else { - purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", messageId); + purple_debug_error("msn", "Received MessageId '%s' with no chunk number!\n", message_id); } } } @@ -314,8 +259,8 @@ purple_debug_warning("msn", "Unhandled content-type '%s'\n", msn_message_get_content_type(msg)); - if (messageId != NULL) - g_hash_table_remove(cmdproc->multiparts, messageId); + if (message_id != NULL) + g_hash_table_remove(cmdproc->multiparts, message_id); } void diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/cmdproc.h --- a/libpurple/protocols/msn/cmdproc.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/cmdproc.h Sat Sep 11 19:03:25 2010 +0000 @@ -51,18 +51,45 @@ void *data; /**< Extra data, like the switchboard. */ }; +/** + * Creates a MsnCmdProc structure. + * + * @param session The session to associate with. + * + * @return A new MsnCmdProc structure. + */ MsnCmdProc *msn_cmdproc_new(MsnSession *session); + +/** + * Destroys an MsnCmdProc. + * + * @param cmdproc The object structure. + */ void msn_cmdproc_destroy(MsnCmdProc *cmdproc); +/** + * Process the queued transactions. + * + * @param cmdproc The MsnCmdProc. + */ void msn_cmdproc_process_queue(MsnCmdProc *cmdproc); +/** + * Sends transaction using this servconn. + * + * @param cmdproc The MsnCmdProc to be used. + * @param trans The MsnTransaction to be sent. + */ void msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans); + +/** + * Add a transaction to the queue to be processed latter. + * + * @param cmdproc The MsnCmdProc in which the transaction will be queued. + * @param trans The MsnTransaction to be queued. + */ void msn_cmdproc_queue_trans(MsnCmdProc *cmdproc, MsnTransaction *trans); -void msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command, - const char *format, ...); -void msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command, - const char *format, ...); void msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg); diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/command.h --- a/libpurple/protocols/msn/command.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/command.h Sat Sep 11 19:03:25 2010 +0000 @@ -54,9 +54,39 @@ void *payload_cbdata; }; +/** + * Create a command object from the incoming string and ref it. + * + * @param string The incoming string. + * + * @return A MsnCommand object. + */ MsnCommand *msn_command_from_string(const char *string); + +/** + * Destroy a MsnCommand object if its ref count is zero, otherwise + * just unref it. + * + * @param cmd The MsnCommand to be destroyed. + */ void msn_command_destroy(MsnCommand *cmd); + +/** + * Increment the ref count. + * + * @param cmd The MsnCommand to be ref. + * + * @return The ref command. + */ MsnCommand *msn_command_ref(MsnCommand *cmd); + +/** + * Decrement the ref count. If the count goes to 0, destroy it. + * + * @param cmd The MsnCommand to be unref. + * + * @return The ref command. + */ MsnCommand *msn_command_unref(MsnCommand *cmd); #endif /* MSN_COMMAND_H */ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/dialog.c --- a/libpurple/protocols/msn/dialog.c Fri Sep 10 08:52:09 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ -/** - * @file dialog.c Dialog functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 "msn.h" -#include "dialog.h" - -typedef struct -{ - PurpleConnection *gc; - char *who; - char *group; - gboolean add; - -} MsnAddRemData; - -/* Remove the buddy referenced by the MsnAddRemData before the serverside list is changed. - * If the buddy will be added, he'll be added back; if he will be removed, he won't be. */ -/* Actually with our MSNP14 code that isn't true yet, he won't be added back :( */ -static void -msn_complete_sync_issue(MsnAddRemData *data) -{ - PurpleBuddy *buddy; - PurpleGroup *group = NULL; - - if (data->group != NULL) - group = purple_find_group(data->group); - - if (group != NULL) - buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group); - else - buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who); - - if (buddy != NULL) - purple_blist_remove_buddy(buddy); -} - - -static void -msn_add_cb(MsnAddRemData *data) -{ -#if 0 - /* this *should* be necessary !! */ - msn_complete_sync_issue(data); -#endif - - if (g_list_find(purple_connections_get_all(), data->gc) != NULL) - { - MsnSession *session = data->gc->proto_data; - MsnUserList *userlist = session->userlist; - - msn_userlist_add_buddy(userlist, data->who, data->group); - } - - g_free(data->group); - g_free(data->who); - g_free(data); -} - -static void -msn_rem_cb(MsnAddRemData *data) -{ - msn_complete_sync_issue(data); - - if (g_list_find(purple_connections_get_all(), data->gc) != NULL) - { - MsnSession *session = data->gc->proto_data; - MsnUserList *userlist = session->userlist; - - if (data->group == NULL) { - msn_userlist_rem_buddy_from_list(userlist, data->who, MSN_LIST_FL); - } else { - g_free(data->group); - } - } - - g_free(data->who); - g_free(data); -} - -void -msn_show_sync_issue(MsnSession *session, const char *passport, - const char *group_name) -{ - PurpleConnection *gc; - PurpleAccount *account; - MsnAddRemData *data; - char *msg, *reason; - - account = session->account; - gc = purple_account_get_connection(account); - - data = g_new0(MsnAddRemData, 1); - data->who = g_strdup(passport); - data->group = g_strdup(group_name); - data->gc = gc; - - msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - - if (group_name != NULL) - { - reason = g_strdup_printf(_("%s on the local list is " - "inside the group \"%s\" but not on " - "the server list. " - "Do you want this buddy to be added?"), - passport, group_name); - } - else - { - reason = g_strdup_printf(_("%s is on the local list but " - "not on the server list. " - "Do you want this buddy to be added?"), - passport); - } - - purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), data->who, NULL, - data, 2, - _("Yes"), G_CALLBACK(msn_add_cb), - _("No"), G_CALLBACK(msn_rem_cb)); - - g_free(reason); - g_free(msg); -} diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/dialog.h --- a/libpurple/protocols/msn/dialog.h Fri Sep 10 08:52:09 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/** - * @file dialog.h Dialog functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 - */ -#ifndef MSN_DIALOG_H -#define MSN_DIALOG_H - -void msn_show_sync_issue(MsnSession *session, const char *passport, - const char *group_name); - -#endif /* MSN_DIALOG_H */ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/error.c --- a/libpurple/protocols/msn/error.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/error.c Sat Sep 11 19:03:25 2010 +0000 @@ -24,6 +24,15 @@ #include "msn.h" #include "error.h" +typedef struct +{ + PurpleConnection *gc; + char *who; + char *group; + gboolean add; + +} MsnAddRemData; + const char * msn_error_get_text(unsigned int type, gboolean *debug) { @@ -264,3 +273,115 @@ g_free(buf); } +/* Remove the buddy referenced by the MsnAddRemData before the serverside list + * is changed. If the buddy will be added, he'll be added back; if he will be + * removed, he won't be. */ +/* Actually with our MSNP14 code that isn't true yet, he won't be added back :( + * */ +static void +msn_complete_sync_issue(MsnAddRemData *data) +{ + PurpleBuddy *buddy; + PurpleGroup *group = NULL; + + if (data->group != NULL) + group = purple_find_group(data->group); + + if (group != NULL) + buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group); + else + buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who); + + if (buddy != NULL) + purple_blist_remove_buddy(buddy); +} + + +static void +msn_add_cb(MsnAddRemData *data) +{ +#if 0 + /* this *should* be necessary !! */ + msn_complete_sync_issue(data); +#endif + + if (g_list_find(purple_connections_get_all(), data->gc) != NULL) + { + MsnSession *session = data->gc->proto_data; + MsnUserList *userlist = session->userlist; + + msn_userlist_add_buddy(userlist, data->who, data->group); + } + + g_free(data->group); + g_free(data->who); + g_free(data); +} + +static void +msn_rem_cb(MsnAddRemData *data) +{ + msn_complete_sync_issue(data); + + if (g_list_find(purple_connections_get_all(), data->gc) != NULL) + { + MsnSession *session = data->gc->proto_data; + MsnUserList *userlist = session->userlist; + + if (data->group == NULL) { + msn_userlist_rem_buddy_from_list(userlist, data->who, MSN_LIST_FL); + } else { + g_free(data->group); + } + } + + g_free(data->who); + g_free(data); +} + +void +msn_error_sync_issue(MsnSession *session, const char *passport, + const char *group_name) +{ + PurpleConnection *gc; + PurpleAccount *account; + MsnAddRemData *data; + char *msg, *reason; + + account = session->account; + gc = purple_account_get_connection(account); + + data = g_new0(MsnAddRemData, 1); + data->who = g_strdup(passport); + data->group = g_strdup(group_name); + data->gc = gc; + + msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"), + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + + if (group_name != NULL) + { + reason = g_strdup_printf(_("%s on the local list is " + "inside the group \"%s\" but not on " + "the server list. " + "Do you want this buddy to be added?"), + passport, group_name); + } + else + { + reason = g_strdup_printf(_("%s is on the local list but " + "not on the server list. " + "Do you want this buddy to be added?"), + passport); + } + + purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, + purple_connection_get_account(gc), data->who, NULL, + data, 2, + _("Yes"), G_CALLBACK(msn_add_cb), + _("No"), G_CALLBACK(msn_rem_cb)); + + g_free(reason); + g_free(msg); +} diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/error.h --- a/libpurple/protocols/msn/error.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/error.h Sat Sep 11 19:03:25 2010 +0000 @@ -44,4 +44,14 @@ */ void msn_error_handle(MsnSession *session, unsigned int type); +/** + * Show the sync issue in a dialog using request api + * + * @param sesion MsnSession associated to this error. + * @param passport The passport associated with the error. + * @param group_name The group in the buddy is suppoused to be + */ +void msn_error_sync_issue(MsnSession *session, const char *passport, + const char *group_name); + #endif /* MSN_ERROR_H */ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/msg.c --- a/libpurple/protocols/msn/msg.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/msg.c Sat Sep 11 19:03:25 2010 +0000 @@ -36,7 +36,7 @@ if (purple_debug_is_verbose()) purple_debug_info("msn", "message new (%p)(%d)\n", msg, type); - msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal, + msg->header_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); msn_message_ref(msg); @@ -64,8 +64,8 @@ g_free(msg->content_type); g_free(msg->charset); - g_hash_table_destroy(msg->attr_table); - g_list_free(msg->attr_list); + g_hash_table_destroy(msg->header_table); + g_list_free(msg->header_list); g_free(msg); } @@ -112,11 +112,11 @@ msg = msn_message_new(MSN_MSG_TEXT); msg->retries = 1; - msn_message_set_attr(msg, "User-Agent", PACKAGE_NAME "/" VERSION); + msn_message_set_header(msg, "User-Agent", PACKAGE_NAME "/" VERSION); msn_message_set_content_type(msg, "text/plain"); msn_message_set_charset(msg, "UTF-8"); msn_message_set_flag(msg, 'A'); - msn_message_set_attr(msg, "X-MMS-IM-Format", + msn_message_set_header(msg, "X-MMS-IM-Format", "FN=Segoe%20UI; EF=; CO=0; CS=1;PF=0"); message_cr = purple_str_add_cr(message); @@ -133,7 +133,7 @@ msg = msn_message_new(MSN_MSG_SLP); - msn_message_set_attr(msg, "User-Agent", NULL); + msn_message_set_header(msg, "User-Agent", NULL); msg->msnslp_message = TRUE; @@ -169,7 +169,7 @@ g_return_if_reached(); } - /* Import the header. */ + /* Extract the binary SLP header */ memcpy(&header, tmp, sizeof(header)); tmp += sizeof(header); @@ -183,7 +183,7 @@ msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id); msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size); - /* Import the body. */ + /* Extract the body */ body_len = len - (tmp - body); /* msg->body_len = msg->msnslp_header.length; */ @@ -211,7 +211,7 @@ memcpy(tmp_base, payload, payload_len); tmp_base[payload_len] = '\0'; - /* Parse the attributes. */ + /* Find the end of the headers */ end = strstr(tmp, body_dem); /* TODO? some clients use \r delimiters instead of \r\n, the official client * doesn't send such messages, but does handle receiving them. We'll just @@ -222,8 +222,8 @@ } *end = '\0'; + /* Split the headers and parse each one */ elems = g_strsplit(tmp, line_dem, 0); - for (cur = elems; *cur != NULL; cur++) { const char *key, *value; @@ -240,7 +240,7 @@ if (!strcmp(key, "boundary")) { char *end = strchr(value, '\"'); *end = '\0'; - msn_message_set_attr(msg, key, value); + msn_message_set_header(msg, key, value); } g_strfreev(tokens); @@ -278,12 +278,11 @@ } else { - msn_message_set_attr(msg, key, value); + msn_message_set_header(msg, key, value); } g_strfreev(tokens); } - g_strfreev(elems); /* Proceed to the end of the "\r\n\r\n" */ @@ -306,7 +305,7 @@ msg->msnslp_message = TRUE; - /* Import the header. */ + /* Extract the binary SLP header */ memcpy(&header, tmp, sizeof(header)); tmp += sizeof(header); @@ -322,7 +321,7 @@ body_len = payload_len - (tmp - tmp_base) - sizeof(footer); - /* Import the body. */ + /* Extract the body */ if (body_len > 0) { msg->body_len = body_len; g_free(msg->body); @@ -332,7 +331,7 @@ tmp += body_len; } - /* Import the footer. */ + /* Extract the footer */ if (body_len >= 0) { memcpy(&footer, tmp, sizeof(footer)); tmp += sizeof(footer); @@ -348,7 +347,7 @@ memcpy(msg->body, tmp, msg->body_len); msg->body[msg->body_len] = '\0'; } - + if ((!content_type || !strcmp(content_type, "text/plain")) && msg->charset == NULL) { char *body = g_convert(msg->body, msg->body_len, "UTF-8", @@ -454,13 +453,13 @@ n += strlen(n); - for (l = msg->attr_list; l != NULL; l = l->next) + for (l = msg->header_list; l != NULL; l = l->next) { const char *key; const char *value; key = l->data; - value = msn_message_get_attr(msg, key); + value = msn_message_get_header_value(msg, key); g_snprintf(n, end - n, "%s: %s\r\n", key, value); n += strlen(n); @@ -610,15 +609,15 @@ } void -msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value) +msn_message_set_header(MsnMessage *msg, const char *name, const char *value) { const char *temp; - char *new_attr; + char *new_name; g_return_if_fail(msg != NULL); - g_return_if_fail(attr != NULL); + g_return_if_fail(name != NULL); - temp = msn_message_get_attr(msg, attr); + temp = msn_message_get_header_value(msg, name); if (value == NULL) { @@ -626,37 +625,37 @@ { GList *l; - for (l = msg->attr_list; l != NULL; l = l->next) + for (l = msg->header_list; l != NULL; l = l->next) { - if (!g_ascii_strcasecmp(l->data, attr)) + if (!g_ascii_strcasecmp(l->data, name)) { - msg->attr_list = g_list_remove(msg->attr_list, l->data); + msg->header_list = g_list_remove(msg->header_list, l->data); break; } } - g_hash_table_remove(msg->attr_table, attr); + g_hash_table_remove(msg->header_table, name); } return; } - new_attr = g_strdup(attr); + new_name = g_strdup(name); - g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value)); + g_hash_table_insert(msg->header_table, new_name, g_strdup(value)); if (temp == NULL) - msg->attr_list = g_list_append(msg->attr_list, new_attr); + msg->header_list = g_list_append(msg->header_list, new_name); } const char * -msn_message_get_attr(const MsnMessage *msg, const char *attr) +msn_message_get_header_value(const MsnMessage *msg, const char *name) { g_return_val_if_fail(msg != NULL, NULL); - g_return_val_if_fail(attr != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); - return g_hash_table_lookup(msg->attr_table, attr); + return g_hash_table_lookup(msg->header_table, name); } GHashTable * @@ -741,13 +740,13 @@ msg->content_type, msg->charset); } - for (l = msg->attr_list; l; l = l->next) + for (l = msg->header_list; l; l = l->next) { char *key; const char *value; key = l->data; - value = msn_message_get_attr(msg, key); + value = msn_message_get_header_value(msg, key); g_string_append_printf(str, "%s: %s\r\n", key, value); } @@ -817,7 +816,6 @@ { PurpleConnection *gc; const char *body; - char *body_str; char *body_enc; char *body_final; size_t body_len; @@ -827,9 +825,7 @@ gc = cmdproc->session->account->gc; body = msn_message_get_bin_data(msg, &body_len); - body_str = g_strndup(body, body_len); - body_enc = g_markup_escape_text(body_str, -1); - g_free(body_str); + body_enc = g_markup_escape_text(body, body_len); passport = msg->remote_user; @@ -840,13 +836,13 @@ } #if 0 - if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL) + if ((value = msn_message_get_header_value(msg, "User-Agent")) != NULL) { purple_debug_misc("msn", "User-Agent = '%s'\n", value); } #endif - if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) + if ((value = msn_message_get_header_value(msg, "X-MMS-IM-Format")) != NULL) { char *pre, *post; @@ -914,7 +910,7 @@ gc = cmdproc->session->account->gc; passport = msg->remote_user; - if (msn_message_get_attr(msg, "TypingUser") == NULL) + if (msn_message_get_header_value(msg, "TypingUser") == NULL) return; if (cmdproc->servconn->type == MSN_SERVCONN_SB) { diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/msg.h --- a/libpurple/protocols/msn/msg.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/msg.h Sat Sep 11 19:03:25 2010 +0000 @@ -109,8 +109,8 @@ MsnSlpHeader msnslp_header; MsnSlpFooter msnslp_footer; - GHashTable *attr_table; - GList *attr_list; + GHashTable *header_table; + GList *header_list; gboolean ack_ref; /**< A flag that states if this message has been ref'ed for using it in a callback. */ @@ -295,24 +295,24 @@ const char *msn_message_get_charset(const MsnMessage *msg); /** - * Sets an attribute in a message. + * Sets a header in a message. * - * @param msg The message. - * @param attr The attribute name. - * @param value The attribute value. + * @param msg The message. + * @param header The header name. + * @param value The header value. */ -void msn_message_set_attr(MsnMessage *msg, const char *attr, +void msn_message_set_header(MsnMessage *msg, const char *name, const char *value); /** - * Returns an attribute from a message. + * Returns the value of a header from a message. * - * @param msg The message. - * @param attr The attribute. + * @param msg The message. + * @param header The header value. * * @return The value, or @c NULL if not found. */ -const char *msn_message_get_attr(const MsnMessage *msg, const char *attr); +const char *msn_message_get_header_value(const MsnMessage *msg, const char *name); /** * Parses the body and returns it in the form of a hashtable. diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/msn.c --- a/libpurple/protocols/msn/msn.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/msn.c Sat Sep 11 19:03:25 2010 +0000 @@ -43,7 +43,6 @@ #include "msg.h" #include "switchboard.h" #include "notification.h" -#include "sync.h" #include "slplink.h" #if PHOTO_SUPPORT @@ -267,9 +266,9 @@ { MsnCmdProc *cmdproc; MsnSession *session; + MsnTransaction *trans; PurpleAccount *account; const char *real_alias; - MsnTransaction *trans; struct public_alias_closure *closure; session = purple_connection_get_protocol_data(pc); @@ -360,19 +359,21 @@ { MsnCmdProc *cmdproc; MsnSession *session; + MsnTransaction *trans; session = gc->proto_data; cmdproc = session->notification->cmdproc; if (entry == NULL || *entry == '\0') { - msn_cmdproc_send(cmdproc, "PRP", "%s", type); + trans = msn_transaction_new(cmdproc, "PRP", "%s", type); } else { - msn_cmdproc_send(cmdproc, "PRP", "%s %s", type, + trans = msn_transaction_new(cmdproc, "PRP", "%s %s", type, purple_url_encode(entry)); } + msn_cmdproc_send_trans(cmdproc, trans); } static void @@ -478,7 +479,7 @@ tmp = g_strdup_printf(_("Set friendly name for %s."), purple_account_get_username(account)); - purple_request_input(gc, _("Set your friendly name."), tmp, + purple_request_input(gc, _("Set Friendly Name"), tmp, _("This is the name that other MSN buddies will " "see you as."), purple_connection_get_display_name(gc), FALSE, FALSE, NULL, @@ -489,6 +490,111 @@ g_free(tmp); } +typedef struct MsnLocationData { + PurpleAccount *account; + MsnSession *session; + PurpleRequestFieldGroup *group; +} MsnLocationData; + +static void +update_endpoint_cb(MsnLocationData *data, PurpleRequestFields *fields) +{ + PurpleAccount *account; + MsnSession *session; + const char *old_name; + const char *name; + GList *others; + + session = data->session; + account = data->account; + + /* Update the current location's name */ + old_name = purple_account_get_string(account, "endpoint-name", NULL); + name = purple_request_fields_get_string(fields, "endpoint-name"); + if (!g_str_equal(old_name, name)) { + purple_account_set_string(account, "endpoint-name", name); + msn_notification_send_uux_private_endpointdata(session); + } + + /* Sign out other locations */ + for (others = purple_request_field_group_get_fields(data->group); + others; + others = g_list_next(others)) { + PurpleRequestField *field = others->data; + if (purple_request_field_get_type(field) != PURPLE_REQUEST_FIELD_BOOLEAN) + continue; + if (purple_request_field_bool_get_value(field)) { + const char *id = purple_request_field_get_id(field); + char *user; + purple_debug_info("msn", "Disconnecting Endpoint %s\n", id); + + user = g_strdup_printf("%s;%s", purple_account_get_username(account), id); + msn_notification_send_uun(session, user, MSN_UNIFIED_NOTIFICATION_MPOP, "goawyplzthxbye"); + g_free(user); + } + } + + g_free(data); +} + +static void +msn_show_locations(PurplePluginAction *action) +{ + PurpleConnection *pc; + PurpleAccount *account; + MsnSession *session; + PurpleRequestFields *fields; + PurpleRequestFieldGroup *group; + PurpleRequestField *field; + GSList *l; + MsnLocationData *data; + + pc = (PurpleConnection *)action->context; + account = purple_connection_get_account(pc); + session = purple_connection_get_protocol_data(pc); + + fields = purple_request_fields_new(); + + group = purple_request_field_group_new(_("This Location")); + purple_request_fields_add_group(fields, group); + field = purple_request_field_label_new("endpoint-label", _("This is the name that identifies this location")); + purple_request_field_group_add_field(group, field); + field = purple_request_field_string_new("endpoint-name", + _("Name"), + purple_account_get_string(account, "endpoint-name", NULL), + FALSE); + purple_request_field_set_required(field, TRUE); + purple_request_field_group_add_field(group, field); + + group = purple_request_field_group_new(_("Other Locations")); + purple_request_fields_add_group(fields, group); + field = purple_request_field_label_new("others-label", _("You can sign out from other locations here")); + purple_request_field_group_add_field(group, field); + + for (l = session->user->endpoints; l; l = l->next) { + MsnUserEndpoint *ep = l->data; + + if (g_str_equal(ep->id, session->guid)) + /* Don't add myself to the list */ + continue; + + field = purple_request_field_bool_new(ep->id, ep->name, FALSE); + purple_request_field_group_add_field(group, field); + } + + data = g_new0(MsnLocationData, 1); + data->account = account; + data->session = session; + data->group = group; + + purple_request_fields(pc, NULL, NULL, NULL, + fields, + _("OK"), G_CALLBACK(update_endpoint_cb), + _("Cancel"), G_CALLBACK(g_free), + account, NULL, NULL, + data); +} + static void msn_show_set_home_phone(PurplePluginAction *action) { @@ -656,6 +762,7 @@ PurpleAccount *account; MsnSession *session; MsnCmdProc *cmdproc; + MsnTransaction *trans; account = purple_connection_get_account(gc); session = gc->proto_data; @@ -663,9 +770,11 @@ if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL || account->perm_deny == PURPLE_PRIVACY_DENY_USERS) - msn_cmdproc_send(cmdproc, "BLP", "%s", "AL"); + trans = msn_transaction_new(cmdproc, "BLP", "%s", "AL"); else - msn_cmdproc_send(cmdproc, "BLP", "%s", "BL"); + trans = msn_transaction_new(cmdproc, "BLP", "%s", "BL"); + + msn_cmdproc_send_trans(cmdproc, trans); } static void @@ -1068,6 +1177,11 @@ m = g_list_append(m, act); m = g_list_append(m, NULL); + act = purple_plugin_action_new(_("View Locations..."), + msn_show_locations); + m = g_list_append(m, act); + m = g_list_append(m, NULL); + act = purple_plugin_action_new(_("Set Home Phone Number..."), msn_show_set_home_phone); m = g_list_append(m, act); @@ -1202,6 +1316,13 @@ username = purple_account_get_string(account, "display-name", NULL); purple_connection_set_display_name(gc, username); + if (purple_account_get_string(account, "endpoint-name", NULL) == NULL) { + GHashTable *ui_info = purple_core_get_ui_info(); + const gchar *ui_name = ui_info ? g_hash_table_lookup(ui_info, "name") : NULL; + purple_account_set_string(account, "endpoint-name", + ui_name && *ui_name ? ui_name : PACKAGE_NAME); + } + if (!msn_session_connect(session, host, port, http_method)) purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, @@ -1412,7 +1533,7 @@ msg = msn_message_new_plain(msgtext); msg->remote_user = g_strdup(who); - msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat); + msn_message_set_header(msg, "X-MMS-IM-Format", msgformat); g_free(msgformat); g_free(msgtext); @@ -1445,7 +1566,7 @@ body_enc = g_markup_escape_text(body_str, -1); g_free(body_str); - format = msn_message_get_attr(msg, "X-MMS-IM-Format"); + format = msn_message_get_header_value(msg, "X-MMS-IM-Format"); msn_parse_format(format, &pre, &post); body_str = g_strdup_printf("%s%s%s", pre ? pre : "", body_enc ? body_enc : "", post ? post : ""); @@ -1520,7 +1641,7 @@ msg = msn_message_new(MSN_MSG_TYPING); msn_message_set_content_type(msg, "text/x-msmsgscontrol"); msn_message_set_flag(msg, 'U'); - msn_message_set_attr(msg, "TypingUser", + msn_message_set_header(msg, "TypingUser", purple_account_get_username(account)); msn_message_set_bin_data(msg, "\r\n", 2); @@ -1927,7 +2048,7 @@ } msg = msn_message_new_plain(msgtext); - msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat); + msn_message_set_header(msg, "X-MMS-IM-Format", msgformat); smileys = msn_msg_grab_emoticons(msg->body, username); while (smileys) { @@ -1967,6 +2088,7 @@ msn_keepalive(PurpleConnection *gc) { MsnSession *session; + MsnTransaction *trans; session = gc->proto_data; @@ -1976,7 +2098,9 @@ cmdproc = session->notification->cmdproc; - msn_cmdproc_send_quick(cmdproc, "PNG", NULL, NULL); + trans = msn_transaction_new(cmdproc, "PNG", NULL); + msn_transaction_set_saveable(trans, FALSE); + msn_cmdproc_send_trans(cmdproc, trans); } } @@ -2712,7 +2836,6 @@ { msn_notification_init(); msn_switchboard_init(); - msn_sync_init(); return TRUE; } @@ -2721,7 +2844,6 @@ { msn_notification_end(); msn_switchboard_end(); - msn_sync_end(); return TRUE; } diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/msn.h --- a/libpurple/protocols/msn/msn.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/msn.h Sat Sep 11 19:03:25 2010 +0000 @@ -109,9 +109,9 @@ #define MSN_SERVER "messenger.hotmail.com" #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com" #define MSN_PORT 1863 -#define WLM_PROT_VER 15 +#define WLM_PROT_VER 16 -#define WLM_MAX_PROTOCOL 15 +#define WLM_MAX_PROTOCOL 16 #define WLM_MIN_PROTOCOL 15 #define MSN_TYPING_RECV_TIMEOUT 6 @@ -134,15 +134,14 @@ /* Index into attention_types */ #define MSN_NUDGE 0 -#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_7_0 +#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_9_0 #define MSN_CLIENT_ID_CAPABILITIES (MSN_CLIENT_CAP_PACKET|MSN_CLIENT_CAP_INK_GIF|MSN_CLIENT_CAP_VOICEIM) +#define MSN_CLIENT_ID_EXT_CAPS (0) #define MSN_CLIENT_ID \ ((MSN_CLIENT_ID_VERSION << 24) | \ (MSN_CLIENT_ID_CAPABILITIES)) -#define MSN_CLIENT_EXT_ID 0 - gboolean msn_email_is_valid(const char *passport); void msn_set_public_alias(PurpleConnection *gc, const char *alias, diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/msnutils.c --- a/libpurple/protocols/msn/msnutils.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/msnutils.c Sat Sep 11 19:03:25 2010 +0000 @@ -26,13 +26,11 @@ #include "cipher.h" -char *rand_guid(void); - /************************************************************************** * Util **************************************************************************/ char * -rand_guid() +rand_guid(void) { return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", rand() % 0xAAFF + 0x1111, diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/msnutils.h --- a/libpurple/protocols/msn/msnutils.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/msnutils.h Sat Sep 11 19:03:25 2010 +0000 @@ -54,7 +54,23 @@ */ void msn_import_html(const char *html, char **attributes, char **message); +/** + * Parses a socket string. + * + * @param str A host:port string. + * @param ret_host Return string value of the host. + * @param ret_port Return integer value of the port. + */ void msn_parse_socket(const char *str, char **ret_host, int *ret_port); + +/** + * Handle MSN Challenge Computation + * This algorithm references + * http://imfreedom.org/wiki/index.php/MSN:NS/Challenges + * + * @param input Challenge input. + * @param output Callenge output. + */ void msn_handle_chl(char *input, char *output); #endif /* MSN_UTILS_H */ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/notification.c --- a/libpurple/protocols/msn/notification.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/notification.c Sat Sep 11 19:03:25 2010 +0000 @@ -22,6 +22,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" +#include "core.h" #include "notification.h" #include "contact.h" #include "state.h" @@ -30,7 +31,6 @@ #include "page.h" #include "userlist.h" -#include "sync.h" #include "slplink.h" static MsnTable *cbs_table; @@ -92,6 +92,7 @@ { MsnCmdProc *cmdproc; MsnSession *session; + MsnTransaction *trans; GString *vers; const char *ver_str; int i; @@ -115,7 +116,8 @@ /* Skip the initial space */ ver_str = (vers->str + 1); - msn_cmdproc_send(cmdproc, "VER", "%s", ver_str); + trans = msn_transaction_new(cmdproc, "VER", "%s", ver_str); + msn_cmdproc_send_trans(cmdproc, trans); g_string_free(vers, TRUE); } @@ -154,22 +156,30 @@ msn_got_login_params(MsnSession *session, const char *ticket, const char *response) { MsnCmdProc *cmdproc; + MsnTransaction *trans; cmdproc = session->notification->cmdproc; msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END); - msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response); + if (session->protocol_ver >= 16) + trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s %s", ticket, response, session->guid); + else + trans = msn_transaction_new(cmdproc, "USR", "SSO S %s %s", ticket, response); + + msn_cmdproc_send_trans(cmdproc, trans); } static void cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { PurpleAccount *account; + MsnTransaction *trans; account = cmdproc->session->account; - msn_cmdproc_send(cmdproc, "USR", "SSO I %s", purple_account_get_username(account)); + trans = msn_transaction_new(cmdproc, "USR", "SSO I %s", purple_account_get_username(account)); + msn_cmdproc_send_trans(cmdproc, trans); } static void @@ -227,22 +237,25 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; + MsnTransaction *trans; PurpleAccount *account; gboolean protocol_supported = FALSE; - char proto_str[8]; + int proto_ver; size_t i; session = cmdproc->session; account = session->account; - g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver); - + session->protocol_ver = 0; for (i = 1; i < cmd->param_count; i++) { - if (!strcmp(cmd->params[i], proto_str)) - { - protocol_supported = TRUE; - break; + if (sscanf(cmd->params[i], "MSNP%d", &proto_ver) == 1) { + if (proto_ver >= WLM_MIN_PROTOCOL + && proto_ver <= WLM_MAX_PROTOCOL + && proto_ver > session->protocol_ver) { + protocol_supported = TRUE; + session->protocol_ver = proto_ver; + } } } @@ -253,15 +266,18 @@ return; } + purple_debug_info("msn", "Negotiated protocol version %d with the server.\n", session->protocol_ver); + /* * Windows Live Messenger 8.5 * Notice :CVR String discriminate! * reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx * to see the Local ID */ - msn_cmdproc_send(cmdproc, "CVR", + trans = msn_transaction_new(cmdproc, "CVR", "0x0409 winnt 5.1 i386 MSNMSGR 8.5.1302 BC01 %s", purple_account_get_username(account)); + msn_cmdproc_send_trans(cmdproc, trans); } /************************************************************************** @@ -283,12 +299,16 @@ void msn_notification_close(MsnNotification *notification) { + MsnTransaction *trans; + g_return_if_fail(notification != NULL); if (!notification->in_use) return; - msn_cmdproc_send_quick(notification->cmdproc, "OUT", NULL, NULL); + trans = msn_transaction_new(notification->cmdproc, "OUT", NULL); + msn_transaction_set_saveable(trans, FALSE); + msn_cmdproc_send_trans(notification->cmdproc, trans); msn_notification_disconnect(notification); } @@ -998,7 +1018,8 @@ PurpleAccount *account; MsnUser *user; MsnObject *msnobj = NULL; - unsigned long clientid; + unsigned long clientid, extcaps; + char *extcap_str; int networkid = 0; const char *state, *passport; char *friendly; @@ -1018,7 +1039,11 @@ /* Yahoo! Buddy, looks like */ networkid = atoi(cmd->params[3]); friendly = g_strdup(purple_url_decode(cmd->params[4])); - clientid = strtoul(cmd->params[5], NULL, 10); + clientid = strtoul(cmd->params[5], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; /* cmd->params[7] seems to be a URL to a Yahoo! icon: https://sec.yimg.com/i/us/nt/b/purpley.1.0.png @@ -1028,7 +1053,11 @@ /* MSNP14+ with Display Picture object */ networkid = atoi(cmd->params[3]); friendly = g_strdup(purple_url_decode(cmd->params[4])); - clientid = strtoul(cmd->params[5], NULL, 10); + clientid = strtoul(cmd->params[5], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6])); } else if (cmd->param_count == 6) { /* Yes, this is 5. The friendly name could start with a number, @@ -1037,17 +1066,29 @@ /* MSNP14 without Display Picture object */ networkid = atoi(cmd->params[3]); friendly = g_strdup(purple_url_decode(cmd->params[4])); - clientid = strtoul(cmd->params[5], NULL, 10); + clientid = strtoul(cmd->params[5], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; } else { /* MSNP8+ with Display Picture object */ friendly = g_strdup(purple_url_decode(cmd->params[3])); - clientid = strtoul(cmd->params[4], NULL, 10); + clientid = strtoul(cmd->params[4], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); } } else if (cmd->param_count == 5) { /* MSNP8+ without Display Picture object */ friendly = g_strdup(purple_url_decode(cmd->params[3])); - clientid = strtoul(cmd->params[4], NULL, 10); + clientid = strtoul(cmd->params[4], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; } else { purple_debug_warning("msn", "Received ILN with unknown number of parameters.\n"); return; @@ -1062,6 +1103,7 @@ user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->phone_mobile[0] == '+'); msn_user_set_clientid(user, clientid); + msn_user_set_extcaps(user, extcaps); msn_user_set_network(user, networkid); msn_user_set_state(user, state); @@ -1199,7 +1241,8 @@ PurpleAccount *account; MsnUser *user; MsnObject *msnobj; - unsigned long clientid; + unsigned long clientid, extcaps; + char *extcap_str; int networkid; const char *state, *passport, *friendly; @@ -1211,7 +1254,10 @@ networkid = atoi(cmd->params[2]); friendly = purple_url_decode(cmd->params[3]); - user = msn_userlist_find_user(session->userlist, passport); + if (g_str_equal(passport, session->user->passport)) + user = session->user; + else + user = msn_userlist_find_user(session->userlist, passport); if (user == NULL) return; if (msn_user_set_friendly_name(user, friendly)) @@ -1229,10 +1275,16 @@ msn_user_set_object(user, NULL); } - clientid = strtoul(cmd->params[4], NULL, 10); + clientid = strtoul(cmd->params[4], &extcap_str, 10); + if (session->protocol_ver >= 16 && extcap_str && *extcap_str) + extcaps = strtoul(extcap_str+1, NULL, 10); + else + extcaps = 0; + user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->extinfo && user->extinfo->phone_mobile && user->extinfo->phone_mobile[0] == '+'); msn_user_set_clientid(user, clientid); + msn_user_set_extcaps(user, extcaps); msn_user_set_network(user, networkid); msn_user_set_state(user, state); @@ -1502,6 +1554,77 @@ /*get the payload content*/ } +static void +parse_user_endpoints(MsnUser *user, xmlnode *payloadNode) +{ + xmlnode *epNode, *capsNode; + MsnUserEndpoint data; + const char *id; + char *caps, *tmp; + + purple_debug_info("msn", "Get EndpointData\n"); + + for (epNode = xmlnode_get_child(payloadNode, "EndpointData"); + epNode; + epNode = xmlnode_get_next_twin(epNode)) { + id = xmlnode_get_attrib(epNode, "id"); + capsNode = xmlnode_get_child(epNode, "Capabilities"); + + if (capsNode != NULL) { + caps = xmlnode_get_data(capsNode); + + data.clientid = strtoul(caps, &tmp, 10); + if (tmp && *tmp) + data.extcaps = strtoul(tmp + 1, NULL, 10); + else + data.extcaps = 0; + + g_free(caps); + + } else { + data.clientid = 0; + data.extcaps = 0; + } + + msn_user_set_endpoint_data(user, id, &data); + } + + /* Need to shortcut this check, probably... */ + if (user == user->userlist->session->user) { + for (epNode = xmlnode_get_child(payloadNode, "PrivateEndpointData"); + epNode; + epNode = xmlnode_get_next_twin(epNode)) { + MsnUserEndpoint *ep; + xmlnode *nameNode, *clientNode; + + /* + Endpoint Name + true/false + 1 + NLN + + */ + id = xmlnode_get_attrib(epNode, "id"); + ep = msn_user_get_endpoint_data(user, id); + + if (ep != NULL) { + nameNode = xmlnode_get_child(epNode, "EpName"); + if (nameNode != NULL) { + g_free(ep->name); + ep->name = xmlnode_get_data(nameNode); + } + + clientNode = xmlnode_get_child(epNode, "ClientType"); + if (clientNode != NULL) { + tmp = xmlnode_get_data(clientNode); + ep->type = strtoul(tmp, NULL, 10); + g_free(tmp); + } + } + } + } +} + static void parse_currentmedia(MsnUser *user, const char *cmedia) { char **cmedia_array; @@ -1567,14 +1690,18 @@ MsnSession *session; MsnUser *user; const char *passport; - char *str; + xmlnode *payloadNode; + char *psm_str, *str; session = cmdproc->session; passport = cmd->params[0]; - user = msn_userlist_find_user(session->userlist, passport); + if (g_str_equal(passport, session->user->passport)) + user = session->user; + else + user = msn_userlist_find_user(session->userlist, passport); if (user == NULL) { - str = g_strndup(payload, len); + char *str = g_strndup(payload, len); purple_debug_info("msn", "unknown user %s, payload is %s\n", passport, str); g_free(str); @@ -1593,13 +1720,28 @@ } if (len != 0) { - str = msn_get_psm(cmd->payload,len); - msn_user_set_statusline(user, str); - g_free(str); - - str = msn_get_currentmedia(cmd->payload, len); + payloadNode = xmlnode_from_str(payload, len); + if (!payloadNode) { + purple_debug_error("msn", "UBX XML parse Error!\n"); + + msn_user_set_statusline(user, NULL); + + msn_user_update(user); + return; + } + + psm_str = msn_get_psm(payloadNode); + msn_user_set_statusline(user, psm_str); + g_free(psm_str); + + str = msn_get_currentmedia(payloadNode); parse_currentmedia(user, str); g_free(str); + + parse_user_endpoints(user, payloadNode); + + xmlnode_free(payloadNode); + } else { msn_user_set_statusline(user, NULL); } @@ -1632,6 +1774,161 @@ cmd->payload_len = atoi(cmd->params[1]); } +void +msn_notification_send_uux(MsnSession *session, const char *payload) +{ + MsnTransaction *trans; + MsnCmdProc *cmdproc; + size_t len = strlen(payload); + + cmdproc = session->notification->cmdproc; + purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload); + trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, len); + msn_transaction_set_payload(trans, payload, len); + msn_cmdproc_send_trans(cmdproc, trans); +} + +void msn_notification_send_uux_endpointdata(MsnSession *session) +{ + xmlnode *epDataNode; + xmlnode *capNode; + char *caps; + char *payload; + int length; + + epDataNode = xmlnode_new("EndpointData"); + + capNode = xmlnode_new_child(epDataNode, "Capabilities"); + if (session->protocol_ver >= 16) + caps = g_strdup_printf("%d:%02d", MSN_CLIENT_ID_CAPABILITIES, MSN_CLIENT_ID_EXT_CAPS); + else + caps = g_strdup_printf("%d", MSN_CLIENT_ID_CAPABILITIES); + xmlnode_insert_data(capNode, caps, -1); + g_free(caps); + + payload = xmlnode_to_str(epDataNode, &length); + + msn_notification_send_uux(session, payload); + + xmlnode_free(epDataNode); + g_free(payload); +} + +void msn_notification_send_uux_private_endpointdata(MsnSession *session) +{ + xmlnode *private; + const char *name; + xmlnode *epname; + xmlnode *idle; + GHashTable *ui_info; + const gchar *ui_type; + xmlnode *client_type; + xmlnode *state; + char *payload; + int length; + + private = xmlnode_new("PrivateEndpointData"); + + name = purple_account_get_string(session->account, "endpoint-name", NULL); + epname = xmlnode_new_child(private, "EpName"); + xmlnode_insert_data(epname, name, -1); + + idle = xmlnode_new_child(private, "Idle"); + xmlnode_insert_data(idle, "false", -1); + + /* ClientType info (from amsn guys): + 0: None + 1: Computer + 2: Website + 3: Mobile / none + 4: Xbox / phone /mobile + 9: MsnGroup + 32: Email member, currently Yahoo! + */ + client_type = xmlnode_new_child(private, "ClientType"); + ui_info = purple_core_get_ui_info(); + ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL; + if (ui_type) { + if (strcmp(ui_type, "pc") == 0) + xmlnode_insert_data(client_type, "1", -1); + else if (strcmp(ui_type, "web") == 0) + xmlnode_insert_data(client_type, "2", -1); + else if (strcmp(ui_type, "phone") == 0) + xmlnode_insert_data(client_type, "3", -1); + else if (strcmp(ui_type, "handheld") == 0) + xmlnode_insert_data(client_type, "3", -1); + else + xmlnode_insert_data(client_type, "1", -1); + } + else + xmlnode_insert_data(client_type, "1", -1); + + state = xmlnode_new_child(private, "State"); + xmlnode_insert_data(state, msn_state_get_text(msn_state_from_account(session->account)), -1); + + payload = xmlnode_to_str(private, &length); + + msn_notification_send_uux(session, payload); + + xmlnode_free(private); + g_free(payload); +} + +static void +ubn_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + /* Do Nothing, right now. */ + if (payload != NULL) + purple_debug_info("msn", "UBN payload:\n%s\n", payload); +} + +static void +ubn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + purple_debug_misc("msn", "UBN received from %s.\n", cmd->params[0]); + cmdproc->last_cmd->payload_cb = ubn_cmd_post; + cmd->payload_len = atoi(cmd->params[2]); +} + +static void +uun_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + /* Do Nothing, right now. */ + if (payload != NULL) + purple_debug_info("msn", "UUN payload:\n%s\n", payload); +} + +static void +uun_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) +{ + if (strcmp(cmd->params[1], "OK") != 0) { + purple_debug_misc("msn", "UUN received.\n"); + cmdproc->last_cmd->payload_cb = uun_cmd_post; + cmd->payload_len = atoi(cmd->params[1]); + } + else + purple_debug_misc("msn", "UUN OK received.\n"); +} + +void +msn_notification_send_uun(MsnSession *session, const char *user, + MsnUnifiedNotificationType type, const char *payload) +{ + MsnTransaction *trans; + MsnCmdProc *cmdproc; + size_t len = strlen(payload); + + cmdproc = session->notification->cmdproc; + purple_debug_misc("msn", "Sending UUN command %d to %s with payload: %s\n", + type, user, payload); + trans = msn_transaction_new(cmdproc, "UUN", "%s %d %" G_GSIZE_FORMAT, + user, type, len); + msn_transaction_set_payload(trans, payload, len); + msn_cmdproc_send_trans(cmdproc, trans); +} + /************************************************************************** * Message Types **************************************************************************/ @@ -1651,39 +1948,39 @@ /* This isn't an official message. */ return; - if ((value = msn_message_get_attr(msg, "kv")) != NULL) + if ((value = msn_message_get_header_value(msg, "kv")) != NULL) { g_free(session->passport_info.kv); session->passport_info.kv = g_strdup(value); } - if ((value = msn_message_get_attr(msg, "sid")) != NULL) + if ((value = msn_message_get_header_value(msg, "sid")) != NULL) { g_free(session->passport_info.sid); session->passport_info.sid = g_strdup(value); } - if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL) + if ((value = msn_message_get_header_value(msg, "MSPAuth")) != NULL) { g_free(session->passport_info.mspauth); session->passport_info.mspauth = g_strdup(value); } - if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL) + if ((value = msn_message_get_header_value(msg, "ClientIP")) != NULL) { g_free(session->passport_info.client_ip); session->passport_info.client_ip = g_strdup(value); } - if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL) + if ((value = msn_message_get_header_value(msg, "ClientPort")) != NULL) { session->passport_info.client_port = ntohs(atoi(value)); } - if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL) + if ((value = msn_message_get_header_value(msg, "LoginTime")) != NULL) session->passport_info.sl = atol(value); - if ((value = msn_message_get_attr(msg, "EmailEnabled")) != NULL) + if ((value = msn_message_get_header_value(msg, "EmailEnabled")) != NULL) session->passport_info.email_enabled = (gboolean)atol(value); #ifdef MSN_PARTIAL_LISTS @@ -2087,6 +2384,9 @@ msn_table_add_cmd(cbs_table, NULL, "UBX", ubx_cmd); msn_table_add_cmd(cbs_table, NULL, "UUX", uux_cmd); + msn_table_add_cmd(cbs_table, NULL, "UBN", ubn_cmd); + msn_table_add_cmd(cbs_table, NULL, "UUN", uun_cmd); + msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd); msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd); diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/notification.h --- a/libpurple/protocols/msn/notification.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/notification.h Sat Sep 11 19:03:25 2010 +0000 @@ -66,6 +66,15 @@ typedef void (*MsnFqyCb)(MsnSession *session, const char *passport, MsnNetwork network, gpointer data); +/* Type used for msn_notification_send_uun */ +typedef enum { + MSN_UNIFIED_NOTIFICATION_SHARED_FOLDERS = 1, + MSN_UNIFIED_NOTIFICATION_UNKNOWN1 = 2, + MSN_UNIFIED_NOTIFICATION_P2P = 3, + MSN_UNIFIED_NOTIFICATION_MPOP = 4 + +} MsnUnifiedNotificationType; + void uum_send_msg(MsnSession *session, MsnMessage *msg); void msn_notification_end(void); @@ -87,6 +96,17 @@ void msn_notification_disconnect(MsnNotification *notification); void msn_notification_dump_contact(MsnSession *session); +void msn_notification_send_uux(MsnSession *session, const char *payload); + +void msn_notification_send_uux_endpointdata(MsnSession *session); + +void msn_notification_send_uux_private_endpointdata(MsnSession *session); + +void msn_notification_send_uun(MsnSession *session, + const char *user, + MsnUnifiedNotificationType type, + const char *payload); + /** * Closes a notification. * diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/oim.c --- a/libpurple/protocols/msn/oim.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/oim.c Sat Sep 11 19:03:25 2010 +0000 @@ -618,7 +618,7 @@ MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM); purple_debug_info("msn", "oim body:{%s}\n", message->body); - boundary = msn_message_get_attr(message, "boundary"); + boundary = msn_message_get_header_value(message, "boundary"); if (boundary != NULL) { char *bounds; @@ -656,7 +656,7 @@ decode_msg = (char *)purple_base64_decode(message->body, &body_len); } - from = msn_message_get_attr(message, "X-OIM-originatingSource"); + from = msn_message_get_header_value(message, "X-OIM-originatingSource"); /* Match number to user's mobile number, FROM is a phone number if the other side pages you using your phone number */ @@ -671,7 +671,7 @@ if (passport == NULL) { char *start, *end; - from = msn_message_get_attr(message, "From"); + from = msn_message_get_header_value(message, "From"); tokens = g_strsplit(from, " ", 2); if (tokens[1] != NULL) @@ -690,7 +690,7 @@ g_strfreev(tokens); } - date = msn_message_get_attr(message, "Date"); + date = msn_message_get_header_value(message, "Date"); stamp = msn_oim_parse_timestamp(date); purple_debug_info("msn", "oim Date:{%s},passport{%s}\n", date, passport); diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/session.c --- a/libpurple/protocols/msn/session.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/session.c Sat Sep 11 19:03:25 2010 +0000 @@ -21,13 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ +#include "error.h" #include "msn.h" +#include "msnutils.h" #include "session.h" #include "notification.h" #include "oim.h" -#include "dialog.h" - MsnSession * msn_session_new(PurpleAccount *account) { @@ -45,7 +45,9 @@ purple_account_get_username(account), NULL); session->oim = msn_oim_new(session); - session->protocol_ver = WLM_PROT_VER; + session->protocol_ver = 0; + + session->guid = rand_guid(); return session; } @@ -77,9 +79,6 @@ while (session->switches != NULL) msn_switchboard_destroy(session->switches->data); - if (session->sync != NULL) - msn_sync_destroy(session->sync); - if (session->oim != NULL) msn_oim_destroy(session->oim); @@ -95,6 +94,7 @@ msn_userlist_destroy(session->userlist); g_free(session->psm); + g_free(session->guid); g_free(session->abch_cachekey); #if 0 g_free(session->blocked_text); @@ -329,7 +329,7 @@ if (!found) { if ((remote_user == NULL) || !(remote_user->list_op & MSN_LIST_FL_OP)) { /* The user is not on the server list */ - msn_show_sync_issue(session, buddy_name, group_name); + msn_error_sync_issue(session, buddy_name, group_name); } else { /* The user is not in that group on the server list */ to_remove = g_list_prepend(to_remove, buddy); @@ -483,6 +483,11 @@ msn_session_sync_users(session); } + if (session->protocol_ver >= 16) { + /* TODO: Send this when updating status instead? */ + msn_notification_send_uux_endpointdata(session); + msn_notification_send_uux_private_endpointdata(session); + } msn_change_status(session); } diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/session.h --- a/libpurple/protocols/msn/session.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/session.h Sat Sep 11 19:03:25 2010 +0000 @@ -69,7 +69,6 @@ #include "slpcall.h" #include "sslconn.h" #include "switchboard.h" -#include "sync.h" #include "user.h" #include "userlist.h" @@ -92,7 +91,6 @@ MsnNotification *notification; MsnNexus *nexus; MsnOim *oim; - MsnSync *sync; MsnUserList *userlist; char *abch_cachekey; @@ -122,6 +120,7 @@ GHashTable *soap_table; guint soap_cleanup_handle; + char *guid; GSList *url_datas; /**< PurpleUtilFetchUrlData to be cancelled on exit */ }; diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/slp.c --- a/libpurple/protocols/msn/slp.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/slp.c Sat Sep 11 19:03:25 2010 +0000 @@ -966,6 +966,7 @@ if (!strncmp(body, "INVITE", strlen("INVITE"))) { + /* This is an INVITE request */ char *branch; char *call_id; char *content; @@ -1017,6 +1018,7 @@ } else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) { + /* This is a response */ char *content; char *content_type; /* Make sure this is "OK" */ @@ -1059,6 +1061,7 @@ } else if (!strncmp(body, "BYE", strlen("BYE"))) { + /* This is a BYE request */ char *call_id; call_id = get_token(body, "Call-ID: {", "}"); diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/slplink.c --- a/libpurple/protocols/msn/slplink.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/slplink.c Sat Sep 11 19:03:25 2010 +0000 @@ -452,7 +452,7 @@ msg->msnslp_header.total_size = slpmsg->size; passport = purple_normalize(slplink->session->account, slplink->remote_user); - msn_message_set_attr(msg, "P2P-Dest", passport); + msn_message_set_header(msg, "P2P-Dest", passport); msg->ack_cb = msg_ack; msg->nak_cb = msg_nak; @@ -568,12 +568,14 @@ { MsnSlpMessage *slpmsg; guint64 offset; - PurpleXfer *xfer = NULL; if (header->total_size < header->length) { - purple_debug_error("msn", "This can't be good\n"); - g_return_if_reached(); + /* We seem to have received a bad header */ + purple_debug_warning("msn", "Total size listed in SLP binary header " + "was less than length of this particular message. This " + "should not happen. Dropping message.\n"); + return; } offset = header->offset; @@ -588,15 +590,13 @@ if (slpmsg->session_id) { - if (slpmsg->slpcall == NULL) - slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); - + slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); if (slpmsg->slpcall != NULL) { if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) { - xfer = slpmsg->slpcall->xfer; + PurpleXfer *xfer = slpmsg->slpcall->xfer; if (xfer != NULL) { slpmsg->ft = TRUE; @@ -640,10 +640,9 @@ if (slpmsg->ft) { - xfer = slpmsg->slpcall->xfer; slpmsg->slpcall->u.incoming_data = g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)data, len); - purple_xfer_prpl_ready(xfer); + purple_xfer_prpl_ready(slpmsg->slpcall->xfer); } else if (slpmsg->size && slpmsg->buffer) { diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/state.c --- a/libpurple/protocols/msn/state.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/state.c Sat Sep 11 19:03:25 2010 +0000 @@ -27,6 +27,7 @@ #include "core.h" #include "msn.h" +#include "notification.h" #include "state.h" static const char *away_text[] = @@ -43,10 +44,6 @@ N_("Available") }; -/* Local Function Prototype*/ -static char *msn_build_psm(const char *psmstr,const char *mediastr, - const char *guidstr); - /* * WLM media PSM info build prcedure * @@ -56,7 +53,7 @@ * \0Office\01\0Office Message\0Office App Name\0" */ static char * -msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr) +msn_build_psm(const char *psmstr,const char *mediastr, const char *guidstr, guint protocol_ver) { xmlnode *dataNode,*psmNode,*mediaNode,*guidNode; char *result; @@ -82,60 +79,50 @@ } xmlnode_insert_child(dataNode, guidNode); + if (protocol_ver >= 16) { + /* TODO: What is this for? */ + xmlnode *ddpNode = xmlnode_new("DDP"); + xmlnode_insert_child(dataNode, ddpNode); + } + result = xmlnode_to_str(dataNode, &length); xmlnode_free(dataNode); return result; } -/* get the CurrentMedia info from the XML string */ +/* get the CurrentMedia info from the XML node */ char * -msn_get_currentmedia(char *xml_str, gsize len) +msn_get_currentmedia(xmlnode *payloadNode) { - xmlnode *payloadNode, *currentmediaNode; + xmlnode *currentmediaNode; char *currentmedia; purple_debug_info("msn", "Get CurrentMedia\n"); - payloadNode = xmlnode_from_str(xml_str, len); - if (!payloadNode) { - purple_debug_error("msn", "PSM XML parse Error!\n"); - return NULL; - } currentmediaNode = xmlnode_get_child(payloadNode, "CurrentMedia"); if (currentmediaNode == NULL) { purple_debug_info("msn", "No CurrentMedia Node\n"); - xmlnode_free(payloadNode); return NULL; } currentmedia = xmlnode_get_data(currentmediaNode); - xmlnode_free(payloadNode); - return currentmedia; } -/*get the PSM info from the XML string*/ +/* Get the PSM info from the XML node */ char * -msn_get_psm(char *xml_str, gsize len) +msn_get_psm(xmlnode *payloadNode) { - xmlnode *payloadNode, *psmNode; + xmlnode *psmNode; char *psm; purple_debug_info("msn", "msn get PSM\n"); - payloadNode = xmlnode_from_str(xml_str, len); - if (!payloadNode) { - purple_debug_error("msn", "PSM XML parse Error!\n"); - return NULL; - } psmNode = xmlnode_get_child(payloadNode, "PSM"); if (psmNode == NULL) { purple_debug_info("msn", "No PSM status Node\n"); - xmlnode_free(payloadNode); return NULL; } psm = xmlnode_get_data(psmNode); - xmlnode_free(payloadNode); - return psm; } @@ -175,14 +162,12 @@ /* set the MSN's PSM info,Currently Read from the status Line * Thanks for Cris Code */ -void +static void msn_set_psm(MsnSession *session) { PurpleAccount *account; PurplePresence *presence; PurpleStatus *status; - MsnCmdProc *cmdproc; - MsnTransaction *trans; char *payload; const char *statusline; gchar *statusline_stripped, *media = NULL; @@ -191,7 +176,6 @@ g_return_if_fail(session->notification != NULL); account = session->account; - cmdproc = session->notification->cmdproc; /* Get the PSM string from Purple's Status Line */ presence = purple_account_get_presence(account); @@ -202,13 +186,11 @@ statusline_stripped = purple_markup_strip_html(statusline); media = create_media_string(presence); g_free(session->psm); - session->psm = msn_build_psm(statusline_stripped, media, NULL); + session->psm = msn_build_psm(statusline_stripped, media, session->protocol_ver >= 16 ? session->guid : NULL, session->protocol_ver); payload = session->psm; - purple_debug_misc("msn", "Sending UUX command with payload: %s\n", payload); - trans = msn_transaction_new(cmdproc, "UUX", "%" G_GSIZE_FORMAT, strlen(payload)); - msn_transaction_set_payload(trans, payload, strlen(payload)); - msn_cmdproc_send_trans(cmdproc, trans); + + msn_notification_send_uux(session, payload); g_free(statusline_stripped); g_free(media); @@ -219,6 +201,7 @@ { PurpleAccount *account; MsnCmdProc *cmdproc; + MsnTransaction *trans; MsnUser *user; MsnObject *msnobj; const char *state_text; @@ -256,11 +239,16 @@ if (!session->logged_in) return; + msn_set_psm(session); + msnobj = msn_user_get_object(user); if (msnobj == NULL) { - msn_cmdproc_send(cmdproc, "CHG", "%s %u", state_text, caps); + if (session->protocol_ver >= 16) + trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u 0", state_text, caps, MSN_CLIENT_ID_EXT_CAPS); + else + trans = msn_transaction_new(cmdproc, "CHG", "%s %u", state_text, caps); } else { @@ -268,12 +256,17 @@ msnobj_str = msn_object_to_string(msnobj); - msn_cmdproc_send(cmdproc, "CHG", "%s %u %s", state_text, - caps, purple_url_encode(msnobj_str)); + if (session->protocol_ver >= 16) + trans = msn_transaction_new(cmdproc, "CHG", "%s %u:%02u %s", state_text, + caps, MSN_CLIENT_ID_EXT_CAPS, purple_url_encode(msnobj_str)); + else + trans = msn_transaction_new(cmdproc, "CHG", "%s %u %s", state_text, + caps, purple_url_encode(msnobj_str)); g_free(msnobj_str); } - msn_set_psm(session); + + msn_cmdproc_send_trans(cmdproc, trans); } const char * diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/state.h --- a/libpurple/protocols/msn/state.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/state.h Sat Sep 11 19:03:25 2010 +0000 @@ -58,13 +58,11 @@ const char *msn_state_get_text(MsnAwayType state); -void msn_set_psm(MsnSession *session); +/* Get the CurrentMedia info from the XML node */ +char *msn_get_currentmedia(xmlnode *payloadNode); -/* Get the CurrentMedia info from the XML string */ -char *msn_get_currentmedia(char *xml_str, gsize len); - -/*get the PSM info from the XML string*/ -char *msn_get_psm(char *xml_str, gsize len); +/* Get the PSM info from the XML node */ +char *msn_get_psm(xmlnode *payloadNode); MsnAwayType msn_state_from_account(PurpleAccount *account); diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/switchboard.c --- a/libpurple/protocols/msn/switchboard.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/switchboard.c Sat Sep 11 19:03:25 2010 +0000 @@ -252,6 +252,12 @@ return; } + /* Don't add ourselves either... */ + if (g_str_equal(passport, purple_account_get_username(account))) { + g_free(passport); + return; + } + swboard->users = g_list_prepend(swboard->users, passport); swboard->current_users++; swboard->empty = FALSE; @@ -274,7 +280,7 @@ PURPLE_CBFLAGS_NONE, TRUE); msn_servconn_set_idle_timeout(swboard->servconn, 0); } - else if (swboard->current_users > 1 || swboard->total_users > 1) + else if (swboard->current_users > 1) { msn_servconn_set_idle_timeout(swboard->servconn, 0); if (swboard->conv == NULL || @@ -514,7 +520,7 @@ body_enc = g_markup_escape_text(body_str, -1); g_free(body_str); - format = msn_message_get_attr(msg, "X-MMS-IM-Format"); + format = msn_message_get_header_value(msg, "X-MMS-IM-Format"); msn_parse_format(format, &pre, &post); body_str = g_strdup_printf("%s%s%s", pre ? pre : "", body_enc ? body_enc : "", post ? post : ""); @@ -970,6 +976,7 @@ MsnTransaction *trans; MsnCmdProc *cmdproc; PurpleAccount *account; + char *username; cmdproc = servconn->cmdproc; g_return_if_fail(cmdproc != NULL); @@ -978,24 +985,33 @@ swboard = cmdproc->data; g_return_if_fail(swboard != NULL); + if (servconn->session->protocol_ver >= 16) + username = g_strdup_printf("%s;{%s}", + purple_account_get_username(account), + servconn->session->guid); + else + username = g_strdup(purple_account_get_username(account)); + if (msn_switchboard_is_invited(swboard)) { swboard->empty = FALSE; trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s", - purple_account_get_username(account), + username, swboard->auth_key, swboard->session_id); } else { trans = msn_transaction_new(cmdproc, "USR", "%s %s", - purple_account_get_username(account), + username, swboard->auth_key); } msn_transaction_set_error_cb(trans, ans_usr_error); msn_transaction_set_data(trans, swboard); msn_cmdproc_send_trans(cmdproc, trans); + + g_free(username); } static void @@ -1213,8 +1229,11 @@ !swboard->session->connected) { MsnCmdProc *cmdproc; + MsnTransaction *trans; cmdproc = swboard->cmdproc; - msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL); + trans = msn_transaction_new(cmdproc, "OUT", NULL); + msn_transaction_set_saveable(trans, FALSE); + msn_cmdproc_send_trans(cmdproc, trans); msn_switchboard_destroy(swboard); } diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/sync.c --- a/libpurple/protocols/msn/sync.c Fri Sep 10 08:52:09 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +0,0 @@ -/** - * @file sync.c MSN list synchronization functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 "msn.h" -#include "sync.h" -#include "state.h" - -static MsnTable *cbs_table; - -static void -blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - PurpleConnection *gc = cmdproc->session->account->gc; - const char *list_name; - - list_name = cmd->params[0]; - - if (!g_ascii_strcasecmp(list_name, "AL")) - { - /* - * If the current setting is AL, messages from users who - * are not in BL will be delivered. - * - * In other words, deny some. - */ - gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS; - } - else - { - /* If the current setting is BL, only messages from people - * who are in the AL will be delivered. - * - * In other words, permit some. - */ - gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS; - } -} - -static void -prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - const char *type, *value; - - type = cmd->params[0]; - value = cmd->params[1]; - - if (cmd->param_count == 2) - { - if (!strcmp(type, "PHH")) - msn_user_set_home_phone(session->user, purple_url_decode(value)); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(session->user, purple_url_decode(value)); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(session->user, purple_url_decode(value)); - } - else - { - if (!strcmp(type, "PHH")) - msn_user_set_home_phone(session->user, NULL); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(session->user, NULL); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(session->user, NULL); - } -} - -static void -lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - const char *name; - const char *group_id; - - group_id = cmd->params[0]; - name = purple_url_decode(cmd->params[1]); - - msn_group_new(session->userlist, group_id, name); - - /* HACK */ - if (group_id == 0) - { - /* Group of ungroupped buddies */ - if (session->sync->total_users == 0) - { - cmdproc->cbs_table = session->sync->old_cbs_table; - - msn_session_finish_login(session); - - msn_sync_destroy(session->sync); - session->sync = NULL; - } - return; - } - - if ((purple_find_group(name)) == NULL) - { - PurpleGroup *g = purple_group_new(name); - purple_blist_add_group(g, NULL); - } -} - -static void -lst_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - char *passport = NULL; - const char *friend = NULL; - int list_op; - MsnUser *user; - - passport = cmd->params[0]; - friend = purple_url_decode(cmd->params[1]); - list_op = atoi(cmd->params[2]); - - user = msn_user_new(session->userlist, passport, friend); - - msn_userlist_add_user(session->userlist, user); - - session->sync->last_user = user; - - /* TODO: This can be improved */ - - if (list_op & MSN_LIST_FL_OP) - { - char **c; - char **tokens; - const char *group_nums; - GSList *group_ids; - - group_nums = cmd->params[3]; - - group_ids = NULL; - - tokens = g_strsplit(group_nums, ",", -1); - - for (c = tokens; *c != NULL; c++) - { - group_ids = g_slist_append(group_ids, *c); - } - - - msn_got_lst_user(session, user, list_op, group_ids); - - g_strfreev(tokens); - g_slist_free(group_ids); - } - else - { - msn_got_lst_user(session, user, list_op, NULL); - } - - session->sync->num_users++; - - if (session->sync->num_users == session->sync->total_users) - { - cmdproc->cbs_table = session->sync->old_cbs_table; - - msn_session_finish_login(session); - - msn_sync_destroy(session->sync); - session->sync = NULL; - } -} - -static void -bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSync *sync = cmdproc->session->sync; - const char *type, *value; - MsnUser *user; - - user = sync->last_user; - - g_return_if_fail(user != NULL); - - type = cmd->params[0]; - value = cmd->params[1]; - - if (value) - { - if (!strcmp(type, "MOB")) - { - if (!strcmp(value, "Y")) - user->mobile = TRUE; - } - else if (!strcmp(type, "PHH")) - msn_user_set_home_phone(user, purple_url_decode(value)); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(user, purple_url_decode(value)); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(user, purple_url_decode(value)); - } -} - -void -msn_sync_init(void) -{ - cbs_table = msn_table_new(); - - /* Syncing */ - msn_table_add_cmd(cbs_table, NULL, "GTC", NULL); - msn_table_add_cmd(cbs_table, NULL, "BLP", blp_cmd); - msn_table_add_cmd(cbs_table, NULL, "PRP", prp_cmd); - msn_table_add_cmd(cbs_table, NULL, "LSG", lsg_cmd); - msn_table_add_cmd(cbs_table, NULL, "LST", lst_cmd); - msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd); -} - -void -msn_sync_end(void) -{ - msn_table_destroy(cbs_table); -} - -MsnSync * -msn_sync_new(MsnSession *session) -{ - MsnSync *sync; - - sync = g_new0(MsnSync, 1); - - sync->session = session; - sync->cbs_table = cbs_table; - - return sync; -} - -void -msn_sync_destroy(MsnSync *sync) -{ - g_free(sync); -} diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/sync.h --- a/libpurple/protocols/msn/sync.h Fri Sep 10 08:52:09 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * @file sync.h MSN list synchronization functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 - */ -#ifndef MSN_SYNC_H -#define MSN_SYNC_H - -typedef struct _MsnSync MsnSync; - -#include "session.h" -#include "table.h" -#include "user.h" - -struct _MsnSync -{ - MsnSession *session; - MsnTable *cbs_table; - - /* - * TODO: What is the intended purpose of old_cbs_table? Nothing - * sets it and it is only read in two places. - */ - MsnTable *old_cbs_table; - - int num_users; - int total_users; - int num_groups; - int total_groups; - MsnUser *last_user; -}; - -void msn_sync_init(void); -void msn_sync_end(void); - -MsnSync *msn_sync_new(MsnSession *session); -void msn_sync_destroy(MsnSync *sync); - -#endif /* MSN_SYNC_H */ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/table.h --- a/libpurple/protocols/msn/table.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/table.h Sat Sep 11 19:03:25 2010 +0000 @@ -34,20 +34,60 @@ struct _MsnTable { - GHashTable *cmds; - GHashTable *msgs; - GHashTable *errors; + GHashTable *cmds; /**< Callbacks that manage command response. */ + GHashTable *msgs; /**< Callbacks that manage incoming messages. */ + GHashTable *errors; /**< Callbacks that manage command errors. */ - GHashTable *async; - GHashTable *fallback; + GHashTable *async; /**< Callbacks that manage incoming asyncronous messages. */ + /* TODO: Does this one is really needed? */ + GHashTable *fallback; /**< Fallback callback. */ }; +/** + * Create a new instance of a MsnTable which map commands, errors and messages + * with callbacks that will handle it. + * + * @return A new MsnTable. + */ MsnTable *msn_table_new(void); + +/** + * Destroy a MsnTable. + * + * @param table The MsnTable to be destroyed. + */ void msn_table_destroy(MsnTable *table); +/** + * Relate an incomming command from server with a callback able to handle + * the event. + * + * @param table The MsnTable. + * @param command If NULL this add an incoming asyncronous command set in answer. + * Else, the command sent. + * @param answer The server answer to 'command'. If 'command' is NULL, + * the asyncronous command sent by the server. + * @param cb Callback to handle this event. + */ void msn_table_add_cmd(MsnTable *table, char *command, char *answer, MsnTransCb cb); + +/** + * Set a callback to handle incoming command errors. + * + * @param table The MsnTable. + * @param answer Incoming command with error. + * @param cb Callback to handle this error. + */ void msn_table_add_error(MsnTable *table, char *answer, MsnErrorCb cb); + +/** + * Relate a message Content-type with a callback able to handle it. + * + * @param table The MsnTable. + * @param type The Message Content-Type. + * @param cb Callback to handle this Content-type. + */ void msn_table_add_msg_type(MsnTable *table, char *type, MsnMsgTypeCb cb); #endif /* MSN_TABLE_H */ diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/transaction.c --- a/libpurple/protocols/msn/transaction.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/transaction.c Sat Sep 11 19:03:25 2010 +0000 @@ -37,6 +37,7 @@ trans->cmdproc = cmdproc; trans->command = g_strdup(command); + trans->saveable = TRUE; if (format != NULL) { @@ -96,8 +97,10 @@ if (trans->params != NULL) str = g_strdup_printf("%s %u %s\r\n", trans->command, trans->trId, trans->params); + else if (trans->saveable) + str = g_strdup_printf("%s %u\r\n", trans->command, trans->trId); else - str = g_strdup_printf("%s %u\r\n", trans->command, trans->trId); + str = g_strdup_printf("%s\r\n", trans->command); return str; } @@ -175,6 +178,14 @@ } void +msn_transaction_set_saveable(MsnTransaction *trans, gboolean saveable) +{ + g_return_if_fail(trans != NULL); + + trans->saveable = saveable; +} + +void msn_transaction_add_cb(MsnTransaction *trans, char *answer, MsnTransCb cb) { diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/transaction.h --- a/libpurple/protocols/msn/transaction.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/transaction.h Sat Sep 11 19:03:25 2010 +0000 @@ -40,7 +40,9 @@ struct _MsnTransaction { MsnCmdProc *cmdproc; - unsigned int trId; + + gboolean saveable; /**< Whether to save this transaction in the history */ + unsigned int trId; /**< The ID of this transaction, if it's being saved */ char *command; char *params; @@ -74,6 +76,7 @@ const char *payload, int payload_len); void msn_transaction_set_data(MsnTransaction *trans, void *data); void msn_transaction_set_data_free(MsnTransaction *trans, GDestroyNotify fn); +void msn_transaction_set_saveable(MsnTransaction *trans, gboolean saveable); void msn_transaction_add_cb(MsnTransaction *trans, char *answer, MsnTransCb cb); void msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb); diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/user.c --- a/libpurple/protocols/msn/user.c Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/user.c Sat Sep 11 19:03:25 2010 +0000 @@ -25,6 +25,13 @@ #include "user.h" #include "slp.h" +static void free_user_endpoint(MsnUserEndpoint *data) +{ + g_free(data->id); + g_free(data->name); + g_free(data); +} + /*new a user object*/ MsnUser * msn_user_new(MsnUserList *userlist, const char *passport, @@ -48,6 +55,11 @@ { g_return_if_fail(user != NULL); + while (user->endpoints != NULL) { + free_user_endpoint(user->endpoints->data); + user->endpoints = g_slist_delete_link(user->endpoints, user->endpoints); + } + if (user->clientcaps != NULL) g_hash_table_destroy(user->clientcaps); @@ -186,6 +198,9 @@ { g_return_val_if_fail(user != NULL, FALSE); + if (user == user->userlist->session->user) + return FALSE; + if (user->friendly_name && name && (!strcmp(user->friendly_name, name) || !strcmp(user->passport, name))) return FALSE; @@ -217,6 +232,47 @@ } void +msn_user_set_endpoint_data(MsnUser *user, const char *input, MsnUserEndpoint *newep) +{ + MsnUserEndpoint *ep; + char *endpoint; + GSList *l; + + g_return_if_fail(user != NULL); + g_return_if_fail(input != NULL); + + endpoint = g_ascii_strdown(input, -1); + + for (l = user->endpoints; l; l = l->next) { + ep = l->data; + if (g_str_equal(ep->id, endpoint)) { + /* We have info about this endpoint! */ + + g_free(endpoint); + + if (newep == NULL) { + /* Delete it and exit */ + user->endpoints = g_slist_delete_link(user->endpoints, l); + free_user_endpoint(ep); + return; + } + + /* Break out of our loop and update it */ + break; + } + } + if (l == NULL) { + /* Need to add a new endpoint */ + ep = g_new0(MsnUserEndpoint, 1); + ep->id = endpoint; + user->endpoints = g_slist_prepend(user->endpoints, ep); + } + + ep->clientid = newep->clientid; + ep->extcaps = newep->extcaps; +} + +void msn_user_set_op(MsnUser *user, MsnListOp list_op) { g_return_if_fail(user != NULL); @@ -406,6 +462,14 @@ } void +msn_user_set_extcaps(MsnUser *user, guint extcaps) +{ + g_return_if_fail(user != NULL); + + user->extcaps = extcaps; +} + +void msn_user_set_network(MsnUser *user, MsnNetwork network) { g_return_if_fail(user != NULL); @@ -423,7 +487,7 @@ user->msnobj = obj; - if (user->list_op & MSN_LIST_FL_OP) + if (user != user->userlist->session->user && user->list_op & MSN_LIST_FL_OP) msn_queue_buddy_icon_request(user); } @@ -496,6 +560,39 @@ return user->clientid; } +guint +msn_user_get_extcaps(const MsnUser *user) +{ + g_return_val_if_fail(user != NULL, 0); + + return user->extcaps; +} + +MsnUserEndpoint * +msn_user_get_endpoint_data(MsnUser *user, const char *input) +{ + char *endpoint; + GSList *l; + MsnUserEndpoint *ep; + + g_return_val_if_fail(user != NULL, NULL); + g_return_val_if_fail(input != NULL, NULL); + + endpoint = g_ascii_strdown(input, -1); + + for (l = user->endpoints; l; l = l->next) { + ep = l->data; + if (g_str_equal(ep->id, endpoint)) { + g_free(endpoint); + return ep; + } + } + + g_free(endpoint); + + return NULL; +} + MsnObject * msn_user_get_object(const MsnUser *user) { @@ -520,3 +617,18 @@ return user->invite_message; } +gboolean +msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap) +{ + g_return_val_if_fail(user != NULL, FALSE); + + if (endpoint != NULL) { + MsnUserEndpoint *ep = msn_user_get_endpoint_data(user, endpoint); + if (ep != NULL) + return (ep->clientid & capability) && (ep->extcaps & extcap); + else + return FALSE; + } + + return (user->clientid & capability) && (user->extcaps & extcap); +} diff -r 9af193ee13b7 -r 6469c68fa093 libpurple/protocols/msn/user.h --- a/libpurple/protocols/msn/user.h Fri Sep 10 08:52:09 2010 +0000 +++ b/libpurple/protocols/msn/user.h Sat Sep 11 19:03:25 2010 +0000 @@ -83,6 +83,7 @@ char *friendly_name; /**< The friendly name. */ char *uid; /*< User ID */ + GSList *endpoints; /*< Endpoint-specific data */ const char *status; /**< The state of the user. */ char *statusline; /**< The state of the user. */ @@ -102,6 +103,7 @@ GHashTable *clientcaps; /**< The client's capabilities. */ guint clientid; /**< The client's ID */ + guint extcaps; /**< The client's extended capabilities */ MsnNetwork networkid; /**< The user's network */ @@ -116,6 +118,18 @@ char *invite_message; /**< Invite message of user request */ }; +/** + * A specific user endpoint. + */ +typedef struct MsnUserEndpoint { + char *id; /**< The client's endpoint ID */ + char *name; /**< The client's endpoint's name */ + int type; /**< The client's endpoint type */ + guint clientid; /**< The client's ID */ + guint extcaps; /**< The client's extended capabilites */ + +} MsnUserEndpoint; + /************************************************************************** ** @name User API * **************************************************************************/ @@ -252,6 +266,16 @@ void msn_user_set_uid(MsnUser *user, const char *uid); /** + * Sets endpoint data for a user. + * + * @param user The user. + * @param endpoint The endpoint. + * @param data The endpoint data. + */ +void +msn_user_set_endpoint_data(MsnUser *user, const char *endpoint, MsnUserEndpoint *data); + +/** * Sets the client id for a user. * * @param user The user. @@ -260,6 +284,14 @@ void msn_user_set_clientid(MsnUser *user, guint clientid); /** + * Sets the client id for a user. + * + * @param user The user. + * @param extcaps The client's extended capabilities. + */ +void msn_user_set_extcaps(MsnUser *user, guint extcaps); + +/** * Sets the network id for a user. * * @param user The user. @@ -346,6 +378,17 @@ const char *msn_user_get_mobile_phone(const MsnUser *user); /** + * Gets endpoint data for a user. + * + * @param user The user. + * @param endpoint The endpoint. + * + * @return The user's endpoint data. + */ +MsnUserEndpoint * +msn_user_get_endpoint_data(MsnUser *user, const char *endpoint); + +/** * Returns the client id for a user. * * @param user The user. @@ -355,6 +398,15 @@ guint msn_user_get_clientid(const MsnUser *user); /** + * Returns the extended capabilities for a user. + * + * @param user The user. + * + * @return The user's extended capabilities. + */ +guint msn_user_get_extcaps(const MsnUser *user); + +/** * Returns the network id for a user. * * @param user The user. @@ -398,11 +450,24 @@ /** * check to see if user is Yahoo User */ -gboolean msn_user_is_yahoo(PurpleAccount *account ,const char *name); +gboolean msn_user_is_yahoo(PurpleAccount *account, const char *name); void msn_user_set_op(MsnUser *user, MsnListOp list_op); void msn_user_unset_op(MsnUser *user, MsnListOp list_op); +/** + * Checks whether a user is capable of some task. + * + * @param user The user. + * @param endpoint The endpoint. Can be @NULL to check overall capabilities. + * @param capability The capability (including client version). + * @param extcap The extended capability. + * + * @return Whether the user supports the capability. + */ +gboolean +msn_user_is_capable(MsnUser *user, char *endpoint, guint capability, guint extcap); + /*@}*/ #endif /* MSN_USER_H */ diff -r 9af193ee13b7 -r 6469c68fa093 po/POTFILES.in --- a/po/POTFILES.in Fri Sep 10 08:52:09 2010 +0000 +++ b/po/POTFILES.in Sat Sep 11 19:03:25 2010 +0000 @@ -106,7 +106,6 @@ libpurple/protocols/jabber/usernick.c libpurple/protocols/jabber/xdata.c libpurple/protocols/msn/contact.c -libpurple/protocols/msn/dialog.c libpurple/protocols/msn/error.c libpurple/protocols/msn/group.h libpurple/protocols/msn/msg.c