summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS2
-rw-r--r--COPYRIGHT6
-rw-r--r--ChangeLog46
-rw-r--r--ChangeLog.API7
-rw-r--r--ChangeLog.win328
-rw-r--r--NEWS4
-rw-r--r--configure.ac89
-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.c64
-rw-r--r--finch/libgnt/gntbutton.c26
-rw-r--r--finch/libgnt/gntkeys.c6
-rw-r--r--libpurple/blist.h7
-rw-r--r--libpurple/cipher.c1
-rw-r--r--libpurple/cmds.c28
-rw-r--r--libpurple/cmds.h38
-rw-r--r--libpurple/connection.h5
-rw-r--r--libpurple/conversation.c28
-rw-r--r--libpurple/conversation.h26
-rw-r--r--libpurple/core.c3
-rw-r--r--libpurple/core.h75
-rw-r--r--libpurple/idle.c2
-rw-r--r--libpurple/internal.h8
-rw-r--r--libpurple/log.c2
-rw-r--r--libpurple/nat-pmp.c4
-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/gg/search.h2
-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/buddy.c16
-rw-r--r--libpurple/protocols/jabber/buddy.h2
-rw-r--r--libpurple/protocols/jabber/chat.c6
-rw-r--r--libpurple/protocols/jabber/disco.c45
-rw-r--r--libpurple/protocols/jabber/google.c10
-rw-r--r--libpurple/protocols/jabber/jabber.c189
-rw-r--r--libpurple/protocols/jabber/jabber.h4
-rw-r--r--libpurple/protocols/jabber/libxmpp.c11
-rw-r--r--libpurple/protocols/jabber/parser.c34
-rw-r--r--libpurple/protocols/jabber/pep.h2
-rw-r--r--libpurple/protocols/jabber/presence.c14
-rw-r--r--libpurple/protocols/jabber/roster.c16
-rw-r--r--libpurple/protocols/jabber/si.c140
-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.c40
-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/msnp9/slplink.c2
-rw-r--r--libpurple/protocols/myspace/user.c4
-rw-r--r--libpurple/protocols/oscar/family_auth.c5
-rw-r--r--libpurple/protocols/oscar/family_icq.c16
-rw-r--r--libpurple/protocols/oscar/family_locate.c46
-rw-r--r--libpurple/protocols/oscar/family_oservice.c10
-rw-r--r--libpurple/protocols/oscar/flap_connection.c108
-rw-r--r--libpurple/protocols/oscar/oscar.c104
-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/ChangeLog125
-rw-r--r--libpurple/protocols/qq/Makefile.am24
-rw-r--r--libpurple/protocols/qq/Makefile.mingw11
-rw-r--r--libpurple/protocols/qq/buddy_info.c364
-rw-r--r--libpurple/protocols/qq/buddy_info.h75
-rw-r--r--libpurple/protocols/qq/buddy_list.c791
-rw-r--r--libpurple/protocols/qq/buddy_list.h43
-rw-r--r--libpurple/protocols/qq/buddy_opt.c157
-rw-r--r--libpurple/protocols/qq/buddy_status.c280
-rw-r--r--libpurple/protocols/qq/buddy_status.h62
-rw-r--r--libpurple/protocols/qq/char_conv.c71
-rw-r--r--libpurple/protocols/qq/char_conv.h2
-rw-r--r--libpurple/protocols/qq/crypt.c131
-rw-r--r--libpurple/protocols/qq/crypt.h8
-rw-r--r--libpurple/protocols/qq/file_trans.c453
-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.c184
-rw-r--r--libpurple/protocols/qq/group_im.h40
-rw-r--r--libpurple/protocols/qq/group_info.c277
-rw-r--r--libpurple/protocols/qq/group_info.h9
-rw-r--r--libpurple/protocols/qq/group_join.c186
-rw-r--r--libpurple/protocols/qq/group_join.h6
-rw-r--r--libpurple/protocols/qq/group_network.c220
-rw-r--r--libpurple/protocols/qq/group_network.h15
-rw-r--r--libpurple/protocols/qq/group_opt.c163
-rw-r--r--libpurple/protocols/qq/group_opt.h8
-rw-r--r--libpurple/protocols/qq/group_search.c85
-rw-r--r--libpurple/protocols/qq/group_search.h2
-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.c440
-rw-r--r--libpurple/protocols/qq/keep_alive.c181
-rw-r--r--libpurple/protocols/qq/login_logout.c504
-rw-r--r--libpurple/protocols/qq/packet_parse.c184
-rw-r--r--libpurple/protocols/qq/packet_parse.h18
-rw-r--r--libpurple/protocols/qq/qq.c350
-rw-r--r--libpurple/protocols/qq/qq.h78
-rw-r--r--libpurple/protocols/qq/qq_base.c523
-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.c1045
-rw-r--r--libpurple/protocols/qq/qq_network.h (renamed from libpurple/protocols/qq/keep_alive.h)21
-rw-r--r--libpurple/protocols/qq/qq_process.c267
-rw-r--r--libpurple/protocols/qq/qq_process.h (renamed from libpurple/protocols/qq/recv_core.h)16
-rw-r--r--libpurple/protocols/qq/qq_proxy.c529
-rw-r--r--libpurple/protocols/qq/qq_proxy.h56
-rw-r--r--libpurple/protocols/qq/qq_trans.c290
-rw-r--r--libpurple/protocols/qq/qq_trans.h61
-rw-r--r--libpurple/protocols/qq/recv_core.c326
-rw-r--r--libpurple/protocols/qq/send_core.c163
-rw-r--r--libpurple/protocols/qq/send_core.h37
-rw-r--r--libpurple/protocols/qq/send_file.c226
-rw-r--r--libpurple/protocols/qq/send_file.h22
-rw-r--r--libpurple/protocols/qq/sendqueue.c156
-rw-r--r--libpurple/protocols/qq/sendqueue.h50
-rw-r--r--libpurple/protocols/qq/sys_msg.c42
-rw-r--r--libpurple/protocols/qq/udp_proxy_s5.c381
-rw-r--r--libpurple/protocols/qq/udp_proxy_s5.h34
-rw-r--r--libpurple/protocols/qq/utils.c129
-rw-r--r--libpurple/protocols/qq/utils.h12
-rw-r--r--libpurple/protocols/silc/buddy.c18
-rw-r--r--libpurple/protocols/silc/silc.c43
-rw-r--r--libpurple/protocols/silc10/buddy.c18
-rw-r--r--libpurple/protocols/silc10/silc.c45
-rw-r--r--libpurple/protocols/simple/simple.c7
-rw-r--r--libpurple/protocols/yahoo/yahoo.c20
-rw-r--r--libpurple/protocols/yahoo/yahoo_profile.c4
-rw-r--r--libpurple/prpl.c104
-rw-r--r--libpurple/prpl.h39
-rwxr-xr-xlibpurple/purple-url-handler61
-rw-r--r--libpurple/roomlist.h1
-rw-r--r--libpurple/server.c86
-rw-r--r--libpurple/server.h4
-rw-r--r--libpurple/sound.h3
-rw-r--r--libpurple/util.c27
-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.c21
-rw-r--r--pidgin/gtkaccount.c4
-rw-r--r--pidgin/gtkblist.c21
-rw-r--r--pidgin/gtkconv.c62
-rw-r--r--pidgin/gtkdialogs.c9
-rw-r--r--pidgin/gtkdocklet.c5
-rw-r--r--pidgin/gtkimhtml.c2
-rw-r--r--pidgin/gtkmain.c2
-rw-r--r--pidgin/gtkmenutray.c9
-rw-r--r--pidgin/gtkroomlist.c73
-rw-r--r--pidgin/gtksmiley.c3
-rw-r--r--pidgin/gtkutils.c7
-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/cap/cap.c2
-rw-r--r--pidgin/plugins/perl/common/Makefile.mingw5
-rw-r--r--pidgin/win32/nsis/pidgin-installer.nsi76
-rw-r--r--pidgin/win32/winpidgin.c101
-rw-r--r--po/POTFILES.in5
-rw-r--r--po/de.po145
-rw-r--r--po/en_GB.po4
-rw-r--r--po/it.po4
-rw-r--r--po/nb.po2
-rw-r--r--po/te.po2
-rw-r--r--po/ur.po2
-rw-r--r--po/vi.po2
-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
279 files changed, 11764 insertions, 9415 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 807931a2fc..7d8488fd2c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,20 +5,41 @@ version 2.5.0 (??/??/2008):
* Ability to create custom smileys (currently only the MSN protocol
utilizes the feature). (Thanks to Mauro Sérgio Ferreira Brasil,
Marcus Lundblad, Jorge Villaseñor and other contributors)
- * Yahoo! Japan now uses UTF-8, matching the behavior of official clients
- and restoring compatibility with the web messenger (Yusuke Odate)
* Add a configure option, --with-system-ssl-certs to allow packagers
to specify a system-wide SSL CA certificates directory. When set,
we don't install our SSL CA certs, so it's important that the
libpurple package depend on the CA certificates.
+ IRC:
+ * /ctcp command (Vladislav Guberinić)
+ * Allow for auto-detection of incoming UTF-8 formatted text on
+ accounts which are configured to use some other encoding.
+
+ MSN:
+ * Update MSN support to protocol 15 (Elliott Sales de Andrade, Jorge
+ Villaseñor, Mike Ruprecht, Carlos Silva, Ma Yuan, Daniel Ljungborg
+ and others)
+ * Personal messages are now supported. They are treated as status
+ messages.
+ * Offline IM is now supported.
+ * Aliasing is now supported server-side.
+ * Buddies are now emblemed. Bots and web clients should now be
+ distinguished.
+ * Update smiley set for non-faces.
+ * Failing to update a buddy icon when the buddy has gone offline no
+ longer crashes.
+ * Custom smileys received in a chat no longer go to a new window.
+ * Processing is no longer completely frozen after the servers block a
+ message because it contains (what they consider) inappropriate text.
+
Pidgin:
* Custom buddy icons can now be added to and removed from buddy list
entries via the buddy list entry right-click menu.
* Resize large incoming custom smileys to a maximum of 96px on either
side.
* Offer to add new buddies into the same contact as existing buddies
- in the same group if the alias given is the same.
+ in the same group if the alias given is the same.
+ * Minor smiley style update.
General:
* Group and Chat buddy list entries can now be given custom buddy
@@ -30,6 +51,25 @@ 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 (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 9fb097b339..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\""
@@ -2223,9 +2252,11 @@ AC_CHECK_HEADERS(sys/select.h sys/uio.h sys/utsname.h sys/wait.h)
AC_CHECK_HEADERS(termios.h)
# sys/sysctl.h on OpenBSD 4.2 requires sys/param.h
+# sys/sysctl.h on FreeBSD requires sys/types.h
AC_CHECK_HEADERS(sys/param.h)
AC_CHECK_HEADERS(sys/sysctl.h, [], [],
[[
+ #include <sys/types.h>
#ifdef HAVE_PARAM_H
# include <sys/param.h>
#endif
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 11584a46ef..0a424b17fa 100644
--- a/finch/gntconv.c
+++ b/finch/gntconv.c
@@ -141,7 +141,7 @@ static void
entry_key_pressed(GntWidget *w, FinchConv *ggconv)
{
const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry));
- if (*text == '/')
+ if (*text == '/' && *(text + 1) != '/')
{
PurpleConversation *conv = ggconv->active_conv;
PurpleCmdStatus status;
@@ -191,7 +191,7 @@ entry_key_pressed(GntWidget *w, FinchConv *ggconv)
}
else
{
- char *escape = g_markup_escape_text(text, -1);
+ char *escape = g_markup_escape_text((*text == '/' ? text + 1 : text), -1);
char *apos = purple_strreplace(escape, "&apos;", "'");
g_free(escape);
escape = apos;
@@ -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/finch/libgnt/gntbutton.c b/finch/libgnt/gntbutton.c
index 090fb5d6bc..1e45a68622 100644
--- a/finch/libgnt/gntbutton.c
+++ b/finch/libgnt/gntbutton.c
@@ -77,18 +77,6 @@ gnt_button_map(GntWidget *widget)
}
static gboolean
-gnt_button_key_pressed(GntWidget *widget, const char *key)
-{
- if (strcmp(key, GNT_KEY_ENTER) == 0 ||
- strcmp(key, SAFE(cursor_down)) == 0)
- {
- gnt_widget_activate(widget);
- return TRUE;
- }
- return FALSE;
-}
-
-static gboolean
gnt_button_clicked(GntWidget *widget, GntMouseEvent event, int x, int y)
{
if (event == GNT_LEFT_MOUSE_DOWN) {
@@ -106,23 +94,33 @@ gnt_button_destroy(GntWidget *widget)
g_free(button->priv);
}
+static gboolean
+button_activate(GntBindable *bind, GList *null)
+{
+ gnt_widget_activate(GNT_WIDGET(bind));
+ return TRUE;
+}
+
static void
gnt_button_class_init(GntWidgetClass *klass)
{
char *style;
+ GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass);
parent_class = GNT_WIDGET_CLASS(klass);
parent_class->draw = gnt_button_draw;
parent_class->map = gnt_button_map;
parent_class->size_request = gnt_button_size_request;
- parent_class->key_pressed = gnt_button_key_pressed;
parent_class->clicked = gnt_button_clicked;
parent_class->destroy = gnt_button_destroy;
style = gnt_style_get_from_name(NULL, "small-button");
small_button = gnt_style_parse_bool(style);
g_free(style);
- GNTDEBUG;
+
+ gnt_bindable_class_register_action(bindable, "activate", button_activate,
+ GNT_KEY_ENTER, NULL);
+ gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass));
}
static void
diff --git a/finch/libgnt/gntkeys.c b/finch/libgnt/gntkeys.c
index c93601ab3a..80523d19f1 100644
--- a/finch/libgnt/gntkeys.c
+++ b/finch/libgnt/gntkeys.c
@@ -80,6 +80,9 @@ void gnt_init_keys()
INSERT_KEY("down", GNT_KEY_DOWN);
INSERT_KEY("tab", "\t");
+ INSERT_KEY("escape", "\033");
+ INSERT_KEY("space", " ");
+ INSERT_KEY("return", GNT_KEY_ENTER);
INSERT_KEY("menu", GNT_KEY_POPUP);
INSERT_KEY("f1", GNT_KEY_F1);
@@ -119,6 +122,9 @@ void gnt_init_keys()
code[ind] = (c ? 1 : 'a') + ch;
INSERT_COMB(str, code);
}
+ if (c == 0) {
+ INSERT_COMB("tab", "\033\t");
+ }
}
}
c = 0;
diff --git a/libpurple/blist.h b/libpurple/blist.h
index a07b35ec3b..a10dcd58eb 100644
--- a/libpurple/blist.h
+++ b/libpurple/blist.h
@@ -31,13 +31,20 @@
#include <glib.h>
+/** @copydoc _PurpleBuddyList */
typedef struct _PurpleBuddyList PurpleBuddyList;
+/** @copydoc _PurpleBlistUiOps */
typedef struct _PurpleBlistUiOps PurpleBlistUiOps;
+/** @copydoc _PurpleBlistNode */
typedef struct _PurpleBlistNode PurpleBlistNode;
+/** @copydoc _PurpleChat */
typedef struct _PurpleChat PurpleChat;
+/** @copydoc _PurpleGroup */
typedef struct _PurpleGroup PurpleGroup;
+/** @copydoc _PurpleContact */
typedef struct _PurpleContact PurpleContact;
+/** @copydoc _PurpleBuddy */
typedef struct _PurpleBuddy PurpleBuddy;
/**************************************************************************/
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 46b4d6087f..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>
@@ -30,6 +31,7 @@
/**************************************************************************/
/*@{*/
+/** The possible results of running a command with purple_cmd_do_command(). */
typedef enum _PurpleCmdStatus {
PURPLE_CMD_STATUS_OK,
PURPLE_CMD_STATUS_FAILED,
@@ -39,16 +41,31 @@ typedef enum _PurpleCmdStatus {
PURPLE_CMD_STATUS_WRONG_TYPE,
} PurpleCmdStatus;
+/** Commands registered with the core return one of these values when run.
+ * Normally, a command will want to return one of the first two; in some
+ * unusual cases, you might want to have several functions called for a
+ * particular command; in this case, they should return
+ * #PURPLE_CMD_RET_CONTINUE to cause the core to fall through to other
+ * commands with the same name.
+ */
typedef enum _PurpleCmdRet {
- PURPLE_CMD_RET_OK, /**< Everything's okay. Don't look for another command to call. */
+ PURPLE_CMD_RET_OK, /**< Everything's okay; Don't look for another command to call. */
PURPLE_CMD_RET_FAILED, /**< The command failed, but stop looking.*/
PURPLE_CMD_RET_CONTINUE, /**< Continue, looking for other commands with the same name to call. */
} PurpleCmdRet;
#define PURPLE_CMD_FUNC(func) ((PurpleCmdFunc)func)
+/** A function implementing a command, as passed to purple_cmd_register().
+ *
+ * @todo document the arguments to these functions.
+ * */
typedef PurpleCmdRet (*PurpleCmdFunc)(PurpleConversation *, const gchar *cmd,
gchar **args, gchar **error, void *data);
+/** A unique integer representing a command registered with
+ * purple_cmd_register(), which can subsequently be passed to
+ * purple_cmd_unregister() to unregister that command.
+ */
typedef guint PurpleCmdId;
typedef enum _PurpleCmdPriority {
@@ -205,6 +222,25 @@ GList *purple_cmd_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/connection.h b/libpurple/connection.h
index 6d237169ff..cb72f42fd9 100644
--- a/libpurple/connection.h
+++ b/libpurple/connection.h
@@ -27,6 +27,7 @@
#ifndef _PURPLE_CONNECTION_H_
#define _PURPLE_CONNECTION_H_
+/** @copydoc _PurpleConnection */
typedef struct _PurpleConnection PurpleConnection;
/**
@@ -121,7 +122,7 @@ typedef enum
*/
PURPLE_CONNECTION_ERROR_CERT_OTHER_ERROR = 15,
- /** Some other error occured which fits into none of the other
+ /** Some other error occurred which fits into none of the other
* categories.
*/
/* purple_connection_error_reason() in connection.c uses the fact that
@@ -223,6 +224,8 @@ typedef struct
void (*_purple_reserved3)(void);
} PurpleConnectionUiOps;
+
+/* Represents an active connection on an account. */
struct _PurpleConnection
{
PurplePlugin *prpl; /**< The protocol plugin. */
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 b67ee04b47..c608cbbb26 100644
--- a/libpurple/conversation.h
+++ b/libpurple/conversation.h
@@ -32,11 +32,17 @@
/**************************************************************************/
+/** @copydoc _PurpleConversationUiOps */
typedef struct _PurpleConversationUiOps PurpleConversationUiOps;
+/** @copydoc _PurpleConversation */
typedef struct _PurpleConversation PurpleConversation;
+/** @copydoc _PurpleConvIm */
typedef struct _PurpleConvIm PurpleConvIm;
+/** @copydoc _PurpleConvChat */
typedef struct _PurpleConvChat PurpleConvChat;
+/** @copydoc _PurpleConvChatBuddy */
typedef struct _PurpleConvChatBuddy PurpleConvChatBuddy;
+/** @copydoc _PurpleConvMessage */
typedef struct _PurpleConvMessage PurpleConvMessage;
/**
@@ -279,11 +285,21 @@ struct _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 a4b2dbea77..98f917f4d4 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"
@@ -130,6 +131,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.
@@ -230,6 +232,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 64d60acfb2..1e4c427309 100644
--- a/libpurple/core.h
+++ b/libpurple/core.h
@@ -1,4 +1,5 @@
/**
+ * @file core.h Startup and shutdown of libpurple
* @defgroup core libpurple
* @see @ref core-signals
*/
@@ -28,12 +29,36 @@
typedef struct PurpleCore PurpleCore;
+/** Callbacks that fire at different points of the initialization and teardown
+ * of libpurple, along with a hook to return descriptive information about the
+ * UI.
+ */
typedef struct
{
+ /** Called just after the preferences subsystem is initialized; the UI
+ * could use this callback to add some preferences it needs to be in
+ * place when other subsystems are initialized.
+ */
void (*ui_prefs_init)(void);
- void (*debug_ui_init)(void); /* Unfortunate necessity. */
+ /** Called just after the debug subsystem is initialized, but before
+ * just about every other component's initialization. The UI should
+ * use this hook to call purple_debug_set_ui_ops() so that debugging
+ * information for other components can be logged during their
+ * initialization.
+ */
+ void (*debug_ui_init)(void);
+ /** Called after all of libpurple has been initialized. The UI should
+ * use this hook to set all other necessary UiOps structures.
+ *
+ * @see @ref ui-ops
+ */
void (*ui_init)(void);
+ /** Called after most of libpurple has been uninitialized. */
void (*quit)(void);
+
+ /** Called by purple_core_get_ui_info(); should return the information
+ * documented there.
+ */
GHashTable* (*get_ui_info)(void);
void (*_purple_reserved1)(void);
@@ -64,17 +89,23 @@ gboolean purple_core_init(const char *ui);
void purple_core_quit(void);
/**
+ * <p>
* Calls purple_core_quit(). This can be used as the function
* passed to purple_timeout_add() when you want to shutdown Purple
* in a specified amount of time. When shutting down Purple
* from a plugin, you must use this instead of purple_core_quit();
* for an immediate exit, use a timeout value of 0:
- * purple_timeout_add(0, purple_core_quitcb, NULL);
+ * </p>
+ *
+ * <code>purple_timeout_add(0, purple_core_quitcb, NULL);</code>
+ *
+ * <p>
* This is ensures that code from your plugin is not being
* executed when purple_core_quit() is called. If the plugin
* called purple_core_quit() directly, you would get a core dump
* after purple_core_quit() executes and control returns to your
* plugin because purple_core_quit() frees all plugins.
+ * </p>
*/
gboolean purple_core_quit_cb(gpointer unused);
@@ -86,7 +117,8 @@ gboolean purple_core_quit_cb(gpointer unused);
const char *purple_core_get_version(void);
/**
- * Returns the ID of the UI that is using the core.
+ * Returns the ID of the UI that is using the core, as passed to
+ * purple_core_init().
*
* @return The ID of the UI that is currently using the core.
*/
@@ -95,7 +127,7 @@ const char *purple_core_get_ui(void);
/**
* Returns a handle to the purple core.
*
- * This is used for such things as signals.
+ * This is used to connect to @ref core-signals "core signals".
*/
PurpleCore *purple_get_core(void);
@@ -114,10 +146,10 @@ void purple_core_set_ui_ops(PurpleCoreUiOps *ops);
PurpleCoreUiOps *purple_core_get_ui_ops(void);
/**
- * Migrates from .gaim to .purple.
+ * Migrates from <tt>.gaim</tt> to <tt>.purple</tt>.
*
- * UIs MUST NOT call this if they have been told to use a custom
- * user directory.
+ * UIs <strong>must not</strong> call this if they have been told to use a
+ * custom user directory.
*
* @return A boolean indicating success or migration failure. On failure,
* the application must display an error to the user and then exit.
@@ -125,20 +157,39 @@ PurpleCoreUiOps *purple_core_get_ui_ops(void);
gboolean purple_core_migrate(void);
/**
- * Ensures that only one instance is running.
+ * Ensures that only one instance is running. If libpurple is built with D-Bus
+ * support, this checks if another process owns the libpurple bus name and if
+ * so whether that process is using the same configuration directory as this
+ * process.
*
- * @return A boolean such that @c TRUE indicates that this is the first instance,
- * whereas @c FALSE indicates that there is another instance running.
+ * @return @c TRUE if this is the first instance of libpurple running;
+ * @c FALSE if there is another instance running.
*
* @since 2.1.0
*/
gboolean purple_core_ensure_single_instance(void);
/**
- * Returns a hashtable containing various information about the UI
+ * Returns a hash table containing various information about the UI. The
+ * following well-known entries may be in the table (along with any others the
+ * UI might choose to include):
+ *
+ * <dl>
+ * <dt><tt>name</tt></dt>
+ * <dd>the user-readable name for the UI.</dd>
+ *
+ * <dt><tt>version</tt></dt>
+ * <dd>a user-readable description of the current version of the UI.</dd>
+ *
+ * <dt><tt>website</tt></dt>
+ * <dd>the UI's website, such as http://pidgin.im.</dd>
+ *
+ * <dt><tt>dev_website</tt></dt>
+ * <dd>the UI's development/support website, such as http://developer.pidgin.im.</dd>
+ * </dl>
*
* @return A GHashTable with strings for keys and values. This
- * hash table must not be freed.
+ * hash table must not be freed and should not be modified.
*
* @since 2.1.0
*
diff --git a/libpurple/idle.c b/libpurple/idle.c
index b7df069315..f71ca9f9b1 100644
--- a/libpurple/idle.c
+++ b/libpurple/idle.c
@@ -252,7 +252,7 @@ signing_off_cb(PurpleConnection *gc, void *data)
PurpleAccount *account;
account = purple_connection_get_account(gc);
- idled_accts = g_list_remove(idled_accts, account);
+ set_account_unidle(account);
}
static void
diff --git a/libpurple/internal.h b/libpurple/internal.h
index 9221a60616..ae0aa26359 100644
--- a/libpurple/internal.h
+++ b/libpurple/internal.h
@@ -140,6 +140,14 @@
# define G_MAXUINT32 ((guint32) 0xffffffff)
#endif
+#ifndef G_MAXSIZE
+# if GLIB_SIZEOF_LONG == 8
+# define G_MAXSIZE ((gsize) 0xffffffffffffffff)
+# else
+# define G_MAXSIZE ((gsize) 0xffffffff)
+# endif
+#endif
+
#if GLIB_CHECK_VERSION(2,6,0)
# include <glib/gstdio.h>
#endif
diff --git a/libpurple/log.c b/libpurple/log.c
index 733cacc89c..4553d6f760 100644
--- a/libpurple/log.c
+++ b/libpurple/log.c
@@ -1083,7 +1083,7 @@ static void log_get_log_sets_common(GHashTable *sets)
}
/* Determine if this (account, name) combination exists as a buddy. */
- if (account != NULL)
+ if (account != NULL && name != NULL && *name != '\0')
set->buddy = (purple_find_buddy(account, name) != NULL);
else
set->buddy = FALSE;
diff --git a/libpurple/nat-pmp.c b/libpurple/nat-pmp.c
index 68f01d41c7..a3717fbbec 100644
--- a/libpurple/nat-pmp.c
+++ b/libpurple/nat-pmp.c
@@ -35,6 +35,10 @@
#include "signals.h"
#include "network.h"
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
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/gg/search.h b/libpurple/protocols/gg/search.h
index 32f158b012..3f89054cf9 100644
--- a/libpurple/protocols/gg/search.h
+++ b/libpurple/protocols/gg/search.h
@@ -130,7 +130,7 @@ ggp_search_destroy(GGPSearches *searches);
* @param gc PurpleConnection.
* @param form Filled in GGPSearchForm.
*
- * @return Sequence number of a search or 0 if an error occured.
+ * @return Sequence number of a search or 0 if an error occurred.
*/
guint32
ggp_search_start(PurpleConnection *gc, GGPSearchForm *form);
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/buddy.c b/libpurple/protocols/jabber/buddy.c
index 56f185e7ca..928e4b95be 100644
--- a/libpurple/protocols/jabber/buddy.c
+++ b/libpurple/protocols/jabber/buddy.c
@@ -1793,22 +1793,6 @@ void jabber_buddy_get_info(PurpleConnection *gc, const char *who)
}
}
-void jabber_buddy_get_info_chat(PurpleConnection *gc, int id,
- const char *resource)
-{
- JabberStream *js = gc->proto_data;
- JabberChat *chat = jabber_chat_find_by_id(js, id);
- char *full_jid;
-
- if(!chat)
- return;
-
- full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server, resource);
- jabber_buddy_get_info_for_jid(js, full_jid);
- g_free(full_jid);
-}
-
-
static void jabber_buddy_set_invisibility(JabberStream *js, const char *who,
gboolean invisible)
{
diff --git a/libpurple/protocols/jabber/buddy.h b/libpurple/protocols/jabber/buddy.h
index 4fa1f41511..3136d62880 100644
--- a/libpurple/protocols/jabber/buddy.h
+++ b/libpurple/protocols/jabber/buddy.h
@@ -96,8 +96,6 @@ void jabber_buddy_resource_free(JabberBuddyResource *jbr);
void jabber_buddy_remove_resource(JabberBuddy *jb, const char *resource);
const char *jabber_buddy_get_status_msg(JabberBuddy *jb);
void jabber_buddy_get_info(PurpleConnection *gc, const char *who);
-void jabber_buddy_get_info_chat(PurpleConnection *gc, int id,
- const char *resource);
GList *jabber_blist_node_menu(PurpleBlistNode *node);
diff --git a/libpurple/protocols/jabber/chat.c b/libpurple/protocols/jabber/chat.c
index c47a2f1bb2..97988ceaf2 100644
--- a/libpurple/protocols/jabber/chat.c
+++ b/libpurple/protocols/jabber/chat.c
@@ -342,12 +342,18 @@ char *jabber_chat_buddy_real_name(PurpleConnection *gc, int id, const char *who)
{
JabberStream *js = gc->proto_data;
JabberChat *chat;
+ JabberChatMember *jcm;
chat = jabber_chat_find_by_id(js, id);
if(!chat)
return NULL;
+ jcm = g_hash_table_lookup(chat->members, who);
+ if (jcm != NULL && jcm->jid)
+ return g_strdup(jcm->jid);
+
+
return g_strdup_printf("%s@%s/%s", chat->room, chat->server, who);
}
diff --git a/libpurple/protocols/jabber/disco.c b/libpurple/protocols/jabber/disco.c
index 7197e60de4..9ebc96d5f7 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);
+ }
}
@@ -280,6 +289,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);
@@ -290,11 +300,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 2b72863798..1c95b85eab 100644
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -68,6 +68,7 @@ GList *jabber_identities = NULL;
GHashTable *jabber_contact_info = NULL;
static void jabber_unregister_account_cb(JabberStream *js);
+static void try_srv_connect(JabberStream *js);
static void jabber_stream_init(JabberStream *js)
{
@@ -278,9 +279,42 @@ static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond
purple_circ_buffer_mark_read(js->write_buffer, ret);
}
-void jabber_send_raw(JabberStream *js, const char *data, int len)
+static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
{
int ret;
+ gboolean success = TRUE;
+
+ if (len == -1)
+ len = strlen(data);
+
+ if (js->writeh == 0)
+ ret = jabber_do_send(js, data, len);
+ else {
+ ret = -1;
+ errno = EAGAIN;
+ }
+
+ if (ret < 0 && errno != EAGAIN) {
+ purple_connection_error_reason (js->gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Write error"));
+ success = FALSE;
+ } else if (ret < len) {
+ if (ret < 0)
+ ret = 0;
+ if (js->writeh == 0)
+ js->writeh = purple_input_add(
+ js->gsc ? js->gsc->fd : js->fd,
+ PURPLE_INPUT_WRITE, jabber_send_cb, js);
+ purple_circ_buffer_append(js->write_buffer,
+ data + ret, len - ret);
+ }
+
+ return success;
+}
+
+void jabber_send_raw(JabberStream *js, const char *data, int len)
+{
/* because printing a tab to debug every minute gets old */
if(strcmp(data, "\t"))
@@ -289,85 +323,39 @@ void jabber_send_raw(JabberStream *js, const char *data, int len)
/* If we've got a security layer, we need to encode the data,
* splitting it on the maximum buffer length negotiated */
-
+
purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data);
if (data == NULL)
return;
-
+
#ifdef HAVE_CYRUS_SASL
if (js->sasl_maxbuf>0) {
- int pos;
+ int pos = 0;
if (!js->gsc && js->fd<0)
return;
- pos = 0;
+
if (len == -1)
len = strlen(data);
+
while (pos < len) {
int towrite;
const char *out;
unsigned olen;
- if ((len - pos) < js->sasl_maxbuf)
- towrite = len - pos;
- else
- towrite = js->sasl_maxbuf;
+ towrite = MIN((len - pos), js->sasl_maxbuf);
sasl_encode(js->sasl, &data[pos], towrite, &out, &olen);
pos += towrite;
- if (js->writeh == 0)
- ret = jabber_do_send(js, out, olen);
- else {
- ret = -1;
- errno = EAGAIN;
- }
-
- if (ret < 0 && errno != EAGAIN)
- purple_connection_error_reason (js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Write error"));
- else if (ret < olen) {
- if (ret < 0)
- ret = 0;
- if (js->writeh == 0)
- js->writeh = purple_input_add(
- js->gsc ? js->gsc->fd : js->fd,
- PURPLE_INPUT_WRITE,
- jabber_send_cb, js);
- purple_circ_buffer_append(js->write_buffer,
- out + ret, olen - ret);
- }
+ if (!do_jabber_send_raw(js, out, olen))
+ break;
}
return;
}
#endif
- if (len == -1)
- len = strlen(data);
-
- if (js->writeh == 0)
- ret = jabber_do_send(js, data, len);
- else {
- ret = -1;
- errno = EAGAIN;
- }
-
- if (ret < 0 && errno != EAGAIN)
- purple_connection_error_reason (js->gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Write error"));
- else if (ret < len) {
- if (ret < 0)
- ret = 0;
- if (js->writeh == 0)
- js->writeh = purple_input_add(
- js->gsc ? js->gsc->fd : js->fd,
- PURPLE_INPUT_WRITE, jabber_send_cb, js);
- purple_circ_buffer_append(js->write_buffer,
- data + ret, len - ret);
- }
- return;
+ do_jabber_send_raw(js, data, len);
}
int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len)
@@ -393,9 +381,9 @@ void jabber_send(JabberStream *js, xmlnode *packet)
g_free(txt);
}
-static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout)
+static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused)
{
- purple_timeout_remove(GPOINTER_TO_INT(timeout));
+ purple_timeout_remove(js->keepalive_timeout);
js->keepalive_timeout = -1;
}
@@ -419,7 +407,7 @@ void jabber_keepalive(PurpleConnection *gc)
xmlnode_set_namespace(ping, "urn:xmpp:ping");
js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc);
- jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout));
+ jabber_iq_set_callback(iq, jabber_pong_cb, NULL);
jabber_iq_send(iq);
}
}
@@ -448,12 +436,17 @@ jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
jabber_stream_init(js);
}
- if(errno == EAGAIN)
+ if(len < 0 && errno == EAGAIN)
return;
- else
+ else {
+ if (len == 0)
+ purple_debug_info("jabber", "Server closed the connection.\n");
+ else
+ purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno));
purple_connection_error_reason (js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Read Error"));
+ }
}
static void
@@ -488,9 +481,13 @@ jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
jabber_parser_process(js, buf, len);
if(js->reinit)
jabber_stream_init(js);
- } else if(errno == EAGAIN) {
+ } else if(len < 0 && errno == EAGAIN) {
return;
} else {
+ if (len == 0)
+ purple_debug_info("jabber", "Server closed the connection.\n");
+ else
+ purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno));
purple_connection_error_reason (js->gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Read Error"));
@@ -556,11 +553,19 @@ jabber_login_callback(gpointer data, gint source, const gchar *error)
JabberStream *js = gc->proto_data;
if (source < 0) {
- purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
- purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc);
+ if (js->srv_rec != NULL) {
+ purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error);
+ try_srv_connect(js);
+ } else {
+ purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
+ purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc);
+ }
return;
}
+ g_free(js->srv_rec);
+ js->srv_rec = NULL;
+
js->fd = source;
if(js->state == JABBER_STREAM_CONNECTING)
@@ -595,37 +600,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;
- js = data;
+ /* Fall back to the defaults (I'm not sure if we should actually do this) */
+ jabber_login_connect(js, js->user->domain, js->user->domain,
+ purple_account_get_int(js->gc->account, "port", 5222), TRUE);
+}
+
+static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data)
+{
+ JabberStream *js = 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);
}
}
@@ -707,7 +737,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);
@@ -1188,7 +1218,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",
@@ -1359,7 +1389,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 d75cd88073..ee67405b92 100644
--- a/libpurple/protocols/jabber/jabber.h
+++ b/libpurple/protocols/jabber/jabber.h
@@ -203,6 +203,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;
+
/* BOSH stuff*/
PurpleBOSHConnection bosh;
};
diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c
index b4940b6572..a898d9af77 100644
--- a/libpurple/protocols/jabber/libxmpp.c
+++ b/libpurple/protocols/jabber/libxmpp.c
@@ -89,7 +89,7 @@ static PurplePluginProtocolInfo prpl_info =
jabber_message_send_chat, /* chat_send */
jabber_keepalive, /* keepalive */
jabber_register_account, /* register_user */
- jabber_buddy_get_info_chat, /* get_cb_info */
+ NULL, /* get_cb_info */
NULL, /* get_cb_away */
jabber_roster_alias_change, /* alias_buddy */
jabber_roster_group_change, /* group_buddy */
@@ -194,6 +194,7 @@ 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/pep.h b/libpurple/protocols/jabber/pep.h
index 0fd76bdc74..80d9875a36 100644
--- a/libpurple/protocols/jabber/pep.h
+++ b/libpurple/protocols/jabber/pep.h
@@ -56,7 +56,7 @@ void jabber_pep_register_handler(const char *xmlns, JabberPEPHandler handlerfunc
* @parameter id The item id of the requested item (may be NULL)
* @parameter cb The callback to be used when this item is received
*
- * The items element passed to the callback will be NULL if any error occured (like a permission error, node doesn't exist etc.)
+ * The items element passed to the callback will be NULL if any error occurred (like a permission error, node doesn't exist etc.)
*/
void jabber_pep_request_item(JabberStream *js, const char *to, const char *node, const char *id, JabberPEPHandler cb);
diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c
index 60e8212376..61e812a957 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..62774fced3 100644
--- a/libpurple/protocols/jabber/roster.c
+++ b/libpurple/protocols/jabber/roster.c
@@ -316,7 +316,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 +328,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 cfb1efc440..aa6a6b9703 100644
--- a/libpurple/protocols/jabber/si.c
+++ b/libpurple/protocols/jabber/si.c
@@ -623,7 +623,7 @@ jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
return;
else if(acceptfd == -1) {
purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
- /* TODO: This should cancel the ft */
+ /* Don't cancel the ft - allow it to fall to the next streamhost.*/
return;
}
@@ -659,8 +659,11 @@ jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet,
jsx = xfer->data;
- if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+ if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result")) {
+ if (type && !strcmp(type, "error"))
+ purple_xfer_cancel_remote(xfer);
return;
+ }
if(!(from = xmlnode_get_attrib(packet, "from")))
return;
@@ -718,14 +721,16 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
JabberSIXfer *jsx;
JabberIq *iq;
xmlnode *query, *streamhost;
- char *jid, port[6];
- const char *local_ip, *public_ip, *ft_proxies;
+ char port[6];
GList *tmp;
JabberBytestreamsStreamhost *sh, *sh2;
+ int streamhost_count = 0;
jsx = xfer->data;
jsx->listen_data = NULL;
+ /* I'm not sure under which conditions this can happen
+ * (it seems like it shouldn't be possible */
if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
purple_xfer_unref(xfer);
return;
@@ -733,13 +738,6 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
purple_xfer_unref(xfer);
- if (sock < 0) {
- purple_xfer_cancel_local(xfer);
- return;
- }
-
- jsx->local_streamhost_fd = sock;
-
iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET,
"http://jabber.org/protocol/bytestreams");
xmlnode_set_attrib(iq->node, "to", xfer->who);
@@ -747,84 +745,43 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
xmlnode_set_attrib(query, "sid", jsx->stream_id);
- jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
- jsx->js->user->domain, jsx->js->user->resource);
- xfer->local_port = purple_network_get_port_from_fd(sock);
- g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
-
- /* TODO: Should there be an option to not use the local host as a ft proxy?
- * (to prevent revealing IP address, etc.) */
-
- /* Include the localhost's IP (for in-network transfers) */
- local_ip = purple_network_get_local_system_ip(jsx->js->fd);
- if (strcmp(local_ip, "0.0.0.0") != 0)
- {
- streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", jid);
- xmlnode_set_attrib(streamhost, "host", local_ip);
- xmlnode_set_attrib(streamhost, "port", port);
- }
-
- /* Include the public IP (assuming that there is a port mapped somehow) */
- /* TODO: Check that it isn't the same as above and is a valid IP */
- public_ip = purple_network_get_my_ip(jsx->js->fd);
- if (strcmp(public_ip, local_ip) != 0)
- {
- streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", jid);
- xmlnode_set_attrib(streamhost, "host", public_ip);
- xmlnode_set_attrib(streamhost, "port", port);
- }
-
- g_free(jid);
-
- /* The listener for the local proxy */
- xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
- jabber_si_xfer_bytestreams_send_connected_cb, xfer);
-
- /* 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);
+ /* If we successfully started listening locally */
+ if (sock >= 0) {
+ gchar *jid;
+ const char *local_ip, *public_ip;
- 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;
+ jsx->local_streamhost_fd = sock;
- if((tmp = strchr(ft_proxy_list[i], ':'))) {
- portnum = atoi(tmp + 1);
- *tmp = '\0';
- } else
- portnum = 7777;
-
- g_snprintf(port, sizeof(port), "%hu", portnum);
-
- purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p",
- jsx, jsx->streamhosts, i, ft_proxy_list[i]);
- if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL)
- continue;
+ jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
+ jsx->js->user->domain, jsx->js->user->resource);
+ xfer->local_port = purple_network_get_port_from_fd(sock);
+ g_snprintf(port, sizeof(port), "%hu", xfer->local_port);
+ /* Include the localhost's IP (for in-network transfers) */
+ local_ip = purple_network_get_local_system_ip(jsx->js->fd);
+ if (strcmp(local_ip, "0.0.0.0") != 0) {
+ streamhost_count++;
streamhost = xmlnode_new_child(query, "streamhost");
- xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]);
- xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]);
+ xmlnode_set_attrib(streamhost, "jid", jid);
+ xmlnode_set_attrib(streamhost, "host", local_ip);
xmlnode_set_attrib(streamhost, "port", port);
+ }
- sh = g_new0(JabberBytestreamsStreamhost, 1);
- sh->jid = g_strdup(ft_proxy_list[i]);
- sh->host = g_strdup(ft_proxy_list[i]);
- sh->port = portnum;
-
- jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh);
+ /* Include the public IP (assuming that there is a port mapped somehow) */
+ public_ip = purple_network_get_my_ip(jsx->js->fd);
+ if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) {
+ streamhost_count++;
+ streamhost = xmlnode_new_child(query, "streamhost");
+ xmlnode_set_attrib(streamhost, "jid", jid);
+ xmlnode_set_attrib(streamhost, "host", public_ip);
+ xmlnode_set_attrib(streamhost, "port", port);
}
- g_strfreev(ft_proxy_list);
+ g_free(jid);
+
+ /* The listener for the local proxy */
+ xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
+ jabber_si_xfer_bytestreams_send_connected_cb, xfer);
}
for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
@@ -840,6 +797,7 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
continue;
+ streamhost_count++;
streamhost = xmlnode_new_child(query, "streamhost");
xmlnode_set_attrib(streamhost, "jid", sh->jid);
xmlnode_set_attrib(streamhost, "host", sh->host);
@@ -855,6 +813,14 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
}
+ /* We have no way of transferring, cancel the transfer */
+ if (streamhost_count == 0) {
+ jabber_iq_free(iq);
+ /* We should probably notify the target, but this really shouldn't ever happen */
+ purple_xfer_cancel_local(xfer);
+ return;
+ }
+
jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
jabber_iq_send(iq);
@@ -869,13 +835,14 @@ jabber_si_xfer_bytestreams_send_init(PurpleXfer *xfer)
purple_xfer_ref(xfer);
jsx = xfer->data;
+
+ /* TODO: Should there be an option to not use the local host as a ft proxy?
+ * (to prevent revealing IP address, etc.) */
jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
jabber_si_xfer_bytestreams_listen_cb, xfer);
if (jsx->listen_data == NULL) {
- purple_xfer_unref(xfer);
- /* XXX: couldn't open a port, we're fscked */
- purple_xfer_cancel_local(xfer);
- return;
+ /* We couldn't open a local port. Perhaps we can use a proxy. */
+ jabber_si_xfer_bytestreams_listen_cb(-1, xfer);
}
}
@@ -1213,9 +1180,6 @@ void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file
js = gc->proto_data;
- if(!purple_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE))
- return;
-
xfer = jabber_si_new_xfer(gc, who);
if (file)
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 088b789d81..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
@@ -593,7 +573,7 @@ msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
}
else if (slpmsg->size)
{
- if ((offset + len) > slpmsg->size)
+ if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
{
purple_debug_error("msn",
"Oversized slpmsg - msgsize=%lld offset=%" G_GSIZE_FORMAT " len=%" G_GSIZE_FORMAT "\n",
@@ -774,8 +754,7 @@ 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/msnp9/slplink.c b/libpurple/protocols/msnp9/slplink.c
index 3cadeec328..d94cc392de 100644
--- a/libpurple/protocols/msnp9/slplink.c
+++ b/libpurple/protocols/msnp9/slplink.c
@@ -597,7 +597,7 @@ msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg)
}
else if (slpmsg->size)
{
- if ((offset + len) > slpmsg->size)
+ if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size)
{
purple_debug_error("msn", "Oversized slpmsg\n");
g_return_if_reached();
diff --git a/libpurple/protocols/myspace/user.c b/libpurple/protocols/myspace/user.c
index 9157ab454d..c5ab898b8b 100644
--- a/libpurple/protocols/myspace/user.c
+++ b/libpurple/protocols/myspace/user.c
@@ -635,7 +635,7 @@ static void msim_username_is_available_cb(MsimSession *session, MsimMessage *use
if (!body) {
purple_debug_info("msim_username_is_available_cb", "No body for %s?!\n", username);
purple_connection_error_reason(session->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
- "An error occured while trying to set the username.\n"
+ "An error occurred while trying to set the username.\n"
"Please try again, or visit http://editprofile.myspace.com/index.cfm?"
"fuseaction=profile.username to set your username.");
return;
@@ -778,7 +778,7 @@ static void msim_username_is_set_cb(MsimSession *session, MsimMessage *userinfo,
uid = msim_msg_get_integer(userinfo, "uid");
lid = msim_msg_get_integer(userinfo, "lid");
body = msim_msg_get_dictionary(userinfo, "body");
- errmsg = g_strdup("An error occured while trying to set the username.\n"
+ errmsg = g_strdup("An error occurred while trying to set the username.\n"
"Please try again, or visit http://editprofile.myspace.com/index.cfm?"
"fuseaction=profile.username to set your username.");
diff --git a/libpurple/protocols/oscar/family_auth.c b/libpurple/protocols/oscar/family_auth.c
index b8da1aedf0..e23f6d60d5 100644
--- a/libpurple/protocols/oscar/family_auth.c
+++ b/libpurple/protocols/oscar/family_auth.c
@@ -81,12 +81,9 @@ aim_encode_password(const char *password, guint8 *encoded)
static int
aim_encode_password_md5(const char *password, size_t password_len, const char *key, guint8 *digest)
{
- PurpleCipher *cipher;
PurpleCipherContext *context;
- cipher = purple_ciphers_find_cipher("md5");
-
- context = purple_cipher_context_new(cipher, NULL);
+ context = purple_cipher_context_new_by_name("md5", NULL);
purple_cipher_context_append(context, (const guchar *)key, strlen(key));
purple_cipher_context_append(context, (const guchar *)password, password_len);
purple_cipher_context_append(context, (const guchar *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
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/family_oservice.c b/libpurple/protocols/oscar/family_oservice.c
index 7f2b6679ad..4d95bc8cb2 100644
--- a/libpurple/protocols/oscar/family_oservice.c
+++ b/libpurple/protocols/oscar/family_oservice.c
@@ -952,13 +952,10 @@ aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 le
byte_stream_putraw(&bs, buf, 0x10);
} else if (buf && (len > 0)) { /* use input buffer */
- PurpleCipher *cipher;
PurpleCipherContext *context;
guchar digest[16];
- cipher = purple_ciphers_find_cipher("md5");
-
- context = purple_cipher_context_new(cipher, NULL);
+ context = purple_cipher_context_new_by_name("md5", NULL);
purple_cipher_context_append(context, buf, len);
purple_cipher_context_digest(context, 16, digest, NULL);
purple_cipher_context_destroy(context);
@@ -966,7 +963,6 @@ aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 le
byte_stream_putraw(&bs, digest, 0x10);
} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
- PurpleCipher *cipher;
PurpleCipherContext *context;
guchar digest[16];
guint8 nil = '\0';
@@ -975,9 +971,7 @@ aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 le
* I'm not sure if we really need the empty append with the
* new MD5 functions, so I'll leave it in, just in case.
*/
- cipher = purple_ciphers_find_cipher("md5");
-
- context = purple_cipher_context_new(cipher, NULL);
+ context = purple_cipher_context_new_by_name("md5", NULL);
purple_cipher_context_append(context, &nil, 0);
purple_cipher_context_digest(context, 16, digest, NULL);
purple_cipher_context_destroy(context);
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 75b9cccb7c..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;
@@ -1977,7 +2023,17 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
}
else
{
- purple_prpl_got_user_status(account, info->sn, status_id, NULL);
+ PurpleBuddy *b = purple_find_buddy(account, info->sn);
+ PurplePresence *presence = purple_buddy_get_presence(b);
+ PurpleStatus *old_status = purple_presence_get_active_status(presence);
+ PurpleStatus *new_status = purple_presence_get_status(presence, status_id);
+
+ /* If our status_id would change with this update, pass it to the core.
+ * However, if our status_id would not change, do nothing, as we would clear out any existing
+ * attributes on the status prematurely. purple_got_infoblock() will update the message as needed.
+ */
+ if (old_status != new_status)
+ purple_prpl_got_user_status(account, info->sn, status_id, NULL);
}
/* Login time stuff */
@@ -2053,8 +2109,28 @@ 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);
+ }
+ }
}
}
@@ -3061,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);
@@ -4907,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);
@@ -6414,15 +6485,12 @@ void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
if (img == NULL) {
aim_ssi_delicon(od);
} else {
- PurpleCipher *cipher;
PurpleCipherContext *context;
guchar md5[16];
gconstpointer data = purple_imgstore_get_data(img);
size_t len = purple_imgstore_get_size(img);
-
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
+ context = purple_cipher_context_new_by_name("md5", NULL);
purple_cipher_context_append(context, data, len);
purple_cipher_context_digest(context, 16, md5, NULL);
purple_cipher_context_destroy(context);
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
new file mode 100644
index 0000000000..1ef12628c6
--- /dev/null
+++ b/libpurple/protocols/qq/ChangeLog
@@ -0,0 +1,125 @@
+2008.08.03 - csyfek <csyfek(at)gmail.com>
+ * Commit lost files to Pidgin
+
+2008.08.02 - csyfek <csyfek(at)gmail.com>
+ * Commit to Pidgin
+ * Tickets:
+ Fixes #1861
+ Fixes #1902
+ References #5112
+
+2008.08.02 - ccpaging <ecc_hy(at)hotmail.com>
+ * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH]
+ * Use random value in inikey
+ * TEA header padding in crypt.c
+ * Rewrite login part of qq_process
+
+2008.07.31 - ccpaging <ecc_hy(at)hotmail.com>
+ * Fixed: send reply when get duplicate server command. The server may not get our reply before.
+ * Tag custom picture as text "(Broken)"
+
+2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+ * Change some debug message
+ * Modify buddy status flag according to eva for QQ2006
+ * Modify buddy status parse and correspond to eva2
+ * Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str
+ * Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation
+ * Source file changed:
+ Merge buddy_status into buddy_list
+ Change login_logout to qq_base
+ Merge keep_alive into qq_base
+ New qq_process extract from qq_network
+ * Fixed: Byte alignment bug in crypt.c, tested in ARM PDA
+ * Fixed: group chat message may get in before getting group info, and so group info is empty
+ * Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c
+ * Add some new group command identify according eva but further program
+ * Add some new QQ client version identify
+ * Fixed: Identify buddy's client version by IM packet, and not by status
+ * Add some new info in buddy's tooltip text
+ * Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly
+ * Use new timeout function to handle send keep_alive, resend packet, update buddy status
+ * Add new advanced options:
+ The end user may change interval of keep_alive, resend packet, update buddy status to feed their need.
+ For example, saving network flow when use mobile phone.
+ Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not.
+ The intervals of keep alive and update status should be multiple of resend's interval,
+ Since we use counter not time() in a single timeout function for efficiency.
+ * Rewrite qq_trans.c, and use one g_list to manage:
+ Store server packet before login, and prase all of them when get login
+ Store client send packet for resend scanning, confirm server reply, filter duplicate server reply
+ Store server packet for filter out duplicate
+ * Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c
+ * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server:
+ In QQ protocol, one packet reply may need a new packet send later.
+ We may call it packet trigger. The triggers always is hided in every qq_process_reply.
+ Now we try to extract those triggers and put into a single function,
+ and then every trigger should be obviously and easy to manage.
+
+2008.07.12 - ccpaging <ecc_hy(at)hotmail.com>
+ * Fixed: Always lost connection. Now send keep alive packet in every 30 seconds
+ * Minor fix for debug information
+ * Filter \r\n and replace with SPCAE in group notive
+ * Fixed a memory leak
+ * Tickets:
+ * Fixes #4024.
+
+2008.06.29 - csyfek <csyfek(at)gmail.com>
+ * Minor bug fix
+ * Add some doxygen syntax for preparing development documentation
+ * References #6199
+
+2008.06.28 - ccpaging <ecc_hy(at)hotmail.com>, moo <phpxcache(at)gmail.com>
+ * Patches from moo<phpxcache@gmail.com> and ccpaging<ccpaging@foxmail.com>.
+ * Tickets:
+ * Fixes #4956.
+ * Fixes #2998.
+
+2008.06.07 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+ * Clean code and apply patches from QuLogic
+
+2008.05.19 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+ * Reconnect server 5 time in 5000 ms, when connect failed
+ * Rename sendqueue.c/sendqueue.h to qq_trans.c/qq_trans.h
+ * Rewrite packet_process
+ * Rewrite qq_send_cmd
+ * Create server list, try to connect every server when failed
+
+2008.05.14 - ccpaging <ecc_hy(at)hotmail.com>
+ * Move function for before login packets storing to sendqueue
+ * Use transaction data structure to store before login packets
+ * Rewrite tcp_pending and packet_process in qq_network.c
+
+2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+ * Remove function _create_packet_head_seq in qq_network.c
+ * Create new function encap in qq_netowork.c
+ * Clean code of qq_send_packet_request_login_token and qq_send_packet_login in login_out.c
+
+2008.05.09 - ccpaging <ecc_hy(at)hotmail.com>
+ * Clean code of packet_parse.c, enable PARSER_DEBUG
+ * Rewrite send_queue
+
+2008.05.08 - ccpaging <ecc_hy(at)hotmail.com>
+ * Rewrite qq_network
+ * Add srv resolve function when qq_login
+ * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
+ * Move orignal qq_disconnect to qq_close
+ * qq_data alloc in qq_open and release in qq_close
+ * Network connect of QQ is created in qq_connect, and release in qq_disconnect
+
+2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+ * Merge function _qq_common_clean in qq_proxy.c to qq_disconnect
+ * Move orignal qq_disconnect to qq_close
+ * qq_data alloc in qq_open and release in qq_close
+ * Network connect of QQ is created in qq_connect, and release in qq_disconnect
+
+2008.05.05 - ccpaging <ecc_hy(at)hotmail.com>
+ * Add qq_hex_dump function
+
+2008.04.25 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com>
+ * Rewrite read_packet and create_packet functions, use qq_put and qq_get functions instead
+ * New logic in accord with protocol models to handle packets, some related functions rewritten
+
+2008.03.24 - ccpaging <ecc_hy(at)hotmail.com>
+ * Remove qq_crypt function in crypt.c, use qq_crypt and qq_decrypt directly
+
+** since pidgin-2.4.0 ***
diff --git a/libpurple/protocols/qq/Makefile.am b/libpurple/protocols/qq/Makefile.am
index 30bd6eaed7..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,28 +42,22 @@ 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 \
qq.h \
- qq_proxy.c \
- qq_proxy.h \
- recv_core.c \
- recv_core.h \
- send_core.c \
- send_core.h \
+ qq_network.c \
+ qq_network.h \
send_file.c \
send_file.h \
- sendqueue.c \
- sendqueue.h \
+ qq_trans.c \
+ qq_trans.h \
sys_msg.c \
sys_msg.h \
- udp_proxy_s5.c \
- udp_proxy_s5.h \
utils.c \
utils.h
diff --git a/libpurple/protocols/qq/Makefile.mingw b/libpurple/protocols/qq/Makefile.mingw
index fc4257f676..0c5f6c992e 100644
--- a/libpurple/protocols/qq/Makefile.mingw
+++ b/libpurple/protocols/qq/Makefile.mingw
@@ -59,17 +59,14 @@ C_SRC = \
group_search.c \
header_info.c \
im.c \
- keep_alive.c \
- login_logout.c \
+ qq_process.c \
+ qq_base.c \
packet_parse.c \
qq.c \
- qq_proxy.c \
- recv_core.c \
- send_core.c \
+ qq_network.c \
send_file.c \
- sendqueue.c \
+ qq_trans.c \
sys_msg.c \
- udp_proxy_s5.c \
utils.c
OBJECTS = $(C_SRC:%.c=%.o)
diff --git a/libpurple/protocols/qq/buddy_info.c b/libpurple/protocols/qq/buddy_info.c
index 624b75b7bc..3859189b22 100644
--- a/libpurple/protocols/qq/buddy_info.c
+++ b/libpurple/protocols/qq/buddy_info.c
@@ -29,12 +29,13 @@
#include "utils.h"
#include "packet_parse.h"
+#include "buddy_list.h"
#include "buddy_info.h"
#include "char_conv.h"
#include "crypt.h"
#include "header_info.h"
-#include "keep_alive.h"
-#include "send_core.h"
+#include "qq_base.h"
+#include "qq_network.h"
#define QQ_PRIMARY_INFORMATION _("Primary Information")
#define QQ_ADDITIONAL_INFORMATION _("Additional Information")
@@ -85,6 +86,7 @@ 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 */
@@ -94,6 +96,46 @@ typedef struct _qq_info_query {
gboolean modify_info;
} qq_info_query;
+typedef struct _contact_info {
+ gchar *uid;
+ gchar *nick;
+ gchar *country;
+ gchar *province;
+ gchar *zipcode;
+ gchar *address;
+ gchar *tel;
+ gchar *age;
+ gchar *gender;
+ gchar *name;
+ gchar *email;
+ gchar *pager_sn;
+ gchar *pager_num;
+ gchar *pager_sp;
+ gchar *pager_base_num;
+ gchar *pager_type;
+ gchar *occupation;
+ gchar *homepage;
+ gchar *auth_type;
+ gchar *unknown1;
+ gchar *unknown2;
+ gchar *face;
+ gchar *hp_num;
+ gchar *hp_type;
+ gchar *intro;
+ gchar *city;
+ gchar *unknown3;
+ gchar *unknown4;
+ gchar *unknown5;
+ gchar *is_open_hp;
+ gchar *is_open_contact;
+ gchar *college;
+ gchar *horoscope;
+ gchar *zodiac;
+ gchar *blood;
+ gchar *qq_show;
+ gchar *unknown6; /* always 0x2D */
+} contact_info;
+
/* We get an info packet on ourselves before we modify our information.
* Even though not all of the information is modifiable, it still
* all needs to be there when we send out the modify info packet */
@@ -137,7 +179,7 @@ static gchar *field_value(const gchar *field, const gchar **choice, gint choice_
} else {
return NULL;
}
- /* else ASCIIized index */
+ /* else ASCIIized index */
} else {
if (strcmp(choice[index], "-") != 0)
return g_strdup(choice[index]);
@@ -161,15 +203,15 @@ static gboolean append_field_value(PurpleNotifyUserInfo *user_info, const gchar
if (value != NULL) {
purple_notify_user_info_add_pair(user_info, title, value);
g_free(value);
-
+
return TRUE;
}
-
+
return FALSE;
}
static PurpleNotifyUserInfo *
-info_to_notify_user_info(const contact_info *info)
+ info_to_notify_user_info(const contact_info *info)
{
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
const gchar *intro;
@@ -209,25 +251,25 @@ info_to_notify_user_info(const contact_info *info)
/* for debugging */
/*
- g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
- append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
- append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
- append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
- append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
- append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
- append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
- append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
- append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
- append_field_value(info_text, info->face, "face", NULL, 0);
- append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
- append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
- append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
- append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
- append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
- append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
- append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
- append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
- */
+ g_string_append_printf(info_text, "<br /><br /><b>%s</b><br />", "Miscellaneous");
+ append_field_value(info_text, info->pager_sn, "pager_sn", NULL, 0);
+ append_field_value(info_text, info->pager_num, "pager_num", NULL, 0);
+ append_field_value(info_text, info->pager_sp, "pager_sp", NULL, 0);
+ append_field_value(info_text, info->pager_base_num, "pager_base_num", NULL, 0);
+ append_field_value(info_text, info->pager_type, "pager_type", NULL, 0);
+ append_field_value(info_text, info->auth_type, "auth_type", NULL, 0);
+ append_field_value(info_text, info->unknown1, "unknown1", NULL, 0);
+ append_field_value(info_text, info->unknown2, "unknown2", NULL, 0);
+ append_field_value(info_text, info->face, "face", NULL, 0);
+ append_field_value(info_text, info->hp_type, "hp_type", NULL, 0);
+ append_field_value(info_text, info->unknown3, "unknown3", NULL, 0);
+ append_field_value(info_text, info->unknown4, "unknown4", NULL, 0);
+ append_field_value(info_text, info->unknown5, "unknown5", NULL, 0);
+ append_field_value(info_text, info->is_open_hp, "is_open_hp", NULL, 0);
+ append_field_value(info_text, info->is_open_contact, "is_open_contact", NULL, 0);
+ append_field_value(info_text, info->qq_show, "qq_show", NULL, 0);
+ append_field_value(info_text, info->unknown6, "unknown6", NULL, 0);
+ */
return user_info;
}
@@ -243,7 +285,7 @@ void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_wi
qd = (qq_data *) gc->proto_data;
g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
- qq_send_cmd(gc, QQ_CMD_GET_USER_INFO, TRUE, 0, TRUE, (guint8 *) uid_str, strlen(uid_str));
+ qq_send_cmd(qd, QQ_CMD_GET_USER_INFO, (guint8 *) uid_str, strlen(uid_str));
query = g_new0(qq_info_query, 1);
query->uid = uid;
@@ -271,27 +313,141 @@ void qq_prepare_modify_info(PurpleConnection *gc)
}
/* send packet to modify personal information */
-static void qq_send_packet_modify_info(PurpleConnection *gc, gchar **segments)
+static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info)
{
- gint i;
- guint8 *raw_data, *cursor, bar;
+ qq_data *qd = (qq_data *) gc->proto_data;
+ gint bytes = 0;
+ guint8 raw_data[MAX_PACKET_SIZE - 128] = {0};
+ guint8 bar;
- g_return_if_fail(segments != NULL);
+ g_return_if_fail(info != NULL);
bar = 0x1f;
- raw_data = g_newa(guint8, MAX_PACKET_SIZE - 128);
- cursor = raw_data;
- create_packet_b(raw_data, &cursor, bar);
+ bytes += qq_put8(raw_data + bytes, bar);
/* important! skip the first uid entry */
- for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
- create_packet_b(raw_data, &cursor, bar);
- create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
- }
- create_packet_b(raw_data, &cursor, bar);
-
- qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+ /*
+ for (i = 1; i < QQ_CONTACT_FIELDS; i++) {
+ create_packet_b(raw_data, &cursor, bar);
+ create_packet_data(raw_data, &cursor, (guint8 *) segments[i], strlen(segments[i]));
+ }
+ */
+ /* uid */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->uid, strlen(info->uid));
+ /* nick */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->nick, strlen(info->nick));
+ /* country */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->country, strlen(info->country));
+ /* province */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->province, strlen(info->province));
+ /* zipcode */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zipcode, strlen(info->zipcode));
+ /* address */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->address, strlen(info->address));
+ /* tel */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->tel, strlen(info->tel));
+ /* age */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->age, strlen(info->age));
+ /* gender */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->gender, strlen(info->gender));
+ /* name */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->name, strlen(info->name));
+ /* email */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->email, strlen(info->email));
+ /* pager_sn */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sn, strlen(info->pager_sn));
+ /* pager_num */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_num, strlen(info->pager_num));
+ /* pager_sp */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_sp, strlen(info->pager_sp));
+ /* pager_base_num */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_base_num, strlen(info->pager_base_num));
+ /* pager_type */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->pager_type, strlen(info->pager_type));
+ /* occupation */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->occupation, strlen(info->occupation));
+ /* homepage */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->homepage, strlen(info->homepage));
+ /* auth_type */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->auth_type, strlen(info->auth_type));
+ /* unknown1 */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown1, strlen(info->unknown1));
+ /* unknown2 */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown2, strlen(info->unknown2));
+ /* face */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->face, strlen(info->face));
+ /* hp_num */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_num, strlen(info->hp_num));
+ /* hp_type */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->hp_type, strlen(info->hp_type));
+ /* intro */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->intro, strlen(info->intro));
+ /* city */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->city, strlen(info->city));
+ /* unknown3 */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown3, strlen(info->unknown3));
+ /* unknown4 */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown4, strlen(info->unknown4));
+ /* unknown5 */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown5, strlen(info->unknown5));
+ /* is_open_hp */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_hp, strlen(info->is_open_hp));
+ /* is_open_contact */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->is_open_contact, strlen(info->is_open_contact));
+ /* college */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->college, strlen(info->college));
+ /* horoscope */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->horoscope, strlen(info->horoscope));
+ /* zodiac */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->zodiac, strlen(info->zodiac));
+ /* blood */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->blood, strlen(info->blood));
+ /* qq_show */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->qq_show, strlen(info->qq_show));
+ /* unknown6 */
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)info->unknown6, strlen(info->unknown6));
+
+ bytes += qq_put8(raw_data + bytes, bar);
+
+ qq_send_cmd(qd, QQ_CMD_UPDATE_INFO, raw_data, bytes);
}
@@ -407,8 +563,11 @@ static void modify_info_ok_cb(modify_info_data *mid, PurpleRequestFields *fields
groups = groups->next;
}
- /* This casting looks like a horrible idea to me -DAA */
- qq_send_packet_modify_info(gc, (gchar **) info);
+ /* This casting looks like a horrible idea to me -DAA
+ * yes, rewritten -s3e
+ * qq_send_packet_modify_info(gc, (gchar **) info);
+ */
+ qq_send_packet_modify_info(gc, info);
g_strfreev((gchar **) mid->info);
g_free(mid);
@@ -520,11 +679,11 @@ static void create_modify_info_dialogue(PurpleConnection *gc, const contact_info
mid->info->unknown6 = g_strdup(info->unknown6);
purple_request_fields(gc, _("Modify my information"),
- _("Modify my information"), NULL, fields,
- _("Update my information"), G_CALLBACK(modify_info_ok_cb),
- _("Cancel"), G_CALLBACK(modify_info_cancel_cb),
- purple_connection_get_account(gc), NULL, NULL,
- mid);
+ _("Modify my information"), NULL, fields,
+ _("Update my information"), G_CALLBACK(modify_info_ok_cb),
+ _("Cancel"), G_CALLBACK(modify_info_cancel_cb),
+ purple_connection_get_account(gc), NULL, NULL,
+ mid);
}
}
@@ -578,10 +737,9 @@ void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const
gchar *data;
gsize len;
- if (!g_file_get_contents(iconfile, &data, &len, NULL))
+ if (!g_file_get_contents(iconfile, &data, &len, NULL)) {
g_return_if_reached();
- else
- {
+ } else {
purple_buddy_icons_set_for_user(account, who, data, len, icon_num);
}
}
@@ -608,10 +766,10 @@ void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
/* make sure we're using an appropriate icon */
if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0
- && icon_path[dir_len] == G_DIR_SEPARATOR
- && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
- && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
- && icon_len <= 3)) {
+ && icon_path[dir_len] == G_DIR_SEPARATOR
+ && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0
+ && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0
+ && icon_len <= 3)) {
if (icon_global)
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", errmsg);
else
@@ -650,13 +808,13 @@ static void _qq_update_buddy_icon(PurpleAccount *account, const gchar *name, gin
old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy);
if (old_icon_num == NULL ||
- strcmp(icon_num_str, old_icon_num))
+ strcmp(icon_num_str, old_icon_num))
{
gchar *icon_path;
icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S,
- QQ_ICON_PREFIX, icon_num_str,
- QQ_ICON_SUFFIX, NULL);
+ QQ_ICON_PREFIX, icon_num_str,
+ QQ_ICON_SUFFIX, NULL);
qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path);
g_free(icon_path);
@@ -665,12 +823,13 @@ static void _qq_update_buddy_icon(PurpleAccount *account, const gchar *name, gin
}
/* after getting info or modify myself, refresh the buddy list accordingly */
-void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
+static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
{
PurpleBuddy *b;
qq_data *qd;
qq_buddy *q_bud;
- gchar *alias_utf8, *purple_name;
+ gchar *alias_utf8;
+ gchar *purple_name;
PurpleAccount *account = purple_connection_get_account(gc);
qd = (qq_data *) gc->proto_data;
@@ -728,7 +887,7 @@ void qq_process_get_info_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
qd->modifying_face = FALSE;
g_free(info->face);
info->face = icon;
- qq_send_packet_modify_info(gc, segments);
+ qq_send_packet_modify_info(gc, (contact_info *)segments);
}
qq_refresh_buddy_and_myself(info, gc);
@@ -777,41 +936,43 @@ void qq_info_query_free(qq_data *qd)
void qq_send_packet_get_level(PurpleConnection *gc, guint32 uid)
{
- guint8 buf[5];
- guint32 tmp = g_htonl(uid);
- buf[0] = 0;
- memcpy(buf+1, &tmp, 4);
- qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, 5);
+ qq_data *qd = (qq_data *) gc->proto_data;
+ guint8 buf[16] = {0};
+ gint bytes = 0;
+
+ bytes += qq_put8(buf + bytes, 0x00);
+ bytes += qq_put32(buf + bytes, uid);
+
+ qd = (qq_data *) gc->proto_data;
+ qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, bytes);
}
void qq_send_packet_get_buddies_levels(PurpleConnection *gc)
{
- guint8 *buf, *tmp;
+ guint8 *buf;
guint16 size;
qq_buddy *q_bud;
qq_data *qd = (qq_data *) gc->proto_data;
GList *node = qd->buddies;
+ 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);
- tmp = buf + 1;
-
- while (node != NULL) {
- guint32 tmp4;
- q_bud = (qq_buddy *) node->data;
- if (q_bud != NULL) {
- tmp4 = g_htonl(q_bud->uid);
- memcpy(tmp, &tmp4, 4);
- tmp += 4;
- }
- node = node->next;
+ if ( qd->buddies == NULL) {
+ return;
+ }
+ /* server only sends back levels for online buddies, no point
+ * in asking for anyone else */
+ size = 4 * g_list_length(qd->buddies) + 1;
+ buf = g_newa(guint8, size);
+ bytes += qq_put8(buf + bytes, 0x00);
+
+ while (NULL != node) {
+ q_bud = (qq_buddy *) node->data;
+ if (NULL != q_bud) {
+ bytes += qq_put32(buf + bytes, q_bud->uid);
}
- qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size);
- g_free(buf);
+ node = node->next;
}
+ qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
}
void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
@@ -822,10 +983,11 @@ void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
PurpleBuddy *b;
qq_buddy *q_bud;
gint decr_len, i;
- guint8 *decr_buf, *tmp;
+ guint8 *decr_buf;
PurpleAccount *account = purple_connection_get_account(gc);
qq_data *qd = (qq_data *) gc->proto_data;
-
+ gint bytes = 0;
+
decr_len = buf_len;
decr_buf = g_new0(guint8, buf_len);
if (!qq_decrypt(buf, buf_len, qd->session_key, decr_buf, &decr_len)) {
@@ -835,28 +997,23 @@ void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
decr_len--;
if (decr_len % 12 != 0) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
+ "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
decr_len -= (decr_len % 12);
}
-
- tmp = decr_buf + 1;
+
+ bytes += 1;
/* this byte seems random */
/*
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
- */
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]);
+ */
for (i = 0; i < decr_len; i += 12) {
- uid = g_ntohl(*(guint32 *) tmp);
- tmp += 4;
- onlineTime = g_ntohl(*(guint32 *) tmp);
- tmp += 4;
- level = g_ntohs(*(guint16 *) tmp);
- tmp += 2;
- timeRemainder = g_ntohs(*(guint16 *) tmp);
- tmp += 2;
- /*
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n",
+ bytes += qq_get32(&uid, decr_buf + bytes);
+ bytes += qq_get32(&onlineTime, decr_buf + bytes);
+ bytes += qq_get16(&level, decr_buf + bytes);
+ bytes += qq_get16(&timeRemainder, decr_buf + bytes);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_LEVEL",
+ "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
uid, onlineTime, level, timeRemainder);
- */
purple_name = uid_to_purple_name(uid);
b = purple_find_buddy(account, purple_name);
q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
@@ -872,9 +1029,10 @@ void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
}
} else {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Got an online buddy %d, but not in my buddy list\n", uid);
+ "Got an online buddy %d, but not in my buddy list\n", uid);
}
g_free(purple_name);
}
g_free(decr_buf);
}
+
diff --git a/libpurple/protocols/qq/buddy_info.h b/libpurple/protocols/qq/buddy_info.h
index 7e121f5d1f..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
@@ -44,47 +67,6 @@
#define QQ_ICON_PREFIX "qq_"
#define QQ_ICON_SUFFIX ".png"
-typedef struct _contact_info {
- gchar *uid;
- gchar *nick;
- gchar *country;
- gchar *province;
- gchar *zipcode;
- gchar *address;
- gchar *tel;
- gchar *age;
- gchar *gender;
- gchar *name;
- gchar *email;
- gchar *pager_sn;
- gchar *pager_num;
- gchar *pager_sp;
- gchar *pager_base_num;
- gchar *pager_type;
- gchar *occupation;
- gchar *homepage;
- gchar *auth_type;
- gchar *unknown1;
- gchar *unknown2;
- gchar *face;
- gchar *hp_num;
- gchar *hp_type;
- gchar *intro;
- gchar *city;
- gchar *unknown3;
- gchar *unknown4;
- gchar *unknown5;
- gchar *is_open_hp;
- gchar *is_open_contact;
- gchar *college;
- gchar *horoscope;
- gchar *zodiac;
- gchar *blood;
- gchar *qq_show;
- gchar *unknown6; /* always 0x2D */
-} contact_info;
-
-void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc);
void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window);
void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img);
void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile);
@@ -95,5 +77,4 @@ void qq_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 7c64455d79..6ad75db382 100644
--- a/libpurple/protocols/qq/buddy_list.c
+++ b/libpurple/protocols/qq/buddy_list.c
@@ -32,57 +32,53 @@
#include "packet_parse.h"
#include "buddy_info.h"
#include "buddy_list.h"
-#include "buddy_status.h"
#include "buddy_opt.h"
#include "char_conv.h"
#include "crypt.h"
#include "header_info.h"
-#include "keep_alive.h"
-#include "send_core.h"
+#include "qq_base.h"
#include "group.h"
#include "group_find.h"
#include "group_internal.h"
#include "group_info.h"
-#include "qq_proxy.h"
+#include "qq_network.h"
#define QQ_GET_ONLINE_BUDDY_02 0x02
#define QQ_GET_ONLINE_BUDDY_03 0x03 /* unknown function */
-#define QQ_ONLINE_BUDDY_ENTRY_LEN 38
-
-typedef struct _qq_friends_online_entry {
- qq_buddy_status *s;
+typedef struct _qq_buddy_online {
+ qq_buddy_status bs;
guint16 unknown1;
- guint8 flag1;
+ guint8 ext_flag;
guint8 comm_flag;
guint16 unknown2;
guint8 ending; /* 0x00 */
-} qq_friends_online_entry;
+} qq_buddy_online;
/* get a list of online_buddies */
void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
{
qq_data *qd;
- guint8 *raw_data, *cursor;
+ guint8 *raw_data;
+ gint bytes = 0;
qd = (qq_data *) gc->proto_data;
raw_data = g_newa(guint8, 5);
- cursor = raw_data;
/* 000-000 get online friends cmd
* only 0x02 and 0x03 returns info from server, other valuse all return 0xff
* I can also only send the first byte (0x02, or 0x03)
* and the result is the same */
- create_packet_b(raw_data, &cursor, QQ_GET_ONLINE_BUDDY_02);
+ bytes += qq_put8(raw_data + bytes, QQ_GET_ONLINE_BUDDY_02);
/* 001-001 seems it supports 255 online buddies at most */
- create_packet_b(raw_data, &cursor, position);
+ bytes += qq_put8(raw_data + bytes, position);
/* 002-002 */
- create_packet_b(raw_data, &cursor, 0x00);
+ bytes += qq_put8(raw_data + bytes, 0x00);
/* 003-004 */
- create_packet_w(raw_data, &cursor, 0x0000);
+ bytes += qq_put16(raw_data + bytes, 0x0000);
- qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_ONLINE, TRUE, 0, TRUE, raw_data, 5);
+ qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5);
qd->last_get_online = time(NULL);
}
@@ -90,326 +86,605 @@ void qq_send_packet_get_buddies_online(PurpleConnection *gc, guint8 position)
* server may return a position tag if list is too long for one packet */
void qq_send_packet_get_buddies_list(PurpleConnection *gc, guint16 position)
{
- guint8 *raw_data, *cursor;
- gint data_len;
+ qq_data *qd = (qq_data *) gc->proto_data;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
- data_len = 3;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
/* 000-001 starting position, can manually specify */
- create_packet_w(raw_data, &cursor, position);
+ bytes += qq_put16(raw_data + bytes, position);
/* before Mar 18, 2004, any value can work, and we sent 00
* I do not know what data QQ server is expecting, as QQ2003iii 0304 itself
* even can sending packets 00 and get no response.
* Now I tested that 00,00,00,00,00,01 work perfectly
* March 22, found the 00,00,00 starts to work as well */
- create_packet_b(raw_data, &cursor, 0x00);
+ bytes += qq_put8(raw_data + bytes, 0x00);
- qq_send_cmd(gc, QQ_CMD_GET_FRIENDS_LIST, TRUE, 0, TRUE, raw_data, data_len);
+ qq_send_cmd(qd, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes);
}
/* get all list, buddies & Quns with groupsid support */
void qq_send_packet_get_all_list_with_group(PurpleConnection *gc, guint32 position)
{
- guint8 *raw_data, *cursor;
- gint data_len;
+ qq_data *qd = (qq_data *) gc->proto_data;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
- data_len = 10;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
/* 0x01 download, 0x02, upload */
- create_packet_b(raw_data, &cursor, 0x01);
+ bytes += qq_put8(raw_data + bytes, 0x01);
/* unknown 0x02 */
- create_packet_b(raw_data, &cursor, 0x02);
+ bytes += qq_put8(raw_data + bytes, 0x02);
/* unknown 00 00 00 00 */
- create_packet_dw(raw_data, &cursor, 0x00000000);
- create_packet_dw(raw_data, &cursor, position);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, position);
- qq_send_cmd(gc, QQ_CMD_GET_ALL_LIST_WITH_GROUP, TRUE, 0, TRUE, raw_data, data_len);
+ 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);
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Online buddy entry, %s", dump->str);
- g_string_free(dump, TRUE);
+ gint bytes = 0;
+
+ g_return_val_if_fail(data != NULL && bs != NULL, -1);
+
+ /* 000-003: uid */
+ bytes += qq_get32(&bs->uid, data + bytes);
+ /* 004-004: 0x01 */
+ bytes += qq_get8(&bs->unknown1, data + bytes);
+ /* this is no longer the IP, it seems QQ (as of 2006) no longer sends
+ * the buddy's IP in this packet. all 0s */
+ /* 005-008: ip */
+ bytes += qq_getIP(&bs->ip, data + bytes);
+ /* port info is no longer here either */
+ /* 009-010: port */
+ bytes += qq_get16(&bs->port, data + bytes);
+ /* 011-011: 0x00 */
+ bytes += qq_get8(&bs->unknown2, data + bytes);
+ /* 012-012: status */
+ bytes += qq_get8(&bs->status, data + bytes);
+ /* 013-014: client_version */
+ bytes += qq_get16(&bs->unknown3, data + bytes);
+ /* 015-030: unknown key */
+ bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_STATUS",
+ "uid: %d, U1: %d, ip: %s:%d, U2:%d, status:%d, U3:%04X\n",
+ bs->uid, bs->unknown1, inet_ntoa(bs->ip), bs->port,
+ bs->unknown2, bs->status, bs->unknown3);
+
+ return bytes;
}
+#define QQ_ONLINE_BUDDY_ENTRY_LEN 38
+
/* process the reply packet for get_buddies_online packet */
-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;
- guint8 *data, *cursor, position;
+ 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;
data = g_newa(guint8, len);
- cursor = data;
purple_debug(PURPLE_DEBUG_INFO, "QQ", "processing get_buddies_online_reply\n");
-
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
-
- _qq_show_packet("Get buddies online reply packet", data, len);
-
- read_packet_b(data, &cursor, len, &position);
-
- fe = g_newa(qq_friends_online_entry, 1);
- fe->s = g_newa(qq_buddy_status, 1);
-
- while (cursor < (data + len)) {
- /* based on one online buddy entry */
- bytes = 0;
- /* 000-030 qq_buddy_status */
- bytes += qq_buddy_status_read(data, &cursor, len, fe->s);
- /* 031-032: unknown4 */
- bytes += read_packet_w(data, &cursor, len, &fe->unknown1);
- /* 033-033: flag1 */
- bytes += read_packet_b(data, &cursor, len, &fe->flag1);
- /* 034-034: comm_flag */
- bytes += read_packet_b(data, &cursor, len, &fe->comm_flag);
- /* 035-036: */
- bytes += read_packet_w(data, &cursor, len, &fe->unknown2);
- /* 037-037: */
- bytes += read_packet_b(data, &cursor, len, &fe->ending); /* 0x00 */
-
- if (fe->s->uid == 0 || bytes != QQ_ONLINE_BUDDY_ENTRY_LEN) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "uid=0 or entry complete len(%d) != %d",
- bytes, QQ_ONLINE_BUDDY_ENTRY_LEN);
- g_free(fe->s->ip);
- g_free(fe->s->unknown_key);
- continue;
- } /* check if it is a valid entry */
-
- if (QQ_DEBUG)
- _qq_buddies_online_reply_dump_unclear(fe);
-
- /* update buddy information */
- b = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(fe->s->uid));
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
-
- if (q_bud != NULL) { /* we find one and update qq_buddy */
- if(0 != fe->s->client_version)
- q_bud->client_version = fe->s->client_version;
- g_memmove(q_bud->ip, fe->s->ip, 4);
- q_bud->port = fe->s->port;
- q_bud->status = fe->s->status;
- q_bud->flag1 = fe->flag1;
- q_bud->comm_flag = fe->comm_flag;
- qq_update_buddy_contact(gc, q_bud);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Got an online buddy %d, but not in my buddy list\n", fe->s->uid);
- }
- g_free(fe->s->ip);
- g_free(fe->s->unknown_key);
- }
-
- if(cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
- }
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+ return -1;
+ }
- if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+ /* qq_show_packet("Get buddies online reply packet", data, len); */
- qq_send_packet_get_buddies_online(gc, position);
- } else {
- qq_send_packet_get_buddies_levels(gc);
- qq_refresh_all_buddy_status(gc);
+ bytes = 0;
+ bytes += qq_get8(&position, data + bytes);
+
+ count = 0;
+ while (bytes < len) {
+ if (len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "[buddies online] only %d, need %d",
+ (len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
+ break;
+ }
+ memset(&bo, 0 ,sizeof(bo));
+
+ /* set flag */
+ bytes_buddy = bytes;
+ /* based on one online buddy entry */
+ /* 000-030 qq_buddy_status */
+ bytes += get_buddy_status(&(bo.bs), data + bytes);
+ /* 031-032: */
+ bytes += qq_get16(&bo.unknown1, data + bytes);
+ /* 033-033: ext_flag */
+ bytes += qq_get8(&bo.ext_flag, data + bytes);
+ /* 034-034: comm_flag */
+ bytes += qq_get8(&bo.comm_flag, data + bytes);
+ /* 035-036: */
+ bytes += qq_get16(&bo.unknown2, data + bytes);
+ /* 037-037: */
+ bytes += qq_get8(&bo.ending, data + bytes); /* 0x00 */
+
+ if (bo.bs.uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "uid=0 or entry complete len(%d) != %d",
+ (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
+ continue;
+ } /* check if it is a valid entry */
+
+ /* update buddy information */
+ b = purple_find_buddy(purple_connection_get_account(gc),
+ uid_to_purple_name(bo.bs.uid) );
+ q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+ if (q_bud == NULL) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
+ continue;
}
+ /* we find one and update qq_buddy */
+ /*
+ if(0 != fe->s->client_version)
+ q_bud->client_version = fe->s->client_version;
+ */
+ q_bud->ip.s_addr = bo.bs.ip.s_addr;
+ q_bud->port = bo.bs.port;
+ q_bud->status = bo.bs.status;
+ q_bud->ext_flag = bo.ext_flag;
+ q_bud->comm_flag = bo.comm_flag;
+ qq_update_buddy_contact(gc, q_bud);
+ count++;
+ }
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+ if(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
}
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d online buddies, nextposition=%u\n",
+ count, (guint) position);
+ return position;
}
+
/* 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, bytes_expected, i;
+ gint len, bytes_expected, count;
+ gint bytes, buddy_bytes;
guint16 position, unknown;
- guint8 *data, *cursor, pascal_len;
+ 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;
data = g_newa(guint8, len);
- cursor = data;
-
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- read_packet_w(data, &cursor, len, &position);
- /* the following data is buddy list in this packet */
- i = 0;
- while (cursor < (data + len)) {
- q_bud = g_new0(qq_buddy, 1);
- bytes = 0;
- /* 000-003: uid */
- bytes += read_packet_dw(data, &cursor, len, &q_bud->uid);
- /* 004-005: icon index (1-255) */
- bytes += read_packet_w(data, &cursor, len, &q_bud->face);
- /* 006-006: age */
- bytes += read_packet_b(data, &cursor, len, &q_bud->age);
- /* 007-007: gender */
- bytes += read_packet_b(data, &cursor, len, &q_bud->gender);
- pascal_len = convert_as_pascal_string(cursor, &q_bud->nickname, QQ_CHARSET_DEFAULT);
- cursor += pascal_len;
- bytes += pascal_len;
- bytes += read_packet_w(data, &cursor, len, &unknown);
- /* flag1: (0-7)
- * bit1 => qq show
- * comm_flag: (0-7)
- * bit1 => member
- * bit4 => TCP mode
- * bit5 => open mobile QQ
- * bit6 => bind to mobile
- * bit7 => whether having a video
- */
- bytes += read_packet_b(data, &cursor, len, &q_bud->flag1);
- bytes += read_packet_b(data, &cursor, len, &q_bud->comm_flag);
-
- bytes_expected = 12 + pascal_len;
-
- if (q_bud->uid == 0 || bytes != bytes_expected) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes);
- g_free(q_bud->nickname);
- g_free(q_bud);
- continue;
- } else {
- i++;
- }
- if (QQ_DEBUG) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "buddy [%09d]: flag1=0x%02x, comm_flag=0x%02x\n",
- q_bud->uid, q_bud->flag1, q_bud->comm_flag);
- }
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+ return -1;
+ }
+ bytes = 0;
+ bytes += qq_get16(&position, data + bytes);
+ /* the following data is buddy list in this packet */
+ count = 0;
+ while (bytes < len) {
+ q_bud = g_new0(qq_buddy, 1);
+ /* set flag */
+ buddy_bytes = bytes;
+ /* 000-003: uid */
+ bytes += qq_get32(&q_bud->uid, data + bytes);
+ /* 004-005: icon index (1-255) */
+ bytes += qq_get16(&q_bud->face, data + bytes);
+ /* 006-006: age */
+ bytes += qq_get8(&q_bud->age, data + bytes);
+ /* 007-007: gender */
+ bytes += qq_get8(&q_bud->gender, data + bytes);
+
+ pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT);
+ bytes += pascal_len;
+
+ bytes += qq_get16(&unknown, data + bytes);
+ bytes += qq_get8(&q_bud->ext_flag, data + bytes);
+ bytes += qq_get8(&q_bud->comm_flag, data + bytes);
+
+ bytes_expected = 12 + pascal_len;
+
+ if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Buddy entry, expect %d bytes, read %d bytes\n", bytes_expected, bytes - buddy_bytes);
+ g_free(q_bud->nickname);
+ g_free(q_bud);
+ continue;
+ } else {
+ count++;
+ }
- name = uid_to_purple_name(q_bud->uid);
- b = purple_find_buddy(gc->account, name);
- g_free(name);
+ if (QQ_DEBUG) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
+ q_bud->uid, q_bud->ext_flag, q_bud->comm_flag, q_bud->nickname);
+ }
- if (b == NULL)
- b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
+ name = uid_to_purple_name(q_bud->uid);
+ b = purple_find_buddy(gc->account, name);
+ g_free(name);
- b->proto_data = q_bud;
- qd->buddies = g_list_append(qd->buddies, q_bud);
- qq_update_buddy_contact(gc, q_bud);
+ if (b == NULL) {
+ b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
}
- if(cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
- }
- if (position == QQ_FRIENDS_LIST_POSITION_END) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get friends list done, %d buddies\n", i);
- qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
- } else {
- qq_send_packet_get_buddies_list(gc, position);
- }
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+ b->proto_data = q_bud;
+ qd->buddies = g_list_append(qd->buddies, q_bud);
+ qq_update_buddy_contact(gc, q_bud);
}
+
+ if(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
+ }
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies, nextposition=%u\n",
+ count, (guint) position);
+ return position;
}
-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;
- guint8 *data, *cursor;
+ gint bytes = 0;
+ guint8 *data;
guint8 sub_cmd, reply_code;
guint32 unknown, position;
guint32 uid;
guint8 type, groupid;
qq_group *group;
- g_return_if_fail(buf != NULL && buf_len != 0);
+ g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
qd = (qq_data *) gc->proto_data;
len = buf_len;
data = g_newa(guint8, len);
- cursor = data;
-
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- read_packet_b(data, &cursor, len, &sub_cmd);
- g_return_if_fail(sub_cmd == 0x01);
- read_packet_b(data, &cursor, len, &reply_code);
- if(0 != reply_code) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Get all list with group reply, reply_code(%d) is not zero", reply_code);
- }
- read_packet_dw(data, &cursor, len, &unknown);
- read_packet_dw(data, &cursor, len, &position);
- /* the following data is all list in this packet */
- i = 0;
- j = 0;
- while (cursor < (data + len)) {
- /* 00-03: uid */
- read_packet_dw(data, &cursor, len, &uid);
- /* 04: type 0x1:buddy 0x4:Qun */
- read_packet_b(data, &cursor, len, &type);
- /* 05: groupid*4 */ /* seems to always be 0 */
- read_packet_b(data, &cursor, len, &groupid);
- /*
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
- groupid >>= 2;
- */
- if (uid == 0 || (type != 0x1 && type != 0x4)) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Buddy entry, uid=%d, type=%d", uid, type);
- continue;
- }
- if(0x1 == type) { /* a buddy */
- /* don't do anything but count - buddies are handled by
- * qq_send_packet_get_buddies_list */
- ++i;
- } else { /* a group */
- group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
- if(group == NULL) {
- qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
- group = g_newa(qq_group, 1);
- group->internal_group_id = uid;
- qq_send_cmd_group_get_group_info(gc, group);
- } else {
- group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
- qq_group_refresh(gc, group);
- qq_send_cmd_group_get_group_info(gc, group);
- }
- ++j;
+
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
+ return -1;
+ }
+
+ bytes += qq_get8(&sub_cmd, data + bytes);
+ g_return_val_if_fail(sub_cmd == 0x01, -1);
+
+ bytes += qq_get8(&reply_code, data + bytes);
+ if(0 != reply_code) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "Get all list with group reply, reply_code(%d) is not zero", reply_code);
+ }
+
+ bytes += qq_get32(&unknown, data + bytes);
+ bytes += qq_get32(&position, data + bytes);
+ /* the following data is all list in this packet */
+ i = 0;
+ j = 0;
+ while (bytes < len) {
+ /* 00-03: uid */
+ bytes += qq_get32(&uid, data + bytes);
+ /* 04: type 0x1:buddy 0x4:Qun */
+ bytes += qq_get8(&type, data + bytes);
+ /* 05: groupid*4 */ /* seems to always be 0 */
+ bytes += qq_get8(&groupid, data + bytes);
+ /*
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "groupid: %i\n", groupid);
+ groupid >>= 2;
+ */
+ if (uid == 0 || (type != 0x1 && type != 0x4)) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Buddy entry, uid=%d, type=%d", uid, type);
+ continue;
+ }
+ if(0x1 == type) { /* a buddy */
+ /* don't do anything but count - buddies are handled by
+ * qq_send_packet_get_buddies_list */
+ ++i;
+ } else { /* a group */
+ group = qq_group_find_by_id(gc, uid, QQ_INTERNAL_ID);
+ if(group == NULL) {
+ qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
+ group = g_newa(qq_group, 1);
+ group->internal_group_id = uid;
+ qq_send_cmd_group_get_group_info(gc, group);
+ } else {
+ group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+ qq_group_refresh(gc, group);
+ qq_send_cmd_group_get_group_info(gc, group);
}
+ ++j;
}
- if(cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
- }
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get all list done, %d buddies and %d Quns\n", i, j);
+ }
+
+ if(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "qq_process_get_all_list_with_group_reply: Dangerous error! maybe protocol changed, notify developers!");
+ }
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Received %d buddies and %d groups, nextposition=%u\n", i, j, (guint) position);
+ return position;
+}
+
+#define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001
+#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */
+
+/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses,
+ * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy,
+ * using different accounts to get info. */
+
+/* check if status means online or offline */
+gboolean is_online(guint8 status)
+{
+ switch(status) {
+ case QQ_BUDDY_ONLINE_NORMAL:
+ case QQ_BUDDY_ONLINE_AWAY:
+ case QQ_BUDDY_ONLINE_INVISIBLE:
+ return TRUE;
+ case QQ_BUDDY_ONLINE_OFFLINE:
+ return FALSE;
+ }
+ return FALSE;
+}
+
+/* Help calculate the correct icon index to tell the server. */
+gint get_icon_offset(PurpleConnection *gc)
+{
+ PurpleAccount *account;
+ PurplePresence *presence;
+
+ account = purple_connection_get_account(gc);
+ presence = purple_account_get_presence(account);
+
+ if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
+ return 2;
+ } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
+ || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
+ || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
+ return 1;
} else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
+ 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 15ea046a05..f94f8b9a01 100644
--- a/libpurple/protocols/qq/buddy_opt.c
+++ b/libpurple/protocols/qq/buddy_opt.c
@@ -34,9 +34,9 @@
#include "crypt.h"
#include "header_info.h"
#include "im.h"
-#include "keep_alive.h"
+#include "qq_base.h"
#include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "utils.h"
#define PURPLE_GROUP_QQ_FORMAT "QQ (%s)"
@@ -61,33 +61,33 @@ typedef struct _qq_add_buddy_request {
/* send packet to remove a buddy from my buddy list */
static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
{
+ qq_data *qd = (qq_data *) gc->proto_data;
gchar uid_str[11];
g_return_if_fail(uid > 0);
g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
- qq_send_cmd(gc, QQ_CMD_DEL_FRIEND, TRUE, 0,
- TRUE, (guint8 *) uid_str, strlen(uid_str));
+ qq_send_cmd(qd, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str));
}
/* try to remove myself from someone's buddy list */
static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
{
- guint8 *raw_data, *cursor;
+ qq_data *qd = (qq_data *) gc->proto_data;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
g_return_if_fail(uid > 0);
- raw_data = g_newa(guint8, 4);
- cursor = raw_data;
- create_packet_dw(raw_data, &cursor, uid);
+ bytes += qq_put32(raw_data + bytes, uid);
- qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, TRUE, 0, TRUE, raw_data, 4);
+ qq_send_cmd(qd, QQ_CMD_REMOVE_SELF, raw_data, bytes);
}
/* try to add a buddy without authentication */
static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
{
- qq_data *qd;
+ qq_data *qd = (qq_data *) gc->proto_data;
qq_add_buddy_request *req;
gchar uid_str[11];
@@ -95,11 +95,9 @@ 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(gc, QQ_CMD_ADD_FRIEND_WO_AUTH, TRUE, 0,
- TRUE, (guint8 *) uid_str, strlen(uid_str));
+ qq_send_cmd(qd, QQ_CMD_ADD_BUDDY_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
/* must be set after sending packet to get the correct send_seq */
- qd = (qq_data *) gc->proto_data;
req = g_new0(qq_add_buddy_request, 1);
req->seq = qd->send_seq;
req->uid = uid;
@@ -109,28 +107,29 @@ static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
/* this buddy needs authentication, text conversion is done at lowest level */
static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
{
+ qq_data *qd = (qq_data *) gc->proto_data;
gchar *text_qq, uid_str[11];
- guint8 bar, *cursor, *raw_data;
+ guint8 bar, *raw_data;
+ gint bytes = 0;
g_return_if_fail(uid != 0);
g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
bar = 0x1f;
raw_data = g_newa(guint8, QQ_MSG_IM_MAX);
- cursor = raw_data;
- create_packet_data(raw_data, &cursor, (guint8 *) uid_str, strlen(uid_str));
- create_packet_b(raw_data, &cursor, bar);
- create_packet_b(raw_data, &cursor, response);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *) uid_str, strlen(uid_str));
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_put8(raw_data + bytes, response);
if (text != NULL) {
text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
- create_packet_b(raw_data, &cursor, bar);
- create_packet_data(raw_data, &cursor, (guint8 *) text_qq, strlen(text_qq));
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *) text_qq, strlen(text_qq));
g_free(text_qq);
}
- qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+ qq_send_cmd(qd, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
}
static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
@@ -210,10 +209,10 @@ void qq_reject_add_request_with_gc_and_uid(gc_and_uid *g)
nombre = uid_to_purple_name(uid);
purple_request_input(gc, _("Reject request"), msg1, msg2,
- _("Sorry, you are not my type..."), TRUE, FALSE,
- NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
- purple_connection_get_account(gc), nombre, NULL,
- g2);
+ _("Sorry, you are not my type..."), TRUE, FALSE,
+ NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
+ purple_connection_get_account(gc), nombre, NULL,
+ g2);
g_free(nombre);
}
@@ -257,7 +256,8 @@ void qq_process_add_buddy_auth_reply(guint8 *buf, gint buf_len, PurpleConnection
{
qq_data *qd;
gint len;
- guint8 *data, *cursor, reply;
+ gint bytes = 0;
+ guint8 *data, reply;
gchar **segments, *msg_utf8;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -265,22 +265,23 @@ void qq_process_add_buddy_auth_reply(guint8 *buf, gint buf_len, PurpleConnection
qd = (qq_data *) gc->proto_data;
len = buf_len;
data = g_newa(guint8, len);
- cursor = data;
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- read_packet_b(data, &cursor, len, &reply);
- if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
- if (NULL == (segments = split_data(data, len, "\x1f", 2)))
- return;
- msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
- purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
- g_free(msg_utf8);
- } else {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
+ }
+
+ bytes += qq_get8(&reply, data + bytes);
+
+ if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Add buddy with auth request failed\n");
+ if (NULL == (segments = split_data(data, len, "\x1f", 2))) {
+ return;
}
+ msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+ purple_notify_error(gc, NULL, _("Add buddy with auth request failed"), msg_utf8);
+ g_free(msg_utf8);
} else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
}
}
@@ -289,7 +290,8 @@ void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, PurpleConnection *
{
qq_data *qd;
gint len;
- guint8 *data, *cursor, reply;
+ gint bytes = 0;
+ guint8 *data, reply;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -297,20 +299,20 @@ void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, PurpleConnection *
len = buf_len;
data = g_newa(guint8, len);
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- cursor = data;
- read_packet_b(data, &cursor, len, &reply);
- if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
- /* there is no reason return from server */
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
- } else { /* if reply */
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
- /* TODO: We don't really need to notify the user about this, do we? */
- purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
- }
- } else {
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n");
}
+
+ bytes += qq_get8(&reply, data + bytes);
+
+ if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
+ /* there is no reason return from server */
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
+ } else { /* if reply */
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove buddy OK\n");
+ /* TODO: We don't really need to notify the user about this, do we? */
+ purple_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
+ }
}
/* process the server reply for my request to remove myself from a buddy */
@@ -318,7 +320,8 @@ void qq_process_remove_self_reply(guint8 *buf, gint buf_len, PurpleConnection *g
{
qq_data *qd;
gint len;
- guint8 *data, *cursor, reply;
+ gint bytes = 0;
+ guint8 *data, reply;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -326,20 +329,20 @@ void qq_process_remove_self_reply(guint8 *buf, gint buf_len, PurpleConnection *g
len = buf_len;
data = g_newa(guint8, len);
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- cursor = data;
- read_packet_b(data, &cursor, len, &reply);
- if (reply != QQ_REMOVE_SELF_REPLY_OK)
- /* there is no reason return from server */
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
- else { /* if reply */
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
- /* TODO: Does the user really need to be notified about this? */
- purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
- }
- } else {
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n");
}
+
+ bytes += qq_get8(&reply, data + bytes);
+
+ if (reply != QQ_REMOVE_SELF_REPLY_OK) {
+ /* there is no reason return from server */
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Remove self fails\n");
+ } else { /* if reply */
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
+ /* TODO: Does the user really need to be notified about this? */
+ purple_notify_info(gc, NULL, _("You have successfully removed yourself from your friend's buddy list"), NULL);
+ }
}
void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection *gc)
@@ -403,14 +406,14 @@ void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleCo
g->uid = for_uid;
msg = g_strdup_printf(_("User %d needs authentication"), for_uid);
purple_request_input(gc, NULL, msg,
- _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
- _("Would you be my friend?"),
- TRUE, FALSE, NULL, _("Send"),
- G_CALLBACK
- (_qq_send_packet_add_buddy_auth_with_gc_and_uid),
- _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
- purple_connection_get_account(gc), nombre, NULL,
- g);
+ _("Input request here"), /* TODO: Awkward string to fix post string freeze - standardize auth dialogues? -evands */
+ _("Would you be my friend?"),
+ TRUE, FALSE, NULL, _("Send"),
+ G_CALLBACK
+ (_qq_send_packet_add_buddy_auth_with_gc_and_uid),
+ _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
+ purple_connection_get_account(gc), nombre, NULL,
+ g);
g_free(msg);
g_free(nombre);
} else { /* add OK */
@@ -457,7 +460,7 @@ PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboo
g_return_val_if_fail(a != NULL && uid != 0, NULL);
group_name = is_known ?
- g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
+ g_strdup_printf(PURPLE_GROUP_QQ_FORMAT, purple_account_get_username(a)) : g_strdup(PURPLE_GROUP_QQ_UNKNOWN);
g = qq_get_purple_group(group_name);
@@ -478,7 +481,7 @@ 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);
@@ -512,8 +515,8 @@ void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
if (b != NULL)
purple_blist_remove_buddy(b);
purple_notify_error(gc, NULL,
- _("QQid Error"),
- _("Invalid QQid"));
+ _("QQid Error"),
+ _("Invalid QQid"));
}
}
diff --git a/libpurple/protocols/qq/buddy_status.c b/libpurple/protocols/qq/buddy_status.c
deleted file mode 100644
index db90b4027b..0000000000
--- a/libpurple/protocols/qq/buddy_status.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/**
- * @file buddy_status.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include <string.h>
-#include "internal.h"
-#include "debug.h"
-#include "prefs.h"
-
-#include "buddy_info.h"
-#include "buddy_status.h"
-#include "crypt.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "packet_parse.h"
-#include "send_core.h"
-#include "utils.h"
-
-#include "qq_proxy.h"
-
-#define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001
-#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */
-
-void qq_buddy_status_dump_unclear(qq_buddy_status *s)
-{
- GString *dump;
-
- g_return_if_fail(s != NULL);
-
- dump = g_string_new("");
- g_string_append_printf(dump, "unclear fields for [%d]:\n", s->uid);
- g_string_append_printf(dump, "004: %02x (unknown)\n", s->unknown1);
- /* g_string_append_printf(dump, "005-008: %09x (ip)\n", *(s->ip)); */
- g_string_append_printf(dump, "009-010: %04x (port)\n", s->port);
- g_string_append_printf(dump, "011: %02x (unknown)\n", s->unknown2);
- g_string_append_printf(dump, "012: %02x (status)\n", s->status);
- g_string_append_printf(dump, "013-014: %04x (client_version)\n", s->client_version);
- /* g_string_append_printf(dump, "015-030: %s (unknown key)\n", s->unknown_key); */
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Buddy status entry, %s", dump->str);
- _qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH);
- g_string_free(dump, TRUE);
-}
-
-/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses,
- * but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy,
- * using different accounts to get info. */
-
-/* parse the data into qq_buddy_status */
-gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s)
-{
- gint bytes;
-
- g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1);
-
- bytes = 0;
-
- /* 000-003: uid */
- bytes += read_packet_dw(data, cursor, len, &s->uid);
- /* 004-004: 0x01 */
- bytes += read_packet_b(data, cursor, len, &s->unknown1);
- /* this is no longer the IP, it seems QQ (as of 2006) no longer sends
- * the buddy's IP in this packet. all 0s */
- /* 005-008: ip */
- s->ip = g_new0(guint8, 4);
- bytes += read_packet_data(data, cursor, len, s->ip, 4);
- /* port info is no longer here either */
- /* 009-010: port */
- bytes += read_packet_w(data, cursor, len, &s->port);
- /* 011-011: 0x00 */
- bytes += read_packet_b(data, cursor, len, &s->unknown2);
- /* 012-012: status */
- bytes += read_packet_b(data, cursor, len, &s->status);
- /* 013-014: client_version */
- bytes += read_packet_w(data, cursor, len, &s->client_version);
- /* 015-030: unknown key */
- s->unknown_key = g_new0(guint8, QQ_KEY_LENGTH);
- bytes += read_packet_data(data, cursor, len, s->unknown_key, QQ_KEY_LENGTH);
-
- if (s->uid == 0 || bytes != 31)
- return -1;
-
- return bytes;
-}
-
-/* check if status means online or offline */
-gboolean is_online(guint8 status)
-{
- switch(status) {
- case QQ_BUDDY_ONLINE_NORMAL:
- case QQ_BUDDY_ONLINE_AWAY:
- case QQ_BUDDY_ONLINE_INVISIBLE:
- return TRUE;
- case QQ_BUDDY_ONLINE_OFFLINE:
- return FALSE;
- }
- return FALSE;
-}
-
- /* Help calculate the correct icon index to tell the server. */
-gint get_icon_offset(PurpleConnection *gc)
-{
- PurpleAccount *account;
- PurplePresence *presence;
-
- account = purple_connection_get_account(gc);
- presence = purple_account_get_presence(account);
-
- if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
- return 2;
- } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
- || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
- || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
- return 1;
- } else {
- return 0;
- }
-}
-
-/* send a packet to change my online status */
-void qq_send_packet_change_status(PurpleConnection *gc)
-{
- qq_data *qd;
- guint8 *raw_data, *cursor, away_cmd;
- guint32 misc_status;
- gboolean fake_video;
- PurpleAccount *account;
- PurplePresence *presence;
-
- account = purple_connection_get_account(gc);
- presence = purple_account_get_presence(account);
-
- qd = (qq_data *) gc->proto_data;
- if (!qd->logged_in)
- return;
-
- if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
- away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
- } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
- || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
- || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
- away_cmd = QQ_BUDDY_ONLINE_AWAY;
- } else {
- away_cmd = QQ_BUDDY_ONLINE_NORMAL;
- }
-
- raw_data = g_new0(guint8, 5);
- cursor = raw_data;
- misc_status = 0x00000000;
-
- fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
- if (fake_video)
- misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
-
- create_packet_b(raw_data, &cursor, away_cmd);
- create_packet_dw(raw_data, &cursor, misc_status);
-
- qq_send_cmd(gc, QQ_CMD_CHANGE_ONLINE_STATUS, TRUE, 0, TRUE, raw_data, 5);
-
- g_free(raw_data);
-}
-
-/* parse the reply packet for change_status */
-void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
- qq_data *qd;
- gint len;
- guint8 *data, *cursor, reply;
- PurpleBuddy *b;
- qq_buddy *q_bud;
- gchar *name;
-
- g_return_if_fail(buf != NULL && buf_len != 0);
-
- qd = (qq_data *) gc->proto_data;
- len = buf_len;
- data = g_newa(guint8, len);
-
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- cursor = data;
- read_packet_b(data, &cursor, len, &reply);
- if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
- } else {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
- name = uid_to_purple_name(qd->uid);
- b = purple_find_buddy(gc->account, name);
- g_free(name);
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- qq_update_buddy_contact(gc, q_bud);
- }
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
- }
-}
-
-/* it is a server message indicating that one of my buddies has changed its status */
-void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
- qq_data *qd;
- gint len, bytes;
- guint32 my_uid;
- guint8 *data, *cursor;
- PurpleBuddy *b;
- qq_buddy *q_bud;
- qq_buddy_status *s;
- gchar *name;
-
- g_return_if_fail(buf != NULL && buf_len != 0);
-
- qd = (qq_data *) gc->proto_data;
- len = buf_len;
- data = g_newa(guint8, len);
- cursor = data;
-
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- s = g_new0(qq_buddy_status, 1);
- bytes = 0;
- /* 000-030: qq_buddy_status */
- bytes += qq_buddy_status_read(data, &cursor, len, s);
- /* 031-034: my uid */
- /* This has a value of 0 when we've changed our status to
- * QQ_BUDDY_ONLINE_INVISIBLE */
- bytes += read_packet_dw(data, &cursor, len, &my_uid);
-
- if (bytes != 35) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
- g_free(s->ip);
- g_free(s->unknown_key);
- g_free(s);
- return;
- }
-
- name = uid_to_purple_name(s->uid);
- b = purple_find_buddy(gc->account, name);
- g_free(name);
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (q_bud) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
- if(0 != *((guint32 *)s->ip)) {
- g_memmove(q_bud->ip, s->ip, 4);
- q_bud->port = s->port;
- }
- q_bud->status = s->status;
- if(0 != s->client_version)
- q_bud->client_version = s->client_version;
- if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL)
- qq_send_packet_get_level(gc, q_bud->uid);
- qq_update_buddy_contact(gc, q_bud);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "got information of unknown buddy %d\n", s->uid);
- }
-
- g_free(s->ip);
- g_free(s->unknown_key);
- g_free(s);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
- }
-}
diff --git a/libpurple/protocols/qq/buddy_status.h b/libpurple/protocols/qq/buddy_status.h
deleted file mode 100644
index e358c9c2e4..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(guint8 *data, guint8 **cursor, gint len, qq_buddy_status *s);
-gint get_icon_offset(PurpleConnection *gc);
-
-void qq_send_packet_change_status(PurpleConnection *gc);
-
-void qq_process_change_status_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection *gc);
-#endif
diff --git a/libpurple/protocols/qq/char_conv.c b/libpurple/protocols/qq/char_conv.c
index 627aacd86d..4c0d0a451f 100644
--- a/libpurple/protocols/qq/char_conv.c
+++ b/libpurple/protocols/qq/char_conv.c
@@ -37,10 +37,7 @@
#define QQ_CHARSET_ENG "ISO-8859-1"
#define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */
-#define QQ_NULL_SMILEY "(SM)" /* return this if smiley conversion fails */
-
-/* a debug function */
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
+#define QQ_NULL_SMILEY "(Broken)" /* return this if smiley conversion fails */
const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
@@ -111,21 +108,26 @@ static gchar *_my_convert(const gchar *str, gssize len, const gchar *to_charset,
ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error);
- if (error == NULL)
+ if (error == NULL) {
return ret; /* conversion is OK */
- else { /* conversion error */
- gchar *failed = hex_dump_to_str((guint8 *) str, (len == -1) ? strlen(str) : len);
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "%s\n", error->message);
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Dump failed text\n%s", failed);
- g_free(failed);
- g_error_free(error);
- return g_strdup(QQ_NULL_MSG);
}
+
+ /* conversion error */
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_CONVERT", "%s\n", error->message);
+
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ_CONVERT",
+ (guint8 *) str, (len == -1) ? strlen(str) : len,
+ "Dump failed text");
+
+ g_error_free(error);
+ return g_strdup(QQ_NULL_MSG);
}
-/* take the input as a pascal string and return a converted c-string in UTF-8
+/*
+ * take the input as a pascal string and return a converted c-string in UTF-8
* returns the number of bytes read, return -1 if fatal error
- * the converted UTF-8 will be saved in ret */
+ * the converted UTF-8 will be saved in ret
+ */
gint convert_as_pascal_string(guint8 *data, gchar **ret, const gchar *from_charset)
{
guint8 len;
@@ -142,22 +144,23 @@ gint convert_as_pascal_string(guint8 *data, gchar **ret, const gchar *from_chars
gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
{
GString *encoded;
- guint8 font_attr, font_size, color[3], bar, *cursor;
+ guint8 font_attr, font_size, color[3], bar;
gboolean is_bold, is_italic, is_underline;
guint16 charset_code;
gchar *font_name, *color_code, *msg_utf8, *tmp, *ret;
+ gint bytes = 0;
- cursor = data;
- _qq_show_packet("QQ_MESG recv for font style", data, len);
+ /* checked qq_show_packet OK */
+ /* qq_show_packet("QQ_MESG recv for font style", data, len); */
- read_packet_b(data, &cursor, len, &font_attr);
- read_packet_data(data, &cursor, len, color, 3); /* red,green,blue */
+ bytes += qq_get8(&font_attr, data + bytes);
+ bytes += qq_getdata(color, 3, data + bytes); /* red,green,blue */
color_code = g_strdup_printf("#%02x%02x%02x", color[0], color[1], color[2]);
- read_packet_b(data, &cursor, len, &bar); /* skip, not sure of its use */
- read_packet_w(data, &cursor, len, &charset_code);
+ bytes += qq_get8(&bar, data + bytes); /* skip, not sure of its use */
+ bytes += qq_get16(&charset_code, data + bytes);
- tmp = g_strndup((gchar *) cursor, data + len - cursor);
+ tmp = g_strndup((gchar *)(data + bytes), len - bytes);
font_name = qq_to_utf8(tmp, QQ_CHARSET_DEFAULT);
g_free(tmp);
@@ -177,11 +180,11 @@ gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
/* Henry: The range QQ sends rounds from 8 to 22, where a font size
* of 10 is equal to 3 in html font tag */
g_string_append_printf(encoded,
- "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
- color_code, font_name, font_size / 3);
+ "<font color=\"%s\"><font face=\"%s\"><font size=\"%d\">",
+ color_code, font_name, font_size / 3);
purple_debug(PURPLE_DEBUG_INFO, "QQ_MESG",
- "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
- color_code, font_name, font_size / 3);
+ "recv <font color=\"%s\"><font face=\"%s\"><font size=\"%d\">\n",
+ color_code, font_name, font_size / 3);
g_string_append(encoded, msg_utf8);
if (is_bold) {
@@ -228,7 +231,7 @@ 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) {
@@ -275,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 d1f2d9af40..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,5 +388,6 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen,
}
}
}
+
return 1;
}
diff --git a/libpurple/protocols/qq/crypt.h b/libpurple/protocols/qq/crypt.h
index 9937ab90ad..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,5 +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);
-
#endif
diff --git a/libpurple/protocols/qq/file_trans.c b/libpurple/protocols/qq/file_trans.c
index f6e31e7014..afdb89eead 100644
--- a/libpurple/protocols/qq/file_trans.c
+++ b/libpurple/protocols/qq/file_trans.c
@@ -38,12 +38,11 @@
#include "im.h"
#include "packet_parse.h"
#include "proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "send_file.h"
#include "utils.h"
struct _qq_file_header {
- guint8 tag;
guint16 client_ver;
guint8 file_key;
guint32 sender_uid;
@@ -58,11 +57,11 @@ static guint32 _get_file_key(guint8 seed)
key = seed | (seed << 8) | (seed << 16) | (seed << 24);
return key;
}
-
+
static guint32 _gen_file_key(void)
{
guint8 seed;
-
+
seed = random();
return _get_file_key(seed);
}
@@ -77,26 +76,10 @@ 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;
@@ -119,23 +102,20 @@ 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 void _qq_get_file_header(guint8 *buf, guint8 **cursor, gint buflen, qq_file_header *fh)
+static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf)
{
- read_packet_b(buf, cursor, buflen, &(fh->tag));
- read_packet_w(buf, cursor, buflen, &(fh->client_ver));
- read_packet_b(buf, cursor, buflen, &fh->file_key);
- read_packet_dw(buf, cursor, buflen, &(fh->sender_uid));
- read_packet_dw(buf, cursor, buflen, &(fh->receiver_uid));
+ gint bytes = 0;
+ bytes += qq_get16(&(fh->client_ver), buf + bytes);
+ bytes += qq_get8(&fh->file_key, buf + bytes);
+ bytes += qq_get32(&(fh->sender_uid), buf + bytes);
+ bytes += qq_get32(&(fh->receiver_uid), buf + bytes);
fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key));
fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key));
+ return bytes;
}
static const gchar *qq_get_file_cmd_desc(gint type)
@@ -190,7 +170,7 @@ static int _qq_xfer_open_file(const gchar *filename, const gchar *method, Purple
fd = open(purple_xfer_get_local_filename(xfer), O_RDWR|O_CREAT, 0644);
info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FILE, fd, 0);
}
-
+
if (info->buffer == NULL) {
return - 1;
}
@@ -258,30 +238,28 @@ void qq_xfer_close_file(PurpleXfer *xfer)
static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid)
{
- gint bytes;
- guint8 *cursor, *buf;
+ guint8 *raw_data;
+ gint bytes = 0;
guint32 file_key;
qq_data *qd;
ft_info *info;
qd = (qq_data *) gc->proto_data;
- g_return_val_if_fail(qd->session_key != NULL, -1);
+
info = (ft_info *) qd->xfer->data;
- bytes = 0;
- buf = g_newa(guint8, MAX_PACKET_SIZE);
- cursor = buf;
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE);
file_key = _gen_file_key();
- bytes += create_packet_b(buf, &cursor, packet_type);
- bytes += create_packet_w(buf, &cursor, QQ_CLIENT);
- bytes += create_packet_b(buf, &cursor, file_key & 0xff);
- bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(qd->uid, file_key));
- bytes += create_packet_dw(buf, &cursor, _encrypt_qq_uid(to_uid, file_key));
- bytes += create_packet_data(buf, &cursor, data, len);
+ bytes += qq_put8(raw_data + bytes, packet_type);
+ bytes += qq_put16(raw_data + bytes, QQ_CLIENT);
+ bytes += qq_put8(raw_data + bytes, file_key & 0xff);
+ bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key));
+ bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key));
+ bytes += qq_putdata(raw_data + bytes, data, len);
if (bytes == len + 12) {
- _qq_xfer_write(buf, bytes, qd->xfer);
+ _qq_xfer_write(raw_data, bytes, qd->xfer);
} else
purple_debug(PURPLE_DEBUG_INFO, "QQ", "send_file: want %d but got %d\n", len + 12, bytes);
return bytes;
@@ -292,57 +270,56 @@ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32
{
qq_data *qd;
gint bytes, bytes_expected, encrypted_len;
- guint8 *raw_data, *cursor, *encrypted_data;
+ guint8 *raw_data, *encrypted_data;
time_t now;
ft_info *info;
-
+
qd = (qq_data *) gc->proto_data;
info = (ft_info *) qd->xfer->data;
- raw_data = g_new0 (guint8, 61);
- cursor = raw_data;
-
+ raw_data = g_newa (guint8, 61);
bytes = 0;
+
now = time(NULL);
- bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
- bytes += create_packet_w(raw_data, &cursor, packet_type);
+ bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
+ bytes += qq_put16(raw_data + bytes, packet_type);
switch (packet_type) {
case QQ_FILE_CMD_SENDER_SAY_HELLO:
case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
case QQ_FILE_CMD_NOTIFY_IP_ACK:
case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
- bytes += create_packet_w(raw_data, &cursor, info->send_seq);
+ bytes += qq_put16(raw_data + bytes, info->send_seq);
break;
default:
- bytes += create_packet_w(raw_data, &cursor, ++qd->send_seq);
+ bytes += qq_put16(raw_data + bytes, ++qd->send_seq);
}
- bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
- bytes += create_packet_b(raw_data, &cursor, 0x00);
- bytes += create_packet_b(raw_data, &cursor, qd->my_icon);
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
- bytes += create_packet_w(raw_data, &cursor, 0x0000);
- bytes += create_packet_b(raw_data, &cursor, 0x00);
+ bytes += qq_put32(raw_data + bytes, (guint32) now);
+ bytes += qq_put8(raw_data + bytes, 0x00);
+ bytes += qq_put8(raw_data + bytes, qd->my_icon);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ bytes += qq_put16(raw_data + bytes, 0x0000);
+ bytes += qq_put8(raw_data + bytes, 0x00);
/* 0x65: send a file, 0x6b: send a custom face */
- bytes += create_packet_b(raw_data, &cursor, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
+ bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */
switch (packet_type)
{
case QQ_FILE_CMD_SENDER_SAY_HELLO:
case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
- bytes += create_packet_b(raw_data, &cursor, 0x00);
- bytes += create_packet_b(raw_data, &cursor, hellobyte);
+ bytes += qq_put8(raw_data + bytes, 0x00);
+ bytes += qq_put8(raw_data + bytes, hellobyte);
bytes_expected = 48;
break;
case QQ_FILE_CMD_PING:
case QQ_FILE_CMD_PONG:
case QQ_FILE_CMD_NOTIFY_IP_ACK:
- bytes += qq_fill_conn_info(raw_data, &cursor, info);
+ bytes += qq_fill_conn_info(raw_data, info);
bytes_expected = 61;
break;
default:
@@ -350,53 +327,55 @@ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32
packet_type);
bytes_expected = 0;
}
-
- if (bytes == bytes_expected) {
- gchar *hex_dump = hex_dump_to_str(raw_data, bytes);
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "sending packet[%s]: \n%s", qq_get_file_cmd_desc(packet_type), hex_dump);
- g_free(hex_dump);
- encrypted_len = bytes + 16;
- encrypted_data = g_newa(guint8, encrypted_len);
- qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
- /*debug: try to decrypt it */
- /*
- if (QQ_DEBUG) {
- guint8 *buf;
- int buflen;
- hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
- g_free(hex_dump);
- buf = g_newa(guint8, MAX_PACKET_SIZE);
- buflen = encrypted_len;
- if (qq_decrypt(encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
- if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
- hex_dump = hex_dump_to_str(buf, buflen);
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
- g_free(hex_dump);
- } else {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
- }
- }
- */
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
- _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
- }
- else
+ if (bytes != bytes_expected) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d",
bytes_expected, bytes);
+ return;
+ }
+
+ qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+ raw_data, bytes,
+ "sending packet[%s]:", qq_get_file_cmd_desc(packet_type));
+
+ encrypted_len = bytes + 16;
+ encrypted_data = g_newa(guint8, encrypted_len);
+ qq_encrypt(raw_data, bytes, info->file_session_key, encrypted_data, &encrypted_len);
+ /*debug: try to decrypt it */
+ /*
+ if (QQ_DEBUG) {
+ guint8 *buf;
+ int buflen;
+ hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "encrypted packet: \n%s", hex_dump);
+ g_free(hex_dump);
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ buflen = encrypted_len;
+ if (qq_crypt(DECRYPT, encrypted_data, encrypted_len, info->file_session_key, buf, &buflen)) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt success\n");
+ if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "checksum ok\n");
+ hex_dump = hex_dump_to_str(buf, buflen);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted packet: \n%s", hex_dump);
+ g_free(hex_dump);
+ } else {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypt fail\n");
+ }
+ }
+ */
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type));
+ _qq_send_file(gc, encrypted_data, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid);
}
/* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */
static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type,
guint32 fragment_index, guint16 seq, guint8 *data, gint len)
{
+ guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
gint bytes;
- guint8 *raw_data, *cursor, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH];
guint32 fragment_size = 1000;
- gchar *filename;
+ const char *filename;
gint filename_len, filesize;
qq_data *qd;
ft_info *info;
@@ -404,32 +383,31 @@ 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);
- cursor = raw_data;
bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, 0x00);
- bytes += create_packet_w(raw_data, &cursor, packet_type);
+ bytes += qq_put8(raw_data + bytes, 0x00);
+ bytes += qq_put16(raw_data + bytes, packet_type);
switch (packet_type) {
case QQ_FILE_BASIC_INFO:
case QQ_FILE_DATA_INFO:
case QQ_FILE_EOF:
- bytes += create_packet_w(raw_data, &cursor, 0x0000);
- bytes += create_packet_b(raw_data, &cursor, 0x00);
+ bytes += qq_put16(raw_data + bytes, 0x0000);
+ bytes += qq_put8(raw_data + bytes, 0x00);
break;
case QQ_FILE_CMD_FILE_OP:
switch(sub_type)
{
case QQ_FILE_BASIC_INFO:
filename_len = strlen(filename);
- _fill_filename_md5(filename, filename_md5);
+ qq_get_md5(filename_md5, sizeof(filename_md5), (guint8 *)filename, filename_len);
_fill_file_md5(purple_xfer_get_local_filename(qd->xfer),
purple_xfer_get_size(qd->xfer),
file_md5);
-
+
info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
@@ -437,44 +415,44 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
"start transfering data, %d fragments with %d length each\n",
info->fragment_num, info->fragment_len);
/* Unknown */
- bytes += create_packet_w(raw_data, &cursor, 0x0000);
+ bytes += qq_put16(raw_data + bytes, 0x0000);
/* Sub-operation type */
- bytes += create_packet_b(raw_data, &cursor, sub_type);
+ bytes += qq_put8(raw_data + bytes, sub_type);
/* Length of file */
- bytes += create_packet_dw(raw_data, &cursor, filesize);
+ bytes += qq_put32(raw_data + bytes, filesize);
/* Number of fragments */
- bytes += create_packet_dw(raw_data, &cursor, info->fragment_num);
+ bytes += qq_put32(raw_data + bytes, info->fragment_num);
/* Length of a single fragment */
- bytes += create_packet_dw(raw_data, &cursor, info->fragment_len);
- bytes += create_packet_data(raw_data, &cursor, file_md5, 16);
- bytes += create_packet_data(raw_data, &cursor, filename_md5, 16);
+ bytes += qq_put32(raw_data + bytes, info->fragment_len);
+ bytes += qq_putdata(raw_data + bytes, file_md5, 16);
+ bytes += qq_putdata(raw_data + bytes, filename_md5, 16);
/* Length of filename */
- bytes += create_packet_w(raw_data, &cursor, filename_len);
+ bytes += qq_put16(raw_data + bytes, filename_len);
/* 8 unknown bytes */
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
/* filename */
- bytes += create_packet_data(raw_data, &cursor, (guint8 *) filename,
+ bytes += qq_putdata(raw_data + bytes, (guint8 *) filename,
filename_len);
break;
case QQ_FILE_DATA_INFO:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
"sending %dth fragment with length %d, offset %d\n",
fragment_index, len, (fragment_index-1)*fragment_size);
- /* bytes += create_packet_w(raw_data, &cursor, ++(qd->send_seq)); */
- bytes += create_packet_w(raw_data, &cursor, info->send_seq);
- bytes += create_packet_b(raw_data, &cursor, sub_type);
- /* bytes += create_packet_dw(raw_data, &cursor, fragment_index); */
- bytes += create_packet_dw(raw_data, &cursor, fragment_index - 1);
- bytes += create_packet_dw(raw_data, &cursor, (fragment_index - 1) * fragment_size);
- bytes += create_packet_w(raw_data, &cursor, len);
- bytes += create_packet_data(raw_data, &cursor, data, len);
+ /* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */
+ bytes += qq_put16(raw_data + bytes, info->send_seq);
+ bytes += qq_put8(raw_data + bytes, sub_type);
+ /* bytes += qq_put32(raw_data + bytes, fragment_index); */
+ bytes += qq_put32(raw_data + bytes, fragment_index - 1);
+ bytes += qq_put32(raw_data + bytes, (fragment_index - 1) * fragment_size);
+ bytes += qq_put16(raw_data + bytes, len);
+ bytes += qq_putdata(raw_data + bytes, data, len);
break;
case QQ_FILE_EOF:
purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of sending data\n");
- /* bytes += create_packet_w(raw_data, &cursor, info->fragment_num + 1); */
- bytes += create_packet_w(raw_data, &cursor, info->fragment_num);
- bytes += create_packet_b(raw_data, &cursor, sub_type);
+ /* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */
+ bytes += qq_put16(raw_data + bytes, info->fragment_num);
+ bytes += qq_put8(raw_data + bytes, sub_type);
/* purple_xfer_set_completed(qd->xfer, TRUE); */
}
break;
@@ -482,18 +460,18 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
switch (sub_type)
{
case QQ_FILE_BASIC_INFO:
- bytes += create_packet_w(raw_data, &cursor, 0x0000);
- bytes += create_packet_b(raw_data, &cursor, sub_type);
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+ bytes += qq_put16(raw_data + bytes, 0x0000);
+ bytes += qq_put8(raw_data + bytes, sub_type);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
break;
case QQ_FILE_DATA_INFO:
- bytes += create_packet_w(raw_data, &cursor, seq);
- bytes += create_packet_b(raw_data, &cursor, sub_type);
- bytes += create_packet_dw(raw_data, &cursor, fragment_index);
+ bytes += qq_put16(raw_data + bytes, seq);
+ bytes += qq_put8(raw_data + bytes, sub_type);
+ bytes += qq_put32(raw_data + bytes, fragment_index);
break;
case QQ_FILE_EOF:
- bytes += create_packet_w(raw_data, &cursor, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
- bytes += create_packet_b(raw_data, &cursor, sub_type);
+ bytes += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2);
+ bytes += qq_put8(raw_data + bytes, sub_type);
break;
}
}
@@ -520,9 +498,11 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
*/
-static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, guint8 *cursor,
- gint len, qq_file_header *fh)
+static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint len)
{
+ gint bytes ;
+ gint decryped_bytes;
+ qq_file_header fh;
guint8 *decrypted_data;
gint decrypted_len;
qq_data *qd = (qq_data *) gc->proto_data;
@@ -531,60 +511,65 @@ static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data,
guint8 hellobyte;
ft_info *info = (ft_info *) qd->xfer->data;
+ bytes = 0;
+ bytes += _qq_get_file_header(&fh, data + bytes);
+
decrypted_data = g_newa(guint8, len);
decrypted_len = len;
- if (qq_decrypt(cursor, len - (cursor - data), qd->session_md5, decrypted_data, &decrypted_len)) {
- gchar *hex_dump;
- cursor = decrypted_data + 16; /* skip md5 section */
- read_packet_w(decrypted_data, &cursor, decrypted_len, &packet_type);
- read_packet_w(decrypted_data, &cursor, decrypted_len, &seq);
- cursor += 4+1+1+19+1;
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
- hex_dump = hex_dump_to_str(decrypted_data, decrypted_len);
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "decrypted control packet received: \n%s", hex_dump);
- g_free(hex_dump);
- switch (packet_type) {
- case QQ_FILE_CMD_NOTIFY_IP_ACK:
- cursor = decrypted_data;
- qq_get_conn_info(decrypted_data, &cursor, decrypted_len, info);
-/* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
- qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
- break;
- case QQ_FILE_CMD_SENDER_SAY_HELLO:
- /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
- cursor += 47;
- read_packet_b(decrypted_data, &cursor,
- decrypted_len, &hellobyte);
-
- qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
- qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh->sender_uid, 0);
- break;
- case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
- /* I'm sender, do nothing */
- break;
- case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
- /* I'm sender, ack the hello packet and send the first data */
- cursor += 47;
- read_packet_b(decrypted_data, &cursor,
- decrypted_len, &hellobyte);
- qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh->sender_uid, hellobyte);
- _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
- break;
- case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
- /* I'm receiver, do nothing */
- break;
- case QQ_FILE_CMD_PING:
- /* I'm receiver, ack the PING */
- qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh->sender_uid, 0);
- break;
- case QQ_FILE_CMD_PONG:
- qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh->sender_uid, 0);
- break;
- default:
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
- }
- }
+ if ( !qq_decrypt(data, len, qd->session_md5, decrypted_data, &decrypted_len) ) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rcv file ctrl packet\n");
+ return;
+ }
+
+ /* only for debug info */
+ decryped_bytes = 16; /* skip md5 section */
+ decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes);
+ decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes);
+ decryped_bytes += 4+1+1+19+1; /* skip something */
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type));
+ qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+ decrypted_data, decrypted_len,
+ "decrypted control packet received:");
+
+ switch (packet_type) {
+ case QQ_FILE_CMD_NOTIFY_IP_ACK:
+ decryped_bytes = 0;
+ qq_get_conn_info(info, decrypted_data + decryped_bytes);
+ /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */
+ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
+ break;
+ case QQ_FILE_CMD_SENDER_SAY_HELLO:
+ /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */
+ decryped_bytes += 47;
+ decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
+ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
+ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0);
+ break;
+ case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK:
+ /* I'm sender, do nothing */
+ break;
+ case QQ_FILE_CMD_RECEIVER_SAY_HELLO:
+ /* I'm sender, ack the hello packet and send the first data */
+ decryped_bytes += 47;
+ decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes);
+ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte);
+ _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0);
+ break;
+ case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK:
+ /* I'm receiver, do nothing */
+ break;
+ case QQ_FILE_CMD_PING:
+ /* I'm receiver, ack the PING */
+ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0);
+ break;
+ case QQ_FILE_CMD_PONG:
+ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0);
+ break;
+ default:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "unprocess file command %d\n", packet_type);
+ }
}
static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16 len, guint32 index, guint32 offset)
@@ -609,15 +594,15 @@ static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16
purple_debug(PURPLE_DEBUG_INFO, "QQ", "duplicate %dth fragment, drop it!\n", index+1);
return;
}
-
+
info->window |= mask;
_qq_xfer_write_file(buffer, index, len, xfer);
-
+
xfer->bytes_sent += len;
xfer->bytes_remaining -= len;
purple_xfer_update_progress(xfer);
-
+
mask = 0x1 << (info->max_fragment_index % sizeof(info->window));
while (info->window & mask)
{
@@ -639,7 +624,7 @@ static void _qq_send_file_progess(PurpleConnection *gc)
guint8 *buffer;
guint i;
gint readbytes;
-
+
if (purple_xfer_get_bytes_remaining(xfer) <= 0) return;
if (info->window == 0 && info->max_fragment_index == 0)
{
@@ -655,7 +640,7 @@ static void _qq_send_file_progess(PurpleConnection *gc)
readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + i, info->fragment_len, xfer);
if (readbytes > 0)
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
- info->max_fragment_index + i + 1, 0, buffer, readbytes);
+ info->max_fragment_index + i + 1, 0, buffer, readbytes);
}
if (mask & 0x8000) mask = 0x0001;
else mask = mask << 1;
@@ -706,8 +691,8 @@ static void _qq_update_send_progess(PurpleConnection *gc, guint32 fragment_index
info->fragment_len, xfer);
if (readbytes > 0)
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
- info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
-
+ info->max_fragment_index + sizeof(info->window) + 1, 0, buffer, readbytes);
+
info->max_fragment_index ++;
if (mask & 0x8000) mask = 0x0001;
else mask = mask << 1;
@@ -718,9 +703,10 @@ static void _qq_update_send_progess(PurpleConnection *gc, guint32 fragment_index
fragment_index, info->window, info->max_fragment_index);
}
-static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint8 *cursor,
- gint len, guint32 to_uid)
+static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len)
{
+ gint bytes ;
+ qq_file_header fh;
guint16 packet_type;
guint16 packet_seq;
guint8 sub_type;
@@ -729,24 +715,27 @@ static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint
guint32 fragment_offset;
qq_data *qd = (qq_data *) gc->proto_data;
ft_info *info = (ft_info *) qd->xfer->data;
-
- cursor += 1; /* skip an unknown byte */
- read_packet_w(data, &cursor, len, &packet_type);
+
+ bytes = 0;
+ bytes += _qq_get_file_header(&fh, data + bytes);
+
+ bytes += 1; /* skip an unknown byte */
+ bytes += qq_get16(&packet_type, data + bytes);
switch(packet_type)
{
case QQ_FILE_CMD_FILE_OP:
- read_packet_w(data, &cursor, len, &packet_seq);
- read_packet_b(data, &cursor, len, &sub_type);
+ bytes += qq_get16(&packet_seq, data + bytes);
+ bytes += qq_get8(&sub_type, data + bytes);
switch (sub_type)
{
case QQ_FILE_BASIC_INFO:
- cursor += 4; /* file length, we have already known it from xfer */
- read_packet_dw(data, &cursor, len, &info->fragment_num);
- read_packet_dw(data, &cursor, len, &info->fragment_len);
+ bytes += 4; /* file length, we have already known it from xfer */
+ bytes += qq_get32(&info->fragment_num, data + bytes);
+ bytes += qq_get32(&info->fragment_len, data + bytes);
- /* FIXME: We must check the md5 here, if md5 doesn't match
- * we will ignore the packet or send sth as error number
- */
+ /* FIXME: We must check the md5 here,
+ * if md5 doesn't match we will ignore
+ * the packet or send sth as error number */
info->max_fragment_index = 0;
info->window = 0;
@@ -757,27 +746,27 @@ static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint
0, 0, NULL, 0);
break;
case QQ_FILE_DATA_INFO:
- read_packet_dw(data, &cursor, len, &fragment_index);
- read_packet_dw(data, &cursor, len, &fragment_offset);
- read_packet_w(data, &cursor, len, &fragment_len);
+ bytes += qq_get32(&fragment_index, data + bytes);
+ bytes += qq_get32(&fragment_offset, data + bytes);
+ bytes += qq_get16(&fragment_len, data + bytes);
purple_debug(PURPLE_DEBUG_INFO, "QQ",
"received %dth fragment with length %d, offset %d\n",
fragment_index, fragment_len, fragment_offset);
-
+
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
fragment_index, packet_seq, NULL, 0);
- _qq_recv_file_progess(gc, cursor, fragment_len, fragment_index, fragment_offset);
+ _qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset);
break;
case QQ_FILE_EOF:
purple_debug(PURPLE_DEBUG_INFO, "QQ", "end of receiving\n");
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
- 0, 0, NULL, 0);
+ 0, 0, NULL, 0);
break;
}
break;
case QQ_FILE_CMD_FILE_OP_ACK:
- read_packet_w(data, &cursor, len, &packet_seq);
- read_packet_b(data, &cursor, len, &sub_type);
+ bytes += qq_get16(&packet_seq, data + bytes);
+ bytes += qq_get8(&sub_type, data + bytes);
switch (sub_type)
{
case QQ_FILE_BASIC_INFO:
@@ -787,16 +776,16 @@ static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint
_qq_send_file_progess(gc);
break;
case QQ_FILE_DATA_INFO:
- read_packet_dw(data, &cursor, len, &fragment_index);
+ bytes += qq_get32(&fragment_index, data + bytes);
_qq_update_send_progess(gc, fragment_index);
if (purple_xfer_is_completed(qd->xfer))
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0);
- /* else
+ /* else
_qq_send_file_progess(gc); */
break;
case QQ_FILE_EOF:
/* FIXME: OK, we can end the connection successfully */
-
+
_qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0);
purple_xfer_set_completed(qd->xfer, TRUE);
break;
@@ -820,21 +809,21 @@ static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, guint
void qq_process_recv_file(PurpleConnection *gc, guint8 *data, gint len)
{
- guint8 *cursor;
- qq_file_header fh;
+ gint bytes;
+ guint8 tag;
qq_data *qd;
qd = (qq_data *) gc->proto_data;
- cursor = data;
- _qq_get_file_header(data, &cursor, len, &fh);
+ bytes = 0;
+ bytes += qq_get8(&tag, data + bytes);
- switch (fh.tag) {
+ switch (tag) {
case QQ_FILE_CONTROL_PACKET_TAG:
- _qq_process_recv_file_ctl_packet(gc, data, cursor, len, &fh);
+ _qq_process_recv_file_ctl_packet(gc, data + bytes, len - bytes);
break;
case QQ_FILE_DATA_PACKET_TAG:
- _qq_process_recv_file_data(gc, data, cursor, len, fh.sender_uid);
+ _qq_process_recv_file_data(gc, data + bytes, len - bytes);
break;
default:
purple_debug(PURPLE_DEBUG_INFO, "QQ", "unknown packet tag");
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 5053d20465..aada103d70 100644
--- a/libpurple/protocols/qq/group_im.c
+++ b/libpurple/protocols/qq/group_im.c
@@ -58,28 +58,28 @@ typedef struct _qq_recv_group_im {
void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg)
{
gint data_len, bytes;
- guint8 *raw_data, *cursor, *send_im_tail;
+ guint8 *raw_data, *send_im_tail;
guint16 msg_len;
gchar *msg_filtered;
g_return_if_fail(group != NULL && msg != NULL);
msg_filtered = purple_markup_strip_html(msg);
- purple_debug_info("QQ_MESG", "filterd qq qun mesg: %s\n", msg_filtered);
+ purple_debug_info("QQ_MESG", "Send qun mesg filterd: %s\n", msg_filtered);
msg_len = strlen(msg_filtered);
+
data_len = 7 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEND_MSG);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
- bytes += create_packet_w(raw_data, &cursor, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
- bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEND_MSG);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
+ bytes += qq_put16(raw_data + bytes, msg_len + QQ_SEND_IM_AFTER_MSG_LEN);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
send_im_tail = qq_get_send_im_tail(NULL, NULL, NULL,
- FALSE, FALSE, FALSE,
- QQ_SEND_IM_AFTER_MSG_LEN);
- bytes += create_packet_data(raw_data, &cursor, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
+ FALSE, FALSE, FALSE,
+ QQ_SEND_IM_AFTER_MSG_LEN);
+ bytes += qq_putdata(raw_data + bytes, send_im_tail, QQ_SEND_IM_AFTER_MSG_LEN);
g_free(send_im_tail);
g_free(msg_filtered);
@@ -87,11 +87,11 @@ void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar
qq_send_group_cmd(gc, group, raw_data, data_len);
else
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
+ "Fail creating group_im packet, expect %d bytes, build %d bytes\n", data_len, bytes);
}
/* this is the ACK */
-void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc)
{
/* return should be the internal group id
* but we have nothing to do with it */
@@ -99,29 +99,26 @@ void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConn
}
/* receive an application to join the group */
-void qq_process_recv_group_im_apply_join
- (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
{
guint32 external_group_id, user_uid;
guint8 group_type;
gchar *reason_utf8, *msg, *reason;
group_member_opt *g;
gchar *nombre;
+ gint bytes = 0;
g_return_if_fail(internal_group_id > 0 && data != NULL && len > 0);
- if (*cursor >= (data + len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg apply_join is empty\n");
- return;
- }
+ /* FIXME: check length here */
- read_packet_dw(data, cursor, len, &external_group_id);
- read_packet_b(data, cursor, len, &group_type);
- read_packet_dw(data, cursor, len, &user_uid);
+ bytes += qq_get32(&external_group_id, data + bytes);
+ bytes += qq_get8(&group_type, data + bytes);
+ bytes += qq_get32(&user_uid, data + bytes);
g_return_if_fail(external_group_id > 0 && user_uid > 0);
- convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+ bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
msg = g_strdup_printf(_("User %d requested to join group %d"), user_uid, external_group_id);
reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
@@ -134,17 +131,17 @@ void qq_process_recv_group_im_apply_join
nombre = uid_to_purple_name(user_uid);
purple_request_action(gc, _("QQ Qun Operation"),
- msg, reason,
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), nombre, NULL,
- g, 3,
- _("Approve"),
- G_CALLBACK
- (qq_group_approve_application_with_struct),
- _("Reject"),
- G_CALLBACK
- (qq_group_reject_application_with_struct),
- _("Search"), G_CALLBACK(qq_group_search_application_with_struct));
+ msg, reason,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(gc), nombre, NULL,
+ g, 3,
+ _("Approve"),
+ G_CALLBACK
+ (qq_group_approve_application_with_struct),
+ _("Reject"),
+ G_CALLBACK
+ (qq_group_reject_application_with_struct),
+ _("Search"), G_CALLBACK(qq_group_search_application_with_struct));
g_free(nombre);
g_free(reason);
@@ -153,31 +150,28 @@ void qq_process_recv_group_im_apply_join
}
/* the request to join a group is rejected */
-void qq_process_recv_group_im_been_rejected
- (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
{
guint32 external_group_id, admin_uid;
guint8 group_type;
gchar *reason_utf8, *msg, *reason;
qq_group *group;
+ gint bytes = 0;
g_return_if_fail(data != NULL && len > 0);
- if (*cursor >= (data + len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_rejected is empty\n");
- return;
- }
+ /* FIXME: check length here */
- read_packet_dw(data, cursor, len, &external_group_id);
- read_packet_b(data, cursor, len, &group_type);
- read_packet_dw(data, cursor, len, &admin_uid);
+ bytes += qq_get32(&external_group_id, data + bytes);
+ bytes += qq_get8(&group_type, data + bytes);
+ bytes += qq_get32(&admin_uid, data + bytes);
g_return_if_fail(external_group_id > 0 && admin_uid > 0);
- convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+ bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
msg = g_strdup_printf
- (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid);
+ (_("Your request to join group %d has been rejected by admin %d"), external_group_id, admin_uid);
reason = g_strdup_printf(_("Reason: %s"), reason_utf8);
purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
@@ -194,31 +188,28 @@ void qq_process_recv_group_im_been_rejected
}
/* the request to join a group is approved */
-void qq_process_recv_group_im_been_approved
- (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
{
guint32 external_group_id, admin_uid;
guint8 group_type;
gchar *reason_utf8, *msg;
qq_group *group;
+ gint bytes = 0;
g_return_if_fail(data != NULL && len > 0);
- if (*cursor >= (data + len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_approved is empty\n");
- return;
- }
+ /* FIXME: check length here */
- read_packet_dw(data, cursor, len, &external_group_id);
- read_packet_b(data, cursor, len, &group_type);
- read_packet_dw(data, cursor, len, &admin_uid);
+ bytes += qq_get32(&external_group_id, data + bytes);
+ bytes += qq_get8(&group_type, data + bytes);
+ bytes += qq_get32(&admin_uid, data + bytes);
g_return_if_fail(external_group_id > 0 && admin_uid > 0);
/* it is also a "无" here, so do not display */
- convert_as_pascal_string(*cursor, &reason_utf8, QQ_CHARSET_DEFAULT);
+ bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
msg = g_strdup_printf
- (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid);
+ (_("Your request to join group %d has been approved by admin %d"), external_group_id, admin_uid);
purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
@@ -233,24 +224,21 @@ void qq_process_recv_group_im_been_approved
}
/* process the packet when removed from a group */
-void qq_process_recv_group_im_been_removed
- (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
{
guint32 external_group_id, uid;
guint8 group_type;
gchar *msg;
qq_group *group;
+ gint bytes = 0;
g_return_if_fail(data != NULL && len > 0);
- if (*cursor >= (data + len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_removed is empty\n");
- return;
- }
+ /* FIXME: check length here */
- read_packet_dw(data, cursor, len, &external_group_id);
- read_packet_b(data, cursor, len, &group_type);
- read_packet_dw(data, cursor, len, &uid);
+ bytes += qq_get32(&external_group_id, data + bytes);
+ bytes += qq_get8(&group_type, data + bytes);
+ bytes += qq_get32(&uid, data + bytes);
g_return_if_fail(external_group_id > 0 && uid > 0);
@@ -267,24 +255,21 @@ void qq_process_recv_group_im_been_removed
}
/* process the packet when added to a group */
-void qq_process_recv_group_im_been_added
- (guint8 *data, guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc)
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc)
{
guint32 external_group_id, uid;
guint8 group_type;
qq_group *group;
gchar *msg;
+ gint bytes = 0;
g_return_if_fail(data != NULL && len > 0);
- if (*cursor >= (data + len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group msg been_added is empty\n");
- return;
- }
+ /* FIXME: check length here */
- read_packet_dw(data, cursor, len, &external_group_id);
- read_packet_b(data, cursor, len, &group_type);
- read_packet_dw(data, cursor, len, &uid);
+ bytes += qq_get32(&external_group_id, data + bytes);
+ bytes += qq_get8(&group_type, data + bytes);
+ bytes += qq_get32(&uid, data + bytes);
g_return_if_fail(external_group_id > 0 && uid > 0);
@@ -307,10 +292,9 @@ void qq_process_recv_group_im_been_added
}
/* recv an IM from a group chat */
-void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
- guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
+void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type)
{
- gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name, *hex_dump;
+ gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name;
guint16 unknown;
guint32 unknown4;
PurpleConversation *conv;
@@ -319,32 +303,30 @@ void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
qq_group *group;
qq_recv_group_im *im_group;
gint skip_len;
+ gint bytes = 0;
g_return_if_fail(data != NULL && data_len > 0);
- qd = (qq_data *) gc->proto_data;
- hex_dump = hex_dump_to_str(*cursor, data_len - (*cursor - data));
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "group im hex dump\n%s\n", hex_dump);
+ /* FIXME: check length here */
- if (*cursor >= (data + data_len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received group im_group is empty\n");
- return;
- }
+ qd = (qq_data *) gc->proto_data;
+
+ /* qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump"); */
im_group = g_newa(qq_recv_group_im, 1);
- read_packet_dw(data, cursor, data_len, &(im_group->external_group_id));
- read_packet_b(data, cursor, data_len, &(im_group->group_type));
+ bytes += qq_get32(&(im_group->external_group_id), data + bytes);
+ bytes += qq_get8(&(im_group->group_type), data + bytes);
if(QQ_RECV_IM_TEMP_QUN_IM == im_type) {
- read_packet_dw(data, cursor, data_len, &(internal_group_id));
+ bytes += qq_get32(&(internal_group_id), data + bytes);
}
- read_packet_dw(data, cursor, data_len, &(im_group->member_uid));
- read_packet_w(data, cursor, data_len, &unknown); /* 0x0001? */
- read_packet_w(data, cursor, data_len, &(im_group->msg_seq));
- read_packet_time(data, cursor, data_len, &im_group->send_time);
- read_packet_dw(data, cursor, data_len, &unknown4); /* versionID */
+ bytes += qq_get32(&(im_group->member_uid), bytes + data);
+ bytes += qq_get16(&unknown, data + bytes); /* 0x0001? */
+ bytes += qq_get16(&(im_group->msg_seq), data + bytes);
+ bytes += qq_getime(&im_group->send_time, data + bytes);
+ bytes += qq_get32(&unknown4, data + bytes); /* versionID */
/*
* length includes font_attr
* this msg_len includes msg and font_attr
@@ -355,7 +337,7 @@ void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
* 3. font_attr
*/
- read_packet_w(data, cursor, data_len, &(im_group->msg_len));
+ bytes += qq_get16(&(im_group->msg_len), data + bytes);
g_return_if_fail(im_group->msg_len > 0);
/*
@@ -371,14 +353,14 @@ void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
skip_len = 10;
else
skip_len = 0;
- *cursor += skip_len;
+ bytes += skip_len;
- im_group->msg = g_strdup((gchar *) *cursor);
- *cursor += strlen(im_group->msg) + 1;
+ im_group->msg = g_strdup((gchar *) data + bytes);
+ bytes += strlen(im_group->msg) + 1;
/* there might not be any font_attr, check it */
im_group->font_attr_len = im_group->msg_len - strlen(im_group->msg) - 1 - skip_len;
if (im_group->font_attr_len > 0)
- im_group->font_attr = g_memdup(*cursor, im_group->font_attr_len);
+ im_group->font_attr = g_memdup(data + bytes, im_group->font_attr_len);
else
im_group->font_attr = NULL;
@@ -386,7 +368,7 @@ void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
msg_with_purple_smiley = qq_smiley_to_purple(im_group->msg);
if (im_group->font_attr_len > 0)
msg_utf8_encoded = qq_encode_to_purple(im_group->font_attr,
- im_group->font_attr_len, msg_with_purple_smiley);
+ im_group->font_attr_len, msg_with_purple_smiley);
else
msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
@@ -395,6 +377,9 @@ void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc));
if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/prompt_group_msg_on_recv")) {
+ /* New conv should open, get group info*/
+ qq_send_cmd_group_get_group_info(gc, group);
+
serv_got_joined_chat(gc, qd->channel++, group->group_name_utf8);
conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->group_name_utf8, purple_connection_get_account(gc));
}
@@ -406,11 +391,10 @@ void qq_process_recv_group_im(guint8 *data, guint8 **cursor, gint data_len,
else
im_src_name = g_strdup(member->nickname);
serv_got_chat_in(gc,
- purple_conv_chat_get_id(PURPLE_CONV_CHAT
- (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
+ purple_conv_chat_get_id(PURPLE_CONV_CHAT
+ (conv)), im_src_name, 0, msg_utf8_encoded, im_group->send_time);
g_free(im_src_name);
}
- g_free(hex_dump);
g_free(msg_with_purple_smiley);
g_free(msg_utf8_encoded);
g_free(im_group->msg);
diff --git a/libpurple/protocols/qq/group_im.h b/libpurple/protocols/qq/group_im.h
index 4b5a373965..7b22ea4a5a 100644
--- a/libpurple/protocols/qq/group_im.h
+++ b/libpurple/protocols/qq/group_im.h
@@ -30,17 +30,31 @@
#include "group.h"
void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg);
-void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_recv_group_im(guint8 *data,
- guint8 **cursor, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
-void qq_process_recv_group_im_apply_join(guint8 *data,
- guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_rejected(guint8 *data,
- guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_approved(guint8 *data,
- guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_removed(guint8 *data,
- guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
-void qq_process_recv_group_im_been_added(guint8 *data,
- guint8 **cursor, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_group_cmd_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc); */
+void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im(guint8 *data, guint8 **cursor,
+ * gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type); */
+void qq_process_recv_group_im(guint8 *data, gint data_len, guint32 internal_group_id, PurpleConnection *gc, guint16 im_type);
+
+/* void qq_process_recv_group_im_apply_join(guint8 *data, guint8 **cursor, gint len,
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_apply_join(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_rejected(guint8 *data, guint8 **cursor, gint len,
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_rejected(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_approved(guint8 *data, guint8 **cursor, gint len,
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_approved(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_removed(guint8 *data, guint8 **cursor, gint len,
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_removed(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
+
+/* void qq_process_recv_group_im_been_added(guint8 *data, guint8 **cursor, gint len,
+ * guint32 internal_group_id, PurpleConnection *gc); */
+void qq_process_recv_group_im_been_added(guint8 *data, gint len, guint32 internal_group_id, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_info.c b/libpurple/protocols/qq/group_info.c
index 190449c23f..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
@@ -43,7 +42,7 @@ static gboolean _is_group_member_need_update_info(qq_buddy *member)
{
g_return_val_if_fail(member != NULL, FALSE);
return (member->nickname == NULL) ||
- (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
+ (time(NULL) - member->last_refresh) > QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL;
}
/* this is done when we receive the reply to get_online_members sub_cmd
@@ -65,100 +64,104 @@ static void _qq_group_set_members_all_offline(qq_group *group)
/* send packet to get detailed information of one group */
void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group)
{
- guint8 *raw_data, *cursor;
- gint bytes, data_len;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
g_return_if_fail(group != NULL);
- data_len = 5;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
-
- bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_GROUP_INFO);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_GROUP_INFO);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
- if (bytes != data_len)
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_GROUP_INFO));
- else
- qq_send_group_cmd(gc, group, raw_data, data_len);
+ qq_send_group_cmd(gc, group, raw_data, bytes);
}
/* send packet to get online group member, called by keep_alive */
+void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc)
+{
+ qq_data *qd;
+ qq_group *group;
+ GList *list;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ list = qd->groups;
+ while (list != NULL) {
+ group = (qq_group *) list->data;
+ if (group->my_status == QQ_GROUP_MEMBER_STATUS_IS_MEMBER ||
+ group->my_status == QQ_GROUP_MEMBER_STATUS_IS_ADMIN)
+ /* no need to get info time and time again, online members enough */
+ qq_send_cmd_group_get_online_members(gc, group);
+
+ list = list->next;
+ }
+}
+
void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group)
{
- guint8 *raw_data, *cursor;
- gint bytes, data_len;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
g_return_if_fail(group != NULL);
/* only get online members when conversation window is on */
if (NULL == purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,group->group_name_utf8, purple_connection_get_account(gc))) {
purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Conv windows for \"%s\" is not on, do not get online members\n", group->group_name_utf8);
+ "Conversation \"%s\" is not open, ignore to get online members\n", group->group_name_utf8);
return;
}
- data_len = 5;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
-
- bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_ONLINE_MEMBER);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
- if (bytes != data_len)
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_ONLINE_MEMBER));
- else
- qq_send_group_cmd(gc, group, raw_data, data_len);
+ qq_send_group_cmd(gc, group, raw_data, bytes);
}
/* send packet to get info for each group member */
void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group)
{
- guint8 *raw_data, *cursor;
- gint bytes, data_len, i;
+ guint8 *raw_data;
+ gint bytes, num, data_len;
GList *list;
qq_buddy *member;
g_return_if_fail(group != NULL);
- for (i = 0, list = group->members; list != NULL; list = list->next) {
+ for (num = 0, list = group->members; list != NULL; list = list->next) {
member = (qq_buddy *) list->data;
if (_is_group_member_need_update_info(member))
- i++;
+ num++;
}
- if (i <= 0) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info now.\n");
+ if (num <= 0) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member info needs to be updated now.\n");
return;
}
- data_len = 5 + 4 * i;
+ data_len = 5 + 4 * num;
raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_GET_MEMBER_INFO);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_GET_MEMBER_INFO);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
list = group->members;
while (list != NULL) {
member = (qq_buddy *) list->data;
if (_is_group_member_need_update_info(member))
- bytes += create_packet_dw(raw_data, &cursor, member->uid);
+ bytes += qq_put32(raw_data + bytes, member->uid);
list = list->next;
}
- if (bytes != data_len)
+ if (bytes != data_len) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
- else
- qq_send_group_cmd(gc, group, raw_data, data_len);
+ "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_GET_MEMBER_INFO));
+ return;
+ }
+
+ qq_send_group_cmd(gc, group, raw_data, bytes);
}
-void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc)
{
qq_group *group;
qq_buddy *member;
@@ -168,18 +171,21 @@ void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len
guint16 unknown, max_members;
guint32 member_uid, internal_group_id, external_group_id;
GSList *pending_id;
- gint pascal_len, i;
guint32 unknown4;
guint8 unknown1;
+ gint bytes, num;
+ gchar *notice;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
- read_packet_dw(data, cursor, len, &(internal_group_id));
- g_return_if_fail(internal_group_id > 0);
- read_packet_dw(data, cursor, len, &(external_group_id));
+ bytes = 0;
+ bytes += qq_get32(&(internal_group_id), data + bytes);
g_return_if_fail(internal_group_id > 0);
+ bytes += qq_get32(&(external_group_id), data + bytes);
+ g_return_if_fail(external_group_id > 0);
+
pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id);
if (pending_id != NULL) {
qq_set_pending_id(&qd->adding_groups_from_server, internal_group_id, FALSE);
@@ -189,45 +195,54 @@ void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len
group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
g_return_if_fail(group != NULL);
- read_packet_b(data, cursor, len, &(group->group_type));
- read_packet_dw(data, cursor, len, &unknown4); /* unknown 4 bytes */
- read_packet_dw(data, cursor, len, &(group->creator_uid));
- read_packet_b(data, cursor, len, &(group->auth_type));
- read_packet_dw(data, cursor, len, &unknown4); /* oldCategory */
- read_packet_w(data, cursor, len, &unknown);
- read_packet_dw(data, cursor, len, &(group->group_category));
- read_packet_w(data, cursor, len, &max_members);
- read_packet_b(data, cursor, len, &unknown1);
- read_packet_dw(data, cursor, len, &(unknown4)); /* versionID */
-
- pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
- *cursor += pascal_len;
- read_packet_w(data, cursor, len, &(unknown)); /* 0x0000 */
- pascal_len = convert_as_pascal_string(*cursor, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
- *cursor += pascal_len;
- pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
- *cursor += pascal_len;
-
- i = 0;
+ bytes += qq_get8(&(group->group_type), data + bytes);
+ bytes += qq_get32(&unknown4, data + bytes); /* unknown 4 bytes */
+ bytes += qq_get32(&(group->creator_uid), data + bytes);
+ bytes += qq_get8(&(group->auth_type), data + bytes);
+ bytes += qq_get32(&unknown4, data + bytes); /* oldCategory */
+ bytes += qq_get16(&unknown, data + bytes);
+ bytes += qq_get32(&(group->group_category), data + bytes);
+ bytes += qq_get16(&max_members, data + bytes);
+ bytes += qq_get8(&unknown1, data + bytes);
+ /* the following, while Eva:
+ * 4(unk), 4(verID), 1(nameLen), nameLen(qunNameContent), 1(0x00),
+ * 2(qunNoticeLen), qunNoticeLen(qunNoticeContent, 1(qunDescLen),
+ * qunDestLen(qunDestcontent)) */
+ bytes += qq_get8(&unknown1, data + bytes);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
+ group->group_type, group->creator_uid, group->group_category, max_members);
+
+ /* strlen + <str content> */
+ bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\"\n", group->group_name_utf8);
+ bytes += qq_get16(&unknown, data + bytes); /* 0x0000 */
+ bytes += convert_as_pascal_string(data + bytes, &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 */
- while (*cursor < data + len) {
- read_packet_dw(data, cursor, len, &member_uid);
- i++;
- read_packet_b(data, cursor, len, &organization);
- read_packet_b(data, cursor, len, &role);
+ while (bytes < len) {
+ bytes += qq_get32(&member_uid, data + bytes);
+ num++;
+ bytes += qq_get8(&organization, data + bytes);
+ bytes += qq_get8(&role, data + bytes);
+ /*
if(organization != 0 || role != 0) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "group member %d: organization=%d, role=%d\n", member_uid, organization, role);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_GRP", "%d, organization=%d, role=%d\n", member_uid, organization, role);
}
+ */
member = qq_group_find_or_add_member(gc, group, member_uid);
if (member != NULL)
member->role = role;
}
- if(*cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
- }
+ if(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
+ }
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, i);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "group \"%s\" has %d members\n", group->group_name_utf8, num);
if (group->creator_uid == qd->uid)
group->my_status = QQ_GROUP_MEMBER_STATUS_IS_ADMIN;
@@ -237,33 +252,37 @@ void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len
purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
group->group_name_utf8, purple_connection_get_account(gc));
if(NULL == purple_conv) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Conv windows for \"%s\" is not on, do not set topic\n", group->group_name_utf8);
- }
- else {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "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, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc)
{
guint32 internal_group_id, member_uid;
guint8 unknown;
- gint bytes, i;
+ gint bytes, num;
qq_group *group;
qq_buddy *member;
g_return_if_fail(data != NULL && len > 0);
- if (data + len - *cursor < 4) {
+ if (len <= 3) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Invalid group online member reply, discard it!\n");
return;
}
bytes = 0;
- i = 0;
- bytes += read_packet_dw(data, cursor, len, &internal_group_id);
- bytes += read_packet_b(data, cursor, len, &unknown); /* 0x3c ?? */
+ bytes += qq_get32(&internal_group_id, data + bytes);
+ bytes += qq_get8(&unknown, data + bytes); /* 0x3c ?? */
g_return_if_fail(internal_group_id > 0);
group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
@@ -275,61 +294,77 @@ void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint
/* set all offline first, then update those online */
_qq_group_set_members_all_offline(group);
- while (*cursor < data + len) {
- bytes += read_packet_dw(data, cursor, len, &member_uid);
- i++;
+ num = 0;
+ while (bytes < len) {
+ bytes += qq_get32(&member_uid, data + bytes);
+ num++;
member = qq_group_find_or_add_member(gc, group, member_uid);
if (member != NULL)
member->status = QQ_BUDDY_ONLINE_NORMAL;
}
- if(*cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
- }
+ if(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
+ }
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, i);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" has %d online members\n", group->group_name_utf8, num);
}
/* process the reply to get_members_info packet */
-void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc)
{
+ gint bytes;
+ gint num;
guint32 internal_group_id, member_uid;
guint16 unknown;
- gint pascal_len, i;
qq_group *group;
qq_buddy *member;
+ gchar *nick;
g_return_if_fail(data != NULL && len > 0);
- read_packet_dw(data, cursor, len, &internal_group_id);
+ bytes = 0;
+ bytes += qq_get32(&internal_group_id, data + bytes);
g_return_if_fail(internal_group_id > 0);
group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
g_return_if_fail(group != NULL);
- i = 0;
+ num = 0;
/* now starts the member info, as get buddy list reply */
- while (*cursor < data + len) {
- read_packet_dw(data, cursor, len, &member_uid);
+ while (bytes < len) {
+ bytes += qq_get32(&member_uid, data + bytes);
g_return_if_fail(member_uid > 0);
member = qq_group_find_member_by_uid(group, member_uid);
g_return_if_fail(member != NULL);
- i++;
- read_packet_w(data, cursor, len, &(member->face));
- read_packet_b(data, cursor, len, &(member->age));
- read_packet_b(data, cursor, len, &(member->gender));
- pascal_len = convert_as_pascal_string(*cursor, &(member->nickname), QQ_CHARSET_DEFAULT);
- *cursor += pascal_len;
- read_packet_w(data, cursor, len, &unknown);
- read_packet_b(data, cursor, len, &(member->flag1));
- read_packet_b(data, cursor, len, &(member->comm_flag));
+ num++;
+ bytes += qq_get16(&(member->face), data + bytes);
+ bytes += qq_get8(&(member->age), data + bytes);
+ bytes += qq_get8(&(member->gender), data + bytes);
+ bytes += convert_as_pascal_string(data + bytes, &nick, QQ_CHARSET_DEFAULT);
+ bytes += qq_get16(&unknown, data + bytes);
+ bytes += qq_get8(&(member->ext_flag), data + bytes);
+ bytes += qq_get8(&(member->comm_flag), data + bytes);
+
+ /* filter \r\n in nick */
+ qq_filter_str(nick);
+ member->nickname = g_strdup(nick);
+ g_free(nick);
+
+ /*
+ if (QQ_DEBUG) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "member [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
+ member_uid, member->ext_flag, member->comm_flag, member->nickname);
+ }
+ */
member->last_refresh = time(NULL);
}
- if(*cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
- }
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, i);
+ if(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
+ }
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Group \"%s\" obtained %d member info\n", group->group_name_utf8, num);
}
diff --git a/libpurple/protocols/qq/group_info.h b/libpurple/protocols/qq/group_info.h
index be3208440b..d6497d5350 100644
--- a/libpurple/protocols/qq/group_info.h
+++ b/libpurple/protocols/qq/group_info.h
@@ -31,9 +31,12 @@
void qq_send_cmd_group_get_group_info(PurpleConnection *gc, qq_group *group);
void qq_send_cmd_group_get_online_members(PurpleConnection *gc, qq_group *group);
+void qq_send_cmd_group_all_get_online_members(PurpleConnection *gc);
+
void qq_send_cmd_group_get_members_info(PurpleConnection *gc, qq_group *group);
-void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_online_members(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_get_members_info(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+
+void qq_process_group_cmd_get_group_info(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_online_members(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_get_members_info(guint8 *data, gint len, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_join.c b/libpurple/protocols/qq/group_join.c
index 8debc1645f..bfa72b5d71 100644
--- a/libpurple/protocols/qq/group_join.c
+++ b/libpurple/protocols/qq/group_join.c
@@ -64,8 +64,8 @@ static void _qq_group_exit_with_gc_and_id(gc_and_uid *g)
/* send packet to join a group without auth */
void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group)
{
- guint8 *raw_data, *cursor;
- gint bytes, data_len;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
g_return_if_fail(group != NULL);
@@ -86,19 +86,11 @@ void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group)
break;
}
- data_len = 5;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
-
bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
- if (bytes != data_len)
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP));
- else
- qq_send_group_cmd(gc, group, raw_data, data_len);
+ qq_send_group_cmd(gc, group, raw_data, bytes);
}
static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8)
@@ -145,7 +137,7 @@ static void _qq_group_join_auth(PurpleConnection *gc, qq_group *group)
void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8)
{
- guint8 *raw_data, *cursor;
+ guint8 *raw_data;
gchar *reason_qq;
gint bytes, data_len;
@@ -164,50 +156,42 @@ void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, g
data_len = 10 + strlen(reason_qq) + 1;
raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
- bytes += create_packet_b(raw_data, &cursor, opt);
- bytes += create_packet_dw(raw_data, &cursor, uid);
- bytes += create_packet_b(raw_data, &cursor, strlen(reason_qq));
- bytes += create_packet_data(raw_data, &cursor, (guint8 *) reason_qq, strlen(reason_qq));
-
- if (bytes != data_len)
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_JOIN_GROUP_AUTH);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
+ bytes += qq_put8(raw_data + bytes, opt);
+ bytes += qq_put32(raw_data + bytes, uid);
+ bytes += qq_put8(raw_data + bytes, strlen(reason_qq));
+ bytes += qq_putdata(raw_data + bytes, (guint8 *) reason_qq, strlen(reason_qq));
+
+ if (bytes != data_len) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
"Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_JOIN_GROUP_AUTH));
- else
- qq_send_group_cmd(gc, group, raw_data, data_len);
+ return;
+ }
+
+ qq_send_group_cmd(gc, group, raw_data, data_len);
}
/* send a packet to exit a group */
void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group)
{
- guint8 *raw_data, *cursor;
- gint bytes, data_len;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
g_return_if_fail(group != NULL);
- data_len = 5;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
-
- bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_EXIT_GROUP);
- bytes += create_packet_dw(raw_data, &cursor, group->internal_group_id);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_EXIT_GROUP);
+ bytes += qq_put32(raw_data + bytes, group->internal_group_id);
- if (bytes != data_len)
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_EXIT_GROUP));
- else
- qq_send_group_cmd(gc, group, raw_data, data_len);
+ qq_send_group_cmd(gc, group, raw_data, bytes);
}
/* If comes here, cmd is OK already */
-void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
{
- gint bytes, expected_bytes;
+ gint bytes;
guint32 internal_group_id;
PurpleChat *chat;
qq_group *group;
@@ -216,96 +200,94 @@ void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, Pu
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
+ if (len < 4) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Invalid exit group reply, expect %d bytes, read %d bytes\n", 4, len);
+ return;
+ }
+
bytes = 0;
- expected_bytes = 4;
- bytes += read_packet_dw(data, cursor, len, &internal_group_id);
-
- if (bytes == expected_bytes) {
- group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
- if (group != NULL) {
- chat =
- purple_blist_find_chat
+ bytes += qq_get32(&internal_group_id, data + bytes);
+
+ group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+ if (group != NULL) {
+ chat = purple_blist_find_chat
(purple_connection_get_account(gc), g_strdup_printf("%d", group->external_group_id));
- if (chat != NULL)
- purple_blist_remove_chat(chat);
- qq_group_delete_internal_record(qd, internal_group_id);
- }
- purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Invalid exit group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
+ if (chat != NULL)
+ purple_blist_remove_chat(chat);
+ qq_group_delete_internal_record(qd, internal_group_id);
}
+ purple_notify_info(gc, _("QQ Qun Operation"), _("You have successfully left the group"), NULL);
}
/* Process the reply to group_auth subcmd */
-void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc)
{
- gint bytes, expected_bytes;
+ gint bytes;
guint32 internal_group_id;
qq_data *qd;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
+ if (len < 4) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Invalid join group reply, expect %d bytes, read %d bytes\n", 4, len);
+ return;
+ }
bytes = 0;
- expected_bytes = 4;
- bytes += read_packet_dw(data, cursor, len, &internal_group_id);
+ bytes += qq_get32(&internal_group_id, data + bytes);
g_return_if_fail(internal_group_id > 0);
- if (bytes == expected_bytes)
- purple_notify_info
- (gc, _("QQ Group Auth"),
+ purple_notify_info(gc, _("QQ Group Auth"),
_("Your authorization request has been accepted by the QQ server"), NULL);
- else
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
}
/* process group cmd reply "join group" */
-void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc)
{
- gint bytes, expected_bytes;
+ gint bytes;
guint32 internal_group_id;
guint8 reply;
qq_group *group;
g_return_if_fail(data != NULL && len > 0);
- bytes = 0;
- expected_bytes = 5;
- bytes += read_packet_dw(data, cursor, len, &internal_group_id);
- bytes += read_packet_b(data, cursor, len, &reply);
-
- if (bytes != expected_bytes) {
+ if (len < 5) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Invalid join group reply, expect %d bytes, read %d bytes\n", expected_bytes, bytes);
+ "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len);
return;
- } else { /* join group OK */
- group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
- /* need to check if group is NULL or not. */
- g_return_if_fail(group != NULL);
- switch (reply) {
- case QQ_GROUP_JOIN_OK:
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
- group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
- qq_group_refresh(gc, group);
- /* this must be shown before getting online members */
- qq_group_conv_show_window(gc, group);
- qq_send_cmd_group_get_group_info(gc, group);
- break;
- case QQ_GROUP_JOIN_NEED_AUTH:
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Fail joining group [%d] %s, needs authentication\n",
- group->external_group_id, group->group_name_utf8);
- group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
- qq_group_refresh(gc, group);
- _qq_group_join_auth(gc, group);
- break;
- default:
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Error joining group [%d] %s, unknown reply: 0x%02x\n",
- group->external_group_id, group->group_name_utf8, reply);
- }
+ }
+
+ bytes = 0;
+ bytes += qq_get32(&internal_group_id, data + bytes);
+ bytes += qq_get8(&reply, data + bytes);
+
+ /* join group OK */
+ group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
+ /* need to check if group is NULL or not. */
+ g_return_if_fail(group != NULL);
+ switch (reply) {
+ case QQ_GROUP_JOIN_OK:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Succeed joining group \"%s\"\n", group->group_name_utf8);
+ group->my_status = QQ_GROUP_MEMBER_STATUS_IS_MEMBER;
+ qq_group_refresh(gc, group);
+ /* this must be shown before getting online members */
+ qq_group_conv_show_window(gc, group);
+ qq_send_cmd_group_get_group_info(gc, group);
+ break;
+ case QQ_GROUP_JOIN_NEED_AUTH:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Fail joining group [%d] %s, needs authentication\n",
+ group->external_group_id, group->group_name_utf8);
+ group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+ qq_group_refresh(gc, group);
+ _qq_group_join_auth(gc, group);
+ break;
+ default:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Error joining group [%d] %s, unknown reply: 0x%02x\n",
+ group->external_group_id, group->group_name_utf8, reply);
}
}
diff --git a/libpurple/protocols/qq/group_join.h b/libpurple/protocols/qq/group_join.h
index 5c42e31ed5..1c346717b6 100644
--- a/libpurple/protocols/qq/group_join.h
+++ b/libpurple/protocols/qq/group_join.h
@@ -46,8 +46,8 @@ void qq_group_join(PurpleConnection *gc, GHashTable *data);
void qq_send_cmd_group_join_group(PurpleConnection *gc, qq_group *group);
void qq_group_exit(PurpleConnection *gc, GHashTable *data);
void qq_send_cmd_group_exit_group(PurpleConnection *gc, qq_group *group);
-void qq_process_group_cmd_exit_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_join_group_auth(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_process_group_cmd_join_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_network.c b/libpurple/protocols/qq/group_network.c
index e88f7bb24e..71de4950cb 100644
--- a/libpurple/protocols/qq/group_network.c
+++ b/libpurple/protocols/qq/group_network.c
@@ -39,7 +39,7 @@
#include "group_opt.h"
#include "group_search.h"
#include "header_info.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "utils.h"
enum {
@@ -75,18 +75,42 @@ 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";
}
}
/* default process of reply error */
-static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *cursor, gint len, PurpleConnection *gc)
+static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *data, gint len, PurpleConnection *gc)
{
gchar *msg, *msg_utf8;
- g_return_if_fail(cursor != NULL && len > 0);
+ g_return_if_fail(data != NULL && len > 0);
- msg = g_strndup((gchar *) cursor, len); /* it will append 0x00 */
+ msg = g_strndup((gchar *) data, len); /* it will append 0x00 */
msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
g_free(msg);
msg = g_strdup_printf(_("Code [0x%02X]: %s"), reply, msg_utf8);
@@ -96,14 +120,13 @@ static void _qq_process_group_cmd_reply_error_default(guint8 reply, guint8 *curs
}
/* default process, dump only */
-static void _qq_process_group_cmd_reply_default(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+static void _qq_process_group_cmd_reply_default(guint8 *data, gint len, PurpleConnection *gc)
{
- gchar *hex_dump;
g_return_if_fail(data != NULL && len > 0);
- hex_dump = hex_dump_to_str(data, len);
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Dump unprocessed group cmd reply:\n%s", hex_dump);
- g_free(hex_dump);
+ qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+ data, len,
+ "Dump unprocessed group cmd reply:");
}
/* The lower layer command of send group cmd */
@@ -116,7 +139,7 @@ void qq_send_group_cmd(PurpleConnection *gc, qq_group *group, guint8 *raw_data,
qd = (qq_data *) gc->proto_data;
- qq_send_cmd(gc, QQ_CMD_GROUP_CMD, TRUE, 0, TRUE, raw_data, data_len);
+ qq_send_cmd(qd, QQ_CMD_GROUP_CMD, raw_data, data_len);
p = g_new0(group_packet, 1);
@@ -136,7 +159,7 @@ void qq_process_group_cmd_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleCo
qq_data *qd;
gint len, bytes;
guint32 internal_group_id;
- guint8 *data, *cursor, sub_cmd, reply;
+ guint8 *data, sub_cmd, reply;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -149,102 +172,101 @@ void qq_process_group_cmd_reply(guint8 *buf, gint buf_len, guint16 seq, PurpleCo
return;
}
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- if (len <= 2) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
- return;
- }
+ if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
+ return;
+ }
- bytes = 0;
- cursor = data;
- bytes += read_packet_b(data, &cursor, len, &sub_cmd);
- bytes += read_packet_b(data, &cursor, len, &reply);
-
- group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
-
- if (reply != QQ_GROUP_CMD_REPLY_OK) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
-
- if (group != NULL)
- qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
-
- switch (reply) { /* this should be all errors */
- case QQ_GROUP_CMD_REPLY_NOT_MEMBER:
- if (group != NULL) {
- purple_debug(PURPLE_DEBUG_WARNING,
- "QQ",
- "You are not a member of group \"%s\"\n", group->group_name_utf8);
- group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
- qq_group_refresh(gc, group);
- }
- break;
- case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
- if (qd->roomlist != NULL) {
- if (purple_roomlist_get_in_progress(qd->roomlist))
- purple_roomlist_set_in_progress(qd->roomlist, FALSE);
- }
- _qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc);
- break;
- default:
- _qq_process_group_cmd_reply_error_default(reply, cursor, len - bytes, gc);
- }
- return;
- }
+ if (len <= 2) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Group cmd reply is too short, only %d bytes\n", len);
+ return;
+ }
+
+ bytes = 0;
+ bytes += qq_get8(&sub_cmd, data + bytes);
+ bytes += qq_get8(&reply, data + bytes);
+
+ group = qq_group_find_by_id(gc, internal_group_id, QQ_INTERNAL_ID);
- /* seems ok so far, so we process the reply according to sub_cmd */
- switch (sub_cmd) {
- case QQ_GROUP_CMD_GET_GROUP_INFO:
- qq_process_group_cmd_get_group_info(data, &cursor, len, gc);
+ if (reply != QQ_GROUP_CMD_REPLY_OK) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "Group cmd reply says cmd %s fails\n", qq_group_cmd_get_desc(sub_cmd));
+
+ if (group != NULL)
+ qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
+
+ switch (reply) { /* this should be all errors */
+ case QQ_GROUP_CMD_REPLY_NOT_MEMBER:
if (group != NULL) {
- qq_send_cmd_group_get_members_info(gc, group);
- qq_send_cmd_group_get_online_members(gc, group);
+ purple_debug(PURPLE_DEBUG_WARNING,
+ "QQ",
+ "You are not a member of group \"%s\"\n", group->group_name_utf8);
+ group->my_status = QQ_GROUP_MEMBER_STATUS_NOT_MEMBER;
+ qq_group_refresh(gc, group);
}
break;
- case QQ_GROUP_CMD_CREATE_GROUP:
- qq_group_process_create_group_reply(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
- qq_group_process_modify_info_reply(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_MEMBER_OPT:
- qq_group_process_modify_members_reply(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_ACTIVATE_GROUP:
- qq_group_process_activate_group_reply(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_SEARCH_GROUP:
- qq_process_group_cmd_search_group(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_JOIN_GROUP:
- qq_process_group_cmd_join_group(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
- qq_process_group_cmd_join_group_auth(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_EXIT_GROUP:
- qq_process_group_cmd_exit_group(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_SEND_MSG:
- qq_process_group_cmd_im(data, &cursor, len, gc);
- break;
- case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
- qq_process_group_cmd_get_online_members(data, &cursor, len, gc);
- if (group != NULL)
- qq_group_conv_refresh_online_member(gc, group);
- break;
- case QQ_GROUP_CMD_GET_MEMBER_INFO:
- qq_process_group_cmd_get_members_info(data, &cursor, len, gc);
- if (group != NULL)
- qq_group_conv_refresh_online_member(gc, group);
+ case QQ_GROUP_CMD_REPLY_SEARCH_ERROR:
+ if (qd->roomlist != NULL) {
+ if (purple_roomlist_get_in_progress(qd->roomlist))
+ purple_roomlist_set_in_progress(qd->roomlist, FALSE);
+ }
+ _qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
break;
default:
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
- _qq_process_group_cmd_reply_default(data, &cursor, len, gc);
+ _qq_process_group_cmd_reply_error_default(reply, data + bytes, len - bytes, gc);
}
+ return;
+ }
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt group cmd reply\n");
+ /* seems ok so far, so we process the reply according to sub_cmd */
+ switch (sub_cmd) {
+ case QQ_GROUP_CMD_GET_GROUP_INFO:
+ qq_process_group_cmd_get_group_info(data + bytes, len - bytes, gc);
+ if (group != NULL) {
+ qq_send_cmd_group_get_members_info(gc, group);
+ qq_send_cmd_group_get_online_members(gc, group);
+ }
+ break;
+ case QQ_GROUP_CMD_CREATE_GROUP:
+ qq_group_process_create_group_reply(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_MODIFY_GROUP_INFO:
+ qq_group_process_modify_info_reply(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_MEMBER_OPT:
+ qq_group_process_modify_members_reply(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_ACTIVATE_GROUP:
+ qq_group_process_activate_group_reply(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_SEARCH_GROUP:
+ qq_process_group_cmd_search_group(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_JOIN_GROUP:
+ qq_process_group_cmd_join_group(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_JOIN_GROUP_AUTH:
+ qq_process_group_cmd_join_group_auth(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_EXIT_GROUP:
+ qq_process_group_cmd_exit_group(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_SEND_MSG:
+ qq_process_group_cmd_im(data + bytes, len - bytes, gc);
+ break;
+ case QQ_GROUP_CMD_GET_ONLINE_MEMBER:
+ qq_process_group_cmd_get_online_members(data + bytes, len - bytes, gc);
+ if (group != NULL)
+ qq_group_conv_refresh_online_member(gc, group);
+ break;
+ case QQ_GROUP_CMD_GET_MEMBER_INFO:
+ qq_process_group_cmd_get_members_info(data + bytes, len - bytes, gc);
+ if (group != NULL)
+ qq_group_conv_refresh_online_member(gc, group);
+ break;
+ default:
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "Group cmd %s is processed by default\n", qq_group_cmd_get_desc(sub_cmd));
+ _qq_process_group_cmd_reply_default(data + bytes, len, gc);
}
}
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/group_opt.c b/libpurple/protocols/qq/group_opt.c
index 8eff65ea64..d673dc6a3c 100644
--- a/libpurple/protocols/qq/group_opt.c
+++ b/libpurple/protocols/qq/group_opt.c
@@ -57,22 +57,24 @@ static void _sort(guint32 *list)
static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members)
{
- guint8 *data, *cursor;
+ guint8 *data;
gint i, count, data_len;
+ gint bytes;
g_return_if_fail(members != NULL);
- for (i = 0; members[i] != 0xffffffff; i++) {;
+ for (count = 0; members[count] != 0xffffffff; count++) {;
}
- count = i;
data_len = 6 + count * 4;
data = g_newa(guint8, data_len);
- cursor = data;
- create_packet_b(data, &cursor, QQ_GROUP_CMD_MEMBER_OPT);
- create_packet_dw(data, &cursor, group->internal_group_id);
- create_packet_b(data, &cursor, operation);
+
+ bytes = 0;
+ bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MEMBER_OPT);
+ bytes += qq_put32(data + bytes, group->internal_group_id);
+ bytes += qq_put8(data + bytes, operation);
for (i = 0; i < count; i++)
- create_packet_dw(data, &cursor, members[i]);
- qq_send_group_cmd(gc, group, data, data_len);
+ bytes += qq_put32(data + bytes, members[i]);
+
+ qq_send_group_cmd(gc, group, data, bytes);
}
static void _qq_group_do_nothing_with_struct(group_member_opt *g)
@@ -97,11 +99,11 @@ void qq_group_search_application_with_struct(group_member_opt *g)
qq_send_packet_get_info(g->gc, g->member, TRUE); /* we want to see window */
purple_request_action(g->gc, NULL, _("Do you want to approve the request?"), "",
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(g->gc), NULL, NULL,
- g, 2,
- _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
- _("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(g->gc), NULL, NULL,
+ g, 2,
+ _("Reject"), G_CALLBACK(qq_group_reject_application_with_struct),
+ _("Approve"), G_CALLBACK(qq_group_approve_application_with_struct));
}
void qq_group_reject_application_with_struct(group_member_opt *g)
@@ -193,13 +195,15 @@ void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new
_qq_group_member_opt(gc, group, QQ_GROUP_MEMBER_ADD, add_members);
}
-void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
{
+ gint bytes;
guint32 internal_group_id;
qq_group *group;
g_return_if_fail(data != NULL);
- read_packet_dw(data, cursor, len, &internal_group_id);
+ bytes = 0;
+ bytes += qq_get32(&internal_group_id, data + bytes);
g_return_if_fail(internal_group_id > 0);
/* we should have its info locally */
@@ -213,8 +217,9 @@ void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint l
void qq_group_modify_info(PurpleConnection *gc, qq_group *group)
{
- gint data_len, data_written;
- guint8 *data, *cursor;
+ guint8 *data;
+ gint data_len;
+ gint bytes;
gchar *group_name, *group_desc, *notice;
g_return_if_fail(group != NULL);
@@ -228,47 +233,50 @@ void qq_group_modify_info(PurpleConnection *gc, qq_group *group)
+ 1 + strlen(notice);
data = g_newa(guint8, data_len);
- cursor = data;
- data_written = 0;
+ bytes = 0;
/* 000-000 */
- data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
+ bytes += qq_put8(data + bytes, QQ_GROUP_CMD_MODIFY_GROUP_INFO);
/* 001-004 */
- data_written += create_packet_dw(data, &cursor, group->internal_group_id);
+ bytes += qq_put32(data + bytes, group->internal_group_id);
/* 005-005 */
- data_written += create_packet_b(data, &cursor, 0x01);
+ bytes += qq_put8(data + bytes, 0x01);
/* 006-006 */
- data_written += create_packet_b(data, &cursor, group->auth_type);
+ bytes += qq_put8(data + bytes, group->auth_type);
/* 007-008 */
- data_written += create_packet_w(data, &cursor, 0x0000);
+ bytes += qq_put16(data + bytes, 0x0000);
/* 009-010 */
- data_written += create_packet_w(data, &cursor, group->group_category);
+ bytes += qq_put16(data + bytes, group->group_category);
- data_written += create_packet_b(data, &cursor, strlen(group_name));
- data_written += create_packet_data(data, &cursor, (guint8 *) group_name, strlen(group_name));
+ bytes += qq_put8(data + bytes, strlen(group_name));
+ bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
- data_written += create_packet_w(data, &cursor, 0x0000);
+ bytes += qq_put16(data + bytes, 0x0000);
- data_written += create_packet_b(data, &cursor, strlen(notice));
- data_written += create_packet_data(data, &cursor, (guint8 *) notice, strlen(notice));
+ bytes += qq_put8(data + bytes, strlen(notice));
+ bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice));
- data_written += create_packet_b(data, &cursor, strlen(group_desc));
- data_written += create_packet_data(data, &cursor, (guint8 *) group_desc, strlen(group_desc));
+ bytes += qq_put8(data + bytes, strlen(group_desc));
+ bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
- if (data_written != data_len)
+ if (bytes != data_len) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
"Fail to create group_modify_info packet, expect %d bytes, wrote %d bytes\n",
- data_len, data_written);
- else
- qq_send_group_cmd(gc, group, data, data_len);
+ data_len, bytes);
+ return;
+ }
+
+ qq_send_group_cmd(gc, group, data, bytes);
}
-void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc)
{
+ gint bytes;
guint32 internal_group_id;
qq_group *group;
g_return_if_fail(data != NULL);
- read_packet_dw(data, cursor, len, &internal_group_id);
+ bytes = 0;
+ bytes += qq_get32(&internal_group_id, data + bytes);
g_return_if_fail(internal_group_id > 0);
/* we should have its info locally */
@@ -284,42 +292,44 @@ void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len,
/* we create a very simple group first, and then let the user to modify */
void qq_group_create_with_name(PurpleConnection *gc, const gchar *name)
{
- gint data_len, data_written;
- guint8 *data, *cursor;
+ gint data_len;
+ guint8 *data;
+ gint bytes;
qq_data *qd;
g_return_if_fail(name != NULL);
qd = (qq_data *) gc->proto_data;
data_len = 7 + 1 + strlen(name) + 2 + 1 + 1 + 4;
data = g_newa(guint8, data_len);
- cursor = data;
- data_written = 0;
+ bytes = 0;
/* we create the simpleset group, only group name is given */
/* 000 */
- data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_CREATE_GROUP);
+ bytes += qq_put8(data + bytes, QQ_GROUP_CMD_CREATE_GROUP);
/* 001 */
- data_written += create_packet_b(data, &cursor, QQ_GROUP_TYPE_PERMANENT);
+ bytes += qq_put8(data + bytes, QQ_GROUP_TYPE_PERMANENT);
/* 002 */
- data_written += create_packet_b(data, &cursor, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
+ bytes += qq_put8(data + bytes, QQ_GROUP_AUTH_TYPE_NEED_AUTH);
/* 003-004 */
- data_written += create_packet_w(data, &cursor, 0x0000);
+ bytes += qq_put16(data + bytes, 0x0000);
/* 005-006 */
- data_written += create_packet_w(data, &cursor, 0x0003);
+ bytes += qq_put16(data + bytes, 0x0003);
/* 007 */
- data_written += create_packet_b(data, &cursor, strlen(name));
- data_written += create_packet_data(data, &cursor, (guint8 *) name, strlen(name));
- data_written += create_packet_w(data, &cursor, 0x0000);
- data_written += create_packet_b(data, &cursor, 0x00); /* no group notice */
- data_written += create_packet_b(data, &cursor, 0x00); /* no group desc */
- data_written += create_packet_dw(data, &cursor, qd->uid); /* I am member of coz */
-
- if (data_written != data_len)
+ bytes += qq_put8(data + bytes, strlen(name));
+ bytes += qq_putdata(data + bytes, (guint8 *) name, strlen(name));
+ bytes += qq_put16(data + bytes, 0x0000);
+ bytes += qq_put8(data + bytes, 0x00); /* no group notice */
+ bytes += qq_put8(data + bytes, 0x00); /* no group desc */
+ bytes += qq_put32(data + bytes, qd->uid); /* I am member of coz */
+
+ if (bytes != data_len) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
"Fail create create_group packet, expect %d bytes, written %d bytes\n",
- data_len, data_written);
- else
- qq_send_group_cmd(gc, NULL, data, data_len);
+ data_len, bytes);
+ return;
+ }
+
+ qq_send_group_cmd(gc, NULL, data, bytes);
}
static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
@@ -335,8 +345,9 @@ static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
g_free(g);
}
-void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc)
{
+ gint bytes;
guint32 internal_group_id, external_group_id;
qq_group *group;
gc_and_uid *g;
@@ -346,8 +357,9 @@ void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len
g_return_if_fail(gc->proto_data != NULL);
qd = (qq_data *) gc->proto_data;
- read_packet_dw(data, cursor, len, &internal_group_id);
- read_packet_dw(data, cursor, len, &external_group_id);
+ bytes = 0;
+ bytes += qq_get32(&internal_group_id, data + bytes);
+ bytes += qq_get32(&external_group_id, data + bytes);
g_return_if_fail(internal_group_id > 0 && external_group_id);
group = qq_group_create_internal_record(gc, internal_group_id, external_group_id, NULL);
@@ -378,36 +390,29 @@ void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len
/* we have to activate group after creation, otherwise the group can not be searched */
void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id)
{
- gint data_len, data_written;
- guint8 *data, *cursor;
+ guint8 data[16] = {0};
+ gint bytes = 0;
g_return_if_fail(internal_group_id > 0);
- data_len = 5;
- data = g_newa(guint8, data_len);
- cursor = data;
-
- data_written = 0;
+ bytes = 0;
/* we create the simplest group, only group name is given */
/* 000 */
- data_written += create_packet_b(data, &cursor, QQ_GROUP_CMD_ACTIVATE_GROUP);
+ bytes += qq_put8(data + bytes, QQ_GROUP_CMD_ACTIVATE_GROUP);
/* 001-005 */
- data_written += create_packet_dw(data, &cursor, internal_group_id);
+ bytes += qq_put32(data + bytes, internal_group_id);
- if (data_written != data_len)
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create activate_group packet, expect %d bytes, written %d bytes\n",
- data_len, data_written);
- else
- qq_send_group_cmd(gc, NULL, data, data_len);
+ qq_send_group_cmd(gc, NULL, data, bytes);
}
-void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc)
{
+ gint bytes;
guint32 internal_group_id;
qq_group *group;
g_return_if_fail(data != NULL);
- read_packet_dw(data, cursor, len, &internal_group_id);
+ bytes = 0;
+ bytes += qq_get32(&internal_group_id, data + bytes);
g_return_if_fail(internal_group_id > 0);
/* we should have its info locally */
diff --git a/libpurple/protocols/qq/group_opt.h b/libpurple/protocols/qq/group_opt.h
index 4aa09a7e0d..7e176bf2b0 100644
--- a/libpurple/protocols/qq/group_opt.h
+++ b/libpurple/protocols/qq/group_opt.h
@@ -54,12 +54,12 @@ void qq_group_approve_application_with_struct(group_member_opt *g);
void qq_group_reject_application_with_struct(group_member_opt *g);
void qq_group_search_application_with_struct(group_member_opt *g);
-void qq_group_process_modify_info_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_group_process_modify_members_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc);
+void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc);
void qq_group_manage_group(PurpleConnection *gc, GHashTable *data);
void qq_group_create_with_name(PurpleConnection *gc, const gchar *name);
void qq_group_activate_group(PurpleConnection *gc, guint32 internal_group_id);
-void qq_group_process_activate_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
-void qq_group_process_create_group_reply(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc);
+void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_search.c b/libpurple/protocols/qq/group_search.c
index fe5789b928..435b353af5 100644
--- a/libpurple/protocols/qq/group_search.c
+++ b/libpurple/protocols/qq/group_search.c
@@ -43,24 +43,18 @@ enum {
/* send packet to search for qq_group */
void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id)
{
- guint8 *raw_data, *cursor, type;
- gint bytes, data_len;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
+ guint8 type;
- data_len = 6;
- raw_data = g_newa(guint8, data_len);
- cursor = raw_data;
type = (external_group_id == 0x00000000) ? QQ_GROUP_SEARCH_TYPE_DEMO : QQ_GROUP_SEARCH_TYPE_BY_ID;
bytes = 0;
- bytes += create_packet_b(raw_data, &cursor, QQ_GROUP_CMD_SEARCH_GROUP);
- bytes += create_packet_b(raw_data, &cursor, type);
- bytes += create_packet_dw(raw_data, &cursor, external_group_id);
-
- if (bytes != data_len)
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create packet for %s\n", qq_group_cmd_get_desc(QQ_GROUP_CMD_SEARCH_GROUP));
- else
- qq_send_group_cmd(gc, NULL, raw_data, data_len);
+ bytes += qq_put8(raw_data + bytes, QQ_GROUP_CMD_SEARCH_GROUP);
+ bytes += qq_put8(raw_data + bytes, type);
+ bytes += qq_put32(raw_data + bytes, external_group_id);
+
+ qq_send_group_cmd(gc, NULL, raw_data, bytes);
}
static void _qq_setup_roomlist(qq_data *qd, qq_group *group)
@@ -89,55 +83,50 @@ static void _qq_setup_roomlist(qq_data *qd, qq_group *group)
}
/* process group cmd reply "search group" */
-void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc)
{
+ gint bytes;
guint8 search_type;
guint16 unknown;
- gint bytes, pascal_len;
+ qq_group group;
qq_data *qd;
- qq_group *group;
GSList *pending_id;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
- read_packet_b(data, cursor, len, &search_type);
- group = g_newa(qq_group, 1);
+ bytes = 0;
+ bytes += qq_get8(&search_type, data + bytes);
/* now it starts with group_info_entry */
- bytes = 0;
- bytes += read_packet_dw(data, cursor, len, &(group->internal_group_id));
- bytes += read_packet_dw(data, cursor, len, &(group->external_group_id));
- bytes += read_packet_b(data, cursor, len, &(group->group_type));
- bytes += read_packet_w(data, cursor, len, &(unknown));
- bytes += read_packet_w(data, cursor, len, &(unknown));
- bytes += read_packet_dw(data, cursor, len, &(group->creator_uid));
- bytes += read_packet_w(data, cursor, len, &(unknown));
- bytes += read_packet_w(data, cursor, len, &(unknown));
- bytes += read_packet_w(data, cursor, len, &(unknown));
- bytes += read_packet_dw(data, cursor, len, &(group->group_category));
- pascal_len = convert_as_pascal_string(*cursor, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
- bytes += pascal_len;
- *cursor += pascal_len;
- bytes += read_packet_w(data, cursor, len, &(unknown));
- bytes += read_packet_b(data, cursor, len, &(group->auth_type));
- pascal_len = convert_as_pascal_string(*cursor, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
- bytes += pascal_len;
- *cursor += pascal_len;
+ bytes += qq_get32(&(group.internal_group_id), data + bytes);
+ bytes += qq_get32(&(group.external_group_id), data + bytes);
+ bytes += qq_get8(&(group.group_type), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get32(&(group.creator_uid), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get32(&(group.group_category), data + bytes);
+ bytes += convert_as_pascal_string(data + bytes, &(group.group_name_utf8), QQ_CHARSET_DEFAULT);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get8(&(group.auth_type), data + bytes);
+ bytes += convert_as_pascal_string(data + bytes, &(group.group_desc_utf8), QQ_CHARSET_DEFAULT);
/* end of one qq_group */
- if(*cursor != (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
- }
+ if(bytes != len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
+ }
- pending_id = qq_get_pending_id(qd->joining_groups, group->external_group_id);
+ pending_id = qq_get_pending_id(qd->joining_groups, group.external_group_id);
if (pending_id != NULL) {
- qq_set_pending_id(&qd->joining_groups, group->external_group_id, FALSE);
- if (qq_group_find_by_id(gc, group->internal_group_id, QQ_INTERNAL_ID) == NULL)
+ qq_set_pending_id(&qd->joining_groups, group.external_group_id, FALSE);
+ if (qq_group_find_by_id(gc, group.internal_group_id, QQ_INTERNAL_ID) == NULL)
qq_group_create_internal_record(gc,
- group->internal_group_id, group->external_group_id, group->group_name_utf8);
- qq_send_cmd_group_join_group(gc, group);
+ group.internal_group_id, group.external_group_id, group.group_name_utf8);
+ qq_send_cmd_group_join_group(gc, &group);
} else {
- _qq_setup_roomlist(qd, group);
+ _qq_setup_roomlist(qd, &group);
}
}
diff --git a/libpurple/protocols/qq/group_search.h b/libpurple/protocols/qq/group_search.h
index 46b889a2fc..c370a9341d 100644
--- a/libpurple/protocols/qq/group_search.h
+++ b/libpurple/protocols/qq/group_search.h
@@ -29,6 +29,6 @@
#include "connection.h"
void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 external_group_id);
-void qq_process_group_cmd_search_group(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc);
+void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc);
#endif
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 f63b7f6dbe..1962d8aadc 100644
--- a/libpurple/protocols/qq/im.c
+++ b/libpurple/protocols/qq/im.c
@@ -40,19 +40,16 @@
#include "header_info.h"
#include "im.h"
#include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "send_file.h"
#include "utils.h"
#define QQ_SEND_IM_REPLY_OK 0x00
#define DEFAULT_FONT_NAME_LEN 4
-/* a debug function */
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
enum
{
- QQ_NORMAL_IM_TEXT = 0x000b,
+ QQ_NORMAL_IM_TEXT = 0x000b,
QQ_NORMAL_IM_FILE_REQUEST_TCP = 0x0001,
QQ_NORMAL_IM_FILE_APPROVE_TCP = 0x0003,
QQ_NORMAL_IM_FILE_REJECT_TCP = 0x0005,
@@ -82,7 +79,7 @@ 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;
};
@@ -112,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;
};
@@ -121,9 +118,9 @@ struct _qq_recv_im_header {
#define DEFAULT_FONT_NAME "\0xcb\0xce\0xcc\0xe5"
guint8 *qq_get_send_im_tail(const gchar *font_color,
- const gchar *font_size,
- const gchar *font_name,
- gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
+ const gchar *font_size,
+ const gchar *font_name,
+ gboolean is_bold, gboolean is_italic, gboolean is_underline, gint tail_len)
{
gchar *s1;
unsigned char *rgb;
@@ -141,7 +138,7 @@ guint8 *qq_get_send_im_tail(const gchar *font_color,
send_im_tail = g_new0(guint8, tail_len);
g_strlcpy((gchar *) (send_im_tail + QQ_SEND_IM_AFTER_MSG_HEADER_LEN),
- font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
+ font_name, tail_len - QQ_SEND_IM_AFTER_MSG_HEADER_LEN);
send_im_tail[tail_len - 1] = (guint8) tail_len;
send_im_tail[0] = 0x00;
@@ -182,39 +179,39 @@ 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;
}
static const gchar *qq_get_recv_im_type_str(gint type)
{
switch (type) {
- case QQ_RECV_IM_TO_BUDDY:
- return "QQ_RECV_IM_TO_BUDDY";
- case QQ_RECV_IM_TO_UNKNOWN:
- return "QQ_RECV_IM_TO_UNKNOWN";
- case QQ_RECV_IM_UNKNOWN_QUN_IM:
- return "QQ_RECV_IM_UNKNOWN_QUN_IM";
- case QQ_RECV_IM_ADD_TO_QUN:
- return "QQ_RECV_IM_ADD_TO_QUN";
- case QQ_RECV_IM_DEL_FROM_QUN:
- return "QQ_RECV_IM_DEL_FROM_QUN";
- case QQ_RECV_IM_APPLY_ADD_TO_QUN:
- return "QQ_RECV_IM_APPLY_ADD_TO_QUN";
- case QQ_RECV_IM_CREATE_QUN:
- return "QQ_RECV_IM_CREATE_QUN";
- case QQ_RECV_IM_SYS_NOTIFICATION:
- return "QQ_RECV_IM_SYS_NOTIFICATION";
- case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
- return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN";
- case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
- return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN";
- case QQ_RECV_IM_TEMP_QUN_IM:
- return "QQ_RECV_IM_TEMP_QUN_IM";
- case QQ_RECV_IM_QUN_IM:
- return "QQ_RECV_IM_QUN_IM";
- default:
- return "QQ_RECV_IM_UNKNOWN";
+ case QQ_RECV_IM_TO_BUDDY:
+ return "QQ_RECV_IM_TO_BUDDY";
+ case QQ_RECV_IM_TO_UNKNOWN:
+ return "QQ_RECV_IM_TO_UNKNOWN";
+ case QQ_RECV_IM_UNKNOWN_QUN_IM:
+ return "QQ_RECV_IM_UNKNOWN_QUN_IM";
+ case QQ_RECV_IM_ADD_TO_QUN:
+ return "QQ_RECV_IM_ADD_TO_QUN";
+ case QQ_RECV_IM_DEL_FROM_QUN:
+ return "QQ_RECV_IM_DEL_FROM_QUN";
+ case QQ_RECV_IM_APPLY_ADD_TO_QUN:
+ return "QQ_RECV_IM_APPLY_ADD_TO_QUN";
+ case QQ_RECV_IM_CREATE_QUN:
+ return "QQ_RECV_IM_CREATE_QUN";
+ case QQ_RECV_IM_SYS_NOTIFICATION:
+ return "QQ_RECV_IM_SYS_NOTIFICATION";
+ case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
+ return "QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN";
+ case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
+ return "QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN";
+ case QQ_RECV_IM_TEMP_QUN_IM:
+ return "QQ_RECV_IM_TEMP_QUN_IM";
+ case QQ_RECV_IM_QUN_IM:
+ return "QQ_RECV_IM_QUN_IM";
+ default:
+ return "QQ_RECV_IM_UNKNOWN";
}
}
@@ -222,27 +219,26 @@ static const gchar *qq_get_recv_im_type_str(gint type)
* we send an ACK which is the first 16 bytes of incoming packet */
static void _qq_send_packet_recv_im_ack(PurpleConnection *gc, guint16 seq, guint8 *data)
{
- qq_send_cmd(gc, QQ_CMD_RECV_IM, FALSE, seq, FALSE, data, 16);
+ qq_data *qd;
+
+ qd = (qq_data *) gc->proto_data;
+ qq_send_cmd_detail(qd, QQ_CMD_RECV_IM, seq, FALSE, data, 16);
}
/* read the common parts of the normal_im,
* returns the bytes read if succeed, or -1 if there is any error */
-static gint _qq_normal_im_common_read(guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common)
+static gint _qq_normal_im_common_read(guint8 *data, gint len, qq_recv_normal_im_common *common)
{
gint bytes;
g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1);
bytes = 0;
/* now push data into common header */
- bytes += read_packet_w(data, cursor, len, &(common->sender_ver));
- bytes += read_packet_dw(data, cursor, len, &(common->sender_uid));
- bytes += read_packet_dw(data, cursor, len, &(common->receiver_uid));
-
- common->session_md5 = g_memdup(*cursor, QQ_KEY_LENGTH);
- bytes += QQ_KEY_LENGTH;
- *cursor += QQ_KEY_LENGTH;
-
- bytes += read_packet_w(data, cursor, len, &(common->normal_im_type));
+ bytes += qq_get16(&(common->sender_ver), data + bytes);
+ bytes += qq_get32(&(common->sender_uid), data + bytes);
+ bytes += qq_get32(&(common->receiver_uid), data + bytes);
+ bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes);
+ bytes += qq_get16(&(common->normal_im_type), data + bytes);
if (bytes != 28) { /* read common place fail */
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Expect 28 bytes, read %d bytes\n", bytes);
@@ -253,8 +249,7 @@ static gint _qq_normal_im_common_read(guint8 *data, guint8 **cursor, gint len, q
}
/* process received normal text IM */
-static void _qq_process_recv_normal_im_text
- (guint8 *data, guint8 **cursor, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
+static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
{
guint16 purple_msg_type;
gchar *name;
@@ -262,62 +257,73 @@ static void _qq_process_recv_normal_im_text
gchar *msg_utf8_encoded;
qq_data *qd;
qq_recv_normal_im_text *im_text;
+ gint bytes = 0;
+ PurpleBuddy *b;
+ qq_buddy *qq_b;
g_return_if_fail(common != NULL);
qd = (qq_data *) gc->proto_data;
/* now it is QQ_NORMAL_IM_TEXT */
- if (*cursor >= (data + len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
- return;
- } else
- im_text = g_newa(qq_recv_normal_im_text, 1);
+ /*
+ if (*cursor >= (data + len - 1)) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
+ return;
+ } else
+ */
+ im_text = g_newa(qq_recv_normal_im_text, 1);
im_text->common = common;
/* push data into im_text */
- read_packet_w(data, cursor, len, &(im_text->msg_seq));
- read_packet_dw(data, cursor, len, &(im_text->send_time));
- read_packet_w(data, cursor, len, &(im_text->sender_icon));
- read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown2), 3);
- read_packet_b(data, cursor, len, &(im_text->is_there_font_attr));
+ bytes += qq_get16(&(im_text->msg_seq), data + bytes);
+ bytes += qq_get32(&(im_text->send_time), data + bytes);
+ bytes += qq_get16(&(im_text->sender_icon), data + bytes);
+ bytes += qq_getdata((guint8 *) & (im_text->unknown2), 3, data + bytes);
+ bytes += qq_get8(&(im_text->is_there_font_attr), data + bytes);
/**
* from lumaqq for unknown3
* totalFragments = buf.get() & 255;
- * fragmentSequence = buf.get() & 255;
- * messageId = buf.getChar();
+ * fragmentSequence = buf.get() & 255;
+ * messageId = buf.getChar();
*/
- read_packet_data(data, cursor, len, (guint8 *) & (im_text->unknown3), 4);
- read_packet_b(data, cursor, len, &(im_text->msg_type));
+ bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes);
+ bytes += qq_get8(&(im_text->msg_type), data + bytes);
/* we need to check if this is auto-reply
* QQ2003iii build 0304, returns the msg without font_attr
* even the is_there_font_attr shows 0x01, and msg does not ends with 0x00 */
if (im_text->msg_type == QQ_IM_AUTO_REPLY) {
im_text->is_there_font_attr = 0x00; /* indeed there is no this flag */
- im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor);
+ im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
} else { /* it is normal mesasge */
if (im_text->is_there_font_attr) {
- im_text->msg = g_strdup(*(gchar **) cursor);
- *cursor += strlen(im_text->msg) + 1;
- im_text->font_attr_len = data + len - *cursor;
- im_text->font_attr = g_memdup(*cursor, im_text->font_attr_len);
+ im_text->msg = g_strdup((gchar *)(data + bytes));
+ bytes += strlen(im_text->msg) + 1; /* length decided by strlen! will it cause a crash? */
+ im_text->font_attr_len = len - bytes;
+ im_text->font_attr = g_memdup(data + bytes, im_text->font_attr_len);
} else /* not im_text->is_there_font_attr */
- im_text->msg = g_strndup(*(gchar **) cursor, data + len - *cursor);
+ im_text->msg = g_strndup((gchar *)(data + bytes), len - bytes);
} /* if im_text->msg_type */
- _qq_show_packet("QQ_MESG recv", data, *cursor - data);
name = uid_to_purple_name(common->sender_uid);
- if (purple_find_buddy(gc->account, name) == NULL)
+ b = purple_find_buddy(gc->account, name);
+ if (b == NULL) {
qq_add_buddy_by_recv_packet(gc, common->sender_uid, FALSE, TRUE);
-
+ b = purple_find_buddy(gc->account, name);
+ }
+ qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+ if (qq_b != NULL) {
+ qq_b->client_version = common->sender_ver;
+ }
+
purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg);
msg_utf8_encoded = im_text->is_there_font_attr ?
- qq_encode_to_purple(im_text->font_attr,
- im_text->font_attr_len,
- msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+ qq_encode_to_purple(im_text->font_attr,
+ im_text->font_attr_len,
+ msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
/* send encoded to purple, note that we use im_text->send_time,
* not the time we receive the message
@@ -333,81 +339,66 @@ static void _qq_process_recv_normal_im_text
}
/* it is a normal IM, maybe text or video request */
-static void _qq_process_recv_normal_im(guint8 *data, guint8 **cursor, gint len, PurpleConnection *gc)
+static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
{
- gint bytes;
+ gint bytes = 0;
qq_recv_normal_im_common *common;
qq_recv_normal_im_unprocessed *im_unprocessed;
- gchar *hex_dump;
g_return_if_fail (data != NULL && len != 0);
- if (*cursor >= (data + len - 1)) {
- purple_debug (PURPLE_DEBUG_WARNING, "QQ",
- "Received normal IM is empty\n");
- return;
- }
- else
- common = g_newa (qq_recv_normal_im_common, 1);
+ common = g_newa (qq_recv_normal_im_common, 1);
- bytes = _qq_normal_im_common_read (data, cursor, len, common);
+ bytes = _qq_normal_im_common_read(data, len, common);
if (bytes < 0) {
purple_debug (PURPLE_DEBUG_ERROR, "QQ",
- "Fail read the common part of normal IM\n");
+ "Fail read the common part of normal IM\n");
return;
}
switch (common->normal_im_type) {
- case QQ_NORMAL_IM_TEXT:
- purple_debug (PURPLE_DEBUG_INFO,
- "QQ",
- "Normal IM, text type:\n [%d] => [%d], src: %s\n",
- common->sender_uid, common->receiver_uid,
- qq_get_source_str (common->sender_ver));
- _qq_process_recv_normal_im_text (data, cursor, len, common,
- gc);
- break;
- case QQ_NORMAL_IM_FILE_REJECT_UDP:
- qq_process_recv_file_reject (data, cursor, len,
- common->sender_uid, gc);
- break;
- case QQ_NORMAL_IM_FILE_APPROVE_UDP:
- qq_process_recv_file_accept (data, cursor, len,
- common->sender_uid, gc);
- break;
- case QQ_NORMAL_IM_FILE_REQUEST_UDP:
- qq_process_recv_file_request (data, cursor, len,
- common->sender_uid, gc);
- break;
- case QQ_NORMAL_IM_FILE_CANCEL:
- qq_process_recv_file_cancel (data, cursor, len,
- common->sender_uid, gc);
- break;
- case QQ_NORMAL_IM_FILE_NOTIFY:
- qq_process_recv_file_notify (data, cursor, len,
- common->sender_uid, gc);
- break;
- default:
- im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
- im_unprocessed->common = common;
- im_unprocessed->unknown = *cursor;
- im_unprocessed->length = data + len - *cursor;
- /* a simple process here, maybe more later */
- purple_debug (PURPLE_DEBUG_WARNING, "QQ",
- "Normal IM, unprocessed type [0x%04x]\n",
- common->normal_im_type);
- hex_dump = hex_dump_to_str(im_unprocessed->unknown, im_unprocessed->length);
- purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Dump unknown part.\n%s", hex_dump);
- g_free(hex_dump);
- g_free (common->session_md5);
- return;
+ case QQ_NORMAL_IM_TEXT:
+ purple_debug (PURPLE_DEBUG_INFO, "QQ",
+ "Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n",
+ common->sender_uid, common->receiver_uid,
+ qq_get_ver_desc (common->sender_ver), common->sender_ver);
+ if (bytes >= len - 1) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received normal IM text is empty\n");
+ return;
+ }
+ _qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_REJECT_UDP:
+ qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_APPROVE_UDP:
+ qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_REQUEST_UDP:
+ qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_CANCEL:
+ qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_NOTIFY:
+ qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc);
+ break;
+ default:
+ im_unprocessed = g_newa (qq_recv_normal_im_unprocessed, 1);
+ im_unprocessed->common = common;
+ im_unprocessed->unknown = data + bytes;
+ im_unprocessed->length = len - bytes;
+ /* a simple process here, maybe more later */
+ purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+ "Normal IM, unprocessed type [0x%04x], len %d\n",
+ common->normal_im_type, im_unprocessed->length);
+ qq_show_packet ("QQ unk-im", im_unprocessed->unknown, im_unprocessed->length);
+ return;
}
-
- g_free (common->session_md5);
}
/* process im from system administrator */
-static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len, PurpleConnection *gc)
+static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
{
gint len;
guint8 reply;
@@ -415,14 +406,9 @@ static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len
g_return_if_fail(data != NULL && data_len != 0);
- if (*cursor >= (data + data_len - 1)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Received sys IM is empty\n");
- return;
- }
-
- len = data + data_len - *cursor;
+ len = data_len;
- if (NULL == (segments = split_data(*cursor, len, "\x2f", 2)))
+ if (NULL == (segments = split_data(data, len, "\x2f", 2)))
return;
reply = strtol(segments[0], NULL, 10);
@@ -436,7 +422,7 @@ static void _qq_process_recv_sys_im(guint8 *data, guint8 **cursor, gint data_len
void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type)
{
qq_data *qd;
- guint8 *cursor, *raw_data, *send_im_tail;
+ guint8 *raw_data, *send_im_tail;
guint16 client_tag, normal_im_type;
gint msg_len, raw_len, font_name_len, tail_len, bytes;
time_t now;
@@ -500,52 +486,51 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty
raw_len = QQ_SEND_IM_BEFORE_MSG_LEN + msg_len + tail_len;
raw_data = g_newa(guint8, raw_len);
- cursor = raw_data;
bytes = 0;
/* 000-003: receiver uid */
- bytes += create_packet_dw(raw_data, &cursor, qd->uid);
+ bytes += qq_put32(raw_data + bytes, qd->uid);
/* 004-007: sender uid */
- bytes += create_packet_dw(raw_data, &cursor, to_uid);
+ bytes += qq_put32(raw_data + bytes, to_uid);
/* 008-009: sender client version */
- bytes += create_packet_w(raw_data, &cursor, client_tag);
+ bytes += qq_put16(raw_data + bytes, client_tag);
/* 010-013: receiver uid */
- bytes += create_packet_dw(raw_data, &cursor, qd->uid);
+ bytes += qq_put32(raw_data + bytes, qd->uid);
/* 014-017: sender uid */
- bytes += create_packet_dw(raw_data, &cursor, to_uid);
+ bytes += qq_put32(raw_data + bytes, to_uid);
/* 018-033: md5 of (uid+session_key) */
- bytes += create_packet_data(raw_data, &cursor, qd->session_md5, 16);
+ bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
/* 034-035: message type */
- bytes += create_packet_w(raw_data, &cursor, normal_im_type);
+ bytes += qq_put16(raw_data + bytes, normal_im_type);
/* 036-037: sequence number */
- bytes += create_packet_w(raw_data, &cursor, qd->send_seq);
+ bytes += qq_put16(raw_data + bytes, qd->send_seq);
/* 038-041: send time */
- bytes += create_packet_dw(raw_data, &cursor, (guint32) now);
+ bytes += qq_put32(raw_data + bytes, (guint32) now);
/* 042-043: sender icon */
- bytes += create_packet_w(raw_data, &cursor, qd->my_icon);
+ bytes += qq_put16(raw_data + bytes, qd->my_icon);
/* 044-046: always 0x00 */
- bytes += create_packet_w(raw_data, &cursor, 0x0000);
- bytes += create_packet_b(raw_data, &cursor, 0x00);
+ bytes += qq_put16(raw_data + bytes, 0x0000);
+ bytes += qq_put8(raw_data + bytes, 0x00);
/* 047-047: we use font attr */
- bytes += create_packet_b(raw_data, &cursor, 0x01);
+ bytes += qq_put8(raw_data + bytes, 0x01);
/* 048-051: always 0x00 */
- bytes += create_packet_dw(raw_data, &cursor, 0x00000000);
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
/* 052-052: text message type (normal/auto-reply) */
- bytes += create_packet_b(raw_data, &cursor, type);
+ bytes += qq_put8(raw_data + bytes, type);
/* 053- : msg ends with 0x00 */
- bytes += create_packet_data(raw_data, &cursor, (guint8 *) msg_filtered, msg_len);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *) msg_filtered, msg_len);
send_im_tail = qq_get_send_im_tail(font_color, font_size, font_name, is_bold,
- is_italic, is_underline, tail_len);
- _qq_show_packet("QQ_MESG debug", send_im_tail, tail_len);
- bytes += create_packet_data(raw_data, &cursor, send_im_tail, tail_len);
+ is_italic, is_underline, tail_len);
+ qq_show_packet("QQ_send_im_tail debug", send_im_tail, tail_len);
+ bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len);
- _qq_show_packet("QQ_MESG raw", raw_data, cursor - raw_data);
+ qq_show_packet("QQ_raw_data debug", raw_data, bytes);
if (bytes == raw_len) /* create packet OK */
- qq_send_cmd(gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data, cursor - raw_data);
+ qq_send_cmd(qd, QQ_CMD_SEND_IM, raw_data, bytes);
else
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
+ "Fail creating send_im packet, expect %d bytes, build %d bytes\n", raw_len, bytes);
if (font_color)
g_free(font_color);
@@ -560,7 +545,8 @@ void qq_process_send_im_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
{
qq_data *qd;
gint len;
- guint8 *data, *cursor, reply;
+ guint8 *data, reply;
+ gint bytes = 0;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -569,8 +555,7 @@ void qq_process_send_im_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
data = g_newa(guint8, len);
if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- cursor = data;
- read_packet_b(data, &cursor, len, &reply);
+ bytes += qq_get8(&reply, data + bytes);
if (reply != QQ_SEND_IM_REPLY_OK) {
purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Send IM fail\n");
purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
@@ -588,7 +573,7 @@ void qq_process_recv_im(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection
{
qq_data *qd;
gint len, bytes;
- guint8 *data, *cursor;
+ guint8 *data;
qq_recv_im_header *im_header;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -597,98 +582,107 @@ void qq_process_recv_im(guint8 *buf, gint buf_len, guint16 seq, PurpleConnection
len = buf_len;
data = g_newa(guint8, len);
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- if (len < 16) { /* we need to ack with the first 16 bytes */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
- return;
- } else
- _qq_send_packet_recv_im_ack(gc, seq, data);
-
- cursor = data;
- bytes = 0;
- im_header = g_newa(qq_recv_im_header, 1);
- bytes += read_packet_dw(data, &cursor, len, &(im_header->sender_uid));
- bytes += read_packet_dw(data, &cursor, len, &(im_header->receiver_uid));
- bytes += read_packet_dw(data, &cursor, len, &(im_header->server_im_seq));
- /* if the message is delivered via server, it is server IP/port */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) & (im_header->sender_ip), 4);
- bytes += read_packet_w(data, &cursor, len, &(im_header->sender_port));
- bytes += read_packet_w(data, &cursor, len, &(im_header->im_type));
-
- if (bytes != 20) { /* length of im_header */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail read recv IM header, expect 20 bytes, read %d bytes\n", bytes);
- return;
- }
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
+ }
- if (im_header->receiver_uid != qd->uid) { /* should not happen */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
- return;
- }
+ if (len < 16) { /* we need to ack with the first 16 bytes */
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM is too short\n");
+ return;
+ } else {
+ _qq_send_packet_recv_im_ack(gc, seq, data);
+ }
+
+ /* check len first */
+ if (len < 20) { /* length of im_header */
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Fail read recv IM header, len should longer than 20 bytes, read %d bytes\n", len);
+ return;
+ }
+
+ bytes = 0;
+ im_header = g_newa(qq_recv_im_header, 1);
+ bytes += qq_get32(&(im_header->sender_uid), data + bytes);
+ bytes += qq_get32(&(im_header->receiver_uid), data + bytes);
+ bytes += qq_get32(&(im_header->server_im_seq), data + bytes);
+ /* if the message is delivered via server, it is server IP/port */
+ bytes += qq_getIP(&(im_header->sender_ip), data + bytes);
+ bytes += qq_get16(&(im_header->sender_port), data + bytes);
+ bytes += qq_get16(&(im_header->im_type), data + bytes);
+ /* im_header prepared */
+
+ if (im_header->receiver_uid != qd->uid) { /* should not happen */
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "IM to [%d], NOT me\n", im_header->receiver_uid);
+ return;
+ }
+
+ /* check bytes */
+ if (bytes >= len - 1) {
+ purple_debug (PURPLE_DEBUG_WARNING, "QQ", "Received IM is empty\n");
+ return;
+ }
- switch (im_header->im_type) {
+ switch (im_header->im_type) {
case QQ_RECV_IM_TO_BUDDY:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
- _qq_process_recv_normal_im(data, &cursor, len, gc);
+ "IM from buddy [%d], I am in his/her buddy list\n", im_header->sender_uid);
+ _qq_process_recv_normal_im(data + bytes, len - bytes, gc); /* position and rest length */
break;
case QQ_RECV_IM_TO_UNKNOWN:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
- _qq_process_recv_normal_im(data, &cursor, len, gc);
+ "IM from buddy [%d], I am a stranger to him/her\n", im_header->sender_uid);
+ _qq_process_recv_normal_im(data + bytes, len - bytes, gc);
break;
case QQ_RECV_IM_UNKNOWN_QUN_IM:
case QQ_RECV_IM_TEMP_QUN_IM:
case QQ_RECV_IM_QUN_IM:
purple_debug(PURPLE_DEBUG_INFO, "QQ", "IM from group, internal_id [%d]\n", im_header->sender_uid);
/* sender_uid is in fact internal_group_id */
- qq_process_recv_group_im(data, &cursor, len, im_header->sender_uid, gc, im_header->im_type);
+ qq_process_recv_group_im(data + bytes, len - bytes, im_header->sender_uid, gc, im_header->im_type);
break;
case QQ_RECV_IM_ADD_TO_QUN:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
+ "IM from group, added by group internal_id [%d]\n", im_header->sender_uid);
/* sender_uid is in fact internal_group_id
* we need this to create a dummy group and add to blist */
- qq_process_recv_group_im_been_added(data, &cursor, len, im_header->sender_uid, gc);
+ qq_process_recv_group_im_been_added(data + bytes, len - bytes, im_header->sender_uid, gc);
break;
case QQ_RECV_IM_DEL_FROM_QUN:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
+ "IM from group, removed by group internal_ID [%d]\n", im_header->sender_uid);
/* sender_uid is in fact internal_group_id */
- qq_process_recv_group_im_been_removed(data, &cursor, len, im_header->sender_uid, gc);
+ qq_process_recv_group_im_been_removed(data + bytes, len - bytes, im_header->sender_uid, gc);
break;
case QQ_RECV_IM_APPLY_ADD_TO_QUN:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
+ "IM from group, apply to join group internal_ID [%d]\n", im_header->sender_uid);
/* sender_uid is in fact internal_group_id */
- qq_process_recv_group_im_apply_join(data, &cursor, len, im_header->sender_uid, gc);
+ qq_process_recv_group_im_apply_join(data + bytes, len - bytes, im_header->sender_uid, gc);
break;
case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM for group system info, approved by group internal_id [%d]\n",
- im_header->sender_uid);
+ "IM for group system info, approved by group internal_id [%d]\n",
+ im_header->sender_uid);
/* sender_uid is in fact internal_group_id */
- qq_process_recv_group_im_been_approved(data, &cursor, len, im_header->sender_uid, gc);
+ qq_process_recv_group_im_been_approved(data + bytes, len - bytes, im_header->sender_uid, gc);
break;
case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM for group system info, rejected by group internal_id [%d]\n",
- im_header->sender_uid);
+ "IM for group system info, rejected by group internal_id [%d]\n",
+ im_header->sender_uid);
/* sender_uid is in fact internal_group_id */
- qq_process_recv_group_im_been_rejected(data, &cursor, len, im_header->sender_uid, gc);
+ qq_process_recv_group_im_been_rejected(data + bytes, len - bytes, im_header->sender_uid, gc);
break;
case QQ_RECV_IM_SYS_NOTIFICATION:
purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "IM from [%d], should be a system administrator\n", im_header->sender_uid);
- _qq_process_recv_sys_im(data, &cursor, len, gc);
+ "IM from [%d], should be a system administrator\n", im_header->sender_uid);
+ _qq_process_recv_sys_im(data + bytes, len - bytes, gc);
break;
default:
purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "IM from [%d], [0x%02x] %s is not processed\n",
- im_header->sender_uid,
- im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
- }
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt rev im\n");
+ "IM from [%d], [0x%02x] %s is not processed\n",
+ im_header->sender_uid,
+ im_header->im_type, qq_get_recv_im_type_str(im_header->im_type));
}
}
+
diff --git a/libpurple/protocols/qq/keep_alive.c b/libpurple/protocols/qq/keep_alive.c
deleted file mode 100644
index 02c5f8ec95..0000000000
--- a/libpurple/protocols/qq/keep_alive.c
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * @file keep_alive.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- *
- *
- * OICQ encryption algorithm
- * Convert from ASM code provided by PerlOICQ
- *
- * Puzzlebird, Nov-Dec 2002
- */
-
-#include "internal.h"
-
-#include "debug.h"
-#include "server.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_status.h"
-#include "crypt.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "packet_parse.h"
-#include "send_core.h"
-#include "utils.h"
-
-#define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */
-
-/* send keep-alive packet to QQ server (it is a heart-beat) */
-void qq_send_packet_keep_alive(PurpleConnection *gc)
-{
- qq_data *qd;
- guint8 *raw_data, *cursor;
-
- qd = (qq_data *) gc->proto_data;
- raw_data = g_newa(guint8, 4);
- cursor = raw_data;
-
- /* In fact, we can send whatever we like to server
- * with this command, server return the same result including
- * the amount of online QQ users, my ip and port */
- create_packet_dw(raw_data, &cursor, qd->uid);
-
- qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, TRUE, 0, TRUE, raw_data, 4);
-}
-
-/* parse the return of keep-alive packet, it includes some system information */
-void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
- qq_data *qd;
- gint len;
- gchar **segments;
- guint8 *data;
-
- g_return_if_fail(buf != NULL && buf_len != 0);
-
- qd = (qq_data *) gc->proto_data;
- len = buf_len;
- data = g_newa(guint8, len);
-
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- /* the last one is 60, don't know what it is */
- if (NULL == (segments = split_data(data, len, "\x1f", 6)))
- return;
- /* segments[0] and segment[1] are all 0x30 ("0") */
- qd->all_online = strtol(segments[2], NULL, 10);
- if(0 == qd->all_online)
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Keep alive error"));
- g_free(qd->my_ip);
- qd->my_ip = g_strdup(segments[3]);
- qd->my_port = strtol(segments[4], NULL, 10);
- g_strfreev(segments);
- } else
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n");
-
- /* we refresh buddies's online status periodically */
- /* qd->last_get_online is updated when setting get_buddies_online packet */
- if ((time(NULL) - qd->last_get_online) >= QQ_UPDATE_ONLINE_INTERVAL)
- qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
-}
-
-/* refresh all buddies online/offline,
- * after receiving reply for get_buddies_online packet */
-void qq_refresh_all_buddy_status(PurpleConnection *gc)
-{
- time_t now;
- GList *list;
- qq_data *qd;
- qq_buddy *q_bud;
-
- qd = (qq_data *) (gc->proto_data);
- now = time(NULL);
- list = qd->buddies;
-
- while (list != NULL) {
- q_bud = (qq_buddy *) list->data;
- if (q_bud != NULL && now > q_bud->last_refresh + QQ_UPDATE_ONLINE_INTERVAL
- && q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) {
- q_bud->status = QQ_BUDDY_ONLINE_OFFLINE;
- qq_update_buddy_contact(gc, q_bud);
- }
- list = list->next;
- }
-}
-
-/*TODO: maybe this should be qq_update_buddy_status() ?*/
-void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
-{
- gchar *name;
- PurpleBuddy *bud;
- gchar *status_id;
-
- g_return_if_fail(q_bud != NULL);
-
- name = uid_to_purple_name(q_bud->uid);
- bud = purple_find_buddy(gc->account, name);
- g_return_if_fail(bud != NULL);
-
- if (bud != NULL) {
- purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */
- q_bud->last_refresh = time(NULL);
-
- /* purple supports signon and idle time
- * but it is not much use for QQ, I do not use them */
- /* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
- status_id = "available";
- switch(q_bud->status) {
- case QQ_BUDDY_OFFLINE:
- status_id = "offline";
- break;
- case QQ_BUDDY_ONLINE_NORMAL:
- status_id = "available";
- break;
- case QQ_BUDDY_ONLINE_OFFLINE:
- status_id = "offline";
- break;
- case QQ_BUDDY_ONLINE_AWAY:
- status_id = "away";
- break;
- case QQ_BUDDY_ONLINE_INVISIBLE:
- status_id = "invisible";
- break;
- default:
- status_id = "invisible";
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown status: %x\n", q_bud->status);
- break;
- }
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "set buddy %d to %s\n", q_bud->uid, status_id);
- purple_prpl_got_user_status(gc->account, name, status_id, NULL);
-
- if (q_bud->comm_flag & QQ_COMM_FLAG_BIND_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE)
- purple_prpl_got_user_status(gc->account, name, "mobile", NULL);
- else
- purple_prpl_got_user_status_deactive(gc->account, name, "mobile");
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid);
- }
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "qq_update_buddy_contact, client=%04x\n", q_bud->client_version);
- g_free(name);
-}
diff --git a/libpurple/protocols/qq/login_logout.c b/libpurple/protocols/qq/login_logout.c
deleted file mode 100644
index 3d8bfc7389..0000000000
--- a/libpurple/protocols/qq/login_logout.c
+++ /dev/null
@@ -1,504 +0,0 @@
-/**
- * @file login_logout.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "debug.h"
-#include "internal.h"
-#include "server.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_status.h"
-#include "char_conv.h"
-#include "crypt.h"
-#include "group.h"
-#include "header_info.h"
-#include "login_logout.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
-#include "utils.h"
-
-#define QQ_LOGIN_DATA_LENGTH 416
-#define QQ_LOGIN_REPLY_OK_PACKET_LEN 139
-#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN 11
-
-#define QQ_REQUEST_LOGIN_TOKEN_REPLY_OK 0x00
-
-#define QQ_LOGIN_REPLY_OK 0x00
-#define QQ_LOGIN_REPLY_REDIRECT 0x01
-#define QQ_LOGIN_REPLY_PWD_ERROR 0x05
-#define QQ_LOGIN_REPLY_MISC_ERROR 0xff /* defined by myself */
-
-/* for QQ 2003iii 0117, fixed value */
-/* static const guint8 login_23_51[29] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
- 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
- 0x95, 0x67, 0xda, 0x2c, 0x01
-}; */
-
-/* for QQ 2003iii 0304, fixed value */
-/*
-static const guint8 login_23_51[29] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
- 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
- 0x40, 0xb8, 0xac, 0x32, 0x01
-};
-*/
-
-/* for QQ 2005? copy from lumaqq */
-static const gint8 login_23_51[29] = {
- 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, -122,
- -52, 76, 53, 44, -45, 115, 108, 20, -10, -10,
- -81, -61, -6, 51, -92, 1
-};
-
-static const gint8 login_53_68[16] = {
- -115, -117, -6, -20, -43, 82, 23, 74, -122, -7,
- -89, 117, -26, 50, -47, 109
-};
-
-static const gint8 login_100_bytes[100] = {
- 64,
- 11, 4, 2, 0, 1, 0, 0, 0, 0, 0,
- 3, 9, 0, 0, 0, 0, 0, 0, 0, 1,
- -23, 3, 1, 0, 0, 0, 0, 0, 1, -13,
- 3, 0, 0, 0, 0, 0, 0, 1, -19, 3,
- 0, 0, 0, 0, 0, 0, 1, -20, 3, 0,
- 0, 0, 0, 0, 0, 3, 5, 0, 0, 0,
- 0, 0, 0, 0, 3, 7, 0, 0, 0, 0,
- 0, 0, 0, 1, -18, 3, 0, 0, 0, 0,
- 0, 0, 1, -17, 3, 0, 0, 0, 0, 0,
- 0, 1, -21, 3, 0, 0, 0, 0, 0
-};
-
-/* fixed value, not affected by version, or mac address */
-/*
-static const guint8 login_53_68[16] = {
- 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
- 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
-};
-*/
-
-
-typedef struct _qq_login_reply_ok qq_login_reply_ok_packet;
-typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
-
-struct _qq_login_reply_ok {
- guint8 result;
- guint8 *session_key;
- guint32 uid;
- guint8 client_ip[4]; /* those detected by server */
- guint16 client_port;
- guint8 server_ip[4];
- guint16 server_port;
- time_t login_time;
- guint8 unknown1[26];
- guint8 unknown_server1_ip[4];
- guint16 unknown_server1_port;
- guint8 unknown_server2_ip[4];
- guint16 unknown_server2_port;
- guint16 unknown2; /* 0x0001 */
- guint16 unknown3; /* 0x0000 */
- guint8 unknown4[32];
- guint8 unknown5[12];
- guint8 last_client_ip[4];
- time_t last_login_time;
- guint8 unknown6[8];
-};
-
-struct _qq_login_reply_redirect {
- guint8 result;
- guint32 uid;
- guint8 new_server_ip[4];
- guint16 new_server_port;
-};
-
-extern gint /* defined in send_core.c */
- _create_packet_head_seq(guint8 *buf,
- guint8 **cursor, PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq);
-extern gint /* defined in send_core.c */
- _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd);
-
-/* It is fixed to 16 bytes 0x01 for QQ2003,
- * Any value works (or a random 16 bytes string) */
-static guint8 *_gen_login_key(void)
-{
- return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
-}
-
-/* process login reply which says OK */
-static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
-{
- gint bytes;
- guint8 *cursor;
- qq_data *qd;
- qq_login_reply_ok_packet lrop;
-
- qd = (qq_data *) gc->proto_data;
- cursor = data;
- bytes = 0;
-
- /* 000-000: reply code */
- bytes += read_packet_b(data, &cursor, len, &lrop.result);
- /* 001-016: session key */
- lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH);
- cursor += QQ_KEY_LENGTH;
- bytes += QQ_KEY_LENGTH;
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n");
- /* 017-020: login uid */
- bytes += read_packet_dw(data, &cursor, len, &lrop.uid);
- /* 021-024: server detected user public IP */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.client_ip, 4);
- /* 025-026: server detected user port */
- bytes += read_packet_w(data, &cursor, len, &lrop.client_port);
- /* 027-030: server detected itself ip 127.0.0.1 ? */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.server_ip, 4);
- /* 031-032: server listening port */
- bytes += read_packet_w(data, &cursor, len, &lrop.server_port);
- /* 033-036: login time for current session */
- bytes += read_packet_time(data, &cursor, len, &lrop.login_time);
- /* 037-062: 26 bytes, unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown1, 26);
- /* 063-066: unknown server1 ip address */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server1_ip, 4);
- /* 067-068: unknown server1 port */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port);
- /* 069-072: unknown server2 ip address */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server2_ip, 4);
- /* 073-074: unknown server2 port */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port);
- /* 075-076: 2 bytes unknown */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown2);
- /* 077-078: 2 bytes unknown */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown3);
- /* 079-110: 32 bytes unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown4, 32);
- /* 111-122: 12 bytes unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown5, 12);
- /* 123-126: login IP of last session */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.last_client_ip, 4);
- /* 127-130: login time of last session */
- bytes += read_packet_time(data, &cursor, len, &lrop.last_login_time);
- /* 131-138: 8 bytes unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown6, 8);
-
- if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Fail parsing login info, expect %d bytes, read %d bytes\n",
- QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
- } /* but we still go on as login OK */
-
- qd->session_key = lrop.session_key;
- qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key);
- qd->my_ip = gen_ip_str(lrop.client_ip);
- qd->my_port = lrop.client_port;
- qd->login_time = lrop.login_time;
- qd->last_login_time = lrop.last_login_time;
- qd->last_login_ip = gen_ip_str(lrop.last_client_ip);
-
- purple_connection_set_state(gc, PURPLE_CONNECTED);
- qd->logged_in = TRUE; /* must be defined after sev_finish_login */
-
- /* now initiate QQ Qun, do it first as it may take longer to finish */
- qq_group_init(gc);
-
- /* Now goes on updating my icon/nickname, not showing info_window */
- qd->modifying_face = FALSE;
- qq_send_packet_get_info(gc, qd->uid, FALSE);
- /* grab my level */
- qq_send_packet_get_level(gc, qd->uid);
-
- qq_send_packet_change_status(gc);
-
- /* refresh buddies */
- qq_send_packet_get_buddies_list(gc, QQ_FRIENDS_LIST_POSITION_START);
- /* refresh groups */
- qq_send_packet_get_all_list_with_group(gc, QQ_FRIENDS_LIST_POSITION_START);
-
- return QQ_LOGIN_REPLY_OK;
-}
-
-/* process login reply packet which includes redirected new server address */
-static gint _qq_process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
-{
- gint bytes, ret;
- guint8 *cursor;
- gchar *new_server_str;
- qq_data *qd;
- qq_login_reply_redirect_packet lrrp;
-
- qd = (qq_data *) gc->proto_data;
- cursor = data;
- bytes = 0;
- /* 000-000: reply code */
- bytes += read_packet_b(data, &cursor, len, &lrrp.result);
- /* 001-004: login uid */
- bytes += read_packet_dw(data, &cursor, len, &lrrp.uid);
- /* 005-008: redirected new server IP */
- bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4);
- /* 009-010: redirected new server port */
- bytes += read_packet_w(data, &cursor, len, &lrrp.new_server_port);
-
- if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
- QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
- ret = QQ_LOGIN_REPLY_MISC_ERROR;
- } else { /* start new connection */
- new_server_str = gen_ip_str(lrrp.new_server_ip);
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Redirected to new server: %s:%d\n", new_server_str, lrrp.new_server_port);
- qq_connect(gc->account, new_server_str, lrrp.new_server_port, qd->use_tcp, TRUE);
- g_free(new_server_str);
- ret = QQ_LOGIN_REPLY_REDIRECT;
- }
-
- return ret;
-}
-
-/* process login reply which says wrong password */
-static gint _qq_process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len)
-{
- gchar *server_reply, *server_reply_utf8;
- server_reply = g_new0(gchar, len);
- g_memmove(server_reply, data + 1, len - 1);
- server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8);
- g_free(server_reply);
- g_free(server_reply_utf8);
-
- return QQ_LOGIN_REPLY_PWD_ERROR;
-}
-
-/* request before login */
-void qq_send_packet_request_login_token(PurpleConnection *gc)
-{
- qq_data *qd;
- guint8 *buf, *cursor;
- guint16 seq_ret;
- gint bytes;
-
- qd = (qq_data *) gc->proto_data;
- buf = g_newa(guint8, MAX_PACKET_SIZE);
-
- cursor = buf;
- bytes = 0;
- bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_REQUEST_LOGIN_TOKEN, TRUE, &seq_ret);
- bytes += create_packet_dw(buf, &cursor, qd->uid);
- bytes += create_packet_b(buf, &cursor, 0);
- bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
-
- if (bytes == (cursor - buf)) /* packet creation OK */
- _qq_send_packet(gc, buf, bytes, QQ_CMD_REQUEST_LOGIN_TOKEN);
- else
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create request login token packet\n");
-}
-
-/* send login packet to QQ server */
-static void qq_send_packet_login(PurpleConnection *gc, guint8 token_length, guint8 *token)
-{
- qq_data *qd;
- guint8 *buf, *cursor, *raw_data, *encrypted_data;
- guint16 seq_ret;
- gint encrypted_len, bytes;
- gint pos;
-
- qd = (qq_data *) gc->proto_data;
- buf = g_newa(guint8, MAX_PACKET_SIZE);
- raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
- encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */
- qd->inikey = _gen_login_key();
-
- /* now generate the encrypted data
- * 000-015 use pwkey as key to encrypt empty string */
- qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len);
- /* 016-016 */
- raw_data[16] = 0x00;
- /* 017-020, used to be IP, now zero */
- *((guint32 *) (raw_data + 17)) = 0x00000000;
- /* 021-022, used to be port, now zero */
- *((guint16 *) (raw_data + 21)) = 0x0000;
- /* 023-051, fixed value, unknown */
- g_memmove(raw_data + 23, login_23_51, 29);
- /* 052-052, login mode */
- raw_data[52] = qd->login_mode;
- /* 053-068, fixed value, maybe related to per machine */
- g_memmove(raw_data + 53, login_53_68, 16);
-
- /* 069, login token length */
- raw_data[69] = token_length;
- pos = 70;
- /* 070-093, login token, normally 24 bytes */
- g_memmove(raw_data + pos, token, token_length);
- pos += token_length;
- /* 100 bytes unknown */
- g_memmove(raw_data + pos, login_100_bytes, 100);
- pos += 100;
- /* all zero left */
- memset(raw_data+pos, 0, QQ_LOGIN_DATA_LENGTH - pos);
-
- qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len);
-
- cursor = buf;
- bytes = 0;
- bytes += _create_packet_head_seq(buf, &cursor, gc, QQ_CMD_LOGIN, TRUE, &seq_ret);
- bytes += create_packet_dw(buf, &cursor, qd->uid);
- bytes += create_packet_data(buf, &cursor, qd->inikey, QQ_KEY_LENGTH);
- bytes += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
- bytes += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
-
- if (bytes == (cursor - buf)) /* packet creation OK */
- _qq_send_packet(gc, buf, bytes, QQ_CMD_LOGIN);
- else
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail create login packet\n");
-}
-
-void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
- qq_data *qd;
- gchar *hex_dump;
-
- g_return_if_fail(buf != NULL && buf_len != 0);
-
- qd = (qq_data *) gc->proto_data;
-
- if (buf[0] == QQ_REQUEST_LOGIN_TOKEN_REPLY_OK) {
- if (buf[1] != buf_len-2) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Malformed login token reply packet. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2);
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Attempting to proceed with the actual packet length.\n");
- }
- hex_dump = hex_dump_to_str(buf+2, buf_len-2);
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "<<< got a token with %d bytes -> [default] decrypt and dump\n%s", buf_len-2, hex_dump);
- qq_send_packet_login(gc, buf_len-2, buf+2);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]);
- hex_dump = hex_dump_to_str(buf, buf_len);
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- ">>> %d bytes -> [default] decrypt and dump\n%s",
- buf_len, hex_dump);
- try_dump_as_gbk(buf, buf_len);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error requesting login token"));
- }
- g_free(hex_dump);
-}
-
-/* send logout packets to QQ server */
-void qq_send_packet_logout(PurpleConnection *gc)
-{
- gint i;
- qq_data *qd;
-
- qd = (qq_data *) gc->proto_data;
- for (i = 0; i < 4; i++)
- qq_send_cmd(gc, QQ_CMD_LOGOUT, FALSE, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
-
- qd->logged_in = FALSE; /* update login status AFTER sending logout packets */
-}
-
-/* process the login reply packet */
-void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
- gint len, ret, bytes;
- guint8 *data;
- qq_data *qd;
- gchar *hex_dump;
-
- g_return_if_fail(buf != NULL && buf_len != 0);
-
- qd = (qq_data *) gc->proto_data;
- len = buf_len;
- data = g_newa(guint8, len);
-
- if (qq_decrypt(buf, buf_len, qd->pwkey, data, &len)) {
- /* should be able to decrypt with pwkey */
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Decrypt login reply packet with pwkey, %d bytes\n", len);
- if (data[0] == QQ_LOGIN_REPLY_OK) {
- ret = _qq_process_login_ok(gc, data, len);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown login reply code : %d\n", data[0]);
- ret = QQ_LOGIN_REPLY_MISC_ERROR;
- }
- } else { /* decrypt with pwkey error */
- len = buf_len; /* reset len, decrypt will fail if len is too short */
- if (qq_decrypt(buf, buf_len, qd->inikey, data, &len)) {
- /* decrypt ok with inipwd, it might be password error */
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Decrypt login reply packet with inikey, %d bytes\n", len);
- bytes = 0;
- switch (data[0]) {
- case QQ_LOGIN_REPLY_REDIRECT:
- ret = _qq_process_login_redirect(gc, data, len);
- break;
- case QQ_LOGIN_REPLY_PWD_ERROR:
- ret = _qq_process_login_wrong_pwd(gc, data, len);
- break;
- default:
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
- hex_dump = hex_dump_to_str(data, len);
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- ">>> %d bytes -> [default] decrypt and dump\n%s",
- buf_len, hex_dump);
- g_free(hex_dump);
- try_dump_as_gbk(data, len);
-
- ret = QQ_LOGIN_REPLY_MISC_ERROR;
- }
- } else { /* no idea how to decrypt */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "No idea how to decrypt login reply\n");
- ret = QQ_LOGIN_REPLY_MISC_ERROR;
- }
- }
-
- switch (ret) {
- case QQ_LOGIN_REPLY_PWD_ERROR:
- if (!purple_account_get_remember_password(gc->account))
- purple_account_set_password(gc->account, NULL);
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password."));
- break;
- case QQ_LOGIN_REPLY_MISC_ERROR:
- if (purple_debug_is_enabled())
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login. Check debug log."));
- else
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login"));
- break;
- case QQ_LOGIN_REPLY_OK:
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login repliess OK; everything is fine\n");
- break;
- case QQ_LOGIN_REPLY_REDIRECT:
- /* the redirect has been done in _qq_process_login_reply */
- break;
- default:{;
- }
- }
-}
diff --git a/libpurple/protocols/qq/packet_parse.c b/libpurple/protocols/qq/packet_parse.c
index a26192731b..8698a78dc8 100644
--- a/libpurple/protocols/qq/packet_parse.c
+++ b/libpurple/protocols/qq/packet_parse.c
@@ -25,119 +25,155 @@
#include <string.h>
#include "packet_parse.h"
+#include "debug.h"
+
+/*------------------------------------------------PUT------------------------------------------------*/
+
+/* note:
+ * 1, in these functions, 'b' stands for byte, 'w' stands for word, 'dw' stands for double word.
+ * 2, we use '*cursor' and 'buf' as two addresses to calculate the length.
+ * 3, change '0' to '1', if want to get more info about the packet parsing. */
+
+#if 0
+#define PARSER_DEBUG
+#endif
/* read one byte from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b)
+gint qq_get8(guint8 *b, guint8 *buf)
{
- if (*cursor <= buf + buflen - sizeof(*b)) {
- *b = **(guint8 **) cursor;
- *cursor += sizeof(*b);
- return sizeof(*b);
- } else {
- return -1;
- }
+ guint8 b_dest;
+ memcpy(&b_dest, buf, sizeof(b_dest));
+ *b = b_dest;
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get8] b_dest 0x%2x, *b 0x%02x\n", b_dest, *b);
+#endif
+ return sizeof(b_dest);
}
+
/* read two bytes as "guint16" from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w)
+gint qq_get16(guint16 *w, guint8 *buf)
{
- if (*cursor <= buf + buflen - sizeof(*w)) {
- *w = g_ntohs(**(guint16 **) cursor);
- *cursor += sizeof(*w);
- return sizeof(*w);
- } else {
- return -1;
- }
+ guint16 w_dest;
+ memcpy(&w_dest, buf, sizeof(w_dest));
+ *w = g_ntohs(w_dest);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get16] w_dest 0x%04x, *w 0x%04x\n", w_dest, *w);
+#endif
+ return sizeof(w_dest);
}
/* read four bytes as "guint32" from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw)
+gint qq_get32(guint32 *dw, guint8 *buf)
{
- if (*cursor <= buf + buflen - sizeof(*dw)) {
- *dw = g_ntohl(**(guint32 **) cursor);
- *cursor += sizeof(*dw);
- return sizeof(*dw);
- } else {
- return -1;
- }
+ guint32 dw_dest;
+ memcpy(&dw_dest, buf, sizeof(dw_dest));
+ *dw = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][get32] dw_dest 0x%08x, *dw 0x%08x\n", dw_dest, *dw);
+#endif
+ return sizeof(dw_dest);
}
-/* read four bytes as "time_t" from buf,
- * return the number of bytes read if succeeds, otherwise return -1
- * This function is a wrapper around read_packet_dw() to avoid casting. */
-gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t)
+gint qq_getIP(struct in_addr *ip, guint8 *buf)
{
- guint32 time;
- gint ret = read_packet_dw(buf, cursor, buflen, &time);
- if (ret != -1 ) {
- *t = time;
- }
- return ret;
+ memcpy(ip, buf, sizeof(struct in_addr));
+ return sizeof(struct in_addr);
}
/* read datalen bytes from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
-gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen) {
- if (*cursor <= buf + buflen - datalen) {
- g_memmove(data, *cursor, datalen);
- *cursor += datalen;
- return datalen;
- } else {
- return -1;
- }
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf)
+{
+ memcpy(data, buf, datalen);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getdata] buf %p\n", (void *)buf);
+#endif
+ return datalen;
+}
+
+
+/* read four bytes as "time_t" from buf,
+ * return the number of bytes read if succeeds, otherwise return -1
+ * This function is a wrapper around read_packet_dw() to avoid casting. */
+gint qq_getime(time_t *t, guint8 *buf)
+{
+ guint32 dw_dest;
+ memcpy(&dw_dest, buf, sizeof(dw_dest));
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest);
+#endif
+ dw_dest = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest);
+#endif
+ memcpy(t, &dw_dest, sizeof(dw_dest));
+ return sizeof(dw_dest);
}
+/*------------------------------------------------PUT------------------------------------------------*/
/* pack one byte into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b)
+gint qq_put8(guint8 *buf, guint8 b)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint8)) {
- **(guint8 **) cursor = b;
- *cursor += sizeof(guint8);
- return sizeof(guint8);
- } else {
- return -1;
- }
+ memcpy(buf, &b, sizeof(b));
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b);
+#endif
+ return sizeof(b);
}
+
/* pack two bytes as "guint16" into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w)
+gint qq_put16(guint8 *buf, guint16 w)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint16)) {
- **(guint16 **) cursor = g_htons(w);
- *cursor += sizeof(guint16);
- return sizeof(guint16);
- } else {
- return -1;
- }
+ guint16 w_porter;
+ w_porter = g_htons(w);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter);
+#endif
+ memcpy(buf, &w_porter, sizeof(w_porter));
+ return sizeof(w_porter);
}
+
/* pack four bytes as "guint32" into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw)
+gint qq_put32(guint8 *buf, guint32 dw)
+{
+ guint32 dw_porter;
+ dw_porter = g_htonl(dw);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
+#endif
+ memcpy(buf, &dw_porter, sizeof(dw_porter));
+ return sizeof(dw_porter);
+}
+
+gint qq_putIP(guint8* buf, struct in_addr *ip)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint32)) {
- **(guint32 **) cursor = g_htonl(dw);
- *cursor += sizeof(guint32);
- return sizeof(guint32);
- } else {
- return -1;
- }
+ memcpy(buf, ip, sizeof(struct in_addr));
+ return sizeof(struct in_addr);
}
/* pack datalen bytes into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen)
+gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - datalen) {
- g_memmove(*cursor, data, datalen);
- *cursor += datalen;
- return datalen;
- } else {
- return -1;
- }
+ memcpy(buf, data, datalen);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][putdata] buf %p\n", (void *)buf);
+#endif
+ return datalen;
}
diff --git a/libpurple/protocols/qq/packet_parse.h b/libpurple/protocols/qq/packet_parse.h
index 65cee7719c..939b808e22 100644
--- a/libpurple/protocols/qq/packet_parse.h
+++ b/libpurple/protocols/qq/packet_parse.h
@@ -37,14 +37,32 @@
*/
#define MAX_PACKET_SIZE 65535
+#include <netinet/in.h>
+
+gint qq_get8(guint8 *b, guint8 *buf);
+gint qq_get16(guint16 *w, guint8 *buf);
+gint qq_get32(guint32 *dw, guint8 *buf);
+gint qq_getIP(struct in_addr *ip, guint8 *buf);
+gint qq_getime(time_t *t, guint8 *buf);
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf);
+
+gint qq_put8(guint8 *buf, guint8 b);
+gint qq_put16(guint8 *buf, guint16 w);
+gint qq_put32(guint8 *buf, guint32 dw);
+gint qq_putIP(guint8* buf, struct in_addr *ip);
+gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen);
+
+/*
gint read_packet_b(guint8 *buf, guint8 **cursor, gint buflen, guint8 *b);
gint read_packet_w(guint8 *buf, guint8 **cursor, gint buflen, guint16 *w);
gint read_packet_dw(guint8 *buf, guint8 **cursor, gint buflen, guint32 *dw);
gint read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t);
gint read_packet_data(guint8 *buf, guint8 **cursor, gint buflen, guint8 *data, gint datalen);
+
gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b);
gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w);
gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw);
gint create_packet_data(guint8 *buf, guint8 **cursor, guint8 *data, gint datalen);
+*/
#endif
diff --git a/libpurple/protocols/qq/qq.c b/libpurple/protocols/qq/qq.c
index 6fa761aca0..919e957999 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,52 +51,98 @@
#include "group_opt.h"
#include "header_info.h"
#include "im.h"
-#include "keep_alive.h"
-#include "login_logout.h"
+#include "qq_process.h"
+#include "qq_base.h"
#include "packet_parse.h"
#include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "send_file.h"
#include "utils.h"
#include "version.h"
#define OPENQ_AUTHOR "Puzzlebird"
#define OPENQ_WEBSITE "http://openq.sourceforge.net"
-#define QQ_TCP_QUERY_PORT "8000"
-#define QQ_UDP_PORT "8000"
-
-const gchar *udp_server_list[] = {
- "sz.tencent.com",
- "sz2.tencent.com",
- "sz3.tencent.com",
- "sz4.tencent.com",
- "sz5.tencent.com",
- "sz6.tencent.com",
- "sz7.tencent.com",
- "sz8.tencent.com",
- "sz9.tencent.com"
-};
-const gint udp_server_amount = (sizeof(udp_server_list) / sizeof(udp_server_list[0]));
+#define QQ_TCP_PORT 8000
+#define QQ_UDP_PORT 8000
-const gchar *tcp_server_list[] = {
- "tcpconn.tencent.com",
- "tcpconn2.tencent.com",
- "tcpconn3.tencent.com",
- "tcpconn4.tencent.com",
- "tcpconn5.tencent.com",
- "tcpconn6.tencent.com"
-};
-const gint tcp_server_amount = (sizeof(tcp_server_list) / sizeof(tcp_server_list[0]));
+static void server_list_create(PurpleAccount *account) {
+ PurpleConnection *gc;
+ qq_data *qd;
+ const gchar *user_server;
+ int port;
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create server list\n");
+ gc = purple_account_get_connection(account);
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = gc->proto_data;
+
+ qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
+ port = purple_account_get_int(account, "port", 0);
+ if (port == 0) {
+ if (qd->use_tcp) {
+ port = QQ_TCP_PORT;
+ } else {
+ port = QQ_UDP_PORT;
+ }
+ }
+ qd->user_port = port;
+
+ g_return_if_fail(qd->user_server == NULL);
+ user_server = purple_account_get_string(account, "server", NULL);
+ if (user_server != NULL && strlen(user_server) > 0) {
+ qd->user_server = g_strdup(user_server);
+ }
+
+ if (qd->user_server != NULL) {
+ qd->servers = g_list_append(qd->servers, qd->user_server);
+ return;
+ }
+ if (qd->use_tcp) {
+ qd->servers = g_list_append(qd->servers, "tcpconn.tencent.com");
+ qd->servers = g_list_append(qd->servers, "tcpconn2.tencent.com");
+ qd->servers = g_list_append(qd->servers, "tcpconn3.tencent.com");
+ qd->servers = g_list_append(qd->servers, "tcpconn4.tencent.com");
+ qd->servers = g_list_append(qd->servers, "tcpconn5.tencent.com");
+ qd->servers = g_list_append(qd->servers, "tcpconn6.tencent.com");
+ return;
+ }
+
+ qd->servers = g_list_append(qd->servers, "sz.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz2.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz3.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz4.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz5.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz6.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz7.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz8.tencent.com");
+ qd->servers = g_list_append(qd->servers, "sz9.tencent.com");
+}
-static void _qq_login(PurpleAccount *account)
+static void server_list_remove_all(qq_data *qd) {
+ g_return_if_fail(qd != NULL);
+
+ if (qd->real_hostname) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+ g_free(qd->real_hostname);
+ qd->real_hostname = NULL;
+ }
+
+ if (qd->user_server != NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free user_server\n");
+ g_free(qd->user_server);
+ qd->user_server = NULL;
+ }
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free server list\n");
+ g_list_free(qd->servers);
+}
+
+static void qq_login(PurpleAccount *account)
{
- const gchar *qq_server, *qq_port;
- qq_data *qd;
PurpleConnection *gc;
+ qq_data *qd;
PurplePresence *presence;
- gboolean use_tcp;
g_return_if_fail(account != NULL);
@@ -109,13 +155,7 @@ static void _qq_login(PurpleAccount *account)
qd->gc = gc;
gc->proto_data = qd;
- qq_server = purple_account_get_string(account, "server", NULL);
- qq_port = purple_account_get_string(account, "port", NULL);
- use_tcp = purple_account_get_bool(account, "use_tcp", FALSE);
presence = purple_account_get_presence(account);
-
- qd->use_tcp = use_tcp;
-
if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
qd->login_mode = QQ_LOGIN_MODE_HIDDEN;
} else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
@@ -125,26 +165,28 @@ static void _qq_login(PurpleAccount *account)
qd->login_mode = QQ_LOGIN_MODE_NORMAL;
}
- if (qq_server == NULL || strlen(qq_server) == 0)
- qq_server = use_tcp ?
- tcp_server_list[random() % tcp_server_amount] :
- udp_server_list[random() % udp_server_amount];
-
- if (qq_port == NULL || strtol(qq_port, NULL, 10) == 0)
- qq_port = use_tcp ? QQ_TCP_QUERY_PORT : QQ_UDP_PORT;
+ server_list_create(account);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Server list has %d\n", g_list_length(qd->servers));
- purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS);
-
- if (qq_connect(account, qq_server, strtol(qq_port, NULL, 10), use_tcp, FALSE) < 0)
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to connect."));
+ qq_connect(account);
}
-/* directly goes for qq_disconnect */
-static void _qq_close(PurpleConnection *gc)
+/* clean up the given QQ connection and free all resources */
+static void qq_close(PurpleConnection *gc)
{
- g_return_if_fail(gc != NULL);
+ qq_data *qd;
+
+ g_return_if_fail(gc != NULL && gc->proto_data);
+ qd = gc->proto_data;
+
qq_disconnect(gc);
+
+ server_list_remove_all(qd);
+
+ g_free(qd);
+
+ gc->proto_data = NULL;
}
/* returns the icon name for a buddy or protocol */
@@ -195,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);
+
+ 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);
+ }
- tmp = g_strdup_printf("%d", q_bud->age);
- purple_notify_user_info_add_pair(user_info, _("Age"), 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);
+ }
- 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);
- }
+ 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->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);
- */
+ 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;
}
@@ -351,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);
@@ -437,14 +512,15 @@ 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");
+ g_string_append_printf(info, _("<b>Server</b>: %s: %d<br>\n"), qd->server_name, qd->real_port);
g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
- g_string_append_printf(info, _("<b>Server IP</b>: %s: %d<br>\n"), qd->server_ip, qd->server_port);
- g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip);
+ g_string_append_printf(info, _("<b>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port);
+ g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), inet_ntoa(qd->my_ip));
g_string_append(info, "<hr>\n");
g_string_append(info, "<i>Information below may not be accurate</i><br>\n");
@@ -593,30 +669,6 @@ 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)
@@ -651,8 +703,8 @@ static PurplePluginProtocolInfo prpl_info = {
_qq_buddy_menu, /* blist_node_menu */
qq_chat_info, /* chat_info */
qq_chat_info_defaults, /* chat_info_defaults */
- _qq_login, /* login */
- _qq_close, /* close */
+ qq_login, /* open */
+ qq_close, /* close */
_qq_send_im, /* send_im */
NULL, /* set_info */
NULL, /* send_typing */
@@ -675,8 +727,8 @@ 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 */
@@ -695,7 +747,7 @@ 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 */
@@ -706,7 +758,6 @@ static PurplePluginProtocolInfo prpl_info = {
NULL,
NULL,
NULL,
- sizeof(PurplePluginProtocolInfo), /* struct_size */
NULL
};
@@ -751,13 +802,22 @@ static void init_plugin(PurplePlugin *plugin)
{
PurpleAccountOption *option;
- option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", FALSE);
+ option = purple_account_option_string_new(_("Server"), "server", NULL);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_string_new(_("Server"), "server", NULL);
+ option = purple_account_option_int_new(_("Port"), "port", 0);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ option = purple_account_option_int_new(_("resend interval(s)"), "resend_interval", 10);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ option = purple_account_option_int_new(_("Keep alive interval(s)"), "keep_alive_interval", 60);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
- option = purple_account_option_string_new(_("Port"), "port", NULL);
+ option = purple_account_option_int_new(_("Update interval(s)"), "update_interval", 300);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
my_protocol = plugin;
diff --git a/libpurple/protocols/qq/qq.h b/libpurple/protocols/qq/qq.h
index bc34455371..0eeea31e41 100644
--- a/libpurple/protocols/qq/qq.h
+++ b/libpurple/protocols/qq/qq.h
@@ -28,10 +28,12 @@
#include <glib.h>
#include "internal.h"
#include "ft.h"
+#include "circbuffer.h"
+#include "dnsquery.h"
+#include "dnssrv.h"
#include "proxy.h"
#include "roomlist.h"
-#define QQ_FACES 100
#define QQ_KEY_LENGTH 16
#define QQ_DEBUG 1 /* whether we are doing DEBUG */
@@ -42,6 +44,13 @@ 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;
@@ -49,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;
@@ -66,42 +75,65 @@ struct _qq_buddy {
};
struct _qq_data {
- gint fd; /* socket file handler */
+ PurpleConnection *gc;
+
+ /* common network resource */
+ GList *servers;
+ gchar *user_server;
+ gint user_port;
+ gboolean use_tcp; /* network in tcp or udp */
+
+ gchar *server_name;
+ gboolean is_redirect;
+ gchar *real_hostname; /* from real connction */
+ guint16 real_port;
+ guint reconnect_timeout;
+ gint reconnect_times;
+
+ PurpleProxyConnectData *connect_data;
+ gint fd; /* socket file handler */
+ gint tx_handler; /* socket can_write handle, use in udp connecting and tcp send out */
+
+ qq_interval itv_config;
+ qq_interval itv_count;
+ guint network_timeout;
+
+ GList *transactions; /* check ack packet and resend */
+
+ /* tcp related */
+ PurpleCircBuffer *tcp_txbuf;
+ guint8 *tcp_rxqueue;
+ int tcp_rxlen;
+
+ /* udp related */
+ PurpleDnsQueryData *udp_query_data;
+
guint32 uid; /* QQ number */
- guint8 *inikey; /* initial key to encrypt login packet */
- guint8 *pwkey; /* password in md5 (or md5' md5) */
- guint8 *session_key; /* later use this as key in this session */
- guint8 *session_md5; /* concatenate my uid with session_key and md5 it */
+ guint8 *token; /* get from server*/
+ int token_len;
+ guint8 inikey[QQ_KEY_LENGTH]; /* initial key to encrypt login packet */
+ guint8 password_twice_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */
+ guint8 session_key[QQ_KEY_LENGTH]; /* later use this as key in this session */
+ guint8 session_md5[QQ_KEY_LENGTH]; /* concatenate my uid with session_key and md5 it */
guint16 send_seq; /* send sequence number */
guint8 login_mode; /* online of invisible */
gboolean logged_in; /* used by qq-add_buddy */
- gboolean use_tcp; /* network in tcp or udp */
-
- PurpleProxyType proxy_type;
- PurpleConnection *gc;
PurpleXfer *xfer; /* file transfer handler */
- struct sockaddr_in dest_sin;
- /* from real connction */
- gchar *server_ip;
- guint16 server_port;
/* get from login reply packet */
time_t login_time;
time_t last_login_time;
gchar *last_login_ip;
/* get from keep_alive packet */
- gchar *my_ip; /* my ip address detected by server */
+ struct in_addr my_ip; /* my ip address detected by server */
guint16 my_port; /* my port detected by server */
guint16 my_icon; /* my icon index */
guint16 my_level; /* my level */
- guint32 all_online; /* the number of online QQ users */
+ guint32 total_online; /* the number of online QQ users */
time_t last_get_online; /* last time send get_friends_online packet */
- guint8 window[1 << 13]; /* check up for duplicated packet */
- gint sendqueue_timeout;
-
PurpleRoomlist *roomlist;
gint channel; /* the id for opened chat conversation */
@@ -112,16 +144,12 @@ struct _qq_data {
GList *buddies;
GList *contact_info_window;
GList *group_info_window;
- GList *sendqueue;
GList *info_query;
GList *add_buddy_request;
- GQueue *before_login_packets;
/* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */
gboolean modifying_info;
gboolean modifying_face;
};
-void qq_function_not_implemented(PurpleConnection *gc);
-
#endif
diff --git a/libpurple/protocols/qq/qq_base.c b/libpurple/protocols/qq/qq_base.c
new file mode 100644
index 0000000000..e4f00c793d
--- /dev/null
+++ b/libpurple/protocols/qq/qq_base.c
@@ -0,0 +1,523 @@
+/**
+ * @file qq_base.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "debug.h"
+#include "internal.h"
+#include "server.h"
+#include "cipher.h"
+
+#include "buddy_info.h"
+#include "buddy_list.h"
+#include "char_conv.h"
+#include "crypt.h"
+#include "group.h"
+#include "header_info.h"
+#include "qq_base.h"
+#include "packet_parse.h"
+#include "qq.h"
+#include "qq_network.h"
+#include "utils.h"
+
+#define QQ_LOGIN_DATA_LENGTH 416
+#define QQ_LOGIN_REPLY_OK_PACKET_LEN 139
+#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN 11
+
+/* for QQ 2003iii 0117, fixed value */
+/* static const guint8 login_23_51[29] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
+ 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
+ 0x95, 0x67, 0xda, 0x2c, 0x01
+}; */
+
+/* for QQ 2003iii 0304, fixed value */
+/*
+static const guint8 login_23_51[29] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
+ 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
+ 0x40, 0xb8, 0xac, 0x32, 0x01
+};
+*/
+
+/* for QQ 2005? copy from lumaqq */
+/* FIXME: change to guint8 */
+static const guint8 login_23_51[29] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
+ 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
+ 0xc3, 0xfa, 0x33, 0xa4, 0x01
+};
+
+static const guint8 login_53_68[16] = {
+ 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
+ 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
+};
+
+static const guint8 login_100_bytes[100] = {
+ 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+
+/* fixed value, not affected by version, or mac address */
+/*
+static const guint8 login_53_68[16] = {
+ 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
+ 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
+};
+*/
+
+
+typedef struct _qq_login_reply_ok qq_login_reply_ok_packet;
+typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
+
+struct _qq_login_reply_ok {
+ guint8 result;
+ guint8 session_key[QQ_KEY_LENGTH];
+ guint32 uid;
+ struct in_addr client_ip; /* those detected by server */
+ guint16 client_port;
+ struct in_addr server_ip;
+ guint16 server_port;
+ time_t login_time;
+ guint8 unknown1[26];
+ struct in_addr unknown_server1_ip;
+ guint16 unknown_server1_port;
+ struct in_addr unknown_server2_ip;
+ guint16 unknown_server2_port;
+ guint16 unknown2; /* 0x0001 */
+ guint16 unknown3; /* 0x0000 */
+ guint8 unknown4[32];
+ guint8 unknown5[12];
+ struct in_addr last_client_ip;
+ time_t last_login_time;
+ guint8 unknown6[8];
+};
+
+struct _qq_login_reply_redirect {
+ guint8 result;
+ guint32 uid;
+ struct in_addr new_server_ip;
+ guint16 new_server_port;
+};
+
+/* generate a md5 key using uid and session_key */
+static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
+{
+ guint8 src[QQ_KEY_LENGTH + QQ_KEY_LENGTH];
+ gint bytes = 0;
+
+ bytes += qq_put32(src + bytes, uid);
+ bytes += qq_putdata(src + bytes, session_key, QQ_KEY_LENGTH);
+
+ qq_get_md5(session_md5, QQ_KEY_LENGTH, src, bytes);
+}
+
+/* process login reply which says OK */
+static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
+{
+ gint bytes;
+ qq_data *qd;
+ qq_login_reply_ok_packet lrop;
+
+ qd = (qq_data *) gc->proto_data;
+ /* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
+ bytes = 0;
+
+ /* 000-000: reply code */
+ bytes += qq_get8(&lrop.result, data + bytes);
+ /* 001-016: session key */
+ bytes += qq_getdata(lrop.session_key, sizeof(lrop.session_key), data + bytes);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Get session_key done\n");
+ /* 017-020: login uid */
+ bytes += qq_get32(&lrop.uid, data + bytes);
+ /* 021-024: server detected user public IP */
+ bytes += qq_getIP(&lrop.client_ip, data + bytes);
+ /* 025-026: server detected user port */
+ bytes += qq_get16(&lrop.client_port, data + bytes);
+ /* 027-030: server detected itself ip 127.0.0.1 ? */
+ bytes += qq_getIP(&lrop.server_ip, data + bytes);
+ /* 031-032: server listening port */
+ bytes += qq_get16(&lrop.server_port, data + bytes);
+ /* 033-036: login time for current session */
+ bytes += qq_getime(&lrop.login_time, data + bytes);
+ /* 037-062: 26 bytes, unknown */
+ bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
+ /* 063-066: unknown server1 ip address */
+ bytes += qq_getIP(&lrop.unknown_server1_ip, data + bytes);
+ /* 067-068: unknown server1 port */
+ bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
+ /* 069-072: unknown server2 ip address */
+ bytes += qq_getIP(&lrop.unknown_server2_ip, data + bytes);
+ /* 073-074: unknown server2 port */
+ bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
+ /* 075-076: 2 bytes unknown */
+ bytes += qq_get16(&lrop.unknown2, data + bytes);
+ /* 077-078: 2 bytes unknown */
+ bytes += qq_get16(&lrop.unknown3, data + bytes);
+ /* 079-110: 32 bytes unknown */
+ bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
+ /* 111-122: 12 bytes unknown */
+ bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
+ /* 123-126: login IP of last session */
+ bytes += qq_getIP(&lrop.last_client_ip, data + bytes);
+ /* 127-130: login time of last session */
+ bytes += qq_getime(&lrop.last_login_time, data + bytes);
+ /* 131-138: 8 bytes unknown */
+ bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
+
+ if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "Fail parsing login info, expect %d bytes, read %d bytes\n",
+ QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
+ } /* but we still go on as login OK */
+
+ memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key));
+ get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+
+ qd->my_ip.s_addr = lrop.client_ip.s_addr;
+
+ qd->my_port = lrop.client_port;
+ qd->login_time = lrop.login_time;
+ qd->last_login_time = lrop.last_login_time;
+ qd->last_login_ip = g_strdup( inet_ntoa(lrop.last_client_ip) );
+
+ return QQ_LOGIN_REPLY_OK;
+}
+
+/* process login reply packet which includes redirected new server address */
+static gint8 process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
+{
+ qq_data *qd;
+ gint bytes;
+ qq_login_reply_redirect_packet lrrp;
+
+ qd = (qq_data *) gc->proto_data;
+ bytes = 0;
+ /* 000-000: reply code */
+ bytes += qq_get8(&lrrp.result, data + bytes);
+ /* 001-004: login uid */
+ bytes += qq_get32(&lrrp.uid, data + bytes);
+ /* 005-008: redirected new server IP */
+ bytes += qq_getIP(&lrrp.new_server_ip, data + bytes);
+ /* 009-010: redirected new server port */
+ bytes += qq_get16(&lrrp.new_server_port, data + bytes);
+
+ if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
+ QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
+ return QQ_LOGIN_REPLY_ERR_MISC;
+ }
+
+ /* redirect to new server, do not disconnect or connect here
+ * those connect should be called at packet_process */
+ if (qd->real_hostname) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+ g_free(qd->real_hostname);
+ qd->real_hostname = NULL;
+ }
+ qd->real_hostname = g_strdup( inet_ntoa(lrrp.new_server_ip) );
+ qd->real_port = lrrp.new_server_port;
+
+ return QQ_LOGIN_REPLY_REDIRECT;
+}
+
+/* process login reply which says wrong password */
+static gint8 process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint len)
+{
+ gchar *server_reply, *server_reply_utf8;
+ server_reply = g_new0(gchar, len);
+ g_memmove(server_reply, data + 1, len - 1);
+ server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Wrong password, server msg in UTF8: %s\n", server_reply_utf8);
+ g_free(server_reply);
+ g_free(server_reply_utf8);
+
+ return QQ_LOGIN_REPLY_ERR_PWD;
+}
+
+/* request before login */
+void qq_send_packet_token(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 buf[16] = {0};
+ gint bytes = 0;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ bytes += qq_put8(buf + bytes, 0);
+
+ qd->send_seq++;
+ qq_send_data(qd, QQ_CMD_TOKEN, qd->send_seq, TRUE, buf, bytes);
+}
+
+/* send login packet to QQ server */
+void qq_send_packet_login(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted_data;
+ gint encrypted_len;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->token != NULL && qd->token_len > 0);
+
+ raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
+ memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
+
+ encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */
+#ifdef DEBUG
+ memset(qd->inikey, 0x01, sizeof(qd->inikey));
+#else
+ for (bytes = 0; bytes < sizeof(qd->inikey); bytes++) {
+ qd->inikey[bytes] = (guint8) (g_random_int_range(0, 255) % 256);
+ }
+#endif
+
+ bytes = 0;
+ /* now generate the encrypted data
+ * 000-015 use password_twice_md5 as key to encrypt empty string */
+ qq_encrypt((guint8 *) "", 0, qd->password_twice_md5, raw_data + bytes, &encrypted_len);
+ bytes += 16;
+ /* 016-016 */
+ bytes += qq_put8(raw_data + bytes, 0x00);
+ /* 017-020, used to be IP, now zero */
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
+ /* 021-022, used to be port, now zero */
+ bytes += qq_put16(raw_data + bytes, 0x0000);
+ /* 023-051, fixed value, unknown */
+ bytes += qq_putdata(raw_data + bytes, login_23_51, 29);
+ /* 052-052, login mode */
+ bytes += qq_put8(raw_data + bytes, qd->login_mode);
+ /* 053-068, fixed value, maybe related to per machine */
+ bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
+ /* 069, login token length */
+ bytes += qq_put8(raw_data + bytes, qd->token_len);
+ /* 070-093, login token, normally 24 bytes */
+ bytes += qq_putdata(raw_data + bytes, qd->token, qd->token_len);
+ /* 100 bytes unknown */
+ bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
+ /* all zero left */
+
+ qq_encrypt(raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey, encrypted_data, &encrypted_len);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_data(qd, QQ_CMD_LOGIN, qd->send_seq, TRUE, buf, bytes);
+}
+
+guint8 qq_process_token_reply(PurpleConnection *gc, gchar *error_msg, guint8 *buf, gint buf_len)
+{
+ qq_data *qd;
+ guint8 ret;
+ int token_len;
+
+ g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+ qd = (qq_data *) gc->proto_data;
+
+ ret = buf[0];
+
+ if (ret != QQ_TOKEN_REPLY_OK) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown request login token reply code : %d\n", buf[0]);
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ buf, buf_len,
+ ">>> [default] decrypt and dump");
+ error_msg = try_dump_as_gbk(buf, buf_len);
+ return ret;
+ }
+
+ token_len = buf_len-2;
+ if (token_len <= 0) {
+ error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
+ return -1;
+ }
+
+ if (buf[1] != token_len) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Invalid token len. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2);
+ }
+ qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+ buf+2, token_len,
+ "<<< got a token -> [default] decrypt and dump");
+
+ qd->token = g_new0(guint8, token_len);
+ qd->token_len = token_len;
+ g_memmove(qd->token, buf + 2, qd->token_len);
+ return ret;
+}
+
+/* send logout packets to QQ server */
+void qq_send_packet_logout(PurpleConnection *gc)
+{
+ gint i;
+ qq_data *qd;
+
+ qd = (qq_data *) gc->proto_data;
+ for (i = 0; i < 4; i++)
+ qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->password_twice_md5, QQ_KEY_LENGTH);
+
+ qd->logged_in = FALSE; /* update login status AFTER sending logout packets */
+}
+
+/* process the login reply packet */
+guint8 qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *data;
+ gint data_len;
+ gchar* error_msg;
+
+ g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
+
+ qd = (qq_data *) gc->proto_data;
+
+ data_len = buf_len;
+ data = g_newa(guint8, data_len);
+
+ if (qq_decrypt(buf, buf_len, qd->inikey, data, &data_len)) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "Decrypt login reply packet with inikey, %d bytes\n", data_len);
+ } else {
+ /* reset data_len since it may changed */
+ data_len = buf_len;
+ if (qq_decrypt(buf, buf_len, qd->password_twice_md5, data, &data_len)) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len);
+ } else {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "No idea how to decrypt login reply\n");
+ return QQ_LOGIN_REPLY_ERR_MISC;
+ }
+ }
+
+ switch (data[0]) {
+ case QQ_LOGIN_REPLY_OK:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is OK\n");
+ return process_login_ok(gc, data, data_len);
+ case QQ_LOGIN_REPLY_REDIRECT:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is redirect\n");
+ return process_login_redirect(gc, data, data_len);
+ case QQ_LOGIN_REPLY_ERR_PWD:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is error password\n");
+ return process_login_wrong_pwd(gc, data, data_len);
+ case QQ_LOGIN_REPLY_NEED_REACTIVE:
+ case QQ_LOGIN_REPLY_REDIRECT_EX:
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login reply is not actived or redirect extend\n");
+ default:
+ break;
+ }
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Unknown reply code: %d\n", data[0]);
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ data, data_len,
+ ">>> [default] decrypt and dump");
+ error_msg = try_dump_as_gbk(data, data_len);
+ if (error_msg) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+ g_free(error_msg);
+ }
+ return QQ_LOGIN_REPLY_ERR_MISC;
+}
+
+/* send keep-alive packet to QQ server (it is a heart-beat) */
+void qq_send_packet_keep_alive(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 raw_data[16] = {0};
+ gint bytes= 0;
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* In fact, we can send whatever we like to server
+ * with this command, server return the same result including
+ * the amount of online QQ users, my ip and port */
+ bytes += qq_put32(raw_data + bytes, qd->uid);
+
+ qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4);
+}
+
+/* parse the return of keep-alive packet, it includes some system information */
+gboolean qq_process_keep_alive(guint8 *buf, gint buf_len, PurpleConnection *gc)
+{
+ qq_data *qd;
+ gint len;
+ gchar **segments;
+ guint8 *data;
+
+ g_return_val_if_fail(buf != NULL && buf_len != 0, FALSE);
+
+ qd = (qq_data *) gc->proto_data;
+ len = buf_len;
+ data = g_newa(guint8, len);
+
+ if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n");
+ return FALSE;
+ }
+
+ /* qq_show_packet("Keep alive reply packet", data, len); */
+
+ /* the last one is 60, don't know what it is */
+ if (NULL == (segments = split_data(data, len, "\x1f", 6)))
+ return TRUE;
+
+ /* segments[0] and segment[1] are all 0x30 ("0") */
+ qd->total_online = strtol(segments[2], NULL, 10);
+ if(0 == qd->total_online) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Keep alive error"));
+ }
+ qd->my_ip.s_addr = inet_addr(segments[3]);
+ qd->my_port = strtol(segments[4], NULL, 10);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "keep alive, %s:%d\n",
+ inet_ntoa(qd->my_ip), qd->my_port);
+
+ g_strfreev(segments);
+ return TRUE;
+}
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
new file mode 100644
index 0000000000..f18cb63fe4
--- /dev/null
+++ b/libpurple/protocols/qq/qq_network.c
@@ -0,0 +1,1045 @@
+/**
+ * @file qq_network.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "cipher.h"
+#include "debug.h"
+#include "internal.h"
+
+#ifdef _WIN32
+#define random rand
+#define srandom srand
+#endif
+
+#include "buddy_info.h"
+#include "group_info.h"
+#include "group_free.h"
+#include "crypt.h"
+#include "header_info.h"
+#include "qq_base.h"
+#include "buddy_list.h"
+#include "packet_parse.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+#include "utils.h"
+#include "qq_process.h"
+
+/* set QQ_RECONNECT_MAX to 1, when test reconnecting */
+#define QQ_RECONNECT_MAX 4
+#define QQ_RECONNECT_INTERVAL 5000
+#define QQ_KEEP_ALIVE_INTERVAL 60000
+#define QQ_TRANS_INTERVAL 10000
+
+static gboolean set_new_server(qq_data *qd)
+{
+ gint count;
+ gint index;
+ GList *it = NULL;
+
+ g_return_val_if_fail(qd != NULL, FALSE);
+
+ if (qd->servers == NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n");
+ return FALSE;
+ }
+
+ if (qd->real_hostname) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+ g_free(qd->real_hostname);
+ qd->real_hostname = NULL;
+ }
+
+ /* remove server used before */
+ if (qd->server_name != NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Remove previous server [%s]\n", qd->server_name);
+ qd->servers = g_list_remove(qd->servers, qd->server_name);
+ qd->server_name = NULL;
+ }
+
+ count = g_list_length(qd->servers);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count);
+ if (count <= 0) {
+ /* no server left, disconnect when result is false */
+ qd->servers = NULL;
+ return FALSE;
+ }
+
+ /* get new server */
+ index = random() % count;
+ it = g_list_nth(qd->servers, index);
+ qd->server_name = it->data; /* do not free server_name */
+ if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index);
+ return FALSE;
+ }
+
+ qd->real_hostname = g_strdup(qd->server_name);
+ qd->real_port = qd->user_port;
+
+ qd->reconnect_times = QQ_RECONNECT_MAX;
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "set new server to %s:%d\n", qd->real_hostname, qd->real_port);
+ return TRUE;
+}
+
+static gint packet_get_header(guint8 *header_tag, guint16 *source_tag,
+ guint16 *cmd, guint16 *seq, guint8 *buf)
+{
+ gint bytes = 0;
+ bytes += qq_get8(header_tag, buf + bytes);
+ bytes += qq_get16(source_tag, buf + bytes);
+ bytes += qq_get16(cmd, buf + bytes);
+ bytes += qq_get16(seq, buf + bytes);
+ return bytes;
+}
+
+static gboolean reconnect_later_cb(gpointer data)
+{
+ PurpleConnection *gc;
+ qq_data *qd;
+
+ gc = (PurpleConnection *) data;
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
+ qd = (qq_data *) gc->proto_data;
+
+ qd->reconnect_timeout = 0;
+
+ qq_connect(gc->account);
+ return FALSE; /* timeout callback stops */
+}
+
+static void reconnect_later(PurpleConnection *gc)
+{
+ qq_data *qd;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ qd->reconnect_times--;
+ if (qd->reconnect_times < 0) {
+ if ( set_new_server(qd) != TRUE) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Failed to connect server"));
+ return;
+ }
+ }
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Reconnect to server %s:%d next retries %d in %d ms\n",
+ qd->real_hostname, qd->real_port,
+ qd->reconnect_times, QQ_RECONNECT_INTERVAL);
+
+ qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL,
+ reconnect_later_cb, gc);
+}
+
+/* process the incoming packet from qq_pending */
+static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
+{
+ qq_data *qd;
+ gint bytes, bytes_not_read;
+
+ gboolean prev_login_status;
+
+ guint8 header_tag;
+ guint16 source_tag;
+ guint16 cmd;
+ guint16 seq; /* May be ack_seq or send_seq, depends on cmd */
+
+ qq_transaction *trans;
+
+ g_return_if_fail(buf != NULL && buf_len > 0);
+
+ qd = (qq_data *) gc->proto_data;
+
+ prev_login_status = qd->logged_in;
+
+ /* Len, header and tail tag have been checked before */
+ bytes = 0;
+ bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
+
+ if (QQ_DEBUG) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "==> [%05d] 0x%04X %s, from (0x%04X %s)\n",
+ seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag));
+ }
+
+ bytes_not_read = buf_len - bytes - 1;
+
+ /* ack packet, we need to update send tranactions */
+ /* we do not check duplication for server ack */
+ trans = qq_trans_find_rcved(qd, cmd, seq);
+ if (trans == NULL) {
+ /* new server command */
+ qq_trans_add_server_cmd(qd, cmd, seq, buf + bytes, bytes_not_read);
+ if ( qd->logged_in ) {
+ qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
+ }
+ return;
+ }
+
+ if (qq_trans_is_dup(trans)) {
+ purple_debug(PURPLE_DEBUG_WARNING,
+ "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
+ return;
+ }
+
+ if (qq_trans_is_server(trans)) {
+ if ( qd->logged_in ) {
+ qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
+ }
+ return;
+ }
+
+ /* this is the length of all the encrypted data (also remove tail tag */
+ qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+
+ /* check is redirect or not, and do it now */
+ if (qd->is_redirect) {
+ /* free resource except real_hostname and port */
+ qq_disconnect(gc);
+ qd->reconnect_times = QQ_RECONNECT_MAX;
+ reconnect_later(gc);
+ return;
+ }
+
+ if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) {
+ /* logged_in, but we have packets before login */
+ qq_trans_process_before_login(qd);
+ }
+}
+
+static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+ PurpleConnection *gc;
+ qq_data *qd;
+ guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */
+ gint buf_len;
+ gint bytes;
+
+ guint8 *pkt;
+ guint16 pkt_len;
+
+ gchar *error_msg;
+ guint8 *jump;
+ gint jump_len;
+
+ gc = (PurpleConnection *) data;
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+ if(cond != PURPLE_INPUT_READ) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Socket error"));
+ return;
+ }
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* test code, not using tcp_rxqueue
+ memset(pkt,0, sizeof(pkt));
+ buf_len = read(qd->fd, pkt, sizeof(pkt));
+ if (buf_len > 2) {
+ packet_process(gc, pkt + 2, buf_len - 2);
+ }
+ return;
+ */
+
+ buf_len = read(qd->fd, buf, sizeof(buf));
+ if (buf_len < 0) {
+ if (errno == EAGAIN)
+ /* No worries */
+ return;
+
+ error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno));
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+ g_free(error_msg);
+ return;
+ } else if (buf_len == 0) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Server closed the connection."));
+ return;
+ }
+
+ /* keep alive will be sent in 30 seconds since last_receive
+ * QQ need a keep alive packet in every 60 seconds
+ gc->last_received = time(NULL);
+ */
+ /*
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+ "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen);
+ */
+ qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen);
+ memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len);
+ qd->tcp_rxlen += buf_len;
+
+ pkt = g_newa(guint8, MAX_PACKET_SIZE);
+ while (1) {
+ if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) {
+ break;
+ }
+
+ bytes = 0;
+ bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes);
+ if (qd->tcp_rxlen < pkt_len) {
+ break;
+ }
+
+ /*
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+ "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen);
+ */
+ if ( pkt_len < QQ_TCP_HEADER_LENGTH
+ || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG
+ || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) {
+ /* HEY! This isn't even a QQ. What are you trying to pull? */
+
+ purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING",
+ "Packet error, failed to check header and tail tag\n");
+
+ jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1);
+ if ( !jump ) {
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+ "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n");
+ g_free(qd->tcp_rxqueue);
+ qd->tcp_rxqueue = NULL;
+ qd->tcp_rxlen = 0;
+ return;
+ }
+
+ /* jump and over QQ_PACKET_TAIL */
+ jump_len = (jump - qd->tcp_rxqueue) + 1;
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
+ "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1);
+ g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len);
+ qd->tcp_rxlen -= jump_len;
+ continue;
+ }
+
+ memset(pkt, 0, MAX_PACKET_SIZE);
+ g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes);
+
+ /* jump to next packet */
+ qd->tcp_rxlen -= pkt_len;
+ if (qd->tcp_rxlen) {
+ /*
+ purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen);
+ */
+ jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen);
+ g_free(qd->tcp_rxqueue);
+ qd->tcp_rxqueue = jump;
+ } else {
+ /* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */
+ g_free(qd->tcp_rxqueue);
+ qd->tcp_rxqueue = NULL;
+ }
+
+ if (pkt == NULL) {
+ continue;
+ }
+ /* do not call packet_process before jump
+ * packet_process may call disconnect and destory tcp_rxqueue */
+ packet_process(gc, pkt, pkt_len - bytes);
+ }
+}
+
+static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
+{
+ PurpleConnection *gc;
+ qq_data *qd;
+ guint8 *buf;
+ gint buf_len;
+
+ gc = (PurpleConnection *) data;
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+ if(cond != PURPLE_INPUT_READ) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Socket error"));
+ return;
+ }
+
+ qd = (qq_data *) gc->proto_data;
+ g_return_if_fail(qd->fd >= 0);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+ /* here we have UDP proxy suppport */
+ buf_len = read(qd->fd, buf, MAX_PACKET_SIZE);
+ if (buf_len <= 0) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to read from socket"));
+ return;
+ }
+
+ /* keep alive will be sent in 30 seconds since last_receive
+ * QQ need a keep alive packet in every 60 seconds
+ gc->last_received = time(NULL);
+ */
+
+ if (buf_len < QQ_UDP_HEADER_LENGTH) {
+ if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) {
+ qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING",
+ buf, buf_len,
+ "Received packet is too short, or no header and tail tag");
+ return;
+ }
+ }
+
+ packet_process(gc, buf, buf_len);
+}
+
+static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len)
+{
+ gint ret;
+
+ g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+
+ /*
+ purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
+ */
+
+ errno = 0;
+ ret = send(qd->fd, data, data_len, 0);
+ if (ret < 0 && errno == EAGAIN) {
+ return ret;
+ }
+
+ if (ret < 0) {
+ /* TODO: what to do here - do we really have to disconnect? */
+ purple_debug(PURPLE_DEBUG_ERROR, "UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno));
+ purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+ }
+ return ret;
+}
+
+static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+ qq_data *qd = data;
+ int ret, writelen;
+
+ writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf);
+ if (writelen == 0) {
+ purple_input_remove(qd->tx_handler);
+ qd->tx_handler = 0;
+ return;
+ }
+
+ ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen);
+ purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE",
+ "total %d bytes is sent %d\n", writelen, ret);
+
+ if (ret < 0 && errno == EAGAIN)
+ return;
+ else if (ret < 0) {
+ /* TODO: what to do here - do we really have to disconnect? */
+ purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Write Error"));
+ return;
+ }
+
+ purple_circ_buffer_mark_read(qd->tcp_txbuf, ret);
+}
+
+static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len)
+{
+ gint ret;
+
+ g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
+
+ /*
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
+ */
+
+ if (qd->tx_handler == 0) {
+ ret = write(qd->fd, data, data_len);
+ } else {
+ ret = -1;
+ errno = EAGAIN;
+ }
+
+ /*
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+ "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret);
+ */
+ if (ret < 0 && errno == EAGAIN) {
+ /* socket is busy, send later */
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n");
+ ret = 0;
+ } else if (ret <= 0) {
+ /* TODO: what to do here - do we really have to disconnect? */
+ purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT",
+ "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
+ purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+ return ret;
+ }
+
+ if (ret < data_len) {
+ purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
+ "Add %d bytes to buffer\n", data_len - ret);
+ if (qd->tx_handler == 0) {
+ qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd);
+ }
+ purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret);
+ }
+ return ret;
+}
+
+static gboolean network_timeout(gpointer data)
+{
+ PurpleConnection *gc = (PurpleConnection *) data;
+ qq_data *qd;
+ gboolean is_lost_conn;
+
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
+ qd = (qq_data *) gc->proto_data;
+
+ is_lost_conn = qq_trans_scan(qd);
+ if (is_lost_conn) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+ return TRUE;
+ }
+
+ if ( !qd->logged_in ) {
+ return TRUE;
+ }
+
+ qd->itv_count.keep_alive--;
+ if (qd->itv_count.keep_alive <= 0) {
+ qd->itv_count.keep_alive = qd->itv_config.keep_alive;
+ qq_send_packet_keep_alive(gc);
+ return TRUE;
+ }
+
+ if (qd->itv_config.update <= 0) {
+ return TRUE;
+ }
+
+ qd->itv_count.update--;
+ if (qd->itv_count.update <= 0) {
+ qd->itv_count.update = qd->itv_config.update;
+ qq_send_packet_get_buddies_online(gc, 0);
+
+ qq_send_cmd_group_all_get_online_members(gc);
+ return TRUE;
+ }
+
+ return TRUE; /* if return FALSE, timeout callback stops */
+}
+
+/* the callback function after socket is built
+ * we setup the qq protocol related configuration here */
+static void qq_connect_cb(gpointer data, gint source, const gchar *error_message)
+{
+ qq_data *qd;
+ PurpleConnection *gc;
+ gchar *conn_msg;
+ const gchar *passwd;
+ PurpleAccount *account ;
+
+ gc = (PurpleConnection *) data;
+
+ if (!PURPLE_CONNECTION_IS_VALID(gc)) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n");
+ close(source);
+ return;
+ }
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+ account = purple_connection_get_account(gc);
+
+ /* Connect is now complete; clear the PurpleProxyConnectData */
+ qd->connect_data = NULL;
+
+ if (source < 0) { /* socket returns -1 */
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n");
+ qq_disconnect(gc);
+ reconnect_later(gc);
+ return;
+ }
+
+ /* _qq_show_socket("Got login socket", source); */
+
+ /* QQ use random seq, to minimize duplicated packets */
+ srandom(time(NULL));
+ qd->send_seq = random() & 0x0000ffff;
+ qd->fd = source;
+ qd->logged_in = FALSE;
+ qd->channel = 1;
+ qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
+
+ /* now generate md5 processed passwd */
+ passwd = purple_account_get_password(purple_connection_get_account(gc));
+
+ /* use twice-md5 of user password as session key since QQ 2003iii */
+ qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
+ (guint8 *)passwd, strlen(passwd));
+ qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
+ qd->password_twice_md5, sizeof(qd->password_twice_md5));
+
+ g_return_if_fail(qd->network_timeout == 0);
+ qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10);
+ if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10;
+
+ qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60);
+ if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30;
+ qd->itv_config.keep_alive /= qd->itv_config.resend;
+ qd->itv_count.keep_alive = qd->itv_config.keep_alive;
+
+ qd->itv_config.update = purple_account_get_int(account, "update_interval", 300);
+ if (qd->itv_config.update > 0) {
+ if (qd->itv_config.update < qd->itv_config.keep_alive) {
+ qd->itv_config.update = qd->itv_config.keep_alive;
+ }
+ qd->itv_config.update /= qd->itv_config.resend;
+ qd->itv_count.update = qd->itv_config.update;
+ } else {
+ qd->itv_config.update = 0;
+ }
+
+ qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc);
+
+ if (qd->use_tcp)
+ gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc);
+ else
+ gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc);
+
+ /* Update the login progress status display */
+ conn_msg = g_strdup_printf("Login as %d", qd->uid);
+ purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
+ g_free(conn_msg);
+
+ qq_send_packet_token(gc);
+}
+
+static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
+{
+ PurpleConnection *gc;
+ qq_data *qd;
+ socklen_t len;
+ int error=0, ret;
+
+ gc = (PurpleConnection *) data;
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+
+
+ purple_debug_info("proxy", "Connected.\n");
+
+ /*
+ * getsockopt after a non-blocking connect returns -1 if something is
+ * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
+ * error holds what connect would have returned if it blocked until now.
+ * Thus, error == 0 is success, error == EINPROGRESS means "try again",
+ * and anything else is a real error.
+ *
+ * (error == EINPROGRESS can happen after a select because the kernel can
+ * be overly optimistic sometimes. select is just a hint that you might be
+ * able to do something.)
+ */
+ len = sizeof(error);
+ ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
+ if (ret == 0 && error == EINPROGRESS)
+ return; /* we'll be called again later */
+
+ purple_input_remove(qd->tx_handler);
+ qd->tx_handler = 0;
+ if (ret < 0 || error != 0) {
+ if(ret != 0)
+ error = errno;
+
+ close(source);
+
+ purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
+
+ qq_connect_cb(gc, -1, _("Unable to connect"));
+ return;
+ }
+
+ qq_connect_cb(gc, source, NULL);
+}
+
+static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) {
+ PurpleConnection *gc;
+ qq_data *qd;
+ struct sockaddr server_addr;
+ int addr_size;
+ gint fd = -1;
+ int flags;
+
+ gc = (PurpleConnection *) data;
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* udp_query_data must be set as NULL.
+ * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */
+ qd->udp_query_data = NULL;
+
+ if (!hosts || !hosts->data) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Couldn't resolve host"));
+ return;
+ }
+
+ addr_size = GPOINTER_TO_INT(hosts->data);
+ hosts = g_slist_remove(hosts, hosts->data);
+ memcpy(&server_addr, hosts->data, addr_size);
+ g_free(hosts->data);
+
+ hosts = g_slist_remove(hosts, hosts->data);
+ while(hosts) {
+ hosts = g_slist_remove(hosts, hosts->data);
+ g_free(hosts->data);
+ hosts = g_slist_remove(hosts, hosts->data);
+ }
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Unable to create socket: %s\n", g_strerror(errno));
+ return;
+ }
+
+ /* we use non-blocking mode to speed up connection */
+ flags = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
+ *
+ * If a UDP socket is unconnected, which is the normal state after a
+ * bind() call, then send() or write() are not allowed, since no
+ * destination is available; only sendto() can be used to send data.
+ *
+ * Calling connect() on the socket simply records the specified address
+ * and port number as being the desired communications partner. That
+ * means that send() or write() are now allowed; they use the destination
+ * address and port given on the connect call as the destination of packets.
+ */
+ if (connect(fd, &server_addr, addr_size) >= 0) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
+ flags = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ qq_connect_cb(gc, fd, NULL);
+ return;
+ }
+
+ /* [EINPROGRESS]
+ * The socket is marked as non-blocking and the connection cannot be
+ * completed immediately. It is possible to select for completion by
+ * selecting the socket for writing.
+ * [EINTR]
+ * A signal interrupted the call.
+ * The connection is established asynchronously.
+ */
+ if ((errno == EINPROGRESS) || (errno == EINTR)) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
+ qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc);
+ return;
+ }
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %s\n", g_strerror(errno));
+ close(fd);
+}
+
+/* establish a generic QQ connection
+ * TCP/UDP, and direct/redirected */
+void qq_connect(PurpleAccount *account)
+{
+ PurpleConnection *gc;
+ qq_data *qd;
+ gchar *conn_msg;
+
+ gc = purple_account_get_connection(account);
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+
+
+ /* test set_new_server
+ while (set_new_server(qd)) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST",
+ "New server %s:%d Real server %s:%d\n",
+ qd->server_name, qd->user_port, qd->real_hostname, qd->real_port);
+ }
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n",
+ qd->servers);
+ exit(1);
+ */
+ if (qd->server_name == NULL) {
+ /* must be first call this function */
+ if ( set_new_server(qd) != TRUE) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Failed to connect server"));
+ return;
+ }
+ }
+
+ if (qd->real_hostname == NULL || qd->real_port == 0) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("hostname is NULL or port is 0"));
+ return;
+ }
+
+ conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"),
+ qd->real_hostname, qd->reconnect_times);
+ purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
+ g_free(conn_msg);
+
+ if (qd->is_redirect) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n",
+ qd->real_hostname, qd->real_port);
+ }
+ qd->is_redirect = FALSE;
+
+ qd->fd = -1;
+ qd->tx_handler = 0;
+
+ /* QQ connection via UDP/TCP.
+ * Now use Purple proxy function to provide TCP proxy support,
+ * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
+ if(qd->use_tcp) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n",
+ qd->real_hostname, qd->real_port);
+
+ /* TODO: is there a good default grow size? */
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n");
+ qd->tcp_txbuf = purple_circ_buffer_new(0);
+
+ qd->connect_data = purple_proxy_connect(NULL, account,
+ qd->real_hostname, qd->real_port, qq_connect_cb, gc);
+ if (qd->connect_data == NULL) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Unable to connect."));
+ }
+ return;
+ }
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n",
+ qd->real_hostname, qd->real_port);
+
+ g_return_if_fail(qd->udp_query_data == NULL);
+ qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port,
+ udp_host_resolved, gc);
+ if (qd->udp_query_data == NULL) {
+ purple_connection_error_reason(qd->gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Could not resolve hostname"));
+ }
+}
+
+/* clean up qq_data structure and all its components
+ * always used before a redirectly connection */
+void qq_disconnect(PurpleConnection *gc)
+{
+ qq_data *qd;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n");
+
+ if (qd->network_timeout > 0) {
+ purple_timeout_remove(qd->network_timeout);
+ qd->network_timeout = 0;
+ }
+
+ /* finish all I/O */
+ if (qd->fd >= 0 && qd->logged_in) {
+ qq_send_packet_logout(gc);
+ }
+
+ if (gc->inpa > 0) {
+ purple_input_remove(gc->inpa);
+ gc->inpa = 0;
+ }
+
+ if (qd->fd >= 0) {
+ close(qd->fd);
+ qd->fd = -1;
+ }
+
+ if (qd->reconnect_timeout > 0) {
+ purple_timeout_remove(qd->reconnect_timeout);
+ qd->reconnect_timeout = 0;
+ }
+
+ if (qd->connect_data != NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n");
+ purple_proxy_connect_cancel(qd->connect_data);
+ }
+
+ if(qd->tcp_txbuf != NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n");
+ purple_circ_buffer_destroy(qd->tcp_txbuf);
+ qd->tcp_txbuf = NULL;
+ }
+
+ if (qd->tx_handler) {
+ purple_input_remove(qd->tx_handler);
+ qd->tx_handler = 0;
+ }
+ if (qd->tcp_rxqueue != NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n");
+ g_free(qd->tcp_rxqueue);
+ qd->tcp_rxqueue = NULL;
+ qd->tcp_rxlen = 0;
+ }
+
+ if (qd->udp_query_data != NULL) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n");
+ purple_dnsquery_destroy(qd->udp_query_data);
+ qd->udp_query_data = NULL;
+ }
+
+ qq_trans_remove_all(qd);
+
+ if (qd->token) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n");
+ g_free(qd->token);
+ qd->token = NULL;
+ qd->token_len = 0;
+ }
+ memset(qd->inikey, 0, sizeof(qd->inikey));
+ memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5));
+ memset(qd->session_key, 0, sizeof(qd->session_key));
+ memset(qd->session_md5, 0, sizeof(qd->session_md5));
+
+ qd->my_ip.s_addr = 0;
+
+ qq_group_packets_free(qd);
+ qq_group_free_all(qd);
+ qq_add_buddy_request_free(qd);
+ qq_info_query_free(qd);
+ qq_buddies_list_free(gc->account, qd);
+}
+
+static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq,
+ guint8 *data, gint data_len)
+{
+ gint bytes = 0;
+ g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1);
+
+ if (data == NULL) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n");
+ return -1;
+ }
+ if (data_len <= 0) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n");
+ return -1;
+ }
+
+ /* QQ TCP packet has two bytes in the begining defines packet length
+ * so leave room here to store packet size */
+ if (qd->use_tcp) {
+ bytes += qq_put16(buf + bytes, 0x0000);
+ }
+ /* now comes the normal QQ packet as UDP */
+ bytes += qq_put8(buf + bytes, QQ_PACKET_TAG);
+ bytes += qq_put16(buf + bytes, QQ_CLIENT);
+ bytes += qq_put16(buf + bytes, cmd);
+
+ bytes += qq_put16(buf + bytes, seq);
+
+ bytes += qq_put32(buf + bytes, qd->uid);
+ bytes += qq_putdata(buf + bytes, data, data_len);
+ bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL);
+
+ /* set TCP packet length at begin of the packet */
+ if (qd->use_tcp) {
+ qq_put16(buf, bytes);
+ }
+
+ return bytes;
+}
+
+/* data has been encrypted before */
+gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+ guint8 *data, gint data_len)
+{
+ guint8 *buf;
+ gint buf_len;
+ gint bytes_sent;
+
+ g_return_val_if_fail(qd != NULL, -1);
+ g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
+ if (buf_len <= 0) {
+ return -1;
+ }
+
+ if (qd->use_tcp) {
+ bytes_sent = tcp_send_out(qd, buf, buf_len);
+ } else {
+ bytes_sent = udp_send_out(qd, buf, buf_len);
+ }
+
+ if (need_ack) {
+ qq_trans_add_client_cmd(qd, cmd, seq, data, data_len);
+ }
+
+ if (QQ_DEBUG) {
+ /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "<== [%05d], %s, total %d bytes is sent %d\n",
+ seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
+ }
+ return bytes_sent;
+}
+
+/* Encrypt data with session_key, then call qq_send_data */
+gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+ guint8 *data, gint data_len)
+{
+ guint8 *encrypted_data;
+ gint encrypted_len;
+
+ g_return_val_if_fail(qd != NULL, -1);
+ g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+ encrypted_len = data_len + 16; /* at most 16 bytes more */
+ encrypted_data = g_newa(guint8, encrypted_len);
+
+ qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
+
+ return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len);
+}
+
+/* set seq and need_ack, then call qq_send_cmd_detail */
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+{
+ g_return_val_if_fail(qd != NULL, -1);
+ g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+ qd->send_seq++;
+ return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len);
+}
diff --git a/libpurple/protocols/qq/keep_alive.h b/libpurple/protocols/qq/qq_network.h
index cddba2fdcc..054f6fccd9 100644
--- a/libpurple/protocols/qq/keep_alive.h
+++ b/libpurple/protocols/qq/qq_network.h
@@ -1,5 +1,5 @@
/**
- * @file keep_alive.h
+ * @file qq_network.h
*
* purple
*
@@ -20,21 +20,26 @@
* 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_NETWORK_H
+#define _QQ_NETWORK_H
#include <glib.h>
#include "connection.h"
+
#include "qq.h"
-void qq_send_packet_keep_alive(PurpleConnection *gc);
+#define QQ_CONNECT_STEPS 3 /* steps in connection */
-void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_refresh_all_buddy_status(PurpleConnection *gc);
+void qq_connect(PurpleAccount *account);
+void qq_disconnect(PurpleConnection *gc);
+void qq_connect_later(PurpleConnection *gc);
-void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud);
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+ guint8 *data, gint data_len);
+gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+ guint8 *data, gint data_len);
#endif
diff --git a/libpurple/protocols/qq/qq_process.c b/libpurple/protocols/qq/qq_process.c
new file mode 100644
index 0000000000..8f7e994fcf
--- /dev/null
+++ b/libpurple/protocols/qq/qq_process.c
@@ -0,0 +1,267 @@
+/**
+ * @file qq_network.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "cipher.h"
+#include "debug.h"
+#include "internal.h"
+
+#ifdef _WIN32
+#define random rand
+#define srandom srand
+#endif
+
+#include "buddy_info.h"
+#include "buddy_list.h"
+#include "buddy_opt.h"
+#include "group_info.h"
+#include "group_free.h"
+#include "char_conv.h"
+#include "crypt.h"
+#include "group_network.h"
+#include "header_info.h"
+#include "qq_base.h"
+#include "im.h"
+#include "qq_process.h"
+#include "packet_parse.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+#include "sys_msg.h"
+#include "utils.h"
+
+/* default process, decrypt and dump */
+static void process_cmd_unknow(PurpleConnection *gc,gchar *title, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+ qq_data *qd;
+ guint8 *data;
+ gint data_len;
+ gchar *msg_utf8 = NULL;
+
+ g_return_if_fail(buf != NULL && buf_len != 0);
+
+ qq_show_packet(title, buf, buf_len);
+
+ qd = (qq_data *) gc->proto_data;
+
+ data_len = buf_len;
+ data = g_newa(guint8, data_len);
+ memset(data, 0, data_len);
+ if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
+ return;
+ }
+
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ data, data_len,
+ ">>> [%d] %s -> [default] decrypt and dump",
+ seq, qq_get_cmd_desc(cmd));
+
+ msg_utf8 = try_dump_as_gbk(data, data_len);
+ if (msg_utf8) {
+ g_free(msg_utf8);
+ }
+}
+
+void qq_proc_cmd_server(PurpleConnection *gc,
+ guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ /* now process the packet */
+ switch (cmd) {
+ case QQ_CMD_RECV_IM:
+ qq_process_recv_im(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_RECV_MSG_SYS:
+ qq_process_msg_sys(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_RECV_MSG_BUDDY_CHANGE_STATUS:
+ qq_process_buddy_change_status(data, data_len, gc);
+ break;
+ default:
+ process_cmd_unknow(gc, "Unknow SERVER CMD", data, data_len, cmd, seq);
+ break;
+ }
+}
+
+static void process_cmd_login(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ qq_data *qd;
+ guint ret_8;
+
+ g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+
+ ret_8 = qq_process_login_reply(data, data_len, gc);
+ if (ret_8 == QQ_LOGIN_REPLY_OK) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Login repliess OK; everything is fine\n");
+
+ purple_connection_set_state(gc, PURPLE_CONNECTED);
+ qd->logged_in = TRUE; /* must be defined after sev_finish_login */
+
+ /* now initiate QQ Qun, do it first as it may take longer to finish */
+ qq_group_init(gc);
+
+ /* Now goes on updating my icon/nickname, not showing info_window */
+ qd->modifying_face = FALSE;
+
+ qq_send_packet_get_info(gc, qd->uid, FALSE);
+ /* grab my level */
+ qq_send_packet_get_level(gc, qd->uid);
+
+ qq_send_packet_change_status(gc);
+
+ /* refresh buddies */
+ qq_send_packet_get_buddies_list(gc, 0);
+
+ /* refresh groups */
+ qq_send_packet_get_all_list_with_group(gc, 0);
+
+ return;
+ }
+
+ if (ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+ qd->is_redirect = TRUE;
+ /*
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port);
+ */
+ return;
+ }
+
+ if (ret_8 == QQ_LOGIN_REPLY_ERR_PWD) {
+ if (!purple_account_get_remember_password(gc->account)) {
+ purple_account_set_password(gc->account, NULL);
+ }
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password."));
+ return;
+ }
+
+ if (ret_8 == QQ_LOGIN_REPLY_ERR_MISC) {
+ if (purple_debug_is_enabled())
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login. Check debug log."));
+ else
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to login"));
+ return;
+ }
+}
+
+void qq_proc_cmd_reply(PurpleConnection *gc,
+ guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ gboolean ret_bool = FALSE;
+ guint8 ret_8 = 0;
+ guint16 ret_16 = 0;
+ guint32 ret_32 = 0;
+ gchar *error_msg = NULL;
+
+ switch (cmd) {
+ case QQ_CMD_TOKEN:
+ ret_8 = qq_process_token_reply(gc, error_msg, data, data_len);
+ if (ret_8 != QQ_TOKEN_REPLY_OK) {
+ if (error_msg == NULL) {
+ error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret_8);
+ }
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+ g_free(error_msg);
+ return;
+ }
+
+ qq_send_packet_login(gc);
+ break;
+ case QQ_CMD_LOGIN:
+ process_cmd_login(gc, data, data_len);
+ break;
+ case QQ_CMD_UPDATE_INFO:
+ qq_process_modify_info_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_ADD_BUDDY_WO_AUTH:
+ qq_process_add_buddy_reply(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_DEL_BUDDY:
+ qq_process_remove_buddy_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_REMOVE_SELF:
+ qq_process_remove_self_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_BUDDY_AUTH:
+ qq_process_add_buddy_auth_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_USER_INFO:
+ qq_process_get_info_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_CHANGE_ONLINE_STATUS:
+ qq_process_change_status_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_SEND_IM:
+ qq_process_send_im_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_KEEP_ALIVE:
+ qq_process_keep_alive(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_BUDDIES_ONLINE:
+ ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc);
+ if (ret_8 > 0 && ret_8 < 0xff) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more online buddies\n");
+ qq_send_packet_get_buddies_online(gc, ret_8);
+ } else {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "All online buddies received\n");
+ /* Fixme: this should not be called once*/
+ qq_send_packet_get_buddies_levels(gc);
+
+ qq_refresh_all_buddy_status(gc);
+ }
+ break;
+ case QQ_CMD_GET_LEVEL:
+ qq_process_get_level_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_BUDDIES_LIST:
+ ret_16 = qq_process_get_buddies_list_reply(data, data_len, gc);
+ if (ret_16 > 0 && ret_16 < 0xffff) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies\n");
+ qq_send_packet_get_buddies_list(gc, ret_16);
+ } else {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies received. Requesting buddies' levels\n");
+ qq_send_packet_get_buddies_online(gc, 0);
+ }
+ break;
+ case QQ_CMD_GROUP_CMD:
+ qq_process_group_cmd_reply(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
+ ret_32 = qq_process_get_all_list_with_group_reply(data, data_len, gc);
+ if (ret_32 > 0 && ret_32 < 0xffffffff) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Requesting for more buddies and groups\n");
+ qq_send_packet_get_all_list_with_group(gc, ret_32);
+ } else {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "All buddies and groups received\n");
+ }
+ break;
+ default:
+ process_cmd_unknow(gc, "Unknow reply CMD", data, data_len, cmd, seq);
+ break;
+ }
+}
+
diff --git a/libpurple/protocols/qq/recv_core.h b/libpurple/protocols/qq/qq_process.h
index 6353b48f85..7b5820ede1 100644
--- a/libpurple/protocols/qq/recv_core.h
+++ b/libpurple/protocols/qq/qq_process.h
@@ -1,5 +1,5 @@
/**
- * @file recv_core.h
+ * @file qq_process.h
*
* purple
*
@@ -22,15 +22,17 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _QQ_RECV_CORE_H_
-#define _QQ_RECV_CORE_H_
+#ifndef _QQ_PROCESS_H
+#define _QQ_PROCESS_H
#include <glib.h>
#include "connection.h"
-#include "qq.h"
-
-void qq_b4_packets_free(qq_data *qd);
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond);
+#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_proxy.c b/libpurple/protocols/qq/qq_proxy.c
deleted file mode 100644
index 3c807f2b66..0000000000
--- a/libpurple/protocols/qq/qq_proxy.c
+++ /dev/null
@@ -1,529 +0,0 @@
-/**
- * @file qq_proxy.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "cipher.h"
-#include "debug.h"
-#include "internal.h"
-
-#ifdef _WIN32
-#define random rand
-#define srandom srand
-#endif
-
-#include "packet_parse.h"
-#include "buddy_info.h"
-#include "buddy_opt.h"
-#include "char_conv.h"
-#include "group_free.h"
-#include "login_logout.h"
-#include "qq_proxy.h"
-#include "recv_core.h"
-#include "send_core.h"
-#include "sendqueue.h"
-#include "udp_proxy_s5.h"
-#include "utils.h"
-
-/* These functions are used only in development phase */
-/*
-static void _qq_show_socket(gchar *desc, gint fd) {
- struct sockaddr_in sin;
- socklen_t len = sizeof(sin);
- getsockname(fd, (struct sockaddr *)&sin, &len);
- purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
- inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
-}
-*/
-
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
-{
- char buf1[8*len+2], buf2[10];
- int i;
- buf1[0] = 0;
- for (i = 0; i < len; i++) {
- sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
- strcat(buf1, buf2);
- }
- strcat(buf1, "\n");
- purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
-}
-
-/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
-static guint8 *_gen_pwkey(const gchar *pwd)
-{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
-
- guchar pwkey_tmp[QQ_KEY_LENGTH];
-
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
- purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
- purple_cipher_context_destroy(context);
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
- purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
- purple_cipher_context_destroy(context);
-
- return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
-}
-
-static gboolean _qq_fill_host(GSList *hosts, struct sockaddr_in *addr, gint *addr_size)
-{
- if (!hosts || !hosts->data)
- return FALSE;
-
- *addr_size = GPOINTER_TO_INT(hosts->data);
-
- hosts = g_slist_remove(hosts, hosts->data);
- memcpy(addr, hosts->data, *addr_size);
- g_free(hosts->data);
- hosts = g_slist_remove(hosts, hosts->data);
- while(hosts) {
- hosts = g_slist_remove(hosts, hosts->data);
- g_free(hosts->data);
- hosts = g_slist_remove(hosts, hosts->data);
- }
-
- return TRUE;
-}
-
-/* set up any finalizing start-up stuff */
-static void _qq_start_services(PurpleConnection *gc)
-{
- /* start watching for IMs about to be sent */
- /*
- purple_signal_connect(purple_conversations_get_handle(),
- "sending-im-msg", gc,
- PURPLE_CALLBACK(qq_sending_im_msg_cb), NULL);
- */
-}
-
-/* the callback function after socket is built
- * we setup the qq protocol related configuration here */
-static void _qq_got_login(gpointer data, gint source, const gchar *error_message)
-{
- qq_data *qd;
- PurpleConnection *gc;
- gchar *buf;
- const gchar *passwd;
-
- gc = (PurpleConnection *) data;
-
- if (!PURPLE_CONNECTION_IS_VALID(gc)) {
- close(source);
- return;
- }
-
- g_return_if_fail(gc != NULL && gc->proto_data != NULL);
-
- if (source < 0) { /* socket returns -1 */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
- return;
- }
-
- qd = (qq_data *) gc->proto_data;
-
- /*
- _qq_show_socket("Got login socket", source);
- */
-
- /* QQ use random seq, to minimize duplicated packets */
- srandom(time(NULL));
- qd->send_seq = random() & 0x0000ffff;
- qd->fd = source;
- qd->logged_in = FALSE;
- qd->channel = 1;
- qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
-
- /* now generate md5 processed passwd */
- passwd = purple_account_get_password(purple_connection_get_account(gc));
- qd->pwkey = _gen_pwkey(passwd);
-
- qd->sendqueue_timeout = purple_timeout_add(QQ_SENDQUEUE_TIMEOUT, qq_sendqueue_timeout_callback, gc);
- gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, qq_input_pending, gc);
-
- /* Update the login progress status display */
- buf = g_strdup_printf("Login as %d", qd->uid);
- purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS);
- g_free(buf);
-
- _qq_start_services(gc);
-
- qq_send_packet_request_login_token(gc);
-}
-
-/* clean up qq_data structure and all its components
- * always used before a redirectly connection */
-static void _qq_common_clean(PurpleConnection *gc)
-{
- qq_data *qd;
-
- g_return_if_fail(gc != NULL && gc->proto_data != NULL);
- qd = (qq_data *) gc->proto_data;
-
- /* finish all I/O */
- if (qd->fd >= 0 && qd->logged_in)
- qq_send_packet_logout(gc);
- close(qd->fd);
-
- if (qd->sendqueue_timeout > 0) {
- purple_timeout_remove(qd->sendqueue_timeout);
- qd->sendqueue_timeout = 0;
- }
-
- if (gc->inpa > 0) {
- purple_input_remove(gc->inpa);
- gc->inpa = 0;
- }
-
- qq_b4_packets_free(qd);
- qq_sendqueue_free(qd);
- qq_group_packets_free(qd);
- qq_group_free_all(qd);
- qq_add_buddy_request_free(qd);
- qq_info_query_free(qd);
- qq_buddies_list_free(gc->account, qd);
-}
-
-static void no_one_calls(gpointer data, gint source, PurpleInputCondition cond)
-{
- struct PHB *phb = data;
- socklen_t len;
- int error=0, ret;
-
- purple_debug_info("proxy", "Connected.\n");
-
- len = sizeof(error);
-
- /*
- * getsockopt after a non-blocking connect returns -1 if something is
- * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
- * error holds what connect would have returned if it blocked until now.
- * Thus, error == 0 is success, error == EINPROGRESS means "try again",
- * and anything else is a real error.
- *
- * (error == EINPROGRESS can happen after a select because the kernel can
- * be overly optimistic sometimes. select is just a hint that you might be
- * able to do something.)
- */
- ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len);
- if (ret == 0 && error == EINPROGRESS)
- return; /* we'll be called again later */
- if (ret < 0 || error != 0) {
- if(ret!=0)
- error = errno;
- close(source);
- purple_input_remove(phb->inpa);
-
- purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error));
-
- phb->func(phb->data, -1, _("Unable to connect"));
- return;
- }
-
- purple_input_remove(phb->inpa);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, source, NULL);
- }
-
- g_free(phb->host);
- g_free(phb);
-}
-
-/* returns -1 if fails, otherwise returns the file handle */
-static gint _qq_proxy_none(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
-{
- gint fd = -1;
- int flags;
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Using UDP without proxy\n");
- fd = socket(PF_INET, SOCK_DGRAM, 0);
-
- if (fd < 0) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ Redirect",
- "Unable to create socket: %s\n", g_strerror(errno));
- return -1;
- }
-
- /* we use non-blocking mode to speed up connection */
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-
- /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/
- *
- * If a UDP socket is unconnected, which is the normal state after a
- * bind() call, then send() or write() are not allowed, since no
- * destination is available; only sendto() can be used to send data.
- *
- * Calling connect() on the socket simply records the specified address
- * and port number as being the desired communications partner. That
- * means that send() or write() are now allowed; they use the destination
- * address and port given on the connect call as the destination of packets.
- */
- if (connect(fd, addr, addrlen) < 0) {
- /* [EINPROGRESS]
- * The socket is marked as non-blocking and the connection cannot be
- * completed immediately. It is possible to select for completion by
- * selecting the socket for writing.
- * [EINTR]
- * A signal interrupted the call.
- * The connection is established asynchronously.
- */
- if ((errno == EINPROGRESS) || (errno == EINTR)) {
- purple_debug_warning("QQ", "Connect in asynchronous mode.\n");
- phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, no_one_calls, phb);
- } else {
- purple_debug_error("QQ", "Connection failed: %s\n", g_strerror(errno));
- close(fd);
- return -1;
- } /* if errno */
- } else { /* connect returns 0 */
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n");
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
- phb->func(phb->data, fd, NULL);
- }
-
- return fd;
-}
-
-static void _qq_proxy_resolved(GSList *hosts, gpointer data, const char *error_message)
-{
- struct PHB *phb = (struct PHB *) data;
- struct sockaddr_in addr;
- gint addr_size, ret = -1;
-
- if(_qq_fill_host(hosts, &addr, &addr_size))
- ret = qq_proxy_socks5(phb, (struct sockaddr *) &addr, addr_size);
-
- if (ret < 0) {
- phb->func(phb->data, -1, _("Unable to connect"));
- g_free(phb->host);
- g_free(phb);
- }
-}
-
-static void _qq_server_resolved(GSList *hosts, gpointer data, const char *error_message)
-{
- struct PHB *phb = (struct PHB *) data;
- PurpleConnection *gc = (PurpleConnection *) phb->data;
- qq_data *qd = (qq_data *) gc->proto_data;
- struct sockaddr_in addr;
- gint addr_size, ret = -1;
-
- if(_qq_fill_host(hosts, &addr, &addr_size)) {
- switch (purple_proxy_info_get_type(phb->gpi)) {
- case PURPLE_PROXY_NONE:
- ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size);
- break;
- case PURPLE_PROXY_SOCKS5:
- ret = 0;
- if (purple_proxy_info_get_host(phb->gpi) == NULL ||
- purple_proxy_info_get_port(phb->gpi) == 0) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Use of socks5 proxy selected but host or port info doesn't exist.\n");
- ret = -1;
- } else {
- /* as the destination is always QQ server during the session,
- * we can set dest_sin here, instead of _qq_s5_canread_again */
- memcpy(&qd->dest_sin, &addr, addr_size);
- if (purple_dnsquery_a(purple_proxy_info_get_host(phb->gpi),
- purple_proxy_info_get_port(phb->gpi),
- _qq_proxy_resolved, phb) == NULL)
- ret = -1;
- }
- break;
- default:
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Proxy type %i is unsupported, not using a proxy.\n",
- purple_proxy_info_get_type(phb->gpi));
- ret = _qq_proxy_none(phb, (struct sockaddr *) &addr, addr_size);
- }
- }
-
- if (ret < 0) {
- phb->func(gc, -1, _("Unable to connect"));
- g_free(phb->host);
- g_free(phb);
- }
-}
-
-/* returns -1 if dns lookup fails, otherwise returns 0 */
-static gint _qq_udp_proxy_connect(PurpleAccount *account,
- const gchar *server, guint16 port,
- void callback(gpointer, gint, const gchar *error_message),
- PurpleConnection *gc)
-{
- PurpleProxyInfo *info;
- struct PHB *phb;
- qq_data *qd = (qq_data *) gc->proto_data;
-
- g_return_val_if_fail(gc != NULL && qd != NULL, -1);
-
- info = purple_proxy_get_setup(account);
-
- phb = g_new0(struct PHB, 1);
- phb->host = g_strdup(server);
- phb->port = port;
- phb->account = account;
- phb->gpi = info;
- phb->func = callback;
- phb->data = gc;
- qd->proxy_type = purple_proxy_info_get_type(phb->gpi);
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Choosing proxy type %d\n",
- purple_proxy_info_get_type(phb->gpi));
-
- if (purple_dnsquery_a(server, port, _qq_server_resolved, phb) == NULL) {
- phb->func(gc, -1, _("Unable to connect"));
- g_free(phb->host);
- g_free(phb);
- return -1;
- } else {
- return 0;
- }
-}
-
-/* QQ connection via UDP/TCP.
- * I use Purple proxy function to provide TCP proxy support,
- * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */
-static gint _proxy_connect_full (PurpleAccount *account, const gchar *host, guint16 port,
- PurpleProxyConnectFunction func, gpointer data, gboolean use_tcp)
-{
- PurpleConnection *gc;
- qq_data *qd;
-
- gc = purple_account_get_connection(account);
- qd = (qq_data *) gc->proto_data;
- qd->server_ip = g_strdup(host);
- qd->server_port = port;
-
- if(use_tcp)
- return (purple_proxy_connect(NULL, account, host, port, func, data) == NULL);
- else
- return _qq_udp_proxy_connect(account, host, port, func, data);
-}
-
-/* establish a generic QQ connection
- * TCP/UDP, and direct/redirected */
-gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port,
- gboolean use_tcp, gboolean is_redirect)
-{
- PurpleConnection *gc;
- qq_data *qd;
-
- g_return_val_if_fail(host != NULL, -1);
- g_return_val_if_fail(port > 0, -1);
-
- gc = purple_account_get_connection(account);
- g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
-
- if (is_redirect)
- _qq_common_clean(gc);
-
- qd = (qq_data *) gc->proto_data;
- qd->before_login_packets = g_queue_new();
-
- return _proxy_connect_full(account, host, port, _qq_got_login, gc, use_tcp);
-}
-
-/* clean up the given QQ connection and free all resources */
-void qq_disconnect(PurpleConnection *gc)
-{
- qq_data *qd;
-
- g_return_if_fail(gc != NULL);
-
- _qq_common_clean(gc);
-
- qd = gc->proto_data;
- g_free(qd->inikey);
- g_free(qd->pwkey);
- g_free(qd->session_key);
- g_free(qd->session_md5);
- g_free(qd->my_ip);
- g_free(qd);
-
- gc->proto_data = NULL;
-}
-
-/* send packet with proxy support */
-gint qq_proxy_write(qq_data *qd, guint8 *data, gint len)
-{
- guint8 *buf;
- gint ret;
-
- g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && len > 0, -1);
-
- /* TCP sock5 may be processed twice
- * so we need to check qd->use_tcp as well */
- if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */
- buf = g_newa(guint8, len + 10);
- buf[0] = 0x00;
- buf[1] = 0x00; /* reserved */
- buf[2] = 0x00; /* frag */
- buf[3] = 0x01; /* type */
- g_memmove(buf + 4, &(qd->dest_sin.sin_addr.s_addr), 4);
- g_memmove(buf + 8, &(qd->dest_sin.sin_port), 2);
- g_memmove(buf + 10, data, len);
- errno = 0;
- ret = send(qd->fd, buf, len + 10, 0);
- } else {
- errno = 0;
- ret = send(qd->fd, data, len, 0);
- }
- if (ret == -1)
- purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
-
- return ret;
-}
-
-/* read packet input with proxy support */
-gint qq_proxy_read(qq_data *qd, guint8 *data, gint len)
-{
- guint8 *buf;
- gint bytes;
- buf = g_newa(guint8, MAX_PACKET_SIZE + 10);
-
- g_return_val_if_fail(qd != NULL && data != NULL && len > 0, -1);
- g_return_val_if_fail(qd->fd > 0, -1);
-
- bytes = read(qd->fd, buf, len + 10);
- if (bytes < 0)
- return -1;
-
- if ((!qd->use_tcp) && qd->proxy_type == PURPLE_PROXY_SOCKS5) { /* UDP sock5 */
- if (bytes < 10)
- return -1;
- bytes -= 10;
- g_memmove(data, buf + 10, bytes); /* cut off the header */
- } else {
- g_memmove(data, buf, bytes);
- }
-
- return bytes;
-}
diff --git a/libpurple/protocols/qq/qq_proxy.h b/libpurple/protocols/qq/qq_proxy.h
deleted file mode 100644
index 4358eab853..0000000000
--- a/libpurple/protocols/qq/qq_proxy.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * @file qq_proxy.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#ifndef _QQ_PROXY_H
-#define _QQ_PROXY_H
-
-#include <glib.h>
-#include "dnsquery.h"
-#include "proxy.h"
-
-#include "qq.h"
-
-#define QQ_CONNECT_STEPS 2 /* steps in connection */
-
-struct PHB {
- PurpleProxyConnectFunction func;
- gpointer data;
- gchar *host;
- gint port;
- gint inpa;
- PurpleProxyInfo *gpi;
- PurpleAccount *account;
- gint udpsock;
- gpointer sockbuf;
-};
-
-gint qq_proxy_read(qq_data *qd, guint8 *data, gint len);
-gint qq_proxy_write(qq_data *qd, guint8 *data, gint len);
-
-gint qq_connect(PurpleAccount *account, const gchar *host, guint16 port, gboolean use_tcp, gboolean is_redirect);
-void qq_disconnect(PurpleConnection *gc);
-
-void _qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
-
-#endif
diff --git a/libpurple/protocols/qq/qq_trans.c b/libpurple/protocols/qq/qq_trans.c
new file mode 100644
index 0000000000..77ee7d7459
--- /dev/null
+++ b/libpurple/protocols/qq/qq_trans.c
@@ -0,0 +1,290 @@
+/**
+ * @file qq_trans.c
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#include "internal.h"
+
+#include "connection.h"
+#include "debug.h"
+#include "notify.h"
+#include "prefs.h"
+#include "request.h"
+
+#include "header_info.h"
+#include "qq_network.h"
+#include "qq_process.h"
+#include "qq_trans.h"
+
+#define QQ_RESEND_MAX 3 /* max resend per packet */
+
+qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq)
+{
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
+
+ if (qd->transactions == NULL) {
+ return NULL;
+ }
+
+ next = qd->transactions;
+ while( (curr = next) ) {
+ next = curr->next;
+
+ trans = (qq_transaction *) (curr->data);
+ if(trans->cmd == cmd && trans->seq == seq) {
+ if (trans->rcved_times == 0) {
+ trans->scan_times = 0;
+ }
+ trans->rcved_times++;
+ if (qq_trans_is_server(trans) && qq_trans_is_dup(trans)) {
+ /* server may not get our confirm reply before, send reply again*/
+ if (trans->data != NULL && trans->data_len > 0) {
+ qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len);
+ }
+ }
+ return trans;
+ }
+ }
+
+ return NULL;
+}
+
+gboolean qq_trans_is_server(qq_transaction *trans)
+{
+ g_return_val_if_fail(trans != NULL, FALSE);
+
+ if (trans->flag & QQ_TRANS_IS_SERVER)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean qq_trans_is_dup(qq_transaction *trans)
+{
+ g_return_val_if_fail(trans != NULL, TRUE);
+
+ if (trans->rcved_times > 1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Remove a packet with seq from send trans */
+static void trans_remove(qq_data *qd, qq_transaction *trans)
+{
+ g_return_if_fail(qd != NULL && trans != NULL);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS",
+ "Remove [%s%05d] retry %d rcved %d scan %d %s\n",
+ (trans->flag & QQ_TRANS_IS_SERVER) ? "SRV-" : "",
+ trans->seq,
+ trans->send_retries, trans->rcved_times, trans->scan_times,
+ qq_get_cmd_desc(trans->cmd));
+
+ if (trans->data) g_free(trans->data);
+ qd->transactions = g_list_remove(qd->transactions, trans);
+ g_free(trans);
+}
+
+void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ qq_transaction *trans = g_new0(qq_transaction, 1);
+
+ g_return_if_fail(trans != NULL);
+
+ trans->flag = 0;
+ if (cmd == QQ_CMD_TOKEN || cmd == QQ_CMD_LOGIN || cmd == QQ_CMD_KEEP_ALIVE) {
+ trans->flag |= QQ_TRANS_CLI_IMPORT;
+ }
+ trans->fd = qd->fd;
+ trans->cmd = cmd;
+ trans->seq = seq;
+ trans->send_retries = QQ_RESEND_MAX;
+ trans->rcved_times = 0;
+ trans->scan_times = 0;
+ trans->data = NULL;
+ trans->data_len = 0;
+ if (data != NULL && data_len > 0) {
+ trans->data = g_memdup(data, data_len); /* don't use g_strdup, may have 0x00 */
+ trans->data_len = data_len;
+ }
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+ "Add client cmd, seq = %d, data = %p, len = %d\n",
+ trans->seq, trans->data, trans->data_len);
+ qd->transactions = g_list_append(qd->transactions, trans);
+}
+
+void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ qq_transaction *trans = g_new0(qq_transaction, 1);
+
+ g_return_if_fail(trans != NULL);
+
+ trans->flag = QQ_TRANS_IS_SERVER;
+ if ( !qd->logged_in ) {
+ trans->flag |= QQ_TRANS_BEFORE_LOGIN;
+ }
+ trans->fd = qd->fd;
+ trans->cmd = cmd;
+ trans->seq = seq;
+ trans->send_retries = 0;
+ trans->rcved_times = 1;
+ trans->scan_times = 0;
+ trans->data = NULL;
+ trans->data_len = 0;
+ if (data != NULL && data_len > 0) {
+ trans->data = g_memdup(data, data_len); /* don't use g_strdup, may have 0x00 */
+ trans->data_len = data_len;
+ }
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+ "Add server cmd, seq = %d, data = %p, len = %d\n",
+ trans->seq, trans->data, trans->data_len);
+ qd->transactions = g_list_append(qd->transactions, trans);
+}
+
+void qq_trans_process_before_login(qq_data *qd)
+{
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
+
+ g_return_if_fail(qd != NULL);
+
+ next = qd->transactions;
+ while( (curr = next) ) {
+ next = curr->next;
+ trans = (qq_transaction *) (curr->data);
+ /* purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
+
+ if ( !(trans->flag & QQ_TRANS_IS_SERVER) ) {
+ continue;
+ }
+ if ( !(trans->flag & QQ_TRANS_BEFORE_LOGIN) ) {
+ continue;
+ }
+ // set QQ_TRANS_BEFORE_LOGIN off
+ trans->flag &= ~QQ_TRANS_BEFORE_LOGIN;
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+ "Process server cmd before login, seq %d, data %p, len %d, send_retries %d\n",
+ trans->seq, trans->data, trans->data_len, trans->send_retries);
+
+ qq_proc_cmd_reply(qd->gc, trans->seq, trans->cmd, trans->data, trans->data_len);
+ }
+
+ /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+ return;
+}
+
+gboolean qq_trans_scan(qq_data *qd)
+{
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
+
+ g_return_val_if_fail(qd != NULL, FALSE);
+
+ next = qd->transactions;
+ while( (curr = next) ) {
+ next = curr->next;
+ trans = (qq_transaction *) (curr->data);
+ /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan [%d]\n", trans->seq); */
+
+ if (trans->flag & QQ_TRANS_BEFORE_LOGIN) {
+ /* keep server cmd before login*/
+ continue;
+ }
+
+ trans->scan_times++;
+ if (trans->scan_times <= 1) {
+ /* skip in 10 seconds */
+ continue;
+ }
+
+ if (trans->rcved_times > 0) {
+ /* Has been received */
+ trans_remove(qd, trans);
+ continue;
+ }
+
+ if (trans->flag & QQ_TRANS_IS_SERVER) {
+ continue;
+ }
+
+ /* Never get reply */
+ trans->send_retries--;
+ if (trans->send_retries <= 0) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ_TRANS",
+ "[%d] %s is lost.\n",
+ trans->seq, qq_get_cmd_desc(trans->cmd));
+ if (trans->flag & QQ_TRANS_CLI_IMPORT) {
+ return TRUE;
+ }
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+ "Lost [%d] %s, data %p, len %d, retries %d\n",
+ trans->seq, qq_get_cmd_desc(trans->cmd),
+ trans->data, trans->data_len, trans->send_retries);
+ trans_remove(qd, trans);
+ continue;
+ }
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+ "Resend [%d] %s data %p, len %d, send_retries %d\n",
+ trans->seq, qq_get_cmd_desc(trans->cmd),
+ trans->data, trans->data_len, trans->send_retries);
+ qq_send_data(qd, trans->cmd, trans->seq, FALSE, trans->data, trans->data_len);
+ }
+
+ /* purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Scan finished\n"); */
+ return FALSE;
+}
+
+/* clean up send trans and free all contents */
+void qq_trans_remove_all(qq_data *qd)
+{
+ GList *curr;
+ GList *next;
+ qq_transaction *trans;
+ gint count = 0;
+
+ curr = qd->transactions;
+ while(curr) {
+ next = curr->next;
+
+ trans = (qq_transaction *) (curr->data);
+ /*
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ_TRANS",
+ "Remove to transaction, seq = %d, buf = %p, len = %d\n",
+ trans->seq, trans->buf, trans->len);
+ */
+ trans_remove(qd, trans);
+
+ count++;
+ curr = next;
+ }
+ g_list_free(qd->transactions);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ_TRANS", "Free all %d packets\n", count);
+}
diff --git a/libpurple/protocols/qq/qq_trans.h b/libpurple/protocols/qq/qq_trans.h
new file mode 100644
index 0000000000..a0afae0821
--- /dev/null
+++ b/libpurple/protocols/qq/qq_trans.h
@@ -0,0 +1,61 @@
+/**
+ * @file qq_trans.h
+ *
+ * purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here. Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ */
+
+#ifndef _QQ_SEND_QUEUE_H_
+#define _QQ_SEND_QUEUE_H_
+
+#include <glib.h>
+#include "qq.h"
+
+enum {
+ QQ_TRANS_IS_SERVER = 0x01, /* Is server command or client command */
+ /* prefix QQ_TRANS_CLI is for client command*/
+ QQ_TRANS_CLI_EMERGE = 0x02, /* send at once; or may wait for next reply*/
+ QQ_TRANS_CLI_IMPORT = 0x04, /* Only notice if not get reply; or resend, disconn if reties get 0*/
+ QQ_TRANS_BEFORE_LOGIN = 0x08, /* server command before login*/
+};
+
+typedef struct _qq_transaction {
+ guint8 flag;
+ guint16 seq;
+ guint16 cmd;
+ guint8 *data;
+ gint data_len;
+
+ gint fd;
+ gint send_retries;
+ gint rcved_times;
+ gint scan_times;
+} qq_transaction;
+
+qq_transaction *qq_trans_find_rcved(qq_data *qd, guint16 cmd, guint16 seq);
+gboolean qq_trans_is_server(qq_transaction *trans) ;
+gboolean qq_trans_is_dup(qq_transaction *trans);
+void qq_trans_add_client_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+void qq_trans_add_server_cmd(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+void qq_trans_process_before_login(qq_data *qd);
+gboolean qq_trans_scan(qq_data *qd);
+void qq_trans_remove_all(qq_data *qd);
+
+#endif
diff --git a/libpurple/protocols/qq/recv_core.c b/libpurple/protocols/qq/recv_core.c
deleted file mode 100644
index 876f0a178b..0000000000
--- a/libpurple/protocols/qq/recv_core.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/**
- * @file recv_core.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "debug.h"
-#include "internal.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "buddy_status.h"
-#include "char_conv.h"
-#include "crypt.h"
-#include "group_network.h"
-#include "header_info.h"
-#include "keep_alive.h"
-#include "im.h"
-#include "login_logout.h"
-#include "packet_parse.h"
-#include "qq_proxy.h"
-#include "recv_core.h"
-#include "sendqueue.h"
-#include "sys_msg.h"
-#include "utils.h"
-
-typedef struct _packet_before_login packet_before_login;
-typedef struct _qq_recv_msg_header qq_recv_msg_header;
-
-struct _packet_before_login {
- guint8 *buf;
- gint len;
-};
-
-struct _qq_recv_msg_header {
- guint8 header_tag;
- guint16 source_tag;
- guint16 cmd;
- guint16 seq; /* can be ack_seq or send_seq, depends on cmd */
-};
-
-/* check whether one sequence number is duplicated or not
- * return TRUE if it is duplicated, otherwise FALSE */
-static gboolean _qq_check_packet_set_window(guint16 seq, PurpleConnection *gc)
-{
- qq_data *qd;
- guint8 *byte, mask;
-
- qd = (qq_data *) gc->proto_data;
- byte = &(qd->window[seq / 8]);
- mask = (1 << (seq % 8));
-
- if ((*byte) & mask)
- return TRUE; /* check mask */
- (*byte) |= mask;
- return FALSE; /* set mask */
-}
-
-/* default process, decrypt and dump */
-static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, PurpleConnection *gc)
-{
- qq_data *qd;
- guint8 *data;
- gchar *msg_utf8;
- gint len;
-
- g_return_if_fail(buf != NULL && buf_len != 0);
-
- qd = (qq_data *) gc->proto_data;
- len = buf_len;
- data = g_newa(guint8, len);
- msg_utf8 = NULL;
-
- _qq_show_packet("Processing unknown packet", buf, len);
- if (qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
- gchar *hex_dump = hex_dump_to_str(data, len);
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s",
- seq, qq_get_cmd_desc(cmd), buf_len, hex_dump);
- g_free(hex_dump);
- try_dump_as_gbk(data, len);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
- }
-}
-
-/* process the incoming packet from qq_pending */
-static void _qq_packet_process(guint8 *buf, gint buf_len, PurpleConnection *gc)
-{
- qq_data *qd;
- gint len, bytes_expected, bytes_read;
- guint16 buf_len_read; /* two bytes in the begining of TCP packet */
- guint8 *cursor;
- qq_recv_msg_header header;
- packet_before_login *b4_packet;
-
- g_return_if_fail(buf != NULL && buf_len > 0);
-
- qd = (qq_data *) gc->proto_data;
- bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH;
-
- if (buf_len < bytes_expected) {
- gchar *hex_dump = hex_dump_to_str(buf, buf_len);
- purple_debug(PURPLE_DEBUG_ERROR,
- "QQ", "Received packet is too short, dump and drop\n%s", hex_dump);
- g_free(hex_dump);
- return;
- }
- /* initialize */
- cursor = buf;
- bytes_read = 0;
-
- /* QQ TCP packet returns first 2 bytes the length of this packet */
- if (qd->use_tcp) {
- bytes_read += read_packet_w(buf, &cursor, buf_len, &buf_len_read);
- if (buf_len_read != buf_len) { /* wrong */
- purple_debug
- (PURPLE_DEBUG_ERROR,
- "QQ",
- "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read);
- buf_len = buf_len_read; /* we believe header is more accurate */
- }
- }
-
- /* now goes the normal QQ packet as UDP packet */
- bytes_read += read_packet_b(buf, &cursor, buf_len, &header.header_tag);
- bytes_read += read_packet_w(buf, &cursor, buf_len, &header.source_tag);
- bytes_read += read_packet_w(buf, &cursor, buf_len, &header.cmd);
- bytes_read += read_packet_w(buf, &cursor, buf_len, &header.seq);
-
- if (bytes_read != bytes_expected) { /* read error */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail reading packet header, expect %d bytes, read %d bytes\n",
- bytes_expected, bytes_read);
- return;
- }
-
- if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) {
- gchar *hex_dump = hex_dump_to_str(buf, buf_len);
- purple_debug(PURPLE_DEBUG_ERROR,
- "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump);
- g_free(hex_dump);
- return;
- }
-
- if (QQ_DEBUG)
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "==> [%05d] %s, from (%s)\n",
- header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag));
-
- if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) {
- if (!qd->logged_in) { /* packets before login */
- b4_packet = g_new0(packet_before_login, 1);
- /* must duplicate, buffer will be freed after exiting this function */
- b4_packet->buf = g_memdup(buf, buf_len);
- b4_packet->len = buf_len;
- if (qd->before_login_packets == NULL)
- qd->before_login_packets = g_queue_new();
- g_queue_push_head(qd->before_login_packets, b4_packet);
- return; /* do not process it now */
- } else if (!g_queue_is_empty(qd->before_login_packets)) {
- /* logged_in, but we have packets before login */
- b4_packet = (packet_before_login *)
- g_queue_pop_head(qd->before_login_packets);
- _qq_packet_process(b4_packet->buf, b4_packet->len, gc);
- /* in fact this is a recursive call,
- * all packets before login will be processed before goes on */
- g_free(b4_packet->buf); /* the buf is duplicated, need to be freed */
- g_free(b4_packet);
- }
- }
-
- /* this is the length of all the encrypted data (also remove tail tag */
- len = buf_len - (bytes_read) - 1;
-
- /* whether it is an ack */
- switch (header.cmd) {
- case QQ_CMD_RECV_IM:
- case QQ_CMD_RECV_MSG_SYS:
- case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
- /* server intiated packet, we need to send ack and check duplicaion
- * this must be put after processing b4_packet
- * as these packets will be passed in twice */
- if (_qq_check_packet_set_window(header.seq, gc)) {
- purple_debug(PURPLE_DEBUG_WARNING,
- "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd));
- return;
- }
- break;
- default:{ /* ack packet, we need to update sendqueue */
- /* we do not check duplication for server ack */
- qq_sendqueue_remove(qd, header.seq);
- if (QQ_DEBUG)
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "ack [%05d] %s, remove from sendqueue\n",
- header.seq, qq_get_cmd_desc(header.cmd));
- }
- }
-
- /* now process the packet */
- switch (header.cmd) {
- case QQ_CMD_KEEP_ALIVE:
- qq_process_keep_alive_reply(cursor, len, gc);
- break;
- case QQ_CMD_UPDATE_INFO:
- qq_process_modify_info_reply(cursor, len, gc);
- break;
- case QQ_CMD_ADD_FRIEND_WO_AUTH:
- qq_process_add_buddy_reply(cursor, len, header.seq, gc);
- break;
- case QQ_CMD_DEL_FRIEND:
- qq_process_remove_buddy_reply(cursor, len, gc);
- break;
- case QQ_CMD_REMOVE_SELF:
- qq_process_remove_self_reply(cursor, len, gc);
- break;
- case QQ_CMD_BUDDY_AUTH:
- qq_process_add_buddy_auth_reply(cursor, len, gc);
- break;
- case QQ_CMD_GET_USER_INFO:
- qq_process_get_info_reply(cursor, len, gc);
- break;
- case QQ_CMD_CHANGE_ONLINE_STATUS:
- qq_process_change_status_reply(cursor, len, gc);
- break;
- case QQ_CMD_SEND_IM:
- qq_process_send_im_reply(cursor, len, gc);
- break;
- case QQ_CMD_RECV_IM:
- qq_process_recv_im(cursor, len, header.seq, gc);
- break;
- case QQ_CMD_LOGIN:
- qq_process_login_reply(cursor, len, gc);
- break;
- case QQ_CMD_GET_FRIENDS_LIST:
- qq_process_get_buddies_list_reply(cursor, len, gc);
- break;
- case QQ_CMD_GET_FRIENDS_ONLINE:
- qq_process_get_buddies_online_reply(cursor, len, gc);
- break;
- case QQ_CMD_GROUP_CMD:
- qq_process_group_cmd_reply(cursor, len, header.seq, gc);
- break;
- case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
- qq_process_get_all_list_with_group_reply(cursor, len, gc);
- break;
- case QQ_CMD_GET_LEVEL:
- qq_process_get_level_reply(cursor, len, gc);
- break;
- case QQ_CMD_REQUEST_LOGIN_TOKEN:
- qq_process_request_login_token_reply(cursor, len, gc);
- break;
- case QQ_CMD_RECV_MSG_SYS:
- qq_process_msg_sys(cursor, len, header.seq, gc);
- break;
- case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
- qq_process_friend_change_status(cursor, len, gc);
- break;
- default:
- _qq_process_packet_default(cursor, len, header.cmd, header.seq, gc);
- break;
- }
-}
-
-/* clean up the packets before login */
-void qq_b4_packets_free(qq_data *qd)
-{
- packet_before_login *b4_packet;
- g_return_if_fail(qd != NULL);
- /* now clean up my own data structures */
- if (qd->before_login_packets != NULL) {
- while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) {
- g_free(b4_packet->buf);
- g_free(b4_packet);
- }
- g_queue_free(qd->before_login_packets);
- }
-}
-
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond)
-{
- PurpleConnection *gc;
- qq_data *qd;
- guint8 *buf;
- gint len;
-
- gc = (PurpleConnection *) data;
-
- if(cond != PURPLE_INPUT_READ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Socket error"));
- return;
- }
-
- qd = (qq_data *) gc->proto_data;
- buf = g_newa(guint8, MAX_PACKET_SIZE);
-
- /* here we have UDP proxy suppport */
- len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE);
- if (len <= 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Unable to read from socket"));
- return;
- } else {
- _qq_packet_process(buf, len, gc);
- }
-}
diff --git a/libpurple/protocols/qq/send_core.c b/libpurple/protocols/qq/send_core.c
deleted file mode 100644
index dec8205655..0000000000
--- a/libpurple/protocols/qq/send_core.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/**
- * @file send_core.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "debug.h"
-#include "internal.h"
-
-#include "crypt.h"
-#include "header_info.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
-#include "sendqueue.h"
-
-/* create qq packet header with given sequence
- * return the number of bytes in header if succeeds
- * return -1 if there is any error */
-gint _create_packet_head_seq(guint8 *buf, guint8 **cursor,
- PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq)
-{
- qq_data *qd;
- gint bytes_expected, bytes_written;
-
- g_return_val_if_fail(buf != NULL && cursor != NULL && *cursor != NULL, -1);
-
- qd = (qq_data *) gc->proto_data;
- if (is_auto_seq)
- *seq = ++(qd->send_seq);
-
- *cursor = buf;
- bytes_written = 0;
- bytes_expected = (qd->use_tcp) ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH;
-
- /* QQ TCP packet has two bytes in the begining defines packet length
- * so I leave room here for size */
- if (qd->use_tcp)
- bytes_written += create_packet_w(buf, cursor, 0x0000);
-
- /* now comes the normal QQ packet as UDP */
- bytes_written += create_packet_b(buf, cursor, QQ_PACKET_TAG);
- bytes_written += create_packet_w(buf, cursor, QQ_CLIENT);
- bytes_written += create_packet_w(buf, cursor, cmd);
- bytes_written += create_packet_w(buf, cursor, *seq);
-
- if (bytes_written != bytes_expected) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail create qq header, expect %d bytes, written %d bytes\n", bytes_expected, bytes_written);
- bytes_written = -1;
- }
- return bytes_written;
-}
-
-/* for those need ack and resend no ack feed back from server
- * return number of bytes written to the socket,
- * return -1 if there is any error */
-gint _qq_send_packet(PurpleConnection *gc, guint8 *buf, gint len, guint16 cmd)
-{
- qq_data *qd;
- qq_sendpacket *p;
- gint bytes_sent;
- guint8 *cursor;
-
- qd = (qq_data *) gc->proto_data;
-
- if (qd->use_tcp) {
- if (len > MAX_PACKET_SIZE) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "xxx [%05d] %s, %d bytes is too large, do not send\n",
- qd->send_seq, qq_get_cmd_desc(cmd), len);
- return -1;
- } else { /* I update the len for TCP packet */
- cursor = buf;
- create_packet_w(buf, &cursor, len);
- }
- }
-
- bytes_sent = qq_proxy_write(qd, buf, len);
-
- if (bytes_sent >= 0) { /* put to queue, for matching server ACK usage */
- p = g_new0(qq_sendpacket, 1);
- p->fd = qd->fd;
- p->cmd = cmd;
- p->send_seq = qd->send_seq;
- p->resend_times = 0;
- p->sendtime = time(NULL);
- p->buf = g_memdup(buf, len); /* don't use g_strdup, may have 0x00 */
- p->len = len;
- qd->sendqueue = g_list_append(qd->sendqueue, p);
- }
-
- return bytes_sent;
-}
-
-/* send the packet generated with the given cmd and data
- * return the number of bytes sent to socket if succeeds
- * return -1 if there is any error */
-gint qq_send_cmd(PurpleConnection *gc, guint16 cmd,
- gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len)
-{
- qq_data *qd;
- guint8 *buf, *cursor, *encrypted_data;
- guint16 seq_ret;
- gint encrypted_len, bytes_written, bytes_expected, bytes_sent;
-
- qd = (qq_data *) gc->proto_data;
- g_return_val_if_fail(qd->session_key != NULL, -1);
-
- buf = g_newa(guint8, MAX_PACKET_SIZE);
- encrypted_len = len + 16; /* at most 16 bytes more */
- encrypted_data = g_newa(guint8, encrypted_len);
- cursor = buf;
- bytes_written = 0;
-
- qq_encrypt(data, len, qd->session_key, encrypted_data, &encrypted_len);
-
- seq_ret = seq;
- if (_create_packet_head_seq(buf, &cursor, gc, cmd, is_auto_seq, &seq_ret) >= 0) {
- bytes_expected = 4 + encrypted_len + 1;
- bytes_written += create_packet_dw(buf, &cursor, (guint32) qd->uid);
- bytes_written += create_packet_data(buf, &cursor, encrypted_data, encrypted_len);
- bytes_written += create_packet_b(buf, &cursor, QQ_PACKET_TAIL);
- if (bytes_written == bytes_expected) { /* packet OK */
- /* if it does not need ACK, we send ACK manually several times */
- if (need_ack) /* my request, send it */
- bytes_sent = _qq_send_packet(gc, buf, cursor - buf, cmd);
- else /* server's request, send ACK */
- bytes_sent = qq_proxy_write(qd, buf, cursor - buf);
-
- if (QQ_DEBUG)
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "<== [%05d] %s, %d bytes\n", seq_ret, qq_get_cmd_desc(cmd), bytes_sent);
- return bytes_sent;
- } else { /* bad packet */
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "Fail creating packet, expect %d bytes, written %d bytes\n",
- bytes_expected, bytes_written);
- return -1;
- }
- }
-
- return -1;
-}
diff --git a/libpurple/protocols/qq/send_core.h b/libpurple/protocols/qq/send_core.h
deleted file mode 100644
index c93fa0a24a..0000000000
--- a/libpurple/protocols/qq/send_core.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * @file send_core.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#ifndef _QQ_SEND_CORE_H_
-#define _QQ_SEND_CORE_H_
-
-#include <glib.h>
-#include "connection.h"
-
-gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq,
- gboolean need_ack, guint8 *data, gint len);
-gint _qq_send_packet(PurpleConnection * gc, guint8 *buf, gint len, guint16 cmd);
-gint _create_packet_head_seq(guint8 *buf, guint8 **cursor,
- PurpleConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 *seq);
-
-#endif
diff --git a/libpurple/protocols/qq/send_file.c b/libpurple/protocols/qq/send_file.c
index baaebc719b..0f130d4fad 100644
--- a/libpurple/protocols/qq/send_file.c
+++ b/libpurple/protocols/qq/send_file.c
@@ -29,14 +29,14 @@
#include "network.h"
#include "notify.h"
-#include "buddy_status.h"
+#include "buddy_list.h"
#include "crypt.h"
#include "file_trans.h"
#include "header_info.h"
#include "im.h"
-#include "keep_alive.h"
+#include "qq_base.h"
#include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "utils.h"
enum
@@ -103,6 +103,7 @@ static ssize_t _qq_xfer_udp_send(const char *buf, size_t len, PurpleXfer *xfer)
return send(info->sender_fd, buf, len, 0);
}
*/
+
static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer)
{
struct sockaddr_in sin;
@@ -243,42 +244,45 @@ static void qq_show_conn_info(ft_info *info)
g_free(internet_ip_str);
}
-void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info)
+#define QQ_CONN_INFO_LEN 61
+gint qq_get_conn_info(ft_info *info, guint8 *data)
{
- read_packet_data(data, cursor, data_len, info->file_session_key, 16);
- *cursor += 30;
- read_packet_b(data, cursor, data_len, &info->conn_method);
- read_packet_dw(data, cursor, data_len, &info->remote_internet_ip);
- read_packet_w(data, cursor, data_len, &info->remote_internet_port);
- read_packet_w(data, cursor, data_len, &info->remote_major_port);
- read_packet_dw(data, cursor, data_len, &info->remote_real_ip);
- read_packet_w(data, cursor, data_len, &info->remote_minor_port);
+ gint bytes = 0;
+ /* 16 + 30 + 1 + 4 + 2 + 2 + 4 + 2 = 61 */
+ bytes += qq_getdata(info->file_session_key, 16, data + bytes);
+ bytes += 30; /* skip 30 bytes */
+ bytes += qq_get8(&info->conn_method, data + bytes);
+ bytes += qq_get32(&info->remote_internet_ip, data + bytes);
+ bytes += qq_get16(&info->remote_internet_port, data + bytes);
+ bytes += qq_get16(&info->remote_major_port, data + bytes);
+ bytes += qq_get32(&info->remote_real_ip, data + bytes);
+ bytes += qq_get16(&info->remote_minor_port, data + bytes);
qq_show_conn_info(info);
+ return bytes;
}
-gint qq_fill_conn_info(guint8 *raw_data, guint8 **cursor, ft_info *info)
+gint qq_fill_conn_info(guint8 *raw_data, ft_info *info)
{
- gint bytes;
- bytes = 0;
+ gint bytes = 0;
/* 064: connection method, UDP 0x00, TCP 0x03 */
- bytes += create_packet_b (raw_data, cursor, info->conn_method);
+ bytes += qq_put8 (raw_data + bytes, info->conn_method);
/* 065-068: outer ip address of sender (proxy address) */
- bytes += create_packet_dw (raw_data, cursor, info->local_internet_ip);
+ bytes += qq_put32 (raw_data + bytes, info->local_internet_ip);
/* 069-070: sender port */
- bytes += create_packet_w (raw_data, cursor, info->local_internet_port);
+ bytes += qq_put16 (raw_data + bytes, info->local_internet_port);
/* 071-072: the first listening port(TCP doesn't have this part) */
- bytes += create_packet_w (raw_data, cursor, info->local_major_port);
+ bytes += qq_put16 (raw_data + bytes, info->local_major_port);
/* 073-076: real ip */
- bytes += create_packet_dw (raw_data, cursor, info->local_real_ip);
+ bytes += qq_put32 (raw_data + bytes, info->local_real_ip);
/* 077-078: the second listening port */
- bytes += create_packet_w (raw_data, cursor, info->local_minor_port);
+ bytes += qq_put16 (raw_data + bytes, info->local_minor_port);
return bytes;
}
/* fill in the common information of file transfer */
static gint _qq_create_packet_file_header
-(guint8 *raw_data, guint8 **cursor, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack)
+(guint8 *raw_data, guint32 to_uid, guint16 message_type, qq_data *qd, gboolean seq_ack)
{
gint bytes;
time_t now;
@@ -294,42 +298,42 @@ static gint _qq_create_packet_file_header
}
/* 000-003: receiver uid */
- bytes += create_packet_dw (raw_data, cursor, qd->uid);
+ bytes += qq_put32 (raw_data + bytes, qd->uid);
/* 004-007: sender uid */
- bytes += create_packet_dw (raw_data, cursor, to_uid);
+ bytes += qq_put32 (raw_data + bytes, to_uid);
/* 008-009: sender client version */
- bytes += create_packet_w (raw_data, cursor, QQ_CLIENT);
+ bytes += qq_put16 (raw_data + bytes, QQ_CLIENT);
/* 010-013: receiver uid */
- bytes += create_packet_dw (raw_data, cursor, qd->uid);
+ bytes += qq_put32 (raw_data + bytes, qd->uid);
/* 014-017: sender uid */
- bytes += create_packet_dw (raw_data, cursor, to_uid);
+ bytes += qq_put32 (raw_data + bytes, to_uid);
/* 018-033: md5 of (uid+session_key) */
- bytes += create_packet_data (raw_data, cursor, qd->session_md5, 16);
+ bytes += qq_putdata (raw_data + bytes, qd->session_md5, 16);
/* 034-035: message type */
- bytes += create_packet_w (raw_data, cursor, message_type);
+ bytes += qq_put16 (raw_data + bytes, message_type);
/* 036-037: sequence number */
- bytes += create_packet_w (raw_data, cursor, seq);
+ bytes += qq_put16 (raw_data + bytes, seq);
/* 038-041: send time */
- bytes += create_packet_dw (raw_data, cursor, (guint32) now);
+ bytes += qq_put32 (raw_data + bytes, (guint32) now);
/* 042-042: always 0x00 */
- bytes += create_packet_b (raw_data, cursor, 0x00);
+ bytes += qq_put8 (raw_data + bytes, 0x00);
/* 043-043: sender icon */
- bytes += create_packet_b (raw_data, cursor, qd->my_icon);
+ bytes += qq_put8 (raw_data + bytes, qd->my_icon);
/* 044-046: always 0x00 */
- bytes += create_packet_w (raw_data, cursor, 0x0000);
- bytes += create_packet_b (raw_data, cursor, 0x00);
+ bytes += qq_put16 (raw_data + bytes, 0x0000);
+ bytes += qq_put8 (raw_data + bytes, 0x00);
/* 047-047: we use font attr */
- bytes += create_packet_b (raw_data, cursor, 0x01);
+ bytes += qq_put8 (raw_data + bytes, 0x01);
/* 048-051: always 0x00 */
- bytes += create_packet_dw (raw_data, cursor, 0x00000000);
+ bytes += qq_put32 (raw_data + bytes, 0x00000000);
/* 052-062: always 0x00 */
- bytes += create_packet_dw (raw_data, cursor, 0x00000000);
- bytes += create_packet_dw (raw_data, cursor, 0x00000000);
- bytes += create_packet_w (raw_data, cursor, 0x0000);
- bytes += create_packet_b (raw_data, cursor, 0x00);
+ bytes += qq_put32 (raw_data + bytes, 0x00000000);
+ bytes += qq_put32 (raw_data + bytes, 0x00000000);
+ bytes += qq_put16 (raw_data + bytes, 0x0000);
+ bytes += qq_put8 (raw_data + bytes, 0x00);
/* 063: transfer_type, 0x65: FILE 0x6b: FACE */
- bytes += create_packet_b (raw_data, cursor, QQ_FILE_TRANSFER_FILE); /* FIXME */
+ bytes += qq_put8 (raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME */
return bytes;
}
@@ -433,7 +437,7 @@ static void _qq_xfer_init_socket(PurpleXfer *xfer)
static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid, gchar *filename, gint filesize)
{
qq_data *qd;
- guint8 *cursor, *raw_data;
+ guint8 *raw_data;
gchar *filelen_str;
gint filename_len, filelen_strlen, packet_len, bytes;
ft_info *info;
@@ -443,7 +447,7 @@ 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;
@@ -455,27 +459,24 @@ static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid,
packet_len = 82 + filename_len + filelen_strlen;
raw_data = g_newa(guint8, packet_len);
- cursor = raw_data;
+ bytes = 0;
- bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid,
+ bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid,
QQ_FILE_TRANS_REQ, qd, FALSE);
- bytes += qq_fill_conn_info(raw_data, &cursor, info);
+ bytes += qq_fill_conn_info(raw_data + bytes, info);
/* 079: 0x20 */
- bytes += create_packet_b (raw_data, &cursor, 0x20);
+ bytes += qq_put8 (raw_data + bytes, 0x20);
/* 080: 0x1f */
- bytes += create_packet_b (raw_data, &cursor, 0x1f);
+ bytes += qq_put8 (raw_data + bytes, 0x1f);
/* undetermined len: filename */
- bytes += create_packet_data (raw_data, &cursor, (guint8 *) filename,
- filename_len);
+ bytes += qq_putdata (raw_data + bytes, (guint8 *) filename, filename_len);
/* 0x1f */
- bytes += create_packet_b (raw_data, &cursor, 0x1f);
+ bytes += qq_put8 (raw_data + bytes, 0x1f);
/* file length */
- bytes += create_packet_data (raw_data, &cursor, (guint8 *) filelen_str,
- filelen_strlen);
+ bytes += qq_putdata (raw_data + bytes, (guint8 *) filelen_str, filelen_strlen);
if (packet_len == bytes)
- qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
- cursor - raw_data);
+ qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_request",
"%d bytes expected but got %d bytes\n",
@@ -488,7 +489,7 @@ static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid,
static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid)
{
qq_data *qd;
- guint8 *cursor, *raw_data;
+ guint8 *raw_data;
guint16 minor_port;
guint32 real_ip;
gint packet_len, bytes;
@@ -502,22 +503,21 @@ static void _qq_send_packet_file_accept(PurpleConnection *gc, guint32 to_uid)
packet_len = 79;
raw_data = g_newa (guint8, packet_len);
- cursor = raw_data;
+ bytes = 0;
minor_port = info->local_minor_port;
real_ip = info->local_real_ip;
info->local_minor_port = 0;
info->local_real_ip = 0;
- bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE);
- bytes += qq_fill_conn_info(raw_data, &cursor, info);
+ bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_ACC_UDP, qd, TRUE);
+ bytes += qq_fill_conn_info(raw_data + bytes, info);
info->local_minor_port = minor_port;
info->local_real_ip = real_ip;
if (packet_len == bytes)
- qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
- cursor - raw_data);
+ qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_accept",
"%d bytes expected but got %d bytes\n",
@@ -529,7 +529,7 @@ static void _qq_send_packet_file_notifyip(PurpleConnection *gc, guint32 to_uid)
PurpleXfer *xfer;
ft_info *info;
qq_data *qd;
- guint8 *cursor, *raw_data;
+ guint8 *raw_data;
gint packet_len, bytes;
qd = (qq_data *) gc->proto_data;
@@ -538,14 +538,13 @@ static void _qq_send_packet_file_notifyip(PurpleConnection *gc, guint32 to_uid)
packet_len = 79;
raw_data = g_newa (guint8, packet_len);
- cursor = raw_data;
+ bytes = 0;
purple_debug(PURPLE_DEBUG_INFO, "QQ", "<== sending qq file notify ip packet\n");
- bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
- bytes += qq_fill_conn_info(raw_data, &cursor, info);
+ bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_NOTIFY, qd, TRUE);
+ bytes += qq_fill_conn_info(raw_data + bytes, info);
if (packet_len == bytes)
- qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
- cursor - raw_data);
+ qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file_notify",
"%d bytes expected but got %d bytes\n",
@@ -560,7 +559,7 @@ static void _qq_send_packet_file_notifyip(PurpleConnection *gc, guint32 to_uid)
static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid)
{
qq_data *qd;
- guint8 *cursor, *raw_data;
+ guint8 *raw_data;
gint packet_len, bytes;
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_reject", "start");
@@ -568,14 +567,12 @@ static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid)
packet_len = 64;
raw_data = g_newa (guint8, packet_len);
- cursor = raw_data;
bytes = 0;
- bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
+ bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_DENY_UDP, qd, TRUE);
if (packet_len == bytes)
- qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
- cursor - raw_data);
+ qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
"%d bytes expected but got %d bytes\n",
@@ -586,7 +583,7 @@ static void _qq_send_packet_file_reject (PurpleConnection *gc, guint32 to_uid)
static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid)
{
qq_data *qd;
- guint8 *cursor, *raw_data;
+ guint8 *raw_data;
gint packet_len, bytes;
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "start\n");
@@ -594,17 +591,15 @@ static void _qq_send_packet_file_cancel (PurpleConnection *gc, guint32 to_uid)
packet_len = 64;
raw_data = g_newa (guint8, packet_len);
- cursor = raw_data;
bytes = 0;
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before create header\n");
- bytes = _qq_create_packet_file_header(raw_data, &cursor, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
+ bytes += _qq_create_packet_file_header(raw_data + bytes, to_uid, QQ_FILE_TRANS_CANCEL, qd, TRUE);
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "end create header\n");
if (packet_len == bytes) {
purple_debug(PURPLE_DEBUG_INFO, "_qq_send_packet_file_cancel", "before send cmd\n");
- qq_send_cmd (gc, QQ_CMD_SEND_IM, TRUE, 0, TRUE, raw_data,
- cursor - raw_data);
+ qq_send_cmd (qd, QQ_CMD_SEND_IM, raw_data, bytes);
}
else
purple_debug (PURPLE_DEBUG_INFO, "qq_send_packet_file",
@@ -688,7 +683,7 @@ static void _qq_xfer_recv_init(PurpleXfer *xfer)
}
/* process reject im for file transfer request */
-void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len,
+void qq_process_recv_file_reject (guint8 *data, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gchar *msg, *filename;
@@ -698,11 +693,13 @@ void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len,
qd = (qq_data *) gc->proto_data;
g_return_if_fail (qd->xfer != NULL);
+ /* border has been checked before
if (*cursor >= (data + data_len - 1)) {
purple_debug (PURPLE_DEBUG_WARNING, "QQ",
"Received file reject message is empty\n");
return;
}
+ */
filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
msg = g_strdup_printf(_("%d has declined the file %s"),
sender_uid, filename);
@@ -715,7 +712,7 @@ void qq_process_recv_file_reject (guint8 *data, guint8 **cursor, gint data_len,
}
/* process cancel im for file transfer request */
-void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len,
+void qq_process_recv_file_cancel (guint8 *data, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gchar *msg, *filename;
@@ -726,11 +723,13 @@ void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len,
g_return_if_fail (qd->xfer != NULL
&& purple_xfer_get_filename(qd->xfer) != NULL);
+ /* border has been checked before
if (*cursor >= (data + data_len - 1)) {
purple_debug (PURPLE_DEBUG_WARNING, "QQ",
"Received file reject message is empty\n");
return;
}
+ */
filename = strrchr(purple_xfer_get_local_filename(qd->xfer), '/') + 1;
msg = g_strdup_printf
(_("%d canceled the transfer of %s"),
@@ -744,27 +743,26 @@ void qq_process_recv_file_cancel (guint8 *data, guint8 **cursor, gint data_len,
}
/* process accept im for file transfer request */
-void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection *gc)
+void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc)
{
qq_data *qd;
+ gint bytes;
ft_info *info;
PurpleXfer *xfer;
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
xfer = qd->xfer;
+ info = (ft_info *) qd->xfer->data;
- if (*cursor >= (data + data_len - 1)) {
+ if (data_len <= 30 + QQ_CONN_INFO_LEN) {
purple_debug (PURPLE_DEBUG_WARNING, "QQ",
"Received file reject message is empty\n");
return;
}
- info = (ft_info *) qd->xfer->data;
-
- *cursor = data + 18 + 12;
- qq_get_conn_info(data, cursor, data_len, info);
+ bytes = 18 + 12; /* skip 30 bytes */
+ qq_get_conn_info(info, data + bytes);
_qq_xfer_init_socket(qd->xfer);
_qq_xfer_init_udp_channel(info);
@@ -772,8 +770,7 @@ void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len,
}
/* process request from buddy's im for file transfer request */
-void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection * gc)
+void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection * gc)
{
qq_data *qd;
PurpleXfer *xfer;
@@ -781,25 +778,27 @@ void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len,
ft_info *info;
PurpleBuddy *b;
qq_buddy *q_bud;
+ gint bytes;
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
- if (*cursor >= (data + data_len - 1)) {
- purple_debug (PURPLE_DEBUG_WARNING, "QQ",
- "Received file reject message is empty\n");
- return;
- }
-
- info = g_new0(ft_info, 1);
- info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
+ info = g_newa(ft_info, 1);
+ info->local_internet_ip = qd->my_ip.s_addr;
info->local_internet_port = qd->my_port;
info->local_real_ip = 0x00000000;
info->to_uid = sender_uid;
- read_packet_w(data, cursor, data_len, &(info->send_seq));
+
+ if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
+ purple_debug (PURPLE_DEBUG_WARNING, "QQ",
+ "Received file request message is empty\n");
+ return;
+ }
+ bytes = 0;
+ bytes += qq_get16(&(info->send_seq), data + bytes);
- *cursor = data + 18 + 12;
- qq_get_conn_info(data, cursor, data_len, info);
+ bytes += 18 + 12; /* skip 30 bytes */
+ bytes += qq_get_conn_info(info, data + bytes);
fileinfo = g_strsplit((gchar *) (data + 81 + 12), "\x1f", 2);
g_return_if_fail (fileinfo != NULL && fileinfo[0] != NULL && fileinfo[1] != NULL);
@@ -815,11 +814,11 @@ void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len,
q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
if (q_bud) {
if(0 != info->remote_real_ip) {
- g_memmove(q_bud->ip, &info->remote_real_ip, 4);
+ g_memmove(&(q_bud->ip), &info->remote_real_ip, sizeof(q_bud->ip));
q_bud->port = info->remote_minor_port;
}
else if (0 != info->remote_internet_ip) {
- g_memmove(q_bud->ip, &info->remote_internet_ip, 4);
+ g_memmove(&(q_bud->ip), &info->remote_internet_ip, sizeof(q_bud->ip));
q_bud->port = info->remote_major_port;
}
@@ -832,7 +831,7 @@ void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len,
}
else
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in my friendlist\n", sender_uid);
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "buddy %d is not in list\n", sender_uid);
g_free(sender_name);
g_strfreev(fileinfo);
@@ -880,9 +879,10 @@ static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, PurpleInputC
*/
}
-void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len,
+void qq_process_recv_file_notify(guint8 *data, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
+ gint bytes;
qq_data *qd;
ft_info *info;
PurpleXfer *xfer;
@@ -890,19 +890,19 @@ void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len,
g_return_if_fail (data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
- if (*cursor >= (data + data_len - 1)) {
+ xfer = qd->xfer;
+ info = (ft_info *) qd->xfer->data;
+ if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
purple_debug (PURPLE_DEBUG_WARNING, "QQ",
"Received file notify message is empty\n");
return;
}
+
+ bytes = 0;
+ bytes += qq_get16(&(info->send_seq), data + bytes);
- xfer = qd->xfer;
- info = (ft_info *) qd->xfer->data;
- /* FIXME */
- read_packet_w(data, cursor, data_len, &(info->send_seq));
-
- *cursor = data + 18 + 12;
- qq_get_conn_info(data, cursor, data_len, info);
+ bytes += 18 + 12;
+ bytes += qq_get_conn_info(info, data + bytes);
_qq_xfer_init_udp_channel(info);
@@ -938,7 +938,7 @@ void qq_send_file(PurpleConnection *gc, const char *who, const char *file)
/*
static void qq_send_packet_request_key(PurpleConnection *gc, guint8 key)
{
- qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, TRUE, 0, TRUE, &key, 1);
+ qq_send_cmd(gc, QQ_CMD_REQUEST_KEY, &key, 1);
}
static void qq_process_recv_request_key(PurpleConnection *gc)
diff --git a/libpurple/protocols/qq/send_file.h b/libpurple/protocols/qq/send_file.h
index 20295d596e..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;
@@ -66,20 +67,15 @@ typedef struct _ft_info {
gboolean use_major;
} ft_info;
-void qq_process_recv_file_accept(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_reject(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_cancel(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_request(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection *gc);
-void qq_process_recv_file_notify(guint8 *data, guint8 **cursor, gint data_len,
- guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_accept(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_reject(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_cancel(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
+void qq_process_recv_file_notify(guint8 *data, gint data_len, guint32 sender_uid, PurpleConnection *gc);
gboolean qq_can_receive_file(PurpleConnection *gc, const char *who);
void qq_send_file(PurpleConnection *gc, const char *who, const char *file);
-void qq_get_conn_info(guint8 *data, guint8 **cursor, gint data_len, ft_info *info);
-gint qq_fill_conn_info(guint8 *data, guint8 **cursor, ft_info *info);
+gint qq_get_conn_info(ft_info *info, guint8 *data);
+gint qq_fill_conn_info(guint8 *data, ft_info *info);
gssize _qq_xfer_write(const guint8 *buf, size_t len, PurpleXfer *xfer);
#endif
diff --git a/libpurple/protocols/qq/sendqueue.c b/libpurple/protocols/qq/sendqueue.c
deleted file mode 100644
index 7b35d81dcb..0000000000
--- a/libpurple/protocols/qq/sendqueue.c
+++ /dev/null
@@ -1,156 +0,0 @@
-/**
- * @file sendqueue.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "internal.h"
-
-#include "connection.h"
-#include "debug.h"
-#include "notify.h"
-#include "prefs.h"
-#include "request.h"
-
-#include "header_info.h"
-#include "qq_proxy.h"
-#include "sendqueue.h"
-
-#define QQ_RESEND_MAX 8 /* max resend per packet */
-
-typedef struct _gc_and_packet gc_and_packet;
-
-struct _gc_and_packet {
- PurpleConnection *gc;
- qq_sendpacket *packet;
-};
-
-/* Remove a packet with send_seq from sendqueue */
-void qq_sendqueue_remove(qq_data *qd, guint16 send_seq)
-{
- GList *list;
- qq_sendpacket *p;
-
- list = qd->sendqueue;
- while (list != NULL) {
- p = (qq_sendpacket *) (list->data);
- if (p->send_seq == send_seq) {
- qd->sendqueue = g_list_remove(qd->sendqueue, p);
- g_free(p->buf);
- g_free(p);
- break;
- }
- list = list->next;
- }
-}
-
-/* clean up sendqueue and free all contents */
-void qq_sendqueue_free(qq_data *qd)
-{
- qq_sendpacket *p;
- gint i;
-
- i = 0;
- while (qd->sendqueue != NULL) {
- p = (qq_sendpacket *) (qd->sendqueue->data);
- qd->sendqueue = g_list_remove(qd->sendqueue, p);
- g_free(p->buf);
- g_free(p);
- i++;
- }
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in sendqueue are freed!\n", i);
-}
-
-/* FIXME We shouldn't be dropping packets, but for now we have to because
- * somewhere we're generating invalid packets that the server won't ack.
- * Given enough time, a buildup of those packets would crash the client. */
-gboolean qq_sendqueue_timeout_callback(gpointer data)
-{
- PurpleConnection *gc;
- qq_data *qd;
- GList *list;
- qq_sendpacket *p;
- time_t now;
- gint wait_time;
-
- gc = (PurpleConnection *) data;
- qd = (qq_data *) gc->proto_data;
- now = time(NULL);
- list = qd->sendqueue;
-
- /* empty queue, return TRUE so that timeout continues functioning */
- if (qd->sendqueue == NULL)
- return TRUE;
-
- while (list != NULL) { /* remove all packet whose resend_times == -1 */
- p = (qq_sendpacket *) list->data;
- if (p->resend_times == -1) { /* to remove */
- qd->sendqueue = g_list_remove(qd->sendqueue, p);
- g_free(p->buf);
- g_free(p);
- list = qd->sendqueue;
- } else {
- list = list->next;
- }
- }
-
- list = qd->sendqueue;
- while (list != NULL) {
- p = (qq_sendpacket *) list->data;
- if (p->resend_times == QQ_RESEND_MAX) { /* reach max */
- switch (p->cmd) {
- case QQ_CMD_KEEP_ALIVE:
- if (qd->logged_in) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
- qd->logged_in = FALSE;
- }
- p->resend_times = -1;
- break;
- case QQ_CMD_LOGIN:
- case QQ_CMD_REQUEST_LOGIN_TOKEN:
- if (!qd->logged_in) /* cancel login progress */
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
- p->resend_times = -1;
- break;
- default:{
- purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "%s packet sent %d times but not acked. Not resending it.\n",
- qq_get_cmd_desc(p->cmd), QQ_RESEND_MAX);
- }
- p->resend_times = -1;
- }
- } else { /* resend_times < QQ_RESEND_MAX, so sent it again */
- wait_time = (gint) (QQ_SENDQUEUE_TIMEOUT / 1000);
- if (difftime(now, p->sendtime) > (wait_time * (p->resend_times + 1))) {
- qq_proxy_write(qd, p->buf, p->len);
- p->resend_times++;
- purple_debug(PURPLE_DEBUG_INFO,
- "QQ", "<<< [%05d] send again for %d times!\n",
- p->send_seq, p->resend_times);
- }
- }
- list = list->next;
- }
- return TRUE; /* if we return FALSE, the timeout callback stops functioning */
-}
diff --git a/libpurple/protocols/qq/sendqueue.h b/libpurple/protocols/qq/sendqueue.h
deleted file mode 100644
index 8111cfa02b..0000000000
--- a/libpurple/protocols/qq/sendqueue.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * @file sendqueue.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#ifndef _QQ_SEND_QUEUE_H_
-#define _QQ_SEND_QUEUE_H_
-
-#include <glib.h>
-#include "qq.h"
-
-#define QQ_SENDQUEUE_TIMEOUT 5000 /* in 1/1000 sec */
-
-typedef struct _qq_sendpacket qq_sendpacket;
-
-struct _qq_sendpacket {
- gint fd;
- gint len;
- guint8 *buf;
- guint16 cmd;
- guint16 send_seq;
- gint resend_times;
- time_t sendtime;
-};
-
-void qq_sendqueue_free(qq_data *qd);
-
-void qq_sendqueue_remove(qq_data *qd, guint16 send_seq);
-gboolean qq_sendqueue_timeout_callback(gpointer data);
-
-#endif
diff --git a/libpurple/protocols/qq/sys_msg.c b/libpurple/protocols/qq/sys_msg.c
index fe63606c40..588610532b 100644
--- a/libpurple/protocols/qq/sys_msg.c
+++ b/libpurple/protocols/qq/sys_msg.c
@@ -35,7 +35,7 @@
#include "header_info.h"
#include "packet_parse.h"
#include "qq.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "sys_msg.h"
#include "utils.h"
@@ -44,6 +44,7 @@ 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
};
@@ -120,27 +121,29 @@ static void _qq_search_before_add_with_gc_and_uid(gc_and_uid *g)
/* Send ACK if the sys message needs an ACK */
static void _qq_send_packet_ack_msg_sys(PurpleConnection *gc, guint8 code, guint32 from, guint16 seq)
{
- guint8 bar, *ack, *cursor;
+ qq_data *qd;
+ guint8 bar, *ack;
gchar *str;
gint ack_len, bytes;
+ qd = (qq_data *) gc->proto_data;
+
str = g_strdup_printf("%d", from);
bar = 0x1e;
ack_len = 1 + 1 + strlen(str) + 1 + 2;
ack = g_newa(guint8, ack_len);
- cursor = ack;
- bytes = 0;
- bytes += create_packet_b(ack, &cursor, code);
- bytes += create_packet_b(ack, &cursor, bar);
- bytes += create_packet_data(ack, &cursor, (guint8 *) str, strlen(str));
- bytes += create_packet_b(ack, &cursor, bar);
- bytes += create_packet_w(ack, &cursor, seq);
+ bytes = 0;
+ bytes += qq_put8(ack + bytes, code);
+ bytes += qq_put8(ack + bytes, bar);
+ bytes += qq_putdata(ack + bytes, (guint8 *) str, strlen(str));
+ bytes += qq_put8(ack + bytes, bar);
+ bytes += qq_put16(ack + bytes, seq);
g_free(str);
if (bytes == ack_len) /* creation OK */
- qq_send_cmd(gc, QQ_CMD_ACK_SYS_MSG, TRUE, 0, FALSE, ack, ack_len);
+ qq_send_cmd_detail(qd, QQ_CMD_ACK_SYS_MSG, 0, FALSE, ack, ack_len);
else
purple_debug(PURPLE_DEBUG_ERROR, "QQ",
"Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
@@ -275,6 +278,20 @@ 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;
@@ -318,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/udp_proxy_s5.c b/libpurple/protocols/qq/udp_proxy_s5.c
deleted file mode 100644
index 9807d138f4..0000000000
--- a/libpurple/protocols/qq/udp_proxy_s5.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/**
- * @file udp_proxy_s5.c
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#include "debug.h"
-
-#include "udp_proxy_s5.h"
-
-static void _qq_s5_canread_again(gpointer data, gint source, PurpleInputCondition cond)
-{
- unsigned char buf[512];
- struct PHB *phb = data;
- struct sockaddr_in sin;
- int len, error;
- socklen_t errlen;
- int flags;
-
- purple_input_remove(phb->inpa);
- purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read again.\n");
-
- len = read(source, buf, 10);
- if (len < 10) {
- purple_debug(PURPLE_DEBUG_WARNING, "socks5 proxy", "or not...\n");
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, source, NULL);
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
- if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
- if ((buf[0] == 0x05) && (buf[1] < 0x09))
- purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "socks5 error: %x\n", buf[1]);
- else
- purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Bad data.\n");
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- sin.sin_family = AF_INET;
- memcpy(&sin.sin_addr.s_addr, buf + 4, 4);
- memcpy(&sin.sin_port, buf + 8, 2);
-
- if (connect(phb->udpsock, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) < 0) {
- purple_debug(PURPLE_DEBUG_INFO, "s5_canread_again", "connect failed: %s\n", g_strerror(errno));
- close(phb->udpsock);
- close(source);
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- error = ETIMEDOUT;
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connect didn't block\n");
- errlen = sizeof(error);
- if (getsockopt(phb->udpsock, SOL_SOCKET, SO_ERROR, &error, &errlen) < 0) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "getsockopt failed.\n");
- close(phb->udpsock);
- return;
- }
- flags = fcntl(phb->udpsock, F_GETFL);
- fcntl(phb->udpsock, F_SETFL, flags & ~O_NONBLOCK);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
- phb->func(phb->data, phb->udpsock, NULL);
- }
-
- g_free(phb->host);
- g_free(phb);
-}
-
-static void _qq_s5_sendconnect(gpointer data, gint source)
-{
- unsigned char buf[512];
- struct PHB *phb = data;
- struct sockaddr_in sin, ctlsin;
- int port;
- socklen_t ctllen;
- int flags;
-
- purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "remote host is %s:%d\n", phb->host, phb->port);
-
- buf[0] = 0x05;
- buf[1] = 0x03; /* udp relay */
- buf[2] = 0x00; /* reserved */
- buf[3] = 0x01; /* address type -- ipv4 */
- memset(buf + 4, 0, 0x04);
-
- ctllen = sizeof(ctlsin);
- if (getsockname(source, (struct sockaddr *) &ctlsin, &ctllen) < 0) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "getsockname: %s\n", g_strerror(errno));
- close(source);
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- phb->udpsock = socket(PF_INET, SOCK_DGRAM, 0);
- purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "UDP socket=%d\n", phb->udpsock);
- if (phb->udpsock < 0) {
- close(source);
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- flags = fcntl(phb->udpsock, F_GETFL);
- fcntl(phb->udpsock, F_SETFL, flags | O_NONBLOCK);
-
- port = g_ntohs(ctlsin.sin_port) + 1;
- while (1) {
- inet_aton("0.0.0.0", &(sin.sin_addr));
- sin.sin_family = AF_INET;
- sin.sin_port = g_htons(port);
- if (bind(phb->udpsock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- port++;
- if (port > 65500) {
- close(source);
- g_free(phb->host);
- g_free(phb);
- return;
- }
- } else
- break;
- }
-
- memset(buf + 4, 0, 0x04);
- memcpy(buf + 8, &(sin.sin_port), 0x02);
-
- if (write(source, buf, 10) < 10) {
- close(source);
- purple_debug(PURPLE_DEBUG_INFO, "s5_sendconnect", "packet too small\n");
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread_again, phb);
-}
-
-static void _qq_s5_readauth(gpointer data, gint source, PurpleInputCondition cond)
-{
- unsigned char buf[512];
- struct PHB *phb = data;
-
- purple_input_remove(phb->inpa);
- purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Got auth response.\n");
-
- if (read(source, buf, 2) < 2) {
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- _qq_s5_sendconnect(phb, source);
-}
-
-static void _qq_s5_canread(gpointer data, gint source, PurpleInputCondition cond)
-{
- unsigned char buf[512];
- struct PHB *phb;
- int ret;
-
- phb = data;
-
- purple_input_remove(phb->inpa);
- purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Able to read.\n");
-
- ret = read(source, buf, 2);
- if (ret < 2) {
- purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "packet smaller than 2 octet\n");
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
- purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "unsupport\n");
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- if (buf[1] == 0x02) {
- unsigned int i, j;
-
- i = strlen(purple_proxy_info_get_username(phb->gpi));
- j = strlen(purple_proxy_info_get_password(phb->gpi));
-
- buf[0] = 0x01; /* version 1 */
- buf[1] = i;
- memcpy(buf + 2, purple_proxy_info_get_username(phb->gpi), i);
- buf[2 + i] = j;
- memcpy(buf + 2 + i + 1, purple_proxy_info_get_password(phb->gpi), j);
-
- if (write(source, buf, 3 + i + j) < 3 + i + j) {
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_readauth, phb);
- } else {
- purple_debug(PURPLE_DEBUG_INFO, "s5_canread", "calling s5_sendconnect\n");
- _qq_s5_sendconnect(phb, source);
- }
-}
-
-static void _qq_s5_canwrite(gpointer data, gint source, PurpleInputCondition cond)
-{
- unsigned char buf[512];
- int i;
- struct PHB *phb = data;
- socklen_t len;
- int error = ETIMEDOUT;
- int flags;
-
- purple_debug(PURPLE_DEBUG_INFO, "socks5 proxy", "Connected.\n");
-
- if (phb->inpa > 0)
- purple_input_remove(phb->inpa);
-
- len = sizeof(error);
- if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
- purple_debug(PURPLE_DEBUG_INFO, "getsockopt", "%s\n", g_strerror(errno));
- close(source);
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
- flags = fcntl(source, F_GETFL);
- fcntl(source, F_SETFL, flags & ~O_NONBLOCK);
-
- i = 0;
- buf[0] = 0x05; /* SOCKS version 5 */
-
- if (purple_proxy_info_get_username(phb->gpi) != NULL) {
- buf[1] = 0x02; /* two methods */
- buf[2] = 0x00; /* no authentication */
- buf[3] = 0x02; /* username/password authentication */
- i = 4;
- } else {
- buf[1] = 0x01;
- buf[2] = 0x00;
- i = 3;
- }
-
- if (write(source, buf, i) < i) {
- purple_debug(PURPLE_DEBUG_INFO, "write", "%s\n", g_strerror(errno));
- purple_debug(PURPLE_DEBUG_ERROR, "socks5 proxy", "Unable to write\n");
- close(source);
-
- if (phb->account == NULL || purple_account_get_connection(phb->account) != NULL) {
-
- phb->func(phb->data, -1, _("Unable to connect"));
- }
-
- g_free(phb->host);
- g_free(phb);
- return;
- }
-
- phb->inpa = purple_input_add(source, PURPLE_INPUT_READ, _qq_s5_canread, phb);
-}
-
-gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen)
-{
- gint fd;
- int flags;
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ",
- "Connecting to %s:%d via %s:%d using SOCKS5\n",
- phb->host, phb->port, purple_proxy_info_get_host(phb->gpi), purple_proxy_info_get_port(phb->gpi));
-
- if ((fd = socket(addr->sa_family, SOCK_STREAM, 0)) < 0)
- return -1;
-
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "proxy_sock5 return fd=%d\n", fd);
-
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- if (connect(fd, addr, addrlen) < 0) {
- if ((errno == EINPROGRESS) || (errno == EINTR)) {
- purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n");
- phb->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, _qq_s5_canwrite, phb);
- } else {
- close(fd);
- return -1;
- }
- } else {
- purple_debug(PURPLE_DEBUG_MISC, "QQ", "Connect in blocking mode.\n");
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
- _qq_s5_canwrite(phb, fd, PURPLE_INPUT_WRITE);
- }
-
- return fd;
-}
diff --git a/libpurple/protocols/qq/udp_proxy_s5.h b/libpurple/protocols/qq/udp_proxy_s5.h
deleted file mode 100644
index 4144e29e6b..0000000000
--- a/libpurple/protocols/qq/udp_proxy_s5.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * @file udp_proxy_s5.h
- *
- * purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
- */
-
-#ifndef _QQ_UDP_PROXY_S5_H_
-#define _QQ_UDP_PROXY_S5_H_
-
-#include "internal.h" /* for socket stuff */
-
-#include "qq_proxy.h"
-
-gint qq_proxy_socks5(struct PHB *phb, struct sockaddr *addr, socklen_t addrlen);
-
-#endif
diff --git a/libpurple/protocols/qq/utils.c b/libpurple/protocols/qq/utils.c
index 6f7c3c7ab5..c3ee33fe6d 100644
--- a/libpurple/protocols/qq/utils.c
+++ b/libpurple/protocols/qq/utils.c
@@ -22,7 +22,6 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#include "cipher.h"
#include "limits.h"
#include "stdlib.h"
#include "string.h"
@@ -31,6 +30,8 @@
#include "win32dep.h"
#endif
+#include "cipher.h"
+
#include "char_conv.h"
#include "debug.h"
#include "prefs.h"
@@ -40,6 +41,32 @@
#define QQ_NAME_FORMAT "%d"
+/* These functions are used only in development phase */
+/*
+ static void _qq_show_socket(gchar *desc, gint fd) {
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(sin);
+ getsockname(fd, (struct sockaddr *)&sin, &len);
+ purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n",
+ inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
+ }
+ */
+
+void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len)
+{
+ PurpleCipher *cipher;
+ PurpleCipherContext *context;
+
+ g_return_if_fail(md5 != NULL && md5_len > 0);
+ g_return_if_fail(src != NULL && src_len > 0);
+
+ cipher = purple_ciphers_find_cipher("md5");
+ context = purple_cipher_context_new(cipher, NULL);
+ purple_cipher_context_append(context, src, src_len);
+ purple_cipher_context_digest(context, md5_len, md5, NULL);
+ purple_cipher_context_destroy(context);
+}
+
gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount)
{
gint index;
@@ -87,7 +114,7 @@ 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;
@@ -113,30 +140,20 @@ gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_f
return segments;
}
-/* generate a md5 key using uid and session_key */
-guint8 *_gen_session_md5(gint uid, guint8 *session_key)
+/* convert Purple name to original QQ UID */
+guint32 purple_name_to_uid(const gchar *const name)
{
- guint8 *src, md5_str[QQ_KEY_LENGTH];
- PurpleCipher *cipher;
- PurpleCipherContext *context;
-
- src = g_newa(guint8, 20);
- memcpy(src, &uid, 4);
- memcpy(src, session_key, QQ_KEY_LENGTH);
-
- cipher = purple_ciphers_find_cipher("md5");
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, src, 20);
- purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL);
- purple_cipher_context_destroy(context);
+ guint32 ret;
+ g_return_val_if_fail(name != NULL, 0);
- return g_memdup(md5_str, QQ_KEY_LENGTH);
+ ret = strtol(name, NULL, 10);
+ if (errno == ERANGE)
+ return 0;
+ else
+ return ret;
}
-/* given a four-byte ip data, convert it into a human readable ip string
- * the return needs to be freed */
-gchar *gen_ip_str(guint8 *ip)
-{
+gchar *gen_ip_str(guint8 *ip) {
gchar *ret;
if (ip == NULL || ip[0] == 0) {
ret = g_new(gchar, 1);
@@ -159,19 +176,6 @@ 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)
@@ -194,7 +198,7 @@ gchar *chat_name_to_purple_name(const gchar *const name)
}
/* try to dump the data as GBK */
-void try_dump_as_gbk(const guint8 *const data, gint len)
+gchar* try_dump_as_gbk(const guint8 *const data, gint len)
{
gint i;
guint8 *incoming;
@@ -215,8 +219,8 @@ void try_dump_as_gbk(const guint8 *const data, gint len)
if (msg_utf8 != NULL) {
purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Try extract GB msg: %s\n", msg_utf8);
- g_free(msg_utf8);
}
+ return msg_utf8;
}
/* strips whitespace */
@@ -294,7 +298,7 @@ guint8 *hex_str_to_bytes(const gchar *const buffer, gint *out_len)
/* Dumps a chunk of raw data into an ASCII hex string.
* The return should be freed later. */
-gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
+static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
{
GString *str;
gchar *ret;
@@ -303,12 +307,12 @@ 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, " ");
@@ -331,6 +335,51 @@ gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
return ret;
}
+void qq_hex_dump(PurpleDebugLevel level, const char *category,
+ const guint8 *pdata, gint bytes,
+ const char *format, ...)
+{
+ va_list args;
+ char *arg_s = NULL;
+ gchar *phex = NULL;
+
+ g_return_if_fail(level != PURPLE_DEBUG_ALL);
+ g_return_if_fail(format != NULL);
+
+ va_start(args, format);
+ arg_s = g_strdup_vprintf(format, args);
+ va_end(args);
+
+ if (bytes <= 0) {
+ purple_debug(level, category, arg_s);
+ return;
+ }
+
+ phex = hex_dump_to_str(pdata, bytes);
+ purple_debug(level, category, "%s - (len %d)\n%s", arg_s, bytes, phex);
+ g_free(phex);
+}
+
+void qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
+{
+ /*
+ char buf1[8*len+2], buf2[10];
+ int i;
+ buf1[0] = 0;
+ for (i = 0; i < len; i++) {
+ sprintf(buf2, " %02x(%d)", buf[i] & 0xff, buf[i] & 0xff);
+ strcat(buf1, buf2);
+ }
+ strcat(buf1, "\n");
+ purple_debug(PURPLE_DEBUG_INFO, desc, "%s", buf1);
+ */
+
+ /* modified by s3e, 20080424 */
+ qq_hex_dump(PURPLE_DEBUG_INFO, desc,
+ buf, len,
+ "");
+}
+
/* convert face num from packet (0-299) to local face (1-100) */
gchar *face_to_icon_str(gint face)
{
diff --git a/libpurple/protocols/qq/utils.h b/libpurple/protocols/qq/utils.h
index 7351b16f15..14dfbcd40d 100644
--- a/libpurple/protocols/qq/utils.h
+++ b/libpurple/protocols/qq/utils.h
@@ -28,12 +28,15 @@
#include <stdio.h>
#include <glib.h>
+#include "debug.h"
+
+void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len);
+
gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount);
gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount);
gint qq_string_to_dec_value(const gchar *str);
gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields);
-guint8 *_gen_session_md5(gint uid, guint8 *session_key);
gchar *gen_ip_str(guint8 *ip);
guint8 *str_ip_gen(gchar *str);
@@ -44,10 +47,13 @@ gchar *chat_name_to_purple_name(const gchar *const name);
gchar *face_to_icon_str(gint face);
-void try_dump_as_gbk(const guint8 *const data, gint len);
+gchar *try_dump_as_gbk(const guint8 *const data, gint len);
+void qq_show_packet(const gchar *desc, const guint8 *buf, gint len);
+void qq_hex_dump(PurpleDebugLevel level, const char *category,
+ const guint8 *pdata, gint bytes,
+ const char *format, ...);
guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len);
-gchar *hex_dump_to_str(const guint8 *buf, gint buf_len);
const gchar *qq_buddy_icon_dir(void);
const gchar *qq_win32_buddy_icon_dir(void);
diff --git a/libpurple/protocols/silc/buddy.c b/libpurple/protocols/silc/buddy.c
index 0a23773820..112d796ddd 100644
--- a/libpurple/protocols/silc/buddy.c
+++ b/libpurple/protocols/silc/buddy.c
@@ -1434,13 +1434,25 @@ void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
void silcpurple_idle_set(PurpleConnection *gc, int idle)
{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
+ SilcPurple sg;
+ SilcClient client;
+ SilcClientConnection conn;
SilcAttributeObjService service;
const char *server;
int port;
+ sg = gc->proto_data;
+ if (sg == NULL)
+ return;
+
+ client = sg->client;
+ if (client == NULL)
+ return;
+
+ conn = sg->conn;
+ if (conn == NULL)
+ return;
+
server = purple_account_get_string(sg->account, "server",
"silc.silcnet.org");
port = purple_account_get_int(sg->account, "port", 706),
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/buddy.c b/libpurple/protocols/silc10/buddy.c
index 680e84eb5b..67e53f630f 100644
--- a/libpurple/protocols/silc10/buddy.c
+++ b/libpurple/protocols/silc10/buddy.c
@@ -1434,13 +1434,25 @@ void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
void silcpurple_idle_set(PurpleConnection *gc, int idle)
{
- SilcPurple sg = gc->proto_data;
- SilcClient client = sg->client;
- SilcClientConnection conn = sg->conn;
+ SilcPurple sg;
+ SilcClient client;
+ SilcClientConnection conn;
SilcAttributeObjService service;
const char *server;
int port;
+ sg = gc->proto_data;
+ if (sg == NULL)
+ return;
+
+ client = sg->client;
+ if (client == NULL)
+ return;
+
+ conn = sg->conn;
+ if (conn == NULL)
+ return;
+
server = purple_account_get_string(sg->account, "server",
"silc.silcnet.org");
port = purple_account_get_int(sg->account, "port", 706),
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/simple/simple.c b/libpurple/protocols/simple/simple.c
index ad615a268f..fb1490f5bb 100644
--- a/libpurple/protocols/simple/simple.c
+++ b/libpurple/protocols/simple/simple.c
@@ -1898,7 +1898,7 @@ static void simple_login(PurpleAccount *account)
PurpleConnection *gc;
struct simple_account_data *sip;
gchar **userserver;
- gchar *hosttoconnect;
+ const gchar *hosttoconnect;
const char *username = purple_account_get_username(account);
gc = purple_account_get_connection(account);
@@ -1934,14 +1934,13 @@ static void simple_login(PurpleAccount *account)
sip->status = g_strdup("available");
if(!purple_account_get_bool(account, "useproxy", FALSE)) {
- hosttoconnect = g_strdup(sip->servername);
+ hosttoconnect = sip->servername;
} else {
- hosttoconnect = g_strdup(purple_account_get_string(account, "proxy", sip->servername));
+ hosttoconnect = purple_account_get_string(account, "proxy", sip->servername);
}
sip->srv_query_data = purple_srv_resolve("sip",
sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip);
- g_free(hosttoconnect);
}
static void simple_close(PurpleConnection *gc)
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_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 680d018b59..43bb6e3cad 100644
--- a/libpurple/prpl.h
+++ b/libpurple/prpl.h
@@ -31,6 +31,7 @@
#define _PURPLE_PRPL_H_
typedef struct _PurplePluginProtocolInfo PurplePluginProtocolInfo;
+/** @copydoc _PurpleAttentionType */
typedef struct _PurpleAttentionType PurpleAttentionType;
/**************************************************************************/
@@ -99,6 +100,9 @@ struct proto_chat_entry {
gboolean secret;
};
+/** Represents "nudges" and "buzzes" that you may send to a buddy to attract
+ * their attention (or vice-versa).
+ */
struct _PurpleAttentionType
{
const char *name; /**< Shown in GUI elements */
@@ -689,6 +693,41 @@ 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/roomlist.h b/libpurple/roomlist.h
index a35c2a7fc3..8158b689c2 100644
--- a/libpurple/roomlist.h
+++ b/libpurple/roomlist.h
@@ -30,6 +30,7 @@
typedef struct _PurpleRoomlist PurpleRoomlist;
typedef struct _PurpleRoomlistRoom PurpleRoomlistRoom;
typedef struct _PurpleRoomlistField PurpleRoomlistField;
+/** @copydoc _PurpleRoomlistUiOps */
typedef struct _PurpleRoomlistUiOps PurpleRoomlistUiOps;
/**
diff --git a/libpurple/server.c b/libpurple/server.c
index 0e39bda37d..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);
}
@@ -727,6 +649,7 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
PurpleStatusPrimitive primitive;
const gchar *auto_reply_pref;
const char *away_msg = NULL;
+ gboolean mobile = FALSE;
auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
@@ -734,9 +657,10 @@ void serv_got_im(PurpleConnection *gc, const char *who, const char *msg,
status = purple_presence_get_active_status(presence);
status_type = purple_status_get_type(status);
primitive = purple_status_type_get_primitive(status_type);
+ mobile = purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE);
if ((primitive == PURPLE_STATUS_AVAILABLE) ||
(primitive == PURPLE_STATUS_INVISIBLE) ||
- (primitive == PURPLE_STATUS_MOBILE) ||
+ mobile ||
!strcmp(auto_reply_pref, "never") ||
(!purple_presence_is_idle(presence) && !strcmp(auto_reply_pref, "awayidle")))
{
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/sound.h b/libpurple/sound.h
index a455e80a96..626d53dbfd 100644
--- a/libpurple/sound.h
+++ b/libpurple/sound.h
@@ -55,6 +55,9 @@ typedef enum _PurpleSoundEventID
} PurpleSoundEventID;
+/** Operations used by the core to request that particular sound files, or the
+ * sound associated with a particular event, should be played.
+ */
typedef struct _PurpleSoundUiOps
{
void (*init)(void);
diff --git a/libpurple/util.c b/libpurple/util.c
index 426e108d77..39306b5d1a 100644
--- a/libpurple/util.c
+++ b/libpurple/util.c
@@ -832,6 +832,11 @@ purple_str_to_time(const char *timestamp, gboolean utc,
if (offset_positive)
tzoff *= -1;
}
+ else if ((*c == 'Z') && (c = c + 1))
+ {
+ /* 'Z' = Zulu = UTC */
+ tzoff = 0;
+ }
else if (utc)
{
static struct tm tmptm;
@@ -3594,7 +3599,7 @@ purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ..
static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message);
static gboolean
-parse_redirect(const char *data, size_t data_len, gint sock,
+parse_redirect(const char *data, size_t data_len,
PurpleUtilFetchUrlData *gfud)
{
gchar *s;
@@ -3768,7 +3773,7 @@ url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond)
header_len, gfud->webdata);
/* See if we can find a redirect. */
- if(parse_redirect(gfud->webdata, header_len, source, gfud))
+ if(parse_redirect(gfud->webdata, header_len, gfud))
return;
gfud->got_headers = TRUE;
@@ -4718,3 +4723,21 @@ 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 071967a48c..f5f2f9e48c 100644
--- a/libpurple/xmlnode.c
+++ b/libpurple/xmlnode.c
@@ -728,6 +728,13 @@ xmlnode_from_str(const char *str, gssize size)
return ret;
}
+static void
+xmlnode_copy_foreach_ns(gpointer key, gpointer value, gpointer user_data)
+{
+ GHashTable *ret = (GHashTable *)user_data;
+ g_hash_table_insert(ret, g_strdup(key), g_strdup(value));
+}
+
xmlnode *
xmlnode_copy(const xmlnode *src)
{
@@ -739,17 +746,23 @@ 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/gtkblist.c b/pidgin/gtkblist.c
index 749d6f291c..be3fd3e1e3 100644
--- a/pidgin/gtkblist.c
+++ b/pidgin/gtkblist.c
@@ -130,6 +130,7 @@ static GtkWidget *accountmenu = NULL;
static guint visibility_manager_count = 0;
static GdkVisibilityState gtk_blist_visibility = GDK_VISIBILITY_UNOBSCURED;
+static gboolean gtk_blist_focused = FALSE;
static gboolean editing_blist = FALSE;
static GList *pidgin_blist_sort_methods = NULL;
@@ -5166,9 +5167,14 @@ headline_style_set (GtkWidget *widget,
/******************************************/
static int
-blist_focus_cb(GtkWidget *widget, gpointer data, PidginBuddyList *gtkblist)
+blist_focus_cb(GtkWidget *widget, GdkEventFocus *event, PidginBuddyList *gtkblist)
{
- pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE);
+ if(event->in) {
+ gtk_blist_focused = TRUE;
+ pidgin_set_urgent(GTK_WINDOW(gtkblist->window), FALSE);
+ } else {
+ gtk_blist_focused = FALSE;
+ }
return 0;
}
@@ -5255,6 +5261,8 @@ static void pidgin_blist_show(PurpleBuddyList *list)
gtkblist->window = pidgin_create_window(_("Buddy List"), 0, "buddy_list", TRUE);
g_signal_connect(G_OBJECT(gtkblist->window), "focus-in-event",
G_CALLBACK(blist_focus_cb), gtkblist);
+ g_signal_connect(G_OBJECT(gtkblist->window), "focus-out-event",
+ G_CALLBACK(blist_focus_cb), gtkblist);
GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE;
gtkblist->main_vbox = gtk_vbox_new(FALSE, 0);
@@ -6988,8 +6996,15 @@ pidgin_blist_toggle_visibility()
{
if (gtkblist && gtkblist->window) {
if (GTK_WIDGET_VISIBLE(gtkblist->window)) {
+ /* make the buddy list visible if it is iconified or if it is
+ * obscured and not currently focused (the focus part ensures
+ * that we do something reasonable if the buddy list is obscured
+ * by a window set to always be on top), otherwise hide the
+ * buddy list
+ */
purple_blist_set_visible(PIDGIN_WINDOW_ICONIFIED(gtkblist->window) ||
- gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED);
+ ((gtk_blist_visibility != GDK_VISIBILITY_UNOBSCURED) &&
+ !gtk_blist_focused));
} else {
purple_blist_set_visible(TRUE);
}
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
index 7742625206..b7ec8e8170 100644
--- a/pidgin/gtkconv.c
+++ b/pidgin/gtkconv.c
@@ -1481,7 +1481,7 @@ chat_do_im(PidginConversation *gtkconv, const char *who)
PurpleAccount *account;
PurpleConnection *gc;
PurplePluginProtocolInfo *prpl_info = NULL;
- char *real_who;
+ gchar *real_who = NULL;
account = purple_conversation_get_account(conv);
g_return_if_fail(account != NULL);
@@ -1494,13 +1494,11 @@ chat_do_im(PidginConversation *gtkconv, const char *who)
if (prpl_info && prpl_info->get_cb_real_name)
real_who = prpl_info->get_cb_real_name(gc,
purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
- else
- real_who = g_strdup(who);
- if(!real_who)
+ if(!who && !real_who)
return;
- pidgin_dialogs_im_with_user(account, real_who);
+ pidgin_dialogs_im_with_user(account, real_who ? real_who : who);
g_free(real_who);
}
@@ -1539,11 +1537,22 @@ menu_chat_im_cb(GtkWidget *w, PidginConversation *gtkconv)
static void
menu_chat_send_file_cb(GtkWidget *w, PidginConversation *gtkconv)
{
+ PurplePluginProtocolInfo *prpl_info;
PurpleConversation *conv = gtkconv->active_conv;
const char *who = g_object_get_data(G_OBJECT(w), "user_data");
PurpleConnection *gc = purple_conversation_get_gc(conv);
+ gchar *real_who = NULL;
- serv_send_file(gc, who, NULL);
+ g_return_if_fail(gc != NULL);
+
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
+
+ if (prpl_info && prpl_info->get_cb_real_name)
+ real_who = prpl_info->get_cb_real_name(gc,
+ purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+
+ serv_send_file(gc, real_who ? real_who : who, NULL);
+ g_free(real_who);
}
static void
@@ -1659,23 +1668,34 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (gc == NULL)
gtk_widget_set_sensitive(button, FALSE);
-
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+ else
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
if (prpl_info && prpl_info->send_file)
{
+ gboolean can_receive_file = TRUE;
+
button = pidgin_new_item_from_stock(menu, _("Send File"),
PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(menu_chat_send_file_cb),
PIDGIN_CONVERSATION(conv), 0, 0, NULL);
- if (gc == NULL || prpl_info == NULL ||
- !(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who)))
- {
- gtk_widget_set_sensitive(button, FALSE);
+ if (gc == NULL || prpl_info == NULL)
+ can_receive_file = FALSE;
+ else {
+ gchar *real_who = NULL;
+ if (prpl_info->get_cb_real_name)
+ real_who = prpl_info->get_cb_real_name(gc,
+ purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)), who);
+ if (!(!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, real_who ? real_who : who)))
+ can_receive_file = FALSE;
+ g_free(real_who);
}
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+ if (!can_receive_file)
+ gtk_widget_set_sensitive(button, FALSE);
+ else
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
}
@@ -1688,8 +1708,8 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (gc == NULL)
gtk_widget_set_sensitive(button, FALSE);
-
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+ else
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
}
if (prpl_info && (prpl_info->get_info || prpl_info->get_cb_info)) {
@@ -1698,8 +1718,8 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (gc == NULL)
gtk_widget_set_sensitive(button, FALSE);
-
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+ else
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
}
if (prpl_info && prpl_info->get_cb_away) {
@@ -1708,8 +1728,8 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (gc == NULL)
gtk_widget_set_sensitive(button, FALSE);
-
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+ else
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
}
if (!is_me && prpl_info && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) {
@@ -1722,8 +1742,8 @@ create_chat_menu(PurpleConversation *conv, const char *who, PurpleConnection *gc
if (gc == NULL)
gtk_widget_set_sensitive(button, FALSE);
-
- g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
+ else
+ g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
}
button = pidgin_new_item_from_stock(menu, _("Last said"), GTK_STOCK_INDEX,
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/gtkimhtml.c b/pidgin/gtkimhtml.c
index 7a96f25e6a..5d42fe09db 100644
--- a/pidgin/gtkimhtml.c
+++ b/pidgin/gtkimhtml.c
@@ -768,7 +768,7 @@ gtk_imhtml_expose_event (GtkWidget *widget,
gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &end,
buf_x + event->area.width, buf_y + event->area.height);
-
+ gtk_text_iter_order(&start, &end);
cur = start;
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/gtkmenutray.c b/pidgin/gtkmenutray.c
index dfe5d2f21c..da7111df4e 100644
--- a/pidgin/gtkmenutray.c
+++ b/pidgin/gtkmenutray.c
@@ -84,19 +84,24 @@ pidgin_menu_tray_get_property(GObject *obj, guint param_id, GValue *value,
}
static void
-pidgin_menu_tray_finalize(GObject *obj) {
+pidgin_menu_tray_finalize(GObject *obj)
+{
+ PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj);
#if 0
/* This _might_ be leaking, but I have a sneaking suspicion that the widget is
* getting destroyed in GtkContainer's finalize function. But if were are
* leaking here, be sure to figure out why this causes a crash.
* -- Gary
*/
- PidginMenuTray *tray = PIDGIN_MENU_TRAY(obj);
if(GTK_IS_WIDGET(tray->tray))
gtk_widget_destroy(GTK_WIDGET(tray->tray));
#endif
+ if (tray->tooltips) {
+ gtk_object_sink(GTK_OBJECT(tray->tooltips));
+ }
+
G_OBJECT_CLASS(parent_class)->finalize(obj);
}
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/gtkutils.c b/pidgin/gtkutils.c
index 6290f369df..9dd223edc9 100644
--- a/pidgin/gtkutils.c
+++ b/pidgin/gtkutils.c
@@ -1001,13 +1001,14 @@ void pidgin_retrieve_user_info_in_chat(PurpleConnection *conn, const char *name,
}
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl);
+ if (prpl_info != NULL && prpl_info->get_cb_real_name)
+ who = prpl_info->get_cb_real_name(conn, chat, name);
if (prpl_info == NULL || prpl_info->get_cb_info == NULL) {
- pidgin_retrieve_user_info(conn, name);
+ pidgin_retrieve_user_info(conn, who ? who : name);
+ g_free(who);
return;
}
- if (prpl_info->get_cb_real_name)
- who = prpl_info->get_cb_real_name(conn, chat, name);
show_retrieveing_info(conn, who ? who : name);
prpl_info->get_cb_info(conn, chat, name);
g_free(who);
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/cap/cap.c b/pidgin/plugins/cap/cap.c
index b17d0baaad..01bb60f7a4 100644
--- a/pidgin/plugins/cap/cap.c
+++ b/pidgin/plugins/cap/cap.c
@@ -333,7 +333,7 @@ static void insert_cap_failure(CapStatistics *stats) {
static gboolean max_message_difference_cb(gpointer data) {
CapStatistics *stats = data;
- purple_debug_info("cap", "Max Message Difference timeout occured\n");
+ purple_debug_info("cap", "Max Message Difference timeout occurred\n");
insert_cap_failure(stats);
stats->timeout_source_id = 0;
return FALSE;
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 884464f145..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
@@ -1388,67 +1395,6 @@ Function un.onInit
FunctionEnd
-; This is a modified StartRadioButtons (from Sections.nsh)
-; The only difference is that it allows for nothing in the group to be selected
-; In that case, the default variable should be set to ""
-!macro StartRadioButtonsUnselectable var
-
- !define StartRadioButtons_Var "${var}"
-
- Push $R0
- Push $R1
-
- ;If we have no selection, don't try to unselect it
- StrCmp "${StartRadioButtons_Var}" "" +4
- SectionGetFlags "${StartRadioButtons_Var}" $R0
- IntOp $R1 $R0 & ${SF_SELECTED}
- IntOp $R0 $R0 & ${SECTION_OFF}
- SectionSetFlags "${StartRadioButtons_Var}" $R0
-
- ; If the previous value isn't currently selected,
- ; we don't want to select it at the end
- IntCmp $R1 ${SF_SELECTED} +2
- StrCpy "${StartRadioButtons_Var}" ""
-
- StrCpy $R1 "${StartRadioButtons_Var}"
-
-!macroend
-
-Function .onSelChange
- Push $0
- Push $1
- Push $2
-
- ; Check that at most one of the non-readonly spelling dictionaries are selected
- ; We can't use $R0 or $R1 in this block since they're used in the macros
- !insertmacro StartRadioButtonsUnselectable $SPELLCHECK_SEL
- ; Start with the first language dictionary
- IntOp $2 ${SecSpellCheck} + 1
-
- start_spellcheck_radio:
- SectionGetFlags $2 $0
-
- IntOp $1 $0 & ${SF_SECGRPEND}
- ; If it is the end of the section group, stop
- IntCmp $1 ${SF_SECGRPEND} end_spellcheck_radio
-
- IntOp $0 $0 & ${SF_RO}
- IntCmp $0 ${SF_RO} after_button_insert
- ; If !readonly, then it is part of the radiobutton group
- !insertmacro RadioButton $2
- after_button_insert:
-
- IntOp $2 $2 + 1 ;Advance to the next section
- Goto start_spellcheck_radio
-
- end_spellcheck_radio:
- !insertmacro EndRadioButtons
-
- Pop $2
- Pop $1
- Pop $0
-FunctionEnd
-
; Page enter and exit functions..
Function preWelcomePage
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 6f60a3e2c1..8e1294d08b 100644
--- a/po/de.po
+++ b/po/de.po
@@ -7,13 +7,12 @@
#
# This file is distributed under the same license as the Pidgin package.
#
-# Bjoern Voigt <bjoern@cs.tu-berlin.de>, 2008.
msgid ""
msgstr ""
"Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-06-09 20:56+0200\n"
-"PO-Revision-Date: 2008-06-09 20:55+0200\n"
+"POT-Creation-Date: 2008-07-11 17:26+0200\n"
+"PO-Revision-Date: 2008-07-11 17:25+0200\n"
"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
"Language-Team: Deutsch <de@li.org>\n"
"MIME-Version: 1.0\n"
@@ -622,7 +621,6 @@ msgstr "Einladungsnachricht"
msgid "Invite"
msgstr "Einladen"
-#, fuzzy
msgid ""
"Please enter the name of the user you wish to invite,\n"
"along with an optional invite message."
@@ -3234,10 +3232,8 @@ 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"
@@ -6174,11 +6170,11 @@ msgstr "Ungenügende Rechte"
msgid "In local permit/deny"
msgstr "In lokaler erlaubt/verboten-Liste"
-msgid "Too evil (sender)"
-msgstr "Zu boshaft (Sender)"
+msgid "Warning level too high (sender)"
+msgstr "Warnstufe zu hoch (Absender)"
-msgid "Too evil (receiver)"
-msgstr "Zu boshaft (Empfänger)"
+msgid "Warning level too high (receiver)"
+msgstr "Warnstufe zu hoch (Empfänger)"
msgid "User temporarily unavailable"
msgstr "Benutzer ist temporär nicht verfügbar"
@@ -6282,9 +6278,8 @@ msgstr "Live-Video"
msgid "Camera"
msgstr "Kamera"
-#, fuzzy
msgid "Screen Sharing"
-msgstr "Screen Sharing"
+msgstr "Gemeinsamer Bildschirm"
msgid "Free For Chat"
msgstr "Bereit zum Chatten"
@@ -6372,7 +6367,6 @@ 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 ""
@@ -6538,20 +6532,26 @@ msgstr[1] ""
"überschritten wurde."
#, c-format
-msgid "You missed %hu message from %s because he/she was too evil."
-msgid_plural "You missed %hu messages from %s because he/she was too evil."
+msgid ""
+"You missed %hu message from %s because his/her warning level is too high."
+msgid_plural ""
+"You missed %hu messages from %s because his/her warning level is too high."
msgstr[0] ""
-"Sie haben %hu Nachricht von %s nicht erhalten, da er/sie zu boshaft war."
+"Sie haben %hu Nachricht von %s nicht erhalten, da seine/ihre Warnstufe zu "
+"hoch ist."
msgstr[1] ""
-"Sie haben %hu Nachrichten von %s nicht erhalten,/sie zu boshaft war."
+"Sie haben %hu Nachrichten von %s nicht erhalten, da seine/ihre Warnstufe zu "
+"hoch ist."
#, c-format
-msgid "You missed %hu message from %s because you are too evil."
-msgid_plural "You missed %hu messages from %s because you are too evil."
+msgid "You missed %hu message from %s because your warning level is too high."
+msgid_plural ""
+"You missed %hu messages from %s because your warning level is too high."
msgstr[0] ""
-"Sie haben %hu Nachricht von %s nicht erhalten, da Sie zu boshaft sind."
+"Sie haben %hu Nachricht von %s nicht erhalten, da Ihre Warnstufe zu hoch ist."
msgstr[1] ""
-"Sie haben %hu Nachrichten von %s nicht erhalten, da Sie zu boshaft sind."
+"Sie haben %hu Nachrichten von %s nicht erhalten, da Ihre Warnstufe zu hoch "
+"ist."
#, c-format
msgid "You missed %hu message from %s for an unknown reason."
@@ -7333,10 +7333,6 @@ 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"
@@ -7362,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"
@@ -7419,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"
@@ -7436,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?"
@@ -8499,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"
@@ -8943,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"
@@ -9395,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"
@@ -9760,7 +9786,6 @@ msgstr "Die Tastenkombination für den Smiley"
msgid "Stored Image"
msgstr "Gespeichertes Bild"
-#, fuzzy
msgid "Stored Image. (that'll have to do for now)"
msgstr "Gespeichertes Bild. (Das muss erstmal reichen)"
@@ -10263,9 +10288,6 @@ msgstr "/Werkzeuge/Buddy-_Alarm"
msgid "/Tools/_Certificates"
msgstr "/Werkzeuge/_Zertifikate"
-msgid "/Tools/Smile_y"
-msgstr "/Werkzeuge/Smile_y"
-
msgid "/Tools/Plu_gins"
msgstr "/Werkzeuge/Plu_gins"
@@ -10275,6 +10297,9 @@ msgstr "/Werkzeuge/_Einstellungen"
msgid "/Tools/Pr_ivacy"
msgstr "/Werkzeuge/Pri_vatsphäre"
+msgid "/Tools/Smile_y"
+msgstr "/Werkzeuge/Smile_y"
+
msgid "/Tools/_File Transfers"
msgstr "/Werkzeuge/_Dateiübertragungen"
@@ -10508,6 +10533,12 @@ msgstr "_Chat verstecken, wenn das Fenster geschlossen wird."
msgid "Please enter the name of the group to be added."
msgstr "Bitte geben Sie den Namen der Gruppe ein, die hinzugefügt werden soll."
+msgid "Enable Account"
+msgstr "Konten aktivieren"
+
+msgid "<PurpleMain>/Accounts/Enable Account"
+msgstr "<PurpleMain>/Konten/Konto aktivieren"
+
msgid "<PurpleMain>/Accounts/"
msgstr "<PurpleMain>/Konten/"
@@ -10520,12 +10551,6 @@ msgstr "Keine Aktionen verfügbar"
msgid "_Disable"
msgstr "_Deaktivieren"
-msgid "Enable Account"
-msgstr "Konten aktivieren"
-
-msgid "<PurpleMain>/Accounts/Enable Account"
-msgstr "<PurpleMain>/Konten/Konto aktivieren"
-
msgid "/Tools"
msgstr "/Werkzeuge"
@@ -11440,9 +11465,8 @@ msgstr "Farbe des Absendernamens für Aktions-Nachrichten"
msgid "Color to draw the name of an action message."
msgstr "Farbe, mit der der Name in einer Aktions-Nachricht dargestellt wird."
-#, fuzzy
msgid "Action Message Name Color for Whispered Message"
-msgstr "Farbe des Absendernamens für Aktions-Nachrichten"
+msgstr "Farbe des Absendernamens für geflüsterte Aktions-Nachrichten"
msgid "Whisper Message Name Color"
msgstr "Farbe des Absendernamens für Flüster-Nachrichten"
@@ -11517,7 +11541,6 @@ msgstr "Bild speichern"
msgid "_Save Image..."
msgstr "Bild _speichern..."
-#, fuzzy
msgid "_Add Custom Smiley..."
msgstr "Benutzerdefinierten Smiley _hinzufügen..."
@@ -11559,16 +11582,18 @@ msgstr "Speichern des Bildes fehlgeschlagen: %s\n"
msgid "Insert Image"
msgstr "Bild einfügen"
+#, c-format
msgid ""
-"This smiley is disabled because a custom smiley exists for this shortcut."
+"This smiley is disabled because a custom smiley exists for this shortcut:\n"
+" %s"
msgstr ""
"Dieser Smiley ist deaktiviert, da ein benutzerdefinierter Smiley für diese "
-"Tastenkombination existiert."
+"Tastenkombination existiert:\n"
+" %s"
msgid "Smile!"
msgstr "Lächeln!"
-#, fuzzy
msgid "_Manage custom smileys"
msgstr "Benutzerdefinierte Smileys _verwalten"
diff --git a/po/en_GB.po b/po/en_GB.po
index 2e41006a77..bf473cd6a7 100644
--- a/po/en_GB.po
+++ b/po/en_GB.po
@@ -2911,7 +2911,7 @@ msgid "Your current password is different from the one that you specified."
msgstr "Your current password is different from the one that you specified."
msgid "Unable to change password. Error occurred.\n"
-msgstr "Unable to change password. An error occured.\n"
+msgstr "Unable to change password. An error occurred.\n"
msgid "Change password for the Gadu-Gadu account"
msgstr "Change password for the Gadu-Gadu account"
@@ -5270,7 +5270,7 @@ msgid "Message could not be sent because the user is offline:"
msgstr "Message could not be sent because the user is offline:"
msgid "Message could not be sent because a connection error occurred:"
-msgstr "Message could not be sent because a connection error occured:"
+msgstr "Message could not be sent because a connection error occurred:"
msgid "Message could not be sent because we are sending too quickly:"
msgstr "Message could not be sent because we are sending too quickly:"
diff --git a/po/it.po b/po/it.po
index 5c84f1879b..b49a2d6875 100644
--- a/po/it.po
+++ b/po/it.po
@@ -3833,8 +3833,8 @@ msgid "Unable to detect ActiveTCL installation. If you wish to use TCL plugins,
msgstr "Impossibile trovare un'installazione di ActiveTCL. Se vuoi usare i plugin TCL, installa ActiveTCL da http://www.activestate.com\n"
#: ../libpurple/protocols/bonjour/bonjour.c:107
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
-msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging per maggiori informazioni."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
+msgstr "Il toolkit Apple Bonjour per Windows non è stato trovato. Leggi le FAQ su: http://d.pidgin.im/BonjourWindows per maggiori informazioni."
#: ../libpurple/protocols/bonjour/bonjour.c:126
msgid "Unable to listen for incoming IM connections\n"
diff --git a/po/nb.po b/po/nb.po
index f98a158857..5ccfb9d2a3 100644
--- a/po/nb.po
+++ b/po/nb.po
@@ -3877,7 +3877,7 @@ msgid "Unable to detect ActiveTCL installation. If you wish to use TCL plugins,
msgstr "Kunne ikke finne en ActiveTCL installasjon. Om du ønsker å bruke TCL tillegg, installer ActiveTCL fra http://www.activestate.com\n"
#: ../libpurple/protocols/bonjour/bonjour.c:101
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
msgstr ""
#: ../libpurple/protocols/bonjour/bonjour.c:120
diff --git a/po/te.po b/po/te.po
index e558902a23..aa581f6578 100644
--- a/po/te.po
+++ b/po/te.po
@@ -4059,7 +4059,7 @@ msgid "Unable to detect ActiveTCL installation. If you wish to use TCL plugins,
msgstr "ActiveTCL ఇన్స్టాలేషన్ ను కనుగొనడంలో అశక్తత. మీరు TCL ప్లగ్ ఇన్లను ఉపయోగించాలనుకుంటే http://www.activestate.com\n"
#: ../libpurple/protocols/bonjour/bonjour.c:108
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
msgstr ""
#: ../libpurple/protocols/bonjour/bonjour.c:127
diff --git a/po/ur.po b/po/ur.po
index 70be1c5e4c..3887431ffa 100644
--- a/po/ur.po
+++ b/po/ur.po
@@ -3891,7 +3891,7 @@ msgid "Unable to detect ActiveTCL installation. If you wish to use TCL plugins,
msgstr "ایكٹیو TCL ا نسٹالیشن كی جانچ نا ممكن۔ اگر آپ TCL پلگ انس استعمال كرنا چاہتے ہیں ، ایكٹیوTCL انسٹال كروfrom http://www.activestate.com\n"
#: ../libpurple/protocols/bonjour/bonjour.c:108
-msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging for more information."
+msgid "The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://d.pidgin.im/BonjourWindows for more information."
msgstr ""
#: ../libpurple/protocols/bonjour/bonjour.c:127
diff --git a/po/vi.po b/po/vi.po
index 103871538f..883dca88e9 100644
--- a/po/vi.po
+++ b/po/vi.po
@@ -3916,7 +3916,7 @@ msgid ""
"The Apple Bonjour For Windows toolkit wasn't found, see the FAQ at: http://"
"developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-"
"LocalMessaging for more information."
-msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://developer.pidgin.im/wiki/Using%20Pidgin#CanIusePidginforBonjourLink-LocalMessaging » để tìm chi tiết."
+msgstr "Không tìm thấy bộ công cụ Apple Bonjour For Windows, xem FAQ (Hỏi Đáp) ở địa chỉ « http://d.pidgin.im/BonjourWindows » để tìm chi tiết."
#: ../libpurple/protocols/bonjour/bonjour.c:120
msgid "Unable to listen for incoming IM connections\n"
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 \