Mercurial > pidgin
changeset 25125:907ca9a36fe0
explicit merge of '714a7c7f903d11c96ffade34966121da549d998f'
and 'd2c40fe4e2181eda5c1c631c7805f17e6b5d22c3'
to branch 'org.darkrain42.pidgin.xmpp'
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Thu, 20 Nov 2008 21:13:56 +0000 |
parents | 94ccccab4e98 (current diff) ba362a67278c (diff) |
children | 9ab681f23007 |
files | libpurple/protocols/jabber/Makefile.mingw libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h libpurple/protocols/msn/slpsession.c libpurple/protocols/msn/slpsession.h libpurple/protocols/msn/soap2.c libpurple/protocols/msn/soap2.h libpurple/protocols/qq/buddy_status.c libpurple/protocols/qq/buddy_status.h libpurple/protocols/qq/keep_alive.c libpurple/protocols/qq/keep_alive.h libpurple/protocols/qq/login_logout.c libpurple/protocols/qq/login_logout.h libpurple/protocols/qq/qq_proxy.c libpurple/protocols/qq/qq_proxy.h libpurple/protocols/qq/recv_core.c libpurple/protocols/qq/recv_core.h libpurple/protocols/qq/send_core.c libpurple/protocols/qq/send_core.h libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sendqueue.h libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/qq/udp_proxy_s5.h |
diffstat | 282 files changed, 11991 insertions(+), 9639 deletions(-) [+] |
line wrap: on
line diff
--- a/AUTHORS Mon Aug 18 17:08:01 2008 +0000 +++ b/AUTHORS Thu Nov 20 21:13:56 2008 +0000 @@ -24,6 +24,7 @@ Bartosz Oler - Developer Etan 'deryni' Reisner - Developer Tim 'marv' Ringenbach - Developer +Elliott 'QuLogic' Sales de Andrade - Developer Luke 'LSchiere' Schierer - Support Megan 'Cae' Schneider - support/QA Evan Schoenberg - Developer @@ -37,7 +38,6 @@ Felipe 'shx' Contreras Dennis 'EvilDennisR' Ristuccia Peter 'Fmoo' Ruibal -Elliott 'QuLogic' Sales de Andrade Gabriel 'Nix' Schulhof Jorge 'Masca' Villaseñor
--- a/COPYRIGHT Mon Aug 18 17:08:01 2008 +0000 +++ b/COPYRIGHT Thu Nov 20 21:13:56 2008 +0000 @@ -8,7 +8,6 @@ Dave Ahlswede Manuel Amador Matt Amato -Elliott Sales de Andrade Geoffrey Antos Daniel Atallah Paul Aurich @@ -49,6 +48,7 @@ Chris Boyle Derrick J Brashear Mauro Sérgio Ferreira Brasil +Luke Bratch Matt Brenneke Jeremy Brooks Jonathan Brossard @@ -157,10 +157,12 @@ Konrad Gräfe Miah Gregory David Grohmann +Vladislav Guberinić Gideon N. Guillen Christian Hammond Erick Hamness Fred Hampton +Phil Hannent Casey Harkins Andy Harrison Andrew Hart (arhart) @@ -228,6 +230,7 @@ Wesley Lin Artem Litvinovich Josh Littlefield +Daniel Ljungborg Syd Logan Lokheed Norberto Lopes @@ -338,6 +341,7 @@ Michael Ruprecht Sam S. Thanumalayan S. +Elliott Sales de Andrade Tomasz Sałaciński <tsalacinski@gmail.com> Pradyumna Sampath Arvind Samptur
--- a/ChangeLog Mon Aug 18 17:08:01 2008 +0000 +++ b/ChangeLog Thu Nov 20 21:13:56 2008 +0000 @@ -5,20 +5,41 @@ * Ability to create custom smileys (currently only the MSN protocol utilizes the feature). (Thanks to Mauro Sérgio Ferreira Brasil, Marcus Lundblad, Jorge Villaseñor and other contributors) - * Yahoo! Japan now uses UTF-8, matching the behavior of official clients - and restoring compatibility with the web messenger (Yusuke Odate) * Add a configure option, --with-system-ssl-certs to allow packagers to specify a system-wide SSL CA certificates directory. When set, we don't install our SSL CA certs, so it's important that the libpurple package depend on the CA certificates. + IRC: + * /ctcp command (Vladislav Guberinić) + * Allow for auto-detection of incoming UTF-8 formatted text on + accounts which are configured to use some other encoding. + + MSN: + * Update MSN support to protocol 15 (Elliott Sales de Andrade, Jorge + Villaseñor, Mike Ruprecht, Carlos Silva, Ma Yuan, Daniel Ljungborg + and others) + * Personal messages are now supported. They are treated as status + messages. + * Offline IM is now supported. + * Aliasing is now supported server-side. + * Buddies are now emblemed. Bots and web clients should now be + distinguished. + * Update smiley set for non-faces. + * Failing to update a buddy icon when the buddy has gone offline no + longer crashes. + * Custom smileys received in a chat no longer go to a new window. + * Processing is no longer completely frozen after the servers block a + message because it contains (what they consider) inappropriate text. + Pidgin: * Custom buddy icons can now be added to and removed from buddy list entries via the buddy list entry right-click menu. * Resize large incoming custom smileys to a maximum of 96px on either side. * Offer to add new buddies into the same contact as existing buddies - in the same group if the alias given is the same. + in the same group if the alias given is the same. + * Minor smiley style update. General: * Group and Chat buddy list entries can now be given custom buddy @@ -30,6 +51,25 @@ logs. * Added '/msgcolor' command to change colors of different classes of messages in a conversation. See '/help msgcolor' for details. + * Added tab-completion for commands in conversation windows. + +version 2.4.3 (07/01/2008): + libpurple: + * Yahoo! Japan now uses UTF-8, matching the behavior of official clients + and restoring compatibility with the web messenger (Yusuke Odate) + * Setting your buddy icon once again works for Yahoo! accounts. + * Fixes in the Yahoo! protocol to prevent a double free, crashes on + aliases, and alias functionality + * Fix crashes in the bonjour protocol + * Always use UTF-8 for Yahoo! (#5973) + * Fix a crash when the given jabber id is invalid. + * Make the IRC "unknown message" debugging messages UTF-8 safe. + * Fix connecting to ICQ + * Fix a memleak when handling jabber xforms. + + Pidgin: + * Include the send button plugin in the win32 build + * Various memory leak fixes version 2.4.2 (05/17/2008): libpurple:
--- a/ChangeLog.API Mon Aug 18 17:08:01 2008 +0000 +++ b/ChangeLog.API Thu Nov 20 21:13:56 2008 +0000 @@ -17,6 +17,10 @@ * purple_buddy_icons_node_set_custom_icon_from_file * purple_notify_user_info_prepend_section_break * purple_notify_user_info_prepend_section_header + * "website" and "dev_website" items to the ui_info hash table + * purple_cmds_get_handle, purple_cmds_init, purple_cmds_uninit + * cmd-added and cmd-removed signals + * purple_get_host_name Deprecated: * purple_blist_update_buddy_icon @@ -25,6 +29,9 @@ * purple_buddy_icons_set_custom_icon * pidgin_set_custom_buddy_icon + Changed: + * xmlnode_copy now copies the prefix and namespace map for nodes. + pidgin: Added: * gtk_imhtml_smiley_create, gtk_imhtml_smiley_reload and
--- a/ChangeLog.win32 Mon Aug 18 17:08:01 2008 +0000 +++ b/ChangeLog.win32 Thu Nov 20 21:13:56 2008 +0000 @@ -1,4 +1,10 @@ -version 2.4.3 (??/??/2008): +version 2.5.0 (??/??/2008): + * Don't install the GSSAPI SASL plugin on NT4 to avoid an error popup. + * Upgrade to Perl 5.10 (System Perl runtime must be upgraded for Perl + plugins to continue to work). + * Upgrade SILC to use the 1.1.7 toolkit + +version 2.4.3 (07/01/2008): * No changes version 2.4.2 (05/17/2008):
--- a/NEWS Mon Aug 18 17:08:01 2008 +0000 +++ b/NEWS Thu Nov 20 21:13:56 2008 +0000 @@ -1,5 +1,9 @@ Pidgin and Finch: The Pimpin' Penguin IM Clients That're Good for the Soul +2.4.3 (07/01/2008): + Richard: This release includes important bug fixes. I'm just cutting + the release. Thank you to the real heroes who did the fixing! + 2.4.2 (5/17/2008): Sadrul: We added some usability changes in this release, including the typing notification, buddyicon and input area size in the conversation
--- a/configure.ac Mon Aug 18 17:08:01 2008 +0000 +++ b/configure.ac Thu Nov 20 21:13:56 2008 +0000 @@ -336,6 +336,10 @@ AC_DEFINE_UNQUOTED(DISPLAY_VERSION, "$VERSION", [display version info]) fi +AC_ARG_ENABLE(missing-dependencies, [AC_HELP_STRING([--disable-missing-dependencies], + [skip missing dependencies instead of aborting configure])], + force_deps="$enableval", force_deps="yes") + AC_ARG_WITH(x, [], with_x="$withval", with_x="yes") AC_ARG_ENABLE(gtkui, [AC_HELP_STRING([--disable-gtkui], @@ -419,10 +423,12 @@ X11_LIBS="$x_libpath_add" X11_CFLAGS="$x_incpath_add" else - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ X11 development headers not found. Use --without-x if you do not need X11 support. ]) + fi fi ]) AC_SUBST(X11_LIBS) @@ -461,11 +467,13 @@ AC_DEFINE(USE_SCREENSAVER, 1, [Define if we're using XScreenSaver.]) AC_SUBST(XSS_LIBS) else - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ XScreenSaver extension development headers not found. Use --disable-screensaver if you do not need XScreenSaver extension support, this is required for detecting idle time by mouse and keyboard usage. ]) + fi fi else AC_MSG_ERROR([X support is required to build with XScreenSaver extensions]) @@ -490,10 +498,12 @@ AC_DEFINE(USE_SM, 1, [Define if we're using X Session Management.]) AC_SUBST(SM_LIBS) else - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ X session management development headers not found. Use --disable-sm if you do not need session management support. ]) + fi fi else AC_MSG_ERROR([X support is required to build with X session management support]) @@ -515,10 +525,12 @@ if test "x$enable_startup_notification" = "xyes"; then PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [ AC_MSG_RESULT(no) - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ Startup notification development headers not found. Use --disable-startup-notification if you do not need it. -])]) +]) + fi]) if test "x$enable_startup_notification" = "xyes"; then AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.]) @@ -533,10 +545,12 @@ if test "x$enable_gtkspell" = "xyes" ; then PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , [ AC_MSG_RESULT(no) - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ GtkSpell development headers not found. Use --disable-gtkspell if you do not need it. -])]) +]) + fi]) if test "x$enable_gtkspell" = "xyes" ; then AC_DEFINE(USE_GTKSPELL, 1, [Define if we're using GtkSpell]) AC_SUBST(GTKSPELL_CFLAGS) @@ -566,10 +580,12 @@ AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS) AC_SUBST(EVOLUTION_ADDRESSBOOK_LIBS) else - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ Evolution development headers not found. Use --disable-gevolution if you do not need it. ]) + fi fi fi @@ -579,10 +595,12 @@ if test "x$enable_cap" = "xyes"; then PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.3,,[ AC_MSG_RESULT(no) - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ sqlite3 development headers not found. Use --disable-cap if you do not need the Contact Availability Prediction plugin. -])]) +]) + fi]) fi @@ -719,10 +737,12 @@ [], [$GSTREAMER_LIBS]) ], [ AC_MSG_RESULT(no) - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ GStreamer development headers not found. Use --disable-gstreamer if you do not need GStreamer (sound) support. -])]) +]) + fi]) fi dnl ####################################################################### @@ -737,10 +757,12 @@ have_meanwhile="yes" ], [ have_meanwhile="no" - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ Meanwhile development headers not found. Use --disable-meanwhile if you do not need meanwhile (Sametime) support. -])]) +]) + fi]) fi AC_SUBST(MEANWHILE_CFLAGS) AC_SUBST(MEANWHILE_LIBS) @@ -783,7 +805,7 @@ fi AC_CHECK_LIB(avahi-client, avahi_client_new, [avahilibs=yes], [avahilibs=no], $AVAHI_LIBS) -if test "x$enable_avahi" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then +if test "x$enable_avahi" = "xyes" -a "x$force_deps" = "xyes" -a \( "x$avahiincludes" = "xno" -o "x$avahilibs" = "xno" \); then AC_MSG_ERROR([ avahi development headers not found. Use --disable-avahi if you do not need avahi (Bonjour) support. @@ -955,8 +977,8 @@ AC_SUBST(GADU_LIBS) AC_SUBST(GADU_CFLAGS) -# change the next line to make MSNP14 the default (s/enable/disable/; s/no/yes/;) -AC_ARG_ENABLE(msnp14,[AC_HELP_STRING([--enable-msnp14], [Enable the newer MSNP14 protocol (unsupported)])],,enable_msnp14=no) +# change the next line to not make MSNP15 the default (s/disable/enable/; s/yes/no/;) +AC_ARG_ENABLE(msnp15,[AC_HELP_STRING([--disable-msnp15], [Disable the newer MSNP15 protocol])],enable_msnp15=$enableval,enable_msnp15=yes) AC_ARG_ENABLE(distrib,,,enable_distrib=no) AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes") @@ -975,7 +997,7 @@ if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'` fi -if test "x$enable_msnp14" != "xyes" ; then +if test "x$enable_msnp15" != "xyes" ; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/msn/msnp9/'` fi if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then @@ -1062,7 +1084,7 @@ if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'` fi -if test "x$enable_msnp14" != "xyes" ; then +if test "x$enable_msnp15" != "xyes" ; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/msn/msnp9/'` fi if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then @@ -1155,7 +1177,6 @@ "-Wmissing-declarations" \ "-Wmissing-noreturn" \ "-Wmissing-prototypes" \ - "-Wnested-externs" \ "-Wpointer-arith" \ "-Wundef" \ ; do @@ -1220,10 +1241,12 @@ AC_SUBST(DBUS_LIBS) enable_dbus=yes ], [ + if test "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ D-Bus development headers not found. Use --disable-dbus if you do not need D-Bus support. -])]) +]) + fi]) dnl Check for NetworkManager.h; if we don't have it, oh well if test "x$enable_nm" = "xyes" ; then @@ -1232,10 +1255,12 @@ AC_SUBST(NETWORKMANAGER_LIBS) AC_DEFINE(HAVE_NETWORKMANAGER, 1, [Define if we have NetworkManager.]) ], [ - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ NetworkManager development headers not found. Use --disable-nm if you do not need NetworkManager support. -])]) +]) + fi]) fi else enable_nm=no @@ -1548,7 +1573,7 @@ AM_CONDITIONAL(USE_PERL, false) fi -if test "x$looked_for_perl" = "xyes" -a "x$enable_perl" = "xno"; then +if test "x$looked_for_perl" = "xyes" -a "x$enable_perl" = "xno" -a "x$force_deps" = "xyes"; then AC_MSG_ERROR([ Perl development headers not found. Use --disable-perl if you do not need Perl scripting support. @@ -1959,19 +1984,19 @@ msg_ssl=$msg_nss elif test "x$msg_gnutls" != "x"; then msg_ssl=$msg_gnutls -elif test "x$looked_for_gnutls" = "xyes" -a "x$looked_for_nss" = "xyes"; then +elif test "x$looked_for_gnutls" = "xyes" -a "x$looked_for_nss" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ Neither GnuTLS or NSS SSL development headers found. Use --disable-nss --disable-gnutls if you do not need SSL support. MSN, Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable! ]) -elif test "x$looked_for_gnutls" = "xyes"; then +elif test "x$looked_for_gnutls" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ GnuTLS SSL development headers not found. Use --disable-gnutls if you do not need SSL support. MSN, Novell Groupwise and Google Talk will not work without SSL support. ]) -elif test "x$looked_for_nss" = "xyes"; then +elif test "x$looked_for_nss" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ NSS SSL development headers not found. Use --disable-nss if you do not need SSL support. @@ -2010,10 +2035,12 @@ if test "$TCLCONFIG" = "no"; then AC_MSG_RESULT([no]) enable_tcl=no - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ Tcl development headers not found. Use --disable-tcl if you do not need Tcl scripting support. ]) + fi else . $TCLCONFIG AC_MSG_CHECKING([Tcl version compatability]) @@ -2078,10 +2105,12 @@ if test "$TKCONFIG" = "no"; then AC_MSG_RESULT([no]) enable_tk=no - AC_MSG_ERROR([ + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ Tk development headers not found. Use --disable-tk if you do not need Tk scripting support. ]) + fi else . $TKCONFIG eval "TK_LIB_SPEC=\"$TK_LIB_SPEC\"" @@ -2223,9 +2252,11 @@ AC_CHECK_HEADERS(termios.h) # sys/sysctl.h on OpenBSD 4.2 requires sys/param.h +# sys/sysctl.h on FreeBSD requires sys/types.h AC_CHECK_HEADERS(sys/param.h) AC_CHECK_HEADERS(sys/sysctl.h, [], [], [[ + #include <sys/types.h> #ifdef HAVE_PARAM_H # include <sys/param.h> #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/cmd-signals.dox Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,29 @@ +/** @page cmd-signals Command Signals + @signals + @signal cmd-added + @signal cmd-removed + @endsignals + + @see cmds.h + + @signaldef cmd-added + @signalproto +void (*cmd_added)(const char *command, PurpleCmdPriority priority, + PurpleCmdFlag flag); + @endsignalproto + @signaldesc + Emitted when a new command is added. + @param command The new command. + @param priority The priority of the new command. + @param flag The command flags. + @endsignaldef + + @signaldef cmd-removed + @signalproto +void (*cmd_removed)(const char *command); + @endsignalproto + @signaldesc + Emitted when a command is removed. + @param command The removed command. + @endsignaldef +*/
--- a/doc/connection-signals.dox Mon Aug 18 17:08:01 2008 +0000 +++ b/doc/connection-signals.dox Thu Nov 20 21:13:56 2008 +0000 @@ -5,6 +5,7 @@ @signal signed-on @signal signing-off @signal signed-off + @signal connection-error @endsignals @see connection.h
--- a/doc/funniest_home_convos.txt Mon Aug 18 17:08:01 2008 +0000 +++ b/doc/funniest_home_convos.txt Thu Nov 20 21:13:56 2008 +0000 @@ -510,3 +510,8 @@ 12:58 <staggered_ranks> why hasn't support for napster been removed? 12:58 <deryni> It has. 12:59 <staggered_ranks> oh.. ok + + +14:39 <rrobbertt> Does anyone know a way to get text to speech with pidgin? +14:41 <elb> do you want to be rooted sooner, or later? +14:42 <seanegan> good question"; rm -rf ~
--- a/doc/pidgin.1.in Mon Aug 18 17:08:01 2008 +0000 +++ b/doc/pidgin.1.in Thu Nov 20 21:13:56 2008 +0000 @@ -582,6 +582,8 @@ .br Tim 'marv' Ringenbach (developer) <\fImarv_sf@users.sf.net\fR> .br + Elliott 'QuLogic' Sales de Andrade (developer) +.br Luke 'LSchiere' Schierer (support) .br Megan 'Cae' Schneider (support/QA) @@ -606,8 +608,6 @@ .br Peter 'fmoo' Ruibal .br - Elliott 'QuLogic' Sales de Andrade -.br Gabriel 'Nix' Schulhof .br Jorge 'Masca' Villaseñor
--- a/finch/finch.c Mon Aug 18 17:08:01 2008 +0000 +++ b/finch/finch.c Thu Nov 20 21:13:56 2008 +0000 @@ -63,6 +63,8 @@ g_hash_table_insert(ui_info, "name", (char*)_("Finch")); g_hash_table_insert(ui_info, "version", VERSION); + g_hash_table_insert(ui_info, "website", "http://pidgin.im"); + g_hash_table_insert(ui_info, "dev_website", "http://developer.pidgin.im"); } return ui_info;
--- a/finch/gntconv.c Mon Aug 18 17:08:01 2008 +0000 +++ b/finch/gntconv.c Thu Nov 20 21:13:56 2008 +0000 @@ -141,7 +141,7 @@ entry_key_pressed(GntWidget *w, FinchConv *ggconv) { const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry)); - if (*text == '/') + if (*text == '/' && *(text + 1) != '/') { PurpleConversation *conv = ggconv->active_conv; PurpleCmdStatus status; @@ -191,7 +191,7 @@ } else { - char *escape = g_markup_escape_text(text, -1); + char *escape = g_markup_escape_text((*text == '/' ? text + 1 : text), -1); char *apos = purple_strreplace(escape, "'", "'"); g_free(escape); escape = apos; @@ -692,11 +692,48 @@ static void completion_cb(GntEntry *entry, const char *start, const char *end) { - if (start == entry->start) + if (start == entry->start && *start != '/') gnt_widget_key_pressed(GNT_WIDGET(entry), ": "); } static void +gg_setup_commands(FinchConv *fconv, gboolean remove_first) +{ + GList *commands; + char command[256] = "/"; + + if (remove_first) { + commands = purple_cmd_list(NULL); + for (; commands; commands = g_list_delete_link(commands, commands)) { + g_strlcpy(command + 1, commands->data, sizeof(command) - 1); + gnt_entry_remove_suggest(GNT_ENTRY(fconv->entry), command); + } + } + + commands = purple_cmd_list(fconv->active_conv); + for (; commands; commands = g_list_delete_link(commands, commands)) { + g_strlcpy(command + 1, commands->data, sizeof(command) - 1); + gnt_entry_add_suggest(GNT_ENTRY(fconv->entry), command); + } +} + +static void +cmd_added_cb(const char *cmd, PurpleCmdPriority prior, PurpleCmdFlag flags, + FinchConv *fconv) +{ + gg_setup_commands(fconv, TRUE); +} + +static void +cmd_removed_cb(const char *cmd, FinchConv *fconv) +{ + char command[256] = "/"; + g_strlcpy(command + 1, cmd, sizeof(command) - 1); + gnt_entry_remove_suggest(GNT_ENTRY(fconv->entry), command); + gg_setup_commands(fconv, TRUE); +} + +static void finch_create_conversation(PurpleConversation *conv) { FinchConv *ggc = FINCH_GET_DATA(conv); @@ -819,6 +856,12 @@ ggc->flags |= FINCH_CONV_NO_SOUND; gg_create_menu(ggc); + gg_setup_commands(ggc, FALSE); + + purple_signal_connect(purple_cmds_get_handle(), "cmd-added", ggc, + G_CALLBACK(cmd_added_cb), ggc); + purple_signal_connect(purple_cmds_get_handle(), "cmd-removed", ggc, + G_CALLBACK(cmd_removed_cb), ggc); g_free(title); gnt_box_give_focus_to_child(GNT_BOX(ggc->window), ggc->entry); @@ -831,11 +874,14 @@ /* do stuff here */ FinchConv *ggc = FINCH_GET_DATA(conv); ggc->list = g_list_remove(ggc->list, conv); - if (ggc->list && conv == ggc->active_conv) + if (ggc->list && conv == ggc->active_conv) { ggc->active_conv = ggc->list->data; - + gg_setup_commands(ggc, TRUE); + } + if (ggc->list == NULL) { g_free(ggc->u.chat); + purple_signals_disconnect_by_handle(ggc); if (ggc->window) gnt_widget_destroy(ggc->window); g_free(ggc); @@ -1013,9 +1059,12 @@ if (!new_arrivals) { /* Print the list of users in the room */ - GString *string = g_string_new(_("List of users:\n")); + GString *string = g_string_new(NULL); GList *iter; + int count = g_list_length(users); + g_string_printf(string, + ngettext("List of %d user:\n", "List of %d users:\n", count), count); for (iter = users; iter; iter = iter->next) { PurpleConvChatBuddy *cbuddy = iter->data; @@ -1404,8 +1453,11 @@ g_return_if_fail(ggconv); g_return_if_fail(g_list_find(ggconv->list, conv)); + if (ggconv->active_conv == conv) + return; ggconv->active_conv = conv; + gg_setup_commands(ggconv, TRUE); account = purple_conversation_get_account(conv); title = get_conversation_title(conv, account); gnt_screen_rename_widget(ggconv->window, title);
--- a/finch/libgnt/gntbutton.c Mon Aug 18 17:08:01 2008 +0000 +++ b/finch/libgnt/gntbutton.c Thu Nov 20 21:13:56 2008 +0000 @@ -77,18 +77,6 @@ } static gboolean -gnt_button_key_pressed(GntWidget *widget, const char *key) -{ - if (strcmp(key, GNT_KEY_ENTER) == 0 || - strcmp(key, SAFE(cursor_down)) == 0) - { - gnt_widget_activate(widget); - return TRUE; - } - return FALSE; -} - -static gboolean gnt_button_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) { if (event == GNT_LEFT_MOUSE_DOWN) { @@ -106,23 +94,33 @@ g_free(button->priv); } +static gboolean +button_activate(GntBindable *bind, GList *null) +{ + gnt_widget_activate(GNT_WIDGET(bind)); + return TRUE; +} + static void gnt_button_class_init(GntWidgetClass *klass) { char *style; + GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); parent_class = GNT_WIDGET_CLASS(klass); parent_class->draw = gnt_button_draw; parent_class->map = gnt_button_map; parent_class->size_request = gnt_button_size_request; - parent_class->key_pressed = gnt_button_key_pressed; parent_class->clicked = gnt_button_clicked; parent_class->destroy = gnt_button_destroy; style = gnt_style_get_from_name(NULL, "small-button"); small_button = gnt_style_parse_bool(style); g_free(style); - GNTDEBUG; + + gnt_bindable_class_register_action(bindable, "activate", button_activate, + GNT_KEY_ENTER, NULL); + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); } static void
--- a/finch/libgnt/gntkeys.c Mon Aug 18 17:08:01 2008 +0000 +++ b/finch/libgnt/gntkeys.c Thu Nov 20 21:13:56 2008 +0000 @@ -80,6 +80,9 @@ INSERT_KEY("down", GNT_KEY_DOWN); INSERT_KEY("tab", "\t"); + INSERT_KEY("escape", "\033"); + INSERT_KEY("space", " "); + INSERT_KEY("return", GNT_KEY_ENTER); INSERT_KEY("menu", GNT_KEY_POPUP); INSERT_KEY("f1", GNT_KEY_F1); @@ -119,6 +122,9 @@ code[ind] = (c ? 1 : 'a') + ch; INSERT_COMB(str, code); } + if (c == 0) { + INSERT_COMB("tab", "\033\t"); + } } } c = 0;
--- a/libpurple/blist.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/blist.h Thu Nov 20 21:13:56 2008 +0000 @@ -31,13 +31,20 @@ #include <glib.h> +/** @copydoc _PurpleBuddyList */ typedef struct _PurpleBuddyList PurpleBuddyList; +/** @copydoc _PurpleBlistUiOps */ typedef struct _PurpleBlistUiOps PurpleBlistUiOps; +/** @copydoc _PurpleBlistNode */ typedef struct _PurpleBlistNode PurpleBlistNode; +/** @copydoc _PurpleChat */ typedef struct _PurpleChat PurpleChat; +/** @copydoc _PurpleGroup */ typedef struct _PurpleGroup PurpleGroup; +/** @copydoc _PurpleContact */ typedef struct _PurpleContact PurpleContact; +/** @copydoc _PurpleBuddy */ typedef struct _PurpleBuddy PurpleBuddy; /**************************************************************************/
--- a/libpurple/cipher.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/cipher.c Thu Nov 20 21:13:56 2008 +0000 @@ -2402,7 +2402,6 @@ g_return_val_if_fail(context, FALSE); cipher = context->cipher; - g_return_val_if_fail(context, FALSE); if(cipher->ops && cipher->ops->digest) return cipher->ops->digest(context, in_len, digest, out_len);
--- a/libpurple/cmds.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/cmds.c Thu Nov 20 21:13:56 2008 +0000 @@ -81,6 +81,8 @@ cmds = g_list_insert_sorted(cmds, c, (GCompareFunc)cmds_compare_func); + purple_signal_emit(purple_cmds_get_handle(), "cmd-added", cmd, p, f); + return id; } @@ -103,6 +105,7 @@ if (c->id == id) { cmds = g_list_remove(cmds, c); + purple_signal_emit(purple_cmds_get_handle(), "cmd-removed", c->cmd); purple_cmd_free(c); return; } @@ -361,3 +364,28 @@ return ret; } +gpointer purple_cmds_get_handle(void) +{ + static int handle; + return &handle; +} + +void purple_cmds_init(void) +{ + gpointer handle = purple_cmds_get_handle(); + + purple_signal_register(handle, "cmd-added", + purple_marshal_VOID__POINTER_INT_INT, NULL, 3, + purple_value_new(PURPLE_TYPE_STRING), + purple_value_new(PURPLE_TYPE_INT), + purple_value_new(PURPLE_TYPE_INT)); + purple_signal_register(handle, "cmd-removed", + purple_marshal_VOID__POINTER, NULL, 1, + purple_value_new(PURPLE_TYPE_STRING)); +} + +void purple_cmds_uninit(void) +{ + purple_signals_unregister_by_instance(purple_cmds_get_handle()); +} +
--- a/libpurple/cmds.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/cmds.h Thu Nov 20 21:13:56 2008 +0000 @@ -1,6 +1,7 @@ /** * @file cmds.h Commands API * @ingroup core + * @see @ref cmd-signals */ /* Copyright (C) 2003 Timothy Ringenbach <omarvo@hotmail.com> @@ -30,6 +31,7 @@ /**************************************************************************/ /*@{*/ +/** The possible results of running a command with purple_cmd_do_command(). */ typedef enum _PurpleCmdStatus { PURPLE_CMD_STATUS_OK, PURPLE_CMD_STATUS_FAILED, @@ -39,16 +41,31 @@ PURPLE_CMD_STATUS_WRONG_TYPE, } PurpleCmdStatus; +/** Commands registered with the core return one of these values when run. + * Normally, a command will want to return one of the first two; in some + * unusual cases, you might want to have several functions called for a + * particular command; in this case, they should return + * #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other + * commands with the same name. + */ typedef enum _PurpleCmdRet { - PURPLE_CMD_RET_OK, /**< Everything's okay. Don't look for another command to call. */ + PURPLE_CMD_RET_OK, /**< Everything's okay; Don't look for another command to call. */ PURPLE_CMD_RET_FAILED, /**< The command failed, but stop looking.*/ PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */ } PurpleCmdRet; #define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func) +/** A function implementing a command, as passed to purple_cmd_register(). + * + * @todo document the arguments to these functions. + * */ typedef PurpleCmdRet (*PurpleCmdFunc)(PurpleConversation *, const gchar *cmd, gchar **args, gchar **error, void *data); +/** A unique integer representing a command registered with + * purple_cmd_register(), which can subsequently be passed to + * purple_cmd_unregister() to unregister that command. + */ typedef guint PurpleCmdId; typedef enum _PurpleCmdPriority { @@ -205,6 +222,25 @@ */ GList *purple_cmd_help(PurpleConversation *conv, const gchar *cmd); +/** + * Get the handle for the commands API + * @return The handle + * @since 2.5.0 + */ +gpointer purple_cmds_get_handle(void); + +/** + * Initialize the commands subsystem. + * @since 2.5.0 + */ +void purple_cmds_init(void); + +/** + * Uninitialize the commands subsystem. + * @since 2.5.0 + */ +void purple_cmds_uninit(void); + /*@}*/ #ifdef __cplusplus
--- a/libpurple/connection.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/connection.h Thu Nov 20 21:13:56 2008 +0000 @@ -27,6 +27,7 @@ #ifndef _PURPLE_CONNECTION_H_ #define _PURPLE_CONNECTION_H_ +/** @copydoc _PurpleConnection */ typedef struct _PurpleConnection PurpleConnection; /** @@ -121,7 +122,7 @@ */ PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR = 15, - /** Some other error occured which fits into none of the other + /** Some other error occurred which fits into none of the other * categories. */ /* purple_connection_error_reason() in connection.c uses the fact that @@ -223,6 +224,8 @@ void (*_purple_reserved3)(void); } PurpleConnectionUiOps; + +/* Represents an active connection on an account. */ struct _PurpleConnection { PurplePlugin *prpl; /**< The protocol plugin. */
--- a/libpurple/conversation.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/conversation.c Thu Nov 20 21:13:56 2008 +0000 @@ -1621,7 +1621,7 @@ } quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(), - "chat-buddy-joining", conv, user, flag)) | + "chat-buddy-joining", conv, user, flag)) || purple_conv_chat_is_user_ignored(chat, user); cbuddy = purple_conv_chat_cb_new(user, alias, flag); @@ -1633,18 +1633,18 @@ cbuddies = g_list_prepend(cbuddies, cbuddy); if (!quiet && new_arrivals) { - char *escaped = g_markup_escape_text(alias, -1); + char *alias_esc = g_markup_escape_text(alias, -1); char *tmp; if (extra_msg == NULL) - tmp = g_strdup_printf(_("%s entered the room."), escaped); + tmp = g_strdup_printf(_("%s entered the room."), alias_esc); else { - char *escaped2 = g_markup_escape_text(extra_msg, -1); + char *extra_msg_esc = g_markup_escape_text(extra_msg, -1); tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."), - escaped, escaped2); - g_free(escaped2); + alias_esc, extra_msg_esc); + g_free(extra_msg_esc); } - g_free(escaped); + g_free(alias_esc); purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY, @@ -1832,7 +1832,7 @@ if (!quiet) { const char *alias = user; - char *escaped; + char *alias_esc; char *tmp; if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { @@ -1842,17 +1842,17 @@ alias = purple_buddy_get_contact_alias(buddy); } - escaped = g_markup_escape_text(alias, -1); + alias_esc = g_markup_escape_text(alias, -1); if (reason == NULL || !*reason) - tmp = g_strdup_printf(_("%s left the room."), escaped); + tmp = g_strdup_printf(_("%s left the room."), alias_esc); else { - char *escaped2 = g_markup_escape_text(reason, -1); + char *reason_esc = g_markup_escape_text(reason, -1); tmp = g_strdup_printf(_("%s left the room (%s)."), - escaped, escaped2); - g_free(escaped2); + alias_esc, reason_esc); + g_free(reason_esc); } - g_free(escaped); + g_free(alias_esc); purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY,
--- a/libpurple/conversation.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/conversation.h Thu Nov 20 21:13:56 2008 +0000 @@ -32,11 +32,17 @@ /**************************************************************************/ +/** @copydoc _PurpleConversationUiOps */ typedef struct _PurpleConversationUiOps PurpleConversationUiOps; +/** @copydoc _PurpleConversation */ typedef struct _PurpleConversation PurpleConversation; +/** @copydoc _PurpleConvIm */ typedef struct _PurpleConvIm PurpleConvIm; +/** @copydoc _PurpleConvChat */ typedef struct _PurpleConvChat PurpleConvChat; +/** @copydoc _PurpleConvChatBuddy */ typedef struct _PurpleConvChatBuddy PurpleConvChatBuddy; +/** @copydoc _PurpleConvMessage */ typedef struct _PurpleConvMessage PurpleConvMessage; /** @@ -279,11 +285,21 @@ */ struct _PurpleConvChatBuddy { - char *name; /**< The name */ - char *alias; /**< The alias */ - char *alias_key; /**< The alias key */ - gboolean buddy; /**< ChatBuddy is on the blist */ - PurpleConvChatBuddyFlags flags; /**< Flags (ops, voice etc.) */ + char *name; /**< The chat participant's name in the chat. */ + char *alias; /**< The chat participant's alias, if known; + * @a NULL otherwise. + */ + char *alias_key; /**< A string by which this buddy will be sorted, + * or @c NULL if the buddy should be sorted by + * its @c name. (This is currently always @c + * NULL.) + */ + gboolean buddy; /**< @a TRUE if this chat participant is on the + * buddy list; @a FALSE otherwise. + */ + PurpleConvChatBuddyFlags flags; /**< A bitwise OR of flags for this participant, + * such as whether they are a channel operator. + */ }; /**
--- a/libpurple/core.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/core.c Thu Nov 20 21:13:56 2008 +0000 @@ -26,6 +26,7 @@ #include "internal.h" #include "cipher.h" #include "certificate.h" +#include "cmds.h" #include "connection.h" #include "conversation.h" #include "core.h" @@ -130,6 +131,7 @@ #endif purple_ciphers_init(); + purple_cmds_init(); /* Since plugins get probed so early we should probably initialize their * subsystem right away too. @@ -230,6 +232,7 @@ purple_dbus_uninit(); #endif + purple_cmds_uninit(); purple_util_uninit(); purple_signals_uninit();
--- a/libpurple/core.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/core.h Thu Nov 20 21:13:56 2008 +0000 @@ -1,4 +1,5 @@ /** + * @file core.h Startup and shutdown of libpurple * @defgroup core libpurple * @see @ref core-signals */ @@ -28,12 +29,36 @@ typedef struct PurpleCore PurpleCore; +/** Callbacks that fire at different points of the initialization and teardown + * of libpurple, along with a hook to return descriptive information about the + * UI. + */ typedef struct { + /** Called just after the preferences subsystem is initialized; the UI + * could use this callback to add some preferences it needs to be in + * place when other subsystems are initialized. + */ void (*ui_prefs_init)(void); - void (*debug_ui_init)(void); /* Unfortunate necessity. */ + /** Called just after the debug subsystem is initialized, but before + * just about every other component's initialization. The UI should + * use this hook to call purple_debug_set_ui_ops() so that debugging + * information for other components can be logged during their + * initialization. + */ + void (*debug_ui_init)(void); + /** Called after all of libpurple has been initialized. The UI should + * use this hook to set all other necessary UiOps structures. + * + * @see @ref ui-ops + */ void (*ui_init)(void); + /** Called after most of libpurple has been uninitialized. */ void (*quit)(void); + + /** Called by purple_core_get_ui_info(); should return the information + * documented there. + */ GHashTable* (*get_ui_info)(void); void (*_purple_reserved1)(void); @@ -64,17 +89,23 @@ void purple_core_quit(void); /** + * <p> * Calls purple_core_quit(). This can be used as the function * passed to purple_timeout_add() when you want to shutdown Purple * in a specified amount of time. When shutting down Purple * from a plugin, you must use this instead of purple_core_quit(); * for an immediate exit, use a timeout value of 0: - * purple_timeout_add(0, purple_core_quitcb, NULL); + * </p> + * + * <code>purple_timeout_add(0, purple_core_quitcb, NULL);</code> + * + * <p> * This is ensures that code from your plugin is not being * executed when purple_core_quit() is called. If the plugin * called purple_core_quit() directly, you would get a core dump * after purple_core_quit() executes and control returns to your * plugin because purple_core_quit() frees all plugins. + * </p> */ gboolean purple_core_quit_cb(gpointer unused); @@ -86,7 +117,8 @@ const char *purple_core_get_version(void); /** - * Returns the ID of the UI that is using the core. + * Returns the ID of the UI that is using the core, as passed to + * purple_core_init(). * * @return The ID of the UI that is currently using the core. */ @@ -95,7 +127,7 @@ /** * Returns a handle to the purple core. * - * This is used for such things as signals. + * This is used to connect to @ref core-signals "core signals". */ PurpleCore *purple_get_core(void); @@ -114,10 +146,10 @@ PurpleCoreUiOps *purple_core_get_ui_ops(void); /** - * Migrates from .gaim to .purple. + * Migrates from <tt>.gaim</tt> to <tt>.purple</tt>. * - * UIs MUST NOT call this if they have been told to use a custom - * user directory. + * UIs <strong>must not</strong> call this if they have been told to use a + * custom user directory. * * @return A boolean indicating success or migration failure. On failure, * the application must display an error to the user and then exit. @@ -125,20 +157,39 @@ gboolean purple_core_migrate(void); /** - * Ensures that only one instance is running. + * Ensures that only one instance is running. If libpurple is built with D-Bus + * support, this checks if another process owns the libpurple bus name and if + * so whether that process is using the same configuration directory as this + * process. * - * @return A boolean such that @c TRUE indicates that this is the first instance, - * whereas @c FALSE indicates that there is another instance running. + * @return @c TRUE if this is the first instance of libpurple running; + * @c FALSE if there is another instance running. * * @since 2.1.0 */ gboolean purple_core_ensure_single_instance(void); /** - * Returns a hashtable containing various information about the UI + * Returns a hash table containing various information about the UI. The + * following well-known entries may be in the table (along with any others the + * UI might choose to include): + * + * <dl> + * <dt><tt>name</tt></dt> + * <dd>the user-readable name for the UI.</dd> + * + * <dt><tt>version</tt></dt> + * <dd>a user-readable description of the current version of the UI.</dd> + * + * <dt><tt>website</tt></dt> + * <dd>the UI's website, such as http://pidgin.im.</dd> + * + * <dt><tt>dev_website</tt></dt> + * <dd>the UI's development/support website, such as http://developer.pidgin.im.</dd> + * </dl> * * @return A GHashTable with strings for keys and values. This - * hash table must not be freed. + * hash table must not be freed and should not be modified. * * @since 2.1.0 *
--- a/libpurple/idle.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/idle.c Thu Nov 20 21:13:56 2008 +0000 @@ -252,7 +252,7 @@ PurpleAccount *account; account = purple_connection_get_account(gc); - idled_accts = g_list_remove(idled_accts, account); + set_account_unidle(account); } static void
--- a/libpurple/internal.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/internal.h Thu Nov 20 21:13:56 2008 +0000 @@ -140,6 +140,14 @@ # define G_MAXUINT32 ((guint32) 0xffffffff) #endif +#ifndef G_MAXSIZE +# if GLIB_SIZEOF_LONG == 8 +# define G_MAXSIZE ((gsize) 0xffffffffffffffff) +# else +# define G_MAXSIZE ((gsize) 0xffffffff) +# endif +#endif + #if GLIB_CHECK_VERSION(2,6,0) # include <glib/gstdio.h> #endif
--- a/libpurple/log.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/log.c Thu Nov 20 21:13:56 2008 +0000 @@ -1083,7 +1083,7 @@ } /* Determine if this (account, name) combination exists as a buddy. */ - if (account != NULL) + if (account != NULL && name != NULL && *name != '\0') set->buddy = (purple_find_buddy(account, name) != NULL); else set->buddy = FALSE;
--- a/libpurple/nat-pmp.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/nat-pmp.c Thu Nov 20 21:13:56 2008 +0000 @@ -35,6 +35,10 @@ #include "signals.h" #include "network.h" +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + #ifdef HAVE_SYS_SYSCTL_H #include <sys/sysctl.h> #endif
--- a/libpurple/plugin.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugin.h Thu Nov 20 21:13:56 2008 +0000 @@ -199,9 +199,10 @@ * Handles the initialization of modules. */ #if !defined(PURPLE_PLUGINS) || defined(PURPLE_STATIC_PRPL) +# define _FUNC_NAME(x) purple_init_##x##_plugin # define PURPLE_INIT_PLUGIN(pluginname, initfunc, plugininfo) \ - gboolean purple_init_##pluginname##_plugin(void);\ - gboolean purple_init_##pluginname##_plugin(void) { \ + gboolean _FUNC_NAME(pluginname)(void);\ + gboolean _FUNC_NAME(pluginname)(void) { \ PurplePlugin *plugin = purple_plugin_new(TRUE, NULL); \ plugin->info = &(plugininfo); \ initfunc((plugin)); \
--- a/libpurple/plugins/autoaccept.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/autoaccept.c Thu Nov 20 21:13:56 2008 +0000 @@ -51,6 +51,7 @@ #define PREF_PATH PREF_PREFIX "/path" #define PREF_STRANGER PREF_PREFIX "/reject_stranger" #define PREF_NOTIFY PREF_PREFIX "/notify" +#define PREF_NEWDIR PREF_PREFIX "/newdir" typedef enum { @@ -116,7 +117,11 @@ { int count = 1; const char *escape; - dirname = g_build_filename(pref, purple_normalize(account, xfer->who), NULL); + + if (purple_prefs_get_bool(PREF_NEWDIR)) + dirname = g_build_filename(pref, purple_normalize(account, xfer->who), NULL); + else + dirname = g_build_filename(pref, NULL); if (!ensure_path_exists(dirname)) { @@ -236,6 +241,10 @@ "(only when there's no conversation with the sender)")); purple_plugin_pref_frame_add(frame, pref); + pref = purple_plugin_pref_new_with_name_and_label(PREF_NEWDIR, + _("Create a new directory for each user")); + purple_plugin_pref_frame_add(frame, pref); + return frame; } @@ -294,6 +303,7 @@ purple_prefs_add_string(PREF_PATH, dirname); purple_prefs_add_bool(PREF_STRANGER, TRUE); purple_prefs_add_bool(PREF_NOTIFY, TRUE); + purple_prefs_add_bool(PREF_NEWDIR, TRUE); g_free(dirname); }
--- a/libpurple/plugins/perl/Makefile.mingw Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/Makefile.mingw Thu Nov 20 21:13:56 2008 +0000 @@ -47,7 +47,7 @@ -lws2_32 \ -lintl \ -lpurple \ - -lperl58 + -lperl510 include $(PIDGIN_COMMON_RULES)
--- a/libpurple/plugins/perl/common/Makefile.mingw Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/common/Makefile.mingw Thu Nov 20 21:13:56 2008 +0000 @@ -5,6 +5,7 @@ # PIDGIN_TREE_TOP := ../../../.. +GCCWARNINGS := -Wno-comment -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wpointer-arith -Wundef -Wno-unused include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = Purple @@ -12,8 +13,6 @@ EXTUTILS ?= C:/perl/lib/ExtUtils PERL_PLUGIN_TOP := .. -CFLAGS += -Wno-comment -Wno-unused - ## ## INCLUDE PATHS ## @@ -77,7 +76,7 @@ ## ## LIBRARIES ## -LIBS = -lperl58 \ +LIBS = -lperl510 \ -lperl \ -lpurple \ -lglib-2.0
--- a/libpurple/plugins/perl/common/Prefs.xs Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/common/Prefs.xs Thu Nov 20 21:13:56 2008 +0000 @@ -1,4 +1,5 @@ #include "module.h" +#include "../perl-handlers.h" MODULE = Purple::Prefs PACKAGE = Purple::Prefs PREFIX = purple_prefs_ PROTOTYPES: ENABLE @@ -62,13 +63,28 @@ void purple_prefs_destroy() +guint +purple_prefs_connect_callback(plugin, name, callback, data = 0); + Purple::Plugin plugin + const char *name + SV *callback + SV *data +CODE: + RETVAL = purple_perl_prefs_connect_callback(plugin, name, callback, data); +OUTPUT: + RETVAL + void -purple_prefs_disconnect_by_handle(handle) - void * handle +purple_prefs_disconnect_by_handle(plugin) + Purple::Plugin plugin +CODE: + purple_perl_pref_cb_clear_for_plugin(plugin); void purple_prefs_disconnect_callback(callback_id) guint callback_id +CODE: + purple_perl_prefs_disconnect_callback(callback_id); gboolean purple_prefs_exists(name)
--- a/libpurple/plugins/perl/perl-common.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/perl-common.c Thu Nov 20 21:13:56 2008 +0000 @@ -32,7 +32,10 @@ static MGVTBL vtbl_free_object = { - NULL, NULL, NULL, NULL, magic_free_object, NULL, NULL + 0, 0, 0, 0, magic_free_object, 0, 0 +#if PERL_API_REVISION > 5 || (PERL_API_REVISION == 5 && PERL_API_VERSION >= 10) + , 0 +#endif }; static SV *
--- a/libpurple/plugins/perl/perl-common.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/perl-common.h Thu Nov 20 21:13:56 2008 +0000 @@ -5,9 +5,9 @@ #ifdef _WIN32 #undef pipe #endif -#include <XSUB.h> #include <EXTERN.h> #include <perl.h> +#include <XSUB.h> /* XXX: perl defines it's own _ but I think it's safe to undef it */ #undef _
--- a/libpurple/plugins/perl/perl-handlers.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/perl-handlers.c Thu Nov 20 21:13:56 2008 +0000 @@ -5,9 +5,10 @@ #include "signals.h" extern PerlInterpreter *my_perl; -static GList *cmd_handlers = NULL; -static GList *signal_handlers = NULL; -static GList *timeout_handlers = NULL; +static GSList *cmd_handlers = NULL; +static GSList *signal_handlers = NULL; +static GSList *timeout_handlers = NULL; +static GSList *pref_handlers = NULL; /* perl < 5.8.0 doesn't define PERL_MAGIC_ext */ #ifndef PERL_MAGIC_ext @@ -70,7 +71,7 @@ STRLEN na; dSP; - gps = (PurplePerlScript *)plugin->info->extra_info; + gps = plugin->info->extra_info; ENTER; SAVETMPS; @@ -131,7 +132,7 @@ STRLEN na; dSP; - gps = (PurplePerlScript *)plugin->info->extra_info; + gps = plugin->info->extra_info; ENTER; SAVETMPS; @@ -212,7 +213,7 @@ { gboolean ret = FALSE; - timeout_handlers = g_list_remove(timeout_handlers, handler); + timeout_handlers = g_slist_remove(timeout_handlers, handler); if (handler->iotag > 0) ret = purple_timeout_remove(handler->iotag); @@ -231,7 +232,7 @@ static void destroy_signal_handler(PurplePerlSignalHandler *handler) { - signal_handlers = g_list_remove(signal_handlers, handler); + signal_handlers = g_slist_remove(signal_handlers, handler); if (handler->callback != NULL) SvREFCNT_dec(handler->callback); @@ -246,7 +247,7 @@ static gboolean perl_timeout_cb(gpointer data) { - PurplePerlTimeoutHandler *handler = (PurplePerlTimeoutHandler *)data; + PurplePerlTimeoutHandler *handler = data; gboolean ret = FALSE; STRLEN na; @@ -282,7 +283,7 @@ static void * perl_signal_cb(va_list args, void *data) { - PurplePerlSignalHandler *handler = (PurplePerlSignalHandler *)data; + PurplePerlSignalHandler *handler = data; void *ret_val = NULL; int i; int count; @@ -414,10 +415,10 @@ find_signal_handler(PurplePlugin *plugin, void *instance, const char *signal) { PurplePerlSignalHandler *handler; - GList *l; + GSList *l; for (l = signal_handlers; l != NULL; l = l->next) { - handler = (PurplePerlSignalHandler *)l->data; + handler = l->data; if (handler->plugin == plugin && handler->instance == instance && @@ -447,9 +448,9 @@ handler->data = (data != NULL && data != &PL_sv_undef ? newSVsv(data) : NULL); - timeout_handlers = g_list_append(timeout_handlers, handler); + timeout_handlers = g_slist_append(timeout_handlers, handler); - handler->iotag = purple_timeout_add(seconds * 1000, perl_timeout_cb, handler); + handler->iotag = purple_timeout_add_seconds(seconds, perl_timeout_cb, handler); return handler->iotag; } @@ -457,15 +458,13 @@ gboolean purple_perl_timeout_remove(guint handle) { - GList *l, *l_next; + PurplePerlTimeoutHandler *handler; + GSList *l, *l_next; for (l = timeout_handlers; l != NULL; l = l_next) { - PurplePerlTimeoutHandler *handler; - + handler = l->data; l_next = l->next; - handler = (PurplePerlTimeoutHandler *)l->data; - if (handler->iotag == handle) return destroy_timeout_handler(handler); } @@ -478,15 +477,13 @@ void purple_perl_timeout_clear_for_plugin(PurplePlugin *plugin) { - GList *l, *l_next; + PurplePerlTimeoutHandler *handler; + GSList *l, *l_next; for (l = timeout_handlers; l != NULL; l = l_next) { - PurplePerlTimeoutHandler *handler; - + handler = l->data; l_next = l->next; - handler = (PurplePerlTimeoutHandler *)l->data; - if (handler->plugin == plugin) destroy_timeout_handler(handler); } @@ -516,7 +513,7 @@ handler->data = (data != NULL && data != &PL_sv_undef ? newSVsv(data) : NULL); - signal_handlers = g_list_append(signal_handlers, handler); + signal_handlers = g_slist_append(signal_handlers, handler); purple_signal_connect_priority_vargs(instance, signal, plugin, PURPLE_CALLBACK(perl_signal_cb), @@ -544,12 +541,11 @@ purple_perl_signal_clear_for_plugin(PurplePlugin *plugin) { PurplePerlSignalHandler *handler; - GList *l, *l_next; + GSList *l, *l_next; for (l = signal_handlers; l != NULL; l = l_next) { l_next = l->next; - - handler = (PurplePerlSignalHandler *)l->data; + handler = l->data; if (handler->plugin == plugin) destroy_signal_handler(handler); @@ -570,7 +566,7 @@ int i = 0, count, ret_value = PURPLE_CMD_RET_OK; STRLEN na; SV *cmdSV, *tmpSV, *convSV; - PurplePerlCmdHandler *handler = (PurplePerlCmdHandler *)data; + PurplePerlCmdHandler *handler = data; dSP; ENTER; @@ -645,7 +641,7 @@ else handler->data = NULL; - cmd_handlers = g_list_append(cmd_handlers, handler); + cmd_handlers = g_slist_append(cmd_handlers, handler); handler->id = purple_cmd_register(command, args, priority, flag, prpl_id, PURPLE_CMD_FUNC(perl_cmd_cb), helpstr, @@ -657,7 +653,7 @@ static void destroy_cmd_handler(PurplePerlCmdHandler *handler) { - cmd_handlers = g_list_remove(cmd_handlers, handler); + cmd_handlers = g_slist_remove(cmd_handlers, handler); if (handler->callback != NULL) SvREFCNT_dec(handler->callback); @@ -673,11 +669,11 @@ void purple_perl_cmd_clear_for_plugin(PurplePlugin *plugin) { - GList *l, *l_next; + PurplePerlCmdHandler *handler; + GSList *l, *l_next; for (l = cmd_handlers; l != NULL; l = l_next) { - PurplePerlCmdHandler *handler = (PurplePerlCmdHandler *)l->data; - + handler = l->data; l_next = l->next; if (handler->plugin == plugin) @@ -688,10 +684,11 @@ static PurplePerlCmdHandler * find_cmd_handler(PurpleCmdId id) { - GList *l; + PurplePerlCmdHandler *handler; + GSList *l; for (l = cmd_handlers; l != NULL; l = l->next) { - PurplePerlCmdHandler *handler = (PurplePerlCmdHandler *)l->data; + handler = (PurplePerlCmdHandler *)l->data; if (handler->id == id) return handler; @@ -715,3 +712,141 @@ purple_cmd_unregister(id); destroy_cmd_handler(handler); } + +static void +perl_pref_cb(const char *name, PurplePrefType type, gconstpointer value, + gpointer data) +{ + PurplePerlPrefsHandler *handler = data; + STRLEN na; + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(sp); + XPUSHs(sv_2mortal(newSVpv(name, 0))); + + XPUSHs(sv_2mortal(newSViv(type))); + + switch(type) { + case PURPLE_PREF_INT: + XPUSHs(sv_2mortal(newSViv(GPOINTER_TO_INT(value)))); + break; + case PURPLE_PREF_BOOLEAN: + XPUSHs((GPOINTER_TO_INT(value) == FALSE) ? &PL_sv_no : &PL_sv_yes); + break; + case PURPLE_PREF_STRING: + case PURPLE_PREF_PATH: + XPUSHs(sv_2mortal(newSVGChar(value))); + break; + case PURPLE_PREF_STRING_LIST: + case PURPLE_PREF_PATH_LIST: + { + AV* av = newAV(); + const GList *l = value; + + /* Append stuff backward to preserve order */ + while (l && l->next) l = l->next; + while (l) { + av_push(av, sv_2mortal(newSVGChar(l->data))); + l = l->prev; + } + XPUSHs(sv_2mortal(newRV_noinc((SV *) av))); + } break; + default: + case PURPLE_PREF_NONE: + XPUSHs(&PL_sv_undef); + break; + } + + XPUSHs((SV *)handler->data); + PUTBACK; + call_sv(handler->callback, G_EVAL | G_VOID | G_DISCARD); + SPAGAIN; + + if (SvTRUE(ERRSV)) { + purple_debug_error("perl", + "Perl prefs callback function exited abnormally: %s\n", + SvPV(ERRSV, na)); + } + + PUTBACK; + FREETMPS; + LEAVE; +} + +guint +purple_perl_prefs_connect_callback(PurplePlugin *plugin, const char *name, + SV *callback, SV *data) +{ + PurplePerlPrefsHandler *handler; + + if (plugin == NULL) { + croak("Invalid handle in adding perl prefs handler.\n"); + return 0; + } + + handler = g_new0(PurplePerlPrefsHandler, 1); + + handler->plugin = plugin; + handler->callback = (callback != NULL && callback != &PL_sv_undef + ? newSVsv(callback) : NULL); + handler->data = (data != NULL && data != &PL_sv_undef + ? newSVsv(data) : NULL); + + pref_handlers = g_slist_prepend(pref_handlers, handler); + + handler->iotag = purple_prefs_connect_callback(plugin, name, perl_pref_cb, handler); + + return handler->iotag; +} + +static void +destroy_prefs_handler(PurplePerlPrefsHandler *handler) +{ + pref_handlers = g_slist_remove(pref_handlers, handler); + + if (handler->iotag > 0) + purple_prefs_disconnect_callback(handler->iotag); + + if (handler->callback != NULL) + SvREFCNT_dec(handler->callback); + + if (handler->data != NULL) + SvREFCNT_dec(handler->data); + + g_free(handler); +} + +void purple_perl_prefs_disconnect_callback(guint callback_id) +{ + GSList *l, *l_next; + PurplePerlPrefsHandler *handler; + + for (l = pref_handlers; l != NULL; l = l_next) { + l_next = l->next; + handler = l->data; + + if (handler->iotag == callback_id) { + destroy_prefs_handler(handler); + return; + } + } + + purple_debug_info("perl", "No prefs handler found with handle %u.\n", + callback_id); +} + +void purple_perl_pref_cb_clear_for_plugin(PurplePlugin *plugin) +{ + GSList *l, *l_next; + PurplePerlPrefsHandler *handler; + + for (l = pref_handlers; l != NULL; l = l_next) { + l_next = l->next; + handler = l->data; + + if (handler->plugin == plugin) + destroy_prefs_handler(handler); + } +}
--- a/libpurple/plugins/perl/perl-handlers.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/perl-handlers.h Thu Nov 20 21:13:56 2008 +0000 @@ -15,8 +15,8 @@ PurpleCmdId id; SV *callback; SV *data; - char *prpl_id; - char *cmd; + gchar *prpl_id; + gchar *cmd; PurplePlugin *plugin; } PurplePerlCmdHandler; @@ -31,7 +31,7 @@ typedef struct { - char *signal; + gchar *signal; SV *callback; SV *data; void *instance; @@ -39,8 +39,17 @@ } PurplePerlSignalHandler; +typedef struct +{ + SV *callback; + SV *data; + PurplePlugin *plugin; + int iotag; + +} PurplePerlPrefsHandler; + void purple_perl_plugin_action_cb(PurplePluginAction * gpa); -GList *purple_perl_plugin_actions(PurplePlugin *plugin, gpointer context); +GList *purple_perl_plugin_actions(PurplePlugin *plugin, gpointer context); PurplePluginPrefFrame *purple_perl_get_plugin_frame(PurplePlugin *plugin); @@ -69,4 +78,8 @@ void purple_perl_cmd_unregister(PurpleCmdId id); void purple_perl_cmd_clear_for_plugin(PurplePlugin *plugin); +guint purple_perl_prefs_connect_callback(PurplePlugin *plugin, const char *name, SV *callback, SV *data); +void purple_perl_prefs_disconnect_callback(guint callback_id); +void purple_perl_pref_cb_clear_for_plugin(PurplePlugin *plugin); + #endif /* _PURPLE_PERL_HANDLERS_H_ */
--- a/libpurple/plugins/perl/perl.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/perl.c Thu Nov 20 21:13:56 2008 +0000 @@ -67,6 +67,10 @@ #undef group /* perl module support */ +#ifdef _WIN32 +EXTERN_C void boot_Win32CORE (pTHX_ CV* cv); +#endif + #ifdef OLD_PERL extern void boot_DynaLoader _((CV * cv)); #else @@ -127,10 +131,14 @@ #endif { char *file = __FILE__; + dXSUB_SYS; /* This one allows dynamic loading of perl modules in perl scripts by * the 'use perlmod;' construction */ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); +#ifdef _WIN32 + newXS("Win32CORE::bootstrap", boot_Win32CORE, file); +#endif } static void @@ -240,20 +248,66 @@ static gboolean probe_perl_plugin(PurplePlugin *plugin) { - /* XXX This would be much faster if I didn't create a new - * PerlInterpreter every time I probed a plugin */ - PerlInterpreter *prober = perl_alloc(); - char *argv[] = {"", plugin->path }; + char *args[] = {"", plugin->path }; + char **argv = args; + int argc = 2, ret; + PerlInterpreter *prober; gboolean status = TRUE; HV *plugin_info; + + PERL_SYS_INIT(&argc, &argv); + + /* XXX This would be much faster if we didn't create a new + * PerlInterpreter every time we probe a plugin */ + prober = perl_alloc(); + PERL_SET_CONTEXT(prober); + PL_perl_destruct_level = 1; perl_construct(prober); - perl_parse(prober, xs_init, 2, argv, NULL); +/* Fix IO redirection to match where pidgin's is going. + * Without this, we lose stdout/stderr unless we redirect to a file */ +#ifdef _WIN32 +{ + PerlIO* newprlIO = PerlIO_open("CONOUT$", "w"); + if (newprlIO) { + int stdout_fd = PerlIO_fileno(PerlIO_stdout()); + int stderr_fd = PerlIO_fileno(PerlIO_stderr()); + PerlIO_close(PerlIO_stdout()); + PerlIO_close(PerlIO_stderr()); + PerlLIO_dup2(PerlIO_fileno(newprlIO), stdout_fd); + PerlLIO_dup2(PerlIO_fileno(newprlIO), stderr_fd); + + PerlIO_close(newprlIO); + } +} +#endif + + ret = perl_parse(prober, xs_init, argc, argv, NULL); - perl_run(prober); + if (ret != 0) { + STRLEN len; + const char * errmsg = "Unknown error"; + if (SvTRUE(ERRSV)) + errmsg = SvPV(ERRSV, len); + purple_debug_error("perl", "Unable to parse plugin %s (%d:%s)\n", + plugin->path, ret, errmsg); + goto cleanup; + } + + ret = perl_run(prober); + + if (ret != 0) { + STRLEN len; + const char * errmsg = "Unknown error"; + if (SvTRUE(ERRSV)) + errmsg = SvPV(ERRSV, len); + purple_debug_error("perl", "Unable to run perl interpreter on plugin %s (%d:%s)\n", + plugin->path, ret, errmsg); + goto cleanup; + } plugin_info = perl_get_hv("PLUGIN_INFO", FALSE); @@ -401,6 +455,7 @@ } } + cleanup: PL_perl_destruct_level = 1; PERL_SET_CONTEXT(prober); perl_destruct(prober); @@ -523,6 +578,7 @@ purple_perl_cmd_clear_for_plugin(plugin); purple_perl_signal_clear_for_plugin(plugin); purple_perl_timeout_clear_for_plugin(plugin); + purple_perl_pref_cb_clear_for_plugin(plugin); destroy_package(gps->package); @@ -578,7 +634,7 @@ load_perl_plugin, /**< load */ unload_perl_plugin, /**< unload */ destroy_perl_plugin, /**< destroy */ - + /* padding */ NULL, NULL,
--- a/libpurple/plugins/perl/scripts/plugin_pref.pl Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/plugins/perl/scripts/plugin_pref.pl Thu Nov 20 21:13:56 2008 +0000 @@ -44,8 +44,8 @@ $ppref = Purple::PluginPref->new_with_name_and_label( "/plugins/core/perl_test/choice", "Choice Preference"); $ppref->set_type(1); - $ppref->add_choice("ch0", $frame); - $ppref->add_choice("ch1", $frame); + $ppref->add_choice("ch0", "ch0-val"); + $ppref->add_choice("ch1", "ch1-val"); $frame->add($ppref); $ppref = Purple::PluginPref->new_with_name_and_label( @@ -56,12 +56,17 @@ return $frame; } +sub pref_cb { + my ($pref, $type, $value, $data) = @_; + + print "pref changed: [$pref]($type)=$value data=$data\n"; +} + sub plugin_init { return %PLUGIN_INFO; } - # This is the sub defined in %PLUGIN_INFO to be called when the plugin is loaded # Note: The plugin has a reference to itself on top of the argument stack. sub plugin_load { @@ -75,7 +80,11 @@ Purple::Prefs::add_bool("/plugins/core/perl_test/bool", 1); Purple::Prefs::add_string("/plugins/core/perl_test/choice", "ch1"); Purple::Prefs::add_string("/plugins/core/perl_test/text", "Foobar"); - + + Purple::Prefs::connect_callback($plugin, "/plugins/core/perl_test", \&pref_cb, "none"); + Purple::Prefs::connect_callback($plugin, "/plugins/core/perl_test/bool", \&pref_cb, "bool"); + Purple::Prefs::connect_callback($plugin, "/plugins/core/perl_test/choice", \&pref_cb, "choice"); + Purple::Prefs::connect_callback($plugin, "/plugins/core/perl_test/text", \&pref_cb, "text"); print "\n\n" . "#" x 80 . "\n\n"; }
--- a/libpurple/protocols/bonjour/bonjour.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Thu Nov 20 21:13:56 2008 +0000 @@ -641,7 +641,6 @@ struct passwd *info; #endif const char *fullname = NULL, *splitpoint, *tmp; - char hostname[255]; gchar *conv = NULL; #ifndef _WIN32 @@ -691,13 +690,7 @@ /* Try to figure out a good host name to use */ /* TODO: Avoid 'localhost,' if possible */ - if (gethostname(hostname, sizeof(hostname)) != 0) { - purple_debug_warning("bonjour", "Error when getting host name: %s. Using \"localhost.\"\n", - g_strerror(errno)); - strcpy(hostname, "localhost"); - } - hostname[sizeof(hostname) - 1] = '\0'; - default_hostname = g_strdup(hostname); + default_hostname = g_strdup(purple_get_host_name()); } static void
--- a/libpurple/protocols/bonjour/bonjour_ft.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Thu Nov 20 21:13:56 2008 +0000 @@ -300,6 +300,8 @@ } if (xf->proxy_connection != NULL) purple_proxy_connect_cancel(xf->proxy_connection); + if (xf->proxy_info != NULL) + purple_proxy_info_destroy(xf->proxy_info); if (xf->listen_data != NULL) purple_network_listen_cancel(xf->listen_data); g_free(xf->iq_id); @@ -802,6 +804,8 @@ xmlnode *q_node, *tmp_node; BonjourData *bd; + xf->proxy_connection = NULL; + if(source < 0) { purple_debug_error("bonjour", "Error connecting via SOCKS5 - %s\n", error_message ? error_message : "(null)"); @@ -815,9 +819,6 @@ bd = xf->data; - purple_proxy_info_destroy(xf->proxy_info); - xf->proxy_connection = NULL; - xf->proxy_info = NULL; /* Here, start the file transfer.*/ /* Notify Initiator of Connection */ @@ -871,8 +872,6 @@ xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel"); /* Cancel the connection */ purple_xfer_cancel_local(xfer); - /*purple_proxy_info_destroy(xf->proxy_info); - xf->proxy_info = NULL;*/ } }
--- a/libpurple/protocols/bonjour/jabber.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Thu Nov 20 21:13:56 2008 +0000 @@ -672,7 +672,6 @@ bonjour_jabber_start(BonjourJabber *jdata) { struct sockaddr_in my_addr; - int yes = 1; int i; gboolean bind_successful; @@ -686,16 +685,6 @@ return -1; } - /* Make the socket reusable */ - if (setsockopt(jdata->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0) - { - purple_debug_error("bonjour", "Error setting socket options: %s\n", g_strerror(errno)); - purple_connection_error_reason (jdata->account->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Error setting socket options")); - return -1; - } - memset(&my_addr, 0, sizeof(struct sockaddr_in)); my_addr.sin_family = AF_INET; @@ -709,6 +698,8 @@ bind_successful = TRUE; break; } + + purple_debug_info("bonjour", "Unable to bind to port %u.(%s)\n", jdata->port, g_strerror(errno)); jdata->port++; }
--- a/libpurple/protocols/gg/gg.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/gg/gg.c Thu Nov 20 21:13:56 2008 +0000 @@ -527,9 +527,11 @@ form->offset = g_strdup(form->last_uin); ggp_search_remove(info->searches, form->seq); + purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u", form->seq); seq = ggp_search_start(gc, form); ggp_search_add(info->searches, seq, form); + purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u", seq); } /* }}} */ @@ -607,6 +609,7 @@ seq = ggp_search_start(gc, form); ggp_search_add(info->searches, seq, form); + purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u", seq); } /* }}} */ @@ -991,6 +994,7 @@ GGPInfo *info = form->user_data; ggp_search_remove(info->searches, form->seq); + purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u", form->seq); ggp_search_form_destroy(form); } /* }}} */ @@ -1206,7 +1210,7 @@ seq = gg_pubdir50_seq(req); form = ggp_search_get(info->searches, seq); - + purple_debug_info("gg", "ggp_pubdir_reply_handler(): seq %u --> form %p", seq, form); /* * this can happen when user will request more results * and close the results window before they arrive. @@ -1819,6 +1823,7 @@ seq = ggp_search_start(gc, form); ggp_search_add(info->searches, seq, form); + purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq); } /* }}} */
--- a/libpurple/protocols/gg/search.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/gg/search.h Thu Nov 20 21:13:56 2008 +0000 @@ -130,7 +130,7 @@ * @param gc PurpleConnection. * @param form Filled in GGPSearchForm. * - * @return Sequence number of a search or 0 if an error occured. + * @return Sequence number of a search or 0 if an error occurred. */ guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form);
--- a/libpurple/protocols/irc/cmds.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/irc/cmds.c Thu Nov 20 21:13:56 2008 +0000 @@ -68,6 +68,31 @@ return 0; } +int irc_cmd_ctcp(struct irc_conn *irc, const char *cmd, const char *target, const char **args) +{ + /* we have defined args as args[0] is target and args[1] is ctcp command */ + char *buf; + GString *string; + + /* check if we have args */ + if (!args || !args[0] || !args[1]) + return 0; + + /* TODO:strip newlines or send each line as separate ctcp or something + * actually, this shouldn't be done here but somewhere else since irc should support escaping newlines */ + + string = g_string_new(args[1]); + g_string_prepend_c (string,'\001'); + g_string_append_c (string,'\001'); + buf = irc_format(irc, "vn:", "PRIVMSG", args[0], string->str); + g_string_free(string,TRUE); + + irc_send(irc, buf); + g_free(buf); + + return 1; +} + int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *target, const char **args) { PurpleConnection *gc = purple_account_get_connection(irc->account);
--- a/libpurple/protocols/irc/irc.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/irc/irc.c Thu Nov 20 21:13:56 2008 +0000 @@ -360,11 +360,11 @@ static gboolean do_login(PurpleConnection *gc) { char *buf, *tmp = NULL; - char hostname[256]; + char *hostname, *server; + const char *hosttmp; const char *username, *realname; struct irc_conn *irc = gc->proto_data; const char *pass = purple_connection_get_password(gc); - int ret; if (pass && *pass) { buf = irc_format(irc, "vv", "PASS", pass); @@ -375,13 +375,6 @@ g_free(buf); } - - ret = gethostname(hostname, sizeof(hostname)); - hostname[sizeof(hostname) - 1] = '\0'; - if (ret < 0 || hostname[0] == '\0') { - purple_debug_warning("irc", "gethostname() failed -- is your hostname set?"); - strcpy(hostname, "localhost"); - } realname = purple_account_get_string(irc->account, "realname", ""); username = purple_account_get_string(irc->account, "username", ""); @@ -396,9 +389,29 @@ } } - buf = irc_format(irc, "vvvv:", "USER", tmp ? tmp : username, hostname, irc->server, - strlen(realname) ? realname : IRC_DEFAULT_ALIAS); + hosttmp = purple_get_host_name(); + if (*hosttmp == ':') { + /* This is either an IPv6 address, or something which + * doesn't belong here. Either way, we need to escape + * it. */ + hostname = g_strdup_printf("0%s", hosttmp); + } else { + /* Ugly, I know. */ + hostname = g_strdup(hosttmp); + } + + if (*irc->server == ':') { + /* Same as hostname, above. */ + server = g_strdup_printf("0%s", irc->server); + } else { + server = g_strdup(irc->server); + } + + buf = irc_format(irc, "vvvv:", "USER", tmp ? tmp : username, hostname, server, + strlen(realname) ? realname : IRC_DEFAULT_ALIAS); g_free(tmp); + g_free(hostname); + g_free(server); if (irc_send(irc, buf) < 0) { g_free(buf); return FALSE; @@ -976,6 +989,9 @@ option = purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_bool_new(_("Auto-detect incoming UTF-8"), "autodetect_utf8", IRC_DEFAULT_AUTODETECT); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + option = purple_account_option_string_new(_("Username"), "username", ""); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
--- a/libpurple/protocols/irc/irc.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/irc/irc.h Thu Nov 20 21:13:56 2008 +0000 @@ -35,6 +35,7 @@ #define IRC_DEFAULT_SSL_PORT 994 #define IRC_DEFAULT_CHARSET "UTF-8" +#define IRC_DEFAULT_AUTODETECT FALSE #define IRC_DEFAULT_ALIAS "purple" #define IRC_DEFAULT_QUIT "Leaving." @@ -164,6 +165,7 @@ int irc_cmd_default(struct irc_conn *irc, const char *cmd, const char *target, const char **args); int irc_cmd_away(struct irc_conn *irc, const char *cmd, const char *target, const char **args); +int irc_cmd_ctcp(struct irc_conn *irc, const char *cmd, const char *target, const char **args); int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *target, const char **args); int irc_cmd_ctcp_version(struct irc_conn *irc, const char *cmd, const char *target, const char **args); int irc_cmd_invite(struct irc_conn *irc, const char *cmd, const char *target, const char **args);
--- a/libpurple/protocols/irc/msgs.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/irc/msgs.c Thu Nov 20 21:13:56 2008 +0000 @@ -215,10 +215,8 @@ /* This is an extended syntax, not in RFC 1459 */ int t1 = atoi(args[4]); time_t t2 = time(NULL); - msg = g_strdup_printf(ngettext("Ban on %s by %s, set %ld second ago", - "Ban on %s by %s, set %ld seconds ago", - t2 - t1), - args[2], args[3], t2 - t1); + msg = g_strdup_printf(_("Ban on %s by %s, set %s ago"), + args[2], args[3], purple_str_seconds_to_string(t2 - t1)); } else { msg = g_strdup_printf(_("Ban on %s"), args[2]); } @@ -1007,6 +1005,9 @@ irc->reqnick = newnick; irc->nickused = TRUE; + purple_connection_set_display_name( + purple_account_get_connection(irc->account), newnick); + buf = irc_format(irc, "vn", "NICK", newnick); irc_send(irc, buf); g_free(buf);
--- a/libpurple/protocols/irc/parse.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/irc/parse.c Thu Nov 20 21:13:56 2008 +0000 @@ -123,6 +123,7 @@ } _irc_cmds[] = { { "action", ":", irc_cmd_ctcp_action, N_("action <action to perform>: Perform an action.") }, { "away", ":", irc_cmd_away, N_("away [message]: Set an away message, or use no message to return from being away.") }, + { "ctcp", "t:", irc_cmd_ctcp, N_("ctcp <nick> <msg>: sends ctcp msg to nick.") }, { "chanserv", ":", irc_cmd_service, N_("chanserv: Send a command to chanserv") }, { "deop", ":", irc_cmd_op, N_("deop <nick1> [nick2] ...: Remove channel operator status from someone. You must be a channel operator to do this.") }, { "devoice", ":", irc_cmd_op, N_("devoice <nick1> [nick2] ...: Remove channel voice status from someone, preventing them from speaking if the channel is moderated (+m). You must be a channel operator to do this.") }, @@ -252,6 +253,7 @@ char *utf8 = NULL; const gchar *charset, *enclist; gchar **encodings; + gboolean autodetect; int i; enclist = purple_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); @@ -262,6 +264,12 @@ return purple_utf8_salvage(string); } + autodetect = purple_account_get_bool(irc->account, "autodetect_utf8", IRC_DEFAULT_AUTODETECT); + + if (autodetect && g_utf8_validate(string, -1, NULL)) { + return g_strdup(string); + } + for (i = 0; encodings[i] != NULL; i++) { charset = encodings[i]; while (*charset == ' ')
--- a/libpurple/protocols/jabber/Makefile.mingw Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/Makefile.mingw Thu Nov 20 21:13:56 2008 +0000 @@ -89,10 +89,6 @@ LIB_PATHS += -L$(CYRUS_SASL_TOP)/bin LIBS += -llibsasl CYRUS_SASL_DLLS = \ - $(CYRUS_SASL_TOP)/bin/comerr32.dll \ - $(CYRUS_SASL_TOP)/bin/gssapi32.dll \ - $(CYRUS_SASL_TOP)/bin/k5sprt32.dll \ - $(CYRUS_SASL_TOP)/bin/krb5_32.dll \ $(CYRUS_SASL_TOP)/bin/libsasl.dll CYRUS_SASL_PLUGINS = \
--- a/libpurple/protocols/jabber/buddy.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.c Thu Nov 20 21:13:56 2008 +0000 @@ -1793,22 +1793,6 @@ } } -void jabber_buddy_get_info_chat(PurpleConnection *gc, int id, - const char *resource) -{ - JabberStream *js = gc->proto_data; - JabberChat *chat = jabber_chat_find_by_id(js, id); - char *full_jid; - - if(!chat) - return; - - full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, resource); - jabber_buddy_get_info_for_jid(js, full_jid); - g_free(full_jid); -} - - static void jabber_buddy_set_invisibility(JabberStream *js, const char *who, gboolean invisible) {
--- a/libpurple/protocols/jabber/buddy.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/buddy.h Thu Nov 20 21:13:56 2008 +0000 @@ -96,8 +96,6 @@ void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource); const char *jabber_buddy_get_status_msg(JabberBuddy *jb); void jabber_buddy_get_info(PurpleConnection *gc, const char *who); -void jabber_buddy_get_info_chat(PurpleConnection *gc, int id, - const char *resource); GList *jabber_blist_node_menu(PurpleBlistNode *node);
--- a/libpurple/protocols/jabber/chat.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/chat.c Thu Nov 20 21:13:56 2008 +0000 @@ -342,12 +342,18 @@ { JabberStream *js = gc->proto_data; JabberChat *chat; + JabberChatMember *jcm; chat = jabber_chat_find_by_id(js, id); if(!chat) return NULL; + jcm = g_hash_table_lookup(chat->members, who); + if (jcm != NULL && jcm->jid) + return g_strdup(jcm->jid); + + return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who); }
--- a/libpurple/protocols/jabber/disco.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/disco.c Thu Nov 20 21:13:56 2008 +0000 @@ -73,6 +73,15 @@ "jid='%s' host='%s' port='%d' zeroconf='%s'\n", from ? from : "", sh->host ? sh->host : "", sh->port, sh->zeroconf ? sh->zeroconf : ""); + + /* TODO: When we support zeroconf proxies, fix this to handle them */ + if (!(sh->jid && sh->host && sh->port > 0)) { + g_free(sh->jid); + g_free(sh->host); + g_free(sh->zeroconf); + g_free(sh); + js->bs_proxies = g_list_remove(js->bs_proxies, sh); + } } @@ -280,6 +289,7 @@ static void jabber_disco_finish_server_info_result_cb(JabberStream *js) { + const char *ft_proxies; jabber_vcard_fetch_mine(js); @@ -290,11 +300,44 @@ /* Send initial presence; this will trigger receipt of presence for contacts on the roster */ jabber_presence_send(js->gc->account, NULL); - + if (js->server_caps & JABBER_CAP_ADHOC) { /* The server supports ad-hoc commands, so let's request the list */ jabber_adhoc_server_get_list(js); } + + /* If there are manually specified bytestream proxies, query them */ + ft_proxies = purple_account_get_string(js->gc->account, "ft_proxies", NULL); + if (ft_proxies) { + JabberIq *iq; + JabberBytestreamsStreamhost *sh; + int i; + char *tmp; + gchar **ft_proxy_list = g_strsplit(ft_proxies, ",", 0); + + for(i = 0; ft_proxy_list[i]; i++) { + g_strstrip(ft_proxy_list[i]); + if(!(*ft_proxy_list[i])) + continue; + + /* We used to allow specifying a port directly here; get rid of it */ + if((tmp = strchr(ft_proxy_list[i], ':'))) + *tmp = '\0'; + + sh = g_new0(JabberBytestreamsStreamhost, 1); + sh->jid = g_strdup(ft_proxy_list[i]); + js->bs_proxies = g_list_prepend(js->bs_proxies, sh); + + iq = jabber_iq_new_query(js, JABBER_IQ_GET, + "http://jabber.org/protocol/bytestreams"); + xmlnode_set_attrib(iq->node, "to", sh->jid); + jabber_iq_set_callback(iq, jabber_disco_bytestream_server_cb, sh); + jabber_iq_send(iq); + } + + g_strfreev(ft_proxy_list); + } + } static void
--- a/libpurple/protocols/jabber/google.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/google.c Thu Nov 20 21:13:56 2008 +0000 @@ -314,11 +314,6 @@ buddies = buddies->next; } - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster"); - - query = xmlnode_get_child(iq->node, "query"); - item = xmlnode_new_child(query, "item"); - xmlnode_set_attrib(item, "jid", who); xmlnode_set_attrib(item, "name", b->alias ? b->alias : ""); xmlnode_set_attrib(item, "gr:t", "B"); @@ -385,11 +380,6 @@ buddies = buddies->next; } - iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster"); - - query = xmlnode_get_child(iq->node, "query"); - item = xmlnode_new_child(query, "item"); - xmlnode_set_attrib(item, "jid", who); xmlnode_set_attrib(item, "name", b->alias ? b->alias : ""); xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
--- a/libpurple/protocols/jabber/jabber.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.c Thu Nov 20 21:13:56 2008 +0000 @@ -68,6 +68,7 @@ GHashTable *jabber_contact_info = NULL; static void jabber_unregister_account_cb(JabberStream *js); +static void try_srv_connect(JabberStream *js); static void jabber_stream_init(JabberStream *js) { @@ -278,9 +279,42 @@ purple_circ_buffer_mark_read(js->write_buffer, ret); } +static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len) +{ + int ret; + gboolean success = TRUE; + + if (len == -1) + len = strlen(data); + + if (js->writeh == 0) + ret = jabber_do_send(js, data, len); + else { + ret = -1; + errno = EAGAIN; + } + + if (ret < 0 && errno != EAGAIN) { + purple_connection_error_reason (js->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Write error")); + success = FALSE; + } else if (ret < len) { + if (ret < 0) + ret = 0; + if (js->writeh == 0) + js->writeh = purple_input_add( + js->gsc ? js->gsc->fd : js->fd, + PURPLE_INPUT_WRITE, jabber_send_cb, js); + purple_circ_buffer_append(js->write_buffer, + data + ret, len - ret); + } + + return success; +} + void jabber_send_raw(JabberStream *js, const char *data, int len) { - int ret; /* because printing a tab to debug every minute gets old */ if(strcmp(data, "\t")) @@ -289,55 +323,33 @@ /* If we've got a security layer, we need to encode the data, * splitting it on the maximum buffer length negotiated */ - + purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data); if (data == NULL) return; - + #ifdef HAVE_CYRUS_SASL if (js->sasl_maxbuf>0) { - int pos; + int pos = 0; if (!js->gsc && js->fd<0) return; - pos = 0; + if (len == -1) len = strlen(data); + while (pos < len) { int towrite; const char *out; unsigned olen; - if ((len - pos) < js->sasl_maxbuf) - towrite = len - pos; - else - towrite = js->sasl_maxbuf; + towrite = MIN((len - pos), js->sasl_maxbuf); sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); pos += towrite; - if (js->writeh == 0) - ret = jabber_do_send(js, out, olen); - else { - ret = -1; - errno = EAGAIN; - } - - if (ret < 0 && errno != EAGAIN) - purple_connection_error_reason (js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Write error")); - else if (ret < olen) { - if (ret < 0) - ret = 0; - if (js->writeh == 0) - js->writeh = purple_input_add( - js->gsc ? js->gsc->fd : js->fd, - PURPLE_INPUT_WRITE, - jabber_send_cb, js); - purple_circ_buffer_append(js->write_buffer, - out + ret, olen - ret); - } + if (!do_jabber_send_raw(js, out, olen)) + break; } return; } @@ -354,29 +366,8 @@ _("Someone tried to send non-XML in a Jabber world.")); } } else { - if (js->writeh == 0) - ret = jabber_do_send(js, data, len); - else { - ret = -1; - errno = EAGAIN; - } - - if (ret < 0 && errno != EAGAIN) - purple_connection_error_reason (js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Write error")); - else if (ret < len) { - if (ret < 0) - ret = 0; - if (js->writeh == 0) - js->writeh = purple_input_add( - js->gsc ? js->gsc->fd : js->fd, - PURPLE_INPUT_WRITE, jabber_send_cb, js); - purple_circ_buffer_append(js->write_buffer, - data + ret, len - ret); - } + do_jabber_send_raw(js, data, len); } - return; } int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len) @@ -402,9 +393,9 @@ g_free(txt); } -static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) +static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused) { - purple_timeout_remove(GPOINTER_TO_INT(timeout)); + purple_timeout_remove(js->keepalive_timeout); js->keepalive_timeout = -1; } @@ -428,7 +419,7 @@ xmlnode_set_namespace(ping, "urn:xmpp:ping"); js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc); - jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout)); + jabber_iq_set_callback(iq, jabber_pong_cb, NULL); jabber_iq_send(iq); } } @@ -457,12 +448,17 @@ jabber_stream_init(js); } - if(errno == EAGAIN) + if(len < 0 && errno == EAGAIN) return; - else + else { + if (len == 0) + purple_debug_info("jabber", "Server closed the connection.\n"); + else + purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno)); purple_connection_error_reason (js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Read Error")); + } } static void @@ -497,9 +493,13 @@ jabber_parser_process(js, buf, len); if(js->reinit) jabber_stream_init(js); - } else if(errno == EAGAIN) { + } else if(len < 0 && errno == EAGAIN) { return; } else { + if (len == 0) + purple_debug_info("jabber", "Server closed the connection.\n"); + else + purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno)); purple_connection_error_reason (js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Read Error")); @@ -578,11 +578,19 @@ JabberStream *js = gc->proto_data; if (source < 0) { - purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); - purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc); + if (js->srv_rec != NULL) { + purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error); + try_srv_connect(js); + } else { + purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); + purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc); + } return; } + g_free(js->srv_rec); + js->srv_rec = NULL; + js->fd = source; if(js->state == JABBER_STREAM_CONNECTING) @@ -617,37 +625,62 @@ jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc); } -static void jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port) +static gboolean jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port, + gboolean fatal_failure) { /* host should be used in preference to domain to * allow SASL authentication to work with FQDN of the server, * but we use domain as fallback for when users enter IP address * in connect server */ + g_free(js->serverFQDN); if (purple_ip_address_is_valid(host)) js->serverFQDN = g_strdup(domain); else js->serverFQDN = g_strdup(host); if (purple_proxy_connect(js->gc, js->gc->account, host, - port, jabber_login_callback, js->gc) == NULL) - purple_connection_error_reason (js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to create socket")); + port, jabber_login_callback, js->gc) == NULL) { + if (fatal_failure) { + purple_connection_error_reason (js->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to create socket")); + } + + return FALSE; + } + + return TRUE; +} + +static void try_srv_connect(JabberStream *js) +{ + while (js->srv_rec != NULL && js->srv_rec_idx < js->max_srv_rec_idx) { + PurpleSrvResponse *tmp_resp = js->srv_rec + (js->srv_rec_idx++); + if (jabber_login_connect(js, tmp_resp->hostname, tmp_resp->hostname, tmp_resp->port, FALSE)) + return; + } + + g_free(js->srv_rec); + js->srv_rec = NULL; + + /* Fall back to the defaults (I'm not sure if we should actually do this) */ + jabber_login_connect(js, js->user->domain, js->user->domain, + purple_account_get_int(js->gc->account, "port", 5222), TRUE); } static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data) { - JabberStream *js; - - js = data; + JabberStream *js = data; js->srv_query_data = NULL; if(results) { - jabber_login_connect(js, resp->hostname, resp->hostname, resp->port); - g_free(resp); + js->srv_rec = resp; + js->srv_rec_idx = 0; + js->max_srv_rec_idx = results; + try_srv_connect(js); } else { jabber_login_connect(js, js->user->domain, js->user->domain, - purple_account_get_int(js->gc->account, "port", 5222)); + purple_account_get_int(js->gc->account, "port", 5222), TRUE); } } @@ -729,7 +762,7 @@ * invoke the magic of SRV lookups, to figure out host and port */ if(!js->gsc) { if(connect_server[0]) { - jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222)); + jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); } else { js->srv_query_data = purple_srv_resolve("xmpp-client", "tcp", js->user->domain, srv_resolved_cb, js); @@ -1210,7 +1243,7 @@ if (connect_server[0]) { jabber_login_connect(js, js->user->domain, server, purple_account_get_int(account, - "port", 5222)); + "port", 5222), TRUE); } else { js->srv_query_data = purple_srv_resolve("xmpp-client", "tcp", @@ -1381,7 +1414,10 @@ if (js->keepalive_timeout != -1) purple_timeout_remove(js->keepalive_timeout); - + + g_free(js->srv_rec); + js->srv_rec = NULL; + g_free(js); gc->proto_data = NULL;
--- a/libpurple/protocols/jabber/jabber.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/jabber.h Thu Nov 20 21:13:56 2008 +0000 @@ -203,6 +203,10 @@ /* A purple timeout tag for the keepalive */ int keepalive_timeout; + PurpleSrvResponse *srv_rec; + guint srv_rec_idx; + guint max_srv_rec_idx; + /* BOSH stuff*/ gboolean use_bosh; PurpleBOSHConnection bosh;
--- a/libpurple/protocols/jabber/libxmpp.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Thu Nov 20 21:13:56 2008 +0000 @@ -89,7 +89,7 @@ jabber_message_send_chat, /* chat_send */ jabber_keepalive, /* keepalive */ jabber_register_account, /* register_user */ - jabber_buddy_get_info_chat, /* get_cb_info */ + NULL, /* get_cb_info */ NULL, /* get_cb_away */ jabber_roster_alias_change, /* alias_buddy */ jabber_roster_group_change, /* group_buddy */ @@ -194,6 +194,7 @@ { #ifdef HAVE_CYRUS_SASL #ifdef _WIN32 + UINT old_error_mode; gchar *sasldir; #endif int ret; @@ -236,7 +237,7 @@ option = purple_account_option_string_new(_("File transfer proxies"), "ft_proxies", /* TODO: Is this an acceptable default? */ - "proxy.jabber.org:7777"); + "proxy.jabber.org"); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); @@ -250,10 +251,16 @@ sasldir = g_build_filename(wpurple_install_dir(), "sasl2", NULL); sasl_set_path(SASL_PATH_TYPE_PLUGIN, sasldir); g_free(sasldir); + /* Suppress error popups for failing to load sasl plugins */ + old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); #endif if ((ret = sasl_client_init(NULL)) != SASL_OK) { purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret); } +#ifdef _WIN32 + /* Restore the original error mode */ + SetErrorMode(old_error_mode); +#endif #endif jabber_register_commands();
--- a/libpurple/protocols/jabber/parser.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/parser.c Thu Nov 20 21:13:56 2008 +0000 @@ -132,6 +132,18 @@ xmlnode_insert_data(js->current, (const char*) text, text_len); } +static void +jabber_parser_structured_error_handler(void *user_data, xmlErrorPtr error) +{ + JabberStream *js = user_data; + + purple_debug_error("jabber", "XML parser error for JabberStream %p: " + "Domain %i, code %i, level %i: %s\n", + js, + error->domain, error->code, error->level, + (error->message ? error->message : "(null)")); +} + static xmlSAXHandler jabber_parser_libxml = { NULL, /*internalSubset*/ NULL, /*isStandalone*/ @@ -164,7 +176,7 @@ NULL, /*_private*/ jabber_parser_element_start_libxml, /*startElementNs*/ jabber_parser_element_end_libxml, /*endElementNs*/ - NULL /*serror*/ + jabber_parser_structured_error_handler /*serror*/ }; void @@ -187,15 +199,25 @@ void jabber_parser_process(JabberStream *js, const char *buf, int len) { - if (js->context == NULL) { + int ret; + + if (js->context == NULL) { /* libxml inconsistently starts parsing on creating the * parser, so do a ParseChunk right afterwards to force it. */ js->context = xmlCreatePushParserCtxt(&jabber_parser_libxml, js, buf, len, NULL); xmlParseChunk(js->context, "", 0, 0); - } else if (xmlParseChunk(js->context, buf, len, 0) < 0) { - purple_connection_error_reason (js->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("XML Parse error")); + } else if ((ret = xmlParseChunk(js->context, buf, len, 0)) != XML_ERR_OK) { + purple_debug_error("jabber", "xmlParseChunk returned error %i", ret); + + if ((ret >= XML_ERR_INVALID_HEX_CHARREF) && (ret <= XML_ERR_INVALID_CHAR)) { + /* If the error involves an invalid character, just drop this message. + * We'll create a new parser next time it's needed. */ + jabber_parser_free(js); + } else { + purple_connection_error_reason (js->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("XML Parse error")); + } } }
--- a/libpurple/protocols/jabber/pep.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/pep.h Thu Nov 20 21:13:56 2008 +0000 @@ -56,7 +56,7 @@ * @parameter id The item id of the requested item (may be NULL) * @parameter cb The callback to be used when this item is received * - * The items element passed to the callback will be NULL if any error occured (like a permission error, node doesn't exist etc.) + * The items element passed to the callback will be NULL if any error occurred (like a permission error, node doesn't exist etc.) */ void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb);
--- a/libpurple/protocols/jabber/presence.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/presence.c Thu Nov 20 21:13:56 2008 +0000 @@ -141,6 +141,11 @@ /* check for buzz support */ allowBuzz = purple_status_get_attr_boolean(status,"buzz"); /* changing the buzz state has to trigger a re-broadcasting of the presence for caps */ + + if (js->googletalk && stripped == NULL && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { + tune = purple_presence_get_status(p, "tune"); + stripped = jabber_google_presence_outgoing(tune); + } #define CHANGED(a,b) ((!a && b) || (a && a[0] == '\0' && b && b[0] != '\0') || \ (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b))) @@ -149,11 +154,6 @@ js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) { js->allowBuzz = allowBuzz; - if (js->googletalk && stripped == NULL && purple_presence_is_status_primitive_active(p, PURPLE_STATUS_TUNE)) { - tune = purple_presence_get_status(p, "tune"); - stripped = jabber_google_presence_outgoing(tune); - } - presence = jabber_presence_create_js(js, state, stripped, priority); if(js->avatar_hash) { @@ -178,9 +178,9 @@ js->old_avatarhash = g_strdup(js->avatar_hash); js->old_state = state; js->old_priority = priority; - g_free(stripped); } - + g_free(stripped); + /* next, check if there are any changes to the tune values */ tune = purple_presence_get_status(p, "tune"); if (tune && purple_status_is_active(tune)) {
--- a/libpurple/protocols/jabber/roster.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/roster.c Thu Nov 20 21:13:56 2008 +0000 @@ -316,7 +316,6 @@ { JabberStream *js = gc->proto_data; char *who; - GSList *groups = NULL; JabberBuddy *jb; JabberBuddyResource *jbr; char *my_bare_jid; @@ -329,20 +328,7 @@ jb = jabber_buddy_find(js, buddy->name, FALSE); - /* - * For some reason if we're waiting for our subscription request - * to be approved and we try to add the buddy to another group - * then we remove the buddy from the old group. I don't understand - * the rationale for this, can someone please explain it? It seems - * like we should pass NULL as the groups parameter to - * jabber_roster_update(). - */ - if(!jb || !(jb->subscription & JABBER_SUB_TO)) { - groups = g_slist_append(groups, group->name); - } - - jabber_roster_update(js, who, groups); - g_slist_free(groups); + jabber_roster_update(js, who, NULL); my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); if(!strcmp(who, my_bare_jid)) {
--- a/libpurple/protocols/jabber/si.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/jabber/si.c Thu Nov 20 21:13:56 2008 +0000 @@ -623,7 +623,7 @@ return; else if(acceptfd == -1) { purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno)); - /* TODO: This should cancel the ft */ + /* Don't cancel the ft - allow it to fall to the next streamhost.*/ return; } @@ -659,8 +659,11 @@ jsx = xfer->data; - if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) + if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) { + if (type && !strcmp(type, "error")) + purple_xfer_cancel_remote(xfer); return; + } if(!(from = xmlnode_get_attrib(packet, "from"))) return; @@ -718,14 +721,16 @@ JabberSIXfer *jsx; JabberIq *iq; xmlnode *query, *streamhost; - char *jid, port[6]; - const char *local_ip, *public_ip, *ft_proxies; + char port[6]; GList *tmp; JabberBytestreamsStreamhost *sh, *sh2; + int streamhost_count = 0; jsx = xfer->data; jsx->listen_data = NULL; + /* I'm not sure under which conditions this can happen + * (it seems like it shouldn't be possible */ if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) { purple_xfer_unref(xfer); return; @@ -733,13 +738,6 @@ purple_xfer_unref(xfer); - if (sock < 0) { - purple_xfer_cancel_local(xfer); - return; - } - - jsx->local_streamhost_fd = sock; - iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, "http://jabber.org/protocol/bytestreams"); xmlnode_set_attrib(iq->node, "to", xfer->who); @@ -747,84 +745,43 @@ xmlnode_set_attrib(query, "sid", jsx->stream_id); - jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, - jsx->js->user->domain, jsx->js->user->resource); - xfer->local_port = purple_network_get_port_from_fd(sock); - g_snprintf(port, sizeof(port), "%hu", xfer->local_port); - - /* TODO: Should there be an option to not use the local host as a ft proxy? - * (to prevent revealing IP address, etc.) */ + /* If we successfully started listening locally */ + if (sock >= 0) { + gchar *jid; + const char *local_ip, *public_ip; - /* Include the localhost's IP (for in-network transfers) */ - local_ip = purple_network_get_local_system_ip(jsx->js->fd); - if (strcmp(local_ip, "0.0.0.0") != 0) - { - streamhost = xmlnode_new_child(query, "streamhost"); - xmlnode_set_attrib(streamhost, "jid", jid); - xmlnode_set_attrib(streamhost, "host", local_ip); - xmlnode_set_attrib(streamhost, "port", port); - } - - /* Include the public IP (assuming that there is a port mapped somehow) */ - /* TODO: Check that it isn't the same as above and is a valid IP */ - public_ip = purple_network_get_my_ip(jsx->js->fd); - if (strcmp(public_ip, local_ip) != 0) - { - streamhost = xmlnode_new_child(query, "streamhost"); - xmlnode_set_attrib(streamhost, "jid", jid); - xmlnode_set_attrib(streamhost, "host", public_ip); - xmlnode_set_attrib(streamhost, "port", port); - } - - g_free(jid); - - /* The listener for the local proxy */ - xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ, - jabber_si_xfer_bytestreams_send_connected_cb, xfer); + jsx->local_streamhost_fd = sock; - /* insert proxies here */ - ft_proxies = purple_account_get_string(xfer->account, "ft_proxies", NULL); - if (ft_proxies) { - int i, portnum; - char *tmp; - gchar **ft_proxy_list = g_strsplit(ft_proxies, ",", 0); - - g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL); - g_list_free(jsx->streamhosts); - jsx->streamhosts = NULL; - - for(i = 0; ft_proxy_list[i]; i++) { - g_strstrip(ft_proxy_list[i]); - if(!(*ft_proxy_list[i])) - continue; + jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, + jsx->js->user->domain, jsx->js->user->resource); + xfer->local_port = purple_network_get_port_from_fd(sock); + g_snprintf(port, sizeof(port), "%hu", xfer->local_port); - if((tmp = strchr(ft_proxy_list[i], ':'))) { - portnum = atoi(tmp + 1); - *tmp = '\0'; - } else - portnum = 7777; - - g_snprintf(port, sizeof(port), "%hu", portnum); - - purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p", - jsx, jsx->streamhosts, i, ft_proxy_list[i]); - if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL) - continue; - + /* Include the localhost's IP (for in-network transfers) */ + local_ip = purple_network_get_local_system_ip(jsx->js->fd); + if (strcmp(local_ip, "0.0.0.0") != 0) { + streamhost_count++; streamhost = xmlnode_new_child(query, "streamhost"); - xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]); - xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]); + xmlnode_set_attrib(streamhost, "jid", jid); + xmlnode_set_attrib(streamhost, "host", local_ip); xmlnode_set_attrib(streamhost, "port", port); - - sh = g_new0(JabberBytestreamsStreamhost, 1); - sh->jid = g_strdup(ft_proxy_list[i]); - sh->host = g_strdup(ft_proxy_list[i]); - sh->port = portnum; - - jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh); } - g_strfreev(ft_proxy_list); + /* Include the public IP (assuming that there is a port mapped somehow) */ + public_ip = purple_network_get_my_ip(jsx->js->fd); + if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) { + streamhost_count++; + streamhost = xmlnode_new_child(query, "streamhost"); + xmlnode_set_attrib(streamhost, "jid", jid); + xmlnode_set_attrib(streamhost, "host", public_ip); + xmlnode_set_attrib(streamhost, "port", port); + } + + g_free(jid); + + /* The listener for the local proxy */ + xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ, + jabber_si_xfer_bytestreams_send_connected_cb, xfer); } for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) { @@ -840,6 +797,7 @@ if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL) continue; + streamhost_count++; streamhost = xmlnode_new_child(query, "streamhost"); xmlnode_set_attrib(streamhost, "jid", sh->jid); xmlnode_set_attrib(streamhost, "host", sh->host); @@ -855,6 +813,14 @@ jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2); } + /* We have no way of transferring, cancel the transfer */ + if (streamhost_count == 0) { + jabber_iq_free(iq); + /* We should probably notify the target, but this really shouldn't ever happen */ + purple_xfer_cancel_local(xfer); + return; + } + jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer); jabber_iq_send(iq); @@ -869,13 +835,14 @@ purple_xfer_ref(xfer); jsx = xfer->data; + + /* TODO: Should there be an option to not use the local host as a ft proxy? + * (to prevent revealing IP address, etc.) */ jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM, jabber_si_xfer_bytestreams_listen_cb, xfer); if (jsx->listen_data == NULL) { - purple_xfer_unref(xfer); - /* XXX: couldn't open a port, we're fscked */ - purple_xfer_cancel_local(xfer); - return; + /* We couldn't open a local port. Perhaps we can use a proxy. */ + jabber_si_xfer_bytestreams_listen_cb(-1, xfer); } } @@ -1213,9 +1180,6 @@ js = gc->proto_data; - if(!purple_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE)) - return; - xfer = jabber_si_new_xfer(gc, who); if (file)
--- a/libpurple/protocols/msn/Makefile.am Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/Makefile.am Thu Nov 20 21:13:56 2008 +0000 @@ -48,12 +48,8 @@ slplink.h \ slpmsg.c \ slpmsg.h \ - slpsession.c \ - slpsession.h \ - soap.c\ - soap.h\ - soap2.c \ - soap2.h \ + soap.c \ + soap.h \ state.c \ state.h \ switchboard.c \
--- a/libpurple/protocols/msn/Makefile.mingw Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/Makefile.mingw Thu Nov 20 21:13:56 2008 +0000 @@ -59,9 +59,7 @@ slpcall.c \ slplink.c \ slpmsg.c \ - slpsession.c \ soap.c\ - soap2.c\ state.c \ switchboard.c \ sync.c \
--- a/libpurple/protocols/msn/cmdproc.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/cmdproc.c Thu Nov 20 21:13:56 2008 +0000 @@ -263,7 +263,7 @@ MsnTransaction *trans = NULL; if (cmd->trId) - trans = msn_history_find(cmdproc->history, cmd->trId); + cmd->trans = trans = msn_history_find(cmdproc->history, cmd->trId); if (trans != NULL) if (trans->timer) { @@ -309,8 +309,6 @@ if (cb == NULL && trans != NULL) { - cmd->trans = trans; - if (trans->callbacks != NULL) cb = g_hash_table_lookup(trans->callbacks, cmd->command); }
--- a/libpurple/protocols/msn/command.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/command.c Thu Nov 20 21:13:56 2008 +0000 @@ -36,53 +36,6 @@ return TRUE; } -/* - * check the command is the command with payload content - * if it is return TRUE - * else return FALSE - */ -static gboolean -msn_check_payload_cmd(const char *str) -{ - g_return_val_if_fail(str != NULL, FALSE); - - if((!strcmp(str,"ADL")) || - (!strcmp(str,"GCF")) || - (!strcmp(str,"SG")) || - (!strcmp(str,"MSG")) || - (!strcmp(str,"RML")) || - (!strcmp(str,"UBX")) || - (!strcmp(str,"UBN")) || - (!strcmp(str,"UUM")) || - (!strcmp(str,"UBM")) || - (!strcmp(str,"FQY")) || - (!strcmp(str,"UUN")) || - (!strcmp(str,"UUX")) || - (!strcmp(str,"IPG")) || - (is_num(str))){ - return TRUE; - } - - return FALSE; -} - -/* - * set command Payload length - */ -static void -msn_set_payload_len(MsnCommand *cmd) -{ - char *param; - int len = 0; - - if (msn_check_payload_cmd(cmd->command) && (cmd->param_count > 0)){ - param = cmd->params[cmd->param_count - 1]; - len = is_num(param) ? atoi(param) : 0; - } - - cmd->payload_len = len; -} - MsnCommand * msn_command_from_string(const char *string) { @@ -98,31 +51,28 @@ if (param_start) { *param_start++ = '\0'; - cmd->params = g_strsplit(param_start, " ", 0); + cmd->params = g_strsplit_set(param_start, " ", 0); } if (cmd->params != NULL) { - char *param; int c; - for (c = 0; cmd->params[c]; c++); + for (c = 0; cmd->params[c] && cmd->params[c][0]; c++); cmd->param_count = c; - param = cmd->params[0]; - - cmd->trId = is_num(param) ? atoi(param) : 0; + if (cmd->param_count) { + char *param = cmd->params[0]; + cmd->trId = is_num(param) ? atoi(param) : 0; + } else { + cmd->trId = 0; + } } else { cmd->trId = 0; } - /* khc: Huh! */ - /*add payload Length checking*/ - msn_set_payload_len(cmd); - purple_debug_info("MSNP14","get payload len:%" G_GSIZE_FORMAT "\n", cmd->payload_len); - msn_command_ref(cmd); return cmd;
--- a/libpurple/protocols/msn/contact.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/contact.c Thu Nov 20 21:13:56 2008 +0000 @@ -1,5 +1,5 @@ /** - * @file contact.c + * @file contact.c * get MSN contacts via SOAP request * created by MaYuan<mayuan2006@gmail.com> * @@ -28,7 +28,8 @@ #include "contact.h" #include "xmlnode.h" #include "group.h" -#include "soap2.h" +#include "soap.h" +#include "nexus.h" const char *MsnSoapPartnerScenarioText[] = { @@ -49,29 +50,10 @@ }; typedef struct { - MsnContact *contact; + MsnSession *session; MsnSoapPartnerScenario which; } GetContactListCbData; -/* new a contact */ -MsnContact * -msn_contact_new(MsnSession *session) -{ - MsnContact *contact; - - contact = g_new0(MsnContact, 1); - contact->session = session; - - return contact; -} - -/* destroy the contact */ -void -msn_contact_destroy(MsnContact *contact) -{ - g_free(contact); -} - MsnCallbackState * msn_callback_state_new(MsnSession *session) { @@ -82,6 +64,22 @@ return state; } +MsnCallbackState * +msn_callback_state_dup(MsnCallbackState *state) +{ + MsnCallbackState *new_state = g_new0(MsnCallbackState, 1); + + new_state->session = state->session; + new_state->who = g_strdup(state->who); + new_state->uid = g_strdup(state->uid); + new_state->old_group_name = g_strdup(state->old_group_name); + new_state->new_group_name = g_strdup(state->new_group_name); + new_state->guid = g_strdup(state->guid); + /* The rest should be made new */ + + return new_state; +} + void msn_callback_state_free(MsnCallbackState *state) { @@ -93,6 +91,7 @@ g_free(state->old_group_name); g_free(state->new_group_name); g_free(state->guid); + xmlnode_free(state->body); g_free(state); } @@ -177,56 +176,60 @@ return 0; } -/*get User Type*/ -static int -msn_get_user_type(char *type) +/* get Network */ +/* QuLogic: These names don't really refer to the MsnNetwork, + * but I haven't yet written the code to properly use them. + */ +static MsnNetwork +msn_get_network(char *type) { g_return_val_if_fail(type != NULL, 0); if (!strcmp(type,"Regular")) { - return MSN_USER_TYPE_PASSPORT; + return MSN_NETWORK_PASSPORT; } if (!strcmp(type,"Live")) { - return MSN_USER_TYPE_PASSPORT; + return MSN_NETWORK_PASSPORT; } if (!strcmp(type,"LivePending")) { - return MSN_USER_TYPE_PASSPORT; + return MSN_NETWORK_PASSPORT; } - return MSN_USER_TYPE_UNKNOWN; + return MSN_NETWORK_UNKNOWN; } /* Create the AddressBook in the server, if we don't have one */ static void msn_create_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - if (resp && msn_soap_xml_get(resp->xml, "Body/Fault") == NULL) { - purple_debug_info("msnab", "Address Book successfully created!\n"); - msn_get_address_book((MsnContact *)data, MSN_PS_INITIAL, NULL, NULL); + if (resp && xmlnode_get_child(resp->xml, "Body/Fault") == NULL) { + purple_debug_info("msn", "Address Book successfully created!\n"); + msn_get_address_book((MsnSession *)data, MSN_PS_INITIAL, NULL, NULL); } else { - purple_debug_info("msnab", "Address Book creation failed!\n"); + purple_debug_info("msn", "Address Book creation failed!\n"); } } static void -msn_create_address_book(MsnContact * contact) +msn_create_address_book(MsnSession *session) { gchar *body; - g_return_if_fail(contact != NULL); - g_return_if_fail(contact->session != NULL); - g_return_if_fail(contact->session->user != NULL); - g_return_if_fail(contact->session->user->passport != NULL); + g_return_if_fail(session != NULL); + g_return_if_fail(session->user != NULL); + g_return_if_fail(session->user->passport != NULL); + + purple_debug_info("msn", "Creating an Address Book.\n"); - purple_debug_info("msnab","Creating an Address Book.\n"); + body = g_markup_printf_escaped(MSN_ADD_ADDRESSBOOK_TEMPLATE, + msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), + session->user->passport); - body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport); - - msn_soap_message_send(contact->session, + msn_soap_message_send(session, msn_soap_message_new(MSN_ADD_ADDRESSBOOK_SOAP_ACTION, xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, msn_create_address_cb, - contact); + MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, + msn_create_address_cb, session); g_free(body); } @@ -240,7 +243,7 @@ char *member_id = xmlnode_get_data(xmlnode_get_child(member, "MembershipId")); MsnUser *user = msn_userlist_find_add_user(session->userlist, passport, NULL); - purple_debug_info("msncl","%s name: %s, Type: %s, MembershipID: %s\n", + purple_debug_info("msn", "CL: %s name: %s, Type: %s, MembershipID: %s\n", node, passport, type, member_id == NULL ? "(null)" : member_id); if (member_id) { @@ -259,7 +262,7 @@ { xmlnode *type; - if ((type = msn_soap_xml_get(service, "Info/Handle/Type"))) { + if ((type = xmlnode_get_child(service, "Info/Handle/Type"))) { char *type_str = xmlnode_get_data(type); if (g_str_equal(type_str, "Profile")) { @@ -269,11 +272,11 @@ char *lastchange_str = xmlnode_get_data(lastchange); xmlnode *membership; - purple_debug_info("msncl","last change: %s\n", lastchange_str); + purple_debug_info("msn", "CL last change: %s\n", lastchange_str); purple_account_set_string(session->account, "CLLastChange", lastchange_str); - for (membership = msn_soap_xml_get(service, + for (membership = xmlnode_get_child(service, "Memberships/Membership"); membership; membership = xmlnode_get_next_twin(membership)) { @@ -282,10 +285,10 @@ MsnListId list = msn_get_memberrole(role_str); xmlnode *member; - purple_debug_info("msncl", "MemberRole role: %s, list: %d\n", + purple_debug_info("msn", "CL MemberRole role: %s, list: %d\n", role_str, list); - for (member = msn_soap_xml_get(membership, "Members/Member"); + for (member = xmlnode_get_child(membership, "Members/Member"); member; member = xmlnode_get_next_twin(member)) { const char *member_type = xmlnode_get_attrib(member, "type"); if (g_str_equal(member_type, "PassportMember")) { @@ -310,7 +313,7 @@ /*parse contact list*/ static void -msn_parse_contact_list(MsnContact *contact, xmlnode *node) +msn_parse_contact_list(MsnSession *session, xmlnode *node) { xmlnode *fault, *faultnode; @@ -321,18 +324,18 @@ * * this is not handled yet */ - if ((fault = msn_soap_xml_get(node, "Body/Fault"))) { + if ((fault = xmlnode_get_child(node, "Body/Fault"))) { if ((faultnode = xmlnode_get_child(fault, "faultstring"))) { char *faultstring = xmlnode_get_data(faultnode); - purple_debug_info("msncl", "Retrieving contact list failed: %s\n", + purple_debug_info("msn", "Retrieving contact list failed: %s\n", faultstring); g_free(faultstring); } - if ((faultnode = msn_soap_xml_get(fault, "detail/errorcode"))) { + if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) { char *errorcode = xmlnode_get_data(faultnode); if (g_str_equal(errorcode, "ABDoesNotExist")) { - msn_create_address_book(contact); + msn_create_address_book(session); g_free(errorcode); return; } @@ -340,14 +343,14 @@ g_free(errorcode); } - msn_get_contact_list(contact, MSN_PS_INITIAL, NULL); + msn_get_contact_list(session, MSN_PS_INITIAL, NULL); } else { xmlnode *service; - for (service = msn_soap_xml_get(node, "Body/FindMembershipResponse/" + for (service = xmlnode_get_child(node, "Body/FindMembershipResponse/" "FindMembershipResult/Services/Service"); service; service = xmlnode_get_next_twin(service)) { - msn_parse_each_service(contact->session, service); + msn_parse_each_service(session, service); } } } @@ -357,8 +360,7 @@ gpointer data) { GetContactListCbData *cb_data = data; - MsnContact *contact = cb_data->contact; - MsnSession *session = contact->session; + MsnSession *session = cb_data->session; g_return_if_fail(session != NULL); @@ -366,9 +368,9 @@ const char *abLastChange; const char *dynamicItemLastChange; - purple_debug_misc("msncl","Got the contact list!\n"); + purple_debug_misc("msn", "Got the contact list!\n"); - msn_parse_contact_list(cb_data->contact, resp->xml); + msn_parse_contact_list(session, resp->xml); abLastChange = purple_account_get_string(session->account, "ablastChange", NULL); dynamicItemLastChange = purple_account_get_string(session->account, @@ -379,9 +381,9 @@ /* XXX: this should be enabled when we can correctly do partial syncs with the server. Currently we need to retrieve the whole list to detect sync issues */ - msn_get_address_book(contact, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange); + msn_get_address_book(session, MSN_PS_INITIAL, abLastChange, dynamicItemLastChange); #else - msn_get_address_book(contact, MSN_PS_INITIAL, NULL, NULL); + msn_get_address_book(session, MSN_PS_INITIAL, NULL, NULL); #endif } } @@ -391,27 +393,29 @@ /*SOAP get contact list*/ void -msn_get_contact_list(MsnContact * contact, +msn_get_contact_list(MsnSession *session, const MsnSoapPartnerScenario partner_scenario, const char *update_time) { gchar *body = NULL; gchar *update_str = NULL; - GetContactListCbData cb_data = { contact, partner_scenario }; + GetContactListCbData cb_data = { session, partner_scenario }; const gchar *partner_scenario_str = MsnSoapPartnerScenarioText[partner_scenario]; - purple_debug_misc("MSNCL","Getting Contact List.\n"); + purple_debug_misc("msn", "Getting Contact List.\n"); - if ( update_time != NULL ) { - purple_debug_info("MSNCL","Last update time: %s\n",update_time); + if (update_time != NULL) { + purple_debug_info("msn", "CL Last update time: %s\n", update_time); update_str = g_strdup_printf(MSN_GET_CONTACT_UPDATE_XML,update_time); } - body = g_strdup_printf(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, update_str ? update_str : ""); + body = g_markup_printf_escaped(MSN_GET_CONTACT_TEMPLATE, partner_scenario_str, + msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), + update_str ? update_str : ""); - msn_soap_message_send(contact->session, + msn_soap_message_send(session, msn_soap_message_new(MSN_GET_CONTACT_SOAP_ACTION, xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL, + MSN_CONTACT_SERVER, MSN_GET_CONTACT_POST_URL, FALSE, msn_get_contact_list_cb, g_memdup(&cb_data, sizeof(cb_data))); g_free(update_str); @@ -419,12 +423,11 @@ } static void -msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node) +msn_parse_addressbook_groups(MsnSession *session, xmlnode *node) { - MsnSession *session = contact->session; xmlnode *group; - purple_debug_info("MSNAB","msn_parse_addressbook_groups()\n"); + purple_debug_info("msn", "msn_parse_addressbook_groups()\n"); for(group = xmlnode_get_child(node, "Group"); group; group = xmlnode_get_next_twin(group)){ @@ -444,7 +447,7 @@ continue; } - purple_debug_info("MsnAB","group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)"); + purple_debug_info("msn", "AB group_id: %s, name: %s\n", group_id, group_name ? group_name : "(null)"); if ((purple_find_group(group_name)) == NULL){ PurpleGroup *g = purple_group_new(group_name); purple_blist_add_group(g, NULL); @@ -507,18 +510,19 @@ } static void -msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node) +msn_parse_addressbook_contacts(MsnSession *session, xmlnode *node) { - MsnSession *session = contact->session; xmlnode *contactNode; - char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL, *mobile_number = NULL; + char *passport = NULL, *Name = NULL, *uid = NULL, *type = NULL, *mobile_number = NULL, *alias = NULL; gboolean mobile = FALSE; + PurpleConnection *pc = purple_account_get_connection(session->account); for(contactNode = xmlnode_get_child(node, "Contact"); contactNode; contactNode = xmlnode_get_next_twin(contactNode)) { xmlnode *contactId, *contactInfo, *contactType, *passportName, *displayName, *guid, *groupIds, *messenger_user; + xmlnode *annotation; MsnUser *user; - MsnUserType usertype; + MsnNetwork networkId; if (!(contactId = xmlnode_get_child(contactNode,"contactId")) || !(contactInfo = xmlnode_get_child(contactNode, "contactInfo")) @@ -527,10 +531,11 @@ g_free(passport); g_free(Name); + g_free(alias); g_free(uid); g_free(type); g_free(mobile_number); - passport = Name = uid = type = mobile_number = NULL; + passport = Name = uid = type = mobile_number = alias = NULL; mobile = FALSE; uid = xmlnode_get_data(contactId); @@ -547,7 +552,7 @@ } /* ignore non-messenger contacts */ - if((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { + if ((messenger_user = xmlnode_get_child(contactInfo, "isMessengerUser"))) { char *is_messenger_user = xmlnode_get_data(messenger_user); if(is_messenger_user && !strcmp(is_messenger_user, "false")) { @@ -558,7 +563,7 @@ g_free(is_messenger_user); } - usertype = msn_get_user_type(type); + networkId = msn_get_network(type); passportName = xmlnode_get_child(contactInfo, "passportName"); if (passportName == NULL) { xmlnode *emailsNode, *contactEmailNode, *emailNode; @@ -572,7 +577,7 @@ /*TODO: need to support the Mobile type*/ continue; } - for(contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode; + for (contactEmailNode = xmlnode_get_child(emailsNode, "ContactEmail"); contactEmailNode; contactEmailNode = xmlnode_get_next_twin(contactEmailNode) ){ if (!(messengerEnabledNode = xmlnode_get_child(contactEmailNode, "isMessengerEnabled"))) { /* XXX: Should this be a continue instead of a break? It seems like it'd cause unpredictable results otherwise. */ @@ -586,15 +591,15 @@ passport = xmlnode_get_data(emailNode); } - if(msnEnabled && !strcmp(msnEnabled, "true")) { + if (msnEnabled && !strcmp(msnEnabled, "true")) { /*Messenger enabled, Get the Passport*/ - purple_debug_info("MsnAB", "Yahoo User %s\n", passport ? passport : "(null)"); - usertype = MSN_USER_TYPE_YAHOO; + purple_debug_info("msn", "AB Yahoo User %s\n", passport ? passport : "(null)"); + networkId = MSN_NETWORK_YAHOO; g_free(msnEnabled); break; } else { /*TODO maybe we can just ignore it in Purple?*/ - purple_debug_info("MSNAB", "Other type user\n"); + purple_debug_info("msn", "AB Other type user\n"); } g_free(msnEnabled); @@ -611,24 +616,33 @@ else Name = g_strdup(passport); + for (annotation = xmlnode_get_child(contactInfo, "annotations/Annotation"); + annotation; annotation = xmlnode_get_next_twin(annotation)) { + char *name; + name = xmlnode_get_data(xmlnode_get_child(annotation, "Name")); + if (!strcmp(name, "AB.NickName")) + alias = xmlnode_get_data(xmlnode_get_child(annotation, "Value")); + g_free(name); + } + mobile = msn_parse_addressbook_mobile(contactInfo, &mobile_number); - purple_debug_misc("MsnAB","passport:{%s} uid:{%s} display:{%s} mobile:{%s} mobile number:{%s}\n", - passport, uid ? uid : "(null)", Name ? Name : "(null)", + purple_debug_misc("msn", "AB passport:{%s} uid:{%s} display:{%s} alias: {%s} mobile:{%s} mobile number:{%s}\n", + passport, uid ? uid : "(null)", Name ? Name : "(null)", alias ? alias : "(null)", mobile ? "true" : "false", mobile_number ? mobile_number : "(null)"); user = msn_userlist_find_add_user(session->userlist, passport, Name); msn_user_set_uid(user, uid); - msn_user_set_type(user, usertype); + msn_user_set_network(user, networkId); msn_user_set_mobile_phone(user, mobile_number); groupIds = xmlnode_get_child(contactInfo, "groupIds"); if (groupIds) { for (guid = xmlnode_get_child(groupIds, "guid"); guid; - guid = xmlnode_get_next_twin(guid)){ + guid = xmlnode_get_next_twin(guid)) { char *group_id = xmlnode_get_data(guid); msn_user_add_group_id(user, group_id); - purple_debug_misc("MsnAB", "guid:%s\n", group_id ? group_id : "(null)"); + purple_debug_misc("msn", "AB guid:%s\n", group_id ? group_id : "(null)"); g_free(group_id); } } else { @@ -639,12 +653,14 @@ msn_got_lst_user(session, user, MSN_LIST_FL_OP, NULL); - if(mobile && user) + if (mobile && user) { user->mobile = TRUE; purple_prpl_got_user_status(session->account, user->passport, "mobile", NULL); purple_prpl_got_user_status(session->account, user->passport, "available", NULL); } + if (alias) + purple_serv_got_private_alias(pc, passport, alias); } g_free(passport); @@ -655,30 +671,27 @@ } static gboolean -msn_parse_addressbook(MsnContact * contact, xmlnode *node) +msn_parse_addressbook(MsnSession *session, xmlnode *node) { - MsnSession * session; xmlnode *result; xmlnode *groups; xmlnode *contacts; xmlnode *abNode; xmlnode *fault; - session = contact->session; - - if ((fault = msn_soap_xml_get(node, "Body/Fault"))) { + if ((fault = xmlnode_get_child(node, "Body/Fault"))) { xmlnode *faultnode; if ((faultnode = xmlnode_get_child(fault, "faultstring"))) { gchar *faultstring = xmlnode_get_data(faultnode); - purple_debug_info("MSNAB","Faultstring: %s\n", faultstring); + purple_debug_info("msn", "AB Faultstring: %s\n", faultstring); g_free(faultstring); } - if ((faultnode = msn_soap_xml_get(fault, "detail/errorcode"))) { + if ((faultnode = xmlnode_get_child(fault, "detail/errorcode"))) { gchar *errorcode = xmlnode_get_data(faultnode); - purple_debug_info("MSNAB", "Error Code: %s\n", errorcode); + purple_debug_info("msn", "AB Error Code: %s\n", errorcode); if (g_str_equal(errorcode, "ABDoesNotExist")) { g_free(errorcode); @@ -690,24 +703,24 @@ return FALSE; } - result = msn_soap_xml_get(node, "Body/ABFindAllResponse/ABFindAllResult"); - if(result == NULL){ - purple_debug_misc("MSNAB","receive no address book update\n"); + result = xmlnode_get_child(node, "Body/ABFindAllResponse/ABFindAllResult"); + if (result == NULL) { + purple_debug_misc("msn", "Received no address book update\n"); return TRUE; } /* I don't see this "groups" tag documented on msnpiki, need to find out if they are really there, and update msnpiki */ /*Process Group List*/ - groups = xmlnode_get_child(result,"groups"); + groups = xmlnode_get_child(result, "groups"); if (groups != NULL) { - msn_parse_addressbook_groups(contact, groups); + msn_parse_addressbook_groups(session, groups); } /*add a default No group to set up the no group Membership*/ msn_group_new(session->userlist, MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME); - purple_debug_misc("MSNAB","group_id:%s name:%s\n", + purple_debug_misc("msn", "AB group_id:%s name:%s\n", MSN_INDIVIDUALS_GROUP_ID, MSN_INDIVIDUALS_GROUP_NAME); if ((purple_find_group(MSN_INDIVIDUALS_GROUP_NAME)) == NULL){ PurpleGroup *g = purple_group_new(MSN_INDIVIDUALS_GROUP_NAME); @@ -716,33 +729,33 @@ /*add a default No group to set up the no group Membership*/ msn_group_new(session->userlist, MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); - purple_debug_misc("MSNAB","group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); - if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL){ + purple_debug_misc("msn", "AB group_id:%s name:%s\n", MSN_NON_IM_GROUP_ID, MSN_NON_IM_GROUP_NAME); + if ((purple_find_group(MSN_NON_IM_GROUP_NAME)) == NULL) { PurpleGroup *g = purple_group_new(MSN_NON_IM_GROUP_NAME); purple_blist_add_group(g, NULL); } /*Process contact List*/ - purple_debug_info("MSNAB","process contact list...\n"); - contacts =xmlnode_get_child(result,"contacts"); + purple_debug_info("msn", "Process contact list...\n"); + contacts = xmlnode_get_child(result, "contacts"); if (contacts != NULL) { - msn_parse_addressbook_contacts(contact, contacts); + msn_parse_addressbook_contacts(session, contacts); } - abNode =xmlnode_get_child(result,"ab"); - if(abNode != NULL){ + abNode = xmlnode_get_child(result, "ab"); + if (abNode != NULL) { xmlnode *node2; char *tmp = NULL; if ((node2 = xmlnode_get_child(abNode, "lastChange"))) tmp = xmlnode_get_data(node2); - purple_debug_info("MSNAB"," lastchanged Time:{%s}\n", tmp ? tmp : "(null)"); + purple_debug_info("msn", "AB lastchanged Time:{%s}\n", tmp ? tmp : "(null)"); purple_account_set_string(session->account, "ablastChange", tmp); g_free(tmp); tmp = NULL; if ((node2 = xmlnode_get_child(abNode, "DynamicItemLastChanged"))) tmp = xmlnode_get_data(node2); - purple_debug_info("MsnAB"," DynamicItemLastChanged :{%s}\n", tmp ? tmp : "(null)"); + purple_debug_info("msn", "AB DynamicItemLastChanged :{%s}\n", tmp ? tmp : "(null)"); purple_account_set_string(session->account, "DynamicItemLastChanged", tmp); g_free(tmp); } @@ -753,30 +766,25 @@ static void msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - MsnContact *contact = data; - MsnSession *session; + MsnSession *session = data; if (resp == NULL) return; - g_return_if_fail(contact != NULL); - session = contact->session; g_return_if_fail(session != NULL); - purple_debug_misc("MSNAB", "Got the Address Book!\n"); + purple_debug_misc("msn", "Got the Address Book!\n"); - if (msn_parse_addressbook(contact, resp->xml)) { - if (!session->logged_in) { - msn_send_privacy(session->account->gc); - msn_notification_dump_contact(session); - } + if (msn_parse_addressbook(session, resp->xml)) { + msn_send_privacy(session->account->gc); + msn_notification_dump_contact(session); } else { /* This is making us loop infinitely when we fail to parse the address book, disable for now (we should re-enable when we send timestamps) */ /* - msn_get_address_book(contact, NULL, NULL); + msn_get_address_book(session, NULL, NULL); */ msn_session_disconnect(session); purple_connection_error_reason(session->account->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to retrieve MSN Address Book")); @@ -785,13 +793,13 @@ /*get the address book*/ void -msn_get_address_book(MsnContact *contact, +msn_get_address_book(MsnSession *session, MsnSoapPartnerScenario partner_scenario, const char *LastChanged, const char *dynamicItemLastChange) { char *body, *update_str = NULL; - purple_debug_misc("MSNAB","Getting Address Book\n"); + purple_debug_misc("msn", "Getting Address Book\n"); /*build SOAP and POST it*/ if (dynamicItemLastChange != NULL) @@ -799,18 +807,121 @@ else if (LastChanged != NULL) update_str = g_strdup_printf(MSN_GET_ADDRESS_UPDATE_XML, LastChanged); - body = g_strdup_printf(MSN_GET_ADDRESS_TEMPLATE, MsnSoapPartnerScenarioText[partner_scenario], update_str ? update_str : ""); + body = g_markup_printf_escaped(MSN_GET_ADDRESS_TEMPLATE, + MsnSoapPartnerScenarioText[partner_scenario], + msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS), + update_str ? update_str : ""); - msn_soap_message_send(contact->session, + msn_soap_message_send(session, msn_soap_message_new(MSN_GET_ADDRESS_SOAP_ACTION, xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, msn_get_address_cb, - contact); + MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, FALSE, + msn_get_address_cb, session); g_free(update_str); g_free(body); } +/*************************************************************** + * Contact Operations + ***************************************************************/ + +static const char * +msn_contact_operation_str(MsnCallbackAction action) +{ + /* Make sure this is large enough when adding more */ + static char buf[BUF_LEN]; + buf[0] = '\0'; + + if (action & MSN_ADD_BUDDY) + strcat(buf, "Adding Buddy,"); + if (action & MSN_MOVE_BUDDY) + strcat(buf, "Moving Buddy,"); + if (action & MSN_ACCEPTED_BUDDY) + strcat(buf, "Accepted Buddy,"); + if (action & MSN_DENIED_BUDDY) + strcat(buf, "Denied Buddy,"); + if (action & MSN_ADD_GROUP) + strcat(buf, "Adding Group,"); + if (action & MSN_DEL_GROUP) + strcat(buf, "Deleting Group,"); + if (action & MSN_RENAME_GROUP) + strcat(buf, "Renaming Group,"); + if (action & MSN_UPDATE_INFO) + strcat(buf, "Updating Contact Info,"); + + return buf; +} + +static gboolean msn_contact_request(MsnCallbackState *state); + +static void +msn_contact_request_cb(MsnSoapMessage *req, MsnSoapMessage *resp, + gpointer data) +{ + MsnCallbackState *state = data; + xmlnode *faultcode; + char *faultcode_str; + + if (resp == NULL) { + purple_debug_error("msn", + "Operation {%s} failed. No response received from server.\n", + msn_contact_operation_str(state->action)); + return; + } + + faultcode = xmlnode_get_child(resp->xml, "Body/Fault/faultcode"); + + if (faultcode == NULL) { + /* No errors */ + if (state->cb) + ((MsnSoapCallback)state->cb)(req, resp, data); + msn_callback_state_free(state); + return; + } + + faultcode_str = xmlnode_get_data(faultcode); + + if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { + purple_debug_info("msn", + "Contact Operation {%s} failed because of bad token." + " Updating token now and retrying operation.\n", + msn_contact_operation_str(state->action)); + /* Token has expired, so renew it, and try again later */ + msn_nexus_update_token(state->session->nexus, MSN_AUTH_CONTACTS, + (GSourceFunc)msn_contact_request, data); + } + else + { + /* We don't know how to respond to this faultcode, so just log it */ + /* XXX: Probably should notify the user or undo the change or something? */ + char *str = xmlnode_to_str(xmlnode_get_child(resp->xml, "Body/Fault"), NULL); + purple_debug_error("msn", "Operation {%s} Failed, SOAP Fault was: %s\n", + msn_contact_operation_str(state->action), str); + g_free(str); + msn_callback_state_free(state); + } + + g_free(faultcode_str); +} + +static gboolean +msn_contact_request(MsnCallbackState *state) +{ + if (state->token == NULL) + state->token = xmlnode_get_child(state->body, + "Header/ABAuthHeader/TicketToken"); + /* delete old & replace with new token */ + xmlnode_free(state->token->child); + xmlnode_insert_data(state->token, + msn_nexus_get_token_str(state->session->nexus, MSN_AUTH_CONTACTS), -1); + msn_soap_message_send(state->session, + msn_soap_message_new(state->post_action, xmlnode_copy(state->body)), + MSN_CONTACT_SERVER, state->post_url, FALSE, + msn_contact_request_cb, state); + return FALSE; +} + static void msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) @@ -818,33 +929,32 @@ MsnCallbackState *state = data; MsnSession *session = state->session; + MsnUserList *userlist; + MsnUser *user; + g_return_if_fail(session != NULL); - if (resp != NULL) { - MsnUserList *userlist = session->userlist; - MsnUser *user; + userlist = session->userlist; - purple_debug_info("MSNCL","Contact added successfully\n"); + purple_debug_info("msn", "Contact added successfully\n"); - // the code this block is replacing didn't send ADL for yahoo contacts, - // but i haven't confirmed this is WLM's behaviour wrt yahoo contacts - if ( !msn_user_is_yahoo(session->account, state->who) ) { - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); - } - - msn_notification_send_fqy(session, state->who); - - user = msn_userlist_find_add_user(userlist, state->who, state->who); - msn_user_add_group_id(user, state->guid); + /* the code this block is replacing didn't send ADL for yahoo contacts, + * but i haven't confirmed this is WLM's behaviour wrt yahoo contacts + */ + if ( !msn_user_is_yahoo(session->account, state->who) ) { + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); } - msn_callback_state_free(state); + msn_notification_send_fqy(session, state->who); + + user = msn_userlist_find_add_user(userlist, state->who, state->who); + msn_user_add_group_id(user, state->guid); } /* add a Contact in MSN_INDIVIDUALS_GROUP */ void -msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passport) +msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport) { gchar *body = NULL; gchar *contact_xml = NULL; @@ -861,16 +971,16 @@ contact_xml = g_strdup_printf(MSN_XML_ADD_CONTACT, escaped_displayname, passport); #endif - purple_debug_info("MSNCL","Adding contact %s to contact list\n", passport); + purple_debug_info("msn", "Adding contact %s to contact list\n", passport); contact_xml = g_strdup_printf(MSN_CONTACT_XML, passport); body = g_strdup_printf(MSN_ADD_CONTACT_TEMPLATE, contact_xml); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_CONTACT_ADD_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_add_contact_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_CONTACT_ADD_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_add_contact_read_cb; + msn_contact_request(state); g_free(contact_xml); g_free(body); @@ -887,41 +997,35 @@ userlist = state->session->userlist; - if (resp != NULL) { - if (msn_userlist_add_buddy_to_group(userlist, state->who, - state->new_group_name)) { - purple_debug_info("MSNCL", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name); - } else { - purple_debug_info("MSNCL","Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name); - } + if (msn_userlist_add_buddy_to_group(userlist, state->who, + state->new_group_name)) { + purple_debug_info("msn", "Contact %s added to group %s successfully!\n", state->who, state->new_group_name); + } else { + purple_debug_info("msn", "Contact %s added to group %s successfully on server, but failed in the local list\n", state->who, state->new_group_name); + } - if (state->action & MSN_ADD_BUDDY) { - MsnUser *user = msn_userlist_find_user(userlist, state->who); - - if ( !msn_user_is_yahoo(state->session->account, state->who) ) { + if (state->action & MSN_ADD_BUDDY) { + MsnUser *user = msn_userlist_find_user(userlist, state->who); - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); - msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); - } - msn_notification_send_fqy(state->session, state->who); + if ( !msn_user_is_yahoo(state->session->account, state->who) ) { + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_AL); + msn_userlist_add_buddy_to_list(userlist, state->who, MSN_LIST_FL); + } + msn_notification_send_fqy(state->session, state->who); - if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { - msn_del_contact_from_list(state->session->contact, NULL, state->who, MSN_LIST_PL); - msn_callback_state_free(state); - return; - } - } - - if (state->action & MSN_MOVE_BUDDY) { - msn_del_contact_from_group(state->session->contact, state->who, state->old_group_name); + if (msn_userlist_user_is_in_list(user, MSN_LIST_PL)) { + msn_del_contact_from_list(state->session, NULL, state->who, MSN_LIST_PL); + return; } } - msn_callback_state_free(state); + if (state->action & MSN_MOVE_BUDDY) { + msn_del_contact_from_group(state->session, state->who, state->old_group_name); + } } void -msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, +msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state, const char *passport, const char *groupId) { MsnUserList *userlist; @@ -931,37 +1035,33 @@ g_return_if_fail(passport != NULL); g_return_if_fail(groupId != NULL); - g_return_if_fail(contact != NULL); - g_return_if_fail(contact->session != NULL); - g_return_if_fail(contact->session->userlist != NULL); + g_return_if_fail(session != NULL); - userlist = contact->session->userlist; + userlist = session->userlist; if (!strcmp(groupId, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(groupId, MSN_NON_IM_GROUP_ID)) { user = msn_userlist_find_add_user(userlist, passport, passport); if (state->action & MSN_ADD_BUDDY) { - msn_add_contact(contact, state, passport); + msn_add_contact(session, state, passport); return; } if (state->action & MSN_MOVE_BUDDY) { msn_user_add_group_id(user, groupId); - msn_del_contact_from_group(contact, passport, state->old_group_name); - } else { - msn_callback_state_free(state); + msn_del_contact_from_group(session, passport, state->old_group_name); } return; } - purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport, + purple_debug_info("msn", "Adding user %s to group %s\n", passport, msn_userlist_find_group_name(userlist, groupId)); user = msn_userlist_find_user(userlist, passport); if (user == NULL) { - purple_debug_warning("MSNCL", "Unable to retrieve user %s from the userlist!\n", passport); + purple_debug_warning("msn", "Unable to retrieve user %s from the userlist!\n", passport); msn_callback_state_free(state); return; /* guess this never happened! */ } @@ -974,11 +1074,11 @@ body = g_strdup_printf(MSN_ADD_CONTACT_GROUP_TEMPLATE, groupId, contact_xml); - msn_soap_message_send(state->session, - msn_soap_message_new(MSN_ADD_CONTACT_GROUP_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_add_contact_to_group_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_ADD_CONTACT_GROUP_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_add_contact_to_group_read_cb; + msn_contact_request(state); g_free(contact_xml); g_free(body); @@ -989,24 +1089,19 @@ gpointer data) { MsnCallbackState *state = data; - - if (resp != NULL) { - MsnUserList *userlist = state->session->userlist; - MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid); - - purple_debug_info("MSNCL","Delete contact successful\n"); + MsnUserList *userlist = state->session->userlist; + MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid); - if (user != NULL) { - msn_userlist_remove_user(userlist, user); - } + purple_debug_info("msn", "Delete contact successful\n"); + + if (user != NULL) { + msn_userlist_remove_user(userlist, user); } - - msn_callback_state_free(state); } /*delete a Contact*/ void -msn_delete_contact(MsnContact *contact, const char *contactId) +msn_delete_contact(MsnSession *session, const char *contactId) { gchar *body = NULL; gchar *contact_id_xml = NULL ; @@ -1015,17 +1110,18 @@ g_return_if_fail(contactId != NULL); contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, contactId); - state = msn_callback_state_new(contact->session); + state = msn_callback_state_new(session); msn_callback_state_set_uid(state, contactId); /* build SOAP request */ - purple_debug_info("MSNCL","Deleting contact with contactId: %s\n", contactId); + purple_debug_info("msn", "Deleting contact with contactId: %s\n", contactId); body = g_strdup_printf(MSN_DEL_CONTACT_TEMPLATE, contact_id_xml); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_CONTACT_DEL_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_delete_contact_read_cb, state); + + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_CONTACT_DEL_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_delete_contact_read_cb; + msn_contact_request(state); g_free(contact_id_xml); g_free(body); @@ -1037,20 +1133,16 @@ { MsnCallbackState *state = data; - if (resp != NULL) { - if (msn_userlist_rem_buddy_from_group(state->session->userlist, - state->who, state->old_group_name)) { - purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name); - } else { - purple_debug_info("MSNCL", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name); - } + if (msn_userlist_rem_buddy_from_group(state->session->userlist, + state->who, state->old_group_name)) { + purple_debug_info("msn", "Contact %s deleted successfully from group %s\n", state->who, state->old_group_name); + } else { + purple_debug_info("msn", "Contact %s deleted successfully from group %s in the server, but failed in the local list\n", state->who, state->old_group_name); } - - msn_callback_state_free(state); } void -msn_del_contact_from_group(MsnContact *contact, const char *passport, const char *group_name) +msn_del_contact_from_group(MsnSession *session, const char *passport, const char *group_name) { MsnUserList * userlist; MsnUser *user; @@ -1060,24 +1152,22 @@ g_return_if_fail(passport != NULL); g_return_if_fail(group_name != NULL); - g_return_if_fail(contact != NULL); - g_return_if_fail(contact->session != NULL); - g_return_if_fail(contact->session->userlist != NULL); + g_return_if_fail(session != NULL); - userlist = contact->session->userlist; + userlist = session->userlist; groupId = msn_userlist_find_group_id(userlist, group_name); if (groupId != NULL) { - purple_debug_info("MSNCL", "Deleting user %s from group %s\n", passport, group_name); + purple_debug_info("msn", "Deleting user %s from group %s\n", passport, group_name); } else { - purple_debug_warning("MSNCL", "Unable to retrieve group id from group %s !\n", group_name); + purple_debug_warning("msn", "Unable to retrieve group id from group %s !\n", group_name); return; } user = msn_userlist_find_user(userlist, passport); if (user == NULL) { - purple_debug_warning("MSNCL", "Unable to retrieve user from passport %s!\n", passport); + purple_debug_warning("msn", "Unable to retrieve user from passport %s!\n", passport); return; } @@ -1086,7 +1176,7 @@ return; } - state = msn_callback_state_new(contact->session); + state = msn_callback_state_new(session); msn_callback_state_set_who(state, passport); msn_callback_state_set_guid(state, groupId); msn_callback_state_set_old_group_name(state, group_name); @@ -1094,11 +1184,11 @@ contact_id_xml = g_strdup_printf(MSN_CONTACT_ID_XML, user->uid); body = g_strdup_printf(MSN_CONTACT_DEL_GROUP_TEMPLATE, contact_id_xml, groupId); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_CONTACT_DEL_GROUP_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_del_contact_from_group_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_CONTACT_DEL_GROUP_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_del_contact_from_group_read_cb; + msn_contact_request(state); g_free(contact_id_xml); g_free(body); @@ -1109,32 +1199,75 @@ msn_update_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - if (resp) - purple_debug_info("MSN CL","Contact updated successfully\n"); - else - purple_debug_info("MSN CL","Contact updated successfully\n"); + purple_debug_info("msn", "Contact updated successfully\n"); } -/* Update a contact's nickname */ +/* Update a contact's info */ void -msn_update_contact(MsnContact *contact, const char* nickname) +msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value) { - gchar *body = NULL, *escaped_nickname; + MsnCallbackState *state; + xmlnode *contact; + xmlnode *contact_info; + xmlnode *changes; + + purple_debug_info("msn", "Update contact information with new %s: %s\n", + type==MSN_UPDATE_DISPLAY ? "display name" : "alias", value); + purple_debug_info("msn", "passport=%s\n", passport); + g_return_if_fail(passport != NULL); + contact_info = xmlnode_new("contactInfo"); + changes = xmlnode_new("propertiesChanged"); - purple_debug_info("MSN CL","Update contact information with new friendly name: %s\n", nickname); + switch (type) { + xmlnode *annotations; + xmlnode *display; + xmlnode *a, *n, *v; + case MSN_UPDATE_DISPLAY: + display = xmlnode_new_child(contact_info, "displayName"); + xmlnode_insert_data(display, value, -1); + xmlnode_insert_data(changes, "DisplayName", -1); + break; - escaped_nickname = g_markup_escape_text(nickname, -1); + case MSN_UPDATE_ALIAS: + annotations = xmlnode_new_child(contact_info, "annotations"); + xmlnode_insert_data(changes, "Annotation ", -1); - body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname); + a = xmlnode_new_child(annotations, "Annotation"); + n = xmlnode_new_child(a, "Name"); + xmlnode_insert_data(n, "AB.NickName", -1); + v = xmlnode_new_child(a, "Value"); + xmlnode_insert_data(v, value, -1); + break; + + default: + g_return_if_reached(); + } + + + + state = msn_callback_state_new(session); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_CONTACT_UPDATE_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_update_contact_read_cb, NULL); + state->body = xmlnode_from_str(MSN_CONTACT_UPDATE_TEMPLATE, -1); + state->action = MSN_UPDATE_INFO; + state->post_action = MSN_CONTACT_UPDATE_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_update_contact_read_cb; + + contact = xmlnode_get_child(state->body, "Body/ABContactUpdate/contacts/Contact"); + xmlnode_insert_child(contact, contact_info); + xmlnode_insert_child(contact, changes); - g_free(escaped_nickname); - g_free(body); + if (!strcmp(passport, "Me")) { + xmlnode *contactType = xmlnode_new_child(contact_info, "contactType"); + xmlnode_insert_data(contactType, "Me", -1); + } else { + MsnUser *user = msn_userlist_find_user(session->userlist, passport); + xmlnode *contactId = xmlnode_new_child(contact, "contactId"); + msn_callback_state_set_uid(state, user->uid); + xmlnode_insert_data(contactId, state->uid, -1); + } + + msn_contact_request(state); } static void @@ -1144,74 +1277,70 @@ MsnCallbackState *state = data; MsnSession *session = state->session; - if (resp != NULL) { - purple_debug_info("MSN CL", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]); + purple_debug_info("msn", "Contact %s deleted successfully from %s list on server!\n", state->who, MsnMemberRole[state->list_id]); - if (state->list_id == MSN_LIST_PL) { - MsnUser *user = msn_userlist_find_user(session->userlist, state->who); + if (state->list_id == MSN_LIST_PL) { + MsnUser *user = msn_userlist_find_user(session->userlist, state->who); + MsnCallbackState *new_state = msn_callback_state_dup(state); - if (user != NULL) - msn_user_unset_op(user, MSN_LIST_PL_OP); + if (user != NULL) + msn_user_unset_op(user, MSN_LIST_PL_OP); - msn_add_contact_to_list(session->contact, state, state->who, MSN_LIST_RL); - return; - } else if (state->list_id == MSN_LIST_AL) { - purple_privacy_permit_remove(session->account, state->who, TRUE); - msn_add_contact_to_list(session->contact, NULL, state->who, MSN_LIST_BL); - } else if (state->list_id == MSN_LIST_BL) { - purple_privacy_deny_remove(session->account, state->who, TRUE); - msn_add_contact_to_list(session->contact, NULL, state->who, MSN_LIST_AL); - } + msn_add_contact_to_list(session, new_state, state->who, MSN_LIST_RL); + return; + } else if (state->list_id == MSN_LIST_AL) { + purple_privacy_permit_remove(session->account, state->who, TRUE); + msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_BL); + } else if (state->list_id == MSN_LIST_BL) { + purple_privacy_deny_remove(session->account, state->who, TRUE); + msn_add_contact_to_list(session, NULL, state->who, MSN_LIST_AL); } - msn_callback_state_free(state); } void -msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state, +msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list) { gchar *body = NULL, *member = NULL; MsnSoapPartnerScenario partner_scenario; MsnUser *user; - g_return_if_fail(contact != NULL); + g_return_if_fail(session != NULL); g_return_if_fail(passport != NULL); g_return_if_fail(list < 5); - purple_debug_info("MSN CL", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]); + purple_debug_info("msn", "Deleting contact %s from %s list\n", passport, MsnMemberRole[list]); if (state == NULL) { - state = msn_callback_state_new(contact->session); + state = msn_callback_state_new(session); } msn_callback_state_set_list_id(state, list); msn_callback_state_set_who(state, passport); if (list == MSN_LIST_PL) { - g_return_if_fail(contact->session != NULL); - g_return_if_fail(contact->session->userlist != NULL); + g_return_if_fail(session->userlist != NULL); - user = msn_userlist_find_user(contact->session->userlist, passport); + user = msn_userlist_find_user(session->userlist, passport); partner_scenario = MSN_PS_CONTACT_API; member = g_strdup_printf(MSN_MEMBER_MEMBERSHIPID_XML, user->membership_id[MSN_LIST_PL]); } else { /* list == MSN_LIST_AL || list == MSN_LIST_BL */ partner_scenario = MSN_PS_BLOCK_UNBLOCK; - + member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport); } - body = g_strdup_printf( MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE, - MsnSoapPartnerScenarioText[partner_scenario], - MsnMemberRole[list], - member); + body = g_strdup_printf(MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE, + MsnSoapPartnerScenarioText[partner_scenario], + MsnMemberRole[list], member); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_SHARE_POST_URL, - msn_del_contact_from_list_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION; + state->post_url = MSN_SHARE_POST_URL; + state->cb = msn_del_contact_from_list_read_cb; + msn_contact_request(state); g_free(member); g_free(body); @@ -1225,47 +1354,41 @@ g_return_if_fail(state != NULL); g_return_if_fail(state->session != NULL); - g_return_if_fail(state->session->contact != NULL); + + purple_debug_info("msn", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]); - if (resp != NULL) { - purple_debug_info("MSN CL", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]); - - if (state->list_id == MSN_LIST_RL) { - MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who); + if (state->list_id == MSN_LIST_RL) { + MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who); - if (user != NULL) { - msn_user_set_op(user, MSN_LIST_RL_OP); - } - - if (state->action & MSN_DENIED_BUDDY) { + if (user != NULL) { + msn_user_set_op(user, MSN_LIST_RL_OP); + } - msn_add_contact_to_list(state->session->contact, NULL, state->who, MSN_LIST_BL); - } else if (state->list_id == MSN_LIST_AL) { - purple_privacy_permit_add(state->session->account, state->who, TRUE); - } else if (state->list_id == MSN_LIST_BL) { - purple_privacy_deny_add(state->session->account, state->who, TRUE); - } + if (state->action & MSN_DENIED_BUDDY) { + msn_add_contact_to_list(state->session, NULL, state->who, MSN_LIST_BL); + } else if (state->list_id == MSN_LIST_AL) { + purple_privacy_permit_add(state->session->account, state->who, TRUE); + } else if (state->list_id == MSN_LIST_BL) { + purple_privacy_deny_add(state->session->account, state->who, TRUE); } } - - msn_callback_state_free(state); } void -msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state, +msn_add_contact_to_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list) { gchar *body = NULL, *member = NULL; MsnSoapPartnerScenario partner_scenario; - g_return_if_fail(contact != NULL); + g_return_if_fail(session != NULL); g_return_if_fail(passport != NULL); g_return_if_fail(list < 5); - purple_debug_info("MSN CL", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]); + purple_debug_info("msn", "Adding contact %s to %s list\n", passport, MsnMemberRole[list]); if (state == NULL) { - state = msn_callback_state_new(contact->session); + state = msn_callback_state_new(session); } msn_callback_state_set_list_id(state, list); msn_callback_state_set_who(state, passport); @@ -1275,15 +1398,14 @@ member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, state->who); body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE, - MsnSoapPartnerScenarioText[partner_scenario], - MsnMemberRole[list], - member); + MsnSoapPartnerScenarioText[partner_scenario], + MsnMemberRole[list], member); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_SHARE_POST_URL, - msn_add_contact_to_list_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION; + state->post_url = MSN_SHARE_POST_URL; + state->cb = msn_add_contact_to_list_read_cb; + msn_contact_request(state); g_free(member); g_free(body); @@ -1293,24 +1415,23 @@ static void msn_gleams_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { - if (resp != NULL) - purple_debug_info("MSNP14","Gleams read done\n"); - else - purple_debug_info("MSNP14","Gleams read failed\n"); + purple_debug_info("msn", "Gleams read done\n"); } /*get the gleams info*/ void -msn_get_gleams(MsnContact *contact) +msn_get_gleams(MsnSession *session) { MsnSoapReq *soap_request; - purple_debug_info("MSNP14","msn get gleams info...\n"); - msn_soap_message_send(contact->session, - msn_soap_message_new(MSN_GET_GLEAMS_SOAP_ACTION, - xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_gleams_read_cb, NULL); + purple_debug_info("msn", "msn get gleams info...\n"); + + state = msn_callback_state_new(session); + state->body = xmlnode_from_str(MSN_GLEAMS_TEMPLATE, -1); + state->post_action = MSN_GET_GLEAMS_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_gleams_read_cb; + msn_contact_request(state); } #endif @@ -1323,68 +1444,62 @@ msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnCallbackState *state = data; + MsnSession *session; + MsnUserList *userlist; - purple_debug_info("MSNCL", "Group request successful.\n"); + purple_debug_info("msn", "Group request successful.\n"); g_return_if_fail(state->session != NULL); g_return_if_fail(state->session->userlist != NULL); - g_return_if_fail(state->session->contact != NULL); + + session = state->session; + userlist = session->userlist; - if (resp == NULL) { - msn_callback_state_free(state); - return; + if (state->action & MSN_RENAME_GROUP) { + msn_userlist_rename_group_id(session->userlist, + state->guid, + state->new_group_name); } - if (state) { - MsnSession *session = state->session; - MsnUserList *userlist = session->userlist; - - if (state->action & MSN_RENAME_GROUP) { - msn_userlist_rename_group_id(session->userlist, - state->guid, - state->new_group_name); - } + if (state->action & MSN_ADD_GROUP) { + /* the response is taken from + http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions + should copy it to msnpiki some day */ + xmlnode *guid_node = xmlnode_get_child(resp->xml, + "Body/ABGroupAddResponse/ABGroupAddResult/guid"); - if (state->action & MSN_ADD_GROUP) { - /* the response is taken from - http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions - should copy it to msnpiki some day */ - xmlnode *guid_node = msn_soap_xml_get(resp->xml, - "Body/ABGroupAddResponse/ABGroupAddResult/guid"); + if (guid_node) { + char *guid = xmlnode_get_data(guid_node); - if (guid_node) { - char *guid = xmlnode_get_data(guid_node); - - /* create and add the new group to the userlist */ - purple_debug_info("MSNCL", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid); - msn_group_new(session->userlist, guid, state->new_group_name); + /* create and add the new group to the userlist */ + purple_debug_info("msn", "Adding group %s with guid = %s to the userlist\n", state->new_group_name, guid); + msn_group_new(session->userlist, guid, state->new_group_name); - if (state->action & MSN_ADD_BUDDY) { - msn_userlist_add_buddy(session->userlist, - state->who, - state->new_group_name); - } else if (state->action & MSN_MOVE_BUDDY) { - msn_add_contact_to_group(session->contact, state, state->who, guid); - g_free(guid); - return; - } - g_free(guid); - } else { - purple_debug_info("MSNCL", "Adding group %s failed\n", + if (state->action & MSN_ADD_BUDDY) { + msn_userlist_add_buddy(session->userlist, + state->who, state->new_group_name); + } else if (state->action & MSN_MOVE_BUDDY) { + /* This will be freed when the add contact callback fires */ + MsnCallbackState *new_state = msn_callback_state_dup(state); + msn_add_contact_to_group(session, new_state, state->who, guid); + g_free(guid); + return; } + g_free(guid); + } else { + purple_debug_info("msn", "Adding group %s failed\n", + state->new_group_name); } - - if (state->action & MSN_DEL_GROUP) { - GList *l; + } - msn_userlist_remove_group_id(session->userlist, state->guid); - for (l = userlist->users; l != NULL; l = l->next) { - msn_user_remove_group_id( (MsnUser *)l->data, state->guid); - } + if (state->action & MSN_DEL_GROUP) { + GList *l; + + msn_userlist_remove_group_id(session->userlist, state->guid); + for (l = userlist->users; l != NULL; l = l->next) { + msn_user_remove_group_id( (MsnUser *)l->data, state->guid); } - - msn_callback_state_free(state); } } @@ -1393,11 +1508,12 @@ msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_name) { char *body = NULL; + char *escaped_group_name = NULL; g_return_if_fail(session != NULL); g_return_if_fail(group_name != NULL); - purple_debug_info("MSNCL","Adding group %s to contact list.\n", group_name); + purple_debug_info("msn", "Adding group %s to contact list.\n", group_name); if (state == NULL) { state = msn_callback_state_new(session); @@ -1409,14 +1525,16 @@ /* escape group name's html special chars so it can safely be sent * in a XML SOAP request */ - body = g_markup_printf_escaped(MSN_GROUP_ADD_TEMPLATE, group_name); + escaped_group_name = g_markup_escape_text(group_name, -1); + body = g_strdup_printf(MSN_GROUP_ADD_TEMPLATE, escaped_group_name); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GROUP_ADD_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_group_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GROUP_ADD_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_group_read_cb; + msn_contact_request(state); + g_free(escaped_group_name); g_free(body); } @@ -1431,7 +1549,7 @@ g_return_if_fail(session != NULL); g_return_if_fail(group_name != NULL); - purple_debug_info("MSNCL","Deleting group %s from contact list\n", group_name); + purple_debug_info("msn", "Deleting group %s from contact list\n", group_name); guid = msn_userlist_find_group_id(session->userlist, group_name); @@ -1439,12 +1557,12 @@ * we need to delete nothing */ if (guid == NULL) { - purple_debug_info("MSNCL", "Group %s guid not found, returning.\n", group_name); + purple_debug_info("msn", "Group %s guid not found, returning.\n", group_name); return; } if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) { - // XXX add back PurpleGroup since it isn't really removed in the server? + /* XXX add back PurpleGroup since it isn't really removed in the server? */ return; } @@ -1454,11 +1572,11 @@ body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GROUP_DEL_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_group_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GROUP_DEL_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_group_read_cb; + msn_contact_request(state); g_free(body); } @@ -1470,13 +1588,14 @@ gchar *body = NULL; const gchar * guid; MsnCallbackState *state; + char *escaped_group_name; g_return_if_fail(session != NULL); g_return_if_fail(session->userlist != NULL); g_return_if_fail(old_group_name != NULL); g_return_if_fail(new_group_name != NULL); - purple_debug_info("MSN CL", "Renaming group %s to %s.\n", old_group_name, new_group_name); + purple_debug_info("msn", "Renaming group %s to %s.\n", old_group_name, new_group_name); guid = msn_userlist_find_group_id(session->userlist, old_group_name); if (guid == NULL) @@ -1487,20 +1606,22 @@ msn_callback_state_set_new_group_name(state, new_group_name); if ( !strcmp(guid, MSN_INDIVIDUALS_GROUP_ID) || !strcmp(guid, MSN_NON_IM_GROUP_ID) ) { - msn_add_group(session, state, new_group_name); - // XXX move every buddy there (we probably need to fix concurrent SOAP reqs first) + MsnCallbackState *new_state = msn_callback_state_dup(state); + msn_add_group(session, new_state, new_group_name); + /* XXX move every buddy there (we probably need to fix concurrent SOAP reqs first) */ } msn_callback_state_set_action(state, MSN_RENAME_GROUP); - body = g_markup_printf_escaped(MSN_GROUP_RENAME_TEMPLATE, - guid, new_group_name); + escaped_group_name = g_markup_escape_text(new_group_name, -1); + body = g_strdup_printf(MSN_GROUP_RENAME_TEMPLATE, guid, escaped_group_name); - msn_soap_message_send(session, - msn_soap_message_new(MSN_GROUP_RENAME_SOAP_ACTION, - xmlnode_from_str(body, -1)), - MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL, - msn_group_read_cb, state); + state->body = xmlnode_from_str(body, -1); + state->post_action = MSN_GROUP_RENAME_SOAP_ACTION; + state->post_url = MSN_ADDRESS_BOOK_POST_URL; + state->cb = msn_group_read_cb; + msn_contact_request(state); + g_free(escaped_group_name); g_free(body); }
--- a/libpurple/protocols/msn/contact.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/contact.h Thu Nov 20 21:13:56 2008 +0000 @@ -25,25 +25,33 @@ #ifndef _MSN_CONTACT_H_ #define _MSN_CONTACT_H_ +#include "session.h" + +#define MSN_APPLICATION_ID "CFE80F9D-180F-4399-82AB-413F33A1FA11" + #define MSN_CONTACT_SERVER "contacts.msn.com" /* Get Contact List */ #define MSN_GET_CONTACT_POST_URL "/abservice/SharingService.asmx" #define MSN_GET_CONTACT_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/FindMembership" -#define MSN_GET_CONTACT_UPDATE_XML "<View>Full</View>"\ + +#define MSN_GET_CONTACT_UPDATE_XML \ + "<View>Full</View>"\ "<deltasOnly>true</deltasOnly>"\ "<lastChange>%s</lastChange>" + #define MSN_GET_CONTACT_TEMPLATE "<?xml version='1.0' encoding='utf-8'?>"\ "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<soap:Header xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId xmlns=\"http://www.msn.com/webservices/AddressBook\">" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration xmlns=\"http://www.msn.com/webservices/AddressBook\">false</IsMigration>"\ "<PartnerScenario xmlns=\"http://www.msn.com/webservices/AddressBook\">%s</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest xmlns=\"http://www.msn.com/webservices/AddressBook\">false</ManagedGroupRequest>"\ + "<TicketToken>%s</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ @@ -72,15 +80,20 @@ #define MSN_ADD_ADDRESSBOOK_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABAdd" #define MSN_ADD_ADDRESSBOOK_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Initial</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>%s</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -98,7 +111,8 @@ /* Get AddressBook */ #define MSN_GET_ADDRESS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll" #define MSN_GET_ADDRESS_FULL_TIME "0001-01-01T00:00:00.0000000-08:00" -#define MSN_GET_ADDRESS_UPDATE_XML "<deltasOnly>true</deltasOnly>"\ +#define MSN_GET_ADDRESS_UPDATE_XML \ + "<deltasOnly>true</deltasOnly>"\ "<lastChange>%s</lastChange>" #define MSN_GET_GLEAM_UPDATE_XML \ @@ -107,15 +121,20 @@ "<dynamicItemLastChange>%s</dynamicItemLastChange>" #define MSN_GET_ADDRESS_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>%s</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>%s</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -131,15 +150,20 @@ /*Gleams SOAP request template*/ #define MSN_GET_GLEAMS_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABFindAll" #define MSN_GLEAMS_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Initial</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -157,34 +181,78 @@ * Contact Management SOAP actions *******************************************************/ -/* Add a new contact t*/ +/* Add a new contact */ #define MSN_CONTACT_ADD_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactAdd" -#define MSN_CONTACT_LIVE_PENDING_XML "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><contactType>LivePending</contactType><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>" +#define MSN_CONTACT_LIVE_PENDING_XML \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<contactInfo>"\ + "<contactType>LivePending</contactType>"\ + "<passportName>%s</passportName>"\ + "<isMessengerUser>true</isMessengerUser>"\ + "</contactInfo>"\ + "</Contact>" -#define MSN_CONTACT_XML "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<contactInfo>"\ - "<passportName>%s</passportName>"\ - "<isSmtp>false</isSmtp>"\ - "<isMessengerUser>true</isMessengerUser>"\ - "</contactInfo>"\ - "</Contact>" +#define MSN_CONTACT_XML \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<contactInfo>"\ + "<passportName>%s</passportName>"\ + "<isSmtp>false</isSmtp>"\ + "<isMessengerUser>true</isMessengerUser>"\ + "</contactInfo>"\ + "</Contact>" -#define MSN_CONTACT_DISPLAYNAME_XML "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\"><contactInfo><displayName>%s</displayName><passportName>%s</passportName><isMessengerUser>true</isMessengerUser></contactInfo></Contact>" - -#define MSN_ADD_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>ContactSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><options><EnableAllowListManagement>true</EnableAllowListManagement></options></ABContactAdd></soap:Body></soap:Envelope>" +#define MSN_CONTACT_DISPLAYNAME_XML \ + "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<contactInfo>"\ + "<displayName>%s</displayName>"\ + "<passportName>%s</passportName>"\ + "<isMessengerUser>true</isMessengerUser>"\ + "</contactInfo>"\ + "</Contact>" -/* Add a contact to a group */ -#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactAdd" -#define MSN_ADD_CONTACT_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +#define MSN_ADD_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>ContactSave</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ + "</ABAuthHeader>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<ABContactAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<abId>00000000-0000-0000-0000-000000000000</abId>"\ + "<contacts>%s</contacts>"\ + "<options>"\ + "<EnableAllowListManagement>true</EnableAllowListManagement>"\ + "</options>"\ + "</ABContactAdd>"\ + "</soap:Body>"\ +"</soap:Envelope>" + +/* Add a contact to a group */ +#define MSN_ADD_CONTACT_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactAdd" +#define MSN_ADD_CONTACT_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ + "<soap:Header>"\ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ + "<IsMigration>false</IsMigration>"\ + "<PartnerScenario>ContactSave</PartnerScenario>"\ + "</ABApplicationHeader>"\ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -207,25 +275,81 @@ /* Delete a contact from the Contact List */ #define MSN_CONTACT_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactDelete" #define MSN_CONTACT_ID_XML "<Contact><contactId>%s</contactId></Contact>" -#define MSN_DEL_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts></ABContactDelete></soap:Body></soap:Envelope>" - -/* Remove a contact from a group */ -#define MSN_CONTACT_DEL_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactDelete" -#define MSN_CONTACT_DEL_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><contacts>%s</contacts><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupContactDelete></soap:Body></soap:Envelope>" - - -/* Update Contact Nickname */ -#define MSN_CONTACT_UPDATE_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate" -#define MSN_CONTACT_UPDATE_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +#define MSN_DEL_CONTACT_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>Timer</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ + "</ABAuthHeader>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<abId>00000000-0000-0000-0000-000000000000</abId>"\ + "<contacts>%s</contacts>"\ + "</ABContactDelete>"\ + "</soap:Body>"\ +"</soap:Envelope>" + +/* Remove a contact from a group */ +#define MSN_CONTACT_DEL_GROUP_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupContactDelete" +#define MSN_CONTACT_DEL_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ + "<soap:Header>"\ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ + "<IsMigration>false</IsMigration>"\ + "<PartnerScenario>Timer</PartnerScenario>"\ + "</ABApplicationHeader>"\ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ + "</ABAuthHeader>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<abId>00000000-0000-0000-0000-000000000000</abId>"\ + "<contacts>%s</contacts>"\ + "<groupFilter>"\ + "<groupIds>"\ + "<guid>%s</guid>"\ + "</groupIds>"\ + "</groupFilter>"\ + "</ABGroupContactDelete>"\ + "</soap:Body>"\ +"</soap:Envelope>" + + +/* Update Contact Information */ +#define MSN_CONTACT_UPDATE_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABContactUpdate" +#define MSN_CONTACT_UPDATE_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ + "<soap:Header>"\ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ + "<IsMigration>false</IsMigration>"\ + "<PartnerScenario>Timer</PartnerScenario>"\ + "</ABApplicationHeader>"\ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -233,18 +357,13 @@ "<abId>00000000-0000-0000-0000-000000000000</abId>"\ "<contacts>"\ "<Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<contactInfo>"\ - "<contactType>Me</contactType>"\ - "<displayName>%s</displayName>"\ - "</contactInfo>"\ - "<propertiesChanged>DisplayName</propertiesChanged>"\ + ""\ "</Contact>"\ "</contacts>"\ "</ABContactUpdate>"\ "</soap:Body>"\ "</soap:Envelope>" - /******************************************************* * Add/Delete contact from lists SOAP actions *******************************************************/ @@ -255,30 +374,37 @@ #define MSN_ADD_MEMBER_TO_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/AddMember" #define MSN_DELETE_MEMBER_FROM_LIST_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/DeleteMember" -#define MSN_MEMBER_PASSPORT_XML "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\ - "<Type>Passport</Type>"\ - "<State>Accepted</State>"\ - "<PassportName>%s</PassportName>"\ - "</Member>" +#define MSN_MEMBER_PASSPORT_XML \ + "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\ + "<Type>Passport</Type>"\ + "<State>Accepted</State>"\ + "<PassportName>%s</PassportName>"\ + "</Member>" -#define MSN_MEMBER_MEMBERSHIPID_XML "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\ - "<Type>Passport</Type>"\ - "<MembershipId>%u</MembershipId>"\ - "<State>Accepted</State>"\ - "</Member>" +#define MSN_MEMBER_MEMBERSHIPID_XML \ + "<Member xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"PassportMember\">"\ + "<Type>Passport</Type>"\ + "<MembershipId>%u</MembershipId>"\ + "<State>Accepted</State>"\ + "</Member>" /* first delete contact from allow list */ #define MSN_CONTACT_DELECT_FROM_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>%s</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -301,15 +427,20 @@ "</soap:Envelope>" #define MSN_CONTACT_ADD_TO_LIST_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ "<soap:Header>"\ "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ - "<ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId>"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ "<IsMigration>false</IsMigration>"\ "<PartnerScenario>%s</PartnerScenario>"\ "</ABApplicationHeader>"\ "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ "</ABAuthHeader>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -339,36 +470,124 @@ /* add a group */ #define MSN_GROUP_ADD_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupAdd" -#define MSN_GROUP_ADD_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo><GroupInfo><name>%s</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo></groupInfo></ABGroupAdd></soap:Body></soap:Envelope>" +#define MSN_GROUP_ADD_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ + "<soap:Header>"\ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ + "<IsMigration>false</IsMigration>"\ + "<PartnerScenario>GroupSave</PartnerScenario>"\ + "</ABApplicationHeader>"\ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ + "</ABAuthHeader>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<abId>00000000-0000-0000-0000-000000000000</abId>"\ + "<groupAddOptions>"\ + "<fRenameOnMsgrConflict>false</fRenameOnMsgrConflict>"\ + "</groupAddOptions>"\ + "<groupInfo>"\ + "<GroupInfo>"\ + "<name>%s</name>"\ + "<groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType>"\ + "<fMessenger>false</fMessenger>"\ + "<annotations>"\ + "<Annotation>"\ + "<Name>MSN.IM.Display</Name>"\ + "<Value>1</Value>"\ + "</Annotation>"\ + "</annotations>"\ + "</GroupInfo>"\ + "</groupInfo>"\ + "</ABGroupAdd>"\ + "</soap:Body>"\ +"</soap:Envelope>" /* delete a group */ #define MSN_GROUP_DEL_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupDelete" -#define MSN_GROUP_DEL_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds><guid>%s</guid></groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>" +#define MSN_GROUP_DEL_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ + "<soap:Header>"\ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ + "<IsMigration>false</IsMigration>"\ + "<PartnerScenario>Timer</PartnerScenario>"\ + "</ABApplicationHeader>"\ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ + "</ABAuthHeader>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<abId>00000000-0000-0000-0000-000000000000</abId>"\ + "<groupFilter>"\ + "<groupIds>"\ + "<guid>%s</guid>"\ + "</groupIds>"\ + "</groupFilter>"\ + "</ABGroupDelete>"\ + "</soap:Body>"\ +"</soap:Envelope>" /* change a group's name */ #define MSN_GROUP_RENAME_SOAP_ACTION "http://www.msn.com/webservices/AddressBook/ABGroupUpdate" -#define MSN_GROUP_RENAME_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groups><Group><groupId>%s</groupId><groupInfo><name>%s</name></groupInfo><propertiesChanged>GroupName </propertiesChanged></Group></groups></ABGroupUpdate></soap:Body></soap:Envelope>" +#define MSN_GROUP_RENAME_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">"\ + "<soap:Header>"\ + "<ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ApplicationId>" MSN_APPLICATION_ID "</ApplicationId>"\ + "<IsMigration>false</IsMigration>"\ + "<PartnerScenario>Timer</PartnerScenario>"\ + "</ABApplicationHeader>"\ + "<ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<ManagedGroupRequest>false</ManagedGroupRequest>"\ + "<TicketToken>EMPTY</TicketToken>"\ + "</ABAuthHeader>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">"\ + "<abId>00000000-0000-0000-0000-000000000000</abId>"\ + "<groups>"\ + "<Group>"\ + "<groupId>%s</groupId>"\ + "<groupInfo>"\ + "<name>%s</name>"\ + "</groupInfo>"\ + "<propertiesChanged>GroupName </propertiesChanged>"\ + "</Group>"\ + "</groups>"\ + "</ABGroupUpdate>"\ + "</soap:Body>"\ +"</soap:Envelope>" typedef enum { - MSN_ADD_BUDDY = 0x01, - MSN_MOVE_BUDDY = 0x02, - MSN_ACCEPTED_BUDDY = 0x04, - MSN_DENIED_BUDDY = 0x08, - MSN_ADD_GROUP = 0x10, - MSN_DEL_GROUP = 0x20, - MSN_RENAME_GROUP = 0x40, + MSN_ADD_BUDDY = 0x01, + MSN_MOVE_BUDDY = 0x02, + MSN_ACCEPTED_BUDDY = 0x04, + MSN_DENIED_BUDDY = 0x08, + MSN_ADD_GROUP = 0x10, + MSN_DEL_GROUP = 0x20, + MSN_RENAME_GROUP = 0x40, + MSN_UPDATE_INFO = 0x80, } MsnCallbackAction; -typedef struct _MsnContact MsnContact; - -struct _MsnContact -{ - MsnSession *session; - - MsnSoapConn *soapconn; -}; - typedef struct _MsnCallbackState MsnCallbackState; struct _MsnCallbackState @@ -381,6 +600,11 @@ MsnListId list_id; MsnCallbackAction action; MsnSession *session; + xmlnode *body; + xmlnode *token; + const gchar *post_action; + const gchar *post_url; + /* MsnSoapCallback */ void *cb; }; typedef enum @@ -392,13 +616,18 @@ MSN_PS_BLOCK_UNBLOCK } MsnSoapPartnerScenario; +typedef enum +{ + MSN_UPDATE_DISPLAY, /* Real display name */ + MSN_UPDATE_ALIAS, /* Aliased display name */ + MSN_UPDATE_COMMENT +} MsnContactUpdateType; + /************************************************ * function prototype ************************************************/ -MsnContact * msn_contact_new(MsnSession *session); -void msn_contact_destroy(MsnContact *contact); - MsnCallbackState * msn_callback_state_new(MsnSession *session); +MsnCallbackState * msn_callback_state_dup(MsnCallbackState *state); void msn_callback_state_free(MsnCallbackState *state); void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who); void msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid); @@ -411,24 +640,24 @@ void msn_callback_state_set_action(MsnCallbackState *state, MsnCallbackAction action); -void msn_contact_connect(MsnContact *contact); -void msn_get_contact_list(MsnContact * contact, +void msn_contact_connect(MsnSession *session); +void msn_get_contact_list(MsnSession *session, const MsnSoapPartnerScenario partner_scenario, const char *update); -void msn_get_address_book(MsnContact *contact, +void msn_get_address_book(MsnSession *session, const MsnSoapPartnerScenario partner_scenario, const char * update, const char * gupdate); /* contact SOAP operations */ -void msn_update_contact(MsnContact *contact, const char* nickname); +void msn_update_contact(MsnSession *session, const char *passport, MsnContactUpdateType type, const char* value); -void msn_add_contact(MsnContact *contact, MsnCallbackState *state, +void msn_add_contact(MsnSession *session, MsnCallbackState *state, const char *passport); -void msn_delete_contact(MsnContact *contact, const char *contactId); +void msn_delete_contact(MsnSession *session, const char *contactId); -void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state, +void msn_add_contact_to_group(MsnSession *session, MsnCallbackState *state, const char *passport, const char *groupId); -void msn_del_contact_from_group(MsnContact *contact, const char *passport, +void msn_del_contact_from_group(MsnSession *session, const char *passport, const char *group_name); /* group operations */ void msn_add_group(MsnSession *session, MsnCallbackState *state, @@ -438,12 +667,10 @@ const char *new_group_name); /* lists operations */ -void msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state, +void msn_add_contact_to_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list); -void msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state, +void msn_del_contact_from_list(MsnSession *session, MsnCallbackState *state, const gchar *passport, const MsnListId list); -void msn_contact_connect_init(MsnSoapConn *soapconn); - #endif /* _MSN_CONTACT_H_ */
--- a/libpurple/protocols/msn/group.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/group.h Thu Nov 20 21:13:56 2008 +0000 @@ -30,16 +30,8 @@ #include "session.h" #include "user.h" -#include "soap.h" #include "userlist.h" -#define MSN_ADD_GROUPS "<GroupInfo><name>test111</name><groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType><fMessenger>false</fMessenger><annotations><Annotation><Name>MSN.IM.Display</Name><Value>1</Value></Annotation></annotations></GroupInfo>" - -#define MSN_ADD_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>GroupSave</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupAddOptions><fRenameOnMsgrConflict>false</fRenameOnMsgrConflict></groupAddOptions><groupInfo>%s</groupInfo></ABGroupAdd></soap:Body></soap:Envelope>" - -#define MSN_GROUP_IDS "<guid>9e57e654-59f0-44d1-aedc-0a7500b7e51f</guid>" -#define MSN_DELETE_GROUP_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\"><soap:Header><ABApplicationHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ApplicationId>09607671-1C32-421F-A6A6-CBFAA51AB5F4</ApplicationId><IsMigration>false</IsMigration><PartnerScenario>Timer</PartnerScenario></ABApplicationHeader><ABAuthHeader xmlns=\"http://www.msn.com/webservices/AddressBook\"><ManagedGroupRequest>false</ManagedGroupRequest></ABAuthHeader></soap:Header><soap:Body><ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\"><abId>00000000-0000-0000-0000-000000000000</abId><groupFilter><groupIds>%s</groupIds></groupFilter></ABGroupDelete></soap:Body></soap:Envelope>" - #define MSN_INDIVIDUALS_GROUP_ID "1983" #define MSN_INDIVIDUALS_GROUP_NAME "Other Contacts" @@ -52,15 +44,14 @@ struct _MsnGroup { MsnSession *session; /**< The MSN session. */ - MsnSoapConn *soapconn; char *id; /**< The group ID. */ char *name; /**< The name of the group. */ }; -/**************************************************************************/ -/** @name Group API */ -/**************************************************************************/ +/************************************************************************** + ** @name Group API * + **************************************************************************/ /*@{*/ /** @@ -114,4 +105,6 @@ * @return The name. */ const char *msn_group_get_name(const MsnGroup *group); + #endif /* _MSN_GROUP_H_ */ +
--- a/libpurple/protocols/msn/httpconn.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/httpconn.c Thu Nov 20 21:13:56 2008 +0000 @@ -381,6 +381,7 @@ else { msn_cmdproc_process_cmd_text(servconn->cmdproc, cur); + servconn->payload_len = servconn->cmdproc->last_cmd->payload_len; } } while (servconn->connected && servconn->rx_len > 0); @@ -588,7 +589,8 @@ if (httpconn->virgin) { - host = "gateway.messenger.hotmail.com"; + /* QuLogic: This doesn't look right to me, but it still seems to work */ + host = MSN_HTTPCONN_SERVER; /* The first time servconn->host is the host we should connect to. */ params = g_strdup_printf("Action=open&Server=%s&IP=%s",
--- a/libpurple/protocols/msn/msn.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/msn.c Thu Nov 20 21:13:56 2008 +0000 @@ -27,6 +27,7 @@ #include "msn.h" #include "accountopt.h" +#include "contact.h" #include "msg.h" #include "page.h" #include "pluginpref.h" @@ -392,6 +393,32 @@ _("Cancel"), NULL); } +/* QuLogic: Disabled until confirmed correct. */ +#if 0 +static void +msn_show_blocked_text(PurplePluginAction *action) +{ + PurpleConnection *pc = (PurpleConnection *) action->context; + MsnSession *session; + char *title; + + session = pc->proto_data; + + title = g_strdup_printf(_("Blocked Text for %s"), session->account->username); + if (session->blocked_text == NULL) { + purple_notify_formatted(pc, title, title, NULL, _("No text is blocked for this account."), NULL, NULL); + } else { + char *blocked_text; + blocked_text = g_strdup_printf(_("MSN servers are currently blocking the following regular expressions:<br/>%s"), + session->blocked_text); + + purple_notify_formatted(pc, title, title, NULL, blocked_text, NULL, NULL); + g_free(blocked_text); + } + g_free(title); +} +#endif + static void msn_show_hotmail_inbox(PurplePluginAction *action) { @@ -401,14 +428,27 @@ gc = (PurpleConnection *) action->context; session = gc->proto_data; - if (session->passport_info.file == NULL) - { + if (!session->passport_info.email_enabled) { purple_notify_error(gc, NULL, - _("This Hotmail account may not be active."), NULL); + _("This account does not have email enabled."), NULL); return; } - purple_notify_uri(gc, session->passport_info.file); + /** apparently the correct value is 777, use 750 as a failsafe */ + if ((session->passport_info.mail_url == NULL) + || (time (NULL) - session->passport_info.mail_timestamp >= 750)) { + MsnTransaction *trans; + MsnCmdProc *cmdproc; + + cmdproc = session->notification->cmdproc; + + trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); + msn_transaction_set_data(trans, GUINT_TO_POINTER(TRUE)); + + msn_cmdproc_send_trans(cmdproc, trans); + + } else + purple_notify_uri(gc, session->passport_info.mail_url); } static void @@ -543,17 +583,26 @@ msn_can_receive_file(PurpleConnection *gc, const char *who) { PurpleAccount *account; - char *normal; + gchar *normal; gboolean ret; account = purple_connection_get_account(gc); normal = g_strdup(msn_normalize(account, purple_account_get_username(account))); - ret = strcmp(normal, msn_normalize(account, who)); - g_free(normal); + if (ret) { + MsnSession *session = gc->proto_data; + if (session) { + MsnUser *user = msn_userlist_find_user(session->userlist, who); + if (user) + /* Include these too: MSN_CLIENT_CAP_MSNMOBILE|MSN_CLIENT_CAP_MSNDIRECT ? */ + ret = (user->clientid & MSN_CLIENT_CAP_WEBMSGR) == 0; + } else + ret = FALSE; + } + return ret; } @@ -567,6 +616,30 @@ return "msn"; } +static const char * +msn_list_emblems(PurpleBuddy *b) +{ + MsnUser *user = b->proto_data; + + if (user != NULL) { + if (user->clientid & MSN_CLIENT_CAP_BOT) + return "bot"; + if (user->clientid & MSN_CLIENT_CAP_WIN_MOBILE) + return "mobile"; +#if 0 + /* XXX: Since we don't support this, there's no point in showing it just yet */ + if (user->clientid & MSN_CLIENT_CAP_SCHANNEL) + return "secure"; +#endif + if (user->clientid & MSN_CLIENT_CAP_WEBMSGR) + return "external"; + if (user->networkid == MSN_NETWORK_YAHOO) + return "yahoo"; + } + + return NULL; +} + /* * Set the User status text */ @@ -773,10 +846,6 @@ static GList * msn_actions(PurplePlugin *plugin, gpointer context) { - PurpleConnection *gc = (PurpleConnection *)context; - PurpleAccount *account; - const char *user; - GList *m = NULL; PurplePluginAction *act; @@ -808,17 +877,18 @@ msn_show_set_mobile_pages); m = g_list_append(m, act); - account = purple_connection_get_account(gc); - user = msn_normalize(account, purple_account_get_username(account)); - - if ((strstr(user, "@hotmail.") != NULL) || - (strstr(user, "@msn.com") != NULL)) - { - m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Open Hotmail Inbox"), - msn_show_hotmail_inbox); - m = g_list_append(m, act); - } +/* QuLogic: Disabled until confirmed correct. */ +#if 0 + m = g_list_append(m, NULL); + act = purple_plugin_action_new(_("View Blocked Text..."), + msn_show_blocked_text); + m = g_list_append(m, act); +#endif + + m = g_list_append(m, NULL); + act = purple_plugin_action_new(_("Open Hotmail Inbox"), + msn_show_hotmail_inbox); + m = g_list_append(m, act); return m; } @@ -1042,15 +1112,20 @@ { PurpleAccount *account; PurpleBuddy *buddy = purple_find_buddy(gc->account, who); + MsnSession *session; + MsnSwitchBoard *swboard; MsnMessage *msg; char *msgformat; char *msgtext; const char *username; - purple_debug_info("MSNP14","send IM {%s} to %s\n",message,who); + purple_debug_info("msn", "send IM {%s} to %s\n",message,who); account = purple_connection_get_account(gc); username = purple_account_get_username(account); + session = gc->proto_data; + swboard = msn_session_find_swboard(session, who); + if (buddy) { PurplePresence *p = purple_buddy_get_presence(buddy); if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { @@ -1062,10 +1137,12 @@ } msn_import_html(message, &msgformat, &msgtext); - if(msn_user_is_online(account, who)|| - msn_user_is_yahoo(account, who)){ - /*User online,then send Online Instant Message*/ - + if (msn_user_is_online(account, who)|| + msn_user_is_yahoo(account, who) || + swboard != NULL){ + /*User online or have a swboard open because it's invisible + * and sent us a message,then send Online Instant Message*/ + if (strlen(msgtext) + strlen(msgformat) + strlen(VERSION) > 1564) { g_free(msgformat); @@ -1081,22 +1158,19 @@ g_free(msgformat); g_free(msgtext); - purple_debug_info("MSNP14","prepare to send online Message\n"); + purple_debug_info("msn", "prepare to send online Message\n"); if (g_ascii_strcasecmp(who, username)) { - MsnSession *session; - MsnSwitchBoard *swboard; MsnEmoticon *smile; GSList *smileys; GString *emoticons = NULL; - session = gc->proto_data; if(msn_user_is_yahoo(account,who)){ /*we send the online and offline Message to Yahoo User via UBM*/ - purple_debug_info("MSNP14","send to Yahoo User\n"); + purple_debug_info("msn", "send to Yahoo User\n"); uum_send_msg(session,msg); }else{ - purple_debug_info("MSNP14","send via switchboard\n"); + purple_debug_info("msn", "send via switchboard\n"); swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM); smileys = msn_msg_grab_emoticons(message, username); while (smileys) { @@ -1145,19 +1219,20 @@ } msn_message_destroy(msg); - }else { + } else { /*send Offline Instant Message,only to MSN Passport User*/ - MsnSession *session; char *friendname; - purple_debug_info("MSNP14","prepare to send offline Message\n"); - session = gc->proto_data; + purple_debug_info("msn", "prepare to send offline Message\n"); friendname = msn_encode_mime(account->username); msn_oim_prep_send_msg_info(session->oim, purple_account_get_username(account), - friendname, who, message); + friendname, who, msgtext); msn_oim_send_msg(session->oim); + + g_free(msgformat); + g_free(msgtext); g_free(friendname); } @@ -1279,6 +1354,7 @@ if (group_id >= 0) { + /* This is wrong... user->group_ids contains g_strdup()'d data now */ user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(group_id)); } @@ -1298,7 +1374,7 @@ userlist = session->userlist; who = msn_normalize(gc->account, buddy->name); - purple_debug_info("MSN","Add user:%s to group:%s\n", who, (group && group->name) ? group->name : "(null)"); + purple_debug_info("msn", "Add user:%s to group:%s\n", who, (group && group->name) ? group->name : "(null)"); if (!session->logged_in) { #if 0 @@ -1369,10 +1445,10 @@ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL); /* delete contact from Block list and add it to Allow in the callback */ - msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL); + msn_del_contact_from_list(session, NULL, who, MSN_LIST_BL); } else { /* just add the contact to Allow list */ - msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_AL); + msn_add_contact_to_list(session, NULL, who, MSN_LIST_AL); } @@ -1397,10 +1473,10 @@ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL); /* delete contact from Allow list and add it to Block in the callback */ - msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL); + msn_del_contact_from_list(session, NULL, who, MSN_LIST_AL); } else { /* just add the contact to Block list */ - msn_add_contact_to_list(session->contact, NULL, who, MSN_LIST_BL); + msn_add_contact_to_list(session, NULL, who, MSN_LIST_BL); } msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL); @@ -1423,7 +1499,7 @@ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_AL); - msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_AL); + msn_del_contact_from_list(session, NULL, who, MSN_LIST_AL); if (user != NULL && user->list_op & MSN_LIST_RL_OP) msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_BL); @@ -1446,7 +1522,7 @@ msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_BL); - msn_del_contact_from_list(session->contact, NULL, who, MSN_LIST_BL); + msn_del_contact_from_list(session, NULL, who, MSN_LIST_BL); if (user != NULL && user->list_op & MSN_LIST_RL_OP) msn_userlist_add_buddy_to_list(userlist, who, MSN_LIST_AL); @@ -1574,6 +1650,15 @@ } } +static void msn_alias_buddy(PurpleConnection *pc, const char *name, const char *alias) +{ + MsnSession *session; + + session = pc->proto_data; + + msn_update_contact(session, name, MSN_UPDATE_ALIAS, alias); +} + static void msn_group_buddy(PurpleConnection *gc, const char *who, const char *old_group_name, const char *new_group_name) @@ -1669,12 +1754,12 @@ session = gc->proto_data; cmdproc = session->notification->cmdproc; - purple_debug_info("MSN", "Remove group %s\n", group->name); + purple_debug_info("msn", "Remove group %s\n", group->name); /*we can't delete the default group*/ if(!strcmp(group->name, MSN_INDIVIDUALS_GROUP_NAME)|| !strcmp(group->name, MSN_NON_IM_GROUP_NAME)) { - purple_debug_info("MSN", "This group can't be removed, returning.\n"); + purple_debug_info("msn", "This group can't be removed, returning.\n"); return ; } @@ -1739,7 +1824,7 @@ } static void msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data, - const gchar *url_text, size_t len, const gchar *error_message); + const gchar *url_text, gsize len, const gchar *error_message); #endif @@ -2174,7 +2259,7 @@ #if PHOTO_SUPPORT /* Find the URL to the photo; must be before the marshalling [Bug 994207] */ photo_url_text = msn_get_photo_url(url_text); - purple_debug_info("MSNP14","photo url:{%s}\n", photo_url_text ? photo_url_text : "(null)"); + purple_debug_info("msn", "photo url:{%s}\n", photo_url_text ? photo_url_text : "(null)"); /* Marshall the existing state */ info2_data = g_new0(MsnGetInfoStepTwoData, 1); @@ -2375,7 +2460,7 @@ NULL, /* protocol_options */ {"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ msn_list_icon, /* list_icon */ - NULL, /* list_emblems */ + msn_list_emblems, /* list_emblems */ msn_status_text, /* status_text */ msn_tooltip_text, /* tooltip_text */ msn_status_types, /* away_states */ @@ -2411,7 +2496,7 @@ NULL, /* register_user */ NULL, /* get_cb_info */ NULL, /* get_cb_away */ - NULL, /* alias_buddy */ + msn_alias_buddy, /* alias_buddy */ msn_group_buddy, /* group_buddy */ msn_rename_group, /* rename_group */ NULL, /* buddy_free */ @@ -2484,11 +2569,11 @@ PurpleAccountOption *option; option = purple_account_option_string_new(_("Server"), "server", - WLM_SERVER); + MSN_SERVER); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_int_new(_("Port"), "port", WLM_PORT); + option = purple_account_option_int_new(_("Port"), "port", MSN_PORT); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
--- a/libpurple/protocols/msn/msn.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/msn.h Thu Nov 20 21:13:56 2008 +0000 @@ -57,35 +57,25 @@ #define MSN_BUF_LEN 8192 -#define USEROPT_MSNSERVER 3 +/* Windows Live Messenger Server*/ #define MSN_SERVER "messenger.hotmail.com" #define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com" -#define USEROPT_MSNPORT 4 #define MSN_PORT 1863 +#define WLM_PROT_VER 15 -/* Windows Live Messenger Server*/ -#define WLM_SERVER "muser.messenger.hotmail.com" -#define WLM_PORT 1863 -#define WLM_PROT_VER 13 -/*This MSNP14 Support chat with Yahoo Messenger*/ -#define WLM_YAHOO_PROT_VER 14 - -#define WLM_MAX_PROTOCOL 14 -#define WLM_MIN_PROTOCOL 13 +#define WLM_MAX_PROTOCOL 15 +#define WLM_MIN_PROTOCOL 15 #define MSN_TYPING_RECV_TIMEOUT 6 #define MSN_TYPING_SEND_TIMEOUT 4 -#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders"w3 -#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" #define PROFILE_URL "http://spaces.live.com/profile.aspx?mem=" #define PHOTO_URL " contactparams:photopreauthurl=\"" -#define USEROPT_HOTMAIL 0 - #define BUDDY_ALIAS_MAXLEN 387 -#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" +#define MSN_FT_GUID "5D3E02AB-6190-11D3-BBBB-00C04F795683" +#define MSN_OBJ_GUID "A4268EEC-FEC5-49E5-95C3-F126696BDBF6" #define MSN_CLIENTINFO \ "Client-Name: Purple/" VERSION "\r\n" \ @@ -107,18 +97,25 @@ typedef enum { - MSN_CLIENT_CAP_WIN_MOBILE = 0x00001, - MSN_CLIENT_CAP_UNKNOWN_1 = 0x00002, - MSN_CLIENT_CAP_INK_GIF = 0x00004, - MSN_CLIENT_CAP_INK_ISF = 0x00008, - MSN_CLIENT_CAP_VIDEO_CHAT = 0x00010, - MSN_CLIENT_CAP_BASE = 0x00020, - MSN_CLIENT_CAP_MSNMOBILE = 0x00040, - MSN_CLIENT_CAP_MSNDIRECT = 0x00080, - MSN_CLIENT_CAP_WEBMSGR = 0x00100, - MSN_CLIENT_CAP_DIRECTIM = 0x04000, - MSN_CLIENT_CAP_WINKS = 0x08000, - MSN_CLIENT_CAP_SEARCH = 0x10000 + MSN_CLIENT_CAP_WIN_MOBILE = 0x000001, + MSN_CLIENT_CAP_INK_GIF = 0x000004, + MSN_CLIENT_CAP_INK_ISF = 0x000008, + MSN_CLIENT_CAP_VIDEO_CHAT = 0x000010, + MSN_CLIENT_CAP_PACKET = 0x000020, + MSN_CLIENT_CAP_MSNMOBILE = 0x000040, + MSN_CLIENT_CAP_MSNDIRECT = 0x000080, + MSN_CLIENT_CAP_WEBMSGR = 0x000200, + MSN_CLIENT_CAP_TGW = 0x000800, + MSN_CLIENT_CAP_SPACE = 0x001000, + MSN_CLIENT_CAP_MCE = 0x002000, + MSN_CLIENT_CAP_DIRECTIM = 0x004000, + MSN_CLIENT_CAP_WINKS = 0x008000, + MSN_CLIENT_CAP_SEARCH = 0x010000, + MSN_CLIENT_CAP_BOT = 0x020000, + MSN_CLIENT_CAP_VOICEIM = 0x040000, + MSN_CLIENT_CAP_SCHANNEL = 0x080000, + MSN_CLIENT_CAP_SIP_INVITE = 0x100000, + MSN_CLIENT_CAP_SDRIVE = 0x400000 } MsnClientCaps; @@ -129,19 +126,18 @@ MSN_CLIENT_VER_6_1 = 0x20, /* MSNC2 */ MSN_CLIENT_VER_6_2 = 0x30, /* MSNC3 */ MSN_CLIENT_VER_7_0 = 0x40, /* MSNC4 */ - MSN_CLIENT_VER_7_5 = 0x50 /* MSNC5 */ + MSN_CLIENT_VER_7_5 = 0x50, /* MSNC5 */ + MSN_CLIENT_VER_8_0 = 0x60, /* MSNC6 */ + MSN_CLIENT_VER_8_1 = 0x70, /* MSNC7 */ + MSN_CLIENT_VER_8_5 = 0x80 /* MSNC8 */ } MsnClientVerId; #define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_7_0 -#define MSN_CLIENT_ID_RESERVED_1 0x00 -#define MSN_CLIENT_ID_RESERVED_2 0x00 -#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_BASE +#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_PACKET #define MSN_CLIENT_ID \ ((MSN_CLIENT_ID_VERSION << 24) | \ - (MSN_CLIENT_ID_RESERVED_1 << 16) | \ - (MSN_CLIENT_ID_RESERVED_2 << 8) | \ (MSN_CLIENT_ID_CAPABILITIES)) void msn_act_id(PurpleConnection *gc, const char *entry);
--- a/libpurple/protocols/msn/msnutils.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/msnutils.c Thu Nov 20 21:13:56 2008 +0000 @@ -23,8 +23,8 @@ */ #include "msn.h" #include "msnutils.h" -#include "time.h" -//#include <openssl/md5.h> + +#include "cipher.h" char *rand_guid(void); @@ -465,132 +465,118 @@ host = g_strdup(str); - if ((c = strchr(host, ':')) != NULL){ + if ((c = strchr(host, ':')) != NULL) { *c = '\0'; port = atoi(c + 1); - }else{ + } else { port = 1863; } *ret_host = host; *ret_port = port; } -/*************************************************************************** - * MSN Time Related Funciton - ***************************************************************************/ -#if 0 -int -msn_convert_iso8601(const char *timestr,struct tm tm_time) -{ - char temp[64]; - struct tm ctime; - time_t ts; - - purple_debug_info("MSNP14","convert string is{%s}\n",timestr); - tzset(); - /*copy string first*/ - memset(temp, 0, sizeof(temp)); - strncpy(temp, timestr, strlen(timestr)); - - /*convert via strptime()*/ - memset(&ctime, 0, sizeof(struct tm)); - strptime(temp, "%d %b %Y %T %Z", &ctime); - ts = mktime(&ctime) - timezone; - localtime_r(&ts, tm_time); -} -#endif /*************************************************************************** * MSN Challenge Computing Function ***************************************************************************/ /* - * Handle MSN Chanllege computation - *This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges + * Handle MSN Challenge computation + * This algorithm references + * http://imfreedom.org/wiki/index.php/MSN:NS/Challenges */ #define BUFSIZE 256 void msn_handle_chl(char *input, char *output) { - PurpleCipher *cipher; - PurpleCipherContext *context; - char *productKey = MSNP13_WLM_PRODUCT_KEY, - *productID = MSNP13_WLM_PRODUCT_ID, - *hexChars = "0123456789abcdef", - buf[BUFSIZE]; - unsigned char md5Hash[16], *newHash; - unsigned int *md5Parts, *chlStringParts, newHashParts[5]; + PurpleCipher *cipher; + PurpleCipherContext *context; + const guchar productKey[] = MSNP15_WLM_PRODUCT_KEY; + const guchar productID[] = MSNP15_WLM_PRODUCT_ID; + const char hexChars[] = "0123456789abcdef"; + char buf[BUFSIZE]; + unsigned char md5Hash[16]; + unsigned char *newHash; + unsigned int *md5Parts; + unsigned int *chlStringParts; + unsigned int newHashParts[5]; - long long nHigh=0, nLow=0; - - int i; + long long nHigh = 0, nLow = 0; - /* Create the MD5 hash by using Purple MD5 algorithm*/ - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); + int len; + int i; - purple_cipher_context_append(context, (const guchar *)input, - strlen(input)); - purple_cipher_context_append(context, (const guchar *)productKey, - strlen(productKey)); - purple_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL); - purple_cipher_context_destroy(context); + /* Create the MD5 hash by using Purple MD5 algorithm */ + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + + purple_cipher_context_append(context, (guchar *)input, strlen(input)); + purple_cipher_context_append(context, productKey, sizeof(productKey) - 1); + purple_cipher_context_digest(context, sizeof(md5Hash), md5Hash, NULL); + purple_cipher_context_destroy(context); - /* Split it into four integers */ - md5Parts = (unsigned int *)md5Hash; - for(i=0; i<4; i++){ - /* adjust endianess */ - md5Parts[i] = GUINT_TO_LE(md5Parts[i]); + /* Split it into four integers */ + md5Parts = (unsigned int *)md5Hash; + for (i = 0; i < 4; i++) { + /* adjust endianess */ + md5Parts[i] = GUINT_TO_LE(md5Parts[i]); - /* & each integer with 0x7FFFFFFF */ - /* and save one unmodified array for later */ - newHashParts[i] = md5Parts[i]; - md5Parts[i] &= 0x7FFFFFFF; - } + /* & each integer with 0x7FFFFFFF */ + /* and save one unmodified array for later */ + newHashParts[i] = md5Parts[i]; + md5Parts[i] &= 0x7FFFFFFF; + } - /* make a new string and pad with '0' */ - snprintf(buf, BUFSIZE-5, "%s%s", input, productID); - i = strlen(buf); - memset(&buf[i], '0', 8 - (i % 8)); - buf[i + (8 - (i % 8))]='\0'; - - /* split into integers */ - chlStringParts = (unsigned int *)buf; + /* make a new string and pad with '0' to length that's a multiple of 8 */ + snprintf(buf, BUFSIZE - 5, "%s%s", input, productID); + len = strlen(buf); + if ((len % 8) != 0) { + int fix = 8 - (len % 8); + memset(&buf[len], '0', fix); + buf[len + fix] = '\0'; + len += fix; + } - /* this is magic */ - for (i=0; i<(strlen(buf)/4)-1; i+=2){ - long long temp; + /* split into integers */ + chlStringParts = (unsigned int *)buf; - chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]); - chlStringParts[i+1] = GUINT_TO_LE(chlStringParts[i+1]); + /* this is magic */ + for (i = 0; i < (strlen(buf) / 4); i += 2) { + long long temp; - temp=(md5Parts[0] * (((0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF)+nHigh) + md5Parts[1])%0x7FFFFFFF; - nHigh=(md5Parts[2] * (((long long)chlStringParts[i+1]+temp) % 0x7FFFFFFF) + md5Parts[3]) % 0x7FFFFFFF; - nLow=nLow + nHigh + temp; - } - nHigh=(nHigh+md5Parts[1]) % 0x7FFFFFFF; - nLow=(nLow+md5Parts[3]) % 0x7FFFFFFF; + chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]); + chlStringParts[i + 1] = GUINT_TO_LE(chlStringParts[i + 1]); + + temp = (0x0E79A9C1 * (long long)chlStringParts[i]) % 0x7FFFFFFF; + temp = (md5Parts[0] * (temp + nLow) + md5Parts[1]) % 0x7FFFFFFF; + nHigh += temp; - newHashParts[0]^=nHigh; - newHashParts[1]^=nLow; - newHashParts[2]^=nHigh; - newHashParts[3]^=nLow; + temp = ((long long)chlStringParts[i + 1] + temp) % 0x7FFFFFFF; + nLow = (md5Parts[2] * temp + md5Parts[3]) % 0x7FFFFFFF; + nHigh += nLow; + } + nLow = (nLow + md5Parts[1]) % 0x7FFFFFFF; + nHigh = (nHigh + md5Parts[3]) % 0x7FFFFFFF; - /* adjust endianness */ - for(i=0; i<4; i++) - newHashParts[i] = GUINT_TO_LE(newHashParts[i]); - - /* make a string of the parts */ - newHash = (unsigned char *)newHashParts; + newHashParts[0] ^= nLow; + newHashParts[1] ^= nHigh; + newHashParts[2] ^= nLow; + newHashParts[3] ^= nHigh; - /* convert to hexadecimal */ - for (i=0; i<16; i++) - { - output[i*2]=hexChars[(newHash[i]>>4)&0xF]; - output[(i*2)+1]=hexChars[newHash[i]&0xF]; - } + /* adjust endianness */ + for(i = 0; i < 4; i++) + newHashParts[i] = GUINT_TO_LE(newHashParts[i]); + + /* make a string of the parts */ + newHash = (unsigned char *)newHashParts; - output[32]='\0'; + /* convert to hexadecimal */ + for (i = 0; i < 16; i++) + { + output[i * 2] = hexChars[(newHash[i] >> 4) & 0xF]; + output[(i * 2) + 1] = hexChars[newHash[i] & 0xF]; + } -// purple_debug_info("MSNP14","chl output{%s}\n",output); + output[32] = '\0'; } +
--- a/libpurple/protocols/msn/nexus.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/nexus.c Thu Nov 20 21:13:56 2008 +0000 @@ -22,11 +22,27 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "msn.h" -#include "soap2.h" +#include "soap.h" #include "nexus.h" #include "notification.h" -#undef NEXUS_LOGIN_TWN +/************************************************************************** + * Valid Ticket Tokens + **************************************************************************/ + +#define SSO_VALID_TICKET_DOMAIN 0 +#define SSO_VALID_TICKET_POLICY 1 +static char *ticket_domains[][2] = { + /* http://msnpiki.msnfanatic.com/index.php/MSNP15:SSO */ + /* {"Domain", "Policy Ref URI"}, Purpose */ + {"messengerclear.live.com", NULL}, /* Authentication for messenger. */ + {"messenger.msn.com", "?id=507"}, /* Authentication for receiving OIMs. */ + {"contacts.msn.com", "MBI"}, /* Authentication for the Contact server. */ + {"messengersecure.live.com", "MBI_SSL"}, /* Authentication for sending OIMs. */ + {"spaces.live.com", "MBI"}, /* Authentication for the Windows Live Spaces */ + {"livecontacts.live.com", "MBI"}, /* Live Contacts API, a simplified version of the Contacts SOAP service */ + {"storage.live.com", "MBI"}, /* Storage REST API */ +}; /************************************************************************** * Main @@ -36,12 +52,17 @@ msn_nexus_new(MsnSession *session) { MsnNexus *nexus; + int i; nexus = g_new0(MsnNexus, 1); nexus->session = session; - nexus->challenge_data = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, g_free); + nexus->token_len = sizeof(ticket_domains) / sizeof(char *[2]); + nexus->tokens = g_new0(MsnTicketToken, nexus->token_len); + + for (i = 0; i < nexus->token_len; i++) + nexus->tokens[i].token = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); return nexus; } @@ -49,79 +70,307 @@ void msn_nexus_destroy(MsnNexus *nexus) { - if (nexus->challenge_data != NULL) - g_hash_table_destroy(nexus->challenge_data); + int i; + for (i = 0; i < nexus->token_len; i++) { + g_hash_table_destroy(nexus->tokens[i].token); + g_free(nexus->tokens[i].secret); + } + + g_free(nexus->tokens); + g_free(nexus->policy); + g_free(nexus->nonce); + g_free(nexus->cipher); + g_free(nexus->secret); + g_free(nexus); +} + +/************************************************************************** + * RPS/SSO Authentication + **************************************************************************/ + +static char * +rps_create_key(const char *key, int key_len, const char *data, size_t data_len) +{ + const guchar magic[] = "WS-SecureConversation"; + const int magic_len = sizeof(magic) - 1; + + PurpleCipherContext *hmac; + guchar hash1[20], hash2[20], hash3[20], hash4[20]; + char *result; + + hmac = purple_cipher_context_new_by_name("hmac", NULL); + + purple_cipher_context_set_option(hmac, "hash", "sha1"); + purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len); + purple_cipher_context_append(hmac, magic, magic_len); + purple_cipher_context_append(hmac, (guchar *)data, data_len); + purple_cipher_context_digest(hmac, sizeof(hash1), hash1, NULL); + + purple_cipher_context_reset(hmac, NULL); + purple_cipher_context_set_option(hmac, "hash", "sha1"); + purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len); + purple_cipher_context_append(hmac, hash1, 20); + purple_cipher_context_append(hmac, magic, magic_len); + purple_cipher_context_append(hmac, (guchar *)data, data_len); + purple_cipher_context_digest(hmac, sizeof(hash2), hash2, NULL); + + purple_cipher_context_reset(hmac, NULL); + purple_cipher_context_set_option(hmac, "hash", "sha1"); + purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len); + purple_cipher_context_append(hmac, hash1, 20); + purple_cipher_context_digest(hmac, sizeof(hash3), hash3, NULL); + + purple_cipher_context_reset(hmac, NULL); + purple_cipher_context_set_option(hmac, "hash", "sha1"); + purple_cipher_context_set_key_with_len(hmac, (guchar *)key, key_len); + purple_cipher_context_append(hmac, hash3, sizeof(hash3)); + purple_cipher_context_append(hmac, magic, magic_len); + purple_cipher_context_append(hmac, (guchar *)data, data_len); + purple_cipher_context_digest(hmac, sizeof(hash4), hash4, NULL); + + purple_cipher_context_destroy(hmac); + + result = g_malloc(24); + memcpy(result, hash2, sizeof(hash2)); + memcpy(result + sizeof(hash2), hash4, 4); + + return result; +} + +static char * +des3_cbc(const char *key, const char *iv, const char *data, int len, gboolean decrypt) +{ + PurpleCipherContext *des3; + char *out; + size_t outlen; - g_free(nexus->challenge_data_str); - g_free(nexus); + des3 = purple_cipher_context_new_by_name("des3", NULL); + purple_cipher_context_set_key(des3, (guchar *)key); + purple_cipher_context_set_batch_mode(des3, PURPLE_CIPHER_BATCH_MODE_CBC); + purple_cipher_context_set_iv(des3, (guchar *)iv, 8); + + out = g_malloc(len); + if (decrypt) + purple_cipher_context_decrypt(des3, (guchar *)data, len, (guchar *)out, &outlen); + else + purple_cipher_context_encrypt(des3, (guchar *)data, len, (guchar *)out, &outlen); + + purple_cipher_context_destroy(des3); + + return out; +} + +#define CRYPT_MODE_CBC 1 +#define CIPHER_TRIPLE_DES 0x6603 +#define HASH_SHA1 0x8004 +static char * +msn_rps_encrypt(MsnNexus *nexus) +{ + MsnUsrKey *usr_key; + const char magic1[] = "SESSION KEY HASH"; + const char magic2[] = "SESSION KEY ENCRYPTION"; + PurpleCipherContext *hmac; + size_t len; + guchar hash[20]; + char *key1, *key2, *key3; + gsize key1_len; + int *iv; + char *nonce_fixed; + char *cipher; + char *response; + + usr_key = g_malloc(sizeof(MsnUsrKey)); + usr_key->size = GUINT32_TO_LE(28); + usr_key->crypt_mode = GUINT32_TO_LE(CRYPT_MODE_CBC); + usr_key->cipher_type = GUINT32_TO_LE(CIPHER_TRIPLE_DES); + usr_key->hash_type = GUINT32_TO_LE(HASH_SHA1); + usr_key->iv_len = GUINT32_TO_LE(8); + usr_key->hash_len = GUINT32_TO_LE(20); + usr_key->cipher_len = GUINT32_TO_LE(72); + + key1 = (char *)purple_base64_decode((const char *)nexus->tokens[MSN_AUTH_MESSENGER].secret, &key1_len); + key2 = rps_create_key(key1, key1_len, magic1, sizeof(magic1) - 1); + key3 = rps_create_key(key1, key1_len, magic2, sizeof(magic2) - 1); + + iv = (int *)usr_key->iv; + iv[0] = rand(); + iv[1] = rand(); + + len = strlen(nexus->nonce); + hmac = purple_cipher_context_new_by_name("hmac", NULL); + purple_cipher_context_set_option(hmac, "hash", "sha1"); + purple_cipher_context_set_key_with_len(hmac, (guchar *)key2, 24); + purple_cipher_context_append(hmac, (guchar *)nexus->nonce, len); + purple_cipher_context_digest(hmac, 20, hash, NULL); + purple_cipher_context_destroy(hmac); + + /* We need to pad this to 72 bytes, apparently */ + nonce_fixed = g_malloc(len + 8); + memcpy(nonce_fixed, nexus->nonce, len); + memset(nonce_fixed + len, 0x08, 8); + cipher = des3_cbc(key3, usr_key->iv, nonce_fixed, len + 8, FALSE); + g_free(nonce_fixed); + + memcpy(usr_key->hash, hash, 20); + memcpy(usr_key->cipher, cipher, 72); + + g_free(key1); + g_free(key2); + g_free(key3); + g_free(cipher); + + response = purple_base64_encode((guchar *)usr_key, sizeof(MsnUsrKey)); + + g_free(usr_key); + + return response; } /************************************************************************** * Login **************************************************************************/ +/* Used to specify which token to update when only doing single updates */ +typedef struct _MsnNexusUpdateData MsnNexusUpdateData; +struct _MsnNexusUpdateData { + MsnNexus *nexus; + int id; + GSourceFunc cb; + gpointer data; +}; + +#if !GLIB_CHECK_VERSION(2, 12, 0) +static gboolean +nexus_remove_all_cb(gpointer key, gpointer val, gpointer data) +{ + return TRUE; +} +#endif + + +static gboolean +nexus_parse_token(MsnNexus *nexus, int id, xmlnode *node) +{ + char *token_str, *expiry_str; + const char *id_str; + char **elems, **cur, **tokens; + xmlnode *token = xmlnode_get_child(node, "RequestedSecurityToken/BinarySecurityToken"); + xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); + xmlnode *expires = xmlnode_get_child(node, "LifeTime/Expires"); + + if (!token) + return FALSE; + + /* Use the ID that the server sent us */ + if (id == -1) { + id_str = xmlnode_get_attrib(token, "Id"); + if (id_str == NULL) + return FALSE; + + id = atol(id_str + 7) - 1; /* 'Compact#' or 'PPToken#' */ + if (id >= nexus->token_len) + return FALSE; /* Where did this come from? */ + } + + token_str = xmlnode_get_data(token); + if (token_str == NULL) + return FALSE; + +#if GLIB_CHECK_VERSION(2, 12, 0) + g_hash_table_remove_all(nexus->tokens[id].token); +#else + g_hash_table_foreach_remove(nexus->tokens[id].token, + nexus_remove_all_cb, NULL); +#endif + + elems = g_strsplit(token_str, "&", 0); + + for (cur = elems; *cur != NULL; cur++) { + tokens = g_strsplit(*cur, "=", 2); + g_hash_table_insert(nexus->tokens[id].token, tokens[0], tokens[1]); + /* Don't free each of the tokens, only the array. */ + g_free(tokens); + } + g_strfreev(elems); + g_free(token_str); + + if (secret) + nexus->tokens[id].secret = xmlnode_get_data(secret); + else + nexus->tokens[id].secret = NULL; + + /* Yay for MS using ISO-8601 */ + expiry_str = xmlnode_get_data(expires); + nexus->tokens[id].expiry = purple_str_to_time(expiry_str, + FALSE, NULL, NULL, NULL); + g_free(expiry_str); + + purple_debug_info("msn", "Updated ticket for domain '%s', expires at %" G_GINT64_FORMAT ".\n", + ticket_domains[id][SSO_VALID_TICKET_DOMAIN], + (gint64)nexus->tokens[id].expiry); + return TRUE; +} + +static gboolean +nexus_parse_collection(MsnNexus *nexus, int id, xmlnode *collection) +{ + xmlnode *node; + gboolean result; + + node = xmlnode_get_child(collection, "RequestSecurityTokenResponse"); + + if (!node) + return FALSE; + + result = TRUE; + for (; node && result; node = node->next) { + xmlnode *endpoint = xmlnode_get_child(node, "AppliesTo/EndpointReference/Address"); + char *address = xmlnode_get_data(endpoint); + + if (g_str_equal(address, "http://Passport.NET/tb")) { + /* This node contains the stuff for updating tokens. */ + char *data; + xmlnode *cipher = xmlnode_get_child(node, "RequestedSecurityToken/EncryptedData/CipherData/CipherValue"); + xmlnode *secret = xmlnode_get_child(node, "RequestedProofToken/BinarySecret"); + + nexus->cipher = xmlnode_get_data(cipher); + data = xmlnode_get_data(secret); + nexus->secret = (char *)purple_base64_decode(data, NULL); + g_free(data); + + } else { + result = nexus_parse_token(nexus, id, node); + } + g_free(address); + } + + return result; +} + static void nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) { MsnNexus *nexus = data; MsnSession *session = nexus->session; - xmlnode *node; + const char *ticket; + char *response; if (resp == NULL) { msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect")); return; } - node = msn_soap_xml_get(resp->xml, "Body/" - "RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse"); - - for (; node; node = node->next) { - xmlnode *token = msn_soap_xml_get(node, - "RequestedSecurityToken/BinarySecurityToken"); - - if (token) { - char *token_str = xmlnode_get_data(token); - char **elems, **cur, **tokens; - char *msn_twn_t, *msn_twn_p, *cert_str; - - if (token_str == NULL) continue; - - elems = g_strsplit(token_str, "&", 0); - - for (cur = elems; *cur != NULL; cur++){ - tokens = g_strsplit(*cur, "=", 2); - g_hash_table_insert(nexus->challenge_data, tokens[0], tokens[1]); - /* Don't free each of the tokens, only the array. */ - g_free(tokens); - } - - g_free(token_str); - g_strfreev(elems); - - msn_twn_t = g_hash_table_lookup(nexus->challenge_data, "t"); - msn_twn_p = g_hash_table_lookup(nexus->challenge_data, "p"); - - /*setup the t and p parameter for session*/ - g_free(session->passport_info.t); - session->passport_info.t = g_strdup(msn_twn_t); - - g_free(session->passport_info.p); - session->passport_info.p = g_strdup(msn_twn_p); - - cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p); - msn_got_login_params(session, cert_str); - - purple_debug_info("MSN Nexus","Close nexus connection!\n"); - g_free(cert_str); - msn_nexus_destroy(nexus); - session->nexus = NULL; - - return; - } + if (!nexus_parse_collection(nexus, -1, + xmlnode_get_child(resp->xml, + "Body/RequestSecurityTokenResponseCollection"))) { + msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Invalid response")); + return; } - /* we must have failed! */ - msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication: cannot find authenticate token in server response")); + ticket = msn_nexus_get_token_str(nexus, MSN_AUTH_MESSENGER); + response = msn_rps_encrypt(nexus); + msn_got_login_params(session, ticket, response); + g_free(response); } /*when connect, do the SOAP Style windows Live ID authentication */ @@ -129,92 +378,258 @@ msn_nexus_connect(MsnNexus *nexus) { MsnSession *session = nexus->session; - char *ru,*lc,*id,*tw,*ct,*kpp,*kv,*ver,*rn,*tpf; - char *fs0,*fs; const char *username; char *password; - char *tail; -#ifdef NEXUS_LOGIN_TWN - char *challenge_str; -#else - char *rst1_str,*rst2_str,*rst3_str; -#endif + GString *domains; + char *request; + int i; MsnSoapMessage *soap; - purple_debug_info("MSN Nexus","Starting Windows Live ID authentication\n"); + purple_debug_info("msn", "Starting Windows Live ID authentication\n"); msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); - /*prepare the Windows Live ID authentication token*/ username = purple_account_get_username(session->account); - password = g_strndup(purple_connection_get_password(session->account->gc), 16); + password = g_markup_escape_text(purple_connection_get_password(session->account->gc), 16); + + purple_debug_info("msn", "Logging on %s, with policy '%s', nonce '%s'\n", + username, nexus->policy, nexus->nonce); + + domains = g_string_new(NULL); + for (i = 0; i < nexus->token_len; i++) { + g_string_append_printf(domains, MSN_SSO_RST_TEMPLATE, + i+1, + ticket_domains[i][SSO_VALID_TICKET_DOMAIN], + ticket_domains[i][SSO_VALID_TICKET_POLICY] != NULL ? + ticket_domains[i][SSO_VALID_TICKET_POLICY] : + nexus->policy); + } + + request = g_strdup_printf(MSN_SSO_TEMPLATE, username, password, domains->str); + g_free(password); + g_string_free(domains, TRUE); + + soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1)); + g_free(request); + msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, TRUE, + nexus_got_response_cb, nexus); +} - lc = (char *)g_hash_table_lookup(nexus->challenge_data, "lc"); - id = (char *)g_hash_table_lookup(nexus->challenge_data, "id"); - tw = (char *)g_hash_table_lookup(nexus->challenge_data, "tw"); - fs0= (char *)g_hash_table_lookup(nexus->challenge_data, "fs"); - ru = (char *)g_hash_table_lookup(nexus->challenge_data, "ru"); - ct = (char *)g_hash_table_lookup(nexus->challenge_data, "ct"); - kpp= (char *)g_hash_table_lookup(nexus->challenge_data, "kpp"); - kv = (char *)g_hash_table_lookup(nexus->challenge_data, "kv"); - ver= (char *)g_hash_table_lookup(nexus->challenge_data, "ver"); - rn = (char *)g_hash_table_lookup(nexus->challenge_data, "rn"); - tpf= (char *)g_hash_table_lookup(nexus->challenge_data, "tpf"); +static void +nexus_got_update_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data) +{ + MsnNexusUpdateData *ud = data; + MsnNexus *nexus = ud->nexus; + char iv[8] = {0,0,0,0,0,0,0,0}; + xmlnode *enckey; + char *tmp; + char *nonce; + gsize len; + char *key; - /* - * add some fail-safe code to avoid windows Purple Crash bug #1540454 - * If any of these string is NULL, will return Authentication Fail! - * for when windows g_strdup_printf() implementation get NULL point,It crashed! - */ - if(!(lc && id && tw && ru && ct && kpp && kv && ver && tpf)){ - purple_debug_error("MSN Nexus","WLM Authenticate Key Error!\n"); - msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication Failed")); - g_free(password); - msn_nexus_destroy(nexus); - session->nexus = NULL; +#if 0 + char *decrypted_pp; +#endif + char *decrypted_data; + + purple_debug_info("msn", "Got Update Response for %s.\n", ticket_domains[ud->id][SSO_VALID_TICKET_DOMAIN]); + + enckey = xmlnode_get_child(resp->xml, "Header/Security/DerivedKeyToken"); + while (enckey) { + if (g_str_equal(xmlnode_get_attrib(enckey, "Id"), "EncKey")) + break; + enckey = xmlnode_get_next_twin(enckey); + } + if (!enckey) { + purple_debug_error("msn", "Invalid response in token update.\n"); return; } - /* - * in old MSN NS server's "USR TWN S" return,didn't include fs string - * so we use a default "1" for fs. - */ - if(fs0){ - fs = g_strdup(fs0); - }else{ - fs = g_strdup("1"); + tmp = xmlnode_get_data(xmlnode_get_child(enckey, "Nonce")); + nonce = (char *)purple_base64_decode(tmp, &len); + key = rps_create_key(nexus->secret, 24, nonce, len); + g_free(tmp); + g_free(nonce); + +#if 0 + /* Don't know what this is for yet */ + tmp = xmlnode_get_data(xmlnode_get_child(resp->xml, + "Header/EncryptedPP/EncryptedData/CipherData/CipherValue")); + if (tmp) { + decrypted_pp = des3_cbc(key, iv, tmp, len, TRUE); + g_free(tmp); + purple_debug_info("msn", "Got Response Header EncryptedPP: %s\n", decrypted_pp); + g_free(decrypted_pp); + } +#endif + + tmp = xmlnode_get_data(xmlnode_get_child(resp->xml, + "Body/EncryptedData/CipherData/CipherValue")); + if (tmp) { + char *unescaped; + xmlnode *rstresponse; + + unescaped = (char *)purple_base64_decode(tmp, &len); + g_free(tmp); + + decrypted_data = des3_cbc(key, iv, unescaped, len, TRUE); + g_free(unescaped); + purple_debug_info("msn", "Got Response Body EncryptedData: %s\n", decrypted_data); + + rstresponse = xmlnode_from_str(decrypted_data, -1); + if (g_str_equal(rstresponse->name, "RequestSecurityTokenResponse")) + nexus_parse_token(nexus, ud->id, rstresponse); + else + nexus_parse_collection(nexus, ud->id, rstresponse); + g_free(decrypted_data); } -#ifdef NEXUS_LOGIN_TWN - challenge_str = g_strdup_printf( - "lc=%s&id=%s&tw=%s&fs=%s&ru=%s&ct=%s&kpp=%s&kv=%s&ver=%s&rn=%s&tpf=%s\r\n", - lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf - ); + if (ud->cb) + purple_timeout_add(0, ud->cb, ud->data); - /*build the SOAP windows Live ID XML body */ - tail = g_strdup_printf(TWN_ENVELOP_TEMPLATE, username, password, challenge_str); - g_free(challenge_str); -#else - rst1_str = g_strdup_printf( - "id=%s&tw=%s&fs=%s&kpp=%s&kv=%s&ver=%s&rn=%s", - id,tw,fs,kpp,kv,ver,rn - ); - rst2_str = g_strdup_printf( - "fs=%s&id=%s&kv=%s&rn=%s&tw=%s&ver=%s", - fs,id,kv,rn,tw,ver - ); - rst3_str = g_strdup_printf("id=%s",id); - tail = g_strdup_printf(TWN_LIVE_ENVELOP_TEMPLATE,username,password,rst1_str,rst2_str,rst3_str); - g_free(rst1_str); - g_free(rst2_str); - g_free(rst3_str); -#endif - g_free(fs); - g_free(password); - - soap = msn_soap_message_new(NULL, xmlnode_from_str(tail, -1)); - g_free(tail); - msn_soap_message_send(nexus->session, soap, MSN_TWN_SERVER, TWN_POST_URL, - nexus_got_response_cb, nexus); + g_free(ud); } +void +msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data) +{ + MsnSession *session = nexus->session; + MsnNexusUpdateData *ud; + PurpleCipherContext *sha1; + PurpleCipherContext *hmac; + + char *key; + + guchar digest[20]; + + struct tm *tm; + time_t now; + char *now_str; + char *timestamp; + char *timestamp_b64; + + char *domain; + char *domain_b64; + + char *signedinfo; + gint32 nonce[6]; + int i; + char *nonce_b64; + char *signature_b64; + guchar signature[20]; + + char *request; + MsnSoapMessage *soap; + + purple_debug_info("msn", + "Updating ticket for user '%s' on domain '%s'\n", + purple_account_get_username(session->account), + ticket_domains[id][SSO_VALID_TICKET_DOMAIN]); + + ud = g_new0(MsnNexusUpdateData, 1); + ud->nexus = nexus; + ud->id = id; + ud->cb = cb; + ud->data = data; + + sha1 = purple_cipher_context_new_by_name("sha1", NULL); + + domain = g_strdup_printf(MSN_SSO_RST_TEMPLATE, + id, + ticket_domains[id][SSO_VALID_TICKET_DOMAIN], + ticket_domains[id][SSO_VALID_TICKET_POLICY] != NULL ? + ticket_domains[id][SSO_VALID_TICKET_POLICY] : + nexus->policy); + purple_cipher_context_append(sha1, (guchar *)domain, strlen(domain)); + purple_cipher_context_digest(sha1, 20, digest, NULL); + domain_b64 = purple_base64_encode(digest, 20); + + now = time(NULL); + tm = gmtime(&now); + now_str = g_strdup(purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm)); + now += 5*60; + tm = gmtime(&now); + timestamp = g_strdup_printf(MSN_SSO_TIMESTAMP_TEMPLATE, + now_str, + purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm)); + purple_cipher_context_reset(sha1, NULL); + purple_cipher_context_append(sha1, (guchar *)timestamp, strlen(timestamp)); + purple_cipher_context_digest(sha1, 20, digest, NULL); + timestamp_b64 = purple_base64_encode(digest, 20); + g_free(now_str); + + purple_cipher_context_destroy(sha1); + + signedinfo = g_strdup_printf(MSN_SSO_SIGNEDINFO_TEMPLATE, + id, + domain_b64, + timestamp_b64); + + for (i = 0; i < 6; i++) + nonce[i] = rand(); + nonce_b64 = purple_base64_encode((guchar *)&nonce, sizeof(nonce)); + + key = rps_create_key(nexus->secret, 24, (char *)nonce, sizeof(nonce)); + hmac = purple_cipher_context_new_by_name("hmac", NULL); + purple_cipher_context_set_option(hmac, "hash", "sha1"); + purple_cipher_context_set_key_with_len(hmac, (guchar *)key, 24); + purple_cipher_context_append(hmac, (guchar *)signedinfo, strlen(signedinfo)); + purple_cipher_context_digest(hmac, 20, signature, NULL); + purple_cipher_context_destroy(hmac); + signature_b64 = purple_base64_encode(signature, 20); + + request = g_strdup_printf(MSN_SSO_TOKEN_UPDATE_TEMPLATE, + nexus->cipher, + nonce_b64, + timestamp, + signedinfo, + signature_b64, + domain); + + g_free(nonce_b64); + g_free(domain_b64); + g_free(timestamp_b64); + g_free(timestamp); + g_free(key); + g_free(signature_b64); + g_free(signedinfo); + g_free(domain); + + soap = msn_soap_message_new(NULL, xmlnode_from_str(request, -1)); + g_free(request); + msn_soap_message_send(session, soap, MSN_SSO_SERVER, SSO_POST_URL, TRUE, + nexus_got_update_cb, ud); +} + +GHashTable * +msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id) +{ + g_return_val_if_fail(nexus != NULL, NULL); + g_return_val_if_fail(id < nexus->token_len, NULL); + + return nexus->tokens[id].token; +} + +const char * +msn_nexus_get_token_str(MsnNexus *nexus, MsnAuthDomains id) +{ + static char buf[1024]; + GHashTable *token = msn_nexus_get_token(nexus, id); + const char *msn_t; + const char *msn_p; + gint ret; + + g_return_val_if_fail(token != NULL, NULL); + + msn_t = g_hash_table_lookup(token, "t"); + msn_p = g_hash_table_lookup(token, "p"); + + g_return_val_if_fail(msn_t != NULL, NULL); + g_return_val_if_fail(msn_p != NULL, NULL); + + ret = g_snprintf(buf, sizeof(buf) - 1, "t=%s&p=%s", msn_t, msn_p); + g_return_val_if_fail(ret != -1, NULL); + + return buf; +} +
--- a/libpurple/protocols/msn/nexus.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/nexus.h Thu Nov 20 21:13:56 2008 +0000 @@ -24,127 +24,210 @@ #ifndef _MSN_NEXUS_H_ #define _MSN_NEXUS_H_ -#include "soap.h" +/* Index into ticket_tokens in nexus.c Keep updated! */ +typedef enum +{ + MSN_AUTH_MESSENGER = 0, + MSN_AUTH_MESSENGER_WEB = 1, + MSN_AUTH_CONTACTS = 2, + MSN_AUTH_LIVE_SECURE = 3, + MSN_AUTH_SPACES = 4, + MSN_AUTH_LIVE_CONTACTS = 5, + MSN_AUTH_STORAGE = 6 +} MsnAuthDomains; -/*#define MSN_TWN_SERVER "loginnet.passport.com"*/ -#define MSN_TWN_SERVER "login.live.com" +#define MSN_SSO_SERVER "login.live.com" +#define SSO_POST_URL "/RST.srf" -#define TWN_START_TOKEN "<wsse:BinarySecurityToken Id=\"PPToken1\">" -#define TWN_END_TOKEN "</wsse:BinarySecurityToken>" +#define MSN_SSO_RST_TEMPLATE \ +"<wst:RequestSecurityToken xmlns=\"http://schemas.xmlsoap.org/ws/2004/04/trust\" Id=\"RST%d\">"\ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ + "<wsp:AppliesTo xmlns=\"http://schemas.xmlsoap.org/ws/2002/12/policy\">"\ + "<wsa:EndpointReference xmlns=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\">"\ + "<wsa:Address>%s</wsa:Address>"\ + "</wsa:EndpointReference>"\ + "</wsp:AppliesTo>"\ + "<wsse:PolicyReference xmlns=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" URI=\"%s\"></wsse:PolicyReference>"\ +"</wst:RequestSecurityToken>" -#define TWN_POST_URL "/RST.srf" -#define TWN_ENVELOP_TEMPLATE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\ - "<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\ - "<Header>"\ - "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\ - "<ps:HostingApp>{3:B}</ps:HostingApp>"\ - "<ps:BinaryVersion>4</ps:BinaryVersion>"\ - "<ps:UIVersion>1</ps:UIVersion>"\ - "<ps:Cookies></ps:Cookies>"\ - "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>"\ - "</ps:AuthInfo>"\ - "<wsse:Security>"\ - "<wsse:UsernameToken Id=\"user\">"\ - "<wsse:Username>%s</wsse:Username>"\ - "<wsse:Password>%s</wsse:Password>"\ - "</wsse:UsernameToken>"\ - "</wsse:Security>"\ - "</Header>"\ - "<Body>"\ - "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\ - "<wst:RequestSecurityToken Id=\"RST0\">"\ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ - "<wsp:AppliesTo>"\ - "<wsa:EndpointReference>"\ +#define MSN_SSO_TEMPLATE "<?xml version='1.0' encoding='utf-8'?>"\ +"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\""\ + " xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\""\ + " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\""\ + " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""\ + " xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\""\ + " xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\""\ + " xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\ + "<Header>"\ + "<ps:AuthInfo"\ + " xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\""\ + " Id=\"PPAuthInfo\">"\ + "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\ + "<ps:BinaryVersion>4</ps:BinaryVersion>"\ + "<ps:UIVersion>1</ps:UIVersion>"\ + "<ps:Cookies></ps:Cookies>"\ + "<ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>"\ + "</ps:AuthInfo>"\ + "<wsse:Security>"\ + "<wsse:UsernameToken Id=\"user\">"\ + "<wsse:Username>%s</wsse:Username>"\ + "<wsse:Password>%s</wsse:Password>"\ + "</wsse:UsernameToken>"\ + "</wsse:Security>"\ + "</Header>"\ + "<Body>"\ + "<ps:RequestMultipleSecurityTokens"\ + " xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\""\ + " Id=\"RSTS\">"\ + "<wst:RequestSecurityToken Id=\"RST0\">"\ + "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ + "<wsp:AppliesTo>"\ + "<wsa:EndpointReference>"\ "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\ - "</wsa:EndpointReference>"\ - "</wsp:AppliesTo>"\ - "</wst:RequestSecurityToken>"\ - "<wst:RequestSecurityToken Id=\"RST1\">"\ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ - "<wsp:AppliesTo>"\ - "<wsa:EndpointReference>"\ - "<wsa:Address>messenger.msn.com</wsa:Address>"\ - "</wsa:EndpointReference>"\ - "</wsp:AppliesTo>"\ - "<wsse:PolicyReference URI=\"?%s\">"\ - "</wsse:PolicyReference>"\ - "</wst:RequestSecurityToken>"\ - "</ps:RequestMultipleSecurityTokens>"\ - "</Body>"\ - "</Envelope>" + "</wsa:EndpointReference>"\ + "</wsp:AppliesTo>"\ + "</wst:RequestSecurityToken>"\ + "%s" /* Other RSTn tokens */\ + "</ps:RequestMultipleSecurityTokens>"\ + "</Body>"\ +"</Envelope>" + +#define MSN_SSO_AUTHINFO_TEMPLATE \ +"<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\ + "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\ + "<ps:BinaryVersion>4</ps:BinaryVersion>"\ + "<ps:UIVersion>1</ps:UIVersion>"\ + "<ps:Cookies></ps:Cookies>"\ + "<ps:RequestParams>AQAAAAIAAABsYwQAAAA0MTA1</ps:RequestParams>"\ +"</ps:AuthInfo>" +/* Not sure what's editable here, so I'll just hard-code the SHA1 hash */ +#define MSN_SSO_AUTHINFO_SHA1_BASE64 "d2IeTF4DAkPEa/tVETHznsivEpc=" + +#define MSN_SSO_TIMESTAMP_TEMPLATE \ +"<wsu:Timestamp xmlns=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" Id=\"Timestamp\">"\ + "<wsu:Created>%s</wsu:Created>"\ + "<wsu:Expires>%s</wsu:Expires>"\ +"</wsu:Timestamp>" -#define TWN_LIVE_START_TOKEN "<wsse:BinarySecurityToken Id=\"PPToken1\">" -#define TWN_LIVE_END_TOKEN "</wsse:BinarySecurityToken>" -#define TWN_LIVE_ENVELOP_TEMPLATE "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"\ -"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\ - "<Header>"\ - "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">"\ - "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>"\ - "<ps:BinaryVersion>4</ps:BinaryVersion>"\ - "<ps:UIVersion>1</ps:UIVersion>"\ - "<ps:Cookies></ps:Cookies>"\ - "<ps:RequestParams>AQAAAAIAAABsYwQAAAAyMDUy</ps:RequestParams>"\ - "</ps:AuthInfo>"\ - "<wsse:Security>"\ - "<wsse:UsernameToken Id=\"user\">"\ - "<wsse:Username>%s</wsse:Username>"\ - "<wsse:Password>%s</wsse:Password>"\ - "</wsse:UsernameToken>"\ - "</wsse:Security>"\ - "</Header>"\ - "<Body>"\ - "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">"\ - "<wst:RequestSecurityToken Id=\"RST0\">"\ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ - "<wsp:AppliesTo>"\ - "<wsa:EndpointReference>"\ - "<wsa:Address>http://Passport.NET/tb</wsa:Address>"\ - "</wsa:EndpointReference>"\ - "</wsp:AppliesTo>"\ - "</wst:RequestSecurityToken>"\ - "<wst:RequestSecurityToken Id=\"RST1\">"\ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ - "<wsp:AppliesTo>"\ - "<wsa:EndpointReference>"\ - "<wsa:Address>messenger.msn.com</wsa:Address>"\ - "</wsa:EndpointReference>"\ - "</wsp:AppliesTo>"\ - "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\ - "</wst:RequestSecurityToken>"\ - "<wst:RequestSecurityToken Id=\"RST2\">"\ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ - "<wsp:AppliesTo>"\ - "<wsa:EndpointReference>"\ - "<wsa:Address>contacts.msn.com</wsa:Address>"\ - "</wsa:EndpointReference>"\ - "</wsp:AppliesTo>"\ - "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\ - " </wst:RequestSecurityToken>"\ - "<wst:RequestSecurityToken Id=\"RST3\">"\ - "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>"\ - "<wsp:AppliesTo>"\ - "<wsa:EndpointReference>"\ - "<wsa:Address>voice.messenger.msn.com</wsa:Address>"\ - "</wsa:EndpointReference>"\ - " </wsp:AppliesTo>"\ - "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>"\ - "</wst:RequestSecurityToken>"\ - "</ps:RequestMultipleSecurityTokens>"\ - "</Body>"\ +#define MSN_SSO_SIGNEDINFO_TEMPLATE \ +"<SignedInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"\ + "<CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></CanonicalizationMethod>"\ + "<SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#hmac-sha1\"></SignatureMethod>"\ + "<Reference URI=\"#RST%d\">"\ + "<Transforms>"\ + "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\ + "</Transforms>"\ + "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\ + "<DigestValue>%s</DigestValue>"\ + "</Reference>"\ + "<Reference URI=\"#Timestamp\">"\ + "<Transforms>"\ + "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\ + "</Transforms>"\ + "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\ + "<DigestValue>%s</DigestValue>"\ + "</Reference>"\ + "<Reference URI=\"#PPAuthInfo\">"\ + "<Transforms>"\ + "<Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></Transform>"\ + "</Transforms>"\ + "<DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"></DigestMethod>"\ + "<DigestValue>" MSN_SSO_AUTHINFO_SHA1_BASE64 "</DigestValue>"\ + "</Reference>"\ +"</SignedInfo>" + +#define MSN_SSO_TOKEN_UPDATE_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<Envelope"\ + " xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\""\ + " xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\""\ + " xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\""\ + " xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\""\ + " xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""\ + " xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\""\ + " xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\""\ + " xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">"\ + "<Header>"\ + MSN_SSO_AUTHINFO_TEMPLATE /* ps:AuthInfo */ \ + "<wsse:Security>"\ + "<EncryptedData xmlns=\"http://www.w3.org/2001/04/xmlenc#\" Id=\"BinaryDAToken0\" Type=\"http://www.w3.org/2001/04/xmlenc#Element\">"\ + "<EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#tripledes-cbc\"></EncryptionMethod>"\ + "<ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">"\ + "<ds:KeyName>http://Passport.NET/STS</ds:KeyName>"\ + "</ds:KeyInfo>"\ + "<CipherData>"\ + "<CipherValue>%s</CipherValue>"\ + "</CipherData>"\ + "</EncryptedData>"\ + "<wssc:DerivedKeyToken Id=\"SignKey\">"\ + "<wsse:RequestedTokenReference>"\ + "<wsse:KeyIdentifier ValueType=\"http://docs.oasis-open.org/wss/2004/XX/oasis-2004XX-wss-saml-token-profile-1.0#SAMLAssertionID\" />"\ + "<wsse:Reference URI=\"#BinaryDAToken0\" />"\ + "</wsse:RequestedTokenReference>"\ + "<wssc:Nonce>%s</wssc:Nonce>"\ + "</wssc:DerivedKeyToken>"\ + "%s" /* wsu:Timestamp */\ + "<Signature xmlns=\"http://www.w3.org/2000/09/xmldsig#\">"\ + "%s" /* SignedInfo */\ + "<SignatureValue>%s</SignatureValue>"\ + "<KeyInfo>"\ + "<wsse:SecurityTokenReference>"\ + "<wsse:Reference URI=\"#SignKey\" />"\ + "</wsse:SecurityTokenReference>"\ + "</KeyInfo>"\ + "</Signature>"\ + "</wsse:Security>"\ + "</Header>"\ + "<Body>"\ + "%s" /* wst:RequestSecurityToken */ \ + "</Body>"\ "</Envelope>" +typedef struct _MsnUsrKey MsnUsrKey; +struct _MsnUsrKey +{ + int size; /* 28. Does not count data */ + int crypt_mode; /* CRYPT_MODE_CBC (1) */ + int cipher_type; /* TripleDES (0x6603) */ + int hash_type; /* SHA1 (0x8004) */ + int iv_len; /* 8 */ + int hash_len; /* 20 */ + int cipher_len; /* 72 */ + /* Data */ + char iv[8]; + char hash[20]; + char cipher[72]; +}; + +typedef struct _MsnTicketToken MsnTicketToken; +struct _MsnTicketToken { + GHashTable *token; + char *secret; + time_t expiry; +}; + typedef struct _MsnNexus MsnNexus; struct _MsnNexus { MsnSession *session; - char * challenge_data_str; - GHashTable *challenge_data; + + /* From server via USR command */ + char *policy; + char *nonce; + + /* From server via SOAP stuff */ + char *cipher; + char *secret; + MsnTicketToken *tokens; + int token_len; }; void msn_nexus_connect(MsnNexus *nexus); MsnNexus *msn_nexus_new(MsnSession *session); void msn_nexus_destroy(MsnNexus *nexus); +GHashTable *msn_nexus_get_token(MsnNexus *nexus, MsnAuthDomains id); +const char *msn_nexus_get_token_str(MsnNexus *nexus, MsnAuthDomains id); +void msn_nexus_update_token(MsnNexus *nexus, int id, GSourceFunc cb, gpointer data); +#endif /* _MSN_NEXUS_H_ */ -#endif /* _MSN_NEXUS_H_ */
--- a/libpurple/protocols/msn/notification.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/notification.c Thu Nov 20 21:13:56 2008 +0000 @@ -23,6 +23,7 @@ */ #include "msn.h" #include "notification.h" +#include "contact.h" #include "state.h" #include "error.h" #include "msnutils.h" @@ -104,7 +105,6 @@ vers = g_string_new(""); -/* for (i = session->protocol_ver; i >= WLM_MIN_PROTOCOL; i--) */ for (i = WLM_MAX_PROTOCOL; i >= WLM_MIN_PROTOCOL; i--) g_string_append_printf(vers, " MSNP%d", i); @@ -132,7 +132,7 @@ servconn = notification->servconn; msn_servconn_set_connect_cb(servconn, connect_cb); - notification->in_use = msn_servconn_connect(servconn, host, port); + notification->in_use = msn_servconn_connect(servconn, host, port, TRUE); return notification->in_use; } @@ -195,7 +195,7 @@ **************************************************************************/ void -msn_got_login_params(MsnSession *session, const char *login_params) +msn_got_login_params(MsnSession *session, const char *ticket, const char *response) { MsnCmdProc *cmdproc; @@ -203,7 +203,7 @@ msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END); - msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params); + msn_cmdproc_send(cmdproc, "USR", "SSO S %s %s", ticket, response); } static void @@ -212,8 +212,8 @@ PurpleAccount *account; account = cmdproc->session->account; - msn_cmdproc_send(cmdproc, "USR", "TWN I %s", - purple_account_get_username(account)); + + msn_cmdproc_send(cmdproc, "USR", "SSO I %s", purple_account_get_username(account)); } static void @@ -228,43 +228,16 @@ if (!g_ascii_strcasecmp(cmd->params[1], "OK")) { /* authenticate OK */ - /* friendly name part no longer true in msnp11 */ -#if 0 - const char *friendly = purple_url_decode(cmd->params[3]); - - purple_connection_set_display_name(gc, friendly); -#endif msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN); - -// msn_cmdproc_send(cmdproc, "SYN", "%s", "0"); - //TODO we should use SOAP contact to fetch contact list } - else if (!g_ascii_strcasecmp(cmd->params[1], "TWN")) + else if (!g_ascii_strcasecmp(cmd->params[1], "SSO")) { - /* Passport authentication */ - char **elems, **cur, **tokens; + /* RPS authentication */ session->nexus = msn_nexus_new(session); - /* Parse the challenge data. */ - session->nexus->challenge_data_str = g_strdup(cmd->params[3]); - elems = g_strsplit(cmd->params[3], ",", 0); - - for (cur = elems; *cur != NULL; cur++) - { - tokens = g_strsplit(*cur, "=", 2); - if(tokens[0] && tokens[1]) - { - purple_debug_info("MSNP14","challenge %p,key:%s,value:%s\n", - session->nexus->challenge_data,tokens[0],tokens[1]); - g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]); - /* Don't free each of the tokens, only the array. */ - g_free(tokens); - } else - g_strfreev(tokens); - } - - g_strfreev(elems); + session->nexus->policy = g_strdup(cmd->params[3]); + session->nexus->nonce = g_strdup(cmd->params[4]); msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START); @@ -327,14 +300,13 @@ } /* - * Windows Live Messenger 8.0 + * 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", -// "0x0409 winnt 5.1 i386 MSG80BETA 8.0.0689 msmsgs %s", - "0x0804 winnt 5.1 i386 MSNMSGR 8.0.0792 msmsgs %s", + "0x0409 winnt 5.1 i386 MSNMSGR 8.5.1288 msmsgs %s", purple_account_get_username(account)); } @@ -345,7 +317,9 @@ static void out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - if (!g_ascii_strcasecmp(cmd->params[0], "OTH")) + if (cmd->param_count == 0) + msn_session_set_error(cmdproc->session, -1, NULL); + else if (!g_ascii_strcasecmp(cmd->params[0], "OTH")) msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER, NULL); else if (!g_ascii_strcasecmp(cmd->params[0], "SSD")) @@ -377,7 +351,7 @@ msg = msn_message_new_from_cmd(cmdproc->session, cmd); - msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM); + msn_message_parse_payload(msg, payload, len, MSG_LINE_DEM, MSG_BODY_DEM); #ifdef MSN_DEBUG_NS msn_message_show_readable(msg, "Notification", TRUE); #endif @@ -390,23 +364,19 @@ static void msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_info("MSNP14","Processing MSG... \n"); - if(cmd->payload_len == 0){ - return; - } + purple_debug_info("msn", "Processing MSG... \n"); + /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued * command and we are processing it */ - if (cmd->payload == NULL) - { + if (cmd->payload == NULL) { cmdproc->last_cmd->payload_cb = msg_cmd_post; - cmdproc->servconn->payload_len = atoi(cmd->params[2]); - } - else - { + cmd->payload_len = atoi(cmd->params[2]); + + } else { g_return_if_fail(cmd->payload_cb != NULL); #if 0 /* glib on win32 doesn't correctly support precision modifiers for a string */ - purple_debug_info("MSNP14", "MSG payload:{%.*s}\n", cmd->payload_len, cmd->payload); + purple_debug_info("msn", "MSG payload:{%.*s}\n", cmd->payload_len, cmd->payload); #endif cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len); } @@ -425,7 +395,7 @@ cmdproc = session->notification->cmdproc; g_return_if_fail(msg != NULL); payload = msn_message_gen_payload(msg, &payload_len); - purple_debug_info("MSNP14", + purple_debug_info("msn", "send UUM, payload{%s}, strlen:%" G_GSIZE_FORMAT ", len:%" G_GSIZE_FORMAT "\n", payload, strlen(payload), payload_len); type = msg->type; @@ -435,6 +405,7 @@ msn_cmdproc_send_trans(cmdproc, trans); } +#if 0 static void ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) @@ -444,7 +415,7 @@ const char *passport; const char *content_type; - purple_debug_info("MSNP14","Process UBM payload:%.*s\n", (guint)len, payload); + purple_debug_info("msn", "Process UBM payload:%.*s\n", (guint)len, payload); msg = msn_message_new_from_cmd(cmdproc->session, cmd); msn_message_parse_payload(msg, payload, len,MSG_LINE_DEM,MSG_BODY_DEM); @@ -456,7 +427,7 @@ passport = msg->remote_user; content_type = msn_message_get_content_type(msg); - purple_debug_info("MSNP14", "type:%s\n", content_type); + purple_debug_info("msn", "type:%s\n", content_type); if(!strcmp(content_type,"text/plain")){ const char *value; const char *body; @@ -508,25 +479,24 @@ } msn_message_destroy(msg); } +#endif /*Yahoo msg process*/ static void ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_info("MSNP14","Processing UBM... \n"); - if(cmd->payload_len == 0){ - return; - } + purple_debug_info("msn", "Processing UBM... \n"); + /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued * command and we are processing it */ - if (cmd->payload == NULL){ - cmdproc->last_cmd->payload_cb = ubm_cmd_post; - cmdproc->servconn->payload_len = atoi(cmd->params[2]); - }else{ + if (cmd->payload == NULL) { + cmdproc->last_cmd->payload_cb = msg_cmd_post; + cmd->payload_len = atoi(cmd->params[4]); + } else { g_return_if_fail(cmd->payload_cb != NULL); - purple_debug_info("MSNP14", "UBM payload:{%.*s}\n", (guint)(cmd->payload_len), cmd->payload); - ubm_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len); + purple_debug_info("msn", "UBM payload:{%.*s}\n", (guint)(cmd->payload_len), cmd->payload); + msg_cmd_post(cmdproc, cmd, cmd->payload, cmd->payload_len); } } @@ -540,27 +510,8 @@ MsnTransaction *trans; char buf[33]; -#if 0 - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, (const guchar *)cmd->params[1], - strlen(cmd->params[1])); - challenge_resp = MSNP13_WLM_PRODUCT_KEY; - - purple_cipher_context_append(context, (const guchar *)challenge_resp, - strlen(challenge_resp)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(context); - - for (i = 0; i < 16; i++) - { - g_snprintf(buf + (i*2), 3, "%02x", digest[i]); - } -#else msn_handle_chl(cmd->params[1], buf); -#endif -// purple_debug_info("MSNP14","<<challenge:{%s}:{%s}\n",cmd->params[1],buf); - trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP13_WLM_PRODUCT_ID); + trans = msn_transaction_new(cmdproc, "QRY", "%s 32", MSNP15_WLM_PRODUCT_ID); msn_transaction_set_payload(trans, buf, 32); @@ -572,7 +523,7 @@ **************************************************************************/ /* add contact to xmlnode */ static void -msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnUserType type) +msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, MsnListOp list_op, MsnNetwork networkId) { xmlnode *d_node,*c_node; char **tokens; @@ -581,7 +532,7 @@ g_return_if_fail(passport != NULL); - purple_debug_info("MSNP14","Passport: %s, type: %d\n", passport, type); + purple_debug_info("msn", "Passport: %s, type: %d\n", passport, networkId); tokens = g_strsplit(passport, "@", 2); email = tokens[0]; domain = tokens[1]; @@ -615,16 +566,16 @@ c_node = xmlnode_new("c"); xmlnode_set_attrib(c_node, "n", email); - purple_debug_info("MSNP14", "list_op: %d\n", list_op); + purple_debug_info("msn", "list_op: %d\n", list_op); g_snprintf(fmt_str, sizeof(fmt_str), "%d", list_op); xmlnode_set_attrib(c_node, "l", fmt_str); - if (type != MSN_USER_TYPE_UNKNOWN) - g_snprintf(fmt_str, sizeof(fmt_str), "%d", type); + if (networkId != MSN_NETWORK_UNKNOWN) + g_snprintf(fmt_str, sizeof(fmt_str), "%d", networkId); else if (msn_user_is_yahoo(session->account, passport)) - g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_USER_TYPE_YAHOO); + g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_NETWORK_YAHOO); else - g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_USER_TYPE_PASSPORT); + g_snprintf(fmt_str, sizeof(fmt_str), "%d", MSN_NETWORK_PASSPORT); /*mobile*/ //type_str = g_strdup_printf("4"); @@ -639,7 +590,7 @@ msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len) { MsnTransaction *trans; - purple_debug_info("MSN Notification","Sending ADL with payload: %s\n", payload); + purple_debug_info("msn", "Sending ADL with payload: %s\n", payload); trans = msn_transaction_new(cmdproc, "ADL", "%i", payload_len); msn_transaction_set_payload(trans, payload, payload_len); msn_cmdproc_send_trans(cmdproc, trans); @@ -670,7 +621,7 @@ continue; msn_add_contact_xml(session, adl_node, user->passport, - user->list_op & MSN_LIST_OP_MASK, user->type); + user->list_op & MSN_LIST_OP_MASK, user->networkid); /* each ADL command may contain up to 150 contacts */ if (++adl_count % 150 == 0 || l->next == NULL) { @@ -743,14 +694,14 @@ { xmlnode *root, *domain_node; - purple_debug_misc("MSN Notification", "Parsing received ADL XML data\n"); + purple_debug_misc("msn", "Parsing received ADL XML data\n"); g_return_if_fail(payload != NULL); root = xmlnode_from_str(payload, (gssize) len); if (root == NULL) { - purple_debug_info("MSN Notification", "Invalid XML!\n"); + purple_debug_info("msn", "Invalid XML in ADL!\n"); return; } for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) { @@ -760,33 +711,18 @@ 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)) { -// gchar *name = NULL, *friendlyname = NULL, *passport= NULL; const gchar *list; gint list_op = 0; -// name = xmlnode_get_attrib(contact_node, "n"); list = xmlnode_get_attrib(contact_node, "l"); if (list != NULL) { list_op = atoi(list); } -// friendlyname = xmlnode_get_attrib(contact_node, "f"); - -// passport = g_strdup_printf("%s@%s", name, domain); - -// if (friendlyname != NULL) { -// decoded_friendlyname = g_strdup(purple_url_decode(friendlyname)); -// } else { -// decoded_friendlyname = g_strdup(passport); -// } if (list_op & MSN_LIST_RL_OP) { /* someone is adding us */ -// got_new_entry(cmdproc->session->account->gc, passport, decoded_friendly_name); - msn_get_contact_list(cmdproc->session->contact, MSN_PS_PENDING_LIST, NULL); + msn_get_contact_list(cmdproc->session, MSN_PS_PENDING_LIST, NULL); } - -// g_free(decoded_friendly_name); -// g_free(passport); } } @@ -805,11 +741,12 @@ session = cmdproc->session; - if ( !strcmp(cmd->params[1], "OK")) { + if (!strcmp(cmd->params[1], "OK")) { /* ADL ack */ msn_session_finish_login(session); } else { cmdproc->last_cmd->payload_cb = adl_cmd_parse; + cmd->payload_len = atoi(cmd->params[1]); } return; @@ -827,7 +764,7 @@ account = session->account; gc = purple_account_get_connection(account); - purple_debug_error("msn","ADL error\n"); + purple_debug_error("msn", "ADL error\n"); reason = g_strdup_printf(_("Unknown error (%d)"), error); purple_notify_error(gc, NULL, _("Unable to add user"), reason); g_free(reason); @@ -837,36 +774,34 @@ fqy_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) { - purple_debug_info("MSN Notification","FQY payload:\n%s\n", payload); + purple_debug_info("msn", "FQY payload:\n%s\n", payload); g_return_if_fail(cmdproc->session != NULL); - g_return_if_fail(cmdproc->session->contact != NULL); -// msn_notification_post_adl(cmdproc, payload, len); -// msn_get_address_book(cmdproc->session->contact, MSN_AB_SAVE_CONTACT, NULL, NULL); +/* msn_notification_post_adl(cmdproc, payload, len); */ +/* msn_get_address_book(cmdproc->session, MSN_AB_SAVE_CONTACT, NULL, NULL); */ } static void fqy_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_info("MSNP14","Process FQY\n"); + purple_debug_info("msn", "Process FQY\n"); cmdproc->last_cmd->payload_cb = fqy_cmd_post; + cmd->payload_len = atoi(cmd->params[1]); +} + +static void +rml_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + if (payload != NULL) + purple_debug_info("msn", "Received RML:\n%s\n", payload); } static void rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { -#if 0 - MsnTransaction *trans; - char * payload; -#endif - - purple_debug_info("MSNP14","Process RML\n"); -#if 0 - trans = msn_transaction_new(cmdproc, "RML",""); - - msn_transaction_set_payload(trans, payload, strlen(payload)); - - msn_cmdproc_send_trans(cmdproc, trans); -#endif + purple_debug_info("msn", "Process RML\n"); + cmd->payload_len = atoi(cmd->params[1]); + cmdproc->last_cmd->payload_cb = rml_cmd_post; } static void @@ -975,7 +910,7 @@ msn_userlist_move_buddy(userlist, data->who, data->old_group_name, group_name); g_free(data->old_group_name); } else { - // msn_add_contact_to_group(userlist, data, data->who, group_name); + /* msn_add_contact_to_group(userlist, data, data->who, group_name); */ } } } @@ -983,29 +918,7 @@ static void qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - MsnSession *session; - static int count = 0; - const char *passport; - PurpleAccount *account; - - session = cmdproc->session; - account = session->account; - - if (session->passport_info.file == NULL) - return; - - passport = purple_normalize(account, purple_account_get_username(account)); - - if ((strstr(passport, "@hotmail.") == NULL) && - (strstr(passport, "@live.com") == NULL) && - (strstr(passport, "@msn.com") == NULL)) - return; - - if (count++ < 26) - return; - - count = 0; - msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX"); + /* TODO: Call PNG after the timeout specified. */ } @@ -1017,7 +930,7 @@ /* Tell libpurple that the user has signed off */ user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]); - user->status = "offline"; + msn_user_set_state(user, NULL); msn_user_update(user); /* If we have an open MsnSlpLink with the user then close it */ @@ -1036,7 +949,7 @@ MsnUser *user; MsnObject *msnobj; unsigned long clientid; - int wlmclient; + int networkid; const char *state, *passport, *friendly; session = cmdproc->session; @@ -1046,7 +959,7 @@ state = cmd->params[1]; passport = cmd->params[2]; /*if a contact is actually on the WLM part or the yahoo part*/ - wlmclient = atoi(cmd->params[3]); + networkid = atoi(cmd->params[3]); friendly = purple_url_decode(cmd->params[4]); user = msn_userlist_find_user(session->userlist, passport); @@ -1055,7 +968,7 @@ msn_user_set_friendly_name(user, friendly); - if (session->protocol_ver >= 9 && cmd->param_count == 8) + if (cmd->param_count == 7) { msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[6])); msn_user_set_object(user, msnobj); @@ -1063,6 +976,8 @@ clientid = strtoul(cmd->params[5], NULL, 10); user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+'); + msn_user_set_clientid(user, clientid); + msn_user_set_network(user, networkid); msn_user_set_state(user, state); msn_user_update(user); @@ -1074,7 +989,8 @@ PurpleConnection *gc; MsnUserList *userlist; char *who = NULL, *text = NULL; - xmlnode *payloadNode, *from, *textNode; + const char *id = NULL; + xmlnode *payloadNode, *from, *msg, *textNode; purple_debug_misc("msn", "Incoming Page: {%s}\n", payload); @@ -1099,9 +1015,27 @@ </NOTIFICATION> */ + /* This is the payload if your message was too long: + <NOTIFICATION id="TrID" siteid="111100400" siteurl="http://mobile.msn.com/"> + <TO name="passport@example.com"> + <VIA agent="mobile"/> + </TO> + <FROM name="tel:+XXXXXXXXXXX"/> + <MSG pri="1" id="407"> + <CAT Id="110110001"/> + <ACTION url="2wayIM.asp"/> + <SUBSCR url="2wayIM.asp"/> + <BODY lcid="1033"> + <TEXT></TEXT> + </BODY> + </MSG> + </NOTIFICATION> + */ + if (!(payloadNode = xmlnode_from_str(payload, len)) || !(from = xmlnode_get_child(payloadNode, "FROM")) || - !(textNode = xmlnode_get_child(payloadNode, "MSG/BODY/TEXT"))) + !(msg = xmlnode_get_child(payloadNode, "MSG")) || + !(textNode = xmlnode_get_child(msg, "BODY/TEXT"))) return; who = g_strdup(xmlnode_get_attrib(from, "name")); @@ -1111,7 +1045,7 @@ /* Match number to user's mobile number, FROM is a phone number if the other side page you using your phone number */ - if(!strncmp(who, "tel:+", 5)) { + if (!strncmp(who, "tel:+", 5)) { MsnUser *user = msn_userlist_find_user_with_mobile_phone(userlist, who + 4); @@ -1121,7 +1055,20 @@ } } - serv_got_im(gc, who, text, 0, time(NULL)); + id = xmlnode_get_attrib(msg, "id"); + + if (id && !strcmp(id, "407")) { + /* TODO: Use this to NAK the transaction, maybe print the text, too. + unsigned int trId; + id = xmlnode_get_attrib(payloadNode, "id"); + trId = atol(id); + */ + purple_conv_present_error(who, gc->account, + _("Mobile message was not sent because it was too long.")); + + } else { + serv_got_im(gc, who, text, 0, time(NULL)); + } g_free(text); g_free(who); @@ -1131,7 +1078,7 @@ static void ipg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - cmdproc->servconn->payload_len = atoi(cmd->params[0]); + cmd->payload_len = atoi(cmd->params[0]); cmdproc->last_cmd->payload_cb = ipg_cmd_post; } @@ -1144,7 +1091,7 @@ MsnUser *user; MsnObject *msnobj; unsigned long clientid; - int wlmclient; + int networkid; const char *state, *passport, *friendly, *old_friendly; session = cmdproc->session; @@ -1153,7 +1100,7 @@ state = cmd->params[0]; passport = cmd->params[1]; - wlmclient = atoi(cmd->params[2]); + networkid = atoi(cmd->params[2]); friendly = purple_url_decode(cmd->params[3]); user = msn_userlist_find_user(session->userlist, passport); @@ -1165,22 +1112,22 @@ msn_user_set_friendly_name(user, friendly); } - if (session->protocol_ver >= 9) + if (cmd->param_count == 6) { - if (cmd->param_count == 7) - { - msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); - msn_user_set_object(user, msnobj); - } - else - { - msn_user_set_object(user, NULL); - } + msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); + msn_user_set_object(user, msnobj); + } + else + { + msn_user_set_object(user, NULL); } clientid = strtoul(cmd->params[4], NULL, 10); user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE) || (user->phone.mobile && user->phone.mobile[0] == '+'); + msn_user_set_clientid(user, clientid); + msn_user_set_network(user, networkid); + msn_user_set_state(user, state); msn_user_update(user); } @@ -1231,35 +1178,6 @@ } static void -rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - const char *friendly; - char *username; - - session = cmdproc->session; - account = session->account; - username = g_strdup(purple_normalize(account, - purple_account_get_username(account))); - - /* Only set display name if our *own* friendly name changed! */ - if (strcmp(username, purple_normalize(account, cmd->params[2]))) - { - g_free(username); - return; - } - - g_free(username); - - gc = account->gc; - friendly = purple_url_decode(cmd->params[3]); - - purple_connection_set_display_name(gc, friendly); -} - -static void prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session = cmdproc->session; @@ -1292,7 +1210,7 @@ if (!strcmp(type, "MFN")) { friendlyname = purple_url_decode(cmd->params[2]); - msn_update_contact(session->contact, friendlyname); + msn_update_contact(session, "Me", MSN_UPDATE_DISPLAY, friendlyname); purple_connection_set_display_name( purple_account_get_connection(session->account), @@ -1330,34 +1248,6 @@ g_strfreev(params); } -#if 0 -static void -rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - MsnUser *user; - const char *group_id, *list, *passport; - MsnListId list_id; - - session = cmdproc->session; - list = cmd->params[1]; - passport = cmd->params[3]; - user = msn_userlist_find_user(session->userlist, passport); - - g_return_if_fail(user != NULL); - - list_id = msn_get_list_id(list); - - if (cmd->param_count == 5) - group_id = cmd->params[4]; - else - group_id = NULL; - - msn_got_rem_user(session, user, list_id, group_id); - msn_user_update(user); -} -#endif - static void rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { @@ -1385,39 +1275,6 @@ g_strfreev(params); } -#if 0 -static void -syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - MsnSync *sync; - int total_users; - - session = cmdproc->session; - - if (cmd->param_count == 2) - { - /* - * This can happen if we sent a SYN with an up-to-date - * buddy list revision, but we send 0 to get a full list. - * So, error out. - */ - - msn_session_set_error(cmdproc->session, MSN_ERROR_BAD_BLIST, NULL); - return; - } - - total_users = atoi(cmd->params[2]); - - sync = msn_sync_new(session); - sync->total_users = total_users; - sync->old_cbs_table = cmdproc->cbs_table; - - session->sync = sync; - cmdproc->cbs_table = sync->cbs_table; -} -#endif - /************************************************************************** * Misc commands **************************************************************************/ @@ -1426,136 +1283,51 @@ url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { MsnSession *session; + PurpleConnection *gc; PurpleAccount *account; const char *rru; const char *url; - PurpleCipher *cipher; - PurpleCipherContext *context; - guchar digest[16]; - FILE *fd; + PurpleCipherContext *cipher; + gchar digest[33]; char *buf; - char buf2[3]; - char sendbuf[64]; - int i; + + gulong tmp_timestamp; session = cmdproc->session; account = session->account; + gc = account->gc; rru = cmd->params[1]; url = cmd->params[2]; + session->passport_info.mail_timestamp = time(NULL); + tmp_timestamp = session->passport_info.mail_timestamp - session->passport_info.sl; + buf = g_strdup_printf("%s%lu%s", session->passport_info.mspauth ? session->passport_info.mspauth : "BOGUS", - time(NULL) - session->passport_info.sl, - purple_connection_get_password(account->gc)); + tmp_timestamp, + purple_connection_get_password(gc)); - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - - purple_cipher_context_append(context, (const guchar *)buf, strlen(buf)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(context); + cipher = purple_cipher_context_new_by_name("md5", NULL); + purple_cipher_context_append(cipher, (const guchar *)buf, strlen(buf)); + purple_cipher_context_digest_to_str(cipher, sizeof(digest), digest, NULL); + purple_cipher_context_destroy(cipher); g_free(buf); - memset(sendbuf, 0, sizeof(sendbuf)); - - for (i = 0; i < 16; i++) - { - g_snprintf(buf2, sizeof(buf2), "%02x", digest[i]); - strcat(sendbuf, buf2); - } - - if (session->passport_info.file != NULL) - { - g_unlink(session->passport_info.file); - g_free(session->passport_info.file); - } - - if ((fd = purple_mkstemp(&session->passport_info.file, FALSE)) == NULL) - { - purple_debug_error("msn", - "Error opening temp passport file: %s\n", - g_strerror(errno)); - } - else - { -#ifdef _WIN32 - fputs("<!-- saved from url=(0013)about:internet -->\n", fd); -#endif - fputs("<html>\n" - "<head>\n" - "<noscript>\n" - "<meta http-equiv=\"Refresh\" content=\"0; " - "url=http://www.hotmail.com\">\n" - "</noscript>\n" - "</head>\n\n", - fd); + g_free(session->passport_info.mail_url); + session->passport_info.mail_url = g_strdup_printf("%s&auth=%s&creds=%s&sl=%ld&username=%s&mode=ttl&sid=%s&id=2&rru=%ssvc_mail&js=yes", + url, + session->passport_info.mspauth ? session->passport_info.mspauth : "BOGUS", + buf, + tmp_timestamp, + msn_user_get_passport(session->user), + session->passport_info.sid, + rru); - fprintf(fd, "<body onload=\"document.pform.submit(); \">\n"); - fprintf(fd, "<form name=\"pform\" action=\"%s\" method=\"POST\">\n\n", - url); - fprintf(fd, "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n"); - fprintf(fd, "<input type=\"hidden\" name=\"login\" value=\"%s\">\n", - purple_account_get_username(account)); - fprintf(fd, "<input type=\"hidden\" name=\"username\" value=\"%s\">\n", - purple_account_get_username(account)); - if (session->passport_info.sid != NULL) - fprintf(fd, "<input type=\"hidden\" name=\"sid\" value=\"%s\">\n", - session->passport_info.sid); - if (session->passport_info.kv != NULL) - fprintf(fd, "<input type=\"hidden\" name=\"kv\" value=\"%s\">\n", - session->passport_info.kv); - fprintf(fd, "<input type=\"hidden\" name=\"id\" value=\"2\">\n"); - fprintf(fd, "<input type=\"hidden\" name=\"sl\" value=\"%ld\">\n", - time(NULL) - session->passport_info.sl); - fprintf(fd, "<input type=\"hidden\" name=\"rru\" value=\"%s\">\n", - rru); - if (session->passport_info.mspauth != NULL) - fprintf(fd, "<input type=\"hidden\" name=\"auth\" value=\"%s\">\n", - session->passport_info.mspauth); - fprintf(fd, "<input type=\"hidden\" name=\"creds\" value=\"%s\">\n", - sendbuf); /* TODO Digest me (huh? -- ChipX86) */ - fprintf(fd, "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n"); - fprintf(fd, "<input type=\"hidden\" name=\"js\" value=\"yes\">\n"); - fprintf(fd, "</form></body>\n"); - fprintf(fd, "</html>\n"); - - if (fclose(fd)) - { - purple_debug_error("msn", - "Error closing temp passport file: %s\n", - g_strerror(errno)); - - g_unlink(session->passport_info.file); - g_free(session->passport_info.file); - session->passport_info.file = NULL; - } -#ifdef _WIN32 - else - { - /* - * Renaming file with .html extension, so that the - * win32 open_url will work. - */ - char *tmp; - - if ((tmp = - g_strdup_printf("%s.html", - session->passport_info.file)) != NULL) - { - if (g_rename(session->passport_info.file, - tmp) == 0) - { - g_free(session->passport_info.file); - session->passport_info.file = tmp; - } - else - g_free(tmp); - } - } -#endif - } + /* The user wants to check his or her email */ + if (cmd->trans && cmd->trans->data) + purple_notify_uri(purple_account_get_connection(account), session->passport_info.mail_url); } /************************************************************************** * Switchboards @@ -1629,42 +1401,62 @@ gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) { - xmlnode * root; - gchar * buf; - int xmllen; +/* QuLogic: Disabled until confirmed correct. */ +#if 0 + xmlnode *root; + xmlnode *policy; g_return_if_fail(cmd->payload != NULL); if ( (root = xmlnode_from_str(cmd->payload, cmd->payload_len)) == NULL) { - purple_debug_error("MSN","Unable to parse GCF payload into a XML tree"); + purple_debug_error("msn", "Unable to parse GCF payload into a XML tree"); return; } - buf = xmlnode_to_formatted_str(root, &xmllen); + + g_free(cmdproc->session->blocked_text); + cmdproc->session->blocked_text = NULL; + + /* We need a get_child with attrib... */ + policy = xmlnode_get_child(root, "Policy"); + while (policy) { + if (g_str_equal(xmlnode_get_attrib(policy, "type"), "SHIELDS")) + break; + policy = xmlnode_get_next_twin(policy); + } - /* get the payload content */ - purple_debug_info("MSNP14","GCF command payload:\n%.*s\n", xmllen, buf); + if (policy) { + GString *blocked = g_string_new(NULL); + xmlnode *imtext = xmlnode_get_child(policy, + "config/block/regexp/imtext"); + while (imtext) { + const char *value = xmlnode_get_attrib(imtext, "value"); + g_string_append_printf(blocked, "%s<br/>\n", + purple_base64_decode(value, NULL)); + imtext = xmlnode_get_next_twin(imtext); + } - g_free(buf); + cmdproc->session->blocked_text = g_string_free(blocked, FALSE); + } + xmlnode_free(root); +#endif } static void gcf_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_info("MSNP14","Processing GCF command\n"); + purple_debug_info("msn", "Processing GCF command\n"); + cmdproc->last_cmd->payload_cb = gcf_cmd_post; - return; + cmd->payload_len = atoi(cmd->params[1]); } static void sbs_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_info("MSNP14","Processing SBS... \n"); - if(cmd->payload_len == 0){ - return; - } + purple_debug_info("msn", "Processing SBS... \n"); /*get the payload content*/ } @@ -1710,17 +1502,26 @@ static void ubx_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_misc("MSNP14","UBX received.\n"); - if(cmd->payload_len == 0){ - return; - } + purple_debug_misc("msn", "UBX received.\n"); cmdproc->last_cmd->payload_cb = ubx_cmd_post; + cmd->payload_len = atoi(cmd->params[2]); +} + +static void +uux_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, + size_t len) +{ + /* Do Nothing, right now. */ + if (payload != NULL) + purple_debug_info("msn", "UUX payload:\n%s\n", payload); } static void uux_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_misc("MSNP14","UUX received.\n"); + purple_debug_misc("msn", "UUX received.\n"); + cmdproc->last_cmd->payload_cb = uux_cmd_post; + cmd->payload_len = atoi(cmd->params[1]); } /************************************************************************** @@ -1772,19 +1573,21 @@ if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL) session->passport_info.sl = atol(value); + if ((value = msn_message_get_attr(msg, "EmailEnabled")) != NULL) + session->passport_info.email_enabled = (gboolean)atol(value); + /*starting retrieve the contact list*/ clLastChange = purple_account_get_string(session->account, "CLLastChange", NULL); - session->contact = msn_contact_new(session); #ifdef MSN_PARTIAL_LISTS /* msn_userlist_load defeats all attempts at trying to detect blist sync issues */ msn_userlist_load(session); - msn_get_contact_list(session->contact, MSN_PS_INITIAL, clLastChange); + msn_get_contact_list(session, MSN_PS_INITIAL, clLastChange); #else /* always get the full list? */ - msn_get_contact_list(session->contact, MSN_PS_INITIAL, NULL); + msn_get_contact_list(session, MSN_PS_INITIAL, NULL); #endif #if 0 - msn_contact_connect(session->contact); + msn_contact_connect(session); #endif } @@ -1803,7 +1606,7 @@ /* This isn't an official message. */ return; - if (session->passport_info.file == NULL) + if (session->passport_info.mail_url == NULL) { MsnTransaction *trans; trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); @@ -1831,7 +1634,7 @@ const char *url; passport = msn_user_get_passport(session->user); - url = session->passport_info.file; + url = session->passport_info.mail_url; purple_notify_emails(gc, count, FALSE, NULL, NULL, &passport, &url, NULL, NULL); @@ -1857,10 +1660,6 @@ /* This isn't an official message. */ return; - /*new a oim session*/ -// session->oim = msn_oim_new(session); -// msn_oim_connect(session->oim); - table = msn_message_get_hashtable_from_body(msg); mdata = g_hash_table_lookup(table, "Mail-Data"); @@ -1874,7 +1673,7 @@ return; } - if (session->passport_info.file == NULL) + if (session->passport_info.mail_url == NULL) { MsnTransaction *trans; trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); @@ -1904,7 +1703,7 @@ const char *url; passport = msn_user_get_passport(session->user); - url = session->passport_info.file; + url = session->passport_info.mail_url; purple_notify_emails(gc, count, FALSE, NULL, NULL, &passport, &url, NULL, NULL); @@ -1918,7 +1717,7 @@ static void delete_oim_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { - purple_debug_misc("MSN Notification","Delete OIM message.\n"); + purple_debug_misc("msn", "Delete OIM message.\n"); } static void @@ -1936,7 +1735,7 @@ /* This isn't an official message. */ return; - if (session->passport_info.file == NULL) + if (session->passport_info.mail_url == NULL) { MsnTransaction *trans; trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); @@ -1966,7 +1765,7 @@ (subject != NULL ? subject : ""), (from != NULL ? from : ""), msn_user_get_passport(session->user), - session->passport_info.file, NULL, NULL); + session->passport_info.mail_url, NULL, NULL); g_free(from); g_free(subject); @@ -2037,7 +1836,7 @@ adl_node->child = NULL; msn_add_contact_xml(notification->session, adl_node, who, list_op, - MSN_USER_TYPE_PASSPORT); + MSN_NETWORK_PASSPORT); payload = xmlnode_to_str(adl_node,&payload_len); xmlnode_free(adl_node); @@ -2063,12 +1862,12 @@ rml_node = xmlnode_new("ml"); rml_node->child = NULL; - msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_USER_TYPE_PASSPORT); + msn_add_contact_xml(notification->session, rml_node, who, list_op, MSN_NETWORK_PASSPORT); payload = xmlnode_to_str(rml_node, &payload_len); xmlnode_free(rml_node); - purple_debug_info("MSN Notification","Send RML with payload:\n%s\n", payload); + purple_debug_info("msn", "Send RML with payload:\n%s\n", payload); trans = msn_transaction_new(cmdproc, "RML","%" G_GSIZE_FORMAT, strlen(payload)); msn_transaction_set_payload(trans, payload, strlen(payload)); msn_cmdproc_send_trans(cmdproc, trans); @@ -2081,25 +1880,19 @@ void msn_notification_init(void) { - /* TODO: check prp, blp */ - cbs_table = msn_table_new(); /* Synchronous */ msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL); msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd); msn_table_add_cmd(cbs_table, "ADL", "ILN", iln_cmd); -// msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd); /* Removed as of MSNP13 */ msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd); msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd); msn_table_add_cmd(cbs_table, "USR", "GCF", gcf_cmd); -// msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd); /* Removed as of MSNP13 */ msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd); msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd); - msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd); msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd); msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); -// msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL); msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd); msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd); msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd); @@ -2137,7 +1930,6 @@ msn_table_add_error(cbs_table, "ADL", adl_error); msn_table_add_error(cbs_table, "REG", reg_error); msn_table_add_error(cbs_table, "RMG", rmg_error); - /* msn_table_add_error(cbs_table, "REA", rea_error); */ msn_table_add_error(cbs_table, "USR", usr_error); msn_table_add_msg_type(cbs_table,
--- a/libpurple/protocols/msn/notification.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/notification.h Thu Nov 20 21:13:56 2008 +0000 @@ -25,6 +25,11 @@ #define _MSN_NOTIFICATION_H_ /*MSN protocol challenge info*/ + +/*MSNP15 challenge: WLM 8.5.1288.816*/ +#define MSNP15_WLM_PRODUCT_KEY "ILTXC!4IXB5FB*PX" +#define MSNP15_WLM_PRODUCT_ID "PROD0119GSJUC$18" + /*MSNP13 challenge*/ #define MSNP13_WLM_PRODUCT_KEY "O4BG@C7BWLYQX?5G" #define MSNP13_WLM_PRODUCT_ID "PROD01065C%ZFN6F" @@ -81,6 +86,6 @@ */ void msn_notification_close(MsnNotification *notification); -void msn_got_login_params(MsnSession *session, const char *login_params); +void msn_got_login_params(MsnSession *session, const char *ticket, const char *response); #endif /* _MSN_NOTIFICATION_H_ */
--- a/libpurple/protocols/msn/oim.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/oim.c Thu Nov 20 21:13:56 2008 +0000 @@ -24,7 +24,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "msn.h" -#include "soap2.h" +#include "soap.h" #include "oim.h" #include "msnutils.h" @@ -41,6 +41,7 @@ } MsnOimRecvData; /*Local Function Prototype*/ +static void msn_parse_oim_xml(MsnOim *oim, xmlnode *node); static void msn_oim_post_single_get_msg(MsnOim *oim, char *msgid); static MsnOimSendReq *msn_oim_new_send_req(const char *from_member, const char *friendname, @@ -72,7 +73,7 @@ { MsnOimSendReq *request; - purple_debug_info("OIM", "destroy the OIM %p\n", oim); + purple_debug_info("msn", "destroy the OIM %p\n", oim); g_free(oim->run_id); g_free(oim->challenge); @@ -114,22 +115,195 @@ } /**************************************** + * Manage OIM Tokens + ****************************************/ +typedef struct _MsnOimRequestData { + MsnOim *oim; + gboolean send; + const char *action; + const char *host; + const char *url; + xmlnode *body; + MsnSoapCallback cb; + gpointer cb_data; +} MsnOimRequestData; + +static void msn_oim_request_helper(MsnOimRequestData *data); + +static void +msn_oim_request_cb(MsnSoapMessage *request, MsnSoapMessage *response, + gpointer req_data) +{ + MsnOimRequestData *data = (MsnOimRequestData *)req_data; + xmlnode *fault = NULL; + xmlnode *faultcode = NULL; + + if (response == NULL) + return; + + fault = xmlnode_get_child(response->xml, "Body/Fault"); + if (fault) + faultcode = xmlnode_get_child(fault, "faultcode"); + + if (faultcode) { + gchar *faultcode_str = xmlnode_get_data(faultcode); + + if (faultcode_str && g_str_equal(faultcode_str, "q0:BadContextToken")) { + purple_debug_warning("msn", "OIM Request Error, Updating token now."); + msn_nexus_update_token(data->oim->session->nexus, + data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB, + (GSourceFunc)msn_oim_request_helper, data); + g_free(faultcode_str); + return; + + } else if (faultcode_str && g_str_equal(faultcode_str, "q0:AuthenticationFailed")) { + if (xmlnode_get_child(fault, "detail/RequiredAuthPolicy") != NULL) { + purple_debug_warning("msn", "OIM Request Error, Updating token now."); + msn_nexus_update_token(data->oim->session->nexus, + data->send ? MSN_AUTH_LIVE_SECURE : MSN_AUTH_MESSENGER_WEB, + (GSourceFunc)msn_oim_request_helper, data); + g_free(faultcode_str); + return; + } + } + g_free(faultcode_str); + } + + if (data->cb) + data->cb(request, response, data->cb_data); + xmlnode_free(data->body); + g_free(data); +} + +static void +msn_oim_request_helper(MsnOimRequestData *data) +{ + MsnSession *session = data->oim->session; + + if (data->send) { + /* The Sending of OIM's uses a different token for some reason. */ + xmlnode *ticket; + ticket = xmlnode_get_child(data->body, "Header/Ticket"); + xmlnode_set_attrib(ticket, "passport", + msn_nexus_get_token_str(session->nexus, MSN_AUTH_LIVE_SECURE)); + } + else + { + xmlnode *passport; + xmlnode *xml_t; + xmlnode *xml_p; + GHashTable *token; + const char *msn_t; + const char *msn_p; + + token = msn_nexus_get_token(session->nexus, MSN_AUTH_MESSENGER_WEB); + g_return_if_fail(token != NULL); + + msn_t = g_hash_table_lookup(token, "t"); + msn_p = g_hash_table_lookup(token, "p"); + + g_return_if_fail(msn_t != NULL); + g_return_if_fail(msn_p != NULL); + + passport = xmlnode_get_child(data->body, "Header/PassportCookie"); + xml_t = xmlnode_get_child(passport, "t"); + xml_p = xmlnode_get_child(passport, "p"); + + /* frees old token text, or the 'EMPTY' text if first time */ + xmlnode_free(xml_t->child); + xmlnode_free(xml_p->child); + + xmlnode_insert_data(xml_t, msn_t, -1); + xmlnode_insert_data(xml_p, msn_p, -1); + } + + msn_soap_message_send(session, + msn_soap_message_new(data->action, xmlnode_copy(data->body)), + data->host, data->url, FALSE, + msn_oim_request_cb, data); +} + + +static void +msn_oim_make_request(MsnOim *oim, gboolean send, const char *action, + const char *host, const char *url, xmlnode *body, MsnSoapCallback cb, + gpointer cb_data) +{ + MsnOimRequestData *data = g_new0(MsnOimRequestData, 1); + data->oim = oim; + data->send = send; + data->action = action; + data->host = host; + data->url = url; + data->body = body; + data->cb = cb; + data->cb_data = cb_data; + + msn_oim_request_helper(data); +} + +/**************************************** + * OIM GetMetadata request + * **************************************/ +static void +msn_oim_get_metadata_cb(MsnSoapMessage *request, MsnSoapMessage *response, + gpointer data) +{ + MsnOim *oim = data; + + if (response) { + msn_parse_oim_xml(oim, + xmlnode_get_child(response->xml, "Body/GetMetadataResponse/MD")); + } +} + +/* Post to get the OIM Metadata */ +static void +msn_oim_get_metadata(MsnOim *oim) +{ + msn_oim_make_request(oim, FALSE, MSN_OIM_GET_METADATA_ACTION, + MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL, + xmlnode_from_str(MSN_OIM_GET_METADATA_TEMPLATE, -1), + msn_oim_get_metadata_cb, oim); +} + +/**************************************** * OIM send SOAP request * **************************************/ /*encode the message to OIM Message Format*/ static gchar * msn_oim_msg_to_str(MsnOim *oim, const char *body) { - char *oim_body,*oim_base64; + GString *oim_body; + char *oim_base64; + char *c; + int len; + size_t base64_len; + + purple_debug_info("msn", "Encoding OIM Message...\n"); + len = strlen(body); + c = oim_base64 = purple_base64_encode((const guchar *)body, len); + base64_len = strlen(oim_base64); + purple_debug_info("msn", "Encoded base64 body:{%s}\n", oim_base64); - purple_debug_info("MSN OIM","encode OIM Message...\n"); - oim_base64 = purple_base64_encode((const guchar *)body, strlen(body)); - purple_debug_info("MSN OIM","encoded base64 body:{%s}\n",oim_base64); - oim_body = g_strdup_printf(MSN_OIM_MSG_TEMPLATE, - oim->run_id,oim->send_seq,oim_base64); + oim_body = g_string_new(NULL); + g_string_printf(oim_body, MSN_OIM_MSG_TEMPLATE, + oim->run_id, oim->send_seq); + +#define OIM_LINE_LEN 76 + while (base64_len > OIM_LINE_LEN) { + g_string_append_len(oim_body, c, OIM_LINE_LEN); + g_string_append_c(oim_body, '\n'); + c += OIM_LINE_LEN; + base64_len -= OIM_LINE_LEN; + } +#undef OIM_LINE_LEN + + g_string_append(oim_body, c); + g_free(oim_base64); - return oim_body; + return g_string_free(oim_body, FALSE); } /* @@ -146,13 +320,13 @@ g_return_if_fail(msg != NULL); if (response == NULL) { - purple_debug_info("MSNP14", "cannot send OIM: %s\n", msg->oim_msg); + purple_debug_info("msn", "cannot send OIM: %s\n", msg->oim_msg); } else { - xmlnode *faultNode = msn_soap_xml_get(response->xml, "Body/Fault"); + xmlnode *faultNode = xmlnode_get_child(response->xml, "Body/Fault"); if (faultNode == NULL) { /*Send OK! return*/ - purple_debug_info("MSNP14", "sent OIM: %s\n", msg->oim_msg); + purple_debug_info("msn", "sent OIM: %s\n", msg->oim_msg); } else { xmlnode *faultcode = xmlnode_get_child(faultNode, "faultcode"); @@ -160,7 +334,7 @@ char *faultcode_str = xmlnode_get_data(faultcode); if (g_str_equal(faultcode_str, "q0:AuthenticationFailed")) { - xmlnode *challengeNode = msn_soap_xml_get(faultNode, + xmlnode *challengeNode = xmlnode_get_child(faultNode, "detail/LockKeyChallenge"); if (challengeNode == NULL) { @@ -168,13 +342,13 @@ g_free(oim->challenge); oim->challenge = NULL; - purple_debug_info("msnoim","resending OIM: %s\n", + purple_debug_info("msn", "Resending OIM: %s\n", msg->oim_msg); g_queue_push_head(oim->send_queue, msg); msn_oim_send_msg(oim); } else { - purple_debug_info("msnoim", - "can't find lock key for OIM: %s\n", + purple_debug_info("msn", + "Can't find lock key for OIM: %s\n", msg->oim_msg); } } else { @@ -186,13 +360,39 @@ g_free(oim->challenge); oim->challenge = g_strndup(buf, sizeof(buf)); g_free(challenge); - purple_debug_info("MSNP14","lockkey:{%s}\n",oim->challenge); + purple_debug_info("msn", "Found lockkey:{%s}\n", oim->challenge); /*repost the send*/ - purple_debug_info("MSNP14","resending OIM: %s\n", msg->oim_msg); + purple_debug_info("msn", "Resending OIM: %s\n", msg->oim_msg); g_queue_push_head(oim->send_queue, msg); msn_oim_send_msg(oim); } + } else { + /* Report the error */ + const char *str_reason; + + if (g_str_equal(faultcode_str, "q0:SystemUnavailable")) { + str_reason = _("Message was not sent because the system is " + "unavailable. This normally happens when the " + "user is blocked or does not exist."); + + } else if (g_str_equal(faultcode_str, "q0:SenderThrottleLimitExceeded")) { + str_reason = _("Message was not sent because messages " + "are being sent too quickly."); + + } else if (g_str_equal(faultcode_str, "q0:InvalidContent")) { + str_reason = _("Message was not sent because an unknown " + "encoding error occurred."); + + } else { + str_reason = _("Message was not sent because an unknown " + "error occurred."); + } + + msn_session_report_user(oim->session, msg->to_member, + str_reason, PURPLE_MESSAGE_ERROR); + msn_session_report_user(oim->session, msg->to_member, + msg->oim_msg, PURPLE_MESSAGE_RAW); } g_free(faultcode_str); @@ -217,24 +417,20 @@ msn_oim_send_msg(MsnOim *oim) { MsnOimSendReq *oim_request; - char *soap_body,*mspauth; + char *soap_body; char *msg_body; g_return_if_fail(oim != NULL); oim_request = g_queue_peek_head(oim->send_queue); g_return_if_fail(oim_request != NULL); - purple_debug_info("MSNP14","sending OIM: %s\n", oim_request->oim_msg); - mspauth = g_strdup_printf("t=%s&p=%s", - oim->session->passport_info.t, - oim->session->passport_info.p - ); + purple_debug_info("msn", "Sending OIM: %s\n", oim_request->oim_msg); /* if we got the challenge lock key, we compute it * else we go for the SOAP fault and resend it. */ - if(oim->challenge == NULL){ - purple_debug_info("MSNP14","no lock key challenge,wait for SOAP Fault and Resend\n"); + if (oim->challenge == NULL){ + purple_debug_info("msn", "No lock key challenge, waiting for SOAP Fault and Resend\n"); } msg_body = msn_oim_msg_to_str(oim, oim_request->oim_msg); @@ -242,23 +438,20 @@ oim_request->from_member, oim_request->friendname, oim_request->to_member, - mspauth, - MSNP13_WLM_PRODUCT_ID, + MSNP15_WLM_PRODUCT_ID, oim->challenge ? oim->challenge : "", oim->send_seq, msg_body); - msn_soap_message_send(oim->session, - msn_soap_message_new(MSN_OIM_SEND_SOAP_ACTION, - xmlnode_from_str(soap_body, -1)), - MSN_OIM_SEND_HOST, MSN_OIM_SEND_URL, msn_oim_send_read_cb, oim); + msn_oim_make_request(oim, TRUE, MSN_OIM_SEND_SOAP_ACTION, MSN_OIM_SEND_HOST, + MSN_OIM_SEND_URL, xmlnode_from_str(soap_body, -1), msn_oim_send_read_cb, + oim); /*increase the offline Sequence control*/ if (oim->challenge != NULL) { oim->send_seq++; } - g_free(mspauth); g_free(msg_body); g_free(soap_body); } @@ -272,13 +465,13 @@ { MsnOimRecvData *rdata = data; - if (response && msn_soap_xml_get(response->xml, "Body/Fault") == NULL) { - purple_debug_info("msnoim", "delete OIM success\n"); + if (response && xmlnode_get_child(response->xml, "Body/Fault") == NULL) { + purple_debug_info("msn", "Delete OIM success\n"); rdata->oim->oim_list = g_list_remove(rdata->oim->oim_list, rdata->msg_id); g_free(rdata->msg_id); } else { - purple_debug_info("msnoim", "delete OIM failed\n"); + purple_debug_info("msn", "Delete OIM failed\n"); } g_free(rdata); @@ -292,16 +485,12 @@ char *msgid = rdata->msg_id; char *soap_body; - purple_debug_info("MSNP14","Delete single OIM Message {%s}\n",msgid); - - soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE, - oim->session->passport_info.t, oim->session->passport_info.p, msgid); + purple_debug_info("msn", "Delete single OIM Message {%s}\n",msgid); - msn_soap_message_send(oim->session, - msn_soap_message_new(MSN_OIM_DEL_SOAP_ACTION, - xmlnode_from_str(soap_body, -1)), - MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL, - msn_oim_delete_read_cb, rdata); + soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE, msgid); + + msn_oim_make_request(oim, FALSE, MSN_OIM_DEL_SOAP_ACTION, MSN_OIM_RETRIEVE_HOST, + MSN_OIM_RETRIEVE_URL, xmlnode_from_str(soap_body, -1), msn_oim_delete_read_cb, rdata); g_free(soap_body); } @@ -351,7 +540,7 @@ long sys_tzoff; #endif - if (!offset_positive) + if (offset_positive) tzoff *= -1; t.tm_year -= 1900; @@ -361,7 +550,7 @@ tzoff += sys_tzoff; #else #ifdef HAVE_TM_GMTOFF - tzoff -= t.tm_gmtoff; + tzoff += t.tm_gmtoff; #else # ifdef HAVE_TIMEZONE tzset(); /* making sure */ @@ -375,7 +564,7 @@ } } - purple_debug_info("MSNP14:OIM", "Can't parse timestamp %s\n", timestamp); + purple_debug_info("msn", "Can't parse timestamp %s\n", timestamp); return tval; } @@ -396,30 +585,30 @@ msn_message_parse_payload(message, msg_str, strlen(msg_str), MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM); - purple_debug_info("MSNP14","oim body:{%s}\n",message->body); + purple_debug_info("msn", "oim body:{%s}\n", message->body); decode_msg = (char *)purple_base64_decode(message->body,&body_len); date = (char *)g_hash_table_lookup(message->attr_table, "Date"); from = (char *)g_hash_table_lookup(message->attr_table, "From"); - if(strstr(from," ")){ + if (strstr(from," ")) { has_nick = 1; } - if(has_nick){ + if (has_nick) { tokens = g_strsplit(from , " " , 2); passport_str = g_strdup(tokens[1]); - purple_debug_info("MSNP14","oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n", - date,tokens[0],tokens[1],passport_str); + purple_debug_info("msn", "oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n", + date, tokens[0], tokens[1], passport_str); g_strfreev(tokens); - }else{ + } else { passport_str = g_strdup(from); - purple_debug_info("MSNP14","oim Date:{%s},passport{%s}\n", - date,passport_str); + purple_debug_info("msn", "oim Date:{%s},passport{%s}\n", + date, passport_str); } start = strstr(passport_str,"<"); start += 1; end = strstr(passport_str,">"); passport = g_strndup(start,end - start); g_free(passport_str); - purple_debug_info("MSN OIM","oim Date:{%s},passport{%s}\n",date,passport); + purple_debug_info("msn", "oim Date:{%s},passport{%s}\n", date, passport); stamp = msn_oim_parse_timestamp(date); @@ -445,7 +634,7 @@ MsnOimRecvData *rdata = data; if (response != NULL) { - xmlnode *msg_node = msn_soap_xml_get(response->xml, + xmlnode *msg_node = xmlnode_get_child(response->xml, "Body/GetMessageResponse/GetMessageResult"); if (msg_node) { @@ -454,11 +643,11 @@ g_free(msg_str); } else { char *str = xmlnode_to_str(response->xml, NULL); - purple_debug_info("msnoim", "Unknown response: %s\n", str); + purple_debug_info("msn", "Unknown OIM response: %s\n", str); g_free(str); } } else { - purple_debug_info("msnoim", "Failed to get OIM\n"); + purple_debug_info("msn", "Failed to get OIM\n"); } } @@ -468,26 +657,43 @@ void msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg) { - xmlnode *node, *mNode; + xmlnode *node; + + purple_debug_info("msn", "%s\n", xmlmsg); + + if (!strcmp(xmlmsg, "too-large")) { + /* Too many OIM's to send via NS, so we need to request them via SOAP. */ + msn_oim_get_metadata(oim); + } else { + node = xmlnode_from_str(xmlmsg, -1); + msn_parse_oim_xml(oim, node); + xmlnode_free(node); + } +} + +static void +msn_parse_oim_xml(MsnOim *oim, xmlnode *node) +{ + xmlnode *mNode; xmlnode *iu_node; MsnSession *session = oim->session; - purple_debug_info("MSNP14:OIM", "%s\n", xmlmsg); + g_return_if_fail(node != NULL); - node = xmlnode_from_str(xmlmsg, -1); if (strcmp(node->name, "MD") != 0) { - purple_debug_info("msnoim", "WTF is this? %s\n", xmlmsg); - xmlnode_free(node); + char *xmlmsg = xmlnode_to_str(node, NULL); + purple_debug_info("msn", "WTF is this? %s\n", xmlmsg); + g_free(xmlmsg); return; } - iu_node = msn_soap_xml_get(node, "E/IU"); + iu_node = xmlnode_get_child(node, "E/IU"); if (iu_node != NULL && purple_account_get_check_mail(session->account)) { char *unread = xmlnode_get_data(iu_node); const char *passport = msn_user_get_passport(session->user); - const char *url = session->passport_info.file; + const char *url = session->passport_info.mail_url; int count = atoi(unread); /* XXX/khc: pretty sure this is wrong */ @@ -515,7 +721,7 @@ if (rt_node != NULL) { rtime = xmlnode_get_data(rt_node); } -/* purple_debug_info("msnoim","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */ +/* purple_debug_info("msn", "E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime); */ if (!g_list_find_custom(oim->oim_list, msgid, (GCompareFunc)strcmp)) { oim->oim_list = g_list_append(oim->oim_list, msgid); @@ -528,8 +734,6 @@ g_free(rtime); g_free(nickname); } - - xmlnode_free(node); } /*Post to get the Offline Instant Message*/ @@ -539,19 +743,16 @@ char *soap_body; MsnOimRecvData *data = g_new0(MsnOimRecvData, 1); - purple_debug_info("MSNP14","Get single OIM Message\n"); + purple_debug_info("msn", "Get single OIM Message\n"); data->oim = oim; data->msg_id = msgid; - soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE, - oim->session->passport_info.t, oim->session->passport_info.p, msgid); + soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE, msgid); - msn_soap_message_send(oim->session, - msn_soap_message_new(MSN_OIM_GET_SOAP_ACTION, - xmlnode_from_str(soap_body, -1)), - MSN_OIM_RETRIEVE_HOST, MSN_OIM_RETRIEVE_URL, - msn_oim_get_read_cb, data); + msn_oim_make_request(oim, FALSE, MSN_OIM_GET_SOAP_ACTION, MSN_OIM_RETRIEVE_HOST, + MSN_OIM_RETRIEVE_URL, xmlnode_from_str(soap_body, -1), msn_oim_get_read_cb, + data); g_free(soap_body); }
--- a/libpurple/protocols/msn/oim.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/oim.h Thu Nov 20 21:13:56 2008 +0000 @@ -25,17 +25,41 @@ #ifndef _MSN_OIM_H_ #define _MSN_OIM_H_ -/*OIM Retrieve SOAP Template*/ +/* OIM Retrieval Info */ #define MSN_OIM_RETRIEVE_HOST "rsi.hotmail.com" #define MSN_OIM_RETRIEVE_URL "/rsi/rsi.asmx" + +/* OIM GetMetadata SOAP Template */ +#define MSN_OIM_GET_METADATA_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata" + +#define MSN_OIM_GET_METADATA_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ +"<soap:Envelope"\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ + "<soap:Header>"\ + "<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\ + "<t>EMPTY</t>"\ + "<p>EMPTY</p>"\ + "</PassportCookie>"\ + "</soap:Header>"\ + "<soap:Body>"\ + "<GetMetadata xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\" />"\ + "</soap:Body>"\ +"</soap:Envelope>" + +/*OIM GetMessage SOAP Template*/ #define MSN_OIM_GET_SOAP_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage" #define MSN_OIM_GET_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ +"<soap:Envelope"\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<soap:Header>"\ "<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\ - "<t>%s</t>"\ - "<p>%s</p>"\ + "<t>EMPTY</t>"\ + "<p>EMPTY</p>"\ "</PassportCookie>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -46,15 +70,18 @@ "</soap:Body>"\ "</soap:Envelope>" -/*OIM Delete SOAP Template*/ +/*OIM DeleteMessages SOAP Template*/ #define MSN_OIM_DEL_SOAP_ACTION "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages" #define MSN_OIM_DEL_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ +"<soap:Envelope"\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<soap:Header>"\ "<PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">"\ - "<t>%s</t>"\ - " <p>%s</p>"\ + "<t>EMPTY</t>"\ + "<p>EMPTY</p>"\ "</PassportCookie>"\ "</soap:Header>"\ "<soap:Body>"\ @@ -72,18 +99,27 @@ "Content-Transfer-Encoding: base64\n"\ "X-OIM-Message-Type: OfflineMessage\n"\ "X-OIM-Run-Id: {%s}\n"\ - "X-OIM-Sequence-Num: %d\n\n"\ - "%s" + "X-OIM-Sequence-Num: %d\n\n" #define MSN_OIM_SEND_HOST "ows.messenger.msn.com" #define MSN_OIM_SEND_URL "/OimWS/oim.asmx" -#define MSN_OIM_SEND_SOAP_ACTION "http://messenger.msn.com/ws/2004/09/oim/Store" +#define MSN_OIM_SEND_SOAP_ACTION "http://messenger.live.com/ws/2006/09/oim/Store2" #define MSN_OIM_SEND_TEMPLATE "<?xml version=\"1.0\" encoding=\"utf-8\"?>"\ -"<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ +"<soap:Envelope"\ + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""\ + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""\ + " xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"\ "<soap:Header>"\ - "<From memberName=\"%s\" friendlyName=\"%s\" xml:lang=\"en-US\" proxy=\"MSNMSGR\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\" msnpVer=\"MSNP14\" buildVer=\"8.0.0792\"/>"\ + "<From"\ + " memberName=\"%s\""\ + " friendlyName=\"%s\""\ + " xml:lang=\"en-US\""\ + " proxy=\"MSNMSGR\""\ + " xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\""\ + " msnpVer=\"MSNP15\""\ + " buildVer=\"8.5.1288\"/>"\ "<To memberName=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\ - "<Ticket passport=\"%s\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\ + "<Ticket passport=\"EMPTY\" appid=\"%s\" lockkey=\"%s\" xmlns=\"http://messenger.msn.com/ws/2004/09/oim/\"/>"\ "<Sequence xmlns=\"http://schemas.xmlsoap.org/ws/2003/03/rm\">"\ "<Identifier xmlns=\"http://schemas.xmlsoap.org/ws/2002/07/utility\">http://messenger.msn.com</Identifier>"\ "<MessageNumber>%d</MessageNumber>"\ @@ -101,10 +137,8 @@ { MsnSession *session; - MsnSoapConn *retrieveconn; GList * oim_list; - MsnSoapConn *sendconn; char *challenge; char *run_id; gint send_seq;
--- a/libpurple/protocols/msn/page.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/page.c Thu Nov 20 21:13:56 2008 +0000 @@ -53,9 +53,9 @@ g_return_val_if_fail(page != NULL, NULL); - str = - g_strdup_printf("<TEXT xml:space=\"preserve\" enc=\"utf-8\">%s</TEXT>", - msn_page_get_body(page)); + str = g_markup_printf_escaped( + "<TEXT xml:space=\"preserve\" enc=\"utf-8\">%s</TEXT>", + msn_page_get_body(page)); if (ret_size != NULL) *ret_size = strlen(str);
--- a/libpurple/protocols/msn/servconn.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/servconn.c Thu Nov 20 21:13:56 2008 +0000 @@ -203,7 +203,7 @@ } gboolean -msn_servconn_connect(MsnServConn *servconn, const char *host, int port) +msn_servconn_connect(MsnServConn *servconn, const char *host, int port, gboolean force) { MsnSession *session; @@ -223,7 +223,7 @@ { /* HTTP Connection. */ - if (!servconn->httpconn->connected) + if (!servconn->httpconn->connected || force) if (!msn_httpconn_connect(servconn->httpconn, host, port)) return FALSE; @@ -255,6 +255,12 @@ { g_return_if_fail(servconn != NULL); + if (servconn->connect_data != NULL) + { + purple_proxy_connect_cancel(servconn->connect_data); + servconn->connect_data = NULL; + } + if (!servconn->connected) { /* We could not connect. */ @@ -273,12 +279,6 @@ return; } - if (servconn->connect_data != NULL) - { - purple_proxy_connect_cancel(servconn->connect_data); - servconn->connect_data = NULL; - } - if (servconn->inpa > 0) { purple_input_remove(servconn->inpa); @@ -391,23 +391,19 @@ session = servconn->session; len = read(servconn->fd, buf, sizeof(buf) - 1); - servconn->session->account->gc->last_received = time(NULL); + if (servconn->type == MSN_SERVCONN_NS) + servconn->session->account->gc->last_received = time(NULL); - if (len <= 0) { - switch (errno) { - - case 0: + if (len < 0 && errno == EAGAIN) { + return; - case EBADF: - case EAGAIN: return; + } else if (len <= 0) { + purple_debug_error("msn", "servconn read error," + "len: %d, errno: %d, error: %s\n", + len, errno, g_strerror(errno)); + msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ); - default: purple_debug_error("msn", "servconn read error," - "len: %d, errno: %d, error: %s\n", - len, errno, g_strerror(errno)); - msn_servconn_got_error(servconn, - MSN_SERVCONN_ERROR_READ); - return; - } + return; } buf[len] = '\0';
--- a/libpurple/protocols/msn/servconn.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/servconn.h Thu Nov 20 21:13:56 2008 +0000 @@ -115,8 +115,10 @@ * @param servconn The connection. * @param host The host. * @param port The port. + * @param force Force this servconn to connect to a new server. */ -gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port); +gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port, + gboolean force); /** * Disconnects.
--- a/libpurple/protocols/msn/session.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/session.c Thu Nov 20 21:13:56 2008 +0000 @@ -45,8 +45,6 @@ purple_account_get_username(account), NULL); session->oim = msn_oim_new(session); - /*if you want to chat with Yahoo Messenger*/ - //session->protocol_ver = WLM_YAHOO_PROT_VER; session->protocol_ver = WLM_PROT_VER; return session; @@ -74,18 +72,15 @@ msn_userlist_destroy(session->userlist); g_free(session->psm); - g_free(session->passport_info.t); - g_free(session->passport_info.p); + + g_free(session->blocked_text); + g_free(session->passport_info.kv); g_free(session->passport_info.sid); g_free(session->passport_info.mspauth); g_free(session->passport_info.client_ip); - if (session->passport_info.file != NULL) - { - g_unlink(session->passport_info.file); - g_free(session->passport_info.file); - } + g_free(session->passport_info.mail_url); if (session->sync != NULL) msn_sync_destroy(session->sync); @@ -93,15 +88,13 @@ if (session->nexus != NULL) msn_nexus_destroy(session->nexus); - if (session->contact != NULL) - msn_contact_destroy(session->contact); if (session->oim != NULL) msn_oim_destroy(session->oim); if (session->user != NULL) msn_user_destroy(session->user); - if (session->soap_table) + if (session->soap_table != NULL) g_hash_table_destroy(session->soap_table); if (session->soap_cleanup_handle) @@ -195,7 +188,7 @@ * passport - the one want to talk to you */ void -msn_session_report_user(MsnSession *session,const char *passport,char *msg,PurpleMessageFlags flags) +msn_session_report_user(MsnSession *session,const char *passport,const char *msg,PurpleMessageFlags flags) { PurpleConversation * conv; @@ -457,7 +450,6 @@ PurpleAccount *account; PurpleConnection *gc; PurpleStoredImage *img; - const char *passport; if (session->logged_in) return; @@ -477,17 +469,5 @@ /* Sync users */ msn_session_sync_users(session); - /* It seems that some accounts that haven't accessed hotmail for a while - * and @msn.com accounts don't automatically get the initial email - * notification so we always request it on login - */ - - passport = purple_normalize(account, purple_account_get_username(account)); - - if ((strstr(passport, "@hotmail.") != NULL) || - (strstr(passport, "@msn.com") != NULL)) - { - msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX"); - } }
--- a/libpurple/protocols/msn/session.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/session.h Thu Nov 20 21:13:56 2008 +0000 @@ -38,7 +38,6 @@ #include "cmdproc.h" #include "nexus.h" #include "httpconn.h" -#include "contact.h" #include "oim.h" #include "userlist.h" @@ -96,7 +95,6 @@ MsnNotification *notification; MsnNexus *nexus; - MsnContact *contact; MsnOim *oim; MsnSync *sync; @@ -109,19 +107,19 @@ /*psm info*/ char *psm; + char *blocked_text; + struct { - /*t and p, get via USR TWN*/ - char *t; - char *p; - char *kv; char *sid; char *mspauth; unsigned long sl; - char *file; char *client_ip; int client_port; + char *mail_url; + gulong mail_timestamp; + gboolean email_enabled; } passport_info; GHashTable *soap_table; @@ -236,6 +234,6 @@ /*post message to User*/ void msn_session_report_user(MsnSession *session,const char *passport, - char *msg,PurpleMessageFlags flags); + const char *msg,PurpleMessageFlags flags); #endif /* _MSN_SESSION_H_ */
--- a/libpurple/protocols/msn/slp.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/slp.c Thu Nov 20 21:13:56 2008 +0000 @@ -25,7 +25,6 @@ #include "slp.h" #include "slpcall.h" #include "slpmsg.h" -#include "slpsession.h" #include "object.h" #include "user.h" @@ -44,7 +43,7 @@ static void send_decline(MsnSlpCall *slpcall, const char *branch, const char *type, const char *content); -void msn_request_user_display(MsnUser *user); +static void request_user_display(MsnUser *user); /************************************************************************** * Util @@ -251,12 +250,11 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch, const char *euf_guid, const char *context) { - if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6")) + if (!strcmp(euf_guid, MSN_OBJ_GUID)) { /* Emoticon or UserDisplay */ char *content; gsize len; - MsnSlpSession *slpsession; MsnSlpLink *slplink; MsnSlpMessage *slpmsg; MsnObject *obj; @@ -306,14 +304,10 @@ g_return_if_reached(); } - slpsession = msn_slplink_find_slp_session(slplink, - slpcall->session_id); - /* DATA PREP */ slpmsg = msn_slpmsg_new(slplink); slpmsg->slpcall = slpcall; - slpmsg->slpsession = slpsession; - slpmsg->session_id = slpsession->id; + slpmsg->session_id = slpcall->session_id; msn_slpmsg_set_body(slpmsg, NULL, 4); #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP DATA PREP"; @@ -323,7 +317,6 @@ /* DATA */ slpmsg = msn_slpmsg_new(slplink); slpmsg->slpcall = slpcall; - slpmsg->slpsession = slpsession; slpmsg->flags = 0x20; #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP DATA"; @@ -332,7 +325,7 @@ msn_slplink_queue_slpmsg(slplink, slpmsg); purple_imgstore_unref(img); } - else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683")) + else if (!strcmp(euf_guid, MSN_FT_GUID)) { /* File Transfer */ PurpleAccount *account; @@ -384,7 +377,8 @@ purple_xfer_request(xfer); } - } + } else + purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid); } void @@ -781,16 +775,13 @@ got_emoticon(MsnSlpCall *slpcall, const guchar *data, gsize size) { - PurpleConversation *conv; - PurpleConnection *gc; - const char *who; + MsnSwitchBoard *swboard; - gc = slpcall->slplink->session->account->gc; - who = slpcall->slplink->remote_user; + swboard = slpcall->slplink->swboard; + conv = swboard->conv; - if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) { - + 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 @@ -808,6 +799,7 @@ { MsnSession *session; MsnSlpLink *slplink; + MsnSwitchBoard *swboard; MsnObject *obj; char **tokens; char *smile, *body_str; @@ -847,8 +839,9 @@ slplink = msn_session_get_slplink(session, who); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, - session->account); + swboard = cmdproc->data; + slplink->swboard = swboard; + conv = swboard->conv; /* 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 @@ -930,7 +923,7 @@ username = user->passport; userlist->buddy_icon_window--; - msn_request_user_display(user); + request_user_display(user); #ifdef MSN_DEBUG_UD purple_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n", @@ -1066,8 +1059,8 @@ msn_release_buddy_icon_request_timeout, userlist); } -void -msn_request_user_display(MsnUser *user) +static void +request_user_display(MsnUser *user) { PurpleAccount *account; MsnSession *session; @@ -1115,7 +1108,7 @@ session->userlist->buddy_icon_window++; #ifdef MSN_DEBUG_UD - purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n", + purple_debug_info("msn", "request_user_display(): buddy_icon_window++ yields =%d\n", session->userlist->buddy_icon_window); #endif
--- a/libpurple/protocols/msn/slpcall.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/slpcall.c Thu Nov 20 21:13:56 2008 +0000 @@ -24,7 +24,6 @@ #include "msn.h" #include "msnutils.h" #include "slpcall.h" -#include "slpsession.h" #include "slp.h" @@ -115,12 +114,8 @@ void msn_slp_call_session_init(MsnSlpCall *slpcall) { - MsnSlpSession *slpsession; - - slpsession = msn_slp_session_new(slpcall); - if (slpcall->session_init_cb) - slpcall->session_init_cb(slpsession); + slpcall->session_init_cb(slpcall); slpcall->started = TRUE; }
--- a/libpurple/protocols/msn/slpcall.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/slpcall.h Thu Nov 20 21:13:56 2008 +0000 @@ -29,7 +29,6 @@ typedef struct _MsnSlpCall MsnSlpCall; #include "slplink.h" -#include "slpsession.h" /* The official client seems to timeout slp calls after 5 minutes */ #define MSN_SLPCALL_TIMEOUT 300000 @@ -66,7 +65,7 @@ void (*progress_cb)(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset); - void (*session_init_cb)(MsnSlpSession *slpsession); + void (*session_init_cb)(MsnSlpCall *slpcall); /* Can be checksum, or smile */ char *data_info;
--- a/libpurple/protocols/msn/slplink.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/slplink.c Thu Nov 20 21:13:56 2008 +0000 @@ -154,23 +154,6 @@ return slplink; } -MsnSlpSession * -msn_slplink_find_slp_session(MsnSlpLink *slplink, long session_id) -{ - GList *l; - MsnSlpSession *slpsession; - - for (l = slplink->slp_sessions; l != NULL; l = l->next) - { - slpsession = l->data; - - if (slpsession->id == session_id) - return slpsession; - } - - return NULL; -} - void msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall) { @@ -394,12 +377,12 @@ } else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) { - MsnSlpSession *slpsession; - slpsession = slpmsg->slpsession; + MsnSlpCall *slpcall; + slpcall = slpmsg->slpcall; - g_return_if_fail(slpsession != NULL); - msg->msnslp_header.session_id = slpsession->id; - msg->msnslp_footer.value = slpsession->app_id; + 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) @@ -476,18 +459,15 @@ } static void -send_file_cb(MsnSlpSession *slpsession) +send_file_cb(MsnSlpCall *slpcall) { - MsnSlpCall *slpcall; MsnSlpMessage *slpmsg; struct stat st; PurpleXfer *xfer; - slpcall = slpsession->slpcall; slpmsg = msn_slpmsg_new(slpcall->slplink); slpmsg->slpcall = slpcall; slpmsg->flags = 0x1000030; - slpmsg->slpsession = slpsession; #ifdef MSN_DEBUG_SLP slpmsg->info = "SLP FILE"; #endif @@ -593,7 +573,7 @@ } else if (slpmsg->size) { - if ((offset + len) > slpmsg->size) + if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size) { purple_debug_error("msn", "Oversized slpmsg - msgsize=%lld offset=%" G_GSIZE_FORMAT " len=%" G_GSIZE_FORMAT "\n", @@ -774,8 +754,7 @@ context = gen_context(fn, fp); - msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2, - context); + msn_slp_call_invite(slpcall, MSN_FT_GUID, 2, context); g_free(context); } @@ -805,8 +784,7 @@ slpcall->cb = cb; slpcall->end_cb = end_cb; - msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1, - msnobj_base64); + msn_slp_call_invite(slpcall, MSN_OBJ_GUID, 1, msnobj_base64); g_free(msnobj_base64); }
--- a/libpurple/protocols/msn/slplink.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/slplink.h Thu Nov 20 21:13:56 2008 +0000 @@ -53,7 +53,6 @@ MsnDirectConn *directconn; GList *slp_calls; - GList *slp_sessions; GList *slp_msgs; GQueue *slp_msg_queue; @@ -74,8 +73,6 @@ */ MsnSlpLink *msn_session_get_slplink(MsnSession *session, const char *username); -MsnSlpSession *msn_slplink_find_slp_session(MsnSlpLink *slplink, - long session_id); void msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall); void msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall); MsnSlpCall *msn_slplink_find_slp_call(MsnSlpLink *slplink,
--- a/libpurple/protocols/msn/slpmsg.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/slpmsg.h Thu Nov 20 21:13:56 2008 +0000 @@ -28,7 +28,6 @@ #include "imgstore.h" -#include "slpsession.h" #include "slpcall.h" #include "slplink.h" #include "session.h" @@ -42,7 +41,6 @@ */ struct _MsnSlpMessage { - MsnSlpSession *slpsession; MsnSlpCall *slpcall; /**< The slpcall to which this slp message belongs (if applicable). */ MsnSlpLink *slplink; /**< The slplink through which this slp message is being sent. */ MsnSession *session;
--- a/libpurple/protocols/msn/slpsession.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/** - * @file slpsession.h SLP Session 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 "slpsession.h" - -/************************************************************************** - * SLP Session - **************************************************************************/ - -MsnSlpSession * -msn_slp_session_new(MsnSlpCall *slpcall) -{ - MsnSlpSession *slpsession; - - g_return_val_if_fail(slpcall != NULL, NULL); - - slpsession = g_new0(MsnSlpSession, 1); - - slpsession->slpcall = slpcall; - slpsession->id = slpcall->session_id; - slpsession->call_id = slpcall->id; - slpsession->app_id = slpcall->app_id; - - slpcall->slplink->slp_sessions = - g_list_append(slpcall->slplink->slp_sessions, slpsession); - - return slpsession; -} - -void -msn_slp_session_destroy(MsnSlpSession *slpsession) -{ - g_return_if_fail(slpsession != NULL); - - if (slpsession->call_id != NULL) - g_free(slpsession->call_id); - - slpsession->slpcall->slplink->slp_sessions = - g_list_remove(slpsession->slpcall->slplink->slp_sessions, slpsession); - - g_free(slpsession); -} - -#if 0 -static void -msn_slp_session_send_slpmsg(MsnSlpSession *slpsession, MsnSlpMessage *slpmsg) -{ - slpmsg->slpsession = slpsession; - -#if 0 - slpmsg->session_id = slpsession->id; - slpmsg->app_id = slpsession->app_id; -#endif - - msn_slplink_send_slpmsg(slpsession->slpcall->slplink, slpmsg); -} -#endif
--- a/libpurple/protocols/msn/slpsession.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/** - * @file slpsession.h SLP Session 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_SLPSESSION_H_ -#define _MSN_SLPSESSION_H_ - -typedef struct _MsnSlpSession MsnSlpSession; - -#include "slpcall.h" -#include "slpsession.h" -#include "slpmsg.h" - -struct _MsnSlpSession -{ - /* MsnSlpLink *slplink; */ - MsnSlpCall *slpcall; - - long id; - - long app_id; - char *call_id; -}; - -MsnSlpSession *msn_slp_session_new(MsnSlpCall *slpcall); -void msn_slp_session_destroy(MsnSlpSession *slpsession); -void msn_slpsession_send_slpmsg(MsnSlpSession *slpsession, - MsnSlpMessage *slpmsg); -#endif /* _MSN_SLPSESSION_H_ */
--- a/libpurple/protocols/msn/soap.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/soap.c Thu Nov 20 21:13:56 2008 +0000 @@ -1,8 +1,7 @@ /** * @file soap.c - * SOAP connection related process - * Author - * MaYuan<mayuan2006@gmail.com> + * C file for SOAP connection related process + * * purple * * Purple is the legal property of its developers, whose names are too numerous @@ -23,856 +22,671 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "msn.h" + +#include "internal.h" + #include "soap.h" -#define MSN_SOAP_DEBUG -/*local function prototype*/ -void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); +#include "session.h" + +#include "debug.h" +#include "xmlnode.h" + +#include <glib.h> +#if !defined(_WIN32) || !defined(_WINERROR_) +#include <error.h> +#endif -/*setup the soap process step*/ -void -msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step) -{ -#ifdef MSN_SOAP_DEBUG - const char *MsnSoapStepText[] = - { - "Unconnected", - "Connecting", - "Connected", - "Processing", - "Connected Idle" - }; +#define SOAP_TIMEOUT (5 * 60) +#define MSN_UNSAFE_DEBUG 1 +typedef struct _MsnSoapRequest { + char *path; + MsnSoapMessage *message; + gboolean secure; + MsnSoapCallback cb; + gpointer cb_data; +} MsnSoapRequest; - purple_debug_info("MSN SOAP", "Setting SOAP process step to %s\n", MsnSoapStepText[step]); -#endif - soapconn->step = step; -} +typedef struct _MsnSoapConnection { + MsnSession *session; + char *host; -/*new a soap connection*/ -MsnSoapConn * -msn_soap_new(MsnSession *session,gpointer data, gboolean ssl) -{ - MsnSoapConn *soapconn; - - soapconn = g_new0(MsnSoapConn, 1); - soapconn->session = session; - soapconn->parent = data; - soapconn->ssl_conn = ssl; + time_t last_used; + PurpleSslConnection *ssl; + gboolean connected; - soapconn->gsc = NULL; - soapconn->input_handler = 0; - soapconn->output_handler = 0; - - msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); - soapconn->soap_queue = g_queue_new(); + guint event_handle; + GString *buf; + gsize handled_len; + gsize body_len; + int response_code; + gboolean headers_done; + gboolean close_when_done; - return soapconn; -} + MsnSoapMessage *message; -/*ssl soap connect callback*/ -void -msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond) -{ - MsnSoapConn * soapconn; - MsnSession *session; - gboolean soapconn_is_valid = FALSE; + GQueue *queue; + MsnSoapRequest *current_request; +} MsnSoapConnection; + +static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data); +static gboolean msn_soap_connection_run(gpointer data); - purple_debug_misc("MSN SOAP","SOAP server connection established!\n"); - - soapconn = data; - g_return_if_fail(soapconn != NULL); +static MsnSoapConnection *msn_soap_connection_new(MsnSession *session, + const char *host); +static void msn_soap_connection_handle_next(MsnSoapConnection *conn); +static void msn_soap_connection_destroy(MsnSoapConnection *conn); - session = soapconn->session; - g_return_if_fail(session != NULL); - - soapconn->gsc = gsc; +static void msn_soap_message_send_internal(MsnSession *session, MsnSoapMessage *message, + const char *host, const char *path, gboolean secure, + MsnSoapCallback cb, gpointer cb_data, gboolean first); - msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED); +static void msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message); +static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect); +static gboolean msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, gboolean initial); +static void msn_soap_process(MsnSoapConnection *conn); - /*connection callback*/ - if (soapconn->connect_cb != NULL) { - soapconn_is_valid = soapconn->connect_cb(soapconn, gsc); - } +static gboolean +msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data) +{ + MsnSoapConnection *conn = value; + time_t *t = data; - if (!soapconn_is_valid) { - return; + if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) { + purple_debug_info("soap", "cleaning up soap conn %p\n", conn); + return TRUE; } - /*we do the SOAP request here*/ - msn_soap_post_head_request(soapconn); -} - -/*ssl soap error callback*/ -static void -msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data) -{ - MsnSoapConn * soapconn = data; - - g_return_if_fail(data != NULL); - - purple_debug_warning("MSN SOAP","Soap connection error!\n"); - - msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); - - /*error callback*/ - if (soapconn->error_cb != NULL) { - soapconn->error_cb(soapconn, gsc, error); - } else { - msn_soap_post(soapconn, NULL); - } -} - -/*init the soap connection*/ -void -msn_soap_init(MsnSoapConn *soapconn,char * host, gboolean ssl, - MsnSoapSslConnectCbFunction connect_cb, - MsnSoapSslErrorCbFunction error_cb) -{ - purple_debug_misc("MSN SOAP","Initializing SOAP connection\n"); - g_free(soapconn->login_host); - soapconn->login_host = g_strdup(host); - soapconn->ssl_conn = ssl; - soapconn->connect_cb = connect_cb; - soapconn->error_cb = error_cb; + return FALSE; } -/*connect the soap connection*/ -void -msn_soap_connect(MsnSoapConn *soapconn) +static gboolean +msn_soap_cleanup_for_session(gpointer data) { - if (soapconn->ssl_conn) { - purple_ssl_connect(soapconn->session->account, soapconn->login_host, - PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb, - soapconn); - } else { + MsnSession *sess = data; + time_t t = time(NULL); + + purple_debug_info("soap", "session cleanup timeout\n"); + + if (sess->soap_table) { + g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each, + &t); + + if (g_hash_table_size(sess->soap_table) == 0) { + purple_timeout_remove(sess->soap_cleanup_handle); + sess->soap_cleanup_handle = 0; + } } - msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTING); -} - - -static void -msn_soap_close_handler(guint *handler) -{ - if (*handler > 0) { - purple_input_remove(*handler); - *handler = 0; - } -#ifdef MSN_SOAP_DEBUG - else { - purple_debug_misc("MSN SOAP", "Handler inactive, not removing\n"); - } -#endif - -} - - -/*close the soap connection*/ -void -msn_soap_close(MsnSoapConn *soapconn) -{ - if (soapconn->ssl_conn) { - if (soapconn->gsc != NULL) { - purple_ssl_close(soapconn->gsc); - soapconn->gsc = NULL; - } - } else { - } - msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); + return TRUE; } -/*clean the unhandled SOAP request*/ -void -msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn) +static MsnSoapConnection * +msn_soap_get_connection(MsnSession *session, const char *host) { - MsnSoapReq *request; - - g_return_if_fail(soapconn != NULL); - - soapconn->body = NULL; - - while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){ - if (soapconn->read_cb) { - soapconn->read_cb(soapconn); - } - msn_soap_request_free(request); - } -} - -/*destroy the soap connection*/ -void -msn_soap_destroy(MsnSoapConn *soapconn) -{ - g_free(soapconn->login_host); - - g_free(soapconn->login_path); + MsnSoapConnection *conn = NULL; - /*remove the write handler*/ - if (soapconn->output_handler > 0){ - purple_input_remove(soapconn->output_handler); - soapconn->output_handler = 0; - } - /*remove the read handler*/ - if (soapconn->input_handler > 0){ - purple_input_remove(soapconn->input_handler); - soapconn->input_handler = 0; - } - msn_soap_free_read_buf(soapconn); - msn_soap_free_write_buf(soapconn); - - /*close ssl connection*/ - msn_soap_close(soapconn); - - /*process the unhandled soap request*/ - msn_soap_clean_unhandled_requests(soapconn); - - g_queue_free(soapconn->soap_queue); - g_free(soapconn); -} - -/*check the soap is connected? - * if connected return 1 - */ -int -msn_soap_connected(MsnSoapConn *soapconn) -{ - if (soapconn->ssl_conn) { - return (soapconn->gsc == NULL ? 0 : 1); - } - return (soapconn->fd > 0 ? 1 : 0); -} - -/*read and append the content to the buffer*/ -static gssize -msn_soap_read(MsnSoapConn *soapconn) -{ - gssize len, requested_len; - char temp_buf[MSN_SOAP_READ_BUFF_SIZE]; - - if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) { - requested_len = MSN_SOAP_READ_BUFF_SIZE; - } - else { - requested_len = soapconn->need_to_read; + if (session->soap_table) { + conn = g_hash_table_lookup(session->soap_table, host); + } else { + session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, (GDestroyNotify)msn_soap_connection_destroy); } - if ( soapconn->ssl_conn ) { - len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len); - } else { - len = read(soapconn->fd, temp_buf, requested_len); + if (session->soap_cleanup_handle == 0) + session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000, + msn_soap_cleanup_for_session, session); + + if (conn == NULL) { + conn = msn_soap_connection_new(session, host); + g_hash_table_insert(session->soap_table, conn->host, conn); } + conn->last_used = time(NULL); - if ( len <= 0 ) { - switch (errno) { + return conn; +} - case 0: - case EBADF: /* we are sometimes getting this in Windows */ - case EAGAIN: return len; +static MsnSoapConnection * +msn_soap_connection_new(MsnSession *session, const char *host) +{ + MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1); + conn->session = session; + conn->host = g_strdup(host); + conn->queue = g_queue_new(); + return conn; +} - default : purple_debug_error("MSN SOAP", "Read error!" - "read len: %" G_GSSIZE_FORMAT ", error = %s\n", - len, g_strerror(errno)); - purple_input_remove(soapconn->input_handler); - //soapconn->input_handler = 0; - g_free(soapconn->read_buf); - soapconn->read_buf = NULL; - soapconn->read_len = 0; - /* TODO: error handling */ - return len; - } - } - else { - soapconn->read_buf = g_realloc(soapconn->read_buf, - soapconn->read_len + len + 1); - if ( soapconn->read_buf != NULL ) { - memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len); - soapconn->read_len += len; - soapconn->read_buf[soapconn->read_len] = '\0'; - } - else { - purple_debug_error("MSN SOAP", - "Failure re-allocating %" G_GSIZE_FORMAT " bytes of memory!\n", - soapconn->read_len + len + 1); - exit(EXIT_FAILURE); - } +static void +msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl, + PurpleInputCondition cond) +{ + MsnSoapConnection *conn = data; + + conn->connected = TRUE; - } + if (conn->event_handle == 0) + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); +} -#if defined(MSN_SOAP_DEBUG) - if (len > 0) - purple_debug_info("MSN SOAP", - "Read %" G_GSIZE_FORMAT " bytes from SOAP server:\n%s\n", len, - soapconn->read_buf + soapconn->read_len - len); -#endif +static void +msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error, + gpointer data) +{ + MsnSoapConnection *conn = data; - return len; + /* sslconn already frees the connection in case of error */ + conn->ssl = NULL; + + g_hash_table_remove(conn->session->soap_table, conn->host); } -/*read the whole SOAP server response*/ -static void -msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond) +static gboolean +msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url) { - MsnSoapConn *soapconn = data; - MsnSession *session; - int len; - char * body_start,*body_len; - char *length_start,*length_end; -#ifdef MSN_SOAP_DEBUG -#if !defined(_WIN32) - gchar * formattedxml = NULL; - gchar * http_headers = NULL; - xmlnode * node = NULL; -#endif - purple_debug_misc("MSN SOAP", "msn_soap_read_cb()\n"); -#endif - session = soapconn->session; - g_return_if_fail(session != NULL); + char *host; + char *path; + + if (purple_url_parse(url, &host, NULL, &path, NULL, NULL)) { + msn_soap_message_send_internal(conn->session, conn->current_request->message, + host, path, conn->current_request->secure, + conn->current_request->cb, conn->current_request->cb_data, TRUE); - - /*read the request header*/ - len = msn_soap_read(soapconn); + msn_soap_request_destroy(conn->current_request, TRUE); + conn->current_request = NULL; - if ( len < 0 ) - return; + g_free(host); + g_free(path); - if (soapconn->read_buf == NULL) { - return; + return TRUE; } - if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL) - || ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) ) - { - /* Redirect. */ - char *location, *c; + return FALSE; +} + +static gboolean +msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response) +{ + xmlnode *body = xmlnode_get_child(response->xml, "Body"); + xmlnode *fault = xmlnode_get_child(response->xml, "Fault"); + + if (fault) { + xmlnode *faultcode = xmlnode_get_child(fault, "faultcode"); + + if (faultcode != NULL) { + char *faultdata = xmlnode_get_data(faultcode); + + if (g_str_equal(faultdata, "psf:Redirect")) { + xmlnode *url = xmlnode_get_child(fault, "redirectUrl"); - purple_debug_info("MSN SOAP", "HTTP Redirect\n"); - location = strstr(soapconn->read_buf, "Location: "); - if (location == NULL) - { - c = (char *) g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); - if (c != NULL) { - /* we have read the whole HTTP headers and found no Location: */ - msn_soap_free_read_buf(soapconn); - msn_soap_post(soapconn, NULL); + if (url) { + char *urldata = xmlnode_get_data(url); + msn_soap_handle_redirect(conn, urldata); + g_free(urldata); + } + + g_free(faultdata); + msn_soap_message_destroy(response); + return TRUE; + } else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) { + xmlnode *reason = xmlnode_get_child(fault, "faultstring"); + char *reasondata = xmlnode_get_data(reason); + + msn_soap_connection_sanitize(conn, TRUE); + msn_session_set_error(conn->session, MSN_ERROR_AUTH, + reasondata); + + g_free(reasondata); + g_free(faultdata); + msn_soap_message_destroy(response); + return FALSE; } - return; - } - location = strchr(location, ' ') + 1; - - if ((c = strchr(location, '\r')) != NULL) - *c = '\0'; - else - return; - - /* Skip the http:// */ - if ((c = strchr(location, '/')) != NULL) - location = c + 2; - - if ((c = strchr(location, '/')) != NULL) - { - g_free(soapconn->login_path); - soapconn->login_path = g_strdup(c); - - *c = '\0'; - } - - g_free(soapconn->login_host); - soapconn->login_host = g_strdup(location); - - msn_soap_close_handler( &(soapconn->input_handler) ); - msn_soap_close(soapconn); - - if (purple_ssl_connect(session->account, soapconn->login_host, - PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, - msn_soap_error_cb, soapconn) == NULL) { - - purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host); - // dispatch next request - msn_soap_post(soapconn, NULL); - } - } - /* Another case of redirection, active on May, 2007 - See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect - */ - else if (strstr(soapconn->read_buf, - "<faultcode>psf:Redirect</faultcode>") != NULL) - { - char *location, *c; - - if ( (location = strstr(soapconn->read_buf, "<psf:redirectUrl>") ) == NULL) - return; - - /* Omit the tag preceding the URL */ - location += strlen("<psf:redirectUrl>"); - if (location > soapconn->read_buf + soapconn->read_len) - return; - if ( (location = strstr(location, "://")) == NULL) - return; - - location += strlen("://"); /* Skip http:// or https:// */ - - if ( (c = strstr(location, "</psf:redirectUrl>")) != NULL ) - *c = '\0'; - else - return; - - if ( (c = strstr(location, "/")) != NULL ) - { - g_free(soapconn->login_path); - soapconn->login_path = g_strdup(c); - *c = '\0'; - } - - g_free(soapconn->login_host); - soapconn->login_host = g_strdup(location); - - msn_soap_close_handler( &(soapconn->input_handler) ); - msn_soap_close(soapconn); - - if (purple_ssl_connect(session->account, soapconn->login_host, - PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, - msn_soap_error_cb, soapconn) == NULL) { - - purple_debug_error("MSN SOAP", "Unable to connect to %s !\n", soapconn->login_host); - // dispatch next request - msn_soap_post(soapconn, NULL); + g_free(faultdata); } } - else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) - { - const char *error; - - purple_debug_error("MSN SOAP", "Received HTTP error 401 Unauthorized\n"); - if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL) - { - if ((error = strstr(error, "cbtxt=")) != NULL) - { - const char *c; - char *temp; - - error += strlen("cbtxt="); - - if ((c = strchr(error, '\n')) == NULL) - c = error + strlen(error); - - temp = g_strndup(error, c - error); - error = purple_url_decode(temp); - g_free(temp); - } - } - - msn_session_set_error(session, MSN_ERROR_AUTH, error); - } - /* Handle Passport 3.0 authentication failures. - * Further info: http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener - */ - else if (strstr(soapconn->read_buf, - "<faultcode>wsse:FailedAuthentication</faultcode>") != NULL) - { - gchar *faultstring; - - faultstring = strstr(soapconn->read_buf, "<faultstring>"); - - if (faultstring != NULL) - { - gchar *c; - faultstring += strlen("<faultstring>"); - if (faultstring < soapconn->read_buf + soapconn->read_len) { - c = strstr(soapconn->read_buf, "</faultstring>"); - if (c != NULL) { - *c = '\0'; - msn_session_set_error(session, MSN_ERROR_AUTH, faultstring); - } - } - } - - } - else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable")) - { - msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - } - else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK")) - ||(strstr(soapconn->read_buf, "HTTP/1.1 500"))) - { - gboolean soapconn_is_valid = FALSE; - - /*OK! process the SOAP body*/ - body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); - if (!body_start) { - return; - } - body_start += 4; - - if (body_start > soapconn->read_buf + soapconn->read_len) - return; - - /* we read the content-length*/ - if ( (length_start = g_strstr_len(soapconn->read_buf, soapconn->read_len, "Content-Length: ")) != NULL) - length_start += strlen("Content-Length: "); - - if (length_start > soapconn->read_buf + soapconn->read_len) - return; - if ( (length_end = strstr(length_start, "\r\n")) == NULL ) - return; - - body_len = g_strndup(length_start, length_end - length_start); - - /*setup the conn body */ - soapconn->body = body_start; - soapconn->body_len = atoi(body_len); - g_free(body_len); -#ifdef MSN_SOAP_DEBUG - purple_debug_misc("MSN SOAP", - "SOAP bytes read so far: %" G_GSIZE_FORMAT ", Content-Length: %d\n", - soapconn->read_len, soapconn->body_len); -#endif - soapconn->need_to_read = (body_start - soapconn->read_buf + soapconn->body_len) - soapconn->read_len; - if ( soapconn->need_to_read > 0 ) { - return; - } - -#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32) - - node = xmlnode_from_str(soapconn->body, soapconn->body_len); - - if (node != NULL) { - formattedxml = xmlnode_to_formatted_str(node, NULL); - http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf); - - purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml); - g_free(http_headers); - g_free(formattedxml); - xmlnode_free(node); - } - else - purple_debug_info("MSN SOAP","Data received from the SOAP server:\n%s\n", soapconn->read_buf); -#endif + if (fault || body) { + MsnSoapRequest *request = conn->current_request; + conn->current_request = NULL; + request->cb(request->message, response, + request->cb_data); + msn_soap_message_destroy(response); + msn_soap_request_destroy(request, FALSE); + } - /*remove the read handler*/ - msn_soap_close_handler( &(soapconn->input_handler) ); -// purple_input_remove(soapconn->input_handler); -// soapconn->input_handler = 0; - /* - * close the soap connection,if more soap request came, - * Just reconnect to do it, - * - * To solve the problem described below: - * When I post the soap request in one socket one after the other, - * The first read is ok, But the second soap read always got 0 bytes, - * Weird! - * */ - msn_soap_close(soapconn); - - /*call the read callback*/ - if ( soapconn->read_cb != NULL ) { - soapconn_is_valid = soapconn->read_cb(soapconn); - } - - if (!soapconn_is_valid) { - return; - } - - /* dispatch next request in queue */ - msn_soap_post(soapconn, NULL); - } - return; -} - -void -msn_soap_free_read_buf(MsnSoapConn *soapconn) -{ - g_return_if_fail(soapconn != NULL); - - if (soapconn->read_buf) { - g_free(soapconn->read_buf); - } - soapconn->read_buf = NULL; - soapconn->read_len = 0; - soapconn->need_to_read = 0; + return TRUE; } -void -msn_soap_free_write_buf(MsnSoapConn *soapconn) +static void +msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond) { - g_return_if_fail(soapconn != NULL); - - if (soapconn->write_buf) { - g_free(soapconn->write_buf); - } - soapconn->write_buf = NULL; - soapconn->written_len = 0; -} + MsnSoapConnection *conn = data; + int count = 0, cnt, perrno; + /* This buffer needs to be larger than any packets received from + login.live.com or Adium will fail to receive the packet + (something weird with the login.live.com server). With NSS it works + fine, so I believe it's some bug with OS X */ + char buf[16 * 1024]; -/*Soap write process func*/ -static void -msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnSoapConn *soapconn = data; - int len, total_len; - - g_return_if_fail(soapconn != NULL); - if ( soapconn->write_buf == NULL ) { - purple_debug_error("MSN SOAP","SOAP write buffer is NULL\n"); - // msn_soap_check_conn_errors(soapconn); - // purple_input_remove(soapconn->output_handler); - // soapconn->output_handler = 0; - msn_soap_close_handler( &(soapconn->output_handler) ); - return; + if (conn->message == NULL) { + conn->message = msn_soap_message_new(NULL, NULL); } - total_len = strlen(soapconn->write_buf); - - /* - * write the content to SSL server, - */ - len = purple_ssl_write(soapconn->gsc, - soapconn->write_buf + soapconn->written_len, - total_len - soapconn->written_len); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0){ - /*SSL write error!*/ -// msn_soap_check_conn_errors(soapconn); - msn_soap_close_handler( &(soapconn->output_handler) ); -// purple_input_remove(soapconn->output_handler); -// soapconn->output_handler = 0; - - msn_soap_close(soapconn); + if (conn->buf == NULL) { + conn->buf = g_string_new_len(buf, 0); + } + + while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) { + purple_debug_info("soap", "read %d bytes\n", cnt); + count += cnt; + g_string_append_len(conn->buf, buf, cnt); + } - /* TODO: notify of the error */ - purple_debug_error("MSN SOAP", "Error writing to SSL connection!\n"); - msn_soap_post(soapconn, NULL); - return; - } - soapconn->written_len += len; - - if (soapconn->written_len < total_len) + /* && count is necessary for Adium, on OS X the last read always + return an error, so we want to proceed anyway. See #5212 for + discussion on this and the above buffer size issues */ + if(cnt < 0 && errno == EAGAIN && count == 0) return; - msn_soap_close_handler( &(soapconn->output_handler) ); -// purple_input_remove(soapconn->output_handler); -// soapconn->output_handler = 0; - - /*clear the write buff*/ - msn_soap_free_write_buf(soapconn); - - /* Write finish! - * callback for write done - */ - if(soapconn->written_cb != NULL){ - soapconn->written_cb(soapconn); - } - /*maybe we need to read the input?*/ - if ( soapconn->input_handler == 0 ) { - soapconn->input_handler = purple_input_add(soapconn->gsc->fd, - PURPLE_INPUT_READ, msn_soap_read_cb, soapconn); - } -} - -/*write the buffer to SOAP connection*/ -void -msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb) -{ - if (soapconn == NULL) { - return; - } - - msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); - - /* Ideally this wouldn't ever be necessary, but i believe that it is leaking the previous value */ - g_free(soapconn->write_buf); - soapconn->write_buf = write_buf; - soapconn->written_len = 0; - soapconn->written_cb = written_cb; - - msn_soap_free_read_buf(soapconn); - - /*clear the read buffer first*/ - /*start the write*/ - soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE, - msn_soap_write_cb, soapconn); - msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE); -} - -/* New a soap request*/ -MsnSoapReq * -msn_soap_request_new(const char *host,const char *post_url,const char *soap_action, - const char *body, const gpointer data_cb, - MsnSoapReadCbFunction read_cb, - MsnSoapWrittenCbFunction written_cb, - MsnSoapConnectInitFunction connect_init) -{ - MsnSoapReq *request; - - request = g_new0(MsnSoapReq, 1); - request->id = 0; - - request->login_host = g_strdup(host); - request->login_path = g_strdup(post_url); - request->soap_action = g_strdup(soap_action); - request->body = g_strdup(body); - request->data_cb = data_cb; - request->read_cb = read_cb; - request->written_cb = written_cb; - request->connect_init = connect_init; - - return request; -} - -/*free a soap request*/ -void -msn_soap_request_free(MsnSoapReq *request) -{ - g_return_if_fail(request != NULL); - - g_free(request->login_host); - g_free(request->login_path); - g_free(request->soap_action); - g_free(request->body); - request->read_cb = NULL; - request->written_cb = NULL; - request->connect_init = NULL; - - g_free(request); -} - -/*post the soap request queue's head request*/ -void -msn_soap_post_head_request(MsnSoapConn *soapconn) -{ - g_return_if_fail(soapconn != NULL); - g_return_if_fail(soapconn->soap_queue != NULL); - - if (soapconn->step == MSN_SOAP_CONNECTED || - soapconn->step == MSN_SOAP_CONNECTED_IDLE) { - - purple_debug_info("MSN SOAP", "Posting new request from head of the queue\n"); - - if ( !g_queue_is_empty(soapconn->soap_queue) ) { - MsnSoapReq *request; - - if ( (request = g_queue_pop_head(soapconn->soap_queue)) != NULL ) { - msn_soap_post_request(soapconn,request); - } - } else { - purple_debug_info("MSN SOAP", "No requests to process found.\n"); - msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED_IDLE); + /* msn_soap_process could alter errno */ + perrno = errno; + msn_soap_process(conn); + + if (cnt < 0 && perrno != EAGAIN) { + purple_debug_info("soap", "read: %s\n", g_strerror(perrno)); + /* It's possible msn_soap_process closed the ssl connection */ + if (conn->ssl) { + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + msn_soap_connection_handle_next(conn); } } } -/*post the soap request , - * if not connected, Connected first. - */ -void -msn_soap_post(MsnSoapConn *soapconn, MsnSoapReq *request) -{ - MsnSoapReq *head_request; +static void +msn_soap_process(MsnSoapConnection *conn) { + gboolean handled = FALSE; + char *cursor; + char *linebreak; + +#ifndef MSN_UNSAFE_DEBUG + if (conn->current_request->secure) + purple_debug_info("soap", "Received secure request.\n"); + else +#endif + purple_debug_info("soap", "current %s\n", conn->buf->str); + + cursor = conn->buf->str + conn->handled_len; + + if (!conn->headers_done) { + while ((linebreak = strstr(cursor, "\r\n")) != NULL) { + conn->handled_len = linebreak - conn->buf->str + 2; + + if (conn->response_code == 0) { + if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) { + /* something horribly wrong */ + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + msn_soap_connection_handle_next(conn); + handled = TRUE; + break; + } else if (conn->response_code == 503) { + msn_soap_connection_sanitize(conn, TRUE); + msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL); + return; + } + } else if (cursor == linebreak) { + /* blank line */ + conn->headers_done = TRUE; + cursor = conn->buf->str + conn->handled_len; + break; + } else { + char *line = g_strndup(cursor, linebreak - cursor); + char *sep = strstr(line, ": "); + char *key = line; + char *value; - if (soapconn == NULL) - return; + if (sep == NULL) { + purple_debug_info("soap", "ignoring malformed line: %s\n", line); + g_free(line); + goto loop_end; + } + + value = sep + 2; + *sep = '\0'; + msn_soap_message_add_header(conn->message, key, value); + + if ((conn->response_code == 301 || conn->response_code == 300) + && strcmp(key, "Location") == 0) { + + msn_soap_handle_redirect(conn, value); + + handled = TRUE; + g_free(line); + break; + } else if (conn->response_code == 401 && + strcmp(key, "WWW-Authenticate") == 0) { + char *error = strstr(value, "cbtxt="); - if (request != NULL) { -#ifdef MSN_SOAP_DEBUG - purple_debug_misc("MSN SOAP", "Request added to the queue\n"); -#endif - g_queue_push_tail(soapconn->soap_queue, request); + if (error) { + error += strlen("cbtxt="); + } + + msn_soap_connection_sanitize(conn, TRUE); + msn_session_set_error(conn->session, MSN_ERROR_AUTH, + error ? purple_url_decode(error) : NULL); + + g_free(line); + return; + } else if (strcmp(key, "Content-Length") == 0) { + conn->body_len = atoi(value); + } else if (strcmp(key, "Connection") == 0) { + if (strcmp(value, "close") == 0) { + conn->close_when_done = TRUE; + } + } + g_free(line); + } + + loop_end: + cursor = conn->buf->str + conn->handled_len; + } } - if ( !g_queue_is_empty(soapconn->soap_queue)) { - - /* we may have to reinitialize the soap connection, so avoid - * reusing the connection for now */ + if (!handled && conn->headers_done) { + if (conn->buf->len - conn->handled_len >= + conn->body_len) { + xmlnode *node = xmlnode_from_str(cursor, conn->body_len); - if (soapconn->step == MSN_SOAP_CONNECTED_IDLE) { - purple_debug_misc("MSN SOAP","Already connected to SOAP server, re-initializing\n"); - msn_soap_close_handler( &(soapconn->input_handler) ); - msn_soap_close_handler( &(soapconn->output_handler) ); - msn_soap_close(soapconn); + if (node == NULL) { + purple_debug_info("soap", "Malformed SOAP response: %s\n", + cursor); + } else { + MsnSoapMessage *message = conn->message; + conn->message = NULL; + message->xml = node; + + if (!msn_soap_handle_body(conn, message)) { + return; + } + } + + msn_soap_connection_handle_next(conn); } - if (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)) { + return; + } - /*not connected?and we have something to process connect it first*/ - purple_debug_misc("MSN SOAP","No connection to SOAP server. Connecting...\n"); - head_request = g_queue_peek_head(soapconn->soap_queue); + if (handled) { + msn_soap_connection_handle_next(conn); + } +} - if (head_request == NULL) { - purple_debug_error("MSN SOAP", "Queue is not empty, but failed to peek the head request!\n"); - return; - } +static void +msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond) +{ + msn_soap_write_cb_internal(data, fd, cond, FALSE); +} - if (head_request->connect_init != NULL) { - head_request->connect_init(soapconn); - } - msn_soap_connect(soapconn); - return; - } +static gboolean +msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond, + gboolean initial) +{ + MsnSoapConnection *conn = data; + int written; + + if (cond != PURPLE_INPUT_WRITE) return TRUE; -#ifdef MSN_SOAP_DEBUG - purple_debug_info("MSN SOAP", "Currently processing another SOAP request\n"); - } else { - purple_debug_info("MSN SOAP", "No requests left to dispatch\n"); -#endif + written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len, + conn->buf->len - conn->handled_len); + + if (written < 0 && errno == EAGAIN) + return TRUE; + else if (written <= 0) { + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + if (!initial) msn_soap_connection_handle_next(conn); + return FALSE; } + conn->handled_len += written; + + if (conn->handled_len < conn->buf->len) + return TRUE; + + /* we are done! */ + g_string_free(conn->buf, TRUE); + conn->buf = NULL; + conn->handled_len = 0; + conn->body_len = 0; + conn->response_code = 0; + conn->headers_done = FALSE; + conn->close_when_done = FALSE; + + purple_input_remove(conn->event_handle); + conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ, + msn_soap_read_cb, conn); + return TRUE; +} + +static gboolean +msn_soap_connection_run(gpointer data) +{ + MsnSoapConnection *conn = data; + MsnSoapRequest *req = g_queue_peek_head(conn->queue); + + conn->event_handle = 0; + + if (req) { + if (conn->ssl == NULL) { + conn->ssl = purple_ssl_connect(conn->session->account, conn->host, + 443, msn_soap_connected_cb, msn_soap_error_cb, conn); + } else if (conn->connected) { + int len = -1; + char *body = xmlnode_to_str(req->message->xml, &len); + GSList *iter; + + g_queue_pop_head(conn->queue); + + conn->buf = g_string_new(""); + + g_string_append_printf(conn->buf, + "POST /%s HTTP/1.1\r\n" + "SOAPAction: %s\r\n" + "Content-Type:text/xml; charset=utf-8\r\n" + "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" + "Accept: */*\r\n" + "Host: %s\r\n" + "Content-Length: %d\r\n" + "Connection: Keep-Alive\r\n" + "Cache-Control: no-cache\r\n", + req->path, req->message->action ? req->message->action : "", + conn->host, len); + + for (iter = req->message->headers; iter; iter = iter->next) { + g_string_append(conn->buf, (char *)iter->data); + g_string_append(conn->buf, "\r\n"); + } + + g_string_append(conn->buf, "\r\n"); + g_string_append(conn->buf, body); + +#ifndef MSN_UNSAFE_DEBUG + if (req->secure) + purple_debug_info("soap", "Sending secure request.\n"); + else +#endif + purple_debug_info("soap", "%s\n", conn->buf->str); + + conn->handled_len = 0; + conn->current_request = req; + + conn->event_handle = purple_input_add(conn->ssl->fd, + PURPLE_INPUT_WRITE, msn_soap_write_cb, conn); + if (!msn_soap_write_cb_internal(conn, conn->ssl->fd, PURPLE_INPUT_WRITE, TRUE)) { + /* Not connected => reconnect and retry */ + purple_debug_info("soap", "not connected, reconnecting\n"); + + conn->connected = FALSE; + conn->current_request = NULL; + msn_soap_connection_sanitize(conn, FALSE); + + g_queue_push_head(conn->queue, req); + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); + } + + g_free(body); + } + } + + return FALSE; +} + +void +msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, + const char *host, const char *path, gboolean secure, + MsnSoapCallback cb, gpointer cb_data) +{ + msn_soap_message_send_internal(session, message, host, path, secure, + cb, cb_data, FALSE); } -/*Post the soap request action*/ -void -msn_soap_post_request(MsnSoapConn *soapconn, MsnSoapReq *request) +static void +msn_soap_message_send_internal(MsnSession *session, MsnSoapMessage *message, + const char *host, const char *path, gboolean secure, + MsnSoapCallback cb, gpointer cb_data, gboolean first) { - char * request_str = NULL; -#ifdef MSN_SOAP_DEBUG -#if !defined(_WIN32) - xmlnode * node; -#endif - purple_debug_misc("MSN SOAP","msn_soap_post_request()\n"); -#endif + MsnSoapConnection *conn = msn_soap_get_connection(session, host); + MsnSoapRequest *req = g_new0(MsnSoapRequest, 1); + + req->path = g_strdup(path); + req->message = message; + req->secure = secure; + req->cb = cb; + req->cb_data = cb_data; + + if (first) { + g_queue_push_head(conn->queue, req); + } else { + g_queue_push_tail(conn->queue, req); + } + + if (conn->event_handle == 0) + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, + conn); +} + +static void +msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect) +{ + if (conn->event_handle) { + purple_input_remove(conn->event_handle); + conn->event_handle = 0; + } - msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING); - request_str = g_strdup_printf( - "POST %s HTTP/1.1\r\n" - "SOAPAction: %s\r\n" - "Content-Type:text/xml; charset=utf-8\r\n" - "Cookie: MSPAuth=%s\r\n" - "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" - "Accept: */*\r\n" - "Host: %s\r\n" - "Content-Length: %" G_GSIZE_FORMAT "\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n\r\n" - "%s", - request->login_path, - request->soap_action, - soapconn->session->passport_info.mspauth, - request->login_host, - strlen(request->body), - request->body - ); + if (conn->message) { + msn_soap_message_destroy(conn->message); + conn->message = NULL; + } + + if (conn->buf) { + g_string_free(conn->buf, TRUE); + conn->buf = NULL; + } + + if (conn->ssl && (disconnect || conn->close_when_done)) { + purple_ssl_close(conn->ssl); + conn->ssl = NULL; + } -#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32) - node = xmlnode_from_str(request->body, -1); - if (node != NULL) { - char *formattedstr = xmlnode_to_formatted_str(node, NULL); - purple_debug_info("MSN SOAP","Posting request to SOAP server:\n%s%s\n",request_str, formattedstr); - g_free(formattedstr); - xmlnode_free(node); + if (conn->current_request) { + msn_soap_request_destroy(conn->current_request, FALSE); + conn->current_request = NULL; } - else - purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str); -#endif +} + +static void +msn_soap_connection_handle_next(MsnSoapConnection *conn) +{ + msn_soap_connection_sanitize(conn, FALSE); - /*free read buffer*/ - // msn_soap_free_read_buf(soapconn); - /*post it to server*/ - soapconn->data_cb = request->data_cb; - msn_soap_write(soapconn, request_str, request->written_cb); + conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); + + if (conn->current_request) { + MsnSoapRequest *req = conn->current_request; + conn->current_request = NULL; + msn_soap_connection_destroy_foreach_cb(req, conn); + } } +static void +msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data) +{ + MsnSoapRequest *req = item; + + if (req->cb) + req->cb(req->message, NULL, req->cb_data); + + msn_soap_request_destroy(req, FALSE); +} + +static void +msn_soap_connection_destroy(MsnSoapConnection *conn) +{ + if (conn->current_request) { + MsnSoapRequest *req = conn->current_request; + conn->current_request = NULL; + msn_soap_connection_destroy_foreach_cb(req, conn); + } + + msn_soap_connection_sanitize(conn, TRUE); + g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn); + g_queue_free(conn->queue); + + g_free(conn->host); + g_free(conn); +} + +MsnSoapMessage * +msn_soap_message_new(const char *action, xmlnode *xml) +{ + MsnSoapMessage *message = g_new0(MsnSoapMessage, 1); + + message->action = g_strdup(action); + message->xml = xml; + + return message; +} + +void +msn_soap_message_destroy(MsnSoapMessage *message) +{ + if (message) { + g_slist_foreach(message->headers, (GFunc)g_free, NULL); + g_slist_free(message->headers); + g_free(message->action); + if (message->xml) + xmlnode_free(message->xml); + g_free(message); + } +} + +void +msn_soap_message_add_header(MsnSoapMessage *message, + const char *name, const char *value) +{ + char *header = g_strdup_printf("%s: %s\r\n", name, value); + + message->headers = g_slist_prepend(message->headers, header); +} + +static void +msn_soap_request_destroy(MsnSoapRequest *req, gboolean keep_message) +{ + g_free(req->path); + if (!keep_message) + msn_soap_message_destroy(req->message); + g_free(req); +} +
--- a/libpurple/protocols/msn/soap.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/soap.h Thu Nov 20 21:13:56 2008 +0000 @@ -1,8 +1,7 @@ /** * @file soap.h * header file for SOAP connection related process - * Author - * MaYuan<mayuan2006@gmail.com> + * * purple * * Purple is the legal property of its developers, whose names are too numerous @@ -23,144 +22,35 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _MSN_SOAP_H_ -#define _MSN_SOAP_H_ -#define MSN_SOAP_READ_BUFF_SIZE 8192 - -/* define this to debug the communications with the SOAP server */ -/* #define MSN_SOAP_DEBUG */ - -#define MSN_SOAP_READ 1 -#define MSN_SOAP_WRITE 2 +#ifndef _MSN_SOAP_H +#define _MSN_SOAP_H -typedef enum -{ - MSN_SOAP_UNCONNECTED, - MSN_SOAP_CONNECTING, - MSN_SOAP_CONNECTED, - MSN_SOAP_PROCESSING, - MSN_SOAP_CONNECTED_IDLE -}MsnSoapStep; - -/* MSN SoapRequest structure*/ -typedef struct _MsnSoapReq MsnSoapReq; +#include "session.h" +#include "sslconn.h" +#include "xmlnode.h" -/* MSN Https connection structure*/ -typedef struct _MsnSoapConn MsnSoapConn; - -typedef void (*MsnSoapConnectInitFunction)(MsnSoapConn *); -typedef gboolean (*MsnSoapReadCbFunction)(MsnSoapConn *); -typedef void (*MsnSoapWrittenCbFunction)(MsnSoapConn *); - -typedef gboolean (*MsnSoapSslConnectCbFunction)(MsnSoapConn *, PurpleSslConnection *); -typedef void (*MsnSoapSslErrorCbFunction)(MsnSoapConn *, PurpleSslConnection *, PurpleSslErrorType); - +#include <glib.h> -struct _MsnSoapReq{ - /*request sequence*/ - int id; +typedef struct _MsnSoapMessage MsnSoapMessage; +typedef void (*MsnSoapCallback)(MsnSoapMessage *request, + MsnSoapMessage *response, gpointer cb_data); - char *login_host; - char *login_path; - char *soap_action; - - char *body; - - gpointer data_cb; - MsnSoapReadCbFunction read_cb; - MsnSoapWrittenCbFunction written_cb; - MsnSoapConnectInitFunction connect_init; +struct _MsnSoapMessage { + char *action; + xmlnode *xml; + GSList *headers; }; -struct _MsnSoapConn{ - MsnSession *session; - gpointer parent; - - char *login_host; - char *login_path; - char *soap_action; - - MsnSoapStep step; - /*ssl connection?*/ - gboolean ssl_conn; - /*normal connection*/ - guint fd; - /*SSL connection*/ - PurpleSslConnection *gsc; - /*ssl connection callback*/ - MsnSoapSslConnectCbFunction connect_cb; - /*ssl error callback*/ - MsnSoapSslErrorCbFunction error_cb; +MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml); - /*read handler*/ - guint input_handler; - /*write handler*/ - guint output_handler; - - /*Queue of SOAP request to send*/ - int soap_id; - GQueue *soap_queue; - - /*write buffer*/ - char *write_buf; - gsize written_len; - MsnSoapWrittenCbFunction written_cb; - - /*read buffer*/ - char *read_buf; - gsize read_len; - gsize need_to_read; - MsnSoapReadCbFunction read_cb; - - gpointer data_cb; +void msn_soap_message_add_header(MsnSoapMessage *req, + const char *name, const char *value); - /*HTTP reply body part*/ - char *body; - int body_len; -}; - - -/*Function Prototype*/ -/*Soap Request Function */ -MsnSoapReq *msn_soap_request_new(const char *host, const char *post_url, - const char *soap_action, const char *body, - const gpointer data_cb, - MsnSoapReadCbFunction read_cb, - MsnSoapWrittenCbFunction written_cb, - MsnSoapConnectInitFunction connect_init); - -void msn_soap_request_free(MsnSoapReq *request); -void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request); -void msn_soap_post_head_request(MsnSoapConn *soapconn); - -/*new a soap conneciton */ -MsnSoapConn *msn_soap_new(MsnSession *session, gpointer data, gboolean ssl); - -/*destroy */ -void msn_soap_destroy(MsnSoapConn *soapconn); +void msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, + const char *host, const char *path, gboolean secure, + MsnSoapCallback cb, gpointer cb_data); -/*init a soap conneciton */ -void msn_soap_init(MsnSoapConn *soapconn, char * host, gboolean ssl, - MsnSoapSslConnectCbFunction connect_cb, - MsnSoapSslErrorCbFunction error_cb); -void msn_soap_connect(MsnSoapConn *soapconn); -void msn_soap_close(MsnSoapConn *soapconn); - -/*write to soap*/ -void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb); -void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request); +void msn_soap_message_destroy(MsnSoapMessage *message); -void msn_soap_free_read_buf(MsnSoapConn *soapconn); -void msn_soap_free_write_buf(MsnSoapConn *soapconn); -void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond); - -/*clean the unhandled requests*/ -void msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn); - -/*check if the soap connection is connected*/ -int msn_soap_connected(MsnSoapConn *soapconn); -void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); - -#endif/*_MSN_SOAP_H_*/ - +#endif
--- a/libpurple/protocols/msn/soap2.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,693 +0,0 @@ -/** - * @file soap2.c - * C file for SOAP connection related process - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "internal.h" - -#include "soap2.h" - -#include "session.h" - -#include "debug.h" -#include "xmlnode.h" - -#include <glib.h> -#if !defined(_WIN32) || !defined(_WINERROR_) -#include <error.h> -#endif - -#define SOAP_TIMEOUT (5 * 60) - -typedef struct _MsnSoapRequest { - char *path; - MsnSoapMessage *message; - MsnSoapCallback cb; - gpointer cb_data; -} MsnSoapRequest; - -typedef struct _MsnSoapConnection { - MsnSession *session; - char *host; - - time_t last_used; - PurpleSslConnection *ssl; - gboolean connected; - - guint event_handle; - GString *buf; - gsize handled_len; - gsize body_len; - int response_code; - gboolean headers_done; - gboolean close_when_done; - - MsnSoapMessage *message; - - GQueue *queue; - MsnSoapRequest *current_request; -} MsnSoapConnection; - -static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data); -static gboolean msn_soap_connection_run(gpointer data); - -static MsnSoapConnection *msn_soap_connection_new(MsnSession *session, - const char *host); -static void msn_soap_connection_handle_next(MsnSoapConnection *conn); -static void msn_soap_connection_destroy(MsnSoapConnection *conn); - -static void msn_soap_message_send_internal(MsnSession *session, - MsnSoapMessage *message, const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data, gboolean first); - -static void msn_soap_request_destroy(MsnSoapRequest *req); -static void msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect); -static void msn_soap_process(MsnSoapConnection *conn); - -static gboolean -msn_soap_cleanup_each(gpointer key, gpointer value, gpointer data) -{ - MsnSoapConnection *conn = value; - time_t *t = data; - - if ((*t - conn->last_used) > SOAP_TIMEOUT * 2) { - purple_debug_info("soap", "cleaning up soap conn %p\n", conn); - return TRUE; - } - - return FALSE; -} - -static gboolean -msn_soap_cleanup_for_session(gpointer data) -{ - MsnSession *sess = data; - time_t t = time(NULL); - - purple_debug_info("soap", "session cleanup timeout\n"); - - if (sess->soap_table) { - g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each, - &t); - - if (g_hash_table_size(sess->soap_table) == 0) { - purple_timeout_remove(sess->soap_cleanup_handle); - sess->soap_cleanup_handle = 0; - } - } - - return TRUE; -} - -static MsnSoapConnection * -msn_soap_get_connection(MsnSession *session, const char *host) -{ - MsnSoapConnection *conn = NULL; - - if (session->soap_table) { - conn = g_hash_table_lookup(session->soap_table, host); - } else { - session->soap_table = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, (GDestroyNotify)msn_soap_connection_destroy); - } - - if (session->soap_cleanup_handle == 0) - session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000, - msn_soap_cleanup_for_session, session); - - if (conn == NULL) { - conn = msn_soap_connection_new(session, host); - g_hash_table_insert(session->soap_table, conn->host, conn); - } - - conn->last_used = time(NULL); - - return conn; -} - -static MsnSoapConnection * -msn_soap_connection_new(MsnSession *session, const char *host) -{ - MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1); - conn->session = session; - conn->host = g_strdup(host); - conn->queue = g_queue_new(); - return conn; -} - -static void -msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl, - PurpleInputCondition cond) -{ - MsnSoapConnection *conn = data; - - conn->connected = TRUE; - - if (conn->event_handle == 0) - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); -} - -static void -msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error, - gpointer data) -{ - MsnSoapConnection *conn = data; - - /* sslconn already frees the connection in case of error */ - conn->ssl = NULL; - - g_hash_table_remove(conn->session->soap_table, conn->host); -} - -static gboolean -msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url) -{ - char *c; - - /* Skip the http:// */ - if ((c = strchr(url, '/')) != NULL) - url += 2; - - if ((c = strchr(url, '/')) != NULL) { - char *host, *path; - - host = g_strndup(url, c - url); - path = g_strdup(c); - - msn_soap_message_send_internal(conn->session, - conn->current_request->message, host, path, - conn->current_request->cb, conn->current_request->cb_data, TRUE); - - msn_soap_request_destroy(conn->current_request); - conn->current_request = NULL; - - g_free(host); - g_free(path); - - return TRUE; - } - - return FALSE; -} - -static gboolean -msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response) -{ - xmlnode *body = xmlnode_get_child(response->xml, "Body"); - xmlnode *fault = xmlnode_get_child(response->xml, "Fault"); - - if (fault) { - xmlnode *faultcode = xmlnode_get_child(fault, "faultcode"); - - if (faultcode != NULL) { - char *faultdata = xmlnode_get_data(faultcode); - - if (g_str_equal(faultdata, "psf:Redirect")) { - xmlnode *url = xmlnode_get_child(body, "redirectUrl"); - - if (url) { - char *urldata = xmlnode_get_data(url); - msn_soap_handle_redirect(conn, urldata); - g_free(urldata); - } - - g_free(faultdata); - return TRUE; - } else if (g_str_equal(faultdata, "wsse:FailedAuthentication")) { - xmlnode *reason = xmlnode_get_child(body, "faultstring"); - char *reasondata = xmlnode_get_data(reason); - - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_AUTH, - reasondata); - - g_free(reasondata); - g_free(faultdata); - return FALSE; - } - - g_free(faultdata); - } - } - - if (fault || body) { - MsnSoapRequest *request = conn->current_request; - conn->current_request = NULL; - request->cb(request->message, response, - request->cb_data); - msn_soap_request_destroy(request); - } - - return TRUE; -} - -static void -msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond) -{ - MsnSoapConnection *conn = data; - int count = 0, cnt, perrno; - /* This buffer needs to be larger than any packets received from - login.live.com or Adium will fail to receive the packet - (something weird with the login.live.com server). With NSS it works - fine, so I believe it's some bug with OS X */ - char buf[16 * 1024]; - - if (conn->message == NULL) { - conn->message = msn_soap_message_new(NULL, NULL); - } - - if (conn->buf == NULL) { - conn->buf = g_string_new_len(buf, 0); - } - - while ((cnt = purple_ssl_read(conn->ssl, buf, sizeof(buf))) > 0) { - purple_debug_info("soap", "read %d bytes\n", cnt); - count += cnt; - g_string_append_len(conn->buf, buf, cnt); - } - - /* && count is necessary for Adium, on OS X the last read always - return an error, so we want to proceed anyway. See #5212 for - discussion on this and the above buffer size issues */ - if(cnt < 0 && errno == EAGAIN && count == 0) - return; - - // msn_soap_process could alter errno - perrno = errno; - msn_soap_process(conn); - - if (cnt < 0 && perrno != EAGAIN) { - purple_debug_info("soap", "read: %s\n", g_strerror(perrno)); - // It's possible msn_soap_process closed the ssl connection - if (conn->ssl) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - msn_soap_connection_handle_next(conn); - } - } -} - -static void -msn_soap_process(MsnSoapConnection *conn) { - gboolean handled = FALSE; - char *cursor; - char *linebreak; - - purple_debug_info("soap", "current %s\n", conn->buf->str); - - cursor = conn->buf->str + conn->handled_len; - - if (!conn->headers_done) { - while ((linebreak = strstr(cursor, "\r\n")) != NULL) { - conn->handled_len = linebreak - conn->buf->str + 2; - - if (conn->response_code == 0) { - if (sscanf(cursor, "HTTP/1.1 %d", &conn->response_code) != 1) { - /* something horribly wrong */ - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - msn_soap_connection_handle_next(conn); - handled = TRUE; - break; - } else if (conn->response_code == 503) { - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - return; - } - } else if (cursor == linebreak) { - /* blank line */ - conn->headers_done = TRUE; - cursor = conn->buf->str + conn->handled_len; - break; - } else { - char *line = g_strndup(cursor, linebreak - cursor); - char *sep = strstr(line, ": "); - char *key = line; - char *value; - - if (sep == NULL) { - purple_debug_info("soap", "ignoring malformed line: %s\n", line); - g_free(line); - goto loop_end; - } - - value = sep + 2; - *sep = '\0'; - msn_soap_message_add_header(conn->message, key, value); - - if ((conn->response_code == 301 || conn->response_code == 300) - && strcmp(key, "Location") == 0) { - - msn_soap_handle_redirect(conn, value); - - handled = TRUE; - g_free(line); - break; - } else if (conn->response_code == 401 && - strcmp(key, "WWW-Authenticate") == 0) { - char *error = strstr(value, "cbtxt="); - - if (error) { - error += strlen("cbtxt="); - } - - msn_soap_connection_sanitize(conn, TRUE); - msn_session_set_error(conn->session, MSN_ERROR_AUTH, - error ? purple_url_decode(error) : NULL); - - g_free(line); - return; - } else if (strcmp(key, "Content-Length") == 0) { - conn->body_len = atoi(value); - } else if (strcmp(key, "Connection") == 0) { - if (strcmp(value, "close") == 0) { - conn->close_when_done = TRUE; - } - } - g_free(line); - } - - loop_end: - cursor = conn->buf->str + conn->handled_len; - } - } - - if (!handled && conn->headers_done) { - if (conn->buf->len - conn->handled_len >= - conn->body_len) { - xmlnode *node = xmlnode_from_str(cursor, conn->body_len); - - if (node == NULL) { - purple_debug_info("soap", "Malformed SOAP response: %s\n", - cursor); - } else { - MsnSoapMessage *message = conn->message; - conn->message = NULL; - message->xml = node; - - if (!msn_soap_handle_body(conn, message)) { - msn_soap_message_destroy(message); - return; - } - msn_soap_message_destroy(message); - } - - msn_soap_connection_handle_next(conn); - } - - return; - } - - if (handled) { - msn_soap_connection_handle_next(conn); - } -} - -static void -msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond) -{ - MsnSoapConnection *conn = data; - int written; - - g_return_if_fail(cond == PURPLE_INPUT_WRITE); - - written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len, - conn->buf->len - conn->handled_len); - - if (written < 0 && errno == EAGAIN) - return; - else if (written <= 0) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - msn_soap_connection_handle_next(conn); - return; - } - - conn->handled_len += written; - - if (conn->handled_len < conn->buf->len) - return; - - /* we are done! */ - g_string_free(conn->buf, TRUE); - conn->buf = NULL; - conn->handled_len = 0; - conn->body_len = 0; - conn->response_code = 0; - conn->headers_done = FALSE; - conn->close_when_done = FALSE; - - purple_input_remove(conn->event_handle); - conn->event_handle = purple_input_add(conn->ssl->fd, PURPLE_INPUT_READ, - msn_soap_read_cb, conn); -} - -static gboolean -msn_soap_connection_run(gpointer data) -{ - MsnSoapConnection *conn = data; - MsnSoapRequest *req = g_queue_peek_head(conn->queue); - - conn->event_handle = 0; - - if (req) { - if (conn->ssl == NULL) { - conn->ssl = purple_ssl_connect(conn->session->account, conn->host, - 443, msn_soap_connected_cb, msn_soap_error_cb, conn); - } else if (conn->connected) { - int len = -1; - char *body = xmlnode_to_str(req->message->xml, &len); - GSList *iter; - char *authstr = NULL; - - g_queue_pop_head(conn->queue); - - conn->buf = g_string_new(""); - - if (conn->session->passport_info.mspauth) - authstr = g_strdup_printf("Cookie: MSPAuth=%s\r\n", - conn->session->passport_info.mspauth); - - - g_string_append_printf(conn->buf, - "POST %s HTTP/1.1\r\n" - "SOAPAction: %s\r\n" - "Content-Type:text/xml; charset=utf-8\r\n" - "%s" - "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" - "Accept: */*\r\n" - "Host: %s\r\n" - "Content-Length: %d\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n", - req->path, req->message->action ? req->message->action : "", - authstr ? authstr : "", conn->host, len); - - for (iter = req->message->headers; iter; iter = iter->next) { - g_string_append(conn->buf, (char *)iter->data); - g_string_append(conn->buf, "\r\n"); - } - - g_string_append(conn->buf, "\r\n"); - g_string_append(conn->buf, body); - - purple_debug_info("soap", "%s\n", conn->buf->str); - - conn->handled_len = 0; - conn->current_request = req; - - conn->event_handle = purple_input_add(conn->ssl->fd, - PURPLE_INPUT_WRITE, msn_soap_write_cb, conn); - msn_soap_write_cb(conn, conn->ssl->fd, PURPLE_INPUT_WRITE); - - g_free(authstr); - g_free(body); - } - } - - return FALSE; -} - -void -msn_soap_message_send(MsnSession *session, MsnSoapMessage *message, - const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data) -{ - msn_soap_message_send_internal(session, message, host, path, cb, cb_data, - FALSE); -} - -static void -msn_soap_message_send_internal(MsnSession *session, - MsnSoapMessage *message, const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data, gboolean first) -{ - MsnSoapConnection *conn = msn_soap_get_connection(session, host); - MsnSoapRequest *req = g_new0(MsnSoapRequest, 1); - - req->path = g_strdup(path); - req->message = message; - req->cb = cb; - req->cb_data = cb_data; - - if (first) { - g_queue_push_head(conn->queue, req); - } else { - g_queue_push_tail(conn->queue, req); - } - - if (conn->event_handle == 0) - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, - conn); -} - -static void -msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect) -{ - if (conn->event_handle) { - purple_input_remove(conn->event_handle); - conn->event_handle = 0; - } - - if (conn->message) { - msn_soap_message_destroy(conn->message); - conn->message = NULL; - } - - if (conn->buf) { - g_string_free(conn->buf, TRUE); - conn->buf = NULL; - } - - if (conn->ssl && (disconnect || conn->close_when_done)) { - purple_ssl_close(conn->ssl); - conn->ssl = NULL; - } - - if (conn->current_request) { - msn_soap_request_destroy(conn->current_request); - conn->current_request = NULL; - } -} - -static void -msn_soap_connection_handle_next(MsnSoapConnection *conn) -{ - msn_soap_connection_sanitize(conn, FALSE); - - conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn); - - if (conn->current_request) { - MsnSoapRequest *req = conn->current_request; - conn->current_request = NULL; - msn_soap_connection_destroy_foreach_cb(req, conn); - } -} - -static void -msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data) -{ - MsnSoapRequest *req = item; - - if (req->cb) - req->cb(req->message, NULL, req->cb_data); - - msn_soap_request_destroy(req); -} - -static void -msn_soap_connection_destroy(MsnSoapConnection *conn) -{ - if (conn->current_request) { - MsnSoapRequest *req = conn->current_request; - conn->current_request = NULL; - msn_soap_connection_destroy_foreach_cb(req, conn); - } - - msn_soap_connection_sanitize(conn, TRUE); - g_queue_foreach(conn->queue, msn_soap_connection_destroy_foreach_cb, conn); - g_queue_free(conn->queue); - - g_free(conn->host); - g_free(conn); -} - -MsnSoapMessage * -msn_soap_message_new(const char *action, xmlnode *xml) -{ - MsnSoapMessage *message = g_new0(MsnSoapMessage, 1); - - message->action = g_strdup(action); - message->xml = xml; - - return message; -} - -void -msn_soap_message_destroy(MsnSoapMessage *message) -{ - if (message) { - g_slist_foreach(message->headers, (GFunc)g_free, NULL); - g_slist_free(message->headers); - g_free(message->action); - if (message->xml) - xmlnode_free(message->xml); - g_free(message); - } -} - -void -msn_soap_message_add_header(MsnSoapMessage *message, - const char *name, const char *value) -{ - char *header = g_strdup_printf("%s: %s\r\n", name, value); - - message->headers = g_slist_prepend(message->headers, header); -} - -static void -msn_soap_request_destroy(MsnSoapRequest *req) -{ - g_free(req->path); - msn_soap_message_destroy(req->message); - g_free(req); -} - -xmlnode * -msn_soap_xml_get(xmlnode *parent, const char *node) -{ - xmlnode *ret = NULL; - char **tokens = g_strsplit(node, "/", -1); - int i; - - for (i = 0; tokens[i]; i++) { - if ((ret = xmlnode_get_child(parent, tokens[i])) != NULL) - parent = ret; - else - break; - } - - g_strfreev(tokens); - return ret; -} -
--- a/libpurple/protocols/msn/soap2.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/** - * @file soap2.h - * header file for SOAP connection related process - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _MSN_SOAP2_H -#define _MSN_SOAP2_H - -#include "session.h" -#include "sslconn.h" -#include "xmlnode.h" - -#include <glib.h> - -typedef struct _MsnSoapMessage MsnSoapMessage; -typedef void (*MsnSoapCallback)(MsnSoapMessage *request, - MsnSoapMessage *response, gpointer cb_data); - -struct _MsnSoapMessage { - char *action; - xmlnode *xml; - GSList *headers; -}; - -MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml); - -void msn_soap_message_add_header(MsnSoapMessage *req, - const char *name, const char *value); - -void msn_soap_message_send(MsnSession *session, - MsnSoapMessage *message, const char *host, const char *path, - MsnSoapCallback cb, gpointer cb_data); - -void msn_soap_message_destroy(MsnSoapMessage *message); - -xmlnode *msn_soap_xml_get(xmlnode *parent, const char *node); - -#endif
--- a/libpurple/protocols/msn/state.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/state.c Thu Nov 20 21:13:56 2008 +0000 @@ -151,15 +151,15 @@ xmlnode *payloadNode, *currentmediaNode; char *currentmedia; - purple_debug_info("msn","msn get CurrentMedia\n"); + 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"); + 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"); + if (currentmediaNode == NULL) { + purple_debug_info("msn", "No CurrentMedia Node"); xmlnode_free(payloadNode); return NULL; } @@ -177,15 +177,15 @@ xmlnode *payloadNode, *psmNode; char *psm; - purple_debug_info("MSNP14","msn get PSM\n"); + purple_debug_info("msn", "msn get PSM\n"); payloadNode = xmlnode_from_str(xml_str, len); if (!payloadNode){ - purple_debug_error("MSNP14","PSM XML parse Error!\n"); + purple_debug_error("msn", "PSM XML parse Error!\n"); return NULL; } psmNode = xmlnode_get_child(payloadNode, "PSM"); if (psmNode == NULL){ - purple_debug_info("MSNP14","No PSM status Node"); + purple_debug_info("msn", "No PSM status Node"); xmlnode_free(payloadNode); return NULL; } @@ -249,7 +249,7 @@ session->psm = msn_build_psm(statusline_stripped, media, NULL); payload = session->psm; - purple_debug_misc("MSNP14","Sending UUX command with payload: %s\n",payload); + 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);
--- a/libpurple/protocols/msn/switchboard.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/switchboard.c Thu Nov 20 21:13:56 2008 +0000 @@ -338,7 +338,7 @@ { g_return_if_fail(swboard != NULL); - purple_debug_warning("msg", "Error: Unable to call the user %s for reason %i\n", + purple_debug_warning("msn", "Error: Unable to call the user %s for reason %i\n", passport ? passport : "(null)", reason); /* TODO: if current_users > 0, this is probably a chat and an invite failed, @@ -540,7 +540,7 @@ payload = msn_message_gen_payload(msg, &payload_len); #ifdef MSN_DEBUG_SB - purple_debug_info("MSNP14","SB length:{%d}",payload_len); + purple_debug_info("msn", "SB length:{%d}", payload_len); msn_message_show_readable(msg, "SB SEND", FALSE); #endif @@ -628,7 +628,7 @@ g_return_if_fail(swboard != NULL); g_return_if_fail(msg != NULL); - purple_debug_info("MSNP14","switchboard send msg..\n"); + purple_debug_info("msn", "switchboard send msg..\n"); if (msn_switchboard_can_send(swboard)) release_msg(swboard, msg); else if (queue) @@ -662,7 +662,7 @@ g_return_if_fail(swboard != NULL); if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL)) - purple_debug_error("msn_switchboard", "bye_cmd: helper bug\n"); + purple_debug_error("msn", "bye_cmd: helper bug\n"); if (swboard->conv == NULL) { @@ -752,15 +752,15 @@ static void msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - cmdproc->servconn->payload_len = atoi(cmd->params[2]); + cmd->payload_len = atoi(cmd->params[2]); cmdproc->last_cmd->payload_cb = msg_cmd_post; } static void ubm_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) { - purple_debug_misc("MSNP14","get UBM...\n"); - cmdproc->servconn->payload_len = atoi(cmd->params[4]); + purple_debug_misc("msn", "get UBM...\n"); + cmd->payload_len = atoi(cmd->params[4]); cmdproc->last_cmd->payload_cb = msg_cmd_post; } @@ -960,17 +960,46 @@ } static void -nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg) +datacast_msg(MsnCmdProc *cmdproc, MsnMessage *msg) { - MsnSwitchBoard *swboard; - PurpleAccount *account; - const char *user; + GHashTable *body; + const char *id; + body = msn_message_get_hashtable_from_body(msg); + + id = g_hash_table_lookup(body, "ID"); + + if (!strcmp(id, "1")) { + /* Nudge */ + MsnSwitchBoard *swboard; + PurpleAccount *account; + const char *user; + + swboard = cmdproc->data; + account = cmdproc->session->account; + user = msg->remote_user; - swboard = cmdproc->data; - account = cmdproc->session->account; - user = msg->remote_user; + if (swboard->current_users > 1 || + ((swboard->conv != NULL) && + purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) + purple_prpl_got_attention_in_chat(account->gc, swboard->chat_id, user, MSN_NUDGE); + + else + purple_prpl_got_attention(account->gc, user, MSN_NUDGE); + + } else if (!strcmp(id, "2")) { + /* Wink */ - serv_got_attention(account->gc, user, MSN_NUDGE); + } else if (!strcmp(id, "3")) { + /* Voiceclip */ + + } else if (!strcmp(id, "4")) { + /* Action */ + + } else { + purple_debug_warning("msn", "Got unknown datacast with ID %s.\n", id); + } + + g_hash_table_destroy(body); } /************************************************************************** @@ -1059,7 +1088,7 @@ msn_servconn_set_connect_cb(swboard->servconn, connect_cb); msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb); - return msn_servconn_connect(swboard->servconn, host, port); + return msn_servconn_connect(swboard->servconn, host, port, FALSE); } void @@ -1114,18 +1143,13 @@ } purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error); - purple_debug_warning("msn", "Will Use Offline Message to sendit\n"); - -// cal_error_helper(trans, reason); - /*offline Message send Process*/ while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL){ - purple_debug_warning("MSNP14","offline msg to send:{%s}\n",msg->body); + purple_debug_warning("msn", "Unable to send msg: {%s}\n", msg->body); /* The messages could not be sent due to a switchboard error */ swboard->error = MSN_SB_ERROR_USER_OFFLINE; msg_error_helper(swboard->cmdproc, msg, MSN_MSG_ERROR_SB); - msn_message_unref(msg); } cal_error_helper(trans, reason); } @@ -1170,7 +1194,7 @@ /* The conversation window was closed. */ return; - purple_debug_info("MSNP14","Switchboard:auth:{%s} socket:{%s}\n",cmd->params[4],cmd->params[2]); + purple_debug_info("msn", "Switchboard:auth:{%s} socket:{%s}\n", cmd->params[4], cmd->params[2]); msn_switchboard_set_auth_key(swboard, cmd->params[4]); msn_parse_socket(cmd->params[2], &host, &port); @@ -1313,7 +1337,7 @@ msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon", msn_emoticon_msg); msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", - nudge_msg); + datacast_msg); #if 0 msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite", msn_invite_msg);
--- a/libpurple/protocols/msn/sync.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/sync.c Thu Nov 20 21:13:56 2008 +0000 @@ -216,8 +216,6 @@ void msn_sync_init(void) { - /* TODO: check prp, blp, bpr */ - cbs_table = msn_table_new(); /* Syncing */
--- a/libpurple/protocols/msn/user.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/user.c Thu Nov 20 21:13:56 2008 +0000 @@ -28,7 +28,7 @@ /*new a user object*/ MsnUser * msn_user_new(MsnUserList *userlist, const char *passport, - const char *store_name) + const char *friendly_name) { MsnUser *user; @@ -37,16 +37,7 @@ user->userlist = userlist; msn_user_set_passport(user, passport); - msn_user_set_store_name(user, store_name); - - /* - * XXX This seems to reset the friendly name from what it should be - * to the passport when moving users. So, screw it :) - */ -#if 0 - if (name != NULL) - msn_user_set_name(user, name); -#endif + msn_user_set_friendly_name(user, friendly_name); return user; } @@ -75,7 +66,6 @@ g_free(user->passport); g_free(user->friendly_name); - g_free(user->store_name); g_free(user->uid); g_free(user->phone.home); g_free(user->phone.work); @@ -92,37 +82,36 @@ msn_user_update(MsnUser *user) { PurpleAccount *account; + gboolean offline; account = user->userlist->session->account; - if (user->status != NULL) { - gboolean offline = (strcmp(user->status, "offline") == 0); + offline = (user->status == NULL); - if (!offline) { - purple_prpl_got_user_status(account, user->passport, user->status, - "message", user->statusline, NULL); + if (!offline) { + purple_prpl_got_user_status(account, user->passport, user->status, + "message", user->statusline, NULL); + } else { + if (user->mobile) { + purple_prpl_got_user_status(account, user->passport, "mobile", NULL); + purple_prpl_got_user_status(account, user->passport, "available", NULL); } else { - if (user->mobile) { - purple_prpl_got_user_status(account, user->passport, "mobile", NULL); - purple_prpl_got_user_status(account, user->passport, "available", NULL); - } else { - purple_prpl_got_user_status(account, user->passport, user->status, NULL); - } + purple_prpl_got_user_status(account, user->passport, "offline", NULL); } + } - if (!offline || !user->mobile) { - purple_prpl_got_user_status_deactive(account, user->passport, "mobile"); - } + if (!offline || !user->mobile) { + purple_prpl_got_user_status_deactive(account, user->passport, "mobile"); + } - if (!offline && user->media.title) { - purple_prpl_got_user_status(account, user->passport, "tune", - PURPLE_TUNE_ARTIST, user->media.artist, - PURPLE_TUNE_ALBUM, user->media.album, - PURPLE_TUNE_TITLE, user->media.title, - NULL); - } else { - purple_prpl_got_user_status_deactive(account, user->passport, "tune"); - } + if (!offline && user->media.title) { + purple_prpl_got_user_status(account, user->passport, "tune", + PURPLE_TUNE_ARTIST, user->media.artist, + PURPLE_TUNE_ALBUM, user->media.album, + PURPLE_TUNE_TITLE, user->media.title, + NULL); + } else { + purple_prpl_got_user_status_deactive(account, user->passport, "tune"); } if (user->idle) @@ -136,6 +125,11 @@ { const char *status; + if (state == NULL) { + user->status = NULL; + return; + } + if (!g_ascii_strcasecmp(state, "BSY")) status = "busy"; else if (!g_ascii_strcasecmp(state, "BRB")) @@ -199,18 +193,6 @@ } void -msn_user_set_store_name(MsnUser *user, const char *name) -{ - g_return_if_fail(user != NULL); - - if (name != NULL) - { - g_free(user->store_name); - user->store_name = g_strdup(name); - } -} - -void msn_user_set_uid(MsnUser *user, const char *uid) { g_return_if_fail(user != NULL); @@ -220,14 +202,6 @@ } void -msn_user_set_type(MsnUser *user, MsnUserType type) -{ - g_return_if_fail(user != NULL); - - user->type = type; -} - -void msn_user_set_op(MsnUser *user, int list_op) { g_return_if_fail(user != NULL); @@ -261,21 +235,19 @@ /*add group id to User object*/ void -msn_user_add_group_id(MsnUser *user, const char* id) +msn_user_add_group_id(MsnUser *user, const char* group_id) { MsnUserList *userlist; PurpleAccount *account; PurpleBuddy *b; PurpleGroup *g; const char *passport; - char *group_id; const char *group_name; g_return_if_fail(user != NULL); - g_return_if_fail(id != NULL); + g_return_if_fail(group_id != NULL); - group_id = g_strdup(id); - user->group_ids = g_list_append(user->group_ids, group_id); + user->group_ids = g_list_append(user->group_ids, g_strdup(group_id)); userlist = user->userlist; account = userlist->session->account; @@ -283,11 +255,11 @@ group_name = msn_userlist_find_group_name(userlist, group_id); - purple_debug_info("User","group id:%s,name:%s,user:%s\n", group_id, group_name, passport); + purple_debug_info("msn", "User: group id:%s,name:%s,user:%s\n", group_id, group_name, passport); g = purple_find_group(group_name); - if ((id == NULL) && (g == NULL)) + if ((group_id == NULL) && (g == NULL)) { g = purple_group_new(group_name); purple_blist_add_group(g, NULL); @@ -325,12 +297,9 @@ if (gc != NULL) session = gc->proto_data; - if ((session != NULL) && (session->protocol_ver == WLM_PROT_VER)) - return FALSE; - if ((session != NULL) && (user = msn_userlist_find_user(session->userlist, name)) != NULL) { - return (user->type == MSN_USER_TYPE_YAHOO); + return (user->networkid == MSN_NETWORK_YAHOO); } return (strstr(name,"@yahoo.") != NULL); } @@ -380,6 +349,22 @@ } void +msn_user_set_clientid(MsnUser *user, guint clientid) +{ + g_return_if_fail(user != NULL); + + user->clientid = clientid; +} + +void +msn_user_set_network(MsnUser *user, MsnNetwork network) +{ + g_return_if_fail(user != NULL); + + user->networkid = network; +} + +void msn_user_set_object(MsnUser *user, MsnObject *obj) { g_return_if_fail(user != NULL); @@ -422,14 +407,6 @@ } const char * -msn_user_get_store_name(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->store_name; -} - -const char * msn_user_get_home_phone(const MsnUser *user) { g_return_val_if_fail(user != NULL, NULL); @@ -453,6 +430,14 @@ return user->phone.mobile; } +guint +msn_user_get_clientid(const MsnUser *user) +{ + g_return_val_if_fail(user != NULL, 0); + + return user->clientid; +} + MsnObject * msn_user_get_object(const MsnUser *user) {
--- a/libpurple/protocols/msn/user.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/user.h Thu Nov 20 21:13:56 2008 +0000 @@ -33,14 +33,14 @@ typedef enum { - MSN_USER_TYPE_UNKNOWN = 0x00, - MSN_USER_TYPE_PASSPORT = 0x01, - MSN_USER_TYPE_UNKNOWN1 = 0x02, - MSN_USER_TYPE_MOBILE = 0x04, - MSN_USER_TYPE_UNKNOWN2 = 0x08, - MSN_USER_TYPE_UNKNOWN3 = 0x10, - MSN_USER_TYPE_YAHOO = 0x20 -} MsnUserType; + MSN_NETWORK_UNKNOWN = 0x00, + MSN_NETWORK_PASSPORT = 0x01, + MSN_NETWORK_COMMUNICATOR = 0x02, + MSN_NETWORK_MOBILE = 0x04, + MSN_NETWORK_MNI = 0x08, + MSN_NETWORK_SMTP = 0x10, + MSN_NETWORK_YAHOO = 0x20 +} MsnNetwork; /** * Current media. @@ -60,7 +60,6 @@ MsnUserList *userlist; char *passport; /**< The passport account. */ - char *store_name; /**< The name stored in the server. */ char *friendly_name; /**< The friendly name. */ char * uid; /*< User Id */ @@ -88,7 +87,9 @@ GHashTable *clientcaps; /**< The client's capabilities. */ - MsnUserType type; /**< The user type */ + guint clientid; /**< The client's ID */ + + MsnNetwork networkid; /**< The user's network */ int list_op; /**< Which lists the user is in */ @@ -96,9 +97,9 @@ indexed by the list it belongs to */ }; -/**************************************************************************/ -/** @name User API */ -/**************************************************************************/ +/************************************************************************** + ** @name User API * + **************************************************************************/ /*@{*/ /** @@ -111,7 +112,7 @@ * @return A new user structure. */ MsnUser *msn_user_new(MsnUserList *userlist, const char *passport, - const char *store_name); + const char *friendly_name); /** * Destroys a user structure. @@ -171,14 +172,6 @@ void msn_user_set_friendly_name(MsnUser *user, const char *name); /** - * Sets the store name for a user. - * - * @param user The user. - * @param name The store name. - */ -void msn_user_set_store_name(MsnUser *user, const char *name); - -/** * Sets the buddy icon for a local user. * * @param user The user. @@ -227,7 +220,22 @@ void msn_user_set_work_phone(MsnUser *user, const char *number); void msn_user_set_uid(MsnUser *user, const char *uid); -void msn_user_set_type(MsnUser *user, MsnUserType type); + +/** + * Sets the client id for a user. + * + * @param user The user. + * @param clientid The client id. + */ +void msn_user_set_clientid(MsnUser *user, guint clientid); + +/** + * Sets the network id for a user. + * + * @param user The user. + * @param network The network id. + */ +void msn_user_set_network(MsnUser *user, MsnNetwork network); /** * Sets the mobile phone number for a user. @@ -273,15 +281,6 @@ const char *msn_user_get_friendly_name(const MsnUser *user); /** - * Returns the store name for a user. - * - * @param user The user. - * - * @return The store name. - */ -const char *msn_user_get_store_name(const MsnUser *user); - -/** * Returns the home phone number for a user. * * @param user The user. @@ -309,6 +308,24 @@ const char *msn_user_get_mobile_phone(const MsnUser *user); /** + * Returns the client id for a user. + * + * @param user The user. + * + * @return The user's client id. + */ +guint msn_user_get_clientid(const MsnUser *user); + +/** + * Returns the network id for a user. + * + * @param user The user. + * + * @return The user's network id. + */ +MsnNetwork msn_user_get_network(const MsnUser *user); + +/** * Returns the MSNObject for a user. * * @param user The user.
--- a/libpurple/protocols/msn/userlist.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msn/userlist.c Thu Nov 20 21:13:56 2008 +0000 @@ -24,6 +24,8 @@ #include "msn.h" #include "userlist.h" +#include "contact.h" + const char *lists[] = { "FL", "AL", "BL", "RL" }; typedef struct @@ -42,7 +44,7 @@ { MsnPermitAdd *pa = data; - purple_debug_misc("MSN Userlist", "Accepted the new buddy: %s\n", pa->who); + purple_debug_misc("msn", "Accepted the new buddy: %s\n", pa->who); if (PURPLE_CONNECTION_IS_VALID(pa->gc)) { @@ -51,7 +53,7 @@ msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_AL); - msn_del_contact_from_list(session->contact, NULL, pa->who, MSN_LIST_PL); + msn_del_contact_from_list(session, NULL, pa->who, MSN_LIST_PL); } g_free(pa->who); @@ -64,7 +66,7 @@ { MsnPermitAdd *pa = data; - purple_debug_misc("MSN Userlist", "Denied the new buddy: %s\n", pa->who); + purple_debug_misc("msn", "Denied the new buddy: %s\n", pa->who); if (PURPLE_CONNECTION_IS_VALID(pa->gc)) { @@ -75,7 +77,7 @@ msn_callback_state_set_action(state, MSN_DENIED_BUDDY); msn_userlist_add_buddy_to_list(userlist, pa->who, MSN_LIST_BL); - msn_del_contact_from_list(session->contact, state, pa->who, MSN_LIST_PL); + msn_del_contact_from_list(session, state, pa->who, MSN_LIST_PL); } g_free(pa->who); @@ -136,36 +138,6 @@ return FALSE; } -#if 0 -static const char* -get_store_name(MsnUser *user) -{ - const char *store_name; - - g_return_val_if_fail(user != NULL, NULL); - - store_name = msn_user_get_store_name(user); - - if (store_name != NULL) - store_name = purple_url_encode(store_name); - else - store_name = msn_user_get_passport(user); - - /* this might be a bit of a hack, but it should prevent notification server - * disconnections for people who have buddies with insane friendly names - * who added you to their buddy list from being disconnected. Stu. */ - /* Shx: What? Isn't the store_name obtained from the server, and hence it's - * below the BUDDY_ALIAS_MAXLEN ? */ - /* Stu: yeah, that's why it's a bit of a hack, as you pointed out, we're - * probably decoding the incoming store_name wrong, or something. bleh. */ - - if (strlen(store_name) > BUDDY_ALIAS_MAXLEN) - store_name = msn_user_get_passport(user); - - return store_name; -} -#endif - /************************************************************************** * Server functions **************************************************************************/ @@ -194,7 +166,7 @@ const char *passport; const char *friendly; - purple_debug_info("MSNP14","got add user...\n"); + purple_debug_info("msn", "got add user...\n"); account = session->account; passport = msn_user_get_passport(user); @@ -254,7 +226,7 @@ * looked at this. Maybe we should use the store * name instead? --KingAnt */ -// got_new_entry(gc, passport, friendly); +/* got_new_entry(gc, passport, friendly); */ } } @@ -340,7 +312,7 @@ gc = purple_account_get_connection(account); passport = msn_user_get_passport(user); - store = msn_user_get_store_name(user); + store = msn_user_get_friendly_name(user); msn_user_set_op(user, list_op); @@ -384,7 +356,7 @@ if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP))) { -// got_new_entry(gc, passport, store); +/* got_new_entry(gc, passport, store); */ } } @@ -454,7 +426,7 @@ user = msn_user_new(userlist, passport, userName); msn_userlist_add_user(userlist, user); } else { - msn_user_set_store_name(user, userName); + msn_user_set_friendly_name(user, userName); } return user; } @@ -482,11 +454,9 @@ { MsnUser *user = (MsnUser *)l->data; -// purple_debug_info("MsnUserList","user passport:%s,passport:%s\n",user->passport,passport); g_return_val_if_fail(user->passport != NULL, NULL); if (!g_strcasecmp(passport, user->passport)){ -// purple_debug_info("MsnUserList","return:%p\n",user); return user; } } @@ -647,7 +617,6 @@ g_return_if_fail(userlist != NULL); g_return_if_fail(userlist->session != NULL); - g_return_if_fail(userlist->session->contact != NULL); g_return_if_fail(who != NULL); user = msn_userlist_find_user(userlist, who); @@ -656,7 +625,7 @@ /* delete the contact from address book via soap action */ if (user != NULL) { - msn_delete_contact(userlist->session->contact, user->uid); + msn_delete_contact(userlist->session, user->uid); } } @@ -674,7 +643,7 @@ if ( !msn_userlist_user_is_in_list(user, list_id)) { list = lists[list_id]; - purple_debug_info("MSN Userlist", "User %s is not in list %s, not removing.\n", who, list); + purple_debug_info("msn", "User %s is not in list %s, not removing.\n", who, list); return; } @@ -693,16 +662,10 @@ new_group_name = group_name == NULL ? MSN_INDIVIDUALS_GROUP_NAME : group_name; - g_return_if_fail(userlist != NULL); g_return_if_fail(userlist->session != NULL); - - purple_debug_info("MSN Userlist", "Add user: %s to group: %s\n", who, new_group_name); - - state = msn_callback_state_new(userlist->session); - msn_callback_state_set_who(state, who); - msn_callback_state_set_new_group_name(state, new_group_name); + purple_debug_info("msn", "Add user: %s to group: %s\n", who, new_group_name); if (!purple_email_is_valid(who)) { @@ -719,12 +682,16 @@ return; } + state = msn_callback_state_new(userlist->session); + msn_callback_state_set_who(state, who); + msn_callback_state_set_new_group_name(state, new_group_name); + group_id = msn_userlist_find_group_id(userlist, new_group_name); if (group_id == NULL) { /* Whoa, we must add that group first. */ - purple_debug_info("MSN Userlist", "Adding user %s to a new group, creating group %s first\n", who, new_group_name); + purple_debug_info("msn", "Adding user %s to a new group, creating group %s first\n", who, new_group_name); msn_callback_state_set_action(state, MSN_ADD_BUDDY); @@ -742,23 +709,24 @@ if ( msn_userlist_user_is_in_list(user, MSN_LIST_FL) ) { - purple_debug_info("MSN Userlist", "User %s already exists\n", who); + 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)) { - purple_debug_info("MSN Userlist", "User %s is already in group %s, returning\n", who, new_group_name); + purple_debug_info("msn", "User %s is already in group %s, returning\n", who, new_group_name); + msn_callback_state_free(state); return; } } - purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id); + purple_debug_info("msn", "Adding user: %s to group id: %s\n", who, group_id); msn_callback_state_set_action(state, MSN_ADD_BUDDY); /* Add contact in the Contact server with a SOAP request and if successful, send ADL with MSN_LIST_AL and MSN_LIST_FL and a FQY */ - msn_add_contact_to_group(userlist->session->contact, state, who, group_id); + msn_add_contact_to_group(userlist->session, state, who, group_id); } void @@ -777,14 +745,10 @@ if (msn_userlist_user_is_in_list(user, list_id)) { list = lists[list_id]; - purple_debug_info("MSN Userlist", "User '%s' is already in list: %s\n", who, list); + purple_debug_info("msn", "User '%s' is already in list: %s\n", who, list); return; } - //store_name = (user != NULL) ? get_store_name(user) : who; - - //purple_debug_info("MSN Userlist", "store_name = %s\n", store_name); - /* XXX: see XXX above, this should really be done when we get the response from the server */ @@ -804,15 +768,15 @@ g_return_val_if_fail(group_name != NULL, FALSE); g_return_val_if_fail(who != NULL, FALSE); - purple_debug_info("MSN Userlist","Adding buddy with passport %s to group %s\n", who, group_name); + purple_debug_info("msn", "Adding buddy with passport %s to group %s\n", who, group_name); if ( (group_id = (gchar *)msn_userlist_find_group_id(userlist, group_name)) == NULL) { - purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name); + purple_debug_error("msn", "Group %s has no guid!\n", group_name); return FALSE; } if ( (user = msn_userlist_find_user(userlist, who)) == NULL) { - purple_debug_error("MSN Userlist", "User %s not found!", who); + purple_debug_error("msn", "User %s not found!", who); return FALSE; } @@ -833,15 +797,15 @@ g_return_val_if_fail(group_name != NULL, FALSE); g_return_val_if_fail(who != NULL, FALSE); - purple_debug_info("MSN Userlist","Removing buddy with passport %s from group %s\n", who, group_name); + purple_debug_info("msn", "Removing buddy with passport %s from group %s\n", who, group_name); if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) { - purple_debug_error("MSN Userlist", "Group %s has no guid!\n", group_name); + purple_debug_error("msn", "Group %s has no guid!\n", group_name); return FALSE; } if ( (user = msn_userlist_find_user(userlist, who)) == NULL) { - purple_debug_error("MSN Userlist", "User %s not found!", who); + purple_debug_error("msn", "User %s not found!", who); return FALSE; } @@ -859,7 +823,6 @@ g_return_if_fail(userlist != NULL); g_return_if_fail(userlist->session != NULL); - g_return_if_fail(userlist->session->contact != NULL); state = msn_callback_state_new(userlist->session); msn_callback_state_set_who(state, who); @@ -878,7 +841,7 @@ /* add the contact to the new group, and remove it from the old one in * the callback */ - msn_add_contact_to_group(userlist->session->contact, state, who, new_group_id); + msn_add_contact_to_group(userlist->session, state, who, new_group_id); } /*load userlist from the Blist file cache*/
--- a/libpurple/protocols/msnp9/command.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msnp9/command.c Thu Nov 20 21:13:56 2008 +0000 @@ -65,9 +65,12 @@ for (c = 0; cmd->params[c]; c++); cmd->param_count = c; - param = cmd->params[0]; - - cmd->trId = is_num(param) ? atoi(param) : 0; + if (cmd->param_count) { + char *param = cmd->params[0]; + cmd->trId = is_num(param) ? atoi(param) : 0; + } else { + cmd->trId = 0; + } } else cmd->trId = 0;
--- a/libpurple/protocols/msnp9/servconn.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msnp9/servconn.c Thu Nov 20 21:13:56 2008 +0000 @@ -251,6 +251,12 @@ { g_return_if_fail(servconn != NULL); + if (servconn->connect_data != NULL) + { + purple_proxy_connect_cancel(servconn->connect_data); + servconn->connect_data = NULL; + } + if (!servconn->connected) { /* We could not connect. */ @@ -269,12 +275,6 @@ return; } - if (servconn->connect_data != NULL) - { - purple_proxy_connect_cancel(servconn->connect_data); - servconn->connect_data = NULL; - } - if (servconn->inpa > 0) { purple_input_remove(servconn->inpa);
--- a/libpurple/protocols/msnp9/slplink.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/msnp9/slplink.c Thu Nov 20 21:13:56 2008 +0000 @@ -597,7 +597,7 @@ } else if (slpmsg->size) { - if ((offset + len) > slpmsg->size) + if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size) { purple_debug_error("msn", "Oversized slpmsg\n"); g_return_if_reached();
--- a/libpurple/protocols/myspace/user.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/myspace/user.c Thu Nov 20 21:13:56 2008 +0000 @@ -635,7 +635,7 @@ if (!body) { purple_debug_info("msim_username_is_available_cb", "No body for %s?!\n", username); purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - "An error occured while trying to set the username.\n" + "An error occurred while trying to set the username.\n" "Please try again, or visit http://editprofile.myspace.com/index.cfm?" "fuseaction=profile.username to set your username."); return; @@ -778,7 +778,7 @@ uid = msim_msg_get_integer(userinfo, "uid"); lid = msim_msg_get_integer(userinfo, "lid"); body = msim_msg_get_dictionary(userinfo, "body"); - errmsg = g_strdup("An error occured while trying to set the username.\n" + errmsg = g_strdup("An error occurred while trying to set the username.\n" "Please try again, or visit http://editprofile.myspace.com/index.cfm?" "fuseaction=profile.username to set your username.");
--- a/libpurple/protocols/oscar/family_auth.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/family_auth.c Thu Nov 20 21:13:56 2008 +0000 @@ -81,12 +81,9 @@ static int aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest) { - PurpleCipher *cipher; PurpleCipherContext *context; - cipher = purple_ciphers_find_cipher("md5"); - - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, (const guchar *)key, strlen(key)); purple_cipher_context_append(context, (const guchar *)password, password_len); purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
--- a/libpurple/protocols/oscar/family_icq.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/family_icq.c Thu Nov 20 21:13:56 2008 +0000 @@ -36,6 +36,8 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) return -EINVAL; + purple_debug_info("oscar", "Requesting offline messages from %s", od->sn); + bslen = 2 + 4 + 2 + 2; byte_stream_new(&bs, 4 + bslen); @@ -68,6 +70,8 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) return -EINVAL; + purple_debug_info("oscar", "Acknowledged receipt of offline messages from %s", od->sn); + bslen = 2 + 4 + 2 + 2; byte_stream_new(&bs, 4 + bslen); @@ -214,7 +218,7 @@ byte_stream_putle16(&bs, 0x04b2); /* shrug. */ byte_stream_putle32(&bs, atoi(uin)); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs); + flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE); byte_stream_destroy(&bs); @@ -242,6 +246,8 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ))) return -EINVAL; + purple_debug_info("oscar", "Requesting ICQ alias for %s", uin); + bslen = 2 + 4 + 2 + 2 + 2 + 4; byte_stream_new(&bs, 4 + bslen); @@ -259,7 +265,7 @@ byte_stream_putle16(&bs, 0x04ba); /* shrug. */ byte_stream_putle32(&bs, atoi(uin)); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs); + flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE); byte_stream_destroy(&bs); @@ -303,7 +309,7 @@ byte_stream_putle16(&bs, 0x051f); /* shrug. */ byte_stream_putle32(&bs, atoi(uin)); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs); + flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE); byte_stream_destroy(&bs); @@ -497,7 +503,7 @@ byte_stream_put16(&bs, strlen(uin)); byte_stream_putstr(&bs, uin); - flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x000, snacid, &bs); + flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x000, snacid, &bs, FALSE); byte_stream_destroy(&bs); @@ -877,7 +883,7 @@ info->next = od->icq_info; od->icq_info = info; - flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs); + flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, FALSE); byte_stream_destroy(&bs); }
--- a/libpurple/protocols/oscar/family_locate.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/family_locate.c Thu Nov 20 21:13:56 2008 +0000 @@ -53,10 +53,9 @@ * These are in ascending numerical order. */ - /* - * Perhaps better called OSCAR_CAPABILITY_SHORTCAPS - */ - {OSCAR_CAPABILITY_ICHAT, + /* Client understands short caps, a UUID of the form + * 0946XXYY-4C7F-11D1-8222-444553540000 where XXYY is the short cap. */ + {OSCAR_CAPABILITY_SHORTCAPS, {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, @@ -64,11 +63,16 @@ {0x09, 0x46, 0x00, 0x01, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + /* OSCAR_CAPABILITY_XHTML_IM */ + {OSCAR_CAPABILITY_GENERICUNKNOWN, + {0x09, 0x46, 0x00, 0x02, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + {OSCAR_CAPABILITY_VIDEO, {0x09, 0x46, 0x01, 0x00, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - /* "Live Video" support in Windows AIM 5.5.3501 and newer */ + /* "Live Video" (SIP/RTC Video) support in Windows AIM 5.5.3501 and newer */ {OSCAR_CAPABILITY_LIVEVIDEO, {0x09, 0x46, 0x01, 0x01, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, @@ -78,22 +82,38 @@ {0x09, 0x46, 0x01, 0x02, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, - /* In Windows AIM 5.5.3501 and newer */ + /* "Microphone" support in Windows AIM 5.5.3501 and newer */ + /* OSCAR_CAPABILITY_MICROPHONE */ {OSCAR_CAPABILITY_GENERICUNKNOWN, {0x09, 0x46, 0x01, 0x03, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + /* Supports RTC Audio */ + /* OSCAR_CAPABILITY_RTCAUDIO */ + {OSCAR_CAPABILITY_GENERICUNKNOWN, + {0x09, 0x46, 0x01, 0x04, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + /* In iChatAV (version numbers...?) */ {OSCAR_CAPABILITY_ICHATAV, {0x09, 0x46, 0x01, 0x05, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x45, 0x53, 0x54, 0x00}}, - /* - * Not really sure about this one. In an email from - * 26 Sep 2003, Matthew Sachs suggested that, "this - * is probably the capability for the SMS features." - */ - {OSCAR_CAPABILITY_SMS, + /* Supports "new status message features" (Who advertises this one?) */ + /* OSCAR_CAPABILITY_HOST_STATUS_TEXT_AWARE */ + {OSCAR_CAPABILITY_GENERICUNKNOWN, + {0x09, 0x46, 0x01, 0x0a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* Supports "see as I type" (Who advertises this one?) */ + /* OSCAR_CAPABILITY_SEE_AS_I_TYPE */ + {OSCAR_CAPABILITY_GENERICUNKNOWN, + {0x09, 0x46, 0x01, 0x0b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* Client only asserts caps for services in which it is participating */ + /* OSCAR_CAPABILITY_SMARTCAPS */ + {OSCAR_CAPABILITY_GENERICUNKNOWN, {0x09, 0x46, 0x01, 0xff, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, @@ -1386,7 +1406,7 @@ byte_stream_putstr(&bs, sn); snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, sn, strlen(sn)+1); - flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, snacid, &bs); + flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_LOCATE, 0x0015, 0x0000, snacid, &bs, FALSE); byte_stream_destroy(&bs);
--- a/libpurple/protocols/oscar/family_oservice.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/family_oservice.c Thu Nov 20 21:13:56 2008 +0000 @@ -952,13 +952,10 @@ byte_stream_putraw(&bs, buf, 0x10); } else if (buf && (len > 0)) { /* use input buffer */ - PurpleCipher *cipher; PurpleCipherContext *context; guchar digest[16]; - cipher = purple_ciphers_find_cipher("md5"); - - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, buf, len); purple_cipher_context_digest(context, 16, digest, NULL); purple_cipher_context_destroy(context); @@ -966,7 +963,6 @@ byte_stream_putraw(&bs, digest, 0x10); } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ - PurpleCipher *cipher; PurpleCipherContext *context; guchar digest[16]; guint8 nil = '\0'; @@ -975,9 +971,7 @@ * I'm not sure if we really need the empty append with the * new MD5 functions, so I'll leave it in, just in case. */ - cipher = purple_ciphers_find_cipher("md5"); - - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, &nil, 0); purple_cipher_context_digest(context, 16, digest, NULL); purple_cipher_context_destroy(context);
--- a/libpurple/protocols/oscar/flap_connection.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Thu Nov 20 21:13:56 2008 +0000 @@ -107,21 +107,21 @@ return MIN(((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize, rateclass->max); } -static gboolean flap_connection_send_queued(gpointer data) +/* + * Attempt to send the contents of a given queue + * + * @return TRUE if the queue was completely emptied or was initially + * empty; FALSE if rate limiting prevented it from being + * emptied. + */ +static gboolean flap_connection_send_snac_queue(FlapConnection *conn, struct timeval now, GQueue *queue) { - FlapConnection *conn; - struct timeval now; - - conn = data; - gettimeofday(&now, NULL); - - purple_debug_info("oscar", "Attempting to send %u queued SNACs for %p\n", g_queue_get_length(conn->queued_snacs), conn); - while (!g_queue_is_empty(conn->queued_snacs)) + while (!g_queue_is_empty(queue)) { QueuedSnac *queued_snac; struct rateclass *rateclass; - queued_snac = g_queue_peek_head(conn->queued_snacs); + queued_snac = g_queue_peek_head(queue); rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype); if (rateclass != NULL) @@ -133,7 +133,7 @@ /* (Add 100ms padding to account for inaccuracies in the calculation) */ if (new_current < rateclass->alert + 100) /* Not ready to send this SNAC yet--keep waiting. */ - return TRUE; + return FALSE; rateclass->current = new_current; rateclass->last.tv_sec = now.tv_sec; @@ -142,11 +142,35 @@ flap_connection_send(conn, queued_snac->frame); g_free(queued_snac); - g_queue_pop_head(conn->queued_snacs); + g_queue_pop_head(queue); } - conn->queued_timeout = 0; - return FALSE; + /* We emptied the queue */ + return TRUE; +} + +static gboolean flap_connection_send_queued(gpointer data) +{ + FlapConnection *conn; + struct timeval now; + + conn = data; + gettimeofday(&now, NULL); + + purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n", + (conn->queued_snacs ? g_queue_get_length(conn->queued_snacs) : 0), + (conn->queued_lowpriority_snacs ? g_queue_get_length(conn->queued_lowpriority_snacs) : 0), + conn); + if (!conn->queued_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_snacs)) { + if (!conn->queued_lowpriority_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_lowpriority_snacs)) { + /* Both queues emptied. */ + conn->queued_timeout = 0; + return FALSE; + } + } + + /* We couldn't send all our SNACs. Keep trying */ + return TRUE; } /** @@ -157,9 +181,12 @@ * * @param data The optional bytestream that makes up the data portion * of this SNAC. For empty SNACs this should be NULL. + * @param high_priority If TRUE, the SNAC will be queued normally if + * needed. If FALSE, it wil be queued separately, to be sent + * only if all high priority SNACs have been sent. */ void -flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data) +flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data, gboolean high_priority) { FlapFrame *frame; guint32 length; @@ -213,7 +240,16 @@ queued_snac->family = family; queued_snac->subtype = subtype; queued_snac->frame = frame; - g_queue_push_tail(conn->queued_snacs, queued_snac); + + if (high_priority) { + if (!conn->queued_snacs) + conn->queued_snacs = g_queue_new(); + g_queue_push_tail(conn->queued_snacs, queued_snac); + } else { + if (!conn->queued_lowpriority_snacs) + conn->queued_lowpriority_snacs = g_queue_new(); + g_queue_push_tail(conn->queued_lowpriority_snacs, queued_snac); + } if (conn->queued_timeout == 0) conn->queued_timeout = purple_timeout_add(500, flap_connection_send_queued, conn); @@ -224,6 +260,12 @@ flap_connection_send(conn, frame); } +void +flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data) +{ + flap_connection_send_snac_with_priority(od, conn, family, subtype, flags, snacid, data, TRUE); +} + /** * This sends an empty channel 4 FLAP. This is sent to signify * that we're logging off. This shouldn't really be necessary-- @@ -275,7 +317,6 @@ conn->fd = -1; conn->subtype = -1; conn->type = type; - conn->queued_snacs = g_queue_new(); od->oscar_connections = g_slist_prepend(od->oscar_connections, conn); @@ -299,12 +340,12 @@ conn->connect_data = NULL; } - if (conn->connect_data != NULL) + if (conn->new_conn_data != NULL) { if (conn->type == SNAC_FAMILY_CHAT) { oscar_chat_destroy(conn->new_conn_data); - conn->connect_data = NULL; + conn->new_conn_data = NULL; } } @@ -434,14 +475,28 @@ conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses); } - while (!g_queue_is_empty(conn->queued_snacs)) - { - QueuedSnac *queued_snac; - queued_snac = g_queue_pop_head(conn->queued_snacs); - flap_frame_destroy(queued_snac->frame); - g_free(queued_snac); + if (conn->queued_snacs) { + while (!g_queue_is_empty(conn->queued_snacs)) + { + QueuedSnac *queued_snac; + queued_snac = g_queue_pop_head(conn->queued_snacs); + flap_frame_destroy(queued_snac->frame); + g_free(queued_snac); + } + g_queue_free(conn->queued_snacs); } - g_queue_free(conn->queued_snacs); + + if (conn->queued_lowpriority_snacs) { + while (!g_queue_is_empty(conn->queued_lowpriority_snacs)) + { + QueuedSnac *queued_snac; + queued_snac = g_queue_pop_head(conn->queued_lowpriority_snacs); + flap_frame_destroy(queued_snac->frame); + g_free(queued_snac); + } + g_queue_free(conn->queued_lowpriority_snacs); + } + if (conn->queued_timeout > 0) purple_timeout_remove(conn->queued_timeout); @@ -998,4 +1053,3 @@ sendframe_flap(conn, frame); flap_frame_destroy(frame); } -
--- a/libpurple/protocols/oscar/oscar.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.c Thu Nov 20 21:13:56 2008 +0000 @@ -66,7 +66,9 @@ #define OSCAR_CONNECT_STEPS 6 -static OscarCapability purple_caps = OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM | OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE | OSCAR_CAPABILITY_ICHAT; +static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM | + OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE | + OSCAR_CAPABILITY_SHORTCAPS); static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02}; static guint8 features_icq[] = {0x01, 0x06}; @@ -1510,6 +1512,7 @@ /* Suspended account */ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended.")); break; + case 0x02: case 0x14: /* service temporarily unavailable */ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable.")); @@ -1519,10 +1522,14 @@ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); break; case 0x1c: + { /* client too old */ - g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), PURPLE_WEBSITE); + GHashTable *ui_info = purple_core_get_ui_info(); + g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf); break; + } case 0x1d: /* IP address connecting too frequently */ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); @@ -1640,8 +1647,10 @@ } if (in != '\n') { char buf[256]; + GHashTable *ui_info = purple_core_get_ui_info(); g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. You may want to use TOC until " - "this is fixed. Check %s for updates."), PURPLE_WEBSITE); + "this is fixed. Check %s for updates."), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); purple_notify_warning(pos->gc, NULL, _("Unable to get a valid AIM login hash."), buf); @@ -1684,8 +1693,10 @@ pos->fd = source; if (source < 0) { + GHashTable *ui_info = purple_core_get_ui_info(); buf = g_strdup_printf(_("You may be disconnected shortly. " - "Check %s for updates."), PURPLE_WEBSITE); + "Check %s for updates."), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); purple_notify_warning(pos->gc, NULL, _("Unable to get a valid AIM login hash."), buf); @@ -1781,10 +1792,13 @@ straight_to_hell, pos) == NULL) { char buf[256]; + GHashTable *ui_info = purple_core_get_ui_info(); g_free(pos->modname); g_free(pos); + g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly. " - "Check %s for updates."), PURPLE_WEBSITE); + "Check %s for updates."), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); purple_notify_warning(pos->gc, NULL, _("Unable to get a valid login hash."), buf); @@ -1882,6 +1896,38 @@ return 1; } +static gboolean purple_requesticqstatusnote(gpointer data) +{ + PurpleConnection *gc = data; + OscarData *od = gc->proto_data; + + while (od->statusnotes_queue != NULL) + { + char *sn; + struct aim_ssi_item *ssi_item; + aim_tlv_t *note_hash; + + sn = od->statusnotes_queue->data; + + ssi_item = aim_ssi_itemlist_finditem(od->ssi.local, + NULL, sn, AIM_SSI_TYPE_BUDDY); + if (ssi_item != NULL) + { + note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); + if (note_hash != NULL) { + aim_icq_getstatusnote(od, sn, note_hash->value, note_hash->length); + } + } + + od->statusnotes_queue = g_slist_remove(od->statusnotes_queue, sn); + g_free(sn); + } + + od->statusnotes_queue_timer = 0; + return FALSE; +} + + static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) { PurpleConnection *gc; @@ -1977,7 +2023,17 @@ } else { - purple_prpl_got_user_status(account, info->sn, status_id, NULL); + PurpleBuddy *b = purple_find_buddy(account, info->sn); + PurplePresence *presence = purple_buddy_get_presence(b); + PurpleStatus *old_status = purple_presence_get_active_status(presence); + PurpleStatus *new_status = purple_presence_get_status(presence, status_id); + + /* If our status_id would change with this update, pass it to the core. + * However, if our status_id would not change, do nothing, as we would clear out any existing + * attributes on the status prematurely. purple_got_infoblock() will update the message as needed. + */ + if (old_status != new_status) + purple_prpl_got_user_status(account, info->sn, status_id, NULL); } /* Login time stuff */ @@ -2053,8 +2109,28 @@ if (ssi_item != NULL) { note_hash = aim_tlv_gettlv(ssi_item->data, 0x015c, 1); - if (note_hash != NULL) - aim_icq_getstatusnote(od, info->sn, note_hash->value, note_hash->length); + if (note_hash != NULL) { + /* We do automatic rate limiting at the FLAP level, so + * a flood of requests won't disconnect us. However, + * it WOULD mean that we would have to wait a + * potentially long time to be able to message in real + * time again. Also, since we're requesting with every + * purple_parse_oncoming() call, which often come in + * groups, we should coalesce to do only one lookup per + * buddy. + */ + if (od->statusnotes_queue == NULL || + g_slist_find_custom(od->statusnotes_queue, info->sn, (GCompareFunc)strcmp) == NULL) + { + od->statusnotes_queue = g_slist_prepend(od->statusnotes_queue, + g_strdup(info->sn)); + + if (od->statusnotes_queue_timer > 0) + purple_timeout_remove(od->statusnotes_queue_timer); + od->statusnotes_queue_timer = purple_timeout_add_seconds(3, + purple_requesticqstatusnote, gc); + } + } } } @@ -3061,7 +3137,7 @@ oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE); - if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) { + if ((userinfo->present & AIM_USERINFO_PRESENT_IDLE) && userinfo->idletime != 0) { tmp = purple_str_seconds_to_string(userinfo->idletime*60); oscar_user_info_add_pair(user_info, _("Idle"), tmp); g_free(tmp); @@ -4907,11 +4983,6 @@ purple_debug_info("oscar", "ssi: syncing local list and server list\n"); - if ((timestamp == 0) || (numitems == 0)) { - purple_debug_info("oscar", "Got AIM SSI with a 0 timestamp or 0 numitems--not syncing. This probably means your buddy list is empty.\n"); - return 1; - } - /* Clean the buddy list */ aim_ssi_cleanlist(od); @@ -6414,15 +6485,12 @@ if (img == NULL) { aim_ssi_delicon(od); } else { - PurpleCipher *cipher; PurpleCipherContext *context; guchar md5[16]; gconstpointer data = purple_imgstore_get_data(img); size_t len = purple_imgstore_get_size(img); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); + context = purple_cipher_context_new_by_name("md5", NULL); purple_cipher_context_append(context, data, len); purple_cipher_context_digest(context, 16, md5, NULL); purple_cipher_context_destroy(context);
--- a/libpurple/protocols/oscar/oscar.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/oscar.h Thu Nov 20 21:13:56 2008 +0000 @@ -258,6 +258,15 @@ "us", "en", \ } +#define CLIENTINFO_ICQ6_6_0_6059 { \ + "ICQ Client", \ + 0x010a, \ + 0x0006, 0x0000, \ + 0x0000, 0x17ab, \ + 0x00007535, \ + "us", "en", \ +} + #define CLIENTINFO_ICQBASIC_14_3_1068 { \ "ICQBasic", \ 0x010a, \ @@ -302,9 +311,9 @@ #define CLIENTINFO_PURPLE_ICQ { \ "Purple/" VERSION, \ 0x010a, \ - 0x0014, 0x0034, \ - 0x0000, 0x0bb8, \ - 0x0000043d, \ + 0x0006, 0x0000, \ + 0x0000, 0x17ab, \ + 0x00007535, \ "us", "en", \ } @@ -344,16 +353,16 @@ OSCAR_CAPABILITY_TRILLIANCRYPT = 0x00010000, OSCAR_CAPABILITY_UNICODE = 0x00020000, OSCAR_CAPABILITY_INTEROPERATE = 0x00040000, - OSCAR_CAPABILITY_ICHAT = 0x00080000, + OSCAR_CAPABILITY_SHORTCAPS = 0x00080000, OSCAR_CAPABILITY_HIPTOP = 0x00100000, OSCAR_CAPABILITY_SECUREIM = 0x00200000, OSCAR_CAPABILITY_SMS = 0x00400000, - OSCAR_CAPABILITY_GENERICUNKNOWN = 0x00800000, - OSCAR_CAPABILITY_VIDEO = 0x01000000, - OSCAR_CAPABILITY_ICHATAV = 0x02000000, - OSCAR_CAPABILITY_LIVEVIDEO = 0x04000000, - OSCAR_CAPABILITY_CAMERA = 0x08000000, - OSCAR_CAPABILITY_ICHAT_SCREENSHARE = 0x10000000, + OSCAR_CAPABILITY_VIDEO = 0x00800000, + OSCAR_CAPABILITY_ICHATAV = 0x01000000, + OSCAR_CAPABILITY_LIVEVIDEO = 0x02000000, + OSCAR_CAPABILITY_CAMERA = 0x04000000, + OSCAR_CAPABILITY_ICHAT_SCREENSHARE = 0x08000000, + OSCAR_CAPABILITY_GENERICUNKNOWN = 0x10000000, OSCAR_CAPABILITY_LAST = 0x20000000 } OscarCapability; @@ -424,6 +433,7 @@ GSList *rateclasses; /* Contains nodes of struct rateclass. */ GQueue *queued_snacs; /**< Contains QueuedSnacs. */ + GQueue *queued_lowpriority_snacs; /**< Contains QueuedSnacs to send only once queued_snacs is empty */ guint queued_timeout; void *internal; /* internal conn-specific libfaim data */ @@ -534,6 +544,10 @@ /** A linked list containing PeerConnections. */ GSList *peer_connections; + + /** Queue of ICQ Status Notes to request. */ + GSList *statusnotes_queue; + guint statusnotes_queue_timer; }; /* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ @@ -616,6 +630,7 @@ void flap_connection_send_version(OscarData *od, FlapConnection *conn); void flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy); void flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data); +void flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, guint16 flags, aim_snacid_t snacid, ByteStream *data, gboolean high_priority); void flap_connection_send_keepalive(OscarData *od, FlapConnection *conn); FlapFrame *flap_frame_new(OscarData *od, guint16 channel, int datalen);
--- a/libpurple/protocols/oscar/oscar_data.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/oscar/oscar_data.c Thu Nov 20 21:13:56 2008 +0000 @@ -88,10 +88,17 @@ while (od->requesticon) { - gchar *sn = od->requesticon->data; - od->requesticon = g_slist_remove(od->requesticon, sn); - g_free(sn); + g_free(od->requesticon->data); + od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon); } + while (od->statusnotes_queue) + { + g_free(od->statusnotes_queue->data); + od->statusnotes_queue = g_slist_delete_link(od->statusnotes_queue, + od->statusnotes_queue); + } + if (od->statusnotes_queue_timer > 0) + purple_timeout_remove(od->statusnotes_queue_timer); g_free(od->email); g_free(od->newp); g_free(od->oldp);
--- a/libpurple/protocols/qq/AUTHORS Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/AUTHORS Thu Nov 20 21:13:56 2008 +0000 @@ -9,11 +9,27 @@ rakescar : provided filter for HTML tag yyw : improved performance on PPC linux lvxiang : provided ip to location original code -csyfek : faces markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007 +ccpaging : maintainer since 2007 +icesky : maintainer since 2007 +csyfek : faces, maintainer since 2007 + +Lovely Patch Writers +===== +gnap : message displaying, documentation +manphiz : qun processing +moo : qun processing +Coly Li : qun processing Acknowledgement ===== Shufeng Tan : http://sf.net/projects/perl-oicq Jeff Ye : http://www.sinomac.com Hu Zheng : http://forlinux.yeah.net +yunfan : http://www.myswear.net +khc@pidgin.im +qulogic@pidgin.im +rlaager@pidgin.im +OpenQ Team +LumaQQ Team +OpenQ Google Group
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/ChangeLog Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,125 @@ +2008.08.03 - csyfek <csyfek(at)gmail.com> + * Commit lost files to Pidgin + +2008.08.02 - csyfek <csyfek(at)gmail.com> + * Commit to Pidgin + * Tickets: + Fixes #1861 + Fixes #1902 + References #5112 + +2008.08.02 - ccpaging <ecc_hy(at)hotmail.com> + * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH] + * Use random value in inikey + * TEA header padding in crypt.c + * Rewrite login part of qq_process + +2008.07.31 - ccpaging <ecc_hy(at)hotmail.com> + * Fixed: send reply when get duplicate server command. The server may not get our reply before. + * Tag custom picture as text "(Broken)" + +2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> + * Change some debug message + * Modify buddy status flag according to eva for QQ2006 + * Modify buddy status parse and correspond to eva2 + * Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str + * Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation + * Source file changed: + Merge buddy_status into buddy_list + Change login_logout to qq_base + Merge keep_alive into qq_base + New qq_process extract from qq_network + * Fixed: Byte alignment bug in crypt.c, tested in ARM PDA + * Fixed: group chat message may get in before getting group info, and so group info is empty + * Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c + * Add some new group command identify according eva but further program + * Add some new QQ client version identify + * Fixed: Identify buddy's client version by IM packet, and not by status + * Add some new info in buddy's tooltip text + * Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly + * Use new timeout function to handle send keep_alive, resend packet, update buddy status + * Add new advanced options: + The end user may change interval of keep_alive, resend packet, update buddy status to feed their need. + For example, saving network flow when use mobile phone. + Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not. + The intervals of keep alive and update status should be multiple of resend's interval, + Since we use counter not time() in a single timeout function for efficiency. + * Rewrite qq_trans.c, and use one g_list to manage: + Store server packet before login, and prase all of them when get login + Store client send packet for resend scanning, confirm server reply, filter duplicate server reply + Store server packet for filter out duplicate + * Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c + * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server: + In QQ protocol, one packet reply may need a new packet send later. + We may call it packet trigger. The triggers always is hided in every qq_process_reply. + Now we try to extract those triggers and put into a single function, + and then every trigger should be obviously and easy to manage. + +2008.07.12 - ccpaging <ecc_hy(at)hotmail.com> + * Fixed: Always lost connection. Now send keep alive packet in every 30 seconds + * Minor fix for debug information + * Filter \r\n and replace with SPCAE in group notive + * Fixed a memory leak + * Tickets: + * Fixes #4024. + +2008.06.29 - csyfek <csyfek(at)gmail.com> + * Minor bug fix + * Add some doxygen syntax for preparing development documentation + * References #6199 + +2008.06.28 - ccpaging <ecc_hy(at)hotmail.com>, moo <phpxcache(at)gmail.com> + * Patches from moo<phpxcache@gmail.com> and ccpaging<ccpaging@foxmail.com>. + * Tickets: + * Fixes #4956. + * Fixes #2998. + +2008.06.07 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> + * Clean code and apply patches from QuLogic + +2008.05.19 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> + * Reconnect server 5 time in 5000 ms, when connect failed + * Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h + * Rewrite packet_process + * Rewrite qq_send_cmd + * Create server list, try to connect every server when failed + +2008.05.14 - ccpaging <ecc_hy(at)hotmail.com> + * Move function for before login packets storing to sendqueue + * Use transaction data structure to store before login packets + * Rewrite tcp_pending and packet_process in qq_network.c + +2008.05.09 - ccpaging <ecc_hy(at)hotmail.com> + * Remove function _create_packet_head_seq in qq_network.c + * Create new function encap in qq_netowork.c + * Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c + +2008.05.09 - ccpaging <ecc_hy(at)hotmail.com> + * Clean code of packet_parse.c, enable PARSER_DEBUG + * Rewrite send_queue + +2008.05.08 - ccpaging <ecc_hy(at)hotmail.com> + * Rewrite qq_network + * Add srv resolve function when qq_login + * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect + * Move orignal qq_disconnect to qq_close + * qq_data alloc in qq_open and release in qq_close + * Network connect of QQ is created in qq_connect, and release in qq_disconnect + +2008.05.05 - ccpaging <ecc_hy(at)hotmail.com> + * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect + * Move orignal qq_disconnect to qq_close + * qq_data alloc in qq_open and release in qq_close + * Network connect of QQ is created in qq_connect, and release in qq_disconnect + +2008.05.05 - ccpaging <ecc_hy(at)hotmail.com> + * Add qq_hex_dump function + +2008.04.25 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> + * Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead + * New logic in accord with protocol models to handle packets, some related functions rewritten + +2008.03.24 - ccpaging <ecc_hy(at)hotmail.com> + * Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly + +** since pidgin-2.4.0 ***
--- a/libpurple/protocols/qq/Makefile.am Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.am Thu Nov 20 21:13:56 2008 +0000 @@ -10,8 +10,6 @@ buddy_list.h \ buddy_opt.c \ buddy_opt.h \ - buddy_status.c \ - buddy_status.h \ char_conv.c \ char_conv.h \ crypt.c \ @@ -44,28 +42,22 @@ header_info.h \ im.c \ im.h \ - keep_alive.c \ - keep_alive.h \ - login_logout.c \ - login_logout.h \ + qq_process.c \ + qq_process.h \ + qq_base.c \ + qq_base.h \ packet_parse.c \ packet_parse.h \ qq.c \ qq.h \ - qq_proxy.c \ - qq_proxy.h \ - recv_core.c \ - recv_core.h \ - send_core.c \ - send_core.h \ + qq_network.c \ + qq_network.h \ send_file.c \ send_file.h \ - sendqueue.c \ - sendqueue.h \ + qq_trans.c \ + qq_trans.h \ sys_msg.c \ sys_msg.h \ - udp_proxy_s5.c \ - udp_proxy_s5.h \ utils.c \ utils.h
--- a/libpurple/protocols/qq/Makefile.mingw Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/Makefile.mingw Thu Nov 20 21:13:56 2008 +0000 @@ -59,17 +59,14 @@ group_search.c \ header_info.c \ im.c \ - keep_alive.c \ - login_logout.c \ + qq_process.c \ + qq_base.c \ packet_parse.c \ qq.c \ - qq_proxy.c \ - recv_core.c \ - send_core.c \ + qq_network.c \ send_file.c \ - sendqueue.c \ + qq_trans.c \ sys_msg.c \ - udp_proxy_s5.c \ utils.c OBJECTS = $(C_SRC:%.c=%.o)
--- a/libpurple/protocols/qq/buddy_info.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.c Thu Nov 20 21:13:56 2008 +0000 @@ -29,12 +29,13 @@ #include "utils.h" #include "packet_parse.h" +#include "buddy_list.h" #include "buddy_info.h" #include "char_conv.h" #include "crypt.h" #include "header_info.h" -#include "keep_alive.h" -#include "send_core.h" +#include "qq_base.h" +#include "qq_network.h" #define QQ_PRIMARY_INFORMATION _("Primary Information") #define QQ_ADDITIONAL_INFORMATION _("Additional Information") @@ -85,6 +86,7 @@ }; #define QQ_CONTACT_FIELDS 37 +#define QQ_FACES 100 /* There is no user id stored in the reply packet for information query * we have to manually store the query, so that we know the query source */ @@ -94,6 +96,46 @@ gboolean modify_info; } qq_info_query; +typedef struct _contact_info { + gchar *uid; + gchar *nick; + gchar *country; + gchar *province; + gchar *zipcode; + gchar *address; + gchar *tel; + gchar *age; + gchar *gender; + gchar *name; + gchar *email; + gchar *pager_sn; + gchar *pager_num; + gchar *pager_sp; + gchar *pager_base_num; + gchar *pager_type; + gchar *occupation; + gchar *homepage; + gchar *auth_type; + gchar *unknown1; + gchar *unknown2; + gchar *face; + gchar *hp_num; + gchar *hp_type; + gchar *intro; + gchar *city; + gchar *unknown3; + gchar *unknown4; + gchar *unknown5; + gchar *is_open_hp; + gchar *is_open_contact; + gchar *college; + gchar *horoscope; + gchar *zodiac; + gchar *blood; + gchar *qq_show; + gchar *unknown6; /* always 0x2D */ +} contact_info; + /* We get an info packet on ourselves before we modify our information. * Even though not all of the information is modifiable, it still * all needs to be there when we send out the modify info packet */ @@ -137,7 +179,7 @@ } else { return NULL; } - /* else ASCIIized index */ + /* else ASCIIized index */ } else { if (strcmp(choice[index], "-") != 0) return g_strdup(choice[index]); @@ -161,15 +203,15 @@ if (value != NULL) { purple_notify_user_info_add_pair(user_info, title, value); g_free(value); - + return TRUE; } - + return FALSE; } static PurpleNotifyUserInfo * -info_to_notify_user_info(const contact_info *info) + info_to_notify_user_info(const contact_info *info) { PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); const gchar *intro; @@ -209,25 +251,25 @@ /* for debugging */ /* - g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous"); - append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0); - append_field_value(info_text, info->pager_num, "pager_num", NULL, 0); - append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0); - append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0); - append_field_value(info_text, info->pager_type, "pager_type", NULL, 0); - append_field_value(info_text, info->auth_type, "auth_type", NULL, 0); - append_field_value(info_text, info->unknown1, "unknown1", NULL, 0); - append_field_value(info_text, info->unknown2, "unknown2", NULL, 0); - append_field_value(info_text, info->face, "face", NULL, 0); - append_field_value(info_text, info->hp_type, "hp_type", NULL, 0); - append_field_value(info_text, info->unknown3, "unknown3", NULL, 0); - append_field_value(info_text, info->unknown4, "unknown4", NULL, 0); - append_field_value(info_text, info->unknown5, "unknown5", NULL, 0); - append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0); - append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0); - append_field_value(info_text, info->qq_show, "qq_show", NULL, 0); - append_field_value(info_text, info->unknown6, "unknown6", NULL, 0); - */ + g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous"); + append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0); + append_field_value(info_text, info->pager_num, "pager_num", NULL, 0); + append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0); + append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0); + append_field_value(info_text, info->pager_type, "pager_type", NULL, 0); + append_field_value(info_text, info->auth_type, "auth_type", NULL, 0); + append_field_value(info_text, info->unknown1, "unknown1", NULL, 0); + append_field_value(info_text, info->unknown2, "unknown2", NULL, 0); + append_field_value(info_text, info->face, "face", NULL, 0); + append_field_value(info_text, info->hp_type, "hp_type", NULL, 0); + append_field_value(info_text, info->unknown3, "unknown3", NULL, 0); + append_field_value(info_text, info->unknown4, "unknown4", NULL, 0); + append_field_value(info_text, info->unknown5, "unknown5", NULL, 0); + append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0); + append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0); + append_field_value(info_text, info->qq_show, "qq_show", NULL, 0); + append_field_value(info_text, info->unknown6, "unknown6", NULL, 0); + */ return user_info; } @@ -243,7 +285,7 @@ qd = (qq_data *) gc->proto_data; g_snprintf(uid_str, sizeof(uid_str), "%d", uid); - qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str)); + qq_send_cmd(qd, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str)); query = g_new0(qq_info_query, 1); query->uid = uid; @@ -271,27 +313,141 @@ } /* send packet to modify personal information */ -static void qq_send_packet_modify_info(PurpleConnection *gc, gchar **segments) +static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info) { - gint i; - guint8 *raw_data, *cursor, bar; + qq_data *qd = (qq_data *) gc->proto_data; + gint bytes = 0; + guint8 raw_data[MAX_PACKET_SIZE - 128] = {0}; + guint8 bar; - g_return_if_fail(segments != NULL); + g_return_if_fail(info != NULL); bar = 0x1f; - raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128); - cursor = raw_data; - create_packet_b(raw_data, &cursor, bar); + bytes += qq_put8(raw_data + bytes, bar); /* important! skip the first uid entry */ - for (i = 1; i < QQ_CONTACT_FIELDS; i++) { - create_packet_b(raw_data, &cursor, bar); - create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i])); - } - create_packet_b(raw_data, &cursor, bar); + /* + for (i = 1; i < QQ_CONTACT_FIELDS; i++) { + create_packet_b(raw_data, &cursor, bar); + create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i])); + } + */ + /* uid */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->uid, strlen(info->uid)); + /* nick */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->nick, strlen(info->nick)); + /* country */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->country, strlen(info->country)); + /* province */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->province, strlen(info->province)); + /* zipcode */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zipcode, strlen(info->zipcode)); + /* address */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->address, strlen(info->address)); + /* tel */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->tel, strlen(info->tel)); + /* age */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->age, strlen(info->age)); + /* gender */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->gender, strlen(info->gender)); + /* name */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->name, strlen(info->name)); + /* email */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->email, strlen(info->email)); + /* pager_sn */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sn, strlen(info->pager_sn)); + /* pager_num */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_num, strlen(info->pager_num)); + /* pager_sp */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sp, strlen(info->pager_sp)); + /* pager_base_num */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_base_num, strlen(info->pager_base_num)); + /* pager_type */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_type, strlen(info->pager_type)); + /* occupation */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->occupation, strlen(info->occupation)); + /* homepage */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->homepage, strlen(info->homepage)); + /* auth_type */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->auth_type, strlen(info->auth_type)); + /* unknown1 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown1, strlen(info->unknown1)); + /* unknown2 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown2, strlen(info->unknown2)); + /* face */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->face, strlen(info->face)); + /* hp_num */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_num, strlen(info->hp_num)); + /* hp_type */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_type, strlen(info->hp_type)); + /* intro */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->intro, strlen(info->intro)); + /* city */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->city, strlen(info->city)); + /* unknown3 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown3, strlen(info->unknown3)); + /* unknown4 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown4, strlen(info->unknown4)); + /* unknown5 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown5, strlen(info->unknown5)); + /* is_open_hp */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_hp, strlen(info->is_open_hp)); + /* is_open_contact */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_contact, strlen(info->is_open_contact)); + /* college */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->college, strlen(info->college)); + /* horoscope */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->horoscope, strlen(info->horoscope)); + /* zodiac */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zodiac, strlen(info->zodiac)); + /* blood */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->blood, strlen(info->blood)); + /* qq_show */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->qq_show, strlen(info->qq_show)); + /* unknown6 */ + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown6, strlen(info->unknown6)); - qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data); + bytes += qq_put8(raw_data + bytes, bar); + + qq_send_cmd(qd, QQ_CMD_UPDATE_INFO, raw_data, bytes); } @@ -407,8 +563,11 @@ groups = groups->next; } - /* This casting looks like a horrible idea to me -DAA */ - qq_send_packet_modify_info(gc, (gchar **) info); + /* This casting looks like a horrible idea to me -DAA + * yes, rewritten -s3e + * qq_send_packet_modify_info(gc, (gchar **) info); + */ + qq_send_packet_modify_info(gc, info); g_strfreev((gchar **) mid->info); g_free(mid); @@ -520,11 +679,11 @@ mid->info->unknown6 = g_strdup(info->unknown6); purple_request_fields(gc, _("Modify my information"), - _("Modify my information"), NULL, fields, - _("Update my information"), G_CALLBACK(modify_info_ok_cb), - _("Cancel"), G_CALLBACK(modify_info_cancel_cb), - purple_connection_get_account(gc), NULL, NULL, - mid); + _("Modify my information"), NULL, fields, + _("Update my information"), G_CALLBACK(modify_info_ok_cb), + _("Cancel"), G_CALLBACK(modify_info_cancel_cb), + purple_connection_get_account(gc), NULL, NULL, + mid); } } @@ -578,10 +737,9 @@ gchar *data; gsize len; - if (!g_file_get_contents(iconfile, &data, &len, NULL)) + if (!g_file_get_contents(iconfile, &data, &len, NULL)) { g_return_if_reached(); - else - { + } else { purple_buddy_icons_set_for_user(account, who, data, len, icon_num); } } @@ -608,10 +766,10 @@ /* make sure we're using an appropriate icon */ if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0 - && icon_path[dir_len] == G_DIR_SEPARATOR - && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0 - && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0 - && icon_len <= 3)) { + && icon_path[dir_len] == G_DIR_SEPARATOR + && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0 + && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0 + && icon_len <= 3)) { if (icon_global) purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg); else @@ -650,13 +808,13 @@ old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy); if (old_icon_num == NULL || - strcmp(icon_num_str, old_icon_num)) + strcmp(icon_num_str, old_icon_num)) { gchar *icon_path; icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S, - QQ_ICON_PREFIX, icon_num_str, - QQ_ICON_SUFFIX, NULL); + QQ_ICON_PREFIX, icon_num_str, + QQ_ICON_SUFFIX, NULL); qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path); g_free(icon_path); @@ -665,12 +823,13 @@ } /* after getting info or modify myself, refresh the buddy list accordingly */ -void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc) +static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc) { PurpleBuddy *b; qq_data *qd; qq_buddy *q_bud; - gchar *alias_utf8, *purple_name; + gchar *alias_utf8; + gchar *purple_name; PurpleAccount *account = purple_connection_get_account(gc); qd = (qq_data *) gc->proto_data; @@ -728,7 +887,7 @@ qd->modifying_face = FALSE; g_free(info->face); info->face = icon; - qq_send_packet_modify_info(gc, segments); + qq_send_packet_modify_info(gc, (contact_info *)segments); } qq_refresh_buddy_and_myself(info, gc); @@ -777,41 +936,43 @@ void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid) { - guint8 buf[5]; - guint32 tmp = g_htonl(uid); - buf[0] = 0; - memcpy(buf+1, &tmp, 4); - qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, 5); + qq_data *qd = (qq_data *) gc->proto_data; + guint8 buf[16] = {0}; + gint bytes = 0; + + bytes += qq_put8(buf + bytes, 0x00); + bytes += qq_put32(buf + bytes, uid); + + qd = (qq_data *) gc->proto_data; + qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, bytes); } void qq_send_packet_get_buddies_levels(PurpleConnection *gc) { - guint8 *buf, *tmp; + guint8 *buf; guint16 size; qq_buddy *q_bud; qq_data *qd = (qq_data *) gc->proto_data; GList *node = qd->buddies; - - if (qd->buddies) { - /* server only sends back levels for online buddies, no point - * in asking for anyone else */ - size = 4*g_list_length(qd->buddies) + 1; - buf = g_new0(guint8, size); - tmp = buf + 1; + gint bytes = 0; - while (node != NULL) { - guint32 tmp4; - q_bud = (qq_buddy *) node->data; - if (q_bud != NULL) { - tmp4 = g_htonl(q_bud->uid); - memcpy(tmp, &tmp4, 4); - tmp += 4; - } - node = node->next; + if ( qd->buddies == NULL) { + return; + } + /* server only sends back levels for online buddies, no point + * in asking for anyone else */ + size = 4 * g_list_length(qd->buddies) + 1; + buf = g_newa(guint8, size); + bytes += qq_put8(buf + bytes, 0x00); + + while (NULL != node) { + q_bud = (qq_buddy *) node->data; + if (NULL != q_bud) { + bytes += qq_put32(buf + bytes, q_bud->uid); } - qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size); - g_free(buf); + node = node->next; } + qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size); } void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) @@ -822,10 +983,11 @@ PurpleBuddy *b; qq_buddy *q_bud; gint decr_len, i; - guint8 *decr_buf, *tmp; + guint8 *decr_buf; PurpleAccount *account = purple_connection_get_account(gc); qq_data *qd = (qq_data *) gc->proto_data; - + gint bytes = 0; + decr_len = buf_len; decr_buf = g_new0(guint8, buf_len); if (!qq_decrypt(buf, buf_len, qd->session_key, decr_buf, &decr_len)) { @@ -835,28 +997,23 @@ decr_len--; if (decr_len % 12 != 0) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12); + "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12); decr_len -= (decr_len % 12); } - - tmp = decr_buf + 1; + + bytes += 1; /* this byte seems random */ /* - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]); - */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]); + */ for (i = 0; i < decr_len; i += 12) { - uid = g_ntohl(*(guint32 *) tmp); - tmp += 4; - onlineTime = g_ntohl(*(guint32 *) tmp); - tmp += 4; - level = g_ntohs(*(guint16 *) tmp); - tmp += 2; - timeRemainder = g_ntohs(*(guint16 *) tmp); - tmp += 2; - /* - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", + bytes += qq_get32(&uid, decr_buf + bytes); + bytes += qq_get32(&onlineTime, decr_buf + bytes); + bytes += qq_get16(&level, decr_buf + bytes); + bytes += qq_get16(&timeRemainder, decr_buf + bytes); + purple_debug(PURPLE_DEBUG_INFO, "QQ_LEVEL", + "%d, tmOnline: %d, level: %d, tmRemainder: %d\n", uid, onlineTime, level, timeRemainder); - */ purple_name = uid_to_purple_name(uid); b = purple_find_buddy(account, purple_name); q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; @@ -872,9 +1029,10 @@ } } else { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Got an online buddy %d, but not in my buddy list\n", uid); + "Got an online buddy %d, but not in my buddy list\n", uid); } g_free(purple_name); } g_free(decr_buf); } +
--- a/libpurple/protocols/qq/buddy_info.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/buddy_info.h Thu Nov 20 21:13:56 2008 +0000 @@ -31,11 +31,34 @@ #include "buddy_opt.h" #include "qq.h" -#define QQ_COMM_FLAG_QQ_MEMBER 0x02 -#define QQ_COMM_FLAG_TCP_MODE 0x10 -#define QQ_COMM_FLAG_MOBILE 0x20 -#define QQ_COMM_FLAG_BIND_MOBILE 0x40 -#define QQ_COMM_FLAG_VIDEO 0x80 +/* use is openq2005 + * ext_flag: (0-7) + * bit1 => qq space + * comm_flag: (0-7) + * bit1 => member + * bit4 => TCP mode + * bit5 => open mobile QQ + * bit6 => bind to mobile + * bit7 => whether having a video +#define QQ_COMM_FLAG_QQ_MEMBER 0x02 +#define QQ_COMM_FLAG_TCP_MODE 0x10 +#define QQ_COMM_FLAG_MOBILE 0x20 +#define QQ_COMM_FLAG_BIND_MOBILE 0x40 +#define QQ_COMM_FLAG_VIDEO 0x80 + */ +/* status in eva for qq2006 +#define QQ_FRIEND_FLAG_QQ_MEMBER 0x01 +#define QQ_FRIEND_FLAG_MOBILE 0x10 +#define QQ_FRIEND_FLAG_BIND_MOBILE 0x20 +*/ +#define QQ_COMM_FLAG_QQ_MEMBER 0x02 +#define QQ_COMM_FLAG_QQ_VIP 0x04 +#define QQ_COMM_FLAG_TCP_MODE 0x10 +#define QQ_COMM_FLAG_MOBILE 0x20 +#define QQ_COMM_FLAG_BIND_MOBILE 0x40 +#define QQ_COMM_FLAG_VIDEO 0x80 + +#define QQ_EXT_FLAG_SPACE 0x02 #define QQ_BUDDY_GENDER_GG 0x00 #define QQ_BUDDY_GENDER_MM 0x01 @@ -44,47 +67,6 @@ #define QQ_ICON_PREFIX "qq_" #define QQ_ICON_SUFFIX ".png" -typedef struct _contact_info { - gchar *uid; - gchar *nick; - gchar *country; - gchar *province; - gchar *zipcode; - gchar *address; - gchar *tel; - gchar *age; - gchar *gender; - gchar *name; - gchar *email; - gchar *pager_sn; - gchar *pager_num; - gchar *pager_sp; - gchar *pager_base_num; - gchar *pager_type; - gchar *occupation; - gchar *homepage; - gchar *auth_type; - gchar *unknown1; - gchar *unknown2; - gchar *face; - gchar *hp_num; - gchar *hp_type; - gchar *intro; - gchar *city; - gchar *unknown3; - gchar *unknown4; - gchar *unknown5; - gchar *is_open_hp; - gchar *is_open_contact; - gchar *college; - gchar *horoscope; - gchar *zodiac; - gchar *blood; - gchar *qq_show; - gchar *unknown6; /* always 0x2D */ -} contact_info; - -void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc); void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window); void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img); void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile); @@ -95,5 +77,4 @@ void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid); void qq_send_packet_get_buddies_levels(PurpleConnection *gc); void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); - #endif
--- a/libpurple/protocols/qq/buddy_list.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.c Thu Nov 20 21:13:56 2008 +0000 @@ -32,57 +32,53 @@ #include "packet_parse.h" #include "buddy_info.h" #include "buddy_list.h" -#include "buddy_status.h" #include "buddy_opt.h" #include "char_conv.h" #include "crypt.h" #include "header_info.h" -#include "keep_alive.h" -#include "send_core.h" +#include "qq_base.h" #include "group.h" #include "group_find.h" #include "group_internal.h" #include "group_info.h" -#include "qq_proxy.h" +#include "qq_network.h" #define QQ_GET_ONLINE_BUDDY_02 0x02 #define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */ -#define QQ_ONLINE_BUDDY_ENTRY_LEN 38 - -typedef struct _qq_friends_online_entry { - qq_buddy_status *s; +typedef struct _qq_buddy_online { + qq_buddy_status bs; guint16 unknown1; - guint8 flag1; + guint8 ext_flag; guint8 comm_flag; guint16 unknown2; guint8 ending; /* 0x00 */ -} qq_friends_online_entry; +} qq_buddy_online; /* get a list of online_buddies */ void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position) { qq_data *qd; - guint8 *raw_data, *cursor; + guint8 *raw_data; + gint bytes = 0; qd = (qq_data *) gc->proto_data; raw_data = g_newa(guint8, 5); - cursor = raw_data; /* 000-000 get online friends cmd * only 0x02 and 0x03 returns info from server, other valuse all return 0xff * I can also only send the first byte (0x02, or 0x03) * and the result is the same */ - create_packet_b(raw_data, &cursor, QQ_GET_ONLINE_BUDDY_02); + bytes += qq_put8(raw_data + bytes, QQ_GET_ONLINE_BUDDY_02); /* 001-001 seems it supports 255 online buddies at most */ - create_packet_b(raw_data, &cursor, position); + bytes += qq_put8(raw_data + bytes, position); /* 002-002 */ - create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put8(raw_data + bytes, 0x00); /* 003-004 */ - create_packet_w(raw_data, &cursor, 0x0000); + bytes += qq_put16(raw_data + bytes, 0x0000); - qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_ONLINE, TRUE, 0, TRUE, raw_data, 5); + qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5); qd->last_get_online = time(NULL); } @@ -90,326 +86,605 @@ * server may return a position tag if list is too long for one packet */ void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position) { - guint8 *raw_data, *cursor; - gint data_len; + qq_data *qd = (qq_data *) gc->proto_data; + guint8 raw_data[16] = {0}; + gint bytes = 0; - data_len = 3; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; /* 000-001 starting position, can manually specify */ - create_packet_w(raw_data, &cursor, position); + bytes += qq_put16(raw_data + bytes, position); /* before Mar 18, 2004, any value can work, and we sent 00 * I do not know what data QQ server is expecting, as QQ2003iii 0304 itself * even can sending packets 00 and get no response. * Now I tested that 00,00,00,00,00,01 work perfectly * March 22, found the 00,00,00 starts to work as well */ - create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put8(raw_data + bytes, 0x00); - qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_LIST, TRUE, 0, TRUE, raw_data, data_len); + qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes); } /* get all list, buddies & Quns with groupsid support */ void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position) { - guint8 *raw_data, *cursor; - gint data_len; + qq_data *qd = (qq_data *) gc->proto_data; + guint8 raw_data[16] = {0}; + gint bytes = 0; - data_len = 10; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; /* 0x01 download, 0x02, upload */ - create_packet_b(raw_data, &cursor, 0x01); + bytes += qq_put8(raw_data + bytes, 0x01); /* unknown 0x02 */ - create_packet_b(raw_data, &cursor, 0x02); + bytes += qq_put8(raw_data + bytes, 0x02); /* unknown 00 00 00 00 */ - create_packet_dw(raw_data, &cursor, 0x00000000); - create_packet_dw(raw_data, &cursor, position); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, position); + + qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes); +} + +/* parse the data into qq_buddy_status */ +static gint get_buddy_status(qq_buddy_status *bs, guint8 *data) +{ + gint bytes = 0; + + g_return_val_if_fail(data != NULL && bs != NULL, -1); + + /* 000-003: uid */ + bytes += qq_get32(&bs->uid, data + bytes); + /* 004-004: 0x01 */ + bytes += qq_get8(&bs->unknown1, data + bytes); + /* this is no longer the IP, it seems QQ (as of 2006) no longer sends + * the buddy's IP in this packet. all 0s */ + /* 005-008: ip */ + bytes += qq_getIP(&bs->ip, data + bytes); + /* port info is no longer here either */ + /* 009-010: port */ + bytes += qq_get16(&bs->port, data + bytes); + /* 011-011: 0x00 */ + bytes += qq_get8(&bs->unknown2, data + bytes); + /* 012-012: status */ + bytes += qq_get8(&bs->status, data + bytes); + /* 013-014: client_version */ + bytes += qq_get16(&bs->unknown3, data + bytes); + /* 015-030: unknown key */ + bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes); + + purple_debug(PURPLE_DEBUG_INFO, "QQ_STATUS", + "uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n", + bs->uid, bs->unknown1, inet_ntoa(bs->ip), bs->port, + bs->unknown2, bs->status, bs->unknown3); + + return bytes; +} + +#define QQ_ONLINE_BUDDY_ENTRY_LEN 38 + +/* process the reply packet for get_buddies_online packet */ +guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) +{ + qq_data *qd; + gint len, bytes, bytes_buddy; + gint count; + guint8 *data, position; + PurpleBuddy *b; + qq_buddy *q_bud; + qq_buddy_online bo; + + g_return_val_if_fail(buf != NULL && buf_len != 0, -1); + + qd = (qq_data *) gc->proto_data; + len = buf_len; + data = g_newa(guint8, len); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n"); + + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online"); + return -1; + } - qq_send_cmd(gc, QQ_CMD_GET_ALL_LIST_WITH_GROUP, TRUE, 0, TRUE, raw_data, data_len); + /* qq_show_packet("Get buddies online reply packet", data, len); */ + + bytes = 0; + bytes += qq_get8(&position, data + bytes); + + count = 0; + while (bytes < len) { + if (len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "[buddies online] only %d, need %d", + (len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN); + break; + } + memset(&bo, 0 ,sizeof(bo)); + + /* set flag */ + bytes_buddy = bytes; + /* based on one online buddy entry */ + /* 000-030 qq_buddy_status */ + bytes += get_buddy_status(&(bo.bs), data + bytes); + /* 031-032: */ + bytes += qq_get16(&bo.unknown1, data + bytes); + /* 033-033: ext_flag */ + bytes += qq_get8(&bo.ext_flag, data + bytes); + /* 034-034: comm_flag */ + bytes += qq_get8(&bo.comm_flag, data + bytes); + /* 035-036: */ + bytes += qq_get16(&bo.unknown2, data + bytes); + /* 037-037: */ + bytes += qq_get8(&bo.ending, data + bytes); /* 0x00 */ + + if (bo.bs.uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "uid=0 or entry complete len(%d) != %d", + (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN); + continue; + } /* check if it is a valid entry */ + + /* update buddy information */ + b = purple_find_buddy(purple_connection_get_account(gc), + uid_to_purple_name(bo.bs.uid) ); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + if (q_bud == NULL) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Got an online buddy %d, but not in my buddy list\n", bo.bs.uid); + continue; + } + /* we find one and update qq_buddy */ + /* + if(0 != fe->s->client_version) + q_bud->client_version = fe->s->client_version; + */ + q_bud->ip.s_addr = bo.bs.ip.s_addr; + q_bud->port = bo.bs.port; + q_bud->status = bo.bs.status; + q_bud->ext_flag = bo.ext_flag; + q_bud->comm_flag = bo.comm_flag; + qq_update_buddy_contact(gc, q_bud); + count++; + } + + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n"); + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n", + count, (guint) position); + return position; } -static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe) + +/* process reply for get_buddies_list */ +guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { - GString *dump; + qq_data *qd; + qq_buddy *q_bud; + gint len, bytes_expected, count; + gint bytes, buddy_bytes; + guint16 position, unknown; + guint8 *data, pascal_len; + gchar *name; + PurpleBuddy *b; + + g_return_val_if_fail(buf != NULL && buf_len != 0, -1); + + qd = (qq_data *) gc->proto_data; + len = buf_len; + data = g_newa(guint8, len); - g_return_if_fail(fe != NULL); + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list"); + return -1; + } + bytes = 0; + bytes += qq_get16(&position, data + bytes); + /* the following data is buddy list in this packet */ + count = 0; + while (bytes < len) { + q_bud = g_new0(qq_buddy, 1); + /* set flag */ + buddy_bytes = bytes; + /* 000-003: uid */ + bytes += qq_get32(&q_bud->uid, data + bytes); + /* 004-005: icon index (1-255) */ + bytes += qq_get16(&q_bud->face, data + bytes); + /* 006-006: age */ + bytes += qq_get8(&q_bud->age, data + bytes); + /* 007-007: gender */ + bytes += qq_get8(&q_bud->gender, data + bytes); - qq_buddy_status_dump_unclear(fe->s); + pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT); + bytes += pascal_len; - dump = g_string_new(""); - g_string_append_printf(dump, "unclear fields for [%d]:\n", fe->s->uid); - g_string_append_printf(dump, "031-032: %04x (unknown)\n", fe->unknown1); - g_string_append_printf(dump, "033: %02x (flag1)\n", fe->flag1); - g_string_append_printf(dump, "034: %02x (comm_flag)\n", fe->comm_flag); - g_string_append_printf(dump, "035-036: %04x (unknown)\n", fe->unknown2); + bytes += qq_get16(&unknown, data + bytes); + bytes += qq_get8(&q_bud->ext_flag, data + bytes); + bytes += qq_get8(&q_bud->comm_flag, data + bytes); + + bytes_expected = 12 + pascal_len; + + if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes); + g_free(q_bud->nickname); + g_free(q_bud); + continue; + } else { + count++; + } + + if (QQ_DEBUG) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n", + q_bud->uid, q_bud->ext_flag, q_bud->comm_flag, q_bud->nickname); + } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Online buddy entry, %s", dump->str); - g_string_free(dump, TRUE); + name = uid_to_purple_name(q_bud->uid); + b = purple_find_buddy(gc->account, name); + g_free(name); + + if (b == NULL) { + b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE); + } + + b->proto_data = q_bud; + qd->buddies = g_list_append(qd->buddies, q_bud); + qq_update_buddy_contact(gc, q_bud); + } + + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"); + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n", + count, (guint) position); + return position; } -/* process the reply packet for get_buddies_online packet */ -void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) +guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) +{ + qq_data *qd; + gint len, i, j; + gint bytes = 0; + guint8 *data; + guint8 sub_cmd, reply_code; + guint32 unknown, position; + guint32 uid; + guint8 type, groupid; + qq_group *group; + + g_return_val_if_fail(buf != NULL && buf_len != 0, -1); + + qd = (qq_data *) gc->proto_data; + len = buf_len; + data = g_newa(guint8, len); + + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group"); + return -1; + } + + bytes += qq_get8(&sub_cmd, data + bytes); + g_return_val_if_fail(sub_cmd == 0x01, -1); + + bytes += qq_get8(&reply_code, data + bytes); + if(0 != reply_code) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Get all list with group reply, reply_code(%d) is not zero", reply_code); + } + + bytes += qq_get32(&unknown, data + bytes); + bytes += qq_get32(&position, data + bytes); + /* the following data is all list in this packet */ + i = 0; + j = 0; + while (bytes < len) { + /* 00-03: uid */ + bytes += qq_get32(&uid, data + bytes); + /* 04: type 0x1:buddy 0x4:Qun */ + bytes += qq_get8(&type, data + bytes); + /* 05: groupid*4 */ /* seems to always be 0 */ + bytes += qq_get8(&groupid, data + bytes); + /* + purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid); + groupid >>= 2; + */ + if (uid == 0 || (type != 0x1 && type != 0x4)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Buddy entry, uid=%d, type=%d", uid, type); + continue; + } + if(0x1 == type) { /* a buddy */ + /* don't do anything but count - buddies are handled by + * qq_send_packet_get_buddies_list */ + ++i; + } else { /* a group */ + group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID); + if(group == NULL) { + qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE); + group = g_newa(qq_group, 1); + group->internal_group_id = uid; + qq_send_cmd_group_get_group_info(gc, group); + } else { + group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; + qq_group_refresh(gc, group); + qq_send_cmd_group_get_group_info(gc, group); + } + ++j; + } + } + + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!"); + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position); + return position; +} + +#define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 +#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */ + +/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, + * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, + * using different accounts to get info. */ + +/* check if status means online or offline */ +gboolean is_online(guint8 status) +{ + switch(status) { + case QQ_BUDDY_ONLINE_NORMAL: + case QQ_BUDDY_ONLINE_AWAY: + case QQ_BUDDY_ONLINE_INVISIBLE: + return TRUE; + case QQ_BUDDY_ONLINE_OFFLINE: + return FALSE; + } + return FALSE; +} + +/* Help calculate the correct icon index to tell the server. */ +gint get_icon_offset(PurpleConnection *gc) +{ + PurpleAccount *account; + PurplePresence *presence; + + account = purple_connection_get_account(gc); + presence = purple_account_get_presence(account); + + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { + return 2; + } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) + || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY) + || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) { + return 1; + } else { + return 0; + } +} + +/* send a packet to change my online status */ +void qq_send_packet_change_status(PurpleConnection *gc) +{ + qq_data *qd; + guint8 raw_data[16] = {0}; + gint bytes = 0; + guint8 away_cmd; + guint32 misc_status; + gboolean fake_video; + PurpleAccount *account; + PurplePresence *presence; + + account = purple_connection_get_account(gc); + presence = purple_account_get_presence(account); + + qd = (qq_data *) gc->proto_data; + if (!qd->logged_in) + return; + + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { + away_cmd = QQ_BUDDY_ONLINE_INVISIBLE; + } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) + || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY) + || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) { + away_cmd = QQ_BUDDY_ONLINE_AWAY; + } else { + away_cmd = QQ_BUDDY_ONLINE_NORMAL; + } + + misc_status = 0x00000000; + fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video"); + if (fake_video) + misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO; + + bytes = 0; + bytes += qq_put8(raw_data + bytes, away_cmd); + bytes += qq_put32(raw_data + bytes, misc_status); + + qq_send_cmd(qd, QQ_CMD_CHANGE_ONLINE_STATUS, raw_data, bytes); +} + +/* parse the reply packet for change_status */ +void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; gint len, bytes; - guint8 *data, *cursor, position; + guint8 *data, reply; PurpleBuddy *b; qq_buddy *q_bud; - qq_friends_online_entry *fe; + gchar *name; g_return_if_fail(buf != NULL && buf_len != 0); qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n"); - - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - - _qq_show_packet("Get buddies online reply packet", data, len); - - read_packet_b(data, &cursor, len, &position); - - fe = g_newa(qq_friends_online_entry, 1); - fe->s = g_newa(qq_buddy_status, 1); - - while (cursor < (data + len)) { - /* based on one online buddy entry */ - bytes = 0; - /* 000-030 qq_buddy_status */ - bytes += qq_buddy_status_read(data, &cursor, len, fe->s); - /* 031-032: unknown4 */ - bytes += read_packet_w(data, &cursor, len, &fe->unknown1); - /* 033-033: flag1 */ - bytes += read_packet_b(data, &cursor, len, &fe->flag1); - /* 034-034: comm_flag */ - bytes += read_packet_b(data, &cursor, len, &fe->comm_flag); - /* 035-036: */ - bytes += read_packet_w(data, &cursor, len, &fe->unknown2); - /* 037-037: */ - bytes += read_packet_b(data, &cursor, len, &fe->ending); /* 0x00 */ - - if (fe->s->uid == 0 || bytes != QQ_ONLINE_BUDDY_ENTRY_LEN) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "uid=0 or entry complete len(%d) != %d", - bytes, QQ_ONLINE_BUDDY_ENTRY_LEN); - g_free(fe->s->ip); - g_free(fe->s->unknown_key); - continue; - } /* check if it is a valid entry */ + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n"); + return; + } - if (QQ_DEBUG) - _qq_buddies_online_reply_dump_unclear(fe); - - /* update buddy information */ - b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid)); - q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - - if (q_bud != NULL) { /* we find one and update qq_buddy */ - if(0 != fe->s->client_version) - q_bud->client_version = fe->s->client_version; - g_memmove(q_bud->ip, fe->s->ip, 4); - q_bud->port = fe->s->port; - q_bud->status = fe->s->status; - q_bud->flag1 = fe->flag1; - q_bud->comm_flag = fe->comm_flag; - qq_update_buddy_contact(gc, q_bud); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Got an online buddy %d, but not in my buddy list\n", fe->s->uid); - } + bytes = 0; + bytes = qq_get8(&reply, data + bytes); + if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail 0x%02X\n", reply); + return; + } - g_free(fe->s->ip); - g_free(fe->s->unknown_key); - } - - if(cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n"); - } - - if (position != QQ_FRIENDS_ONLINE_POSITION_END) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position); - - qq_send_packet_get_buddies_online(gc, position); - } else { - qq_send_packet_get_buddies_levels(gc); - qq_refresh_all_buddy_status(gc); - } - - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online"); + /* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); */ + name = uid_to_purple_name(qd->uid); + b = purple_find_buddy(gc->account, name); + g_free(name); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + if (q_bud != NULL) { + qq_update_buddy_contact(gc, q_bud); } } -/* process reply for get_buddies_list */ -void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) +/* it is a server message indicating that one of my buddies has changed its status */ +void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc) { qq_data *qd; + gint bytes; + guint32 my_uid; + guint8 *data; + gint data_len; + PurpleBuddy *b; qq_buddy *q_bud; - gint len, bytes, bytes_expected, i; - guint16 position, unknown; - guint8 *data, *cursor, pascal_len; + qq_buddy_status bs; gchar *name; - PurpleBuddy *b; g_return_if_fail(buf != NULL && buf_len != 0); qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - cursor = data; + data_len = buf_len; + data = g_newa(guint8, data_len); + + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] Failed decrypt\n"); + return; + } + + if (data_len < 35) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "[buddy status change] only %d, need 35 bytes\n", data_len); + return; + } + + memset(&bs, 0, sizeof(bs)); + bytes = 0; + /* 000-030: qq_buddy_status */ + bytes += get_buddy_status(&bs, data + bytes); + /* 031-034: Unknow, maybe my uid */ + /* This has a value of 0 when we've changed our status to + * QQ_BUDDY_ONLINE_INVISIBLE */ + bytes += qq_get32(&my_uid, data + bytes); + + name = uid_to_purple_name(bs.uid); + b = purple_find_buddy(gc->account, name); + g_free(name); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + if (q_bud == NULL) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "got information of unknown buddy %d\n", bs.uid); + return; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "status:.uid = %d, q_bud->uid = %d\n", bs.uid , q_bud->uid); + if(bs.ip.s_addr != 0) { + q_bud->ip.s_addr = bs.ip.s_addr; + q_bud->port = bs.port; + } + q_bud->status =bs.status; + + if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) { + qq_send_packet_get_level(gc, q_bud->uid); + } + qq_update_buddy_contact(gc, q_bud); +} + +/*TODO: maybe this should be qq_update_buddy_status() ?*/ +void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud) +{ + gchar *name; + PurpleBuddy *bud; + gchar *status_id; + + g_return_if_fail(q_bud != NULL); + + name = uid_to_purple_name(q_bud->uid); + bud = purple_find_buddy(gc->account, name); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - read_packet_w(data, &cursor, len, &position); - /* the following data is buddy list in this packet */ - i = 0; - while (cursor < (data + len)) { - q_bud = g_new0(qq_buddy, 1); - bytes = 0; - /* 000-003: uid */ - bytes += read_packet_dw(data, &cursor, len, &q_bud->uid); - /* 004-005: icon index (1-255) */ - bytes += read_packet_w(data, &cursor, len, &q_bud->face); - /* 006-006: age */ - bytes += read_packet_b(data, &cursor, len, &q_bud->age); - /* 007-007: gender */ - bytes += read_packet_b(data, &cursor, len, &q_bud->gender); - pascal_len = convert_as_pascal_string(cursor, &q_bud->nickname, QQ_CHARSET_DEFAULT); - cursor += pascal_len; - bytes += pascal_len; - bytes += read_packet_w(data, &cursor, len, &unknown); - /* flag1: (0-7) - * bit1 => qq show - * comm_flag: (0-7) - * bit1 => member - * bit4 => TCP mode - * bit5 => open mobile QQ - * bit6 => bind to mobile - * bit7 => whether having a video - */ - bytes += read_packet_b(data, &cursor, len, &q_bud->flag1); - bytes += read_packet_b(data, &cursor, len, &q_bud->comm_flag); + if (bud == NULL) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid); + g_free(name); + return; + } + + purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */ + q_bud->last_refresh = time(NULL); - bytes_expected = 12 + pascal_len; - - if (q_bud->uid == 0 || bytes != bytes_expected) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes); - g_free(q_bud->nickname); - g_free(q_bud); - continue; - } else { - i++; - } + /* purple supports signon and idle time + * but it is not much use for QQ, I do not use them */ + /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */ + status_id = "available"; + switch(q_bud->status) { + case QQ_BUDDY_OFFLINE: + status_id = "offline"; + break; + case QQ_BUDDY_ONLINE_NORMAL: + status_id = "available"; + break; + case QQ_BUDDY_ONLINE_OFFLINE: + status_id = "offline"; + break; + case QQ_BUDDY_ONLINE_AWAY: + status_id = "away"; + break; + case QQ_BUDDY_ONLINE_INVISIBLE: + status_id = "invisible"; + break; + default: + status_id = "invisible"; + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status); + break; + } + purple_debug(PURPLE_DEBUG_INFO, "QQ", "buddy %d %s\n", q_bud->uid, status_id); + purple_prpl_got_user_status(gc->account, name, status_id, NULL); - if (QQ_DEBUG) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n", - q_bud->uid, q_bud->flag1, q_bud->comm_flag); - } + if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE) + purple_prpl_got_user_status(gc->account, name, "mobile", NULL); + else + purple_prpl_got_user_status_deactive(gc->account, name, "mobile"); + + if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO && q_bud->status != QQ_BUDDY_OFFLINE) + purple_prpl_got_user_status(gc->account, name, "video", NULL); + else + purple_prpl_got_user_status_deactive(gc->account, name, "video"); + + g_free(name); +} - name = uid_to_purple_name(q_bud->uid); - b = purple_find_buddy(gc->account, name); - g_free(name); +/* refresh all buddies online/offline, + * after receiving reply for get_buddies_online packet */ +void qq_refresh_all_buddy_status(PurpleConnection *gc) +{ + time_t now; + GList *list; + qq_data *qd; + qq_buddy *q_bud; - if (b == NULL) - b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE); + qd = (qq_data *) (gc->proto_data); + now = time(NULL); + list = qd->buddies; - b->proto_data = q_bud; - qd->buddies = g_list_append(qd->buddies, q_bud); + while (list != NULL) { + q_bud = (qq_buddy *) list->data; + if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL + && q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) { + q_bud->status = QQ_BUDDY_ONLINE_OFFLINE; qq_update_buddy_contact(gc, q_bud); } - - if(cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!"); - } - if (position == QQ_FRIENDS_LIST_POSITION_END) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i); - qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START); - } else { - qq_send_packet_get_buddies_list(gc, position); - } - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list"); + list = list->next; } } - -void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint len, i, j; - guint8 *data, *cursor; - guint8 sub_cmd, reply_code; - guint32 unknown, position; - guint32 uid; - guint8 type, groupid; - qq_group *group; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - cursor = data; - - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - read_packet_b(data, &cursor, len, &sub_cmd); - g_return_if_fail(sub_cmd == 0x01); - read_packet_b(data, &cursor, len, &reply_code); - if(0 != reply_code) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Get all list with group reply, reply_code(%d) is not zero", reply_code); - } - read_packet_dw(data, &cursor, len, &unknown); - read_packet_dw(data, &cursor, len, &position); - /* the following data is all list in this packet */ - i = 0; - j = 0; - while (cursor < (data + len)) { - /* 00-03: uid */ - read_packet_dw(data, &cursor, len, &uid); - /* 04: type 0x1:buddy 0x4:Qun */ - read_packet_b(data, &cursor, len, &type); - /* 05: groupid*4 */ /* seems to always be 0 */ - read_packet_b(data, &cursor, len, &groupid); - /* - purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid); - groupid >>= 2; - */ - if (uid == 0 || (type != 0x1 && type != 0x4)) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Buddy entry, uid=%d, type=%d", uid, type); - continue; - } - if(0x1 == type) { /* a buddy */ - /* don't do anything but count - buddies are handled by - * qq_send_packet_get_buddies_list */ - ++i; - } else { /* a group */ - group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID); - if(group == NULL) { - qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE); - group = g_newa(qq_group, 1); - group->internal_group_id = uid; - qq_send_cmd_group_get_group_info(gc, group); - } else { - group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; - qq_group_refresh(gc, group); - qq_send_cmd_group_get_group_info(gc, group); - } - ++j; - } - } - if(cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!"); - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group"); - } -}
--- a/libpurple/protocols/qq/buddy_list.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/buddy_list.h Thu Nov 20 21:13:56 2008 +0000 @@ -28,16 +28,45 @@ #include <glib.h> #include "connection.h" -#define QQ_FRIENDS_LIST_POSITION_START 0x0000 -#define QQ_FRIENDS_LIST_POSITION_END 0xffff -#define QQ_FRIENDS_ONLINE_POSITION_START 0x00 -#define QQ_FRIENDS_ONLINE_POSITION_END 0xff +#include "qq.h" +typedef struct _qq_buddy_status { + guint32 uid; + guint8 unknown1; + struct in_addr ip; + guint16 port; + guint8 unknown2; + guint8 status; + guint16 unknown3; + guint8 unknown_key[QQ_KEY_LENGTH]; +} qq_buddy_status; + +enum { + QQ_BUDDY_OFFLINE = 0x00, + QQ_BUDDY_ONLINE_NORMAL = 0x0a, + QQ_BUDDY_ONLINE_OFFLINE = 0x14, + QQ_BUDDY_ONLINE_AWAY = 0x1e, + QQ_BUDDY_ONLINE_INVISIBLE = 0x28 +}; void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position); -void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); +guint8 qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); + void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position); -void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); +guint16 qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); + void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position); -void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); +guint32 qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); + +void qq_refresh_all_buddy_status(PurpleConnection *gc); + +gboolean is_online(guint8 status); + +gint get_icon_offset(PurpleConnection *gc); +void qq_send_packet_change_status(PurpleConnection *gc); +void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); +void qq_process_buddy_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc); + +void qq_refresh_all_buddy_status(PurpleConnection *gc); +void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud); #endif
--- a/libpurple/protocols/qq/buddy_opt.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/buddy_opt.c Thu Nov 20 21:13:56 2008 +0000 @@ -34,9 +34,9 @@ #include "crypt.h" #include "header_info.h" #include "im.h" -#include "keep_alive.h" +#include "qq_base.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" #define PURPLE_GROUP_QQ_FORMAT "QQ (%s)" @@ -61,33 +61,33 @@ /* send packet to remove a buddy from my buddy list */ static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid) { + qq_data *qd = (qq_data *) gc->proto_data; gchar uid_str[11]; g_return_if_fail(uid > 0); g_snprintf(uid_str, sizeof(uid_str), "%d", uid); - qq_send_cmd(gc, QQ_CMD_DEL_FRIEND, TRUE, 0, - TRUE, (guint8 *) uid_str, strlen(uid_str)); + qq_send_cmd(qd, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str)); } /* try to remove myself from someone's buddy list */ static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid) { - guint8 *raw_data, *cursor; + qq_data *qd = (qq_data *) gc->proto_data; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(uid > 0); - raw_data = g_newa(guint8, 4); - cursor = raw_data; - create_packet_dw(raw_data, &cursor, uid); + bytes += qq_put32(raw_data + bytes, uid); - qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, TRUE, 0, TRUE, raw_data, 4); + qq_send_cmd(qd, QQ_CMD_REMOVE_SELF, raw_data, bytes); } /* try to add a buddy without authentication */ static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid) { - qq_data *qd; + qq_data *qd = (qq_data *) gc->proto_data; qq_add_buddy_request *req; gchar uid_str[11]; @@ -95,11 +95,9 @@ /* we need to send the ascii code of this uid to qq server */ g_snprintf(uid_str, sizeof(uid_str), "%d", uid); - qq_send_cmd(gc, QQ_CMD_ADD_FRIEND_WO_AUTH, TRUE, 0, - TRUE, (guint8 *) uid_str, strlen(uid_str)); + qq_send_cmd(qd, QQ_CMD_ADD_BUDDY_WO_AUTH, (guint8 *) uid_str, strlen(uid_str)); /* must be set after sending packet to get the correct send_seq */ - qd = (qq_data *) gc->proto_data; req = g_new0(qq_add_buddy_request, 1); req->seq = qd->send_seq; req->uid = uid; @@ -109,28 +107,29 @@ /* this buddy needs authentication, text conversion is done at lowest level */ static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text) { + qq_data *qd = (qq_data *) gc->proto_data; gchar *text_qq, uid_str[11]; - guint8 bar, *cursor, *raw_data; + guint8 bar, *raw_data; + gint bytes = 0; g_return_if_fail(uid != 0); g_snprintf(uid_str, sizeof(uid_str), "%d", uid); bar = 0x1f; raw_data = g_newa(guint8, QQ_MSG_IM_MAX); - cursor = raw_data; - create_packet_data(raw_data, &cursor, (guint8 *) uid_str, strlen(uid_str)); - create_packet_b(raw_data, &cursor, bar); - create_packet_b(raw_data, &cursor, response); + bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str)); + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_put8(raw_data + bytes, response); if (text != NULL) { text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT); - create_packet_b(raw_data, &cursor, bar); - create_packet_data(raw_data, &cursor, (guint8 *) text_qq, strlen(text_qq)); + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq)); g_free(text_qq); } - qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, TRUE, 0, TRUE, raw_data, cursor - raw_data); + qq_send_cmd(qd, QQ_CMD_BUDDY_AUTH, raw_data, bytes); } static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text) @@ -210,10 +209,10 @@ nombre = uid_to_purple_name(uid); purple_request_input(gc, _("Reject request"), msg1, msg2, - _("Sorry, you are not my type..."), TRUE, FALSE, - NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL, - purple_connection_get_account(gc), nombre, NULL, - g2); + _("Sorry, you are not my type..."), TRUE, FALSE, + NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL, + purple_connection_get_account(gc), nombre, NULL, + g2); g_free(nombre); } @@ -257,7 +256,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + gint bytes = 0; + guint8 *data, reply; gchar **segments, *msg_utf8; g_return_if_fail(buf != NULL && buf_len != 0); @@ -265,22 +265,23 @@ qd = (qq_data *) gc->proto_data; len = buf_len; data = g_newa(guint8, len); - cursor = data; + + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n"); + } + + bytes += qq_get8(&reply, data + bytes); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n"); - if (NULL == (segments = split_data(data, len, "\x1f", 2))) - return; - msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); - purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8); - g_free(msg_utf8); - } else { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n"); + if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n"); + if (NULL == (segments = split_data(data, len, "\x1f", 2))) { + return; } + msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); + purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8); + g_free(msg_utf8); } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n"); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n"); } } @@ -289,7 +290,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + gint bytes = 0; + guint8 *data, reply; g_return_if_fail(buf != NULL && buf_len != 0); @@ -297,20 +299,20 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_REMOVE_BUDDY_REPLY_OK) { - /* there is no reason return from server */ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n"); - } else { /* if reply */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n"); - /* TODO: We don't really need to notify the user about this, do we? */ - purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL); - } - } else { + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n"); } + + bytes += qq_get8(&reply, data + bytes); + + if (reply != QQ_REMOVE_BUDDY_REPLY_OK) { + /* there is no reason return from server */ + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n"); + } else { /* if reply */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n"); + /* TODO: We don't really need to notify the user about this, do we? */ + purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL); + } } /* process the server reply for my request to remove myself from a buddy */ @@ -318,7 +320,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + gint bytes = 0; + guint8 *data, reply; g_return_if_fail(buf != NULL && buf_len != 0); @@ -326,20 +329,20 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_REMOVE_SELF_REPLY_OK) - /* there is no reason return from server */ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n"); - else { /* if reply */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n"); - /* TODO: Does the user really need to be notified about this? */ - purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL); - } - } else { + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n"); } + + bytes += qq_get8(&reply, data + bytes); + + if (reply != QQ_REMOVE_SELF_REPLY_OK) { + /* there is no reason return from server */ + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n"); + } else { /* if reply */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n"); + /* TODO: Does the user really need to be notified about this? */ + purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL); + } } void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc) @@ -403,14 +406,14 @@ g->uid = for_uid; msg = g_strdup_printf(_("User %d needs authentication"), for_uid); purple_request_input(gc, NULL, msg, - _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */ - _("Would you be my friend?"), - TRUE, FALSE, NULL, _("Send"), - G_CALLBACK - (_qq_send_packet_add_buddy_auth_with_gc_and_uid), - _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), - purple_connection_get_account(gc), nombre, NULL, - g); + _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */ + _("Would you be my friend?"), + TRUE, FALSE, NULL, _("Send"), + G_CALLBACK + (_qq_send_packet_add_buddy_auth_with_gc_and_uid), + _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), + purple_connection_get_account(gc), nombre, NULL, + g); g_free(msg); g_free(nombre); } else { /* add OK */ @@ -457,7 +460,7 @@ g_return_val_if_fail(a != NULL && uid != 0, NULL); group_name = is_known ? - g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN); + g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN); g = qq_get_purple_group(group_name); @@ -478,7 +481,7 @@ b->proto_data = q_bud; qd->buddies = g_list_append(qd->buddies, q_bud); qq_send_packet_get_info(gc, q_bud->uid, FALSE); - qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START); + qq_send_packet_get_buddies_online(gc, 0); } purple_blist_add_buddy(b, NULL, g, NULL); @@ -512,8 +515,8 @@ if (b != NULL) purple_blist_remove_buddy(b); purple_notify_error(gc, NULL, - _("QQid Error"), - _("Invalid QQid")); + _("QQid Error"), + _("Invalid QQid")); } }
--- a/libpurple/protocols/qq/buddy_status.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,280 +0,0 @@ -/** - * @file buddy_status.c - * - * 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 <string.h> -#include "internal.h" -#include "debug.h" -#include "prefs.h" - -#include "buddy_info.h" -#include "buddy_status.h" -#include "crypt.h" -#include "header_info.h" -#include "keep_alive.h" -#include "packet_parse.h" -#include "send_core.h" -#include "utils.h" - -#include "qq_proxy.h" - -#define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001 -#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */ - -void qq_buddy_status_dump_unclear(qq_buddy_status *s) -{ - GString *dump; - - g_return_if_fail(s != NULL); - - dump = g_string_new(""); - g_string_append_printf(dump, "unclear fields for [%d]:\n", s->uid); - g_string_append_printf(dump, "004: %02x (unknown)\n", s->unknown1); - /* g_string_append_printf(dump, "005-008: %09x (ip)\n", *(s->ip)); */ - g_string_append_printf(dump, "009-010: %04x (port)\n", s->port); - g_string_append_printf(dump, "011: %02x (unknown)\n", s->unknown2); - g_string_append_printf(dump, "012: %02x (status)\n", s->status); - g_string_append_printf(dump, "013-014: %04x (client_version)\n", s->client_version); - /* g_string_append_printf(dump, "015-030: %s (unknown key)\n", s->unknown_key); */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str); - _qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH); - g_string_free(dump, TRUE); -} - -/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses, - * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy, - * using different accounts to get info. */ - -/* parse the data into qq_buddy_status */ -gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s) -{ - gint bytes; - - g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1); - - bytes = 0; - - /* 000-003: uid */ - bytes += read_packet_dw(data, cursor, len, &s->uid); - /* 004-004: 0x01 */ - bytes += read_packet_b(data, cursor, len, &s->unknown1); - /* this is no longer the IP, it seems QQ (as of 2006) no longer sends - * the buddy's IP in this packet. all 0s */ - /* 005-008: ip */ - s->ip = g_new0(guint8, 4); - bytes += read_packet_data(data, cursor, len, s->ip, 4); - /* port info is no longer here either */ - /* 009-010: port */ - bytes += read_packet_w(data, cursor, len, &s->port); - /* 011-011: 0x00 */ - bytes += read_packet_b(data, cursor, len, &s->unknown2); - /* 012-012: status */ - bytes += read_packet_b(data, cursor, len, &s->status); - /* 013-014: client_version */ - bytes += read_packet_w(data, cursor, len, &s->client_version); - /* 015-030: unknown key */ - s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH); - bytes += read_packet_data(data, cursor, len, s->unknown_key, QQ_KEY_LENGTH); - - if (s->uid == 0 || bytes != 31) - return -1; - - return bytes; -} - -/* check if status means online or offline */ -gboolean is_online(guint8 status) -{ - switch(status) { - case QQ_BUDDY_ONLINE_NORMAL: - case QQ_BUDDY_ONLINE_AWAY: - case QQ_BUDDY_ONLINE_INVISIBLE: - return TRUE; - case QQ_BUDDY_ONLINE_OFFLINE: - return FALSE; - } - return FALSE; -} - - /* Help calculate the correct icon index to tell the server. */ -gint get_icon_offset(PurpleConnection *gc) -{ - PurpleAccount *account; - PurplePresence *presence; - - account = purple_connection_get_account(gc); - presence = purple_account_get_presence(account); - - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { - return 2; - } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) { - return 1; - } else { - return 0; - } -} - -/* send a packet to change my online status */ -void qq_send_packet_change_status(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *raw_data, *cursor, away_cmd; - guint32 misc_status; - gboolean fake_video; - PurpleAccount *account; - PurplePresence *presence; - - account = purple_connection_get_account(gc); - presence = purple_account_get_presence(account); - - qd = (qq_data *) gc->proto_data; - if (!qd->logged_in) - return; - - if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { - away_cmd = QQ_BUDDY_ONLINE_INVISIBLE; - } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY) - || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) { - away_cmd = QQ_BUDDY_ONLINE_AWAY; - } else { - away_cmd = QQ_BUDDY_ONLINE_NORMAL; - } - - raw_data = g_new0(guint8, 5); - cursor = raw_data; - misc_status = 0x00000000; - - fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video"); - if (fake_video) - misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO; - - create_packet_b(raw_data, &cursor, away_cmd); - create_packet_dw(raw_data, &cursor, misc_status); - - qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5); - - g_free(raw_data); -} - -/* parse the reply packet for change_status */ -void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint len; - guint8 *data, *cursor, reply; - PurpleBuddy *b; - qq_buddy *q_bud; - gchar *name; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); - if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n"); - } else { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n"); - name = uid_to_purple_name(qd->uid); - b = purple_find_buddy(gc->account, name); - g_free(name); - q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - qq_update_buddy_contact(gc, q_bud); - } - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n"); - } -} - -/* it is a server message indicating that one of my buddies has changed its status */ -void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint len, bytes; - guint32 my_uid; - guint8 *data, *cursor; - PurpleBuddy *b; - qq_buddy *q_bud; - qq_buddy_status *s; - gchar *name; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - cursor = data; - - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - s = g_new0(qq_buddy_status, 1); - bytes = 0; - /* 000-030: qq_buddy_status */ - bytes += qq_buddy_status_read(data, &cursor, len, s); - /* 031-034: my uid */ - /* This has a value of 0 when we've changed our status to - * QQ_BUDDY_ONLINE_INVISIBLE */ - bytes += read_packet_dw(data, &cursor, len, &my_uid); - - if (bytes != 35) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes); - g_free(s->ip); - g_free(s->unknown_key); - g_free(s); - return; - } - - name = uid_to_purple_name(s->uid); - b = purple_find_buddy(gc->account, name); - g_free(name); - q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; - if (q_bud) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid); - if(0 != *((guint32 *)s->ip)) { - g_memmove(q_bud->ip, s->ip, 4); - q_bud->port = s->port; - } - q_bud->status = s->status; - if(0 != s->client_version) - q_bud->client_version = s->client_version; - if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) - qq_send_packet_get_level(gc, q_bud->uid); - qq_update_buddy_contact(gc, q_bud); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "got information of unknown buddy %d\n", s->uid); - } - - g_free(s->ip); - g_free(s->unknown_key); - g_free(s); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n"); - } -}
--- a/libpurple/protocols/qq/buddy_status.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/** - * @file buddy_status.h - * - * 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 _QQ_BUDDY_STATUS_H_ -#define _QQ_BUDDY_STATUS_H_ - -#include <glib.h> -#include "connection.h" -#include "qq.h" - -typedef struct _qq_buddy_status { - guint32 uid; - guint8 unknown1; - guint8 *ip; - guint16 port; - guint8 unknown2; - guint8 status; - guint16 client_version; - guint8 *unknown_key; -} qq_buddy_status; - -enum { - QQ_BUDDY_OFFLINE = 0x00, - QQ_BUDDY_ONLINE_NORMAL = 0x0a, - QQ_BUDDY_ONLINE_OFFLINE = 0x14, - QQ_BUDDY_ONLINE_AWAY = 0x1e, - QQ_BUDDY_ONLINE_INVISIBLE = 0x28 -}; - -void qq_buddy_status_dump_unclear(qq_buddy_status *s); -gboolean is_online(guint8 status); - -gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s); -gint get_icon_offset(PurpleConnection *gc); - -void qq_send_packet_change_status(PurpleConnection *gc); - -void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); -void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc); -#endif
--- a/libpurple/protocols/qq/char_conv.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.c Thu Nov 20 21:13:56 2008 +0000 @@ -37,10 +37,7 @@ #define QQ_CHARSET_ENG "ISO-8859-1" #define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */ -#define QQ_NULL_SMILEY "(SM)" /* return this if smiley conversion fails */ - -/* a debug function */ -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); +#define QQ_NULL_SMILEY "(Broken)" /* return this if smiley conversion fails */ const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = { 0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48, @@ -111,21 +108,26 @@ ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error); - if (error == NULL) + if (error == NULL) { return ret; /* conversion is OK */ - else { /* conversion error */ - gchar *failed = hex_dump_to_str((guint8 *) str, (len == -1) ? strlen(str) : len); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Dump failed text\n%s", failed); - g_free(failed); - g_error_free(error); - return g_strdup(QQ_NULL_MSG); } + + /* conversion error */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ_CONVERT", "%s\n", error->message); + + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT", + (guint8 *) str, (len == -1) ? strlen(str) : len, + "Dump failed text"); + + g_error_free(error); + return g_strdup(QQ_NULL_MSG); } -/* take the input as a pascal string and return a converted c-string in UTF-8 +/* + * take the input as a pascal string and return a converted c-string in UTF-8 * returns the number of bytes read, return -1 if fatal error - * the converted UTF-8 will be saved in ret */ + * the converted UTF-8 will be saved in ret + */ gint convert_as_pascal_string(guint8 *data, gchar **ret, const gchar *from_charset) { guint8 len; @@ -142,22 +144,23 @@ gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg) { GString *encoded; - guint8 font_attr, font_size, color[3], bar, *cursor; + guint8 font_attr, font_size, color[3], bar; gboolean is_bold, is_italic, is_underline; guint16 charset_code; gchar *font_name, *color_code, *msg_utf8, *tmp, *ret; + gint bytes = 0; - cursor = data; - _qq_show_packet("QQ_MESG recv for font style", data, len); + /* checked qq_show_packet OK */ + /* qq_show_packet("QQ_MESG recv for font style", data, len); */ - read_packet_b(data, &cursor, len, &font_attr); - read_packet_data(data, &cursor, len, color, 3); /* red,green,blue */ + bytes += qq_get8(&font_attr, data + bytes); + bytes += qq_getdata(color, 3, data + bytes); /* red,green,blue */ color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]); - read_packet_b(data, &cursor, len, &bar); /* skip, not sure of its use */ - read_packet_w(data, &cursor, len, &charset_code); + bytes += qq_get8(&bar, data + bytes); /* skip, not sure of its use */ + bytes += qq_get16(&charset_code, data + bytes); - tmp = g_strndup((gchar *) cursor, data + len - cursor); + tmp = g_strndup((gchar *)(data + bytes), len - bytes); font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT); g_free(tmp); @@ -177,11 +180,11 @@ /* Henry: The range QQ sends rounds from 8 to 22, where a font size * of 10 is equal to 3 in html font tag */ g_string_append_printf(encoded, - "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">", - color_code, font_name, font_size / 3); + "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">", + color_code, font_name, font_size / 3); purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG", - "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n", - color_code, font_name, font_size / 3); + "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n", + color_code, font_name, font_size / 3); g_string_append(encoded, msg_utf8); if (is_bold) { @@ -228,7 +231,7 @@ GString *converted; converted = g_string_new(""); - segments = split_data((guint8 *) text, strlen(text), "\x14", 0); + segments = split_data((guint8 *) text, strlen(text), "\x14\x15", 0); g_string_append(converted, segments[0]); while ((*(++segments)) != NULL) { @@ -275,3 +278,17 @@ g_string_free(converted, FALSE); return ret; } + +void qq_filter_str(gchar *str) { + gchar *temp; + if (str == NULL) { + return; + } + + for (temp = str; *temp != 0; temp++) { + if (*temp == '\r' || *temp == '\n') *temp = ' '; + } + g_strstrip(str); +} + +
--- a/libpurple/protocols/qq/char_conv.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/char_conv.h Thu Nov 20 21:13:56 2008 +0000 @@ -40,5 +40,5 @@ gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg); gchar *qq_im_filter_html(const gchar *text); - +void qq_filter_str(gchar *str); #endif
--- a/libpurple/protocols/qq/crypt.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/crypt.c Thu Nov 20 21:13:56 2008 +0000 @@ -19,7 +19,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * QQ encryption algorithm @@ -28,7 +28,7 @@ * Puzzlebird, Nov-Dec 2002 */ -/*Notes: (QQ uses 16 rounds, and modified something...) +/* Notes: (QQ uses 16 rounds, and modified something...) IN : 64 bits of data in v[0] - v[1]. OUT: 64 bits of data in w[0] - w[1]. @@ -45,6 +45,13 @@ #include "crypt.h" #include "debug.h" +/* 1, fixed alignment problem, when compiled on different platform + * 2, whether we need core debug + * 20070717, s3e */ +#if 0 +#define CORE_DEBUG +#endif + /******************************************************************** * encryption *******************************************************************/ @@ -52,7 +59,8 @@ /* Tiny Encryption Algorithm (TEA) */ static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const w) { - register guint32 y = g_ntohl(v[0]), + register guint32 + y = g_ntohl(v[0]), z = g_ntohl(v[1]), a = g_ntohl(k[0]), b = g_ntohl(k[1]), @@ -72,24 +80,86 @@ w[1] = g_htonl(z); } -static gint rand(void) { /* it can be the real random seed function */ - return 0xdead; -} /* override with number, convenient for debug */ +/* it can be the real random seed function */ +/* override with number, convenient for debug */ +#ifdef DEBUG +static gint rand(void) { + return 0xdead; +} +#else +#include <stdlib.h> +#endif /* 64-bit blocks and some kind of feedback mode of operation */ -static void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, +static inline void encrypt_block(guint8 *plain, guint8 *plain_pre_8, guint8 **crypted, guint8 **crypted_pre_8, const guint8 *const key, gint *count, gint *pos_in_block, gint *is_header) { + /* loop it */ + int j; + /* ships in encipher */ + guint32 ptr_p[2]; /* 64 bits, guint32[2] */ + guint32 ptr_k[4]; /* 128 bits, guint32[4] */ + guint32 ptr_c[2]; /* 64 bits, guint32[2] */ + /* prepare input text */ - if (!*is_header) - *(guint64 *) plain ^= **(guint64 **) crypted_pre_8; +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_ERROR, "QQ_CORE_DEBUG", + "!we are in encrypt_block! *pos_in_block comes: %d, *is_header comes: %d\n", + *pos_in_block, *is_header); +#endif + for(j = 0; j < 8; j++) { +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG", + "plain[%d]: 0x%02x, plain_pre_8[%d]: 0x%02x\n", + j, plain[j], j, plain_pre_8[j]); +#endif + if (!*is_header) { +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG", + "(*crypted_pre_8 + %d): 0x%02x\n", + j, *(*crypted_pre_8 + j)); +#endif + plain[j] ^= (*(*crypted_pre_8 + j)); +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG", + "NOW plain[%d]: 0x%02x\n", + j, plain[j]); +#endif + } else { + plain[j] ^= plain_pre_8[j]; +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG", + "NOW plain[%d]: 0x%02x\n", + j, plain[j]); +#endif + } + } + + g_memmove(ptr_p, plain, 8); + g_memmove(ptr_k, key, 16); + g_memmove(ptr_c, *crypted, 8); /* encrypt it */ - qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted); + qq_encipher(ptr_p, ptr_k, ptr_c); + + g_memmove(plain, ptr_p, 8); + g_memmove(*crypted, ptr_c, 8); - **(guint64 **) crypted ^= *(guint64 *) plain_pre_8; - + for(j = 0; j < 8; j++) { +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG", + "j: %d, *(*crypted + %d): 0x%02x, plain_pre_8[%d]: 0x%02x\n", + j, j, *(*crypted + j), j, plain_pre_8[j]); +#endif + (*(*crypted + j)) ^= plain_pre_8[j]; +#ifdef CORE_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ_CORE_DEBUG", + "NOW *(*crypted + [%d]): 0x%02x\n", + j, *(*crypted + j)); +#endif + } + memcpy(plain_pre_8, plain, 8); /* prepare next */ *crypted_pre_8 = *crypted; /* store position of previous 8 byte */ @@ -171,7 +241,8 @@ static void qq_decipher(guint32 *const v, const guint32 *const k, guint32 *const w) { - register guint32 y = g_ntohl(v[0]), + register guint32 + y = g_ntohl(v[0]), z = g_ntohl(v[1]), a = g_ntohl(k[0]), b = g_ntohl(k[1]), @@ -196,12 +267,25 @@ const guint8 *const key, gint *context_start, guint8 *decrypted, gint *pos_in_block) { + /* loop */ + int i; + /* ships in decipher */ + guint32 ptr_v[2]; + guint32 ptr_k[4]; + if (*context_start == instrlen) return 1; - *(guint64 *) decrypted ^= **(guint64 **) crypt_buff; + for(i = 0; i < 8; i++) { + decrypted[i] ^= (*(*crypt_buff + i)); + } + + g_memmove(ptr_v, decrypted, 8); + g_memmove(ptr_k, key, 16); - qq_decipher((guint32 *) decrypted, (guint32 *) key, (guint32 *) decrypted); + qq_decipher(ptr_v, ptr_k, ptr_v); + + g_memmove(decrypted, ptr_v, 8); *context_start += 8; *crypt_buff += 8; @@ -218,6 +302,10 @@ guint8 decrypted[8], m[8], *outp; const guint8 *crypt_buff, *crypt_buff_pre_8; gint count, context_start, pos_in_block, padding; + /* ships */ + guint32 ptr_instr[2]; + guint32 ptr_key[4]; + guint32 ptr_decr[2]; /* at least 16 bytes and %8 == 0 */ if ((instrlen % 8) || (instrlen < 16)) { @@ -226,8 +314,14 @@ instrlen); return 0; } - /* get information from header */ - qq_decipher((guint32 *) instr, (guint32 *) key, (guint32 *) decrypted); + g_memmove(ptr_instr, instr, 8); + g_memmove(ptr_key, key, 16); + g_memmove(ptr_decr, decrypted, 8); + + qq_decipher(ptr_instr, ptr_key, ptr_decr); + + g_memmove(decrypted, ptr_decr, 8); + pos_in_block = decrypted[0] & 0x7; count = instrlen - pos_in_block - 10; /* this is the plaintext length */ /* return if outstr buffer is not large enough or error plaintext length */ @@ -294,5 +388,6 @@ } } } + return 1; }
--- a/libpurple/protocols/qq/crypt.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/crypt.h Thu Nov 20 21:13:56 2008 +0000 @@ -1,4 +1,4 @@ -/** + /** * @file crypt.h * * purple @@ -19,7 +19,7 @@ * * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _QQ_CRYPT_H_ @@ -27,6 +27,9 @@ #include <glib.h> +#define DECRYPT 0x00 +#define ENCRYPT 0x01 + void qq_encrypt(const guint8 *const instr, gint instrlen, const guint8 *const key, guint8 *outstr, gint *outstrlen_ptr); @@ -34,5 +37,4 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen, const guint8 *const key, guint8 *outstr, gint *outstrlen_ptr); - #endif
--- a/libpurple/protocols/qq/file_trans.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/file_trans.c Thu Nov 20 21:13:56 2008 +0000 @@ -38,12 +38,11 @@ #include "im.h" #include "packet_parse.h" #include "proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" struct _qq_file_header { - guint8 tag; guint16 client_ver; guint8 file_key; guint32 sender_uid; @@ -58,11 +57,11 @@ key = seed | (seed << 8) | (seed << 16) | (seed << 24); return key; } - + static guint32 _gen_file_key(void) { guint8 seed; - + seed = random(); return _get_file_key(seed); } @@ -77,26 +76,10 @@ return (~uid) ^ key; } -static void _fill_filename_md5(const gchar *filename, guint8 *md5) -{ - PurpleCipher *cipher; - PurpleCipherContext *context; - - g_return_if_fail(filename != NULL && md5 != NULL); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, (guint8 *) filename, strlen(filename)); - purple_cipher_context_digest(context, 16, md5, NULL); - purple_cipher_context_destroy(context); -} - static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5) { FILE *fp; guint8 *buffer; - PurpleCipher *cipher; - PurpleCipherContext *context; size_t wc; const gint QQ_MAX_FILE_MD5_LENGTH = 10002432; @@ -119,23 +102,20 @@ return; } - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, buffer, filelen); - purple_cipher_context_digest(context, 16, md5, NULL); - purple_cipher_context_destroy(context); + qq_get_md5(md5, QQ_KEY_LENGTH, buffer, filelen); } -static void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh) +static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf) { - read_packet_b(buf, cursor, buflen, &(fh->tag)); - read_packet_w(buf, cursor, buflen, &(fh->client_ver)); - read_packet_b(buf, cursor, buflen, &fh->file_key); - read_packet_dw(buf, cursor, buflen, &(fh->sender_uid)); - read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid)); + gint bytes = 0; + bytes += qq_get16(&(fh->client_ver), buf + bytes); + bytes += qq_get8(&fh->file_key, buf + bytes); + bytes += qq_get32(&(fh->sender_uid), buf + bytes); + bytes += qq_get32(&(fh->receiver_uid), buf + bytes); fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key)); fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key)); + return bytes; } static const gchar *qq_get_file_cmd_desc(gint type) @@ -190,7 +170,7 @@ fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644); info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0); } - + if (info->buffer == NULL) { return - 1; } @@ -258,30 +238,28 @@ static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid) { - gint bytes; - guint8 *cursor, *buf; + guint8 *raw_data; + gint bytes = 0; guint32 file_key; qq_data *qd; ft_info *info; qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(qd->session_key != NULL, -1); + info = (ft_info *) qd->xfer->data; - bytes = 0; - buf = g_newa(guint8, MAX_PACKET_SIZE); - cursor = buf; + raw_data = g_newa(guint8, MAX_PACKET_SIZE); file_key = _gen_file_key(); - bytes += create_packet_b(buf, &cursor, packet_type); - bytes += create_packet_w(buf, &cursor, QQ_CLIENT); - bytes += create_packet_b(buf, &cursor, file_key & 0xff); - bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key)); - bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key)); - bytes += create_packet_data(buf, &cursor, data, len); + bytes += qq_put8(raw_data + bytes, packet_type); + bytes += qq_put16(raw_data + bytes, QQ_CLIENT); + bytes += qq_put8(raw_data + bytes, file_key & 0xff); + bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key)); + bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key)); + bytes += qq_putdata(raw_data + bytes, data, len); if (bytes == len + 12) { - _qq_xfer_write(buf, bytes, qd->xfer); + _qq_xfer_write(raw_data, bytes, qd->xfer); } else purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes); return bytes; @@ -292,57 +270,56 @@ { qq_data *qd; gint bytes, bytes_expected, encrypted_len; - guint8 *raw_data, *cursor, *encrypted_data; + guint8 *raw_data, *encrypted_data; time_t now; ft_info *info; - + qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; - raw_data = g_new0 (guint8, 61); - cursor = raw_data; - + raw_data = g_newa (guint8, 61); bytes = 0; + now = time(NULL); - bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16); - bytes += create_packet_w(raw_data, &cursor, packet_type); + bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); + bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: case QQ_FILE_CMD_NOTIFY_IP_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: - bytes += create_packet_w(raw_data, &cursor, info->send_seq); + bytes += qq_put16(raw_data + bytes, info->send_seq); break; default: - bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq); + bytes += qq_put16(raw_data + bytes, ++qd->send_seq); } - bytes += create_packet_dw(raw_data, &cursor, (guint32) now); - bytes += create_packet_b(raw_data, &cursor, 0x00); - bytes += create_packet_b(raw_data, &cursor, qd->my_icon); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put32(raw_data + bytes, (guint32) now); + bytes += qq_put8(raw_data + bytes, 0x00); + bytes += qq_put8(raw_data + bytes, qd->my_icon); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, 0x00); /* 0x65: send a file, 0x6b: send a custom face */ - bytes += create_packet_b(raw_data, &cursor, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */ + bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */ switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: - bytes += create_packet_b(raw_data, &cursor, 0x00); - bytes += create_packet_b(raw_data, &cursor, hellobyte); + bytes += qq_put8(raw_data + bytes, 0x00); + bytes += qq_put8(raw_data + bytes, hellobyte); bytes_expected = 48; break; case QQ_FILE_CMD_PING: case QQ_FILE_CMD_PONG: case QQ_FILE_CMD_NOTIFY_IP_ACK: - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += qq_fill_conn_info(raw_data, info); bytes_expected = 61; break; default: @@ -350,53 +327,55 @@ packet_type); bytes_expected = 0; } - - if (bytes == bytes_expected) { - gchar *hex_dump = hex_dump_to_str(raw_data, bytes); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type), hex_dump); - g_free(hex_dump); - encrypted_len = bytes + 16; - encrypted_data = g_newa(guint8, encrypted_len); - qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len); - /*debug: try to decrypt it */ - /* - if (QQ_DEBUG) { - guint8 *buf; - int buflen; - hex_dump = hex_dump_to_str(encrypted_data, encrypted_len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump); - g_free(hex_dump); - buf = g_newa(guint8, MAX_PACKET_SIZE); - buflen = encrypted_len; - if (qq_decrypt(encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n"); - if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) - purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n"); - hex_dump = hex_dump_to_str(buf, buflen); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump); - g_free(hex_dump); - } else { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n"); - } - } - */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); - _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid); - } - else + if (bytes != bytes_expected) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d", bytes_expected, bytes); + return; + } + + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + raw_data, bytes, + "sending packet[%s]:", qq_get_file_cmd_desc(packet_type)); + + encrypted_len = bytes + 16; + encrypted_data = g_newa(guint8, encrypted_len); + qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len); + /*debug: try to decrypt it */ + /* + if (QQ_DEBUG) { + guint8 *buf; + int buflen; + hex_dump = hex_dump_to_str(encrypted_data, encrypted_len); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump); + g_free(hex_dump); + buf = g_newa(guint8, MAX_PACKET_SIZE); + buflen = encrypted_len; + if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n"); + if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) + purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n"); + hex_dump = hex_dump_to_str(buf, buflen); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump); + g_free(hex_dump); + } else { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n"); + } + } + */ + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); + _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid); } /* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, guint32 fragment_index, guint16 seq, guint8 *data, gint len) { + guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH]; gint bytes; - guint8 *raw_data, *cursor, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH]; guint32 fragment_size = 1000; - gchar *filename; + const char *filename; gint filename_len, filesize; qq_data *qd; ft_info *info; @@ -404,32 +383,31 @@ qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; - filename = (gchar *) purple_xfer_get_filename(qd->xfer); + filename = purple_xfer_get_filename(qd->xfer); filesize = purple_xfer_get_size(qd->xfer); raw_data = g_newa(guint8, MAX_PACKET_SIZE); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, 0x00); - bytes += create_packet_w(raw_data, &cursor, packet_type); + bytes += qq_put8(raw_data + bytes, 0x00); + bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_BASIC_INFO: case QQ_FILE_DATA_INFO: case QQ_FILE_EOF: - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, 0x00); break; case QQ_FILE_CMD_FILE_OP: switch(sub_type) { case QQ_FILE_BASIC_INFO: filename_len = strlen(filename); - _fill_filename_md5(filename, filename_md5); + qq_get_md5(filename_md5, sizeof(filename_md5), (guint8 *)filename, filename_len); _fill_file_md5(purple_xfer_get_local_filename(qd->xfer), purple_xfer_get_size(qd->xfer), file_md5); - + info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1; info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN; @@ -437,44 +415,44 @@ "start transfering data, %d fragments with %d length each\n", info->fragment_num, info->fragment_len); /* Unknown */ - bytes += create_packet_w(raw_data, &cursor, 0x0000); + bytes += qq_put16(raw_data + bytes, 0x0000); /* Sub-operation type */ - bytes += create_packet_b(raw_data, &cursor, sub_type); + bytes += qq_put8(raw_data + bytes, sub_type); /* Length of file */ - bytes += create_packet_dw(raw_data, &cursor, filesize); + bytes += qq_put32(raw_data + bytes, filesize); /* Number of fragments */ - bytes += create_packet_dw(raw_data, &cursor, info->fragment_num); + bytes += qq_put32(raw_data + bytes, info->fragment_num); /* Length of a single fragment */ - bytes += create_packet_dw(raw_data, &cursor, info->fragment_len); - bytes += create_packet_data(raw_data, &cursor, file_md5, 16); - bytes += create_packet_data(raw_data, &cursor, filename_md5, 16); + bytes += qq_put32(raw_data + bytes, info->fragment_len); + bytes += qq_putdata(raw_data + bytes, file_md5, 16); + bytes += qq_putdata(raw_data + bytes, filename_md5, 16); /* Length of filename */ - bytes += create_packet_w(raw_data, &cursor, filename_len); + bytes += qq_put16(raw_data + bytes, filename_len); /* 8 unknown bytes */ - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); /* filename */ - bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename, + bytes += qq_putdata(raw_data + bytes, (guint8 *) filename, filename_len); break; case QQ_FILE_DATA_INFO: purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending %dth fragment with length %d, offset %d\n", fragment_index, len, (fragment_index-1)*fragment_size); - /* bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq)); */ - bytes += create_packet_w(raw_data, &cursor, info->send_seq); - bytes += create_packet_b(raw_data, &cursor, sub_type); - /* bytes += create_packet_dw(raw_data, &cursor, fragment_index); */ - bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1); - bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size); - bytes += create_packet_w(raw_data, &cursor, len); - bytes += create_packet_data(raw_data, &cursor, data, len); + /* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */ + bytes += qq_put16(raw_data + bytes, info->send_seq); + bytes += qq_put8(raw_data + bytes, sub_type); + /* bytes += qq_put32(raw_data + bytes, fragment_index); */ + bytes += qq_put32(raw_data + bytes, fragment_index - 1); + bytes += qq_put32(raw_data + bytes, (fragment_index - 1) * fragment_size); + bytes += qq_put16(raw_data + bytes, len); + bytes += qq_putdata(raw_data + bytes, data, len); break; case QQ_FILE_EOF: purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of sending data\n"); - /* bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1); */ - bytes += create_packet_w(raw_data, &cursor, info->fragment_num); - bytes += create_packet_b(raw_data, &cursor, sub_type); + /* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */ + bytes += qq_put16(raw_data + bytes, info->fragment_num); + bytes += qq_put8(raw_data + bytes, sub_type); /* purple_xfer_set_completed(qd->xfer, TRUE); */ } break; @@ -482,18 +460,18 @@ switch (sub_type) { case QQ_FILE_BASIC_INFO: - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, sub_type); - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, sub_type); + bytes += qq_put32(raw_data + bytes, 0x00000000); break; case QQ_FILE_DATA_INFO: - bytes += create_packet_w(raw_data, &cursor, seq); - bytes += create_packet_b(raw_data, &cursor, sub_type); - bytes += create_packet_dw(raw_data, &cursor, fragment_index); + bytes += qq_put16(raw_data + bytes, seq); + bytes += qq_put8(raw_data + bytes, sub_type); + bytes += qq_put32(raw_data + bytes, fragment_index); break; case QQ_FILE_EOF: - bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2); - bytes += create_packet_b(raw_data, &cursor, sub_type); + bytes += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2); + bytes += qq_put8(raw_data + bytes, sub_type); break; } } @@ -520,9 +498,11 @@ */ -static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, guint8 *cursor, - gint len, qq_file_header *fh) +static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint len) { + gint bytes ; + gint decryped_bytes; + qq_file_header fh; guint8 *decrypted_data; gint decrypted_len; qq_data *qd = (qq_data *) gc->proto_data; @@ -531,60 +511,65 @@ guint8 hellobyte; ft_info *info = (ft_info *) qd->xfer->data; + bytes = 0; + bytes += _qq_get_file_header(&fh, data + bytes); + decrypted_data = g_newa(guint8, len); decrypted_len = len; - if (qq_decrypt(cursor, len - (cursor - data), qd->session_md5, decrypted_data, &decrypted_len)) { - gchar *hex_dump; - cursor = decrypted_data + 16; /* skip md5 section */ - read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type); - read_packet_w(decrypted_data, &cursor, decrypted_len, &seq); - cursor += 4+1+1+19+1; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type)); - hex_dump = hex_dump_to_str(decrypted_data, decrypted_len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s", hex_dump); - g_free(hex_dump); - switch (packet_type) { - case QQ_FILE_CMD_NOTIFY_IP_ACK: - cursor = decrypted_data; - qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info); -/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0); - break; - case QQ_FILE_CMD_SENDER_SAY_HELLO: - /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ - cursor += 47; - read_packet_b(decrypted_data, &cursor, - decrypted_len, &hellobyte); + if ( !qq_decrypt(data, len, qd->session_md5, decrypted_data, &decrypted_len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rcv file ctrl packet\n"); + return; + } + + /* only for debug info */ + decryped_bytes = 16; /* skip md5 section */ + decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes); + decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes); + decryped_bytes += 4+1+1+19+1; /* skip something */ + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type)); + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + decrypted_data, decrypted_len, + "decrypted control packet received:"); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0); - break; - case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: - /* I'm sender, do nothing */ - break; - case QQ_FILE_CMD_RECEIVER_SAY_HELLO: - /* I'm sender, ack the hello packet and send the first data */ - cursor += 47; - read_packet_b(decrypted_data, &cursor, - decrypted_len, &hellobyte); - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte); - _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0); - break; - case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: - /* I'm receiver, do nothing */ - break; - case QQ_FILE_CMD_PING: - /* I'm receiver, ack the PING */ - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0); - break; - case QQ_FILE_CMD_PONG: - qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0); - break; - default: - purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type); - } - } + switch (packet_type) { + case QQ_FILE_CMD_NOTIFY_IP_ACK: + decryped_bytes = 0; + qq_get_conn_info(info, decrypted_data + decryped_bytes); + /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); + break; + case QQ_FILE_CMD_SENDER_SAY_HELLO: + /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ + decryped_bytes += 47; + decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0); + break; + case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: + /* I'm sender, do nothing */ + break; + case QQ_FILE_CMD_RECEIVER_SAY_HELLO: + /* I'm sender, ack the hello packet and send the first data */ + decryped_bytes += 47; + decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); + _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0); + break; + case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: + /* I'm receiver, do nothing */ + break; + case QQ_FILE_CMD_PING: + /* I'm receiver, ack the PING */ + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0); + break; + case QQ_FILE_CMD_PONG: + qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); + break; + default: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type); + } } static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset) @@ -609,15 +594,15 @@ purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1); return; } - + info->window |= mask; _qq_xfer_write_file(buffer, index, len, xfer); - + xfer->bytes_sent += len; xfer->bytes_remaining -= len; purple_xfer_update_progress(xfer); - + mask = 0x1 << (info->max_fragment_index % sizeof(info->window)); while (info->window & mask) { @@ -639,7 +624,7 @@ guint8 *buffer; guint i; gint readbytes; - + if (purple_xfer_get_bytes_remaining(xfer) <= 0) return; if (info->window == 0 && info->max_fragment_index == 0) { @@ -655,7 +640,7 @@ readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer); if (readbytes > 0) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO, - info->max_fragment_index + i + 1, 0, buffer, readbytes); + info->max_fragment_index + i + 1, 0, buffer, readbytes); } if (mask & 0x8000) mask = 0x0001; else mask = mask << 1; @@ -706,8 +691,8 @@ info->fragment_len, xfer); if (readbytes > 0) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO, - info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes); - + info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes); + info->max_fragment_index ++; if (mask & 0x8000) mask = 0x0001; else mask = mask << 1; @@ -718,9 +703,10 @@ fragment_index, info->window, info->max_fragment_index); } -static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint8 *cursor, - gint len, guint32 to_uid) +static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len) { + gint bytes ; + qq_file_header fh; guint16 packet_type; guint16 packet_seq; guint8 sub_type; @@ -729,24 +715,27 @@ guint32 fragment_offset; qq_data *qd = (qq_data *) gc->proto_data; ft_info *info = (ft_info *) qd->xfer->data; - - cursor += 1; /* skip an unknown byte */ - read_packet_w(data, &cursor, len, &packet_type); + + bytes = 0; + bytes += _qq_get_file_header(&fh, data + bytes); + + bytes += 1; /* skip an unknown byte */ + bytes += qq_get16(&packet_type, data + bytes); switch(packet_type) { case QQ_FILE_CMD_FILE_OP: - read_packet_w(data, &cursor, len, &packet_seq); - read_packet_b(data, &cursor, len, &sub_type); + bytes += qq_get16(&packet_seq, data + bytes); + bytes += qq_get8(&sub_type, data + bytes); switch (sub_type) { case QQ_FILE_BASIC_INFO: - cursor += 4; /* file length, we have already known it from xfer */ - read_packet_dw(data, &cursor, len, &info->fragment_num); - read_packet_dw(data, &cursor, len, &info->fragment_len); + bytes += 4; /* file length, we have already known it from xfer */ + bytes += qq_get32(&info->fragment_num, data + bytes); + bytes += qq_get32(&info->fragment_len, data + bytes); - /* FIXME: We must check the md5 here, if md5 doesn't match - * we will ignore the packet or send sth as error number - */ + /* FIXME: We must check the md5 here, + * if md5 doesn't match we will ignore + * the packet or send sth as error number */ info->max_fragment_index = 0; info->window = 0; @@ -757,27 +746,27 @@ 0, 0, NULL, 0); break; case QQ_FILE_DATA_INFO: - read_packet_dw(data, &cursor, len, &fragment_index); - read_packet_dw(data, &cursor, len, &fragment_offset); - read_packet_w(data, &cursor, len, &fragment_len); + bytes += qq_get32(&fragment_index, data + bytes); + bytes += qq_get32(&fragment_offset, data + bytes); + bytes += qq_get16(&fragment_len, data + bytes); purple_debug(PURPLE_DEBUG_INFO, "QQ", "received %dth fragment with length %d, offset %d\n", fragment_index, fragment_len, fragment_offset); - + _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, fragment_index, packet_seq, NULL, 0); - _qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset); + _qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset); break; case QQ_FILE_EOF: purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of receiving\n"); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, - 0, 0, NULL, 0); + 0, 0, NULL, 0); break; } break; case QQ_FILE_CMD_FILE_OP_ACK: - read_packet_w(data, &cursor, len, &packet_seq); - read_packet_b(data, &cursor, len, &sub_type); + bytes += qq_get16(&packet_seq, data + bytes); + bytes += qq_get8(&sub_type, data + bytes); switch (sub_type) { case QQ_FILE_BASIC_INFO: @@ -787,16 +776,16 @@ _qq_send_file_progess(gc); break; case QQ_FILE_DATA_INFO: - read_packet_dw(data, &cursor, len, &fragment_index); + bytes += qq_get32(&fragment_index, data + bytes); _qq_update_send_progess(gc, fragment_index); if (purple_xfer_is_completed(qd->xfer)) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0); - /* else + /* else _qq_send_file_progess(gc); */ break; case QQ_FILE_EOF: /* FIXME: OK, we can end the connection successfully */ - + _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0); purple_xfer_set_completed(qd->xfer, TRUE); break; @@ -820,21 +809,21 @@ void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len) { - guint8 *cursor; - qq_file_header fh; + gint bytes; + guint8 tag; qq_data *qd; qd = (qq_data *) gc->proto_data; - cursor = data; - _qq_get_file_header(data, &cursor, len, &fh); + bytes = 0; + bytes += qq_get8(&tag, data + bytes); - switch (fh.tag) { + switch (tag) { case QQ_FILE_CONTROL_PACKET_TAG: - _qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh); + _qq_process_recv_file_ctl_packet(gc, data + bytes, len - bytes); break; case QQ_FILE_DATA_PACKET_TAG: - _qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid); + _qq_process_recv_file_data(gc, data + bytes, len - bytes); break; default: purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag");
--- a/libpurple/protocols/qq/group_conv.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_conv.c Thu Nov 20 21:13:56 2008 +0000 @@ -27,8 +27,8 @@ #include "conversation.h" -#include "buddy_status.h" #include "group_conv.h" +#include "buddy_list.h" #include "utils.h" /* show group conversation window */ @@ -99,7 +99,9 @@ list = list->next; } - purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE); + if (names != NULL && flags != NULL) { + purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE); + } } /* clean up names */ while (names != NULL) {
--- a/libpurple/protocols/qq/group_find.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_find.c Thu Nov 20 21:13:56 2008 +0000 @@ -138,6 +138,9 @@ group = NULL; while (list != NULL) { group = (qq_group *) list->data; + if (group->group_name_utf8 == NULL) { + continue; + } if (!g_ascii_strcasecmp(purple_conversation_get_name(conv), group->group_name_utf8)) break; list = list->next;
--- a/libpurple/protocols/qq/group_free.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_free.c Thu Nov 20 21:13:56 2008 +0000 @@ -26,7 +26,7 @@ #include "debug.h" -#include "buddy_status.h" +#include "buddy_list.h" #include "group_free.h" #include "group_network.h" @@ -55,8 +55,10 @@ { g_return_if_fail(group != NULL); qq_group_free_member(group); + g_free(group->my_status_desc); g_free(group->group_name_utf8); g_free(group->group_desc_utf8); + g_free(group->notice_utf8); g_free(group); }
--- a/libpurple/protocols/qq/group_im.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_im.c Thu Nov 20 21:13:56 2008 +0000 @@ -58,28 +58,28 @@ void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg) { gint data_len, bytes; - guint8 *raw_data, *cursor, *send_im_tail; + guint8 *raw_data, *send_im_tail; guint16 msg_len; gchar *msg_filtered; g_return_if_fail(group != NULL && msg != NULL); msg_filtered = purple_markup_strip_html(msg); - purple_debug_info("QQ_MESG", "filterd qq qun mesg: %s\n", msg_filtered); + purple_debug_info("QQ_MESG", "Send qun mesg filterd: %s\n", msg_filtered); msg_len = strlen(msg_filtered); + data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN; raw_data = g_newa(guint8, data_len); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN); - bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEND_MSG); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); + bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN); + bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len); send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL, - FALSE, FALSE, FALSE, - QQ_SEND_IM_AFTER_MSG_LEN); - bytes += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN); + FALSE, FALSE, FALSE, + QQ_SEND_IM_AFTER_MSG_LEN); + bytes += qq_putdata(raw_data + bytes, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN); g_free(send_im_tail); g_free(msg_filtered); @@ -87,11 +87,11 @@ qq_send_group_cmd(gc, group, raw_data, data_len); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); + "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes); } /* this is the ACK */ -void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc) { /* return should be the internal group id * but we have nothing to do with it */ @@ -99,29 +99,26 @@ } /* receive an application to join the group */ -void qq_process_recv_group_im_apply_join - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, user_uid; guint8 group_type; gchar *reason_utf8, *msg, *reason; group_member_opt *g; gchar *nombre; + gint bytes = 0; g_return_if_fail(internal_group_id > 0 && data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg apply_join is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &user_uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&user_uid, data + bytes); g_return_if_fail(external_group_id > 0 && user_uid > 0); - convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, external_group_id); reason = g_strdup_printf(_("Reason: %s"), reason_utf8); @@ -134,17 +131,17 @@ nombre = uid_to_purple_name(user_uid); purple_request_action(gc, _("QQ Qun Operation"), - msg, reason, - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), nombre, NULL, - g, 3, - _("Approve"), - G_CALLBACK - (qq_group_approve_application_with_struct), - _("Reject"), - G_CALLBACK - (qq_group_reject_application_with_struct), - _("Search"), G_CALLBACK(qq_group_search_application_with_struct)); + msg, reason, + PURPLE_DEFAULT_ACTION_NONE, + purple_connection_get_account(gc), nombre, NULL, + g, 3, + _("Approve"), + G_CALLBACK + (qq_group_approve_application_with_struct), + _("Reject"), + G_CALLBACK + (qq_group_reject_application_with_struct), + _("Search"), G_CALLBACK(qq_group_search_application_with_struct)); g_free(nombre); g_free(reason); @@ -153,31 +150,28 @@ } /* the request to join a group is rejected */ -void qq_process_recv_group_im_been_rejected - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, admin_uid; guint8 group_type; gchar *reason_utf8, *msg, *reason; qq_group *group; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_rejected is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &admin_uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&admin_uid, data + bytes); g_return_if_fail(external_group_id > 0 && admin_uid > 0); - convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf - (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); + (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid); reason = g_strdup_printf(_("Reason: %s"), reason_utf8); purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason); @@ -194,31 +188,28 @@ } /* the request to join a group is approved */ -void qq_process_recv_group_im_been_approved - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, admin_uid; guint8 group_type; gchar *reason_utf8, *msg; qq_group *group; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_approved is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &admin_uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&admin_uid, data + bytes); g_return_if_fail(external_group_id > 0 && admin_uid > 0); /* it is also a "无" here, so do not display */ - convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT); + bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT); msg = g_strdup_printf - (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid); + (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid); purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL); @@ -233,24 +224,21 @@ } /* process the packet when removed from a group */ -void qq_process_recv_group_im_been_removed - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, uid; guint8 group_type; gchar *msg; qq_group *group; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_removed is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&uid, data + bytes); g_return_if_fail(external_group_id > 0 && uid > 0); @@ -267,24 +255,21 @@ } /* process the packet when added to a group */ -void qq_process_recv_group_im_been_added - (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc) +void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc) { guint32 external_group_id, uid; guint8 group_type; qq_group *group; gchar *msg; + gint bytes = 0; g_return_if_fail(data != NULL && len > 0); - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_added is empty\n"); - return; - } + /* FIXME: check length here */ - read_packet_dw(data, cursor, len, &external_group_id); - read_packet_b(data, cursor, len, &group_type); - read_packet_dw(data, cursor, len, &uid); + bytes += qq_get32(&external_group_id, data + bytes); + bytes += qq_get8(&group_type, data + bytes); + bytes += qq_get32(&uid, data + bytes); g_return_if_fail(external_group_id > 0 && uid > 0); @@ -307,10 +292,9 @@ } /* recv an IM from a group chat */ -void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len, - guint32 internal_group_id, PurpleConnection *gc, guint16 im_type) +void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type) { - gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name, *hex_dump; + gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name; guint16 unknown; guint32 unknown4; PurpleConversation *conv; @@ -319,32 +303,30 @@ qq_group *group; qq_recv_group_im *im_group; gint skip_len; + gint bytes = 0; g_return_if_fail(data != NULL && data_len > 0); + + /* FIXME: check length here */ + qd = (qq_data *) gc->proto_data; - hex_dump = hex_dump_to_str(*cursor, data_len - (*cursor - data)); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "group im hex dump\n%s\n", hex_dump); - - if (*cursor >= (data + data_len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group im_group is empty\n"); - return; - } + /* qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump"); */ im_group = g_newa(qq_recv_group_im, 1); - read_packet_dw(data, cursor, data_len, &(im_group->external_group_id)); - read_packet_b(data, cursor, data_len, &(im_group->group_type)); + bytes += qq_get32(&(im_group->external_group_id), data + bytes); + bytes += qq_get8(&(im_group->group_type), data + bytes); if(QQ_RECV_IM_TEMP_QUN_IM == im_type) { - read_packet_dw(data, cursor, data_len, &(internal_group_id)); + bytes += qq_get32(&(internal_group_id), data + bytes); } - read_packet_dw(data, cursor, data_len, &(im_group->member_uid)); - read_packet_w(data, cursor, data_len, &unknown); /* 0x0001? */ - read_packet_w(data, cursor, data_len, &(im_group->msg_seq)); - read_packet_time(data, cursor, data_len, &im_group->send_time); - read_packet_dw(data, cursor, data_len, &unknown4); /* versionID */ + bytes += qq_get32(&(im_group->member_uid), bytes + data); + bytes += qq_get16(&unknown, data + bytes); /* 0x0001? */ + bytes += qq_get16(&(im_group->msg_seq), data + bytes); + bytes += qq_getime(&im_group->send_time, data + bytes); + bytes += qq_get32(&unknown4, data + bytes); /* versionID */ /* * length includes font_attr * this msg_len includes msg and font_attr @@ -355,7 +337,7 @@ * 3. font_attr */ - read_packet_w(data, cursor, data_len, &(im_group->msg_len)); + bytes += qq_get16(&(im_group->msg_len), data + bytes); g_return_if_fail(im_group->msg_len > 0); /* @@ -371,14 +353,14 @@ skip_len = 10; else skip_len = 0; - *cursor += skip_len; + bytes += skip_len; - im_group->msg = g_strdup((gchar *) *cursor); - *cursor += strlen(im_group->msg) + 1; + im_group->msg = g_strdup((gchar *) data + bytes); + bytes += strlen(im_group->msg) + 1; /* there might not be any font_attr, check it */ im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len; if (im_group->font_attr_len > 0) - im_group->font_attr = g_memdup(*cursor, im_group->font_attr_len); + im_group->font_attr = g_memdup(data + bytes, im_group->font_attr_len); else im_group->font_attr = NULL; @@ -386,7 +368,7 @@ msg_with_purple_smiley = qq_smiley_to_purple(im_group->msg); if (im_group->font_attr_len > 0) msg_utf8_encoded = qq_encode_to_purple(im_group->font_attr, - im_group->font_attr_len, msg_with_purple_smiley); + im_group->font_attr_len, msg_with_purple_smiley); else msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); @@ -395,6 +377,9 @@ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc)); if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/prompt_group_msg_on_recv")) { + /* New conv should open, get group info*/ + qq_send_cmd_group_get_group_info(gc, group); + serv_got_joined_chat(gc, qd->channel++, group->group_name_utf8); conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc)); } @@ -406,11 +391,10 @@ else im_src_name = g_strdup(member->nickname); serv_got_chat_in(gc, - purple_conv_chat_get_id(PURPLE_CONV_CHAT - (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time); + purple_conv_chat_get_id(PURPLE_CONV_CHAT + (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time); g_free(im_src_name); } - g_free(hex_dump); g_free(msg_with_purple_smiley); g_free(msg_utf8_encoded); g_free(im_group->msg);
--- a/libpurple/protocols/qq/group_im.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_im.h Thu Nov 20 21:13:56 2008 +0000 @@ -30,17 +30,31 @@ #include "group.h" void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg); -void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_recv_group_im(guint8 *data, - guint8 **cursor, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); -void qq_process_recv_group_im_apply_join(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_rejected(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_approved(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_removed(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); -void qq_process_recv_group_im_been_added(guint8 *data, - guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); */ +void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc); + +/* void qq_process_recv_group_im(guint8 *data, guint8 **cursor, + * gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); */ +void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); + +/* void qq_process_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_rejected(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_approved(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_removed(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); + +/* void qq_process_recv_group_im_been_added(guint8 *data, guint8 **cursor, gint len, + * guint32 internal_group_id, PurpleConnection *gc); */ +void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_info.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_info.c Thu Nov 20 21:13:56 2008 +0000 @@ -27,12 +27,11 @@ #include "conversation.h" #include "debug.h" -#include "buddy_status.h" #include "char_conv.h" #include "group_find.h" #include "group_internal.h" #include "group_info.h" -#include "buddy_status.h" +#include "buddy_list.h" #include "group_network.h" /* we check who needs to update member info every minutes @@ -43,7 +42,7 @@ { g_return_val_if_fail(member != NULL, FALSE); return (member->nickname == NULL) || - (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL; + (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL; } /* this is done when we receive the reply to get_online_members sub_cmd @@ -65,100 +64,104 @@ /* send packet to get detailed information of one group */ void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_GROUP_INFO); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_GROUP_INFO); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_GROUP_INFO)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } /* send packet to get online group member, called by keep_alive */ +void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc) +{ + qq_data *qd; + qq_group *group; + GList *list; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + list = qd->groups; + while (list != NULL) { + group = (qq_group *) list->data; + if (group->my_status == QQ_GROUP_MEMBER_STATUS_IS_MEMBER || + group->my_status == QQ_GROUP_MEMBER_STATUS_IS_ADMIN) + /* no need to get info time and time again, online members enough */ + qq_send_cmd_group_get_online_members(gc, group); + + list = list->next; + } +} + void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); /* only get online members when conversation window is on */ if (NULL == purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,group->group_name_utf8, purple_connection_get_account(gc))) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8); + "Conversation \"%s\" is not open, ignore to get online members\n", group->group_name_utf8); return; } - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_ONLINE_MEMBER); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_ONLINE_MEMBER); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_ONLINE_MEMBER)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } /* send packet to get info for each group member */ void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len, i; + guint8 *raw_data; + gint bytes, num, data_len; GList *list; qq_buddy *member; g_return_if_fail(group != NULL); - for (i = 0, list = group->members; list != NULL; list = list->next) { + for (num = 0, list = group->members; list != NULL; list = list->next) { member = (qq_buddy *) list->data; if (_is_group_member_need_update_info(member)) - i++; + num++; } - if (i <= 0) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n"); + if (num <= 0) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member info needs to be updated now.\n"); return; } - data_len = 5 + 4 * i; + data_len = 5 + 4 * num; raw_data = g_newa(guint8, data_len); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_MEMBER_INFO); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_MEMBER_INFO); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); list = group->members; while (list != NULL) { member = (qq_buddy *) list->data; if (_is_group_member_need_update_info(member)) - bytes += create_packet_dw(raw_data, &cursor, member->uid); + bytes += qq_put32(raw_data + bytes, member->uid); list = list->next; } - if (bytes != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO)); + return; + } + + qq_send_group_cmd(gc, group, raw_data, bytes); } -void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc) { qq_group *group; qq_buddy *member; @@ -168,17 +171,20 @@ guint16 unknown, max_members; guint32 member_uid, internal_group_id, external_group_id; GSList *pending_id; - gint pascal_len, i; guint32 unknown4; guint8 unknown1; + gint bytes, num; + gchar *notice; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; - read_packet_dw(data, cursor, len, &(internal_group_id)); + bytes = 0; + bytes += qq_get32(&(internal_group_id), data + bytes); g_return_if_fail(internal_group_id > 0); - read_packet_dw(data, cursor, len, &(external_group_id)); - g_return_if_fail(internal_group_id > 0); + + bytes += qq_get32(&(external_group_id), data + bytes); + g_return_if_fail(external_group_id > 0); pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id); if (pending_id != NULL) { @@ -189,45 +195,54 @@ group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); g_return_if_fail(group != NULL); - read_packet_b(data, cursor, len, &(group->group_type)); - read_packet_dw(data, cursor, len, &unknown4); /* unknown 4 bytes */ - read_packet_dw(data, cursor, len, &(group->creator_uid)); - read_packet_b(data, cursor, len, &(group->auth_type)); - read_packet_dw(data, cursor, len, &unknown4); /* oldCategory */ - read_packet_w(data, cursor, len, &unknown); - read_packet_dw(data, cursor, len, &(group->group_category)); - read_packet_w(data, cursor, len, &max_members); - read_packet_b(data, cursor, len, &unknown1); - read_packet_dw(data, cursor, len, &(unknown4)); /* versionID */ + bytes += qq_get8(&(group->group_type), data + bytes); + bytes += qq_get32(&unknown4, data + bytes); /* unknown 4 bytes */ + bytes += qq_get32(&(group->creator_uid), data + bytes); + bytes += qq_get8(&(group->auth_type), data + bytes); + bytes += qq_get32(&unknown4, data + bytes); /* oldCategory */ + bytes += qq_get16(&unknown, data + bytes); + bytes += qq_get32(&(group->group_category), data + bytes); + bytes += qq_get16(&max_members, data + bytes); + bytes += qq_get8(&unknown1, data + bytes); + /* the following, while Eva: + * 4(unk), 4(verID), 1(nameLen), nameLen(qunNameContent), 1(0x00), + * 2(qunNoticeLen), qunNoticeLen(qunNoticeContent, 1(qunDescLen), + * qunDestLen(qunDestcontent)) */ + bytes += qq_get8(&unknown1, data + bytes); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n", + group->group_type, group->creator_uid, group->group_category, max_members); + + /* strlen + <str content> */ + bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\"\n", group->group_name_utf8); + bytes += qq_get16(&unknown, data + bytes); /* 0x0000 */ + bytes += convert_as_pascal_string(data + bytes, ¬ice, QQ_CHARSET_DEFAULT); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "notice \"%s\"\n", notice); + bytes += convert_as_pascal_string(data + bytes, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "group_desc \"%s\"\n", group->group_desc_utf8); - pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - read_packet_w(data, cursor, len, &(unknown)); /* 0x0000 */ - pascal_len = convert_as_pascal_string(*cursor, &(group->notice_utf8), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - - i = 0; + num = 0; /* now comes the member list separated by 0x00 */ - while (*cursor < data + len) { - read_packet_dw(data, cursor, len, &member_uid); - i++; - read_packet_b(data, cursor, len, &organization); - read_packet_b(data, cursor, len, &role); + while (bytes < len) { + bytes += qq_get32(&member_uid, data + bytes); + num++; + bytes += qq_get8(&organization, data + bytes); + bytes += qq_get8(&role, data + bytes); + /* if(organization != 0 || role != 0) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "group member %d: organization=%d, role=%d\n", member_uid, organization, role); + purple_debug(PURPLE_DEBUG_INFO, "QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role); } + */ member = qq_group_find_or_add_member(gc, group, member_uid); if (member != NULL) member->role = role; } - if(*cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!"); - } + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!"); + } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, i); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, num); if (group->creator_uid == qd->uid) group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN; @@ -237,33 +252,37 @@ purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc)); if(NULL == purple_conv) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8); + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Conversation \"%s\" is not open, do not set topic\n", group->group_name_utf8); + return; } - else { - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8); - } + + /* filter \r\n in notice */ + qq_filter_str(notice); + group->notice_utf8 = strdup(notice); + g_free(notice); + + purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8); } -void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc) { guint32 internal_group_id, member_uid; guint8 unknown; - gint bytes, i; + gint bytes, num; qq_group *group; qq_buddy *member; g_return_if_fail(data != NULL && len > 0); - if (data + len - *cursor < 4) { + if (len <= 3) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n"); return; } bytes = 0; - i = 0; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); - bytes += read_packet_b(data, cursor, len, &unknown); /* 0x3c ?? */ + bytes += qq_get32(&internal_group_id, data + bytes); + bytes += qq_get8(&unknown, data + bytes); /* 0x3c ?? */ g_return_if_fail(internal_group_id > 0); group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); @@ -275,61 +294,77 @@ /* set all offline first, then update those online */ _qq_group_set_members_all_offline(group); - while (*cursor < data + len) { - bytes += read_packet_dw(data, cursor, len, &member_uid); - i++; + num = 0; + while (bytes < len) { + bytes += qq_get32(&member_uid, data + bytes); + num++; member = qq_group_find_or_add_member(gc, group, member_uid); if (member != NULL) member->status = QQ_BUDDY_ONLINE_NORMAL; } - if(*cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!"); - } + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!"); + } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, i); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, num); } /* process the reply to get_members_info packet */ -void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; + gint num; guint32 internal_group_id, member_uid; guint16 unknown; - gint pascal_len, i; qq_group *group; qq_buddy *member; + gchar *nick; g_return_if_fail(data != NULL && len > 0); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); g_return_if_fail(group != NULL); - i = 0; + num = 0; /* now starts the member info, as get buddy list reply */ - while (*cursor < data + len) { - read_packet_dw(data, cursor, len, &member_uid); + while (bytes < len) { + bytes += qq_get32(&member_uid, data + bytes); g_return_if_fail(member_uid > 0); member = qq_group_find_member_by_uid(group, member_uid); g_return_if_fail(member != NULL); - i++; - read_packet_w(data, cursor, len, &(member->face)); - read_packet_b(data, cursor, len, &(member->age)); - read_packet_b(data, cursor, len, &(member->gender)); - pascal_len = convert_as_pascal_string(*cursor, &(member->nickname), QQ_CHARSET_DEFAULT); - *cursor += pascal_len; - read_packet_w(data, cursor, len, &unknown); - read_packet_b(data, cursor, len, &(member->flag1)); - read_packet_b(data, cursor, len, &(member->comm_flag)); + num++; + bytes += qq_get16(&(member->face), data + bytes); + bytes += qq_get8(&(member->age), data + bytes); + bytes += qq_get8(&(member->gender), data + bytes); + bytes += convert_as_pascal_string(data + bytes, &nick, QQ_CHARSET_DEFAULT); + bytes += qq_get16(&unknown, data + bytes); + bytes += qq_get8(&(member->ext_flag), data + bytes); + bytes += qq_get8(&(member->comm_flag), data + bytes); + + /* filter \r\n in nick */ + qq_filter_str(nick); + member->nickname = g_strdup(nick); + g_free(nick); + + /* + if (QQ_DEBUG) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "member [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n", + member_uid, member->ext_flag, member->comm_flag, member->nickname); + } + */ member->last_refresh = time(NULL); } - if(*cursor > (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, i); + if(bytes > len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); + } + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, num); }
--- a/libpurple/protocols/qq/group_info.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_info.h Thu Nov 20 21:13:56 2008 +0000 @@ -31,9 +31,12 @@ void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group); void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group); +void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc); + void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group); -void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); + +void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_join.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_join.c Thu Nov 20 21:13:56 2008 +0000 @@ -64,8 +64,8 @@ /* send packet to join a group without auth */ void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); @@ -86,19 +86,11 @@ break; } - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes = 0; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8) @@ -145,7 +137,7 @@ void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8) { - guint8 *raw_data, *cursor; + guint8 *raw_data; gchar *reason_qq; gint bytes, data_len; @@ -164,50 +156,42 @@ data_len = 10 + strlen(reason_qq) + 1; raw_data = g_newa(guint8, data_len); - cursor = raw_data; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP_AUTH); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - bytes += create_packet_b(raw_data, &cursor, opt); - bytes += create_packet_dw(raw_data, &cursor, uid); - bytes += create_packet_b(raw_data, &cursor, strlen(reason_qq)); - bytes += create_packet_data(raw_data, &cursor, (guint8 *) reason_qq, strlen(reason_qq)); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP_AUTH); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); + bytes += qq_put8(raw_data + bytes, opt); + bytes += qq_put32(raw_data + bytes, uid); + bytes += qq_put8(raw_data + bytes, strlen(reason_qq)); + bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq)); - if (bytes != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP_AUTH)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + return; + } + + qq_send_group_cmd(gc, group, raw_data, data_len); } /* send a packet to exit a group */ void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group) { - guint8 *raw_data, *cursor; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; g_return_if_fail(group != NULL); - data_len = 5; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_EXIT_GROUP); + bytes += qq_put32(raw_data + bytes, group->internal_group_id); - bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_EXIT_GROUP); - bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id); - - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_EXIT_GROUP)); - else - qq_send_group_cmd(gc, group, raw_data, data_len); + qq_send_group_cmd(gc, group, raw_data, bytes); } /* If comes here, cmd is OK already */ -void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes, expected_bytes; + gint bytes; guint32 internal_group_id; PurpleChat *chat; qq_group *group; @@ -216,96 +200,94 @@ g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; - bytes = 0; - expected_bytes = 4; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); + if (len < 4) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len); + return; + } - if (bytes == expected_bytes) { - group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); - if (group != NULL) { - chat = - purple_blist_find_chat + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); + + group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); + if (group != NULL) { + chat = purple_blist_find_chat (purple_connection_get_account(gc), g_strdup_printf("%d", group->external_group_id)); - if (chat != NULL) - purple_blist_remove_chat(chat); - qq_group_delete_internal_record(qd, internal_group_id); - } - purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Invalid exit group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); + if (chat != NULL) + purple_blist_remove_chat(chat); + qq_group_delete_internal_record(qd, internal_group_id); } + purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL); } /* Process the reply to group_auth subcmd */ -void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes, expected_bytes; + gint bytes; guint32 internal_group_id; qq_data *qd; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; + if (len < 4) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Invalid join group reply, expect %d bytes, read %d bytes\n", 4, len); + return; + } bytes = 0; - expected_bytes = 4; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); - if (bytes == expected_bytes) - purple_notify_info - (gc, _("QQ Group Auth"), + purple_notify_info(gc, _("QQ Group Auth"), _("Your authorization request has been accepted by the QQ server"), NULL); - else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); } /* process group cmd reply "join group" */ -void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes, expected_bytes; + gint bytes; guint32 internal_group_id; guint8 reply; qq_group *group; g_return_if_fail(data != NULL && len > 0); - bytes = 0; - expected_bytes = 5; - bytes += read_packet_dw(data, cursor, len, &internal_group_id); - bytes += read_packet_b(data, cursor, len, &reply); - - if (bytes != expected_bytes) { + if (len < 5) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes); + "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len); return; - } else { /* join group OK */ - group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); - /* need to check if group is NULL or not. */ - g_return_if_fail(group != NULL); - switch (reply) { - case QQ_GROUP_JOIN_OK: - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8); - group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; - qq_group_refresh(gc, group); - /* this must be shown before getting online members */ - qq_group_conv_show_window(gc, group); - qq_send_cmd_group_get_group_info(gc, group); - break; - case QQ_GROUP_JOIN_NEED_AUTH: - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Fail joining group [%d] %s, needs authentication\n", - group->external_group_id, group->group_name_utf8); - group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; - qq_group_refresh(gc, group); - _qq_group_join_auth(gc, group); - break; - default: - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Error joining group [%d] %s, unknown reply: 0x%02x\n", - group->external_group_id, group->group_name_utf8, reply); - } + } + + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); + bytes += qq_get8(&reply, data + bytes); + + /* join group OK */ + group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); + /* need to check if group is NULL or not. */ + g_return_if_fail(group != NULL); + switch (reply) { + case QQ_GROUP_JOIN_OK: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8); + group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER; + qq_group_refresh(gc, group); + /* this must be shown before getting online members */ + qq_group_conv_show_window(gc, group); + qq_send_cmd_group_get_group_info(gc, group); + break; + case QQ_GROUP_JOIN_NEED_AUTH: + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Fail joining group [%d] %s, needs authentication\n", + group->external_group_id, group->group_name_utf8); + group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; + qq_group_refresh(gc, group); + _qq_group_join_auth(gc, group); + break; + default: + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Error joining group [%d] %s, unknown reply: 0x%02x\n", + group->external_group_id, group->group_name_utf8, reply); } }
--- a/libpurple/protocols/qq/group_join.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_join.h Thu Nov 20 21:13:56 2008 +0000 @@ -46,8 +46,8 @@ void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group); void qq_group_exit(PurpleConnection *gc, GHashTable *data); void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group); -void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc); +void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_network.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_network.c Thu Nov 20 21:13:56 2008 +0000 @@ -39,7 +39,7 @@ #include "group_opt.h" #include "group_search.h" #include "header_info.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" enum { @@ -75,18 +75,42 @@ return "QQ_GROUP_CMD_GET_ONLINE_MEMBER"; case QQ_GROUP_CMD_GET_MEMBER_INFO: return "QQ_GROUP_CMD_GET_MEMBER_INFO"; + case QQ_GROUP_CMD_MODIFY_CARD: + return "QQ_GROUP_CMD_MODIFY_CARD"; + case QQ_GROUP_CMD_REQUEST_ALL_REALNAMES: + return "QQ_GROUP_CMD_REQUEST_ALL_REALNAMES"; + case QQ_GROUP_CMD_REQUEST_CARD: + return "QQ_GROUP_CMD_REQUEST_CARD"; + case QQ_GROUP_CMD_SEND_IM_EX: + return "QQ_GROUP_CMD_SEND_IM_EX"; + case QQ_GROUP_CMD_ADMIN: + return "QQ_GROUP_CMD_ADMIN"; + case QQ_GROUP_CMD_TRANSFER: + return "QQ_GROUP_CMD_TRANSFER"; + case QQ_GROUP_CMD_CREATE_TEMP_QUN: + return "QQ_GROUP_CMD_CREATE_TEMP_QUN"; + case QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER: + return "QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER"; + case QQ_GROUP_CMD_EXIT_TEMP_QUN: + return "QQ_GROUP_CMD_EXIT_TEMP_QUN"; + case QQ_GROUP_CMD_GET_TEMP_QUN_INFO: + return "QQ_GROUP_CMD_GET_TEMP_QUN_INFO"; + case QQ_GROUP_CMD_SEND_TEMP_QUN_IM: + return "QQ_GROUP_CMD_SEND_TEMP_QUN_IM"; + case QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS: + return "QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS"; default: return "Unknown QQ Group Command"; } } /* default process of reply error */ -static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *cursor, gint len, PurpleConnection *gc) +static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *data, gint len, PurpleConnection *gc) { gchar *msg, *msg_utf8; - g_return_if_fail(cursor != NULL && len > 0); + g_return_if_fail(data != NULL && len > 0); - msg = g_strndup((gchar *) cursor, len); /* it will append 0x00 */ + msg = g_strndup((gchar *) data, len); /* it will append 0x00 */ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); g_free(msg); msg = g_strdup_printf(_("Code [0x%02X]: %s"), reply, msg_utf8); @@ -96,14 +120,13 @@ } /* default process, dump only */ -static void _qq_process_group_cmd_reply_default(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +static void _qq_process_group_cmd_reply_default(guint8 *data, gint len, PurpleConnection *gc) { - gchar *hex_dump; g_return_if_fail(data != NULL && len > 0); - hex_dump = hex_dump_to_str(data, len); - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Dump unprocessed group cmd reply:\n%s", hex_dump); - g_free(hex_dump); + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + data, len, + "Dump unprocessed group cmd reply:"); } /* The lower layer command of send group cmd */ @@ -116,7 +139,7 @@ qd = (qq_data *) gc->proto_data; - qq_send_cmd(gc, QQ_CMD_GROUP_CMD, TRUE, 0, TRUE, raw_data, data_len); + qq_send_cmd(qd, QQ_CMD_GROUP_CMD, raw_data, data_len); p = g_new0(group_packet, 1); @@ -136,7 +159,7 @@ qq_data *qd; gint len, bytes; guint32 internal_group_id; - guint8 *data, *cursor, sub_cmd, reply; + guint8 *data, sub_cmd, reply; g_return_if_fail(buf != NULL && buf_len != 0); @@ -149,102 +172,101 @@ return; } - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - if (len <= 2) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len); - return; - } + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n"); + return; + } - bytes = 0; - cursor = data; - bytes += read_packet_b(data, &cursor, len, &sub_cmd); - bytes += read_packet_b(data, &cursor, len, &reply); - - group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); + if (len <= 2) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len); + return; + } - if (reply != QQ_GROUP_CMD_REPLY_OK) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd)); + bytes = 0; + bytes += qq_get8(&sub_cmd, data + bytes); + bytes += qq_get8(&reply, data + bytes); - if (group != NULL) - qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE); + group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID); - switch (reply) { /* this should be all errors */ - case QQ_GROUP_CMD_REPLY_NOT_MEMBER: - if (group != NULL) { - purple_debug(PURPLE_DEBUG_WARNING, - "QQ", - "You are not a member of group \"%s\"\n", group->group_name_utf8); - group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; - qq_group_refresh(gc, group); - } - break; - case QQ_GROUP_CMD_REPLY_SEARCH_ERROR: - if (qd->roomlist != NULL) { - if (purple_roomlist_get_in_progress(qd->roomlist)) - purple_roomlist_set_in_progress(qd->roomlist, FALSE); - } - _qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc); - break; - default: - _qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc); - } - return; - } + if (reply != QQ_GROUP_CMD_REPLY_OK) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd)); + + if (group != NULL) + qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE); - /* seems ok so far, so we process the reply according to sub_cmd */ - switch (sub_cmd) { - case QQ_GROUP_CMD_GET_GROUP_INFO: - qq_process_group_cmd_get_group_info(data, &cursor, len, gc); + switch (reply) { /* this should be all errors */ + case QQ_GROUP_CMD_REPLY_NOT_MEMBER: if (group != NULL) { - qq_send_cmd_group_get_members_info(gc, group); - qq_send_cmd_group_get_online_members(gc, group); + purple_debug(PURPLE_DEBUG_WARNING, + "QQ", + "You are not a member of group \"%s\"\n", group->group_name_utf8); + group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER; + qq_group_refresh(gc, group); } break; - case QQ_GROUP_CMD_CREATE_GROUP: - qq_group_process_create_group_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_MODIFY_GROUP_INFO: - qq_group_process_modify_info_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_MEMBER_OPT: - qq_group_process_modify_members_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_ACTIVATE_GROUP: - qq_group_process_activate_group_reply(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_SEARCH_GROUP: - qq_process_group_cmd_search_group(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_JOIN_GROUP: - qq_process_group_cmd_join_group(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_JOIN_GROUP_AUTH: - qq_process_group_cmd_join_group_auth(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_EXIT_GROUP: - qq_process_group_cmd_exit_group(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_SEND_MSG: - qq_process_group_cmd_im(data, &cursor, len, gc); - break; - case QQ_GROUP_CMD_GET_ONLINE_MEMBER: - qq_process_group_cmd_get_online_members(data, &cursor, len, gc); - if (group != NULL) - qq_group_conv_refresh_online_member(gc, group); - break; - case QQ_GROUP_CMD_GET_MEMBER_INFO: - qq_process_group_cmd_get_members_info(data, &cursor, len, gc); - if (group != NULL) - qq_group_conv_refresh_online_member(gc, group); + case QQ_GROUP_CMD_REPLY_SEARCH_ERROR: + if (qd->roomlist != NULL) { + if (purple_roomlist_get_in_progress(qd->roomlist)) + purple_roomlist_set_in_progress(qd->roomlist, FALSE); + } + _qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc); break; default: - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd)); - _qq_process_group_cmd_reply_default(data, &cursor, len, gc); + _qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc); + } + return; + } + + /* seems ok so far, so we process the reply according to sub_cmd */ + switch (sub_cmd) { + case QQ_GROUP_CMD_GET_GROUP_INFO: + qq_process_group_cmd_get_group_info(data + bytes, len - bytes, gc); + if (group != NULL) { + qq_send_cmd_group_get_members_info(gc, group); + qq_send_cmd_group_get_online_members(gc, group); } - - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n"); + break; + case QQ_GROUP_CMD_CREATE_GROUP: + qq_group_process_create_group_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_MODIFY_GROUP_INFO: + qq_group_process_modify_info_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_MEMBER_OPT: + qq_group_process_modify_members_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_ACTIVATE_GROUP: + qq_group_process_activate_group_reply(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_SEARCH_GROUP: + qq_process_group_cmd_search_group(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_JOIN_GROUP: + qq_process_group_cmd_join_group(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_JOIN_GROUP_AUTH: + qq_process_group_cmd_join_group_auth(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_EXIT_GROUP: + qq_process_group_cmd_exit_group(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_SEND_MSG: + qq_process_group_cmd_im(data + bytes, len - bytes, gc); + break; + case QQ_GROUP_CMD_GET_ONLINE_MEMBER: + qq_process_group_cmd_get_online_members(data + bytes, len - bytes, gc); + if (group != NULL) + qq_group_conv_refresh_online_member(gc, group); + break; + case QQ_GROUP_CMD_GET_MEMBER_INFO: + qq_process_group_cmd_get_members_info(data + bytes, len - bytes, gc); + if (group != NULL) + qq_group_conv_refresh_online_member(gc, group); + break; + default: + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd)); + _qq_process_group_cmd_reply_default(data + bytes, len, gc); } }
--- a/libpurple/protocols/qq/group_network.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_network.h Thu Nov 20 21:13:56 2008 +0000 @@ -42,7 +42,20 @@ QQ_GROUP_CMD_EXIT_GROUP = 0x09, QQ_GROUP_CMD_SEND_MSG = 0x0a, QQ_GROUP_CMD_GET_ONLINE_MEMBER = 0x0b, - QQ_GROUP_CMD_GET_MEMBER_INFO = 0x0c + QQ_GROUP_CMD_GET_MEMBER_INFO = 0x0c, + + QQ_GROUP_CMD_MODIFY_CARD = 0x0E, + QQ_GROUP_CMD_REQUEST_ALL_REALNAMES = 0x0F, + QQ_GROUP_CMD_REQUEST_CARD = 0x10, + QQ_GROUP_CMD_SEND_IM_EX = 0x1A, + QQ_GROUP_CMD_ADMIN = 0x1B, + QQ_GROUP_CMD_TRANSFER = 0x1C, + QQ_GROUP_CMD_CREATE_TEMP_QUN = 0x30, + QQ_GROUP_CMD_MODIFY_TEMP_QUN_MEMBER = 0x31, + QQ_GROUP_CMD_EXIT_TEMP_QUN = 0x32, + QQ_GROUP_CMD_GET_TEMP_QUN_INFO = 0x33, + QQ_GROUP_CMD_SEND_TEMP_QUN_IM = 0x35, + QQ_GROUP_CMD_GET_TEMP_QUN_MEMBERS = 0x37, } qq_group_cmd; typedef struct _group_packet {
--- a/libpurple/protocols/qq/group_opt.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.c Thu Nov 20 21:13:56 2008 +0000 @@ -57,22 +57,24 @@ static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members) { - guint8 *data, *cursor; + guint8 *data; gint i, count, data_len; + gint bytes; g_return_if_fail(members != NULL); - for (i = 0; members[i] != 0xffffffff; i++) {; + for (count = 0; members[count] != 0xffffffff; count++) {; } - count = i; data_len = 6 + count * 4; data = g_newa(guint8, data_len); - cursor = data; - create_packet_b(data, &cursor, QQ_GROUP_CMD_MEMBER_OPT); - create_packet_dw(data, &cursor, group->internal_group_id); - create_packet_b(data, &cursor, operation); + + bytes = 0; + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MEMBER_OPT); + bytes += qq_put32(data + bytes, group->internal_group_id); + bytes += qq_put8(data + bytes, operation); for (i = 0; i < count; i++) - create_packet_dw(data, &cursor, members[i]); - qq_send_group_cmd(gc, group, data, data_len); + bytes += qq_put32(data + bytes, members[i]); + + qq_send_group_cmd(gc, group, data, bytes); } static void _qq_group_do_nothing_with_struct(group_member_opt *g) @@ -97,11 +99,11 @@ qq_send_packet_get_info(g->gc, g->member, TRUE); /* we want to see window */ purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "", - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(g->gc), NULL, NULL, - g, 2, - _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct), - _("Approve"), G_CALLBACK(qq_group_approve_application_with_struct)); + PURPLE_DEFAULT_ACTION_NONE, + purple_connection_get_account(g->gc), NULL, NULL, + g, 2, + _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct), + _("Approve"), G_CALLBACK(qq_group_approve_application_with_struct)); } void qq_group_reject_application_with_struct(group_member_opt *g) @@ -193,13 +195,15 @@ _qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_ADD, add_members); } -void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id; qq_group *group; g_return_if_fail(data != NULL); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); /* we should have its info locally */ @@ -213,8 +217,9 @@ void qq_group_modify_info(PurpleConnection *gc, qq_group *group) { - gint data_len, data_written; - guint8 *data, *cursor; + guint8 *data; + gint data_len; + gint bytes; gchar *group_name, *group_desc, *notice; g_return_if_fail(group != NULL); @@ -228,47 +233,50 @@ + 1 + strlen(notice); data = g_newa(guint8, data_len); - cursor = data; - data_written = 0; + bytes = 0; /* 000-000 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_MODIFY_GROUP_INFO); + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MODIFY_GROUP_INFO); /* 001-004 */ - data_written += create_packet_dw(data, &cursor, group->internal_group_id); + bytes += qq_put32(data + bytes, group->internal_group_id); /* 005-005 */ - data_written += create_packet_b(data, &cursor, 0x01); + bytes += qq_put8(data + bytes, 0x01); /* 006-006 */ - data_written += create_packet_b(data, &cursor, group->auth_type); + bytes += qq_put8(data + bytes, group->auth_type); /* 007-008 */ - data_written += create_packet_w(data, &cursor, 0x0000); + bytes += qq_put16(data + bytes, 0x0000); /* 009-010 */ - data_written += create_packet_w(data, &cursor, group->group_category); + bytes += qq_put16(data + bytes, group->group_category); - data_written += create_packet_b(data, &cursor, strlen(group_name)); - data_written += create_packet_data(data, &cursor, (guint8 *) group_name, strlen(group_name)); + bytes += qq_put8(data + bytes, strlen(group_name)); + bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name)); - data_written += create_packet_w(data, &cursor, 0x0000); + bytes += qq_put16(data + bytes, 0x0000); - data_written += create_packet_b(data, &cursor, strlen(notice)); - data_written += create_packet_data(data, &cursor, (guint8 *) notice, strlen(notice)); + bytes += qq_put8(data + bytes, strlen(notice)); + bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice)); - data_written += create_packet_b(data, &cursor, strlen(group_desc)); - data_written += create_packet_data(data, &cursor, (guint8 *) group_desc, strlen(group_desc)); + bytes += qq_put8(data + bytes, strlen(group_desc)); + bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc)); - if (data_written != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail to create group_modify_info packet, expect %d bytes, wrote %d bytes\n", - data_len, data_written); - else - qq_send_group_cmd(gc, group, data, data_len); + data_len, bytes); + return; + } + + qq_send_group_cmd(gc, group, data, bytes); } -void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id; qq_group *group; g_return_if_fail(data != NULL); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); /* we should have its info locally */ @@ -284,42 +292,44 @@ /* we create a very simple group first, and then let the user to modify */ void qq_group_create_with_name(PurpleConnection *gc, const gchar *name) { - gint data_len, data_written; - guint8 *data, *cursor; + gint data_len; + guint8 *data; + gint bytes; qq_data *qd; g_return_if_fail(name != NULL); qd = (qq_data *) gc->proto_data; data_len = 7 + 1 + strlen(name) + 2 + 1 + 1 + 4; data = g_newa(guint8, data_len); - cursor = data; - data_written = 0; + bytes = 0; /* we create the simpleset group, only group name is given */ /* 000 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_CREATE_GROUP); + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_CREATE_GROUP); /* 001 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_TYPE_PERMANENT); + bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT); /* 002 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_AUTH_TYPE_NEED_AUTH); + bytes += qq_put8(data + bytes, QQ_GROUP_AUTH_TYPE_NEED_AUTH); /* 003-004 */ - data_written += create_packet_w(data, &cursor, 0x0000); + bytes += qq_put16(data + bytes, 0x0000); /* 005-006 */ - data_written += create_packet_w(data, &cursor, 0x0003); + bytes += qq_put16(data + bytes, 0x0003); /* 007 */ - data_written += create_packet_b(data, &cursor, strlen(name)); - data_written += create_packet_data(data, &cursor, (guint8 *) name, strlen(name)); - data_written += create_packet_w(data, &cursor, 0x0000); - data_written += create_packet_b(data, &cursor, 0x00); /* no group notice */ - data_written += create_packet_b(data, &cursor, 0x00); /* no group desc */ - data_written += create_packet_dw(data, &cursor, qd->uid); /* I am member of coz */ + bytes += qq_put8(data + bytes, strlen(name)); + bytes += qq_putdata(data + bytes, (guint8 *) name, strlen(name)); + bytes += qq_put16(data + bytes, 0x0000); + bytes += qq_put8(data + bytes, 0x00); /* no group notice */ + bytes += qq_put8(data + bytes, 0x00); /* no group desc */ + bytes += qq_put32(data + bytes, qd->uid); /* I am member of coz */ - if (data_written != data_len) + if (bytes != data_len) { purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create create_group packet, expect %d bytes, written %d bytes\n", - data_len, data_written); - else - qq_send_group_cmd(gc, NULL, data, data_len); + data_len, bytes); + return; + } + + qq_send_group_cmd(gc, NULL, data, bytes); } static void qq_group_setup_with_gc_and_uid(gc_and_uid *g) @@ -335,8 +345,9 @@ g_free(g); } -void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id, external_group_id; qq_group *group; gc_and_uid *g; @@ -346,8 +357,9 @@ g_return_if_fail(gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - read_packet_dw(data, cursor, len, &internal_group_id); - read_packet_dw(data, cursor, len, &external_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); + bytes += qq_get32(&external_group_id, data + bytes); g_return_if_fail(internal_group_id > 0 && external_group_id); group = qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL); @@ -378,36 +390,29 @@ /* we have to activate group after creation, otherwise the group can not be searched */ void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id) { - gint data_len, data_written; - guint8 *data, *cursor; + guint8 data[16] = {0}; + gint bytes = 0; g_return_if_fail(internal_group_id > 0); - data_len = 5; - data = g_newa(guint8, data_len); - cursor = data; - - data_written = 0; + bytes = 0; /* we create the simplest group, only group name is given */ /* 000 */ - data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_ACTIVATE_GROUP); + bytes += qq_put8(data + bytes, QQ_GROUP_CMD_ACTIVATE_GROUP); /* 001-005 */ - data_written += create_packet_dw(data, &cursor, internal_group_id); + bytes += qq_put32(data + bytes, internal_group_id); - if (data_written != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create activate_group packet, expect %d bytes, written %d bytes\n", - data_len, data_written); - else - qq_send_group_cmd(gc, NULL, data, data_len); + qq_send_group_cmd(gc, NULL, data, bytes); } -void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint32 internal_group_id; qq_group *group; g_return_if_fail(data != NULL); - read_packet_dw(data, cursor, len, &internal_group_id); + bytes = 0; + bytes += qq_get32(&internal_group_id, data + bytes); g_return_if_fail(internal_group_id > 0); /* we should have its info locally */
--- a/libpurple/protocols/qq/group_opt.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_opt.h Thu Nov 20 21:13:56 2008 +0000 @@ -54,12 +54,12 @@ void qq_group_reject_application_with_struct(group_member_opt *g); void qq_group_search_application_with_struct(group_member_opt *g); -void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc); +void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc); void qq_group_manage_group(PurpleConnection *gc, GHashTable *data); void qq_group_create_with_name(PurpleConnection *gc, const gchar *name); void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id); -void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); -void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc); +void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/group_search.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_search.c Thu Nov 20 21:13:56 2008 +0000 @@ -43,24 +43,18 @@ /* send packet to search for qq_group */ void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id) { - guint8 *raw_data, *cursor, type; - gint bytes, data_len; + guint8 raw_data[16] = {0}; + gint bytes = 0; + guint8 type; - data_len = 6; - raw_data = g_newa(guint8, data_len); - cursor = raw_data; type = (external_group_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID; bytes = 0; - bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEARCH_GROUP); - bytes += create_packet_b(raw_data, &cursor, type); - bytes += create_packet_dw(raw_data, &cursor, external_group_id); + bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEARCH_GROUP); + bytes += qq_put8(raw_data + bytes, type); + bytes += qq_put32(raw_data + bytes, external_group_id); - if (bytes != data_len) - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_SEARCH_GROUP)); - else - qq_send_group_cmd(gc, NULL, raw_data, data_len); + qq_send_group_cmd(gc, NULL, raw_data, bytes); } static void _qq_setup_roomlist(qq_data *qd, qq_group *group) @@ -89,55 +83,50 @@ } /* process group cmd reply "search group" */ -void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc) { + gint bytes; guint8 search_type; guint16 unknown; - gint bytes, pascal_len; + qq_group group; qq_data *qd; - qq_group *group; GSList *pending_id; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; - read_packet_b(data, cursor, len, &search_type); - group = g_newa(qq_group, 1); + bytes = 0; + bytes += qq_get8(&search_type, data + bytes); /* now it starts with group_info_entry */ - bytes = 0; - bytes += read_packet_dw(data, cursor, len, &(group->internal_group_id)); - bytes += read_packet_dw(data, cursor, len, &(group->external_group_id)); - bytes += read_packet_b(data, cursor, len, &(group->group_type)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_dw(data, cursor, len, &(group->creator_uid)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_dw(data, cursor, len, &(group->group_category)); - pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT); - bytes += pascal_len; - *cursor += pascal_len; - bytes += read_packet_w(data, cursor, len, &(unknown)); - bytes += read_packet_b(data, cursor, len, &(group->auth_type)); - pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT); - bytes += pascal_len; - *cursor += pascal_len; + bytes += qq_get32(&(group.internal_group_id), data + bytes); + bytes += qq_get32(&(group.external_group_id), data + bytes); + bytes += qq_get8(&(group.group_type), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get32(&(group.creator_uid), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get32(&(group.group_category), data + bytes); + bytes += convert_as_pascal_string(data + bytes, &(group.group_name_utf8), QQ_CHARSET_DEFAULT); + bytes += qq_get16(&(unknown), data + bytes); + bytes += qq_get8(&(group.auth_type), data + bytes); + bytes += convert_as_pascal_string(data + bytes, &(group.group_desc_utf8), QQ_CHARSET_DEFAULT); /* end of one qq_group */ - if(*cursor != (data + len)) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!"); - } + if(bytes != len) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!"); + } - pending_id = qq_get_pending_id(qd->joining_groups, group->external_group_id); + pending_id = qq_get_pending_id(qd->joining_groups, group.external_group_id); if (pending_id != NULL) { - qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE); - if (qq_group_find_by_id(gc, group->internal_group_id, QQ_INTERNAL_ID) == NULL) + qq_set_pending_id(&qd->joining_groups, group.external_group_id, FALSE); + if (qq_group_find_by_id(gc, group.internal_group_id, QQ_INTERNAL_ID) == NULL) qq_group_create_internal_record(gc, - group->internal_group_id, group->external_group_id, group->group_name_utf8); - qq_send_cmd_group_join_group(gc, group); + group.internal_group_id, group.external_group_id, group.group_name_utf8); + qq_send_cmd_group_join_group(gc, &group); } else { - _qq_setup_roomlist(qd, group); + _qq_setup_roomlist(qd, &group); } }
--- a/libpurple/protocols/qq/group_search.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/group_search.h Thu Nov 20 21:13:56 2008 +0000 @@ -29,6 +29,6 @@ #include "connection.h" void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id); -void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); +void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc); #endif
--- a/libpurple/protocols/qq/header_info.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/header_info.c Thu Nov 20 21:13:56 2008 +0000 @@ -34,11 +34,31 @@ #define QQ_CLIENT_0B2F 0x0b2f /* GB QQ2003iii build 0117 */ #define QQ_CLIENT_0B35 0x0b35 /* GB QQ2003iii build 0304 (offical release) */ #define QQ_CLIENT_0B37 0x0b37 /* GB QQ2003iii build 0304 (April 05 updates) */ -#define QQ_CLIENT_0E1B 0x0e1b /* QQ2005? QQ2006? */ +#define QQ_CLIENT_0E1B 0x0e1b /* QQ2005 ? */ #define QQ_CLIENT_0E35 0x0e35 /* EN QQ2005 V05.0.200.020 */ #define QQ_CLIENT_0F15 0x0f15 /* QQ2006 Spring Festival build */ #define QQ_CLIENT_0F5F 0x0f5f /* QQ2006 final build */ +#define QQ_CLIENT_0C0B 0x0C0B /* QQ2004 */ +#define QQ_CLIENT_0C0D 0x0C0D /* QQ2004 preview*/ +#define QQ_CLIENT_0C21 0x0C21 /* QQ2004 */ +#define QQ_CLIENT_0C49 0x0C49 /* QQ2004II */ +#define QQ_CLIENT_0D05 0x0D05 /* QQ2005 beta1 */ +#define QQ_CLIENT_0D51 0x0D51 /* QQ2005 beta2 */ +#define QQ_CLIENT_0D61 0x0D61 /* QQ2005 */ +#define QQ_CLIENT_05A5 0x05A5 /* ? */ +#define QQ_CLIENT_05F1 0x0F15 /* QQ2006 Spring Festival */ +#define QQ_CLIENT_0F4B 0x0F4B /* QQ2006 Beta 3 */ + +#define QQ_CLIENT_1105 0x1105 /* QQ2007 beta4*/ +#define QQ_CLIENT_111D 0x111D /* QQ2007 */ +#define QQ_CLIENT_115B 0x115B /* QQ2008 */ +#define QQ_CLIENT_1203 0x1203 /* QQ2008 */ +#define QQ_CLIENT_1205 0x1205 /* QQ2008 */ +#define QQ_CLIENT_120B 0x120B /* QQ2008 July 8.0.978.400 */ +#define QQ_CLIENT_1412 0x1412 /* QQMac 1.0 preview1 build 670 */ +#define QQ_CLIENT_1441 0x1441 /* QQ2009 preview2 */ + #define QQ_SERVER_0100 0x0100 /* server */ /* given command alias, return the command name accordingly */ @@ -55,10 +75,10 @@ return "QQ_CMD_SEARCH_USER"; case QQ_CMD_GET_USER_INFO: return "QQ_CMD_GET_USER_INFO"; - case QQ_CMD_ADD_FRIEND_WO_AUTH: - return "QQ_CMD_ADD_FRIEND_WO_AUTH"; - case QQ_CMD_DEL_FRIEND: - return "QQ_CMD_DEL_FRIEND"; + case QQ_CMD_ADD_BUDDY_WO_AUTH: + return "QQ_CMD_ADD_BUDDY_WO_AUTH"; + case QQ_CMD_DEL_BUDDY: + return "QQ_CMD_DEL_BUDDY"; case QQ_CMD_BUDDY_AUTH: return "QQ_CMD_BUDDY_AUTH"; case QQ_CMD_CHANGE_ONLINE_STATUS: @@ -73,29 +93,29 @@ return "QQ_CMD_REMOVE_SELF"; case QQ_CMD_LOGIN: return "QQ_CMD_LOGIN"; - case QQ_CMD_GET_FRIENDS_LIST: - return "QQ_CMD_GET_FRIENDS_LIST"; - case QQ_CMD_GET_FRIENDS_ONLINE: - return "QQ_CMD_GET_FRIENDS_ONLINE"; + case QQ_CMD_GET_BUDDIES_LIST: + return "QQ_CMD_GET_BUDDIES_LIST"; + case QQ_CMD_GET_BUDDIES_ONLINE: + return "QQ_CMD_GET_BUDDIES_ONLINE"; case QQ_CMD_GROUP_CMD: return "QQ_CMD_GROUP_CMD"; case QQ_CMD_GET_ALL_LIST_WITH_GROUP: return "QQ_CMD_GET_ALL_LIST_WITH_GROUP"; case QQ_CMD_GET_LEVEL: return "QQ_CMD_GET_LEVEL"; - case QQ_CMD_REQUEST_LOGIN_TOKEN: - return "QQ_CMD_REQUEST_LOGIN_TOKEN"; + case QQ_CMD_TOKEN: + return "QQ_CMD_TOKEN"; case QQ_CMD_RECV_MSG_SYS: return "QQ_CMD_RECV_MSG_SYS"; - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - return "QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS"; + case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS: + return "QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS"; default: - return "UNKNOWN_TYPE"; + return "Unknown"; } } /* given source tag, return its description accordingly */ -const gchar *qq_get_source_str(gint source) +const gchar *qq_get_ver_desc(gint source) { switch (source) { case QQ_CLIENT_062E: @@ -114,17 +134,46 @@ return "GB QQ2003iii build 0304"; case QQ_CLIENT_0B37: return "GB QQ2003iii build 0304 (April 5 update)"; + case QQ_CLIENT_0C0B: + return "QQ2004"; + case QQ_CLIENT_0C0D: + return "QQ2004 preview"; + case QQ_CLIENT_0C21: + return "QQ2004"; + case QQ_CLIENT_0C49: + return "QQ2004II"; + case QQ_CLIENT_0D05: + return "QQ2005 beta1"; + case QQ_CLIENT_0D51: + return "QQ2005 beta2"; + case QQ_CLIENT_0D61: + return "QQ2005"; case QQ_CLIENT_0E1B: return "QQ2005 or QQ2006"; case QQ_CLIENT_0E35: return "En QQ2005 V05.0.200.020"; case QQ_CLIENT_0F15: - return "QQ2006 Spring Festival build"; + return "QQ2006 Spring Festival"; + case QQ_CLIENT_0F4B: + return "QQ2006 beta3"; case QQ_CLIENT_0F5F: return "QQ2006 final build"; + case QQ_CLIENT_1105: + return "QQ2007 beta4"; + case QQ_CLIENT_111D: + return "QQ2007"; + case QQ_CLIENT_115B: + case QQ_CLIENT_1203: + case QQ_CLIENT_1205: + case QQ_CLIENT_120B: + return "QQ2008"; + case QQ_CLIENT_1412: + return "QQMac 1.0 preview1 build 670"; + case QQ_CLIENT_1441: + return "QQ2009 preview2"; case QQ_SERVER_0100: return "QQ Server 0100"; default: - return "QQ unknown version"; + return "Unknown"; } }
--- a/libpurple/protocols/qq/header_info.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/header_info.h Thu Nov 20 21:13:56 2008 +0000 @@ -42,8 +42,8 @@ QQ_CMD_UPDATE_INFO = 0x0004, /* update information */ QQ_CMD_SEARCH_USER = 0x0005, /* search for user */ QQ_CMD_GET_USER_INFO = 0x0006, /* get user information */ - QQ_CMD_ADD_FRIEND_WO_AUTH = 0x0009, /* add friend without auth */ - QQ_CMD_DEL_FRIEND = 0x000a, /* delete a friend */ + QQ_CMD_ADD_BUDDY_WO_AUTH = 0x0009, /* add buddy without auth */ + QQ_CMD_DEL_BUDDY = 0x000a, /* delete a buddy */ QQ_CMD_BUDDY_AUTH = 0x000b, /* buddy authentication */ QQ_CMD_CHANGE_ONLINE_STATUS = 0x000d, /* change my online status */ QQ_CMD_ACK_SYS_MSG = 0x0012, /* ack system message */ @@ -53,19 +53,19 @@ QQ_CMD_REQUEST_KEY = 0x001d, /* request key for file transfer */ QQ_CMD_CELL_PHONE_1 = 0x0021, /* cell phone 1 */ QQ_CMD_LOGIN = 0x0022, /* login */ - QQ_CMD_GET_FRIENDS_LIST = 0x0026, /* retrieve my freinds list */ - QQ_CMD_GET_FRIENDS_ONLINE = 0x0027, /* get my online friends list */ + QQ_CMD_GET_BUDDIES_LIST = 0x0026, /* get buddies list */ + QQ_CMD_GET_BUDDIES_ONLINE = 0x0027, /* get online buddies list */ QQ_CMD_CELL_PHONE_2 = 0x0029, /* cell phone 2 */ QQ_CMD_GROUP_CMD = 0x0030, /* group command */ QQ_CMD_GET_ALL_LIST_WITH_GROUP = 0x0058, QQ_CMD_GET_LEVEL = 0x005C, /* get level for one or more buddies */ - QQ_CMD_REQUEST_LOGIN_TOKEN = 0x0062, /* get login token */ + QQ_CMD_TOKEN = 0x0062, /* get login token */ QQ_CMD_RECV_MSG_SYS = 0x0080, /* receive a system message */ - QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS = 0x0081, /* friends change status */ + QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS = 0x0081, /* buddy change status */ }; const gchar *qq_get_cmd_desc(gint type); -const gchar *qq_get_source_str(gint source); +const gchar *qq_get_ver_desc(gint source); #endif
--- a/libpurple/protocols/qq/im.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/im.c Thu Nov 20 21:13:56 2008 +0000 @@ -40,19 +40,16 @@ #include "header_info.h" #include "im.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" #define QQ_SEND_IM_REPLY_OK 0x00 #define DEFAULT_FONT_NAME_LEN 4 -/* a debug function */ -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - enum { - QQ_NORMAL_IM_TEXT = 0x000b, + QQ_NORMAL_IM_TEXT = 0x000b, QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001, QQ_NORMAL_IM_FILE_APPROVE_TCP = 0x0003, QQ_NORMAL_IM_FILE_REJECT_TCP = 0x0005, @@ -82,7 +79,7 @@ guint16 sender_ver; guint32 sender_uid; guint32 receiver_uid; - guint8 *session_md5; + guint8 session_md5[QQ_KEY_LENGTH]; guint16 normal_im_type; }; @@ -112,7 +109,7 @@ guint32 sender_uid; guint32 receiver_uid; guint32 server_im_seq; - guint8 sender_ip[4]; + struct in_addr sender_ip; guint16 sender_port; guint16 im_type; }; @@ -121,9 +118,9 @@ #define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5" guint8 *qq_get_send_im_tail(const gchar *font_color, - const gchar *font_size, - const gchar *font_name, - gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len) + const gchar *font_size, + const gchar *font_name, + gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len) { gchar *s1; unsigned char *rgb; @@ -141,7 +138,7 @@ send_im_tail = g_new0(guint8, tail_len); g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN), - font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN); + font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN); send_im_tail[tail_len - 1] = (guint8) tail_len; send_im_tail[0] = 0x00; @@ -182,39 +179,39 @@ send_im_tail[5] = 0x00; send_im_tail[6] = 0x86; send_im_tail[7] = 0x22; /* encoding, 0x8622=GB, 0x0000=EN, define BIG5 support here */ - _qq_show_packet("QQ_MESG", send_im_tail, tail_len); + /* qq_show_packet("QQ_MESG", send_im_tail, tail_len); */ return (guint8 *) send_im_tail; } static const gchar *qq_get_recv_im_type_str(gint type) { switch (type) { - case QQ_RECV_IM_TO_BUDDY: - return "QQ_RECV_IM_TO_BUDDY"; - case QQ_RECV_IM_TO_UNKNOWN: - return "QQ_RECV_IM_TO_UNKNOWN"; - case QQ_RECV_IM_UNKNOWN_QUN_IM: - return "QQ_RECV_IM_UNKNOWN_QUN_IM"; - case QQ_RECV_IM_ADD_TO_QUN: - return "QQ_RECV_IM_ADD_TO_QUN"; - case QQ_RECV_IM_DEL_FROM_QUN: - return "QQ_RECV_IM_DEL_FROM_QUN"; - case QQ_RECV_IM_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_CREATE_QUN: - return "QQ_RECV_IM_CREATE_QUN"; - case QQ_RECV_IM_SYS_NOTIFICATION: - return "QQ_RECV_IM_SYS_NOTIFICATION"; - case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: - return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN"; - case QQ_RECV_IM_TEMP_QUN_IM: - return "QQ_RECV_IM_TEMP_QUN_IM"; - case QQ_RECV_IM_QUN_IM: - return "QQ_RECV_IM_QUN_IM"; - default: - return "QQ_RECV_IM_UNKNOWN"; + case QQ_RECV_IM_TO_BUDDY: + return "QQ_RECV_IM_TO_BUDDY"; + case QQ_RECV_IM_TO_UNKNOWN: + return "QQ_RECV_IM_TO_UNKNOWN"; + case QQ_RECV_IM_UNKNOWN_QUN_IM: + return "QQ_RECV_IM_UNKNOWN_QUN_IM"; + case QQ_RECV_IM_ADD_TO_QUN: + return "QQ_RECV_IM_ADD_TO_QUN"; + case QQ_RECV_IM_DEL_FROM_QUN: + return "QQ_RECV_IM_DEL_FROM_QUN"; + case QQ_RECV_IM_APPLY_ADD_TO_QUN: + return "QQ_RECV_IM_APPLY_ADD_TO_QUN"; + case QQ_RECV_IM_CREATE_QUN: + return "QQ_RECV_IM_CREATE_QUN"; + case QQ_RECV_IM_SYS_NOTIFICATION: + return "QQ_RECV_IM_SYS_NOTIFICATION"; + case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: + return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN"; + case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: + return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN"; + case QQ_RECV_IM_TEMP_QUN_IM: + return "QQ_RECV_IM_TEMP_QUN_IM"; + case QQ_RECV_IM_QUN_IM: + return "QQ_RECV_IM_QUN_IM"; + default: + return "QQ_RECV_IM_UNKNOWN"; } } @@ -222,27 +219,26 @@ * we send an ACK which is the first 16 bytes of incoming packet */ static void _qq_send_packet_recv_im_ack(PurpleConnection *gc, guint16 seq, guint8 *data) { - qq_send_cmd(gc, QQ_CMD_RECV_IM, FALSE, seq, FALSE, data, 16); + qq_data *qd; + + qd = (qq_data *) gc->proto_data; + qq_send_cmd_detail(qd, QQ_CMD_RECV_IM, seq, FALSE, data, 16); } /* read the common parts of the normal_im, * returns the bytes read if succeed, or -1 if there is any error */ -static gint _qq_normal_im_common_read(guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common) +static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common) { gint bytes; g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1); bytes = 0; /* now push data into common header */ - bytes += read_packet_w(data, cursor, len, &(common->sender_ver)); - bytes += read_packet_dw(data, cursor, len, &(common->sender_uid)); - bytes += read_packet_dw(data, cursor, len, &(common->receiver_uid)); - - common->session_md5 = g_memdup(*cursor, QQ_KEY_LENGTH); - bytes += QQ_KEY_LENGTH; - *cursor += QQ_KEY_LENGTH; - - bytes += read_packet_w(data, cursor, len, &(common->normal_im_type)); + bytes += qq_get16(&(common->sender_ver), data + bytes); + bytes += qq_get32(&(common->sender_uid), data + bytes); + bytes += qq_get32(&(common->receiver_uid), data + bytes); + bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes); + bytes += qq_get16(&(common->normal_im_type), data + bytes); if (bytes != 28) { /* read common place fail */ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Expect 28 bytes, read %d bytes\n", bytes); @@ -253,8 +249,7 @@ } /* process received normal text IM */ -static void _qq_process_recv_normal_im_text - (guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc) +static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc) { guint16 purple_msg_type; gchar *name; @@ -262,62 +257,73 @@ gchar *msg_utf8_encoded; qq_data *qd; qq_recv_normal_im_text *im_text; + gint bytes = 0; + PurpleBuddy *b; + qq_buddy *qq_b; g_return_if_fail(common != NULL); qd = (qq_data *) gc->proto_data; /* now it is QQ_NORMAL_IM_TEXT */ - if (*cursor >= (data + len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n"); - return; - } else - im_text = g_newa(qq_recv_normal_im_text, 1); + /* + if (*cursor >= (data + len - 1)) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n"); + return; + } else + */ + im_text = g_newa(qq_recv_normal_im_text, 1); im_text->common = common; /* push data into im_text */ - read_packet_w(data, cursor, len, &(im_text->msg_seq)); - read_packet_dw(data, cursor, len, &(im_text->send_time)); - read_packet_w(data, cursor, len, &(im_text->sender_icon)); - read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown2), 3); - read_packet_b(data, cursor, len, &(im_text->is_there_font_attr)); + bytes += qq_get16(&(im_text->msg_seq), data + bytes); + bytes += qq_get32(&(im_text->send_time), data + bytes); + bytes += qq_get16(&(im_text->sender_icon), data + bytes); + bytes += qq_getdata((guint8 *) & (im_text->unknown2), 3, data + bytes); + bytes += qq_get8(&(im_text->is_there_font_attr), data + bytes); /** * from lumaqq for unknown3 * totalFragments = buf.get() & 255; - * fragmentSequence = buf.get() & 255; - * messageId = buf.getChar(); + * fragmentSequence = buf.get() & 255; + * messageId = buf.getChar(); */ - read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown3), 4); - read_packet_b(data, cursor, len, &(im_text->msg_type)); + bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes); + bytes += qq_get8(&(im_text->msg_type), data + bytes); /* we need to check if this is auto-reply * QQ2003iii build 0304, returns the msg without font_attr * even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */ if (im_text->msg_type == QQ_IM_AUTO_REPLY) { im_text->is_there_font_attr = 0x00; /* indeed there is no this flag */ - im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor); + im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); } else { /* it is normal mesasge */ if (im_text->is_there_font_attr) { - im_text->msg = g_strdup(*(gchar **) cursor); - *cursor += strlen(im_text->msg) + 1; - im_text->font_attr_len = data + len - *cursor; - im_text->font_attr = g_memdup(*cursor, im_text->font_attr_len); + im_text->msg = g_strdup((gchar *)(data + bytes)); + bytes += strlen(im_text->msg) + 1; /* length decided by strlen! will it cause a crash? */ + im_text->font_attr_len = len - bytes; + im_text->font_attr = g_memdup(data + bytes, im_text->font_attr_len); } else /* not im_text->is_there_font_attr */ - im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor); + im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes); } /* if im_text->msg_type */ - _qq_show_packet("QQ_MESG recv", data, *cursor - data); name = uid_to_purple_name(common->sender_uid); - if (purple_find_buddy(gc->account, name) == NULL) + b = purple_find_buddy(gc->account, name); + if (b == NULL) { qq_add_buddy_by_recv_packet(gc, common->sender_uid, FALSE, TRUE); - + b = purple_find_buddy(gc->account, name); + } + qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + if (qq_b != NULL) { + qq_b->client_version = common->sender_ver; + } + purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0; msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg); msg_utf8_encoded = im_text->is_there_font_attr ? - qq_encode_to_purple(im_text->font_attr, - im_text->font_attr_len, - msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); + qq_encode_to_purple(im_text->font_attr, + im_text->font_attr_len, + msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT); /* send encoded to purple, note that we use im_text->send_time, * not the time we receive the message @@ -333,81 +339,66 @@ } /* it is a normal IM, maybe text or video request */ -static void _qq_process_recv_normal_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc) +static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc) { - gint bytes; + gint bytes = 0; qq_recv_normal_im_common *common; qq_recv_normal_im_unprocessed *im_unprocessed; - gchar *hex_dump; g_return_if_fail (data != NULL && len != 0); - if (*cursor >= (data + len - 1)) { - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Received normal IM is empty\n"); - return; - } - else - common = g_newa (qq_recv_normal_im_common, 1); + common = g_newa (qq_recv_normal_im_common, 1); - bytes = _qq_normal_im_common_read (data, cursor, len, common); + bytes = _qq_normal_im_common_read(data, len, common); if (bytes < 0) { purple_debug (PURPLE_DEBUG_ERROR, "QQ", - "Fail read the common part of normal IM\n"); + "Fail read the common part of normal IM\n"); return; } switch (common->normal_im_type) { - case QQ_NORMAL_IM_TEXT: - purple_debug (PURPLE_DEBUG_INFO, - "QQ", - "Normal IM, text type:\n [%d] => [%d], src: %s\n", - common->sender_uid, common->receiver_uid, - qq_get_source_str (common->sender_ver)); - _qq_process_recv_normal_im_text (data, cursor, len, common, - gc); - break; - case QQ_NORMAL_IM_FILE_REJECT_UDP: - qq_process_recv_file_reject (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_APPROVE_UDP: - qq_process_recv_file_accept (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_REQUEST_UDP: - qq_process_recv_file_request (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_CANCEL: - qq_process_recv_file_cancel (data, cursor, len, - common->sender_uid, gc); - break; - case QQ_NORMAL_IM_FILE_NOTIFY: - qq_process_recv_file_notify (data, cursor, len, - common->sender_uid, gc); - break; - default: - im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1); - im_unprocessed->common = common; - im_unprocessed->unknown = *cursor; - im_unprocessed->length = data + len - *cursor; - /* a simple process here, maybe more later */ - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Normal IM, unprocessed type [0x%04x]\n", - common->normal_im_type); - hex_dump = hex_dump_to_str(im_unprocessed->unknown, im_unprocessed->length); - purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Dump unknown part.\n%s", hex_dump); - g_free(hex_dump); - g_free (common->session_md5); - return; + case QQ_NORMAL_IM_TEXT: + purple_debug (PURPLE_DEBUG_INFO, "QQ", + "Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n", + common->sender_uid, common->receiver_uid, + qq_get_ver_desc (common->sender_ver), common->sender_ver); + if (bytes >= len - 1) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n"); + return; + } + _qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc); + break; + case QQ_NORMAL_IM_FILE_REJECT_UDP: + qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_APPROVE_UDP: + qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_REQUEST_UDP: + qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_CANCEL: + qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc); + break; + case QQ_NORMAL_IM_FILE_NOTIFY: + qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc); + break; + default: + im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1); + im_unprocessed->common = common; + im_unprocessed->unknown = data + bytes; + im_unprocessed->length = len - bytes; + /* a simple process here, maybe more later */ + purple_debug (PURPLE_DEBUG_WARNING, "QQ", + "Normal IM, unprocessed type [0x%04x], len %d\n", + common->normal_im_type, im_unprocessed->length); + qq_show_packet ("QQ unk-im", im_unprocessed->unknown, im_unprocessed->length); + return; } - - g_free (common->session_md5); } /* process im from system administrator */ -static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len, PurpleConnection *gc) +static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc) { gint len; guint8 reply; @@ -415,14 +406,9 @@ g_return_if_fail(data != NULL && data_len != 0); - if (*cursor >= (data + data_len - 1)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received sys IM is empty\n"); - return; - } + len = data_len; - len = data + data_len - *cursor; - - if (NULL == (segments = split_data(*cursor, len, "\x2f", 2))) + if (NULL == (segments = split_data(data, len, "\x2f", 2))) return; reply = strtol(segments[0], NULL, 10); @@ -436,7 +422,7 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type) { qq_data *qd; - guint8 *cursor, *raw_data, *send_im_tail; + guint8 *raw_data, *send_im_tail; guint16 client_tag, normal_im_type; gint msg_len, raw_len, font_name_len, tail_len, bytes; time_t now; @@ -500,52 +486,51 @@ raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len; raw_data = g_newa(guint8, raw_len); - cursor = raw_data; bytes = 0; /* 000-003: receiver uid */ - bytes += create_packet_dw(raw_data, &cursor, qd->uid); + bytes += qq_put32(raw_data + bytes, qd->uid); /* 004-007: sender uid */ - bytes += create_packet_dw(raw_data, &cursor, to_uid); + bytes += qq_put32(raw_data + bytes, to_uid); /* 008-009: sender client version */ - bytes += create_packet_w(raw_data, &cursor, client_tag); + bytes += qq_put16(raw_data + bytes, client_tag); /* 010-013: receiver uid */ - bytes += create_packet_dw(raw_data, &cursor, qd->uid); + bytes += qq_put32(raw_data + bytes, qd->uid); /* 014-017: sender uid */ - bytes += create_packet_dw(raw_data, &cursor, to_uid); + bytes += qq_put32(raw_data + bytes, to_uid); /* 018-033: md5 of (uid+session_key) */ - bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16); + bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ - bytes += create_packet_w(raw_data, &cursor, normal_im_type); + bytes += qq_put16(raw_data + bytes, normal_im_type); /* 036-037: sequence number */ - bytes += create_packet_w(raw_data, &cursor, qd->send_seq); + bytes += qq_put16(raw_data + bytes, qd->send_seq); /* 038-041: send time */ - bytes += create_packet_dw(raw_data, &cursor, (guint32) now); + bytes += qq_put32(raw_data + bytes, (guint32) now); /* 042-043: sender icon */ - bytes += create_packet_w(raw_data, &cursor, qd->my_icon); + bytes += qq_put16(raw_data + bytes, qd->my_icon); /* 044-046: always 0x00 */ - bytes += create_packet_w(raw_data, &cursor, 0x0000); - bytes += create_packet_b(raw_data, &cursor, 0x00); + bytes += qq_put16(raw_data + bytes, 0x0000); + bytes += qq_put8(raw_data + bytes, 0x00); /* 047-047: we use font attr */ - bytes += create_packet_b(raw_data, &cursor, 0x01); + bytes += qq_put8(raw_data + bytes, 0x01); /* 048-051: always 0x00 */ - bytes += create_packet_dw(raw_data, &cursor, 0x00000000); + bytes += qq_put32(raw_data + bytes, 0x00000000); /* 052-052: text message type (normal/auto-reply) */ - bytes += create_packet_b(raw_data, &cursor, type); + bytes += qq_put8(raw_data + bytes, type); /* 053- : msg ends with 0x00 */ - bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len); + bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len); send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold, - is_italic, is_underline, tail_len); - _qq_show_packet("QQ_MESG debug", send_im_tail, tail_len); - bytes += create_packet_data(raw_data, &cursor, send_im_tail, tail_len); + is_italic, is_underline, tail_len); + qq_show_packet("QQ_send_im_tail debug", send_im_tail, tail_len); + bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len); - _qq_show_packet("QQ_MESG raw", raw_data, cursor - raw_data); + qq_show_packet("QQ_raw_data debug", raw_data, bytes); if (bytes == raw_len) /* create packet OK */ - qq_send_cmd(gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, cursor - raw_data); + qq_send_cmd(qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes); + "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes); if (font_color) g_free(font_color); @@ -560,7 +545,8 @@ { qq_data *qd; gint len; - guint8 *data, *cursor, reply; + guint8 *data, reply; + gint bytes = 0; g_return_if_fail(buf != NULL && buf_len != 0); @@ -569,8 +555,7 @@ data = g_newa(guint8, len); if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - cursor = data; - read_packet_b(data, &cursor, len, &reply); + bytes += qq_get8(&reply, data + bytes); if (reply != QQ_SEND_IM_REPLY_OK) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n"); purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL); @@ -588,7 +573,7 @@ { qq_data *qd; gint len, bytes; - guint8 *data, *cursor; + guint8 *data; qq_recv_im_header *im_header; g_return_if_fail(buf != NULL && buf_len != 0); @@ -597,98 +582,107 @@ len = buf_len; data = g_newa(guint8, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - if (len < 16) { /* we need to ack with the first 16 bytes */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n"); - return; - } else - _qq_send_packet_recv_im_ack(gc, seq, data); + if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n"); + } + + if (len < 16) { /* we need to ack with the first 16 bytes */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n"); + return; + } else { + _qq_send_packet_recv_im_ack(gc, seq, data); + } + + /* check len first */ + if (len < 20) { /* length of im_header */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", len); + return; + } - cursor = data; - bytes = 0; - im_header = g_newa(qq_recv_im_header, 1); - bytes += read_packet_dw(data, &cursor, len, &(im_header->sender_uid)); - bytes += read_packet_dw(data, &cursor, len, &(im_header->receiver_uid)); - bytes += read_packet_dw(data, &cursor, len, &(im_header->server_im_seq)); - /* if the message is delivered via server, it is server IP/port */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) & (im_header->sender_ip), 4); - bytes += read_packet_w(data, &cursor, len, &(im_header->sender_port)); - bytes += read_packet_w(data, &cursor, len, &(im_header->im_type)); + bytes = 0; + im_header = g_newa(qq_recv_im_header, 1); + bytes += qq_get32(&(im_header->sender_uid), data + bytes); + bytes += qq_get32(&(im_header->receiver_uid), data + bytes); + bytes += qq_get32(&(im_header->server_im_seq), data + bytes); + /* if the message is delivered via server, it is server IP/port */ + bytes += qq_getIP(&(im_header->sender_ip), data + bytes); + bytes += qq_get16(&(im_header->sender_port), data + bytes); + bytes += qq_get16(&(im_header->im_type), data + bytes); + /* im_header prepared */ - if (bytes != 20) { /* length of im_header */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail read recv IM header, expect 20 bytes, read %d bytes\n", bytes); - return; - } + if (im_header->receiver_uid != qd->uid) { /* should not happen */ + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid); + return; + } - if (im_header->receiver_uid != qd->uid) { /* should not happen */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid); - return; - } + /* check bytes */ + if (bytes >= len - 1) { + purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n"); + return; + } - switch (im_header->im_type) { + switch (im_header->im_type) { case QQ_RECV_IM_TO_BUDDY: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid); - _qq_process_recv_normal_im(data, &cursor, len, gc); + "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid); + _qq_process_recv_normal_im(data + bytes, len - bytes, gc); /* position and rest length */ break; case QQ_RECV_IM_TO_UNKNOWN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid); - _qq_process_recv_normal_im(data, &cursor, len, gc); + "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid); + _qq_process_recv_normal_im(data + bytes, len - bytes, gc); break; case QQ_RECV_IM_UNKNOWN_QUN_IM: case QQ_RECV_IM_TEMP_QUN_IM: case QQ_RECV_IM_QUN_IM: purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM from group, internal_id [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im(data, &cursor, len, im_header->sender_uid, gc, im_header->im_type); + qq_process_recv_group_im(data + bytes, len - bytes, im_header->sender_uid, gc, im_header->im_type); break; case QQ_RECV_IM_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from group, added by group internal_id [%d]\n", im_header->sender_uid); + "IM from group, added by group internal_id [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id * we need this to create a dummy group and add to blist */ - qq_process_recv_group_im_been_added(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_added(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_DEL_FROM_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid); + "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_been_removed(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_removed(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_APPLY_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid); + "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_apply_join(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_apply_join(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM for group system info, approved by group internal_id [%d]\n", - im_header->sender_uid); + "IM for group system info, approved by group internal_id [%d]\n", + im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_been_approved(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_approved(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM for group system info, rejected by group internal_id [%d]\n", - im_header->sender_uid); + "IM for group system info, rejected by group internal_id [%d]\n", + im_header->sender_uid); /* sender_uid is in fact internal_group_id */ - qq_process_recv_group_im_been_rejected(data, &cursor, len, im_header->sender_uid, gc); + qq_process_recv_group_im_been_rejected(data + bytes, len - bytes, im_header->sender_uid, gc); break; case QQ_RECV_IM_SYS_NOTIFICATION: purple_debug(PURPLE_DEBUG_INFO, "QQ", - "IM from [%d], should be a system administrator\n", im_header->sender_uid); - _qq_process_recv_sys_im(data, &cursor, len, gc); + "IM from [%d], should be a system administrator\n", im_header->sender_uid); + _qq_process_recv_sys_im(data + bytes, len - bytes, gc); break; default: purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "IM from [%d], [0x%02x] %s is not processed\n", - im_header->sender_uid, - im_header->im_type, qq_get_recv_im_type_str(im_header->im_type)); - } - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n"); + "IM from [%d], [0x%02x] %s is not processed\n", + im_header->sender_uid, + im_header->im_type, qq_get_recv_im_type_str(im_header->im_type)); } } +
--- a/libpurple/protocols/qq/keep_alive.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/** - * @file keep_alive.c - * - * 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 - * - * - * OICQ encryption algorithm - * Convert from ASM code provided by PerlOICQ - * - * Puzzlebird, Nov-Dec 2002 - */ - -#include "internal.h" - -#include "debug.h" -#include "server.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_status.h" -#include "crypt.h" -#include "header_info.h" -#include "keep_alive.h" -#include "packet_parse.h" -#include "send_core.h" -#include "utils.h" - -#define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */ - -/* send keep-alive packet to QQ server (it is a heart-beat) */ -void qq_send_packet_keep_alive(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *raw_data, *cursor; - - qd = (qq_data *) gc->proto_data; - raw_data = g_newa(guint8, 4); - cursor = raw_data; - - /* In fact, we can send whatever we like to server - * with this command, server return the same result including - * the amount of online QQ users, my ip and port */ - create_packet_dw(raw_data, &cursor, qd->uid); - - qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, TRUE, 0, TRUE, raw_data, 4); -} - -/* parse the return of keep-alive packet, it includes some system information */ -void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint len; - gchar **segments; - guint8 *data; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - /* the last one is 60, don't know what it is */ - if (NULL == (segments = split_data(data, len, "\x1f", 6))) - return; - /* segments[0] and segment[1] are all 0x30 ("0") */ - qd->all_online = strtol(segments[2], NULL, 10); - if(0 == qd->all_online) - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Keep alive error")); - g_free(qd->my_ip); - qd->my_ip = g_strdup(segments[3]); - qd->my_port = strtol(segments[4], NULL, 10); - g_strfreev(segments); - } else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n"); - - /* we refresh buddies's online status periodically */ - /* qd->last_get_online is updated when setting get_buddies_online packet */ - if ((time(NULL) - qd->last_get_online) >= QQ_UPDATE_ONLINE_INTERVAL) - qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START); -} - -/* refresh all buddies online/offline, - * after receiving reply for get_buddies_online packet */ -void qq_refresh_all_buddy_status(PurpleConnection *gc) -{ - time_t now; - GList *list; - qq_data *qd; - qq_buddy *q_bud; - - qd = (qq_data *) (gc->proto_data); - now = time(NULL); - list = qd->buddies; - - while (list != NULL) { - q_bud = (qq_buddy *) list->data; - if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL - && q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) { - q_bud->status = QQ_BUDDY_ONLINE_OFFLINE; - qq_update_buddy_contact(gc, q_bud); - } - list = list->next; - } -} - -/*TODO: maybe this should be qq_update_buddy_status() ?*/ -void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud) -{ - gchar *name; - PurpleBuddy *bud; - gchar *status_id; - - g_return_if_fail(q_bud != NULL); - - name = uid_to_purple_name(q_bud->uid); - bud = purple_find_buddy(gc->account, name); - g_return_if_fail(bud != NULL); - - if (bud != NULL) { - purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */ - q_bud->last_refresh = time(NULL); - - /* purple supports signon and idle time - * but it is not much use for QQ, I do not use them */ - /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */ - status_id = "available"; - switch(q_bud->status) { - case QQ_BUDDY_OFFLINE: - status_id = "offline"; - break; - case QQ_BUDDY_ONLINE_NORMAL: - status_id = "available"; - break; - case QQ_BUDDY_ONLINE_OFFLINE: - status_id = "offline"; - break; - case QQ_BUDDY_ONLINE_AWAY: - status_id = "away"; - break; - case QQ_BUDDY_ONLINE_INVISIBLE: - status_id = "invisible"; - break; - default: - status_id = "invisible"; - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status); - break; - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "set buddy %d to %s\n", q_bud->uid, status_id); - purple_prpl_got_user_status(gc->account, name, status_id, NULL); - - if (q_bud->comm_flag & QQ_COMM_FLAG_BIND_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE) - purple_prpl_got_user_status(gc->account, name, "mobile", NULL); - else - purple_prpl_got_user_status_deactive(gc->account, name, "mobile"); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid); - } - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "qq_update_buddy_contact, client=%04x\n", q_bud->client_version); - g_free(name); -}
--- a/libpurple/protocols/qq/keep_alive.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/** - * @file keep_alive.h - * - * 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 _QQ_KEEP_ALIVE_H_ -#define _QQ_KEEP_ALIVE_H_ - -#include <glib.h> -#include "connection.h" -#include "qq.h" - -void qq_send_packet_keep_alive(PurpleConnection *gc); - -void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); -void qq_refresh_all_buddy_status(PurpleConnection *gc); - -void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud); - -#endif
--- a/libpurple/protocols/qq/login_logout.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,504 +0,0 @@ -/** - * @file login_logout.c - * - * 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 "debug.h" -#include "internal.h" -#include "server.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_status.h" -#include "char_conv.h" -#include "crypt.h" -#include "group.h" -#include "header_info.h" -#include "login_logout.h" -#include "packet_parse.h" -#include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" -#include "utils.h" - -#define QQ_LOGIN_DATA_LENGTH 416 -#define QQ_LOGIN_REPLY_OK_PACKET_LEN 139 -#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN 11 - -#define QQ_REQUEST_LOGIN_TOKEN_REPLY_OK 0x00 - -#define QQ_LOGIN_REPLY_OK 0x00 -#define QQ_LOGIN_REPLY_REDIRECT 0x01 -#define QQ_LOGIN_REPLY_PWD_ERROR 0x05 -#define QQ_LOGIN_REPLY_MISC_ERROR 0xff /* defined by myself */ - -/* for QQ 2003iii 0117, fixed value */ -/* static const guint8 login_23_51[29] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20, - 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13, - 0x95, 0x67, 0xda, 0x2c, 0x01 -}; */ - -/* for QQ 2003iii 0304, fixed value */ -/* -static const guint8 login_23_51[29] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85, - 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6, - 0x40, 0xb8, 0xac, 0x32, 0x01 -}; -*/ - -/* for QQ 2005? copy from lumaqq */ -static const gint8 login_23_51[29] = { - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, -122, - -52, 76, 53, 44, -45, 115, 108, 20, -10, -10, - -81, -61, -6, 51, -92, 1 -}; - -static const gint8 login_53_68[16] = { - -115, -117, -6, -20, -43, 82, 23, 74, -122, -7, - -89, 117, -26, 50, -47, 109 -}; - -static const gint8 login_100_bytes[100] = { - 64, - 11, 4, 2, 0, 1, 0, 0, 0, 0, 0, - 3, 9, 0, 0, 0, 0, 0, 0, 0, 1, - -23, 3, 1, 0, 0, 0, 0, 0, 1, -13, - 3, 0, 0, 0, 0, 0, 0, 1, -19, 3, - 0, 0, 0, 0, 0, 0, 1, -20, 3, 0, - 0, 0, 0, 0, 0, 3, 5, 0, 0, 0, - 0, 0, 0, 0, 3, 7, 0, 0, 0, 0, - 0, 0, 0, 1, -18, 3, 0, 0, 0, 0, - 0, 0, 1, -17, 3, 0, 0, 0, 0, 0, - 0, 1, -21, 3, 0, 0, 0, 0, 0 -}; - -/* fixed value, not affected by version, or mac address */ -/* -static const guint8 login_53_68[16] = { - 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c, - 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf -}; -*/ - - -typedef struct _qq_login_reply_ok qq_login_reply_ok_packet; -typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet; - -struct _qq_login_reply_ok { - guint8 result; - guint8 *session_key; - guint32 uid; - guint8 client_ip[4]; /* those detected by server */ - guint16 client_port; - guint8 server_ip[4]; - guint16 server_port; - time_t login_time; - guint8 unknown1[26]; - guint8 unknown_server1_ip[4]; - guint16 unknown_server1_port; - guint8 unknown_server2_ip[4]; - guint16 unknown_server2_port; - guint16 unknown2; /* 0x0001 */ - guint16 unknown3; /* 0x0000 */ - guint8 unknown4[32]; - guint8 unknown5[12]; - guint8 last_client_ip[4]; - time_t last_login_time; - guint8 unknown6[8]; -}; - -struct _qq_login_reply_redirect { - guint8 result; - guint32 uid; - guint8 new_server_ip[4]; - guint16 new_server_port; -}; - -extern gint /* defined in send_core.c */ - _create_packet_head_seq(guint8 *buf, - guint8 **cursor, PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq); -extern gint /* defined in send_core.c */ - _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd); - -/* It is fixed to 16 bytes 0x01 for QQ2003, - * Any value works (or a random 16 bytes string) */ -static guint8 *_gen_login_key(void) -{ - return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01); -} - -/* process login reply which says OK */ -static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len) -{ - gint bytes; - guint8 *cursor; - qq_data *qd; - qq_login_reply_ok_packet lrop; - - qd = (qq_data *) gc->proto_data; - cursor = data; - bytes = 0; - - /* 000-000: reply code */ - bytes += read_packet_b(data, &cursor, len, &lrop.result); - /* 001-016: session key */ - lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH); - cursor += QQ_KEY_LENGTH; - bytes += QQ_KEY_LENGTH; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n"); - /* 017-020: login uid */ - bytes += read_packet_dw(data, &cursor, len, &lrop.uid); - /* 021-024: server detected user public IP */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.client_ip, 4); - /* 025-026: server detected user port */ - bytes += read_packet_w(data, &cursor, len, &lrop.client_port); - /* 027-030: server detected itself ip 127.0.0.1 ? */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.server_ip, 4); - /* 031-032: server listening port */ - bytes += read_packet_w(data, &cursor, len, &lrop.server_port); - /* 033-036: login time for current session */ - bytes += read_packet_time(data, &cursor, len, &lrop.login_time); - /* 037-062: 26 bytes, unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown1, 26); - /* 063-066: unknown server1 ip address */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server1_ip, 4); - /* 067-068: unknown server1 port */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port); - /* 069-072: unknown server2 ip address */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server2_ip, 4); - /* 073-074: unknown server2 port */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port); - /* 075-076: 2 bytes unknown */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown2); - /* 077-078: 2 bytes unknown */ - bytes += read_packet_w(data, &cursor, len, &lrop.unknown3); - /* 079-110: 32 bytes unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown4, 32); - /* 111-122: 12 bytes unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown5, 12); - /* 123-126: login IP of last session */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.last_client_ip, 4); - /* 127-130: login time of last session */ - bytes += read_packet_time(data, &cursor, len, &lrop.last_login_time); - /* 131-138: 8 bytes unknown */ - bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown6, 8); - - if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Fail parsing login info, expect %d bytes, read %d bytes\n", - QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes); - } /* but we still go on as login OK */ - - qd->session_key = lrop.session_key; - qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key); - qd->my_ip = gen_ip_str(lrop.client_ip); - qd->my_port = lrop.client_port; - qd->login_time = lrop.login_time; - qd->last_login_time = lrop.last_login_time; - qd->last_login_ip = gen_ip_str(lrop.last_client_ip); - - purple_connection_set_state(gc, PURPLE_CONNECTED); - qd->logged_in = TRUE; /* must be defined after sev_finish_login */ - - /* now initiate QQ Qun, do it first as it may take longer to finish */ - qq_group_init(gc); - - /* Now goes on updating my icon/nickname, not showing info_window */ - qd->modifying_face = FALSE; - qq_send_packet_get_info(gc, qd->uid, FALSE); - /* grab my level */ - qq_send_packet_get_level(gc, qd->uid); - - qq_send_packet_change_status(gc); - - /* refresh buddies */ - qq_send_packet_get_buddies_list(gc, QQ_FRIENDS_LIST_POSITION_START); - /* refresh groups */ - qq_send_packet_get_all_list_with_group(gc, QQ_FRIENDS_LIST_POSITION_START); - - return QQ_LOGIN_REPLY_OK; -} - -/* process login reply packet which includes redirected new server address */ -static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len) -{ - gint bytes, ret; - guint8 *cursor; - gchar *new_server_str; - qq_data *qd; - qq_login_reply_redirect_packet lrrp; - - qd = (qq_data *) gc->proto_data; - cursor = data; - bytes = 0; - /* 000-000: reply code */ - bytes += read_packet_b(data, &cursor, len, &lrrp.result); - /* 001-004: login uid */ - bytes += read_packet_dw(data, &cursor, len, &lrrp.uid); - /* 005-008: redirected new server IP */ - bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4); - /* 009-010: redirected new server port */ - bytes += read_packet_w(data, &cursor, len, &lrrp.new_server_port); - - if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n", - QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes); - ret = QQ_LOGIN_REPLY_MISC_ERROR; - } else { /* start new connection */ - new_server_str = gen_ip_str(lrrp.new_server_ip); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port); - qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE); - g_free(new_server_str); - ret = QQ_LOGIN_REPLY_REDIRECT; - } - - return ret; -} - -/* process login reply which says wrong password */ -static gint _qq_process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len) -{ - gchar *server_reply, *server_reply_utf8; - server_reply = g_new0(gchar, len); - g_memmove(server_reply, data + 1, len - 1); - server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT); - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8); - g_free(server_reply); - g_free(server_reply_utf8); - - return QQ_LOGIN_REPLY_PWD_ERROR; -} - -/* request before login */ -void qq_send_packet_request_login_token(PurpleConnection *gc) -{ - qq_data *qd; - guint8 *buf, *cursor; - guint16 seq_ret; - gint bytes; - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); - - cursor = buf; - bytes = 0; - bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret); - bytes += create_packet_dw(buf, &cursor, qd->uid); - bytes += create_packet_b(buf, &cursor, 0); - bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); - - if (bytes == (cursor - buf)) /* packet creation OK */ - _qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN); - else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n"); -} - -/* send login packet to QQ server */ -static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token) -{ - qq_data *qd; - guint8 *buf, *cursor, *raw_data, *encrypted_data; - guint16 seq_ret; - gint encrypted_len, bytes; - gint pos; - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); - raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH); - encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */ - qd->inikey = _gen_login_key(); - - /* now generate the encrypted data - * 000-015 use pwkey as key to encrypt empty string */ - qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len); - /* 016-016 */ - raw_data[16] = 0x00; - /* 017-020, used to be IP, now zero */ - *((guint32 *) (raw_data + 17)) = 0x00000000; - /* 021-022, used to be port, now zero */ - *((guint16 *) (raw_data + 21)) = 0x0000; - /* 023-051, fixed value, unknown */ - g_memmove(raw_data + 23, login_23_51, 29); - /* 052-052, login mode */ - raw_data[52] = qd->login_mode; - /* 053-068, fixed value, maybe related to per machine */ - g_memmove(raw_data + 53, login_53_68, 16); - - /* 069, login token length */ - raw_data[69] = token_length; - pos = 70; - /* 070-093, login token, normally 24 bytes */ - g_memmove(raw_data + pos, token, token_length); - pos += token_length; - /* 100 bytes unknown */ - g_memmove(raw_data + pos, login_100_bytes, 100); - pos += 100; - /* all zero left */ - memset(raw_data+pos, 0, QQ_LOGIN_DATA_LENGTH - pos); - - qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len); - - cursor = buf; - bytes = 0; - bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret); - bytes += create_packet_dw(buf, &cursor, qd->uid); - bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH); - bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len); - bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); - - if (bytes == (cursor - buf)) /* packet creation OK */ - _qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN); - else - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n"); -} - -void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gchar *hex_dump; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - - if (buf[0] == QQ_REQUEST_LOGIN_TOKEN_REPLY_OK) { - if (buf[1] != buf_len-2) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Malformed login token reply packet. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2); - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Attempting to proceed with the actual packet length.\n"); - } - hex_dump = hex_dump_to_str(buf+2, buf_len-2); - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<<< got a token with %d bytes -> [default] decrypt and dump\n%s", buf_len-2, hex_dump); - qq_send_packet_login(gc, buf_len-2, buf+2); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]); - hex_dump = hex_dump_to_str(buf, buf_len); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - ">>> %d bytes -> [default] decrypt and dump\n%s", - buf_len, hex_dump); - try_dump_as_gbk(buf, buf_len); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token")); - } - g_free(hex_dump); -} - -/* send logout packets to QQ server */ -void qq_send_packet_logout(PurpleConnection *gc) -{ - gint i; - qq_data *qd; - - qd = (qq_data *) gc->proto_data; - for (i = 0; i < 4; i++) - qq_send_cmd(gc, QQ_CMD_LOGOUT, FALSE, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH); - - qd->logged_in = FALSE; /* update login status AFTER sending logout packets */ -} - -/* process the login reply packet */ -void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - gint len, ret, bytes; - guint8 *data; - qq_data *qd; - gchar *hex_dump; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - - if (qq_decrypt(buf, buf_len, qd->pwkey, data, &len)) { - /* should be able to decrypt with pwkey */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Decrypt login reply packet with pwkey, %d bytes\n", len); - if (data[0] == QQ_LOGIN_REPLY_OK) { - ret = _qq_process_login_ok(gc, data, len); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown login reply code : %d\n", data[0]); - ret = QQ_LOGIN_REPLY_MISC_ERROR; - } - } else { /* decrypt with pwkey error */ - len = buf_len; /* reset len, decrypt will fail if len is too short */ - if (qq_decrypt(buf, buf_len, qd->inikey, data, &len)) { - /* decrypt ok with inipwd, it might be password error */ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Decrypt login reply packet with inikey, %d bytes\n", len); - bytes = 0; - switch (data[0]) { - case QQ_LOGIN_REPLY_REDIRECT: - ret = _qq_process_login_redirect(gc, data, len); - break; - case QQ_LOGIN_REPLY_PWD_ERROR: - ret = _qq_process_login_wrong_pwd(gc, data, len); - break; - default: - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]); - hex_dump = hex_dump_to_str(data, len); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - ">>> %d bytes -> [default] decrypt and dump\n%s", - buf_len, hex_dump); - g_free(hex_dump); - try_dump_as_gbk(data, len); - - ret = QQ_LOGIN_REPLY_MISC_ERROR; - } - } else { /* no idea how to decrypt */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "No idea how to decrypt login reply\n"); - ret = QQ_LOGIN_REPLY_MISC_ERROR; - } - } - - switch (ret) { - case QQ_LOGIN_REPLY_PWD_ERROR: - if (!purple_account_get_remember_password(gc->account)) - purple_account_set_password(gc->account, NULL); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password.")); - break; - case QQ_LOGIN_REPLY_MISC_ERROR: - if (purple_debug_is_enabled()) - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login. Check debug log.")); - else - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login")); - break; - case QQ_LOGIN_REPLY_OK: - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login repliess OK; everything is fine\n"); - break; - case QQ_LOGIN_REPLY_REDIRECT: - /* the redirect has been done in _qq_process_login_reply */ - break; - default:{; - } - } -}
--- a/libpurple/protocols/qq/login_logout.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/** - * file login_logout.h - * - * 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 _QQ_LOGIN_LOGOUT_H_ -#define _QQ_LOGIN_LOGOUT_H_ - -#include <glib.h> -#include "connection.h" - -#define QQ_LOGIN_MODE_NORMAL 0x0a -#define QQ_LOGIN_MODE_AWAY 0x1e -#define QQ_LOGIN_MODE_HIDDEN 0x28 - -void qq_send_packet_request_login_token(PurpleConnection *gc); -void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); -void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); -void qq_send_packet_logout(PurpleConnection *gc); - -#endif
--- a/libpurple/protocols/qq/packet_parse.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.c Thu Nov 20 21:13:56 2008 +0000 @@ -25,119 +25,155 @@ #include <string.h> #include "packet_parse.h" +#include "debug.h" + +/*------------------------------------------------PUT------------------------------------------------*/ + +/* note: + * 1, in these functions, 'b' stands for byte, 'w' stands for word, 'dw' stands for double word. + * 2, we use '*cursor' and 'buf' as two addresses to calculate the length. + * 3, change '0' to '1', if want to get more info about the packet parsing. */ + +#if 0 +#define PARSER_DEBUG +#endif /* read one byte from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b) +gint qq_get8(guint8 *b, guint8 *buf) { - if (*cursor <= buf + buflen - sizeof(*b)) { - *b = **(guint8 **) cursor; - *cursor += sizeof(*b); - return sizeof(*b); - } else { - return -1; - } + guint8 b_dest; + memcpy(&b_dest, buf, sizeof(b_dest)); + *b = b_dest; +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b); +#endif + return sizeof(b_dest); } + /* read two bytes as "guint16" from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w) +gint qq_get16(guint16 *w, guint8 *buf) { - if (*cursor <= buf + buflen - sizeof(*w)) { - *w = g_ntohs(**(guint16 **) cursor); - *cursor += sizeof(*w); - return sizeof(*w); - } else { - return -1; - } + guint16 w_dest; + memcpy(&w_dest, buf, sizeof(w_dest)); + *w = g_ntohs(w_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w); +#endif + return sizeof(w_dest); } /* read four bytes as "guint32" from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw) +gint qq_get32(guint32 *dw, guint8 *buf) { - if (*cursor <= buf + buflen - sizeof(*dw)) { - *dw = g_ntohl(**(guint32 **) cursor); - *cursor += sizeof(*dw); - return sizeof(*dw); - } else { - return -1; - } + guint32 dw_dest; + memcpy(&dw_dest, buf, sizeof(dw_dest)); + *dw = g_ntohl(dw_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw); +#endif + return sizeof(dw_dest); } -/* read four bytes as "time_t" from buf, - * return the number of bytes read if succeeds, otherwise return -1 - * This function is a wrapper around read_packet_dw() to avoid casting. */ -gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t) +gint qq_getIP(struct in_addr *ip, guint8 *buf) { - guint32 time; - gint ret = read_packet_dw(buf, cursor, buflen, &time); - if (ret != -1 ) { - *t = time; - } - return ret; + memcpy(ip, buf, sizeof(struct in_addr)); + return sizeof(struct in_addr); } /* read datalen bytes from buf, * return the number of bytes read if succeeds, otherwise return -1 */ -gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen) { - if (*cursor <= buf + buflen - datalen) { - g_memmove(data, *cursor, datalen); - *cursor += datalen; - return datalen; - } else { - return -1; - } +gint qq_getdata(guint8 *data, gint datalen, guint8 *buf) +{ + memcpy(data, buf, datalen); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getdata] buf %p\n", (void *)buf); +#endif + return datalen; } + +/* read four bytes as "time_t" from buf, + * return the number of bytes read if succeeds, otherwise return -1 + * This function is a wrapper around read_packet_dw() to avoid casting. */ +gint qq_getime(time_t *t, guint8 *buf) +{ + guint32 dw_dest; + memcpy(&dw_dest, buf, sizeof(dw_dest)); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest); +#endif + dw_dest = g_ntohl(dw_dest); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest); +#endif + memcpy(t, &dw_dest, sizeof(dw_dest)); + return sizeof(dw_dest); +} + +/*------------------------------------------------PUT------------------------------------------------*/ /* pack one byte into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b) +gint qq_put8(guint8 *buf, guint8 b) { - if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint8)) { - **(guint8 **) cursor = b; - *cursor += sizeof(guint8); - return sizeof(guint8); - } else { - return -1; - } + memcpy(buf, &b, sizeof(b)); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b); +#endif + return sizeof(b); } + /* pack two bytes as "guint16" into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w) +gint qq_put16(guint8 *buf, guint16 w) { - if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint16)) { - **(guint16 **) cursor = g_htons(w); - *cursor += sizeof(guint16); - return sizeof(guint16); - } else { - return -1; - } + guint16 w_porter; + w_porter = g_htons(w); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter); +#endif + memcpy(buf, &w_porter, sizeof(w_porter)); + return sizeof(w_porter); } + /* pack four bytes as "guint32" into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw) +gint qq_put32(guint8 *buf, guint32 dw) { - if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint32)) { - **(guint32 **) cursor = g_htonl(dw); - *cursor += sizeof(guint32); - return sizeof(guint32); - } else { - return -1; - } + guint32 dw_porter; + dw_porter = g_htonl(dw); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter); +#endif + memcpy(buf, &dw_porter, sizeof(dw_porter)); + return sizeof(dw_porter); +} + +gint qq_putIP(guint8* buf, struct in_addr *ip) +{ + memcpy(buf, ip, sizeof(struct in_addr)); + return sizeof(struct in_addr); } /* pack datalen bytes into buf * return the number of bytes packed, otherwise return -1 */ -gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen) +gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen) { - if (*cursor <= buf + MAX_PACKET_SIZE - datalen) { - g_memmove(*cursor, data, datalen); - *cursor += datalen; - return datalen; - } else { - return -1; - } + memcpy(buf, data, datalen); +#ifdef PARSER_DEBUG + purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][putdata] buf %p\n", (void *)buf); +#endif + return datalen; }
--- a/libpurple/protocols/qq/packet_parse.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/packet_parse.h Thu Nov 20 21:13:56 2008 +0000 @@ -37,14 +37,32 @@ */ #define MAX_PACKET_SIZE 65535 +#include <netinet/in.h> + +gint qq_get8(guint8 *b, guint8 *buf); +gint qq_get16(guint16 *w, guint8 *buf); +gint qq_get32(guint32 *dw, guint8 *buf); +gint qq_getIP(struct in_addr *ip, guint8 *buf); +gint qq_getime(time_t *t, guint8 *buf); +gint qq_getdata(guint8 *data, gint datalen, guint8 *buf); + +gint qq_put8(guint8 *buf, guint8 b); +gint qq_put16(guint8 *buf, guint16 w); +gint qq_put32(guint8 *buf, guint32 dw); +gint qq_putIP(guint8* buf, struct in_addr *ip); +gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen); + +/* gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b); gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w); gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw); gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t); gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen); + gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b); gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w); gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw); gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen); +*/ #endif
--- a/libpurple/protocols/qq/qq.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/qq.c Thu Nov 20 21:13:56 2008 +0000 @@ -40,7 +40,7 @@ #include "buddy_info.h" #include "buddy_opt.h" -#include "buddy_status.h" +#include "buddy_list.h" #include "char_conv.h" #include "crypt.h" #include "group.h" @@ -51,52 +51,98 @@ #include "group_opt.h" #include "header_info.h" #include "im.h" -#include "keep_alive.h" -#include "login_logout.h" +#include "qq_process.h" +#include "qq_base.h" #include "packet_parse.h" #include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" +#include "qq_network.h" #include "send_file.h" #include "utils.h" #include "version.h" #define OPENQ_AUTHOR "Puzzlebird" #define OPENQ_WEBSITE "http://openq.sourceforge.net" -#define QQ_TCP_QUERY_PORT "8000" -#define QQ_UDP_PORT "8000" + +#define QQ_TCP_PORT 8000 +#define QQ_UDP_PORT 8000 + +static void server_list_create(PurpleAccount *account) { + PurpleConnection *gc; + qq_data *qd; + const gchar *user_server; + int port; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create server list\n"); + gc = purple_account_get_connection(account); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = gc->proto_data; -const gchar *udp_server_list[] = { - "sz.tencent.com", - "sz2.tencent.com", - "sz3.tencent.com", - "sz4.tencent.com", - "sz5.tencent.com", - "sz6.tencent.com", - "sz7.tencent.com", - "sz8.tencent.com", - "sz9.tencent.com" -}; -const gint udp_server_amount = (sizeof(udp_server_list) / sizeof(udp_server_list[0])); + qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE); + port = purple_account_get_int(account, "port", 0); + if (port == 0) { + if (qd->use_tcp) { + port = QQ_TCP_PORT; + } else { + port = QQ_UDP_PORT; + } + } + qd->user_port = port; + g_return_if_fail(qd->user_server == NULL); + user_server = purple_account_get_string(account, "server", NULL); + if (user_server != NULL && strlen(user_server) > 0) { + qd->user_server = g_strdup(user_server); + } -const gchar *tcp_server_list[] = { - "tcpconn.tencent.com", - "tcpconn2.tencent.com", - "tcpconn3.tencent.com", - "tcpconn4.tencent.com", - "tcpconn5.tencent.com", - "tcpconn6.tencent.com" -}; -const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0])); + if (qd->user_server != NULL) { + qd->servers = g_list_append(qd->servers, qd->user_server); + return; + } + if (qd->use_tcp) { + qd->servers = g_list_append(qd->servers, "tcpconn.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn2.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn3.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn4.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn5.tencent.com"); + qd->servers = g_list_append(qd->servers, "tcpconn6.tencent.com"); + return; + } + + qd->servers = g_list_append(qd->servers, "sz.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz2.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz3.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz4.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz5.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz6.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz7.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz8.tencent.com"); + qd->servers = g_list_append(qd->servers, "sz9.tencent.com"); +} -static void _qq_login(PurpleAccount *account) +static void server_list_remove_all(qq_data *qd) { + g_return_if_fail(qd != NULL); + + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + + if (qd->user_server != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free user_server\n"); + g_free(qd->user_server); + qd->user_server = NULL; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free server list\n"); + g_list_free(qd->servers); +} + +static void qq_login(PurpleAccount *account) { - const gchar *qq_server, *qq_port; + PurpleConnection *gc; qq_data *qd; - PurpleConnection *gc; PurplePresence *presence; - gboolean use_tcp; g_return_if_fail(account != NULL); @@ -109,13 +155,7 @@ qd->gc = gc; gc->proto_data = qd; - qq_server = purple_account_get_string(account, "server", NULL); - qq_port = purple_account_get_string(account, "port", NULL); - use_tcp = purple_account_get_bool(account, "use_tcp", FALSE); presence = purple_account_get_presence(account); - - qd->use_tcp = use_tcp; - if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) { qd->login_mode = QQ_LOGIN_MODE_HIDDEN; } else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY) @@ -125,26 +165,28 @@ qd->login_mode = QQ_LOGIN_MODE_NORMAL; } - if (qq_server == NULL || strlen(qq_server) == 0) - qq_server = use_tcp ? - tcp_server_list[random() % tcp_server_amount] : - udp_server_list[random() % udp_server_amount]; + server_list_create(account); + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Server list has %d\n", g_list_length(qd->servers)); - if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0) - qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT; - - purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS); - - if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0) - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect.")); + qq_connect(account); } -/* directly goes for qq_disconnect */ -static void _qq_close(PurpleConnection *gc) +/* clean up the given QQ connection and free all resources */ +static void qq_close(PurpleConnection *gc) { - g_return_if_fail(gc != NULL); + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data); + qd = gc->proto_data; + qq_disconnect(gc); + + server_list_remove_all(qd); + + g_free(qd); + + gc->proto_data = NULL; } /* returns the icon name for a buddy or protocol */ @@ -195,78 +237,110 @@ static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { qq_buddy *q_bud; - gchar *ip_str; - char *tmp; - const char *tmp2; + gchar *tmp; + GString *str; g_return_if_fail(b != NULL); q_bud = (qq_buddy *) b->proto_data; - g_return_if_fail(q_bud != NULL); + if (q_bud == NULL) + return; + + /* if (PURPLE_BUDDY_IS_ONLINE(b) && q_bud != NULL) */ + if (q_bud->ip.s_addr != 0) { + str = g_string_new(NULL); + g_string_printf(str, "%s:%d", inet_ntoa(q_bud->ip), q_bud->port); + if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) { + g_string_append(str, " TCP"); + } else { + g_string_append(str, " UDP"); + } + g_string_free(str, TRUE); + } - if (PURPLE_BUDDY_IS_ONLINE(b) && q_bud != NULL) - { - ip_str = gen_ip_str(q_bud->ip); - if (strlen(ip_str) != 0) { - if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) - tmp2 = _("TCP Address"); - else - tmp2 = _("UDP Address"); - tmp = g_strdup_printf("%s:%d", ip_str, q_bud->port); - purple_notify_user_info_add_pair(user_info, tmp2, tmp); - g_free(tmp); - } - g_free(ip_str); + tmp = g_strdup_printf("%d", q_bud->age); + purple_notify_user_info_add_pair(user_info, _("Age"), tmp); + g_free(tmp); - tmp = g_strdup_printf("%d", q_bud->age); - purple_notify_user_info_add_pair(user_info, _("Age"), tmp); + switch (q_bud->gender) { + case QQ_BUDDY_GENDER_GG: + purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male")); + break; + case QQ_BUDDY_GENDER_MM: + purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female")); + break; + case QQ_BUDDY_GENDER_UNKNOWN: + purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown")); + break; + default: + tmp = g_strdup_printf("Error (%d)", q_bud->gender); + purple_notify_user_info_add_pair(user_info, _("Gender"), tmp); g_free(tmp); + } - switch (q_bud->gender) { - case QQ_BUDDY_GENDER_GG: - purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male")); - break; - case QQ_BUDDY_GENDER_MM: - purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female")); - break; - case QQ_BUDDY_GENDER_UNKNOWN: - purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown")); - break; - default: - tmp = g_strdup_printf("Error (%d)", q_bud->gender); - purple_notify_user_info_add_pair(user_info, _("Gender"), tmp); - g_free(tmp); - } + if (q_bud->level) { + tmp = g_strdup_printf("%d", q_bud->level); + purple_notify_user_info_add_pair(user_info, _("Level"), tmp); + g_free(tmp); + } - if (q_bud->level) { - tmp = g_strdup_printf("%d", q_bud->level); - purple_notify_user_info_add_pair(user_info, _("Level"), tmp); - g_free(tmp); - } - /* For debugging */ - /* - g_string_append_printf(tooltip, "\n<b>Flag:</b> %01x", q_bud->flag1); - g_string_append_printf(tooltip, "\n<b>CommFlag:</b> %01x", q_bud->comm_flag); - g_string_append_printf(tooltip, "\n<b>Client:</b> %04x", q_bud->client_version); - */ + str = g_string_new(NULL); + if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) { + g_string_append( str, _("Member") ); + } + if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_VIP) { + g_string_append( str, _(" VIP") ); + } + if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) { + g_string_append( str, _(" TCP") ); + } + if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE) { + g_string_append( str, _(" FromMobile") ); + } + if (q_bud->comm_flag & QQ_COMM_FLAG_BIND_MOBILE) { + g_string_append( str, _(" BindMobile") ); + } + if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO) { + g_string_append( str, _(" Video") ); } + + if (q_bud->ext_flag & QQ_EXT_FLAG_SPACE) { + g_string_append( str, _(" Space") ); + } + purple_notify_user_info_add_pair(user_info, _("Flag"), str->str); + + g_string_free(str, TRUE); + +#ifdef DEBUG + tmp = g_strdup_printf( "%s (%04X)", + qq_get_ver_desc(q_bud->client_version), + q_bud->client_version ); + purple_notify_user_info_add_pair(user_info, _("Ver"), tmp); + g_free(tmp); + + tmp = g_strdup_printf( "Ext 0x%X, Comm 0x%X", + q_bud->ext_flag, q_bud->comm_flag ); + purple_notify_user_info_add_pair(user_info, _("Flag"), tmp); + g_free(tmp); +#endif } /* we can show tiny icons on the four corners of buddy icon, */ static const char *_qq_list_emblem(PurpleBuddy *b) { /* each char** are refering to a filename in pixmaps/purple/status/default/ */ - - qq_buddy *q_bud = b->proto_data; + qq_buddy *q_bud; + + if (!b || !(q_bud = b->proto_data)) { + return NULL; + } - if (q_bud) { - if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) - return "qq_member"; - /* - if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO) - return "video"; - */ - } + if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE) + return "mobile"; + if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO) + return "video"; + if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) + return "qq_member"; return NULL; } @@ -351,6 +425,7 @@ group = qq_group_find_by_channel(gc, channel); g_return_val_if_fail(group != NULL, -1); + purple_debug_info("QQ_MESG", "Send qun mesg in utf8: %s\n", message); msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT); msg_with_qq_smiley = purple_smiley_to_qq(msg); qq_send_packet_group_im(gc, group, msg_with_qq_smiley); @@ -437,14 +512,15 @@ qd = (qq_data *) gc->proto_data; info = g_string_new("<html><body>\n"); - g_string_append_printf(info, _("<b>Current Online</b>: %d<br>\n"), qd->all_online); + g_string_append_printf(info, _("<b>Current Online</b>: %d<br>\n"), qd->total_online); g_string_append_printf(info, _("<b>Last Refresh</b>: %s<br>\n"), ctime(&qd->last_get_online)); g_string_append(info, "<hr>\n"); + g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port); g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP"); - g_string_append_printf(info, _("<b>Server IP</b>: %s: %d<br>\n"), qd->server_ip, qd->server_port); - g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip); + g_string_append_printf(info, _("<b>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port); + g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), inet_ntoa(qd->my_ip)); g_string_append(info, "<hr>\n"); g_string_append(info, "<i>Information below may not be accurate</i><br>\n"); @@ -593,30 +669,6 @@ return m; } - -static void _qq_keep_alive(PurpleConnection *gc) -{ - qq_group *group; - qq_data *qd; - GList *list; - - if (NULL == (qd = (qq_data *) gc->proto_data)) - return; - - list = qd->groups; - while (list != NULL) { - group = (qq_group *) list->data; - if (group->my_status == QQ_GROUP_MEMBER_STATUS_IS_MEMBER || - group->my_status == QQ_GROUP_MEMBER_STATUS_IS_ADMIN) - /* no need to get info time and time again, online members enough */ - qq_send_cmd_group_get_online_members(gc, group); - - list = list->next; - } - - qq_send_packet_keep_alive(gc); -} - /* convert chat nickname to qq-uid to get this buddy info */ /* who is the nickname of buddy in QQ chat-room (Qun) */ static void _qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who) @@ -651,8 +703,8 @@ _qq_buddy_menu, /* blist_node_menu */ qq_chat_info, /* chat_info */ qq_chat_info_defaults, /* chat_info_defaults */ - _qq_login, /* login */ - _qq_close, /* close */ + qq_login, /* open */ + qq_close, /* close */ _qq_send_im, /* send_im */ NULL, /* set_info */ NULL, /* send_typing */ @@ -675,8 +727,8 @@ NULL, /* chat_invite */ NULL, /* chat_leave */ NULL, /* chat_whisper */ - _qq_chat_send, /* chat_send */ - _qq_keep_alive, /* keepalive */ + _qq_chat_send, /* chat_send */ + NULL, /* keepalive */ NULL, /* register_user */ _qq_get_chat_buddy_info, /* get_cb_info */ NULL, /* get_cb_away */ @@ -695,7 +747,7 @@ qq_roomlist_cancel, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ - qq_send_file, /* send_file */ + NULL, /* qq_send_file send_file */ NULL, /* new xfer */ NULL, /* offline_message */ NULL, /* PurpleWhiteboardPrplOps */ @@ -706,7 +758,6 @@ NULL, NULL, NULL, - sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL }; @@ -751,13 +802,22 @@ { PurpleAccountOption *option; - option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_string_new(_("Server"), "server", NULL); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_string_new(_("Port"), "port", NULL); + option = purple_account_option_int_new(_("Port"), "port", 0); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_int_new(_("resend interval(s)"), "resend_interval", 10); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_int_new(_("Keep alive interval(s)"), "keep_alive_interval", 60); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_int_new(_("Update interval(s)"), "update_interval", 300); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); my_protocol = plugin;
--- a/libpurple/protocols/qq/qq.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/qq.h Thu Nov 20 21:13:56 2008 +0000 @@ -28,10 +28,12 @@ #include <glib.h> #include "internal.h" #include "ft.h" +#include "circbuffer.h" +#include "dnsquery.h" +#include "dnssrv.h" #include "proxy.h" #include "roomlist.h" -#define QQ_FACES 100 #define QQ_KEY_LENGTH 16 #define QQ_DEBUG 1 /* whether we are doing DEBUG */ @@ -42,6 +44,13 @@ typedef struct _qq_data qq_data; typedef struct _qq_buddy qq_buddy; +typedef struct _qq_interval qq_interval; + +struct _qq_interval { + gint resend; + gint keep_alive; + gint update; +}; struct _qq_buddy { guint32 uid; @@ -49,10 +58,10 @@ guint8 age; guint8 gender; gchar *nickname; - guint8 ip[4]; + struct in_addr ip; guint16 port; guint8 status; - guint8 flag1; + guint8 ext_flag; guint8 comm_flag; /* details in qq_buddy_list.c */ guint16 client_version; guint8 onlineTime; @@ -66,42 +75,65 @@ }; struct _qq_data { - gint fd; /* socket file handler */ + PurpleConnection *gc; + + /* common network resource */ + GList *servers; + gchar *user_server; + gint user_port; + gboolean use_tcp; /* network in tcp or udp */ + + gchar *server_name; + gboolean is_redirect; + gchar *real_hostname; /* from real connction */ + guint16 real_port; + guint reconnect_timeout; + gint reconnect_times; + + PurpleProxyConnectData *connect_data; + gint fd; /* socket file handler */ + gint tx_handler; /* socket can_write handle, use in udp connecting and tcp send out */ + + qq_interval itv_config; + qq_interval itv_count; + guint network_timeout; + + GList *transactions; /* check ack packet and resend */ + + /* tcp related */ + PurpleCircBuffer *tcp_txbuf; + guint8 *tcp_rxqueue; + int tcp_rxlen; + + /* udp related */ + PurpleDnsQueryData *udp_query_data; + guint32 uid; /* QQ number */ - guint8 *inikey; /* initial key to encrypt login packet */ - guint8 *pwkey; /* password in md5 (or md5' md5) */ - guint8 *session_key; /* later use this as key in this session */ - guint8 *session_md5; /* concatenate my uid with session_key and md5 it */ + guint8 *token; /* get from server*/ + int token_len; + guint8 inikey[QQ_KEY_LENGTH]; /* initial key to encrypt login packet */ + guint8 password_twice_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */ + guint8 session_key[QQ_KEY_LENGTH]; /* later use this as key in this session */ + guint8 session_md5[QQ_KEY_LENGTH]; /* concatenate my uid with session_key and md5 it */ guint16 send_seq; /* send sequence number */ guint8 login_mode; /* online of invisible */ gboolean logged_in; /* used by qq-add_buddy */ - gboolean use_tcp; /* network in tcp or udp */ - - PurpleProxyType proxy_type; - PurpleConnection *gc; PurpleXfer *xfer; /* file transfer handler */ - struct sockaddr_in dest_sin; - /* from real connction */ - gchar *server_ip; - guint16 server_port; /* get from login reply packet */ time_t login_time; time_t last_login_time; gchar *last_login_ip; /* get from keep_alive packet */ - gchar *my_ip; /* my ip address detected by server */ + struct in_addr my_ip; /* my ip address detected by server */ guint16 my_port; /* my port detected by server */ guint16 my_icon; /* my icon index */ guint16 my_level; /* my level */ - guint32 all_online; /* the number of online QQ users */ + guint32 total_online; /* the number of online QQ users */ time_t last_get_online; /* last time send get_friends_online packet */ - guint8 window[1 << 13]; /* check up for duplicated packet */ - gint sendqueue_timeout; - PurpleRoomlist *roomlist; gint channel; /* the id for opened chat conversation */ @@ -112,16 +144,12 @@ GList *buddies; GList *contact_info_window; GList *group_info_window; - GList *sendqueue; GList *info_query; GList *add_buddy_request; - GQueue *before_login_packets; /* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */ gboolean modifying_info; gboolean modifying_face; }; -void qq_function_not_implemented(PurpleConnection *gc); - #endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_base.c Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,523 @@ +/** + * @file qq_base.c + * + * 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 "debug.h" +#include "internal.h" +#include "server.h" +#include "cipher.h" + +#include "buddy_info.h" +#include "buddy_list.h" +#include "char_conv.h" +#include "crypt.h" +#include "group.h" +#include "header_info.h" +#include "qq_base.h" +#include "packet_parse.h" +#include "qq.h" +#include "qq_network.h" +#include "utils.h" + +#define QQ_LOGIN_DATA_LENGTH 416 +#define QQ_LOGIN_REPLY_OK_PACKET_LEN 139 +#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN 11 + +/* for QQ 2003iii 0117, fixed value */ +/* static const guint8 login_23_51[29] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20, + 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13, + 0x95, 0x67, 0xda, 0x2c, 0x01 +}; */ + +/* for QQ 2003iii 0304, fixed value */ +/* +static const guint8 login_23_51[29] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85, + 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6, + 0x40, 0xb8, 0xac, 0x32, 0x01 +}; +*/ + +/* for QQ 2005? copy from lumaqq */ +/* FIXME: change to guint8 */ +static const guint8 login_23_51[29] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35, + 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf, + 0xc3, 0xfa, 0x33, 0xa4, 0x01 +}; + +static const guint8 login_53_68[16] = { + 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A, + 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D +}; + +static const guint8 login_100_bytes[100] = { + 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + + +/* fixed value, not affected by version, or mac address */ +/* +static const guint8 login_53_68[16] = { + 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c, + 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf +}; +*/ + + +typedef struct _qq_login_reply_ok qq_login_reply_ok_packet; +typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet; + +struct _qq_login_reply_ok { + guint8 result; + guint8 session_key[QQ_KEY_LENGTH]; + guint32 uid; + struct in_addr client_ip; /* those detected by server */ + guint16 client_port; + struct in_addr server_ip; + guint16 server_port; + time_t login_time; + guint8 unknown1[26]; + struct in_addr unknown_server1_ip; + guint16 unknown_server1_port; + struct in_addr unknown_server2_ip; + guint16 unknown_server2_port; + guint16 unknown2; /* 0x0001 */ + guint16 unknown3; /* 0x0000 */ + guint8 unknown4[32]; + guint8 unknown5[12]; + struct in_addr last_client_ip; + time_t last_login_time; + guint8 unknown6[8]; +}; + +struct _qq_login_reply_redirect { + guint8 result; + guint32 uid; + struct in_addr new_server_ip; + guint16 new_server_port; +}; + +/* generate a md5 key using uid and session_key */ +static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key) +{ + guint8 src[QQ_KEY_LENGTH + QQ_KEY_LENGTH]; + gint bytes = 0; + + bytes += qq_put32(src + bytes, uid); + bytes += qq_putdata(src + bytes, session_key, QQ_KEY_LENGTH); + + qq_get_md5(session_md5, QQ_KEY_LENGTH, src, bytes); +} + +/* process login reply which says OK */ +static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len) +{ + gint bytes; + qq_data *qd; + qq_login_reply_ok_packet lrop; + + qd = (qq_data *) gc->proto_data; + /* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */ + bytes = 0; + + /* 000-000: reply code */ + bytes += qq_get8(&lrop.result, data + bytes); + /* 001-016: session key */ + bytes += qq_getdata(lrop.session_key, sizeof(lrop.session_key), data + bytes); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n"); + /* 017-020: login uid */ + bytes += qq_get32(&lrop.uid, data + bytes); + /* 021-024: server detected user public IP */ + bytes += qq_getIP(&lrop.client_ip, data + bytes); + /* 025-026: server detected user port */ + bytes += qq_get16(&lrop.client_port, data + bytes); + /* 027-030: server detected itself ip 127.0.0.1 ? */ + bytes += qq_getIP(&lrop.server_ip, data + bytes); + /* 031-032: server listening port */ + bytes += qq_get16(&lrop.server_port, data + bytes); + /* 033-036: login time for current session */ + bytes += qq_getime(&lrop.login_time, data + bytes); + /* 037-062: 26 bytes, unknown */ + bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes); + /* 063-066: unknown server1 ip address */ + bytes += qq_getIP(&lrop.unknown_server1_ip, data + bytes); + /* 067-068: unknown server1 port */ + bytes += qq_get16(&lrop.unknown_server1_port, data + bytes); + /* 069-072: unknown server2 ip address */ + bytes += qq_getIP(&lrop.unknown_server2_ip, data + bytes); + /* 073-074: unknown server2 port */ + bytes += qq_get16(&lrop.unknown_server2_port, data + bytes); + /* 075-076: 2 bytes unknown */ + bytes += qq_get16(&lrop.unknown2, data + bytes); + /* 077-078: 2 bytes unknown */ + bytes += qq_get16(&lrop.unknown3, data + bytes); + /* 079-110: 32 bytes unknown */ + bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes); + /* 111-122: 12 bytes unknown */ + bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes); + /* 123-126: login IP of last session */ + bytes += qq_getIP(&lrop.last_client_ip, data + bytes); + /* 127-130: login time of last session */ + bytes += qq_getime(&lrop.last_login_time, data + bytes); + /* 131-138: 8 bytes unknown */ + bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes); + + if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */ + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Fail parsing login info, expect %d bytes, read %d bytes\n", + QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes); + } /* but we still go on as login OK */ + + memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key)); + get_session_md5(qd->session_md5, qd->uid, qd->session_key); + + qd->my_ip.s_addr = lrop.client_ip.s_addr; + + qd->my_port = lrop.client_port; + qd->login_time = lrop.login_time; + qd->last_login_time = lrop.last_login_time; + qd->last_login_ip = g_strdup( inet_ntoa(lrop.last_client_ip) ); + + return QQ_LOGIN_REPLY_OK; +} + +/* process login reply packet which includes redirected new server address */ +static gint8 process_login_redirect(PurpleConnection *gc, guint8 *data, gint len) +{ + qq_data *qd; + gint bytes; + qq_login_reply_redirect_packet lrrp; + + qd = (qq_data *) gc->proto_data; + bytes = 0; + /* 000-000: reply code */ + bytes += qq_get8(&lrrp.result, data + bytes); + /* 001-004: login uid */ + bytes += qq_get32(&lrrp.uid, data + bytes); + /* 005-008: redirected new server IP */ + bytes += qq_getIP(&lrrp.new_server_ip, data + bytes); + /* 009-010: redirected new server port */ + bytes += qq_get16(&lrrp.new_server_port, data + bytes); + + if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n", + QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes); + return QQ_LOGIN_REPLY_ERR_MISC; + } + + /* redirect to new server, do not disconnect or connect here + * those connect should be called at packet_process */ + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + qd->real_hostname = g_strdup( inet_ntoa(lrrp.new_server_ip) ); + qd->real_port = lrrp.new_server_port; + + return QQ_LOGIN_REPLY_REDIRECT; +} + +/* process login reply which says wrong password */ +static gint8 process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len) +{ + gchar *server_reply, *server_reply_utf8; + server_reply = g_new0(gchar, len); + g_memmove(server_reply, data + 1, len - 1); + server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT); + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8); + g_free(server_reply); + g_free(server_reply_utf8); + + return QQ_LOGIN_REPLY_ERR_PWD; +} + +/* request before login */ +void qq_send_packet_token(PurpleConnection *gc) +{ + qq_data *qd; + guint8 buf[16] = {0}; + gint bytes = 0; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + bytes += qq_put8(buf + bytes, 0); + + qd->send_seq++; + qq_send_data(qd, QQ_CMD_TOKEN, qd->send_seq, TRUE, buf, bytes); +} + +/* send login packet to QQ server */ +void qq_send_packet_login(PurpleConnection *gc) +{ + qq_data *qd; + guint8 *buf, *raw_data; + gint bytes; + guint8 *encrypted_data; + gint encrypted_len; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + g_return_if_fail(qd->token != NULL && qd->token_len > 0); + + raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH); + memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH); + + encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */ +#ifdef DEBUG + memset(qd->inikey, 0x01, sizeof(qd->inikey)); +#else + for (bytes = 0; bytes < sizeof(qd->inikey); bytes++) { + qd->inikey[bytes] = (guint8) (g_random_int_range(0, 255) % 256); + } +#endif + + bytes = 0; + /* now generate the encrypted data + * 000-015 use password_twice_md5 as key to encrypt empty string */ + qq_encrypt((guint8 *) "", 0, qd->password_twice_md5, raw_data + bytes, &encrypted_len); + bytes += 16; + /* 016-016 */ + bytes += qq_put8(raw_data + bytes, 0x00); + /* 017-020, used to be IP, now zero */ + bytes += qq_put32(raw_data + bytes, 0x00000000); + /* 021-022, used to be port, now zero */ + bytes += qq_put16(raw_data + bytes, 0x0000); + /* 023-051, fixed value, unknown */ + bytes += qq_putdata(raw_data + bytes, login_23_51, 29); + /* 052-052, login mode */ + bytes += qq_put8(raw_data + bytes, qd->login_mode); + /* 053-068, fixed value, maybe related to per machine */ + bytes += qq_putdata(raw_data + bytes, login_53_68, 16); + /* 069, login token length */ + bytes += qq_put8(raw_data + bytes, qd->token_len); + /* 070-093, login token, normally 24 bytes */ + bytes += qq_putdata(raw_data + bytes, qd->token, qd->token_len); + /* 100 bytes unknown */ + bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100); + /* all zero left */ + + qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + bytes = 0; + bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH); + bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len); + + qd->send_seq++; + qq_send_data(qd, QQ_CMD_LOGIN, qd->send_seq, TRUE, buf, bytes); +} + +guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len) +{ + qq_data *qd; + guint8 ret; + int token_len; + + g_return_val_if_fail(buf != NULL && buf_len != 0, -1); + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); + qd = (qq_data *) gc->proto_data; + + ret = buf[0]; + + if (ret != QQ_TOKEN_REPLY_OK) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]); + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + buf, buf_len, + ">>> [default] decrypt and dump"); + error_msg = try_dump_as_gbk(buf, buf_len); + return ret; + } + + token_len = buf_len-2; + if (token_len <= 0) { + error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len); + return -1; + } + + if (buf[1] != token_len) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Invalid token len. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2); + } + qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", + buf+2, token_len, + "<<< got a token -> [default] decrypt and dump"); + + qd->token = g_new0(guint8, token_len); + qd->token_len = token_len; + g_memmove(qd->token, buf + 2, qd->token_len); + return ret; +} + +/* send logout packets to QQ server */ +void qq_send_packet_logout(PurpleConnection *gc) +{ + gint i; + qq_data *qd; + + qd = (qq_data *) gc->proto_data; + for (i = 0; i < 4; i++) + qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->password_twice_md5, QQ_KEY_LENGTH); + + qd->logged_in = FALSE; /* update login status AFTER sending logout packets */ +} + +/* process the login reply packet */ +guint8 qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc) +{ + qq_data *qd; + guint8 *data; + gint data_len; + gchar* error_msg; + + g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR_MISC); + + qd = (qq_data *) gc->proto_data; + + data_len = buf_len; + data = g_newa(guint8, data_len); + + if (qq_decrypt(buf, buf_len, qd->inikey, data, &data_len)) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Decrypt login reply packet with inikey, %d bytes\n", data_len); + } else { + /* reset data_len since it may changed */ + data_len = buf_len; + if (qq_decrypt(buf, buf_len, qd->password_twice_md5, data, &data_len)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len); + } else { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "No idea how to decrypt login reply\n"); + return QQ_LOGIN_REPLY_ERR_MISC; + } + } + + switch (data[0]) { + case QQ_LOGIN_REPLY_OK: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is OK\n"); + return process_login_ok(gc, data, data_len); + case QQ_LOGIN_REPLY_REDIRECT: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is redirect\n"); + return process_login_redirect(gc, data, data_len); + case QQ_LOGIN_REPLY_ERR_PWD: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is error password\n"); + return process_login_wrong_pwd(gc, data, data_len); + case QQ_LOGIN_REPLY_NEED_REACTIVE: + case QQ_LOGIN_REPLY_REDIRECT_EX: + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is not actived or redirect extend\n"); + default: + break; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]); + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + data, data_len, + ">>> [default] decrypt and dump"); + error_msg = try_dump_as_gbk(data, data_len); + if (error_msg) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + } + return QQ_LOGIN_REPLY_ERR_MISC; +} + +/* send keep-alive packet to QQ server (it is a heart-beat) */ +void qq_send_packet_keep_alive(PurpleConnection *gc) +{ + qq_data *qd; + guint8 raw_data[16] = {0}; + gint bytes= 0; + + qd = (qq_data *) gc->proto_data; + + /* In fact, we can send whatever we like to server + * with this command, server return the same result including + * the amount of online QQ users, my ip and port */ + bytes += qq_put32(raw_data + bytes, qd->uid); + + qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4); +} + +/* parse the return of keep-alive packet, it includes some system information */ +gboolean qq_process_keep_alive(guint8 *buf, gint buf_len, PurpleConnection *gc) +{ + qq_data *qd; + gint len; + gchar **segments; + guint8 *data; + + g_return_val_if_fail(buf != NULL && buf_len != 0, FALSE); + + qd = (qq_data *) gc->proto_data; + len = buf_len; + data = g_newa(guint8, len); + + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n"); + return FALSE; + } + + /* qq_show_packet("Keep alive reply packet", data, len); */ + + /* the last one is 60, don't know what it is */ + if (NULL == (segments = split_data(data, len, "\x1f", 6))) + return TRUE; + + /* segments[0] and segment[1] are all 0x30 ("0") */ + qd->total_online = strtol(segments[2], NULL, 10); + if(0 == qd->total_online) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Keep alive error")); + } + qd->my_ip.s_addr = inet_addr(segments[3]); + qd->my_port = strtol(segments[4], NULL, 10); + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "keep alive, %s:%d\n", + inet_ntoa(qd->my_ip), qd->my_port); + + g_strfreev(segments); + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_base.h Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,56 @@ +/** + * file qq_base.h + * + * 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 _QQ_BASE_H_ +#define _QQ_BASE_H_ + +#include <glib.h> +#include "connection.h" + +#define QQ_TOKEN_REPLY_OK 0x00 + +#define QQ_LOGIN_REPLY_OK 0x00 +#define QQ_LOGIN_REPLY_REDIRECT 0x01 +#define QQ_LOGIN_REPLY_ERR_PWD 0x05 +#define QQ_LOGIN_REPLY_NEED_REACTIVE 0x06 +#define QQ_LOGIN_REPLY_REDIRECT_EX 0x0A +#define QQ_LOGIN_REPLY_ERR_MISC 0xff /* defined by myself */ + +#define QQ_LOGIN_MODE_NORMAL 0x0a +#define QQ_LOGIN_MODE_AWAY 0x1e +#define QQ_LOGIN_MODE_HIDDEN 0x28 + +#define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */ + +void qq_send_packet_token(PurpleConnection *gc); +guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len); + +void qq_send_packet_login(PurpleConnection *gc); +guint8 qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc); + +void qq_send_packet_logout(PurpleConnection *gc); + +void qq_send_packet_keep_alive(PurpleConnection *gc); +gboolean qq_process_keep_alive(guint8 *buf, gint buf_len, PurpleConnection *gc); +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_network.c Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,1045 @@ +/** + * @file qq_network.c + * + * 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 "cipher.h" +#include "debug.h" +#include "internal.h" + +#ifdef _WIN32 +#define random rand +#define srandom srand +#endif + +#include "buddy_info.h" +#include "group_info.h" +#include "group_free.h" +#include "crypt.h" +#include "header_info.h" +#include "qq_base.h" +#include "buddy_list.h" +#include "packet_parse.h" +#include "qq_network.h" +#include "qq_trans.h" +#include "utils.h" +#include "qq_process.h" + +/* set QQ_RECONNECT_MAX to 1, when test reconnecting */ +#define QQ_RECONNECT_MAX 4 +#define QQ_RECONNECT_INTERVAL 5000 +#define QQ_KEEP_ALIVE_INTERVAL 60000 +#define QQ_TRANS_INTERVAL 10000 + +static gboolean set_new_server(qq_data *qd) +{ + gint count; + gint index; + GList *it = NULL; + + g_return_val_if_fail(qd != NULL, FALSE); + + if (qd->servers == NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n"); + return FALSE; + } + + if (qd->real_hostname) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); + g_free(qd->real_hostname); + qd->real_hostname = NULL; + } + + /* remove server used before */ + if (qd->server_name != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Remove previous server [%s]\n", qd->server_name); + qd->servers = g_list_remove(qd->servers, qd->server_name); + qd->server_name = NULL; + } + + count = g_list_length(qd->servers); + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count); + if (count <= 0) { + /* no server left, disconnect when result is false */ + qd->servers = NULL; + return FALSE; + } + + /* get new server */ + index = random() % count; + it = g_list_nth(qd->servers, index); + qd->server_name = it->data; /* do not free server_name */ + if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index); + return FALSE; + } + + qd->real_hostname = g_strdup(qd->server_name); + qd->real_port = qd->user_port; + + qd->reconnect_times = QQ_RECONNECT_MAX; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "set new server to %s:%d\n", qd->real_hostname, qd->real_port); + return TRUE; +} + +static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, + guint16 *cmd, guint16 *seq, guint8 *buf) +{ + gint bytes = 0; + bytes += qq_get8(header_tag, buf + bytes); + bytes += qq_get16(source_tag, buf + bytes); + bytes += qq_get16(cmd, buf + bytes); + bytes += qq_get16(seq, buf + bytes); + return bytes; +} + +static gboolean reconnect_later_cb(gpointer data) +{ + PurpleConnection *gc; + qq_data *qd; + + gc = (PurpleConnection *) data; + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); + qd = (qq_data *) gc->proto_data; + + qd->reconnect_timeout = 0; + + qq_connect(gc->account); + return FALSE; /* timeout callback stops */ +} + +static void reconnect_later(PurpleConnection *gc) +{ + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + qd->reconnect_times--; + if (qd->reconnect_times < 0) { + if ( set_new_server(qd) != TRUE) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Failed to connect server")); + return; + } + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "Reconnect to server %s:%d next retries %d in %d ms\n", + qd->real_hostname, qd->real_port, + qd->reconnect_times, QQ_RECONNECT_INTERVAL); + + qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL, + reconnect_later_cb, gc); +} + +/* process the incoming packet from qq_pending */ +static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) +{ + qq_data *qd; + gint bytes, bytes_not_read; + + gboolean prev_login_status; + + guint8 header_tag; + guint16 source_tag; + guint16 cmd; + guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ + + qq_transaction *trans; + + g_return_if_fail(buf != NULL && buf_len > 0); + + qd = (qq_data *) gc->proto_data; + + prev_login_status = qd->logged_in; + + /* Len, header and tail tag have been checked before */ + bytes = 0; + bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); + + if (QQ_DEBUG) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "==> [%05d] 0x%04X %s, from (0x%04X %s)\n", + seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag)); + } + + bytes_not_read = buf_len - bytes - 1; + + /* ack packet, we need to update send tranactions */ + /* we do not check duplication for server ack */ + trans = qq_trans_find_rcved(qd, cmd, seq); + if (trans == NULL) { + /* new server command */ + qq_trans_add_server_cmd(qd, cmd, seq, buf + bytes, bytes_not_read); + if ( qd->logged_in ) { + qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); + } + return; + } + + if (qq_trans_is_dup(trans)) { + purple_debug(PURPLE_DEBUG_WARNING, + "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); + return; + } + + if (qq_trans_is_server(trans)) { + if ( qd->logged_in ) { + qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); + } + return; + } + + /* this is the length of all the encrypted data (also remove tail tag */ + qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read); + + /* check is redirect or not, and do it now */ + if (qd->is_redirect) { + /* free resource except real_hostname and port */ + qq_disconnect(gc); + qd->reconnect_times = QQ_RECONNECT_MAX; + reconnect_later(gc); + return; + } + + if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { + /* logged_in, but we have packets before login */ + qq_trans_process_before_login(qd); + } +} + +static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ + gint buf_len; + gint bytes; + + guint8 *pkt; + guint16 pkt_len; + + gchar *error_msg; + guint8 *jump; + gint jump_len; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + if(cond != PURPLE_INPUT_READ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Socket error")); + return; + } + + qd = (qq_data *) gc->proto_data; + + /* test code, not using tcp_rxqueue + memset(pkt,0, sizeof(pkt)); + buf_len = read(qd->fd, pkt, sizeof(pkt)); + if (buf_len > 2) { + packet_process(gc, pkt + 2, buf_len - 2); + } + return; + */ + + buf_len = read(qd->fd, buf, sizeof(buf)); + if (buf_len < 0) { + if (errno == EAGAIN) + /* No worries */ + return; + + error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno)); + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + return; + } else if (buf_len == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Server closed the connection.")); + return; + } + + /* keep alive will be sent in 30 seconds since last_receive + * QQ need a keep alive packet in every 60 seconds + gc->last_received = time(NULL); + */ + /* + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); + */ + qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); + memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); + qd->tcp_rxlen += buf_len; + + pkt = g_newa(guint8, MAX_PACKET_SIZE); + while (1) { + if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { + break; + } + + bytes = 0; + bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); + if (qd->tcp_rxlen < pkt_len) { + break; + } + + /* + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); + */ + if ( pkt_len < QQ_TCP_HEADER_LENGTH + || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG + || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { + /* HEY! This isn't even a QQ. What are you trying to pull? */ + + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", + "Packet error, failed to check header and tail tag\n"); + + jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); + if ( !jump ) { + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + qd->tcp_rxlen = 0; + return; + } + + /* jump and over QQ_PACKET_TAIL */ + jump_len = (jump - qd->tcp_rxqueue) + 1; + purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", + "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); + g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); + qd->tcp_rxlen -= jump_len; + continue; + } + + memset(pkt, 0, MAX_PACKET_SIZE); + g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); + + /* jump to next packet */ + qd->tcp_rxlen -= pkt_len; + if (qd->tcp_rxlen) { + /* + purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); + */ + jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = jump; + } else { + /* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */ + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + } + + if (pkt == NULL) { + continue; + } + /* do not call packet_process before jump + * packet_process may call disconnect and destory tcp_rxqueue */ + packet_process(gc, pkt, pkt_len - bytes); + } +} + +static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + guint8 *buf; + gint buf_len; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + if(cond != PURPLE_INPUT_READ) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Socket error")); + return; + } + + qd = (qq_data *) gc->proto_data; + g_return_if_fail(qd->fd >= 0); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + + /* here we have UDP proxy suppport */ + buf_len = read(qd->fd, buf, MAX_PACKET_SIZE); + if (buf_len <= 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to read from socket")); + return; + } + + /* keep alive will be sent in 30 seconds since last_receive + * QQ need a keep alive packet in every 60 seconds + gc->last_received = time(NULL); + */ + + if (buf_len < QQ_UDP_HEADER_LENGTH) { + if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) { + qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING", + buf, buf_len, + "Received packet is too short, or no header and tail tag"); + return; + } + } + + packet_process(gc, buf, buf_len); +} + +static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len) +{ + gint ret; + + g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + + /* + purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); + */ + + errno = 0; + ret = send(qd->fd, data, data_len, 0); + if (ret < 0 && errno == EAGAIN) { + return ret; + } + + if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_debug(PURPLE_DEBUG_ERROR, "UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno)); + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + } + return ret; +} + +static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) +{ + qq_data *qd = data; + int ret, writelen; + + writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf); + if (writelen == 0) { + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + return; + } + + ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen); + purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE", + "total %d bytes is sent %d\n", writelen, ret); + + if (ret < 0 && errno == EAGAIN) + return; + else if (ret < 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Write Error")); + return; + } + + purple_circ_buffer_mark_read(qd->tcp_txbuf, ret); +} + +static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len) +{ + gint ret; + + g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); + + /* + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); + */ + + if (qd->tx_handler == 0) { + ret = write(qd->fd, data, data_len); + } else { + ret = -1; + errno = EAGAIN; + } + + /* + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); + */ + if (ret < 0 && errno == EAGAIN) { + /* socket is busy, send later */ + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); + ret = 0; + } else if (ret <= 0) { + /* TODO: what to do here - do we really have to disconnect? */ + purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", + "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); + purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); + return ret; + } + + if (ret < data_len) { + purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", + "Add %d bytes to buffer\n", data_len - ret); + if (qd->tx_handler == 0) { + qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd); + } + purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); + } + return ret; +} + +static gboolean network_timeout(gpointer data) +{ + PurpleConnection *gc = (PurpleConnection *) data; + qq_data *qd; + gboolean is_lost_conn; + + g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); + qd = (qq_data *) gc->proto_data; + + is_lost_conn = qq_trans_scan(qd); + if (is_lost_conn) { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); + return TRUE; + } + + if ( !qd->logged_in ) { + return TRUE; + } + + qd->itv_count.keep_alive--; + if (qd->itv_count.keep_alive <= 0) { + qd->itv_count.keep_alive = qd->itv_config.keep_alive; + qq_send_packet_keep_alive(gc); + return TRUE; + } + + if (qd->itv_config.update <= 0) { + return TRUE; + } + + qd->itv_count.update--; + if (qd->itv_count.update <= 0) { + qd->itv_count.update = qd->itv_config.update; + qq_send_packet_get_buddies_online(gc, 0); + + qq_send_cmd_group_all_get_online_members(gc); + return TRUE; + } + + return TRUE; /* if return FALSE, timeout callback stops */ +} + +/* the callback function after socket is built + * we setup the qq protocol related configuration here */ +static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) +{ + qq_data *qd; + PurpleConnection *gc; + gchar *conn_msg; + const gchar *passwd; + PurpleAccount *account ; + + gc = (PurpleConnection *) data; + + if (!PURPLE_CONNECTION_IS_VALID(gc)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); + close(source); + return; + } + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + account = purple_connection_get_account(gc); + + /* Connect is now complete; clear the PurpleProxyConnectData */ + qd->connect_data = NULL; + + if (source < 0) { /* socket returns -1 */ + purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n"); + qq_disconnect(gc); + reconnect_later(gc); + return; + } + + /* _qq_show_socket("Got login socket", source); */ + + /* QQ use random seq, to minimize duplicated packets */ + srandom(time(NULL)); + qd->send_seq = random() & 0x0000ffff; + qd->fd = source; + qd->logged_in = FALSE; + qd->channel = 1; + qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); + + /* now generate md5 processed passwd */ + passwd = purple_account_get_password(purple_connection_get_account(gc)); + + /* use twice-md5 of user password as session key since QQ 2003iii */ + qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), + (guint8 *)passwd, strlen(passwd)); + qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), + qd->password_twice_md5, sizeof(qd->password_twice_md5)); + + g_return_if_fail(qd->network_timeout == 0); + qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10); + if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10; + + qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60); + if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30; + qd->itv_config.keep_alive /= qd->itv_config.resend; + qd->itv_count.keep_alive = qd->itv_config.keep_alive; + + qd->itv_config.update = purple_account_get_int(account, "update_interval", 300); + if (qd->itv_config.update > 0) { + if (qd->itv_config.update < qd->itv_config.keep_alive) { + qd->itv_config.update = qd->itv_config.keep_alive; + } + qd->itv_config.update /= qd->itv_config.resend; + qd->itv_count.update = qd->itv_config.update; + } else { + qd->itv_config.update = 0; + } + + qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc); + + if (qd->use_tcp) + gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); + else + gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); + + /* Update the login progress status display */ + conn_msg = g_strdup_printf("Login as %d", qd->uid); + purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); + g_free(conn_msg); + + qq_send_packet_token(gc); +} + +static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleConnection *gc; + qq_data *qd; + socklen_t len; + int error=0, ret; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + + purple_debug_info("proxy", "Connected.\n"); + + /* + * getsockopt after a non-blocking connect returns -1 if something is + * really messed up (bad descriptor, usually). Otherwise, it returns 0 and + * error holds what connect would have returned if it blocked until now. + * Thus, error == 0 is success, error == EINPROGRESS means "try again", + * and anything else is a real error. + * + * (error == EINPROGRESS can happen after a select because the kernel can + * be overly optimistic sometimes. select is just a hint that you might be + * able to do something.) + */ + len = sizeof(error); + ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); + if (ret == 0 && error == EINPROGRESS) + return; /* we'll be called again later */ + + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + if (ret < 0 || error != 0) { + if(ret != 0) + error = errno; + + close(source); + + purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); + + qq_connect_cb(gc, -1, _("Unable to connect")); + return; + } + + qq_connect_cb(gc, source, NULL); +} + +static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { + PurpleConnection *gc; + qq_data *qd; + struct sockaddr server_addr; + int addr_size; + gint fd = -1; + int flags; + + gc = (PurpleConnection *) data; + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + /* udp_query_data must be set as NULL. + * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */ + qd->udp_query_data = NULL; + + if (!hosts || !hosts->data) { + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Couldn't resolve host")); + return; + } + + addr_size = GPOINTER_TO_INT(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + memcpy(&server_addr, hosts->data, addr_size); + g_free(hosts->data); + + hosts = g_slist_remove(hosts, hosts->data); + while(hosts) { + hosts = g_slist_remove(hosts, hosts->data); + g_free(hosts->data); + hosts = g_slist_remove(hosts, hosts->data); + } + + fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", + "Unable to create socket: %s\n", g_strerror(errno)); + return; + } + + /* we use non-blocking mode to speed up connection */ + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ + * + * If a UDP socket is unconnected, which is the normal state after a + * bind() call, then send() or write() are not allowed, since no + * destination is available; only sendto() can be used to send data. + * + * Calling connect() on the socket simply records the specified address + * and port number as being the desired communications partner. That + * means that send() or write() are now allowed; they use the destination + * address and port given on the connect call as the destination of packets. + */ + if (connect(fd, &server_addr, addr_size) >= 0) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); + flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); + qq_connect_cb(gc, fd, NULL); + return; + } + + /* [EINPROGRESS] + * The socket is marked as non-blocking and the connection cannot be + * completed immediately. It is possible to select for completion by + * selecting the socket for writing. + * [EINTR] + * A signal interrupted the call. + * The connection is established asynchronously. + */ + if ((errno == EINPROGRESS) || (errno == EINTR)) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); + qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); + return; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %s\n", g_strerror(errno)); + close(fd); +} + +/* establish a generic QQ connection + * TCP/UDP, and direct/redirected */ +void qq_connect(PurpleAccount *account) +{ + PurpleConnection *gc; + qq_data *qd; + gchar *conn_msg; + + gc = purple_account_get_connection(account); + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + + /* test set_new_server + while (set_new_server(qd)) { + purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", + "New server %s:%d Real server %s:%d\n", + qd->server_name, qd->user_port, qd->real_hostname, qd->real_port); + } + purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n", + qd->servers); + exit(1); + */ + if (qd->server_name == NULL) { + /* must be first call this function */ + if ( set_new_server(qd) != TRUE) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Failed to connect server")); + return; + } + } + + if (qd->real_hostname == NULL || qd->real_port == 0) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("hostname is NULL or port is 0")); + return; + } + + conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), + qd->real_hostname, qd->reconnect_times); + purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); + g_free(conn_msg); + + if (qd->is_redirect) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", + qd->real_hostname, qd->real_port); + } + qd->is_redirect = FALSE; + + qd->fd = -1; + qd->tx_handler = 0; + + /* QQ connection via UDP/TCP. + * Now use Purple proxy function to provide TCP proxy support, + * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ + if(qd->use_tcp) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", + qd->real_hostname, qd->real_port); + + /* TODO: is there a good default grow size? */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n"); + qd->tcp_txbuf = purple_circ_buffer_new(0); + + qd->connect_data = purple_proxy_connect(NULL, account, + qd->real_hostname, qd->real_port, qq_connect_cb, gc); + if (qd->connect_data == NULL) { + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Unable to connect.")); + } + return; + } + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n", + qd->real_hostname, qd->real_port); + + g_return_if_fail(qd->udp_query_data == NULL); + qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port, + udp_host_resolved, gc); + if (qd->udp_query_data == NULL) { + purple_connection_error_reason(qd->gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, + _("Could not resolve hostname")); + } +} + +/* clean up qq_data structure and all its components + * always used before a redirectly connection */ +void qq_disconnect(PurpleConnection *gc) +{ + qq_data *qd; + + g_return_if_fail(gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); + + if (qd->network_timeout > 0) { + purple_timeout_remove(qd->network_timeout); + qd->network_timeout = 0; + } + + /* finish all I/O */ + if (qd->fd >= 0 && qd->logged_in) { + qq_send_packet_logout(gc); + } + + if (gc->inpa > 0) { + purple_input_remove(gc->inpa); + gc->inpa = 0; + } + + if (qd->fd >= 0) { + close(qd->fd); + qd->fd = -1; + } + + if (qd->reconnect_timeout > 0) { + purple_timeout_remove(qd->reconnect_timeout); + qd->reconnect_timeout = 0; + } + + if (qd->connect_data != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); + purple_proxy_connect_cancel(qd->connect_data); + } + + if(qd->tcp_txbuf != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); + purple_circ_buffer_destroy(qd->tcp_txbuf); + qd->tcp_txbuf = NULL; + } + + if (qd->tx_handler) { + purple_input_remove(qd->tx_handler); + qd->tx_handler = 0; + } + if (qd->tcp_rxqueue != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n"); + g_free(qd->tcp_rxqueue); + qd->tcp_rxqueue = NULL; + qd->tcp_rxlen = 0; + } + + if (qd->udp_query_data != NULL) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); + purple_dnsquery_destroy(qd->udp_query_data); + qd->udp_query_data = NULL; + } + + qq_trans_remove_all(qd); + + if (qd->token) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n"); + g_free(qd->token); + qd->token = NULL; + qd->token_len = 0; + } + memset(qd->inikey, 0, sizeof(qd->inikey)); + memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5)); + memset(qd->session_key, 0, sizeof(qd->session_key)); + memset(qd->session_md5, 0, sizeof(qd->session_md5)); + + qd->my_ip.s_addr = 0; + + qq_group_packets_free(qd); + qq_group_free_all(qd); + qq_add_buddy_request_free(qd); + qq_info_query_free(qd); + qq_buddies_list_free(gc->account, qd); +} + +static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, + guint8 *data, gint data_len) +{ + gint bytes = 0; + g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); + + if (data == NULL) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n"); + return -1; + } + if (data_len <= 0) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n"); + return -1; + } + + /* QQ TCP packet has two bytes in the begining defines packet length + * so leave room here to store packet size */ + if (qd->use_tcp) { + bytes += qq_put16(buf + bytes, 0x0000); + } + /* now comes the normal QQ packet as UDP */ + bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); + bytes += qq_put16(buf + bytes, QQ_CLIENT); + bytes += qq_put16(buf + bytes, cmd); + + bytes += qq_put16(buf + bytes, seq); + + bytes += qq_put32(buf + bytes, qd->uid); + bytes += qq_putdata(buf + bytes, data, data_len); + bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); + + /* set TCP packet length at begin of the packet */ + if (qd->use_tcp) { + qq_put16(buf, bytes); + } + + return bytes; +} + +/* data has been encrypted before */ +gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, + guint8 *data, gint data_len) +{ + guint8 *buf; + gint buf_len; + gint bytes_sent; + + g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + buf = g_newa(guint8, MAX_PACKET_SIZE); + memset(buf, 0, MAX_PACKET_SIZE); + buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); + if (buf_len <= 0) { + return -1; + } + + if (qd->use_tcp) { + bytes_sent = tcp_send_out(qd, buf, buf_len); + } else { + bytes_sent = udp_send_out(qd, buf, buf_len); + } + + if (need_ack) { + qq_trans_add_client_cmd(qd, cmd, seq, data, data_len); + } + + if (QQ_DEBUG) { + /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */ + purple_debug(PURPLE_DEBUG_INFO, "QQ", + "<== [%05d], %s, total %d bytes is sent %d\n", + seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); + } + return bytes_sent; +} + +/* Encrypt data with session_key, then call qq_send_data */ +gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, + guint8 *data, gint data_len) +{ + guint8 *encrypted_data; + gint encrypted_len; + + g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + encrypted_len = data_len + 16; /* at most 16 bytes more */ + encrypted_data = g_newa(guint8, encrypted_len); + + qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); + + return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len); +} + +/* set seq and need_ack, then call qq_send_cmd_detail */ +gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) +{ + g_return_val_if_fail(qd != NULL, -1); + g_return_val_if_fail(data != NULL && data_len > 0, -1); + + qd->send_seq++; + return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_network.h Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,45 @@ +/** + * @file qq_network.h + * + * 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 _QQ_NETWORK_H +#define _QQ_NETWORK_H + +#include <glib.h> +#include "connection.h" + +#include "qq.h" + +#define QQ_CONNECT_STEPS 3 /* steps in connection */ + +void qq_connect(PurpleAccount *account); +void qq_disconnect(PurpleConnection *gc); +void qq_connect_later(PurpleConnection *gc); + +gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen); +gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, + guint8 *data, gint data_len); +gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, + guint8 *data, gint data_len); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_process.c Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,267 @@ +/** + * @file qq_network.c + * + * 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 "cipher.h" +#include "debug.h" +#include "internal.h" + +#ifdef _WIN32 +#define random rand +#define srandom srand +#endif + +#include "buddy_info.h" +#include "buddy_list.h" +#include "buddy_opt.h" +#include "group_info.h" +#include "group_free.h" +#include "char_conv.h" +#include "crypt.h" +#include "group_network.h" +#include "header_info.h" +#include "qq_base.h" +#include "im.h" +#include "qq_process.h" +#include "packet_parse.h" +#include "qq_network.h" +#include "qq_trans.h" +#include "sys_msg.h" +#include "utils.h" + +/* default process, decrypt and dump */ +static void process_cmd_unknow(PurpleConnection *gc,gchar *title, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) +{ + qq_data *qd; + guint8 *data; + gint data_len; + gchar *msg_utf8 = NULL; + + g_return_if_fail(buf != NULL && buf_len != 0); + + qq_show_packet(title, buf, buf_len); + + qd = (qq_data *) gc->proto_data; + + data_len = buf_len; + data = g_newa(guint8, data_len); + memset(data, 0, data_len); + if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) { + purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); + return; + } + + qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", + data, data_len, + ">>> [%d] %s -> [default] decrypt and dump", + seq, qq_get_cmd_desc(cmd)); + + msg_utf8 = try_dump_as_gbk(data, data_len); + if (msg_utf8) { + g_free(msg_utf8); + } +} + +void qq_proc_cmd_server(PurpleConnection *gc, + guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + /* now process the packet */ + switch (cmd) { + case QQ_CMD_RECV_IM: + qq_process_recv_im(data, data_len, seq, gc); + break; + case QQ_CMD_RECV_MSG_SYS: + qq_process_msg_sys(data, data_len, seq, gc); + break; + case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS: + qq_process_buddy_change_status(data, data_len, gc); + break; + default: + process_cmd_unknow(gc, "Unknow SERVER CMD", data, data_len, cmd, seq); + break; + } +} + +static void process_cmd_login(PurpleConnection *gc, guint8 *data, gint data_len) +{ + qq_data *qd; + guint ret_8; + + g_return_if_fail (gc != NULL && gc->proto_data != NULL); + + qd = (qq_data *) gc->proto_data; + + ret_8 = qq_process_login_reply(data, data_len, gc); + if (ret_8 == QQ_LOGIN_REPLY_OK) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login repliess OK; everything is fine\n"); + + purple_connection_set_state(gc, PURPLE_CONNECTED); + qd->logged_in = TRUE; /* must be defined after sev_finish_login */ + + /* now initiate QQ Qun, do it first as it may take longer to finish */ + qq_group_init(gc); + + /* Now goes on updating my icon/nickname, not showing info_window */ + qd->modifying_face = FALSE; + + qq_send_packet_get_info(gc, qd->uid, FALSE); + /* grab my level */ + qq_send_packet_get_level(gc, qd->uid); + + qq_send_packet_change_status(gc); + + /* refresh buddies */ + qq_send_packet_get_buddies_list(gc, 0); + + /* refresh groups */ + qq_send_packet_get_all_list_with_group(gc, 0); + + return; + } + + if (ret_8 == QQ_LOGIN_REPLY_REDIRECT) { + qd->is_redirect = TRUE; + /* + purple_debug(PURPLE_DEBUG_WARNING, "QQ", + "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port); + */ + return; + } + + if (ret_8 == QQ_LOGIN_REPLY_ERR_PWD) { + if (!purple_account_get_remember_password(gc->account)) { + purple_account_set_password(gc->account, NULL); + } + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password.")); + return; + } + + if (ret_8 == QQ_LOGIN_REPLY_ERR_MISC) { + if (purple_debug_is_enabled()) + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login. Check debug log.")); + else + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login")); + return; + } +} + +void qq_proc_cmd_reply(PurpleConnection *gc, + guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + gboolean ret_bool = FALSE; + guint8 ret_8 = 0; + guint16 ret_16 = 0; + guint32 ret_32 = 0; + gchar *error_msg = NULL; + + switch (cmd) { + case QQ_CMD_TOKEN: + ret_8 = qq_process_token_reply(gc, error_msg, data, data_len); + if (ret_8 != QQ_TOKEN_REPLY_OK) { + if (error_msg == NULL) { + error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret_8); + } + purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); + g_free(error_msg); + return; + } + + qq_send_packet_login(gc); + break; + case QQ_CMD_LOGIN: + process_cmd_login(gc, data, data_len); + break; + case QQ_CMD_UPDATE_INFO: + qq_process_modify_info_reply(data, data_len, gc); + break; + case QQ_CMD_ADD_BUDDY_WO_AUTH: + qq_process_add_buddy_reply(data, data_len, seq, gc); + break; + case QQ_CMD_DEL_BUDDY: + qq_process_remove_buddy_reply(data, data_len, gc); + break; + case QQ_CMD_REMOVE_SELF: + qq_process_remove_self_reply(data, data_len, gc); + break; + case QQ_CMD_BUDDY_AUTH: + qq_process_add_buddy_auth_reply(data, data_len, gc); + break; + case QQ_CMD_GET_USER_INFO: + qq_process_get_info_reply(data, data_len, gc); + break; + case QQ_CMD_CHANGE_ONLINE_STATUS: + qq_process_change_status_reply(data, data_len, gc); + break; + case QQ_CMD_SEND_IM: + qq_process_send_im_reply(data, data_len, gc); + break; + case QQ_CMD_KEEP_ALIVE: + qq_process_keep_alive(data, data_len, gc); + break; + case QQ_CMD_GET_BUDDIES_ONLINE: + ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc); + if (ret_8 > 0 && ret_8 < 0xff) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more online buddies\n"); + qq_send_packet_get_buddies_online(gc, ret_8); + } else { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "All online buddies received\n"); + /* Fixme: this should not be called once*/ + qq_send_packet_get_buddies_levels(gc); + + qq_refresh_all_buddy_status(gc); + } + break; + case QQ_CMD_GET_LEVEL: + qq_process_get_level_reply(data, data_len, gc); + break; + case QQ_CMD_GET_BUDDIES_LIST: + ret_16 = qq_process_get_buddies_list_reply(data, data_len, gc); + if (ret_16 > 0 && ret_16 < 0xffff) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies\n"); + qq_send_packet_get_buddies_list(gc, ret_16); + } else { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies received. Requesting buddies' levels\n"); + qq_send_packet_get_buddies_online(gc, 0); + } + break; + case QQ_CMD_GROUP_CMD: + qq_process_group_cmd_reply(data, data_len, seq, gc); + break; + case QQ_CMD_GET_ALL_LIST_WITH_GROUP: + ret_32 = qq_process_get_all_list_with_group_reply(data, data_len, gc); + if (ret_32 > 0 && ret_32 < 0xffffffff) { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies and groups\n"); + qq_send_packet_get_all_list_with_group(gc, ret_32); + } else { + purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n"); + } + break; + default: + process_cmd_unknow(gc, "Unknow reply CMD", data, data_len, cmd, seq); + break; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_process.h Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,38 @@ +/** + * @file qq_process.h + * + * 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 _QQ_PROCESS_H +#define _QQ_PROCESS_H + +#include <glib.h> +#include "connection.h" + +#include "qq.h" + +void qq_proc_cmd_reply(PurpleConnection *gc, + guint16 cmd, guint16 seq, guint8 *data, gint data_len); +void qq_proc_cmd_server(PurpleConnection *gc, + guint16 cmd, guint16 seq, guint8 *data, gint data_len); +#endif +
--- a/libpurple/protocols/qq/qq_proxy.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,529 +0,0 @@ -/** - * @file qq_proxy.c - * - * 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 "cipher.h" -#include "debug.h" -#include "internal.h" - -#ifdef _WIN32 -#define random rand -#define srandom srand -#endif - -#include "packet_parse.h" -#include "buddy_info.h" -#include "buddy_opt.h" -#include "char_conv.h" -#include "group_free.h" -#include "login_logout.h" -#include "qq_proxy.h" -#include "recv_core.h" -#include "send_core.h" -#include "sendqueue.h" -#include "udp_proxy_s5.h" -#include "utils.h" - -/* These functions are used only in development phase */ -/* -static void _qq_show_socket(gchar *desc, gint fd) { - struct sockaddr_in sin; - socklen_t len = sizeof(sin); - getsockname(fd, (struct sockaddr *)&sin, &len); - purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", - inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); -} -*/ - -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len) -{ - char buf1[8*len+2], buf2[10]; - int i; - buf1[0] = 0; - for (i = 0; i < len; i++) { - sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff); - strcat(buf1, buf2); - } - strcat(buf1, "\n"); - purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1); -} - -/* QQ 2003iii uses double MD5 for the pwkey to get the session key */ -static guint8 *_gen_pwkey(const gchar *pwd) -{ - PurpleCipher *cipher; - PurpleCipherContext *context; - - guchar pwkey_tmp[QQ_KEY_LENGTH]; - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd)); - purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); - purple_cipher_context_destroy(context); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH); - purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); - purple_cipher_context_destroy(context); - - return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); -} - -static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size) -{ - if (!hosts || !hosts->data) - return FALSE; - - *addr_size = GPOINTER_TO_INT(hosts->data); - - hosts = g_slist_remove(hosts, hosts->data); - memcpy(addr, hosts->data, *addr_size); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - while(hosts) { - hosts = g_slist_remove(hosts, hosts->data); - g_free(hosts->data); - hosts = g_slist_remove(hosts, hosts->data); - } - - return TRUE; -} - -/* set up any finalizing start-up stuff */ -static void _qq_start_services(PurpleConnection *gc) -{ - /* start watching for IMs about to be sent */ - /* - purple_signal_connect(purple_conversations_get_handle(), - "sending-im-msg", gc, - PURPLE_CALLBACK(qq_sending_im_msg_cb), NULL); - */ -} - -/* the callback function after socket is built - * we setup the qq protocol related configuration here */ -static void _qq_got_login(gpointer data, gint source, const gchar *error_message) -{ - qq_data *qd; - PurpleConnection *gc; - gchar *buf; - const gchar *passwd; - - gc = (PurpleConnection *) data; - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - close(source); - return; - } - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - - if (source < 0) { /* socket returns -1 */ - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); - return; - } - - qd = (qq_data *) gc->proto_data; - - /* - _qq_show_socket("Got login socket", source); - */ - - /* QQ use random seq, to minimize duplicated packets */ - srandom(time(NULL)); - qd->send_seq = random() & 0x0000ffff; - qd->fd = source; - qd->logged_in = FALSE; - qd->channel = 1; - qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); - - /* now generate md5 processed passwd */ - passwd = purple_account_get_password(purple_connection_get_account(gc)); - qd->pwkey = _gen_pwkey(passwd); - - qd->sendqueue_timeout = purple_timeout_add(QQ_SENDQUEUE_TIMEOUT, qq_sendqueue_timeout_callback, gc); - gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, qq_input_pending, gc); - - /* Update the login progress status display */ - buf = g_strdup_printf("Login as %d", qd->uid); - purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS); - g_free(buf); - - _qq_start_services(gc); - - qq_send_packet_request_login_token(gc); -} - -/* clean up qq_data structure and all its components - * always used before a redirectly connection */ -static void _qq_common_clean(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL && gc->proto_data != NULL); - qd = (qq_data *) gc->proto_data; - - /* finish all I/O */ - if (qd->fd >= 0 && qd->logged_in) - qq_send_packet_logout(gc); - close(qd->fd); - - if (qd->sendqueue_timeout > 0) { - purple_timeout_remove(qd->sendqueue_timeout); - qd->sendqueue_timeout = 0; - } - - if (gc->inpa > 0) { - purple_input_remove(gc->inpa); - gc->inpa = 0; - } - - qq_b4_packets_free(qd); - qq_sendqueue_free(qd); - qq_group_packets_free(qd); - qq_group_free_all(qd); - qq_add_buddy_request_free(qd); - qq_info_query_free(qd); - qq_buddies_list_free(gc->account, qd); -} - -static void no_one_calls(gpointer data, gint source, PurpleInputCondition cond) -{ - struct PHB *phb = data; - socklen_t len; - int error=0, ret; - - purple_debug_info("proxy", "Connected.\n"); - - len = sizeof(error); - - /* - * getsockopt after a non-blocking connect returns -1 if something is - * really messed up (bad descriptor, usually). Otherwise, it returns 0 and - * error holds what connect would have returned if it blocked until now. - * Thus, error == 0 is success, error == EINPROGRESS means "try again", - * and anything else is a real error. - * - * (error == EINPROGRESS can happen after a select because the kernel can - * be overly optimistic sometimes. select is just a hint that you might be - * able to do something.) - */ - ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); - if (ret == 0 && error == EINPROGRESS) - return; /* we'll be called again later */ - if (ret < 0 || error != 0) { - if(ret!=0) - error = errno; - close(source); - purple_input_remove(phb->inpa); - - purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); - - phb->func(phb->data, -1, _("Unable to connect")); - return; - } - - purple_input_remove(phb->inpa); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, NULL); - } - - g_free(phb->host); - g_free(phb); -} - -/* returns -1 if fails, otherwise returns the file handle */ -static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) -{ - gint fd = -1; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n"); - fd = socket(PF_INET, SOCK_DGRAM, 0); - - if (fd < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ Redirect", - "Unable to create socket: %s\n", g_strerror(errno)); - return -1; - } - - /* we use non-blocking mode to speed up connection */ - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - - /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ - * - * If a UDP socket is unconnected, which is the normal state after a - * bind() call, then send() or write() are not allowed, since no - * destination is available; only sendto() can be used to send data. - * - * Calling connect() on the socket simply records the specified address - * and port number as being the desired communications partner. That - * means that send() or write() are now allowed; they use the destination - * address and port given on the connect call as the destination of packets. - */ - if (connect(fd, addr, addrlen) < 0) { - /* [EINPROGRESS] - * The socket is marked as non-blocking and the connection cannot be - * completed immediately. It is possible to select for completion by - * selecting the socket for writing. - * [EINTR] - * A signal interrupted the call. - * The connection is established asynchronously. - */ - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug_warning("QQ", "Connect in asynchronous mode.\n"); - phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, no_one_calls, phb); - } else { - purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno)); - close(fd); - return -1; - } /* if errno */ - } else { /* connect returns 0 */ - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - phb->func(phb->data, fd, NULL); - } - - return fd; -} - -static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message) -{ - struct PHB *phb = (struct PHB *) data; - struct sockaddr_in addr; - gint addr_size, ret = -1; - - if(_qq_fill_host(hosts, &addr, &addr_size)) - ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size); - - if (ret < 0) { - phb->func(phb->data, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - } -} - -static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message) -{ - struct PHB *phb = (struct PHB *) data; - PurpleConnection *gc = (PurpleConnection *) phb->data; - qq_data *qd = (qq_data *) gc->proto_data; - struct sockaddr_in addr; - gint addr_size, ret = -1; - - if(_qq_fill_host(hosts, &addr, &addr_size)) { - switch (purple_proxy_info_get_type(phb->gpi)) { - case PURPLE_PROXY_NONE: - ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); - break; - case PURPLE_PROXY_SOCKS5: - ret = 0; - if (purple_proxy_info_get_host(phb->gpi) == NULL || - purple_proxy_info_get_port(phb->gpi) == 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Use of socks5 proxy selected but host or port info doesn't exist.\n"); - ret = -1; - } else { - /* as the destination is always QQ server during the session, - * we can set dest_sin here, instead of _qq_s5_canread_again */ - memcpy(&qd->dest_sin, &addr, addr_size); - if (purple_dnsquery_a(purple_proxy_info_get_host(phb->gpi), - purple_proxy_info_get_port(phb->gpi), - _qq_proxy_resolved, phb) == NULL) - ret = -1; - } - break; - default: - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "Proxy type %i is unsupported, not using a proxy.\n", - purple_proxy_info_get_type(phb->gpi)); - ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size); - } - } - - if (ret < 0) { - phb->func(gc, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - } -} - -/* returns -1 if dns lookup fails, otherwise returns 0 */ -static gint _qq_udp_proxy_connect(PurpleAccount *account, - const gchar *server, guint16 port, - void callback(gpointer, gint, const gchar *error_message), - PurpleConnection *gc) -{ - PurpleProxyInfo *info; - struct PHB *phb; - qq_data *qd = (qq_data *) gc->proto_data; - - g_return_val_if_fail(gc != NULL && qd != NULL, -1); - - info = purple_proxy_get_setup(account); - - phb = g_new0(struct PHB, 1); - phb->host = g_strdup(server); - phb->port = port; - phb->account = account; - phb->gpi = info; - phb->func = callback; - phb->data = gc; - qd->proxy_type = purple_proxy_info_get_type(phb->gpi); - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Choosing proxy type %d\n", - purple_proxy_info_get_type(phb->gpi)); - - if (purple_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) { - phb->func(gc, -1, _("Unable to connect")); - g_free(phb->host); - g_free(phb); - return -1; - } else { - return 0; - } -} - -/* QQ connection via UDP/TCP. - * I use Purple proxy function to provide TCP proxy support, - * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ -static gint _proxy_connect_full (PurpleAccount *account, const gchar *host, guint16 port, - PurpleProxyConnectFunction func, gpointer data, gboolean use_tcp) -{ - PurpleConnection *gc; - qq_data *qd; - - gc = purple_account_get_connection(account); - qd = (qq_data *) gc->proto_data; - qd->server_ip = g_strdup(host); - qd->server_port = port; - - if(use_tcp) - return (purple_proxy_connect(NULL, account, host, port, func, data) == NULL); - else - return _qq_udp_proxy_connect(account, host, port, func, data); -} - -/* establish a generic QQ connection - * TCP/UDP, and direct/redirected */ -gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, - gboolean use_tcp, gboolean is_redirect) -{ - PurpleConnection *gc; - qq_data *qd; - - g_return_val_if_fail(host != NULL, -1); - g_return_val_if_fail(port > 0, -1); - - gc = purple_account_get_connection(account); - g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); - - if (is_redirect) - _qq_common_clean(gc); - - qd = (qq_data *) gc->proto_data; - qd->before_login_packets = g_queue_new(); - - return _proxy_connect_full(account, host, port, _qq_got_login, gc, use_tcp); -} - -/* clean up the given QQ connection and free all resources */ -void qq_disconnect(PurpleConnection *gc) -{ - qq_data *qd; - - g_return_if_fail(gc != NULL); - - _qq_common_clean(gc); - - qd = gc->proto_data; - g_free(qd->inikey); - g_free(qd->pwkey); - g_free(qd->session_key); - g_free(qd->session_md5); - g_free(qd->my_ip); - g_free(qd); - - gc->proto_data = NULL; -} - -/* send packet with proxy support */ -gint qq_proxy_write(qq_data *qd, guint8 *data, gint len) -{ - guint8 *buf; - gint ret; - - g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && len > 0, -1); - - /* TCP sock5 may be processed twice - * so we need to check qd->use_tcp as well */ - if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */ - buf = g_newa(guint8, len + 10); - buf[0] = 0x00; - buf[1] = 0x00; /* reserved */ - buf[2] = 0x00; /* frag */ - buf[3] = 0x01; /* type */ - g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4); - g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2); - g_memmove(buf + 10, data, len); - errno = 0; - ret = send(qd->fd, buf, len + 10, 0); - } else { - errno = 0; - ret = send(qd->fd, data, len, 0); - } - if (ret == -1) - purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); - - return ret; -} - -/* read packet input with proxy support */ -gint qq_proxy_read(qq_data *qd, guint8 *data, gint len) -{ - guint8 *buf; - gint bytes; - buf = g_newa(guint8, MAX_PACKET_SIZE + 10); - - g_return_val_if_fail(qd != NULL && data != NULL && len > 0, -1); - g_return_val_if_fail(qd->fd > 0, -1); - - bytes = read(qd->fd, buf, len + 10); - if (bytes < 0) - return -1; - - if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */ - if (bytes < 10) - return -1; - bytes -= 10; - g_memmove(data, buf + 10, bytes); /* cut off the header */ - } else { - g_memmove(data, buf, bytes); - } - - return bytes; -}
--- a/libpurple/protocols/qq/qq_proxy.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file qq_proxy.h - * - * 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 _QQ_PROXY_H -#define _QQ_PROXY_H - -#include <glib.h> -#include "dnsquery.h" -#include "proxy.h" - -#include "qq.h" - -#define QQ_CONNECT_STEPS 2 /* steps in connection */ - -struct PHB { - PurpleProxyConnectFunction func; - gpointer data; - gchar *host; - gint port; - gint inpa; - PurpleProxyInfo *gpi; - PurpleAccount *account; - gint udpsock; - gpointer sockbuf; -}; - -gint qq_proxy_read(qq_data *qd, guint8 *data, gint len); -gint qq_proxy_write(qq_data *qd, guint8 *data, gint len); - -gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect); -void qq_disconnect(PurpleConnection *gc); - -void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len); - -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_trans.c Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,290 @@ +/** + * @file qq_trans.c + * + * 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 "internal.h" + +#include "connection.h" +#include "debug.h" +#include "notify.h" +#include "prefs.h" +#include "request.h" + +#include "header_info.h" +#include "qq_network.h" +#include "qq_process.h" +#include "qq_trans.h" + +#define QQ_RESEND_MAX 3 /* max resend per packet */ + +qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq) +{ + GList *curr; + GList *next; + qq_transaction *trans; + + if (qd->transactions == NULL) { + return NULL; + } + + next = qd->transactions; + while( (curr = next) ) { + next = curr->next; + + trans = (qq_transaction *) (curr->data); + if(trans->cmd == cmd && trans->seq == seq) { + if (trans->rcved_times == 0) { + trans->scan_times = 0; + } + trans->rcved_times++; + if (qq_trans_is_server(trans) && qq_trans_is_dup(trans)) { + /* server may not get our confirm reply before, send reply again*/ + if (trans->data != NULL && trans->data_len > 0) { + qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len); + } + } + return trans; + } + } + + return NULL; +} + +gboolean qq_trans_is_server(qq_transaction *trans) +{ + g_return_val_if_fail(trans != NULL, FALSE); + + if (trans->flag & QQ_TRANS_IS_SERVER) + return TRUE; + else + return FALSE; +} + +gboolean qq_trans_is_dup(qq_transaction *trans) +{ + g_return_val_if_fail(trans != NULL, TRUE); + + if (trans->rcved_times > 1) + return TRUE; + else + return FALSE; +} + +/* Remove a packet with seq from send trans */ +static void trans_remove(qq_data *qd, qq_transaction *trans) +{ + g_return_if_fail(qd != NULL && trans != NULL); + + purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", + "Remove [%s%05d] retry %d rcved %d scan %d %s\n", + (trans->flag & QQ_TRANS_IS_SERVER) ? "SRV-" : "", + trans->seq, + trans->send_retries, trans->rcved_times, trans->scan_times, + qq_get_cmd_desc(trans->cmd)); + + if (trans->data) g_free(trans->data); + qd->transactions = g_list_remove(qd->transactions, trans); + g_free(trans); +} + +void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + qq_transaction *trans = g_new0(qq_transaction, 1); + + g_return_if_fail(trans != NULL); + + trans->flag = 0; + if (cmd == QQ_CMD_TOKEN || cmd == QQ_CMD_LOGIN || cmd == QQ_CMD_KEEP_ALIVE) { + trans->flag |= QQ_TRANS_CLI_IMPORT; + } + trans->fd = qd->fd; + trans->cmd = cmd; + trans->seq = seq; + trans->send_retries = QQ_RESEND_MAX; + trans->rcved_times = 0; + trans->scan_times = 0; + trans->data = NULL; + trans->data_len = 0; + if (data != NULL && data_len > 0) { + trans->data = g_memdup(data, data_len); /* don't use g_strdup, may have 0x00 */ + trans->data_len = data_len; + } + purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", + "Add client cmd, seq = %d, data = %p, len = %d\n", + trans->seq, trans->data, trans->data_len); + qd->transactions = g_list_append(qd->transactions, trans); +} + +void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len) +{ + qq_transaction *trans = g_new0(qq_transaction, 1); + + g_return_if_fail(trans != NULL); + + trans->flag = QQ_TRANS_IS_SERVER; + if ( !qd->logged_in ) { + trans->flag |= QQ_TRANS_BEFORE_LOGIN; + } + trans->fd = qd->fd; + trans->cmd = cmd; + trans->seq = seq; + trans->send_retries = 0; + trans->rcved_times = 1; + trans->scan_times = 0; + trans->data = NULL; + trans->data_len = 0; + if (data != NULL && data_len > 0) { + trans->data = g_memdup(data, data_len); /* don't use g_strdup, may have 0x00 */ + trans->data_len = data_len; + } + purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", + "Add server cmd, seq = %d, data = %p, len = %d\n", + trans->seq, trans->data, trans->data_len); + qd->transactions = g_list_append(qd->transactions, trans); +} + +void qq_trans_process_before_login(qq_data *qd) +{ + GList *curr; + GList *next; + qq_transaction *trans; + + g_return_if_fail(qd != NULL); + + next = qd->transactions; + while( (curr = next) ) { + next = curr->next; + trans = (qq_transaction *) (curr->data); + /* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */ + + if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) { + continue; + } + if ( !(trans->flag & QQ_TRANS_BEFORE_LOGIN) ) { + continue; + } + // set QQ_TRANS_BEFORE_LOGIN off + trans->flag &= ~QQ_TRANS_BEFORE_LOGIN; + + purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", + "Process server cmd before login, seq %d, data %p, len %d, send_retries %d\n", + trans->seq, trans->data, trans->data_len, trans->send_retries); + + qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len); + } + + /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */ + return; +} + +gboolean qq_trans_scan(qq_data *qd) +{ + GList *curr; + GList *next; + qq_transaction *trans; + + g_return_val_if_fail(qd != NULL, FALSE); + + next = qd->transactions; + while( (curr = next) ) { + next = curr->next; + trans = (qq_transaction *) (curr->data); + /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan [%d]\n", trans->seq); */ + + if (trans->flag & QQ_TRANS_BEFORE_LOGIN) { + /* keep server cmd before login*/ + continue; + } + + trans->scan_times++; + if (trans->scan_times <= 1) { + /* skip in 10 seconds */ + continue; + } + + if (trans->rcved_times > 0) { + /* Has been received */ + trans_remove(qd, trans); + continue; + } + + if (trans->flag & QQ_TRANS_IS_SERVER) { + continue; + } + + /* Never get reply */ + trans->send_retries--; + if (trans->send_retries <= 0) { + purple_debug(PURPLE_DEBUG_WARNING, "QQ_TRANS", + "[%d] %s is lost.\n", + trans->seq, qq_get_cmd_desc(trans->cmd)); + if (trans->flag & QQ_TRANS_CLI_IMPORT) { + return TRUE; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", + "Lost [%d] %s, data %p, len %d, retries %d\n", + trans->seq, qq_get_cmd_desc(trans->cmd), + trans->data, trans->data_len, trans->send_retries); + trans_remove(qd, trans); + continue; + } + + purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", + "Resend [%d] %s data %p, len %d, send_retries %d\n", + trans->seq, qq_get_cmd_desc(trans->cmd), + trans->data, trans->data_len, trans->send_retries); + qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len); + } + + /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */ + return FALSE; +} + +/* clean up send trans and free all contents */ +void qq_trans_remove_all(qq_data *qd) +{ + GList *curr; + GList *next; + qq_transaction *trans; + gint count = 0; + + curr = qd->transactions; + while(curr) { + next = curr->next; + + trans = (qq_transaction *) (curr->data); + /* + purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", + "Remove to transaction, seq = %d, buf = %p, len = %d\n", + trans->seq, trans->buf, trans->len); + */ + trans_remove(qd, trans); + + count++; + curr = next; + } + g_list_free(qd->transactions); + + purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/qq/qq_trans.h Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,61 @@ +/** + * @file qq_trans.h + * + * 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 _QQ_SEND_QUEUE_H_ +#define _QQ_SEND_QUEUE_H_ + +#include <glib.h> +#include "qq.h" + +enum { + QQ_TRANS_IS_SERVER = 0x01, /* Is server command or client command */ + /* prefix QQ_TRANS_CLI is for client command*/ + QQ_TRANS_CLI_EMERGE = 0x02, /* send at once; or may wait for next reply*/ + QQ_TRANS_CLI_IMPORT = 0x04, /* Only notice if not get reply; or resend, disconn if reties get 0*/ + QQ_TRANS_BEFORE_LOGIN = 0x08, /* server command before login*/ +}; + +typedef struct _qq_transaction { + guint8 flag; + guint16 seq; + guint16 cmd; + guint8 *data; + gint data_len; + + gint fd; + gint send_retries; + gint rcved_times; + gint scan_times; +} qq_transaction; + +qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq); +gboolean qq_trans_is_server(qq_transaction *trans) ; +gboolean qq_trans_is_dup(qq_transaction *trans); +void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len); +void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len); +void qq_trans_process_before_login(qq_data *qd); +gboolean qq_trans_scan(qq_data *qd); +void qq_trans_remove_all(qq_data *qd); + +#endif
--- a/libpurple/protocols/qq/recv_core.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,326 +0,0 @@ -/** - * @file recv_core.c - * - * 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 "debug.h" -#include "internal.h" - -#include "buddy_info.h" -#include "buddy_list.h" -#include "buddy_opt.h" -#include "buddy_status.h" -#include "char_conv.h" -#include "crypt.h" -#include "group_network.h" -#include "header_info.h" -#include "keep_alive.h" -#include "im.h" -#include "login_logout.h" -#include "packet_parse.h" -#include "qq_proxy.h" -#include "recv_core.h" -#include "sendqueue.h" -#include "sys_msg.h" -#include "utils.h" - -typedef struct _packet_before_login packet_before_login; -typedef struct _qq_recv_msg_header qq_recv_msg_header; - -struct _packet_before_login { - guint8 *buf; - gint len; -}; - -struct _qq_recv_msg_header { - guint8 header_tag; - guint16 source_tag; - guint16 cmd; - guint16 seq; /* can be ack_seq or send_seq, depends on cmd */ -}; - -/* check whether one sequence number is duplicated or not - * return TRUE if it is duplicated, otherwise FALSE */ -static gboolean _qq_check_packet_set_window(guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - guint8 *byte, mask; - - qd = (qq_data *) gc->proto_data; - byte = &(qd->window[seq / 8]); - mask = (1 << (seq % 8)); - - if ((*byte) & mask) - return TRUE; /* check mask */ - (*byte) |= mask; - return FALSE; /* set mask */ -} - -/* default process, decrypt and dump */ -static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, PurpleConnection *gc) -{ - qq_data *qd; - guint8 *data; - gchar *msg_utf8; - gint len; - - g_return_if_fail(buf != NULL && buf_len != 0); - - qd = (qq_data *) gc->proto_data; - len = buf_len; - data = g_newa(guint8, len); - msg_utf8 = NULL; - - _qq_show_packet("Processing unknown packet", buf, len); - if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) { - gchar *hex_dump = hex_dump_to_str(data, len); - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s", - seq, qq_get_cmd_desc(cmd), buf_len, hex_dump); - g_free(hex_dump); - try_dump_as_gbk(data, len); - } else { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); - } -} - -/* process the incoming packet from qq_pending */ -static void _qq_packet_process(guint8 *buf, gint buf_len, PurpleConnection *gc) -{ - qq_data *qd; - gint len, bytes_expected, bytes_read; - guint16 buf_len_read; /* two bytes in the begining of TCP packet */ - guint8 *cursor; - qq_recv_msg_header header; - packet_before_login *b4_packet; - - g_return_if_fail(buf != NULL && buf_len > 0); - - qd = (qq_data *) gc->proto_data; - bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; - - if (buf_len < bytes_expected) { - gchar *hex_dump = hex_dump_to_str(buf, buf_len); - purple_debug(PURPLE_DEBUG_ERROR, - "QQ", "Received packet is too short, dump and drop\n%s", hex_dump); - g_free(hex_dump); - return; - } - /* initialize */ - cursor = buf; - bytes_read = 0; - - /* QQ TCP packet returns first 2 bytes the length of this packet */ - if (qd->use_tcp) { - bytes_read += read_packet_w(buf, &cursor, buf_len, &buf_len_read); - if (buf_len_read != buf_len) { /* wrong */ - purple_debug - (PURPLE_DEBUG_ERROR, - "QQ", - "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read); - buf_len = buf_len_read; /* we believe header is more accurate */ - } - } - - /* now goes the normal QQ packet as UDP packet */ - bytes_read += read_packet_b(buf, &cursor, buf_len, &header.header_tag); - bytes_read += read_packet_w(buf, &cursor, buf_len, &header.source_tag); - bytes_read += read_packet_w(buf, &cursor, buf_len, &header.cmd); - bytes_read += read_packet_w(buf, &cursor, buf_len, &header.seq); - - if (bytes_read != bytes_expected) { /* read error */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail reading packet header, expect %d bytes, read %d bytes\n", - bytes_expected, bytes_read); - return; - } - - if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) { - gchar *hex_dump = hex_dump_to_str(buf, buf_len); - purple_debug(PURPLE_DEBUG_ERROR, - "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump); - g_free(hex_dump); - return; - } - - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "==> [%05d] %s, from (%s)\n", - header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag)); - - if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { - if (!qd->logged_in) { /* packets before login */ - b4_packet = g_new0(packet_before_login, 1); - /* must duplicate, buffer will be freed after exiting this function */ - b4_packet->buf = g_memdup(buf, buf_len); - b4_packet->len = buf_len; - if (qd->before_login_packets == NULL) - qd->before_login_packets = g_queue_new(); - g_queue_push_head(qd->before_login_packets, b4_packet); - return; /* do not process it now */ - } else if (!g_queue_is_empty(qd->before_login_packets)) { - /* logged_in, but we have packets before login */ - b4_packet = (packet_before_login *) - g_queue_pop_head(qd->before_login_packets); - _qq_packet_process(b4_packet->buf, b4_packet->len, gc); - /* in fact this is a recursive call, - * all packets before login will be processed before goes on */ - g_free(b4_packet->buf); /* the buf is duplicated, need to be freed */ - g_free(b4_packet); - } - } - - /* this is the length of all the encrypted data (also remove tail tag */ - len = buf_len - (bytes_read) - 1; - - /* whether it is an ack */ - switch (header.cmd) { - case QQ_CMD_RECV_IM: - case QQ_CMD_RECV_MSG_SYS: - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - /* server intiated packet, we need to send ack and check duplicaion - * this must be put after processing b4_packet - * as these packets will be passed in twice */ - if (_qq_check_packet_set_window(header.seq, gc)) { - purple_debug(PURPLE_DEBUG_WARNING, - "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd)); - return; - } - break; - default:{ /* ack packet, we need to update sendqueue */ - /* we do not check duplication for server ack */ - qq_sendqueue_remove(qd, header.seq); - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "ack [%05d] %s, remove from sendqueue\n", - header.seq, qq_get_cmd_desc(header.cmd)); - } - } - - /* now process the packet */ - switch (header.cmd) { - case QQ_CMD_KEEP_ALIVE: - qq_process_keep_alive_reply(cursor, len, gc); - break; - case QQ_CMD_UPDATE_INFO: - qq_process_modify_info_reply(cursor, len, gc); - break; - case QQ_CMD_ADD_FRIEND_WO_AUTH: - qq_process_add_buddy_reply(cursor, len, header.seq, gc); - break; - case QQ_CMD_DEL_FRIEND: - qq_process_remove_buddy_reply(cursor, len, gc); - break; - case QQ_CMD_REMOVE_SELF: - qq_process_remove_self_reply(cursor, len, gc); - break; - case QQ_CMD_BUDDY_AUTH: - qq_process_add_buddy_auth_reply(cursor, len, gc); - break; - case QQ_CMD_GET_USER_INFO: - qq_process_get_info_reply(cursor, len, gc); - break; - case QQ_CMD_CHANGE_ONLINE_STATUS: - qq_process_change_status_reply(cursor, len, gc); - break; - case QQ_CMD_SEND_IM: - qq_process_send_im_reply(cursor, len, gc); - break; - case QQ_CMD_RECV_IM: - qq_process_recv_im(cursor, len, header.seq, gc); - break; - case QQ_CMD_LOGIN: - qq_process_login_reply(cursor, len, gc); - break; - case QQ_CMD_GET_FRIENDS_LIST: - qq_process_get_buddies_list_reply(cursor, len, gc); - break; - case QQ_CMD_GET_FRIENDS_ONLINE: - qq_process_get_buddies_online_reply(cursor, len, gc); - break; - case QQ_CMD_GROUP_CMD: - qq_process_group_cmd_reply(cursor, len, header.seq, gc); - break; - case QQ_CMD_GET_ALL_LIST_WITH_GROUP: - qq_process_get_all_list_with_group_reply(cursor, len, gc); - break; - case QQ_CMD_GET_LEVEL: - qq_process_get_level_reply(cursor, len, gc); - break; - case QQ_CMD_REQUEST_LOGIN_TOKEN: - qq_process_request_login_token_reply(cursor, len, gc); - break; - case QQ_CMD_RECV_MSG_SYS: - qq_process_msg_sys(cursor, len, header.seq, gc); - break; - case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: - qq_process_friend_change_status(cursor, len, gc); - break; - default: - _qq_process_packet_default(cursor, len, header.cmd, header.seq, gc); - break; - } -} - -/* clean up the packets before login */ -void qq_b4_packets_free(qq_data *qd) -{ - packet_before_login *b4_packet; - g_return_if_fail(qd != NULL); - /* now clean up my own data structures */ - if (qd->before_login_packets != NULL) { - while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) { - g_free(b4_packet->buf); - g_free(b4_packet); - } - g_queue_free(qd->before_login_packets); - } -} - -void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleConnection *gc; - qq_data *qd; - guint8 *buf; - gint len; - - gc = (PurpleConnection *) data; - - if(cond != PURPLE_INPUT_READ) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Socket error")); - return; - } - - qd = (qq_data *) gc->proto_data; - buf = g_newa(guint8, MAX_PACKET_SIZE); - - /* here we have UDP proxy suppport */ - len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE); - if (len <= 0) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to read from socket")); - return; - } else { - _qq_packet_process(buf, len, gc); - } -}
--- a/libpurple/protocols/qq/recv_core.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/** - * @file recv_core.h - * - * 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 _QQ_RECV_CORE_H_ -#define _QQ_RECV_CORE_H_ - -#include <glib.h> -#include "connection.h" -#include "qq.h" - -void qq_b4_packets_free(qq_data *qd); - -void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond); - -#endif
--- a/libpurple/protocols/qq/send_core.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ -/** - * @file send_core.c - * - * 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 "debug.h" -#include "internal.h" - -#include "crypt.h" -#include "header_info.h" -#include "packet_parse.h" -#include "qq.h" -#include "qq_proxy.h" -#include "send_core.h" -#include "sendqueue.h" - -/* create qq packet header with given sequence - * return the number of bytes in header if succeeds - * return -1 if there is any error */ -gint _create_packet_head_seq(guint8 *buf, guint8 **cursor, - PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq) -{ - qq_data *qd; - gint bytes_expected, bytes_written; - - g_return_val_if_fail(buf != NULL && cursor != NULL && *cursor != NULL, -1); - - qd = (qq_data *) gc->proto_data; - if (is_auto_seq) - *seq = ++(qd->send_seq); - - *cursor = buf; - bytes_written = 0; - bytes_expected = (qd->use_tcp) ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; - - /* QQ TCP packet has two bytes in the begining defines packet length - * so I leave room here for size */ - if (qd->use_tcp) - bytes_written += create_packet_w(buf, cursor, 0x0000); - - /* now comes the normal QQ packet as UDP */ - bytes_written += create_packet_b(buf, cursor, QQ_PACKET_TAG); - bytes_written += create_packet_w(buf, cursor, QQ_CLIENT); - bytes_written += create_packet_w(buf, cursor, cmd); - bytes_written += create_packet_w(buf, cursor, *seq); - - if (bytes_written != bytes_expected) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes_written); - bytes_written = -1; - } - return bytes_written; -} - -/* for those need ack and resend no ack feed back from server - * return number of bytes written to the socket, - * return -1 if there is any error */ -gint _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd) -{ - qq_data *qd; - qq_sendpacket *p; - gint bytes_sent; - guint8 *cursor; - - qd = (qq_data *) gc->proto_data; - - if (qd->use_tcp) { - if (len > MAX_PACKET_SIZE) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "xxx [%05d] %s, %d bytes is too large, do not send\n", - qd->send_seq, qq_get_cmd_desc(cmd), len); - return -1; - } else { /* I update the len for TCP packet */ - cursor = buf; - create_packet_w(buf, &cursor, len); - } - } - - bytes_sent = qq_proxy_write(qd, buf, len); - - if (bytes_sent >= 0) { /* put to queue, for matching server ACK usage */ - p = g_new0(qq_sendpacket, 1); - p->fd = qd->fd; - p->cmd = cmd; - p->send_seq = qd->send_seq; - p->resend_times = 0; - p->sendtime = time(NULL); - p->buf = g_memdup(buf, len); /* don't use g_strdup, may have 0x00 */ - p->len = len; - qd->sendqueue = g_list_append(qd->sendqueue, p); - } - - return bytes_sent; -} - -/* send the packet generated with the given cmd and data - * return the number of bytes sent to socket if succeeds - * return -1 if there is any error */ -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, - gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len) -{ - qq_data *qd; - guint8 *buf, *cursor, *encrypted_data; - guint16 seq_ret; - gint encrypted_len, bytes_written, bytes_expected, bytes_sent; - - qd = (qq_data *) gc->proto_data; - g_return_val_if_fail(qd->session_key != NULL, -1); - - buf = g_newa(guint8, MAX_PACKET_SIZE); - encrypted_len = len + 16; /* at most 16 bytes more */ - encrypted_data = g_newa(guint8, encrypted_len); - cursor = buf; - bytes_written = 0; - - qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len); - - seq_ret = seq; - if (_create_packet_head_seq(buf, &cursor, gc, cmd, is_auto_seq, &seq_ret) >= 0) { - bytes_expected = 4 + encrypted_len + 1; - bytes_written += create_packet_dw(buf, &cursor, (guint32) qd->uid); - bytes_written += create_packet_data(buf, &cursor, encrypted_data, encrypted_len); - bytes_written += create_packet_b(buf, &cursor, QQ_PACKET_TAIL); - if (bytes_written == bytes_expected) { /* packet OK */ - /* if it does not need ACK, we send ACK manually several times */ - if (need_ack) /* my request, send it */ - bytes_sent = _qq_send_packet(gc, buf, cursor - buf, cmd); - else /* server's request, send ACK */ - bytes_sent = qq_proxy_write(qd, buf, cursor - buf); - - if (QQ_DEBUG) - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "<== [%05d] %s, %d bytes\n", seq_ret, qq_get_cmd_desc(cmd), bytes_sent); - return bytes_sent; - } else { /* bad packet */ - purple_debug(PURPLE_DEBUG_ERROR, "QQ", - "Fail creating packet, expect %d bytes, written %d bytes\n", - bytes_expected, bytes_written); - return -1; - } - } - - return -1; -}
--- a/libpurple/protocols/qq/send_core.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/** - * @file send_core.h - * - * 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 _QQ_SEND_CORE_H_ -#define _QQ_SEND_CORE_H_ - -#include <glib.h> -#include "connection.h" - -gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, - gboolean need_ack, guint8 *data, gint len); -gint _qq_send_packet(PurpleConnection * gc, guint8 *buf, gint len, guint16 cmd); -gint _create_packet_head_seq(guint8 *buf, guint8 **cursor, - PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq); - -#endif
--- a/libpurple/protocols/qq/send_file.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/send_file.c Thu Nov 20 21:13:56 2008 +0000 @@ -29,14 +29,14 @@ #include "network.h" #include "notify.h" -#include "buddy_status.h" +#include "buddy_list.h" #include "crypt.h" #include "file_trans.h" #include "header_info.h" #include "im.h" -#include "keep_alive.h" +#include "qq_base.h" #include "packet_parse.h" -#include "send_core.h" +#include "qq_network.h" #include "utils.h" enum @@ -103,6 +103,7 @@ return send(info->sender_fd, buf, len, 0); } */ + static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer) { struct sockaddr_in sin; @@ -243,42 +244,45 @@ g_free(internet_ip_str); } -void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info) +#define QQ_CONN_INFO_LEN 61 +gint qq_get_conn_info(ft_info *info, guint8 *data) { - read_packet_data(data, cursor, data_len, info->file_session_key, 16); - *cursor += 30; - read_packet_b(data, cursor, data_len, &info->conn_method); - read_packet_dw(data, cursor, data_len, &info->remote_internet_ip); - read_packet_w(data, cursor, data_len, &info->remote_internet_port); - read_packet_w(data, cursor, data_len, &info->remote_major_port); - read_packet_dw(data, cursor, data_len, &info->remote_real_ip); - read_packet_w(data, cursor, data_len, &info->remote_minor_port); + gint bytes = 0; + /* 16 + 30 + 1 + 4 + 2 + 2 + 4 + 2 = 61 */ + bytes += qq_getdata(info->file_session_key, 16, data + bytes); + bytes += 30; /* skip 30 bytes */ + bytes += qq_get8(&info->conn_method, data + bytes); + bytes += qq_get32(&info->remote_internet_ip, data + bytes); + bytes += qq_get16(&info->remote_internet_port, data + bytes); + bytes += qq_get16(&info->remote_major_port, data + bytes); + bytes += qq_get32(&info->remote_real_ip, data + bytes); + bytes += qq_get16(&info->remote_minor_port, data + bytes); qq_show_conn_info(info); + return bytes; } -gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info) +gint qq_fill_conn_info(guint8 *raw_data, ft_info *info) { - gint bytes; - bytes = 0; + gint bytes = 0; /* 064: connection method, UDP 0x00, TCP 0x03 */ - bytes += create_packet_b (raw_data, cursor, info->conn_method); + bytes += qq_put8 (raw_data + bytes, info->conn_method); /* 065-068: outer ip address of sender (proxy address) */ - bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip); + bytes += qq_put32 (raw_data + bytes, info->local_internet_ip); /* 069-070: sender port */ - bytes += create_packet_w (raw_data, cursor, info->local_internet_port); + bytes += qq_put16 (raw_data + bytes, info->local_internet_port); /* 071-072: the first listening port(TCP doesn't have this part) */ - bytes += create_packet_w (raw_data, cursor, info->local_major_port); + bytes += qq_put16 (raw_data + bytes, info->local_major_port); /* 073-076: real ip */ - bytes += create_packet_dw (raw_data, cursor, info->local_real_ip); + bytes += qq_put32 (raw_data + bytes, info->local_real_ip); /* 077-078: the second listening port */ - bytes += create_packet_w (raw_data, cursor, info->local_minor_port); + bytes += qq_put16 (raw_data + bytes, info->local_minor_port); return bytes; } /* fill in the common information of file transfer */ static gint _qq_create_packet_file_header -(guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) +(guint8 *raw_data, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack) { gint bytes; time_t now; @@ -294,42 +298,42 @@ } /* 000-003: receiver uid */ - bytes += create_packet_dw (raw_data, cursor, qd->uid); + bytes += qq_put32 (raw_data + bytes, qd->uid); /* 004-007: sender uid */ - bytes += create_packet_dw (raw_data, cursor, to_uid); + bytes += qq_put32 (raw_data + bytes, to_uid); /* 008-009: sender client version */ - bytes += create_packet_w (raw_data, cursor, QQ_CLIENT); + bytes += qq_put16 (raw_data + bytes, QQ_CLIENT); /* 010-013: receiver uid */ - bytes += create_packet_dw (raw_data, cursor, qd->uid); + bytes += qq_put32 (raw_data + bytes, qd->uid); /* 014-017: sender uid */ - bytes += create_packet_dw (raw_data, cursor, to_uid); + bytes += qq_put32 (raw_data + bytes, to_uid); /* 018-033: md5 of (uid+session_key) */ - bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16); + bytes += qq_putdata (raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ - bytes += create_packet_w (raw_data, cursor, message_type); + bytes += qq_put16 (raw_data + bytes, message_type); /* 036-037: sequence number */ - bytes += create_packet_w (raw_data, cursor, seq); + bytes += qq_put16 (raw_data + bytes, seq); /* 038-041: send time */ - bytes += create_packet_dw (raw_data, cursor, (guint32) now); + bytes += qq_put32 (raw_data + bytes, (guint32) now); /* 042-042: always 0x00 */ - bytes += create_packet_b (raw_data, cursor, 0x00); + bytes += qq_put8 (raw_data + bytes, 0x00); /* 043-043: sender icon */ - bytes += create_packet_b (raw_data, cursor, qd->my_icon); + bytes += qq_put8 (raw_data + bytes, qd->my_icon); /* 044-046: always 0x00 */ - bytes += create_packet_w (raw_data, cursor, 0x0000); - bytes += create_packet_b (raw_data, cursor, 0x00); + bytes += qq_put16 (raw_data + bytes, 0x0000); + bytes += qq_put8 (raw_data + bytes, 0x00); /* 047-047: we use font attr */ - bytes += create_packet_b (raw_data, cursor, 0x01); + bytes += qq_put8 (raw_data + bytes, 0x01); /* 048-051: always 0x00 */ - bytes += create_packet_dw (raw_data, cursor, 0x00000000); + bytes += qq_put32 (raw_data + bytes, 0x00000000); /* 052-062: always 0x00 */ - bytes += create_packet_dw (raw_data, cursor, 0x00000000); - bytes += create_packet_dw (raw_data, cursor, 0x00000000); - bytes += create_packet_w (raw_data, cursor, 0x0000); - bytes += create_packet_b (raw_data, cursor, 0x00); + bytes += qq_put32 (raw_data + bytes, 0x00000000); + bytes += qq_put32 (raw_data + bytes, 0x00000000); + bytes += qq_put16 (raw_data + bytes, 0x0000); + bytes += qq_put8 (raw_data + bytes, 0x00); /* 063: transfer_type, 0x65: FILE 0x6b: FACE */ - bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */ + bytes += qq_put8 (raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME */ return bytes; } @@ -433,7 +437,7 @@ static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gchar *filelen_str; gint filename_len, filelen_strlen, packet_len, bytes; ft_info *info; @@ -443,7 +447,7 @@ info = g_new0(ft_info, 1); info->to_uid = to_uid; info->send_seq = qd->send_seq; - info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip)); + info->local_internet_ip = qd->my_ip.s_addr; info->local_internet_port = qd->my_port; info->local_real_ip = 0x00000000; info->conn_method = 0x00; @@ -455,27 +459,24 @@ packet_len = 82 + filename_len + filelen_strlen; raw_data = g_newa(guint8, packet_len); - cursor = raw_data; + bytes = 0; - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_REQ, qd, FALSE); - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += qq_fill_conn_info(raw_data + bytes, info); /* 079: 0x20 */ - bytes += create_packet_b (raw_data, &cursor, 0x20); + bytes += qq_put8 (raw_data + bytes, 0x20); /* 080: 0x1f */ - bytes += create_packet_b (raw_data, &cursor, 0x1f); + bytes += qq_put8 (raw_data + bytes, 0x1f); /* undetermined len: filename */ - bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename, - filename_len); + bytes += qq_putdata (raw_data + bytes, (guint8 *) filename, filename_len); /* 0x1f */ - bytes += create_packet_b (raw_data, &cursor, 0x1f); + bytes += qq_put8 (raw_data + bytes, 0x1f); /* file length */ - bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str, - filelen_strlen); + bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen); if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request", "%d bytes expected but got %d bytes\n", @@ -488,7 +489,7 @@ static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; guint16 minor_port; guint32 real_ip; gint packet_len, bytes; @@ -502,22 +503,21 @@ packet_len = 79; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; + bytes = 0; minor_port = info->local_minor_port; real_ip = info->local_real_ip; info->local_minor_port = 0; info->local_real_ip = 0; - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE); - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE); + bytes += qq_fill_conn_info(raw_data + bytes, info); info->local_minor_port = minor_port; info->local_real_ip = real_ip; if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept", "%d bytes expected but got %d bytes\n", @@ -529,7 +529,7 @@ PurpleXfer *xfer; ft_info *info; qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gint packet_len, bytes; qd = (qq_data *) gc->proto_data; @@ -538,14 +538,13 @@ packet_len = 79; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; + bytes = 0; purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n"); - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE); - bytes += qq_fill_conn_info(raw_data, &cursor, info); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE); + bytes += qq_fill_conn_info(raw_data + bytes, info); if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify", "%d bytes expected but got %d bytes\n", @@ -560,7 +559,7 @@ static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gint packet_len, bytes; purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start"); @@ -568,14 +567,12 @@ packet_len = 64; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; bytes = 0; - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE); if (packet_len == bytes) - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file", "%d bytes expected but got %d bytes\n", @@ -586,7 +583,7 @@ static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid) { qq_data *qd; - guint8 *cursor, *raw_data; + guint8 *raw_data; gint packet_len, bytes; purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n"); @@ -594,17 +591,15 @@ packet_len = 64; raw_data = g_newa (guint8, packet_len); - cursor = raw_data; bytes = 0; purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n"); - bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); + bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE); purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n"); if (packet_len == bytes) { purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n"); - qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, - cursor - raw_data); + qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes); } else purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file", @@ -688,7 +683,7 @@ } /* process reject im for file transfer request */ -void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len, +void qq_process_recv_file_reject (guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gchar *msg, *filename; @@ -698,11 +693,13 @@ qd = (qq_data *) gc->proto_data; g_return_if_fail (qd->xfer != NULL); + /* border has been checked before if (*cursor >= (data + data_len - 1)) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } + */ filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; msg = g_strdup_printf(_("%d has declined the file %s"), sender_uid, filename); @@ -715,7 +712,7 @@ } /* process cancel im for file transfer request */ -void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len, +void qq_process_recv_file_cancel (guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { gchar *msg, *filename; @@ -726,11 +723,13 @@ g_return_if_fail (qd->xfer != NULL && purple_xfer_get_filename(qd->xfer) != NULL); + /* border has been checked before if (*cursor >= (data + data_len - 1)) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } + */ filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1; msg = g_strdup_printf (_("%d canceled the transfer of %s"), @@ -744,27 +743,26 @@ } /* process accept im for file transfer request */ -void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc) +void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { qq_data *qd; + gint bytes; ft_info *info; PurpleXfer *xfer; g_return_if_fail (data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; xfer = qd->xfer; + info = (ft_info *) qd->xfer->data; - if (*cursor >= (data + data_len - 1)) { + if (data_len <= 30 + QQ_CONN_INFO_LEN) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file reject message is empty\n"); return; } - info = (ft_info *) qd->xfer->data; - - *cursor = data + 18 + 12; - qq_get_conn_info(data, cursor, data_len, info); + bytes = 18 + 12; /* skip 30 bytes */ + qq_get_conn_info(info, data + bytes); _qq_xfer_init_socket(qd->xfer); _qq_xfer_init_udp_channel(info); @@ -772,8 +770,7 @@ } /* process request from buddy's im for file transfer request */ -void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection * gc) +void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection * gc) { qq_data *qd; PurpleXfer *xfer; @@ -781,25 +778,27 @@ ft_info *info; PurpleBuddy *b; qq_buddy *q_bud; + gint bytes; g_return_if_fail (data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; - if (*cursor >= (data + data_len - 1)) { - purple_debug (PURPLE_DEBUG_WARNING, "QQ", - "Received file reject message is empty\n"); - return; - } - - info = g_new0(ft_info, 1); - info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip)); + info = g_newa(ft_info, 1); + info->local_internet_ip = qd->my_ip.s_addr; info->local_internet_port = qd->my_port; info->local_real_ip = 0x00000000; info->to_uid = sender_uid; - read_packet_w(data, cursor, data_len, &(info->send_seq)); + + if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { + purple_debug (PURPLE_DEBUG_WARNING, "QQ", + "Received file request message is empty\n"); + return; + } + bytes = 0; + bytes += qq_get16(&(info->send_seq), data + bytes); - *cursor = data + 18 + 12; - qq_get_conn_info(data, cursor, data_len, info); + bytes += 18 + 12; /* skip 30 bytes */ + bytes += qq_get_conn_info(info, data + bytes); fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2); g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL); @@ -815,11 +814,11 @@ q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; if (q_bud) { if(0 != info->remote_real_ip) { - g_memmove(q_bud->ip, &info->remote_real_ip, 4); + g_memmove(&(q_bud->ip), &info->remote_real_ip, sizeof(q_bud->ip)); q_bud->port = info->remote_minor_port; } else if (0 != info->remote_internet_ip) { - g_memmove(q_bud->ip, &info->remote_internet_ip, 4); + g_memmove(&(q_bud->ip), &info->remote_internet_ip, sizeof(q_bud->ip)); q_bud->port = info->remote_major_port; } @@ -832,7 +831,7 @@ } else - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid); + purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in list\n", sender_uid); g_free(sender_name); g_strfreev(fileinfo); @@ -880,9 +879,10 @@ */ } -void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, +void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc) { + gint bytes; qq_data *qd; ft_info *info; PurpleXfer *xfer; @@ -890,19 +890,19 @@ g_return_if_fail (data != NULL && data_len != 0); qd = (qq_data *) gc->proto_data; - if (*cursor >= (data + data_len - 1)) { + xfer = qd->xfer; + info = (ft_info *) qd->xfer->data; + if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) { purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received file notify message is empty\n"); return; } + + bytes = 0; + bytes += qq_get16(&(info->send_seq), data + bytes); - xfer = qd->xfer; - info = (ft_info *) qd->xfer->data; - /* FIXME */ - read_packet_w(data, cursor, data_len, &(info->send_seq)); - - *cursor = data + 18 + 12; - qq_get_conn_info(data, cursor, data_len, info); + bytes += 18 + 12; + bytes += qq_get_conn_info(info, data + bytes); _qq_xfer_init_udp_channel(info); @@ -938,7 +938,7 @@ /* static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key) { - qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1); + qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, &key, 1); } static void qq_process_recv_request_key(PurpleConnection *gc)
--- a/libpurple/protocols/qq/send_file.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/send_file.h Thu Nov 20 21:13:56 2008 +0000 @@ -26,11 +26,12 @@ #define _QQ_QQ_SEND_FILE_H_ #include "ft.h" +#include "qq.h" typedef struct _ft_info { guint32 to_uid; guint16 send_seq; - guint8 file_session_key[16]; + guint8 file_session_key[QQ_KEY_LENGTH]; guint8 conn_method; guint32 remote_internet_ip; guint16 remote_internet_port; @@ -66,20 +67,15 @@ gboolean use_major; } ft_info; -void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_reject(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_cancel(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); -void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len, - guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_reject(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_cancel(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); +void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc); gboolean qq_can_receive_file(PurpleConnection *gc, const char *who); void qq_send_file(PurpleConnection *gc, const char *who, const char *file); -void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info); -gint qq_fill_conn_info(guint8 *data, guint8 **cursor, ft_info *info); +gint qq_get_conn_info(ft_info *info, guint8 *data); +gint qq_fill_conn_info(guint8 *data, ft_info *info); gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer); #endif
--- a/libpurple/protocols/qq/sendqueue.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -/** - * @file sendqueue.c - * - * 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 "internal.h" - -#include "connection.h" -#include "debug.h" -#include "notify.h" -#include "prefs.h" -#include "request.h" - -#include "header_info.h" -#include "qq_proxy.h" -#include "sendqueue.h" - -#define QQ_RESEND_MAX 8 /* max resend per packet */ - -typedef struct _gc_and_packet gc_and_packet; - -struct _gc_and_packet { - PurpleConnection *gc; - qq_sendpacket *packet; -}; - -/* Remove a packet with send_seq from sendqueue */ -void qq_sendqueue_remove(qq_data *qd, guint16 send_seq) -{ - GList *list; - qq_sendpacket *p; - - list = qd->sendqueue; - while (list != NULL) { - p = (qq_sendpacket *) (list->data); - if (p->send_seq == send_seq) { - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - break; - } - list = list->next; - } -} - -/* clean up sendqueue and free all contents */ -void qq_sendqueue_free(qq_data *qd) -{ - qq_sendpacket *p; - gint i; - - i = 0; - while (qd->sendqueue != NULL) { - p = (qq_sendpacket *) (qd->sendqueue->data); - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - i++; - } - purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i); -} - -/* FIXME We shouldn't be dropping packets, but for now we have to because - * somewhere we're generating invalid packets that the server won't ack. - * Given enough time, a buildup of those packets would crash the client. */ -gboolean qq_sendqueue_timeout_callback(gpointer data) -{ - PurpleConnection *gc; - qq_data *qd; - GList *list; - qq_sendpacket *p; - time_t now; - gint wait_time; - - gc = (PurpleConnection *) data; - qd = (qq_data *) gc->proto_data; - now = time(NULL); - list = qd->sendqueue; - - /* empty queue, return TRUE so that timeout continues functioning */ - if (qd->sendqueue == NULL) - return TRUE; - - while (list != NULL) { /* remove all packet whose resend_times == -1 */ - p = (qq_sendpacket *) list->data; - if (p->resend_times == -1) { /* to remove */ - qd->sendqueue = g_list_remove(qd->sendqueue, p); - g_free(p->buf); - g_free(p); - list = qd->sendqueue; - } else { - list = list->next; - } - } - - list = qd->sendqueue; - while (list != NULL) { - p = (qq_sendpacket *) list->data; - if (p->resend_times == QQ_RESEND_MAX) { /* reach max */ - switch (p->cmd) { - case QQ_CMD_KEEP_ALIVE: - if (qd->logged_in) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); - qd->logged_in = FALSE; - } - p->resend_times = -1; - break; - case QQ_CMD_LOGIN: - case QQ_CMD_REQUEST_LOGIN_TOKEN: - if (!qd->logged_in) /* cancel login progress */ - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); - p->resend_times = -1; - break; - default:{ - purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "%s packet sent %d times but not acked. Not resending it.\n", - qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX); - } - p->resend_times = -1; - } - } else { /* resend_times < QQ_RESEND_MAX, so sent it again */ - wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000); - if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) { - qq_proxy_write(qd, p->buf, p->len); - p->resend_times++; - purple_debug(PURPLE_DEBUG_INFO, - "QQ", "<<< [%05d] send again for %d times!\n", - p->send_seq, p->resend_times); - } - } - list = list->next; - } - return TRUE; /* if we return FALSE, the timeout callback stops functioning */ -}
--- a/libpurple/protocols/qq/sendqueue.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * @file sendqueue.h - * - * 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 _QQ_SEND_QUEUE_H_ -#define _QQ_SEND_QUEUE_H_ - -#include <glib.h> -#include "qq.h" - -#define QQ_SENDQUEUE_TIMEOUT 5000 /* in 1/1000 sec */ - -typedef struct _qq_sendpacket qq_sendpacket; - -struct _qq_sendpacket { - gint fd; - gint len; - guint8 *buf; - guint16 cmd; - guint16 send_seq; - gint resend_times; - time_t sendtime; -}; - -void qq_sendqueue_free(qq_data *qd); - -void qq_sendqueue_remove(qq_data *qd, guint16 send_seq); -gboolean qq_sendqueue_timeout_callback(gpointer data); - -#endif
--- a/libpurple/protocols/qq/sys_msg.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/sys_msg.c Thu Nov 20 21:13:56 2008 +0000 @@ -35,7 +35,7 @@ #include "header_info.h" #include "packet_parse.h" #include "qq.h" -#include "send_core.h" +#include "qq_network.h" #include "sys_msg.h" #include "utils.h" @@ -44,6 +44,7 @@ QQ_MSG_SYS_ADD_CONTACT_REQUEST = 0x02, QQ_MSG_SYS_ADD_CONTACT_APPROVED = 0x03, QQ_MSG_SYS_ADD_CONTACT_REJECTED = 0x04, + QQ_MSG_SYS_NOTICE= 0x06, QQ_MSG_SYS_NEW_VERSION = 0x09 }; @@ -120,27 +121,29 @@ /* Send ACK if the sys message needs an ACK */ static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq) { - guint8 bar, *ack, *cursor; + qq_data *qd; + guint8 bar, *ack; gchar *str; gint ack_len, bytes; + qd = (qq_data *) gc->proto_data; + str = g_strdup_printf("%d", from); bar = 0x1e; ack_len = 1 + 1 + strlen(str) + 1 + 2; ack = g_newa(guint8, ack_len); - cursor = ack; + bytes = 0; - - bytes += create_packet_b(ack, &cursor, code); - bytes += create_packet_b(ack, &cursor, bar); - bytes += create_packet_data(ack, &cursor, (guint8 *) str, strlen(str)); - bytes += create_packet_b(ack, &cursor, bar); - bytes += create_packet_w(ack, &cursor, seq); + bytes += qq_put8(ack + bytes, code); + bytes += qq_put8(ack + bytes, bar); + bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str)); + bytes += qq_put8(ack + bytes, bar); + bytes += qq_put16(ack + bytes, seq); g_free(str); if (bytes == ack_len) /* creation OK */ - qq_send_cmd(gc, QQ_CMD_ACK_SYS_MSG, TRUE, 0, FALSE, ack, ack_len); + qq_send_cmd_detail(qd, QQ_CMD_ACK_SYS_MSG, 0, FALSE, ack, ack_len); else purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes); @@ -275,6 +278,20 @@ g_free(name); } +static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8) +{ + gchar *title, *content; + + g_return_if_fail(from != NULL && to != NULL); + + title = g_strdup_printf(_("Notice from: %s"), from); + content = g_strdup_printf(_("%s"), msg_utf8); + + purple_notify_info(gc, NULL, title, content); + g_free(title); + g_free(content); +} + void qq_process_msg_sys(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc) { qq_data *qd; @@ -318,9 +335,12 @@ case QQ_MSG_SYS_ADD_CONTACT_REJECTED: _qq_process_msg_sys_add_contact_rejected(gc, from, to, msg_utf8); break; + case QQ_MSG_SYS_NOTICE: + _qq_process_msg_sys_notice(gc, from, to, msg_utf8); + break; case QQ_MSG_SYS_NEW_VERSION: purple_debug(PURPLE_DEBUG_WARNING, "QQ", - "QQ server says there is newer version than %s\n", qq_get_source_str(QQ_CLIENT)); + "QQ server says there is newer version than %s\n", qq_get_ver_desc(QQ_CLIENT)); break; default: purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Recv unknown sys msg code: %s\n", code);
--- a/libpurple/protocols/qq/udp_proxy_s5.c Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,381 +0,0 @@ -/** - * @file udp_proxy_s5.c - * - * 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 "debug.h" - -#include "udp_proxy_s5.h" - -static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb = data; - struct sockaddr_in sin; - int len, error; - socklen_t errlen; - int flags; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n"); - - len = read(source, buf, 10); - if (len < 10) { - purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, source, NULL); - } - - g_free(phb->host); - g_free(phb); - return; - } - if ((buf[0] != 0x05) || (buf[1] != 0x00)) { - if ((buf[0] == 0x05) && (buf[1] < 0x09)) - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]); - else - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - sin.sin_family = AF_INET; - memcpy(&sin.sin_addr.s_addr, buf + 4, 4); - memcpy(&sin.sin_port, buf + 8, 2); - - if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno)); - close(phb->udpsock); - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - error = ETIMEDOUT; - purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n"); - errlen = sizeof(error); - if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) { - purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n"); - close(phb->udpsock); - return; - } - flags = fcntl(phb->udpsock, F_GETFL); - fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, phb->udpsock, NULL); - } - - g_free(phb->host); - g_free(phb); -} - -static void _qq_s5_sendconnect(gpointer data, gint source) -{ - unsigned char buf[512]; - struct PHB *phb = data; - struct sockaddr_in sin, ctlsin; - int port; - socklen_t ctllen; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port); - - buf[0] = 0x05; - buf[1] = 0x03; /* udp relay */ - buf[2] = 0x00; /* reserved */ - buf[3] = 0x01; /* address type -- ipv4 */ - memset(buf + 4, 0, 0x04); - - ctllen = sizeof(ctlsin); - if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno)); - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0); - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock); - if (phb->udpsock < 0) { - close(source); - g_free(phb->host); - g_free(phb); - return; - } - - flags = fcntl(phb->udpsock, F_GETFL); - fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK); - - port = g_ntohs(ctlsin.sin_port) + 1; - while (1) { - inet_aton("0.0.0.0", &(sin.sin_addr)); - sin.sin_family = AF_INET; - sin.sin_port = g_htons(port); - if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { - port++; - if (port > 65500) { - close(source); - g_free(phb->host); - g_free(phb); - return; - } - } else - break; - } - - memset(buf + 4, 0, 0x04); - memcpy(buf + 8, &(sin.sin_port), 0x02); - - if (write(source, buf, 10) < 10) { - close(source); - purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n"); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb); -} - -static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb = data; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n"); - - if (read(source, buf, 2) < 2) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if ((buf[0] != 0x01) || (buf[1] != 0x00)) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - _qq_s5_sendconnect(phb, source); -} - -static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - struct PHB *phb; - int ret; - - phb = data; - - purple_input_remove(phb->inpa); - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n"); - - ret = read(source, buf, 2); - if (ret < 2) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if ((buf[0] != 0x05) || (buf[1] == 0xff)) { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - if (buf[1] == 0x02) { - unsigned int i, j; - - i = strlen(purple_proxy_info_get_username(phb->gpi)); - j = strlen(purple_proxy_info_get_password(phb->gpi)); - - buf[0] = 0x01; /* version 1 */ - buf[1] = i; - memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i); - buf[2 + i] = j; - memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j); - - if (write(source, buf, 3 + i + j) < 3 + i + j) { - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb); - } else { - purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n"); - _qq_s5_sendconnect(phb, source); - } -} - -static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond) -{ - unsigned char buf[512]; - int i; - struct PHB *phb = data; - socklen_t len; - int error = ETIMEDOUT; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n"); - - if (phb->inpa > 0) - purple_input_remove(phb->inpa); - - len = sizeof(error); - if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { - purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno)); - close(source); - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - flags = fcntl(source, F_GETFL); - fcntl(source, F_SETFL, flags & ~O_NONBLOCK); - - i = 0; - buf[0] = 0x05; /* SOCKS version 5 */ - - if (purple_proxy_info_get_username(phb->gpi) != NULL) { - buf[1] = 0x02; /* two methods */ - buf[2] = 0x00; /* no authentication */ - buf[3] = 0x02; /* username/password authentication */ - i = 4; - } else { - buf[1] = 0x01; - buf[2] = 0x00; - i = 3; - } - - if (write(source, buf, i) < i) { - purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno)); - purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n"); - close(source); - - if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) { - - phb->func(phb->data, -1, _("Unable to connect")); - } - - g_free(phb->host); - g_free(phb); - return; - } - - phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb); -} - -gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen) -{ - gint fd; - int flags; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", - "Connecting to %s:%d via %s:%d using SOCKS5\n", - phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi)); - - if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0) - return -1; - - purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd); - - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - if (connect(fd, addr, addrlen) < 0) { - if ((errno == EINPROGRESS) || (errno == EINTR)) { - purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); - phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb); - } else { - close(fd); - return -1; - } - } else { - purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n"); - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - _qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE); - } - - return fd; -}
--- a/libpurple/protocols/qq/udp_proxy_s5.h Mon Aug 18 17:08:01 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/** - * @file udp_proxy_s5.h - * - * 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 _QQ_UDP_PROXY_S5_H_ -#define _QQ_UDP_PROXY_S5_H_ - -#include "internal.h" /* for socket stuff */ - -#include "qq_proxy.h" - -gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen); - -#endif
--- a/libpurple/protocols/qq/utils.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/utils.c Thu Nov 20 21:13:56 2008 +0000 @@ -22,7 +22,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ -#include "cipher.h" #include "limits.h" #include "stdlib.h" #include "string.h" @@ -31,6 +30,8 @@ #include "win32dep.h" #endif +#include "cipher.h" + #include "char_conv.h" #include "debug.h" #include "prefs.h" @@ -40,6 +41,32 @@ #define QQ_NAME_FORMAT "%d" +/* These functions are used only in development phase */ +/* + static void _qq_show_socket(gchar *desc, gint fd) { + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + getsockname(fd, (struct sockaddr *)&sin, &len); + purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", + inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); + } + */ + +void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len) +{ + PurpleCipher *cipher; + PurpleCipherContext *context; + + g_return_if_fail(md5 != NULL && md5_len > 0); + g_return_if_fail(src != NULL && src_len > 0); + + cipher = purple_ciphers_find_cipher("md5"); + context = purple_cipher_context_new(cipher, NULL); + purple_cipher_context_append(context, src, src_len); + purple_cipher_context_digest(context, md5_len, md5, NULL); + purple_cipher_context_destroy(context); +} + gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount) { gint index; @@ -87,7 +114,7 @@ g_memmove(input, data, len); input[len] = 0x00; - segments = g_strsplit((gchar *) input, delimit, 0); + segments = g_strsplit_set((gchar *) input, delimit, 0); if (expected_fields <= 0) return segments; @@ -113,30 +140,20 @@ return segments; } -/* generate a md5 key using uid and session_key */ -guint8 *_gen_session_md5(gint uid, guint8 *session_key) +/* convert Purple name to original QQ UID */ +guint32 purple_name_to_uid(const gchar *const name) { - guint8 *src, md5_str[QQ_KEY_LENGTH]; - PurpleCipher *cipher; - PurpleCipherContext *context; + guint32 ret; + g_return_val_if_fail(name != NULL, 0); - src = g_newa(guint8, 20); - memcpy(src, &uid, 4); - memcpy(src, session_key, QQ_KEY_LENGTH); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - purple_cipher_context_append(context, src, 20); - purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL); - purple_cipher_context_destroy(context); - - return g_memdup(md5_str, QQ_KEY_LENGTH); + ret = strtol(name, NULL, 10); + if (errno == ERANGE) + return 0; + else + return ret; } -/* given a four-byte ip data, convert it into a human readable ip string - * the return needs to be freed */ -gchar *gen_ip_str(guint8 *ip) -{ +gchar *gen_ip_str(guint8 *ip) { gchar *ret; if (ip == NULL || ip[0] == 0) { ret = g_new(gchar, 1); @@ -159,19 +176,6 @@ return ip; } -/* convert Purple name to original QQ UID */ -guint32 purple_name_to_uid(const gchar *const name) -{ - guint32 ret; - g_return_val_if_fail(name != NULL, 0); - - ret = strtol(name, NULL, 10); - if (errno == ERANGE) - return 0; - else - return ret; -} - /* convert a QQ UID to a unique name of Purple * the return needs to be freed */ gchar *uid_to_purple_name(guint32 uid) @@ -194,7 +198,7 @@ } /* try to dump the data as GBK */ -void try_dump_as_gbk(const guint8 *const data, gint len) +gchar* try_dump_as_gbk(const guint8 *const data, gint len) { gint i; guint8 *incoming; @@ -215,8 +219,8 @@ if (msg_utf8 != NULL) { purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8); - g_free(msg_utf8); } + return msg_utf8; } /* strips whitespace */ @@ -294,7 +298,7 @@ /* Dumps a chunk of raw data into an ASCII hex string. * The return should be freed later. */ -gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes) +static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes) { GString *str; gchar *ret; @@ -303,12 +307,12 @@ str = g_string_new(""); for (i = 0; i < bytes; i += 16) { /* length label */ - g_string_append_printf(str, "%04d: ", i); + g_string_append_printf(str, "%07x: ", i); /* dump hex value */ for (j = 0; j < 16; j++) if ((i + j) < bytes) - g_string_append_printf(str, " %02X", buffer[i + j]); + g_string_append_printf(str, " %02x", buffer[i + j]); else g_string_append(str, " "); g_string_append(str, " "); @@ -331,6 +335,51 @@ return ret; } +void qq_hex_dump(PurpleDebugLevel level, const char *category, + const guint8 *pdata, gint bytes, + const char *format, ...) +{ + va_list args; + char *arg_s = NULL; + gchar *phex = NULL; + + g_return_if_fail(level != PURPLE_DEBUG_ALL); + g_return_if_fail(format != NULL); + + va_start(args, format); + arg_s = g_strdup_vprintf(format, args); + va_end(args); + + if (bytes <= 0) { + purple_debug(level, category, arg_s); + return; + } + + phex = hex_dump_to_str(pdata, bytes); + purple_debug(level, category, "%s - (len %d)\n%s", arg_s, bytes, phex); + g_free(phex); +} + +void qq_show_packet(const gchar *desc, const guint8 *buf, gint len) +{ + /* + char buf1[8*len+2], buf2[10]; + int i; + buf1[0] = 0; + for (i = 0; i < len; i++) { + sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff); + strcat(buf1, buf2); + } + strcat(buf1, "\n"); + purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1); + */ + + /* modified by s3e, 20080424 */ + qq_hex_dump(PURPLE_DEBUG_INFO, desc, + buf, len, + ""); +} + /* convert face num from packet (0-299) to local face (1-100) */ gchar *face_to_icon_str(gint face) {
--- a/libpurple/protocols/qq/utils.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/qq/utils.h Thu Nov 20 21:13:56 2008 +0000 @@ -28,12 +28,15 @@ #include <stdio.h> #include <glib.h> +#include "debug.h" + +void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len); + gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount); gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount); gint qq_string_to_dec_value(const gchar *str); gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields); -guint8 *_gen_session_md5(gint uid, guint8 *session_key); gchar *gen_ip_str(guint8 *ip); guint8 *str_ip_gen(gchar *str); @@ -44,10 +47,13 @@ gchar *face_to_icon_str(gint face); -void try_dump_as_gbk(const guint8 *const data, gint len); +gchar *try_dump_as_gbk(const guint8 *const data, gint len); +void qq_show_packet(const gchar *desc, const guint8 *buf, gint len); +void qq_hex_dump(PurpleDebugLevel level, const char *category, + const guint8 *pdata, gint bytes, + const char *format, ...); guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len); -gchar *hex_dump_to_str(const guint8 *buf, gint buf_len); const gchar *qq_buddy_icon_dir(void); const gchar *qq_win32_buddy_icon_dir(void);
--- a/libpurple/protocols/silc/buddy.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/silc/buddy.c Thu Nov 20 21:13:56 2008 +0000 @@ -1434,13 +1434,25 @@ void silcpurple_idle_set(PurpleConnection *gc, int idle) { - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; + SilcPurple sg; + SilcClient client; + SilcClientConnection conn; SilcAttributeObjService service; const char *server; int port; + sg = gc->proto_data; + if (sg == NULL) + return; + + client = sg->client; + if (client == NULL) + return; + + conn = sg->conn; + if (conn == NULL) + return; + server = purple_account_get_string(sg->account, "server", "silc.silcnet.org"); port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/protocols/silc/silc.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/silc/silc.c Thu Nov 20 21:13:56 2008 +0000 @@ -22,6 +22,7 @@ #include "silcpurple.h" #include "version.h" #include "wb.h" +#include "core.h" extern SilcClientOperations ops; static PurplePlugin *silc_plugin = NULL; @@ -669,13 +670,31 @@ #if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1) SilcPurpleTask task; #endif /* __SILC_TOOLKIT_VERSION */ + GHashTable *ui_info; + const char *ui_name = NULL, *ui_website = NULL; + char *quit_msg; g_return_if_fail(sg != NULL); + ui_info = purple_core_get_ui_info(); + + if(ui_info) { + ui_name = g_hash_table_lookup(ui_info, "name"); + ui_website = g_hash_table_lookup(ui_info, "website"); + } + + if(!ui_name || !ui_website) { + ui_name = "Pidgin"; + ui_website = PURPLE_WEBSITE; + } + quit_msg = g_strdup_printf(_("Download %s: %s"), + ui_name, ui_website); + /* Send QUIT */ silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", "Download Pidgin: " PURPLE_WEBSITE, + "QUIT", quit_msg, NULL); + g_free(quit_msg); if (sg->conn) silc_client_close_connection(sg->client, sg->conn); @@ -1816,7 +1835,10 @@ { PurpleConnection *gc; SilcPurple sg; - + GHashTable *ui_info; + const char *ui_name = NULL, *ui_website = NULL; + char *quit_msg; + gc = purple_conversation_get_gc(conv); if (gc == NULL) @@ -1827,8 +1849,23 @@ if (sg == NULL) return PURPLE_CMD_RET_FAILED; + ui_info = purple_core_get_ui_info(); + + if(ui_info) { + ui_name = g_hash_table_lookup(ui_info, "name"); + ui_website = g_hash_table_lookup(ui_info, "website"); + } + + if(!ui_name || !ui_website) { + ui_name = "Pidgin"; + ui_website = PURPLE_WEBSITE; + } + quit_msg = g_strdup_printf(_("Download %s: %s"), + ui_name, ui_website); + silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", (args && args[0]) ? args[0] : "Download Pidgin: " PURPLE_WEBSITE, NULL); + "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL); + g_free(quit_msg); return PURPLE_CMD_RET_OK; }
--- a/libpurple/protocols/silc10/buddy.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/silc10/buddy.c Thu Nov 20 21:13:56 2008 +0000 @@ -1434,13 +1434,25 @@ void silcpurple_idle_set(PurpleConnection *gc, int idle) { - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; + SilcPurple sg; + SilcClient client; + SilcClientConnection conn; SilcAttributeObjService service; const char *server; int port; + sg = gc->proto_data; + if (sg == NULL) + return; + + client = sg->client; + if (client == NULL) + return; + + conn = sg->conn; + if (conn == NULL) + return; + server = purple_account_get_string(sg->account, "server", "silc.silcnet.org"); port = purple_account_get_int(sg->account, "port", 706),
--- a/libpurple/protocols/silc10/silc.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/silc10/silc.c Thu Nov 20 21:13:56 2008 +0000 @@ -22,6 +22,7 @@ #include "silcpurple.h" #include "version.h" #include "wb.h" +#include "core.h" extern SilcClientOperations ops; static PurplePlugin *silc_plugin = NULL; @@ -384,12 +385,30 @@ silcpurple_close(PurpleConnection *gc) { SilcPurple sg = gc->proto_data; + GHashTable *ui_info; + const char *ui_name = NULL, *ui_website = NULL; + char *quit_msg; + + g_return_if_fail(sg != NULL); - g_return_if_fail(sg != NULL); + ui_info = purple_core_get_ui_info(); + + if(ui_info) { + ui_name = g_hash_table_lookup(ui_info, "name"); + ui_website = g_hash_table_lookup(ui_info, "website"); + } + + if(!ui_name || !ui_website) { + ui_name = "Pidgin"; + ui_website = PURPLE_WEBSITE; + } + quit_msg = g_strdup_printf(_("Download %s: %s"), + ui_name, ui_website); /* Send QUIT */ silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", "Download this: " PURPLE_WEBSITE, NULL); + "QUIT", quit_msg, NULL); + g_free(quit_msg); if (sg->conn) silc_client_close_connection(sg->client, sg->conn); @@ -1535,7 +1554,10 @@ { PurpleConnection *gc; SilcPurple sg; - + GHashTable *ui_info; + const char *ui_name = NULL, *ui_website = NULL; + char *quit_msg; + gc = purple_conversation_get_gc(conv); if (gc == NULL) @@ -1546,8 +1568,23 @@ if (sg == NULL) return PURPLE_CMD_RET_FAILED; + ui_info = purple_core_get_ui_info(); + + if(ui_info) { + ui_name = g_hash_table_lookup(ui_info, "name"); + ui_website = g_hash_table_lookup(ui_info, "website"); + } + + if(!ui_name || !ui_website) { + ui_name = "Pidgin"; + ui_website = PURPLE_WEBSITE; + } + quit_msg = g_strdup_printf(_("Download %s: %s"), + ui_name, ui_website); + silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", (args && args[0]) ? args[0] : "Download this: " PURPLE_WEBSITE, NULL); + "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL); + g_free(quit_msg); return PURPLE_CMD_RET_OK; }
--- a/libpurple/protocols/simple/simple.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/simple/simple.c Thu Nov 20 21:13:56 2008 +0000 @@ -1898,7 +1898,7 @@ PurpleConnection *gc; struct simple_account_data *sip; gchar **userserver; - gchar *hosttoconnect; + const gchar *hosttoconnect; const char *username = purple_account_get_username(account); gc = purple_account_get_connection(account); @@ -1934,14 +1934,13 @@ sip->status = g_strdup("available"); if(!purple_account_get_bool(account, "useproxy", FALSE)) { - hosttoconnect = g_strdup(sip->servername); + hosttoconnect = sip->servername; } else { - hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->servername)); + hosttoconnect = purple_account_get_string(account, "proxy", sip->servername); } sip->srv_query_data = purple_srv_resolve("sip", sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip); - g_free(hosttoconnect); } static void simple_close(PurpleConnection *gc)
--- a/libpurple/protocols/yahoo/yahoo.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo.c Thu Nov 20 21:13:56 2008 +0000 @@ -1995,13 +1995,19 @@ yahoo_process_auth_new(gc, seed); break; default: - buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized " - "authentication method. You will probably not be able " - "to successfully sign on to Yahoo. Check %s for updates."), PURPLE_WEBSITE); - purple_notify_error(gc, "", _("Failed Yahoo! Authentication"), - buf); - g_free(buf); - yahoo_process_auth_new(gc, seed); /* Can't hurt to try it anyway. */ + { + GHashTable *ui_info = purple_core_get_ui_info(); + + buf = g_strdup_printf(_("The Yahoo server has requested the use of an unrecognized " + "authentication method. You will probably not be able " + "to successfully sign on to Yahoo. Check %s for updates."), + ((ui_info && g_hash_table_lookup(ui_info, "website")) ? (char *)g_hash_table_lookup(ui_info, "website") : PURPLE_WEBSITE)); + purple_notify_error(gc, "", _("Failed Yahoo! Authentication"), + buf); + g_free(buf); + yahoo_process_auth_new(gc, seed); /* Can't hurt to try it anyway. */ + break; + } } } }
--- a/libpurple/protocols/yahoo/yahoo_profile.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Thu Nov 20 21:13:56 2008 +0000 @@ -1282,6 +1282,10 @@ url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, yahoo_got_info, data); if (url_data != NULL) yd->url_datas = g_slist_prepend(yd->url_datas, url_data); + else { + g_free(data->name); + g_free(data); + } g_free(url); }
--- a/libpurple/prpl.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/prpl.c Thu Nov 20 21:13:56 2008 +0000 @@ -389,6 +389,110 @@ return statuses; } +void +purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code) +{ + PurpleAttentionType *attn; + PurpleMessageFlags flags; + PurplePlugin *prpl; + PurpleConversation *conv; + gboolean (*send_attention)(PurpleConnection *, const char *, guint); + PurpleBuddy *buddy; + const char *alias; + gchar *description; + time_t mtime; + + g_return_if_fail(gc != NULL); + g_return_if_fail(who != NULL); + + prpl = purple_find_prpl(purple_account_get_protocol_id(gc->account)); + send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention; + g_return_if_fail(send_attention != NULL); + + mtime = time(NULL); + + attn = purple_get_attention_type_from_code(gc->account, type_code); + + if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL) + alias = purple_buddy_get_contact_alias(buddy); + else + alias = who; + + if (attn && purple_attention_type_get_outgoing_desc(attn)) { + description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias); + } else { + description = g_strdup_printf(_("Requesting %s's attention..."), alias); + } + + flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM; + + purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n", + description, who); + + if (!send_attention(gc, who, type_code)) + return; + + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who); + purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime); + + g_free(description); +} + +static void +got_attention(PurpleConnection *gc, int id, const char *who, guint type_code) +{ + PurpleMessageFlags flags; + PurpleAttentionType *attn; + PurpleBuddy *buddy; + const char *alias; + gchar *description; + time_t mtime; + + mtime = time(NULL); + + attn = purple_get_attention_type_from_code(gc->account, type_code); + + /* PURPLE_MESSAGE_NOTIFY is for attention messages. */ + flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV; + + /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display + * it next to the attention command. And if it is null, display a generic icon. */ + + if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL) + alias = purple_buddy_get_contact_alias(buddy); + else + alias = who; + + if (attn && purple_attention_type_get_incoming_desc(attn)) { + description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias); + } else { + description = g_strdup_printf(_("%s has requested your attention!"), alias); + } + + purple_debug_info("server", "got_attention: got '%s' from %s\n", + description, who); + + if (id == -1) + serv_got_im(gc, who, description, flags, mtime); + else + serv_got_chat_in(gc, id, who, flags, description, mtime); + + /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */ + + g_free(description); +} + +void +purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code) +{ + got_attention(gc, -1, who, type_code); +} + +void +purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code) +{ + got_attention(gc, id, who, type_code); +} /************************************************************************** * Protocol Plugin Subsystem API
--- a/libpurple/prpl.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/prpl.h Thu Nov 20 21:13:56 2008 +0000 @@ -31,6 +31,7 @@ #define _PURPLE_PRPL_H_ typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo; +/** @copydoc _PurpleAttentionType */ typedef struct _PurpleAttentionType PurpleAttentionType; /**************************************************************************/ @@ -99,6 +100,9 @@ gboolean secret; }; +/** Represents "nudges" and "buzzes" that you may send to a buddy to attract + * their attention (or vice-versa). + */ struct _PurpleAttentionType { const char *name; /**< Shown in GUI elements */ @@ -689,6 +693,41 @@ */ GList *purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence); +/** Send an attention request message. + * + * @param gc The connection to send the message on. + * @param who Whose attention to request. + * @param type_code An index into the prpl's attention_types list determining the type + * of the attention request command to send. 0 if prpl only defines one + * (for example, Yahoo and MSN), but some protocols define more (MySpaceIM). + * + * Note that you can't send arbitrary PurpleAttentionType's, because there is + * only a fixed set of attention commands. + * @since 2.5.0 + */ +void purple_prpl_send_attention(PurpleConnection *gc, const char *who, guint type_code); + +/** Process an incoming attention message. + * + * @param gc The connection that received the attention message. + * @param who Who requested your attention. + * @param type_code An index into the prpl's attention_types list determining the type + * of the attention request command to send. + * @since 2.5.0 + */ +void purple_prpl_got_attention(PurpleConnection *gc, const char *who, guint type_code); + +/** Process an incoming attention message in a chat. + * + * @param gc The connection that received the attention message. + * @param id The chat id. + * @param who Who requested your attention. + * @param type_code An index into the prpl's attention_types list determining the type + * of the attention request command to send. + * @since 2.5.0 + */ +void purple_prpl_got_attention_in_chat(PurpleConnection *gc, int id, const char *who, guint type_code); + /*@}*/ /**************************************************************************/
--- a/libpurple/purple-url-handler Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/purple-url-handler Thu Nov 20 21:13:56 2008 +0000 @@ -6,7 +6,15 @@ import time import urllib -obj = dbus.SessionBus().get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject") +bus = dbus.SessionBus() +obj = None +try: + obj = bus.get_object("im.pidgin.purple.PurpleService", + "/im/pidgin/purple/PurpleObject") +except dbus.DBusException, e: + if e._dbus_error_name == "org.freedesktop.DBus.Error.ServiceUnknown": + print "Error: no libpurple-powered client is running. Try starting Pidgin or Finch." + sys.exit(1) purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface") class CheckedObject: @@ -50,20 +58,39 @@ except: return value -def findaccount(protocolname, accountname=""): +def account_not_found(): + print "No matching account found." + sys.exit(1) + +def bring_account_online(account): + if not cpurple.PurpleAccountIsConnected(account): + # The last argument is meant to be a GList * but the D-Bus binding + # generator thing just wants a UInt32, which is pretty failing. + # Happily, passing a 0 to mean an empty list turns out to work anyway. + purple.PurpleAccountSetStatusList(account, "online", 1, 0) + purple.PurpleAccountConnect(account) + +def findaccount(protocolname, accountname="", matcher=None): + if matcher: + for account in cpurple.PurpleAccountsGetAll(): + if accountname != "" and accountname != cpurple.PurpleAccountGetUsername(a): + continue + if matcher(account): + bring_account_online(account) + return account + account_not_found() + # prefer connected accounts account = cpurple.PurpleAccountsFindConnected(accountname, protocolname) if (account != 0): - return account + return account # try to get any account and connect it account = cpurple.PurpleAccountsFindAny(accountname, protocolname) if (account == 0): - print "No matching account found." - sys.exit(1) + account_not_found() - purple.PurpleAccountSetStatusVargs(account, "online", 1) - purple.PurpleAccountConnect(account) + bring_account_online(account) return account def goim(account, screenname, message=None): @@ -178,12 +205,16 @@ key, value = extendlist(param.split("=", 1), 2, "") params[key] = urllib.unquote_plus(value) - account = findaccount(protocol) + def correct_server(account): + username = cpurple.PurpleAccountGetUsername(account) + return (server == (username.split("@"))[1]) + + account = findaccount(protocol, matcher=correct_server) if (target != ""): if (isnick): goim(account, urllib.unquote_plus(target.split(",")[0]), params.get("msg")) - else: + else: channel = urllib.unquote_plus(target.split(",")[0]) if channel[0] != "#": channel = "#" + channel @@ -213,9 +244,9 @@ addbuddy(account, screenname) def myim(uri): - protocol = "prpl-myspace" - print "TODO: send uri: ", uri - assert False, "Not implemented" + protocol = "prpl-myspace" + print "TODO: send uri: ", uri + assert False, "Not implemented" def sip(uri): protocol = "prpl-simple" @@ -328,9 +359,9 @@ ymsgr(uri) else: print "Unknown protocol: %s" % type - except dbus.dbus_bindings.DBusException: - print "ERROR: Is there a libpurple-powered client (e.g. Pidgin or Finch) running?" - + except dbus.DBusException, e: + print "Error: %s" % (e.message) + sys.exit(1) if __name__ == "__main__": main()
--- a/libpurple/roomlist.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/roomlist.h Thu Nov 20 21:13:56 2008 +0000 @@ -30,6 +30,7 @@ typedef struct _PurpleRoomlist PurpleRoomlist; typedef struct _PurpleRoomlistRoom PurpleRoomlistRoom; typedef struct _PurpleRoomlistField PurpleRoomlistField; +/** @copydoc _PurpleRoomlistUiOps */ typedef struct _PurpleRoomlistUiOps PurpleRoomlistUiOps; /**
--- a/libpurple/server.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/server.c Thu Nov 20 21:13:56 2008 +0000 @@ -323,91 +323,13 @@ void serv_send_attention(PurpleConnection *gc, const char *who, guint type_code) { - PurpleAttentionType *attn; - PurpleMessageFlags flags; - PurplePlugin *prpl; - PurpleConversation *conv; - gboolean (*send_attention)(PurpleConnection *, const char *, guint); - PurpleBuddy *buddy; - const char *alias; - gchar *description; - time_t mtime; - - g_return_if_fail(gc != NULL); - g_return_if_fail(who != NULL); - - prpl = purple_find_prpl(purple_account_get_protocol_id(gc->account)); - send_attention = PURPLE_PLUGIN_PROTOCOL_INFO(prpl)->send_attention; - g_return_if_fail(send_attention != NULL); - - mtime = time(NULL); - - attn = purple_get_attention_type_from_code(gc->account, type_code); - - if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL) - alias = purple_buddy_get_contact_alias(buddy); - else - alias = who; - - if (attn && purple_attention_type_get_outgoing_desc(attn)) { - description = g_strdup_printf(purple_attention_type_get_outgoing_desc(attn), alias); - } else { - description = g_strdup_printf(_("Requesting %s's attention..."), alias); - } - - flags = PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_SYSTEM; - - purple_debug_info("server", "serv_send_attention: sending '%s' to %s\n", - description, who); - - if (!send_attention(gc, who, type_code)) - return; - - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, gc->account, who); - purple_conv_im_write(PURPLE_CONV_IM(conv), NULL, description, flags, mtime); - - g_free(description); + purple_prpl_send_attention(gc, who, type_code); } void serv_got_attention(PurpleConnection *gc, const char *who, guint type_code) { - PurpleMessageFlags flags; - PurpleAttentionType *attn; - PurpleBuddy *buddy; - const char *alias; - gchar *description; - time_t mtime; - - mtime = time(NULL); - - attn = purple_get_attention_type_from_code(gc->account, type_code); - - /* PURPLE_MESSAGE_NOTIFY is for attention messages. */ - flags = PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY | PURPLE_MESSAGE_RECV; - - /* TODO: if (attn->icon_name) is non-null, use it to lookup an emoticon and display - * it next to the attention command. And if it is null, display a generic icon. */ - - if ((buddy = purple_find_buddy(purple_connection_get_account(gc), who)) != NULL) - alias = purple_buddy_get_contact_alias(buddy); - else - alias = who; - - if (attn && purple_attention_type_get_incoming_desc(attn)) { - description = g_strdup_printf(purple_attention_type_get_incoming_desc(attn), alias); - } else { - description = g_strdup_printf(_("%s has requested your attention!"), alias); - } - - purple_debug_info("server", "serv_got_attention: got '%s' from %s\n", - description, who); - - serv_got_im(gc, who, description, flags, mtime); - - /* TODO: sounds (depending on PurpleAttentionType), shaking, etc. */ - - g_free(description); + purple_prpl_got_attention(gc, who, type_code); } @@ -727,6 +649,7 @@ PurpleStatusPrimitive primitive; const gchar *auto_reply_pref; const char *away_msg = NULL; + gboolean mobile = FALSE; auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply"); @@ -734,9 +657,10 @@ status = purple_presence_get_active_status(presence); status_type = purple_status_get_type(status); primitive = purple_status_type_get_primitive(status_type); + mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE); if ((primitive == PURPLE_STATUS_AVAILABLE) || (primitive == PURPLE_STATUS_INVISIBLE) || - (primitive == PURPLE_STATUS_MOBILE) || + mobile || !strcmp(auto_reply_pref, "never") || (!purple_presence_is_idle(presence) && !strcmp(auto_reply_pref, "awayidle"))) {
--- a/libpurple/server.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/server.h Thu Nov 20 21:13:56 2008 +0000 @@ -63,6 +63,8 @@ /** Send an attention request message. * + * @deprecated Use purple_prpl_send_attention() instead. + * * @param gc The connection to send the message on. * @param who Whose attention to request. * @param type_code An index into the prpl's attention_types list determining the type @@ -76,6 +78,8 @@ /** Process an incoming attention message. * + * @deprecated Use purple_prpl_got_attention() instead. + * * @param gc The connection that received the attention message. * @param who Who requested your attention. * @param type_code An index into the prpl's attention_types list determining the type
--- a/libpurple/sound.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/sound.h Thu Nov 20 21:13:56 2008 +0000 @@ -55,6 +55,9 @@ } PurpleSoundEventID; +/** Operations used by the core to request that particular sound files, or the + * sound associated with a particular event, should be played. + */ typedef struct _PurpleSoundUiOps { void (*init)(void);
--- a/libpurple/util.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/util.c Thu Nov 20 21:13:56 2008 +0000 @@ -832,6 +832,11 @@ if (offset_positive) tzoff *= -1; } + else if ((*c == 'Z') && (c = c + 1)) + { + /* 'Z' = Zulu = UTC */ + tzoff = 0; + } else if (utc) { static struct tm tmptm; @@ -3594,7 +3599,7 @@ static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message); static gboolean -parse_redirect(const char *data, size_t data_len, gint sock, +parse_redirect(const char *data, size_t data_len, PurpleUtilFetchUrlData *gfud) { gchar *s; @@ -3768,7 +3773,7 @@ header_len, gfud->webdata); /* See if we can find a redirect. */ - if(parse_redirect(gfud->webdata, header_len, source, gfud)) + if(parse_redirect(gfud->webdata, header_len, gfud)) return; gfud->got_headers = TRUE; @@ -4718,3 +4723,21 @@ return g_string_free(string, FALSE); } +const gchar * +purple_get_host_name(void) +{ +#if GLIB_CHECK_VERSION(2,8,0) + return g_get_host_name(); +#else + static char hostname[256]; + int ret = gethostname(hostname, sizeof(hostname)); + hostname[sizeof(hostname) - 1] = '\0'; + + if (ret == -1 || hostname[0] == '\0') { + purple_debug_info("purple_get_host_name: ", "could not find host name"); + return "localhost"; + } else { + return hostname; + } +#endif +}
--- a/libpurple/util.h Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/util.h Thu Nov 20 21:13:56 2008 +0000 @@ -1271,6 +1271,14 @@ */ void purple_restore_default_signal_handlers(void); +/** + * Gets the host name of the machine. If it not possible to determine the + * host name, "localhost" is returned + * + * @constreturn The hostname + */ +const gchar *purple_get_host_name(void); + #ifdef __cplusplus } #endif
--- a/libpurple/win32/global.mak Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/win32/global.mak Thu Nov 20 21:13:56 2008 +0000 @@ -19,8 +19,8 @@ MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa1 NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4 NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4 -PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl58 -SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.5 +PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0 +SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.7 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5 GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13 @@ -56,7 +56,7 @@ PIDGIN_EXE := $(PIDGIN_TOP)/pidgin.exe PIDGIN_PORTABLE_EXE := $(PIDGIN_TOP)/pidgin-portable.exe -GCCWARNINGS := -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wundef +GCCWARNINGS ?= -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wundef # parse the version number from the configure.ac file if it is newer #m4_define([purple_major_version], [2])
--- a/libpurple/win32/libc_interface.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/win32/libc_interface.c Thu Nov 20 21:13:56 2008 +0000 @@ -327,9 +327,12 @@ case WSAETIMEDOUT: /* 10060 */ g_snprintf(errbuf, sizeof(errbuf), _("Connection timed out.")); break; - case WSAECONNREFUSED: /*10061 */ + case WSAECONNREFUSED: /* 10061 */ g_snprintf(errbuf, sizeof(errbuf), _("Connection refused.")); break; + case WSAEADDRINUSE: /* 10048 */ + g_snprintf(errbuf, sizeof(errbuf), _("Address already in use.")); + break; default: g_snprintf(errbuf, sizeof(errbuf), "Windows socket error #%d", errornum); }
--- a/libpurple/xmlnode.c Mon Aug 18 17:08:01 2008 +0000 +++ b/libpurple/xmlnode.c Thu Nov 20 21:13:56 2008 +0000 @@ -728,6 +728,13 @@ return ret; } +static void +xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data) +{ + GHashTable *ret = (GHashTable *)user_data; + g_hash_table_insert(ret, g_strdup(key), g_strdup(value)); +} + xmlnode * xmlnode_copy(const xmlnode *src) { @@ -739,17 +746,23 @@ ret = new_node(src->name, src->type); ret->xmlns = g_strdup(src->xmlns); - if(src->data) { - if(src->data_sz) { + if (src->data) { + if (src->data_sz) { ret->data = g_memdup(src->data, src->data_sz); ret->data_sz = src->data_sz; } else { ret->data = g_strdup(src->data); } } + ret->prefix = g_strdup(src->prefix); + if (src->namespace_map) { + ret->namespace_map = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + g_hash_table_foreach(src->namespace_map, xmlnode_copy_foreach_ns, ret->namespace_map); + } - for(child = src->child; child; child = child->next) { - if(sibling) { + for (child = src->child; child; child = child->next) { + if (sibling) { sibling->next = xmlnode_copy(child); sibling = sibling->next; } else {
--- a/pidgin/gtkaccount.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkaccount.c Thu Nov 20 21:13:56 2008 +0000 @@ -1186,7 +1186,6 @@ char *tmp; gboolean new_acct = FALSE, icon_change = FALSE; PurpleAccount *account; - PurplePluginProtocolInfo *prpl_info; /* Build the username string. */ username = g_strdup(gtk_entry_get_text(GTK_ENTRY(dialog->screenname_entry))); @@ -1254,8 +1253,7 @@ purple_account_set_alias(account, NULL); /* Buddy Icon */ - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(dialog->plugin); - if (prpl_info != NULL && prpl_info->icon_spec.format != NULL) + if (dialog->prpl_info != NULL && dialog->prpl_info->icon_spec.format != NULL) { const char *filename;
--- a/pidgin/gtkblist.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkblist.c Thu Nov 20 21:13:56 2008 +0000 @@ -130,6 +130,7 @@ static guint visibility_manager_count = 0; static GdkVisibilityState gtk_blist_visibility = GDK_VISIBILITY_UNOBSCURED; +static gboolean gtk_blist_focused = FALSE; static gboolean editing_blist = FALSE; static GList *pidgin_blist_sort_methods = NULL; @@ -5166,9 +5167,14 @@ /******************************************/ static int -blist_focus_cb(GtkWidget *widget, gpointer data, PidginBuddyList *gtkblist) -{ - pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE); +blist_focus_cb(GtkWidget *widget, GdkEventFocus *event, PidginBuddyList *gtkblist) +{ + if(event->in) { + gtk_blist_focused = TRUE; + pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE); + } else { + gtk_blist_focused = FALSE; + } return 0; } @@ -5255,6 +5261,8 @@ gtkblist->window = pidgin_create_window(_("Buddy List"), 0, "buddy_list", TRUE); g_signal_connect(G_OBJECT(gtkblist->window), "focus-in-event", G_CALLBACK(blist_focus_cb), gtkblist); + g_signal_connect(G_OBJECT(gtkblist->window), "focus-out-event", + G_CALLBACK(blist_focus_cb), gtkblist); GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; gtkblist->main_vbox = gtk_vbox_new(FALSE, 0); @@ -6988,8 +6996,15 @@ { if (gtkblist && gtkblist->window) { if (GTK_WIDGET_VISIBLE(gtkblist->window)) { + /* make the buddy list visible if it is iconified or if it is + * obscured and not currently focused (the focus part ensures + * that we do something reasonable if the buddy list is obscured + * by a window set to always be on top), otherwise hide the + * buddy list + */ purple_blist_set_visible(PIDGIN_WINDOW_ICONIFIED(gtkblist->window) || - gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED); + ((gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED) && + !gtk_blist_focused)); } else { purple_blist_set_visible(TRUE); }
--- a/pidgin/gtkconv.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkconv.c Thu Nov 20 21:13:56 2008 +0000 @@ -1481,7 +1481,7 @@ PurpleAccount *account; PurpleConnection *gc; PurplePluginProtocolInfo *prpl_info = NULL; - char *real_who; + gchar *real_who = NULL; account = purple_conversation_get_account(conv); g_return_if_fail(account != NULL); @@ -1494,13 +1494,11 @@ if (prpl_info && prpl_info->get_cb_real_name) real_who = prpl_info->get_cb_real_name(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who); - else - real_who = g_strdup(who); - - if(!real_who) + + if(!who && !real_who) return; - pidgin_dialogs_im_with_user(account, real_who); + pidgin_dialogs_im_with_user(account, real_who ? real_who : who); g_free(real_who); } @@ -1539,11 +1537,22 @@ static void menu_chat_send_file_cb(GtkWidget *w, PidginConversation *gtkconv) { + PurplePluginProtocolInfo *prpl_info; PurpleConversation *conv = gtkconv->active_conv; const char *who = g_object_get_data(G_OBJECT(w), "user_data"); PurpleConnection *gc = purple_conversation_get_gc(conv); - - serv_send_file(gc, who, NULL); + gchar *real_who = NULL; + + g_return_if_fail(gc != NULL); + + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if (prpl_info && prpl_info->get_cb_real_name) + real_who = prpl_info->get_cb_real_name(gc, + purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who); + + serv_send_file(gc, real_who ? real_who : who, NULL); + g_free(real_who); } static void @@ -1659,23 +1668,34 @@ if (gc == NULL) gtk_widget_set_sensitive(button, FALSE); - - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); + else + g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); if (prpl_info && prpl_info->send_file) { + gboolean can_receive_file = TRUE; + button = pidgin_new_item_from_stock(menu, _("Send File"), PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(menu_chat_send_file_cb), PIDGIN_CONVERSATION(conv), 0, 0, NULL); - if (gc == NULL || prpl_info == NULL || - !(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who))) - { + if (gc == NULL || prpl_info == NULL) + can_receive_file = FALSE; + else { + gchar *real_who = NULL; + if (prpl_info->get_cb_real_name) + real_who = prpl_info->get_cb_real_name(gc, + purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who); + if (!(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, real_who ? real_who : who))) + can_receive_file = FALSE; + g_free(real_who); + } + + if (!can_receive_file) gtk_widget_set_sensitive(button, FALSE); - } - - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); + else + g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } @@ -1688,8 +1708,8 @@ if (gc == NULL) gtk_widget_set_sensitive(button, FALSE); - - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); + else + g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } if (prpl_info && (prpl_info->get_info || prpl_info->get_cb_info)) { @@ -1698,8 +1718,8 @@ if (gc == NULL) gtk_widget_set_sensitive(button, FALSE); - - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); + else + g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } if (prpl_info && prpl_info->get_cb_away) { @@ -1708,8 +1728,8 @@ if (gc == NULL) gtk_widget_set_sensitive(button, FALSE); - - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); + else + g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } if (!is_me && prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { @@ -1722,8 +1742,8 @@ if (gc == NULL) gtk_widget_set_sensitive(button, FALSE); - - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); + else + g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); } button = pidgin_new_item_from_stock(menu, _("Last said"), GTK_STOCK_INDEX,
--- a/pidgin/gtkdialogs.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkdialogs.c Thu Nov 20 21:13:56 2008 +0000 @@ -73,7 +73,8 @@ {"John 'rekkanoryo' Bailey", N_("developer"), NULL}, {"Ethan 'Paco-Paco' Blanton", N_("developer"), NULL}, {"Thomas Butter", N_("developer"), NULL}, - {"Ka-Hing Cheung", N_("developer"), NULL}, + /* feel free to not translate this */ + {N_("Ka-Hing Cheung"), N_("developer"), NULL}, {"Sadrul Habib Chowdhury", N_("developer"), NULL}, {"Mark 'KingAnt' Doliner", N_("developer"), "mark@kingant.net"}, {"Sean Egan", N_("developer"), "sean.egan@gmail.com"}, @@ -85,6 +86,7 @@ {"Bartosz Oler", N_("developer"), NULL}, {"Etan 'deryni' Reisner", N_("developer"), NULL}, {"Tim 'marv' Ringenbach", N_("developer"), NULL}, + {"Elliott 'QuLogic' Sales de Andrade", N_("developer"), NULL}, {"Luke 'LSchiere' Schierer", N_("support"), "lschiere@users.sf.net"}, {"Megan 'Cae' Schneider", N_("support/QA"), NULL}, {"Evan Schoenberg", N_("developer"), NULL}, @@ -100,7 +102,6 @@ {"Felipe 'shx' Contreras", NULL, NULL}, {"Dennis 'EvilDennisR' Ristuccia", N_("Senior Contributor/QA"), NULL}, {"Peter 'Fmoo' Ruibal", NULL, NULL}, - {"Elliott 'QuLogic' Sales de Andrade", NULL, NULL}, {"Gabriel 'Nix' Schulhof", NULL, NULL}, {"Jorge 'Masca' Villaseñor", NULL, NULL}, {NULL, NULL, NULL} @@ -424,11 +425,11 @@ for (i = 0; developers[i].name != NULL; i++) { if (developers[i].email != NULL) { g_string_append_printf(str, " %s (%s) <<a href=\"mailto:%s\">%s</a>><br/>", - developers[i].name, _(developers[i].role), + _(developers[i].name), _(developers[i].role), developers[i].email, developers[i].email); } else { g_string_append_printf(str, " %s (%s)<br/>", - developers[i].name, _(developers[i].role)); + _(developers[i].name), _(developers[i].role)); } } g_string_append(str, "<BR/>");
--- a/pidgin/gtkdocklet.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkdocklet.c Thu Nov 20 21:13:56 2008 +0000 @@ -708,6 +708,11 @@ if (status == PURPLE_STATUS_OFFLINE) gtk_widget_set_sensitive(menuitem, FALSE); + menuitem = pidgin_new_item_from_stock(menu, _("Join Chat..."), PIDGIN_STOCK_CHAT, + G_CALLBACK(pidgin_blist_joinchat_show), NULL, 0, 0, NULL); + if (status == PURPLE_STATUS_OFFLINE) + gtk_widget_set_sensitive(menuitem, FALSE); + menuitem = docklet_status_submenu(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
--- a/pidgin/gtkimhtml.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkimhtml.c Thu Nov 20 21:13:56 2008 +0000 @@ -768,7 +768,7 @@ gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end, buf_x + event->area.width, buf_y + event->area.height); - + gtk_text_iter_order(&start, &end); cur = start;
--- a/pidgin/gtkmain.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkmain.c Thu Nov 20 21:13:56 2008 +0000 @@ -350,6 +350,8 @@ g_hash_table_insert(ui_info, "name", (char*)PIDGIN_NAME); g_hash_table_insert(ui_info, "version", VERSION); + g_hash_table_insert(ui_info, "website", "http://pidgin.im"); + g_hash_table_insert(ui_info, "dev_website", "http://developer.pidgin.im"); } return ui_info;
--- a/pidgin/gtkmenutray.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkmenutray.c Thu Nov 20 21:13:56 2008 +0000 @@ -84,19 +84,24 @@ } static void -pidgin_menu_tray_finalize(GObject *obj) { +pidgin_menu_tray_finalize(GObject *obj) +{ + PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj); #if 0 /* This _might_ be leaking, but I have a sneaking suspicion that the widget is * getting destroyed in GtkContainer's finalize function. But if were are * leaking here, be sure to figure out why this causes a crash. * -- Gary */ - PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj); if(GTK_IS_WIDGET(tray->tray)) gtk_widget_destroy(GTK_WIDGET(tray->tray)); #endif + if (tray->tooltips) { + gtk_object_sink(GTK_OBJECT(tray->tooltips)); + } + G_OBJECT_CLASS(parent_class)->finalize(obj); }
--- a/pidgin/gtkroomlist.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkroomlist.c Thu Nov 20 21:13:56 2008 +0000 @@ -53,7 +53,6 @@ PurpleRoomlist *roomlist; gboolean pg_needs_pulse; - gboolean pg_to_active; guint pg_update_to; } PidginRoomlistDialog; @@ -84,32 +83,34 @@ static gint delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) { - PidginRoomlistDialog *dialog; - - dialog = (PidginRoomlistDialog *) d; + PidginRoomlistDialog *dialog = d; if (dialog->roomlist && purple_roomlist_get_in_progress(dialog->roomlist)) purple_roomlist_cancel_get_list(dialog->roomlist); + if (dialog->pg_update_to > 0) + purple_timeout_remove(dialog->pg_update_to); + if (dialog->roomlist) { - if (dialog->pg_to_active) { - purple_timeout_remove(dialog->pg_update_to); - dialog->pg_to_active = FALSE; + PidginRoomlist *rl = dialog->roomlist->ui_data; + + if (dialog->pg_update_to > 0) /* yes, that's right, unref it twice. */ purple_roomlist_unref(dialog->roomlist); - } + + if (rl) + rl->dialog = NULL; + purple_roomlist_unref(dialog->roomlist); } - /* free stuff here */ - if (dialog->roomlist) - purple_roomlist_unref(dialog->roomlist); + dialog->progress = NULL; g_free(dialog); return FALSE; } static void dialog_select_account_cb(GObject *w, PurpleAccount *account, - PidginRoomlistDialog *dialog) + PidginRoomlistDialog *dialog) { dialog->account = account; } @@ -186,9 +187,7 @@ GValue val; PurpleRoomlistRoom *room; static struct _menu_cb_info *info; - PidginRoomlistDialog *dialog; - - dialog = grl->dialog; + PidginRoomlistDialog *dialog = grl->dialog; if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { val.g_type = 0; @@ -239,9 +238,7 @@ { PurpleRoomlist *rl = dialog->roomlist; PidginRoomlist *grl = rl->ui_data; - struct _menu_cb_info *info; - - info = (struct _menu_cb_info*)g_object_get_data(G_OBJECT(button), "room-info"); + struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info"); if(info != NULL) do_add_room_cb(grl->tree, info); @@ -256,9 +253,7 @@ { PurpleRoomlist *rl = dialog->roomlist; PidginRoomlist *grl = rl->ui_data; - struct _menu_cb_info *info; - - info = (struct _menu_cb_info*)g_object_get_data(G_OBJECT(button), "room-info"); + struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "room-info"); if(info != NULL) do_join_cb(grl->tree, info); @@ -490,12 +485,13 @@ static gboolean account_filter_func(PurpleAccount *account) { - PurpleConnection *gc = purple_account_get_connection(account); + PurpleConnection *conn = purple_account_get_connection(account); PurplePluginProtocolInfo *prpl_info = NULL; - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + if (conn) + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl); - return (prpl_info->roomlist_get_list != NULL); + return (prpl_info && prpl_info->roomlist_get_list != NULL); } gboolean @@ -518,10 +514,7 @@ pidgin_roomlist_dialog_new_with_account(PurpleAccount *account) { PidginRoomlistDialog *dialog; - GtkWidget *window; - GtkWidget *vbox; - GtkWidget *vbox2; - GtkWidget *bbox; + GtkWidget *window, *vbox, *vbox2, *bbox; dialog = g_new0(PidginRoomlistDialog, 1); dialog->account = account; @@ -611,9 +604,8 @@ void pidgin_roomlist_dialog_show_with_account(PurpleAccount *account) { - PidginRoomlistDialog *dialog; + PidginRoomlistDialog *dialog = pidgin_roomlist_dialog_new_with_account(account); - dialog = pidgin_roomlist_dialog_new_with_account(account); if (!dialog) return; @@ -627,9 +619,7 @@ static void pidgin_roomlist_new(PurpleRoomlist *list) { - PidginRoomlist *rl; - - rl = g_new0(PidginRoomlist, 1); + PidginRoomlist *rl = g_new0(PidginRoomlist, 1); list->ui_data = rl; @@ -802,7 +792,7 @@ if (!rl || !rl->dialog || !rl->dialog->pg_needs_pulse) { if (rl && rl->dialog) - rl->dialog->pg_to_active = FALSE; + rl->dialog->pg_update_to = 0; purple_roomlist_unref(list); return FALSE; } @@ -827,15 +817,14 @@ rl->num_rooms++; if (rl->dialog) { - if (!rl->dialog->pg_to_active) { - rl->dialog->pg_to_active = TRUE; + if (rl->dialog->pg_update_to == 0) { purple_roomlist_ref(list); rl->dialog->pg_update_to = g_timeout_add(100, pidgin_progress_bar_pulse, list); gtk_progress_bar_pulse(GTK_PROGRESS_BAR(rl->dialog->progress)); - } else { + } else rl->dialog->pg_needs_pulse = TRUE; - } } + if (room->parent) { parentrr = g_hash_table_lookup(rl->cats, room->parent); path = gtk_tree_row_reference_get_path(parentrr); @@ -881,14 +870,14 @@ } } -static void pidgin_roomlist_in_progress(PurpleRoomlist *list, gboolean flag) +static void pidgin_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress) { PidginRoomlist *rl = list->ui_data; if (!rl || !rl->dialog) return; - if (flag) { + if (in_progress) { if (rl->dialog->account_widget) gtk_widget_set_sensitive(rl->dialog->account_widget, FALSE); gtk_widget_set_sensitive(rl->dialog->stop_button, TRUE); @@ -905,12 +894,10 @@ static void pidgin_roomlist_destroy(PurpleRoomlist *list) { - PidginRoomlist *rl; + PidginRoomlist *rl = list->ui_data; roomlists = g_list_remove(roomlists, list); - rl = list->ui_data; - g_return_if_fail(rl != NULL); g_hash_table_destroy(rl->cats);
--- a/pidgin/gtksmiley.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtksmiley.c Thu Nov 20 21:13:56 2008 +0000 @@ -275,7 +275,8 @@ g_free(buffer); } emoticon = purple_smiley_new_from_file(entry, s->filename); - pidgin_smiley_add_to_list(emoticon); + if (emoticon) + pidgin_smiley_add_to_list(emoticon); } if (smiley_manager != NULL)
--- a/pidgin/gtkutils.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/gtkutils.c Thu Nov 20 21:13:56 2008 +0000 @@ -1001,13 +1001,14 @@ } prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl); + if (prpl_info != NULL && prpl_info->get_cb_real_name) + who = prpl_info->get_cb_real_name(conn, chat, name); if (prpl_info == NULL || prpl_info->get_cb_info == NULL) { - pidgin_retrieve_user_info(conn, name); + pidgin_retrieve_user_info(conn, who ? who : name); + g_free(who); return; } - if (prpl_info->get_cb_real_name) - who = prpl_info->get_cb_real_name(conn, chat, name); show_retrieveing_info(conn, who ? who : name); prpl_info->get_cb_info(conn, chat, name); g_free(who);
--- a/pidgin/pidgintooltip.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/pidgintooltip.c Thu Nov 20 21:13:56 2008 +0000 @@ -59,6 +59,7 @@ destroy_tooltip_data(PidginTooltipData *data) { gtk_tree_path_free(data->common.treeview.path); + pidgin_tooltip_destroy(); g_free(data); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pixmaps/emblems/16/scalable/birthday.svg Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,629 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16px" + height="16px" + id="svg8140" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:docname="birthday.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs8142"> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4232" + id="linearGradient3007" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.7499999,14.982194,1.1250003)" + x1="7.5089025" + y1="2.218369" + x2="7.5089025" + y2="4.8258252" /> + <linearGradient + id="linearGradient4380" + inkscape:collect="always"> + <stop + id="stop4382" + offset="0" + style="stop-color:#fcaf3e;stop-opacity:1;" /> + <stop + id="stop4384" + offset="1" + style="stop-color:#ffffff;stop-opacity:1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4380" + id="linearGradient3005" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.7499999,-2.9821948,0.3750003)" + x1="15.491097" + y1="4.2733984" + x2="15.491097" + y2="2.7707961" /> + <linearGradient + id="linearGradient4142" + inkscape:collect="always"> + <stop + id="stop4144" + offset="0" + style="stop-color:#5c3566;stop-opacity:1;" /> + <stop + id="stop4146" + offset="1" + style="stop-color:#9253a2;stop-opacity:1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4142" + id="linearGradient3029" + gradientUnits="userSpaceOnUse" + x1="15.5" + y1="10.635184" + x2="15.5" + y2="7.1438446" + gradientTransform="matrix(1,0,0,0.8000001,-2.9821944,0.2999994)" /> + <linearGradient + id="linearGradient4150" + inkscape:collect="always"> + <stop + id="stop4152" + offset="0" + style="stop-color:#3c7704;stop-opacity:1" /> + <stop + id="stop4154" + offset="1" + style="stop-color:#59b106;stop-opacity:1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4150" + id="linearGradient3034" + gradientUnits="userSpaceOnUse" + x1="7.5" + y1="9.4861355" + x2="7.5" + y2="7.0554562" + gradientTransform="matrix(1,0,0,0.8000001,-3,0.2999994)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4232" + id="linearGradient3048" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(1,0,0,0.7499999,-3.0178049,0.3750005)" + x1="7.5089025" + y1="2.218369" + x2="7.5089025" + y2="4.8258252" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4374"> + <stop + style="stop-color:#fcaf3e;stop-opacity:1;" + offset="0" + id="stop4376" /> + <stop + style="stop-color:#ffffff;stop-opacity:1" + offset="1" + id="stop4378" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4374" + id="linearGradient3046" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(2.463041,0,0,1.1368063,-10.850902,0.678176)" + x1="6.3242626" + y1="2.3645318" + x2="6.3242626" + y2="1.6300712" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4232"> + <stop + style="stop-color:#f57900;stop-opacity:1;" + offset="0" + id="stop4234" /> + <stop + style="stop-color:#b25800;stop-opacity:1" + offset="1" + id="stop4236" /> + </linearGradient> + <linearGradient + y2="4.8258252" + x2="7.5089025" + y1="2.218369" + x1="7.5089025" + gradientTransform="matrix(1,0,0,0.7499999,1.0000002,-0.6249996)" + gradientUnits="userSpaceOnUse" + id="linearGradient4308" + xlink:href="#linearGradient4232" + inkscape:collect="always" /> + <linearGradient + id="linearGradient4368" + inkscape:collect="always"> + <stop + id="stop4370" + offset="0" + style="stop-color:#fcaf3e;stop-opacity:1;" /> + <stop + id="stop4372" + offset="1" + style="stop-color:#ffffff;stop-opacity:1" /> + </linearGradient> + <linearGradient + y2="1.6300712" + x2="6.3242626" + y1="2.3645318" + x1="6.3242626" + gradientTransform="matrix(2.463041,0,0,1.1368063,-6.8330964,-0.3218242)" + gradientUnits="userSpaceOnUse" + id="linearGradient4306" + xlink:href="#linearGradient4368" + inkscape:collect="always" /> + <linearGradient + inkscape:collect="always" + id="linearGradient3425"> + <stop + style="stop-color:#204a87;stop-opacity:1;" + offset="0" + id="stop3427" /> + <stop + style="stop-color:#2e69c2;stop-opacity:1" + offset="1" + id="stop3429" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3425" + id="linearGradient3431" + x1="11.5" + y1="9.961833" + x2="11.241222" + y2="6.6366434" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-2.9999997,-2.0000001)" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4388"> + <stop + style="stop-color:#d3d7cf;stop-opacity:1;" + offset="0" + id="stop4390" /> + <stop + style="stop-color:#d3d7cf;stop-opacity:0;" + offset="1" + id="stop4392" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4388" + id="linearGradient4394" + x1="2.9999998" + y1="11.5" + x2="21" + y2="11.5" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4158"> + <stop + style="stop-color:#9a9c98;stop-opacity:1" + offset="0" + id="stop4160" /> + <stop + style="stop-color:#666763;stop-opacity:1" + offset="1" + id="stop4162" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4158" + id="linearGradient4164" + x1="16.274719" + y1="9.7764273" + x2="17.448" + y2="13.753902" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.7894737,0,0,0.6666667,-1.4736843,1.8333322)" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4048"> + <stop + style="stop-color:#ffffff;stop-opacity:1;" + offset="0" + id="stop4050" /> + <stop + style="stop-color:#ffffff;stop-opacity:0;" + offset="1" + id="stop4052" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4048" + id="linearGradient4054" + x1="-10.516191" + y1="10.124428" + x2="36.795452" + y2="19.026175" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + id="linearGradient3941"> + <stop + style="stop-color:#af6d02;stop-opacity:1" + offset="0" + id="stop3943" /> + <stop + style="stop-color:#5f3b00;stop-opacity:1" + offset="1" + id="stop3945" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3941" + id="linearGradient3947" + x1="15.917198" + y1="16.659033" + x2="16.463091" + y2="20.489477" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.7894737,0,0,0.7,-1.473684,0.4500012)" /> + <linearGradient + inkscape:collect="always" + id="linearGradient4003"> + <stop + style="stop-color:#c17d11;stop-opacity:1" + offset="0" + id="stop4005" /> + <stop + style="stop-color:#e9b96e;stop-opacity:1" + offset="1" + id="stop4007" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient4003" + id="linearGradient4009" + x1="16.815628" + y1="16.941942" + x2="10.718681" + y2="16.941942" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.7894737,0,0,0.7,-1.473684,0.4500012)" /> + <filter + inkscape:collect="always" + id="filter4540" + x="-0.087152615" + width="1.1743052" + y="-0.21174857" + height="1.4234971"> + <feGaussianBlur + inkscape:collect="always" + stdDeviation="0.37221428" + id="feGaussianBlur4542" /> + </filter> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="16 : 8 : 1" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + id="perspective8148" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.197802" + inkscape:cx="8" + inkscape:cy="8" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:grid-bbox="true" + inkscape:document-units="px" + inkscape:window-width="641" + inkscape:window-height="669" + inkscape:window-x="0" + inkscape:window-y="22"> + <inkscape:grid + type="xygrid" + id="grid8150" /> + </sodipodi:namedview> + <metadata + id="metadata8145"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <path + sodipodi:type="arc" + style="opacity:0.47499999999999998;fill:#edd400;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;filter:url(#filter4540);enable-background:accumulate" + id="path4466" + sodipodi:cx="12.03125" + sodipodi:cy="4.265625" + sodipodi:rx="5.125" + sodipodi:ry="2.109375" + d="M 17.15625,4.265625 A 5.125,2.109375 0 1 1 6.90625,4.265625 A 5.125,2.109375 0 1 1 17.15625,4.265625 z" + transform="matrix(1.3658536,0,0,1.1656218,-8.4329267,-2.4721054)" /> + <path + style="fill:url(#linearGradient4009);fill-opacity:1;stroke:url(#linearGradient3947);stroke-width:1.00000072000000007;stroke-miterlimit:4;stroke-opacity:1" + d="m 0.50000029,9.8437508 0,3.5562497 C 0.50000029,14.5592 3.8600006,15.5 8.0000002,15.5 12.14,15.5 15.5,14.559199 15.5,13.4 l 0,-3.5562497 z" + id="path3936" + sodipodi:nodetypes="ccsccc" /> + <rect + style="opacity:0.26499999000000002;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect4624" + width="2" + height="3" + x="3" + y="11" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="-1.0054175" + inkscape:original="M 2.5 11.5 L 2.5 18.5 C 2.5 20.155999 6.7560004 21.5 12 21.5 C 17.243999 21.5 21.5 20.155998 21.5 18.5 L 21.5 11.5 L 2.5 11.5 z " + style="fill:none;stroke:url(#linearGradient4054);stroke-width:1.44648218000000006;stroke-miterlimit:4;stroke-opacity:1" + id="path3983" + d="m 3.5,12.5 0,6 c 0,-0.016662 0.0034472,0.121066 0.34375,0.375 0.3403028,0.253934 0.9602178,0.531845 1.75,0.78125 C 7.1733144,20.15506 9.4638941,20.5 12,20.5 c 2.536106,0 4.826685,-0.34494 6.40625,-0.84375 0.789782,-0.249405 1.409697,-0.527316 1.75,-0.78125 C 20.496553,18.621066 20.5,18.483337 20.5,18.5 l 0,-6 z" + transform="matrix(0.7647058,0,0,0.6249999,-1.1764702,1.6875019)" /> + <rect + style="opacity:0.83499995000000005;color:#000000;fill:#8f5902;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect4056" + width="1" + height="3" + x="6" + y="11" + rx="0.5" + ry="0.5" /> + <rect + style="opacity:0.83499995000000005;color:#000000;fill:#8f5902;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect4078" + width="1" + height="3" + x="3" + y="10" + rx="0.5" + ry="0.5" /> + <rect + style="opacity:0.83499995000000005;color:#000000;fill:#8f5902;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect4131" + width="1" + height="3" + x="12" + y="10" + rx="0.5" + ry="0.5" /> + <rect + style="opacity:0.26499999000000002;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect2655" + width="2" + height="3" + x="10" + y="11" /> + <rect + style="opacity:0.26499999000000002;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect2664" + width="2" + height="2" + x="13" + y="11" /> + <rect + style="opacity:0.26499999000000002;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect2646" + width="2" + height="3" + x="7" + y="12" /> + <rect + style="opacity:0.83499995000000005;color:#000000;fill:#8f5902;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect4140" + width="1" + height="3" + x="8" + y="10" + rx="0.5" + ry="0.5" /> + <path + style="fill:#eeeeec;fill-opacity:1;stroke:url(#linearGradient4164);stroke-width:1.00000059999999991;stroke-miterlimit:4;stroke-opacity:1" + d="M 15.500001,9.5000002 C 15.500001,10.604 12.139999,11.5 8,11.5 3.8600003,11.5 0.5000003,10.604 0.5000003,9.5000004 0.5000003,8.3960006 3.8600004,7.5000001 8,7.5000001 c 4.140001,0 7.500001,0.89600032 7.500001,2.0000001 z" + id="path1307" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="-1.0051613" + inkscape:original="M 12 8.5 C 6.7560004 8.5 2.5000001 9.844 2.5 11.5 C 2.5 13.155999 6.7560004 14.5 12 14.5 C 17.243999 14.5 21.5 13.155999 21.5 11.5 C 21.5 9.844 17.244 8.5000008 12 8.5 z " + xlink:href="#path4198" + style="fill:url(#linearGradient4394);fill-opacity:1;stroke:#ffffff;stroke-width:1.61721622999999992;stroke-miterlimit:4;stroke-opacity:1" + id="path4200" + d="M 12,9.5 C 9.4638721,9.5 7.1733501,9.8449289 5.59375,10.34375 4.80395,10.593161 4.1840886,10.87104 3.84375,11.125 3.5034114,11.37896 3.5,11.516552 3.5,11.5 c 0,-0.016553 0.0034113,0.12104 0.34375,0.375 0.3403387,0.25396 0.9601999,0.531839 1.75,0.78125 C 7.1733501,13.155071 9.4638722,13.5 12,13.5 c 2.536128,0 4.82665,-0.344929 6.40625,-0.84375 0.7898,-0.249411 1.409661,-0.52729 1.75,-0.78125 C 20.496589,11.62104 20.5,11.483447 20.5,11.5 c 0,0.016552 -0.003411,-0.12104 -0.34375,-0.375 -0.340339,-0.25396 -0.9602,-0.53184 -1.75,-0.78125 C 16.82665,9.844929 14.536128,9.5000004 12,9.5 z" + transform="matrix(0.7647058,0,0,0.4999999,-1.1764702,3.7500016)" /> + <rect + style="opacity:1;color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3431);stroke-width:1.00000011999999994;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3304" + width="2" + height="4.999999" + x="7.5" + y="4.500001" + rx="1" + ry="1" /> + <rect + style="opacity:0.55500033999999998;color:#000000;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3308" + width="1" + height="1" + x="8" + y="6" /> + <rect + style="opacity:0.55500033999999998;color:#000000;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3330" + width="1" + height="1" + x="8" + y="8" /> + <rect + style="opacity:0.31000000999999999;color:#000000;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3608" + width="1" + height="1" + x="7" + y="6" /> + <rect + style="opacity:0.31000000999999999;color:#000000;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3617" + width="1" + height="1" + x="7" + y="8" /> + <rect + style="opacity:0.31000000999999999;color:#000000;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3626" + width="1" + height="1" + x="9" + y="8" /> + <rect + style="opacity:0.31000000999999999;color:#000000;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3635" + width="1" + height="1" + x="9" + y="6" /> + <path + style="fill:url(#linearGradient4306);fill-opacity:1;stroke:url(#linearGradient4308);stroke-width:1.00000119000000010;stroke-miterlimit:4;stroke-opacity:1" + d="m 8.9999763,3.4999993 c -0.827986,0 -1.499975,-0.6719998 -1.499975,-1.4999993 0,-0.82799966 0.671989,-1.4999994 1.4999755,-1.4999994 0.5378079,0 0.8279864,2.9999986 0,2.9999987 z" + id="path4284" + sodipodi:nodetypes="csss" /> + <path + style="fill:url(#linearGradient3046);fill-opacity:1;stroke:url(#linearGradient3048);stroke-width:1.00000119000000010;stroke-miterlimit:4;stroke-opacity:1" + d="M 4.9821711,4.4999994 C 4.1541851,4.4999994 3.4821961,3.8279997 3.4821961,3.0000001 C 3.4821961,2.1720004 4.1541851,1.5000007 4.9821711,1.5000007 C 5.5199791,1.5000007 5.8101571,4.4999993 4.9821711,4.4999994 L 4.9821711,4.4999994 z" + id="path2289" + sodipodi:nodetypes="csss" /> + <rect + style="fill:#6ec31b;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3034);stroke-width:1.00000024000000010;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3469" + width="2" + height="4" + x="3.5" + y="5.5" + rx="1" + ry="1" /> + <rect + style="opacity:0.55500033999999998;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3471" + width="1" + height="1" + x="4" + y="5.9999995" /> + <rect + style="opacity:0.55500033999999998;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3473" + width="1" + height="1" + x="4" + y="7.9999995" /> + <rect + style="fill:#ad7fa8;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3029);stroke-width:1.00000024000000010;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3533" + width="2" + height="4" + x="11.517806" + y="5.5" + rx="1" + ry="1" /> + <rect + style="opacity:0.55500033999999998;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3535" + width="1" + height="1" + x="12.017806" + y="5.9999995" /> + <rect + style="opacity:0.55500033999999998;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3537" + width="1" + height="1" + x="12.017806" + y="7.9999995" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3559" + width="1" + height="1" + x="3" + y="6" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3581" + width="1" + height="1" + x="3" + y="8" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3590" + width="1" + height="1" + x="5" + y="8" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3599" + width="1" + height="1" + x="5" + y="6" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3644" + width="1" + height="1" + x="11.017806" + y="6" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3653" + width="1" + height="1" + x="13.017806" + y="6" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3662" + width="1" + height="1" + x="13.017806" + y="8" /> + <rect + style="opacity:0.31000000999999999;fill:#c6d7ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" + id="rect3671" + width="1" + height="1" + x="11.017806" + y="8" /> + <path + style="fill:url(#linearGradient3005);fill-opacity:1;stroke:url(#linearGradient3007);stroke-width:1.00000119000000010;stroke-miterlimit:4;stroke-opacity:1" + d="M 12.999976,4.4999993 C 12.17199,4.4999993 11.500001,3.8279995 11.500001,3 C 11.500001,2.1720003 12.17199,1.5000006 12.999977,1.5000006 C 13.537784,1.5000006 13.827963,4.4999992 12.999977,4.4999993 L 12.999976,4.4999993 z" + id="path4348" + sodipodi:nodetypes="csss" /> + </g> +</svg>
--- a/pidgin/pixmaps/emotes/default/24/Makefile.am Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/pixmaps/emotes/default/24/Makefile.am Thu Nov 20 21:13:56 2008 +0000 @@ -16,6 +16,7 @@ boy.png \ brb.png \ bulgy-eyes.png \ + bunny.png \ bye.png \ cake.png \ call-me.png \
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/pixmaps/emotes/default/24/default.theme.in Thu Nov 20 21:13:56 2008 +0000 @@ -3,7 +3,6 @@ Icon=wink.png Author=Hylke Bons - # Default smileys [default] smile.png :) :-) @@ -125,6 +124,7 @@ party.png <:o) eyeroll.png 8-) yawn.png |-) +bunny.png ('.') ! skywalker.png C:-) c:-) C:) c:) ! monkey.png :-(|) :(|)
--- a/pidgin/pixmaps/toolbar/16/scalable/emote-select.svg Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/pixmaps/toolbar/16/scalable/emote-select.svg Thu Nov 20 21:13:56 2008 +0000 @@ -2,113 +2,26 @@ <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" + xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16px" - height="16px" - id="svg4346" + id="svg2" sodipodi:version="0.32" - inkscape:version="0.44.1" - sodipodi:docbase="/home/hbons/Desktop/pidgin improvements" - sodipodi:docname="inser-emote.svg" - inkscape:export-filename="/home/hbons/Desktop/pidgin improvements/insert-emote.png" + inkscape:version="0.46" + width="16" + height="16" + version="1.0" + sodipodi:docname="emote-select.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape" + inkscape:export-filename="/home/hbons/Pidgin objects refresh/emote-select.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90"> - <defs - id="defs4348"> - <linearGradient - inkscape:collect="always" - id="linearGradient5269"> - <stop - style="stop-color:#fcaf3e;stop-opacity:1;" - offset="0" - id="stop5271" /> - <stop - style="stop-color:#fcaf3e;stop-opacity:0;" - offset="1" - id="stop5273" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3104"> - <stop - style="stop-color:#eeeeec;stop-opacity:1;" - offset="0" - id="stop3106" /> - <stop - style="stop-color:#eeeeec;stop-opacity:0;" - offset="1" - id="stop3108" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3104" - id="radialGradient3114" - cx="5.7434092" - cy="16.737026" - fx="5.7434092" - fy="16.737026" - r="9.975256" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-1.30241,1.304442,-1.325199,-1.323009,41.46631,16.11711)" /> - <linearGradient - inkscape:collect="always" - id="linearGradient3150"> - <stop - style="stop-color:#2e3436;stop-opacity:1;" - offset="0" - id="stop3152" /> - <stop - style="stop-color:#2e3436;stop-opacity:0;" - offset="1" - id="stop3154" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3150" - id="radialGradient3156" - cx="10.748654" - cy="10.457643" - fx="10.748654" - fy="10.457643" - r="6.6449099" - gradientTransform="matrix(-0.842757,0,0,-0.35721,19.80716,14.19321)" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient5269" - id="linearGradient5275" - x1="26.243328" - y1="13.001364" - x2="26.243328" - y2="10.507664" - gradientUnits="userSpaceOnUse" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="31.392433" - inkscape:cx="14.469085" - inkscape:cy="9.6077349" - inkscape:current-layer="layer1" - showgrid="true" - inkscape:grid-bbox="true" - inkscape:document-units="px" - inkscape:window-width="1274" - inkscape:window-height="966" - inkscape:window-x="3" - inkscape:window-y="25" /> <metadata - id="metadata4351"> + id="metadata7"> <rdf:RDF> <cc:Work rdf:about=""> @@ -118,84 +31,573 @@ </cc:Work> </rdf:RDF> </metadata> - <g - id="layer1" - inkscape:label="Layer 1" - inkscape:groupmode="layer"> - <path - sodipodi:type="arc" - style="opacity:1;fill:#fce94f;fill-opacity:1;stroke:#f57900;stroke-width:1.53872597;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path1307" - sodipodi:cx="11.806158" - sodipodi:cy="10.983024" - sodipodi:rx="9.975256" - sodipodi:ry="9.975256" - d="M 21.781414 10.983024 A 9.975256 9.975256 0 1 1 1.8309021,10.983024 A 9.975256 9.975256 0 1 1 21.781414 10.983024 z" - transform="matrix(0.651503,0,0,0.651499,-0.691896,-0.15554)" /> - <path - sodipodi:type="arc" - style="opacity:0.79545456;fill:url(#radialGradient3114);fill-opacity:1;stroke:none;stroke-width:1.05274069;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path3102" - sodipodi:cx="11.806158" - sodipodi:cy="10.983024" - sodipodi:rx="9.975256" - sodipodi:ry="9.975256" - d="M 21.781414 10.983024 A 9.975256 9.975256 0 1 1 1.8309021,10.983024 A 9.975256 9.975256 0 1 1 21.781414 10.983024 z" - transform="matrix(0.601488,0,0,0.601488,-0.101266,0.39384)" /> - <path - sodipodi:type="arc" - style="opacity:0.5;fill:none;fill-opacity:1;stroke:white;stroke-width:1.81368434;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path2184" - sodipodi:cx="11.806158" - sodipodi:cy="10.983024" - sodipodi:rx="9.975256" - sodipodi:ry="9.975256" - d="M 21.781414 10.983024 A 9.975256 9.975256 0 1 1 1.8309021,10.983024 A 9.975256 9.975256 0 1 1 21.781414 10.983024 z" - transform="matrix(0.551364,0,0,0.551364,0.490507,0.944359)" /> - <path - style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - d="M 15.725806,10.961292 C 15.725806,15.032532 14.403819,17 12,17 C 9.596182,17 8.1945569,15.02924 8.1945569,10.961292 C 9.1602707,14.990359 9.904443,15.152467 12,15.152467 C 14.095556,15.152468 14.914933,15.003949 15.725806,10.961292 z " - id="path2186" - sodipodi:nodetypes="cscsc" - transform="matrix(1.062241,0,0,0.5,-5.704588,1.519354)" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path2191" - sodipodi:cx="9.0598059" - sodipodi:cy="8.7845774" - sodipodi:rx="1.1679889" - sodipodi:ry="1.4520943" - d="M 10.227795 8.7845774 A 1.1679889 1.4520943 0 1 1 7.891817,8.7845774 A 1.1679889 1.4520943 0 1 1 10.227795 8.7845774 z" - transform="matrix(0.856175,0,0,1.032991,-2.756776,-3.574387)" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path2193" - sodipodi:cx="9.0598059" - sodipodi:cy="8.7845774" - sodipodi:rx="1.1679889" - sodipodi:ry="1.4520943" - d="M 10.227795 8.7845774 A 1.1679889 1.4520943 0 1 1 7.891817,8.7845774 A 1.1679889 1.4520943 0 1 1 10.227795 8.7845774 z" - transform="matrix(0.85617,0,0,1.032991,1.243263,-3.574387)" /> - <image - id="image4376" - height="16" - width="16" - sodipodi:absref="/home/hbons/Desktop/pidgin improvements/insert-image.png" - xlink:href="insert-image.png" - transform="translate(17,-1)" /> - <path - transform="matrix(1.046767,0,0,0.836179,-16.0259,3.13821)" - style="fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06887114;stroke-miterlimit:4;stroke-opacity:1" - d="M 22,10 L 29.642574,10 L 25.830275,14.783663 L 22,10 z " - id="path4382" - sodipodi:nodetypes="cccc" /> - <path - transform="matrix(1.046767,0,0,0.836179,-16.52887,3.138211)" - style="fill:url(#linearGradient5275);fill-opacity:1;stroke:#d5680b;stroke-width:1.06887114;stroke-miterlimit:4;stroke-opacity:1" - d="M 22,10 L 29.642574,10 L 25.830275,14.783663 L 22,10 z " - id="rect4379" - sodipodi:nodetypes="cccc" /> - </g> + <defs + id="defs5"> + <linearGradient + inkscape:collect="always" + id="linearGradient3398"> + <stop + style="stop-color:#fe9906;stop-opacity:1;" + offset="0" + id="stop3400" /> + <stop + style="stop-color:#ffffff;stop-opacity:1" + offset="1" + id="stop3402" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3390"> + <stop + style="stop-color:#fe9906;stop-opacity:1" + offset="0" + id="stop3392" /> + <stop + style="stop-color:#ffffff;stop-opacity:1" + offset="1" + id="stop3394" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3378"> + <stop + style="stop-color:#a64a00;stop-opacity:1;" + offset="0" + id="stop3380" /> + <stop + style="stop-color:#a64a00;stop-opacity:0;" + offset="1" + id="stop3382" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3357"> + <stop + style="stop-color:#dd6c00;stop-opacity:1" + offset="0" + id="stop3359" /> + <stop + style="stop-color:#7c3c00;stop-opacity:1" + offset="1" + id="stop3361" /> + </linearGradient> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="744.09448 : 526.18109 : 1" + inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + id="perspective9" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="10.507664" + x2="26.243328" + y1="13.001364" + x1="26.243328" + id="linearGradient5275" + xlink:href="#linearGradient5269" + inkscape:collect="always" /> + <radialGradient + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.842757,0,0,-0.35721,19.80716,14.19321)" + r="6.6449099" + fy="10.457643" + fx="10.748654" + cy="10.457643" + cx="10.748654" + id="radialGradient3156" + xlink:href="#linearGradient3150" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3150" + inkscape:collect="always"> + <stop + id="stop3152" + offset="0" + style="stop-color:#2e3436;stop-opacity:1;" /> + <stop + id="stop3154" + offset="1" + style="stop-color:#2e3436;stop-opacity:0;" /> + </linearGradient> + <radialGradient + gradientTransform="matrix(-1.30241,1.304442,-1.325199,-1.323009,41.46631,16.11711)" + gradientUnits="userSpaceOnUse" + r="9.975256" + fy="16.737026" + fx="5.7434092" + cy="16.737026" + cx="5.7434092" + id="radialGradient3114" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3104" + inkscape:collect="always"> + <stop + id="stop3106" + offset="0" + style="stop-color:#eeeeec;stop-opacity:1;" /> + <stop + id="stop3108" + offset="1" + style="stop-color:#eeeeec;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient5269" + inkscape:collect="always"> + <stop + id="stop5271" + offset="0" + style="stop-color:#fcaf3e;stop-opacity:1;" /> + <stop + id="stop5273" + offset="1" + style="stop-color:#fcaf3e;stop-opacity:0;" /> + </linearGradient> + <inkscape:perspective + id="perspective3186" + inkscape:persp3d-origin="8 : 5.3333333 : 1" + inkscape:vp_z="16 : 8 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 8 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + id="radialGradient2264" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient2233" + cx="8.3343515" + cy="14.186539" + fx="8.3343515" + fy="14.186539" + r="9.975256" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" /> + <radialGradient + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + id="radialGradient2349" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient2303" + cx="8.3343515" + cy="14.186539" + fx="8.3343515" + fy="14.186539" + r="9.975256" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="17.682426" + x2="12.720216" + y1="20.952612" + x1="12.720216" + id="linearGradient2873" + xlink:href="#linearGradient2867" + inkscape:collect="always" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="20.761486" + x2="12.746171" + y1="18.202251" + x1="12.5" + id="linearGradient2853" + xlink:href="#linearGradient2847" + inkscape:collect="always" /> + <radialGradient + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + id="radialGradient2230" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <linearGradient + id="linearGradient2382" + inkscape:collect="always"> + <stop + id="stop2384" + offset="0" + style="stop-color:#d3d7cf;stop-opacity:1;" /> + <stop + id="stop2386" + offset="1" + style="stop-color:#d3d7cf;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient2847" + inkscape:collect="always"> + <stop + id="stop2849" + offset="0" + style="stop-color:#888a85;stop-opacity:1;" /> + <stop + id="stop2851" + offset="1" + style="stop-color:#888a85;stop-opacity:0;" /> + </linearGradient> + <linearGradient + id="linearGradient2867" + inkscape:collect="always"> + <stop + id="stop2869" + offset="0" + style="stop-color:#d3d7cf;stop-opacity:1;" /> + <stop + id="stop2871" + offset="1" + style="stop-color:#d3d7cf;stop-opacity:0;" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient2382" + id="radialGradient2259" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.591138,1.574803,-1.783257,-1.76495,68.854751,-2.8442229)" + cx="17.911736" + cy="11.083743" + fx="17.911736" + fy="11.083743" + r="2.5781252" /> + <radialGradient + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + id="radialGradient3313" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <radialGradient + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + id="radialGradient2255" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient2214" + cx="8.7359829" + cy="18.005522" + fx="8.7359829" + fy="18.005522" + r="9.975256" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.3308754,1.3308124,-1.4391023,-1.4390341,45.391773,16.51952)" /> + <radialGradient + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.842757,0,0,-0.35721,19.80716,14.19321)" + r="6.6449099" + fy="10.457643" + fx="10.748654" + cy="10.457643" + cx="10.748654" + id="radialGradient3292" + xlink:href="#linearGradient3150" + inkscape:collect="always" /> + <radialGradient + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + id="radialGradient3175" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient3191" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.3308754,1.3308124,-1.4391023,-1.4390341,45.391773,16.51952)" + cx="8.7359829" + cy="18.005522" + fx="8.7359829" + fy="18.005522" + r="9.975256" /> + <linearGradient + gradientUnits="userSpaceOnUse" + y2="15.449714" + x2="10.698112" + y1="16.037401" + x1="12.845698" + id="linearGradient3269" + xlink:href="#linearGradient3263" + inkscape:collect="always" /> + <radialGradient + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + id="radialGradient2216" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <linearGradient + id="linearGradient3263" + inkscape:collect="always"> + <stop + id="stop3265" + offset="0" + style="stop-color:#555753;stop-opacity:1;" /> + <stop + id="stop3267" + offset="1" + style="stop-color:#555753;stop-opacity:0;" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient2247" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.3308754,1.3308124,-1.4391023,-1.4390341,45.391773,16.51952)" + cx="8.7359829" + cy="18.005522" + fx="8.7359829" + fy="18.005522" + r="9.975256" /> + <radialGradient + gradientTransform="matrix(-0.9327,0.932656,-0.947494,-0.947449,33.02126,11.96667)" + gradientUnits="userSpaceOnUse" + r="9.975256" + fy="14.186539" + fx="8.3343515" + cy="14.186539" + cx="8.3343515" + id="radialGradient3274" + xlink:href="#linearGradient3104" + inkscape:collect="always" /> + <inkscape:perspective + id="perspective3265" + inkscape:persp3d-origin="12 : 8 : 1" + inkscape:vp_z="24 : 12 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_x="0 : 12 : 1" + sodipodi:type="inkscape:persp3d" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient3331" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.30241,1.304442,-1.325199,-1.323009,41.46631,16.11711)" + cx="5.7434092" + cy="16.737026" + fx="5.7434092" + fy="16.737026" + r="9.975256" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3104" + id="radialGradient3344" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-1.30241,1.304442,-1.325199,-1.323009,41.46631,16.11711)" + cx="5.7434092" + cy="16.737026" + fx="5.7434092" + fy="16.737026" + r="9.975256" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3357" + id="linearGradient3363" + x1="12.549859" + y1="12.357164" + x2="13.745301" + y2="19.751348" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3378" + id="radialGradient3384" + cx="11.76924" + cy="25.560368" + fx="11.76924" + fy="25.560368" + r="9.8412299" + gradientTransform="matrix(1.6444693,0,0,1.4596828,-7.5849142,-16.236248)" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3390" + id="linearGradient3396" + x1="5.0167913" + y1="6.0080996" + x2="5.9832082" + y2="6.9919" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3398" + id="linearGradient3404" + x1="9.9935493" + y1="9.1751938" + x2="9.4590645" + y2="10.073978" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + inkscape:window-height="849" + inkscape:window-width="1440" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + showgrid="true" + inkscape:snap-bbox="true" + inkscape:snap-nodes="false" + inkscape:zoom="20.32932" + inkscape:cx="16.928433" + inkscape:cy="11.656784" + inkscape:window-x="0" + inkscape:window-y="0" + inkscape:current-layer="svg2"> + <inkscape:grid + type="xygrid" + id="grid3156" + visible="true" + enabled="true" /> + </sodipodi:namedview> + <path + sodipodi:type="arc" + style="fill:#edd400;fill-opacity:1;stroke:url(#linearGradient3363);stroke-width:1.33355486000000001;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3317" + sodipodi:cx="11.806158" + sodipodi:cy="10.983024" + sodipodi:rx="9.975256" + sodipodi:ry="9.975256" + d="M 21.781414,10.983024 A 9.975256,9.975256 0 1 1 1.8309021,10.983024 A 9.975256,9.975256 0 1 1 21.781414,10.983024 z" + transform="matrix(0.751736,0,0,0.751736,-0.8751143,-0.2563346)" /> + <path + sodipodi:type="arc" + style="opacity:0.64044948999999995;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.53465486000000007;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3321" + sodipodi:cx="11.806158" + sodipodi:cy="10.983024" + sodipodi:rx="9.975256" + sodipodi:ry="9.975256" + d="M 21.781414,10.983024 A 9.975256,9.975256 0 1 1 1.8309021,10.983024 A 9.975256,9.975256 0 1 1 21.781414,10.983024 z" + transform="matrix(0.6516124,0,0,0.6516124,0.3069616,0.8433262)" /> + <path + sodipodi:type="arc" + style="opacity:0.79545456;fill:url(#radialGradient3344);fill-opacity:1;stroke:none;stroke-width:1.05274069;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3319" + sodipodi:cx="11.806158" + sodipodi:cy="10.983024" + sodipodi:rx="9.975256" + sodipodi:ry="9.975256" + d="M 21.781414,10.983024 A 9.975256,9.975256 0 1 1 1.8309021,10.983024 A 9.975256,9.975256 0 1 1 21.781414,10.983024 z" + transform="matrix(0.7017364,0,0,0.7017364,-0.2848107,0.2928127)" /> + <path + style="fill:#eeeeec;fill-opacity:1;stroke:url(#linearGradient3396);stroke-width:0.99999987999999995;stroke-miterlimit:4;stroke-opacity:1" + d="M 7.5,6.5000001 C 7.5,7.6040001 6.6040006,8.5000002 5.5000006,8.5000002 C 4.3960003,8.5000002 3.4999999,7.6040001 3.4999999,6.5000001 C 3.4999999,5.3959999 4.3960003,4.5 5.5000006,4.5 C 6.6040006,4.5 7.5,5.3959999 7.5,6.5000001 z" + id="path3154" /> + <path + sodipodi:type="arc" + style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2172" + sodipodi:cx="9.7069349" + sodipodi:cy="9.6526775" + sodipodi:rx="1.0259361" + sodipodi:ry="1.9413869" + d="M 10.732871,9.6526775 A 1.0259361,1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361,1.9413869 0 1 1 10.732871,9.6526775 z" + transform="matrix(0.9747194,0,0,0.5150957,-3.4615376,2.0279472)" /> + <path + sodipodi:type="arc" + style="fill:#eeeeec;fill-opacity:1;stroke:url(#linearGradient3404);stroke-width:0.70564497000000004;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3152" + sodipodi:cx="9.7069349" + sodipodi:cy="9.6526775" + sodipodi:rx="1.0259361" + sodipodi:ry="1.9413869" + d="M 10.732871,9.6526775 A 1.0259361,1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361,1.9413869 0 1 1 10.732871,9.6526775 z" + transform="matrix(1.9494389,0,0,1.0301914,-8.4230764,-3.4441052)" /> + <path + sodipodi:type="arc" + style="opacity:0.75;fill:none;fill-opacity:1;stroke:#ce5c00;stroke-width:0.85073602000000004;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2259" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(1.4017776,0.3494647,-0.2648498,0.9196421,1.6361947,-7.2342213)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> + <path + sodipodi:type="arc" + style="opacity:0.75;fill:none;fill-opacity:1;stroke:#ce5c00;stroke-width:0.85073620000000005;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2261" + sodipodi:cx="8.3258924" + sodipodi:cy="9.2232141" + sodipodi:rx="1.2276785" + sodipodi:ry="1.7410715" + d="M 7.2133909,8.4869402 A 1.2276785,1.7410715 0 0 1 9.288462,8.1425499" + transform="matrix(-1.4017777,0.3494645,0.2648498,0.9196416,14.363806,-7.2342154)" + sodipodi:start="3.5782199" + sodipodi:end="5.6135639" + sodipodi:open="true" /> + <path + sodipodi:type="arc" + style="fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:0.98640186;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3346" + sodipodi:cx="9.7069349" + sodipodi:cy="9.6526775" + sodipodi:rx="1.0259361" + sodipodi:ry="1.9413869" + d="M 10.732871,9.6526775 A 1.0259361,1.9413869 0 1 1 8.6809988,9.6526775 A 1.0259361,1.9413869 0 1 1 10.732871,9.6526775 z" + transform="matrix(0.9747195,0,0,0.5150957,0.5384613,2.0279474)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3384);stroke-width:2.21672367999999986;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3367" + sodipodi:cx="11.806158" + sodipodi:cy="10.983024" + sodipodi:rx="9.975256" + sodipodi:ry="9.975256" + d="M 20.502108,15.870372 A 9.975256,9.975256 0 0 1 3.0363724,15.736611" + transform="matrix(0.4511163,0,0,0.4511162,2.6740503,3.0453797)" + sodipodi:start="0.51202919" + sodipodi:end="2.6448802" + sodipodi:open="true" /> </svg>
--- a/pidgin/plugins/cap/cap.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/plugins/cap/cap.c Thu Nov 20 21:13:56 2008 +0000 @@ -333,7 +333,7 @@ static gboolean max_message_difference_cb(gpointer data) { CapStatistics *stats = data; - purple_debug_info("cap", "Max Message Difference timeout occured\n"); + purple_debug_info("cap", "Max Message Difference timeout occurred\n"); insert_cap_failure(stats); stats->timeout_source_id = 0; return FALSE;
--- a/pidgin/plugins/perl/common/Makefile.mingw Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/plugins/perl/common/Makefile.mingw Thu Nov 20 21:13:56 2008 +0000 @@ -5,13 +5,12 @@ # PIDGIN_TREE_TOP := ../../../.. +GCCWARNINGS := -Wno-comment -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wpointer-arith -Wundef -Wno-unused include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = Pidgin EXTUTILS ?= C:/perl/lib/ExtUtils -CFLAGS += -Wno-comment -Wno-unused - ## ## INCLUDE PATHS ## @@ -72,7 +71,7 @@ ## ## LIBRARIES ## -LIBS = -lperl58 \ +LIBS = -lperl510 \ -lperl \ -lpurple \ -lpidgin \
--- a/pidgin/win32/nsis/pidgin-installer.nsi Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Thu Nov 20 21:13:56 2008 +0000 @@ -72,7 +72,7 @@ !define GTK_MIN_VERSION "2.6.10" !define GTK_REG_KEY "SOFTWARE\GTK\2.0" !define PERL_REG_KEY "SOFTWARE\Perl" -!define PERL_DLL "perl58.dll" +!define PERL_DLL "perl510.dll" !define GTK_DEFAULT_INSTALL_PATH "$COMMONFILES\GTK\2.0" !define GTK_RUNTIME_INSTALLER "..\..\..\..\gtk_installer\gtk-runtime*.exe" @@ -376,7 +376,7 @@ StrCmp $R0 "2" +2 ; Upgrade isn't optional MessageBox MB_YESNO $(GTK_UPGRADE_PROMPT) /SD IDYES IDNO done ClearErrors - ExecWait '"$TEMP\gtk-runtime.exe" /L=$LANGUAGE /S /D=$GTK_FOLDER' + ExecWait '"$TEMP\gtk-runtime.exe" /L=$LANGUAGE $ISSILENT /D=$GTK_FOLDER' IfErrors gtk_install_error done gtk_install_error: @@ -505,11 +505,16 @@ ; If this is under NT4, delete the SILC support stuff ; there is a bug that will prevent any account from connecting ; See https://lists.silcnet.org/pipermail/silc-devel/2005-January/001588.html + ; Also, remove the GSSAPI SASL plugin and associated files as they aren't + ; compatible with NT4. ${If} ${IsNT} ${AndIf} ${IsWinNT4} + ;SILC Delete "$INSTDIR\plugins\libsilc.dll" Delete "$INSTDIR\libsilcclient-1-1-2.dll" Delete "$INSTDIR\libsilc-1-1-2.dll" + ;GSSAPI + Delete "$INSTDIR\sasl2\saslGSSAPI.dll" ${EndIf} SetOutPath "$INSTDIR" @@ -704,6 +709,8 @@ Push "ymsgr" Call un.UnregisterURIHandler + Delete "$INSTDIR\ca-certs\CAcert_Class3.pem" + Delete "$INSTDIR\ca-certs\CAcert_Root.pem" Delete "$INSTDIR\ca-certs\Equifax_Secure_CA.pem" Delete "$INSTDIR\ca-certs\GTE_CyberTrust_Global_Root.pem" Delete "$INSTDIR\ca-certs\Microsoft_Secure_Server_Authority.pem" @@ -1304,12 +1311,12 @@ ;Reset ShellVarContext because we may have changed it SetShellVarContext "current" - StrCpy $ISSILENT "/NOUI" + StrCpy $ISSILENT "/S" ; GTK installer has two silent states.. one with Message boxes, one without ; If pidgin installer was run silently, we want to supress gtk installer msg boxes. IfSilent 0 set_gtk_normal - StrCpy $ISSILENT "/S" + StrCpy $ISSILENT "/NOUI" set_gtk_normal: ${GetParameters} $R0 @@ -1388,67 +1395,6 @@ FunctionEnd -; This is a modified StartRadioButtons (from Sections.nsh) -; The only difference is that it allows for nothing in the group to be selected -; In that case, the default variable should be set to "" -!macro StartRadioButtonsUnselectable var - - !define StartRadioButtons_Var "${var}" - - Push $R0 - Push $R1 - - ;If we have no selection, don't try to unselect it - StrCmp "${StartRadioButtons_Var}" "" +4 - SectionGetFlags "${StartRadioButtons_Var}" $R0 - IntOp $R1 $R0 & ${SF_SELECTED} - IntOp $R0 $R0 & ${SECTION_OFF} - SectionSetFlags "${StartRadioButtons_Var}" $R0 - - ; If the previous value isn't currently selected, - ; we don't want to select it at the end - IntCmp $R1 ${SF_SELECTED} +2 - StrCpy "${StartRadioButtons_Var}" "" - - StrCpy $R1 "${StartRadioButtons_Var}" - -!macroend - -Function .onSelChange - Push $0 - Push $1 - Push $2 - - ; Check that at most one of the non-readonly spelling dictionaries are selected - ; We can't use $R0 or $R1 in this block since they're used in the macros - !insertmacro StartRadioButtonsUnselectable $SPELLCHECK_SEL - ; Start with the first language dictionary - IntOp $2 ${SecSpellCheck} + 1 - - start_spellcheck_radio: - SectionGetFlags $2 $0 - - IntOp $1 $0 & ${SF_SECGRPEND} - ; If it is the end of the section group, stop - IntCmp $1 ${SF_SECGRPEND} end_spellcheck_radio - - IntOp $0 $0 & ${SF_RO} - IntCmp $0 ${SF_RO} after_button_insert - ; If !readonly, then it is part of the radiobutton group - !insertmacro RadioButton $2 - after_button_insert: - - IntOp $2 $2 + 1 ;Advance to the next section - Goto start_spellcheck_radio - - end_spellcheck_radio: - !insertmacro EndRadioButtons - - Pop $2 - Pop $1 - Pop $0 -FunctionEnd - ; Page enter and exit functions.. Function preWelcomePage
--- a/pidgin/win32/winpidgin.c Mon Aug 18 17:08:01 2008 +0000 +++ b/pidgin/win32/winpidgin.c Thu Nov 20 21:13:56 2008 +0000 @@ -89,9 +89,8 @@ const char *err_msg = get_win32_error_message(retv); printf("Could not read reg key '%s' subkey '%s' value: '%s'.\nMessage: (%ld) %s\n", - ((key == HKEY_LOCAL_MACHINE) ? "HKLM" : - (key == HKEY_CURRENT_USER) ? "HKCU" : - "???"), + (key == HKEY_LOCAL_MACHINE) ? "HKLM" + : ((key == HKEY_CURRENT_USER) ? "HKCU" : "???"), sub_key, val_name, retv, err_msg); } RegCloseKey(hkey); @@ -216,13 +215,13 @@ /* Set up the settings dir base to be \\path\to * The actual settings dir will be \\path\to\.purple */ - snprintf(path2, sizeof(path2), "PURPLEHOME=%s", path); + _snprintf(path2, sizeof(path2), "PURPLEHOME=%s", path); printf("Setting settings dir: %s\n", path2); - putenv(path2); + _putenv(path2); - snprintf(path2, sizeof(path2), "PIDGIN_ASPELL_DIR=%s\\Aspell\\bin", path); + _snprintf(path2, sizeof(path2), "PIDGIN_ASPELL_DIR=%s\\Aspell\\bin", path); printf("%s\n", path2); - putenv(path2); + _putenv(path2); /* set the GTK+ path to be \\path\to\GTK\bin */ strcat(path, "\\GTK\\bin"); @@ -437,9 +436,82 @@ locale = winpidgin_get_locale(); - snprintf(envstr, 25, "LANG=%s", locale); + _snprintf(envstr, 25, "LANG=%s", locale); printf("Setting locale: %s\n", envstr); - putenv(envstr); + _putenv(envstr); +} + + +static void winpidgin_add_stuff_to_path() { + char perl_path[MAX_PATH + 1]; + char *ppath = NULL; + char mit_kerberos_path[MAX_PATH + 1]; + char *mpath = NULL; + DWORD plen; + + printf("%s", "Looking for Perl... "); + + plen = sizeof(perl_path); + if (read_reg_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\Perl", "", + (LPBYTE) &perl_path, &plen)) { + /* We *could* check for perl510.dll, but it seems unnecessary. */ + printf("found in '%s'.\n", perl_path); + + if (perl_path[strlen(perl_path) - 1] != '\\') + strcat(perl_path, "\\"); + strcat(perl_path, "bin"); + + ppath = perl_path; + } else + printf("%s", "not found.\n"); + + printf("%s", "Looking for MIT Kerberos... "); + + plen = sizeof(mit_kerberos_path); + if (read_reg_string(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", "InstallDir", + (LPBYTE) &mit_kerberos_path, &plen)) { + /* We *could* check for gssapi32.dll */ + printf("found in '%s'.\n", mit_kerberos_path); + + if (mit_kerberos_path[strlen(mit_kerberos_path) - 1] != '\\') + strcat(mit_kerberos_path, "\\"); + strcat(mit_kerberos_path, "bin"); + + mpath = mit_kerberos_path; + } else + printf("%s", "not found.\n"); + + if (ppath != NULL || mpath != NULL) { + const char *path = getenv("PATH"); + BOOL add_ppath = ppath != NULL && (path == NULL || !strstr(path, ppath)); + BOOL add_mpath = mpath != NULL && (path == NULL || !strstr(path, mpath)); + char *newpath; + int newlen; + + if (add_ppath || add_mpath) { + /* Enough to add "PATH=" + path + ";" + ppath + ";" + mpath + \0 */ + newlen = 6 + (path ? strlen(path) + 1 : 0); + if (add_ppath) + newlen += strlen(ppath) + 1; + if (add_mpath) + newlen += strlen(mpath) + 1; + newpath = malloc(newlen); + *newpath = '\0'; + + _snprintf(newpath, newlen, "PATH=%s%s%s%s%s%s", + path ? path : "", + path ? ";" : "", + add_ppath ? ppath : "", + add_ppath ? ";" : "", + add_mpath ? mpath : "", + add_mpath ? ";" : ""); + + printf("New PATH: %s\n", newpath); + + _putenv(newpath); + free(newpath); + } + } } #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13) @@ -598,10 +670,10 @@ } else { DWORD dw = GetLastError(); const char *err_msg = get_win32_error_message(dw); - snprintf(errbuf, 512, + _snprintf(errbuf, 512, "Error getting module filename.\nError: (%u) %s", (UINT) dw, err_msg); - printf("%s", errbuf); + printf("%s\n", errbuf); MessageBox(NULL, errbuf, NULL, MB_OK | MB_TOPMOST); pidgin_dir[0] = '\0'; } @@ -631,6 +703,9 @@ dll_prep(); winpidgin_set_locale(); + + winpidgin_add_stuff_to_path(); + /* If help, version or multiple flag used, do not check Mutex */ if (!strstr(lpszCmdLine, "-h") && !strstr(lpszCmdLine, "-v")) if (!winpidgin_set_running(getenv("PIDGIN_MULTI_INST") == NULL && strstr(lpszCmdLine, "-m") == NULL)) @@ -645,11 +720,11 @@ BOOL mod_not_found = (dw == ERROR_MOD_NOT_FOUND || dw == ERROR_DLL_NOT_FOUND); const char *err_msg = get_win32_error_message(dw); - snprintf(errbuf, 512, "Error loading pidgin.dll.\nError: (%u) %s%s%s", + _snprintf(errbuf, 512, "Error loading pidgin.dll.\nError: (%u) %s%s%s", (UINT) dw, err_msg, mod_not_found ? "\n" : "", mod_not_found ? "This probably means that GTK+ can't be found." : ""); - printf("%s", errbuf); + printf("%s\n", errbuf); MessageBox(NULL, errbuf, TEXT("Error"), MB_OK | MB_TOPMOST); return 0;
--- a/po/POTFILES.in Mon Aug 18 17:08:01 2008 +0000 +++ b/po/POTFILES.in Thu Nov 20 21:13:56 2008 +0000 @@ -143,12 +143,9 @@ libpurple/protocols/qq/keep_alive.c libpurple/protocols/qq/login_logout.c libpurple/protocols/qq/qq.c -libpurple/protocols/qq/qq_proxy.c -libpurple/protocols/qq/recv_core.c +libpurple/protocols/qq/qq_network.c libpurple/protocols/qq/send_file.c -libpurple/protocols/qq/sendqueue.c libpurple/protocols/qq/sys_msg.c -libpurple/protocols/qq/udp_proxy_s5.c libpurple/protocols/sametime/sametime.c libpurple/protocols/silc/buddy.c libpurple/protocols/silc/chat.c
--- a/po/de.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/de.po Thu Nov 20 21:13:56 2008 +0000 @@ -7,13 +7,12 @@ # # This file is distributed under the same license as the Pidgin package. # -# Bjoern Voigt <bjoern@cs.tu-berlin.de>, 2008. msgid "" msgstr "" "Project-Id-Version: de\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-06-09 20:56+0200\n" -"PO-Revision-Date: 2008-06-09 20:55+0200\n" +"POT-Creation-Date: 2008-07-11 17:26+0200\n" +"PO-Revision-Date: 2008-07-11 17:25+0200\n" "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n" "Language-Team: Deutsch <de@li.org>\n" "MIME-Version: 1.0\n" @@ -622,7 +621,6 @@ msgid "Invite" msgstr "Einladen" -#, fuzzy msgid "" "Please enter the name of the user you wish to invite,\n" "along with an optional invite message." @@ -3234,10 +3232,8 @@ msgstr "Falscher Modus" #, c-format -msgid "Ban on %s by %s, set %ld second ago" -msgid_plural "Ban on %s by %s, set %ld seconds ago" -msgstr[0] "Verbot zu %s von %s, gesetzt vor %ld Sekunde" -msgstr[1] "Verbot zu %s von %s, gesetzt vor %ld Sekunden" +msgid "Ban on %s by %s, set %s ago" +msgstr "Verbot zu %s von %s, gesetzt vor %s" #, c-format msgid "Ban on %s" @@ -6174,11 +6170,11 @@ msgid "In local permit/deny" msgstr "In lokaler erlaubt/verboten-Liste" -msgid "Too evil (sender)" -msgstr "Zu boshaft (Sender)" - -msgid "Too evil (receiver)" -msgstr "Zu boshaft (Empfänger)" +msgid "Warning level too high (sender)" +msgstr "Warnstufe zu hoch (Absender)" + +msgid "Warning level too high (receiver)" +msgstr "Warnstufe zu hoch (Empfänger)" msgid "User temporarily unavailable" msgstr "Benutzer ist temporär nicht verfügbar" @@ -6282,9 +6278,8 @@ msgid "Camera" msgstr "Kamera" -#, fuzzy msgid "Screen Sharing" -msgstr "Screen Sharing" +msgstr "Gemeinsamer Bildschirm" msgid "Free For Chat" msgstr "Bereit zum Chatten" @@ -6372,7 +6367,6 @@ "versuchen Sie es noch einmal. Wenn Sie es weiterversuchen, müssen Sie sogar " "noch länger warten." -#. client too old #, c-format msgid "The client version you are using is too old. Please upgrade at %s" msgstr "" @@ -6538,20 +6532,26 @@ "überschritten wurde." #, c-format -msgid "You missed %hu message from %s because he/she was too evil." -msgid_plural "You missed %hu messages from %s because he/she was too evil." +msgid "" +"You missed %hu message from %s because his/her warning level is too high." +msgid_plural "" +"You missed %hu messages from %s because his/her warning level is too high." msgstr[0] "" -"Sie haben %hu Nachricht von %s nicht erhalten, da er/sie zu boshaft war." +"Sie haben %hu Nachricht von %s nicht erhalten, da seine/ihre Warnstufe zu " +"hoch ist." msgstr[1] "" -"Sie haben %hu Nachrichten von %s nicht erhalten,/sie zu boshaft war." - -#, c-format -msgid "You missed %hu message from %s because you are too evil." -msgid_plural "You missed %hu messages from %s because you are too evil." +"Sie haben %hu Nachrichten von %s nicht erhalten, da seine/ihre Warnstufe zu " +"hoch ist." + +#, c-format +msgid "You missed %hu message from %s because your warning level is too high." +msgid_plural "" +"You missed %hu messages from %s because your warning level is too high." msgstr[0] "" -"Sie haben %hu Nachricht von %s nicht erhalten, da Sie zu boshaft sind." +"Sie haben %hu Nachricht von %s nicht erhalten, da Ihre Warnstufe zu hoch ist." msgstr[1] "" -"Sie haben %hu Nachrichten von %s nicht erhalten, da Sie zu boshaft sind." +"Sie haben %hu Nachrichten von %s nicht erhalten, da Ihre Warnstufe zu hoch " +"ist." #, c-format msgid "You missed %hu message from %s for an unknown reason." @@ -7333,10 +7333,6 @@ msgid "Unable to login" msgstr "Anmeldung fehlgeschlagen" -#. we didn't successfully connect. tdt->toc_fd is valid here -msgid "Unable to connect." -msgstr "Verbindung nicht möglich." - #, c-format msgid "Unknown-%d" msgstr "Unbekannt-%d" @@ -7362,12 +7358,16 @@ msgstr "<b>Letzte Aktualisierung</b>: %s<br>\n" #, c-format +msgid "<b>Server</b>: %s: %d<br>\n" +msgstr "<b>Server</b>: %s: %d<br>\n" + +#, c-format msgid "<b>Connection Mode</b>: %s<br>\n" msgstr "<b>Verbindungsmodus</b>: %s<br>\n" #, c-format -msgid "<b>Server IP</b>: %s: %d<br>\n" -msgstr "<b>Server-IP</b>: %s: %d<br>\n" +msgid "<b>Real hostname</b>: %s: %d<br>\n" +msgstr "<b>Wirklicher Hostname</b>: %s: %d<br>\n" #, c-format msgid "<b>My Public IP</b>: %s<br>\n" @@ -7419,12 +7419,49 @@ msgid "Connect using TCP" msgstr "Über TCP verbinden" +msgid "Failed to connect server" +msgstr "Verbinden zum Server fehlgeschlagen" + msgid "Socket error" msgstr "Socket-Fehler" +#, c-format +msgid "" +"Lost connection with server:\n" +"%d, %s" +msgstr "" +"Verbindung zum Server verloren:\n" +"%d, %s" + msgid "Unable to read from socket" msgstr "Socket kann nicht gelesen werden" +msgid "Write Error" +msgstr "Schreibfehler" + +msgid "Connection lost" +msgstr "Verbindung verloren" + +msgid "Login failed, no reply" +msgstr "Anmeldung fehlgeschlagen, keine Antwort" + +msgid "Couldn't resolve host" +msgstr "Kann den Hostnamen nicht auflösen" + +msgid "hostname is NULL or port is 0" +msgstr "Hostname ist NULL oder Port ist 0" + +#, c-format +msgid "Connecting server %s, retries %d" +msgstr "Verbinde zu Server %s, %d Wiederholungen" + +#. we didn't successfully connect. tdt->toc_fd is valid here +msgid "Unable to connect." +msgstr "Verbindung nicht möglich." + +msgid "Could not resolve hostname" +msgstr "Konnte den Hostnamen nicht auflösen" + #, c-format msgid "%d has declined the file %s" msgstr "%d hat die Datei %s abgelehnt" @@ -7436,12 +7473,6 @@ msgid "%d canceled the transfer of %s" msgstr "%d hat die Übertragung von %s abgebrochen" -msgid "Connection lost" -msgstr "Verbindung verloren" - -msgid "Login failed, no reply" -msgstr "Anmeldung fehlgeschlagen, keine Antwort" - msgid "Do you want to add this buddy?" msgstr "Möchten Sie diesen Buddy hinzufügen?" @@ -8499,6 +8530,10 @@ msgid "Error loading SILC key pair" msgstr "Fehler beim Laden des SILC-Schlüsselpaares" +#, c-format +msgid "Download %s: %s" +msgstr "Download %s: %s" + msgid "Your Current Mood" msgstr "Ihre momentane Stimmung" @@ -8943,12 +8978,6 @@ msgid "Could not create listen socket" msgstr "Kann Listen-Socket nicht erstellen" -msgid "Couldn't resolve host" -msgstr "Kann den Hostnamen nicht auflösen" - -msgid "Could not resolve hostname" -msgstr "Konnte den Hostnamen nicht auflösen" - msgid "SIP usernames may not contain whitespaces or @ symbols" msgstr "SIP-Benutzernamen dürfen keine Leerzeichen oder @-Symbole enthalten" @@ -9395,9 +9424,6 @@ msgid "%s is trying to send you a group of %d files.\n" msgstr "%s versucht, Ihnen eine Gruppe von %d Dateien zu senden.\n" -msgid "Write Error" -msgstr "Schreibfehler" - msgid "Yahoo! Japan Profile" msgstr "Yahoo!-Japan-Profil" @@ -9760,7 +9786,6 @@ msgid "Stored Image" msgstr "Gespeichertes Bild" -#, fuzzy msgid "Stored Image. (that'll have to do for now)" msgstr "Gespeichertes Bild. (Das muss erstmal reichen)" @@ -10263,9 +10288,6 @@ msgid "/Tools/_Certificates" msgstr "/Werkzeuge/_Zertifikate" -msgid "/Tools/Smile_y" -msgstr "/Werkzeuge/Smile_y" - msgid "/Tools/Plu_gins" msgstr "/Werkzeuge/Plu_gins" @@ -10275,6 +10297,9 @@ msgid "/Tools/Pr_ivacy" msgstr "/Werkzeuge/Pri_vatsphäre" +msgid "/Tools/Smile_y" +msgstr "/Werkzeuge/Smile_y" + msgid "/Tools/_File Transfers" msgstr "/Werkzeuge/_Dateiübertragungen" @@ -10508,6 +10533,12 @@ msgid "Please enter the name of the group to be added." msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll." +msgid "Enable Account" +msgstr "Konten aktivieren" + +msgid "<PurpleMain>/Accounts/Enable Account" +msgstr "<PurpleMain>/Konten/Konto aktivieren" + msgid "<PurpleMain>/Accounts/" msgstr "<PurpleMain>/Konten/" @@ -10520,12 +10551,6 @@ msgid "_Disable" msgstr "_Deaktivieren" -msgid "Enable Account" -msgstr "Konten aktivieren" - -msgid "<PurpleMain>/Accounts/Enable Account" -msgstr "<PurpleMain>/Konten/Konto aktivieren" - msgid "/Tools" msgstr "/Werkzeuge" @@ -11440,9 +11465,8 @@ msgid "Color to draw the name of an action message." msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird." -#, fuzzy msgid "Action Message Name Color for Whispered Message" -msgstr "Farbe des Absendernamens für Aktions-Nachrichten" +msgstr "Farbe des Absendernamens für geflüsterte Aktions-Nachrichten" msgid "Whisper Message Name Color" msgstr "Farbe des Absendernamens für Flüster-Nachrichten" @@ -11517,7 +11541,6 @@ msgid "_Save Image..." msgstr "Bild _speichern..." -#, fuzzy msgid "_Add Custom Smiley..." msgstr "Benutzerdefinierten Smiley _hinzufügen..." @@ -11559,16 +11582,18 @@ msgid "Insert Image" msgstr "Bild einfügen" -msgid "" -"This smiley is disabled because a custom smiley exists for this shortcut." +#, c-format +msgid "" +"This smiley is disabled because a custom smiley exists for this shortcut:\n" +" %s" msgstr "" "Dieser Smiley ist deaktiviert, da ein benutzerdefinierter Smiley für diese " -"Tastenkombination existiert." +"Tastenkombination existiert:\n" +" %s" msgid "Smile!" msgstr "Lächeln!" -#, fuzzy msgid "_Manage custom smileys" msgstr "Benutzerdefinierte Smileys _verwalten"
--- a/po/en_GB.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/en_GB.po Thu Nov 20 21:13:56 2008 +0000 @@ -2911,7 +2911,7 @@ msgstr "Your current password is different from the one that you specified." msgid "Unable to change password. Error occurred.\n" -msgstr "Unable to change password. An error occured.\n" +msgstr "Unable to change password. An error occurred.\n" msgid "Change password for the Gadu-Gadu account" msgstr "Change password for the Gadu-Gadu account" @@ -5270,7 +5270,7 @@ msgstr "Message could not be sent because the user is offline:" msgid "Message could not be sent because a connection error occurred:" -msgstr "Message could not be sent because a connection error occured:" +msgstr "Message could not be sent because a connection error occurred:" msgid "Message could not be sent because we are sending too quickly:" msgstr "Message could not be sent because we are sending too quickly:"
--- a/po/it.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/it.po Thu Nov 20 21:13:56 2008 +0000 @@ -3833,8 +3833,8 @@ msgstr "Impossibile trovare un'installazione di ActiveTCL. Se vuoi usare i plugin TCL, installa ActiveTCL da http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:107 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." -msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging per maggiori informazioni." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." +msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://d.pidgin.im/BonjourWindows per maggiori informazioni." #: ../libpurple/protocols/bonjour/bonjour.c:126 msgid "Unable to listen for incoming IM connections\n"
--- a/po/nb.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/nb.po Thu Nov 20 21:13:56 2008 +0000 @@ -3877,7 +3877,7 @@ msgstr "Kunne ikke finne en ActiveTCL installasjon. Om du ønsker å bruke TCL tillegg, installer ActiveTCL fra http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:101 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." msgstr "" #: ../libpurple/protocols/bonjour/bonjour.c:120
--- a/po/te.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/te.po Thu Nov 20 21:13:56 2008 +0000 @@ -4059,7 +4059,7 @@ msgstr "ActiveTCL ఇన్స్టాలేషన్ ను కనుగొనడంలో అశక్తత. మీరు TCL ప్లగ్ ఇన్లను ఉపయోగించాలనుకుంటే http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:108 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." msgstr "" #: ../libpurple/protocols/bonjour/bonjour.c:127
--- a/po/ur.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/ur.po Thu Nov 20 21:13:56 2008 +0000 @@ -3891,7 +3891,7 @@ msgstr "ایكٹیو TCL ا نسٹالیشن كی جانچ نا ممكن۔ اگر آپ TCL پلگ انس استعمال كرنا چاہتے ہیں ، ایكٹیوTCL انسٹال كروfrom http://www.activestate.com\n" #: ../libpurple/protocols/bonjour/bonjour.c:108 -msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information." +msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information." msgstr "" #: ../libpurple/protocols/bonjour/bonjour.c:127
--- a/po/vi.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/vi.po Thu Nov 20 21:13:56 2008 +0000 @@ -3916,7 +3916,7 @@ "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://" "developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-" "LocalMessaging for more information." -msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging » để tìm chi tiết." +msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://d.pidgin.im/BonjourWindows » để tìm chi tiết." #: ../libpurple/protocols/bonjour/bonjour.c:120 msgid "Unable to listen for incoming IM connections\n"
--- a/po/zh_CN.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/zh_CN.po Thu Nov 20 21:13:56 2008 +0000 @@ -17454,3 +17454,8 @@ msgid "This plugin is useful for debbuging XMPP servers or clients." msgstr "此插件用于调试 XMPP 服务器或客户端。" +#. feel free to not translate this +#: ../pidgin/gtkdialogs.c:77 +msgid "Ka-Hing Cheung" +msgstr "张家兴" +
--- a/po/zh_TW.po Mon Aug 18 17:08:01 2008 +0000 +++ b/po/zh_TW.po Thu Nov 20 21:13:56 2008 +0000 @@ -13684,6 +13684,11 @@ msgid "This plugin is useful for debbuging XMPP servers or clients." msgstr "幫助為 XMPP 伺服器或客戶端進行除錯。" +#. feel free to not translate this +#: ../pidgin/gtkdialogs.c:77 +msgid "Ka-Hing Cheung" +msgstr "張家興" + #~ msgid "_Resume" #~ msgstr "恢復(_R)"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/CAcert_Class3.pem Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGCDCCA/CgAwIBAgIBATANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 +IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB +IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA +Y2FjZXJ0Lm9yZzAeFw0wNTEwMTQwNzM2NTVaFw0zMzAzMjgwNzM2NTVaMFQxFDAS +BgNVBAoTC0NBY2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5v +cmcxHDAaBgNVBAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCrSTURSHzSJn5TlM9Dqd0o10Iqi/OHeBlYfA+e2ol9 +4fvrcpANdKGWZKufoCSZc9riVXbHF3v1BKxGuMO+f2SNEGwk82GcwPKQ+lHm9WkB +Y8MPVuJKQs/iRIwlKKjFeQl9RrmK8+nzNCkIReQcn8uUBByBqBSzmGXEQ+xOgo0J +0b2qW42S0OzekMV/CsLj6+YxWl50PpczWejDAz1gM7/30W9HxM3uYoNSbi4ImqTZ +FRiRpoWSR7CuSOtttyHshRpocjWr//AQXcD0lKdq1TuSfkyQBX6TwSyLpI5idBVx +bgtxA+qvFTia1NIFcm+M+SvrWnIl+TlG43IbPgTDZCciECqKT1inA62+tC4T7V2q +SNfVfdQqe1z6RgRQ5MwOQluM7dvyz/yWk+DbETZUYjQ4jwxgmzuXVjit89Jbi6Bb +6k6WuHzX1aCGcEDTkSm3ojyt9Yy7zxqSiuQ0e8DYbF/pCsLDpyCaWt8sXVJcukfV +m+8kKHA4IC/VfynAskEDaJLM4JzMl0tF7zoQCqtwOpiVcK01seqFK6QcgCExqa5g +eoAmSAC4AcCTY1UikTxW56/bOiXzjzFU6iaLgVn5odFTEcV7nQP2dBHgbbEsPyyG +kZlxmqZ3izRg0RS0LKydr4wQ05/EavhvE/xzWfdmQnQeiuP43NJvmJzLR5iVQAX7 +6QIDAQABo4G/MIG8MA8GA1UdEwEB/wQFMAMBAf8wXQYIKwYBBQUHAQEEUTBPMCMG +CCsGAQUFBzABhhdodHRwOi8vb2NzcC5DQWNlcnQub3JnLzAoBggrBgEFBQcwAoYc +aHR0cDovL3d3dy5DQWNlcnQub3JnL2NhLmNydDBKBgNVHSAEQzBBMD8GCCsGAQQB +gZBKMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuQ0FjZXJ0Lm9yZy9pbmRleC5w +aHA/aWQ9MTAwDQYJKoZIhvcNAQEEBQADggIBAH8IiKHaGlBJ2on7oQhy84r3HsQ6 +tHlbIDCxRd7CXdNlafHCXVRUPIVfuXtCkcKZ/RtRm6tGpaEQU55tiKxzbiwzpvD0 +nuB1wT6IRanhZkP+VlrRekF490DaSjrxC1uluxYG5sLnk7mFTZdPsR44Q4Dvmw2M +77inYACHV30eRBzLI++bPJmdr7UpHEV5FpZNJ23xHGzDwlVks7wU4vOkHx4y/CcV +Bc/dLq4+gmF78CEQGPZE6lM5+dzQmiDgxrvgu1pPxJnIB721vaLbLmINQjRBvP+L +ivVRIqqIMADisNS8vmW61QNXeZvo3MhN+FDtkaVSKKKs+zZYPumUK5FQhxvWXtaM +zPcPEAxSTtAWYeXlCmy/F8dyRlecmPVsYGN6b165Ti/Iubm7aoW8mA3t+T6XhDSU +rgCvoeXnkm5OvfPi2RSLXNLrAWygF6UtEOucekq9ve7O/e0iQKtwOIj1CodqwqsF +YMlIBdpTwd5Ed2qz8zw87YC8pjhKKSRf/lk7myV6VmMAZLldpGJ9VzZPrYPvH5JT +oI53V93lYRE9IwCQTDz6o2CTBKOvNfYOao9PSmCnhQVsRqGP9Md246FZV/dxssRu +FFxtbUFm3xuTsdQAw+7Lzzw9IYCpX2Nl/N3gX6T0K/CFcUHUZyX7GrGXrtaZghNB +0m6lG5kngOcLqagA +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/CAcert_Root.pem Thu Nov 20 21:13:56 2008 +0000 @@ -0,0 +1,41 @@ +-----BEGIN CERTIFICATE----- +MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 +IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB +IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA +Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO +BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi +MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ +ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ +8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 +zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y +fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 +w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc +G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k +epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q +laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ +QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU +fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 +YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w +ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY +gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe +MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 +IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy +dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw +czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 +dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl +aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC +AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg +b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB +ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc +nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg +18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c +gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl +Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY +sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T +SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF +CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum +GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk +zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW +omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD +-----END CERTIFICATE-----