summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Kramlich <grim@reaperworld.com>2008-08-06 02:39:40 +0000
committerGary Kramlich <grim@reaperworld.com>2008-08-06 02:39:40 +0000
commite9d14aba6eb2938e303ee19ea846a9429af68391 (patch)
tree42a22b815c14ab52c550036dfe794130f2fa6b59
parent9b8532e3f95e4ef67ec365c8437548caef1b760c (diff)
parentcb6c2a99732c27035fd55492d1c72e76baa8097e (diff)
downloadpidgin-e9d14aba6eb2938e303ee19ea846a9429af68391.tar.gz
propagate from branch 'im.pidgin.pidgin' (head 0a9e9676f7b59e3e389ea6951ee9433269746808)
to branch 'im.pidgin.soc.2008.themes' (head 6473e06ebc9fc546181082d71febf66abe8fee5d)
-rw-r--r--AUTHORS2
-rw-r--r--COPYRIGHT6
-rw-r--r--ChangeLog40
-rw-r--r--ChangeLog.API7
-rw-r--r--ChangeLog.win328
-rw-r--r--NEWS4
-rw-r--r--configure.ac87
-rw-r--r--doc/cmd-signals.dox29
-rw-r--r--doc/connection-signals.dox1
-rw-r--r--doc/funniest_home_convos.txt5
-rw-r--r--doc/pidgin.1.in4
-rw-r--r--finch/finch.c2
-rw-r--r--finch/gntconv.c60
-rw-r--r--libpurple/cipher.c1
-rw-r--r--libpurple/cmds.c28
-rw-r--r--libpurple/cmds.h20
-rw-r--r--libpurple/conversation.c28
-rw-r--r--libpurple/conversation.h20
-rw-r--r--libpurple/core.c3
-rw-r--r--libpurple/core.h6
-rw-r--r--libpurple/plugin.h5
-rw-r--r--libpurple/plugins/autoaccept.c12
-rwxr-xr-xlibpurple/plugins/perl/Makefile.mingw2
-rw-r--r--libpurple/plugins/perl/common/Makefile.mingw5
-rw-r--r--libpurple/plugins/perl/common/Prefs.xs20
-rw-r--r--libpurple/plugins/perl/perl-common.c5
-rw-r--r--libpurple/plugins/perl/perl-common.h2
-rw-r--r--libpurple/plugins/perl/perl-handlers.c205
-rw-r--r--libpurple/plugins/perl/perl-handlers.h21
-rw-r--r--libpurple/plugins/perl/perl.c70
-rw-r--r--libpurple/plugins/perl/scripts/plugin_pref.pl17
-rw-r--r--libpurple/protocols/bonjour/bonjour.c9
-rw-r--r--libpurple/protocols/bonjour/bonjour_ft.c9
-rw-r--r--libpurple/protocols/bonjour/jabber.c13
-rw-r--r--libpurple/protocols/gg/gg.c7
-rw-r--r--libpurple/protocols/irc/cmds.c25
-rw-r--r--libpurple/protocols/irc/irc.c38
-rw-r--r--libpurple/protocols/irc/irc.h2
-rw-r--r--libpurple/protocols/irc/msgs.c9
-rw-r--r--libpurple/protocols/irc/parse.c8
-rw-r--r--libpurple/protocols/jabber/Makefile.mingw4
-rw-r--r--libpurple/protocols/jabber/disco.c45
-rw-r--r--libpurple/protocols/jabber/google.c10
-rw-r--r--libpurple/protocols/jabber/jabber.c77
-rw-r--r--libpurple/protocols/jabber/jabber.h4
-rw-r--r--libpurple/protocols/jabber/libxmpp.c9
-rw-r--r--libpurple/protocols/jabber/parser.c34
-rw-r--r--libpurple/protocols/jabber/presence.c14
-rw-r--r--libpurple/protocols/jabber/roster.c53
-rw-r--r--libpurple/protocols/jabber/si.c47
-rw-r--r--libpurple/protocols/msn/Makefile.am8
-rw-r--r--libpurple/protocols/msn/Makefile.mingw2
-rw-r--r--libpurple/protocols/msn/cmdproc.c4
-rw-r--r--libpurple/protocols/msn/command.c66
-rw-r--r--libpurple/protocols/msn/contact.c921
-rw-r--r--libpurple/protocols/msn/contact.h395
-rw-r--r--libpurple/protocols/msn/group.h17
-rw-r--r--libpurple/protocols/msn/httpconn.c4
-rw-r--r--libpurple/protocols/msn/msn.c189
-rw-r--r--libpurple/protocols/msn/msn.h64
-rw-r--r--libpurple/protocols/msn/msnutils.c186
-rw-r--r--libpurple/protocols/msn/nexus.c661
-rw-r--r--libpurple/protocols/msn/nexus.h303
-rw-r--r--libpurple/protocols/msn/notification.c642
-rw-r--r--libpurple/protocols/msn/notification.h7
-rw-r--r--libpurple/protocols/msn/oim.c355
-rw-r--r--libpurple/protocols/msn/oim.h66
-rw-r--r--libpurple/protocols/msn/page.c6
-rw-r--r--libpurple/protocols/msn/servconn.c40
-rw-r--r--libpurple/protocols/msn/servconn.h4
-rw-r--r--libpurple/protocols/msn/session.c32
-rw-r--r--libpurple/protocols/msn/session.h14
-rw-r--r--libpurple/protocols/msn/slp.c43
-rw-r--r--libpurple/protocols/msn/slpcall.c7
-rw-r--r--libpurple/protocols/msn/slpcall.h3
-rw-r--r--libpurple/protocols/msn/slplink.c38
-rw-r--r--libpurple/protocols/msn/slplink.h3
-rw-r--r--libpurple/protocols/msn/slpmsg.h2
-rw-r--r--libpurple/protocols/msn/slpsession.c77
-rw-r--r--libpurple/protocols/msn/slpsession.h48
-rw-r--r--libpurple/protocols/msn/soap.c1210
-rw-r--r--libpurple/protocols/msn/soap.h154
-rw-r--r--libpurple/protocols/msn/soap2.c693
-rw-r--r--libpurple/protocols/msn/soap2.h58
-rw-r--r--libpurple/protocols/msn/state.c18
-rw-r--r--libpurple/protocols/msn/switchboard.c72
-rw-r--r--libpurple/protocols/msn/sync.c2
-rw-r--r--libpurple/protocols/msn/user.c133
-rw-r--r--libpurple/protocols/msn/user.h81
-rw-r--r--libpurple/protocols/msn/userlist.c101
-rw-r--r--libpurple/protocols/msnp9/command.c9
-rw-r--r--libpurple/protocols/msnp9/servconn.c12
-rw-r--r--libpurple/protocols/oscar/family_icq.c16
-rw-r--r--libpurple/protocols/oscar/family_locate.c46
-rw-r--r--libpurple/protocols/oscar/flap_connection.c108
-rw-r--r--libpurple/protocols/oscar/oscar.c87
-rw-r--r--libpurple/protocols/oscar/oscar.h35
-rw-r--r--libpurple/protocols/oscar/oscar_data.c13
-rw-r--r--libpurple/protocols/qq/AUTHORS18
-rw-r--r--libpurple/protocols/qq/ChangeLog76
-rw-r--r--libpurple/protocols/qq/Makefile.am10
-rw-r--r--libpurple/protocols/qq/Makefile.mingw7
-rw-r--r--libpurple/protocols/qq/buddy_info.c46
-rw-r--r--libpurple/protocols/qq/buddy_info.h34
-rw-r--r--libpurple/protocols/qq/buddy_list.c472
-rw-r--r--libpurple/protocols/qq/buddy_list.h43
-rw-r--r--libpurple/protocols/qq/buddy_opt.c8
-rw-r--r--libpurple/protocols/qq/buddy_status.c278
-rw-r--r--libpurple/protocols/qq/buddy_status.h62
-rw-r--r--libpurple/protocols/qq/char_conv.c30
-rw-r--r--libpurple/protocols/qq/char_conv.h2
-rw-r--r--libpurple/protocols/qq/crypt.c146
-rw-r--r--libpurple/protocols/qq/crypt.h17
-rw-r--r--libpurple/protocols/qq/file_trans.c32
-rw-r--r--libpurple/protocols/qq/group_conv.c6
-rw-r--r--libpurple/protocols/qq/group_find.c3
-rw-r--r--libpurple/protocols/qq/group_free.c4
-rw-r--r--libpurple/protocols/qq/group_im.c9
-rw-r--r--libpurple/protocols/qq/group_info.c81
-rw-r--r--libpurple/protocols/qq/group_info.h3
-rw-r--r--libpurple/protocols/qq/group_network.c24
-rw-r--r--libpurple/protocols/qq/group_network.h15
-rw-r--r--libpurple/protocols/qq/header_info.c83
-rw-r--r--libpurple/protocols/qq/header_info.h14
-rw-r--r--libpurple/protocols/qq/im.c38
-rw-r--r--libpurple/protocols/qq/keep_alive.c180
-rw-r--r--libpurple/protocols/qq/packet_parse.c12
-rw-r--r--libpurple/protocols/qq/packet_parse.h8
-rw-r--r--libpurple/protocols/qq/qq.c205
-rw-r--r--libpurple/protocols/qq/qq.h36
-rw-r--r--libpurple/protocols/qq/qq_base.c (renamed from libpurple/protocols/qq/login_logout.c)387
-rw-r--r--libpurple/protocols/qq/qq_base.h (renamed from libpurple/protocols/qq/login_logout.h)28
-rw-r--r--libpurple/protocols/qq/qq_network.c503
-rw-r--r--libpurple/protocols/qq/qq_network.h7
-rw-r--r--libpurple/protocols/qq/qq_process.c266
-rw-r--r--libpurple/protocols/qq/qq_process.h (renamed from libpurple/protocols/qq/keep_alive.h)20
-rw-r--r--libpurple/protocols/qq/qq_trans.c354
-rw-r--r--libpurple/protocols/qq/qq_trans.h35
-rw-r--r--libpurple/protocols/qq/send_file.c16
-rw-r--r--libpurple/protocols/qq/send_file.h3
-rw-r--r--libpurple/protocols/qq/sys_msg.c20
-rw-r--r--libpurple/protocols/qq/utils.c52
-rw-r--r--libpurple/protocols/qq/utils.h2
-rw-r--r--libpurple/protocols/silc/silc.c43
-rw-r--r--libpurple/protocols/silc10/silc.c45
-rw-r--r--libpurple/protocols/yahoo/yahoo.c20
-rw-r--r--libpurple/protocols/yahoo/yahoo_picture.c6
-rw-r--r--libpurple/protocols/yahoo/yahoo_profile.c4
-rw-r--r--libpurple/prpl.c104
-rw-r--r--libpurple/prpl.h35
-rwxr-xr-xlibpurple/purple-url-handler61
-rw-r--r--libpurple/server.c82
-rw-r--r--libpurple/server.h4
-rw-r--r--libpurple/util.c18
-rw-r--r--libpurple/util.h8
-rw-r--r--libpurple/win32/global.mak6
-rw-r--r--libpurple/win32/libc_interface.c5
-rw-r--r--libpurple/xmlnode.c87
-rw-r--r--pidgin/gtkaccount.c4
-rw-r--r--pidgin/gtkdialogs.c9
-rw-r--r--pidgin/gtkdocklet.c5
-rw-r--r--pidgin/gtkmain.c2
-rw-r--r--pidgin/gtkroomlist.c73
-rw-r--r--pidgin/gtksmiley.c3
-rw-r--r--pidgin/pidgintooltip.c1
-rw-r--r--pidgin/pixmaps/emblems/16/birthday.pngbin601 -> 1055 bytes
-rw-r--r--pidgin/pixmaps/emblems/16/scalable/birthday.svg629
-rw-r--r--pidgin/pixmaps/emotes/default/24/Makefile.am1
-rw-r--r--pidgin/pixmaps/emotes/default/24/airplane.pngbin940 -> 1058 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/bad.pngbin1054 -> 1232 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/beer.pngbin1382 -> 1532 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/bomb.pngbin1110 -> 1228 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/bowl.pngbin1157 -> 1565 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/boy.pngbin1177 -> 1250 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/brb.pngbin873 -> 879 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/bunny.pngbin0 -> 1694 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/cake.pngbin1114 -> 1660 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/camera.pngbin974 -> 1064 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/car.pngbin1481 -> 1338 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/cat.pngbin1269 -> 1470 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/clock.pngbin1352 -> 1485 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/coffee.pngbin1326 -> 1643 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/coins.pngbin1330 -> 1492 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/console.pngbin1001 -> 1102 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/default.theme.in2
-rw-r--r--pidgin/pixmaps/emotes/default/24/dog.pngbin1497 -> 1520 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/drink.pngbin1125 -> 1215 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/film.pngbin1365 -> 1720 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/girl.pngbin990 -> 1170 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/goat.pngbin1336 -> 1464 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/good.pngbin1011 -> 1153 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/handcuffs.pngbin1393 -> 1674 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/lamp.pngbin1259 -> 1349 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/love-over.pngbin1342 -> 1413 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/love.pngbin1069 -> 1339 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/mail.pngbin881 -> 932 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/mobile.pngbin690 -> 735 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/moon.pngbin1180 -> 1358 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/msn.pngbin1281 -> 1407 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/musical-note.pngbin941 -> 1173 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/phone.pngbin1067 -> 1247 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/pizza.pngbin943 -> 1020 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/plate.pngbin1291 -> 1492 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/present.pngbin975 -> 1242 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/rain.pngbin765 -> 1385 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/rose-dead.pngbin1289 -> 1133 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/rose.pngbin1062 -> 928 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/sheep.pngbin1123 -> 1486 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/soccerball.pngbin1351 -> 1435 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/star.pngbin976 -> 1317 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/sun.pngbin1243 -> 1584 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/thunder.pngbin977 -> 1336 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/turtle.pngbin1078 -> 1415 bytes
-rw-r--r--pidgin/pixmaps/emotes/default/24/umbrella.pngbin1095 -> 1241 bytes
-rw-r--r--pidgin/pixmaps/toolbar/16/emote-select.pngbin884 -> 953 bytes
-rw-r--r--pidgin/pixmaps/toolbar/16/scalable/emote-select.svg702
-rw-r--r--pidgin/pixmaps/tray/22/tray-away.pngbin1148 -> 1048 bytes
-rw-r--r--pidgin/pixmaps/tray/22/tray-busy.pngbin1031 -> 1017 bytes
-rw-r--r--pidgin/pixmaps/tray/22/tray-online.pngbin1094 -> 1046 bytes
-rw-r--r--pidgin/plugins/perl/common/Makefile.mingw5
-rw-r--r--pidgin/win32/nsis/pidgin-installer.nsi15
-rw-r--r--pidgin/win32/winpidgin.c101
-rw-r--r--po/POTFILES.in5
-rw-r--r--po/de.po81
-rw-r--r--po/zh_CN.po5
-rw-r--r--po/zh_TW.po5
-rw-r--r--share/ca-certs/CAcert_Class3.pem35
-rw-r--r--share/ca-certs/CAcert_Root.pem41
-rw-r--r--share/ca-certs/Makefile.am2
229 files changed, 8117 insertions, 6029 deletions
diff --git a/AUTHORS b/AUTHORS
index d955aeab49..4eec1be2f5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -24,6 +24,7 @@ Christopher 'siege' O'Brien - Developer
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 @@ Crazy Patch Writers:
Felipe 'shx' Contreras
Dennis 'EvilDennisR' Ristuccia
Peter 'Fmoo' Ruibal
-Elliott 'QuLogic' Sales de Andrade
Gabriel 'Nix' Schulhof
Jorge 'Masca' Villaseñor
diff --git a/COPYRIGHT b/COPYRIGHT
index cb1991b92d..dc276339c5 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -8,7 +8,6 @@ Saleem Abdulrasool
Dave Ahlswede
Manuel Amador
Matt Amato
-Elliott Sales de Andrade
Geoffrey Antos
Daniel Atallah
Paul Aurich
@@ -49,6 +48,7 @@ Craig Boston
Chris Boyle
Derrick J Brashear
Mauro Sérgio Ferreira Brasil
+Luke Bratch
Matt Brenneke
Jeremy Brooks
Jonathan Brossard
@@ -157,10 +157,12 @@ Ryan C. Gordon
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 @@ Nicolas Lichtmaier
Wesley Lin
Artem Litvinovich
Josh Littlefield
+Daniel Ljungborg
Syd Logan
Lokheed
Norberto Lopes
@@ -338,6 +341,7 @@ Peter Ruibal
Michael Ruprecht
Sam S.
Thanumalayan S.
+Elliott Sales de Andrade
Tomasz Sałaciński <tsalacinski@gmail.com>
Pradyumna Sampath
Arvind Samptur
diff --git a/ChangeLog b/ChangeLog
index d26d262b25..7d8488fd2c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,13 +10,36 @@ version 2.5.0 (??/??/2008):
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
@@ -28,12 +51,25 @@ version 2.5.0 (??/??/2008):
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 (??/??/2008):
+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:
diff --git a/ChangeLog.API b/ChangeLog.API
index 309357241b..5954ac5055 100644
--- a/ChangeLog.API
+++ b/ChangeLog.API
@@ -17,6 +17,10 @@ version 2.5.0 (??/??/2008):
* 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 @@ version 2.5.0 (??/??/2008):
* 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
diff --git a/ChangeLog.win32 b/ChangeLog.win32
index 52f463622a..5f16051d03 100644
--- a/ChangeLog.win32
+++ b/ChangeLog.win32
@@ -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):
diff --git a/NEWS b/NEWS
index c7b58e7aa0..e33c13573a 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/configure.ac b/configure.ac
index 2e630bf460..8e1199ad2d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -336,6 +336,10 @@ else
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 @@ If you want to build only Finch then specify --disable-gtkui when running config
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 @@ Use --without-x if you do not need X11 support.
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 @@ this is required for detecting idle time by mouse and keyboard usage.
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 @@ Use --disable-sm if you do not need session management support.
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 @@ Use --disable-startup-notification if you do not need it.
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 @@ Use --disable-gtkspell if you do not need it.
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 @@ Use --disable-gevolution if you do not need it.
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 @@ if test "x$enable_gst" != "xno"; then
[], [$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 @@ if test "x$enable_meanwhile" = "xyes"; then
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 @@ if test "$ac_avahi_client_libs" != "no"; then
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 @@ AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes")
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 @@ fi
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 @@ fi
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 @@ if test "x$GCC" = "xyes"; then
"-Wmissing-declarations" \
"-Wmissing-noreturn" \
"-Wmissing-prototypes" \
- "-Wnested-externs" \
"-Wpointer-arith" \
"-Wundef" \
; do
@@ -1220,10 +1241,12 @@ if test "x$enable_dbus" = "xyes" ; then
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 @@ dnl Check for NetworkManager.h; if we don't have it, oh well
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 @@ else
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 @@ elif test "x$msg_nss" != "x"; then
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 "$enable_tcl" = yes; then
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 "$enable_tcl" = yes -a "$enable_tk" = yes; then
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\""
diff --git a/doc/cmd-signals.dox b/doc/cmd-signals.dox
new file mode 100644
index 0000000000..f9bfdd83ca
--- /dev/null
+++ b/doc/cmd-signals.dox
@@ -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
+*/
diff --git a/doc/connection-signals.dox b/doc/connection-signals.dox
index 36827de9c2..530be8fa70 100644
--- a/doc/connection-signals.dox
+++ b/doc/connection-signals.dox
@@ -5,6 +5,7 @@
@signal signed-on
@signal signing-off
@signal signed-off
+ @signal connection-error
@endsignals
@see connection.h
diff --git a/doc/funniest_home_convos.txt b/doc/funniest_home_convos.txt
index 4e3dc3ef67..b7440c7294 100644
--- a/doc/funniest_home_convos.txt
+++ b/doc/funniest_home_convos.txt
@@ -510,3 +510,8 @@ not :)
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 ~
diff --git a/doc/pidgin.1.in b/doc/pidgin.1.in
index 00a67e88d7..0fb69d894f 100644
--- a/doc/pidgin.1.in
+++ b/doc/pidgin.1.in
@@ -582,6 +582,8 @@ Pidgin's active developers are:
.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 @@ Our crazy patch writers include:
.br
Peter 'fmoo' Ruibal
.br
- Elliott 'QuLogic' Sales de Andrade
-.br
Gabriel 'Nix' Schulhof
.br
Jorge 'Masca' Villaseñor
diff --git a/finch/finch.c b/finch/finch.c
index 5736ad73ba..41d2755c10 100644
--- a/finch/finch.c
+++ b/finch/finch.c
@@ -63,6 +63,8 @@ static GHashTable *finch_ui_get_info(void)
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;
diff --git a/finch/gntconv.c b/finch/gntconv.c
index 6bf3bfb5c6..0a424b17fa 100644
--- a/finch/gntconv.c
+++ b/finch/gntconv.c
@@ -692,11 +692,48 @@ gained_focus_cb(GntWindow *window, FinchConv *fc)
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 @@ finch_create_conversation(PurpleConversation *conv)
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 @@ finch_destroy_conversation(PurpleConversation *conv)
/* 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 @@ finch_chat_add_users(PurpleConversation *conv, GList *users, gboolean new_arriva
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 @@ void finch_conversation_set_active(PurpleConversation *conv)
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);
diff --git a/libpurple/cipher.c b/libpurple/cipher.c
index e73847ba77..215b66a446 100644
--- a/libpurple/cipher.c
+++ b/libpurple/cipher.c
@@ -2402,7 +2402,6 @@ purple_cipher_context_digest(PurpleCipherContext *context, size_t in_len,
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);
diff --git a/libpurple/cmds.c b/libpurple/cmds.c
index f84d26f99c..79553864fe 100644
--- a/libpurple/cmds.c
+++ b/libpurple/cmds.c
@@ -81,6 +81,8 @@ PurpleCmdId purple_cmd_register(const gchar *cmd, const gchar *args,
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 @@ void purple_cmd_unregister(PurpleCmdId id)
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 @@ GList *purple_cmd_help(PurpleConversation *conv, const gchar *cmd)
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());
+}
+
diff --git a/libpurple/cmds.h b/libpurple/cmds.h
index 1c95ac450c..d6c2ba2795 100644
--- a/libpurple/cmds.h
+++ b/libpurple/cmds.h
@@ -1,6 +1,7 @@
/**
* @file cmds.h Commands API
* @ingroup core
+ * @see @ref cmd-signals
*/
/* Copyright (C) 2003 Timothy Ringenbach <omarvo@hotmail.com>
@@ -221,6 +222,25 @@ GList *purple_cmd_list(PurpleConversation *conv);
*/
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
diff --git a/libpurple/conversation.c b/libpurple/conversation.c
index 9d29dfa6cd..407067c8ae 100644
--- a/libpurple/conversation.c
+++ b/libpurple/conversation.c
@@ -1621,7 +1621,7 @@ purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs
}
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 @@ purple_conv_chat_add_users(PurpleConvChat *chat, GList *users, GList *extra_msgs
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 @@ purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users, const char *re
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 @@ purple_conv_chat_remove_users(PurpleConvChat *chat, GList *users, const char *re
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,
diff --git a/libpurple/conversation.h b/libpurple/conversation.h
index cf8df55a52..c608cbbb26 100644
--- a/libpurple/conversation.h
+++ b/libpurple/conversation.h
@@ -285,11 +285,21 @@ struct _PurpleConvChat
*/
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.
+ */
};
/**
diff --git a/libpurple/core.c b/libpurple/core.c
index 5f962e7049..b345b2778a 100644
--- a/libpurple/core.c
+++ b/libpurple/core.c
@@ -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"
@@ -132,6 +133,7 @@ purple_core_init(const char *ui)
#endif
purple_ciphers_init();
+ purple_cmds_init();
/* Since plugins get probed so early we should probably initialize their
* subsystem right away too.
@@ -235,6 +237,7 @@ purple_core_quit(void)
purple_dbus_uninit();
#endif
+ purple_cmds_uninit();
purple_util_uninit();
purple_signals_uninit();
diff --git a/libpurple/core.h b/libpurple/core.h
index 17c85ec508..1e4c427309 100644
--- a/libpurple/core.h
+++ b/libpurple/core.h
@@ -180,6 +180,12 @@ gboolean purple_core_ensure_single_instance(void);
*
* <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
diff --git a/libpurple/plugin.h b/libpurple/plugin.h
index 259027a73d..52c995dda3 100644
--- a/libpurple/plugin.h
+++ b/libpurple/plugin.h
@@ -199,9 +199,10 @@ struct _PurplePluginAction {
* 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)); \
diff --git a/libpurple/plugins/autoaccept.c b/libpurple/plugins/autoaccept.c
index c75e881ab5..e167ee0429 100644
--- a/libpurple/plugins/autoaccept.c
+++ b/libpurple/plugins/autoaccept.c
@@ -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 @@ file_recv_request_cb(PurpleXfer *xfer, gpointer handle)
{
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 @@ get_plugin_pref_frame(PurplePlugin *plugin)
"(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 @@ init_plugin(PurplePlugin *plugin) {
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);
}
diff --git a/libpurple/plugins/perl/Makefile.mingw b/libpurple/plugins/perl/Makefile.mingw
index 31f94b2040..1768f5d2f3 100755
--- a/libpurple/plugins/perl/Makefile.mingw
+++ b/libpurple/plugins/perl/Makefile.mingw
@@ -47,7 +47,7 @@ LIBS = \
-lws2_32 \
-lintl \
-lpurple \
- -lperl58
+ -lperl510
include $(PIDGIN_COMMON_RULES)
diff --git a/libpurple/plugins/perl/common/Makefile.mingw b/libpurple/plugins/perl/common/Makefile.mingw
index 1a4b8150e7..3107578cbd 100644
--- a/libpurple/plugins/perl/common/Makefile.mingw
+++ b/libpurple/plugins/perl/common/Makefile.mingw
@@ -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 @@ AUTOSPLIT = lib/auto/Purple/autosplit.ix
EXTUTILS ?= C:/perl/lib/ExtUtils
PERL_PLUGIN_TOP := ..
-CFLAGS += -Wno-comment -Wno-unused
-
##
## INCLUDE PATHS
##
@@ -77,7 +76,7 @@ OBJECTS = $(C_FILES:%.c=%.o)
##
## LIBRARIES
##
-LIBS = -lperl58 \
+LIBS = -lperl510 \
-lperl \
-lpurple \
-lglib-2.0
diff --git a/libpurple/plugins/perl/common/Prefs.xs b/libpurple/plugins/perl/common/Prefs.xs
index b1bf4959ba..24669f27fa 100644
--- a/libpurple/plugins/perl/common/Prefs.xs
+++ b/libpurple/plugins/perl/common/Prefs.xs
@@ -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 @@ PPCODE:
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)
diff --git a/libpurple/plugins/perl/perl-common.c b/libpurple/plugins/perl/perl-common.c
index 1ddcfe7c38..0ffef004b1 100644
--- a/libpurple/plugins/perl/perl-common.c
+++ b/libpurple/plugins/perl/perl-common.c
@@ -32,7 +32,10 @@ magic_free_object(pTHX_ SV *sv, MAGIC *mg)
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 *
diff --git a/libpurple/plugins/perl/perl-common.h b/libpurple/plugins/perl/perl-common.h
index 362a85379e..235113e96c 100644
--- a/libpurple/plugins/perl/perl-common.h
+++ b/libpurple/plugins/perl/perl-common.h
@@ -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 _
diff --git a/libpurple/plugins/perl/perl-handlers.c b/libpurple/plugins/perl/perl-handlers.c
index 1ed58500b2..50917ddfa9 100644
--- a/libpurple/plugins/perl/perl-handlers.c
+++ b/libpurple/plugins/perl/perl-handlers.c
@@ -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 @@ purple_perl_plugin_actions(PurplePlugin *plugin, gpointer context)
STRLEN na;
dSP;
- gps = (PurplePerlScript *)plugin->info->extra_info;
+ gps = plugin->info->extra_info;
ENTER;
SAVETMPS;
@@ -131,7 +132,7 @@ purple_perl_gtk_get_plugin_frame(PurplePlugin *plugin)
STRLEN na;
dSP;
- gps = (PurplePerlScript *)plugin->info->extra_info;
+ gps = plugin->info->extra_info;
ENTER;
SAVETMPS;
@@ -212,7 +213,7 @@ destroy_timeout_handler(PurplePerlTimeoutHandler *handler)
{
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 @@ destroy_timeout_handler(PurplePerlTimeoutHandler *handler)
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 @@ destroy_signal_handler(PurplePerlSignalHandler *handler)
static gboolean
perl_timeout_cb(gpointer data)
{
- PurplePerlTimeoutHandler *handler = (PurplePerlTimeoutHandler *)data;
+ PurplePerlTimeoutHandler *handler = data;
gboolean ret = FALSE;
STRLEN na;
@@ -282,7 +283,7 @@ typedef void *DATATYPE;
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 @@ static PurplePerlSignalHandler *
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 @@ purple_perl_timeout_add(PurplePlugin *plugin, int seconds, SV *callback, SV *dat
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 @@ purple_perl_timeout_add(PurplePlugin *plugin, int seconds, SV *callback, SV *dat
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 @@ purple_perl_timeout_remove(guint handle)
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 @@ purple_perl_signal_connect(PurplePlugin *plugin, void *instance,
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 @@ void
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 @@ perl_cmd_cb(PurpleConversation *conv, const gchar *command,
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 @@ purple_perl_cmd_register(PurplePlugin *plugin, const gchar *command,
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 @@ purple_perl_cmd_register(PurplePlugin *plugin, const gchar *command,
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 @@ destroy_cmd_handler(PurplePerlCmdHandler *handler)
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 @@ purple_perl_cmd_clear_for_plugin(PurplePlugin *plugin)
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_perl_cmd_unregister(PurpleCmdId id)
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);
+ }
+}
diff --git a/libpurple/plugins/perl/perl-handlers.h b/libpurple/plugins/perl/perl-handlers.h
index c71bc93136..c0079d2fb9 100644
--- a/libpurple/plugins/perl/perl-handlers.h
+++ b/libpurple/plugins/perl/perl-handlers.h
@@ -15,8 +15,8 @@ typedef struct
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
typedef struct
{
- char *signal;
+ gchar *signal;
SV *callback;
SV *data;
void *instance;
@@ -39,8 +39,17 @@ typedef struct
} 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 @@ PurpleCmdId purple_perl_cmd_register(PurplePlugin *plugin, const gchar *cmd,
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_ */
diff --git a/libpurple/plugins/perl/perl.c b/libpurple/plugins/perl/perl.c
index ae4954c67b..44e9fc3b62 100644
--- a/libpurple/plugins/perl/perl.c
+++ b/libpurple/plugins/perl/perl.c
@@ -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 @@ xs_init(pTHX)
#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 @@ purple_perl_callXS(void (*subaddr)(pTHX_ CV *cv), CV *cv, SV **mark)
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 @@ probe_perl_plugin(PurplePlugin *plugin)
}
}
+ cleanup:
PL_perl_destruct_level = 1;
PERL_SET_CONTEXT(prober);
perl_destruct(prober);
@@ -523,6 +578,7 @@ unload_perl_plugin(PurplePlugin *plugin)
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 @@ static PurplePluginLoaderInfo loader_info =
load_perl_plugin, /**< load */
unload_perl_plugin, /**< unload */
destroy_perl_plugin, /**< destroy */
-
+
/* padding */
NULL,
NULL,
diff --git a/libpurple/plugins/perl/scripts/plugin_pref.pl b/libpurple/plugins/perl/scripts/plugin_pref.pl
index a30b3e1476..fc9bf93b58 100644
--- a/libpurple/plugins/perl/scripts/plugin_pref.pl
+++ b/libpurple/plugins/perl/scripts/plugin_pref.pl
@@ -44,8 +44,8 @@ sub foo {
$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 @@ sub foo {
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 @@ sub plugin_load {
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";
}
diff --git a/libpurple/protocols/bonjour/bonjour.c b/libpurple/protocols/bonjour/bonjour.c
index 5ba66b5109..8b3f567165 100644
--- a/libpurple/protocols/bonjour/bonjour.c
+++ b/libpurple/protocols/bonjour/bonjour.c
@@ -641,7 +641,6 @@ initialize_default_account_values(void)
struct passwd *info;
#endif
const char *fullname = NULL, *splitpoint, *tmp;
- char hostname[255];
gchar *conv = NULL;
#ifndef _WIN32
@@ -691,13 +690,7 @@ initialize_default_account_values(void)
/* 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
diff --git a/libpurple/protocols/bonjour/bonjour_ft.c b/libpurple/protocols/bonjour/bonjour_ft.c
index c49cb1742e..b3c7a342a5 100644
--- a/libpurple/protocols/bonjour/bonjour_ft.c
+++ b/libpurple/protocols/bonjour/bonjour_ft.c
@@ -300,6 +300,8 @@ bonjour_free_xfer(PurpleXfer *xfer)
}
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 @@ bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_me
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 @@ bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_me
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 @@ bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb)
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;*/
}
}
diff --git a/libpurple/protocols/bonjour/jabber.c b/libpurple/protocols/bonjour/jabber.c
index 5afe2678d2..b509a40be5 100644
--- a/libpurple/protocols/bonjour/jabber.c
+++ b/libpurple/protocols/bonjour/jabber.c
@@ -672,7 +672,6 @@ gint
bonjour_jabber_start(BonjourJabber *jdata)
{
struct sockaddr_in my_addr;
- int yes = 1;
int i;
gboolean bind_successful;
@@ -686,16 +685,6 @@ bonjour_jabber_start(BonjourJabber *jdata)
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 @@ bonjour_jabber_start(BonjourJabber *jdata)
bind_successful = TRUE;
break;
}
+
+ purple_debug_info("bonjour", "Unable to bind to port %u.(%s)\n", jdata->port, g_strerror(errno));
jdata->port++;
}
diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c
index a1601a5811..0166e9135b 100644
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -527,9 +527,11 @@ static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer us
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 @@ static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields
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 @@ static void ggp_sr_close_cb(gpointer user_data)
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 @@ static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req)
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 @@ static void ggp_get_info(PurpleConnection *gc, const char *name)
seq = ggp_search_start(gc, form);
ggp_search_add(info->searches, seq, form);
+ purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq);
}
/* }}} */
diff --git a/libpurple/protocols/irc/cmds.c b/libpurple/protocols/irc/cmds.c
index 61454448ca..5a792368c2 100644
--- a/libpurple/protocols/irc/cmds.c
+++ b/libpurple/protocols/irc/cmds.c
@@ -68,6 +68,31 @@ int irc_cmd_away(struct irc_conn *irc, const char *cmd, const char *target, cons
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);
diff --git a/libpurple/protocols/irc/irc.c b/libpurple/protocols/irc/irc.c
index 10407c560b..707a7389f8 100644
--- a/libpurple/protocols/irc/irc.c
+++ b/libpurple/protocols/irc/irc.c
@@ -360,11 +360,11 @@ static void irc_login(PurpleAccount *account)
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 @@ static gboolean do_login(PurpleConnection *gc) {
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 @@ static gboolean do_login(PurpleConnection *gc) {
}
}
- 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 @@ static void _init_plugin(PurplePlugin *plugin)
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);
diff --git a/libpurple/protocols/irc/irc.h b/libpurple/protocols/irc/irc.h
index 18790c4932..7d512e30e7 100644
--- a/libpurple/protocols/irc/irc.h
+++ b/libpurple/protocols/irc/irc.h
@@ -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 @@ void irc_cmd_table_build(struct irc_conn *irc);
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);
diff --git a/libpurple/protocols/irc/msgs.c b/libpurple/protocols/irc/msgs.c
index 502c3ee938..f22bda64ac 100644
--- a/libpurple/protocols/irc/msgs.c
+++ b/libpurple/protocols/irc/msgs.c
@@ -215,10 +215,8 @@ void irc_msg_ban(struct irc_conn *irc, const char *name, const char *from, char
/* 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 @@ void irc_msg_nickused(struct irc_conn *irc, const char *name, const char *from,
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);
diff --git a/libpurple/protocols/irc/parse.c b/libpurple/protocols/irc/parse.c
index d2481d7435..9030d269bd 100644
--- a/libpurple/protocols/irc/parse.c
+++ b/libpurple/protocols/irc/parse.c
@@ -123,6 +123,7 @@ static struct _irc_user_cmd {
} _irc_cmds[] = {
{ "action", ":", irc_cmd_ctcp_action, N_("action &lt;action to perform&gt;: 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 &lt;nick1&gt; [nick2] ...: Remove channel operator status from someone. You must be a channel operator to do this.") },
{ "devoice", ":", irc_cmd_op, N_("devoice &lt;nick1&gt; [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 @@ static char *irc_recv_convert(struct irc_conn *irc, const char *string)
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 @@ static char *irc_recv_convert(struct irc_conn *irc, const char *string)
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 == ' ')
diff --git a/libpurple/protocols/jabber/Makefile.mingw b/libpurple/protocols/jabber/Makefile.mingw
index 5540a1c3ff..9a46c6afff 100644
--- a/libpurple/protocols/jabber/Makefile.mingw
+++ b/libpurple/protocols/jabber/Makefile.mingw
@@ -88,10 +88,6 @@ INCLUDE_PATHS += -I$(CYRUS_SASL_TOP)/include
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 = \
diff --git a/libpurple/protocols/jabber/disco.c b/libpurple/protocols/jabber/disco.c
index 7cfbcd6f2c..e0fb8e347e 100644
--- a/libpurple/protocols/jabber/disco.c
+++ b/libpurple/protocols/jabber/disco.c
@@ -73,6 +73,15 @@ jabber_disco_bytestream_server_cb(JabberStream *js, xmlnode *packet, gpointer da
"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);
+ }
}
@@ -329,6 +338,7 @@ void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) {
static void
jabber_disco_finish_server_info_result_cb(JabberStream *js)
{
+ const char *ft_proxies;
jabber_vcard_fetch_mine(js);
@@ -339,11 +349,44 @@ jabber_disco_finish_server_info_result_cb(JabberStream *js)
/* 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
diff --git a/libpurple/protocols/jabber/google.c b/libpurple/protocols/jabber/google.c
index 115ab768e0..b509096e5f 100644
--- a/libpurple/protocols/jabber/google.c
+++ b/libpurple/protocols/jabber/google.c
@@ -314,11 +314,6 @@ void jabber_google_roster_add_deny(PurpleConnection *gc, const char *who)
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 @@ void jabber_google_roster_rem_deny(PurpleConnection *gc, const char *who)
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");
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
index 27691f81d9..c1d2bb05ef 100644
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -63,6 +63,7 @@ static PurplePlugin *my_protocol = NULL;
GList *jabber_features = NULL;
static void jabber_unregister_account_cb(JabberStream *js);
+static void try_srv_connect(JabberStream *js);
static void jabber_stream_init(JabberStream *js)
{
@@ -520,15 +521,23 @@ jabber_login_callback(gpointer data, gint source, const gchar *error)
JabberStream *js = gc->proto_data;
if (source < 0) {
- gchar *tmp;
- tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
- error);
- purple_connection_error_reason (gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
- g_free(tmp);
+ 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 {
+ gchar *tmp;
+ tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"),
+ error);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
+ g_free(tmp);
+ }
return;
}
+ g_free(js->srv_rec);
+ js->srv_rec = NULL;
+
js->fd = source;
if(js->state == JABBER_STREAM_CONNECTING)
@@ -563,37 +572,62 @@ static void tls_init(JabberStream *js)
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 srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data)
+static void try_srv_connect(JabberStream *js)
{
- 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);
+}
- js = data;
+static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer 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);
}
}
@@ -675,7 +709,7 @@ jabber_login(PurpleAccount *account)
* 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);
@@ -1156,7 +1190,7 @@ void jabber_register_account(PurpleAccount *account)
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",
@@ -1327,7 +1361,10 @@ void jabber_close(PurpleConnection *gc)
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;
diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h
index 81cc8e0d03..326de16fc8 100644
--- a/libpurple/protocols/jabber/jabber.h
+++ b/libpurple/protocols/jabber/jabber.h
@@ -201,6 +201,10 @@ struct _JabberStream
/* A purple timeout tag for the keepalive */
int keepalive_timeout;
+
+ PurpleSrvResponse *srv_rec;
+ guint srv_rec_idx;
+ guint max_srv_rec_idx;
};
typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace);
diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c
index 08501e74de..dd81c2c0a9 100644
--- a/libpurple/protocols/jabber/libxmpp.c
+++ b/libpurple/protocols/jabber/libxmpp.c
@@ -194,6 +194,7 @@ init_plugin(PurplePlugin *plugin)
{
#ifdef HAVE_CYRUS_SASL
#ifdef _WIN32
+ UINT old_error_mode;
gchar *sasldir;
#endif
int ret;
@@ -236,7 +237,7 @@ init_plugin(PurplePlugin *plugin)
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 @@ init_plugin(PurplePlugin *plugin)
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();
diff --git a/libpurple/protocols/jabber/parser.c b/libpurple/protocols/jabber/parser.c
index 7b5dd10e51..8504d71aaa 100644
--- a/libpurple/protocols/jabber/parser.c
+++ b/libpurple/protocols/jabber/parser.c
@@ -132,6 +132,18 @@ jabber_parser_element_text_libxml(void *user_data, const xmlChar *text, int text
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 @@ static xmlSAXHandler jabber_parser_libxml = {
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_free(JabberStream *js) {
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"));
+ }
}
}
diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c
index 53bc34969c..d5af230b4e 100644
--- a/libpurple/protocols/jabber/presence.c
+++ b/libpurple/protocols/jabber/presence.c
@@ -141,6 +141,11 @@ void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
/* 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 @@ void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
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 @@ void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
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)) {
diff --git a/libpurple/protocols/jabber/roster.c b/libpurple/protocols/jabber/roster.c
index 7d08e026f0..6390c922f9 100644
--- a/libpurple/protocols/jabber/roster.c
+++ b/libpurple/protocols/jabber/roster.c
@@ -31,6 +31,36 @@
#include <string.h>
+/*
+ * This boolean was added to eliminate a heinous bug where we would
+ * get into a loop with the server and move a buddy back and forth
+ * from one group to another.
+ *
+ * The sequence goes something like this:
+ * 1. Our resource and another resource both approve an authorization
+ * request at the exact same time. We put the buddy in group A and
+ * the other resource put the buddy in group B.
+ * 2. The server receives the roster add for group B and sends us a
+ * roster push.
+ * 3. We receive this roster push and modify our local blist. This
+ * triggers us to send a roster add for group B.
+ * 4. The server recieves our earlier roster add for group A and sends
+ * us a roster push.
+ * 5. We receive this roster push and modify our local blist. This
+ * triggers us to send a roster add for group A.
+ * 6. The server receives our earlier roster add for group B and sends
+ * us a roster push.
+ * (repeat steps 3 through 6 ad infinitum)
+ *
+ * This boolean is used to short-circuit the sending of a roster add
+ * when we receive a roster push.
+ *
+ * See these bug reports:
+ * http://trac.adiumx.com/ticket/8834
+ * http://developer.pidgin.im/ticket/5484
+ * http://developer.pidgin.im/ticket/6188
+ */
+static gboolean parsing_from_server = FALSE;
void jabber_roster_request(JabberStream *js)
{
@@ -169,6 +199,8 @@ void jabber_roster_parse(JabberStream *js, xmlnode *packet)
if(!query)
return;
+ parsing_from_server = TRUE;
+
for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
{
const char *jid, *name, *subscription, *ask;
@@ -251,6 +283,8 @@ void jabber_roster_parse(JabberStream *js, xmlnode *packet)
}
}
+ parsing_from_server = FALSE;
+
/* if we're just now parsing the roster for the first time,
* then now would be the time to send our initial presence */
if(!js->roster_parsed) {
@@ -269,6 +303,9 @@ static void jabber_roster_update(JabberStream *js, const char *name,
JabberIq *iq;
xmlnode *query, *item, *group;
+ if (parsing_from_server)
+ return;
+
if(!(b = purple_find_buddy(js->gc->account, name)))
return;
@@ -316,7 +353,6 @@ void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
{
JabberStream *js = gc->proto_data;
char *who;
- GSList *groups = NULL;
JabberBuddy *jb;
JabberBuddyResource *jbr;
char *my_bare_jid;
@@ -329,20 +365,7 @@ void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
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)) {
diff --git a/libpurple/protocols/jabber/si.c b/libpurple/protocols/jabber/si.c
index 2651ae68ca..aa6a6b9703 100644
--- a/libpurple/protocols/jabber/si.c
+++ b/libpurple/protocols/jabber/si.c
@@ -721,7 +721,6 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
JabberSIXfer *jsx;
JabberIq *iq;
xmlnode *query, *streamhost;
- const char *ft_proxies;
char port[6];
GList *tmp;
JabberBytestreamsStreamhost *sh, *sh2;
@@ -785,52 +784,6 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
jabber_si_xfer_bytestreams_send_connected_cb, xfer);
}
- /* 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;
-
- 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\n",
- 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;
-
- 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, "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);
- }
-
for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
sh = tmp->data;
diff --git a/libpurple/protocols/msn/Makefile.am b/libpurple/protocols/msn/Makefile.am
index a4305fedf2..39ecdb3200 100644
--- a/libpurple/protocols/msn/Makefile.am
+++ b/libpurple/protocols/msn/Makefile.am
@@ -48,12 +48,8 @@ MSNSOURCES = \
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 \
diff --git a/libpurple/protocols/msn/Makefile.mingw b/libpurple/protocols/msn/Makefile.mingw
index bb8695c6d0..fe75c1cb68 100644
--- a/libpurple/protocols/msn/Makefile.mingw
+++ b/libpurple/protocols/msn/Makefile.mingw
@@ -59,9 +59,7 @@ C_SRC = cmdproc.c \
slpcall.c \
slplink.c \
slpmsg.c \
- slpsession.c \
soap.c\
- soap2.c\
state.c \
switchboard.c \
sync.c \
diff --git a/libpurple/protocols/msn/cmdproc.c b/libpurple/protocols/msn/cmdproc.c
index 3c91d64b32..5cb8b9eaf1 100644
--- a/libpurple/protocols/msn/cmdproc.c
+++ b/libpurple/protocols/msn/cmdproc.c
@@ -263,7 +263,7 @@ msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
if (cb == NULL && trans != NULL)
{
- cmd->trans = trans;
-
if (trans->callbacks != NULL)
cb = g_hash_table_lookup(trans->callbacks, cmd->command);
}
diff --git a/libpurple/protocols/msn/command.c b/libpurple/protocols/msn/command.c
index 16efcb40e0..1ef24a9acf 100644
--- a/libpurple/protocols/msn/command.c
+++ b/libpurple/protocols/msn/command.c
@@ -36,53 +36,6 @@ is_num(const char *str)
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 @@ msn_command_from_string(const char *string)
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;
diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c
index 9470171cab..b05731524c 100644
--- a/libpurple/protocols/msn/contact.c
+++ b/libpurple/protocols/msn/contact.c
@@ -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,37 +50,34 @@ const char *MsnMemberRole[] =
};
typedef struct {
- MsnContact *contact;
+ MsnSession *session;
MsnSoapPartnerScenario which;
} GetContactListCbData;
-/* new a contact */
-MsnContact *
-msn_contact_new(MsnSession *session)
+MsnCallbackState *
+msn_callback_state_new(MsnSession *session)
{
- MsnContact *contact;
-
- contact = g_new0(MsnContact, 1);
- contact->session = session;
+ MsnCallbackState *state = g_new0(MsnCallbackState, 1);
- return contact;
-}
+ state->session = session;
-/* destroy the contact */
-void
-msn_contact_destroy(MsnContact *contact)
-{
- g_free(contact);
+ return state;
}
MsnCallbackState *
-msn_callback_state_new(MsnSession *session)
+msn_callback_state_dup(MsnCallbackState *state)
{
- MsnCallbackState *state = g_new0(MsnCallbackState, 1);
+ MsnCallbackState *new_state = g_new0(MsnCallbackState, 1);
- state->session = session;
+ 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 state;
+ return new_state;
}
void
@@ -93,6 +91,7 @@ msn_callback_state_free(MsnCallbackState *state)
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 @@ msn_get_memberrole(const char *role)
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);
-
- purple_debug_info("msnab","Creating an Address Book.\n");
+ 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");
- body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
+ body = g_markup_printf_escaped(MSN_ADD_ADDRESSBOOK_TEMPLATE,
+ msn_nexus_get_token_str(session->nexus, MSN_AUTH_CONTACTS),
+ 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 @@ msn_parse_each_member(MsnSession *session, xmlnode *member, const char *node,
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 @@ msn_parse_each_service(MsnSession *session, xmlnode *service)
{
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 @@ msn_parse_each_service(MsnSession *session, xmlnode *service)
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 @@ msn_parse_each_service(MsnSession *session, xmlnode *service)
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 @@ msn_parse_each_service(MsnSession *session, xmlnode *service)
/*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 @@ msn_parse_contact_list(MsnContact *contact, xmlnode *node)
*
* 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 @@ msn_parse_contact_list(MsnContact *contact, xmlnode *node)
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 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
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 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
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 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
/* 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 @@ msn_get_contact_list_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
/*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 @@ msn_get_contact_list(MsnContact * contact,
}
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 @@ msn_parse_addressbook_groups(MsnContact *contact, xmlnode *node)
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 @@ msn_parse_addressbook_mobile(xmlnode *contactInfo, char **inout_mobile_number)
}
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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
}
/* 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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
/*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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
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_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
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 @@ msn_parse_addressbook_contacts(MsnContact *contact, xmlnode *node)
}
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 @@ msn_parse_addressbook(MsnContact * contact, xmlnode *node)
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 @@ msn_parse_addressbook(MsnContact * contact, xmlnode *node)
/*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 @@ msn_parse_addressbook(MsnContact * contact, xmlnode *node)
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 @@ msn_get_address_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
/*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 @@ msn_get_address_book(MsnContact *contact,
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 @@ msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
MsnCallbackState *state = data;
MsnSession *session = state->session;
- g_return_if_fail(session != NULL);
-
- if (resp != NULL) {
- MsnUserList *userlist = session->userlist;
- MsnUser *user;
+ MsnUserList *userlist;
+ MsnUser *user;
- purple_debug_info("MSNCL","Contact added successfully\n");
+ g_return_if_fail(session != NULL);
- // 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);
- }
+ userlist = session->userlist;
- msn_notification_send_fqy(session, state->who);
+ purple_debug_info("msn", "Contact added successfully\n");
- 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 @@ msn_add_contact(MsnContact *contact, MsnCallbackState *state, const char *passpo
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 @@ msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
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 (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 (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);
+ }
- 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 (state->action & MSN_ADD_BUDDY) {
+ MsnUser *user = msn_userlist_find_user(userlist, 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 ( !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 (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 @@ msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
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 @@ msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
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 @@ msn_delete_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
gpointer data)
{
MsnCallbackState *state = data;
+ MsnUserList *userlist = state->session->userlist;
+ MsnUser *user = msn_userlist_find_user_with_id(userlist, state->uid);
- 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");
+ purple_debug_info("msn", "Delete contact successful\n");
- if (user != NULL) {
- msn_userlist_remove_user(userlist, user);
- }
+ 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 @@ msn_delete_contact(MsnContact *contact, const char *contactId)
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 @@ msn_del_contact_from_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
{
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 @@ msn_del_contact_from_group(MsnContact *contact, const char *passport, const char
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 @@ msn_del_contact_from_group(MsnContact *contact, const char *passport, const char
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 @@ msn_del_contact_from_group(MsnContact *contact, const char *passport, const char
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 @@ static void
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 CL","Update contact information with new friendly name: %s\n", nickname);
+ 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");
+
+ 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;
+
+ case MSN_UPDATE_ALIAS:
+ annotations = xmlnode_new_child(contact_info, "annotations");
+ xmlnode_insert_data(changes, "Annotation ", -1);
+
+ 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();
+ }
- escaped_nickname = g_markup_escape_text(nickname, -1);
- body = g_strdup_printf(MSN_CONTACT_UPDATE_TEMPLATE, escaped_nickname);
- 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 = msn_callback_state_new(session);
- g_free(escaped_nickname);
- g_free(body);
+ 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);
+
+ 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 @@ msn_del_contact_from_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
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 @@ msn_add_contact_to_list_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
g_return_if_fail(state != NULL);
g_return_if_fail(state->session != NULL);
- g_return_if_fail(state->session->contact != NULL);
- 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);
+ purple_debug_info("msn", "Contact %s added successfully to %s list on server!\n", state->who, MsnMemberRole[state->list_id]);
- if (user != NULL) {
- msn_user_set_op(user, MSN_LIST_RL_OP);
- }
+ if (state->list_id == MSN_LIST_RL) {
+ MsnUser *user = msn_userlist_find_user(state->session->userlist, state->who);
- 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 @@ msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
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 @@ msn_add_contact_to_list(MsnContact *contact, MsnCallbackState *state,
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 @@ static void
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);
- if (resp == NULL) {
- msn_callback_state_free(state);
- return;
+ session = state->session;
+ userlist = session->userlist;
+
+ 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_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_RENAME_GROUP) {
- msn_userlist_rename_group_id(session->userlist,
- state->guid,
- state->new_group_name);
- }
+ if (guid_node) {
+ char *guid = xmlnode_get_data(guid_node);
- 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);
-
- /* 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);
-
- 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",
+ /* 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) {
+ /* 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;
+ 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_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 @@ void
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 @@ msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_na
/* 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 @@ msn_del_group(MsnSession *session, const gchar *group_name)
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 @@ msn_del_group(MsnSession *session, const gchar *group_name)
* 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 @@ msn_del_group(MsnSession *session, const gchar *group_name)
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 @@ msn_contact_rename_group(MsnSession *session, const char *old_group_name, const
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_contact_rename_group(MsnSession *session, const char *old_group_name, const
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);
}
diff --git a/libpurple/protocols/msn/contact.h b/libpurple/protocols/msn/contact.h
index ed1dca2c3e..45fccff653 100644
--- a/libpurple/protocols/msn/contact.h
+++ b/libpurple/protocols/msn/contact.h
@@ -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_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_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_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>" 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: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>"\
@@ -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>"
+#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>" 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>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>"
+#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 Nickname */
+/* 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: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>"\
@@ -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 @@ struct _MsnCallbackState
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 @@ typedef enum
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_list_id(MsnCallbackState *state, MsnListId list_id);
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 @@ void msn_contact_rename_group(MsnSession *session, const char *old_group_name,
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_ */
diff --git a/libpurple/protocols/msn/group.h b/libpurple/protocols/msn/group.h
index 0884ad059f..0d1510474b 100644
--- a/libpurple/protocols/msn/group.h
+++ b/libpurple/protocols/msn/group.h
@@ -30,16 +30,8 @@ typedef struct _MsnGroup MsnGroup;
#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 @@ typedef struct _MsnGroup MsnGroup;
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 @@ char* msn_group_get_id(const MsnGroup *group);
* @return The name.
*/
const char *msn_group_get_name(const MsnGroup *group);
+
#endif /* _MSN_GROUP_H_ */
+
diff --git a/libpurple/protocols/msn/httpconn.c b/libpurple/protocols/msn/httpconn.c
index 94bfcae937..624186cf07 100644
--- a/libpurple/protocols/msn/httpconn.c
+++ b/libpurple/protocols/msn/httpconn.c
@@ -381,6 +381,7 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
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 @@ msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t body_len)
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",
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index ac9b5176b0..4f35d511ca 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -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 @@ msn_show_set_mobile_pages(PurplePluginAction *action)
_("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 @@ msn_show_hotmail_inbox(PurplePluginAction *action)
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 @@ static gboolean
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 @@ msn_list_icon(PurpleAccount *a, PurpleBuddy *b)
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 @@ msn_status_types(PurpleAccount *account)
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_actions(PurplePlugin *plugin, gpointer context)
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));
+/* 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
- 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);
- }
+ 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 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
{
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_send_im(PurpleConnection *gc, const char *who, const char *message,
}
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 @@ msn_send_im(PurpleConnection *gc, const char *who, const char *message,
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_send_im(PurpleConnection *gc, const char *who, const char *message,
}
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 @@ fake_userlist_add_buddy(MsnUserList *userlist,
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 @@ msn_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
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_add_permit(PurpleConnection *gc, const char *who)
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_add_deny(PurpleConnection *gc, const char *who)
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_rem_permit(PurpleConnection *gc, const char *who)
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_rem_deny(PurpleConnection *gc, const char *who)
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 @@ msn_keepalive(PurpleConnection *gc)
}
}
+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 @@ msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
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 @@ msn_get_photo_url(const char *url_text)
}
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 @@ msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data,
#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 @@ static PurplePluginProtocolInfo prpl_info =
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 @@ static PurplePluginProtocolInfo prpl_info =
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 @@ init_plugin(PurplePlugin *plugin)
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);
diff --git a/libpurple/protocols/msn/msn.h b/libpurple/protocols/msn/msn.h
index fb587e0eb2..debe079c51 100644
--- a/libpurple/protocols/msn/msn.h
+++ b/libpurple/protocols/msn/msn.h
@@ -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
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 @@ typedef enum
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);
diff --git a/libpurple/protocols/msn/msnutils.c b/libpurple/protocols/msn/msnutils.c
index 76b53e93a4..d02fa63f4e 100644
--- a/libpurple/protocols/msn/msnutils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -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 @@ msn_parse_socket(const char *str, char **ret_host, int *ret_port)
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];
-
- long long nHigh=0, nLow=0;
-
- int i;
-
- /* 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, (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);
-
- /* 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;
- }
+ 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 len;
+ int i;
+
+ /* 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]);
+
+ /* & 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';
+ /* 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;
+ }
- /* split into integers */
- chlStringParts = (unsigned int *)buf;
+ /* split into integers */
+ chlStringParts = (unsigned int *)buf;
- /* this is magic */
- for (i=0; i<(strlen(buf)/4)-1; i+=2){
- long long temp;
+ /* this is magic */
+ for (i = 0; i < (strlen(buf) / 4); i += 2) {
+ long long temp;
- chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]);
- chlStringParts[i+1] = GUINT_TO_LE(chlStringParts[i+1]);
+ chlStringParts[i] = GUINT_TO_LE(chlStringParts[i]);
+ chlStringParts[i + 1] = GUINT_TO_LE(chlStringParts[i + 1]);
- 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;
+ 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]);
+ newHashParts[0] ^= nLow;
+ newHashParts[1] ^= nHigh;
+ newHashParts[2] ^= nLow;
+ newHashParts[3] ^= nHigh;
- /* make a string of the parts */
- newHash = (unsigned char *)newHashParts;
+ /* adjust endianness */
+ for(i = 0; i < 4; i++)
+ newHashParts[i] = GUINT_TO_LE(newHashParts[i]);
- /* 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];
- }
+ /* 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';
}
+
diff --git a/libpurple/protocols/msn/nexus.c b/libpurple/protocols/msn/nexus.c
index 56293ebd8b..a016e5a33d 100644
--- a/libpurple/protocols/msn/nexus.c
+++ b/libpurple/protocols/msn/nexus.c
@@ -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 @@ MsnNexus *
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 @@ msn_nexus_new(MsnSession *session)
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->challenge_data_str);
+ 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;
+
+ 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
**************************************************************************/
-static void
-nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
+/* 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)
{
- MsnNexus *nexus = data;
- MsnSession *session = nexus->session;
- xmlnode *node;
+ return TRUE;
+}
+#endif
- if (resp == NULL) {
- msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect"));
- return;
+
+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? */
}
- node = msn_soap_xml_get(resp->xml, "Body/"
- "RequestSecurityTokenResponseCollection/RequestSecurityTokenResponse");
+ token_str = xmlnode_get_data(token);
+ if (token_str == NULL)
+ return FALSE;
- for (; node; node = node->next) {
- xmlnode *token = msn_soap_xml_get(node,
- "RequestedSecurityToken/BinarySecurityToken");
+#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
- if (token) {
- char *token_str = xmlnode_get_data(token);
- char **elems, **cur, **tokens;
- char *msn_twn_t, *msn_twn_p, *cert_str;
+ elems = g_strsplit(token_str, "&", 0);
- if (token_str == NULL) continue;
+ 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;
+}
- elems = g_strsplit(token_str, "&", 0);
+static gboolean
+nexus_parse_collection(MsnNexus *nexus, int id, xmlnode *collection)
+{
+ xmlnode *node;
+ gboolean result;
- 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);
- }
+ node = xmlnode_get_child(collection, "RequestSecurityTokenResponse");
- g_free(token_str);
- g_strfreev(elems);
+ if (!node)
+ return FALSE;
- msn_twn_t = g_hash_table_lookup(nexus->challenge_data, "t");
- msn_twn_p = g_hash_table_lookup(nexus->challenge_data, "p");
+ result = TRUE;
+ for (; node && result; node = node->next) {
+ xmlnode *endpoint = xmlnode_get_child(node, "AppliesTo/EndpointReference/Address");
+ char *address = xmlnode_get_data(endpoint);
- /*setup the t and p parameter for session*/
- g_free(session->passport_info.t);
- session->passport_info.t = g_strdup(msn_twn_t);
+ 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");
- g_free(session->passport_info.p);
- session->passport_info.p = g_strdup(msn_twn_p);
+ nexus->cipher = xmlnode_get_data(cipher);
+ data = xmlnode_get_data(secret);
+ nexus->secret = (char *)purple_base64_decode(data, NULL);
+ g_free(data);
- cert_str = g_strdup_printf("t=%s&p=%s",msn_twn_t,msn_twn_p);
- msn_got_login_params(session, cert_str);
+ } else {
+ result = nexus_parse_token(nexus, id, node);
+ }
+ g_free(address);
+ }
- purple_debug_info("MSN Nexus","Close nexus connection!\n");
- g_free(cert_str);
- msn_nexus_destroy(nexus);
- session->nexus = NULL;
+ return result;
+}
- return;
- }
+static void
+nexus_got_response_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
+{
+ MsnNexus *nexus = data;
+ MsnSession *session = nexus->session;
+ const char *ticket;
+ char *response;
+
+ if (resp == NULL) {
+ msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Windows Live ID authentication:Unable to connect"));
+ return;
}
- /* we must have failed! */
- msn_session_set_error(session, MSN_ERROR_AUTH, _("Windows Live ID authentication: cannot find authenticate token in server response"));
+ 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;
+ }
+
+ 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 @@ void
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);
-
- 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");
-
- /*
- * 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;
+ 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);
+}
+
+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;
+
+#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
-#ifdef NEXUS_LOGIN_TWN
- challenge_str = g_strdup_printf(
- "lc=%s&amp;id=%s&amp;tw=%s&amp;fs=%s&amp;ru=%s&amp;ct=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s&amp;tpf=%s\r\n",
- lc,id,tw,fs,ru,ct,kpp,kv,ver,rn,tpf
- );
+ 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);
+ }
- /*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&amp;tw=%s&amp;fs=%s&amp;kpp=%s&amp;kv=%s&amp;ver=%s&amp;rn=%s",
- id,tw,fs,kpp,kv,ver,rn
- );
- rst2_str = g_strdup_printf(
- "fs=%s&amp;id=%s&amp;kv=%s&amp;rn=%s&amp;tw=%s&amp;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);
+ if (ud->cb)
+ purple_timeout_add(0, ud->cb, ud->data);
+
+ 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);
- 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);
+ return buf;
}
diff --git a/libpurple/protocols/msn/nexus.h b/libpurple/protocols/msn/nexus.h
index 02bccaf854..c168be114e 100644
--- a/libpurple/protocols/msn/nexus.h
+++ b/libpurple/protocols/msn/nexus.h
@@ -24,127 +24,210 @@
#ifndef _MSN_NEXUS_H_
#define _MSN_NEXUS_H_
-#include "soap.h"
-
-/*#define MSN_TWN_SERVER "loginnet.passport.com"*/
-#define MSN_TWN_SERVER "login.live.com"
-
-#define TWN_START_TOKEN "<wsse:BinarySecurityToken Id=\"PPToken1\">"
-#define TWN_END_TOKEN "</wsse:BinarySecurityToken>"
-
-#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>"\
+/* 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_SSO_SERVER "login.live.com"
+#define SSO_POST_URL "/RST.srf"
+
+#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 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>"
-
-#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>"\
+ "</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 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_ */
+
diff --git a/libpurple/protocols/msn/notification.c b/libpurple/protocols/msn/notification.c
index 0fc7560390..a2a1dfed45 100644
--- a/libpurple/protocols/msn/notification.c
+++ b/libpurple/protocols/msn/notification.c
@@ -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 @@ connect_cb(MsnServConn *servconn)
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 @@ msn_notification_connect(MsnNotification *notification, const char *host, int po
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 @@ group_error_helper(MsnSession *session, const char *msg, const char *group_id, i
**************************************************************************/
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_got_login_params(MsnSession *session, const char *login_params)
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 @@ cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
/*
- * 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 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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 @@ msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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 @@ uum_send_msg(MsnSession *session,MsnMessage *msg)
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 @@ uum_send_msg(MsnSession *session,MsnMessage *msg)
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 @@ ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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 @@ ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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 @@ ubm_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
}
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 @@ chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
**************************************************************************/
/* 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 @@ msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, M
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 @@ msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport, M
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 @@ static void
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 @@ msn_notification_dump_contact(MsnSession *session)
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 @@ adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
{
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 @@ adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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 @@ adl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ adl_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
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 @@ static void
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(MsnCmdProc *cmdproc, MsnCommand *cmd)
+rml_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
+ size_t len)
{
-#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));
+ if (payload != NULL)
+ purple_debug_info("msn", "Received RML:\n%s\n", payload);
+}
- msn_cmdproc_send_trans(cmdproc, trans);
-#endif
+static void
+rml_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
+{
+ 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 @@ adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
/* 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 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
MsnUser *user;
MsnObject *msnobj;
unsigned long clientid;
- int wlmclient;
+ int networkid;
const char *state, *passport, *friendly;
session = cmdproc->session;
@@ -1046,7 +959,7 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
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 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
</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 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
/* 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 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
}
}
- 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 @@ ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
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 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ not_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
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 @@ prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
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 @@ rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
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 @@ static void
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));
-
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
+ tmp_timestamp,
+ purple_connection_get_password(gc));
- 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);
-
- 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_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);
- 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 @@ static void
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);
- /* get the payload content */
- purple_debug_info("MSNP14","GCF command payload:\n%.*s\n", xmllen, buf);
+ 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);
+ }
+
+ 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);
+ }
+
+ cmdproc->session->blocked_text = g_string_free(blocked, FALSE);
+ }
- g_free(buf);
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 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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 @@ profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
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 @@ initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
/* 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 @@ initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
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 @@ initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
/* 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 @@ initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
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 @@ initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
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 @@ initial_mdata_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
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 @@ email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
/* 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 @@ email_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
(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 @@ msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list
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 @@ msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId li
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 @@ msn_notification_rem_buddy_from_list(MsnNotification *notification, MsnListId li
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_notification_init(void)
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,
diff --git a/libpurple/protocols/msn/notification.h b/libpurple/protocols/msn/notification.h
index 493120012c..01fdeebc76 100644
--- a/libpurple/protocols/msn/notification.h
+++ b/libpurple/protocols/msn/notification.h
@@ -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_dump_contact(MsnSession *session);
*/
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_ */
diff --git a/libpurple/protocols/msn/oim.c b/libpurple/protocols/msn/oim.c
index bd2441fd72..f7684848d6 100644
--- a/libpurple/protocols/msn/oim.c
+++ b/libpurple/protocols/msn/oim.c
@@ -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 @@ typedef struct {
} 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 @@ msn_oim_destroy(MsnOim *oim)
{
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 @@ msn_oim_free_send_req(MsnOimSendReq *req)
}
/****************************************
+ * 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);
+
+ 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);
- 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);
g_free(oim_base64);
- return oim_body;
+ return g_string_free(oim_body, FALSE);
}
/*
@@ -146,13 +320,13 @@ msn_oim_send_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ msn_oim_send_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ msn_oim_send_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ msn_oim_send_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ void
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&amp;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 @@ msn_oim_send_msg(MsnOim *oim)
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 @@ msn_oim_delete_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
{
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 @@ msn_oim_post_delete_msg(MsnOimRecvData *rdata)
char *msgid = rdata->msg_id;
char *soap_body;
- purple_debug_info("MSNP14","Delete single OIM Message {%s}\n",msgid);
+ purple_debug_info("msn", "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);
+ soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE, 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);
+ 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 @@ msn_oim_parse_timestamp(const char *timestamp)
long sys_tzoff;
#endif
- if (!offset_positive)
+ if (offset_positive)
tzoff *= -1;
t.tm_year -= 1900;
@@ -361,7 +550,7 @@ msn_oim_parse_timestamp(const char *timestamp)
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 @@ msn_oim_parse_timestamp(const char *timestamp)
}
}
- 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_oim_report_to_user(MsnOimRecvData *rdata, const char *msg_str)
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 @@ msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
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 @@ msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
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 @@ msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
g_free(rtime);
g_free(nickname);
}
-
- xmlnode_free(node);
}
/*Post to get the Offline Instant Message*/
@@ -539,19 +743,16 @@ msn_oim_post_single_get_msg(MsnOim *oim, char *msgid)
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);
}
diff --git a/libpurple/protocols/msn/oim.h b/libpurple/protocols/msn/oim.h
index 1bec1ed30c..dc5df9c54c 100644
--- a/libpurple/protocols/msn/oim.h
+++ b/libpurple/protocols/msn/oim.h
@@ -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 @@ struct _MsnOim
{
MsnSession *session;
- MsnSoapConn *retrieveconn;
GList * oim_list;
- MsnSoapConn *sendconn;
char *challenge;
char *run_id;
gint send_seq;
diff --git a/libpurple/protocols/msn/page.c b/libpurple/protocols/msn/page.c
index 250a7221c3..c35b4bf1d3 100644
--- a/libpurple/protocols/msn/page.c
+++ b/libpurple/protocols/msn/page.c
@@ -53,9 +53,9 @@ msn_page_gen_payload(const MsnPage *page, size_t *ret_size)
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);
diff --git a/libpurple/protocols/msn/servconn.c b/libpurple/protocols/msn/servconn.c
index c69c4353ae..96a164f391 100644
--- a/libpurple/protocols/msn/servconn.c
+++ b/libpurple/protocols/msn/servconn.c
@@ -203,7 +203,7 @@ connect_cb(gpointer data, gint source, const char *error_message)
}
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 @@ msn_servconn_connect(MsnServConn *servconn, const char *host, int port)
{
/* 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 @@ msn_servconn_disconnect(MsnServConn *servconn)
{
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 @@ msn_servconn_disconnect(MsnServConn *servconn)
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 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
session = servconn->session;
len = read(servconn->fd, buf, sizeof(buf) - 1);
- servconn->session->account->gc->last_received = time(NULL);
-
- if (len <= 0) {
- switch (errno) {
+ if (servconn->type == MSN_SERVCONN_NS)
+ servconn->session->account->gc->last_received = time(NULL);
- 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';
diff --git a/libpurple/protocols/msn/servconn.h b/libpurple/protocols/msn/servconn.h
index 34bf5b8b08..24884d2f75 100644
--- a/libpurple/protocols/msn/servconn.h
+++ b/libpurple/protocols/msn/servconn.h
@@ -115,8 +115,10 @@ void msn_servconn_destroy(MsnServConn *servconn);
* @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.
diff --git a/libpurple/protocols/msn/session.c b/libpurple/protocols/msn/session.c
index aec81a7ada..b388390032 100644
--- a/libpurple/protocols/msn/session.c
+++ b/libpurple/protocols/msn/session.c
@@ -45,8 +45,6 @@ msn_session_new(PurpleAccount *account)
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_session_destroy(MsnSession *session)
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 @@ msn_session_destroy(MsnSession *session)
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 @@ msn_session_get_conv(MsnSession *session,const char *passport)
* 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 @@ msn_session_finish_login(MsnSession *session)
PurpleAccount *account;
PurpleConnection *gc;
PurpleStoredImage *img;
- const char *passport;
if (session->logged_in)
return;
@@ -477,17 +469,5 @@ msn_session_finish_login(MsnSession *session)
/* 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");
- }
}
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index 4cc9f5627f..5d7c39fbdb 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -38,7 +38,6 @@ typedef struct _MsnSession MsnSession;
#include "cmdproc.h"
#include "nexus.h"
#include "httpconn.h"
-#include "contact.h"
#include "oim.h"
#include "userlist.h"
@@ -96,7 +95,6 @@ struct _MsnSession
MsnNotification *notification;
MsnNexus *nexus;
- MsnContact *contact;
MsnOim *oim;
MsnSync *sync;
@@ -109,19 +107,19 @@ struct _MsnSession
/*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 @@ void msn_session_finish_login(MsnSession *session);
/*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_ */
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index 3c0074d7c2..4836b91f5d 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -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_ok(MsnSlpCall *slpcall, const char *branch,
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 @@ static void
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 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
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 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
/* 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 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
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 @@ got_sessionreq(MsnSlpCall *slpcall, const char *branch,
purple_xfer_request(xfer);
}
- }
+ } else
+ purple_debug_warning("msn", "SLP SessionReq with unknown EUF-GUID: %s\n", euf_guid);
}
void
@@ -781,16 +775,13 @@ static void
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;
-
- if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) {
+ swboard = slpcall->slplink->swboard;
+ conv = swboard->conv;
+ if (conv) {
/* FIXME: it would be better if we wrote the data as we received it
instead of all at once, calling write multiple times and
close once at the very end
@@ -808,6 +799,7 @@ msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
MsnSession *session;
MsnSlpLink *slplink;
+ MsnSwitchBoard *swboard;
MsnObject *obj;
char **tokens;
char *smile, *body_str;
@@ -847,8 +839,9 @@ msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
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 @@ msn_release_buddy_icon_request(MsnUserList *userlist)
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 @@ end_user_display(MsnSlpCall *slpcall, MsnSession *session)
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 @@ msn_request_user_display(MsnUser *user)
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
diff --git a/libpurple/protocols/msn/slpcall.c b/libpurple/protocols/msn/slpcall.c
index 9c9fc5ea70..81179e20b4 100644
--- a/libpurple/protocols/msn/slpcall.c
+++ b/libpurple/protocols/msn/slpcall.c
@@ -24,7 +24,6 @@
#include "msn.h"
#include "msnutils.h"
#include "slpcall.h"
-#include "slpsession.h"
#include "slp.h"
@@ -115,12 +114,8 @@ msn_slp_call_init(MsnSlpCall *slpcall, MsnSlpCallType type)
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;
}
diff --git a/libpurple/protocols/msn/slpcall.h b/libpurple/protocols/msn/slpcall.h
index 1029df5ce1..c1abc69186 100644
--- a/libpurple/protocols/msn/slpcall.h
+++ b/libpurple/protocols/msn/slpcall.h
@@ -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 @@ struct _MsnSlpCall
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;
diff --git a/libpurple/protocols/msn/slplink.c b/libpurple/protocols/msn/slplink.c
index 078b9000d5..c16a30286f 100644
--- a/libpurple/protocols/msn/slplink.c
+++ b/libpurple/protocols/msn/slplink.c
@@ -154,23 +154,6 @@ msn_session_get_slplink(MsnSession *session, const char *username)
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 @@ msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg)
}
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 @@ msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg)
}
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
@@ -774,8 +754,7 @@ msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer)
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 @@ msn_slplink_request_object(MsnSlpLink *slplink,
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);
}
diff --git a/libpurple/protocols/msn/slplink.h b/libpurple/protocols/msn/slplink.h
index 4c650b1ce0..eef61411a0 100644
--- a/libpurple/protocols/msn/slplink.h
+++ b/libpurple/protocols/msn/slplink.h
@@ -53,7 +53,6 @@ struct _MsnSlpLink
MsnDirectConn *directconn;
GList *slp_calls;
- GList *slp_sessions;
GList *slp_msgs;
GQueue *slp_msg_queue;
@@ -74,8 +73,6 @@ MsnSlpLink *msn_session_find_slplink(MsnSession *session,
*/
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,
diff --git a/libpurple/protocols/msn/slpmsg.h b/libpurple/protocols/msn/slpmsg.h
index a8b14e84e1..677613a226 100644
--- a/libpurple/protocols/msn/slpmsg.h
+++ b/libpurple/protocols/msn/slpmsg.h
@@ -28,7 +28,6 @@ typedef struct _MsnSlpMessage MsnSlpMessage;
#include "imgstore.h"
-#include "slpsession.h"
#include "slpcall.h"
#include "slplink.h"
#include "session.h"
@@ -42,7 +41,6 @@ typedef struct _MsnSlpMessage MsnSlpMessage;
*/
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;
diff --git a/libpurple/protocols/msn/slpsession.c b/libpurple/protocols/msn/slpsession.c
deleted file mode 100644
index e3bb8ed013..0000000000
--- a/libpurple/protocols/msn/slpsession.c
+++ /dev/null
@@ -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
diff --git a/libpurple/protocols/msn/slpsession.h b/libpurple/protocols/msn/slpsession.h
deleted file mode 100644
index 0be991bf51..0000000000
--- a/libpurple/protocols/msn/slpsession.h
+++ /dev/null
@@ -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_ */
diff --git a/libpurple/protocols/msn/soap.c b/libpurple/protocols/msn/soap.c
index 9f43e836eb..30c59e4aea 100644
--- a/libpurple/protocols/msn/soap.c
+++ b/libpurple/protocols/msn/soap.c
@@ -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 "soap.h"
-#define MSN_SOAP_DEBUG
-/*local function prototype*/
-void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step);
+#include "internal.h"
-/*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"
- };
-
- purple_debug_info("MSN SOAP", "Setting SOAP process step to %s\n", MsnSoapStepText[step]);
-#endif
- soapconn->step = step;
-}
+#include "soap.h"
-/*new a soap connection*/
-MsnSoapConn *
-msn_soap_new(MsnSession *session,gpointer data, gboolean ssl)
-{
- MsnSoapConn *soapconn;
+#include "session.h"
- soapconn = g_new0(MsnSoapConn, 1);
- soapconn->session = session;
- soapconn->parent = data;
- soapconn->ssl_conn = ssl;
+#include "debug.h"
+#include "xmlnode.h"
- soapconn->gsc = NULL;
- soapconn->input_handler = 0;
- soapconn->output_handler = 0;
+#include <glib.h>
+#if !defined(_WIN32) || !defined(_WINERROR_)
+#include <error.h>
+#endif
- msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
- soapconn->soap_queue = g_queue_new();
+#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;
+
+typedef struct _MsnSoapConnection {
+ MsnSession *session;
+ char *host;
- return soapconn;
-}
+ time_t last_used;
+ PurpleSslConnection *ssl;
+ gboolean connected;
-/*ssl soap connect callback*/
-void
-msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc,
- PurpleInputCondition cond)
-{
- MsnSoapConn * soapconn;
- MsnSession *session;
- gboolean soapconn_is_valid = FALSE;
+ guint event_handle;
+ GString *buf;
+ gsize handled_len;
+ gsize body_len;
+ int response_code;
+ gboolean headers_done;
+ gboolean close_when_done;
- purple_debug_misc("MSN SOAP","SOAP server connection established!\n");
+ MsnSoapMessage *message;
- soapconn = data;
- g_return_if_fail(soapconn != NULL);
+ GQueue *queue;
+ MsnSoapRequest *current_request;
+} MsnSoapConnection;
- session = soapconn->session;
- g_return_if_fail(session != NULL);
+static void msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data);
+static gboolean msn_soap_connection_run(gpointer data);
- soapconn->gsc = gsc;
+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);
- msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTED);
+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);
- /*connection callback*/
- if (soapconn->connect_cb != NULL) {
- soapconn_is_valid = soapconn->connect_cb(soapconn, gsc);
- }
+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);
- if (!soapconn_is_valid) {
- return;
+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;
}
- /*we do the SOAP request here*/
- msn_soap_post_head_request(soapconn);
+ return FALSE;
}
-/*ssl soap error callback*/
-static void
-msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
+static gboolean
+msn_soap_cleanup_for_session(gpointer data)
{
- MsnSoapConn * soapconn = data;
-
- g_return_if_fail(data != NULL);
+ MsnSession *sess = data;
+ time_t t = time(NULL);
- purple_debug_warning("MSN SOAP","Soap connection error!\n");
+ purple_debug_info("soap", "session cleanup timeout\n");
- msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED);
+ if (sess->soap_table) {
+ g_hash_table_foreach_remove(sess->soap_table, msn_soap_cleanup_each,
+ &t);
- /*error callback*/
- if (soapconn->error_cb != NULL) {
- soapconn->error_cb(soapconn, gsc, error);
- } else {
- msn_soap_post(soapconn, NULL);
+ if (g_hash_table_size(sess->soap_table) == 0) {
+ purple_timeout_remove(sess->soap_cleanup_handle);
+ sess->soap_cleanup_handle = 0;
+ }
}
-}
-/*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 TRUE;
}
-/*connect the soap connection*/
-void
-msn_soap_connect(MsnSoapConn *soapconn)
+static MsnSoapConnection *
+msn_soap_get_connection(MsnSession *session, const char *host)
{
- 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);
+ 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);
}
- msn_soap_set_process_step(soapconn, MSN_SOAP_CONNECTING);
-}
-
+ if (session->soap_cleanup_handle == 0)
+ session->soap_cleanup_handle = purple_timeout_add(SOAP_TIMEOUT * 1000,
+ msn_soap_cleanup_for_session, session);
-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");
+ if (conn == NULL) {
+ conn = msn_soap_connection_new(session, host);
+ g_hash_table_insert(session->soap_table, conn->host, conn);
}
-#endif
-}
+ conn->last_used = time(NULL);
+ return conn;
+}
-/*close the soap connection*/
-void
-msn_soap_close(MsnSoapConn *soapconn)
+static MsnSoapConnection *
+msn_soap_connection_new(MsnSession *session, const char *host)
{
- 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);
+ MsnSoapConnection *conn = g_new0(MsnSoapConnection, 1);
+ conn->session = session;
+ conn->host = g_strdup(host);
+ conn->queue = g_queue_new();
+ return conn;
}
-/*clean the unhandled SOAP request*/
-void
-msn_soap_clean_unhandled_requests(MsnSoapConn *soapconn)
+static void
+msn_soap_connected_cb(gpointer data, PurpleSslConnection *ssl,
+ PurpleInputCondition cond)
{
- MsnSoapReq *request;
-
- g_return_if_fail(soapconn != NULL);
+ MsnSoapConnection *conn = data;
- soapconn->body = NULL;
+ conn->connected = TRUE;
- while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){
- if (soapconn->read_cb) {
- soapconn->read_cb(soapconn);
- }
- msn_soap_request_free(request);
- }
+ if (conn->event_handle == 0)
+ conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
}
-/*destroy the soap connection*/
-void
-msn_soap_destroy(MsnSoapConn *soapconn)
+static void
+msn_soap_error_cb(PurpleSslConnection *ssl, PurpleSslErrorType error,
+ gpointer data)
{
- g_free(soapconn->login_host);
+ MsnSoapConnection *conn = data;
- g_free(soapconn->login_path);
+ /* sslconn already frees the connection in case of error */
+ conn->ssl = 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);
+ g_hash_table_remove(conn->session->soap_table, conn->host);
+}
- /*close ssl connection*/
- msn_soap_close(soapconn);
+static gboolean
+msn_soap_handle_redirect(MsnSoapConnection *conn, const char *url)
+{
+ char *host;
+ char *path;
- /*process the unhandled soap request*/
- msn_soap_clean_unhandled_requests(soapconn);
+ 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);
- g_queue_free(soapconn->soap_queue);
- g_free(soapconn);
-}
+ msn_soap_request_destroy(conn->current_request, TRUE);
+ conn->current_request = NULL;
-/*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);
+ g_free(host);
+ g_free(path);
+
+ return TRUE;
}
- return (soapconn->fd > 0 ? 1 : 0);
+
+ return FALSE;
}
-/*read and append the content to the buffer*/
-static gssize
-msn_soap_read(MsnSoapConn *soapconn)
+static gboolean
+msn_soap_handle_body(MsnSoapConnection *conn, MsnSoapMessage *response)
{
- gssize len, requested_len;
- char temp_buf[MSN_SOAP_READ_BUFF_SIZE];
+ xmlnode *body = xmlnode_get_child(response->xml, "Body");
+ xmlnode *fault = xmlnode_get_child(response->xml, "Fault");
- 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 (fault) {
+ xmlnode *faultcode = xmlnode_get_child(fault, "faultcode");
- if ( soapconn->ssl_conn ) {
- len = purple_ssl_read(soapconn->gsc, temp_buf, requested_len);
- } else {
- len = read(soapconn->fd, temp_buf, requested_len);
- }
+ if (faultcode != NULL) {
+ char *faultdata = xmlnode_get_data(faultcode);
+ if (g_str_equal(faultdata, "psf:Redirect")) {
+ xmlnode *url = xmlnode_get_child(fault, "redirectUrl");
- if ( len <= 0 ) {
- switch (errno) {
+ if (url) {
+ char *urldata = xmlnode_get_data(url);
+ msn_soap_handle_redirect(conn, urldata);
+ g_free(urldata);
+ }
- case 0:
- case EBADF: /* we are sometimes getting this in Windows */
- case EAGAIN: return len;
+ 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;
+ }
- 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;
+ g_free(faultdata);
}
}
- 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);
- }
+ 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);
}
-#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
-
- return len;
+ return TRUE;
}
-/*read the whole SOAP server response*/
static void
-msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
{
- 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);
+ 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);
+ }
- /*read the request header*/
- len = msn_soap_read(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);
+ }
- if ( len < 0 )
+ /* && 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;
- if (soapconn->read_buf == NULL) {
- 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;
+
+#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 (sep == NULL) {
+ purple_debug_info("soap", "ignoring malformed line: %s\n", line);
+ g_free(line);
+ goto loop_end;
+ }
- if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
- || ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) )
- {
- /* Redirect. */
- char *location, *c;
-
- 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);
+ 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);
}
- return;
+ loop_end:
+ cursor = conn->buf->str + conn->handled_len;
}
- location = strchr(location, ' ') + 1;
+ }
- if ((c = strchr(location, '\r')) != NULL)
- *c = '\0';
- else
- return;
+ if (!handled && conn->headers_done) {
+ if (conn->buf->len - conn->handled_len >=
+ conn->body_len) {
+ xmlnode *node = xmlnode_from_str(cursor, conn->body_len);
- /* Skip the http:// */
- if ((c = strchr(location, '/')) != NULL)
- location = c + 2;
+ 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 ((c = strchr(location, '/')) != NULL)
- {
- g_free(soapconn->login_path);
- soapconn->login_path = g_strdup(c);
+ if (!msn_soap_handle_body(conn, message)) {
+ return;
+ }
+ }
- *c = '\0';
+ msn_soap_connection_handle_next(conn);
}
- 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);
- }
+ return;
}
- /* 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);
- }
+ if (handled) {
+ msn_soap_connection_handle_next(conn);
}
- 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);
- }
- }
- }
+static void
+msn_soap_write_cb(gpointer data, gint fd, PurpleInputCondition cond)
+{
+ msn_soap_write_cb_internal(data, fd, cond, FALSE);
+}
- }
- 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;
+static gboolean
+msn_soap_write_cb_internal(gpointer data, gint fd, PurpleInputCondition cond,
+ gboolean initial)
+{
+ MsnSoapConnection *conn = data;
+ int written;
- if (body_start > soapconn->read_buf + soapconn->read_len)
- return;
+ if (cond != PURPLE_INPUT_WRITE) return TRUE;
- /* 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: ");
+ written = purple_ssl_write(conn->ssl, conn->buf->str + conn->handled_len,
+ conn->buf->len - conn->handled_len);
- if (length_start > soapconn->read_buf + soapconn->read_len)
- return;
+ 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;
+ }
- if ( (length_end = strstr(length_start, "\r\n")) == NULL )
- return;
+ conn->handled_len += written;
- body_len = g_strndup(length_start, length_end - length_start);
+ if (conn->handled_len < conn->buf->len)
+ return TRUE;
- /*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;
- }
+ /* 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;
-#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
+ 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;
+}
- node = xmlnode_from_str(soapconn->body, soapconn->body_len);
+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");
+ }
- if (node != NULL) {
- formattedxml = xmlnode_to_formatted_str(node, NULL);
- http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf);
+ g_string_append(conn->buf, "\r\n");
+ g_string_append(conn->buf, body);
- 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);
+#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);
+ }
- /*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;
+ g_free(body);
}
-
- /* dispatch next request in queue */
- msn_soap_post(soapconn, NULL);
}
- return;
+
+ return FALSE;
}
void
-msn_soap_free_read_buf(MsnSoapConn *soapconn)
+msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
+ const char *host, const char *path, gboolean secure,
+ MsnSoapCallback cb, gpointer cb_data)
{
- 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;
+ msn_soap_message_send_internal(session, message, host, path, secure,
+ cb, cb_data, FALSE);
}
-void
-msn_soap_free_write_buf(MsnSoapConn *soapconn)
+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)
{
- g_return_if_fail(soapconn != NULL);
+ 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 (soapconn->write_buf) {
- g_free(soapconn->write_buf);
+ if (first) {
+ g_queue_push_head(conn->queue, req);
+ } else {
+ g_queue_push_tail(conn->queue, req);
}
- soapconn->write_buf = NULL;
- soapconn->written_len = 0;
+
+ if (conn->event_handle == 0)
+ conn->event_handle = purple_timeout_add(0, msn_soap_connection_run,
+ conn);
}
-/*Soap write process func*/
static void
-msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond)
+msn_soap_connection_sanitize(MsnSoapConnection *conn, gboolean disconnect)
{
- 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->event_handle) {
+ purple_input_remove(conn->event_handle);
+ conn->event_handle = 0;
}
- 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);
-
- /* TODO: notify of the error */
- purple_debug_error("MSN SOAP", "Error writing to SSL connection!\n");
- msn_soap_post(soapconn, NULL);
- return;
+ if (conn->message) {
+ msn_soap_message_destroy(conn->message);
+ conn->message = NULL;
}
- soapconn->written_len += len;
- if (soapconn->written_len < total_len)
- 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);
+ if (conn->buf) {
+ g_string_free(conn->buf, TRUE);
+ conn->buf = NULL;
+ }
- /* Write finish!
- * callback for write done
- */
- if(soapconn->written_cb != NULL){
- soapconn->written_cb(soapconn);
+ if (conn->ssl && (disconnect || conn->close_when_done)) {
+ purple_ssl_close(conn->ssl);
+ conn->ssl = NULL;
}
- /*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);
+
+ if (conn->current_request) {
+ msn_soap_request_destroy(conn->current_request, FALSE);
+ conn->current_request = NULL;
}
}
-/*write the buffer to SOAP connection*/
-void
-msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction written_cb)
+static void
+msn_soap_connection_handle_next(MsnSoapConnection *conn)
{
- if (soapconn == NULL) {
- return;
- }
+ msn_soap_connection_sanitize(conn, FALSE);
- msn_soap_set_process_step(soapconn, MSN_SOAP_PROCESSING);
+ conn->event_handle = purple_timeout_add(0, msn_soap_connection_run, conn);
- /* 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);
+ if (conn->current_request) {
+ MsnSoapRequest *req = conn->current_request;
+ conn->current_request = NULL;
+ msn_soap_connection_destroy_foreach_cb(req, conn);
+ }
}
-/* 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)
+static void
+msn_soap_connection_destroy_foreach_cb(gpointer item, gpointer data)
{
- MsnSoapReq *request;
-
- request = g_new0(MsnSoapReq, 1);
- request->id = 0;
+ MsnSoapRequest *req = item;
- 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;
+ if (req->cb)
+ req->cb(req->message, NULL, req->cb_data);
- return request;
+ msn_soap_request_destroy(req, FALSE);
}
-/*free a soap request*/
-void
-msn_soap_request_free(MsnSoapReq *request)
+static void
+msn_soap_connection_destroy(MsnSoapConnection *conn)
{
- g_return_if_fail(request != NULL);
+ if (conn->current_request) {
+ MsnSoapRequest *req = conn->current_request;
+ conn->current_request = NULL;
+ msn_soap_connection_destroy_foreach_cb(req, conn);
+ }
- 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;
+ 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(request);
+ g_free(conn->host);
+ g_free(conn);
}
-/*post the soap request queue's head request*/
-void
-msn_soap_post_head_request(MsnSoapConn *soapconn)
+MsnSoapMessage *
+msn_soap_message_new(const char *action, xmlnode *xml)
{
- g_return_if_fail(soapconn != NULL);
- g_return_if_fail(soapconn->soap_queue != NULL);
+ MsnSoapMessage *message = g_new0(MsnSoapMessage, 1);
- if (soapconn->step == MSN_SOAP_CONNECTED ||
- soapconn->step == MSN_SOAP_CONNECTED_IDLE) {
+ message->action = g_strdup(action);
+ message->xml = xml;
- 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);
- }
- }
+ return message;
}
-/*post the soap request ,
- * if not connected, Connected first.
- */
void
-msn_soap_post(MsnSoapConn *soapconn, MsnSoapReq *request)
+msn_soap_message_destroy(MsnSoapMessage *message)
{
- MsnSoapReq *head_request;
-
- if (soapconn == NULL)
- return;
-
- 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 ( !g_queue_is_empty(soapconn->soap_queue)) {
-
- /* we may have to reinitialize the soap connection, so avoid
- * reusing the connection for now */
-
- 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 (!msn_soap_connected(soapconn) && (soapconn->step == MSN_SOAP_UNCONNECTED)) {
-
- /*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 (head_request == NULL) {
- purple_debug_error("MSN SOAP", "Queue is not empty, but failed to peek the head request!\n");
- return;
- }
-
- if (head_request->connect_init != NULL) {
- head_request->connect_init(soapconn);
- }
- msn_soap_connect(soapconn);
- return;
- }
-
-#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
+ 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);
}
-
}
-/*Post the soap request action*/
void
-msn_soap_post_request(MsnSoapConn *soapconn, MsnSoapReq *request)
+msn_soap_message_add_header(MsnSoapMessage *message,
+ const char *name, const char *value)
{
- 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
+ char *header = g_strdup_printf("%s: %s\r\n", name, value);
- 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 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);
- }
- else
- purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str);
-#endif
+ message->headers = g_slist_prepend(message->headers, header);
+}
- /*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);
+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);
}
diff --git a/libpurple/protocols/msn/soap.h b/libpurple/protocols/msn/soap.h
index ada2a1ab15..7d1a64c656 100644
--- a/libpurple/protocols/msn/soap.h
+++ b/libpurple/protocols/msn/soap.h
@@ -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
-
-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;
-
-/* 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);
-
-
-struct _MsnSoapReq{
- /*request sequence*/
- int id;
-
- char *login_host;
- char *login_path;
- char *soap_action;
-
- char *body;
- gpointer data_cb;
- MsnSoapReadCbFunction read_cb;
- MsnSoapWrittenCbFunction written_cb;
- MsnSoapConnectInitFunction connect_init;
-};
-
-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;
+#ifndef _MSN_SOAP_H
+#define _MSN_SOAP_H
- /*read handler*/
- guint input_handler;
- /*write handler*/
- guint output_handler;
+#include "session.h"
+#include "sslconn.h"
+#include "xmlnode.h"
- /*Queue of SOAP request to send*/
- int soap_id;
- GQueue *soap_queue;
+#include <glib.h>
- /*write buffer*/
- char *write_buf;
- gsize written_len;
- MsnSoapWrittenCbFunction written_cb;
+typedef struct _MsnSoapMessage MsnSoapMessage;
+typedef void (*MsnSoapCallback)(MsnSoapMessage *request,
+ MsnSoapMessage *response, gpointer cb_data);
- /*read buffer*/
- char *read_buf;
- gsize read_len;
- gsize need_to_read;
- MsnSoapReadCbFunction read_cb;
-
- gpointer data_cb;
-
- /*HTTP reply body part*/
- char *body;
- int body_len;
+struct _MsnSoapMessage {
+ char *action;
+ xmlnode *xml;
+ GSList *headers;
};
+MsnSoapMessage *msn_soap_message_new(const char *action, xmlnode *xml);
-/*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);
-
-/*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_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);
+void msn_soap_message_add_header(MsnSoapMessage *req,
+ const char *name, const char *value);
-/*check if the soap connection is connected*/
-int msn_soap_connected(MsnSoapConn *soapconn);
-void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step);
+void msn_soap_message_send(MsnSession *session, MsnSoapMessage *message,
+ const char *host, const char *path, gboolean secure,
+ MsnSoapCallback cb, gpointer cb_data);
-#endif/*_MSN_SOAP_H_*/
+void msn_soap_message_destroy(MsnSoapMessage *message);
+#endif
diff --git a/libpurple/protocols/msn/soap2.c b/libpurple/protocols/msn/soap2.c
deleted file mode 100644
index 416c58da93..0000000000
--- a/libpurple/protocols/msn/soap2.c
+++ /dev/null
@@ -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;
-}
-
diff --git a/libpurple/protocols/msn/soap2.h b/libpurple/protocols/msn/soap2.h
deleted file mode 100644
index 4d956f4ed8..0000000000
--- a/libpurple/protocols/msn/soap2.h
+++ /dev/null
@@ -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
diff --git a/libpurple/protocols/msn/state.c b/libpurple/protocols/msn/state.c
index 4f8aa50ba1..e58a448524 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -151,15 +151,15 @@ msn_get_currentmedia(char *xml_str, gsize len)
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 @@ msn_get_psm(char *xml_str, gsize len)
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 @@ msn_set_psm(MsnSession *session)
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);
diff --git a/libpurple/protocols/msn/switchboard.c b/libpurple/protocols/msn/switchboard.c
index bf1222fbfc..3e113f7dab 100644
--- a/libpurple/protocols/msn/switchboard.c
+++ b/libpurple/protocols/msn/switchboard.c
@@ -338,7 +338,7 @@ swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport)
{
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 @@ release_msg(MsnSwitchBoard *swboard, MsnMessage *msg)
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 @@ msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg,
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 @@ bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
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 @@ msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len)
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 @@ clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
}
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);
- swboard = cmdproc->data;
- account = cmdproc->session->account;
- user = msg->remote_user;
+ 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;
+
+ 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);
- serv_got_attention(account->gc, user, MSN_NUDGE);
+ else
+ purple_prpl_got_attention(account->gc, user, MSN_NUDGE);
+
+ } else if (!strcmp(id, "2")) {
+ /* Wink */
+
+ } 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_switchboard_connect(MsnSwitchBoard *swboard, const char *host, int port)
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 @@ cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error)
}
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 @@ got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd)
/* 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_switchboard_init(void)
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);
diff --git a/libpurple/protocols/msn/sync.c b/libpurple/protocols/msn/sync.c
index 7f727fc2b7..0fa21e734a 100644
--- a/libpurple/protocols/msn/sync.c
+++ b/libpurple/protocols/msn/sync.c
@@ -216,8 +216,6 @@ bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
void
msn_sync_init(void)
{
- /* TODO: check prp, blp, bpr */
-
cbs_table = msn_table_new();
/* Syncing */
diff --git a/libpurple/protocols/msn/user.c b/libpurple/protocols/msn/user.c
index a73cec2a8f..9fde531cd4 100644
--- a/libpurple/protocols/msn/user.c
+++ b/libpurple/protocols/msn/user.c
@@ -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 @@ msn_user_new(MsnUserList *userlist, const char *passport,
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 @@ msn_user_destroy(MsnUser *user)
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 @@ void
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 @@ msn_user_set_state(MsnUser *user, const char *state)
{
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 @@ msn_user_set_currentmedia(MsnUser *user, const CurrentMedia *media)
}
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 @@ msn_user_set_uid(MsnUser *user, const char *uid)
}
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 @@ msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img)
/*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 @@ msn_user_add_group_id(MsnUser *user, const char* id)
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 @@ msn_user_is_yahoo(PurpleAccount *account, const char *name)
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 @@ msn_user_set_mobile_phone(MsnUser *user, const char *number)
}
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 @@ msn_user_get_friendly_name(const MsnUser *user)
}
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 @@ msn_user_get_mobile_phone(const MsnUser *user)
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)
{
diff --git a/libpurple/protocols/msn/user.h b/libpurple/protocols/msn/user.h
index 220eccc5fa..402464ae91 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -33,14 +33,14 @@ typedef struct _MsnUser MsnUser;
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 @@ struct _MsnUser
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 @@ struct _MsnUser
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 @@ struct _MsnUser
indexed by the list it belongs to */
};
-/**************************************************************************/
-/** @name User API */
-/**************************************************************************/
+/**************************************************************************
+ ** @name User API *
+ **************************************************************************/
/*@{*/
/**
@@ -111,7 +112,7 @@ struct _MsnUser
* @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_passport(MsnUser *user, const char *passport);
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_home_phone(MsnUser *user, const char *number);
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_passport(const MsnUser *user);
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_work_phone(const MsnUser *user);
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.
diff --git a/libpurple/protocols/msn/userlist.c b/libpurple/protocols/msn/userlist.c
index 8a32460f71..7755099977 100644
--- a/libpurple/protocols/msn/userlist.c
+++ b/libpurple/protocols/msn/userlist.c
@@ -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 @@ msn_accept_add_cb(gpointer data)
{
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_accept_add_cb(gpointer data)
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 @@ msn_cancel_add_cb(gpointer data)
{
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_cancel_add_cb(gpointer data)
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 @@ msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
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 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
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 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
* 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 @@ msn_got_lst_user(MsnSession *session, MsnUser *user,
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 @@ msn_got_lst_user(MsnSession *session, MsnUser *user,
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 @@ msn_userlist_find_add_user(MsnUserList *userlist,const char *passport,const char
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 @@ msn_userlist_find_user(MsnUserList *userlist, const char *passport)
{
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 @@ msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
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 @@ msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
/* 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 @@ msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
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 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
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 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
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 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
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 @@ msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
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 @@ msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
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 @@ msn_userlist_rem_buddy_from_group(MsnUserList *userlist, const char *who,
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 @@ msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
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 @@ msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
/* 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*/
diff --git a/libpurple/protocols/msnp9/command.c b/libpurple/protocols/msnp9/command.c
index 187fb227af..5b5ea1f69c 100644
--- a/libpurple/protocols/msnp9/command.c
+++ b/libpurple/protocols/msnp9/command.c
@@ -65,9 +65,12 @@ msn_command_from_string(const char *string)
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;
diff --git a/libpurple/protocols/msnp9/servconn.c b/libpurple/protocols/msnp9/servconn.c
index c47df148c7..cdbe0efbdc 100644
--- a/libpurple/protocols/msnp9/servconn.c
+++ b/libpurple/protocols/msnp9/servconn.c
@@ -251,6 +251,12 @@ msn_servconn_disconnect(MsnServConn *servconn)
{
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 @@ msn_servconn_disconnect(MsnServConn *servconn)
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);
diff --git a/libpurple/protocols/oscar/family_icq.c b/libpurple/protocols/oscar/family_icq.c
index ce907db9af..441945b13f 100644
--- a/libpurple/protocols/oscar/family_icq.c
+++ b/libpurple/protocols/oscar/family_icq.c
@@ -36,6 +36,8 @@ int aim_icq_reqofflinemsgs(OscarData *od)
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 @@ int aim_icq_ackofflinemsgs(OscarData *od)
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 @@ int aim_icq_getallinfo(OscarData *od, const char *uin)
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 @@ int aim_icq_getalias(OscarData *od, const char *uin)
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 @@ int aim_icq_getalias(OscarData *od, const char *uin)
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 @@ int aim_icq_getsimpleinfo(OscarData *od, const char *uin)
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 @@ int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, gui
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 @@ icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
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);
}
diff --git a/libpurple/protocols/oscar/family_locate.c b/libpurple/protocols/oscar/family_locate.c
index 3d7bf0bd37..ae5408ff3d 100644
--- a/libpurple/protocols/oscar/family_locate.c
+++ b/libpurple/protocols/oscar/family_locate.c
@@ -53,10 +53,9 @@ static const struct {
* 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 @@ static const struct {
{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 @@ static const struct {
{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 @@ aim_locate_getinfoshort(OscarData *od, const char *sn, guint32 flags)
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);
diff --git a/libpurple/protocols/oscar/flap_connection.c b/libpurple/protocols/oscar/flap_connection.c
index aa71e38cce..3e8ffeaa49 100644
--- a/libpurple/protocols/oscar/flap_connection.c
+++ b/libpurple/protocols/oscar/flap_connection.c
@@ -107,21 +107,21 @@ rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, str
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 @@ static gboolean flap_connection_send_queued(gpointer data)
/* (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 @@ static gboolean flap_connection_send_queued(gpointer data)
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 @@ static gboolean flap_connection_send_queued(gpointer data)
*
* @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 @@ flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, c
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_snac(OscarData *od, FlapConnection *conn, guint16 family, c
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 @@ flap_connection_new(OscarData *od, int type)
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 @@ flap_connection_close(OscarData *od, FlapConnection *conn)
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 @@ flap_connection_destroy_cb(gpointer data)
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 @@ flap_connection_send(FlapConnection *conn, FlapFrame *frame)
sendframe_flap(conn, frame);
flap_frame_destroy(frame);
}
-
diff --git a/libpurple/protocols/oscar/oscar.c b/libpurple/protocols/oscar/oscar.c
index c8129383c9..c55d11dea3 100644
--- a/libpurple/protocols/oscar/oscar.c
+++ b/libpurple/protocols/oscar/oscar.c
@@ -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 @@ purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
/* 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_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
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 @@ static void damn_you(gpointer data, gint source, PurpleInputCondition c)
}
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 @@ straight_to_hell(gpointer data, gint source, const gchar *error_message)
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 @@ int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
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 @@ purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
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;
@@ -2063,8 +2109,28 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
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);
+ }
+ }
}
}
@@ -3071,7 +3137,7 @@ static int purple_parse_userinfo(OscarData *od, FlapConnection *conn, FlapFrame
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);
@@ -4917,11 +4983,6 @@ static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *
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);
diff --git a/libpurple/protocols/oscar/oscar.h b/libpurple/protocols/oscar/oscar.h
index e26100e4d9..6b51938458 100644
--- a/libpurple/protocols/oscar/oscar.h
+++ b/libpurple/protocols/oscar/oscar.h
@@ -258,6 +258,15 @@ struct _ClientInfo
"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 @@ struct _ClientInfo
#define CLIENTINFO_PURPLE_ICQ { \
"Purple/" VERSION, \
0x010a, \
- 0x0014, 0x0034, \
- 0x0000, 0x0bb8, \
- 0x0000043d, \
+ 0x0006, 0x0000, \
+ 0x0000, 0x17ab, \
+ 0x00007535, \
"us", "en", \
}
@@ -344,16 +353,16 @@ typedef enum
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 @@ struct _FlapConnection
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 @@ struct _OscarData
/** 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(FlapConnection *conn, FlapFrame *frame);
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);
diff --git a/libpurple/protocols/oscar/oscar_data.c b/libpurple/protocols/oscar/oscar_data.c
index 96d5993d3a..7b340c75eb 100644
--- a/libpurple/protocols/oscar/oscar_data.c
+++ b/libpurple/protocols/oscar/oscar_data.c
@@ -88,10 +88,17 @@ oscar_data_destroy(OscarData *od)
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);
diff --git a/libpurple/protocols/qq/AUTHORS b/libpurple/protocols/qq/AUTHORS
index ce8b9a1ac8..5cb48830dd 100644
--- a/libpurple/protocols/qq/AUTHORS
+++ b/libpurple/protocols/qq/AUTHORS
@@ -9,11 +9,27 @@ arfankai : fixed bugs in char_conv.c
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
diff --git a/libpurple/protocols/qq/ChangeLog b/libpurple/protocols/qq/ChangeLog
index 29595f3384..1ef12628c6 100644
--- a/libpurple/protocols/qq/ChangeLog
+++ b/libpurple/protocols/qq/ChangeLog
@@ -1,3 +1,79 @@
+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
diff --git a/libpurple/protocols/qq/Makefile.am b/libpurple/protocols/qq/Makefile.am
index 7e310589bb..f9ba4744ab 100644
--- a/libpurple/protocols/qq/Makefile.am
+++ b/libpurple/protocols/qq/Makefile.am
@@ -10,8 +10,6 @@ QQSOURCES = \
buddy_list.h \
buddy_opt.c \
buddy_opt.h \
- buddy_status.c \
- buddy_status.h \
char_conv.c \
char_conv.h \
crypt.c \
@@ -44,10 +42,10 @@ QQSOURCES = \
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 \
diff --git a/libpurple/protocols/qq/Makefile.mingw b/libpurple/protocols/qq/Makefile.mingw
index e3601114ea..0341cb59f9 100644
--- a/libpurple/protocols/qq/Makefile.mingw
+++ b/libpurple/protocols/qq/Makefile.mingw
@@ -42,7 +42,6 @@ C_SRC = \
buddy_info.c \
buddy_list.c \
buddy_opt.c \
- buddy_status.c \
char_conv.c \
crypt.c \
file_trans.c \
@@ -59,13 +58,13 @@ C_SRC = \
group_search.c \
header_info.c \
im.c \
- keep_alive.c \
- login_logout.c \
packet_parse.c \
qq.c \
+ qq_base.c \
qq_network.c \
- send_file.c \
+ qq_process.c \
qq_trans.c \
+ send_file.c \
sys_msg.c \
utils.c
diff --git a/libpurple/protocols/qq/buddy_info.c b/libpurple/protocols/qq/buddy_info.c
index 8363c4965c..3859189b22 100644
--- a/libpurple/protocols/qq/buddy_info.c
+++ b/libpurple/protocols/qq/buddy_info.c
@@ -29,11 +29,12 @@
#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 "qq_base.h"
#include "qq_network.h"
#define QQ_PRIMARY_INFORMATION _("Primary Information")
@@ -85,6 +86,7 @@ static const gchar *genders[] = {
};
#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 */
@@ -208,8 +210,8 @@ static gboolean append_field_value(PurpleNotifyUserInfo *user_info, const gchar
return FALSE;
}
- static PurpleNotifyUserInfo *
-info_to_notify_user_info(const contact_info *info)
+static PurpleNotifyUserInfo *
+ info_to_notify_user_info(const contact_info *info)
{
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
const gchar *intro;
@@ -826,7 +828,8 @@ 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;
@@ -953,23 +956,23 @@ void qq_send_packet_get_buddies_levels(PurpleConnection *gc)
GList *node = qd->buddies;
gint bytes = 0;
- 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);
- bytes += 1;
-
- while (NULL != node) {
- q_bud = (qq_buddy *) node->data;
- if (NULL != q_bud) {
- bytes += qq_put32(buf + bytes, q_bud->uid);
- }
- 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(qd, QQ_CMD_GET_LEVEL, 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)
@@ -1008,8 +1011,8 @@ void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
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 packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n",
+ 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);
@@ -1032,3 +1035,4 @@ void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
}
g_free(decr_buf);
}
+
diff --git a/libpurple/protocols/qq/buddy_info.h b/libpurple/protocols/qq/buddy_info.h
index 63be1da164..ee66575144 100644
--- a/libpurple/protocols/qq/buddy_info.h
+++ b/libpurple/protocols/qq/buddy_info.h
@@ -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
@@ -54,5 +77,4 @@ void qq_info_query_free(qq_data *qd);
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
diff --git a/libpurple/protocols/qq/buddy_list.c b/libpurple/protocols/qq/buddy_list.c
index de6b4a5010..6ad75db382 100644
--- a/libpurple/protocols/qq/buddy_list.c
+++ b/libpurple/protocols/qq/buddy_list.c
@@ -32,12 +32,11 @@
#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 "qq_base.h"
#include "group.h"
#include "group_find.h"
#include "group_internal.h"
@@ -48,16 +47,14 @@
#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)
@@ -81,7 +78,7 @@ void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
/* 003-004 */
bytes += qq_put16(raw_data + bytes, 0x0000);
- qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_ONLINE, raw_data, 5);
+ qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5);
qd->last_get_online = time(NULL);
}
@@ -102,7 +99,7 @@ void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position)
* March 22, found the 00,00,00 starts to work as well */
bytes += qq_put8(raw_data + bytes, 0x00);
- qq_send_cmd(qd, QQ_CMD_GET_FRIENDS_LIST, raw_data, bytes);
+ qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes);
}
/* get all list, buddies & Quns with groupsid support */
@@ -123,36 +120,55 @@ void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 positi
qq_send_cmd(qd, QQ_CMD_GET_ALL_LIST_WITH_GROUP, raw_data, bytes);
}
-static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe)
+/* parse the data into qq_buddy_status */
+static gint get_buddy_status(qq_buddy_status *bs, guint8 *data)
{
- GString *dump;
-
- g_return_if_fail(fe != NULL);
-
- qq_buddy_status_dump_unclear(fe->s);
-
- 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);
+ gint bytes = 0;
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Online buddy entry, %s", dump->str);
- g_string_free(dump, TRUE);
+ 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 */
-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)
{
qq_data *qd;
gint len, bytes, bytes_buddy;
+ gint count;
guint8 *data, position;
PurpleBuddy *b;
qq_buddy *q_bud;
- qq_friends_online_entry *fe;
+ qq_buddy_online bo;
- g_return_if_fail(buf != NULL && buf_len != 0);
+ g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
qd = (qq_data *) gc->proto_data;
len = buf_len;
@@ -162,68 +178,68 @@ void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnec
if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
- return;
+ return -1;
}
- qq_show_packet("Get buddies online reply packet", data, len);
+ /* qq_show_packet("Get buddies online reply packet", data, len); */
bytes = 0;
bytes += qq_get8(&position, data + bytes);
- fe = g_newa(qq_friends_online_entry, 1);
- fe->s = g_newa(qq_buddy_status, 1);
-
+ 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 */
- /* ATTTENTION! NEWED in the sub function, but FREED here */
/* 000-030 qq_buddy_status */
- bytes += qq_buddy_status_read(fe->s, data + bytes);
- /* 031-032: unknown4 */
- bytes += qq_get16(&fe->unknown1, data + bytes);
- /* 033-033: flag1 */
- bytes += qq_get8(&fe->flag1, data + bytes);
+ 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(&fe->comm_flag, data + bytes);
+ bytes += qq_get8(&bo.comm_flag, data + bytes);
/* 035-036: */
- bytes += qq_get16(&fe->unknown2, data + bytes);
+ bytes += qq_get16(&bo.unknown2, data + bytes);
/* 037-037: */
- bytes += qq_get8(&fe->ending, data + bytes); /* 0x00 */
+ bytes += qq_get8(&bo.ending, data + bytes); /* 0x00 */
- if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+ 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);
- g_free(fe->s->ip);
- g_free(fe->s->unknown_key);
continue;
} /* check if it is a valid entry */
- 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));
+ 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) { /* 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 {
+ if (q_bud == NULL) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
+ "Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
+ continue;
}
-
- g_free(fe->s->ip);
- g_free(fe->s->unknown_key);
+ /* 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) {
@@ -231,30 +247,25 @@ void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnec
"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);
- }
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n",
+ count, (guint) position);
+ return position;
}
/* process reply for get_buddies_list */
-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)
{
qq_data *qd;
qq_buddy *q_bud;
- gint len, bytes_expected, i;
+ gint len, bytes_expected, count;
gint bytes, buddy_bytes;
guint16 position, unknown;
guint8 *data, pascal_len;
gchar *name;
PurpleBuddy *b;
- g_return_if_fail(buf != NULL && buf_len != 0);
+ g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
qd = (qq_data *) gc->proto_data;
len = buf_len;
@@ -262,12 +273,12 @@ void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnecti
if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
- return;
+ return -1;
}
bytes = 0;
bytes += qq_get16(&position, data + bytes);
/* the following data is buddy list in this packet */
- i = 0;
+ count = 0;
while (bytes < len) {
q_bud = g_new0(qq_buddy, 1);
/* set flag */
@@ -285,16 +296,7 @@ void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnecti
bytes += pascal_len;
bytes += qq_get16(&unknown, data + bytes);
- /* 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 += qq_get8(&q_bud->flag1, 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;
@@ -306,13 +308,13 @@ void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnecti
g_free(q_bud);
continue;
} else {
- i++;
+ count++;
}
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);
+ "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);
}
name = uid_to_purple_name(q_bud->uid);
@@ -332,15 +334,13 @@ void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnecti
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);
- }
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n",
+ count, (guint) position);
+ return 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)
{
qq_data *qd;
gint len, i, j;
@@ -352,7 +352,7 @@ void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleC
guint8 type, groupid;
qq_group *group;
- g_return_if_fail(buf != NULL && buf_len != 0);
+ g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
qd = (qq_data *) gc->proto_data;
len = buf_len;
@@ -360,11 +360,11 @@ void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleC
if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
- return;
+ return -1;
}
bytes += qq_get8(&sub_cmd, data + bytes);
- g_return_if_fail(sub_cmd == 0x01);
+ g_return_val_if_fail(sub_cmd == 0x01, -1);
bytes += qq_get8(&reply_code, data + bytes);
if(0 != reply_code) {
@@ -418,5 +418,273 @@ void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleC
"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);
+ 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, 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) ) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+ return;
+ }
+
+ 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;
+ }
+
+ /* 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);
+ }
+}
+
+/* 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;
+ qq_buddy_status bs;
+ gchar *name;
+
+ g_return_if_fail(buf != NULL && buf_len != 0);
+
+ qd = (qq_data *) gc->proto_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 (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);
+
+ /* 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 (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);
+}
+
+/* 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;
+ }
}
diff --git a/libpurple/protocols/qq/buddy_list.h b/libpurple/protocols/qq/buddy_list.h
index c546ccd6c6..976ec18aef 100644
--- a/libpurple/protocols/qq/buddy_list.h
+++ b/libpurple/protocols/qq/buddy_list.h
@@ -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
diff --git a/libpurple/protocols/qq/buddy_opt.c b/libpurple/protocols/qq/buddy_opt.c
index 6aee8b4374..f94f8b9a01 100644
--- a/libpurple/protocols/qq/buddy_opt.c
+++ b/libpurple/protocols/qq/buddy_opt.c
@@ -34,7 +34,7 @@
#include "crypt.h"
#include "header_info.h"
#include "im.h"
-#include "keep_alive.h"
+#include "qq_base.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "utils.h"
@@ -67,7 +67,7 @@ static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
g_return_if_fail(uid > 0);
g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
- qq_send_cmd(qd, QQ_CMD_DEL_FRIEND, (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 */
@@ -95,7 +95,7 @@ static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
/* 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(qd, QQ_CMD_ADD_FRIEND_WO_AUTH, (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 */
req = g_new0(qq_add_buddy_request, 1);
@@ -481,7 +481,7 @@ PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboo
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);
diff --git a/libpurple/protocols/qq/buddy_status.c b/libpurple/protocols/qq/buddy_status.c
deleted file mode 100644
index d96b65f37a..0000000000
--- a/libpurple/protocols/qq/buddy_status.c
+++ /dev/null
@@ -1,278 +0,0 @@
-/**
- * @file buddy_status.c
- *
- * purple
- *
- * Purple is the legal property ofr 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 "utils.h"
-
-#include "qq_network.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(qq_buddy_status *s, guint8 *data)
-{
- gint bytes = 0;
-
- g_return_val_if_fail(data != NULL && s != NULL, -1);
-
- /* 000-003: uid */
- bytes += qq_get32(&s->uid, data + bytes);
- /* 004-004: 0x01 */
- bytes += qq_get8(&s->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 */
- s->ip = g_new0(guint8, 4);
- bytes += qq_getdata(s->ip, 4, data + bytes);
- /* port info is no longer here either */
- /* 009-010: port */
- bytes += qq_get16(&s->port, data + bytes);
- /* 011-011: 0x00 */
- bytes += qq_get8(&s->unknown2, data + bytes);
- /* 012-012: status */
- bytes += qq_get8(&s->status, data + bytes);
- /* 013-014: client_version */
- bytes += qq_get16(&s->client_version, data + bytes);
- /* 015-030: unknown key */
- s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH);
- bytes += qq_getdata(s->unknown_key, QQ_KEY_LENGTH, data + bytes);
-
- 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[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, 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) ) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
- return;
- }
-
- 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\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);
- }
-}
-
-/* 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;
- 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);
-
- if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
- return;
- }
-
- s = g_new0(qq_buddy_status, 1);
- bytes = 0;
- /* 000-030: qq_buddy_status */
- bytes += qq_buddy_status_read(s, data + bytes);
- /* 031-034: 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);
-
- 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);
-}
diff --git a/libpurple/protocols/qq/buddy_status.h b/libpurple/protocols/qq/buddy_status.h
deleted file mode 100644
index 18c9b77dab..0000000000
--- a/libpurple/protocols/qq/buddy_status.h
+++ /dev/null
@@ -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(qq_buddy_status *s, guint8 *data);
-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
diff --git a/libpurple/protocols/qq/char_conv.c b/libpurple/protocols/qq/char_conv.c
index 93b49b975b..4c0d0a451f 100644
--- a/libpurple/protocols/qq/char_conv.c
+++ b/libpurple/protocols/qq/char_conv.c
@@ -37,7 +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 */
+#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,
@@ -113,9 +113,9 @@ static gchar *_my_convert(const gchar *str, gssize len, const gchar *to_charset,
}
/* conversion error */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_CONVERT", "%s\n", error->message);
- qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT",
(guint8 *) str, (len == -1) ? strlen(str) : len,
"Dump failed text");
@@ -123,9 +123,11 @@ static gchar *_my_convert(const gchar *str, gssize len, const gchar *to_charset,
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;
@@ -149,7 +151,7 @@ gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
gint bytes = 0;
/* checked qq_show_packet OK */
- qq_show_packet("QQ_MESG recv for font style", data, len);
+ /* qq_show_packet("QQ_MESG recv for font style", data, len); */
bytes += qq_get8(&font_attr, data + bytes);
bytes += qq_getdata(color, 3, data + bytes); /* red,green,blue */
@@ -229,7 +231,7 @@ gchar *qq_smiley_to_purple(gchar *text)
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) {
@@ -276,3 +278,17 @@ gchar *purple_smiley_to_qq(gchar *text)
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);
+}
+
+
diff --git a/libpurple/protocols/qq/char_conv.h b/libpurple/protocols/qq/char_conv.h
index fc3e671af1..1d6c90bd38 100644
--- a/libpurple/protocols/qq/char_conv.h
+++ b/libpurple/protocols/qq/char_conv.h
@@ -40,5 +40,5 @@ gchar *qq_to_utf8(const gchar *str, const gchar *from_charset);
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
diff --git a/libpurple/protocols/qq/crypt.c b/libpurple/protocols/qq/crypt.c
index 296034610d..f2a1df5e0e 100644
--- a/libpurple/protocols/qq/crypt.c
+++ b/libpurple/protocols/qq/crypt.c
@@ -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 @@ the golden ratio: Sqrt(5/4) - 1/2 ~ 0.618034 multiplied by 2^32.
#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 @@ the golden ratio: Sqrt(5/4) - 1/2 ~ 0.618034 multiplied by 2^32.
/* 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 @@ static void qq_encipher(guint32 *const v, const guint32 *const k, guint32 *const
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)
{
- /* prepare input text */
- if (!*is_header)
- *(guint64 *) plain ^= **(guint64 **) crypted_pre_8;
+ /* 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] */
- /* encrypt it */
- qq_encipher((guint32 *) plain, (guint32 *) key, (guint32 *) *crypted);
+ /* prepare input text */
+#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
+ }
+ }
- **(guint64 **) crypted ^= *(guint64 *) plain_pre_8;
+ g_memmove(ptr_p, plain, 8);
+ g_memmove(ptr_k, key, 16);
+ g_memmove(ptr_c, *crypted, 8);
+ /* encrypt it */
+ qq_encipher(ptr_p, ptr_k, ptr_c);
+
+ g_memmove(plain, ptr_p, 8);
+ g_memmove(*crypted, ptr_c, 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 @@ void qq_encrypt(const guint8 *const instr, gint instrlen,
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 @@ static gint decrypt_block(const guint8 **crypt_buff, const gint instrlen,
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(ptr_v, ptr_k, ptr_v);
- qq_decipher((guint32 *) decrypted, (guint32 *) key, (guint32 *) decrypted);
+ g_memmove(decrypted, ptr_v, 8);
*context_start += 8;
*crypt_buff += 8;
@@ -218,6 +302,10 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen,
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 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen,
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,22 +388,6 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen,
}
}
}
- return 1;
-}
-
-/* return 1 is succeed, otherwise return 0
-gint qq_crypt(gint flag,
- const guint8 *const instr, gint instrlen,
- const guint8 *const key,
- guint8 *outstr, gint *outstrlen_ptr)
-{
- if (flag == DECRYPT)
- return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr);
- else if (flag == ENCRYPT)
- qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr);
- else
- return 0;
return 1;
}
-*/
diff --git a/libpurple/protocols/qq/crypt.h b/libpurple/protocols/qq/crypt.h
index 49ae65aa8c..74c56a0b42 100644
--- a/libpurple/protocols/qq/crypt.h
+++ b/libpurple/protocols/qq/crypt.h
@@ -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,14 +37,4 @@ void qq_encrypt(const guint8 *const instr, gint instrlen,
gint qq_decrypt(const guint8 *const instr, gint instrlen,
const guint8 *const key,
guint8 *outstr, gint *outstrlen_ptr);
-
-/*
-#define DECRYPT 0x00
-#define ENCRYPT 0x01
-
-gint qq_crypt(gint flag,
- const guint8 *const instr, gint instrlen,
- const guint8 *const key,
- guint8 *outstr, gint *outstrlen_ptr);
-*/
#endif
diff --git a/libpurple/protocols/qq/file_trans.c b/libpurple/protocols/qq/file_trans.c
index bc6c2f024d..afdb89eead 100644
--- a/libpurple/protocols/qq/file_trans.c
+++ b/libpurple/protocols/qq/file_trans.c
@@ -76,26 +76,10 @@ static guint32 _encrypt_qq_uid(guint32 uid, guint32 key)
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;
@@ -118,11 +102,7 @@ static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5)
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 gint _qq_get_file_header(qq_file_header *fh, guint8 *buf)
@@ -265,7 +245,7 @@ static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16
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;
raw_data = g_newa(guint8, MAX_PACKET_SIZE);
@@ -395,7 +375,7 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
gint bytes;
guint32 fragment_size = 1000;
- gchar *filename;
+ const char *filename;
gint filename_len, filesize;
qq_data *qd;
ft_info *info;
@@ -403,7 +383,7 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
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);
@@ -423,11 +403,11 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_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;
diff --git a/libpurple/protocols/qq/group_conv.c b/libpurple/protocols/qq/group_conv.c
index d75fe130f5..f49bd32f00 100644
--- a/libpurple/protocols/qq/group_conv.c
+++ b/libpurple/protocols/qq/group_conv.c
@@ -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 @@ void qq_group_conv_refresh_online_member(PurpleConnection *gc, qq_group *group)
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) {
diff --git a/libpurple/protocols/qq/group_find.c b/libpurple/protocols/qq/group_find.c
index 6b8ea92820..317de8b2c7 100644
--- a/libpurple/protocols/qq/group_find.c
+++ b/libpurple/protocols/qq/group_find.c
@@ -138,6 +138,9 @@ qq_group *qq_group_find_by_channel(PurpleConnection *gc, gint channel)
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;
diff --git a/libpurple/protocols/qq/group_free.c b/libpurple/protocols/qq/group_free.c
index f1eab11795..0900bb4e01 100644
--- a/libpurple/protocols/qq/group_free.c
+++ b/libpurple/protocols/qq/group_free.c
@@ -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 @@ void qq_group_free(qq_group *group)
{
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);
}
diff --git a/libpurple/protocols/qq/group_im.c b/libpurple/protocols/qq/group_im.c
index 23e62d87a8..aada103d70 100644
--- a/libpurple/protocols/qq/group_im.c
+++ b/libpurple/protocols/qq/group_im.c
@@ -65,7 +65,7 @@ void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar
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;
@@ -311,9 +311,7 @@ void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_grou
qd = (qq_data *) gc->proto_data;
- qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
- data, data_len,
- "group im hex dump");
+ /* qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump"); */
im_group = g_newa(qq_recv_group_im, 1);
@@ -379,6 +377,9 @@ void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_grou
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));
}
diff --git a/libpurple/protocols/qq/group_info.c b/libpurple/protocols/qq/group_info.c
index f9cefcc9d8..b7da8a4d3d 100644
--- a/libpurple/protocols/qq/group_info.c
+++ b/libpurple/protocols/qq/group_info.c
@@ -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
@@ -77,6 +76,27 @@ void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group)
}
/* 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[16] = {0};
@@ -87,7 +107,7 @@ void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group)
/* 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;
}
@@ -113,7 +133,7 @@ void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group)
}
if (num <= 0) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n");
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member info needs to be updated now.\n");
return;
}
@@ -154,6 +174,7 @@ void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnectio
guint32 unknown4;
guint8 unknown1;
gint bytes, num;
+ gchar *notice;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
@@ -163,7 +184,7 @@ void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnectio
g_return_if_fail(internal_group_id > 0);
bytes += qq_get32(&(external_group_id), data + bytes);
- g_return_if_fail(internal_group_id > 0);
+ 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) {
@@ -183,13 +204,22 @@ void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnectio
bytes += qq_get32(&(group->group_category), data + bytes);
bytes += qq_get16(&max_members, data + bytes);
bytes += qq_get8(&unknown1, data + bytes);
- bytes += qq_get32(&(unknown4), data + bytes); /* versionID */
-
+ /* 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, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
+ bytes += convert_as_pascal_string(data + bytes, &notice, 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);
num = 0;
/* now comes the member list separated by 0x00 */
@@ -199,9 +229,11 @@ void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnectio
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;
@@ -221,11 +253,16 @@ void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnectio
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);
- }
- else {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
+ "Conversation \"%s\" is not open, do not set topic\n", group->group_name_utf8);
+ return;
}
+
+ /* 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, gint len, PurpleConnection *gc)
@@ -282,6 +319,7 @@ void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnect
guint16 unknown;
qq_group *group;
qq_buddy *member;
+ gchar *nick;
g_return_if_fail(data != NULL && len > 0);
@@ -304,11 +342,24 @@ void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnect
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, &(member->nickname), QQ_CHARSET_DEFAULT);
+ bytes += convert_as_pascal_string(data + bytes, &nick, QQ_CHARSET_DEFAULT);
bytes += qq_get16(&unknown, data + bytes);
- bytes += qq_get8(&(member->flag1), 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(bytes > len) {
diff --git a/libpurple/protocols/qq/group_info.h b/libpurple/protocols/qq/group_info.h
index dfa3d8aa19..d6497d5350 100644
--- a/libpurple/protocols/qq/group_info.h
+++ b/libpurple/protocols/qq/group_info.h
@@ -31,7 +31,10 @@
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, 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);
diff --git a/libpurple/protocols/qq/group_network.c b/libpurple/protocols/qq/group_network.c
index f872478b4f..71de4950cb 100644
--- a/libpurple/protocols/qq/group_network.c
+++ b/libpurple/protocols/qq/group_network.c
@@ -75,6 +75,30 @@ const gchar *qq_group_cmd_get_desc(qq_group_cmd cmd)
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";
}
diff --git a/libpurple/protocols/qq/group_network.h b/libpurple/protocols/qq/group_network.h
index 972eb95430..7a287a34f6 100644
--- a/libpurple/protocols/qq/group_network.h
+++ b/libpurple/protocols/qq/group_network.h
@@ -42,7 +42,20 @@ typedef enum {
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 {
diff --git a/libpurple/protocols/qq/header_info.c b/libpurple/protocols/qq/header_info.c
index 8366dd1bef..9c06db2944 100644
--- a/libpurple/protocols/qq/header_info.c
+++ b/libpurple/protocols/qq/header_info.c
@@ -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 @@ const gchar *qq_get_cmd_desc(gint type)
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 @@ const gchar *qq_get_cmd_desc(gint type)
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 @@ const gchar *qq_get_source_str(gint source)
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";
}
}
diff --git a/libpurple/protocols/qq/header_info.h b/libpurple/protocols/qq/header_info.h
index d56daf7e97..373e782ac8 100644
--- a/libpurple/protocols/qq/header_info.h
+++ b/libpurple/protocols/qq/header_info.h
@@ -42,8 +42,8 @@ enum {
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 @@ enum {
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
diff --git a/libpurple/protocols/qq/im.c b/libpurple/protocols/qq/im.c
index 5afe847d18..1962d8aadc 100644
--- a/libpurple/protocols/qq/im.c
+++ b/libpurple/protocols/qq/im.c
@@ -79,7 +79,7 @@ struct _qq_recv_normal_im_common {
guint16 sender_ver;
guint32 sender_uid;
guint32 receiver_uid;
- guint8 *session_md5;
+ guint8 session_md5[QQ_KEY_LENGTH];
guint16 normal_im_type;
};
@@ -109,7 +109,7 @@ struct _qq_recv_im_header {
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;
};
@@ -179,7 +179,7 @@ guint8 *qq_get_send_im_tail(const gchar *font_color,
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;
}
@@ -237,10 +237,7 @@ static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_
bytes += qq_get16(&(common->sender_ver), data + bytes);
bytes += qq_get32(&(common->sender_uid), data + bytes);
bytes += qq_get32(&(common->receiver_uid), data + bytes);
-
- common->session_md5 = g_memdup(data + bytes, QQ_KEY_LENGTH);
- bytes += QQ_KEY_LENGTH;
-
+ 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 */
@@ -261,6 +258,8 @@ static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_norm
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;
@@ -308,9 +307,16 @@ static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_norm
} /* if im_text->msg_type */
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);
@@ -353,9 +359,9 @@ static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection
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",
+ "Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n",
common->sender_uid, common->receiver_uid,
- qq_get_source_str (common->sender_ver));
+ 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;
@@ -384,13 +390,11 @@ static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection
im_unprocessed->length = len - bytes;
/* a simple process here, maybe more later */
purple_debug (PURPLE_DEBUG_WARNING, "QQ",
- "Normal IM, unprocessed type [0x%04x], unknown [0x%02x], len %d\n",
- common->normal_im_type, im_unprocessed->unknown, im_unprocessed->length);
- g_free (common->session_md5);
+ "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 */
@@ -602,7 +606,7 @@ void qq_process_recv_im(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection
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_getdata((guint8 *) & (im_header->sender_ip), 4, data + bytes);
+ 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 */
diff --git a/libpurple/protocols/qq/keep_alive.c b/libpurple/protocols/qq/keep_alive.c
deleted file mode 100644
index 8eafb24e00..0000000000
--- a/libpurple/protocols/qq/keep_alive.c
+++ /dev/null
@@ -1,180 +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 "qq_network.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[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 */
-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);
-}
diff --git a/libpurple/protocols/qq/packet_parse.c b/libpurple/protocols/qq/packet_parse.c
index 7248647dd5..8698a78dc8 100644
--- a/libpurple/protocols/qq/packet_parse.c
+++ b/libpurple/protocols/qq/packet_parse.c
@@ -27,7 +27,6 @@
#include "packet_parse.h"
#include "debug.h"
-
/*------------------------------------------------PUT------------------------------------------------*/
/* note:
@@ -68,7 +67,6 @@ gint qq_get16(guint16 *w, guint8 *buf)
return sizeof(w_dest);
}
-
/* read four bytes as "guint32" from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
gint qq_get32(guint32 *dw, guint8 *buf)
@@ -83,6 +81,11 @@ gint qq_get32(guint32 *dw, guint8 *buf)
return sizeof(dw_dest);
}
+gint qq_getIP(struct in_addr *ip, guint8 *buf)
+{
+ 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 */
@@ -158,6 +161,11 @@ gint qq_put32(guint8 *buf, guint32 dw)
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 */
diff --git a/libpurple/protocols/qq/packet_parse.h b/libpurple/protocols/qq/packet_parse.h
index 20dc7154e4..6c0cf2b5f0 100644
--- a/libpurple/protocols/qq/packet_parse.h
+++ b/libpurple/protocols/qq/packet_parse.h
@@ -37,15 +37,23 @@
*/
#define MAX_PACKET_SIZE 65535
+#ifndef _WIN32
+#include <netinet/in.h>
+#else
+#include "win32dep.h"
+#endif
+
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);
/*
diff --git a/libpurple/protocols/qq/qq.c b/libpurple/protocols/qq/qq.c
index e243153b17..35968d8422 100644
--- a/libpurple/protocols/qq/qq.c
+++ b/libpurple/protocols/qq/qq.c
@@ -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,8 +51,8 @@
#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_network.h"
@@ -237,79 +237,111 @@ static gchar *_qq_status_text(PurpleBuddy *b)
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 (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);
+ 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_free(ip_str);
+ g_string_free(str, TRUE);
+ }
+
+ 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;
-
- 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";
- */
+ qq_buddy *q_bud;
+
+ if (!b || !(q_bud = b->proto_data)) {
+ return NULL;
}
+ 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;
}
@@ -393,6 +425,7 @@ static int _qq_chat_send(PurpleConnection *gc, int channel, const char *message,
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);
@@ -479,7 +512,7 @@ static void _qq_menu_show_login_info(PurplePluginAction *action)
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");
@@ -487,7 +520,7 @@ static void _qq_menu_show_login_info(PurplePluginAction *action)
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>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"), qd->my_ip);
+ 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");
@@ -636,30 +669,6 @@ static GList *_qq_buddy_menu(PurpleBlistNode * node)
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)
@@ -680,8 +689,8 @@ static gchar *_qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, c
return chat_name_to_purple_name(who);
}
-PurplePlugin *my_protocol = NULL;
-static PurplePluginProtocolInfo prpl_info = {
+static PurplePluginProtocolInfo prpl_info =
+{
OPT_PROTO_CHAT_TOPIC | OPT_PROTO_USE_POINTSIZE,
NULL, /* user_splits */
NULL, /* protocol_options */
@@ -718,8 +727,8 @@ static PurplePluginProtocolInfo prpl_info = {
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 */
@@ -738,18 +747,17 @@ static PurplePluginProtocolInfo prpl_info = {
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 */
NULL, /* send_raw */
NULL, /* roomlist_room_serialize */
+ NULL, /* unregister_user */
+ NULL, /* send_attention */
+ NULL, /* get attention_types */
- /* padding */
- NULL,
- NULL,
- NULL,
- sizeof(PurplePluginProtocolInfo), /* struct_size */
+ sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL
};
@@ -803,7 +811,14 @@ static void init_plugin(PurplePlugin *plugin)
option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- my_protocol = plugin;
+ 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);
purple_prefs_add_none("/plugins/prpl/qq");
purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
diff --git a/libpurple/protocols/qq/qq.h b/libpurple/protocols/qq/qq.h
index 32c2f47a06..0eeea31e41 100644
--- a/libpurple/protocols/qq/qq.h
+++ b/libpurple/protocols/qq/qq.h
@@ -34,7 +34,6 @@
#include "proxy.h"
#include "roomlist.h"
-#define QQ_FACES 100
#define QQ_KEY_LENGTH 16
#define QQ_DEBUG 1 /* whether we are doing DEBUG */
@@ -45,6 +44,13 @@ const char *qq_win32_buddy_icon_dir(void);
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;
@@ -52,10 +58,10 @@ struct _qq_buddy {
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;
@@ -88,12 +94,12 @@ struct _qq_data {
gint fd; /* socket file handler */
gint tx_handler; /* socket can_write handle, use in udp connecting and tcp send out */
- GList *send_trans; /* check ack packet and resend */
- guint resend_timeout;
-
- guint8 rcv_window[1 << 13]; /* windows for check duplicate packet */
- GQueue *rcv_trans; /* queue to store packet can not process before login */
+ 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;
@@ -103,10 +109,12 @@ struct _qq_data {
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 */
@@ -119,11 +127,11 @@ struct _qq_data {
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 */
PurpleRoomlist *roomlist;
diff --git a/libpurple/protocols/qq/login_logout.c b/libpurple/protocols/qq/qq_base.c
index 15fd4c01a6..e4f00c793d 100644
--- a/libpurple/protocols/qq/login_logout.c
+++ b/libpurple/protocols/qq/qq_base.c
@@ -1,5 +1,5 @@
/**
- * @file login_logout.c
+ * @file qq_base.c
*
* purple
*
@@ -29,12 +29,11 @@
#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 "qq_base.h"
#include "packet_parse.h"
#include "qq.h"
#include "qq_network.h"
@@ -44,13 +43,6 @@
#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,
@@ -114,23 +106,23 @@ typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
struct _qq_login_reply_ok {
guint8 result;
- guint8 *session_key;
+ guint8 session_key[QQ_KEY_LENGTH];
guint32 uid;
- guint8 client_ip[4]; /* those detected by server */
+ struct in_addr client_ip; /* those detected by server */
guint16 client_port;
- guint8 server_ip[4];
+ struct in_addr server_ip;
guint16 server_port;
time_t login_time;
guint8 unknown1[26];
- guint8 unknown_server1_ip[4];
+ struct in_addr unknown_server1_ip;
guint16 unknown_server1_port;
- guint8 unknown_server2_ip[4];
+ struct in_addr unknown_server2_ip;
guint16 unknown_server2_port;
guint16 unknown2; /* 0x0001 */
guint16 unknown3; /* 0x0000 */
guint8 unknown4[32];
guint8 unknown5[12];
- guint8 last_client_ip[4];
+ struct in_addr last_client_ip;
time_t last_login_time;
guint8 unknown6[8];
};
@@ -138,33 +130,24 @@ struct _qq_login_reply_ok {
struct _qq_login_reply_redirect {
guint8 result;
guint32 uid;
- guint8 new_server_ip[4];
+ struct in_addr new_server_ip;
guint16 new_server_port;
};
/* generate a md5 key using uid and session_key */
-static guint8 *gen_session_md5(gint uid, guint8 *session_key)
+static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
{
- guint8 *src, md5_str[QQ_KEY_LENGTH];
- PurpleCipher *cipher;
- PurpleCipherContext *context;
-
- src = g_newa(guint8, 20);
- /* bug found by QuLogic */
- memcpy(src, &uid, sizeof(uid));
- memcpy(src + sizeof(uid), 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);
+ 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 gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
+static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
{
gint bytes;
qq_data *qd;
@@ -177,17 +160,16 @@ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
/* 000-000: reply code */
bytes += qq_get8(&lrop.result, data + bytes);
/* 001-016: session key */
- lrop.session_key = g_memdup(data + bytes, QQ_KEY_LENGTH);
- bytes += QQ_KEY_LENGTH;
+ 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_getdata((guint8 *) &lrop.client_ip, 4, data + bytes);
+ 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_getdata((guint8 *) &lrop.server_ip, 4, data + bytes);
+ 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 */
@@ -195,11 +177,11 @@ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
/* 037-062: 26 bytes, unknown */
bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
/* 063-066: unknown server1 ip address */
- bytes += qq_getdata((guint8 *) &lrop.unknown_server1_ip, 4, data + bytes);
+ 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_getdata((guint8 *) &lrop.unknown_server2_ip, 4, data + bytes);
+ 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 */
@@ -211,7 +193,7 @@ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
/* 111-122: 12 bytes unknown */
bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
/* 123-126: login IP of last session */
- bytes += qq_getdata((guint8 *) &lrop.last_client_ip, 4, data + bytes);
+ 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 */
@@ -223,47 +205,24 @@ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
} /* but we still go on as login OK */
- g_return_val_if_fail(qd->session_key == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
- qd->session_key = lrop.session_key;
+ memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key));
+ get_session_md5(qd->session_md5, qd->uid, qd->session_key);
- g_return_val_if_fail(qd->session_md5 == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
- qd->session_md5 = gen_session_md5(qd->uid, qd->session_key);
-
- g_return_val_if_fail(qd->my_ip == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
- qd->my_ip = gen_ip_str(lrop.client_ip);
+ 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 = 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);
+ 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 gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
+static gint8 process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
{
- gint bytes, ret;
qq_data *qd;
+ gint bytes;
qq_login_reply_redirect_packet lrrp;
qd = (qq_data *) gc->proto_data;
@@ -273,7 +232,7 @@ static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint
/* 001-004: login uid */
bytes += qq_get32(&lrrp.uid, data + bytes);
/* 005-008: redirected new server IP */
- bytes += qq_getdata(lrrp.new_server_ip, 4, data + bytes);
+ bytes += qq_getIP(&lrrp.new_server_ip, data + bytes);
/* 009-010: redirected new server port */
bytes += qq_get16(&lrrp.new_server_port, data + bytes);
@@ -281,30 +240,24 @@ static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint
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 {
- /* 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 = gen_ip_str(lrrp.new_server_ip);
- qd->real_port = lrrp.new_server_port;
- qd->is_redirect = TRUE;
-
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port);
-
- ret = QQ_LOGIN_REPLY_REDIRECT;
+ 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 ret;
+ return QQ_LOGIN_REPLY_REDIRECT;
}
/* process login reply which says wrong password */
-static gint _qq_process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len)
+static gint8 process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len)
{
gchar *server_reply, *server_reply_utf8;
server_reply = g_new0(gchar, len);
@@ -314,11 +267,11 @@ static gint _qq_process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint
g_free(server_reply);
g_free(server_reply_utf8);
- return QQ_LOGIN_REPLY_PWD_ERROR;
+ return QQ_LOGIN_REPLY_ERR_PWD;
}
/* request before login */
-void qq_send_packet_request_login_token(PurpleConnection *gc)
+void qq_send_packet_token(PurpleConnection *gc)
{
qq_data *qd;
guint8 buf[16] = {0};
@@ -329,11 +282,12 @@ void qq_send_packet_request_login_token(PurpleConnection *gc)
bytes += qq_put8(buf + bytes, 0);
- qq_send_data(qd, QQ_CMD_REQUEST_LOGIN_TOKEN, buf, bytes);
+ qd->send_seq++;
+ qq_send_data(qd, QQ_CMD_TOKEN, qd->send_seq, TRUE, buf, bytes);
}
/* send login packet to QQ server */
-static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token)
+void qq_send_packet_login(PurpleConnection *gc)
{
qq_data *qd;
guint8 *buf, *raw_data;
@@ -344,19 +298,24 @@ static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guin
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 */
- if (qd->inikey) {
- g_free(qd->inikey);
+#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);
}
- qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
+#endif
bytes = 0;
/* now generate the encrypted data
- * 000-015 use pwkey as key to encrypt empty string */
- qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data + bytes, &encrypted_len);
+ * 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);
@@ -371,9 +330,9 @@ static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guin
/* 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, token_length);
+ bytes += qq_put8(raw_data + bytes, qd->token_len);
/* 070-093, login token, normally 24 bytes */
- bytes += qq_putdata(raw_data + bytes, token, token_length);
+ 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 */
@@ -386,43 +345,50 @@ static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guin
bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
- qq_send_data(qd, QQ_CMD_LOGIN, buf, bytes);
+ qd->send_seq++;
+ qq_send_data(qd, QQ_CMD_LOGIN, qd->send_seq, TRUE, buf, bytes);
}
-void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len)
{
qq_data *qd;
- gchar *error_msg;
+ guint8 ret;
+ int token_len;
- g_return_if_fail(buf != NULL && buf_len != 0);
+ 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;
- 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");
- }
- qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
- buf+2, buf_len-2,
- "<<< got a token -> [default] decrypt and dump");
- qq_send_packet_login(gc, buf_len-2, buf+2);
- } else {
+ 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);
- if (error_msg) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
- g_free(error_msg);
- } else {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Error requesting login token"));
- }
+ 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 */
@@ -433,88 +399,125 @@ void qq_send_packet_logout(PurpleConnection *gc)
qd = (qq_data *) gc->proto_data;
for (i = 0; i < 4; i++)
- qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
+ 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 */
-void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+guint8 qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
{
- gint len, ret, bytes;
- guint8 *data;
qq_data *qd;
+ guint8 *data;
+ gint data_len;
gchar* error_msg;
- g_return_if_fail(buf != NULL && buf_len != 0);
+ g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
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);
+ 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", "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]);
- qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
- data, len,
- ">>> [default] decrypt and dump");
- error_msg = try_dump_as_gbk(data, len);
- if (error_msg) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
- g_free(error_msg);
- }
- 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;
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "No idea how to decrypt login reply\n");
+ return QQ_LOGIN_REPLY_ERR_MISC;
}
}
-
- 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 */
+
+ 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;
- default:{;
- }
}
+
+ 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;
}
diff --git a/libpurple/protocols/qq/login_logout.h b/libpurple/protocols/qq/qq_base.h
index aa73a42e41..32d4188512 100644
--- a/libpurple/protocols/qq/login_logout.h
+++ b/libpurple/protocols/qq/qq_base.h
@@ -1,5 +1,5 @@
/**
- * file login_logout.h
+ * file qq_base.h
*
* purple
*
@@ -22,19 +22,35 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _QQ_LOGIN_LOGOUT_H_
-#define _QQ_LOGIN_LOGOUT_H_
+#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
-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);
+#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
diff --git a/libpurple/protocols/qq/qq_network.c b/libpurple/protocols/qq/qq_network.c
index 08013030cc..f18cb63fe4 100644
--- a/libpurple/protocols/qq/qq_network.c
+++ b/libpurple/protocols/qq/qq_network.c
@@ -32,26 +32,23 @@
#endif
#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "buddy_status.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 "keep_alive.h"
-#include "im.h"
-#include "login_logout.h"
+#include "qq_base.h"
+#include "buddy_list.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "qq_trans.h"
-#include "sys_msg.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)
{
@@ -107,60 +104,6 @@ static gboolean set_new_server(qq_data *qd)
return TRUE;
}
-/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
-static guint8 *encrypt_account_password(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);
-}
-
-/* default process, decrypt and dump */
-static void process_cmd_unknow(PurpleConnection *gc, 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("Processing unknown packet", 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);
- }
-}
-
static gint packet_get_header(guint8 *header_tag, guint16 *source_tag,
guint16 *cmd, guint16 *seq, guint8 *buf)
{
@@ -172,38 +115,6 @@ static gint packet_get_header(guint8 *header_tag, guint16 *source_tag,
return bytes;
}
-/* check whether one sequence number is duplicated or not
- * return TRUE if it is duplicated, otherwise FALSE */
-static gboolean packet_is_dup(qq_data *qd, guint16 seq)
-{
- guint8 *byte, mask;
-
- g_return_val_if_fail(qd != NULL, FALSE);
-
- byte = &(qd->rcv_window[seq / 8]);
- mask = (1 << (seq % 8));
-
- if ((*byte) & mask)
- return TRUE; /* check mask */
- (*byte) |= mask;
- return FALSE; /* set mask */
-}
-
-static gboolean packet_check_ack(qq_data *qd, guint16 seq)
-{
- gpointer trans;
-
- g_return_val_if_fail(qd != NULL, FALSE);
-
- trans = qq_send_trans_find(qd, seq);
- if (trans == NULL) {
- return FALSE;
- }
-
- qq_send_trans_remove(qd, trans);
- return TRUE;
-}
-
static gboolean reconnect_later_cb(gpointer data)
{
PurpleConnection *gc;
@@ -244,85 +155,6 @@ static void reconnect_later(PurpleConnection *gc)
reconnect_later_cb, gc);
}
-static void process_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_FRIEND_CHANGE_STATUS:
- qq_process_friend_change_status(data, data_len, gc);
- break;
- default:
- process_cmd_unknow(gc, data, data_len, cmd, seq);
- break;
- }
-}
-
-static void process_cmd_reply(
- PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
-{
- /* now process the packet */
- switch (cmd) {
- case QQ_CMD_KEEP_ALIVE:
- qq_process_keep_alive_reply(data, data_len, gc);
- break;
- case QQ_CMD_UPDATE_INFO:
- qq_process_modify_info_reply(data, data_len, gc);
- break;
- case QQ_CMD_ADD_FRIEND_WO_AUTH:
- qq_process_add_buddy_reply(data, data_len, seq, gc);
- break;
- case QQ_CMD_DEL_FRIEND:
- 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_LOGIN:
- qq_process_login_reply(data, data_len, gc);
- break;
- case QQ_CMD_GET_FRIENDS_LIST:
- qq_process_get_buddies_list_reply(data, data_len, gc);
- break;
- case QQ_CMD_GET_FRIENDS_ONLINE:
- qq_process_get_buddies_online_reply(data, data_len, gc);
- 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:
- qq_process_get_all_list_with_group_reply(data, data_len, gc);
- break;
- case QQ_CMD_GET_LEVEL:
- qq_process_get_level_reply(data, data_len, gc);
- break;
- case QQ_CMD_REQUEST_LOGIN_TOKEN:
- qq_process_request_login_token_reply(data, data_len, gc);
- break;
- default:
- process_cmd_unknow(gc, data, data_len, cmd, seq);
- break;
- }
-}
-
/* process the incoming packet from qq_pending */
static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
{
@@ -330,15 +162,13 @@ static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
gint bytes, bytes_not_read;
gboolean prev_login_status;
- guint8 *new_data;
- gint new_data_len;
guint8 header_tag;
guint16 source_tag;
guint16 cmd;
guint16 seq; /* May be ack_seq or send_seq, depends on cmd */
- gboolean is_reply;
+ qq_transaction *trans;
g_return_if_fail(buf != NULL && buf_len > 0);
@@ -353,35 +183,38 @@ static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
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_source_str(source_tag));
+ 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 */
- is_reply = packet_check_ack(qd, seq);
- if ( !is_reply ) {
- if ( !qd->logged_in ) {
- /* packets before login */
- qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read);
- return; /* do not process it now */
+ 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);
}
-
- /* 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 (packet_is_dup(qd, seq)) {
- purple_debug(PURPLE_DEBUG_WARNING,
- "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
- return;
+ 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);
}
- process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
return;
}
/* this is the length of all the encrypted data (also remove tail tag */
- process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+ 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) {
@@ -394,18 +227,7 @@ static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) {
/* logged_in, but we have packets before login */
- new_data = g_newa(guint8, MAX_PACKET_SIZE);
- while (1) {
- memset(new_data, 0, MAX_PACKET_SIZE);
- new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE);
- if (new_data_len < 0) {
- break;
- }
- if (new_data_len == 0) {
- continue;
- }
- process_cmd_reply(gc, seq, cmd, new_data, new_data_len);
- }
+ qq_trans_process_before_login(qd);
}
}
@@ -460,9 +282,14 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- gc->last_received = time(NULL);
+ /* 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;
@@ -479,9 +306,10 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
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) {
@@ -515,14 +343,14 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
/* 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);
+ /*
+ 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");
+ /* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */
g_free(qd->tcp_rxqueue);
qd->tcp_rxqueue = NULL;
}
@@ -565,7 +393,10 @@ static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- gc->last_received = time(NULL);
+ /* 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) {
@@ -585,8 +416,10 @@ static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len)
g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd);
-
+ /*
+ 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) {
@@ -595,7 +428,7 @@ static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len)
if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno));
+ 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;
@@ -636,7 +469,7 @@ static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
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);
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
*/
if (qd->tx_handler == 0) {
@@ -646,13 +479,13 @@ static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
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");
- */
+ 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? */
@@ -673,70 +506,44 @@ static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
return ret;
}
-static gboolean trans_timeout(gpointer data)
+static gboolean network_timeout(gpointer data)
{
- PurpleConnection *gc;
+ PurpleConnection *gc = (PurpleConnection *) data;
qq_data *qd;
- guint8 *buf;
- gint buf_len = 0;
- guint16 cmd;
- gint retries = 0;
- int index;
-
- gc = (PurpleConnection *) data;
- g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
+ 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;
+ }
- index = 0;
- buf = g_newa(guint8, MAX_PACKET_SIZE);
+ 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;
+ }
- while (1) {
- if (index < 0) {
- /* next record is NULL */
- break;
- }
- /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */
- memset(buf, 0, MAX_PACKET_SIZE);
- buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries);
- if (buf_len <= 0) {
- /* curr record is empty, whole trans is NULL */
- break;
- }
- /* index = -1, when get last record of transactions */
-
- /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */
- if (retries > 0) {
- if (qd->use_tcp) {
- tcp_send_out(qd, buf, buf_len);
- } else {
- udp_send_out(qd, buf, buf_len);
- }
- continue;
- }
+ if (qd->itv_config.update <= 0) {
+ return TRUE;
+ }
- /* retries <= 0 */
- switch (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;
- }
- 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"));
- }
- break;
- default:
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "%s packet lost.\n", qq_get_cmd_desc(cmd));
- }
+ 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 */
@@ -750,6 +557,7 @@ static void qq_connect_cb(gpointer data, gint source, const gchar *error_message
PurpleConnection *gc;
gchar *conn_msg;
const gchar *passwd;
+ PurpleAccount *account ;
gc = (PurpleConnection *) data;
@@ -762,6 +570,7 @@ static void qq_connect_cb(gpointer data, gint source, const gchar *error_message
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;
@@ -785,12 +594,34 @@ static void qq_connect_cb(gpointer data, gint source, const gchar *error_message
/* now generate md5 processed passwd */
passwd = purple_account_get_password(purple_connection_get_account(gc));
- g_return_if_fail(qd->pwkey == NULL);
- qd->pwkey = encrypt_account_password(passwd);
- g_return_if_fail(qd->resend_timeout == 0);
- /* call trans_timeout every 5 seconds */
- qd->resend_timeout = purple_timeout_add(5000, trans_timeout, 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);
@@ -802,7 +633,7 @@ static void qq_connect_cb(gpointer data, gint source, const gchar *error_message
purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
g_free(conn_msg);
- qq_send_packet_request_login_token(gc);
+ qq_send_packet_token(gc);
}
static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
@@ -933,7 +764,7 @@ static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_me
return;
}
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno));
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %s\n", g_strerror(errno));
close(fd);
}
@@ -1033,16 +864,17 @@ void qq_disconnect(PurpleConnection *gc)
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 (qd->resend_timeout > 0) {
- purple_timeout_remove(qd->resend_timeout);
- qd->resend_timeout = 0;
- }
-
if (gc->inpa > 0) {
purple_input_remove(gc->inpa);
gc->inpa = 0;
@@ -1086,35 +918,20 @@ void qq_disconnect(PurpleConnection *gc)
qd->udp_query_data = NULL;
}
- memset(qd->rcv_window, 0, sizeof(qd->rcv_window));
- qq_rcv_trans_remove_all(qd);
- qq_send_trans_remove_all(qd);
+ qq_trans_remove_all(qd);
- if (qd->inikey) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n");
- g_free(qd->inikey);
- qd->inikey = NULL;
- }
- if (qd->pwkey) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n");
- g_free(qd->pwkey);
- qd->pwkey = NULL;
- }
- if (qd->session_key) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n");
- g_free(qd->session_key);
- qd->session_key = NULL;
- }
- if (qd->session_md5) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n");
- g_free(qd->session_md5);
- qd->session_md5 = NULL;
- }
- if (qd->my_ip) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n");
- g_free(qd->my_ip);
- qd->my_ip = NULL;
+ 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);
@@ -1162,19 +979,19 @@ static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 se
return bytes;
}
-gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+/* 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;
- gint seq;
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);
- seq = ++(qd->send_seq);
buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
if (buf_len <= 0) {
return -1;
@@ -1186,11 +1003,12 @@ gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
bytes_sent = udp_send_out(qd, buf, buf_len);
}
- /* always need ack */
- qq_send_trans_append(qd, buf, buf_len, cmd, seq);
-
+ 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);
+ /* 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);
@@ -1198,19 +1016,14 @@ gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
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 */
+/* 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 *buf;
- gint buf_len;
guint8 *encrypted_data;
gint encrypted_len;
- gint bytes_sent;
- g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
+ 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 */
@@ -1218,36 +1031,10 @@ gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack
qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
- buf = g_newa(guint8, MAX_PACKET_SIZE);
- memset(buf, 0, MAX_PACKET_SIZE);
- buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len);
- if (buf_len <= 0) {
- return -1;
- }
-
- if (QQ_DEBUG) {
- qq_show_packet("QQ_SEND_CMD", buf, buf_len);
- }
- if (qd->use_tcp) {
- bytes_sent = tcp_send_out(qd, buf, buf_len);
- } else {
- bytes_sent = udp_send_out(qd, buf, buf_len);
- }
-
- /* if it does not need ACK, we send ACK manually several times */
- if (need_ack) {
- qq_send_trans_append(qd, buf, buf_len, cmd, seq);
- }
-
- if (QQ_DEBUG) {
- qq_show_packet("QQ_SEND_CMD", 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;
+ 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);
diff --git a/libpurple/protocols/qq/qq_network.h b/libpurple/protocols/qq/qq_network.h
index c45aa226de..054f6fccd9 100644
--- a/libpurple/protocols/qq/qq_network.h
+++ b/libpurple/protocols/qq/qq_network.h
@@ -22,8 +22,8 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _QQ_PROXY_H
-#define _QQ_PROXY_H
+#ifndef _QQ_NETWORK_H
+#define _QQ_NETWORK_H
#include <glib.h>
#include "connection.h"
@@ -36,8 +36,9 @@ void qq_connect(PurpleAccount *account);
void qq_disconnect(PurpleConnection *gc);
void qq_connect_later(PurpleConnection *gc);
-gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
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);
diff --git a/libpurple/protocols/qq/qq_process.c b/libpurple/protocols/qq/qq_process.c
new file mode 100644
index 0000000000..2b8749d000
--- /dev/null
+++ b/libpurple/protocols/qq/qq_process.c
@@ -0,0 +1,266 @@
+/**
+ * @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)
+{
+ 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;
+ }
+}
+
diff --git a/libpurple/protocols/qq/keep_alive.h b/libpurple/protocols/qq/qq_process.h
index cddba2fdcc..7b5820ede1 100644
--- a/libpurple/protocols/qq/keep_alive.h
+++ b/libpurple/protocols/qq/qq_process.h
@@ -1,5 +1,5 @@
/**
- * @file keep_alive.h
+ * @file qq_process.h
*
* purple
*
@@ -20,21 +20,19 @@
* 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_
+#ifndef _QQ_PROCESS_H
+#define _QQ_PROCESS_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);
+#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
+
diff --git a/libpurple/protocols/qq/qq_trans.c b/libpurple/protocols/qq/qq_trans.c
index 9dc5eb52e2..77ee7d7459 100644
--- a/libpurple/protocols/qq/qq_trans.c
+++ b/libpurple/protocols/qq/qq_trans.c
@@ -32,215 +32,259 @@
#include "header_info.h"
#include "qq_network.h"
+#include "qq_process.h"
#include "qq_trans.h"
-#define QQ_RESEND_MAX 8 /* max resend per packet */
+#define QQ_RESEND_MAX 3 /* max resend per packet */
-typedef struct _transaction {
- guint16 seq;
- guint16 cmd;
- guint8 *buf;
- gint buf_len;
-
- gint fd;
- gint retries;
- time_t create_time;
-} transaction;
-
-void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq)
{
- transaction *trans = g_new0(transaction, 1);
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
- g_return_if_fail(trans != NULL);
+ if (qd->transactions == NULL) {
+ return NULL;
+ }
- trans->fd = qd->fd;
- trans->cmd = cmd;
- trans->seq = seq;
- trans->retries = QQ_RESEND_MAX;
- trans->create_time = time(NULL);
- trans->buf = g_memdup(buf, buf_len); /* don't use g_strdup, may have 0x00 */
- trans->buf_len = buf_len;
-
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Add to transaction, seq = %d, buf = %p, len = %d\n",
- trans->seq, trans->buf, trans->buf_len);
- qd->send_trans = g_list_append(qd->send_trans, trans);
+ 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;
}
-/* Remove a packet with seq from send trans */
-void qq_send_trans_remove(qq_data *qd, gpointer data)
+gboolean qq_trans_is_server(qq_transaction *trans)
{
- transaction *trans = (transaction *)data;
+ g_return_val_if_fail(trans != NULL, FALSE);
+
+ if (trans->flag & QQ_TRANS_IS_SERVER)
+ return TRUE;
+ else
+ return FALSE;
+}
- g_return_if_fail(qd != NULL && data != NULL);
+gboolean qq_trans_is_dup(qq_transaction *trans)
+{
+ g_return_val_if_fail(trans != NULL, TRUE);
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "ack [%05d] %s, remove from send tranactions\n",
- trans->seq, qq_get_cmd_desc(trans->cmd));
+ if (trans->rcved_times > 1)
+ return TRUE;
+ else
+ return FALSE;
+}
- if (trans->buf) g_free(trans->buf);
- qd->send_trans = g_list_remove(qd->send_trans, trans);
+/* 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);
}
-gpointer qq_send_trans_find(qq_data *qd, guint16 seq)
+void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
{
- GList *curr;
- GList *next;
- transaction *trans;
+ qq_transaction *trans = g_new0(qq_transaction, 1);
- curr = qd->send_trans;
- while(curr) {
- next = curr->next;
- trans = (transaction *) (curr->data);
- if(trans->seq == seq) {
- return trans;
- }
- curr = next;
- }
+ g_return_if_fail(trans != NULL);
- return 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);
}
-/* clean up send trans and free all contents */
-void qq_send_trans_remove_all(qq_data *qd)
+void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
{
- GList *curr;
- GList *next;
- transaction *trans;
- gint count = 0;
+ qq_transaction *trans = g_new0(qq_transaction, 1);
- curr = qd->send_trans;
- while(curr) {
- next = curr->next;
-
- trans = (transaction *) (curr->data);
- /*
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Remove to transaction, seq = %d, buf = %p, len = %d\n",
- trans->seq, trans->buf, trans->len);
- */
- qq_send_trans_remove(qd, trans);
+ g_return_if_fail(trans != NULL);
- count++;
- curr = next;
+ trans->flag = QQ_TRANS_IS_SERVER;
+ if ( !qd->logged_in ) {
+ trans->flag |= QQ_TRANS_BEFORE_LOGIN;
}
- g_list_free(qd->send_trans);
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count);
+ 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);
}
-gint qq_send_trans_scan(qq_data *qd, gint *start,
- guint8 *buf, gint maxlen, guint16 *cmd, gint *retries)
+void qq_trans_process_before_login(qq_data *qd)
{
GList *curr;
- GList *next = NULL;
- transaction *trans;
- gint copylen;
+ GList *next;
+ qq_transaction *trans;
- g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1);
-
- /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); */
- curr = g_list_nth(qd->send_trans, *start);
- while(curr) {
+ g_return_if_fail(qd != NULL);
+
+ next = qd->transactions;
+ while( (curr = next) ) {
next = curr->next;
- *start = g_list_position(qd->send_trans, next);
+ trans = (qq_transaction *) (curr->data);
+ /* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
- trans = (transaction *) (curr->data);
- if (trans->buf == NULL || trans->buf_len <= 0) {
- qq_send_trans_remove(qd, trans);
- curr = next;
+ if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) {
continue;
}
-
- if (trans->retries < 0) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
- trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
- qq_send_trans_remove(qd, trans);
- curr = next;
+ 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",
- "Resend transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
- trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
- copylen = MIN(trans->buf_len, maxlen);
- g_memmove(buf, trans->buf, copylen);
+ 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);
- *cmd = trans->cmd;
- *retries = trans->retries;
- trans->retries--;
- return copylen;
+ qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len);
}
- /* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */
- return -1;
+ /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+ return;
}
-void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+gboolean qq_trans_scan(qq_data *qd)
{
- transaction *trans = g_new0(transaction, 1);
-
- g_return_if_fail(data != NULL && data_len > 0);
- g_return_if_fail(trans != NULL);
-
- trans->cmd = cmd;
- trans->seq = seq;
- trans->buf = g_memdup(data, data_len);
- trans->buf_len = data_len;
- trans->create_time = time(NULL);
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
- if (qd->rcv_trans == NULL)
- qd->rcv_trans = g_queue_new();
+ 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;
+ }
- g_queue_push_head(qd->rcv_trans, trans);
-}
+ trans->scan_times++;
+ if (trans->scan_times <= 1) {
+ /* skip in 10 seconds */
+ continue;
+ }
-gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len)
-{
- transaction *trans = NULL;
- gint copy_len;
+ if (trans->rcved_times > 0) {
+ /* Has been received */
+ trans_remove(qd, trans);
+ continue;
+ }
- g_return_val_if_fail(data != NULL && max_len > 0, -1);
+ 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;
+ }
- if (g_queue_is_empty(qd->rcv_trans)) {
- return -1;
- }
- trans = (transaction *) g_queue_pop_head(qd->rcv_trans);
- if (trans == NULL) {
- return 0;
+ 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);
}
- if (trans->buf == NULL || trans->buf_len <= 0) {
- return 0;
- }
-
- copy_len = MIN(max_len, trans->buf_len);
- g_memmove(data, trans->buf, copy_len);
- *cmd = trans->cmd;
- *seq = trans->seq;
- g_free(trans->buf);
- g_free(trans);
- return copy_len;
+ /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+ return FALSE;
}
-/* clean up the packets before login */
-void qq_rcv_trans_remove_all(qq_data *qd)
+/* clean up send trans and free all contents */
+void qq_trans_remove_all(qq_data *qd)
{
- transaction *trans = NULL;
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
gint count = 0;
- g_return_if_fail(qd != NULL);
+ 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);
- /* now clean up my own data structures */
- if (qd->rcv_trans != NULL) {
- while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) {
- g_free(trans->buf);
- g_free(trans);
- count++;
- }
- g_queue_free(qd->rcv_trans);
+ count++;
+ curr = next;
}
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count);
+ g_list_free(qd->transactions);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count);
}
diff --git a/libpurple/protocols/qq/qq_trans.h b/libpurple/protocols/qq/qq_trans.h
index a6bbf9073f..a0afae0821 100644
--- a/libpurple/protocols/qq/qq_trans.h
+++ b/libpurple/protocols/qq/qq_trans.h
@@ -28,15 +28,34 @@
#include <glib.h>
#include "qq.h"
-void qq_send_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq);
-void qq_send_trans_remove(qq_data *qd, gpointer data);
-gpointer qq_send_trans_find(qq_data *qd, guint16 seq);
-void qq_send_trans_remove_all(qq_data *qd);
+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*/
+};
-gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries);
+typedef struct _qq_transaction {
+ guint8 flag;
+ guint16 seq;
+ guint16 cmd;
+ guint8 *data;
+ gint data_len;
-void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
-gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len);
-void qq_rcv_trans_remove_all(qq_data *qd);
+ 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
diff --git a/libpurple/protocols/qq/send_file.c b/libpurple/protocols/qq/send_file.c
index e5a41efc96..0f130d4fad 100644
--- a/libpurple/protocols/qq/send_file.c
+++ b/libpurple/protocols/qq/send_file.c
@@ -29,12 +29,12 @@
#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 "qq_network.h"
#include "utils.h"
@@ -156,7 +156,7 @@ static void _qq_xfer_recv_packet(gpointer data, gint source, PurpleInputConditio
gint size;
/* FIXME: It seems that the transfer never use a packet
* larger than 1500 bytes, so if it happened to be a
- * larger packet, either error occurred or protocol should
+ * larger packet, either error occured or protocol should
* be modified
*/
ft_info *info;
@@ -447,7 +447,7 @@ static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid,
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;
@@ -784,7 +784,7 @@ void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_ui
qd = (qq_data *) gc->proto_data;
info = g_newa(ft_info, 1);
- 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->to_uid = sender_uid;
@@ -814,11 +814,11 @@ void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_ui
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;
}
@@ -831,7 +831,7 @@ void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_ui
}
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);
diff --git a/libpurple/protocols/qq/send_file.h b/libpurple/protocols/qq/send_file.h
index e3ed1723c9..7e4e0dd69a 100644
--- a/libpurple/protocols/qq/send_file.h
+++ b/libpurple/protocols/qq/send_file.h
@@ -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;
diff --git a/libpurple/protocols/qq/sys_msg.c b/libpurple/protocols/qq/sys_msg.c
index 008751b983..588610532b 100644
--- a/libpurple/protocols/qq/sys_msg.c
+++ b/libpurple/protocols/qq/sys_msg.c
@@ -44,6 +44,7 @@ enum {
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
};
@@ -277,6 +278,20 @@ static void _qq_process_msg_sys_add_contact_request(PurpleConnection *gc, gchar
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;
@@ -320,9 +335,12 @@ void qq_process_msg_sys(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection
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);
diff --git a/libpurple/protocols/qq/utils.c b/libpurple/protocols/qq/utils.c
index 5757855f72..c3ee33fe6d 100644
--- a/libpurple/protocols/qq/utils.c
+++ b/libpurple/protocols/qq/utils.c
@@ -30,6 +30,8 @@
#include "win32dep.h"
#endif
+#include "cipher.h"
+
#include "char_conv.h"
#include "debug.h"
#include "prefs.h"
@@ -50,6 +52,21 @@
}
*/
+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;
@@ -97,7 +114,7 @@ gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_f
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;
@@ -123,10 +140,20 @@ gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_f
return segments;
}
-/* 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)
+/* 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;
+}
+
+gchar *gen_ip_str(guint8 *ip) {
gchar *ret;
if (ip == NULL || ip[0] == 0) {
ret = g_new(gchar, 1);
@@ -149,19 +176,6 @@ guint8 *str_ip_gen(gchar *str) {
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)
@@ -293,12 +307,12 @@ static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
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, " ");
diff --git a/libpurple/protocols/qq/utils.h b/libpurple/protocols/qq/utils.h
index 1ba7af2332..14dfbcd40d 100644
--- a/libpurple/protocols/qq/utils.h
+++ b/libpurple/protocols/qq/utils.h
@@ -30,6 +30,8 @@
#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);
diff --git a/libpurple/protocols/silc/silc.c b/libpurple/protocols/silc/silc.c
index e625420093..df71164a54 100644
--- a/libpurple/protocols/silc/silc.c
+++ b/libpurple/protocols/silc/silc.c
@@ -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 @@ silcpurple_close(PurpleConnection *gc)
#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 @@ static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
{
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 @@ static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
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;
}
diff --git a/libpurple/protocols/silc10/silc.c b/libpurple/protocols/silc10/silc.c
index 94292364a6..fe68af53da 100644
--- a/libpurple/protocols/silc10/silc.c
+++ b/libpurple/protocols/silc10/silc.c
@@ -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 @@ static void
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);
+ 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 @@ static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
{
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 @@ static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv,
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;
}
diff --git a/libpurple/protocols/yahoo/yahoo.c b/libpurple/protocols/yahoo/yahoo.c
index 0598d60852..86de70d43b 100644
--- a/libpurple/protocols/yahoo/yahoo.c
+++ b/libpurple/protocols/yahoo/yahoo.c
@@ -1995,13 +1995,19 @@ static void yahoo_process_auth(PurpleConnection *gc, struct yahoo_packet *pkt)
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;
+ }
}
}
}
diff --git a/libpurple/protocols/yahoo/yahoo_picture.c b/libpurple/protocols/yahoo/yahoo_picture.c
index 90f2bfedac..704a9e0f68 100644
--- a/libpurple/protocols/yahoo/yahoo_picture.c
+++ b/libpurple/protocols/yahoo/yahoo_picture.c
@@ -27,6 +27,7 @@
#include "accountopt.h"
#include "blist.h"
#include "debug.h"
+#include "privacy.h"
#include "prpl.h"
#include "proxy.h"
#include "util.h"
@@ -109,6 +110,11 @@ void yahoo_process_picture(PurpleConnection *gc, struct yahoo_packet *pkt)
l = l->next;
}
+ if (!purple_privacy_check(purple_connection_get_account(gc), who)) {
+ purple_debug_info("yahoo", "Picture packet from %s dropped.\n", who);
+ return;
+ }
+
/* Yahoo IM 6 spits out 0.png as the URL if the buddy icon is not set */
if (who && got_icon_info && url && !g_ascii_strncasecmp(url, "http://", 7)) {
/* TODO: make this work p2p, try p2p before the url */
diff --git a/libpurple/protocols/yahoo/yahoo_profile.c b/libpurple/protocols/yahoo/yahoo_profile.c
index 3656e907b2..c30487b086 100644
--- a/libpurple/protocols/yahoo/yahoo_profile.c
+++ b/libpurple/protocols/yahoo/yahoo_profile.c
@@ -1282,6 +1282,10 @@ void yahoo_get_info(PurpleConnection *gc, const char *name)
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);
}
diff --git a/libpurple/prpl.c b/libpurple/prpl.c
index b5afec785b..0f9ab49b91 100644
--- a/libpurple/prpl.c
+++ b/libpurple/prpl.c
@@ -389,6 +389,110 @@ purple_prpl_get_statuses(PurpleAccount *account, PurplePresence *presence)
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
diff --git a/libpurple/prpl.h b/libpurple/prpl.h
index 67c945ab56..43bb6e3cad 100644
--- a/libpurple/prpl.h
+++ b/libpurple/prpl.h
@@ -693,6 +693,41 @@ void purple_prpl_change_account_status(PurpleAccount *account,
*/
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);
+
/*@}*/
/**************************************************************************/
diff --git a/libpurple/purple-url-handler b/libpurple/purple-url-handler
index 9314f6024b..3493677069 100755
--- a/libpurple/purple-url-handler
+++ b/libpurple/purple-url-handler
@@ -6,7 +6,15 @@ import sys
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 @@ def convert(value):
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 @@ def irc(uri):
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 @@ def msnim(uri):
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 @@ def main(argv=sys.argv):
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()
diff --git a/libpurple/server.c b/libpurple/server.c
index e4739df518..39a188a35b 100644
--- a/libpurple/server.c
+++ b/libpurple/server.c
@@ -323,91 +323,13 @@ PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account,
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);
}
diff --git a/libpurple/server.h b/libpurple/server.h
index 83172474c9..8832e30583 100644
--- a/libpurple/server.h
+++ b/libpurple/server.h
@@ -63,6 +63,8 @@ PurpleAttentionType *purple_get_attention_type_from_code(PurpleAccount *account,
/** 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 @@ void serv_send_attention(PurpleConnection *gc, const char *who, guint type_code)
/** 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
diff --git a/libpurple/util.c b/libpurple/util.c
index dbe639de3c..ba6a2ce946 100644
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -4660,3 +4660,21 @@ char * purple_util_format_song_info(const char *title, const char *artist, const
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
+}
diff --git a/libpurple/util.h b/libpurple/util.h
index e15a036591..18bc815714 100644
--- a/libpurple/util.h
+++ b/libpurple/util.h
@@ -1271,6 +1271,14 @@ const char *_purple_oscar_convert(const char *act, const char *protocol);
*/
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
diff --git a/libpurple/win32/global.mak b/libpurple/win32/global.mak
index 673d125a68..60366fc195 100644
--- a/libpurple/win32/global.mak
+++ b/libpurple/win32/global.mak
@@ -19,8 +19,8 @@ LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.6.30
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_DLL := $(PIDGIN_TOP)/pidgin.dll
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])
diff --git a/libpurple/win32/libc_interface.c b/libpurple/win32/libc_interface.c
index 92b17d58b1..666a0578b8 100644
--- a/libpurple/win32/libc_interface.c
+++ b/libpurple/win32/libc_interface.c
@@ -327,9 +327,12 @@ char* wpurple_strerror(int errornum) {
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);
}
diff --git a/libpurple/xmlnode.c b/libpurple/xmlnode.c
index b58e3354f7..f5f2f9e48c 100644
--- a/libpurple/xmlnode.c
+++ b/libpurple/xmlnode.c
@@ -728,76 +728,11 @@ xmlnode_from_str(const char *str, gssize size)
return ret;
}
-xmlnode *
-xmlnode_from_file(const char *dir, const char *filename, const char *description, const char *process)
+static void
+xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
{
- gchar *filename_full;
- GError *error = NULL;
- gchar *contents = NULL;
- gsize length;
- xmlnode *node = NULL;
-
- g_return_val_if_fail(dir != NULL, NULL);
-
- purple_debug_info(process, "Reading file %s from directory %s\n",
- filename, dir);
-
- filename_full = g_build_filename(dir, filename, NULL);
-
- if (!g_file_test(filename_full, G_FILE_TEST_EXISTS))
- {
- purple_debug_info(process, "File %s does not exist (this is not "
- "necessarily an error)\n", filename_full);
- g_free(filename_full);
- return NULL;
- }
-
- if (!g_file_get_contents(filename_full, &contents, &length, &error))
- {
- purple_debug_error(process, "Error reading file %s: %s\n",
- filename_full, error->message);
- g_error_free(error);
- }
-
- if ((contents != NULL) && (length > 0))
- {
- node = xmlnode_from_str(contents, length);
-
- /* If we were unable to parse the file then save its contents to a backup file */
- if (node == NULL)
- {
- gchar *filename_temp, *filename_temp_full;
-
- filename_temp = g_strdup_printf("%s~", filename);
- filename_temp_full = g_build_filename(dir, filename_temp, NULL);
-
- purple_debug_error("util", "Error parsing file %s. Renaming old "
- "file to %s\n", filename_full, filename_temp);
- purple_util_write_data_to_file_absolute(filename_temp_full, contents, length);
-
- g_free(filename_temp_full);
- g_free(filename_temp);
- }
-
- g_free(contents);
- }
-
- /* If we could not parse the file then show the user an error message */
- if (node == NULL)
- {
- gchar *title, *msg;
- title = g_strdup_printf(_("Error Reading %s"), filename);
- msg = g_strdup_printf(_("An error was encountered reading your "
- "%s. The file has not been loaded, and the old file "
- "has been renamed to %s~."), description, filename_full);
- purple_notify_error(NULL, NULL, title, msg);
- g_free(title);
- g_free(msg);
- }
-
- g_free(filename_full);
-
- return node;
+ GHashTable *ret = (GHashTable *)user_data;
+ g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
}
xmlnode *
@@ -811,17 +746,23 @@ xmlnode_copy(const xmlnode *src)
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 {
diff --git a/pidgin/gtkaccount.c b/pidgin/gtkaccount.c
index 963ea2a520..5734b10224 100644
--- a/pidgin/gtkaccount.c
+++ b/pidgin/gtkaccount.c
@@ -1186,7 +1186,6 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
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 @@ ok_account_prefs_cb(GtkWidget *w, AccountPrefsDialog *dialog)
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;
diff --git a/pidgin/gtkdialogs.c b/pidgin/gtkdialogs.c
index 78b3fbbd45..bf32d437ff 100644
--- a/pidgin/gtkdialogs.c
+++ b/pidgin/gtkdialogs.c
@@ -73,7 +73,8 @@ static const struct developer developers[] = {
{"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 @@ static const struct developer developers[] = {
{"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 @@ static const struct developer patch_writers[] = {
{"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 @@ void pidgin_dialogs_about()
for (i = 0; developers[i].name != NULL; i++) {
if (developers[i].email != NULL) {
g_string_append_printf(str, " %s (%s) &lt;<a href=\"mailto:%s\">%s</a>&gt;<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/>");
diff --git a/pidgin/gtkdocklet.c b/pidgin/gtkdocklet.c
index 95cdf930ad..b223dec6c2 100644
--- a/pidgin/gtkdocklet.c
+++ b/pidgin/gtkdocklet.c
@@ -708,6 +708,11 @@ docklet_menu(void)
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);
diff --git a/pidgin/gtkmain.c b/pidgin/gtkmain.c
index f56dda8f70..911408d8fc 100644
--- a/pidgin/gtkmain.c
+++ b/pidgin/gtkmain.c
@@ -350,6 +350,8 @@ static GHashTable *pidgin_ui_get_info(void)
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;
diff --git a/pidgin/gtkroomlist.c b/pidgin/gtkroomlist.c
index 937f6cbf2c..0798943cae 100644
--- a/pidgin/gtkroomlist.c
+++ b/pidgin/gtkroomlist.c
@@ -53,7 +53,6 @@ typedef struct _PidginRoomlistDialog {
PurpleRoomlist *roomlist;
gboolean pg_needs_pulse;
- gboolean pg_to_active;
guint pg_update_to;
} PidginRoomlistDialog;
@@ -84,32 +83,34 @@ static GList *roomlists = NULL;
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);
- }
- }
- /* free stuff here */
- if (dialog->roomlist)
+ if (rl)
+ rl->dialog = NULL;
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 @@ selection_changed_cb(GtkTreeSelection *selection, PidginRoomlist *grl) {
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 @@ static void add_room_to_blist_cb(GtkButton *button, PidginRoomlistDialog *dialog
{
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 @@ static void join_button_cb(GtkButton *button, PidginRoomlistDialog *dialog)
{
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 @@ pidgin_roomlist_create_tooltip(GtkWidget *widget, GtkTreePath *path,
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 @@ static PidginRoomlistDialog *
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 @@ pidgin_roomlist_dialog_new_with_account(PurpleAccount *account)
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 @@ void pidgin_roomlist_dialog_show(void)
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 @@ static gboolean pidgin_progress_bar_pulse(gpointer data)
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 @@ static void pidgin_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *r
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_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *r
}
}
-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_in_progress(PurpleRoomlist *list, gboolean flag)
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);
diff --git a/pidgin/gtksmiley.c b/pidgin/gtksmiley.c
index 100e562484..a39065a88c 100644
--- a/pidgin/gtksmiley.c
+++ b/pidgin/gtksmiley.c
@@ -275,7 +275,8 @@ static void do_add(GtkWidget *widget, PidginSmiley *s)
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)
diff --git a/pidgin/pidgintooltip.c b/pidgin/pidgintooltip.c
index d1b7447fff..964fb0cca8 100644
--- a/pidgin/pidgintooltip.c
+++ b/pidgin/pidgintooltip.c
@@ -59,6 +59,7 @@ static void
destroy_tooltip_data(PidginTooltipData *data)
{
gtk_tree_path_free(data->common.treeview.path);
+ pidgin_tooltip_destroy();
g_free(data);
}
diff --git a/pidgin/pixmaps/emblems/16/birthday.png b/pidgin/pixmaps/emblems/16/birthday.png
index 066dce094a..4dcd6f58e4 100644
--- a/pidgin/pixmaps/emblems/16/birthday.png
+++ b/pidgin/pixmaps/emblems/16/birthday.png
Binary files differ
diff --git a/pidgin/pixmaps/emblems/16/scalable/birthday.svg b/pidgin/pixmaps/emblems/16/scalable/birthday.svg
new file mode 100644
index 0000000000..152f38067b
--- /dev/null
+++ b/pidgin/pixmaps/emblems/16/scalable/birthday.svg
@@ -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>
diff --git a/pidgin/pixmaps/emotes/default/24/Makefile.am b/pidgin/pixmaps/emotes/default/24/Makefile.am
index a7297e18bd..78f6147962 100644
--- a/pidgin/pixmaps/emotes/default/24/Makefile.am
+++ b/pidgin/pixmaps/emotes/default/24/Makefile.am
@@ -16,6 +16,7 @@ SMILEYS = act-up.png \
boy.png \
brb.png \
bulgy-eyes.png \
+ bunny.png \
bye.png \
cake.png \
call-me.png \
diff --git a/pidgin/pixmaps/emotes/default/24/airplane.png b/pidgin/pixmaps/emotes/default/24/airplane.png
index 89b45efe5a..dd87f7a0d4 100644
--- a/pidgin/pixmaps/emotes/default/24/airplane.png
+++ b/pidgin/pixmaps/emotes/default/24/airplane.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/bad.png b/pidgin/pixmaps/emotes/default/24/bad.png
index 07fb09bd49..a297f8680f 100644
--- a/pidgin/pixmaps/emotes/default/24/bad.png
+++ b/pidgin/pixmaps/emotes/default/24/bad.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/beer.png b/pidgin/pixmaps/emotes/default/24/beer.png
index e955770a38..6f82e46b9d 100644
--- a/pidgin/pixmaps/emotes/default/24/beer.png
+++ b/pidgin/pixmaps/emotes/default/24/beer.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/bomb.png b/pidgin/pixmaps/emotes/default/24/bomb.png
index 9f28ff03b8..f50d407587 100644
--- a/pidgin/pixmaps/emotes/default/24/bomb.png
+++ b/pidgin/pixmaps/emotes/default/24/bomb.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/bowl.png b/pidgin/pixmaps/emotes/default/24/bowl.png
index d236b07590..e800c7287f 100644
--- a/pidgin/pixmaps/emotes/default/24/bowl.png
+++ b/pidgin/pixmaps/emotes/default/24/bowl.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/boy.png b/pidgin/pixmaps/emotes/default/24/boy.png
index 4608f0d472..3e70c54719 100644
--- a/pidgin/pixmaps/emotes/default/24/boy.png
+++ b/pidgin/pixmaps/emotes/default/24/boy.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/brb.png b/pidgin/pixmaps/emotes/default/24/brb.png
index 3afe83e97c..61b3a146a5 100644
--- a/pidgin/pixmaps/emotes/default/24/brb.png
+++ b/pidgin/pixmaps/emotes/default/24/brb.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/bunny.png b/pidgin/pixmaps/emotes/default/24/bunny.png
new file mode 100644
index 0000000000..77d97c4ffe
--- /dev/null
+++ b/pidgin/pixmaps/emotes/default/24/bunny.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/cake.png b/pidgin/pixmaps/emotes/default/24/cake.png
index 5cf3dd6b05..519fd4ceae 100644
--- a/pidgin/pixmaps/emotes/default/24/cake.png
+++ b/pidgin/pixmaps/emotes/default/24/cake.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/camera.png b/pidgin/pixmaps/emotes/default/24/camera.png
index 86ae7c186c..d8ccc97b69 100644
--- a/pidgin/pixmaps/emotes/default/24/camera.png
+++ b/pidgin/pixmaps/emotes/default/24/camera.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/car.png b/pidgin/pixmaps/emotes/default/24/car.png
index 0e605fa3f5..97622bc2c4 100644
--- a/pidgin/pixmaps/emotes/default/24/car.png
+++ b/pidgin/pixmaps/emotes/default/24/car.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/cat.png b/pidgin/pixmaps/emotes/default/24/cat.png
index 9454c7b4fa..4b602dae8a 100644
--- a/pidgin/pixmaps/emotes/default/24/cat.png
+++ b/pidgin/pixmaps/emotes/default/24/cat.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/clock.png b/pidgin/pixmaps/emotes/default/24/clock.png
index 4de10e06b3..e0bbb85478 100644
--- a/pidgin/pixmaps/emotes/default/24/clock.png
+++ b/pidgin/pixmaps/emotes/default/24/clock.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/coffee.png b/pidgin/pixmaps/emotes/default/24/coffee.png
index e80fee3bd4..51c4f90ad2 100644
--- a/pidgin/pixmaps/emotes/default/24/coffee.png
+++ b/pidgin/pixmaps/emotes/default/24/coffee.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/coins.png b/pidgin/pixmaps/emotes/default/24/coins.png
index 2c28453b18..7e06f3eab3 100644
--- a/pidgin/pixmaps/emotes/default/24/coins.png
+++ b/pidgin/pixmaps/emotes/default/24/coins.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/console.png b/pidgin/pixmaps/emotes/default/24/console.png
index 46e4287f36..4933bc5eec 100644
--- a/pidgin/pixmaps/emotes/default/24/console.png
+++ b/pidgin/pixmaps/emotes/default/24/console.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/default.theme.in b/pidgin/pixmaps/emotes/default/24/default.theme.in
index ef8357ebe9..c9b4a52247 100644
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in
+++ b/pidgin/pixmaps/emotes/default/24/default.theme.in
@@ -3,7 +3,6 @@ _Description=Pidgin smileys
Icon=wink.png
Author=Hylke Bons
-
# Default smileys
[default]
smile.png :) :-)
@@ -125,6 +124,7 @@ thunder.png (li)
party.png <:o)
eyeroll.png 8-)
yawn.png |-)
+bunny.png ('.')
! skywalker.png C:-) c:-) C:) c:)
! monkey.png :-(|) :(|)
diff --git a/pidgin/pixmaps/emotes/default/24/dog.png b/pidgin/pixmaps/emotes/default/24/dog.png
index 8e9b455ee9..d8c1ddf6fe 100644
--- a/pidgin/pixmaps/emotes/default/24/dog.png
+++ b/pidgin/pixmaps/emotes/default/24/dog.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/drink.png b/pidgin/pixmaps/emotes/default/24/drink.png
index 46412bece8..563ab94029 100644
--- a/pidgin/pixmaps/emotes/default/24/drink.png
+++ b/pidgin/pixmaps/emotes/default/24/drink.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/film.png b/pidgin/pixmaps/emotes/default/24/film.png
index 2d9b0b54d9..66acb127ca 100644
--- a/pidgin/pixmaps/emotes/default/24/film.png
+++ b/pidgin/pixmaps/emotes/default/24/film.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/girl.png b/pidgin/pixmaps/emotes/default/24/girl.png
index f758c2bece..82c9428203 100644
--- a/pidgin/pixmaps/emotes/default/24/girl.png
+++ b/pidgin/pixmaps/emotes/default/24/girl.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/goat.png b/pidgin/pixmaps/emotes/default/24/goat.png
index dfd4dc3cb7..f44db4a4b9 100644
--- a/pidgin/pixmaps/emotes/default/24/goat.png
+++ b/pidgin/pixmaps/emotes/default/24/goat.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/good.png b/pidgin/pixmaps/emotes/default/24/good.png
index 127d07313d..c32988d22a 100644
--- a/pidgin/pixmaps/emotes/default/24/good.png
+++ b/pidgin/pixmaps/emotes/default/24/good.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/handcuffs.png b/pidgin/pixmaps/emotes/default/24/handcuffs.png
index 3463bdf132..849ee912d7 100644
--- a/pidgin/pixmaps/emotes/default/24/handcuffs.png
+++ b/pidgin/pixmaps/emotes/default/24/handcuffs.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/lamp.png b/pidgin/pixmaps/emotes/default/24/lamp.png
index 0431d75bb4..85b7968aa1 100644
--- a/pidgin/pixmaps/emotes/default/24/lamp.png
+++ b/pidgin/pixmaps/emotes/default/24/lamp.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/love-over.png b/pidgin/pixmaps/emotes/default/24/love-over.png
index fc7ad6fc81..59c8a6ef69 100644
--- a/pidgin/pixmaps/emotes/default/24/love-over.png
+++ b/pidgin/pixmaps/emotes/default/24/love-over.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/love.png b/pidgin/pixmaps/emotes/default/24/love.png
index 5171deed67..31aeea275e 100644
--- a/pidgin/pixmaps/emotes/default/24/love.png
+++ b/pidgin/pixmaps/emotes/default/24/love.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/mail.png b/pidgin/pixmaps/emotes/default/24/mail.png
index 027bdb0900..a1a365f0d7 100644
--- a/pidgin/pixmaps/emotes/default/24/mail.png
+++ b/pidgin/pixmaps/emotes/default/24/mail.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/mobile.png b/pidgin/pixmaps/emotes/default/24/mobile.png
index 50781531fc..ac3469007d 100644
--- a/pidgin/pixmaps/emotes/default/24/mobile.png
+++ b/pidgin/pixmaps/emotes/default/24/mobile.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/moon.png b/pidgin/pixmaps/emotes/default/24/moon.png
index 42a41b3375..0679d3dec3 100644
--- a/pidgin/pixmaps/emotes/default/24/moon.png
+++ b/pidgin/pixmaps/emotes/default/24/moon.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/msn.png b/pidgin/pixmaps/emotes/default/24/msn.png
index 97977f2ed1..3c2adcd1b0 100644
--- a/pidgin/pixmaps/emotes/default/24/msn.png
+++ b/pidgin/pixmaps/emotes/default/24/msn.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/musical-note.png b/pidgin/pixmaps/emotes/default/24/musical-note.png
index 5990ae544f..d003bc6170 100644
--- a/pidgin/pixmaps/emotes/default/24/musical-note.png
+++ b/pidgin/pixmaps/emotes/default/24/musical-note.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/phone.png b/pidgin/pixmaps/emotes/default/24/phone.png
index 83b614f587..a3c86155c3 100644
--- a/pidgin/pixmaps/emotes/default/24/phone.png
+++ b/pidgin/pixmaps/emotes/default/24/phone.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/pizza.png b/pidgin/pixmaps/emotes/default/24/pizza.png
index 461f218ce8..9b8da769b4 100644
--- a/pidgin/pixmaps/emotes/default/24/pizza.png
+++ b/pidgin/pixmaps/emotes/default/24/pizza.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/plate.png b/pidgin/pixmaps/emotes/default/24/plate.png
index 45036b35f9..0ed2c6fdb2 100644
--- a/pidgin/pixmaps/emotes/default/24/plate.png
+++ b/pidgin/pixmaps/emotes/default/24/plate.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/present.png b/pidgin/pixmaps/emotes/default/24/present.png
index cf39af2ff2..ac43e38dbe 100644
--- a/pidgin/pixmaps/emotes/default/24/present.png
+++ b/pidgin/pixmaps/emotes/default/24/present.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/rain.png b/pidgin/pixmaps/emotes/default/24/rain.png
index a67718ed9c..6954d826a1 100644
--- a/pidgin/pixmaps/emotes/default/24/rain.png
+++ b/pidgin/pixmaps/emotes/default/24/rain.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/rose-dead.png b/pidgin/pixmaps/emotes/default/24/rose-dead.png
index 8cbccc402a..927cf90eb1 100644
--- a/pidgin/pixmaps/emotes/default/24/rose-dead.png
+++ b/pidgin/pixmaps/emotes/default/24/rose-dead.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/rose.png b/pidgin/pixmaps/emotes/default/24/rose.png
index ff7180ed37..33cfb13782 100644
--- a/pidgin/pixmaps/emotes/default/24/rose.png
+++ b/pidgin/pixmaps/emotes/default/24/rose.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/sheep.png b/pidgin/pixmaps/emotes/default/24/sheep.png
index 8dffc36e0a..dc3feaa458 100644
--- a/pidgin/pixmaps/emotes/default/24/sheep.png
+++ b/pidgin/pixmaps/emotes/default/24/sheep.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/soccerball.png b/pidgin/pixmaps/emotes/default/24/soccerball.png
index 157a6fd818..358a157b31 100644
--- a/pidgin/pixmaps/emotes/default/24/soccerball.png
+++ b/pidgin/pixmaps/emotes/default/24/soccerball.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/star.png b/pidgin/pixmaps/emotes/default/24/star.png
index 6e3c436c5e..cc92a69a71 100644
--- a/pidgin/pixmaps/emotes/default/24/star.png
+++ b/pidgin/pixmaps/emotes/default/24/star.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/sun.png b/pidgin/pixmaps/emotes/default/24/sun.png
index b3c2663beb..43a61280c7 100644
--- a/pidgin/pixmaps/emotes/default/24/sun.png
+++ b/pidgin/pixmaps/emotes/default/24/sun.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/thunder.png b/pidgin/pixmaps/emotes/default/24/thunder.png
index 282e74a129..fef2007b8c 100644
--- a/pidgin/pixmaps/emotes/default/24/thunder.png
+++ b/pidgin/pixmaps/emotes/default/24/thunder.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/turtle.png b/pidgin/pixmaps/emotes/default/24/turtle.png
index 505d5b221a..a95b64f03a 100644
--- a/pidgin/pixmaps/emotes/default/24/turtle.png
+++ b/pidgin/pixmaps/emotes/default/24/turtle.png
Binary files differ
diff --git a/pidgin/pixmaps/emotes/default/24/umbrella.png b/pidgin/pixmaps/emotes/default/24/umbrella.png
index 68f6615d41..8c5a8518f8 100644
--- a/pidgin/pixmaps/emotes/default/24/umbrella.png
+++ b/pidgin/pixmaps/emotes/default/24/umbrella.png
Binary files differ
diff --git a/pidgin/pixmaps/toolbar/16/emote-select.png b/pidgin/pixmaps/toolbar/16/emote-select.png
index 2d8c11ab23..1a79494f98 100644
--- a/pidgin/pixmaps/toolbar/16/emote-select.png
+++ b/pidgin/pixmaps/toolbar/16/emote-select.png
Binary files differ
diff --git a/pidgin/pixmaps/toolbar/16/scalable/emote-select.svg b/pidgin/pixmaps/toolbar/16/scalable/emote-select.svg
index 6472d4f0c7..fc0e42ec70 100644
--- a/pidgin/pixmaps/toolbar/16/scalable/emote-select.svg
+++ b/pidgin/pixmaps/toolbar/16/scalable/emote-select.svg
@@ -2,200 +2,602 @@
<!-- 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">
+ <metadata
+ id="metadata7">
+ <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>
<defs
- id="defs4348">
+ id="defs5">
<linearGradient
inkscape:collect="always"
- id="linearGradient5269">
+ id="linearGradient3398">
<stop
- style="stop-color:#fcaf3e;stop-opacity:1;"
+ style="stop-color:#fe9906;stop-opacity:1;"
offset="0"
- id="stop5271" />
+ id="stop3400" />
<stop
- style="stop-color:#fcaf3e;stop-opacity:0;"
+ style="stop-color:#ffffff;stop-opacity:1"
offset="1"
- id="stop5273" />
+ id="stop3402" />
</linearGradient>
<linearGradient
inkscape:collect="always"
- id="linearGradient3104">
+ 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
- style="stop-color:#eeeeec;stop-opacity:1;"
+ id="stop3106"
offset="0"
- id="stop3106" />
+ style="stop-color:#eeeeec;stop-opacity:1;" />
<stop
- style="stop-color:#eeeeec;stop-opacity:0;"
+ id="stop3108"
offset="1"
- id="stop3108" />
+ 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="radialGradient3114"
- cx="5.7434092"
- cy="16.737026"
- fx="5.7434092"
- fy="16.737026"
+ id="radialGradient2233"
+ cx="8.3343515"
+ cy="14.186539"
+ fx="8.3343515"
+ fy="14.186539"
r="9.975256"
gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(-1.30241,1.304442,-1.325199,-1.323009,41.46631,16.11711)" />
- <linearGradient
+ 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"
- id="linearGradient3150">
+ 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
- style="stop-color:#2e3436;stop-opacity:1;"
+ id="stop2869"
offset="0"
- id="stop3152" />
+ style="stop-color:#d3d7cf;stop-opacity:1;" />
<stop
- style="stop-color:#2e3436;stop-opacity:0;"
+ id="stop2871"
offset="1"
- id="stop3154" />
+ style="stop-color:#d3d7cf;stop-opacity:0;" />
</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"
+ 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="#linearGradient5269"
- id="linearGradient5275"
- x1="26.243328"
- y1="13.001364"
- x2="26.243328"
- y2="10.507664"
+ 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
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
+ inkscape:window-height="849"
+ inkscape:window-width="1440"
inkscape:pageshadow="2"
- inkscape:zoom="31.392433"
- inkscape:cx="14.469085"
- inkscape:cy="9.6077349"
- inkscape:current-layer="layer1"
+ 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: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">
- <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: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>
+ 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>
diff --git a/pidgin/pixmaps/tray/22/tray-away.png b/pidgin/pixmaps/tray/22/tray-away.png
index 13ac98c889..070045823d 100644
--- a/pidgin/pixmaps/tray/22/tray-away.png
+++ b/pidgin/pixmaps/tray/22/tray-away.png
Binary files differ
diff --git a/pidgin/pixmaps/tray/22/tray-busy.png b/pidgin/pixmaps/tray/22/tray-busy.png
index bfacdbb3c7..1b70cc7e52 100644
--- a/pidgin/pixmaps/tray/22/tray-busy.png
+++ b/pidgin/pixmaps/tray/22/tray-busy.png
Binary files differ
diff --git a/pidgin/pixmaps/tray/22/tray-online.png b/pidgin/pixmaps/tray/22/tray-online.png
index a3f792cc0e..58e6f4dd11 100644
--- a/pidgin/pixmaps/tray/22/tray-online.png
+++ b/pidgin/pixmaps/tray/22/tray-online.png
Binary files differ
diff --git a/pidgin/plugins/perl/common/Makefile.mingw b/pidgin/plugins/perl/common/Makefile.mingw
index 13746d762b..1c2b145fab 100644
--- a/pidgin/plugins/perl/common/Makefile.mingw
+++ b/pidgin/plugins/perl/common/Makefile.mingw
@@ -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 @@ OBJECTS = $(C_FILES:%.c=%.o)
##
## LIBRARIES
##
-LIBS = -lperl58 \
+LIBS = -lperl510 \
-lperl \
-lpurple \
-lpidgin \
diff --git a/pidgin/win32/nsis/pidgin-installer.nsi b/pidgin/win32/nsis/pidgin-installer.nsi
index faa6cb099c..c219a33ca8 100644
--- a/pidgin/win32/nsis/pidgin-installer.nsi
+++ b/pidgin/win32/nsis/pidgin-installer.nsi
@@ -72,7 +72,7 @@ SetDateSave on
!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 @@ Section $(GTK_SECTION_TITLE) SecGtk
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 @@ Section $(PIDGIN_SECTION_TITLE) SecPidgin
; 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 @@ Section Uninstall
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 @@ Function .onInit
;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
diff --git a/pidgin/win32/winpidgin.c b/pidgin/win32/winpidgin.c
index 73dadcc557..7e14883866 100644
--- a/pidgin/win32/winpidgin.c
+++ b/pidgin/win32/winpidgin.c
@@ -89,9 +89,8 @@ static BOOL read_reg_string(HKEY key, char* sub_key, char* val_name, LPBYTE data
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 @@ static void portable_mode_dll_prep(const char *pidgin_dir) {
/* 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 @@ static void winpidgin_set_locale() {
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 @@ WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
} 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 @@ WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
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 @@ WinMain (struct HINSTANCE__ *hInstance, struct HINSTANCE__ *hPrevInstance,
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;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 786dde60dc..97fb0ab54f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -143,12 +143,9 @@ libpurple/protocols/qq/im.c
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
diff --git a/po/de.po b/po/de.po
index ec22c0299c..8e1294d08b 100644
--- a/po/de.po
+++ b/po/de.po
@@ -11,9 +11,9 @@ msgid ""
msgstr ""
"Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-06-25 23:30+0200\n"
-"PO-Revision-Date: 2008-06-25 23:27+0200\n"
-"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\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"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -3232,10 +3232,8 @@ msgid "Bad mode"
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"
@@ -6369,7 +6367,6 @@ msgstr ""
"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 ""
@@ -7336,10 +7333,6 @@ msgstr "Anmeldung fehlgeschlagen, Debugmitschnitt beachten."
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"
@@ -7365,12 +7358,16 @@ msgid "<b>Last Refresh</b>: %s<br>\n"
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"
@@ -7422,12 +7419,49 @@ msgstr "QQ-Protokoll-Plugin"
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"
@@ -7439,12 +7473,6 @@ msgstr "Dateiübertragung"
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?"
@@ -8502,6 +8530,10 @@ msgstr "Kann das SILC-Protokoll nicht initialisieren"
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"
@@ -8946,12 +8978,6 @@ msgstr "Unbekannte Serverantwort."
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"
@@ -9398,9 +9424,6 @@ msgstr "Konnte Dateibeschreibung nicht erstellen."
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"
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 0555798a39..b1906606fc 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -17454,3 +17454,8 @@ msgstr "发送和接受原始 XMPP 节。"
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 "张家兴"
+
diff --git a/po/zh_TW.po b/po/zh_TW.po
index 82490c3ad1..e992127fa5 100644
--- a/po/zh_TW.po
+++ b/po/zh_TW.po
@@ -13684,6 +13684,11 @@ msgstr "送出、讀取未加工的 XMPP 段落。"
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)"
diff --git a/share/ca-certs/CAcert_Class3.pem b/share/ca-certs/CAcert_Class3.pem
new file mode 100644
index 0000000000..35e2689d98
--- /dev/null
+++ b/share/ca-certs/CAcert_Class3.pem
@@ -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-----
diff --git a/share/ca-certs/CAcert_Root.pem b/share/ca-certs/CAcert_Root.pem
new file mode 100644
index 0000000000..e7dfc82947
--- /dev/null
+++ b/share/ca-certs/CAcert_Root.pem
@@ -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-----
diff --git a/share/ca-certs/Makefile.am b/share/ca-certs/Makefile.am
index 9994491538..d9d341e8a9 100644
--- a/share/ca-certs/Makefile.am
+++ b/share/ca-certs/Makefile.am
@@ -1,4 +1,6 @@
CERTIFICATES = \
+ CAcert_Root.pem \
+ CAcert_Class3.pem \
Equifax_Secure_CA.pem \
GTE_CyberTrust_Global_Root.pem \
Microsoft_Secure_Server_Authority.pem \