Mercurial > pidgin.yaz
changeset 31350:0462bec07414
merged from im.pidgin.pidgin
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Wed, 10 Nov 2010 11:41:51 +0900 |
parents | 6d99f7cdc654 (diff) 63b9cb97d356 (current diff) |
children | ccd6a1da0d59 |
files | configure.ac libpurple/protocols/msn/msg.c libpurple/protocols/msn/msn.c libpurple/protocols/oscar/encoding.c libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/oscar.c libpurple/protocols/oscar/oscar.h pidgin/pixmaps/icons/hicolor/16x16/apps/pidgin.png pidgin/pixmaps/icons/hicolor/16x16/apps/scalable/pidgin.svg pidgin/pixmaps/icons/hicolor/22x22/apps/pidgin.png pidgin/pixmaps/icons/hicolor/24x24/apps/pidgin.png pidgin/pixmaps/icons/hicolor/24x24/apps/scalable/pidgin.svg pidgin/pixmaps/icons/hicolor/32x32/apps/pidgin.png pidgin/pixmaps/icons/hicolor/32x32/apps/scalable/pidgin.svg pidgin/pixmaps/icons/hicolor/48x48/apps/pidgin.png |
diffstat | 105 files changed, 4631 insertions(+), 3403 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Sat Oct 23 16:12:36 2010 +0900 +++ b/ChangeLog Wed Nov 10 11:41:51 2010 +0900 @@ -1,6 +1,42 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.7.5 (MM/DD/YYYY): +version 2.7.6 (??/??/????): + MSN: + * Added support for MSNP16, including Multiple Points of Presence (MPOP) + which allows multiple simultaneous sign-ins. + * Added extended capabilities support (none implemented). + * Merged the work done on the Google SoC (major rewrite of SLP code) + * Reworked the data transfer architecture. + (http://developer.pidgin.im/wiki/SlpArchitecture) + * Lots of little changes. + +version 2.7.5 (10/31/2010): + General: + * Added Verisign Class 3 Public CA - G2 root CA. + + Pidgin: + * Properly differentiate between bn and bn_IN in the Translation + Information dialog. + + AIM and/or ICQ: + * Display the "Authorize buddy?" minidialog when the requestor has an + empty nickname. (#12810) + * New ICQ accounts default to proper ICQ servers. Old accounts using one + of the old default servers will be silently migrated to use the proper + servers. + * ICQ accounts using clientLogin now use the correct ICQ servers. This is + separate from the server settings mentioned above. + * '<' should no longer cause ICQ status messages to be truncated in some + locations. (#11964, #12593) + * Fix sending messages to chat rooms. (#12768) + + Bonjour: + * Don't crash when attempting to log into a Bonjour account and init + failed. + + Windows-Specific Changes: + * Quote the path stored in the registry when the "run at startup" option + in the Windows Pidgin Options plugin is used. (#12781) version 2.7.4 (10/20/2010): General: @@ -44,6 +80,9 @@ conversation becomes disabled or inactive. (Keith Moyer) (#12471) * xdg-open is now the default browser for new users on non-Windows platforms. (Stanislav Brabec) (#12505) + * The "Authorize buddy?" mini-dialog now shows the nickname of + the buddy requesting authorization as well as the icon of + the IM protocol he is using. (#5038) Finch: * Add support for drop-down account options (like the SILC cipher @@ -70,6 +109,16 @@ _xmppconnect TXT record returns results, but none of them result in a valid BOSH URI. (#a14367, #12744) + AIM and ICQ: + * Add support for managing Visible/Invisible lists. (#10967) + * Fix a problem with receiving HTML messages from + QIP/Miranda/Trillian. (#12044) + * Hopefully fixed all encoding-related problems, both + for sending and receiving messages. (#10833 and the like) + * Fix a problem with receiving messages from pyicqt. (#12284) + * Don't set a custom status text when going Invisible to avoid + being detected as Invisible. (#10633) + Yahoo/Yahoo JAPAN: * Stop doing unnecessary lookups of certain alias information. This solves deadlocks when a given Yahoo account has a ridiculously large
--- a/ChangeLog.API Sat Oct 23 16:12:36 2010 +0900 +++ b/ChangeLog.API Wed Nov 10 11:41:51 2010 +0900 @@ -1,6 +1,9 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul -version 2.7.5 (MM/DD/YYYY): +version 2.7.6 (??/??/????): + +version 2.7.5 (10/31/2010): + * No changes version 2.7.4 (10/20/2010): Perl:
--- a/NEWS Sat Oct 23 16:12:36 2010 +0900 +++ b/NEWS Wed Nov 10 11:41:51 2010 +0900 @@ -2,7 +2,19 @@ Our development blog is available at: http://planet.pidgin.im -2.7.5 (MM/DD/YYYY): +2.7.6 (??/??/????): + Jorge: In this release I have merged two branches where I have spent + most of my time in the last months, the MSNP16 and SLP-rewrite. I + hope you all will enjoy the hability to be connected on multiple + instances at the same time. I also hope that the SLP rewrite + fix a lot of old bugs that we have in the tracker. I am really + happy with this rewrite because there was untoched code from almost + 5 years ago. I hope you like this release! + +2.7.5 (10/31/2010): + John: A bugfix release for all of you! This time we fixed a bunch of + bugs ranging from annoying regressions to long-standing bugs we didn't + realize until now were bugs. Enjoy! 2.7.4 (10/20/2010): John: This release came at this particular time due to some security
--- a/configure.ac Sat Oct 23 16:12:36 2010 +0900 +++ b/configure.ac Wed Nov 10 11:41:51 2010 +0900 @@ -46,7 +46,7 @@ m4_define([purple_lt_current], [7]) m4_define([purple_major_version], [2]) m4_define([purple_minor_version], [7]) -m4_define([purple_micro_version], [5]) +m4_define([purple_micro_version], [6]) m4_define([purple_version_suffix], [devel]) m4_define([purple_version], [purple_major_version.purple_minor_version.purple_micro_version]) @@ -55,7 +55,7 @@ m4_define([gnt_lt_current], [8]) m4_define([gnt_major_version], [2]) m4_define([gnt_minor_version], [8]) -m4_define([gnt_micro_version], [2]) +m4_define([gnt_micro_version], [3]) m4_define([gnt_version_suffix], [devel]) m4_define([gnt_version], [gnt_major_version.gnt_minor_version.gnt_micro_version])
--- a/libpurple/account.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/account.c Wed Nov 10 11:41:51 2010 +0900 @@ -508,8 +508,27 @@ purple_account_remove_setting(account, "xferjp_host"); } - - return; +} + +static void +migrate_icq_server(PurpleAccount *account) +{ + /* Migrate the login server setting for ICQ accounts. See + * 'mtn log --last 1 --no-graph --from b6d7712e90b68610df3bd2d8cbaf46d94c8b3794' + * for details on the change. */ + + if(purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) { + const char *tmp = purple_account_get_string(account, "server", NULL); + + /* Non-secure server */ + if(purple_strequal(tmp, "login.messaging.aol.com") || + purple_strequal(tmp, "login.oscar.aol.com")) + purple_account_set_string(account, "server", "login.icq.com"); + + /* Secure server */ + if(purple_strequal(tmp, "slogin.oscar.aol.com")) + purple_account_set_string(account, "server", "slogin.icq.com"); + } } static void @@ -598,6 +617,9 @@ /* we do this here because we need access to account settings to determine * if we can/should migrate an old Yahoo! JAPAN account */ migrate_yahoo_japan(account); + /* we do this here because we need access to account settings to determine + * if we can/should migrate an ICQ account's server setting */ + migrate_icq_server(account); /* we do this here because we need to do it before the user views the * Edit Account dialog. */ migrate_xmpp_encryption(account);
--- a/libpurple/connection.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/connection.c Wed Nov 10 11:41:51 2010 +0900 @@ -135,7 +135,7 @@ !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) { - purple_debug_error("connection", "Can not connect to account %s without " + purple_debug_error("connection", "Cannot connect to account %s without " "a password.\n", purple_account_get_username(account)); return; } @@ -210,7 +210,7 @@ !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) { - purple_debug_error("connection", "Can not connect to account %s without " + purple_debug_error("connection", "Cannot connect to account %s without " "a password.\n", purple_account_get_username(account)); return; }
--- a/libpurple/protocols/bonjour/bonjour.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/bonjour/bonjour.c Wed Nov 10 11:41:51 2010 +0900 @@ -189,7 +189,8 @@ purple_xfer_cancel_local(bd->xfer_lists->data); } - g_free(bd->jid); + if (bd != NULL) + g_free(bd->jid); g_free(bd); connection->proto_data = NULL; }
--- a/libpurple/protocols/jabber/adhoccommands.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/jabber/adhoccommands.c Wed Nov 10 11:41:51 2010 +0900 @@ -125,7 +125,8 @@ xmlnode_set_attrib(command,"node",actionInfo->node); /* cancel is handled differently on ad-hoc commands than regular forms */ - if(!strcmp(xmlnode_get_namespace(result),"jabber:x:data") && !strcmp(xmlnode_get_attrib(result, "type"),"cancel")) { + if (purple_strequal(xmlnode_get_namespace(result), "jabber:x:data") && + purple_strequal(xmlnode_get_attrib(result, "type"), "cancel")) { xmlnode_set_attrib(command,"action","cancel"); } else { if(actionhandle)
--- a/libpurple/protocols/jabber/auth.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/jabber/auth.c Wed Nov 10 11:41:51 2010 +0900 @@ -502,20 +502,30 @@ return 0; } +void jabber_auth_add_mech(JabberSaslMech *mech) +{ + auth_mechs = g_slist_insert_sorted(auth_mechs, mech, compare_mech); +} + +void jabber_auth_remove_mech(JabberSaslMech *mech) +{ + auth_mechs = g_slist_remove(auth_mechs, mech); +} + void jabber_auth_init(void) { JabberSaslMech **tmp; gint count, i; - auth_mechs = g_slist_insert_sorted(auth_mechs, jabber_auth_get_plain_mech(), compare_mech); - auth_mechs = g_slist_insert_sorted(auth_mechs, jabber_auth_get_digest_md5_mech(), compare_mech); + jabber_auth_add_mech(jabber_auth_get_plain_mech()); + jabber_auth_add_mech(jabber_auth_get_digest_md5_mech()); #ifdef HAVE_CYRUS_SASL - auth_mechs = g_slist_insert_sorted(auth_mechs, jabber_auth_get_cyrus_mech(), compare_mech); + jabber_auth_add_mech(jabber_auth_get_cyrus_mech()); #endif tmp = jabber_auth_get_scram_mechs(&count); for (i = 0; i < count; ++i) - auth_mechs = g_slist_insert_sorted(auth_mechs, tmp[i], compare_mech); + jabber_auth_add_mech(tmp[i]); } void jabber_auth_uninit(void)
--- a/libpurple/protocols/jabber/auth.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/jabber/auth.h Wed Nov 10 11:41:51 2010 +0900 @@ -58,6 +58,9 @@ JabberSaslMech *jabber_auth_get_cyrus_mech(void); #endif +void jabber_auth_add_mech(JabberSaslMech *); +void jabber_auth_remove_mech(JabberSaslMech *); + void jabber_auth_init(void); void jabber_auth_uninit(void);
--- a/libpurple/protocols/jabber/presence.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/jabber/presence.c Wed Nov 10 11:41:51 2010 +0900 @@ -995,12 +995,14 @@ } for (child = packet->child; child; child = child->next) { + const char *xmlns; char *key; JabberPresenceHandler *pih; if (child->type != XMLNODE_TYPE_TAG) continue; - key = g_strdup_printf("%s %s", child->name, xmlnode_get_namespace(child)); + xmlns = xmlnode_get_namespace(child); + key = g_strdup_printf("%s %s", child->name, xmlns ? xmlns : ""); pih = g_hash_table_lookup(presence_handlers, key); g_free(key); if (pih)
--- a/libpurple/protocols/msn/Makefile.am Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/Makefile.am Wed Nov 10 11:41:51 2010 +0900 @@ -12,8 +12,6 @@ command.h \ contact.c\ contact.h\ - dialog.c \ - dialog.h \ directconn.c \ directconn.h \ error.c \ @@ -36,6 +34,8 @@ object.h \ oim.c\ oim.h\ + p2p.c \ + p2p.h \ page.c \ page.h \ servconn.c \ @@ -50,14 +50,16 @@ slplink.h \ slpmsg.c \ slpmsg.h \ + slpmsg_part.c \ + slpmsg_part.h \ soap.c \ soap.h \ state.c \ state.h \ + sbconn.c \ + sbconn.h \ switchboard.c \ switchboard.h \ - sync.c \ - sync.h \ table.c \ table.h \ transaction.c \ @@ -66,6 +68,8 @@ user.h \ userlist.c \ userlist.h \ + xfer.c \ + xfer.h \ msnutils.c \ msnutils.h
--- a/libpurple/protocols/msn/Makefile.mingw Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/Makefile.mingw Wed Nov 10 11:41:51 2010 +0900 @@ -40,7 +40,6 @@ C_SRC = cmdproc.c \ command.c \ contact.c\ - dialog.c \ directconn.c \ error.c \ group.c \ @@ -52,6 +51,7 @@ notification.c \ object.c \ oim.c\ + p2p.c \ page.c \ servconn.c \ session.c \ @@ -59,14 +59,16 @@ slpcall.c \ slplink.c \ slpmsg.c \ + slpmsg_part.c \ soap.c\ state.c \ + sbconn.c \ switchboard.c \ - sync.c \ table.c \ transaction.c \ user.c \ userlist.c \ + xfer.c \ msnutils.c OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/msn/cmdproc.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/cmdproc.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,8 +21,12 @@ * 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 "internal.h" +#include "debug.h" + #include "cmdproc.h" +#include "error.h" MsnCmdProc * msn_cmdproc_new(MsnSession *session) @@ -122,7 +126,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 +160,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 +179,73 @@ 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) { + if (first != NULL) { + 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, messageId: '%s'\n", - chunk+1, first->total_chunks, messageId); + 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; - else - /* We're done! Send it along... The caller takes care of - freeing the old one. */ - msg = first; + + /* + * We have all the chunks for this message, great! 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); - return; + purple_debug_error("msn", + "Unable to find first chunk of message_id '%s' to correspond with chunk %d.\n", + message_id, chunk + 1); } } 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 +265,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
--- a/libpurple/protocols/msn/cmdproc.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/cmdproc.h Wed Nov 10 11:41:51 2010 +0900 @@ -27,7 +27,6 @@ typedef struct _MsnCmdProc MsnCmdProc; #include "command.h" -#include "error.h" #include "history.h" #include "servconn.h" #include "session.h" @@ -51,18 +50,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);
--- a/libpurple/protocols/msn/command.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/command.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,7 +21,8 @@ * 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 "internal.h" + #include "command.h" static gboolean
--- a/libpurple/protocols/msn/command.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/command.h Wed Nov 10 11:41:51 2010 +0900 @@ -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 */
--- a/libpurple/protocols/msn/contact.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/contact.c Wed Nov 10 11:41:51 2010 +0900 @@ -24,12 +24,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "msn.h" +#include "internal.h" +#include "debug.h" + #include "contact.h" #include "xmlnode.h" #include "group.h" +#include "msnutils.h" #include "soap.h" #include "nexus.h" +#include "user.h" const char *MsnSoapPartnerScenarioText[] = { @@ -1172,7 +1176,7 @@ msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); - if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { + if (msn_user_is_in_list(user, MSN_LIST_PL)) { msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL); return; }
--- a/libpurple/protocols/msn/dialog.c Sat Oct 23 16:12:36 2010 +0900 +++ /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); -}
--- a/libpurple/protocols/msn/dialog.h Sat Oct 23 16:12:36 2010 +0900 +++ /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 */
--- a/libpurple/protocols/msn/directconn.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/directconn.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,30 +21,20 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "internal.h" +#include "cipher.h" +#include "debug.h" + #include "msn.h" #include "directconn.h" #include "slp.h" #include "slpmsg.h" +#include "p2p.h" -#pragma pack(push,1) -typedef struct { - guint32 session_id; - guint32 seq_id; - guint64 offset; - guint64 total_size; - guint32 length; - guint32 flags; - guint32 ack_id; - guint32 ack_uid; - guint64 ack_size; -/* guint8 body[1]; */ -} MsnDcContext; -#pragma pack(pop) - -#define DC_PACKET_HEADER_SIZE sizeof(MsnDcContext) #define DC_MAX_BODY_SIZE 8*1024 -#define DC_MAX_PACKET_SIZE (DC_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE) +#define DC_MAX_PACKET_SIZE (P2P_PACKET_HEADER_SIZE + DC_MAX_BODY_SIZE) static void msn_dc_calculate_nonce_hash(MsnDirectConnNonceType type, @@ -121,8 +111,12 @@ { g_free(p->data); +#if 0 if (p->msg) msn_message_unref(p->msg); +#endif + if (p->part) + msn_slpmsgpart_unref(p->part); g_free(p); } @@ -191,7 +185,7 @@ if (slplink) { slplink->dc = NULL; if (slplink->swboard == NULL) - msn_slplink_destroy(slplink); + msn_slplink_unref(slplink); } g_free(dc->msg_body); @@ -353,7 +347,7 @@ if (queue) { while (!g_queue_is_empty(queue)) { MsnDirectConnPacket *p = g_queue_pop_head(queue); - msn_slplink_send_msg(slplink, p->msg); + msn_slplink_send_msgpart(slplink, (MsnSlpMessage*)p->part->ack_data); msn_dc_destroy_packet(p); } g_queue_free(queue); @@ -363,51 +357,6 @@ } static void -msn_dc_parse_binary_header(MsnDirectConn *dc) -{ - MsnSlpHeader *h; - MsnDcContext *context; - - g_return_if_fail(dc != NULL); - - h = &dc->header; - /* Skip packet size */ - context = (MsnDcContext *)(dc->in_buffer + 4); - - h->session_id = GUINT32_FROM_LE(context->session_id); - h->id = GUINT32_FROM_LE(context->seq_id); - h->offset = GUINT64_FROM_LE(context->offset); - h->total_size = GUINT64_FROM_LE(context->total_size); - h->length = GUINT32_FROM_LE(context->length); - h->flags = GUINT32_FROM_LE(context->flags); - h->ack_id = GUINT32_FROM_LE(context->ack_id); - h->ack_sub_id = GUINT32_FROM_LE(context->ack_uid); - h->ack_size = GUINT64_FROM_LE(context->ack_size); -} - -static const gchar * -msn_dc_serialize_binary_header(MsnDirectConn *dc) { - MsnSlpHeader *h; - static MsnDcContext bin_header; - - g_return_val_if_fail(dc != NULL, NULL); - - h = &dc->header; - - bin_header.session_id = GUINT32_TO_LE(h->session_id); - bin_header.seq_id = GUINT32_TO_LE(h->id); - bin_header.offset = GUINT64_TO_LE(h->offset); - bin_header.total_size = GUINT64_TO_LE(h->total_size); - bin_header.length = GUINT32_TO_LE(h->length); - bin_header.flags = GUINT32_TO_LE(h->flags); - bin_header.ack_id = GUINT32_TO_LE(h->ack_id); - bin_header.ack_uid = GUINT32_TO_LE(h->ack_sub_id); - bin_header.ack_size = GUINT64_TO_LE(h->ack_size); - - return (const gchar *)&bin_header; -} - -static void msn_dc_send_cb(gpointer data, gint fd, PurpleInputCondition cond) { MsnDirectConn *dc = data; @@ -502,10 +451,11 @@ { const gchar *h; - h = msn_dc_serialize_binary_header(dc); - memcpy(p->data, h, DC_PACKET_HEADER_SIZE); + h = (gchar*) msn_p2p_header_to_wire(&dc->header); - memcpy(p->data + offsetof(MsnDcContext, ack_id), dc->nonce, 16); + memcpy(p->data, h, P2P_PACKET_HEADER_SIZE); + + memcpy(p->data + offsetof(MsnP2PHeader, ack_id), dc->nonce, 16); msn_dc_enqueue_packet(dc, p); } @@ -515,7 +465,7 @@ { MsnDirectConnPacket *p; - p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE); + p = msn_dc_new_packet(P2P_PACKET_HEADER_SIZE); dc->header.session_id = 0; dc->header.id = dc->slpcall->slplink->slp_seq_id++; @@ -532,7 +482,7 @@ { MsnDirectConnPacket *p; - p = msn_dc_new_packet(DC_PACKET_HEADER_SIZE); + p = msn_dc_new_packet(P2P_PACKET_HEADER_SIZE); dc->header.id = dc->slpcall->slplink->slp_seq_id++; dc->header.length = 0; @@ -546,10 +496,10 @@ guchar nonce[16]; gchar nonce_hash[37]; - if (packet_length != DC_PACKET_HEADER_SIZE) + if (packet_length != P2P_PACKET_HEADER_SIZE) return FALSE; - memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnDcContext, ack_id), 16); + memcpy(nonce, dc->in_buffer + 4 + offsetof(MsnP2PHeader, ack_id), 16); if (dc->nonce_type == DC_NONCE_PLAIN) { if (memcmp(dc->nonce, nonce, 16) == 0) { @@ -589,6 +539,14 @@ static void msn_dc_send_packet_cb(MsnDirectConnPacket *p) { + if (p->part != NULL && p->part->ack_cb != NULL) + p->part->ack_cb(p->part, p->part->ack_data); +} + +#if 0 +static void +msn_dc_send_packet_cb(MsnDirectConnPacket *p) +{ if (p->msg != NULL && p->msg->ack_cb != NULL) p->msg->ack_cb(p->msg, p->msg->ack_data); } @@ -599,21 +557,42 @@ MsnDirectConnPacket *p; guint32 length; - length = msg->body_len + DC_PACKET_HEADER_SIZE; + length = msg->body_len + P2P_PACKET_HEADER_SIZE; p = msn_dc_new_packet(length); - memcpy(p->data, &msg->msnslp_header, DC_PACKET_HEADER_SIZE); - memcpy(p->data + DC_PACKET_HEADER_SIZE, msg->body, msg->body_len); + memcpy(p->data, msg->slpmsg->header, P2P_PACKET_HEADER_SIZE); + memcpy(p->data + P2P_PACKET_HEADER_SIZE, msg->body, msg->body_len); p->sent_cb = msn_dc_send_packet_cb; p->msg = msn_message_ref(msg); msn_dc_enqueue_packet(dc, p); } +#endif + +void +msn_dc_enqueue_part(MsnDirectConn *dc, MsnSlpMessagePart *part) +{ + MsnDirectConnPacket *p; + guint32 length; + + length = part->size + P2P_PACKET_HEADER_SIZE; + p = msn_dc_new_packet(length); + + memcpy(p->data, part->header, P2P_PACKET_HEADER_SIZE); + memcpy(p->data + P2P_PACKET_HEADER_SIZE, part->buffer, part->size); + + p->sent_cb = msn_dc_send_packet_cb; + p->part = part; + + msn_dc_enqueue_packet(dc, p); +} static int msn_dc_process_packet(MsnDirectConn *dc, guint32 packet_length) { + MsnSlpMessagePart *part; + g_return_val_if_fail(dc != NULL, DC_PROCESS_ERROR); switch (dc->state) { @@ -650,12 +629,9 @@ break; case DC_STATE_ESTABLISHED: - msn_slplink_process_msg( - dc->slplink, - &dc->header, - dc->in_buffer + 4 + DC_PACKET_HEADER_SIZE, - dc->header.length - ); + + part = msn_slpmsgpart_new_from_data(dc->in_buffer + 4, dc->header.length); + msn_slplink_process_msg(dc->slplink, part); /* if (dc->num_calls == 0) { @@ -727,7 +703,15 @@ return; if (dc->state != DC_STATE_FOO) { - msn_dc_parse_binary_header(dc); + MsnP2PHeader *context; + MsnP2PHeader *h; + + /* Skip packet size */ + context = (MsnP2PHeader *)(dc->in_buffer + 4); + + h = msn_p2p_header_from_wire(context); + memcpy(&dc->header, h, P2P_PACKET_HEADER_SIZE); + g_free(h); } switch (msn_dc_process_packet(dc, packet_length)) {
--- a/libpurple/protocols/msn/directconn.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/directconn.h Wed Nov 10 11:41:51 2010 +0900 @@ -30,10 +30,13 @@ #include "proxy.h" #include "circbuffer.h" -#include "msg.h" #include "slp.h" #include "slplink.h" #include "slpmsg.h" +#include "slpmsg_part.h" +#include "p2p.h" + +#define MSN_DCCONN_MAX_SIZE 1352 typedef enum { @@ -68,7 +71,7 @@ guchar *data; void (*sent_cb)(struct _MsnDirectConnPacket*); - MsnMessage *msg; + MsnSlpMessagePart *part; }; struct _MsnDirectConn @@ -100,7 +103,7 @@ GQueue *out_queue; /**< The outgoing packet queue */ int msg_pos; /**< The position of next byte to be sent in the actual packet */ - MsnSlpHeader header; /**< SLP header for parsing / serializing */ + MsnP2PHeader header; /**< SLP header for parsing / serializing */ /** The callback used for sending information to the peer about the opened socket */ void (*send_connection_info_msg_cb)(MsnDirectConn *); @@ -124,8 +127,12 @@ /* * Queues an MSN message to be sent via direct connection. */ +#if 0 void msn_dc_enqueue_msg(MsnDirectConn *dc, MsnMessage *msg); +#endif +void +msn_dc_enqueue_part(MsnDirectConn *dc, MsnSlpMessagePart *part); /* * Creates, initializes, and returns a new MsnDirectConn structure.
--- a/libpurple/protocols/msn/error.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/error.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,9 +21,23 @@ * 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 "internal.h" +#include "debug.h" +/* Masca: can we get rid of the sync issue dialog? */ +#include "request.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 +278,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); +}
--- a/libpurple/protocols/msn/error.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/error.h Wed Nov 10 11:41:51 2010 +0900 @@ -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 */
--- a/libpurple/protocols/msn/group.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/group.h Wed Nov 10 11:41:51 2010 +0900 @@ -26,7 +26,7 @@ typedef struct _MsnGroup MsnGroup; -#include <stdio.h> +#include "internal.h" #include "session.h" #include "user.h"
--- a/libpurple/protocols/msn/history.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/history.h Wed Nov 10 11:41:51 2010 +0900 @@ -24,6 +24,8 @@ #ifndef MSN_HISTORY_H #define MSN_HISTORY_H +#include "internal.h" + typedef struct _MsnHistory MsnHistory; #include "transaction.h"
--- a/libpurple/protocols/msn/httpconn.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/httpconn.h Wed Nov 10 11:41:51 2010 +0900 @@ -28,6 +28,7 @@ #include "circbuffer.h" #include "servconn.h" +#include "session.h" /** * An HTTP Connection.
--- a/libpurple/protocols/msn/msg.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/msg.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,9 +21,15 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "internal.h" +#include "debug.h" + #include "msn.h" #include "msg.h" #include "msnutils.h" +#include "slpmsg.h" +#include "slpmsg_part.h" MsnMessage * msn_message_new(MsnMsgType type) @@ -36,7 +42,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 +70,9 @@ 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); + msn_slpmsgpart_destroy(msg->part); g_free(msg); } @@ -113,11 +120,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); @@ -136,7 +143,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; @@ -160,46 +167,6 @@ } void -msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len) -{ - MsnSlpHeader header; - const char *tmp; - int body_len; - - tmp = body; - - if (len < sizeof(header)) { - g_return_if_reached(); - } - - /* Import the header. */ - memcpy(&header, tmp, sizeof(header)); - tmp += sizeof(header); - - msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id); - msg->msnslp_header.id = GUINT32_FROM_LE(header.id); - msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset); - msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size); - msg->msnslp_header.length = GUINT32_FROM_LE(header.length); - msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags); - msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id); - 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. */ - body_len = len - (tmp - body); - /* msg->body_len = msg->msnslp_header.length; */ - - if (body_len > 0) { - msg->body_len = len - (tmp - body); - msg->body = g_malloc(msg->body_len + 1); - memcpy(msg->body, tmp, msg->body_len); - msg->body[msg->body_len] = '\0'; - tmp += body_len; - } -} - -void msn_message_parse_payload(MsnMessage *msg, const char *payload, size_t payload_len, const char *line_dem,const char *body_dem) @@ -214,7 +181,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 @@ -225,8 +192,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; @@ -243,7 +210,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); @@ -281,12 +248,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" */ @@ -296,70 +262,26 @@ content_type = msn_message_get_content_type(msg); if (content_type != NULL && - !strcmp(content_type, "application/x-msnmsgrp2p")) - { - MsnSlpHeader header; - MsnSlpFooter footer; - int body_len; - - if (payload_len - (tmp - tmp_base) < sizeof(header)) { - g_free(tmp_base); - g_return_if_reached(); - } - + !strcmp(content_type, "application/x-msnmsgrp2p")) { msg->msnslp_message = TRUE; - - /* Import the header. */ - memcpy(&header, tmp, sizeof(header)); - tmp += sizeof(header); - - msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id); - msg->msnslp_header.id = GUINT32_FROM_LE(header.id); - msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset); - msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size); - msg->msnslp_header.length = GUINT32_FROM_LE(header.length); - msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags); - msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id); - msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id); - msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size); - - body_len = payload_len - (tmp - tmp_base) - sizeof(footer); + msg->part = msn_slpmsgpart_new_from_data(tmp, payload_len - (tmp - tmp_base)); + } - /* Import the body. */ - if (body_len > 0) { - msg->body_len = body_len; - g_free(msg->body); - msg->body = g_malloc(msg->body_len + 1); - memcpy(msg->body, tmp, msg->body_len); - msg->body[msg->body_len] = '\0'; - tmp += body_len; - } - - /* Import the footer. */ - if (body_len >= 0) { - memcpy(&footer, tmp, sizeof(footer)); - tmp += sizeof(footer); - msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value); - } + if (payload_len - (tmp - tmp_base) > 0) { + msg->body_len = payload_len - (tmp - tmp_base); + g_free(msg->body); + msg->body = g_malloc(msg->body_len + 1); + memcpy(msg->body, tmp, msg->body_len); + msg->body[msg->body_len] = '\0'; } - else - { - if (payload_len - (tmp - tmp_base) > 0) { - msg->body_len = payload_len - (tmp - tmp_base); - g_free(msg->body); - msg->body = g_malloc(msg->body_len + 1); - memcpy(msg->body, tmp, msg->body_len); - msg->body[msg->body_len] = '\0'; - } - - if ((!content_type || !strcmp(content_type, "text/plain")) + + if ((!content_type || !strcmp(content_type, "text/plain")) && msg->charset == NULL) { - char *body = g_convert(msg->body, msg->body_len, "UTF-8", - "ISO-8859-1", NULL, &msg->body_len, NULL); - g_free(msg->body); - msg->body = body; - msg->charset = g_strdup("UTF-8"); - } + char *body = g_convert(msg->body, msg->body_len, "UTF-8", + "ISO-8859-1", NULL, &msg->body_len, NULL); + g_free(msg->body); + msg->body = body; + msg->charset = g_strdup("UTF-8"); } g_free(tmp_base); @@ -384,43 +306,10 @@ char * msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size) { - MsnSlpHeader header; - - char *tmp, *base; - const void *body; - size_t len, body_len; - - g_return_val_if_fail(msg != NULL, NULL); - - len = MSN_BUF_LEN; - - base = tmp = g_malloc(len + 1); - - body = msn_message_get_bin_data(msg, &body_len); + char *tmp; - header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id); - header.id = GUINT32_TO_LE(msg->msnslp_header.id); - header.offset = GUINT64_TO_LE(msg->msnslp_header.offset); - header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size); - header.length = GUINT32_TO_LE(msg->msnslp_header.length); - header.flags = GUINT32_TO_LE(msg->msnslp_header.flags); - header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id); - header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id); - header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size); - - memcpy(tmp, &header, 48); - tmp += 48; - - if (body != NULL) - { - memcpy(tmp, body, body_len); - tmp += body_len; - } - - if (ret_size != NULL) - *ret_size = tmp - base; - - return base; + tmp = msn_slpmsgpart_serialize(msg->part, ret_size); + return tmp; } char * @@ -457,13 +346,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); @@ -475,33 +364,13 @@ if (msg->msnslp_message) { - MsnSlpHeader header; - MsnSlpFooter footer; - - header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id); - header.id = GUINT32_TO_LE(msg->msnslp_header.id); - header.offset = GUINT64_TO_LE(msg->msnslp_header.offset); - header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size); - header.length = GUINT32_TO_LE(msg->msnslp_header.length); - header.flags = GUINT32_TO_LE(msg->msnslp_header.flags); - header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id); - header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id); - header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size); + size_t siz; + char *body; + + body = msn_slpmsgpart_serialize(msg->part, &siz); - memcpy(n, &header, 48); - n += 48; - - if (body != NULL) - { - memcpy(n, body, body_len); - - n += body_len; - } - - footer.value = GUINT32_TO_BE(msg->msnslp_footer.value); - - memcpy(n, &footer, 4); - n += 4; + memcpy(n, body, siz); + n += siz; } else { @@ -613,15 +482,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) { @@ -629,37 +498,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 * @@ -744,13 +613,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); } @@ -761,15 +630,15 @@ if (msg->msnslp_message) { - g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id); - g_string_append_printf(str, "ID: %u\r\n", msg->msnslp_header.id); - g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset); - g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size); - g_string_append_printf(str, "Length: %u\r\n", msg->msnslp_header.length); - g_string_append_printf(str, "Flags: 0x%x\r\n", msg->msnslp_header.flags); - g_string_append_printf(str, "ACK ID: %u\r\n", msg->msnslp_header.ack_id); - g_string_append_printf(str, "SUB ID: %u\r\n", msg->msnslp_header.ack_sub_id); - g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size); + g_string_append_printf(str, "Session ID: %u\r\n", msg->part->header->session_id); + g_string_append_printf(str, "ID: %u\r\n", msg->part->header->id); + g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->part->header->offset); + g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->part->header->total_size); + g_string_append_printf(str, "Length: %u\r\n", msg->part->header->length); + g_string_append_printf(str, "Flags: 0x%x\r\n", msg->part->header->flags); + g_string_append_printf(str, "ACK ID: %u\r\n", msg->part->header->ack_id); + g_string_append_printf(str, "SUB ID: %u\r\n", msg->part->header->ack_sub_id); + g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->part->header->ack_size); if (purple_debug_is_verbose() && body != NULL) { @@ -786,17 +655,27 @@ else { int i; - for (i = 0; i < msg->body_len; i++) + int bin_len; + + if (msg->part->footer->value == P2P_APPID_SESION) + bin_len = P2P_PACKET_HEADER_SIZE; + else + bin_len = body_len; + + for (i = 0; i < bin_len; i++) { g_string_append_printf(str, "%.2hhX ", body[i]); if ((i % 16) == 15) g_string_append(str, "\r\n"); } + + if (bin_len == P2P_PACKET_HEADER_SIZE) + g_string_append_printf(str, "%s ", body + P2P_PACKET_HEADER_SIZE); g_string_append(str, "\r\n"); } } - g_string_append_printf(str, "Footer: %u\r\n", msg->msnslp_footer.value); + g_string_append_printf(str, "Footer: 0x%08X\r\n", msg->part->footer->value); } else { @@ -844,13 +723,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; @@ -898,8 +777,9 @@ swboard->flag |= MSN_SB_FLAG_IM; } } - else + else if (!g_str_equal(passport, purple_account_get_username(gc->account))) { + /* Don't im ourselves ... */ serv_got_im(gc, passport, body_final, 0, time(NULL)); if (swboard->conv == NULL) { @@ -925,7 +805,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) { @@ -1046,6 +926,145 @@ } void +msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + MsnSession *session; + MsnSlpLink *slplink; + + session = cmdproc->servconn->session; + slplink = msn_session_get_slplink(session, msg->remote_user); + + if (slplink->swboard == NULL) + { + /* + * We will need swboard in order to change its flags. If its + * NULL, something has probably gone wrong earlier on. I + * didn't want to do this, but MSN 7 is somehow causing us + * to crash here, I couldn't reproduce it to debug more, + * and people are reporting bugs. Hopefully this doesn't + * cause more crashes. Stu. + */ + if (cmdproc->data == NULL) + g_warning("msn_p2p_msg cmdproc->data was NULL\n"); + else { + slplink->swboard = (MsnSwitchBoard *)cmdproc->data; + slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); + } + } + + if (msg->part) { + msn_slplink_process_msg(slplink, msg->part); + } + else /* This should never happen. */ + purple_debug_fatal("msn", "P2P message without a Part.\n"); +} + +static void +got_emoticon(MsnSlpCall *slpcall, + const guchar *data, gsize size) +{ + PurpleConversation *conv; + MsnSwitchBoard *swboard; + + swboard = slpcall->slplink->swboard; + conv = swboard->conv; + + if (conv) { + /* FIXME: it would be better if we wrote the data as we received it + instead of all at once, calling write multiple times and + close once at the very end + */ + purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size); + purple_conv_custom_smiley_close(conv, slpcall->data_info ); + } + if (purple_debug_is_verbose()) + purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); +} + +void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +{ + MsnSession *session; + MsnSlpLink *slplink; + MsnSwitchBoard *swboard; + MsnObject *obj; + char **tokens; + char *smile, *body_str; + const char *body, *who, *sha1; + guint tok; + size_t body_len; + + PurpleConversation *conv; + + session = cmdproc->servconn->session; + + if (!purple_account_get_bool(session->account, "custom_smileys", TRUE)) + return; + + swboard = cmdproc->data; + conv = swboard->conv; + + body = msn_message_get_bin_data(msg, &body_len); + if (!body || !body_len) + return; + body_str = g_strndup(body, body_len); + + /* MSN Messenger 7 may send more than one MSNObject in a single message... + * Maybe 10 tokens is a reasonable max value. */ + tokens = g_strsplit(body_str, "\t", 10); + + g_free(body_str); + + for (tok = 0; tok < 9; tok += 2) { + if (tokens[tok] == NULL || tokens[tok + 1] == NULL) { + break; + } + + smile = tokens[tok]; + obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1])); + + if (obj == NULL) + break; + + who = msn_object_get_creator(obj); + sha1 = msn_object_get_sha1(obj); + + slplink = msn_session_get_slplink(session, who); + if (slplink->swboard != swboard) { + if (slplink->swboard != NULL) + /* + * Apparently we're using a different switchboard now or + * something? I don't know if this is normal, but it + * definitely happens. So make sure the old switchboard + * doesn't still have a reference to us. + */ + slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); + slplink->swboard = swboard; + slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); + } + + /* If the conversation doesn't exist then this is a custom smiley + * used in the first message in a MSN conversation: we need to create + * the conversation now, otherwise the custom smiley won't be shown. + * This happens because every GtkIMHtml has its own smiley tree: if + * the conversation doesn't exist then we cannot associate the new + * smiley with its GtkIMHtml widget. */ + if (!conv) { + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who); + } + + if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) { + msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj); + } + + msn_object_destroy(obj); + obj = NULL; + who = NULL; + sha1 = NULL; + } + g_strfreev(tokens); +} + +void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { GHashTable *body;
--- a/libpurple/protocols/msn/msg.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/msg.h Wed Nov 10 11:41:51 2010 +0900 @@ -58,6 +58,8 @@ #include "session.h" #include "transaction.h" #include "user.h" +#include "slpmsg.h" +#include "slpmsg_part.h" typedef void (*MsnMsgCb)(MsnMessage *, void *data); @@ -67,24 +69,6 @@ #define MSG_OIM_BODY_DEM "\n\n" #define MSG_OIM_LINE_DEM "\n" -typedef struct -{ - guint32 session_id; - guint32 id; - guint64 offset; - guint64 total_size; - guint32 length; - guint32 flags; - guint32 ack_id; - guint32 ack_sub_id; - guint64 ack_size; -} MsnSlpHeader; - -typedef struct -{ - guint32 value; -} MsnSlpFooter; - /** * A message. */ @@ -95,6 +79,8 @@ MsnMsgType type; gboolean msnslp_message; + MsnSlpMessage *slpmsg; + MsnSlpMessagePart *part; char *remote_user; char flag; @@ -106,11 +92,8 @@ guint total_chunks; /**< How many chunks in this multi-part message */ guint received_chunks; /**< How many chunks we've received so far */ - 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 +278,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. @@ -326,9 +309,6 @@ void msn_message_show_readable(MsnMessage *msg, const char *info, gboolean text_body); -void msn_message_parse_slp_body(MsnMessage *msg, const char *body, - size_t len); - char *msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size); char *msn_message_to_string(MsnMessage *msg); @@ -337,8 +317,32 @@ void msn_control_msg(MsnCmdProc *cmdproc, MsnMessage *msg); +/** + * Processes peer to peer messages. + * + * @param cmdproc The command processor. + * @param msg The message. + */ +void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg); + +/** + * Processes emoticon messages. + * + * @param cmdproc The command processor. + * @param msg The message. + */ +void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg); + void msn_datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg); +/** + * Processes INVITE messages. + * + * @param cmdproc The command processor. + * @param msg The message. + */ +void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg); + void msn_handwritten_msg(MsnCmdProc *cmdproc, MsnMessage *msg); #endif /* MSN_MSG_H */
--- a/libpurple/protocols/msn/msn.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/msn.c Wed Nov 10 11:41:51 2010 +0900 @@ -23,7 +23,11 @@ */ #define PHOTO_SUPPORT 1 -#include "msn.h" +#include "internal.h" + +#include "debug.h" +#include "request.h" + #include "accountopt.h" #include "contact.h" #include "msg.h" @@ -40,10 +44,10 @@ #include "msnutils.h" #include "version.h" +#include "error.h" #include "msg.h" #include "switchboard.h" #include "notification.h" -#include "sync.h" #include "slplink.h" #if PHOTO_SUPPORT @@ -116,29 +120,6 @@ return buf; } -gboolean -msn_email_is_valid(const char *passport) -{ - if (purple_email_is_valid(passport)) { - /* Special characters aren't allowed in domains, so only go to '@' */ - while (*passport != '@') { - if (*passport == '/') - return FALSE; - else if (*passport == '?') - return FALSE; - else if (*passport == '=') - return FALSE; - /* MSN also doesn't like colons, but that's checked already */ - - passport++; - } - - return TRUE; - } - - return FALSE; -} - static gboolean msn_send_attention(PurpleConnection *gc, const char *username, guint type) { @@ -267,9 +248,9 @@ { MsnCmdProc *cmdproc; MsnSession *session; + MsnTransaction *trans; PurpleAccount *account; const char *real_alias; - MsnTransaction *trans; struct public_alias_closure *closure; gchar *tmp; gsize dummy; @@ -362,19 +343,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 @@ -480,7 +463,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, @@ -491,6 +474,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_ipc_init(PurplePlugin *plugin) { @@ -675,6 +763,7 @@ PurpleAccount *account; MsnSession *session; MsnCmdProc *cmdproc; + MsnTransaction *trans; account = purple_connection_get_account(gc); session = gc->proto_data; @@ -682,9 +771,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 @@ -728,9 +819,7 @@ static void t_msn_xfer_init(PurpleXfer *xfer) { - MsnSlpLink *slplink = xfer->data; - msn_slplink_request_ft(slplink, xfer); - msn_slplink_unref(slplink); + msn_request_ft(xfer); } static void @@ -1087,6 +1176,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); @@ -1221,6 +1315,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, @@ -1436,7 +1537,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); @@ -1469,7 +1570,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 : ""); @@ -1544,7 +1645,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); @@ -1600,7 +1701,7 @@ MsnUser *user2 = msn_userlist_find_user(userlist, who); if (user2 != NULL) { /* User already in userlist, so just update it. */ - msn_user_destroy(user); + msn_user_unref(user); user = user2; } else { msn_userlist_add_user(userlist, user); @@ -1620,7 +1721,7 @@ /* Remove from local list */ purple_blist_remove_buddy(buddy); - msn_user_destroy(user); + msn_user_unref(user); } g_free(group); } @@ -1951,7 +2052,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) { @@ -1991,6 +2092,7 @@ msn_keepalive(PurpleConnection *gc) { MsnSession *session; + MsnTransaction *trans; session = gc->proto_data; @@ -2000,7 +2102,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); } } @@ -2736,7 +2840,6 @@ { msn_notification_init(); msn_switchboard_init(); - msn_sync_init(); // yaz msn_ipc_init(plugin); @@ -2748,7 +2851,6 @@ { msn_notification_end(); msn_switchboard_end(); - msn_sync_end(); // yaz msn_ipc_end(plugin);
--- a/libpurple/protocols/msn/msn.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/msn.h Wed Nov 10 11:41:51 2010 +0900 @@ -26,16 +26,6 @@ typedef enum { - MSN_LIST_FL_OP = 0x01, - MSN_LIST_AL_OP = 0x02, - MSN_LIST_BL_OP = 0x04, - MSN_LIST_RL_OP = 0x08, - MSN_LIST_PL_OP = 0x10 -} MsnListOp; -#define MSN_LIST_OP_MASK 0x07 - -typedef enum -{ MSN_CLIENT_CAP_WIN_MOBILE = 0x0000001, MSN_CLIENT_CAP_INK_GIF = 0x0000004, MSN_CLIENT_CAP_INK_ISF = 0x0000008, @@ -83,23 +73,7 @@ #include "internal.h" -#include "account.h" -#include "accountopt.h" -#include "blist.h" -#include "connection.h" -#include "conversation.h" -#include "debug.h" -#include "cipher.h" -#include "notify.h" -#include "privacy.h" -#include "proxy.h" -#include "prpl.h" -#include "request.h" -#include "servconn.h" -#include "sslconn.h" -#include "util.h" - -#include "ft.h" +#include "session.h" #include "msg.h" @@ -109,9 +83,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,16 +108,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, PurpleSetPublicAliasSuccessCallback success_cb,
--- a/libpurple/protocols/msn/msnutils.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/msnutils.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,18 +21,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "internal.h" + #include "msn.h" #include "msnutils.h" #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, @@ -476,6 +477,29 @@ *ret_port = port; } +gboolean +msn_email_is_valid(const char *passport) +{ + if (purple_email_is_valid(passport)) { + /* Special characters aren't allowed in domains, so only go to '@' */ + while (*passport != '@') { + if (*passport == '/') + return FALSE; + else if (*passport == '?') + return FALSE; + else if (*passport == '=') + return FALSE; + /* MSN also doesn't like colons, but that's checked already */ + + passport++; + } + + return TRUE; + } + + return FALSE; +} + /*************************************************************************** * MSN Challenge Computing Function ***************************************************************************/
--- a/libpurple/protocols/msn/msnutils.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/msnutils.h Wed Nov 10 11:41:51 2010 +0900 @@ -54,7 +54,32 @@ */ 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); + +/** + * Verify if the email is a vaild passport. + * + * @param passport The email + * + * @return True if it is a valid passport, else FALSE + */ +gboolean msn_email_is_valid(const char *passport); + +/** + * 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 */
--- a/libpurple/protocols/msn/nexus.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/nexus.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,7 +21,11 @@ * 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 "internal.h" +#include "cipher.h" +#include "debug.h" + #include "soap.h" #include "nexus.h" #include "notification.h"
--- a/libpurple/protocols/msn/nexus.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/nexus.h Wed Nov 10 11:41:51 2010 +0900 @@ -24,6 +24,8 @@ #ifndef MSN_NEXUS_H #define MSN_NEXUS_H +#include "internal.h" + typedef struct _MsnNexus MsnNexus; typedef struct _MsnTicketToken MsnTicketToken; typedef struct _MsnUsrKey MsnUsrKey;
--- a/libpurple/protocols/msn/notification.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/notification.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,17 +21,19 @@ * 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 "internal.h" +#include "cipher.h" +#include "core.h" +#include "debug.h" + #include "notification.h" + #include "contact.h" -#include "state.h" #include "error.h" #include "msnutils.h" -#include "page.h" - +#include "state.h" #include "userlist.h" -#include "sync.h" -#include "slplink.h" static MsnTable *cbs_table; @@ -92,6 +94,7 @@ { MsnCmdProc *cmdproc; MsnSession *session; + MsnTransaction *trans; GString *vers; const char *ver_str; int i; @@ -115,7 +118,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,30 +158,36 @@ 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 usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - MsnSession *session; - - session = cmdproc->session; + MsnSession *session = cmdproc->session; if (!g_ascii_strcasecmp(cmd->params[1], "OK")) { @@ -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); } @@ -706,11 +726,8 @@ for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) { - const gchar * domain = NULL; xmlnode *contact_node = NULL; - domain = xmlnode_get_attrib(domain_node, "n"); - for (contact_node = xmlnode_get_child(domain_node, "c"); contact_node; contact_node = xmlnode_get_next_twin(contact_node)) { @@ -995,16 +1012,15 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; - 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; session = cmdproc->session; - account = session->account; state = cmd->params[1]; passport = cmd->params[2]; @@ -1018,7 +1034,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 +1048,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 +1061,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 +1098,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); @@ -1196,22 +1233,24 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; - PurpleAccount *account; MsnUser *user; MsnObject *msnobj; - unsigned long clientid; + unsigned long clientid, extcaps; + char *extcap_str; int networkid; const char *state, *passport, *friendly; session = cmdproc->session; - account = session->account; state = cmd->params[0]; passport = cmd->params[1]; 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 +1268,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); @@ -1379,13 +1424,11 @@ MsnSession *session; MsnSwitchBoard *swboard; const char *session_id; - const char *auth_key; char *host; int port; session = cmdproc->session; session_id = cmd->params[0]; - auth_key = cmd->params[3]; msn_parse_socket(cmd->params[1], &host, &port); @@ -1396,7 +1439,7 @@ msn_switchboard_set_invited(swboard, TRUE); msn_switchboard_set_session_id(swboard, session_id); - msn_switchboard_set_auth_key(swboard, auth_key); + msn_switchboard_set_auth_key(swboard, cmd->params[3]); swboard->im_user = g_strdup(cmd->params[4]); /* msn_switchboard_add_user(swboard, cmd->params[4]); */ @@ -1502,6 +1545,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; + + /* <PrivateEndpointData id='{GUID}'> + <EpName>Endpoint Name</EpName> + <Idle>true/false</Idle> + <ClientType>1</ClientType> + <State>NLN</State> + </PrivateEndpointData> + */ + 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 +1681,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 +1711,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 +1765,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 +1939,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 +2375,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);
--- a/libpurple/protocols/msn/notification.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/notification.h Wed Nov 10 11:41:51 2010 +0900 @@ -49,6 +49,7 @@ #include "servconn.h" #include "state.h" #include "user.h" +#include "userlist.h" struct _MsnNotification { @@ -66,6 +67,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 +97,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. *
--- a/libpurple/protocols/msn/object.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/object.c Wed Nov 10 11:41:51 2010 +0900 @@ -400,7 +400,7 @@ return obj->url1; } -static MsnObject * +MsnObject * msn_object_find_local(const char *sha1) { GList *l;
--- a/libpurple/protocols/msn/object.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/object.h Wed Nov 10 11:41:51 2010 +0900 @@ -269,6 +269,8 @@ */ const char *msn_object_get_url1(const MsnObject *obj); +MsnObject * msn_object_find_local(const char *sha1); + void msn_object_set_local(MsnObject *obj); #endif /* MSN_OBJECT_H */
--- a/libpurple/protocols/msn/oim.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/oim.c Wed Nov 10 11:41:51 2010 +0900 @@ -23,7 +23,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "msn.h" + +#include "internal.h" +#include "debug.h" + #include "soap.h" #include "oim.h" #include "msnutils.h" @@ -618,7 +621,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 +659,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 +674,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 +693,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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/p2p.c Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,76 @@ +#include "internal.h" + +#include "p2p.h" + +MsnP2PHeader * +msn_p2p_header_from_wire(MsnP2PHeader *wire) +{ + MsnP2PHeader *header; + + header = g_new(MsnP2PHeader, 1); + + header->session_id = GUINT32_FROM_LE(wire->session_id); + header->id = GUINT32_FROM_LE(wire->id); + header->offset = GUINT64_FROM_LE(wire->offset); + header->total_size = GUINT64_FROM_LE(wire->total_size); + header->length = GUINT32_FROM_LE(wire->length); + header->flags = GUINT32_FROM_LE(wire->flags); + header->ack_id = GUINT32_FROM_LE(wire->ack_id); + header->ack_sub_id = GUINT32_FROM_LE(wire->ack_sub_id); + header->ack_size = GUINT64_FROM_LE(wire->ack_size); + + return header; +} + +MsnP2PHeader * +msn_p2p_header_to_wire(MsnP2PHeader *header) +{ + MsnP2PHeader *wire; + + wire = g_new(MsnP2PHeader, 1); + + wire->session_id = GUINT32_TO_LE(header->session_id); + wire->id = GUINT32_TO_LE(header->id); + wire->offset = GUINT64_TO_LE(header->offset); + wire->total_size = GUINT64_TO_LE(header->total_size); + wire->length = GUINT32_TO_LE(header->length); + wire->flags = GUINT32_TO_LE(header->flags); + wire->ack_id = GUINT32_TO_LE(header->ack_id); + wire->ack_sub_id = GUINT32_TO_LE(header->ack_sub_id); + wire->ack_size = GUINT64_TO_LE(header->ack_size); + + return wire; + +} + +MsnP2PFooter * +msn_p2p_footer_from_wire(MsnP2PFooter *wire) +{ + MsnP2PFooter *footer; + + footer = g_new(MsnP2PFooter, 1); + + footer->value = GUINT32_FROM_BE(wire->value); + + return footer; +} + +MsnP2PFooter * +msn_p2p_footer_to_wire(MsnP2PFooter *footer) +{ + MsnP2PFooter *wire; + + wire = g_new(MsnP2PFooter, 1); + + wire->value = GUINT32_TO_BE(footer->value); + + return wire; +} + +gboolean +msn_p2p_msg_is_data(const MsnP2PHeaderFlag flags) +{ + return (flags == P2P_MSN_OBJ_DATA || + flags == (P2P_WML2009_COMP | P2P_MSN_OBJ_DATA) || + flags == P2P_FILE_DATA); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/p2p.h Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,88 @@ +#ifndef MSN_P2P_H +#define MSN_P2P_H + + +#pragma pack(push,1) +typedef struct { + guint32 session_id; + guint32 id; + /** + * In a MsnSlpMessage: + * For outgoing messages this is the number of bytes from buffer that + * have already been sent out. For incoming messages this is the + * number of bytes that have been written to buffer. + */ + guint64 offset; + guint64 total_size; + guint32 length; + guint32 flags; + guint32 ack_id; + guint32 ack_sub_id; + guint64 ack_size; +/* guint8 body[1]; */ +} MsnP2PHeader; +#pragma pack(pop) + +#pragma pack(push,1) +typedef struct { + guint8 header_len; + guint8 opcode; + guint16 message_len; + guint32 base_id; +} MsnP2Pv2Header; +#pragma pack(pop) + +typedef struct +{ + guint32 value; +} MsnP2PFooter; + +typedef enum +{ + P2P_NO_FLAG = 0x0, /**< No flags specified */ + P2P_OUT_OF_ORDER = 0x1, /**< Chunk out-of-order */ + P2P_ACK = 0x2, /**< Acknowledgement */ + P2P_PENDING_INVITE = 0x4, /**< There is a pending invite */ + P2P_BINARY_ERROR = 0x8, /**< Error on the binary level */ + P2P_FILE = 0x10, /**< File */ + P2P_MSN_OBJ_DATA = 0x20, /**< MsnObject data */ + P2P_CLOSE = 0x40, /**< Close session */ + P2P_TLP_ERROR = 0x80, /**< Error at transport layer protocol */ + P2P_DC_HANDSHAKE = 0x100, /**< Direct Handshake */ + P2P_WML2009_COMP = 0x1000000, /**< Compatibility with WLM 2009 */ + P2P_FILE_DATA = 0x1000030 /**< File transfer data */ +} MsnP2PHeaderFlag; +/* Info From: + * http://msnpiki.msnfanatic.com/index.php/MSNC:P2Pv1_Headers#Flags + * http://trac.kmess.org/changeset/ba04d0c825769d23370511031c47f6be75fe9b86 + * #7180 + */ + +typedef enum +{ + P2P_APPID_SESION = 0x0, /**< Negotiating session */ + P2P_APPID_OBJ = 0x1, /**< MsnObject (Display or Emoticon) */ + P2P_APPID_FILE = 0x2, /**< File transfer */ + P2P_APPID_EMOTE = 0xB, /**< CustomEmoticon */ + P2P_APPID_DISPLAY = 0xC /**< Display Image */ +} MsnP2PAppId; + +#define P2P_PACKET_HEADER_SIZE sizeof(MsnP2PHeader) +#define P2P_PACKET_FOOTER_SIZE sizeof(MsnP2PFooter) + +MsnP2PHeader * +msn_p2p_header_from_wire(MsnP2PHeader *wire); + +MsnP2PHeader * +msn_p2p_header_to_wire(MsnP2PHeader *header); + +MsnP2PFooter * +msn_p2p_footer_from_wire(MsnP2PFooter *wire); + +MsnP2PFooter * +msn_p2p_footer_to_wire(MsnP2PFooter *footer); + +gboolean +msn_p2p_msg_is_data(const MsnP2PHeaderFlag flags); + +#endif /* MSN_P2P_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/sbconn.c Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,151 @@ +#include "internal.h" +#include "debug.h" + +#include "msg.h" +#include "sbconn.h" + +void msn_sbconn_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part) +{ + MsnMessage *msg; + const char *passport; + char *data; + size_t size; + + msg = msn_message_new_msnslp(); + + passport = purple_normalize(slplink->session->account, slplink->remote_user); + msn_message_set_header(msg, "P2P-Dest", passport); + + data = msn_slpmsgpart_serialize(part, &size); + msg->part = msn_slpmsgpart_ref(part); + + msn_message_set_bin_data(msg, data, size); + + if (slplink->swboard == NULL) + { + slplink->swboard = msn_session_get_swboard(slplink->session, + slplink->remote_user, MSN_SB_FLAG_FT); + + g_return_if_fail(slplink->swboard != NULL); + + /* If swboard is destroyed we will be too */ + slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); + } + + msn_switchboard_send_msg(slplink->swboard, msg, TRUE); +} + +/** Called when a message times out. */ +static void +msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans) +{ + MsnMessage *msg; + + msg = trans->data; + + msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT); +} + +static void +release_msg(MsnSwitchBoard *swboard, MsnMessage *msg) +{ + MsnCmdProc *cmdproc; + MsnTransaction *trans; + char *payload; + gsize payload_len; + char flag; + + g_return_if_fail(swboard != NULL); + g_return_if_fail(msg != NULL); + + cmdproc = swboard->cmdproc; + + payload = msn_message_gen_payload(msg, &payload_len); + + if (purple_debug_is_verbose()) { + purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len); + msn_message_show_readable(msg, "SB SEND", FALSE); + } + + flag = msn_message_get_flag(msg); + trans = msn_transaction_new(cmdproc, "MSG", "%c %" G_GSIZE_FORMAT, + flag, payload_len); + + /* Data for callbacks */ + msn_transaction_set_data(trans, msg); + + if (flag != 'U') { + if (msg->type == MSN_MSG_TEXT) + { + msg->ack_ref = TRUE; + msn_message_ref(msg); + swboard->ack_list = g_list_append(swboard->ack_list, msg); + msn_transaction_set_timeout_cb(trans, msg_timeout); + } + else if (msg->type == MSN_MSG_SLP) + { + msg->ack_ref = TRUE; + msn_message_ref(msg); + swboard->ack_list = g_list_append(swboard->ack_list, msg); + msn_transaction_set_timeout_cb(trans, msg_timeout); +#if 0 + if (msg->ack_cb != NULL) + { + msn_transaction_add_cb(trans, "ACK", msg_ack); + msn_transaction_add_cb(trans, "NAK", msg_nak); + } +#endif + } + } + + trans->payload = payload; + trans->payload_len = payload_len; + + msg->trans = trans; + + msn_cmdproc_send_trans(cmdproc, trans); +} + +static void +queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg) +{ + g_return_if_fail(swboard != NULL); + g_return_if_fail(msg != NULL); + + purple_debug_info("msn", "Appending message to queue.\n"); + + g_queue_push_tail(swboard->msg_queue, msg); + + msn_message_ref(msg); +} + +void +msn_sbconn_process_queue(MsnSwitchBoard *swboard) +{ + MsnMessage *msg; + + g_return_if_fail(swboard != NULL); + + purple_debug_info("msn", "Processing queue\n"); + + while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL) + { + purple_debug_info("msn", "Sending message\n"); + release_msg(swboard, msg); + msn_message_unref(msg); + } +} + +void +msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg, + gboolean queue) +{ + g_return_if_fail(swboard != NULL); + g_return_if_fail(msg != NULL); + + purple_debug_info("msn", "switchboard send msg..\n"); + if (msn_switchboard_can_send(swboard)) + release_msg(swboard, msg); + else if (queue) + queue_msg(swboard, msg); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/sbconn.h Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,17 @@ +#ifndef MSN_SBCONN_H +#define MSN_SBCONN_H + +#include "msg.h" +#include "slplink.h" + +#define MSN_SBCONN_MAX_SIZE 1202 + +void msn_sbconn_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part); + +void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg, + gboolean queue); + +void +msn_sbconn_process_queue(MsnSwitchBoard *swboard); + +#endif /* MSN_SBCONN_H */
--- a/libpurple/protocols/msn/servconn.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/servconn.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,7 +21,9 @@ * 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 "internal.h" +#include "debug.h" + #include "servconn.h" #include "error.h"
--- a/libpurple/protocols/msn/servconn.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/servconn.h Wed Nov 10 11:41:51 2010 +0900 @@ -46,6 +46,7 @@ MSN_SERVCONN_SB } MsnServConnType; +#include "internal.h" #include "proxy.h" #include "cmdproc.h"
--- a/libpurple/protocols/msn/session.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/session.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,13 +21,16 @@ * 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 "internal.h" +#include "debug.h" + +#include "error.h" +#include "msnutils.h" #include "session.h" #include "notification.h" #include "oim.h" -#include "dialog.h" - MsnSession * msn_session_new(PurpleAccount *account) { @@ -45,7 +48,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; } @@ -72,14 +77,11 @@ g_hash_table_destroy(session->soap_table); while (session->slplinks != NULL) - msn_slplink_destroy(session->slplinks->data); + msn_slplink_unref(session->slplinks->data); 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); @@ -87,7 +89,7 @@ msn_nexus_destroy(session->nexus); if (session->user != NULL) - msn_user_destroy(session->user); + msn_user_unref(session->user); if (session->notification != NULL) msn_notification_destroy(session->notification); @@ -95,6 +97,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 +332,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 +486,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); }
--- a/libpurple/protocols/msn/session.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/session.h Wed Nov 10 11:41:51 2010 +0900 @@ -61,15 +61,12 @@ #define MSN_LOGIN_FQY_TIMEOUT 30 -#include "group.h" -#include "httpconn.h" +#define MSN_LOGIN_FQY_TIMEOUT 30 + #include "nexus.h" #include "notification.h" #include "oim.h" -#include "slpcall.h" -#include "sslconn.h" #include "switchboard.h" -#include "sync.h" #include "user.h" #include "userlist.h" @@ -92,7 +89,6 @@ MsnNotification *notification; MsnNexus *nexus; MsnOim *oim; - MsnSync *sync; MsnUserList *userlist; char *abch_cachekey; @@ -122,6 +118,7 @@ GHashTable *soap_table; guint soap_cleanup_handle; + char *guid; GSList *url_datas; /**< PurpleUtilFetchUrlData to be cancelled on exit */ };
--- a/libpurple/protocols/msn/slp.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slp.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,7 +21,10 @@ * 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 "internal.h" +#include "debug.h" + #include "slp.h" #include "slpcall.h" #include "slpmsg.h" @@ -29,16 +32,14 @@ #include "object.h" #include "user.h" -#include "switchboard.h" +#include "sbconn.h" #include "directconn.h" - -#include "smiley.h" +#include "p2p.h" +#include "xfer.h" /* seconds to delay between sending buddy icon requests to the server. */ #define BUDDY_ICON_DELAY 20 -static void request_user_display(MsnUser *user); - typedef struct { MsnSession *session; const char *remote_user; @@ -46,167 +47,6 @@ } MsnFetchUserDisplayData; /************************************************************************** - * Util - **************************************************************************/ - -static char * -get_token(const char *str, const char *start, const char *end) -{ - const char *c, *c2; - - if ((c = strstr(str, start)) == NULL) - return NULL; - - c += strlen(start); - - if (end != NULL) - { - if ((c2 = strstr(c, end)) == NULL) - return NULL; - - return g_strndup(c, c2 - c); - } - else - { - /* This has to be changed */ - return g_strdup(c); - } - -} - -/************************************************************************** - * Xfer - **************************************************************************/ - -static void -msn_xfer_init(PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - /* MsnSlpLink *slplink; */ - char *content; - - purple_debug_info("msn", "xfer_init\n"); - - slpcall = xfer->data; - - /* Send Ok */ - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - msn_slplink_send_queued_slpmsgs(slpcall->slplink); -} - -void -msn_xfer_cancel(PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - char *content; - - g_return_if_fail(xfer != NULL); - g_return_if_fail(xfer->data != NULL); - - slpcall = xfer->data; - - if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) - { - if (slpcall->started) - { - msn_slpcall_close(slpcall); - } - else - { - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - msn_slplink_send_queued_slpmsgs(slpcall->slplink); - - if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) - slpcall->wasted = TRUE; - else - msn_slpcall_destroy(slpcall); - } - } -} - -gssize -msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - - g_return_val_if_fail(xfer != NULL, -1); - g_return_val_if_fail(data != NULL, -1); - g_return_val_if_fail(len > 0, -1); - - g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1); - - slpcall = xfer->data; - /* Not sure I trust it'll be there */ - g_return_val_if_fail(slpcall != NULL, -1); - - g_return_val_if_fail(slpcall->xfer_msg != NULL, -1); - - slpcall->u.outgoing.len = len; - slpcall->u.outgoing.data = data; - msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg); - msn_message_unref(slpcall->xfer_msg->msg); - return MIN(1202, len); -} - -gssize -msn_xfer_read(guchar **data, PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - gsize len; - - g_return_val_if_fail(xfer != NULL, -1); - g_return_val_if_fail(data != NULL, -1); - - g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1); - - slpcall = xfer->data; - /* Not sure I trust it'll be there */ - g_return_val_if_fail(slpcall != NULL, -1); - - /* Just pass up the whole GByteArray. We'll make another. */ - *data = slpcall->u.incoming_data->data; - len = slpcall->u.incoming_data->len; - - g_byte_array_free(slpcall->u.incoming_data, FALSE); - slpcall->u.incoming_data = g_byte_array_new(); - - return len; -} - -void -msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session) -{ - if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) && - (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) && - (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL)) - { - purple_xfer_cancel_remote(slpcall->xfer); - } -} - -void -msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body, - gsize size) -{ - PurpleXfer *xfer = slpcall->xfer; - - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); -} - -/************************************************************************** * SLP Control **************************************************************************/ @@ -250,1037 +90,10 @@ msn_slplink_queue_slpmsg(slplink, slpmsg); } -/* XXX: this could be improved if we tracked custom smileys - * per-protocol, per-account, per-session or (ideally) per-conversation - */ -static PurpleStoredImage * -find_valid_emoticon(PurpleAccount *account, const char *path) -{ - GList *smileys; - - if (!purple_account_get_bool(account, "custom_smileys", TRUE)) - return NULL; - - smileys = purple_smileys_get_all(); - - for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { - PurpleSmiley *smiley; - PurpleStoredImage *img; - - smiley = smileys->data; - img = purple_smiley_get_stored_image(smiley); - - if (purple_strequal(path, purple_imgstore_get_filename(img))) { - g_list_free(smileys); - return img; - } - - purple_imgstore_unref(img); - } - - purple_debug_error("msn", "Received illegal request for file %s\n", path); - return NULL; -} - -static char * -parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype) -{ - char *nonce; - - *ntype = DC_NONCE_UNKNOWN; - - nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); - if (nonce) { - *ntype = DC_NONCE_SHA1; - } else { - guint32 n1, n6; - guint16 n2, n3, n4, n5; - nonce = get_token(content, "Nonce: {", "}\r\n"); - if (nonce - && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x", - &n1, &n2, &n3, &n4, &n5, &n6) == 6) { - *ntype = DC_NONCE_PLAIN; - g_free(nonce); - nonce = g_malloc(16); - *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1); - *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2); - *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3); - *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4); - *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5); - *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6); - } else { - /* Invalid nonce, so ignore request */ - g_free(nonce); - nonce = NULL; - } - } - - return nonce; -} - -static void -msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) -{ - /* A direct connection negotiation response */ - char *bridge; - char *nonce; - char *listening; - MsnDirectConn *dc = slpcall->slplink->dc; - MsnDirectConnNonceType ntype; - - purple_debug_info("msn", "process_transresp\n"); - - /* Direct connections are disabled. */ - if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE)) - return; - - g_return_if_fail(dc != NULL); - g_return_if_fail(dc->state == DC_STATE_CLOSED); - - bridge = get_token(content, "Bridge: ", "\r\n"); - nonce = parse_dc_nonce(content, &ntype); - listening = get_token(content, "Listening: ", "\r\n"); - if (listening && bridge && !strcmp(bridge, "TCPv1")) { - /* Ok, the client supports direct TCP connection */ - - /* We always need this. */ - if (ntype == DC_NONCE_SHA1) { - strncpy(dc->remote_nonce, nonce, 36); - dc->remote_nonce[36] = '\0'; - } - - if (!strcasecmp(listening, "false")) { - if (dc->listen_data != NULL) { - /* - * We'll listen for incoming connections but - * the listening socket isn't ready yet so we cannot - * send the INVITE packet now. Put the slpcall into waiting mode - * and let the callback send the invite. - */ - slpcall->wait_for_socket = TRUE; - - } else if (dc->listenfd != -1) { - /* The listening socket is ready. Send the INVITE here. */ - msn_dc_send_invite(dc); - - } else { - /* We weren't able to create a listener either. Use SB. */ - msn_dc_fallback_to_sb(dc); - } - - } else { - /* - * We should connect to the client so parse - * IP/port from response. - */ - char *ip, *port_str; - int port = 0; - - if (ntype == DC_NONCE_PLAIN) { - /* Only needed for listening side. */ - memcpy(dc->nonce, nonce, 16); - } - - /* Cancel any listen attempts because we don't need them. */ - if (dc->listenfd_handle != 0) { - purple_input_remove(dc->listenfd_handle); - dc->listenfd_handle = 0; - } - if (dc->connect_timeout_handle != 0) { - purple_timeout_remove(dc->connect_timeout_handle); - dc->connect_timeout_handle = 0; - } - if (dc->listenfd != -1) { - purple_network_remove_port_mapping(dc->listenfd); - close(dc->listenfd); - dc->listenfd = -1; - } - if (dc->listen_data != NULL) { - purple_network_listen_cancel(dc->listen_data); - dc->listen_data = NULL; - } - - /* Save external IP/port for later use. We'll try local connection first. */ - dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); - port_str = get_token(content, "IPv4External-Port: ", "\r\n"); - if (port_str) { - dc->ext_port = atoi(port_str); - g_free(port_str); - } - - ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); - port_str = get_token(content, "IPv4Internal-Port: ", "\r\n"); - if (port_str) { - port = atoi(port_str); - g_free(port_str); - } - - if (ip && port) { - /* Try internal address first */ - dc->connect_data = purple_proxy_connect( - NULL, - slpcall->slplink->session->account, - ip, - port, - msn_dc_connected_to_peer_cb, - dc - ); - - if (dc->connect_data) { - /* Add connect timeout handle */ - dc->connect_timeout_handle = purple_timeout_add_seconds( - DC_OUTGOING_TIMEOUT, - msn_dc_outgoing_connection_timeout_cb, - dc - ); - } else { - /* - * Connection failed - * Try external IP/port (if specified) - */ - msn_dc_outgoing_connection_timeout_cb(dc); - } - - } else { - /* - * Omitted or invalid internal IP address / port - * Try external IP/port (if specified) - */ - msn_dc_outgoing_connection_timeout_cb(dc); - } - - g_free(ip); - } - - } else { - /* - * Invalid direct connect invitation or - * TCP connection is not supported - */ - } - - g_free(listening); - g_free(nonce); - g_free(bridge); - - return; -} - -static void -got_sessionreq(MsnSlpCall *slpcall, const char *branch, - const char *euf_guid, const char *context) -{ - gboolean accepted = FALSE; - - if (!strcmp(euf_guid, MSN_OBJ_GUID)) - { - /* Emoticon or UserDisplay */ - char *content; - gsize len; - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - MsnObject *obj; - char *msnobj_data; - PurpleStoredImage *img = NULL; - int type; - - /* Send Ok */ - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - - slplink = slpcall->slplink; - - msnobj_data = (char *)purple_base64_decode(context, &len); - obj = msn_object_new_from_string(msnobj_data); - type = msn_object_get_type(obj); - g_free(msnobj_data); - if (type == MSN_OBJECT_EMOTICON) { - img = find_valid_emoticon(slplink->session->account, obj->location); - } else if (type == MSN_OBJECT_USERTILE) { - img = msn_object_get_image(obj); - if (img) - purple_imgstore_ref(img); - } - msn_object_destroy(obj); - - if (img != NULL) { - /* DATA PREP */ - slpmsg = msn_slpmsg_new(slplink); - slpmsg->slpcall = slpcall; - slpmsg->session_id = slpcall->session_id; - msn_slpmsg_set_body(slpmsg, NULL, 4); - slpmsg->info = "SLP DATA PREP"; - msn_slplink_queue_slpmsg(slplink, slpmsg); - - /* DATA */ - slpmsg = msn_slpmsg_new(slplink); - slpmsg->slpcall = slpcall; - slpmsg->flags = 0x20; - slpmsg->info = "SLP DATA"; - msn_slpmsg_set_image(slpmsg, img); - msn_slplink_queue_slpmsg(slplink, slpmsg); - purple_imgstore_unref(img); - - accepted = TRUE; - - } else { - purple_debug_error("msn", "Wrong object.\n"); - } - } - - else if (!strcmp(euf_guid, MSN_FT_GUID)) - { - /* File Transfer */ - PurpleAccount *account; - PurpleXfer *xfer; - MsnFileContext *header; - gsize bin_len; - guint32 file_size; - char *file_name; - - account = slpcall->slplink->session->account; - - slpcall->end_cb = msn_xfer_end_cb; - slpcall->branch = g_strdup(branch); - - slpcall->pending = TRUE; - - xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, - slpcall->slplink->remote_user); - - header = (MsnFileContext *)purple_base64_decode(context, &bin_len); - if (header != NULL && bin_len >= sizeof(MsnFileContext) - 1 && - (header->version == 2 || - (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { - file_size = GUINT64_FROM_LE(header->file_size); - - file_name = g_convert((const gchar *)&header->file_name, - MAX_FILE_NAME_LEN * 2, - "UTF-8", "UTF-16LE", - NULL, NULL, NULL); - - purple_xfer_set_filename(xfer, file_name ? file_name : ""); - g_free(file_name); - purple_xfer_set_size(xfer, file_size); - purple_xfer_set_init_fnc(xfer, msn_xfer_init); - purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); - purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); - purple_xfer_set_read_fnc(xfer, msn_xfer_read); - purple_xfer_set_write_fnc(xfer, msn_xfer_write); - - slpcall->u.incoming_data = g_byte_array_new(); - - slpcall->xfer = xfer; - purple_xfer_ref(slpcall->xfer); - - xfer->data = slpcall; - - if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) { - purple_xfer_set_thumbnail(xfer, &header->preview, - bin_len - sizeof(MsnFileContext), - "image/png"); - } - - purple_xfer_request(xfer); - } - g_free(header); - - accepted = TRUE; - - } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { - purple_debug_info("msn", "Cam request.\n"); - if (slpcall && slpcall->slplink && - slpcall->slplink->session) { - PurpleConversation *conv; - gchar *from = slpcall->slplink->remote_user; - conv = purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, from, - slpcall->slplink->session->account); - if (conv) { - char *buf; - buf = g_strdup_printf( - _("%s requests to view your " - "webcam, but this request is " - "not yet supported."), from); - purple_conversation_write(conv, NULL, buf, - PURPLE_MESSAGE_SYSTEM | - PURPLE_MESSAGE_NOTIFY, - time(NULL)); - g_free(buf); - } - } - - } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { - purple_debug_info("msn", "Cam invite.\n"); - if (slpcall && slpcall->slplink && - slpcall->slplink->session) { - PurpleConversation *conv; - gchar *from = slpcall->slplink->remote_user; - conv = purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, from, - slpcall->slplink->session->account); - if (conv) { - char *buf; - buf = g_strdup_printf( - _("%s invited you to view his/her webcam, but " - "this is not yet supported."), from); - purple_conversation_write(conv, NULL, buf, - PURPLE_MESSAGE_SYSTEM | - PURPLE_MESSAGE_NOTIFY, - time(NULL)); - g_free(buf); - } - } - - } else - purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); - - if (!accepted) { - char *content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content); - g_free(content); - } -} - -void -send_bye(MsnSlpCall *slpcall, const char *type) -{ - MsnSlpLink *slplink; - PurpleAccount *account; - MsnSlpMessage *slpmsg; - char *header; - - slplink = slpcall->slplink; - - g_return_if_fail(slplink != NULL); - - account = slplink->session->account; - - header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", - purple_account_get_username(account)); - - slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, - "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", - type, - "\r\n"); - g_free(header); - - slpmsg->info = "SLP BYE"; - slpmsg->text_body = TRUE; - - msn_slplink_queue_slpmsg(slplink, slpmsg); -} - -static void -got_invite(MsnSlpCall *slpcall, - const char *branch, const char *type, const char *content) -{ - MsnSlpLink *slplink; - - slplink = slpcall->slplink; - - if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) - { - char *euf_guid, *context; - char *temp; - - euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); - - temp = get_token(content, "SessionID: ", "\r\n"); - if (temp != NULL) - slpcall->session_id = atoi(temp); - g_free(temp); - - temp = get_token(content, "AppID: ", "\r\n"); - if (temp != NULL) - slpcall->app_id = atoi(temp); - g_free(temp); - - context = get_token(content, "Context: ", "\r\n"); - - if (context != NULL) - got_sessionreq(slpcall, branch, euf_guid, context); - - g_free(context); - g_free(euf_guid); - } - else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) - { - /* A direct connection negotiation request */ - char *bridges; - char *nonce; - MsnDirectConnNonceType ntype; - - purple_debug_info("msn", "got_invite: transreqbody received\n"); - - /* Direct connections may be disabled. */ - if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) { - msn_slp_send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", - "Bridge: TCPv1\r\n" - "Listening: false\r\n" - "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" - "\r\n"); - msn_slpcall_session_init(slpcall); - - return; - } - - /* Don't do anything if we already have a direct connection */ - if (slplink->dc != NULL) - return; - - bridges = get_token(content, "Bridges: ", "\r\n"); - nonce = parse_dc_nonce(content, &ntype); - if (bridges && strstr(bridges, "TCPv1") != NULL) { - /* - * Ok, the client supports direct TCP connection - * Try to create a listening port - */ - MsnDirectConn *dc; - - dc = msn_dc_new(slpcall); - if (ntype == DC_NONCE_PLAIN) { - /* There is only one nonce for plain auth. */ - dc->nonce_type = ntype; - memcpy(dc->nonce, nonce, 16); - } else if (ntype == DC_NONCE_SHA1) { - /* Each side has a nonce in SHA1 auth. */ - dc->nonce_type = ntype; - strncpy(dc->remote_nonce, nonce, 36); - dc->remote_nonce[36] = '\0'; - } - - dc->listen_data = purple_network_listen_range( - 0, 0, - SOCK_STREAM, - msn_dc_listen_socket_created_cb, - dc - ); - - if (dc->listen_data == NULL) { - /* Listen socket creation failed */ - - purple_debug_info("msn", "got_invite: listening failed\n"); - - if (dc->nonce_type != DC_NONCE_PLAIN) - msn_slp_send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", - "Bridge: TCPv1\r\n" - "Listening: false\r\n" - "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" - "\r\n"); - else - msn_slp_send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", - "Bridge: TCPv1\r\n" - "Listening: false\r\n" - "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" - "\r\n"); - - } else { - /* - * Listen socket created successfully. - * Don't send anything here because we don't know the parameters - * of the created socket yet. msn_dc_send_ok will be called from - * the callback function: dc_listen_socket_created_cb - */ - purple_debug_info("msn", "got_invite: listening socket created\n"); - - dc->send_connection_info_msg_cb = msn_dc_send_ok; - slpcall->wait_for_socket = TRUE; - } - - } else { - /* - * Invalid direct connect invitation or - * TCP connection is not supported. - */ - } - - g_free(nonce); - g_free(bridges); - } - else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) - { - /* A direct connection negotiation response */ - msn_slp_process_transresp(slpcall, content); - } -} - -static void -got_ok(MsnSlpCall *slpcall, - const char *type, const char *content) -{ - g_return_if_fail(slpcall != NULL); - g_return_if_fail(type != NULL); - - if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) - { - char *content; - char *header; - char *nonce = NULL; - MsnSession *session = slpcall->slplink->session; - MsnSlpMessage *msg; - MsnDirectConn *dc; - MsnUser *user; - - if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) { - /* Don't attempt a direct connection if disabled. */ - msn_slpcall_session_init(slpcall); - return; - } - - if (slpcall->slplink->dc != NULL) { - /* If we already have an established direct connection - * then just start the transfer. - */ - msn_slpcall_session_init(slpcall); - return; - } - - user = msn_userlist_find_user(session->userlist, - slpcall->slplink->remote_user); - if (!user || !(user->clientid & 0xF0000000)) { - /* Just start a normal SB transfer. */ - msn_slpcall_session_init(slpcall); - return; - } - - /* Try direct file transfer by sending a second INVITE */ - dc = msn_dc_new(slpcall); - slpcall->branch = rand_guid(); - - dc->listen_data = purple_network_listen_range( - 0, 0, - SOCK_STREAM, - msn_dc_listen_socket_created_cb, - dc - ); - - header = g_strdup_printf( - "INVITE MSNMSGR:%s MSNSLP/1.0", - slpcall->slplink->remote_user - ); - - if (dc->nonce_type == DC_NONCE_SHA1) - nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash); - - if (dc->listen_data == NULL) { - /* Listen socket creation failed */ - purple_debug_info("msn", "got_ok: listening failed\n"); - - content = g_strdup_printf( - "Bridges: TCPv1\r\n" - "NetID: %u\r\n" - "Conn-Type: IP-Restrict-NAT\r\n" - "UPnPNat: false\r\n" - "ICF: false\r\n" - "%s" - "\r\n", - - rand() % G_MAXUINT32, - nonce ? nonce : "" - ); - - } else { - /* Listen socket created successfully. */ - purple_debug_info("msn", "got_ok: listening socket created\n"); - - content = g_strdup_printf( - "Bridges: TCPv1\r\n" - "NetID: 0\r\n" - "Conn-Type: Direct-Connect\r\n" - "UPnPNat: false\r\n" - "ICF: false\r\n" - "%s" - "\r\n", - - nonce ? nonce : "" - ); - } - - msg = msn_slpmsg_sip_new( - slpcall, - 0, - header, - slpcall->branch, - "application/x-msnmsgr-transreqbody", - content - ); - msg->info = "DC INVITE"; - msg->text_body = TRUE; - g_free(nonce); - g_free(header); - g_free(content); - - msn_slplink_queue_slpmsg(slpcall->slplink, msg); - } - else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) - { - /* Do we get this? */ - purple_debug_info("msn", "OK with transreqbody\n"); - } - else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) - { - msn_slp_process_transresp(slpcall, content); - } -} - -static void -got_error(MsnSlpCall *slpcall, - const char *error, const char *type, const char *content) -{ - /* It's not valid. Kill this off. */ - purple_debug_error("msn", "Received non-OK result: %s\n", - error ? error : "Unknown"); - - if (type && (!strcmp(type, "application/x-msnmsgr-transreqbody") - || !strcmp(type, "application/x-msnmsgr-transrespbody"))) { - MsnDirectConn *dc = slpcall->slplink->dc; - if (dc) { - msn_dc_fallback_to_sb(dc); - return; - } - } - - slpcall->wasted = TRUE; -} - -MsnSlpCall * -msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) -{ - MsnSlpCall *slpcall; - - if (body == NULL) - { - purple_debug_warning("msn", "received bogus message\n"); - return NULL; - } - - if (!strncmp(body, "INVITE", strlen("INVITE"))) - { - char *branch; - char *call_id; - char *content; - char *content_type; - - /* From: <msnmsgr:buddy@hotmail.com> */ -#if 0 - slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); -#endif - - branch = get_token(body, ";branch={", "}"); - - call_id = get_token(body, "Call-ID: {", "}"); - -#if 0 - long content_len = -1; - - temp = get_token(body, "Content-Length: ", "\r\n"); - if (temp != NULL) - content_len = atoi(temp); - g_free(temp); -#endif - content_type = get_token(body, "Content-Type: ", "\r\n"); - - content = get_token(body, "\r\n\r\n", NULL); - - slpcall = NULL; - if (branch && call_id) - { - slpcall = msn_slplink_find_slp_call(slplink, call_id); - if (slpcall) - { - g_free(slpcall->branch); - slpcall->branch = g_strdup(branch); - got_invite(slpcall, branch, content_type, content); - } - else if (content_type && content) - { - slpcall = msn_slpcall_new(slplink); - slpcall->id = g_strdup(call_id); - got_invite(slpcall, branch, content_type, content); - } - } - - g_free(call_id); - g_free(branch); - g_free(content_type); - g_free(content); - } - else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) - { - char *content; - char *content_type; - /* Make sure this is "OK" */ - const char *status = body + strlen("MSNSLP/1.0 "); - char *call_id; - - call_id = get_token(body, "Call-ID: {", "}"); - slpcall = msn_slplink_find_slp_call(slplink, call_id); - g_free(call_id); - - g_return_val_if_fail(slpcall != NULL, NULL); - - content_type = get_token(body, "Content-Type: ", "\r\n"); - - content = get_token(body, "\r\n\r\n", NULL); - - if (strncmp(status, "200 OK", 6)) - { - char *error = NULL; - const char *c; - - /* Eww */ - if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || - (c = strchr(status, '\0'))) - { - size_t len = c - status; - error = g_strndup(status, len); - } - - got_error(slpcall, error, content_type, content); - g_free(error); - - } else { - /* Everything's just dandy */ - got_ok(slpcall, content_type, content); - } - - g_free(content_type); - g_free(content); - } - else if (!strncmp(body, "BYE", strlen("BYE"))) - { - char *call_id; - - call_id = get_token(body, "Call-ID: {", "}"); - slpcall = msn_slplink_find_slp_call(slplink, call_id); - g_free(call_id); - - if (slpcall != NULL) - slpcall->wasted = TRUE; - - /* msn_slpcall_destroy(slpcall); */ - } - else - slpcall = NULL; - - return slpcall; -} - /************************************************************************** * Msg Callbacks **************************************************************************/ -void -msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - MsnSlpLink *slplink; - const char *data; - gsize len; - - session = cmdproc->servconn->session; - slplink = msn_session_get_slplink(session, msg->remote_user); - - if (slplink->swboard == NULL) - { - /* - * We will need swboard in order to change its flags. If its - * NULL, something has probably gone wrong earlier on. I - * didn't want to do this, but MSN 7 is somehow causing us - * to crash here, I couldn't reproduce it to debug more, - * and people are reporting bugs. Hopefully this doesn't - * cause more crashes. Stu. - */ - if (cmdproc->data == NULL) - g_warning("msn_p2p_msg cmdproc->data was NULL\n"); - else { - slplink->swboard = (MsnSwitchBoard *)cmdproc->data; - slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); - } - } - - data = msn_message_get_bin_data(msg, &len); - - msn_slplink_process_msg(slplink, &msg->msnslp_header, data, len); -} - -static void -got_emoticon(MsnSlpCall *slpcall, - const guchar *data, gsize size) -{ - PurpleConversation *conv; - MsnSwitchBoard *swboard; - - swboard = slpcall->slplink->swboard; - conv = swboard->conv; - - if (conv) { - /* FIXME: it would be better if we wrote the data as we received it - instead of all at once, calling write multiple times and - close once at the very end - */ - purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size); - purple_conv_custom_smiley_close(conv, slpcall->data_info ); - } - if (purple_debug_is_verbose()) - purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); -} - -void -msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - MsnSlpLink *slplink; - MsnSwitchBoard *swboard; - MsnObject *obj; - char **tokens; - char *smile, *body_str; - const char *body, *who, *sha1; - guint tok; - size_t body_len; - - PurpleConversation *conv; - - session = cmdproc->servconn->session; - - if (!purple_account_get_bool(session->account, "custom_smileys", TRUE)) - return; - - swboard = cmdproc->data; - conv = swboard->conv; - - body = msn_message_get_bin_data(msg, &body_len); - if (!body || !body_len) - return; - body_str = g_strndup(body, body_len); - - /* MSN Messenger 7 may send more than one MSNObject in a single message... - * Maybe 10 tokens is a reasonable max value. */ - tokens = g_strsplit(body_str, "\t", 10); - - g_free(body_str); - - for (tok = 0; tok < 9; tok += 2) { - if (tokens[tok] == NULL || tokens[tok + 1] == NULL) { - break; - } - - smile = tokens[tok]; - obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1])); - - if (obj == NULL) - break; - - who = msn_object_get_creator(obj); - sha1 = msn_object_get_sha1(obj); - - slplink = msn_session_get_slplink(session, who); - if (slplink->swboard != swboard) { - if (slplink->swboard != NULL) - /* - * Apparently we're using a different switchboard now or - * something? I don't know if this is normal, but it - * definitely happens. So make sure the old switchboard - * doesn't still have a reference to us. - */ - slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); - slplink->swboard = swboard; - slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); - } - - /* If the conversation doesn't exist then this is a custom smiley - * used in the first message in a MSN conversation: we need to create - * the conversation now, otherwise the custom smiley won't be shown. - * This happens because every GtkIMHtml has its own smiley tree: if - * the conversation doesn't exist then we cannot associate the new - * smiley with its GtkIMHtml widget. */ - if (!conv) { - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who); - } - - if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) { - msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj); - } - - msn_object_destroy(obj); - obj = NULL; - who = NULL; - sha1 = NULL; - } - g_strfreev(tokens); -} - -static gboolean -buddy_icon_cached(PurpleConnection *gc, MsnObject *obj) -{ - PurpleAccount *account; - PurpleBuddy *buddy; - const char *old; - const char *new; - - g_return_val_if_fail(obj != NULL, FALSE); - - account = purple_connection_get_account(gc); - - buddy = purple_find_buddy(account, msn_object_get_creator(obj)); - if (buddy == NULL) - return FALSE; - - old = purple_buddy_icons_get_checksum_for_user(buddy); - new = msn_object_get_sha1(obj); - - if (new == NULL) - return FALSE; - - /* If the old and new checksums are the same, and the file actually exists, - * then return TRUE */ - if (old != NULL && !strcmp(old, new)) - return TRUE; - - return FALSE; -} - -static void -msn_release_buddy_icon_request(MsnUserList *userlist) -{ - MsnUser *user; - - g_return_if_fail(userlist != NULL); - - if (purple_debug_is_verbose()) - purple_debug_info("msn", "Releasing buddy icon request\n"); - - if (userlist->buddy_icon_window > 0) - { - GQueue *queue; - - queue = userlist->buddy_icon_requests; - - if (g_queue_is_empty(userlist->buddy_icon_requests)) - return; - - user = g_queue_pop_head(queue); - - userlist->buddy_icon_window--; - request_user_display(user); - - if (purple_debug_is_verbose()) - purple_debug_info("msn", - "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n", - userlist->buddy_icon_window); - } -} - /* * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next * buddy icon request if there is one. @@ -1301,61 +114,22 @@ return FALSE; } -void -msn_queue_buddy_icon_request(MsnUser *user) -{ - PurpleAccount *account; - MsnObject *obj; - GQueue *queue; - - g_return_if_fail(user != NULL); - - account = user->userlist->session->account; - - obj = msn_user_get_object(user); - - if (obj == NULL) - { - purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL); - return; - } - - if (!buddy_icon_cached(account->gc, obj)) - { - MsnUserList *userlist; - - userlist = user->userlist; - queue = userlist->buddy_icon_requests; - - if (purple_debug_is_verbose()) - purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n", - user->passport, userlist->buddy_icon_window); - - g_queue_push_tail(queue, user); - - if (userlist->buddy_icon_window > 0) - msn_release_buddy_icon_request(userlist); - } -} - static void got_user_display(MsnSlpCall *slpcall, const guchar *data, gsize size) { - MsnSlpLink *slplink; const char *info; PurpleAccount *account; g_return_if_fail(slpcall != NULL); - slplink = slpcall->slplink; info = slpcall->data_info; if (purple_debug_is_verbose()) - purple_debug_info("msn", "Got User Display: %s\n", slplink->remote_user); + purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user); - account = slplink->session->account; + account = slpcall->slplink->session->account; - purple_buddy_icons_set_for_user(account, slplink->remote_user, + purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user, g_memdup(data, size), size, info); } @@ -1416,7 +190,43 @@ } static void -request_user_display(MsnUser *user) +request_own_user_display(MsnUser *user) +{ + PurpleAccount *account; + MsnSession *session; + MsnObject *my_obj = NULL; + gconstpointer data = NULL; + const char *info = NULL; + size_t len = 0; + + if (purple_debug_is_verbose()) + purple_debug_info("msn", "Requesting our own user display\n"); + + session = user->userlist->session; + account = session->account; + my_obj = msn_user_get_object(user); + + if (my_obj != NULL) { + PurpleStoredImage *img = msn_object_get_image(my_obj); + data = purple_imgstore_get_data(img); + len = purple_imgstore_get_size(img); + info = msn_object_get_sha1(my_obj); + } + + purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); + + /* Free one window slot */ + session->userlist->buddy_icon_window++; + + if (purple_debug_is_verbose()) + purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n", + session->userlist->buddy_icon_window); + + msn_release_buddy_icon_request(session->userlist); +} + +void +msn_request_user_display(MsnUser *user) { PurpleAccount *account; MsnSession *session; @@ -1452,32 +262,134 @@ } } else - { - MsnObject *my_obj = NULL; - gconstpointer data = NULL; - size_t len = 0; + request_own_user_display(user); +} + +static void +send_file_cb(MsnSlpCall *slpcall) +{ + MsnSlpMessage *slpmsg; + PurpleXfer *xfer; + + xfer = (PurpleXfer *)slpcall->xfer; + if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED) + return; + + purple_xfer_ref(xfer); + purple_xfer_start(xfer, -1, NULL, 0); + if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) { + purple_xfer_unref(xfer); + return; + } + purple_xfer_unref(xfer); + + slpmsg = msn_slpmsg_file_new(slpcall, purple_xfer_get_size(xfer)); + msn_slpmsg_set_slplink(slpmsg, slpcall->slplink); + + msn_slplink_send_slpmsg(slpcall->slplink, slpmsg); +} - if (purple_debug_is_verbose()) - purple_debug_info("msn", "Requesting our own user display\n"); +static gchar * +gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path) +{ + gsize size = 0; + MsnFileContext *header; + gchar *u8 = NULL; + gchar *ret; + gunichar2 *uni = NULL; + glong currentChar = 0; + glong len = 0; + const char *preview; + gsize preview_len; + + size = purple_xfer_get_size(xfer); + + purple_xfer_prepare_thumbnail(xfer, "png"); - my_obj = msn_user_get_object(session->user); + if (!file_name) { + gchar *basename = g_path_get_basename(file_path); + u8 = purple_utf8_try_convert(basename); + g_free(basename); + file_name = u8; + } + + uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL); + + if (u8) { + g_free(u8); + file_name = NULL; + u8 = NULL; + } + + preview = purple_xfer_get_thumbnail(xfer, &preview_len); + header = g_malloc(sizeof(MsnFileContext) + preview_len); - if (my_obj != NULL) - { - PurpleStoredImage *img = msn_object_get_image(my_obj); - data = purple_imgstore_get_data(img); - len = purple_imgstore_get_size(img); - } + header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1); + header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */ + header->file_size = GUINT64_TO_LE(size); + if (preview) + header->type = GUINT32_TO_LE(0); + else + header->type = GUINT32_TO_LE(1); - purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); + len = MIN(len, MAX_FILE_NAME_LEN); + for (currentChar = 0; currentChar < len; currentChar++) { + header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]); + } + memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2); + + memset(&header->unknown1, 0, sizeof(header->unknown1)); + header->unknown2 = GUINT32_TO_LE(0xffffffff); + if (preview) { + memcpy(&header->preview, preview, preview_len); + } + header->preview[preview_len] = '\0'; + + g_free(uni); + ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len); + g_free(header); + return ret; +} - /* Free one window slot */ - session->userlist->buddy_icon_window++; +void +msn_request_ft(PurpleXfer *xfer) +{ + MsnSlpCall *slpcall; + MsnSlpLink *slplink; + char *context; + const char *fn; + const char *fp; + + fn = purple_xfer_get_filename(xfer); + fp = purple_xfer_get_local_filename(xfer); + + slplink = xfer->data; + + g_return_if_fail(slplink != NULL); + g_return_if_fail(fp != NULL); + + slpcall = msn_slpcall_new(slplink); + msn_slpcall_init(slpcall, MSN_SLPCALL_DC); - if (purple_debug_is_verbose()) - purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n", - session->userlist->buddy_icon_window); + slpcall->session_init_cb = send_file_cb; + slpcall->end_cb = msn_xfer_end_cb; + slpcall->cb = msn_xfer_completed_cb; + slpcall->xfer = xfer; + purple_xfer_ref(slpcall->xfer); + + slpcall->pending = TRUE; - msn_release_buddy_icon_request(session->userlist); - } + purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel); + purple_xfer_set_read_fnc(xfer, msn_xfer_read); + purple_xfer_set_write_fnc(xfer, msn_xfer_write); + + xfer->data = slpcall; + + context = gen_context(xfer, fn, fp); + + msn_slpcall_invite(slpcall, MSN_FT_GUID, P2P_APPID_FILE, context); + msn_slplink_unref(slplink); + + g_free(context); } +
--- a/libpurple/protocols/msn/slp.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slp.h Wed Nov 10 11:41:51 2010 +0900 @@ -25,10 +25,12 @@ #define MSN_SLP_H #include "internal.h" +#include "ft.h" -#include "ft.h" #include "session.h" #include "slpcall.h" +#include "slplink.h" +#include "user.h" #define MAX_FILE_NAME_LEN 260 /* MAX_PATH in Windows */ @@ -49,8 +51,6 @@ } MsnFileContext; #pragma pack(pop) -MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink, - const char *body); void msn_slp_send_ok(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content); @@ -62,15 +62,9 @@ void send_bye(MsnSlpCall *slpcall, const char *type); -void msn_xfer_completed_cb(MsnSlpCall *slpcall, - const guchar *body, gsize size); -void msn_xfer_cancel(PurpleXfer *xfer); -gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer); -gssize msn_xfer_read(guchar **data, PurpleXfer *xfer); +void msn_request_user_display(MsnUser *user); -void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session); - -void msn_queue_buddy_icon_request(MsnUser *user); +void msn_request_ft(PurpleXfer *xfer); #endif /* MSN_SLP_H */
--- a/libpurple/protocols/msn/slpcall.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slpcall.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,11 +21,17 @@ * 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 "internal.h" +#include "debug.h" +#include "smiley.h" + #include "msnutils.h" #include "slpcall.h" #include "slp.h" +#include "p2p.h" +#include "xfer.h" /************************************************************************** * Main @@ -140,7 +146,7 @@ void msn_slpcall_invite(MsnSlpCall *slpcall, const char *euf_guid, - int app_id, const char *context) + MsnP2PAppId app_id, const char *context) { MsnSlpLink *slplink; MsnSlpMessage *slpmsg; @@ -190,6 +196,859 @@ msn_slpcall_destroy(slpcall); } +/***************************************************************************** + * Parse received SLP messages + ****************************************************************************/ + +/************************************************************************** + *** Util + **************************************************************************/ + +static char * +get_token(const char *str, const char *start, const char *end) +{ + const char *c, *c2; + + if ((c = strstr(str, start)) == NULL) + return NULL; + + c += strlen(start); + + if (end != NULL) + { + if ((c2 = strstr(c, end)) == NULL) + return NULL; + + return g_strndup(c, c2 - c); + } + else + { + /* This has to be changed */ + return g_strdup(c); + } + +} + +/* XXX: this could be improved if we tracked custom smileys + * per-protocol, per-account, per-session or (ideally) per-conversation + */ +static PurpleStoredImage * +find_valid_emoticon(PurpleAccount *account, const char *path) +{ + GList *smileys; + + if (!purple_account_get_bool(account, "custom_smileys", TRUE)) + return NULL; + + smileys = purple_smileys_get_all(); + + for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { + PurpleSmiley *smiley; + PurpleStoredImage *img; + + smiley = smileys->data; + img = purple_smiley_get_stored_image(smiley); + + if (purple_strequal(path, purple_imgstore_get_filename(img))) { + g_list_free(smileys); + return img; + } + + purple_imgstore_unref(img); + } + + purple_debug_error("msn", "Received illegal request for file %s\n", path); + return NULL; +} + +static char * +parse_dc_nonce(const char *content, MsnDirectConnNonceType *ntype) +{ + char *nonce; + + *ntype = DC_NONCE_UNKNOWN; + + nonce = get_token(content, "Hashed-Nonce: {", "}\r\n"); + if (nonce) { + *ntype = DC_NONCE_SHA1; + } else { + guint32 n1, n6; + guint16 n2, n3, n4, n5; + nonce = get_token(content, "Nonce: {", "}\r\n"); + if (nonce + && sscanf(nonce, "%08x-%04hx-%04hx-%04hx-%04hx%08x", + &n1, &n2, &n3, &n4, &n5, &n6) == 6) { + *ntype = DC_NONCE_PLAIN; + g_free(nonce); + nonce = g_malloc(16); + *(guint32 *)(nonce + 0) = GUINT32_TO_LE(n1); + *(guint16 *)(nonce + 4) = GUINT16_TO_LE(n2); + *(guint16 *)(nonce + 6) = GUINT16_TO_LE(n3); + *(guint16 *)(nonce + 8) = GUINT16_TO_BE(n4); + *(guint16 *)(nonce + 10) = GUINT16_TO_BE(n5); + *(guint32 *)(nonce + 12) = GUINT32_TO_BE(n6); + } else { + /* Invalid nonce, so ignore request */ + g_free(nonce); + nonce = NULL; + } + } + + return nonce; +} + +static void +msn_slp_process_transresp(MsnSlpCall *slpcall, const char *content) +{ + /* A direct connection negotiation response */ + char *bridge; + char *nonce; + char *listening; + MsnDirectConn *dc = slpcall->slplink->dc; + MsnDirectConnNonceType ntype; + + purple_debug_info("msn", "process_transresp\n"); + + /* Direct connections are disabled. */ + if (!purple_account_get_bool(slpcall->slplink->session->account, "direct_connect", TRUE)) + return; + + g_return_if_fail(dc != NULL); + g_return_if_fail(dc->state == DC_STATE_CLOSED); + + bridge = get_token(content, "Bridge: ", "\r\n"); + nonce = parse_dc_nonce(content, &ntype); + listening = get_token(content, "Listening: ", "\r\n"); + if (listening && bridge && !strcmp(bridge, "TCPv1")) { + /* Ok, the client supports direct TCP connection */ + + /* We always need this. */ + if (ntype == DC_NONCE_SHA1) { + strncpy(dc->remote_nonce, nonce, 36); + dc->remote_nonce[36] = '\0'; + } + + if (!strcasecmp(listening, "false")) { + if (dc->listen_data != NULL) { + /* + * We'll listen for incoming connections but + * the listening socket isn't ready yet so we cannot + * send the INVITE packet now. Put the slpcall into waiting mode + * and let the callback send the invite. + */ + slpcall->wait_for_socket = TRUE; + + } else if (dc->listenfd != -1) { + /* The listening socket is ready. Send the INVITE here. */ + msn_dc_send_invite(dc); + + } else { + /* We weren't able to create a listener either. Use SB. */ + msn_dc_fallback_to_sb(dc); + } + + } else { + /* + * We should connect to the client so parse + * IP/port from response. + */ + char *ip, *port_str; + int port = 0; + + if (ntype == DC_NONCE_PLAIN) { + /* Only needed for listening side. */ + memcpy(dc->nonce, nonce, 16); + } + + /* Cancel any listen attempts because we don't need them. */ + if (dc->listenfd_handle != 0) { + purple_input_remove(dc->listenfd_handle); + dc->listenfd_handle = 0; + } + if (dc->connect_timeout_handle != 0) { + purple_timeout_remove(dc->connect_timeout_handle); + dc->connect_timeout_handle = 0; + } + if (dc->listenfd != -1) { + purple_network_remove_port_mapping(dc->listenfd); + close(dc->listenfd); + dc->listenfd = -1; + } + if (dc->listen_data != NULL) { + purple_network_listen_cancel(dc->listen_data); + dc->listen_data = NULL; + } + + /* Save external IP/port for later use. We'll try local connection first. */ + dc->ext_ip = get_token(content, "IPv4External-Addrs: ", "\r\n"); + port_str = get_token(content, "IPv4External-Port: ", "\r\n"); + if (port_str) { + dc->ext_port = atoi(port_str); + g_free(port_str); + } + + ip = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); + port_str = get_token(content, "IPv4Internal-Port: ", "\r\n"); + if (port_str) { + port = atoi(port_str); + g_free(port_str); + } + + if (ip && port) { + /* Try internal address first */ + dc->connect_data = purple_proxy_connect( + NULL, + slpcall->slplink->session->account, + ip, + port, + msn_dc_connected_to_peer_cb, + dc + ); + + if (dc->connect_data) { + /* Add connect timeout handle */ + dc->connect_timeout_handle = purple_timeout_add_seconds( + DC_OUTGOING_TIMEOUT, + msn_dc_outgoing_connection_timeout_cb, + dc + ); + } else { + /* + * Connection failed + * Try external IP/port (if specified) + */ + msn_dc_outgoing_connection_timeout_cb(dc); + } + + } else { + /* + * Omitted or invalid internal IP address / port + * Try external IP/port (if specified) + */ + msn_dc_outgoing_connection_timeout_cb(dc); + } + + g_free(ip); + } + + } else { + /* + * Invalid direct connect invitation or + * TCP connection is not supported + */ + } + + g_free(listening); + g_free(nonce); + g_free(bridge); + + return; +} + +static void +got_sessionreq(MsnSlpCall *slpcall, const char *branch, + const char *euf_guid, const char *context) +{ + gboolean accepted = FALSE; + + if (!strcmp(euf_guid, MSN_OBJ_GUID)) + { + /* Emoticon or UserDisplay */ + char *content; + gsize len; + MsnSlpLink *slplink; + MsnSlpMessage *slpmsg; + MsnObject *obj; + char *msnobj_data; + PurpleStoredImage *img = NULL; + int type; + + /* Send Ok */ + content = g_strdup_printf("SessionID: %lu\r\n\r\n", + slpcall->session_id); + + msn_slp_send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", + content); + + g_free(content); + + slplink = slpcall->slplink; + + msnobj_data = (char *)purple_base64_decode(context, &len); + obj = msn_object_new_from_string(msnobj_data); + type = msn_object_get_type(obj); + g_free(msnobj_data); + if (type == MSN_OBJECT_EMOTICON) { + img = find_valid_emoticon(slplink->session->account, obj->location); + } else if (type == MSN_OBJECT_USERTILE) { + img = msn_object_get_image(obj); + if (img) + purple_imgstore_ref(img); + } + msn_object_destroy(obj); + + if (img != NULL) { + /* DATA PREP */ + slpmsg = msn_slpmsg_dataprep_new(slpcall); + msn_slpmsg_set_slplink(slpmsg, slplink); + msn_slplink_queue_slpmsg(slplink, slpmsg); + + /* DATA */ + slpmsg = msn_slpmsg_obj_new(slpcall, img); + msn_slpmsg_set_slplink(slpmsg, slplink); + msn_slplink_queue_slpmsg(slplink, slpmsg); + purple_imgstore_unref(img); + + accepted = TRUE; + + } else { + purple_debug_error("msn", "Wrong object.\n"); + } + } + + else if (!strcmp(euf_guid, MSN_FT_GUID)) + { + /* File Transfer */ + PurpleAccount *account; + PurpleXfer *xfer; + MsnFileContext *header; + gsize bin_len; + guint32 file_size; + char *file_name; + + account = slpcall->slplink->session->account; + + slpcall->end_cb = msn_xfer_end_cb; + slpcall->branch = g_strdup(branch); + + slpcall->pending = TRUE; + + xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, + slpcall->slplink->remote_user); + + header = (MsnFileContext *)purple_base64_decode(context, &bin_len); + if (header != NULL && bin_len >= sizeof(MsnFileContext) - 1 && + (header->version == 2 || + (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { + file_size = GUINT64_FROM_LE(header->file_size); + + file_name = g_convert((const gchar *)&header->file_name, + MAX_FILE_NAME_LEN * 2, + "UTF-8", "UTF-16LE", + NULL, NULL, NULL); + + purple_xfer_set_filename(xfer, file_name ? file_name : ""); + g_free(file_name); + purple_xfer_set_size(xfer, file_size); + purple_xfer_set_init_fnc(xfer, msn_xfer_init); + purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); + purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); + purple_xfer_set_read_fnc(xfer, msn_xfer_read); + purple_xfer_set_write_fnc(xfer, msn_xfer_write); + + slpcall->u.incoming_data = g_byte_array_new(); + + slpcall->xfer = xfer; + purple_xfer_ref(slpcall->xfer); + + xfer->data = slpcall; + + if (header->type == 0 && bin_len >= sizeof(MsnFileContext)) { + purple_xfer_set_thumbnail(xfer, &header->preview, + bin_len - sizeof(MsnFileContext), + "image/png"); + } + + purple_xfer_request(xfer); + } + g_free(header); + + accepted = TRUE; + + } else if (!strcmp(euf_guid, MSN_CAM_REQUEST_GUID)) { + purple_debug_info("msn", "Cam request.\n"); + if (slpcall->slplink && slpcall->slplink->session) { + PurpleConversation *conv; + gchar *from = slpcall->slplink->remote_user; + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + slpcall->slplink->session->account); + if (conv) { + char *buf; + buf = g_strdup_printf( + _("%s requests to view your " + "webcam, but this request is " + "not yet supported."), from); + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } + + } else if (!strcmp(euf_guid, MSN_CAM_GUID)) { + purple_debug_info("msn", "Cam invite.\n"); + if (slpcall->slplink && slpcall->slplink->session) { + PurpleConversation *conv; + gchar *from = slpcall->slplink->remote_user; + conv = purple_find_conversation_with_account( + PURPLE_CONV_TYPE_IM, from, + slpcall->slplink->session->account); + if (conv) { + char *buf; + buf = g_strdup_printf( + _("%s invited you to view his/her webcam, but " + "this is not yet supported."), from); + purple_conversation_write(conv, NULL, buf, + PURPLE_MESSAGE_SYSTEM | + PURPLE_MESSAGE_NOTIFY, + time(NULL)); + g_free(buf); + } + } + + } else + purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); + + if (!accepted) { + char *content = g_strdup_printf("SessionID: %lu\r\n\r\n", + slpcall->session_id); + msn_slp_send_decline(slpcall, branch, "application/x-msnmsgr-sessionreqbody", content); + g_free(content); + } +} + +void +send_bye(MsnSlpCall *slpcall, const char *type) +{ + MsnSlpLink *slplink; + PurpleAccount *account; + MsnSlpMessage *slpmsg; + char *header; + + slplink = slpcall->slplink; + + g_return_if_fail(slplink != NULL); + + account = slplink->session->account; + + header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", + purple_account_get_username(account)); + + slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, + "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", + type, + "\r\n"); + g_free(header); + + slpmsg->info = "SLP BYE"; + slpmsg->text_body = TRUE; + + msn_slplink_queue_slpmsg(slplink, slpmsg); +} + +static void +got_invite(MsnSlpCall *slpcall, + const char *branch, const char *type, const char *content) +{ + MsnSlpLink *slplink; + + slplink = slpcall->slplink; + + if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) + { + char *euf_guid, *context; + char *temp; + + euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); + + temp = get_token(content, "SessionID: ", "\r\n"); + if (temp != NULL) + slpcall->session_id = atoi(temp); + g_free(temp); + + temp = get_token(content, "AppID: ", "\r\n"); + if (temp != NULL) + slpcall->app_id = atoi(temp); + g_free(temp); + + context = get_token(content, "Context: ", "\r\n"); + + if (context != NULL) + got_sessionreq(slpcall, branch, euf_guid, context); + + g_free(context); + g_free(euf_guid); + } + else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) + { + /* A direct connection negotiation request */ + char *bridges; + char *nonce; + MsnDirectConnNonceType ntype; + + purple_debug_info("msn", "got_invite: transreqbody received\n"); + + /* Direct connections may be disabled. */ + if (!purple_account_get_bool(slplink->session->account, "direct_connect", TRUE)) { + msn_slp_send_ok(slpcall, branch, + "application/x-msnmsgr-transrespbody", + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"); + msn_slpcall_session_init(slpcall); + + return; + } + + /* Don't do anything if we already have a direct connection */ + if (slplink->dc != NULL) + return; + + bridges = get_token(content, "Bridges: ", "\r\n"); + nonce = parse_dc_nonce(content, &ntype); + if (bridges && strstr(bridges, "TCPv1") != NULL) { + /* + * Ok, the client supports direct TCP connection + * Try to create a listening port + */ + MsnDirectConn *dc; + + dc = msn_dc_new(slpcall); + if (ntype == DC_NONCE_PLAIN) { + /* There is only one nonce for plain auth. */ + dc->nonce_type = ntype; + memcpy(dc->nonce, nonce, 16); + } else if (ntype == DC_NONCE_SHA1) { + /* Each side has a nonce in SHA1 auth. */ + dc->nonce_type = ntype; + strncpy(dc->remote_nonce, nonce, 36); + dc->remote_nonce[36] = '\0'; + } + + dc->listen_data = purple_network_listen_range( + 0, 0, + SOCK_STREAM, + msn_dc_listen_socket_created_cb, + dc + ); + + if (dc->listen_data == NULL) { + /* Listen socket creation failed */ + + purple_debug_info("msn", "got_invite: listening failed\n"); + + if (dc->nonce_type != DC_NONCE_PLAIN) + msn_slp_send_ok(slpcall, branch, + "application/x-msnmsgr-transrespbody", + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"); + else + msn_slp_send_ok(slpcall, branch, + "application/x-msnmsgr-transrespbody", + "Bridge: TCPv1\r\n" + "Listening: false\r\n" + "Nonce: {00000000-0000-0000-0000-000000000000}\r\n" + "\r\n"); + + } else { + /* + * Listen socket created successfully. + * Don't send anything here because we don't know the parameters + * of the created socket yet. msn_dc_send_ok will be called from + * the callback function: dc_listen_socket_created_cb + */ + purple_debug_info("msn", "got_invite: listening socket created\n"); + + dc->send_connection_info_msg_cb = msn_dc_send_ok; + slpcall->wait_for_socket = TRUE; + } + + } else { + /* + * Invalid direct connect invitation or + * TCP connection is not supported. + */ + } + + g_free(nonce); + g_free(bridges); + } + else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) + { + /* A direct connection negotiation response */ + msn_slp_process_transresp(slpcall, content); + } +} + +static void +got_ok(MsnSlpCall *slpcall, + const char *type, const char *content) +{ + g_return_if_fail(slpcall != NULL); + g_return_if_fail(type != NULL); + + if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) + { + char *content; + char *header; + char *nonce = NULL; + MsnSession *session = slpcall->slplink->session; + MsnSlpMessage *msg; + MsnDirectConn *dc; + MsnUser *user; + + if (!purple_account_get_bool(session->account, "direct_connect", TRUE)) { + /* Don't attempt a direct connection if disabled. */ + msn_slpcall_session_init(slpcall); + return; + } + + if (slpcall->slplink->dc != NULL) { + /* If we already have an established direct connection + * then just start the transfer. + */ + msn_slpcall_session_init(slpcall); + return; + } + + user = msn_userlist_find_user(session->userlist, + slpcall->slplink->remote_user); + if (!user || !(user->clientid & 0xF0000000)) { + /* Just start a normal SB transfer. */ + msn_slpcall_session_init(slpcall); + return; + } + + /* Try direct file transfer by sending a second INVITE */ + dc = msn_dc_new(slpcall); + slpcall->branch = rand_guid(); + + dc->listen_data = purple_network_listen_range( + 0, 0, + SOCK_STREAM, + msn_dc_listen_socket_created_cb, + dc + ); + + header = g_strdup_printf( + "INVITE MSNMSGR:%s MSNSLP/1.0", + slpcall->slplink->remote_user + ); + + if (dc->nonce_type == DC_NONCE_SHA1) + nonce = g_strdup_printf("Hashed-Nonce: {%s}\r\n", dc->nonce_hash); + + if (dc->listen_data == NULL) { + /* Listen socket creation failed */ + purple_debug_info("msn", "got_ok: listening failed\n"); + + content = g_strdup_printf( + "Bridges: TCPv1\r\n" + "NetID: %u\r\n" + "Conn-Type: IP-Restrict-NAT\r\n" + "UPnPNat: false\r\n" + "ICF: false\r\n" + "%s" + "\r\n", + + rand() % G_MAXUINT32, + nonce ? nonce : "" + ); + + } else { + /* Listen socket created successfully. */ + purple_debug_info("msn", "got_ok: listening socket created\n"); + + content = g_strdup_printf( + "Bridges: TCPv1\r\n" + "NetID: 0\r\n" + "Conn-Type: Direct-Connect\r\n" + "UPnPNat: false\r\n" + "ICF: false\r\n" + "%s" + "\r\n", + + nonce ? nonce : "" + ); + } + + msg = msn_slpmsg_sip_new( + slpcall, + 0, + header, + slpcall->branch, + "application/x-msnmsgr-transreqbody", + content + ); + msg->info = "DC INVITE"; + msg->text_body = TRUE; + g_free(nonce); + g_free(header); + g_free(content); + + msn_slplink_queue_slpmsg(slpcall->slplink, msg); + } + else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) + { + /* Do we get this? */ + purple_debug_info("msn", "OK with transreqbody\n"); + } + else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) + { + msn_slp_process_transresp(slpcall, content); + } +} + +static void +got_error(MsnSlpCall *slpcall, + const char *error, const char *type, const char *content) +{ + /* It's not valid. Kill this off. */ + purple_debug_error("msn", "Received non-OK result: %s\n", + error ? error : "Unknown"); + + if (type && !strcmp(type, "application/x-msnmsgr-transreqbody")) { + MsnDirectConn *dc = slpcall->slplink->dc; + if (dc) { + msn_dc_fallback_to_sb(dc); + return; + } + } + + slpcall->wasted = TRUE; +} + +static MsnSlpCall * +msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) +{ + MsnSlpCall *slpcall; + + if (body == NULL) + { + purple_debug_warning("msn", "received bogus message\n"); + return NULL; + } + + if (!strncmp(body, "INVITE", strlen("INVITE"))) + { + /* This is an INVITE request */ + char *branch; + char *call_id; + char *content; + char *content_type; + + /* From: <msnmsgr:buddy@hotmail.com> */ +#if 0 + slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); +#endif + + branch = get_token(body, ";branch={", "}"); + + call_id = get_token(body, "Call-ID: {", "}"); + +#if 0 + long content_len = -1; + + temp = get_token(body, "Content-Length: ", "\r\n"); + if (temp != NULL) + content_len = atoi(temp); + g_free(temp); +#endif + content_type = get_token(body, "Content-Type: ", "\r\n"); + + content = get_token(body, "\r\n\r\n", NULL); + + slpcall = NULL; + if (branch && call_id) + { + slpcall = msn_slplink_find_slp_call(slplink, call_id); + if (slpcall) + { + g_free(slpcall->branch); + slpcall->branch = g_strdup(branch); + got_invite(slpcall, branch, content_type, content); + } + else if (content_type && content) + { + slpcall = msn_slpcall_new(slplink); + slpcall->id = g_strdup(call_id); + got_invite(slpcall, branch, content_type, content); + } + } + + g_free(call_id); + g_free(branch); + g_free(content_type); + g_free(content); + } + 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" */ + const char *status = body + strlen("MSNSLP/1.0 "); + char *call_id; + + call_id = get_token(body, "Call-ID: {", "}"); + slpcall = msn_slplink_find_slp_call(slplink, call_id); + g_free(call_id); + + g_return_val_if_fail(slpcall != NULL, NULL); + + content_type = get_token(body, "Content-Type: ", "\r\n"); + + content = get_token(body, "\r\n\r\n", NULL); + + if (strncmp(status, "200 OK", 6)) + { + char *error = NULL; + const char *c; + + /* Eww */ + if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || + (c = strchr(status, '\0'))) + { + size_t len = c - status; + error = g_strndup(status, len); + } + + got_error(slpcall, error, content_type, content); + g_free(error); + + } else { + /* Everything's just dandy */ + got_ok(slpcall, content_type, content); + } + + g_free(content_type); + g_free(content); + } + else if (!strncmp(body, "BYE", strlen("BYE"))) + { + /* This is a BYE request */ + char *call_id; + + call_id = get_token(body, "Call-ID: {", "}"); + slpcall = msn_slplink_find_slp_call(slplink, call_id); + g_free(call_id); + + if (slpcall != NULL) + slpcall->wasted = TRUE; + + /* msn_slpcall_destroy(slpcall); */ + } + else + slpcall = NULL; + + return slpcall; +} + MsnSlpCall * msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { @@ -199,13 +1058,13 @@ slpcall = NULL; body = slpmsg->buffer; - body_len = slpmsg->offset; + body_len = slpmsg->header->offset; - if (slpmsg->flags == 0x0 || slpmsg->flags == 0x1000000) + if (slpmsg->header->flags == P2P_NO_FLAG || slpmsg->header->flags == P2P_WML2009_COMP) { char *body_str; - if (slpmsg->session_id == 64) + if (slpmsg->header->session_id == 64) { /* This is for handwritten messages (Ink) */ GError *error = NULL; @@ -262,11 +1121,9 @@ } g_free(body_str); } - else if (slpmsg->flags == 0x20 || - slpmsg->flags == 0x1000020 || - slpmsg->flags == 0x1000030) + else if (msn_p2p_msg_is_data(slpmsg->header->flags)) { - slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); + slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->header->session_id); if (slpcall != NULL) { @@ -282,7 +1139,7 @@ } } #if 0 - else if (slpmsg->flags == 0x100) + else if (slpmsg->header->flags == 0x100) { slpcall = slplink->directconn->initial_call; @@ -290,13 +1147,13 @@ msn_slpcall_session_init(slpcall); } #endif - else if (slpmsg->flags == 0x2) + else if (slpmsg->header->flags == P2P_ACK) { /* Acknowledgement of previous message. Don't do anything currently. */ } else - purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%08lx\n", - slpmsg->flags); + purple_debug_warning("msn", "Unprocessed SLP message with flags 0x%04x\n", + slpmsg->header->flags); return slpcall; }
--- a/libpurple/protocols/msn/slpcall.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slpcall.h Wed Nov 10 11:41:51 2010 +0900 @@ -34,8 +34,6 @@ #include "internal.h" -#include "ft.h" - #include "slplink.h" /* The official client seems to timeout slp calls after 5 minutes */ @@ -94,7 +92,7 @@ void msn_slpcall_session_init(MsnSlpCall *slpcall); void msn_slpcall_destroy(MsnSlpCall *slpcall); void msn_slpcall_invite(MsnSlpCall *slpcall, const char *euf_guid, - int app_id, const char *context); + MsnP2PAppId app_id, const char *context); void msn_slpcall_close(MsnSlpCall *slpcall); #endif /* MSN_SLPCALL_H */
--- a/libpurple/protocols/msn/slplink.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slplink.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,30 +21,37 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "internal.h" +#include "debug.h" + #include "msn.h" #include "slplink.h" +#include "slpmsg_part.h" +#include "sbconn.h" #include "switchboard.h" #include "slp.h" +#include "p2p.h" #ifdef MSN_DEBUG_SLP_FILES static int m_sc = 0; static int m_rc = 0; static void -debug_msg_to_file(MsnMessage *msg, gboolean send) +debug_part_to_file(MsnSlpMessage *msg, gboolean send) { char *tmp; char *dir; - char *pload; + char *data; int c; - gsize pload_size; + gsize data_size; dir = send ? "send" : "recv"; c = send ? m_sc++ : m_rc++; tmp = g_strdup_printf("%s/msntest/%s/%03d", g_get_home_dir(), dir, c); - pload = msn_message_gen_payload(msg, &pload_size); - if (!purple_util_write_data_to_file_absolute(tmp, pload, pload_size)) + data = msn_slpmsg_serialize(msg, &data_size); + if (!purple_util_write_data_to_file_absolute(tmp, data, data_size)) { purple_debug_error("msn", "could not save debug file\n"); } @@ -81,7 +88,7 @@ return msn_slplink_ref(slplink); } -void +static void msn_slplink_destroy(MsnSlpLink *slplink) { MsnSession *session; @@ -264,78 +271,69 @@ return NULL; } -void -msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg) +static void +msn_slplink_send_part(MsnSlpLink *slplink, MsnSlpMessagePart *part) { if (slplink->dc != NULL && slplink->dc->state == DC_STATE_ESTABLISHED) { - msn_dc_enqueue_msg(slplink->dc, msg); + msn_dc_enqueue_part(slplink->dc, part); } else { - if (slplink->swboard == NULL) - { - slplink->swboard = msn_session_get_swboard(slplink->session, - slplink->remote_user, MSN_SB_FLAG_FT); - - g_return_if_fail(slplink->swboard != NULL); - - /* If swboard is destroyed we will be too */ - slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); - } - - msn_switchboard_send_msg(slplink->swboard, msg, TRUE); + msn_sbconn_send_part(slplink, part); } } void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { - MsnMessage *msg; + MsnSlpMessagePart *part; long long real_size; size_t len = 0; /* Maybe we will want to create a new msg for this slpmsg instead of * reusing the same one all the time. */ - msg = slpmsg->msg; + part = msn_slpmsgpart_new(slpmsg->header, slpmsg->footer); + part->ack_data = slpmsg; - real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size; + real_size = (slpmsg->header->flags == P2P_ACK) ? 0 : slpmsg->size; - if (slpmsg->offset < real_size) + if (slpmsg->header->offset < real_size) { if (slpmsg->slpcall && slpmsg->slpcall->xfer && purple_xfer_get_type(slpmsg->slpcall->xfer) == PURPLE_XFER_SEND && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED) { - len = MIN(1202, slpmsg->slpcall->u.outgoing.len); - msn_message_set_bin_data(msg, slpmsg->slpcall->u.outgoing.data, len); + len = MIN(MSN_SBCONN_MAX_SIZE, slpmsg->slpcall->u.outgoing.len); + msn_slpmsgpart_set_bin_data(part, slpmsg->slpcall->u.outgoing.data, len); } else { - len = slpmsg->size - slpmsg->offset; + len = slpmsg->size - slpmsg->header->offset; - if (len > 1202) - len = 1202; + if (len > MSN_SBCONN_MAX_SIZE) + len = MSN_SBCONN_MAX_SIZE; - msn_message_set_bin_data(msg, slpmsg->buffer + slpmsg->offset, len); + msn_slpmsgpart_set_bin_data(part, slpmsg->buffer + slpmsg->header->offset, len); } - msg->msnslp_header.offset = slpmsg->offset; - msg->msnslp_header.length = len; + slpmsg->header->length = len; } +#if 0 + /* TODO: port this function to SlpMessageParts */ if (purple_debug_is_verbose()) msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body); - -#ifdef MSN_DEBUG_SLP_FILES - debug_msg_to_file(msg, TRUE); #endif - slpmsg->msgs = - g_list_append(slpmsg->msgs, msn_message_ref(msg)); - msn_slplink_send_msg(slplink, msg); +#ifdef MSN_DEBUG_SLP_FILES + debug_part_to_file(slpmsg, TRUE); +#endif - if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 || - slpmsg->flags == 0x1000030) && + slpmsg->parts = g_list_append(slpmsg->parts, part); + msn_slplink_send_part(slplink, part); + + + if (msn_p2p_msg_is_data(slpmsg->header->flags) && (slpmsg->slpcall != NULL)) { slpmsg->slpcall->progress = TRUE; @@ -343,124 +341,39 @@ if (slpmsg->slpcall->progress_cb != NULL) { slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, - len, slpmsg->offset); + len, slpmsg->header->offset); } } /* slpmsg->offset += len; */ } -/* We have received the message ack */ -static void -msg_ack(MsnMessage *msg, void *data) -{ - MsnSlpMessage *slpmsg; - long long real_size; - - slpmsg = data; - - real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size; - - slpmsg->offset += msg->msnslp_header.length; - - slpmsg->msgs = g_list_remove(slpmsg->msgs, msg); - - if (slpmsg->offset < real_size) - { - if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED) - { - slpmsg->slpcall->xfer_msg = slpmsg; - msn_message_ref(msg); - purple_xfer_prpl_ready(slpmsg->slpcall->xfer); - } - else - msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); - } - else - { - /* The whole message has been sent */ - if (slpmsg->flags == 0x20 || - slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) - { - if (slpmsg->slpcall != NULL) - { - if (slpmsg->slpcall->cb) - slpmsg->slpcall->cb(slpmsg->slpcall, - NULL, 0); - } - } - } - - msn_message_unref(msg); -} - -/* We have received the message nak. */ -static void -msg_nak(MsnMessage *msg, void *data) -{ - MsnSlpMessage *slpmsg; - - slpmsg = data; - - msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); - - slpmsg->msgs = g_list_remove(slpmsg->msgs, msg); - msn_message_unref(msg); -} - static void msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) { - MsnMessage *msg; - const char *passport; - - slpmsg->msg = msg = msn_message_new_msnslp(); + slpmsg = slpmsg; + slpmsg->footer = g_new0(MsnP2PFooter, 1); - if (slpmsg->flags == 0x0) + if (slpmsg->header->flags == P2P_NO_FLAG) { - msg->msnslp_header.session_id = slpmsg->session_id; - msg->msnslp_header.ack_id = rand() % 0xFFFFFF00; + slpmsg->header->ack_id = rand() % 0xFFFFFF00; } - else if (slpmsg->flags == 0x2) - { - msg->msnslp_header.session_id = slpmsg->session_id; - msg->msnslp_header.ack_id = slpmsg->ack_id; - msg->msnslp_header.ack_size = slpmsg->ack_size; - msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id; - } - else if (slpmsg->flags == 0x20 || - slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) + else if (msn_p2p_msg_is_data(slpmsg->header->flags)) { MsnSlpCall *slpcall; slpcall = slpmsg->slpcall; g_return_if_fail(slpcall != NULL); - msg->msnslp_header.session_id = slpcall->session_id; - msg->msnslp_footer.value = slpcall->app_id; - msg->msnslp_header.ack_id = rand() % 0xFFFFFF00; - } - else if (slpmsg->flags == 0x100) - { - msg->msnslp_header.ack_id = slpmsg->ack_id; - msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id; - msg->msnslp_header.ack_size = slpmsg->ack_size; + slpmsg->header->session_id = slpcall->session_id; + slpmsg->footer->value = slpcall->app_id; + slpmsg->header->ack_id = rand() % 0xFFFFFF00; } - msg->msnslp_header.id = slpmsg->id; - msg->msnslp_header.flags = slpmsg->flags; - - msg->msnslp_header.total_size = slpmsg->size; + slpmsg->header->id = slpmsg->id; - passport = purple_normalize(slplink->session->account, slplink->remote_user); - msn_message_set_attr(msg, "P2P-Dest", passport); - - msg->ack_cb = msg_ack; - msg->nak_cb = msg_nak; - msg->ack_data = slpmsg; + slpmsg->header->total_size = slpmsg->size; msn_slplink_send_msgpart(slplink, slpmsg); - - msn_message_destroy(msg); } void @@ -494,25 +407,18 @@ } static MsnSlpMessage * -msn_slplink_create_ack(MsnSlpLink *slplink, MsnSlpHeader *header) +msn_slplink_create_ack(MsnSlpLink *slplink, MsnP2PHeader *header) { MsnSlpMessage *slpmsg; - slpmsg = msn_slpmsg_new(slplink); - - slpmsg->session_id = header->session_id; - slpmsg->size = header->total_size; - slpmsg->flags = 0x02; - slpmsg->ack_id = header->id; - slpmsg->ack_sub_id = header->ack_id; - slpmsg->ack_size = header->total_size; - slpmsg->info = "SLP ACK"; + slpmsg = msn_slpmsg_ack_new(header); + msn_slpmsg_set_slplink(slpmsg, slplink); return slpmsg; } static void -msn_slplink_send_ack(MsnSlpLink *slplink, MsnSlpHeader *header) +msn_slplink_send_ack(MsnSlpLink *slplink, MsnP2PHeader *header) { MsnSlpMessage *slpmsg = msn_slplink_create_ack(slplink, header); @@ -520,33 +426,6 @@ msn_slpmsg_destroy(slpmsg); } -static void -send_file_cb(MsnSlpCall *slpcall) -{ - MsnSlpMessage *slpmsg; - PurpleXfer *xfer; - - xfer = (PurpleXfer *)slpcall->xfer; - if (purple_xfer_get_status(xfer) >= PURPLE_XFER_STATUS_STARTED) - return; - - purple_xfer_ref(xfer); - purple_xfer_start(xfer, -1, NULL, 0); - if (purple_xfer_get_status(xfer) != PURPLE_XFER_STATUS_STARTED) { - purple_xfer_unref(xfer); - return; - } - purple_xfer_unref(xfer); - - slpmsg = msn_slpmsg_new(slpcall->slplink); - slpmsg->slpcall = slpcall; - slpmsg->flags = 0x1000030; - slpmsg->info = "SLP FILE"; - slpmsg->size = purple_xfer_get_size(xfer); - - msn_slplink_send_slpmsg(slpcall->slplink, slpmsg); -} - static MsnSlpMessage * msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id) { @@ -556,79 +435,164 @@ { MsnSlpMessage *slpmsg = e->data; - if ((slpmsg->session_id == session_id) && (slpmsg->id == id)) + if ((slpmsg->header->session_id == session_id) && (slpmsg->id == id)) return slpmsg; } return NULL; } -void -msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len) +static MsnSlpMessage * +init_first_msg(MsnSlpLink *slplink, MsnP2PHeader *header) { MsnSlpMessage *slpmsg; + + slpmsg = msn_slpmsg_new(slplink); + slpmsg->id = header->id; + slpmsg->header->session_id = header->session_id; + slpmsg->size = header->total_size; + slpmsg->header->flags = header->flags; + + if (slpmsg->header->session_id) + { + slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->header->session_id); + if (slpmsg->slpcall != NULL) + { + if (msn_p2p_msg_is_data(header->flags)) + { + PurpleXfer *xfer = slpmsg->slpcall->xfer; + if (xfer != NULL) + { + slpmsg->ft = TRUE; + slpmsg->slpcall->xfer_msg = slpmsg; + + purple_xfer_ref(xfer); + purple_xfer_start(xfer, -1, NULL, 0); + + if (xfer->data == NULL) { + purple_xfer_unref(xfer); + msn_slpmsg_destroy(slpmsg); + g_return_val_if_reached(NULL); + } else { + purple_xfer_unref(xfer); + } + } + } + } + } + if (!slpmsg->ft && slpmsg->size) + { + slpmsg->buffer = g_try_malloc(slpmsg->size); + if (slpmsg->buffer == NULL) + { + purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n"); + msn_slpmsg_destroy(slpmsg); + return NULL; + } + } + + return slpmsg; +} + +static void +process_complete_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg, MsnP2PHeader *header) +{ + MsnSlpCall *slpcall; + + slpcall = msn_slp_process_msg(slplink, slpmsg); + + if (slpcall == NULL) { + msn_slpmsg_destroy(slpmsg); + return; + } + + purple_debug_info("msn", "msn_slplink_process_msg: slpmsg complete\n"); + + if (/* !slpcall->wasted && */ slpmsg->header->flags == P2P_DC_HANDSHAKE) + { +#if 0 + MsnDirectConn *directconn; + + directconn = slplink->directconn; + if (!directconn->acked) + msn_directconn_send_handshake(directconn); +#endif + } + else if (slpmsg->header->flags == P2P_NO_FLAG || slpmsg->header->flags == P2P_WML2009_COMP || + msn_p2p_msg_is_data(slpmsg->header->flags)) + { + /* Release all the messages and send the ACK */ + + if (slpcall->wait_for_socket) { + /* + * Save ack for later because we have to send + * a 200 OK message to the previous direct connect + * invitation before ACK but the listening socket isn't + * created yet. + */ + purple_debug_info("msn", "msn_slplink_process_msg: save ACK\n"); + + slpcall->slplink->dc->prev_ack = msn_slplink_create_ack(slplink, header); + } else if (!slpcall->wasted) { + purple_debug_info("msn", "msn_slplink_process_msg: send ACK\n"); + + msn_slplink_send_ack(slplink, header); + msn_slplink_send_queued_slpmsgs(slplink); + } + } + + msn_slpmsg_destroy(slpmsg); + + if (!slpcall->wait_for_socket && slpcall->wasted) + msn_slpcall_destroy(slpcall); +} + +static void +slpmsg_add_part(MsnSlpMessage *slpmsg, MsnSlpMessagePart *part) +{ + if (slpmsg->ft) { + slpmsg->slpcall->u.incoming_data = + g_byte_array_append(slpmsg->slpcall->u.incoming_data, (const guchar *)part->buffer, part->size); + purple_xfer_prpl_ready(slpmsg->slpcall->xfer); + } + else if (slpmsg->size && slpmsg->buffer) { + if (G_MAXSIZE - part->size < part->header->offset + || (part->header->offset + part->size) > slpmsg->size + || slpmsg->header->offset != part->header->offset) { + purple_debug_error("msn", + "Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n", + slpmsg->size, part->header->offset, part->size); + g_return_if_reached(); + } else { + memcpy(slpmsg->buffer + part->header->offset, part->buffer, part->size); + slpmsg->header->offset += part->size; + } + } +} + +void +msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpMessagePart *part) +{ + MsnSlpMessage *slpmsg; + MsnP2PHeader *header; guint64 offset; - PurpleXfer *xfer = NULL; + + header = part->header; 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; if (offset == 0) - { - slpmsg = msn_slpmsg_new(slplink); - slpmsg->id = header->id; - slpmsg->session_id = header->session_id; - slpmsg->size = header->total_size; - slpmsg->flags = header->flags; - - if (slpmsg->session_id) - { - if (slpmsg->slpcall == NULL) - 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; - if (xfer != NULL) - { - slpmsg->ft = TRUE; - slpmsg->slpcall->xfer_msg = slpmsg; - - purple_xfer_ref(xfer); - purple_xfer_start(xfer, -1, NULL, 0); - - if (xfer->data == NULL) { - purple_xfer_unref(xfer); - msn_slpmsg_destroy(slpmsg); - g_return_if_reached(); - } else { - purple_xfer_unref(xfer); - } - } - } - } - } - if (!slpmsg->ft && slpmsg->size) - { - slpmsg->buffer = g_try_malloc(slpmsg->size); - if (slpmsg->buffer == NULL) - { - purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n"); - msn_slpmsg_destroy(slpmsg); - return; - } - } - } - else - { + slpmsg = init_first_msg(slplink, header); + else { slpmsg = msn_slplink_message_find(slplink, header->session_id, header->id); if (slpmsg == NULL) { @@ -638,29 +602,10 @@ } } - 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); - } - else if (slpmsg->size && slpmsg->buffer) - { - if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size || slpmsg->offset != offset) - { - purple_debug_error("msn", - "Oversized slpmsg - msgsize=%lld offset=%" G_GUINT64_FORMAT " len=%" G_GSIZE_FORMAT "\n", - slpmsg->size, offset, len); - g_return_if_reached(); - } else { - memcpy(slpmsg->buffer + offset, data, len); - slpmsg->offset += len; - } - } + slpmsg_add_part(slpmsg, part); - if ((slpmsg->flags == 0x20 || - slpmsg->flags == 0x1000020 || slpmsg->flags == 0x1000030) && + + if (msn_p2p_msg_is_data(slpmsg->header->flags) && (slpmsg->slpcall != NULL)) { slpmsg->slpcall->progress = TRUE; @@ -668,7 +613,7 @@ if (slpmsg->slpcall->progress_cb != NULL) { slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, - len, offset); + part->size, offset); } } @@ -677,159 +622,9 @@ return; #endif + /* All the pieces of the slpmsg have been received */ if (header->offset + header->length >= header->total_size) - { - /* All the pieces of the slpmsg have been received */ - MsnSlpCall *slpcall; - - slpcall = msn_slp_process_msg(slplink, slpmsg); - - if (slpcall == NULL) { - msn_slpmsg_destroy(slpmsg); - return; - } - - purple_debug_info("msn", "msn_slplink_process_msg: slpmsg complete\n"); - - if (/* !slpcall->wasted && */ slpmsg->flags == 0x100) - { -#if 0 - MsnDirectConn *directconn; - - directconn = slplink->directconn; - if (!directconn->acked) - msn_directconn_send_handshake(directconn); -#endif - } - else if (slpmsg->flags == 0x00 || slpmsg->flags == 0x1000000 || - slpmsg->flags == 0x20 || slpmsg->flags == 0x1000020 || - slpmsg->flags == 0x1000030) - { - /* Release all the messages and send the ACK */ - - if (slpcall->wait_for_socket) { - /* - * Save ack for later because we have to send - * a 200 OK message to the previous direct connect - * invitation before ACK but the listening socket isn't - * created yet. - */ - purple_debug_info("msn", "msn_slplink_process_msg: save ACK\n"); - - slpcall->slplink->dc->prev_ack = msn_slplink_create_ack(slplink, header); - } else if (!slpcall->wasted) { - purple_debug_info("msn", "msn_slplink_process_msg: send ACK\n"); - - msn_slplink_send_ack(slplink, header); - msn_slplink_send_queued_slpmsgs(slplink); - } - } - - msn_slpmsg_destroy(slpmsg); - - if (!slpcall->wait_for_socket && slpcall->wasted) - msn_slpcall_destroy(slpcall); - } -} - -static gchar * -gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path) -{ - gsize size = 0; - MsnFileContext *header; - gchar *u8 = NULL; - gchar *ret; - gunichar2 *uni = NULL; - glong currentChar = 0; - glong len = 0; - const char *preview; - gsize preview_len; - - size = purple_xfer_get_size(xfer); - - purple_xfer_prepare_thumbnail(xfer, "png"); - - if (!file_name) { - gchar *basename = g_path_get_basename(file_path); - u8 = purple_utf8_try_convert(basename); - g_free(basename); - file_name = u8; - } - - uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL); - - if (u8) { - g_free(u8); - file_name = NULL; - u8 = NULL; - } - - preview = purple_xfer_get_thumbnail(xfer, &preview_len); - header = g_malloc(sizeof(MsnFileContext) + preview_len); - - header->length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1); - header->version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */ - header->file_size = GUINT64_TO_LE(size); - if (preview) - header->type = GUINT32_TO_LE(0); - else - header->type = GUINT32_TO_LE(1); - - len = MIN(len, MAX_FILE_NAME_LEN); - for (currentChar = 0; currentChar < len; currentChar++) { - header->file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]); - } - memset(&header->file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2); - - memset(&header->unknown1, 0, sizeof(header->unknown1)); - header->unknown2 = GUINT32_TO_LE(0xffffffff); - if (preview) { - memcpy(&header->preview, preview, preview_len); - } - header->preview[preview_len] = '\0'; - - g_free(uni); - ret = purple_base64_encode((const guchar *)header, sizeof(MsnFileContext) + preview_len); - g_free(header); - return ret; -} - -void -msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - char *context; - const char *fn; - const char *fp; - - fn = purple_xfer_get_filename(xfer); - fp = purple_xfer_get_local_filename(xfer); - - g_return_if_fail(slplink != NULL); - g_return_if_fail(fp != NULL); - - slpcall = msn_slpcall_new(slplink); - msn_slpcall_init(slpcall, MSN_SLPCALL_DC); - - slpcall->session_init_cb = send_file_cb; - slpcall->end_cb = msn_xfer_end_cb; - slpcall->cb = msn_xfer_completed_cb; - slpcall->xfer = xfer; - purple_xfer_ref(slpcall->xfer); - - slpcall->pending = TRUE; - - purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel); - purple_xfer_set_read_fnc(xfer, msn_xfer_read); - purple_xfer_set_write_fnc(xfer, msn_xfer_write); - - xfer->data = slpcall; - - context = gen_context(xfer, fn, fp); - - msn_slpcall_invite(slpcall, MSN_FT_GUID, 2, context); - - g_free(context); + process_complete_msg(slplink, slpmsg, header); } void @@ -857,7 +652,7 @@ slpcall->cb = cb; slpcall->end_cb = end_cb; - msn_slpcall_invite(slpcall, MSN_OBJ_GUID, 1, msnobj_base64); + msn_slpcall_invite(slpcall, MSN_OBJ_GUID, P2P_APPID_OBJ, msnobj_base64); g_free(msnobj_base64); }
--- a/libpurple/protocols/msn/slplink.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slplink.h Wed Nov 10 11:41:51 2010 +0900 @@ -26,8 +26,6 @@ typedef struct _MsnSlpLink MsnSlpLink; -#include "ft.h" - #include "directconn.h" #include "session.h" #include "slpcall.h" @@ -59,8 +57,6 @@ MsnSlpLink *msn_slplink_ref(MsnSlpLink *slplink); void msn_slplink_unref(MsnSlpLink *slplink); -void msn_slplink_destroy(MsnSlpLink *slplink); - /** * @return An MsnSlpLink for the given user, or NULL if there is no * existing MsnSlpLink. @@ -83,10 +79,8 @@ void msn_slplink_send_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg); void msn_slplink_send_queued_slpmsgs(MsnSlpLink *slplink); -void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpHeader *header, const char *data, gsize len); -void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer); +void msn_slplink_process_msg(MsnSlpLink *slplink, MsnSlpMessagePart *part); -void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg); /* Only exported for msn_xfer_write */ void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg);
--- a/libpurple/protocols/msn/slpmsg.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slpmsg.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,8 +21,12 @@ * 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 "internal.h" +#include "debug.h" + #include "slpmsg.h" +#include "slpmsg_part.h" #include "slplink.h" /************************************************************************** @@ -39,10 +43,48 @@ if (purple_debug_is_verbose()) purple_debug_info("msn", "slpmsg new (%p)\n", slpmsg); - slpmsg->slplink = slplink; + if (slplink) + msn_slpmsg_set_slplink(slpmsg, slplink); + else + slpmsg->slplink = NULL; + + slpmsg->header = g_new0(MsnP2PHeader, 1); + slpmsg->footer = NULL; + + return slpmsg; +} + +MsnSlpMessage *msn_slpmsg_new_from_data(const char *data, size_t data_len) +{ + MsnSlpMessage *slpmsg; + MsnP2PHeader *header; + const char *tmp; + int body_len; + + tmp = data; + slpmsg = msn_slpmsg_new(NULL); - slplink->slp_msgs = - g_list_append(slplink->slp_msgs, slpmsg); + if (data_len < sizeof(*header)) { + return NULL; + } + + /* Extract the binary SLP header */ + slpmsg->header = msn_p2p_header_from_wire((MsnP2PHeader*)tmp); + + /* Extract the body */ + body_len = data_len - (tmp - data); + /* msg->body_len = msg->msnslp_header.length; */ + + if (body_len > 0) { + slpmsg->size = body_len; + slpmsg->buffer = g_malloc(body_len); + memcpy(slpmsg->buffer, tmp, body_len); + tmp += body_len; + } + + /* Extract the footer */ + if (body_len >= 0) + slpmsg->footer = msn_p2p_footer_from_wire((MsnP2PFooter*)tmp); return slpmsg; } @@ -67,26 +109,41 @@ if (slpmsg->img == NULL) g_free(slpmsg->buffer); - for (cur = slpmsg->msgs; cur != NULL; cur = g_list_delete_link(cur, cur)) + for (cur = slpmsg->parts; cur != NULL; cur = g_list_delete_link(cur, cur)) { /* Something is pointing to this slpmsg, so we should remove that * pointer to prevent a crash. */ /* Ex: a user goes offline and after that we receive an ACK */ - MsnMessage *msg = cur->data; + MsnSlpMessagePart *part = cur->data; - msg->ack_cb = NULL; - msg->nak_cb = NULL; - msg->ack_data = NULL; - msn_message_unref(msg); + part->ack_cb = NULL; + part->nak_cb = NULL; + part->ack_data = NULL; + msn_slpmsgpart_destroy(part); } slplink->slp_msgs = g_list_remove(slplink->slp_msgs, slpmsg); + g_free(slpmsg->header); + g_free(slpmsg->footer); + g_free(slpmsg); } void +msn_slpmsg_set_slplink(MsnSlpMessage *slpmsg, MsnSlpLink *slplink) +{ + g_return_if_fail(slplink != NULL); + + slpmsg->slplink = slplink; + + slplink->slp_msgs = + g_list_append(slplink->slp_msgs, slpmsg); + +} + +void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body, long long size) { @@ -116,34 +173,6 @@ slpmsg->size = purple_imgstore_get_size(img); } -void -msn_slpmsg_show(MsnMessage *msg) -{ - const char *info; - gboolean text; - guint32 flags; - - text = FALSE; - - flags = GUINT32_TO_LE(msg->msnslp_header.flags); - - switch (flags) - { - case 0x0: - info = "SLP CONTROL"; - text = TRUE; - break; - case 0x2: - info = "SLP ACK"; break; - case 0x20: - case 0x1000030: - info = "SLP DATA"; break; - default: - info = "SLP UNKNOWN"; break; - } - - msn_message_show_readable(msg, info, text); -} MsnSlpMessage * msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq, @@ -206,3 +235,128 @@ return slpmsg; } + +MsnSlpMessage *msn_slpmsg_ack_new(MsnP2PHeader *header) +{ + MsnSlpMessage *slpmsg; + + slpmsg = msn_slpmsg_new(NULL); + + slpmsg->header->session_id = header->session_id; + slpmsg->size = header->total_size; + slpmsg->header->flags = P2P_ACK; + slpmsg->header->ack_id = header->id; + slpmsg->header->ack_sub_id = header->ack_id; + slpmsg->header->ack_size = header->total_size; + slpmsg->info = "SLP ACK"; + + return slpmsg; +} + +MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleStoredImage *img) +{ + MsnSlpMessage *slpmsg; + + slpmsg = msn_slpmsg_new(NULL); + slpmsg->slpcall = slpcall; + slpmsg->header->flags = P2P_MSN_OBJ_DATA; + slpmsg->info = "SLP DATA"; + + msn_slpmsg_set_image(slpmsg, img); + + return slpmsg; +} + +MsnSlpMessage *msn_slpmsg_dataprep_new(MsnSlpCall *slpcall) +{ + MsnSlpMessage *slpmsg; + + slpmsg = msn_slpmsg_new(NULL); + + slpmsg->slpcall = slpcall; + slpmsg->header->session_id = slpcall->session_id; + msn_slpmsg_set_body(slpmsg, NULL, 4); + slpmsg->info = "SLP DATA PREP"; + + return slpmsg; + +} + +MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, size_t size) +{ + MsnSlpMessage *slpmsg; + + slpmsg = msn_slpmsg_new(NULL); + + slpmsg->slpcall = slpcall; + slpmsg->header->flags = P2P_FILE_DATA; + slpmsg->info = "SLP FILE"; + slpmsg->size = size; + + return slpmsg; +} + +char *msn_slpmsg_serialize(MsnSlpMessage *slpmsg, size_t *ret_size) +{ + MsnP2PHeader *header; + MsnP2PFooter *footer; + char *base; + char *tmp; + size_t siz; + + base = g_malloc(P2P_PACKET_HEADER_SIZE + slpmsg->size + sizeof(MsnP2PFooter)); + tmp = base; + + header = msn_p2p_header_to_wire(slpmsg->header); + footer = msn_p2p_footer_to_wire(slpmsg->footer); + + siz = sizeof(MsnP2PHeader); + /* Copy header */ + memcpy(tmp, (char*)header, siz); + tmp += siz; + + /* Copy body */ + memcpy(tmp, slpmsg->buffer, slpmsg->size); + tmp += slpmsg->size; + + /* Copy footer */ + siz = sizeof(MsnP2PFooter); + memcpy(tmp, (char*)footer, siz); + tmp += siz; + + *ret_size = tmp - base; + + return base; +} + +void msn_slpmsg_show_readable(MsnSlpMessage *slpmsg) +{ + GString *str; + + str = g_string_new(NULL); + + g_string_append_printf(str, "Session ID: %u\r\n", slpmsg->header->session_id); + g_string_append_printf(str, "ID: %u\r\n", slpmsg->header->id); + g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", slpmsg->header->offset); + g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", slpmsg->header->total_size); + g_string_append_printf(str, "Length: %u\r\n", slpmsg->header->length); + g_string_append_printf(str, "Flags: 0x%x\r\n", slpmsg->header->flags); + g_string_append_printf(str, "ACK ID: %u\r\n", slpmsg->header->ack_id); + g_string_append_printf(str, "SUB ID: %u\r\n", slpmsg->header->ack_sub_id); + g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", slpmsg->header->ack_size); + + if (purple_debug_is_verbose() && slpmsg->buffer != NULL) { + g_string_append_len(str, (gchar*)slpmsg->buffer, slpmsg->size); + + if (slpmsg->buffer[slpmsg->size - 1] == '\0') { + str->len--; + g_string_append(str, " 0x00"); + } + g_string_append(str, "\r\n"); + + } + + g_string_append_printf(str, "Footer: %u\r\n", slpmsg->footer->value); + + purple_debug_info("msn", "SlpMessage %s:\n{%s}\n", slpmsg->info, str->str); +}
--- a/libpurple/protocols/msn/slpmsg.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/slpmsg.h Wed Nov 10 11:41:51 2010 +0900 @@ -31,7 +31,7 @@ #include "slpcall.h" #include "slplink.h" #include "session.h" -#include "msg.h" +#include "p2p.h" #include "slp.h" @@ -45,37 +45,24 @@ MsnSlpLink *slplink; /**< The slplink through which this slp message is being sent. */ MsnSession *session; - long session_id; + MsnP2PHeader *header; + MsnP2PFooter *footer; + long id; - long ack_id; - long ack_sub_id; - long long ack_size; gboolean sip; /**< A flag that states if this is a SIP slp message. */ - long flags; gboolean ft; PurpleStoredImage *img; guchar *buffer; /** - * For outgoing messages this is the number of bytes from buffer that - * have already been sent out. For incoming messages this is the - * number of bytes that have been written to buffer. - */ - long long offset; - - /** * This is the size of buffer, unless this is an outgoing file transfer, * in which case this is the size of the file. */ long long size; - GList *msgs; /**< The real messages. */ - -#if 1 - MsnMessage *msg; /**< The temporary real message that will be sent. */ -#endif + GList *parts; /**< A list with the SlpMsgParts */ const char *info; gboolean text_body; @@ -85,17 +72,35 @@ * Creates a new slp message * * @param slplink The slplink through which this slp message will be sent. + * If it's set to NULL, it is a temporary SlpMessage. * @return The created slp message. */ MsnSlpMessage *msn_slpmsg_new(MsnSlpLink *slplink); /** + * Creates a MsnSlpMessage without a MsnSlpLink by parsing the raw data. + * + * @param data The raw data with the slp message. + * @param data_len The len of the data + * + * @return The createed slp message. + */ +MsnSlpMessage *msn_slpmsg_new_from_data(const char *data, size_t data_len); + +/** * Destroys a slp message * * @param slpmsg The slp message to destory. */ void msn_slpmsg_destroy(MsnSlpMessage *slpmsg); +/** + * Relate this SlpMessage with an existing SlpLink + * + * @param slplink The SlpLink that will send this message. + */ +void msn_slpmsg_set_slplink(MsnSlpMessage *slpmsg, MsnSlpLink *slplink); + void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body, long long size); void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img); @@ -107,6 +112,54 @@ const char *content_type, const char *content); -void msn_slpmsg_show(MsnMessage *msg); +/** + * Create a new SLP Ack message + * + * @param header the value of the header in this slpmsg. + * + * @return A new SlpMessage with ACK headers + */ +MsnSlpMessage *msn_slpmsg_ack_new(MsnP2PHeader *header); + +/** + * Create a new SLP message for MsnObject data. + * + * @param slpcall The slpcall that manages this message. + * @param img The image to be sent in this message. + * + * @return A new SlpMessage with MsnObject info. + */ +MsnSlpMessage *msn_slpmsg_obj_new(MsnSlpCall *slpcall, PurpleStoredImage *img); + +/** + * Create a new SLP message for data preparation. + * + * @param slpcall The slpcall that manages this message. + * + * @return A new SlpMessage with data preparation info. + */ +MsnSlpMessage *msn_slpmsg_dataprep_new(MsnSlpCall *slpcall); + +/** + * Create a new SLP message for File transfer. + * + * @param slpcall The slpcall that manages this message. + * @param size The size of the file being transsmited. + * + * @return A new SlpMessage with the file transfer info. + */ +MsnSlpMessage *msn_slpmsg_file_new(MsnSlpCall *slpcall, size_t size); + +/** + * Serialize the MsnSlpMessage in a way it can be used to be transmited + * + * @param slpmsg The MsnSlpMessage. + * @param ret_size The size of the buffer cointaining the message. + * + * @return a buffer with the serialized data. + */ +char *msn_slpmsg_serialize(MsnSlpMessage *slpmsg, size_t *ret_size); + +void msn_slpmsg_show_readable(MsnSlpMessage *slpmsg); #endif /* _MSN_SLPMSG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/slpmsg_part.c Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,210 @@ +#include "internal.h" +#include "debug.h" + +#include "slpmsg.h" +#include "slpmsg_part.h" + +MsnSlpMessagePart *msn_slpmsgpart_new(MsnP2PHeader *header, MsnP2PFooter *footer) +{ + MsnSlpMessagePart *part; + + part = g_new0(MsnSlpMessagePart, 1); + + if (header) + part->header = g_memdup(header, P2P_PACKET_HEADER_SIZE); + if (footer) + part->footer = g_memdup(footer, P2P_PACKET_FOOTER_SIZE); + + part->ack_cb = msn_slpmsgpart_ack; + part->nak_cb = msn_slpmsgpart_nak; + + return msn_slpmsgpart_ref(part); +} + +MsnSlpMessagePart *msn_slpmsgpart_new_from_data(const char *data, size_t data_len) +{ + MsnSlpMessagePart *part; + MsnP2PHeader *header; + const char *tmp; + int body_len; + + tmp = data; + part = msn_slpmsgpart_new(NULL, NULL); + + if (data_len < sizeof(*header)) { + return NULL; + } + + /* Extract the binary SLP header */ + part->header = msn_p2p_header_from_wire((MsnP2PHeader*)tmp); + tmp += P2P_PACKET_HEADER_SIZE; + + /* Extract the body */ + body_len = data_len - P2P_PACKET_HEADER_SIZE - P2P_PACKET_FOOTER_SIZE; + /* msg->body_len = msg->msnslp_header.length; */ + + if (body_len > 0) { + part->size = body_len; + part->buffer = g_malloc(body_len); + memcpy(part->buffer, tmp, body_len); + tmp += body_len; + } + + /* Extract the footer */ + if (body_len >= 0) + part->footer = msn_p2p_footer_from_wire((MsnP2PFooter*)tmp); + + return part; +} + +void msn_slpmsgpart_destroy(MsnSlpMessagePart *part) +{ + if (!part) + return; + + if (part->ref_count > 0) { + msn_slpmsgpart_unref(part); + + return; + } + + g_free(part->header); + g_free(part->footer); + + g_free(part); + +} + +MsnSlpMessagePart *msn_slpmsgpart_ref(MsnSlpMessagePart *part) +{ + g_return_val_if_fail(part != NULL, NULL); + part->ref_count ++; + + if (purple_debug_is_verbose()) + purple_debug_info("msn", "part ref (%p)[%d]\n", part, part->ref_count); + + return part; +} + +MsnSlpMessagePart *msn_slpmsgpart_unref(MsnSlpMessagePart *part) +{ + g_return_val_if_fail(part != NULL, NULL); + g_return_val_if_fail(part->ref_count > 0, NULL); + + part->ref_count--; + + if (purple_debug_is_verbose()) + purple_debug_info("msn", "part unref (%p)[%d]\n", part, part->ref_count); + + if (part->ref_count == 0) { + msn_slpmsgpart_destroy(part); + + return NULL; + } + + return part; +} + +void msn_slpmsgpart_set_bin_data(MsnSlpMessagePart *part, const void *data, size_t len) +{ + g_return_if_fail(part != NULL); + + if (part->buffer != NULL) + g_free(part->buffer); + + if (data != NULL && len > 0) { + part->buffer = g_malloc(len + 1); + memcpy(part->buffer, data, len); + part->buffer[len] = '\0'; + part->size = len; + } else { + part->buffer = NULL; + part->size = 0; + } + +} + +char *msn_slpmsgpart_serialize(MsnSlpMessagePart *part, size_t *ret_size) +{ + MsnP2PHeader *header; + MsnP2PFooter *footer; + char *base; + char *tmp; + size_t siz; + + base = g_malloc(P2P_PACKET_HEADER_SIZE + part->size + sizeof(MsnP2PFooter)); + tmp = base; + + header = msn_p2p_header_to_wire(part->header); + footer = msn_p2p_footer_to_wire(part->footer); + + siz = sizeof(MsnP2PHeader); + /* Copy header */ + memcpy(tmp, (char*)header, siz); + tmp += siz; + + /* Copy body */ + memcpy(tmp, part->buffer, part->size); + tmp += part->size; + + /* Copy footer */ + siz = sizeof(MsnP2PFooter); + memcpy(tmp, (char*)footer, siz); + tmp += siz; + + *ret_size = tmp - base; + + return base; +} +/* We have received the message ack */ +void +msn_slpmsgpart_ack(MsnSlpMessagePart *part, void *data) +{ + MsnSlpMessage *slpmsg; + long long real_size; + + slpmsg = data; + + real_size = (slpmsg->header->flags == P2P_ACK) ? 0 : slpmsg->size; + + slpmsg->header->offset += part->header->length; + + slpmsg->parts = g_list_remove(slpmsg->parts, part); + + if (slpmsg->header->offset < real_size) + { + if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED) + { + slpmsg->slpcall->xfer_msg = slpmsg; + purple_xfer_prpl_ready(slpmsg->slpcall->xfer); + } + else + msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); + } + else + { + /* The whole message has been sent */ + if (msn_p2p_msg_is_data(slpmsg->header->flags)) + { + if (slpmsg->slpcall != NULL) + { + if (slpmsg->slpcall->cb) + slpmsg->slpcall->cb(slpmsg->slpcall, + NULL, 0); + } + } + } +} + +/* We have received the message nak. */ +void +msn_slpmsgpart_nak(MsnSlpMessagePart *part, void *data) +{ + MsnSlpMessage *slpmsg; + + slpmsg = data; + + msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); + + slpmsg->parts = g_list_remove(slpmsg->parts, part); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/slpmsg_part.h Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,41 @@ +#ifndef MSN_SLPMSG_PART_H +#define MSN_SLPMSG_PART_H + +#include "p2p.h" + +typedef struct _MsnSlpMessagePart MsnSlpMessagePart; +typedef void (*MsnSlpPartCb)(MsnSlpMessagePart *part, void *data); + +struct _MsnSlpMessagePart +{ + int ref_count; + + MsnP2PHeader *header; + MsnP2PFooter *footer; + + MsnSlpPartCb ack_cb; + MsnSlpPartCb nak_cb; + void *ack_data; + + guchar *buffer; + size_t size; +}; + +MsnSlpMessagePart *msn_slpmsgpart_new(MsnP2PHeader *header, MsnP2PFooter *footer); + +MsnSlpMessagePart *msn_slpmsgpart_new_from_data(const char *data, size_t data_len); + +void msn_slpmsgpart_destroy(MsnSlpMessagePart *part); + +MsnSlpMessagePart *msn_slpmsgpart_ref(MsnSlpMessagePart *part); + +MsnSlpMessagePart *msn_slpmsgpart_unref(MsnSlpMessagePart *part); + +void msn_slpmsgpart_set_bin_data(MsnSlpMessagePart *part, const void *data, size_t len); + +char *msn_slpmsgpart_serialize(MsnSlpMessagePart *part, size_t *ret_size); + +void msn_slpmsgpart_ack(MsnSlpMessagePart *part, void *data); + +void msn_slpmsgpart_nak(MsnSlpMessagePart *part, void *data); +#endif /* MSN_SLPMSG_PART_H */
--- a/libpurple/protocols/msn/state.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/state.c Wed Nov 10 11:41:51 2010 +0900 @@ -23,10 +23,11 @@ */ #include "internal.h" +#include "debug.h" #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 @@ * <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>" */ 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 *
--- a/libpurple/protocols/msn/state.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/state.h Wed Nov 10 11:41:51 2010 +0900 @@ -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);
--- a/libpurple/protocols/msn/switchboard.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/switchboard.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,19 +21,19 @@ * 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 "prefs.h" + +#include "internal.h" +#include "debug.h" + +#include "msnutils.h" #include "switchboard.h" -#include "notification.h" -#include "msnutils.h" - -#include "error.h" +#include "sbconn.h" +#include "slplink.h" +#include "user.h" +#include "userlist.h" static MsnTable *cbs_table; -static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, - MsnMsgErrorType error); - /************************************************************************** * Main **************************************************************************/ @@ -90,9 +90,11 @@ while (swboard->slplinks != NULL) { MsnSlpLink *slplink = swboard->slplinks->data; + swboard->slplinks = g_list_remove(swboard->slplinks, slplink); + /* Destroy only those slplinks which use the switchboard */ if (slplink->dc == NULL) - msn_slplink_destroy(slplink); + msn_slplink_unref(slplink); else { swboard->slplinks = g_list_remove(swboard->slplinks, slplink); slplink->swboard = NULL; @@ -123,7 +125,7 @@ g_free(swboard->session_id); for (; swboard->users; swboard->users = g_list_delete_link(swboard->users, swboard->users)) - g_free(swboard->users->data); + msn_user_unref(swboard->users->data); session = swboard->session; session->switches = g_list_remove(session->switches, swboard); @@ -231,6 +233,8 @@ { MsnCmdProc *cmdproc; PurpleAccount *account; + MsnUserList *userlist; + MsnUser *msnuser; char *semicolon; char *passport; @@ -246,13 +250,36 @@ else passport = g_strdup(user); + userlist = swboard->session->userlist; + msnuser = msn_userlist_find_user(userlist, passport); + /* Don't add multiple endpoints to the conversation. */ - if (g_list_find_custom(swboard->users, passport, (GCompareFunc)strcmp)) { + if (g_list_find_custom(swboard->users, passport, (GCompareFunc)msn_user_passport_cmp)) { + g_free(passport); + 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); + /* Don't add ourselves either... */ + if (g_str_equal(passport, purple_account_get_username(account))) { + g_free(passport); + return; + } + + if (!msnuser) { + purple_debug_info("msn","User %s is not on our list.\n", passport); + msnuser = msn_user_new(userlist, passport, NULL); + } else + msn_user_ref(msnuser); + + g_free(passport); + + swboard->users = g_list_prepend(swboard->users, msnuser); swboard->current_users++; swboard->empty = FALSE; @@ -270,11 +297,11 @@ if ((swboard->conv != NULL) && (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) { - purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL, + purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), msnuser->passport, NULL, 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 || @@ -299,7 +326,7 @@ { const char *tmp_user; - tmp_user = l->data; + tmp_user = ((MsnUser*)l->data)->passport; purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE); @@ -316,7 +343,7 @@ else if (swboard->conv == NULL) { swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - user, account); + msnuser->passport, account); } else { @@ -406,7 +433,7 @@ return FALSE; } -static void +void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error) { MsnSwitchBoard *swboard; @@ -514,7 +541,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 : ""); @@ -543,17 +570,6 @@ * Message Stuff **************************************************************************/ -/** Called when a message times out. */ -static void -msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans) -{ - MsnMessage *msg; - - msg = trans->data; - - msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT); -} - /** Called when we receive an error of a message. */ static void msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) @@ -588,96 +604,6 @@ } #endif -static void -release_msg(MsnSwitchBoard *swboard, MsnMessage *msg) -{ - MsnCmdProc *cmdproc; - MsnTransaction *trans; - char *payload; - gsize payload_len; - char flag; - - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - cmdproc = swboard->cmdproc; - - payload = msn_message_gen_payload(msg, &payload_len); - - if (purple_debug_is_verbose()) { - purple_debug_info("msn", "SB length:{%" G_GSIZE_FORMAT "}\n", payload_len); - msn_message_show_readable(msg, "SB SEND", FALSE); - } - - flag = msn_message_get_flag(msg); - trans = msn_transaction_new(cmdproc, "MSG", "%c %" G_GSIZE_FORMAT, - flag, payload_len); - - /* Data for callbacks */ - msn_transaction_set_data(trans, msg); - - if (flag != 'U') { - if (msg->type == MSN_MSG_TEXT) - { - msg->ack_ref = TRUE; - msn_message_ref(msg); - swboard->ack_list = g_list_append(swboard->ack_list, msg); - msn_transaction_set_timeout_cb(trans, msg_timeout); - } - else if (msg->type == MSN_MSG_SLP) - { - msg->ack_ref = TRUE; - msn_message_ref(msg); - swboard->ack_list = g_list_append(swboard->ack_list, msg); - msn_transaction_set_timeout_cb(trans, msg_timeout); -#if 0 - if (msg->ack_cb != NULL) - { - msn_transaction_add_cb(trans, "ACK", msg_ack); - msn_transaction_add_cb(trans, "NAK", msg_nak); - } -#endif - } - } - - trans->payload = payload; - trans->payload_len = payload_len; - - msg->trans = trans; - - msn_cmdproc_send_trans(cmdproc, trans); -} - -static void -queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg) -{ - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - purple_debug_info("msn", "Appending message to queue.\n"); - - g_queue_push_tail(swboard->msg_queue, msg); - - msn_message_ref(msg); -} - -static void -process_queue(MsnSwitchBoard *swboard) -{ - MsnMessage *msg; - - g_return_if_fail(swboard != NULL); - - purple_debug_info("msn", "Processing queue\n"); - - while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL) - { - purple_debug_info("msn", "Sending message\n"); - release_msg(swboard, msg); - msn_message_unref(msg); - } -} - gboolean msn_switchboard_can_send(MsnSwitchBoard *swboard) { @@ -689,20 +615,6 @@ return TRUE; } -void -msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg, - gboolean queue) -{ - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - purple_debug_info("msn", "switchboard send msg..\n"); - if (msn_switchboard_can_send(swboard)) - release_msg(swboard, msg); - else if (queue) - queue_msg(swboard, msg); -} - /************************************************************************** * Switchboard Commands **************************************************************************/ @@ -745,7 +657,10 @@ purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL); passport = g_list_find_custom(swboard->users, user, (GCompareFunc)strcmp); - g_free(passport->data); + if (passport) + g_free(passport->data); + else + purple_debug_warning("msn", "Can't find user %s in the switchboard\n", user); swboard->users = g_list_delete_link(swboard->users, passport); swboard->current_users--; if (swboard->current_users == 0) @@ -761,8 +676,10 @@ static void iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { + PurpleAccount *account; MsnSwitchBoard *swboard; + account = cmdproc->session->account; swboard = cmdproc->data; swboard->total_users = atoi(cmd->params[2]); @@ -774,17 +691,19 @@ joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; + PurpleAccount *account; MsnSwitchBoard *swboard; const char *passport; passport = cmd->params[0]; session = cmdproc->session; + account = session->account; swboard = cmdproc->data; msn_switchboard_add_user(swboard, passport); - process_queue(swboard); + msn_sbconn_process_queue(swboard); if (!session->http_method) send_clientcaps(swboard); @@ -847,8 +766,8 @@ msg = cmd->trans->data; - if (msg->ack_cb != NULL) - msg->ack_cb(msg, msg->ack_data); + if (msg->part && msg->part->ack_cb != NULL) + msg->part->ack_cb(msg->part, msg->part->ack_data); swboard = cmdproc->data; if (swboard) @@ -970,6 +889,7 @@ MsnTransaction *trans; MsnCmdProc *cmdproc; PurpleAccount *account; + char *username; cmdproc = servconn->cmdproc; g_return_if_fail(cmdproc != NULL); @@ -978,24 +898,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 +1142,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); }
--- a/libpurple/protocols/msn/switchboard.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/switchboard.h Wed Nov 10 11:41:51 2010 +0900 @@ -50,12 +50,10 @@ MSN_SB_FLAG_FT = 0x02 /**< This switchboard is being used for file transfer. */ } MsnSBFlag; -#include "conversation.h" - +#include "cmdproc.h" #include "msg.h" #include "servconn.h" -#include "slplink.h" -#include "user.h" +#include "session.h" /** * A switchboard. @@ -246,6 +244,9 @@ void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg, gboolean queue); +void +msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error); + gboolean msn_switchboard_chat_leave(MsnSwitchBoard *swboard); gboolean msn_switchboard_chat_invite(MsnSwitchBoard *swboard, const char *who); @@ -253,30 +254,6 @@ void msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user); /** - * Processes peer to peer messages. - * - * @param cmdproc The command processor. - * @param msg The message. - */ -void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg); - -/** - * Processes emoticon messages. - * - * @param cmdproc The command processor. - * @param msg The message. - */ -void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg); - -/** - * Processes INVITE messages. - * - * @param cmdproc The command processor. - * @param msg The message. - */ -void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg); - -/** * Shows an ink message from this switchboard. * * @param swboard The switchboard.
--- a/libpurple/protocols/msn/sync.c Sat Oct 23 16:12:36 2010 +0900 +++ /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); -}
--- a/libpurple/protocols/msn/sync.h Sat Oct 23 16:12:36 2010 +0900 +++ /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 */
--- a/libpurple/protocols/msn/table.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/table.h Wed Nov 10 11:41:51 2010 +0900 @@ -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 */
--- a/libpurple/protocols/msn/transaction.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/transaction.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,6 +21,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ + +#include "internal.h" +#include "debug.h" + #include "msn.h" #include "transaction.h" @@ -37,6 +41,7 @@ trans->cmdproc = cmdproc; trans->command = g_strdup(command); + trans->saveable = TRUE; if (format != NULL) { @@ -96,8 +101,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 +182,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) {
--- a/libpurple/protocols/msn/transaction.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/transaction.h Wed Nov 10 11:41:51 2010 +0900 @@ -24,6 +24,8 @@ #ifndef MSN_TRANSACTION_H #define MSN_TRANSACTION_H +#include "internal.h" + typedef struct _MsnTransaction MsnTransaction; #include "cmdproc.h" @@ -40,7 +42,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 +78,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);
--- a/libpurple/protocols/msn/user.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/user.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,10 +21,21 @@ * 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 "internal.h" +#include "debug.h" +#include "util.h" + #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, @@ -39,15 +50,25 @@ msn_user_set_passport(user, passport); msn_user_set_friendly_name(user, friendly_name); - return user; + return msn_user_ref(user); } /*destroy a user object*/ -void +static void msn_user_destroy(MsnUser *user) { g_return_if_fail(user != NULL); + if (user->refcount > 1) { + msn_user_unref(user); + return; + } + + 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); @@ -82,6 +103,27 @@ g_free(user); } +MsnUser * +msn_user_ref(MsnUser *user) +{ + g_return_val_if_fail(user != NULL, NULL); + + user->refcount++; + + return user; +} + +void +msn_user_unref(MsnUser *user) +{ + g_return_if_fail(user != NULL); + + user->refcount--; + + if(user->refcount == 0) + msn_user_destroy(user); +} + void msn_user_update(MsnUser *user) { @@ -186,6 +228,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 +262,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 +492,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); @@ -413,18 +507,83 @@ user->networkid = network; } +static gboolean +buddy_icon_cached(PurpleConnection *gc, MsnObject *obj) +{ + PurpleAccount *account; + PurpleBuddy *buddy; + const char *old; + const char *new; + + g_return_val_if_fail(obj != NULL, FALSE); + + account = purple_connection_get_account(gc); + + buddy = purple_find_buddy(account, msn_object_get_creator(obj)); + if (buddy == NULL) + return FALSE; + + old = purple_buddy_icons_get_checksum_for_user(buddy); + new = msn_object_get_sha1(obj); + + if (new == NULL) + return FALSE; + + /* If the old and new checksums are the same, and the file actually exists, + * then return TRUE */ + if (old != NULL && !strcmp(old, new)) + return TRUE; + + return FALSE; +} + +static void +queue_buddy_icon_request(MsnUser *user) +{ + PurpleAccount *account; + MsnObject *obj; + GQueue *queue; + + g_return_if_fail(user != NULL); + + account = user->userlist->session->account; + + obj = msn_user_get_object(user); + + if (obj == NULL) { + purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL); + return; + } + + if (!buddy_icon_cached(account->gc, obj)) { + MsnUserList *userlist; + + userlist = user->userlist; + queue = userlist->buddy_icon_requests; + + if (purple_debug_is_verbose()) + purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n", + user->passport, userlist->buddy_icon_window); + + g_queue_push_tail(queue, user); + + if (userlist->buddy_icon_window > 0) + msn_release_buddy_icon_request(userlist); + } +} + void msn_user_set_object(MsnUser *user, MsnObject *obj) { g_return_if_fail(user != NULL); - if (user->msnobj != NULL) + if (user->msnobj != NULL && !msn_object_find_local(msn_object_get_sha1(obj))) msn_object_destroy(user->msnobj); user->msnobj = obj; - if (user->list_op & MSN_LIST_FL_OP) - msn_queue_buddy_icon_request(user); + if (user != user->userlist->session->user && user->list_op & MSN_LIST_FL_OP) + queue_buddy_icon_request(user); } void @@ -496,6 +655,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 +712,60 @@ 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); +} + +/************************************************************************** + * Utility functions + **************************************************************************/ + +int +msn_user_passport_cmp(MsnUser *user, const char *passport) +{ + const char *str; + char *pass; + int result; + + str = purple_normalize_nocase(NULL, msn_user_get_passport(user)); + pass = g_strdup(str); + + result = g_strcmp0(pass, purple_normalize_nocase(NULL, passport)); + g_free(pass); + + return result; +} + +gboolean +msn_user_is_in_group(MsnUser *user, const char * group_id) +{ + if (user == NULL) + return FALSE; + + if (group_id == NULL) + return FALSE; + + return (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) != NULL; +} + +gboolean +msn_user_is_in_list(MsnUser *user, MsnListId list_id) +{ + if (user == NULL) + return FALSE; + + return (user->list_op & (1 << list_id)); +} +
--- a/libpurple/protocols/msn/user.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/user.h Wed Nov 10 11:41:51 2010 +0900 @@ -79,10 +79,13 @@ { MsnUserList *userlist; + guint8 refcount; /**< The reference count of this object */ + char *passport; /**< The passport account. */ 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 +105,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 +120,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 * **************************************************************************/ @@ -134,12 +150,20 @@ const char *friendly_name); /** - * Destroys a user structure. + * Increment the reference count. + * + * @param user The user. * - * @param user The user to destroy. + * @return user. */ -void msn_user_destroy(MsnUser *user); +MsnUser *msn_user_ref(MsnUser *user); +/** + * Decrement the reference count. + * + * @param user The user + */ +void msn_user_unref(MsnUser *user); /** * Updates the user. @@ -252,6 +276,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 +294,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 +388,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 +408,39 @@ 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); + +/************************************************************************** + * Utility functions + **************************************************************************/ + + +/** + * Check if the user is part of the group. + * + * @param user The user we are asking group membership. + * @param group_id The group where the user may be in. + * + * @return TRUE if user is part of the group. Otherwise, FALSE. + */ +gboolean msn_user_is_in_group(MsnUser *user, const char * group_id); + +/** + * Check if user is on list. + * + * @param user The user we are asking list membership. + * @param list_id The list where the user may be in. + * + * @return TRUE if the user is on the list, else FALSE. + */ +gboolean msn_user_is_in_list(MsnUser *user, MsnListId list_id); +/** * Returns the network id for a user. * * @param user The user. @@ -398,11 +484,36 @@ /** * 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); +/** + * Compare the given passport with the one of the user + * + * @param user User to compare. + * @oaran passport Passport to compare. + * + * @return Zero if the passport match with the one of the user, otherwise + * a positive integer if the user passport is greather than the one given + * and a negative integer if it is less. + */ +int msn_user_passport_cmp(MsnUser *user, const char *passport); + +/** + * 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 */
--- a/libpurple/protocols/msn/userlist.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/userlist.c Wed Nov 10 11:41:51 2010 +0900 @@ -21,7 +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 "internal.h" +#include "debug.h" +#include "request.h" + #include "msn.h" +#include "msnutils.h" #include "userlist.h" #include "contact.h" @@ -107,31 +113,6 @@ } /************************************************************************** - * Utility functions - **************************************************************************/ - -gboolean -msn_userlist_user_is_in_group(MsnUser *user, const char * group_id) -{ - if (user == NULL) - return FALSE; - - if (group_id == NULL) - return FALSE; - - return (g_list_find_custom(user->group_ids, group_id, (GCompareFunc)strcmp)) != NULL; -} - -gboolean -msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id) -{ - if (user == NULL) - return FALSE; - - return (user->list_op & (1 << list_id)); -} - -/************************************************************************** * Server functions **************************************************************************/ @@ -235,7 +216,7 @@ /*destroy userlist*/ for (l = userlist->users; l != NULL; l = l->next) { - msn_user_destroy(l->data); + msn_user_unref(l->data); } g_list_free(userlist->users); @@ -503,7 +484,7 @@ g_return_if_fail(user != NULL); - if ( !msn_userlist_user_is_in_list(user, list_id)) { + if ( !msn_user_is_in_list(user, list_id)) { list = lists[list_id]; purple_debug_info("msn", "User %s is not in list %s, not removing.\n", who, list); return; @@ -569,13 +550,13 @@ user = msn_userlist_find_add_user(userlist, who, who); - if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) { + if ( msn_user_is_in_list(user, MSN_LIST_FL) ) { purple_debug_info("msn", "User %s already exists\n", who); msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL); - if (msn_userlist_user_is_in_group(user, group_id)) { + if (msn_user_is_in_group(user, group_id)) { purple_debug_info("msn", "User %s is already in group %s, returning\n", who, new_group_name); msn_callback_state_free(state); return; @@ -604,7 +585,7 @@ user = msn_userlist_find_add_user(userlist, who, who); /* First we're going to check if it's already there. */ - if (msn_userlist_user_is_in_list(user, list_id)) + if (msn_user_is_in_list(user, list_id)) { list = lists[list_id]; purple_debug_info("msn", "User '%s' is already in list: %s\n", who, list); @@ -706,6 +687,37 @@ msn_add_contact_to_group(userlist->session, state, who, new_group_id); } +void +msn_release_buddy_icon_request(MsnUserList *userlist) +{ + MsnUser *user; + + g_return_if_fail(userlist != NULL); + + if (purple_debug_is_verbose()) + purple_debug_info("msn", "Releasing buddy icon request\n"); + + if (userlist->buddy_icon_window > 0) { + GQueue *queue; + + queue = userlist->buddy_icon_requests; + + if (g_queue_is_empty(userlist->buddy_icon_requests)) + return; + + user = g_queue_pop_head(queue); + + userlist->buddy_icon_window--; + + msn_request_user_display(user); + + if (purple_debug_is_verbose()) + purple_debug_info("msn", + "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n", + userlist->buddy_icon_window); + } +} + /*load userlist from the Blist file cache*/ void msn_userlist_load(MsnSession *session)
--- a/libpurple/protocols/msn/userlist.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/msn/userlist.h Wed Nov 10 11:41:51 2010 +0900 @@ -35,6 +35,16 @@ MSN_LIST_PL /**< Pending list */ } MsnListId; +typedef enum +{ + MSN_LIST_FL_OP = 0x01, + MSN_LIST_AL_OP = 0x02, + MSN_LIST_BL_OP = 0x04, + MSN_LIST_RL_OP = 0x08, + MSN_LIST_PL_OP = 0x10 +} MsnListOp; +#define MSN_LIST_OP_MASK 0x07 + #include "group.h" #include "msn.h" #include "user.h" @@ -52,9 +62,6 @@ }; -gboolean msn_userlist_user_is_in_group(MsnUser *user, const char * group_id); -gboolean msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id); - void msn_got_lst_user(MsnSession *session, MsnUser *user, MsnListOp list_op, GSList *group_ids); @@ -98,6 +105,7 @@ MsnListId list_id); void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who, MsnListId list_id); +void msn_release_buddy_icon_request(MsnUserList *userlist); void msn_userlist_load(MsnSession *session);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/xfer.c Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,138 @@ +#include "internal.h" +#include "debug.h" + +#include "sbconn.h" +#include "xfer.h" + +/************************************************************************** + * Xfer + **************************************************************************/ + +void +msn_xfer_init(PurpleXfer *xfer) +{ + MsnSlpCall *slpcall; + /* MsnSlpLink *slplink; */ + char *content; + + purple_debug_info("msn", "xfer_init\n"); + + slpcall = xfer->data; + + /* Send Ok */ + content = g_strdup_printf("SessionID: %lu\r\n\r\n", + slpcall->session_id); + + msn_slp_send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", + content); + + g_free(content); + msn_slplink_send_queued_slpmsgs(slpcall->slplink); +} + +void +msn_xfer_cancel(PurpleXfer *xfer) +{ + MsnSlpCall *slpcall; + char *content; + + g_return_if_fail(xfer != NULL); + g_return_if_fail(xfer->data != NULL); + + slpcall = xfer->data; + + if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) + { + if (slpcall->started) + { + msn_slpcall_close(slpcall); + } + else + { + content = g_strdup_printf("SessionID: %lu\r\n\r\n", + slpcall->session_id); + + msn_slp_send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", + content); + + g_free(content); + msn_slplink_send_queued_slpmsgs(slpcall->slplink); + + if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) + slpcall->wasted = TRUE; + else + msn_slpcall_destroy(slpcall); + } + } +} + +gssize +msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer) +{ + MsnSlpCall *slpcall; + + g_return_val_if_fail(xfer != NULL, -1); + g_return_val_if_fail(data != NULL, -1); + g_return_val_if_fail(len > 0, -1); + + g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND, -1); + + slpcall = xfer->data; + /* Not sure I trust it'll be there */ + g_return_val_if_fail(slpcall != NULL, -1); + + g_return_val_if_fail(slpcall->xfer_msg != NULL, -1); + + slpcall->u.outgoing.len = len; + slpcall->u.outgoing.data = data; + msn_slplink_send_msgpart(slpcall->slplink, slpcall->xfer_msg); + + return MIN(MSN_SBCONN_MAX_SIZE, len); +} + +gssize +msn_xfer_read(guchar **data, PurpleXfer *xfer) +{ + MsnSlpCall *slpcall; + gsize len; + + g_return_val_if_fail(xfer != NULL, -1); + g_return_val_if_fail(data != NULL, -1); + + g_return_val_if_fail(purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE, -1); + + slpcall = xfer->data; + /* Not sure I trust it'll be there */ + g_return_val_if_fail(slpcall != NULL, -1); + + /* Just pass up the whole GByteArray. We'll make another. */ + *data = slpcall->u.incoming_data->data; + len = slpcall->u.incoming_data->len; + + g_byte_array_free(slpcall->u.incoming_data, FALSE); + slpcall->u.incoming_data = g_byte_array_new(); + + return len; +} + +void +msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session) +{ + if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) && + (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) && + (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL)) + { + purple_xfer_cancel_remote(slpcall->xfer); + } +} + +void +msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body, + gsize size) +{ + PurpleXfer *xfer = slpcall->xfer; + + purple_xfer_set_completed(xfer, TRUE); + purple_xfer_end(xfer); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/msn/xfer.h Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,12 @@ + +#include "slpcall.h" + +void msn_xfer_init(PurpleXfer *xfer); +void msn_xfer_cancel(PurpleXfer *xfer); + +gssize msn_xfer_write(const guchar *data, gsize len, PurpleXfer *xfer); +gssize msn_xfer_read(guchar **data, PurpleXfer *xfer); + +void msn_xfer_completed_cb(MsnSlpCall *slpcall, + const guchar *body, gsize size); +void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session);
--- a/libpurple/protocols/oscar/clientlogin.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/clientlogin.c Wed Nov 10 11:41:51 2010 +0900 @@ -42,8 +42,36 @@ #include "cipher.h" #include "core.h" -#define URL_CLIENT_LOGIN "https://api.screenname.aol.com/auth/clientLogin" -#define URL_START_OSCAR_SESSION "https://api.oscar.aol.com/aim/startOSCARSession" +#define AIM_LOGIN_HOST "api.screenname.aol.com" +#define ICQ_LOGIN_HOST "api.login.icq.net" + +#define AIM_API_HOST "api.oscar.aol.com" +#define ICQ_API_HOST "api.icq.net" + +#define CLIENT_LOGIN_PAGE "/auth/clientLogin" +#define START_OSCAR_SESSION_PAGE "/aim/startOSCARSession" + +#define HTTPS_FORMAT_URL(host, page) "https://" host page + +static const gchar *client_login_urls[] = { + HTTPS_FORMAT_URL(AIM_LOGIN_HOST, CLIENT_LOGIN_PAGE), + HTTPS_FORMAT_URL(ICQ_LOGIN_HOST, CLIENT_LOGIN_PAGE), +}; + +static const gchar *start_oscar_session_urls[] = { + HTTPS_FORMAT_URL(AIM_API_HOST, START_OSCAR_SESSION_PAGE), + HTTPS_FORMAT_URL(ICQ_API_HOST, START_OSCAR_SESSION_PAGE), +}; + +static const gchar *get_client_login_url(OscarData *od) +{ + return client_login_urls[od->icq ? 1 : 0]; +} + +static const gchar *get_start_oscar_session_url(OscarData *od) +{ + return start_oscar_session_urls[od->icq ? 1 : 0]; +} /* * Using clientLogin requires a developer ID. This key is for libpurple. @@ -65,10 +93,22 @@ static gchar *generate_error_message(xmlnode *resp, const char *url) { xmlnode *text; + xmlnode *status_code_node; + gchar *status_code; + gboolean have_error_code = TRUE; gchar *err = NULL; gchar *details = NULL; - if (resp && (text = xmlnode_get_child(resp, "statusText"))) { + status_code_node = xmlnode_get_child(resp, "statusCode"); + if (status_code_node) { + /* We can get 200 OK here if the server omitted something we think it shouldn't have (see #12783). + * No point in showing the "Ok" string to the user. + */ + if ((status_code = xmlnode_get_data_unescaped(status_code_node)) && strcmp(status_code, "200") == 0) { + have_error_code = FALSE; + } + } + if (have_error_code && resp && (text = xmlnode_get_child(resp, "statusText"))) { details = xmlnode_get_data(text); } @@ -125,13 +165,12 @@ static gboolean parse_start_oscar_session_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **host, unsigned short *port, char **cookie, char **tls_certname) { + OscarData *od = purple_connection_get_protocol_data(gc); xmlnode *response_node, *tmp_node, *data_node; xmlnode *host_node = NULL, *port_node = NULL, *cookie_node = NULL, *tls_node = NULL; - gboolean use_tls; char *tmp; guint code; - - use_tls = purple_account_get_bool(purple_connection_get_account(gc), "use_ssl", OSCAR_DEFAULT_USE_SSL); + const gchar *encryption_type = purple_account_get_string(purple_connection_get_account(gc), "encryption", OSCAR_DEFAULT_ENCRYPTION); /* Parse the response as XML */ response_node = xmlnode_from_str(response, response_len); @@ -142,7 +181,7 @@ "response as XML: %s\n", response); /* Note to translators: %s in this string is a URL */ msg = generate_error_message(response_node, - URL_START_OSCAR_SESSION); + get_start_oscar_session_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -156,7 +195,6 @@ host_node = xmlnode_get_child(data_node, "host"); port_node = xmlnode_get_child(data_node, "port"); cookie_node = xmlnode_get_child(data_node, "cookie"); - tls_node = xmlnode_get_child(data_node, "tlsCertName"); } /* Make sure we have a status code */ @@ -165,7 +203,7 @@ purple_debug_error("oscar", "startOSCARSession response was " "missing statusCode: %s\n", response); msg = generate_error_message(response_node, - URL_START_OSCAR_SESSION); + get_start_oscar_session_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -203,7 +241,7 @@ else { char *msg; msg = generate_error_message(response_node, - URL_START_OSCAR_SESSION); + get_start_oscar_session_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg); g_free(msg); @@ -216,15 +254,13 @@ g_free(tmp); /* Make sure we have everything else */ - if (data_node == NULL || host_node == NULL || - port_node == NULL || cookie_node == NULL || - (use_tls && tls_node == NULL)) + if (data_node == NULL || host_node == NULL || port_node == NULL || cookie_node == NULL) { char *msg; purple_debug_error("oscar", "startOSCARSession response was missing " "something: %s\n", response); msg = generate_error_message(response_node, - URL_START_OSCAR_SESSION); + get_start_oscar_session_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -232,22 +268,37 @@ return FALSE; } + if (strcmp(encryption_type, OSCAR_NO_ENCRYPTION) != 0) { + tls_node = xmlnode_get_child(data_node, "tlsCertName"); + if (tls_node != NULL) { + *tls_certname = xmlnode_get_data_unescaped(tls_node); + } else { + if (strcmp(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION) == 0) { + purple_debug_warning("oscar", "We haven't received a tlsCertName to use. We will not do SSL to BOS.\n"); + } else { + purple_debug_error("oscar", "startOSCARSession was missing tlsCertName: %s\n", response); + purple_connection_error_reason( + gc, + PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, + _("You required encryption in your account settings, but one of the servers doesn't support it.")); + xmlnode_free(response_node); + return FALSE; + } + } + } + /* Extract data from the XML */ *host = xmlnode_get_data_unescaped(host_node); tmp = xmlnode_get_data_unescaped(port_node); *cookie = xmlnode_get_data_unescaped(cookie_node); - if (use_tls) - *tls_certname = xmlnode_get_data_unescaped(tls_node); - - if (*host == NULL || **host == '\0' || tmp == NULL || *tmp == '\0' || *cookie == NULL || **cookie == '\0' || - (use_tls && (*tls_certname == NULL || **tls_certname == '\0'))) + if (*host == NULL || **host == '\0' || tmp == NULL || *tmp == '\0' || *cookie == NULL || **cookie == '\0') { char *msg; purple_debug_error("oscar", "startOSCARSession response was missing " "something: %s\n", response); msg = generate_error_message(response_node, - URL_START_OSCAR_SESSION); + get_start_oscar_session_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -284,7 +335,7 @@ /* Note to translators: The first %s is a URL, the second is an error message. */ tmp = g_strdup_printf(_("Error requesting %s: %s"), - URL_START_OSCAR_SESSION, error_message); + get_start_oscar_session_url(od), error_message); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); g_free(tmp); @@ -306,11 +357,8 @@ static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime) { char *query_string, *signature, *url; - PurpleAccount *account; - gboolean use_tls; - - account = purple_connection_get_account(od->gc); - use_tls = purple_account_get_bool(account, "use_ssl", OSCAR_DEFAULT_USE_SSL); + PurpleAccount *account = purple_connection_get_account(od->gc); + const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); /* * Construct the GET parameters. 0x00000611 is the distid given to @@ -323,12 +371,13 @@ "&ts=%" PURPLE_TIME_T_MODIFIER "&useTLS=%d", purple_url_encode(token), - oscar_get_ui_info_int(od->icq ? "prpl-icq-distid" - : "prpl-aim-distid", 0x00000611), - get_client_key(od), hosttime, use_tls); - signature = generate_signature("GET", URL_START_OSCAR_SESSION, + oscar_get_ui_info_int(od->icq ? "prpl-icq-distid" : "prpl-aim-distid", 0x00000611), + get_client_key(od), + hosttime, + strcmp(encryption_type, OSCAR_NO_ENCRYPTION) != 0 ? 1 : 0); + signature = generate_signature("GET", get_start_oscar_session_url(od), query_string, session_key); - url = g_strdup_printf(URL_START_OSCAR_SESSION "?%s&sig_sha256=%s", + url = g_strdup_printf("%s?%s&sig_sha256=%s", get_start_oscar_session_url(od), query_string, signature); g_free(query_string); g_free(signature); @@ -367,6 +416,7 @@ */ static gboolean parse_client_login_response(PurpleConnection *gc, const gchar *response, gsize response_len, char **token, char **secret, time_t *hosttime) { + OscarData *od = purple_connection_get_protocol_data(gc); xmlnode *response_node, *tmp_node, *data_node; xmlnode *secret_node = NULL, *hosttime_node = NULL, *token_node = NULL, *tokena_node = NULL; char *tmp; @@ -379,7 +429,7 @@ purple_debug_error("oscar", "clientLogin could not parse " "response as XML: %s\n", response); msg = generate_error_message(response_node, - URL_CLIENT_LOGIN); + get_client_login_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -403,7 +453,7 @@ purple_debug_error("oscar", "clientLogin response was " "missing statusCode: %s\n", response); msg = generate_error_message(response_node, - URL_CLIENT_LOGIN); + get_client_login_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -446,7 +496,7 @@ } else { char *msg; msg = generate_error_message(response_node, - URL_CLIENT_LOGIN); + get_client_login_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, msg); g_free(msg); @@ -465,7 +515,7 @@ purple_debug_error("oscar", "clientLogin response was missing " "something: %s\n", response); msg = generate_error_message(response_node, - URL_CLIENT_LOGIN); + get_client_login_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -483,7 +533,7 @@ purple_debug_error("oscar", "clientLogin response was missing " "something: %s\n", response); msg = generate_error_message(response_node, - URL_CLIENT_LOGIN); + get_client_login_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg); g_free(msg); @@ -520,10 +570,10 @@ gchar *tmp; if (error_message != NULL) tmp = g_strdup_printf(_("Error requesting %s: %s"), - URL_CLIENT_LOGIN, error_message); + get_client_login_url(od), error_message); else tmp = g_strdup_printf(_("Error requesting %s"), - URL_CLIENT_LOGIN); + get_client_login_url(od)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); g_free(tmp); @@ -599,7 +649,7 @@ /* Send the POST request */ od->url_data = purple_util_fetch_url_request_len_with_account( - purple_connection_get_account(gc), URL_CLIENT_LOGIN, + purple_connection_get_account(gc), get_client_login_url(od), TRUE, NULL, FALSE, request->str, FALSE, -1, client_login_cb, od); g_string_free(request, TRUE);
--- a/libpurple/protocols/oscar/encoding.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/encoding.c Wed Nov 10 11:41:51 2010 +0900 @@ -80,7 +80,6 @@ glib_encoding = "UTF-16BE"; sanitize_ucs((gchar *)text, textlen); } else if (g_ascii_strcasecmp(extracted_encoding, "utf-8")) { - purple_debug_warning("oscar", "Unrecognized character encoding \"%s\", attempting to convert to UTF-8 anyway\n", extracted_encoding); glib_encoding = extracted_encoding; }
--- a/libpurple/protocols/oscar/family_chat.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/family_chat.c Wed Nov 10 11:41:51 2010 +0900 @@ -88,6 +88,8 @@ return 1; } + byte_stream_get16(bs); /* skip the TLV count */ + /* * Everything else are TLVs. */
--- a/libpurple/protocols/oscar/family_icbm.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/family_icbm.c Wed Nov 10 11:41:51 2010 +0900 @@ -1731,7 +1731,7 @@ purple_debug_misc("oscar", "X-Status: Received XML reply\n"); if (xml) { GString *xstatus; - char *tmp1, *tmp2; + char *tmp1, *tmp2, *unescaped_xstatus; /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */ @@ -1749,29 +1749,32 @@ tmp1 += 12; tmp2 = strstr(tmp1, "</desc>"); if (tmp2 != NULL) { - if (xstatus->len > 0) + if (xstatus->len > 0 && tmp2 > tmp1) g_string_append(xstatus, " - "); g_string_append_len(xstatus, tmp1, tmp2 - tmp1); } } - if (xstatus->len > 0) { - purple_debug_misc("oscar", "X-Status reply: %s\n", xstatus->str); + unescaped_xstatus = purple_unescape_text(xstatus->str); + g_string_free(xstatus, TRUE); + if (*unescaped_xstatus) { + purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus); account = purple_connection_get_account(od->gc); buddy = purple_find_buddy(account, bn); presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_active_status(presence); - purple_prpl_got_user_status(account, bn, - purple_status_get_id(status), - "message", xstatus->str, NULL); + status = purple_presence_get_status(presence, "mood"); + if (status) { + purple_prpl_got_user_status(account, bn, + "mood", + PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME), + PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL); + } } - g_string_free(xstatus, TRUE); + g_free(unescaped_xstatus); } else { purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n"); } } else { purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n"); - /* if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, sn, reason); */ } }
--- a/libpurple/protocols/oscar/family_icq.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/family_icq.c Wed Nov 10 11:41:51 2010 +0900 @@ -408,27 +408,27 @@ return 0; } -static int +static void gotalias(OscarData *od, struct aim_icq_info *info) { PurpleConnection *gc = od->gc; PurpleAccount *account = purple_connection_get_account(gc); - gchar who[16], *utf8; PurpleBuddy *b; + gchar *utf8 = oscar_utf8_try_convert(account, od, info->nick); - if (info->nick[0] && (utf8 = oscar_utf8_try_convert(account, od, info->nick))) { - if (info->for_auth_request) { - oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason); - } else { + if (info->for_auth_request) { + oscar_auth_recvrequest(gc, g_strdup_printf("%u", info->uin), utf8, info->auth_request_reason); + } else { + if (utf8 && *utf8) { + gchar who[16]; g_snprintf(who, sizeof(who), "%u", info->uin); serv_got_alias(gc, who, utf8); if ((b = purple_find_buddy(account, who))) { purple_blist_node_set_string((PurpleBlistNode*)b, "servernick", utf8); } - g_free(utf8); } + g_free(utf8); } - return 1; } /**
--- a/libpurple/protocols/oscar/family_oservice.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/family_oservice.c Wed Nov 10 11:41:51 2010 +0900 @@ -1041,63 +1041,45 @@ static int aim_parse_extstatus(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs) { - guint16 type; - guint8 flags, length; - - type = byte_stream_get16(bs); - flags = byte_stream_get8(bs); - length = byte_stream_get8(bs); - - /* - * A flag of 0x01 could mean "this is the checksum we have for you" - * A flag of 0x40 could mean "I don't have your icon, upload it" - */ - - switch (type) { - case 0x0000: - case 0x0001: { /* buddy icon checksum */ - /* not sure what the difference between 1 and 0 is */ - guint8 *md5 = byte_stream_getraw(bs, length); + guint16 type = byte_stream_get16(bs); + if (type == 0x0000 || type == 0x0001) { + /* buddy icon checksum */ + /* not sure what the difference between 1 and 0 is */ + guint8 flags = byte_stream_get8(bs); + guint8 length = byte_stream_get8(bs); + guint8 *md5 = byte_stream_getraw(bs, length); - if ((flags == 0x00) || (flags == 0x41)) { - if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) { - od->iconconnecting = TRUE; - od->set_icon = TRUE; - aim_srv_requestnew(od, SNAC_FAMILY_BART); - } else { - PurpleAccount *account = purple_connection_get_account(od->gc); - PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account); - if (img == NULL) { - aim_ssi_delicon(od); - } else { - - purple_debug_info("oscar", - "Uploading icon to icon server\n"); - aim_bart_upload(od, purple_imgstore_get_data(img), - purple_imgstore_get_size(img)); - purple_imgstore_unref(img); - } - } - } else if (flags == 0x81) { + if ((flags == 0x00) || (flags == 0x41)) { + if (!flap_connection_getbytype(od, SNAC_FAMILY_BART) && !od->iconconnecting) { + od->iconconnecting = TRUE; + od->set_icon = TRUE; + aim_srv_requestnew(od, SNAC_FAMILY_BART); + } else { PurpleAccount *account = purple_connection_get_account(od->gc); PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account); - if (img == NULL) + if (img == NULL) { aim_ssi_delicon(od); - else { - aim_ssi_seticon(od, md5, length); + } else { + + purple_debug_info("oscar", + "Uploading icon to icon server\n"); + aim_bart_upload(od, purple_imgstore_get_data(img), + purple_imgstore_get_size(img)); purple_imgstore_unref(img); } } - - g_free(md5); - } break; + } else if (flags == 0x81) { + PurpleAccount *account = purple_connection_get_account(od->gc); + PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account); + if (img == NULL) + aim_ssi_delicon(od); + else { + aim_ssi_seticon(od, md5, length); + purple_imgstore_unref(img); + } + } - case 0x0002: { - /* We just set an available message? */ - /* there is a second length that is just for the message */ - char *msg = byte_stream_getstr(bs, byte_stream_get16(bs)); - g_free(msg); - } break; + g_free(md5); } return 0;
--- a/libpurple/protocols/oscar/flap_connection.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/flap_connection.c Wed Nov 10 11:41:51 2010 +0900 @@ -435,11 +435,16 @@ aim_rxcallback_t userfunc; conn = data; + /* Explicitly added for debugging #5927. Don't re-order this, only + * consider removing it. + */ + purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn); + od = conn->od; account = purple_connection_get_account(od->gc); - purple_debug_info("oscar", "Destroying oscar connection of " - "type 0x%04hx. Disconnect reason is %d\n", + purple_debug_info("oscar", "Destroying oscar connection (%p) of " + "type 0x%04hx. Disconnect reason is %d\n", conn, conn->type, conn->disconnect_reason); od->oscar_connections = g_slist_remove(od->oscar_connections, conn); @@ -575,7 +580,7 @@ return; purple_debug_info("oscar", "Scheduling destruction of FLAP " - "connection of type 0x%04hx\n", conn->type); + "connection %p of type 0x%04hx\n", conn, conn->type); conn->disconnect_reason = reason; g_free(conn->error_message); conn->error_message = g_strdup(error_message);
--- a/libpurple/protocols/oscar/libaim.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/libaim.c Wed Nov 10 11:41:51 2010 +0900 @@ -143,7 +143,7 @@ static void init_plugin(PurplePlugin *plugin) { - oscar_init(plugin); + oscar_init(plugin, FALSE); } PURPLE_INIT_PLUGIN(aim, init_plugin, info);
--- a/libpurple/protocols/oscar/libicq.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/libicq.c Wed Nov 10 11:41:51 2010 +0900 @@ -155,7 +155,7 @@ { PurpleAccountOption *option; - oscar_init(plugin); + oscar_init(plugin, TRUE); option = purple_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
--- a/libpurple/protocols/oscar/oscar.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/oscar.c Wed Nov 10 11:41:51 2010 +0900 @@ -73,7 +73,6 @@ static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02}; static guint8 features_icq[] = {0x01}; -static guint8 ck[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; struct create_room { char *name; @@ -376,12 +375,10 @@ aim_request_login(od, conn, purple_account_get_username(account)); purple_debug_info("oscar", "Username sent, waiting for response\n"); purple_connection_update_progress(gc, _("Username sent"), 1, OSCAR_CONNECT_STEPS); - ck[1] = 0x65; } else if (conn->type == SNAC_FAMILY_LOCATE) { purple_connection_update_progress(gc, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS); - ck[4] = 0x61; } else if (conn->type == SNAC_FAMILY_CHAT) { @@ -612,11 +609,44 @@ aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES); } +static const gchar *login_servers[] = { + AIM_DEFAULT_LOGIN_SERVER, + AIM_DEFAULT_SSL_LOGIN_SERVER, + ICQ_DEFAULT_LOGIN_SERVER, + ICQ_DEFAULT_SSL_LOGIN_SERVER, +}; + +static const gchar * +get_login_server(gboolean is_icq, gboolean use_ssl) +{ + return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)]; +} + +static gint +compare_handlers(gconstpointer a, gconstpointer b) +{ + guint aa = GPOINTER_TO_UINT(a); + guint bb = GPOINTER_TO_UINT(b); + guint family1 = aa >> 16; + guint family2 = bb >> 16; + guint subtype1 = aa & 0xFFFF; + guint subtype2 = bb & 0xFFFF; + if (family1 != family2) { + return family1 - family2; + } + return subtype1 - subtype2; +} + void oscar_login(PurpleAccount *account) { PurpleConnection *gc; OscarData *od; + const gchar *encryption_type; + GList *handlers; + GList *sorted_handlers; + GList *cur; + GString *msg = g_string_new(""); gc = purple_account_get_connection(account); od = oscar_data_new(); @@ -675,6 +705,18 @@ oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, purple_parse_searcherror, 0); oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0); + g_string_append(msg, "Registered handlers: "); + handlers = g_hash_table_get_keys(od->handlerlist); + sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers); + for (cur = sorted_handlers; cur; cur = cur->next) { + guint x = GPOINTER_TO_UINT(cur->data); + g_string_append_printf(msg, "%04x/%04x, ", x >> 16, x & 0xFFFF); + } + g_list_free(sorted_handlers); + g_list_free(handlers); + purple_debug_misc("oscar", "%s\n", msg->str); + g_string_free(msg, TRUE); + purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc); if (!oscar_util_valid_name(purple_account_get_username(account))) { @@ -694,7 +736,16 @@ } od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT); - od->use_ssl = purple_account_get_bool(account, "use_ssl", OSCAR_DEFAULT_USE_SSL); + + encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); + if (!purple_ssl_is_supported() && strcmp(encryption_type, OSCAR_REQUIRE_ENCRYPTION) == 0) { + purple_connection_error_reason( + gc, + PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, + _("You required encryption in your account settings, but encryption is not supported by your system.")); + return; + } + od->use_ssl = purple_ssl_is_supported() && strcmp(encryption_type, OSCAR_NO_ENCRYPTION) != 0; /* Connect to core Purple signals */ purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc); @@ -719,13 +770,7 @@ newconn = flap_connection_new(od, SNAC_FAMILY_AUTH); if (od->use_ssl) { - if (!purple_ssl_is_supported()) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("SSL support unavailable")); - return; - } - - server = purple_account_get_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER); + server = purple_account_get_string(account, "server", get_login_server(od->icq, TRUE)); /* * If the account's server is what the oscar prpl has offered as @@ -734,27 +779,27 @@ * do what we know is best for them and change the setting out * from under them to the SSL login server. */ - if (!strcmp(server, OSCAR_DEFAULT_LOGIN_SERVER) || !strcmp(server, OSCAR_OLD_LOGIN_SERVER)) { + if (!strcmp(server, get_login_server(od->icq, FALSE))) { purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n"); - purple_account_set_string(account, "server", OSCAR_DEFAULT_SSL_LOGIN_SERVER); - server = OSCAR_DEFAULT_SSL_LOGIN_SERVER; + purple_account_set_string(account, "server", get_login_server(od->icq, TRUE)); + server = get_login_server(od->icq, TRUE); } newconn->gsc = purple_ssl_connect(account, server, purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT), ssl_connection_established_cb, ssl_connection_error_cb, newconn); } else { - server = purple_account_get_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); + server = purple_account_get_string(account, "server", get_login_server(od->icq, FALSE)); /* * See the comment above. We do the reverse here. If they don't want * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER, * set it back to the default. */ - if (!strcmp(server, OSCAR_DEFAULT_SSL_LOGIN_SERVER)) { + if (!strcmp(server, get_login_server(od->icq, TRUE))) { purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n"); - purple_account_set_string(account, "server", OSCAR_DEFAULT_LOGIN_SERVER); - server = OSCAR_DEFAULT_LOGIN_SERVER; + purple_account_set_string(account, "server", get_login_server(od->icq, FALSE)); + server = get_login_server(od->icq, FALSE); } newconn->connect_data = purple_proxy_connect(NULL, account, server, @@ -770,7 +815,6 @@ } purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS); - ck[0] = 0x5a; } void @@ -969,8 +1013,8 @@ conn->cookie = g_memdup(cookie, cookielen); /* - * tls_certname is only set (and must be set if we get this far) if - * SSL is enabled. + * Use TLS only if the server provided us with a tls_certname. The server might not specify a tls_certname even if we requested to use TLS, + * and that is something we should be prepared to. */ if (tls_certname) { @@ -994,7 +1038,6 @@ od->default_port = port; purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); - ck[3] = 0x64; return 1; } @@ -1114,7 +1157,6 @@ } purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS); - ck[3] = 0x64; return 1; } @@ -1198,7 +1240,6 @@ purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS)); purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS); - ck[2] = 0x6c; return 1; } @@ -1228,6 +1269,20 @@ else host = g_strdup(redir->ip); + if (!redir->use_ssl) { + const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION); + if (strcmp(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION) == 0) { + purple_debug_warning("oscar", "We won't use SSL for FLAP type 0x%04hx.\n", redir->group); + } else if (strcmp(encryption_type, OSCAR_REQUIRE_ENCRYPTION) == 0) { + purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.", host, port, redir->group); + purple_connection_error_reason( + gc, + PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, + _("You required encryption in your account settings, but one of the servers doesn't support it.")); + return 0; + } + } + /* * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext. @@ -1235,14 +1290,11 @@ if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN || redir->group == SNAC_FAMILY_BART)) { - purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", - redir->group); + purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", redir->group); redir->use_ssl = 0; } - purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx%s\n", - host, port, redir->group, - od->use_ssl && !redir->use_ssl ? " without SSL, despite main stream encryption" : ""); + purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n", host, port, redir->group); newconn = flap_connection_new(od, redir->group); newconn->cookielen = redir->cookielen; @@ -1292,6 +1344,8 @@ { PurpleConnection *gc; PurpleAccount *account; + PurpleBuddy *buddy = NULL; + PurpleStatus *previous_status = NULL; struct buddyinfo *bi; time_t time_idle = 0, signon = 0; int type = 0; @@ -1301,8 +1355,6 @@ aim_userinfo_t *info; char *message = NULL; char *itmsurl = NULL; - char *tmp; - const char *tmp2; gc = od->gc; account = purple_connection_get_account(gc); @@ -1314,6 +1366,11 @@ g_return_val_if_fail(info != NULL, 1); g_return_val_if_fail(info->bn != NULL, 1); + buddy = purple_find_buddy(account, info->bn); + if (buddy) { + previous_status = purple_presence_get_active_status(purple_buddy_get_presence(buddy)); + } + /* * If this is an AIM buddy and their name has formatting, set their * server alias. @@ -1377,40 +1434,33 @@ status_id = OSCAR_STATUS_ID_AVAILABLE; } - if (info->flags & AIM_FLAG_WIRELESS) - { + if (info->flags & AIM_FLAG_WIRELESS) { purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL); } else { purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE); } - if (info->status != NULL && info->status[0] != '\0') { - /* Grab the available message */ - message = oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len); + /* Empty status means we should unset the status message. NULL status means we should keep it from the previous active status. + * Same goes for itmsurl (which is available only for the "available" status). + */ + if (info->status != NULL) { + message = (info->status_len > 0) ? oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len) : NULL; + } else if (previous_status != NULL) { + message = g_strdup(purple_status_get_attr_string(previous_status, "message")); } - tmp2 = tmp = (message ? purple_markup_escape_text(message, -1) : NULL); - if (strcmp(status_id, OSCAR_STATUS_ID_AVAILABLE) == 0) { - if (info->itmsurl_encoding && info->itmsurl && info->itmsurl_len) { - /* Grab the iTunes Music Store URL */ - itmsurl = oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len); + if (info->itmsurl != NULL) { + itmsurl = (info->itmsurl_len > 0) ? oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len) : NULL; + } else if (previous_status != NULL && purple_status_is_available(previous_status)) { + itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl")); } - - if (tmp2 == NULL && itmsurl != NULL) - /* - * The message can't be NULL because NULL means it was the - * last attribute, so the itmsurl would get ignored below. - */ - tmp2 = ""; - - purple_prpl_got_user_status(account, info->bn, status_id, - "message", tmp2, "itmsurl", itmsurl, NULL); + purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message, itmsurl); + purple_prpl_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL); + } else { + purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message); + purple_prpl_got_user_status(account, info->bn, status_id, "message", message, NULL); } - else - purple_prpl_got_user_status(account, info->bn, status_id, "message", tmp2, NULL); - - g_free(tmp); g_free(message); g_free(itmsurl); @@ -1471,13 +1521,6 @@ return 1; } -static void purple_check_comment(OscarData *od, const char *str) { - if ((str == NULL) || strcmp(str, (const char *)ck)) - aim_locate_setcaps(od, purple_caps); - else - aim_locate_setcaps(od, purple_caps | OSCAR_CAPABILITY_SECUREIM); -} - static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { PurpleConnection *gc = od->gc; PurpleAccount *account = purple_connection_get_account(gc); @@ -2376,6 +2419,7 @@ switch(type) { case 0x0002: { + GString *msg = g_string_new(""); guint8 maxrooms; struct aim_chat_exchangeinfo *exchanges; int exchangecount, i; @@ -2384,15 +2428,17 @@ exchangecount = va_arg(ap, int); exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); - purple_debug_misc("oscar", "chat info: Chat Rights:\n"); - purple_debug_misc("oscar", - "chat info: \tMax Concurrent Rooms: %hhd\n", maxrooms); - purple_debug_misc("oscar", - "chat info: \tExchange List: (%d total)\n", exchangecount); - for (i = 0; i < exchangecount; i++) - purple_debug_misc("oscar", - "chat info: \t\t%hu %s\n", - exchanges[i].number, exchanges[i].name ? exchanges[i].name : ""); + g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %hhd, Exchange List (%d total): ", maxrooms, exchangecount); + for (i = 0; i < exchangecount; i++) { + g_string_append_printf(msg, "%hu", exchanges[i].number); + if (exchanges[i].name) { + g_string_append_printf(msg, " %s", exchanges[i].name); + } + g_string_append(msg, ", "); + } + purple_debug_misc("oscar", "%s\n", msg->str); + g_string_free(msg, TRUE); + while (od->create_rooms) { struct create_room *cr = od->create_rooms->data; purple_debug_info("oscar", @@ -3917,14 +3963,6 @@ "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname); purple_blist_add_buddy(b, NULL, g, NULL); } - if (!oscar_util_name_compare(curitem->name, purple_account_get_username(account))) { - char *comment = aim_ssi_getcomment(od->ssi.local, gname, curitem->name); - if (comment != NULL) - { - purple_check_comment(od, comment); - g_free(comment); - } - } /* Mobile users should always be online */ if (curitem->name[0] == '+') { @@ -4569,7 +4607,7 @@ od = purple_connection_get_protocol_data(gc); userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b)); - oscar_user_info_append_status(gc, user_info, b, userinfo, /* strip_html_tags */ TRUE); + oscar_user_info_append_status(gc, user_info, b, userinfo, /* use_html_status */ FALSE); if (full) oscar_user_info_append_extra_info(gc, user_info, b, userinfo); @@ -4607,13 +4645,9 @@ message = purple_status_get_attr_string(status, "message"); if (message != NULL) { - gchar *tmp1, *tmp2; - tmp1 = purple_markup_strip_html(message); - purple_util_chrreplace(tmp1, '\n', ' '); - tmp2 = g_markup_escape_text(tmp1, -1); - ret = oscar_util_format_string(tmp2, purple_account_get_username(account)); - g_free(tmp1); - g_free(tmp2); + gchar *tmp = oscar_util_format_string(message, purple_account_get_username(account)); + ret = purple_markup_escape_text(tmp, -1); + g_free(tmp); } else if (purple_status_is_available(status)) { @@ -4797,7 +4831,6 @@ OscarData *od; PurpleBuddy *b; PurpleGroup *g; - const char *username; gc = data->gc; od = purple_connection_get_protocol_data(gc); @@ -4816,11 +4849,6 @@ } aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text); - - username = purple_account_get_username(account); - if (!oscar_util_name_compare(data->name, username)) - purple_check_comment(od, text); - oscar_free_name_data(data); } @@ -5666,20 +5694,39 @@ return FALSE; } -void oscar_init(PurplePlugin *plugin) +void oscar_init(PurplePlugin *plugin, gboolean is_icq) { PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); PurpleAccountOption *option; static gboolean init = FALSE; - - option = purple_account_option_string_new(_("Server"), "server", OSCAR_DEFAULT_LOGIN_SERVER); + static const gchar *encryption_keys[] = { + N_("Use encryption if available"), + N_("Require encryption"), + N_("Don't use encryption"), + NULL + }; + static const gchar *encryption_values[] = { + OSCAR_OPPORTUNISTIC_ENCRYPTION, + OSCAR_REQUIRE_ENCRYPTION, + OSCAR_NO_ENCRYPTION, + NULL + }; + GList *encryption_options = NULL; + int i; + + option = purple_account_option_string_new(_("Server"), "server", get_login_server(is_icq, TRUE)); prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT); prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); - option = purple_account_option_bool_new(_("Use SSL"), "use_ssl", - OSCAR_DEFAULT_USE_SSL); + for (i = 0; encryption_keys[i]; i++) { + PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); + kvp->key = g_strdup(encryption_keys[i]); + kvp->value = g_strdup(encryption_values[i]); + encryption_options = g_list_append(encryption_options, kvp); + } + option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options); prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option); option = purple_account_option_bool_new(_("Use clientLogin"), "use_clientlogin",
--- a/libpurple/protocols/oscar/oscar.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/oscar.h Wed Nov 10 11:41:51 2010 +0900 @@ -1305,7 +1305,7 @@ /* userinfo.c - displaying user information */ -void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags); +void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status); void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo); void oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy); void oscar_user_info_display_icq(OscarData *od, struct aim_icq_info *info);
--- a/libpurple/protocols/oscar/oscar_data.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/oscar_data.c Wed Nov 10 11:41:51 2010 +0900 @@ -37,6 +37,8 @@ oscar_data_new(void) { OscarData *od; + aim_module_t *cur; + GString *msg; od = g_new0(OscarData, 1); @@ -70,6 +72,20 @@ aim__registermodule(od, auth_modfirst); aim__registermodule(od, email_modfirst); + msg = g_string_new("Registered modules: "); + for (cur = od->modlistv; cur; cur = cur->next) { + g_string_append_printf( + msg, + "%s (family=0x%04x, version=0x%04x, toolid=0x%04x, toolversion=0x%04x), ", + cur->name, + cur->family, + cur->version, + cur->toolid, + cur->toolversion); + } + purple_debug_misc("oscar", "%s\n", msg->str); + g_string_free(msg, TRUE); + return od; } @@ -118,8 +134,6 @@ { SnacHandler *snac_handler; - purple_debug_misc("oscar", "Adding handler for %04x/%04x\n", family, subtype); - snac_handler = g_new0(SnacHandler, 1); snac_handler->family = family;
--- a/libpurple/protocols/oscar/oscarcommon.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/oscarcommon.h Wed Nov 10 11:41:51 2010 +0900 @@ -32,10 +32,17 @@ #include "notify.h" #include "status.h" -#define OSCAR_DEFAULT_LOGIN_SERVER "login.messaging.aol.com" +#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com" +#define AIM_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com" +#define ICQ_DEFAULT_LOGIN_SERVER "login.icq.com" +#define ICQ_DEFAULT_SSL_LOGIN_SERVER "slogin.icq.com" + #define OSCAR_DEFAULT_LOGIN_PORT 5190 -#define OSCAR_DEFAULT_SSL_LOGIN_SERVER "slogin.oscar.aol.com" -#define OSCAR_OLD_LOGIN_SERVER "login.oscar.aol.com" + +#define OSCAR_OPPORTUNISTIC_ENCRYPTION "opportunistic_encryption" +#define OSCAR_REQUIRE_ENCRYPTION "require_encryption" +#define OSCAR_NO_ENCRYPTION "no_encryption" + #ifndef _WIN32 #define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1" #else @@ -46,8 +53,8 @@ #define OSCAR_DEFAULT_WEB_AWARE FALSE #define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE #define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE -#define OSCAR_DEFAULT_USE_SSL TRUE #define OSCAR_DEFAULT_USE_CLIENTLOGIN TRUE +#define OSCAR_DEFAULT_ENCRYPTION OSCAR_OPPORTUNISTIC_ENCRYPTION #ifdef _WIN32 const char *oscar_get_locale_charset(void); @@ -96,4 +103,4 @@ gboolean oscar_offline_message(const PurpleBuddy *buddy); void oscar_format_username(PurpleConnection *gc, const char *nick); GList *oscar_actions(PurplePlugin *plugin, gpointer context); -void oscar_init(PurplePlugin *plugin); +void oscar_init(PurplePlugin *plugin, gboolean is_icq);
--- a/libpurple/protocols/oscar/peer.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/peer.c Wed Nov 10 11:41:51 2010 +0900 @@ -879,7 +879,9 @@ } conn->verified_connect_data = purple_proxy_connect(NULL, account, - (conn->proxyip != NULL) ? conn->proxyip : PEER_PROXY_SERVER, + (conn->proxyip != NULL) + ? conn->proxyip + : (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER), PEER_PROXY_PORT, peer_proxy_connection_established_cb, conn); if (conn->verified_connect_data != NULL)
--- a/libpurple/protocols/oscar/peer.h Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/peer.h Wed Nov 10 11:41:51 2010 +0900 @@ -58,7 +58,8 @@ /* * For peer proxying */ -#define PEER_PROXY_SERVER "ars.oscar.aol.com" +#define AIM_PEER_PROXY_SERVER "ars.oscar.aol.com" +#define ICQ_PEER_PROXY_SERVER "ars.icq.com" #define PEER_PROXY_PORT 5190 /* The port we should always connect to */ #define PEER_PROXY_PACKET_VERSION 0x044a
--- a/libpurple/protocols/oscar/rxhandlers.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/rxhandlers.c Wed Nov 10 11:41:51 2010 +0900 @@ -69,8 +69,6 @@ mod->next = (aim_module_t *)od->modlistv; od->modlistv = mod; - purple_debug_misc("oscar", "registered module %s (family 0x%04x, version = 0x%04x, tool 0x%04x, tool version 0x%04x)\n", mod->name, mod->family, mod->version, mod->toolid, mod->toolversion); - return 0; }
--- a/libpurple/protocols/oscar/userinfo.c Sat Oct 23 16:12:36 2010 +0900 +++ b/libpurple/protocols/oscar/userinfo.c Wed Nov 10 11:41:51 2010 +0900 @@ -173,17 +173,17 @@ * @param user_info A PurpleNotifyUserInfo object to which status information will be added * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status(). * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status(). - * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped. + * @param use_html_status If TRUE, prefer HTML-formatted away message over plaintext available message. */ void -oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags) +oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean use_html_status) { PurpleAccount *account = purple_connection_get_account(gc); OscarData *od; PurplePresence *presence = NULL; PurpleStatus *status = NULL; gchar *message = NULL, *itmsurl = NULL, *tmp; - gboolean is_away; + gboolean escaping_needed = TRUE; od = purple_connection_get_protocol_data(gc); @@ -205,9 +205,10 @@ the "message" attribute of the status contains only the plaintext message. */ if (userinfo) { - if ((userinfo->flags & AIM_FLAG_AWAY) && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) { + if ((userinfo->flags & AIM_FLAG_AWAY) && use_html_status && userinfo->away_len > 0 && userinfo->away != NULL && userinfo->away_encoding != NULL) { /* Away message */ message = oscar_encoding_to_utf8(userinfo->away_encoding, userinfo->away, userinfo->away_len); + escaping_needed = FALSE; } else { /* * Available message or non-HTML away message (because that's @@ -227,44 +228,26 @@ itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl")); } - is_away = ((status && !purple_status_is_available(status)) || - (userinfo && (userinfo->flags & AIM_FLAG_AWAY))); - - if (strip_html_tags) { - /* Away messages are HTML, but available messages were originally plain text. - * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags. - */ - /* - * It seems like the above comment no longer applies. All messages need - * to be escaped. - */ - if (message) { - gchar *tmp2; - tmp = purple_markup_strip_html(message); - g_free(message); - tmp2 = g_markup_escape_text(tmp, -1); - g_free(tmp); - message = tmp2; - } - - } else { - if (itmsurl) { - tmp = g_strdup_printf("<a href=\"%s\">%s</a>", - itmsurl, message); + if (message) { + tmp = oscar_util_format_string(message, purple_account_get_username(account)); + g_free(message); + message = tmp; + if (escaping_needed) { + tmp = purple_markup_escape_text(message, -1); g_free(message); message = tmp; } } - g_free(itmsurl); - if (message) { - tmp = oscar_util_format_string(message, purple_account_get_username(account)); + if (use_html_status && itmsurl) { + tmp = g_strdup_printf("<a href=\"%s\">%s</a>", itmsurl, message); g_free(message); message = tmp; } if (b) { if (purple_presence_is_online(presence)) { + gboolean is_away = ((status && !purple_status_is_available(status)) || (userinfo && (userinfo->flags & AIM_FLAG_AWAY))); if (oscar_util_valid_name_icq(purple_buddy_get_name(b)) || is_away || !message || !(*message)) { /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message. * If the status name and the message are the same, only show one. */ @@ -299,12 +282,22 @@ if (presence) { const char *mood; - const char *description; + const char *comment; + char *description; status = purple_presence_get_status(presence, "mood"); - mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); - description = icq_get_custom_icon_description(mood); - if (description && *description) - purple_notify_user_info_add_pair(user_info, _("Mood"), _(description)); + mood = icq_get_custom_icon_description(purple_status_get_attr_string(status, PURPLE_MOOD_NAME)); + if (mood) { + comment = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT); + if (comment) { + char *escaped_comment = purple_markup_escape_text(comment, -1); + description = g_strdup_printf("%s (%s)", _(mood), escaped_comment); + g_free(escaped_comment); + } else { + description = g_strdup(_(mood)); + } + purple_notify_user_info_add_pair(user_info, _("Mood"), description); + g_free(description); + } } purple_notify_user_info_add_pair(user_info, _("Status"), message); @@ -458,7 +451,7 @@ } oscar_user_info_convert_and_add_hyperlink(account, od, user_info, _("Personal Web Page"), info->email, ""); if (buddy != NULL) - oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE); + oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* use_html_status */ TRUE); oscar_user_info_convert_and_add(account, od, user_info, _("Additional Information"), info->info); purple_notify_user_info_add_section_break(user_info); @@ -504,7 +497,7 @@ PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); gchar *tmp = NULL, *info_utf8 = NULL, *base_profile_url = NULL; - oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE); + oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* use_html_status */ TRUE); if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) { tmp = purple_str_seconds_to_string(userinfo->idletime*60);
--- a/pidgin/gtkaccount.c Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/gtkaccount.c Wed Nov 10 11:41:51 2010 +0900 @@ -2468,6 +2468,7 @@ GtkWidget *alert; GdkPixbuf *prpl_icon; struct auth_request *aa; + gboolean have_valid_alias = alias && *alias; gc = purple_account_get_connection(account); if (message != NULL && *message == '\0') @@ -2475,9 +2476,9 @@ buffer = g_strdup_printf(_("%s%s%s%s wants to add you (%s) to his or her buddy list%s%s"), remote_user, - (alias != NULL ? " (" : ""), - (alias != NULL ? alias : ""), - (alias != NULL ? ")" : ""), + (have_valid_alias ? " (" : ""), + (have_valid_alias ? alias : ""), + (have_valid_alias ? ")" : ""), (id != NULL ? id : (purple_connection_get_display_name(gc) != NULL
--- a/pidgin/gtkdialogs.c Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/gtkdialogs.c Wed Nov 10 11:41:51 2010 +0900 @@ -151,7 +151,7 @@ {N_("Bengali"), "bn", "Israt Jahan", "israt@ankur.org.bd"}, {N_("Bengali"), "bn", "Jamil Ahmed", "jamil@bengalinux.org"}, {N_("Bengali"), "bn", "Samia Nimatullah", "mailsamia2001@yahoo.com"}, - {N_("Bengali-India"), "bn", "Runa Bhattacharjee", "runab@fedoraproject.org"}, + {N_("Bengali-India"), "bn_IN", "Runa Bhattacharjee", "runab@fedoraproject.org"}, {N_("Bosnian"), "bs", "Lejla Hadzialic", "lejlah@gmail.com"}, {N_("Catalan"), "ca", "Josep Puigdemont", "josep.puigdemont@gmail.com"}, {N_("Valencian-Catalan"), "ca@valencia", "Toni Hermoso", "toniher@softcatala.org"},
--- a/pidgin/minidialog.c Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/minidialog.c Wed Nov 10 11:41:51 2010 +0900 @@ -332,6 +332,7 @@ break; case PROP_CUSTOM_ICON: gtk_image_set_from_pixbuf(priv->icon, g_value_get_object(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); }
--- a/pidgin/plugins/Makefile.mingw Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/plugins/Makefile.mingw Wed Nov 10 11:41:51 2010 +0900 @@ -73,10 +73,19 @@ $(MAKE) -C $(WINPREFS_PLUGIN) -f $(MINGW_MAKEFILE) install cp *.dll $(PIDGIN_INSTALL_PLUGINS_DIR) +THEMEEDIT_SRC = themeedit.c themeedit-icon.c +THEMEEDIT_OBJECTS = $(THEMEEDIT_SRC:%.c=%.o) + +themeedit.dll: $(THEMEEDIT_OBJECTS) + $(CC) -shared $(THEMEEDIT_OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@ + %.dll: %.c $(PURPLE_CONFIG_H) $(PURPLE_VERSION_H) $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@.o -c $< $(CC) -shared $@.o $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $@ + +include $(PIDGIN_COMMON_RULES) + plugins: \ convcolors.dll \ extplacement.dll \ @@ -89,6 +98,7 @@ relnot.dll \ sendbutton.dll \ spellchk.dll \ + themeedit.dll \ timestamp_format.dll \ timestamp.dll \ xmppconsole.dll
--- a/pidgin/plugins/themeedit-icon.c Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/plugins/themeedit-icon.c Wed Nov 10 11:41:51 2010 +0900 @@ -106,9 +106,16 @@ create_icon_theme(GtkWidget *window) { int s, i, j; - char *dirname = "/tmp"; /* FIXME */ - PidginStatusIconTheme *theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, "type", "status-icon", - "author", getlogin(), + const char *dirname = g_get_tmp_dir(); + PidginStatusIconTheme *theme; + const char *author; +#ifndef _WIN32 + author = getlogin(); +#else + author = "user"; +#endif + theme = g_object_new(PIDGIN_TYPE_STATUS_ICON_THEME, "type", "status-icon", + "author", author, "directory", dirname, NULL);
--- a/pidgin/plugins/themeedit.c Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/plugins/themeedit.c Wed Nov 10 11:41:51 2010 +0900 @@ -268,8 +268,14 @@ theme = pidgin_blist_get_theme(); if (!theme) { + const char *author; +#ifndef _WIN32 + author = getlogin(); +#else + author = "user"; +#endif theme = g_object_new(PIDGIN_TYPE_BLIST_THEME, "type", "blist", - "author", getlogin(), + "author", author, NULL); pidgin_blist_set_theme(theme); }
--- a/pidgin/plugins/win32/winprefs/winprefs.c Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/plugins/win32/winprefs/winprefs.c Wed Nov 10 11:41:51 2010 +0900 @@ -193,7 +193,7 @@ char *runval = NULL; if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) - runval = g_strdup_printf("%s" G_DIR_SEPARATOR_S "pidgin.exe", wpurple_install_dir()); + runval = g_strdup_printf("\"%s" G_DIR_SEPARATOR_S "pidgin.exe\"", wpurple_install_dir()); if(!wpurple_write_reg_string(HKEY_CURRENT_USER, RUNKEY, "Pidgin", runval) /* For Win98 */
--- a/pidgin/win32/nsis/pidgin-installer.nsi Sat Oct 23 16:12:36 2010 +0900 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Nov 10 11:41:51 2010 +0900 @@ -549,6 +549,7 @@ Delete "$INSTDIR\ca-certs\ValiCert_Class_2_VA.crt" Delete "$INSTDIR\ca-certs\VeriSign_Class3_Extended_Validation_CA.pem" Delete "$INSTDIR\ca-certs\Verisign_Class3_Primary_CA.pem" + Delete "$INSTDIR\ca-certs\VeriSign_Class_3_Public_Primary_Certification_Authority_-_G2.pem" Delete "$INSTDIR\ca-certs\VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem" Delete "$INSTDIR\ca-certs\VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5_2.pem" Delete "$INSTDIR\ca-certs\VeriSign_International_Server_Class_3_CA.pem" @@ -598,6 +599,7 @@ Delete "$INSTDIR\plugins\ssl.dll" Delete "$INSTDIR\plugins\statenotify.dll" Delete "$INSTDIR\plugins\tcl.dll" + Delete "$INSTDIR\plugins\themeedit.dll" Delete "$INSTDIR\plugins\ticker.dll" Delete "$INSTDIR\plugins\timestamp.dll" Delete "$INSTDIR\plugins\timestamp_format.dll"
--- a/po/ChangeLog Sat Oct 23 16:12:36 2010 +0900 +++ b/po/ChangeLog Wed Nov 10 11:41:51 2010 +0900 @@ -1,6 +1,9 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +version 2.7.6 + version 2.7.5 + * German translation updated (Björn Voigt, Jochen Kemnade) * Romanian translation updated (Mişu Moldovan) version 2.7.4
--- a/po/POTFILES.in Sat Oct 23 16:12:36 2010 +0900 +++ b/po/POTFILES.in Wed Nov 10 11:41:51 2010 +0900 @@ -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
--- a/po/de.po Sat Oct 23 16:12:36 2010 +0900 +++ b/po/de.po Wed Nov 10 11:41:51 2010 +0900 @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2010-10-21 00:11-0400\n" -"PO-Revision-Date: 2010-10-20 21:23+0200\n" +"POT-Creation-Date: 2010-10-28 19:57+0200\n" +"PO-Revision-Date: 2010-10-28 19:50+0200\n" "Last-Translator: Björn Voigt <bjoern@cs.tu-berlin.de>\n" "Language-Team: German <de@li.org>\n" "Language: de\n" @@ -7843,10 +7843,10 @@ msgstr "Privatsphärenoptionen setzen..." msgid "Show Visible List" -msgstr "Zeige Liste „Sichtbar“" +msgstr "Zeige Liste „Immer sichtbar“" msgid "Show Invisible List" -msgstr "Zeige Liste „Unsichtbar“" +msgstr "Zeige Liste „Immer unsichtbar“" #. AIM actions msgid "Confirm Account" @@ -8115,13 +8115,13 @@ "einem Rechtsklick anklicken und „%s“ auswählen." msgid "Visible List" -msgstr "Liste Sichtbar" +msgstr "Liste \"Immer sichtbar\"" msgid "These buddies will see your status when you switch to \"Invisible\"" -msgstr "Diese Buddys werden Ihren Status sehen wenn Sie „unsichtbar“ sind" +msgstr "Diese Buddys werden Ihren Status sehen, wenn Sie „unsichtbar“ sind" msgid "Invisible List" -msgstr "Liste Unsichtbar" +msgstr "Liste \"Immer unsichtbar\"" msgid "These buddies will always see you as offline" msgstr "Für diese Buddys werden Sie immer als offline angezeigt" @@ -12448,6 +12448,9 @@ msgid "Lao" msgstr "Laotisch" +msgid "Maithili" +msgstr "Maithili" + msgid "Macedonian" msgstr "Makedonisch"
--- a/po/ro.po Sat Oct 23 16:12:36 2010 +0900 +++ b/po/ro.po Wed Nov 10 11:41:51 2010 +0900 @@ -15363,7 +15363,7 @@ "from http://pidgin.im/download/windows/ ." msgstr "" "Eroare la descărcarea GTK+ ($R2).$\\rAceastă componentă este necesară pentru " -"funcționarea Pidgin. Dacă reîncercarea eșuează, încercați varianta „Offline " +"funcţionarea Pidgin. Dacă reîncercarea eşuează, încercaţi varianta „Offline " "Installer” de la http://pidgin.im/download/windows/ ." #. $R2 will display the URL that the Debug Symbols failed to download from @@ -15372,7 +15372,7 @@ "use the 'Offline Installer' from http://pidgin.im/download/windows/ ." msgstr "" "Eroare la instalarea simbolurilor de depanare ($R2).$\\rDacă reîncercarea " -"eșuează, încercați varianta 'Offline Installer' from http://pidgin.im/" +"eşuează, încercaţi varianta 'Offline Installer' from http://pidgin.im/" "download/windows/ ." #. $R3 will display the URL that the Dictionary failed to download from @@ -15383,7 +15383,7 @@ "%20Pidgin#manual_win32_spellcheck_installation" msgstr "" "Eroare la instalarea verificării ortografice ($R3).$\\rDacă reîncercarea " -"eșuează, găsiți instrucțiuni de instalare manuală la: http://developer." +"eşuează, găsiţi instrucţiuni de instalare manuală la: http://developer." "pidgin.im/wiki/Installing%20Pidgin#manual_win32_spellcheck_installation" #. Installer Subsection Text @@ -15408,7 +15408,7 @@ "Runtime?" msgstr "" "Pidgin necesită o instalare GTK+ compatibilă (care nu pare să fie prezentă " -"deja).$\\rSigur doriți să omiteți instalarea bibliotecilor GTK+?" +"deja).$\\rSigur doriţi să omiteţi instalarea bibliotecilor GTK+?" #. Installer Subsection Text msgid "Shortcuts"
--- a/po/stats.pl Sat Oct 23 16:12:36 2010 +0900 +++ b/po/stats.pl Wed Nov 10 11:41:51 2010 +0900 @@ -27,6 +27,7 @@ $lang{'ca@valencia'} = "Catalan (Valencian)"; $lang{'be@latin'} = "Belarusian (Latin)"; +$lang{bn_IN} = "Bengali-India"; $lang{en_AU} = "English (Australian)"; $lang{en_CA} = "English (Canadian)"; $lang{en_GB} = "English (British)";
--- a/share/ca-certs/Makefile.am Sat Oct 23 16:12:36 2010 +0900 +++ b/share/ca-certs/Makefile.am Wed Nov 10 11:41:51 2010 +0900 @@ -15,6 +15,7 @@ ValiCert_Class_2_VA.pem \ Verisign_RSA_Secure_Server_CA.pem \ Verisign_Class3_Primary_CA.pem \ + VeriSign_Class_3_Public_Primary_Certification_Authority_-_G2.pem \ VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem \ VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5_2.pem
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/VeriSign_Class_3_Public_Primary_Certification_Authority_-_G2.pem Wed Nov 10 11:41:51 2010 +0900 @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ +BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh +c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy +MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp +emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X +DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw +FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg +UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo +YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 +MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 +pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 +13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID +AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk +U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i +F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY +oJ2daZH9 +-----END CERTIFICATE-----