summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Kramlich <grim@reaperworld.com>2008-06-30 23:12:54 +0000
committerGary Kramlich <grim@reaperworld.com>2008-06-30 23:12:54 +0000
commitaddad6fa63e6750212d7973c0458649e2b4b1004 (patch)
tree831dbed7c036043ece65076480a9fe6245c0c57b
parent68a34379e78833ea602fb45c90d6eb217f3a2e3d (diff)
parent30d2294740f85529ed1973f0c69b05b97feb74ad (diff)
downloadpidgin-addad6fa63e6750212d7973c0458649e2b4b1004.tar.gz
propagate from branch 'im.pidgin.pidgin' (head 19dd5b0bbe76eaaafdcc8eeff1b0f7fa9332048d)
to branch 'im.pidgin.soc.2008.themes' (head 79934bf9a3492ef2ac9bc22902e313778cfe1b47)
-rw-r--r--COPYRIGHT2
-rw-r--r--ChangeLog23
-rw-r--r--ChangeLog.API2
-rw-r--r--configure.ac21
-rw-r--r--doc/gtkrc-2.02
-rw-r--r--finch/gntblist.c26
-rw-r--r--finch/gntblist.h4
-rw-r--r--finch/gntconv.c103
-rw-r--r--finch/gntft.c4
-rw-r--r--finch/gntlog.c69
-rw-r--r--finch/libgnt/gntbutton.c26
-rw-r--r--finch/libgnt/gntcolors.c3
-rw-r--r--finch/libgnt/gntcolors.h2
-rw-r--r--finch/libgnt/gntentry.c12
-rw-r--r--finch/libgnt/gntkeys.c6
-rw-r--r--finch/libgnt/gntmenu.c8
-rw-r--r--finch/libgnt/gnttree.c5
-rw-r--r--libpurple/Makefile.am6
-rw-r--r--libpurple/blist.h7
-rw-r--r--libpurple/certificate.c7
-rw-r--r--libpurple/cmds.h20
-rw-r--r--libpurple/connection.h5
-rw-r--r--libpurple/conversation.c2
-rw-r--r--libpurple/conversation.h6
-rw-r--r--libpurple/core.h69
-rw-r--r--libpurple/idle.c2
-rw-r--r--libpurple/internal.h8
-rw-r--r--libpurple/log.c4
-rw-r--r--libpurple/nat-pmp.c4
-rw-r--r--libpurple/plugins/perl/perl-handlers.c3
-rw-r--r--libpurple/plugins/ssl/ssl-gnutls.c4
-rw-r--r--libpurple/privacy.c20
-rw-r--r--libpurple/protocols/gg/gg.c83
-rw-r--r--libpurple/protocols/gg/search.h2
-rw-r--r--libpurple/protocols/irc/msgs.c10
-rw-r--r--libpurple/protocols/irc/parse.c11
-rw-r--r--libpurple/protocols/jabber/auth.c94
-rw-r--r--libpurple/protocols/jabber/buddy.c34
-rw-r--r--libpurple/protocols/jabber/buddy.h2
-rw-r--r--libpurple/protocols/jabber/chat.c8
-rw-r--r--libpurple/protocols/jabber/chat.h1
-rw-r--r--libpurple/protocols/jabber/jabber.c143
-rw-r--r--libpurple/protocols/jabber/libxmpp.c2
-rw-r--r--libpurple/protocols/jabber/pep.h2
-rw-r--r--libpurple/protocols/jabber/presence.c15
-rw-r--r--libpurple/protocols/jabber/si.c150
-rw-r--r--libpurple/protocols/jabber/xdata.c16
-rw-r--r--libpurple/protocols/msn/contact.c94
-rw-r--r--libpurple/protocols/msn/contact.h20
-rw-r--r--libpurple/protocols/msn/dialog.c2
-rw-r--r--libpurple/protocols/msn/error.c2
-rw-r--r--libpurple/protocols/msn/msn.c6
-rw-r--r--libpurple/protocols/msn/msnutils.c8
-rw-r--r--libpurple/protocols/msn/notification.c49
-rw-r--r--libpurple/protocols/msn/notification.h9
-rw-r--r--libpurple/protocols/msn/object.c2
-rw-r--r--libpurple/protocols/msn/oim.c18
-rw-r--r--libpurple/protocols/msn/oim.h3
-rw-r--r--libpurple/protocols/msn/servconn.c6
-rw-r--r--libpurple/protocols/msn/session.h1
-rw-r--r--libpurple/protocols/msn/slp.c12
-rw-r--r--libpurple/protocols/msn/slplink.c4
-rw-r--r--libpurple/protocols/msn/slplink.h12
-rw-r--r--libpurple/protocols/msn/soap.c48
-rw-r--r--libpurple/protocols/msn/soap.h2
-rw-r--r--libpurple/protocols/msn/soap2.c55
-rw-r--r--libpurple/protocols/msn/state.c6
-rw-r--r--libpurple/protocols/msn/transaction.h2
-rw-r--r--libpurple/protocols/msn/user.c4
-rw-r--r--libpurple/protocols/msn/user.h9
-rw-r--r--libpurple/protocols/msn/userlist.c68
-rw-r--r--libpurple/protocols/msn/userlist.h11
-rw-r--r--libpurple/protocols/msnp9/slplink.c2
-rw-r--r--libpurple/protocols/myspace/myspace.c12
-rw-r--r--libpurple/protocols/myspace/user.c4
-rw-r--r--libpurple/protocols/oscar/family_admin.c24
-rw-r--r--libpurple/protocols/oscar/family_advert.c4
-rw-r--r--libpurple/protocols/oscar/family_alert.c10
-rw-r--r--libpurple/protocols/oscar/family_auth.c27
-rw-r--r--libpurple/protocols/oscar/family_bart.c14
-rw-r--r--libpurple/protocols/oscar/family_bos.c10
-rw-r--r--libpurple/protocols/oscar/family_buddy.c14
-rw-r--r--libpurple/protocols/oscar/family_chat.c6
-rw-r--r--libpurple/protocols/oscar/family_chatnav.c12
-rw-r--r--libpurple/protocols/oscar/family_icbm.c100
-rw-r--r--libpurple/protocols/oscar/family_icq.c64
-rw-r--r--libpurple/protocols/oscar/family_invite.c2
-rw-r--r--libpurple/protocols/oscar/family_locate.c32
-rw-r--r--libpurple/protocols/oscar/family_odir.c20
-rw-r--r--libpurple/protocols/oscar/family_oservice.c56
-rw-r--r--libpurple/protocols/oscar/family_popup.c2
-rw-r--r--libpurple/protocols/oscar/family_stats.c2
-rw-r--r--libpurple/protocols/oscar/family_translate.c2
-rw-r--r--libpurple/protocols/oscar/family_userlookup.c6
-rw-r--r--libpurple/protocols/oscar/oscar.c386
-rw-r--r--libpurple/protocols/oscar/oscar.h5
-rw-r--r--libpurple/protocols/oscar/oscarcommon.h1
-rw-r--r--libpurple/protocols/qq/ChangeLog49
-rw-r--r--libpurple/protocols/qq/Makefile.am14
-rw-r--r--libpurple/protocols/qq/Makefile.mingw7
-rw-r--r--libpurple/protocols/qq/buddy_info.c340
-rw-r--r--libpurple/protocols/qq/buddy_info.h41
-rw-r--r--libpurple/protocols/qq/buddy_list.c453
-rw-r--r--libpurple/protocols/qq/buddy_opt.c153
-rw-r--r--libpurple/protocols/qq/buddy_status.c180
-rw-r--r--libpurple/protocols/qq/buddy_status.h2
-rw-r--r--libpurple/protocols/qq/char_conv.c47
-rw-r--r--libpurple/protocols/qq/crypt.c17
-rw-r--r--libpurple/protocols/qq/crypt.h9
-rw-r--r--libpurple/protocols/qq/file_trans.c421
-rw-r--r--libpurple/protocols/qq/group_im.c181
-rw-r--r--libpurple/protocols/qq/group_im.h40
-rw-r--r--libpurple/protocols/qq/group_info.c210
-rw-r--r--libpurple/protocols/qq/group_info.h6
-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.c196
-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/im.c418
-rw-r--r--libpurple/protocols/qq/keep_alive.c11
-rw-r--r--libpurple/protocols/qq/login_logout.c286
-rw-r--r--libpurple/protocols/qq/packet_parse.c178
-rw-r--r--libpurple/protocols/qq/packet_parse.h14
-rw-r--r--libpurple/protocols/qq/qq.c161
-rw-r--r--libpurple/protocols/qq/qq.h52
-rw-r--r--libpurple/protocols/qq/qq_network.c1258
-rw-r--r--libpurple/protocols/qq/qq_network.h (renamed from libpurple/protocols/qq/recv_core.h)18
-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.c246
-rw-r--r--libpurple/protocols/qq/qq_trans.h (renamed from libpurple/protocols/qq/sendqueue.h)26
-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.c214
-rw-r--r--libpurple/protocols/qq/send_file.h19
-rw-r--r--libpurple/protocols/qq/sendqueue.c156
-rw-r--r--libpurple/protocols/qq/sys_msg.c22
-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.c83
-rw-r--r--libpurple/protocols/qq/utils.h10
-rw-r--r--libpurple/protocols/silc/buddy.c18
-rw-r--r--libpurple/protocols/silc10/buddy.c18
-rw-r--r--libpurple/protocols/simple/simple.c7
-rw-r--r--libpurple/protocols/yahoo/yahoo.c1
-rw-r--r--libpurple/protocols/yahoo/yahoo_picture.c105
-rw-r--r--libpurple/protocols/yahoo/yahoo_profile.c1
-rw-r--r--libpurple/proxy.c7
-rw-r--r--libpurple/proxy.h12
-rw-r--r--libpurple/prpl.h4
-rw-r--r--libpurple/roomlist.h1
-rw-r--r--libpurple/server.c5
-rw-r--r--libpurple/smiley.c5
-rw-r--r--libpurple/sound.h3
-rw-r--r--libpurple/status.c2
-rw-r--r--libpurple/util.c135
-rw-r--r--libpurple/win32/global.mak2
-rw-r--r--pidgin/gtkaccount.c3
-rw-r--r--pidgin/gtkblist.c126
-rw-r--r--pidgin/gtkconv.c65
-rw-r--r--pidgin/gtkdialogs.c2
-rw-r--r--pidgin/gtkft.c6
-rw-r--r--pidgin/gtkimhtml.c109
-rw-r--r--pidgin/gtkimhtml.h25
-rw-r--r--pidgin/gtkimhtmltoolbar.c26
-rw-r--r--pidgin/gtkmenutray.c9
-rw-r--r--pidgin/gtksavedstatuses.c224
-rw-r--r--pidgin/gtksmiley.c16
-rw-r--r--pidgin/gtkstatusbox.c57
-rw-r--r--pidgin/gtkutils.c7
-rw-r--r--pidgin/pidginstock.c1
-rw-r--r--pidgin/pidginstock.h1
-rw-r--r--pidgin/pixmaps/Makefile.am1
-rw-r--r--pidgin/pixmaps/status/16/available.pngbin846 -> 886 bytes
-rw-r--r--pidgin/pixmaps/status/16/away.pngbin823 -> 890 bytes
-rw-r--r--pidgin/pixmaps/status/16/busy.pngbin742 -> 822 bytes
-rw-r--r--pidgin/pixmaps/status/16/chat.pngbin865 -> 945 bytes
-rw-r--r--pidgin/pixmaps/status/16/offline.pngbin894 -> 973 bytes
-rw-r--r--pidgin/pixmaps/status/16/person.pngbin765 -> 804 bytes
-rw-r--r--pidgin/pixmaps/status/16/scalable/available.svg111
-rw-r--r--pidgin/pixmaps/status/16/scalable/away.svg90
-rw-r--r--pidgin/pixmaps/status/16/scalable/busy.svg343
-rw-r--r--pidgin/pixmaps/status/16/scalable/chat.svg488
-rw-r--r--pidgin/pixmaps/status/16/scalable/offline.svg121
-rw-r--r--pidgin/pixmaps/status/16/scalable/person.svg318
-rw-r--r--pidgin/pixmaps/toolbar/16/send-file.pngbin548 -> 479 bytes
-rw-r--r--pidgin/pixmaps/toolbar/16/transfer.pngbin0 -> 548 bytes
-rw-r--r--pidgin/plugins/Makefile.mingw1
-rw-r--r--pidgin/plugins/cap/cap.c2
-rw-r--r--pidgin/plugins/spellchk.c1
-rw-r--r--pidgin/win32/nsis/pidgin-installer.nsi62
-rw-r--r--po/ar.po54
-rw-r--r--po/bn.po12
-rw-r--r--po/bs.po2
-rw-r--r--po/ca@valencia.po19
-rw-r--r--po/de.po267
-rw-r--r--po/en_AU.po2
-rw-r--r--po/en_GB.po4
-rw-r--r--po/fi.po1
-rw-r--r--po/gl.po2
-rw-r--r--po/it.po4
-rw-r--r--po/ko.po52
-rw-r--r--po/lo.po2
-rw-r--r--po/lt.po32
-rw-r--r--po/nb.po4
-rw-r--r--po/nl.po25
-rw-r--r--po/ps.po2
-rw-r--r--po/sq.po1
-rw-r--r--po/ta.po2
-rw-r--r--po/te.po160
-rw-r--r--po/th.po7
-rw-r--r--po/ur.po214
-rw-r--r--po/vi.po7
-rw-r--r--po/zh_CN.po2
-rw-r--r--share/ca-certs/Makefile.am10
-rw-r--r--share/ca-certs/Makefile.mingw7
220 files changed, 7614 insertions, 5663 deletions
diff --git a/COPYRIGHT b/COPYRIGHT
index 697d3d412e..cb1991b92d 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -178,6 +178,7 @@ Fernando Herrera
hjheins
Hil
Casey Ho
+Andrew Hoffman
Iain Holmes
Joshua Honeycutt
Nigel Horne
@@ -388,6 +389,7 @@ Kevin Stange
Richard Stellingwerff
Charlie Stockman
David Stoddard
+Adam Strzelecki
Andreas Stührk
Oleg Sukhodolsky
Sun Microsystems
diff --git a/ChangeLog b/ChangeLog
index d91ef75f02..d26d262b25 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,19 +5,36 @@ 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.
Pidgin:
- * Custom buddy icons can now be added and removed to buddy list
+ * 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.
General:
* Group and Chat buddy list entries can now be given custom buddy
icons.
+ Finch:
+ * Added "Invite..." menu to chats.
+ * Added "View All Logs" menu in the buddylist to display a list of all IM
+ logs.
+ * Added '/msgcolor' command to change colors of different classes of
+ messages in a conversation. See '/help msgcolor' for details.
+
+version 2.4.3 (??/??/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.
+
version 2.4.2 (05/17/2008):
libpurple:
* In MySpaceIM, messages from spambots are discarded (Justin Williams)
diff --git a/ChangeLog.API b/ChangeLog.API
index 6f009f7656..309357241b 100644
--- a/ChangeLog.API
+++ b/ChangeLog.API
@@ -36,7 +36,7 @@ version 2.5.0 (??/??/2008):
* GTK_IMHTML_CUSTOM_SMILEY flag for GtkIMHtml.
* GTK+ Custom Smiley API.
-version 2.4.2:
+version 2.4.2 (05/17/2008):
perl:
Added:
* Purple::Prefs::get_children_names.
diff --git a/configure.ac b/configure.ac
index 72c9946e03..2e630bf460 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1207,8 +1207,8 @@ dnl #######################################################################
dnl # Check for D-Bus libraries
dnl #######################################################################
-AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--enable-dbus], [enable D-Bus support])], , enable_dbus=yes)
-AC_ARG_ENABLE(nm, [AC_HELP_STRING([--enable-nm], [enable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
+AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes)
+AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes)
if test "x$enable_dbus" = "xyes" ; then
AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no)
@@ -1561,6 +1561,18 @@ dnl #
dnl # Thanks go to Evolution for the checks.
dnl #######################################################################
+AC_ARG_WITH(with-system-ssl-certs, [AC_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])])
+
+SSL_CERTIFICATES_DIR=""
+if ! test -z "$with_system_ssl_certs" ; then
+ if ! test -d "$with_system_ssl_certs" ; then
+ AC_MSG_ERROR([$with_system_ssl_certs does not exist, if this is the correct location please make sure that it exists.])
+ fi
+ SSL_CERTIFICATES_DIR="$with_system_ssl_certs"
+fi
+AC_SUBST(SSL_CERTIFICATES_DIR)
+AM_CONDITIONAL(INSTALL_SSL_CERTIFICATES, test "x$SSL_CERTIFICATES_DIR" = "x")
+
dnl These two are inverses of each other <-- stolen from evolution!
AC_ARG_ENABLE(gnutls,
@@ -2211,9 +2223,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
@@ -2409,6 +2423,9 @@ if test "x$enable_dbus" = "xyes" ; then
fi
echo Build with NetworkManager..... : $enable_nm
echo SSL Library/Libraries......... : $msg_ssl
+if test "x$SSL_CERTIFICATES_DIR" != "x" ; then
+ eval eval echo SSL CA certificates directory. : $SSL_CERTIFICATES_DIR
+fi
echo Build with Cyrus SASL support. : $enable_cyrus_sasl
echo Use kerberos 4 with zephyr.... : $kerberos
echo Use external libzephyr........ : $zephyr
diff --git a/doc/gtkrc-2.0 b/doc/gtkrc-2.0
index 802d9f666b..6817535301 100644
--- a/doc/gtkrc-2.0
+++ b/doc/gtkrc-2.0
@@ -45,7 +45,7 @@ style "my-style-name" {
# Change the color of the typing notification
GtkIMHtml::typing-notification-color = "#ff0000"
# Disable the typing notification
- GtkIMHtml::typing-notification-enable = 1
+ GtkIMHtml::typing-notification-enable = 0
# The following settings will change the behaviour in all GTK+ applications
# Change the cursor color
diff --git a/finch/gntblist.c b/finch/gntblist.c
index 65b13bf9bc..101e5fd069 100644
--- a/finch/gntblist.c
+++ b/finch/gntblist.c
@@ -2722,6 +2722,7 @@ join_chat_select_cb(gpointer data, PurpleRequestFields *fields)
PurpleConnection *gc;
PurpleChat *chat;
GHashTable *hash = NULL;
+ PurpleConversation *conv;
account = purple_request_fields_get_account(fields, "account");
name = purple_request_fields_get_string(fields, "chat");
@@ -2730,7 +2731,16 @@ join_chat_select_cb(gpointer data, PurpleRequestFields *fields)
return;
gc = purple_account_get_connection(account);
- purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
+ /* Create a new conversation now. This will give focus to the new window.
+ * But it's necessary to pretend that we left the chat, because otherwise
+ * a new conversation window will pop up when we finally join the chat. */
+ if (!(conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, account))) {
+ conv = purple_conversation_new(PURPLE_CONV_TYPE_CHAT, account, name);
+ purple_conv_chat_left(PURPLE_CONV_CHAT(conv));
+ } else {
+ purple_conversation_present(conv);
+ }
+
chat = purple_blist_find_chat(account, name);
if (chat == NULL) {
PurplePluginProtocolInfo *info = PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc));
@@ -2841,6 +2851,12 @@ view_log_cb(GntMenuItem *item, gpointer n)
}
static void
+view_all_logs_cb(GntMenuItem *item, gpointer n)
+{
+ finch_log_show(PURPLE_LOG_IM, NULL, NULL);
+}
+
+static void
menu_add_buddy_cb(GntMenuItem *item, gpointer null)
{
purple_blist_request_add_buddy(NULL, NULL, NULL, NULL);
@@ -2905,6 +2921,11 @@ create_menu(void)
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), view_log_cb, NULL);
+ item = gnt_menuitem_new(_("View All Logs"));
+ gnt_menuitem_set_id(GNT_MENU_ITEM(item), "view-all-logs");
+ gnt_menu_add_item(GNT_MENU(sub), item);
+ gnt_menuitem_set_callback(GNT_MENU_ITEM(item), view_all_logs_cb, NULL);
+
item = gnt_menuitem_new(_("Show"));
gnt_menu_add_item(GNT_MENU(sub), item);
subsub = gnt_menu_new(GNT_MENU_POPUP);
@@ -3012,9 +3033,6 @@ blist_show(PurpleBuddyList *list)
gnt_widget_set_position(ggblist->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
purple_prefs_get_int(PREF_ROOT "/position/y"));
- gnt_tree_set_col_width(GNT_TREE(ggblist->tree), 0,
- purple_prefs_get_int(PREF_ROOT "/size/width") - 1);
-
gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
ggblist->status = gnt_combo_box_new();
diff --git a/finch/gntblist.h b/finch/gntblist.h
index a4a2c6abf0..a8037d597d 100644
--- a/finch/gntblist.h
+++ b/finch/gntblist.h
@@ -34,6 +34,10 @@
**********************************************************************/
/*@{*/
+/**
+ * Buddylist manager for finch. This decides the visility, ordering and hierarchy
+ * of the buddylist nodes. This also manages the creation of tooltips.
+ */
typedef struct
{
const char *id; /**< An identifier for the manager. */
diff --git a/finch/gntconv.c b/finch/gntconv.c
index e589bd2ee2..6bf3bfb5c6 100644
--- a/finch/gntconv.c
+++ b/finch/gntconv.c
@@ -38,10 +38,11 @@
#include "gntdebug.h"
#include "gntlog.h"
#include "gntplugin.h"
+#include "gntpounce.h"
#include "gntprefs.h"
+#include "gntrequest.h"
#include "gntsound.h"
#include "gntstatus.h"
-#include "gntpounce.h"
#include "gnt.h"
#include "gntbox.h"
@@ -140,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;
@@ -190,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;
@@ -557,6 +558,47 @@ generate_send_to_menu(FinchConv *ggc)
}
static void
+invite_select_cb(FinchConv *fc, PurpleRequestFields *fields)
+{
+ PurpleConversation *conv = fc->active_conv;
+ const char *buddy = purple_request_fields_get_string(fields, "screenname");
+ const char *message = purple_request_fields_get_string(fields, "message");
+ serv_chat_invite(purple_conversation_get_gc(conv),
+ purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)),
+ message, buddy);
+
+}
+
+static void
+invite_cb(GntMenuItem *item, gpointer ggconv)
+{
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+
+ fields = purple_request_fields_new();
+
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
+ purple_request_field_set_type_hint(field, "screenname");
+ purple_request_field_set_required(field, TRUE);
+ purple_request_field_group_add_field(group, field);
+ field = purple_request_field_string_new("message", _("Invite message"), NULL, FALSE);
+ purple_request_field_group_add_field(group, field);
+ purple_request_fields(finch_conv_get_handle(), _("Invite"),
+ NULL,
+ _("Please enter the name of the user "
+ "you wish to invite,\nalong with an optional invite message."),
+ fields,
+ _("OK"), G_CALLBACK(invite_select_cb),
+ _("Cancel"), NULL,
+ NULL, NULL, NULL,
+ ggconv);
+}
+
+static void
gg_create_menu(FinchConv *ggc)
{
GntWidget *menu, *sub;
@@ -606,6 +648,10 @@ gg_create_menu(FinchConv *ggc)
}
generate_send_to_menu(ggc);
+ } else if (purple_conversation_get_type(ggc->active_conv) == PURPLE_CONV_TYPE_CHAT) {
+ item = gnt_menuitem_new(_("Invite..."));
+ gnt_menu_add_item(GNT_MENU(sub), item);
+ gnt_menuitem_set_callback(item, invite_cb, ggc);
}
item = gnt_menuitem_new(_("View Log..."));
@@ -1195,6 +1241,47 @@ cmd_show_window(PurpleConversation *conv, const char *cmd, char **args, char **e
}
static PurpleCmdRet
+cmd_message_color(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
+{
+ int *msgclass = NULL;
+ int fg, bg;
+
+ if (strcmp(args[0], "receive") == 0)
+ msgclass = &color_message_receive;
+ else if (strcmp(args[0], "send") == 0)
+ msgclass = &color_message_send;
+ else if (strcmp(args[0], "highlight") == 0)
+ msgclass = &color_message_highlight;
+ else if (strcmp(args[0], "action") == 0)
+ msgclass = &color_message_action;
+ else if (strcmp(args[0], "timestamp") == 0)
+ msgclass = &color_timestamp;
+ else {
+ if (error)
+ *error = g_strdup_printf(_("%s is not a valid message class. See '/help msgcolor' for valid message classes."), args[0]);
+ return PURPLE_CMD_STATUS_FAILED;
+ }
+
+ fg = gnt_colors_get_color(args[1]);
+ if (fg == -EINVAL) {
+ if (error)
+ *error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[1]);
+ return PURPLE_CMD_STATUS_FAILED;
+ }
+
+ bg = gnt_colors_get_color(args[2]);
+ if (bg == -EINVAL) {
+ if (error)
+ *error = g_strdup_printf(_("%s is not a valid color. See '/help msgcolor' for valid colors."), args[2]);
+ return PURPLE_CMD_STATUS_FAILED;
+ }
+
+ init_pair(*msgclass, fg, bg);
+
+ return PURPLE_CMD_STATUS_OK;
+}
+
+static PurpleCmdRet
users_command_cb(PurpleConversation *conv, const char *cmd, char **args, char **error, gpointer data)
{
FinchConv *fc = FINCH_GET_DATA(conv);
@@ -1278,6 +1365,16 @@ void finch_conversation_init()
PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
cmd_show_window, _("statuses: Show the savedstatuses window."), finch_savedstatus_show_all);
+ /* Allow customizing the message colors using a command during run-time */
+ purple_cmd_register("msgcolor", "www", PURPLE_CMD_P_DEFAULT,
+ PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM, NULL,
+ cmd_message_color, _("msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: "
+ "Set the color for different classes of messages in the conversation window.<br>"
+ " &lt;class&gt;: receive, send, highlight, action, timestamp<br>"
+ " &lt;foreground/background&gt;: black, red, green, blue, white, gray, darkgray, magenta, cyan, default<br><br>"
+ "EXAMPLE:<br> msgcolor send cyan default"),
+ NULL);
+
purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", finch_conv_get_handle(),
PURPLE_CALLBACK(update_buddy_typing), NULL);
purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", finch_conv_get_handle(),
diff --git a/finch/gntft.c b/finch/gntft.c
index 7bf2adcc99..43cd76c52e 100644
--- a/finch/gntft.c
+++ b/finch/gntft.c
@@ -117,7 +117,9 @@ update_title_progress(void)
total_pct = 100 * total_bytes_xferred / total_file_size;
}
- title = g_strdup_printf(_("File Transfers - %d%% of %d files"),
+ title = g_strdup_printf(ngettext("File Transfers - %d%% of %d file",
+ "File Transfers - %d%% of %d files",
+ num_active_xfers),
total_pct, num_active_xfers);
gnt_screen_rename_widget((xfer_dialog->window), title);
g_free(title);
diff --git a/finch/gntlog.c b/finch/gntlog.c
index 0fc6b6c04d..1dc973fbe4 100644
--- a/finch/gntlog.c
+++ b/finch/gntlog.c
@@ -61,8 +61,12 @@ static guint log_viewer_hash(gconstpointer data)
if (viewer->contact != NULL)
return g_direct_hash(viewer->contact);
- return g_str_hash(viewer->screenname) +
- g_str_hash(purple_account_get_username(viewer->account));
+ if (viewer->account) {
+ return g_str_hash(viewer->screenname) +
+ g_str_hash(purple_account_get_username(viewer->account));
+ }
+
+ return (guint)viewer;
}
static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
@@ -84,10 +88,14 @@ static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
return FALSE;
}
- normal = g_strdup(purple_normalize(a->account, a->screenname));
- ret = (a->account == b->account) &&
- !strcmp(normal, purple_normalize(b->account, b->screenname));
- g_free(normal);
+ if (a->screenname && b->screenname) {
+ normal = g_strdup(purple_normalize(a->account, a->screenname));
+ ret = (a->account == b->account) &&
+ !strcmp(normal, purple_normalize(b->account, b->screenname));
+ g_free(normal);
+ } else {
+ ret = (a == b);
+ }
return ret;
}
@@ -348,14 +356,28 @@ static FinchLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *l
return lv;
}
-void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account) {
+static void
+our_logging_blows(PurpleLogSet *set, PurpleLogSet *setagain, GList **list)
+{
+ /* The iteration happens on the first list. So we use the shorter list in front */
+ if (set->type != PURPLE_LOG_IM)
+ return;
+ *list = g_list_concat(purple_log_get_logs(PURPLE_LOG_IM, set->name, set->account), *list);
+}
+
+void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *account)
+{
struct log_viewer_hash_t *ht;
FinchLogViewer *lv = NULL;
const char *name = screenname;
char *title;
+ GList *logs = NULL;
+ int size = 0;
- g_return_if_fail(account != NULL);
- g_return_if_fail(screenname != NULL);
+ if (type != PURPLE_LOG_IM) {
+ g_return_if_fail(account != NULL);
+ g_return_if_fail(screenname != NULL);
+ }
ht = g_new0(struct log_viewer_hash_t, 1);
@@ -383,20 +405,35 @@ void finch_log_show(PurpleLogType type, const char *screenname, PurpleAccount *a
} else {
PurpleBuddy *buddy;
- buddy = purple_find_buddy(account, screenname);
- if (buddy != NULL)
- name = purple_buddy_get_contact_alias(buddy);
+ if (screenname) {
+ buddy = purple_find_buddy(account, screenname);
+ if (buddy != NULL)
+ name = purple_buddy_get_contact_alias(buddy);
+ title = g_strdup_printf(_("Conversations with %s"), name);
+ } else {
+ title = g_strdup(_("All Conversations"));
+ }
+ }
- title = g_strdup_printf(_("Conversations with %s"), name);
+ if (screenname) {
+ logs = purple_log_get_logs(type, screenname, account);
+ size = purple_log_get_total_size(type, screenname, account);
+ } else {
+ /* This will happen only for IMs */
+ GHashTable *table = purple_log_get_log_sets();
+ g_hash_table_foreach(table, (GHFunc)our_logging_blows, &logs);
+ g_hash_table_destroy(table);
+ logs = g_list_sort(logs, purple_log_compare);
+ size = 0;
}
- display_log_viewer(ht, purple_log_get_logs(type, screenname, account),
- title, purple_log_get_total_size(type, screenname, account));
+ display_log_viewer(ht, logs, title, size);
g_free(title);
}
-void finch_log_show_contact(PurpleContact *contact) {
+void finch_log_show_contact(PurpleContact *contact)
+{
struct log_viewer_hash_t *ht;
PurpleBlistNode *child;
FinchLogViewer *lv = NULL;
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/gntcolors.c b/finch/libgnt/gntcolors.c
index aac69aa8e5..64401d78f9 100644
--- a/finch/libgnt/gntcolors.c
+++ b/finch/libgnt/gntcolors.c
@@ -29,6 +29,7 @@
#include <glib.h>
+#include <errno.h>
#include <stdlib.h>
#include <string.h>
@@ -168,7 +169,7 @@ gnt_colors_get_color(char *key)
color = -1;
else {
g_warning("Invalid color name: %s\n", key);
- color = -1;
+ color = -EINVAL;
}
return color;
}
diff --git a/finch/libgnt/gntcolors.h b/finch/libgnt/gntcolors.h
index 60eb8c899f..6904b5e797 100644
--- a/finch/libgnt/gntcolors.h
+++ b/finch/libgnt/gntcolors.h
@@ -91,7 +91,7 @@ void gnt_color_pairs_parse(GKeyFile *kfile);
*
* @param kfile The string value
*
- * @return A color
+ * @return A color. For an unknown color name, returns -EINVAL.
*
* @since 2.4.0
*/
diff --git a/finch/libgnt/gntentry.c b/finch/libgnt/gntentry.c
index 916599fb72..e373951132 100644
--- a/finch/libgnt/gntentry.c
+++ b/finch/libgnt/gntentry.c
@@ -580,11 +580,13 @@ next_begin_word(const char *text, const char *end)
while (text && text < end && g_unichar_isspace(g_utf8_get_char(text)))
text = g_utf8_find_next_char(text, end);
- ch = g_utf8_get_char(text);
- while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
- gunichar cur = g_utf8_get_char(text);
- if (!SAME(ch, cur))
- break;
+ if (text) {
+ ch = g_utf8_get_char(text);
+ while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) {
+ gunichar cur = g_utf8_get_char(text);
+ if (!SAME(ch, cur))
+ break;
+ }
}
return (text ? text : end);
}
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/finch/libgnt/gntmenu.c b/finch/libgnt/gntmenu.c
index 621083a402..0d4ab0181c 100644
--- a/finch/libgnt/gntmenu.c
+++ b/finch/libgnt/gntmenu.c
@@ -283,6 +283,8 @@ gnt_menu_key_pressed(GntWidget *widget, const char *text)
do sub = sub->submenu; while (sub->submenu);
if (gnt_widget_key_pressed(GNT_WIDGET(sub), text))
return TRUE;
+ if (menu->type != GNT_MENU_TOPLEVEL)
+ return FALSE;
}
if ((text[0] == 27 && text[1] == 0) ||
@@ -332,10 +334,12 @@ gnt_menu_key_pressed(GntWidget *widget, const char *text)
return TRUE;
}
}
+ if (gnt_bindable_perform_action_key(GNT_BINDABLE(widget), text))
+ return TRUE;
return org_key_pressed(widget, text);
}
- return FALSE;
+ return gnt_bindable_perform_action_key(GNT_BINDABLE(widget), text);
}
static void
@@ -434,7 +438,7 @@ gnt_menu_init(GTypeInstance *instance, gpointer class)
{
GntWidget *widget = GNT_WIDGET(instance);
GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER |
- GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_TRANSIENT);
+ GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_TRANSIENT | GNT_WIDGET_DISABLE_ACTIONS);
GNTDEBUG;
}
diff --git a/finch/libgnt/gnttree.c b/finch/libgnt/gnttree.c
index 98f1554a39..15431ddc45 100644
--- a/finch/libgnt/gnttree.c
+++ b/finch/libgnt/gnttree.c
@@ -110,13 +110,14 @@ readjust_columns(GntTree *tree)
gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL);
if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER))
width -= 2;
+ width -= 1; /* Exclude the scrollbar from the calculation */
for (i = 0, total = 0; i < tree->ncol ; i++) {
if (tree->columns[i].flags & GNT_TREE_COLUMN_INVISIBLE)
continue;
if (tree->columns[i].flags & GNT_TREE_COLUMN_FIXED_SIZE)
- width -= WIDTH(i) + 1;
+ width -= WIDTH(i) + (tree->priv->lastvisible != i);
else
- total += WIDTH(i) + 1;
+ total += WIDTH(i) + (tree->priv->lastvisible != i);
}
if (total == 0)
diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
index b61f3d5fd4..2dba10e01b 100644
--- a/libpurple/Makefile.am
+++ b/libpurple/Makefile.am
@@ -271,3 +271,9 @@ AM_CPPFLAGS = \
$(DBUS_CFLAGS) \
$(LIBXML_CFLAGS) \
$(NETWORKMANAGER_CFLAGS)
+
+# INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty.
+# We want to use SSL_CERTIFICATES_DIR when it's not empty.
+if ! INSTALL_SSL_CERTIFICATES
+AM_CPPFLAGS += -DSSL_CERTIFICATES_DIR=\"$(SSL_CERTIFICATES_DIR)\"
+endif
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/certificate.c b/libpurple/certificate.c
index 332a88cb29..ca2a3421f3 100644
--- a/libpurple/certificate.c
+++ b/libpurple/certificate.c
@@ -745,8 +745,12 @@ x509_ca_init(void)
x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
"ca-certs", NULL));
#else
+# ifdef SSL_CERTIFICATES_DIR
+ x509_ca_paths = g_list_append(NULL, SSL_CERTIFICATES_DIR);
+# else
x509_ca_paths = g_list_append(NULL, g_build_filename(DATADIR,
"purple", "ca-certs", NULL));
+# endif
#endif
}
@@ -787,8 +791,7 @@ x509_ca_locate_cert(GList *lst, const gchar *dn)
for (cur = lst; cur; cur = cur->next) {
x509_ca_element *el = cur->data;
- /* TODO: Unsafe? */
- if ( !strcmp(dn, el->dn) ) {
+ if (el->dn && !strcmp(dn, el->dn)) {
return el;
}
}
diff --git a/libpurple/cmds.h b/libpurple/cmds.h
index 7cadba3a2e..1c95ac450c 100644
--- a/libpurple/cmds.h
+++ b/libpurple/cmds.h
@@ -30,6 +30,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 +40,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 {
@@ -171,7 +187,7 @@ void purple_cmd_unregister(PurpleCmdId id);
* include both the default formatting and any extra manual formatting.
* @param errormsg If the command failed errormsg is filled in with the appropriate error
* message. It must be freed by the caller with g_free().
- * @return A #PurpleCmdStatus indicated if the command succeeded or failed.
+ * @return A #PurpleCmdStatus indicating if the command succeeded or failed.
*/
PurpleCmdStatus purple_cmd_do_command(PurpleConversation *conv, const gchar *cmdline,
const gchar *markup, gchar **errormsg);
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 f164d47e75..9d29dfa6cd 100644
--- a/libpurple/conversation.c
+++ b/libpurple/conversation.c
@@ -98,7 +98,7 @@ common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags ms
char *displayed = NULL, *sent = NULL;
int err = 0;
- if (strlen(message) == 0)
+ if (*message == '\0')
return;
account = purple_conversation_get_account(conv);
diff --git a/libpurple/conversation.h b/libpurple/conversation.h
index b67ee04b47..cf8df55a52 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;
/**
diff --git a/libpurple/core.h b/libpurple/core.h
index 64d60acfb2..17c85ec508 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,33 @@ 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>
+ * </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 c5a6557283..4553d6f760 100644
--- a/libpurple/log.c
+++ b/libpurple/log.c
@@ -1067,7 +1067,7 @@ static void log_get_log_sets_common(GHashTable *sets)
set->normalized_name = g_strdup(purple_normalize(account, name));
/* Chat for .chat or .system at the end of the name to determine the type. */
- if (len > 7) {
+ if (len >= 7) {
gchar *tmp = &name[len - 7];
if (!strcmp(tmp, ".system")) {
set->type = PURPLE_LOG_SYSTEM;
@@ -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/plugins/perl/perl-handlers.c b/libpurple/plugins/perl/perl-handlers.c
index 819dca8db2..1ed58500b2 100644
--- a/libpurple/plugins/perl/perl-handlers.c
+++ b/libpurple/plugins/perl/perl-handlers.c
@@ -383,6 +383,9 @@ perl_signal_cb(va_list args, void *data)
case PURPLE_TYPE_BOXED:
*((void **)copy_args[i]) = (void *)SvIV(sv_args[i]);
break;
+ case PURPLE_TYPE_SUBTYPE:
+ *((void **)copy_args[i]) = purple_perl_ref_object(sv_args[i]);
+ break;
default:
break;
diff --git a/libpurple/plugins/ssl/ssl-gnutls.c b/libpurple/plugins/ssl/ssl-gnutls.c
index 78f895f242..91d83ce3c1 100644
--- a/libpurple/plugins/ssl/ssl-gnutls.c
+++ b/libpurple/plugins/ssl/ssl-gnutls.c
@@ -54,8 +54,8 @@ ssl_gnutls_init_gnutls(void)
If there are strange bugs, perhaps look here (yes, I am a
hypocrite) */
gnutls_global_set_mem_functions(
- (gnutls_alloc_function) g_malloc0, /* malloc */
- (gnutls_alloc_function) g_malloc0, /* secure malloc */
+ (gnutls_alloc_function) g_malloc, /* malloc */
+ (gnutls_alloc_function) g_malloc, /* secure malloc */
NULL, /* mem_is_secure */
(gnutls_realloc_function) g_realloc, /* realloc */
(gnutls_free_function) g_free /* free */
diff --git a/libpurple/privacy.c b/libpurple/privacy.c
index 196fd60fdf..f92827cf21 100644
--- a/libpurple/privacy.c
+++ b/libpurple/privacy.c
@@ -241,6 +241,7 @@ purple_privacy_allow(PurpleAccount *account, const char *who, gboolean local,
gboolean restore)
{
GSList *list;
+ PurplePrivacyType type = account->perm_deny;
switch (account->perm_deny) {
case PURPLE_PRIVACY_ALLOW_ALL:
@@ -254,10 +255,12 @@ purple_privacy_allow(PurpleAccount *account, const char *who, gboolean local,
case PURPLE_PRIVACY_DENY_ALL:
if (!restore) {
/* Empty the allow-list. */
+ const char *norm = purple_normalize(account, who);
for (list = account->permit; list != NULL;) {
- char *who = list->data;
+ char *person = list->data;
list = list->next;
- purple_privacy_permit_remove(account, who, local);
+ if (strcmp(norm, person) != 0)
+ purple_privacy_permit_remove(account, person, local);
}
}
purple_privacy_permit_add(account, who, local);
@@ -273,6 +276,10 @@ purple_privacy_allow(PurpleAccount *account, const char *who, gboolean local,
default:
g_return_if_reached();
}
+
+ /* Notify the server if the privacy setting was changed */
+ if (type != account->perm_deny && purple_account_is_connected(account))
+ serv_set_permit_deny(purple_account_get_connection(account));
}
/*
@@ -286,15 +293,18 @@ purple_privacy_deny(PurpleAccount *account, const char *who, gboolean local,
gboolean restore)
{
GSList *list;
+ PurplePrivacyType type = account->perm_deny;
switch (account->perm_deny) {
case PURPLE_PRIVACY_ALLOW_ALL:
if (!restore) {
/* Empty the deny-list. */
+ const char *norm = purple_normalize(account, who);
for (list = account->deny; list != NULL; ) {
char *person = list->data;
list = list->next;
- purple_privacy_deny_remove(account, person, local);
+ if (strcmp(norm, person) != 0)
+ purple_privacy_deny_remove(account, person, local);
}
}
purple_privacy_deny_add(account, who, local);
@@ -318,6 +328,10 @@ purple_privacy_deny(PurpleAccount *account, const char *who, gboolean local,
default:
g_return_if_reached();
}
+
+ /* Notify the server if the privacy setting was changed */
+ if (type != account->perm_deny && purple_account_is_connected(account))
+ serv_set_permit_deny(purple_account_get_connection(account));
}
gboolean
diff --git a/libpurple/protocols/gg/gg.c b/libpurple/protocols/gg/gg.c
index 609705cd29..a1601a5811 100644
--- a/libpurple/protocols/gg/gg.c
+++ b/libpurple/protocols/gg/gg.c
@@ -343,7 +343,8 @@ static void ggp_action_buddylist_load(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *)action->context;
- purple_request_file(action, "Load buddylist from file...", NULL, FALSE,
+ purple_request_file(action, _("Load buddylist from file..."), NULL,
+ FALSE,
G_CALLBACK(ggp_callback_buddylist_load_ok), NULL,
purple_connection_get_account(gc), NULL, NULL,
gc);
@@ -926,8 +927,10 @@ static void ggp_bmenu_block(PurpleBlistNode *node, gpointer ignored)
/* ----- INTERNAL CALLBACKS --------------------------------------------- */
/* ---------------------------------------------------------------------- */
-/* just a prototype */
+/* Prototypes */
static void ggp_set_status(PurpleAccount *account, PurpleStatus *status);
+static int ggp_to_gg_status(PurpleStatus *status, char **msg);
+
/**
* Handle change of the status of the buddy.
@@ -1488,23 +1491,12 @@ static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition
break;
case GG_EVENT_CONN_SUCCESS:
{
- PurpleAccount *account;
- PurplePresence *presence;
- PurpleStatus *status;
-
purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n");
purple_input_remove(gc->inpa);
gc->inpa = purple_input_add(info->session->fd,
PURPLE_INPUT_READ,
ggp_callback_recv, gc);
- /* gg_change_status(info->session, GG_STATUS_AVAIL); */
-
- account = purple_connection_get_account(gc);
- presence = purple_account_get_presence(account);
- status = purple_presence_get_active_status(presence);
-
- ggp_set_status(account, status);
purple_connection_set_state(gc, PURPLE_CONNECTED);
ggp_buddylist_send(gc);
}
@@ -1692,6 +1684,8 @@ static GList *ggp_chat_info(PurpleConnection *gc)
static void ggp_login(PurpleAccount *account)
{
PurpleConnection *gc;
+ PurplePresence *presence;
+ PurpleStatus *status;
struct gg_login_params *glp;
GGPInfo *info;
@@ -1714,8 +1708,11 @@ static void ggp_login(PurpleAccount *account)
glp->uin = ggp_get_uin(account);
glp->password = (char *)purple_account_get_password(account);
+ presence = purple_account_get_presence(account);
+ status = purple_presence_get_active_status(presence);
+
glp->async = 1;
- glp->status = GG_STATUS_AVAIL;
+ glp->status = ggp_to_gg_status(status, &glp->status_descr);
glp->tls = 0;
info->session = gg_login(glp);
@@ -1826,22 +1823,15 @@ static void ggp_get_info(PurpleConnection *gc, const char *name)
/* }}} */
/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
-static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
+static int ggp_to_gg_status(PurpleStatus *status, char **msg)
{
- PurpleConnection *gc;
- GGPInfo *info;
- const char *status_id, *msg;
+ const char *status_id = purple_status_get_id(status);
int new_status, new_status_descr;
+ const char *new_msg;
- if (!purple_status_is_active(status))
- return;
-
- gc = purple_account_get_connection(account);
- info = gc->proto_data;
-
- status_id = purple_status_get_id(status);
+ g_return_val_if_fail(msg == NULL, 0);
- purple_debug_info("gg", "ggp_set_status: Requested status = %s\n",
+ purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n",
status_id);
if (strcmp(status_id, "available") == 0) {
@@ -1860,22 +1850,45 @@ static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
new_status = GG_STATUS_AVAIL;
new_status_descr = GG_STATUS_AVAIL_DESCR;
purple_debug_info("gg",
- "ggp_set_status: uknown status requested (status_id=%s)\n",
+ "ggp_set_status: unknown status requested (status_id=%s)\n",
status_id);
}
- msg = purple_status_get_attr_string(status, "message");
+ new_msg = purple_status_get_attr_string(status, "message");
- if (msg == NULL) {
- gg_change_status(info->session, new_status);
+ if(new_msg) {
+ char *tmp = purple_markup_strip_html(new_msg);
+ *msg = charset_convert(tmp, "UTF-8", "CP1250");
+ g_free(tmp);
+
+ return new_status_descr;
} else {
- gchar *tmp, *new_msg;
+ *msg = NULL;
+ return new_status;
+ }
+}
+/* }}} */
- tmp = charset_convert(msg, "UTF-8", "CP1250");
- new_msg = purple_markup_strip_html(tmp);
- g_free(tmp);
+/* static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) {{{ */
+static void ggp_set_status(PurpleAccount *account, PurpleStatus *status)
+{
+ PurpleConnection *gc;
+ GGPInfo *info;
+ int new_status;
+ char *new_msg = NULL;
- gg_change_status_descr(info->session, new_status_descr, new_msg);
+ if (!purple_status_is_active(status))
+ return;
+
+ gc = purple_account_get_connection(account);
+ info = gc->proto_data;
+
+ new_status = ggp_to_gg_status(status, &new_msg);
+
+ if (new_msg == NULL) {
+ gg_change_status(info->session, new_status);
+ } else {
+ gg_change_status_descr(info->session, new_status, new_msg);
g_free(new_msg);
}
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/msgs.c b/libpurple/protocols/irc/msgs.c
index db88d9b4f5..502c3ee938 100644
--- a/libpurple/protocols/irc/msgs.c
+++ b/libpurple/protocols/irc/msgs.c
@@ -122,7 +122,11 @@ static void irc_connected(struct irc_conn *irc, const char *nick)
void irc_msg_default(struct irc_conn *irc, const char *name, const char *from, char **args)
{
- purple_debug(PURPLE_DEBUG_INFO, "irc", "Unrecognized message: %s\n", args[0]);
+ char *clean;
+ /* This, too, should be escaped somehow (smarter) */
+ clean = purple_utf8_salvage(args[0]);
+ purple_debug(PURPLE_DEBUG_INFO, "irc", "Unrecognized message: %s\n", clean);
+ g_free(clean);
}
void irc_msg_features(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -211,7 +215,9 @@ 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(_("Ban on %s by %s, set %ld seconds ago"),
+ 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);
} else {
msg = g_strdup_printf(_("Ban on %s"), args[2]);
diff --git a/libpurple/protocols/irc/parse.c b/libpurple/protocols/irc/parse.c
index 198f4de827..d2481d7435 100644
--- a/libpurple/protocols/irc/parse.c
+++ b/libpurple/protocols/irc/parse.c
@@ -232,7 +232,7 @@ static char *irc_send_convert(struct irc_conn *irc, const char *string)
if (encodings[0] == NULL || !g_ascii_strcasecmp("UTF-8", encodings[0])) {
g_strfreev(encodings);
- return g_strdup(string);
+ return NULL;
}
utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err);
@@ -597,7 +597,7 @@ char *irc_format(struct irc_conn *irc, const char *format, ...)
case 'n':
case 'c':
tmp = irc_send_convert(irc, tok);
- g_string_append(string, tmp);
+ g_string_append(string, tmp ? tmp : tok);
g_free(tmp);
break;
default:
@@ -710,5 +710,10 @@ void irc_parse_msg(struct irc_conn *irc, char *input)
static void irc_parse_error_cb(struct irc_conn *irc, char *input)
{
- purple_debug(PURPLE_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input);
+ char *clean;
+ /* This really should be escaped somehow that you can tell what
+ * the junk was -- but as it is, it can crash glib. */
+ clean = purple_utf8_salvage(input);
+ purple_debug(PURPLE_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", clean);
+ g_free(clean);
}
diff --git a/libpurple/protocols/jabber/auth.c b/libpurple/protocols/jabber/auth.c
index 31aa729933..664f11d927 100644
--- a/libpurple/protocols/jabber/auth.c
+++ b/libpurple/protocols/jabber/auth.c
@@ -589,75 +589,6 @@ static void auth_old_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
}
}
-/*!
- * @brief Given the server challenge (message) and the key (password), calculate the HMAC-MD5 digest
- *
- * This is the crammd5 response. Inspired by cyrus-sasl's _sasl_hmac_md5()
- */
-static void
-auth_hmac_md5(const char *challenge, size_t challenge_len, const char *key, size_t key_len, guchar *digest)
-{
- PurpleCipher *cipher;
- PurpleCipherContext *context;
- int i;
- /* inner padding - key XORd with ipad */
- unsigned char k_ipad[65];
- /* outer padding - key XORd with opad */
- unsigned char k_opad[65];
-
- cipher = purple_ciphers_find_cipher("md5");
-
- /* if key is longer than 64 bytes reset it to key=MD5(key) */
- if (strlen(key) > 64) {
- guchar keydigest[16];
-
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, (const guchar *)key, strlen(key));
- purple_cipher_context_digest(context, 16, keydigest, NULL);
- purple_cipher_context_destroy(context);
-
- key = (char *)keydigest;
- key_len = 16;
- }
-
- /*
- * the HMAC_MD5 transform looks like:
- *
- * MD5(K XOR opad, MD5(K XOR ipad, text))
- *
- * where K is an n byte key
- * ipad is the byte 0x36 repeated 64 times
- * opad is the byte 0x5c repeated 64 times
- * and text is the data being protected
- */
-
- /* start out by storing key in pads */
- memset(k_ipad, '\0', sizeof k_ipad);
- memset(k_opad, '\0', sizeof k_opad);
- memcpy(k_ipad, (void *)key, key_len);
- memcpy(k_opad, (void *)key, key_len);
-
- /* XOR key with ipad and opad values */
- for (i=0; i<64; i++) {
- k_ipad[i] ^= 0x36;
- k_opad[i] ^= 0x5c;
- }
-
- /* perform inner MD5 */
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, k_ipad, 64); /* start with inner pad */
- purple_cipher_context_append(context, (const guchar *)challenge, challenge_len); /* then text of datagram */
- purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 1st pass */
- purple_cipher_context_destroy(context);
-
- /* perform outer MD5 */
- context = purple_cipher_context_new(cipher, NULL);
- purple_cipher_context_append(context, k_opad, 64); /* start with outer pad */
- purple_cipher_context_append(context, digest, 16); /* then results of 1st hash */
- purple_cipher_context_digest(context, 16, digest, NULL); /* finish up 2nd pass */
- purple_cipher_context_destroy(context);
-}
-
static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
{
JabberIq *iq;
@@ -703,14 +634,19 @@ static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
jabber_iq_send(iq);
- } else if(js->stream_id && xmlnode_get_child(query, "crammd5")) {
+ } else if(js->stream_id && (x = xmlnode_get_child(query, "crammd5"))) {
const char *challenge;
- guchar digest[16];
- char h[17], *p;
- int i;
-
- challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
- auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), digest);
+ gchar digest[33];
+ PurpleCipherContext *hmac;
+
+ /* Calculate the MHAC-MD5 digest */
+ challenge = xmlnode_get_attrib(x, "challenge");
+ hmac = purple_cipher_context_new_by_name("hmac", NULL);
+ purple_cipher_context_set_option(hmac, "hash", "md5");
+ purple_cipher_context_set_key(hmac, (guchar *)pw);
+ purple_cipher_context_append(hmac, (guchar *)challenge, strlen(challenge));
+ purple_cipher_context_digest_to_str(hmac, 33, digest, NULL);
+ purple_cipher_context_destroy(hmac);
/* Create the response query */
iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
@@ -723,11 +659,7 @@ static void auth_old_cb(JabberStream *js, xmlnode *packet, gpointer data)
x = xmlnode_new_child(query, "crammd5");
- /* Translate the digest to a hexadecimal notation */
- p = h;
- for(i=0; i<16; i++, p+=2)
- snprintf(p, 3, "%02x", digest[i]);
- xmlnode_insert_data(x, h, -1);
+ xmlnode_insert_data(x, digest, 32);
jabber_iq_set_callback(iq, auth_old_result_cb, NULL);
jabber_iq_send(iq);
diff --git a/libpurple/protocols/jabber/buddy.c b/libpurple/protocols/jabber/buddy.c
index 9bdd5b16fb..928e4b95be 100644
--- a/libpurple/protocols/jabber/buddy.c
+++ b/libpurple/protocols/jabber/buddy.c
@@ -824,10 +824,14 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
}
if(jbr) {
char *purdy = NULL;
+ const char *status_name = jabber_buddy_state_get_name(jbr->state);
if(jbr->status)
purdy = purple_strdup_withhtml(jbr->status);
- tmp = g_strdup_printf("%s%s%s", jabber_buddy_state_get_name(jbr->state),
- (purdy ? ": " : ""),
+ if(status_name && purdy && !strcmp(status_name, purdy))
+ status_name = NULL;
+
+ tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""),
+ ((status_name && purdy) ? ": " : ""),
(purdy ? purdy : ""));
purple_notify_user_info_prepend_pair(user_info, _("Status"), tmp);
g_free(tmp);
@@ -964,6 +968,8 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
for(resources = jbi->jb->resources; resources; resources = resources->next) {
char *purdy = NULL;
+ const char *status_name = NULL;
+
jbr = resources->data;
if(jbr->client.name) {
@@ -987,10 +993,14 @@ static void jabber_buddy_info_show_if_ready(JabberBuddyInfo *jbi)
}
}
+ status_name = jabber_buddy_state_get_name(jbr->state);
if(jbr->status)
purdy = purple_strdup_withhtml(jbr->status);
- tmp = g_strdup_printf("%s%s%s", jabber_buddy_state_get_name(jbr->state),
- (purdy ? ": " : ""),
+ if(status_name && purdy && !strcmp(status_name, purdy))
+ status_name = NULL;
+
+ tmp = g_strdup_printf("%s%s%s", (status_name ? status_name : ""),
+ ((status_name && purdy) ? ": " : ""),
(purdy ? purdy : ""));
purple_notify_user_info_prepend_pair(user_info, _("Status"), tmp);
g_free(tmp);
@@ -1783,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 c41c4caf1e..97988ceaf2 100644
--- a/libpurple/protocols/jabber/chat.c
+++ b/libpurple/protocols/jabber/chat.c
@@ -308,7 +308,7 @@ void jabber_chat_leave(PurpleConnection *gc, int id)
jabber_chat_part(chat, NULL);
- chat->conv = NULL;
+ chat->left = TRUE;
}
void jabber_chat_destroy(JabberChat *chat)
@@ -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/chat.h b/libpurple/protocols/jabber/chat.h
index 6c680d0940..64f924786a 100644
--- a/libpurple/protocols/jabber/chat.h
+++ b/libpurple/protocols/jabber/chat.h
@@ -49,6 +49,7 @@ typedef struct _JabberChat {
PurpleRequestType config_dialog_type;
void *config_dialog_handle;
GHashTable *members;
+ gboolean left;
} JabberChat;
GList *jabber_chat_info(PurpleConnection *gc);
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
index 2489a1abc3..27691f81d9 100644
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -273,9 +273,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"))
@@ -284,85 +317,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)
@@ -388,9 +375,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;
}
@@ -414,7 +401,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);
}
}
@@ -443,12 +430,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
@@ -483,9 +475,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"));
@@ -627,7 +623,7 @@ jabber_login(PurpleAccount *account)
js->write_buffer = purple_circ_buffer_new(512);
js->old_length = 0;
js->keepalive_timeout = -1;
- js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain);
+ js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user ? js->user->domain : NULL);
if(!js->user) {
purple_connection_error_reason (gc,
@@ -1504,8 +1500,7 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
if (full) {
PurpleStatus *status;
- PurpleValue *value;
-
+
if(jb->subscription & JABBER_SUB_FROM) {
if(jb->subscription & JABBER_SUB_TO)
sub = _("Both");
@@ -1521,17 +1516,17 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
else
sub = _("None");
}
-
+
purple_notify_user_info_add_pair(user_info, _("Subscription"), sub);
-
+
status = purple_presence_get_active_status(presence);
- value = purple_status_get_attr_value(status, "mood");
- if (value && purple_value_get_type(value) == PURPLE_TYPE_STRING && (mood = purple_value_get_string(value))) {
-
- value = purple_status_get_attr_value(status, "moodtext");
- if(value && purple_value_get_type(value) == PURPLE_TYPE_STRING) {
- char *moodplustext = g_strdup_printf("%s (%s)",mood,purple_value_get_string(value));
-
+ mood = purple_status_get_attr_string(status, "mood");
+ if(mood != NULL) {
+ const char *moodtext;
+ moodtext = purple_status_get_attr_string(status, "moodtext");
+ if(moodtext != NULL) {
+ char *moodplustext = g_strdup_printf("%s (%s)", mood, moodtext);
+
purple_notify_user_info_add_pair(user_info, _("Mood"), moodplustext);
g_free(moodplustext);
} else
diff --git a/libpurple/protocols/jabber/libxmpp.c b/libpurple/protocols/jabber/libxmpp.c
index 0d719a6ca0..08501e74de 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 */
diff --git a/libpurple/protocols/jabber/pep.h b/libpurple/protocols/jabber/pep.h
index dd43f80c6a..f1e1aca059 100644
--- a/libpurple/protocols/jabber/pep.h
+++ b/libpurple/protocols/jabber/pep.h
@@ -57,7 +57,7 @@ void jabber_pep_register_handler(const char *shortname, const char *xmlns, Jabbe
* @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 e902354eeb..53bc34969c 100644
--- a/libpurple/protocols/jabber/presence.c
+++ b/libpurple/protocols/jabber/presence.c
@@ -49,7 +49,7 @@ static void chats_send_presence_foreach(gpointer key, gpointer val,
xmlnode *presence = user_data;
char *chat_full_jid;
- if(!chat->conv)
+ if(!chat->conv || chat->left)
return;
chat_full_jid = g_strdup_printf("%s@%s/%s", chat->room, chat->server,
@@ -581,13 +581,13 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
if(state == JABBER_BUDDY_STATE_ERROR) {
char *title, *msg = jabber_parse_error(js, packet, NULL);
- if(chat->conv) {
+ if (!chat->conv) {
+ title = g_strdup_printf(_("Error joining chat %s"), from);
+ purple_serv_got_join_chat_failed(js->gc, chat->components);
+ } else {
title = g_strdup_printf(_("Error in chat %s"), from);
if (g_hash_table_size(chat->members) == 0)
serv_got_chat_left(js->gc, chat->id);
- } else {
- title = g_strdup_printf(_("Error joining chat %s"), from);
- purple_serv_got_join_chat_failed(js->gc, chat->components);
}
purple_notify_error(js->gc, title, title, msg);
g_free(title);
@@ -609,8 +609,9 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
/* If we haven't joined the chat yet, we don't care that someone
* left, or it was us leaving after we closed the chat */
- if(!chat->conv) {
- if(jid->resource && chat->handle && !strcmp(jid->resource, chat->handle))
+ if (!chat->conv || chat->left) {
+ if (chat->left &&
+ jid->resource && chat->handle && !strcmp(jid->resource, chat->handle))
jabber_chat_destroy(chat);
jabber_id_free(jid);
g_free(status);
diff --git a/libpurple/protocols/jabber/si.c b/libpurple/protocols/jabber/si.c
index 0ebd7f670a..2651ae68ca 100644
--- a/libpurple/protocols/jabber/si.c
+++ b/libpurple/protocols/jabber/si.c
@@ -63,6 +63,7 @@ typedef struct _JabberSIXfer {
char *rxqueue;
size_t rxlen;
gsize rxmaxlen;
+ int local_streamhost_fd;
} JabberSIXfer;
static PurpleXfer*
@@ -347,7 +348,11 @@ jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source,
g_free(jsx->rxqueue);
jsx->rxqueue = NULL;
- purple_xfer_start(xfer, source, NULL, -1);
+ /* Before actually starting sending the file, we need to wait until the
+ * recipient sends the IQ result with <streamhost-used/>
+ */
+ purple_debug_info("jabber", "SOCKS5 connection negotiation completed. "
+ "Waiting for IQ result to start file transfer.\n");
}
static void
@@ -608,6 +613,7 @@ jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
PurpleInputCondition cond)
{
PurpleXfer *xfer = data;
+ JabberSIXfer *jsx = xfer->data;
int acceptfd;
purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
@@ -617,12 +623,13 @@ 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;
}
purple_input_remove(xfer->watcher);
close(source);
+ jsx->local_streamhost_fd = -1;
xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
jabber_si_xfer_bytestreams_send_read_cb, xfer);
@@ -633,19 +640,30 @@ jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet,
gpointer data)
{
PurpleXfer *xfer = data;
- JabberSIXfer *jsx = xfer->data;
+ JabberSIXfer *jsx;
xmlnode *query, *streamhost_used;
const char *from, *type, *jid;
GList *matched;
/* TODO: This need to send errors if we don't see what we're looking for */
+ /* Make sure that the xfer is actually still valid and we're not just receiving an old iq response */
+ if (!g_list_find(js->file_transfers, xfer)) {
+ purple_debug_error("jabber", "Got bytestreams response for no longer existing xfer (%p)\n", xfer);
+ return;
+ }
+
/* In the case of a direct file transfer, this is expected to return */
- if(!jsx)
+ if(!xfer->data)
return;
- if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+ jsx = xfer->data;
+
+ 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;
@@ -659,22 +677,33 @@ jabber_si_connect_proxy_cb(JabberStream *js, xmlnode *packet,
if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
return;
- purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %p",
+ purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %s\n",
jsx, jsx->streamhosts, jid);
if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid)))
{
gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
jsx->js->user->domain, jsx->js->user->resource);
- if (!strcmp(jid, my_jid))
+ if (!strcmp(jid, my_jid)) {
purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
- else
+ purple_xfer_start(xfer, xfer->fd, NULL, -1);
+ } else {
purple_debug_info("jabber", "streamhost-used does not match any proxy that was offered to target\n");
+ purple_xfer_cancel_local(xfer);
+ }
g_free(my_jid);
return;
}
- /* TODO: Clean up the local SOCKS5 proxy - it isn't going to be used.*/
+ /* Clean up the local streamhost - it isn't going to be used.*/
+ if (xfer->watcher > 0) {
+ purple_input_remove(xfer->watcher);
+ xfer->watcher = 0;
+ }
+ if (jsx->local_streamhost_fd >= 0) {
+ close(jsx->local_streamhost_fd);
+ jsx->local_streamhost_fd = -1;
+ }
jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
@@ -692,14 +721,17 @@ 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;
+ const char *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;
@@ -707,11 +739,6 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
purple_xfer_unref(xfer);
- if (sock < 0) {
- purple_xfer_cancel_local(xfer);
- return;
- }
-
iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET,
"http://jabber.org/protocol/bytestreams");
xmlnode_set_attrib(iq->node, "to", xfer->who);
@@ -719,40 +746,44 @@ 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);
+ /* If we successfully started listening locally */
+ if (sock >= 0) {
+ gchar *jid;
+ const char *local_ip, *public_ip;
- /* TODO: Should there be an option to not use the local host as a ft proxy?
- * (to prevent revealing IP address, etc.) */
+ jsx->local_streamhost_fd = sock;
- /* 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);
- }
+ 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 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);
- }
+ /* 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", jid);
+ xmlnode_set_attrib(streamhost, "host", local_ip);
+ xmlnode_set_attrib(streamhost, "port", port);
+ }
- g_free(jid);
+ /* Include the public IP (assuming that there is a port mapped somehow) */
+ public_ip = purple_network_get_my_ip(jsx->js->fd);
+ if (strcmp(public_ip, local_ip) != 0 && strcmp(public_ip, "0.0.0.0") != 0) {
+ streamhost_count++;
+ streamhost = xmlnode_new_child(query, "streamhost");
+ xmlnode_set_attrib(streamhost, "jid", jid);
+ xmlnode_set_attrib(streamhost, "host", public_ip);
+ xmlnode_set_attrib(streamhost, "port", port);
+ }
+
+ g_free(jid);
- /* The listener for the local proxy */
- xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
- jabber_si_xfer_bytestreams_send_connected_cb, xfer);
+ /* 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);
@@ -778,11 +809,12 @@ jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
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",
+ purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and ft_proxy_list[%i] %p\n",
jsx, jsx->streamhosts, i, ft_proxy_list[i]);
if(g_list_find_custom(jsx->streamhosts, ft_proxy_list[i], jabber_si_compare_jid) != NULL)
continue;
+ streamhost_count++;
streamhost = xmlnode_new_child(query, "streamhost");
xmlnode_set_attrib(streamhost, "jid", ft_proxy_list[i]);
xmlnode_set_attrib(streamhost, "host", ft_proxy_list[i]);
@@ -812,6 +844,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);
@@ -827,6 +860,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);
@@ -841,13 +882,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);
}
}
@@ -956,7 +998,8 @@ static void jabber_si_xfer_free(PurpleXfer *xfer)
purple_network_listen_cancel(jsx->listen_data);
if (jsx->iq_id != NULL)
jabber_iq_remove_callback_by_id(js, jsx->iq_id);
-
+ if (jsx->local_streamhost_fd >= 0)
+ close(jsx->local_streamhost_fd);
if (jsx->connect_timeout > 0)
purple_timeout_remove(jsx->connect_timeout);
@@ -1164,6 +1207,7 @@ PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who)
{
xfer->data = jsx = g_new0(JabberSIXfer, 1);
jsx->js = js;
+ jsx->local_streamhost_fd = -1;
purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
@@ -1183,9 +1227,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)
@@ -1237,6 +1278,7 @@ void jabber_si_parse(JabberStream *js, xmlnode *packet)
return;
jsx = g_new0(JabberSIXfer, 1);
+ jsx->local_streamhost_fd = -1;
for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
const char *var = xmlnode_get_attrib(field, "var");
diff --git a/libpurple/protocols/jabber/xdata.c b/libpurple/protocols/jabber/xdata.c
index ec8fee7453..9eff9a84f9 100644
--- a/libpurple/protocols/jabber/xdata.c
+++ b/libpurple/protocols/jabber/xdata.c
@@ -201,7 +201,7 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
xmlnode *fn, *x;
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
- PurpleRequestField *field;
+ PurpleRequestField *field = NULL;
char *title = NULL;
char *instructions = NULL;
@@ -232,10 +232,6 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
if(!label)
label = var;
- if((valuenode = xmlnode_get_child(fn, "value")))
- value = xmlnode_get_data(valuenode);
-
-
if(!strcmp(type, "text-private")) {
if((valuenode = xmlnode_get_child(fn, "value")))
value = xmlnode_get_data(valuenode);
@@ -333,14 +329,16 @@ void *jabber_x_data_request_with_actions(JabberStream *js, xmlnode *packet, GLis
g_hash_table_replace(data->fields, g_strdup(var), GINT_TO_POINTER(JABBER_X_DATA_BOOLEAN));
g_free(value);
- } else if(!strcmp(type, "fixed") && value) {
+ } else if(!strcmp(type, "fixed")) {
if((valuenode = xmlnode_get_child(fn, "value")))
value = xmlnode_get_data(valuenode);
- field = purple_request_field_label_new("", value);
- purple_request_field_group_add_field(group, field);
+ if(value != NULL) {
+ field = purple_request_field_label_new("", value);
+ purple_request_field_group_add_field(group, field);
- g_free(value);
+ g_free(value);
+ }
} else if(!strcmp(type, "hidden")) {
if((valuenode = xmlnode_get_child(fn, "value")))
value = xmlnode_get_data(valuenode);
diff --git a/libpurple/protocols/msn/contact.c b/libpurple/protocols/msn/contact.c
index 61e1f2067d..9470171cab 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>
*
@@ -80,7 +80,7 @@ msn_callback_state_new(MsnSession *session)
state->session = session;
return state;
-}
+}
void
msn_callback_state_free(MsnCallbackState *state)
@@ -217,7 +217,7 @@ msn_create_address_book(MsnContact * contact)
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");
body = g_strdup_printf(MSN_ADD_ADDRESSBOOK_TEMPLATE, contact->session->user->passport);
@@ -247,7 +247,7 @@ msn_parse_each_member(MsnSession *session, xmlnode *member, const char *node,
user->membership_id[list] = atoi(member_id);
}
- msn_got_lst_user(session, user, 1 << list, NULL);
+ msn_got_lst_user(session, user, 1 << list, NULL);
g_free(passport);
g_free(type);
@@ -269,7 +269,7 @@ 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("msncl","last change: %s\n", lastchange_str);
purple_account_set_string(session->account, "CLLastChange",
lastchange_str);
@@ -480,7 +480,7 @@ msn_parse_addressbook_mobile(xmlnode *contactInfo, char **inout_mobile_number)
if (phone_type && !strcmp(phone_type, "ContactPhoneMobile")) {
xmlnode *number;
-
+
if ((number = xmlnode_get_child(contact_phone, "number"))) {
xmlnode *messenger_enabled;
char *is_messenger_enabled = NULL;
@@ -489,8 +489,8 @@ msn_parse_addressbook_mobile(xmlnode *contactInfo, char **inout_mobile_number)
mobile_number = xmlnode_get_data(number);
if (mobile_number &&
- (messenger_enabled = xmlnode_get_child(contact_phone, "isMessengerEnabled"))
- && (is_messenger_enabled = xmlnode_get_data(messenger_enabled))
+ (messenger_enabled = xmlnode_get_child(contact_phone, "isMessengerEnabled"))
+ && (is_messenger_enabled = xmlnode_get_data(messenger_enabled))
&& !strcmp(is_messenger_enabled, "true"))
mobile = TRUE;
@@ -679,7 +679,7 @@ msn_parse_addressbook(MsnContact * contact, xmlnode *node)
gchar *errorcode = xmlnode_get_data(faultnode);
purple_debug_info("MSNAB", "Error Code: %s\n", errorcode);
-
+
if (g_str_equal(errorcode, "ABDoesNotExist")) {
g_free(errorcode);
return TRUE;
@@ -823,7 +823,7 @@ msn_add_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
if (resp != NULL) {
MsnUserList *userlist = session->userlist;
MsnUser *user;
-
+
purple_debug_info("MSNCL","Contact added successfully\n");
// the code this block is replacing didn't send ADL for yahoo contacts,
@@ -921,7 +921,7 @@ msn_add_contact_to_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
}
void
-msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
+msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
const char *passport, const char *groupId)
{
MsnUserList *userlist;
@@ -934,11 +934,11 @@ msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
g_return_if_fail(contact != NULL);
g_return_if_fail(contact->session != NULL);
g_return_if_fail(contact->session->userlist != NULL);
-
+
userlist = contact->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) {
@@ -956,13 +956,13 @@ msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
return;
}
- purple_debug_info("MSNCL", "Adding user %s to group %s\n", passport,
+ purple_debug_info("MSNCL", "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);
- msn_callback_state_free(state);
+ msn_callback_state_free(state);
return; /* guess this never happened! */
}
@@ -1007,7 +1007,7 @@ msn_delete_contact_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
/*delete a Contact*/
void
msn_delete_contact(MsnContact *contact, const char *contactId)
-{
+{
gchar *body = NULL;
gchar *contact_id_xml = NULL ;
MsnCallbackState *state;
@@ -1045,7 +1045,7 @@ msn_del_contact_from_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp,
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);
}
}
-
+
msn_callback_state_free(state);
}
@@ -1057,15 +1057,15 @@ msn_del_contact_from_group(MsnContact *contact, const char *passport, const char
MsnCallbackState *state;
gchar *body, *contact_id_xml;
const gchar *groupId;
-
+
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);
-
+
userlist = contact->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);
@@ -1073,9 +1073,9 @@ msn_del_contact_from_group(MsnContact *contact, const char *passport, const char
purple_debug_warning("MSNCL", "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);
return;
@@ -1099,7 +1099,7 @@ msn_del_contact_from_group(MsnContact *contact, const char *passport, const char
xmlnode_from_str(body, -1)),
MSN_CONTACT_SERVER, MSN_ADDRESS_BOOK_POST_URL,
msn_del_contact_from_group_read_cb, state);
-
+
g_free(contact_id_xml);
g_free(body);
}
@@ -1198,7 +1198,7 @@ msn_del_contact_from_list(MsnContact *contact, MsnCallbackState *state,
} else {
/* list == MSN_LIST_AL || list == MSN_LIST_BL */
partner_scenario = MSN_PS_BLOCK_UNBLOCK;
-
+
member = g_strdup_printf(MSN_MEMBER_PASSPORT_XML, passport);
}
@@ -1226,13 +1226,13 @@ 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);
-
+
if (user != NULL) {
msn_user_set_op(user, MSN_LIST_RL_OP);
}
@@ -1274,9 +1274,9 @@ 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,
+ body = g_strdup_printf(MSN_CONTACT_ADD_TO_LIST_TEMPLATE,
MsnSoapPartnerScenarioText[partner_scenario],
- MsnMemberRole[list],
+ MsnMemberRole[list],
member);
msn_soap_message_send(contact->session,
@@ -1323,9 +1323,9 @@ static void
msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
{
MsnCallbackState *state = data;
-
+
purple_debug_info("MSNCL", "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);
@@ -1338,13 +1338,13 @@ msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
if (state) {
MsnSession *session = state->session;
MsnUserList *userlist = session->userlist;
-
+
if (state->action & MSN_RENAME_GROUP) {
msn_userlist_rename_group_id(session->userlist,
state->guid,
state->new_group_name);
}
-
+
if (state->action & MSN_ADD_GROUP) {
/* the response is taken from
http://telepathy.freedesktop.org/wiki/Pymsn/MSNP/ContactListActions
@@ -1364,7 +1364,7 @@ msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
state->who,
state->new_group_name);
} else if (state->action & MSN_MOVE_BUDDY) {
- msn_add_contact_to_group(session->contact, state, state->who, guid);
+ msn_add_contact_to_group(session->contact, state, state->who, guid);
g_free(guid);
return;
}
@@ -1374,16 +1374,16 @@ msn_group_read_cb(MsnSoapMessage *req, MsnSoapMessage *resp, gpointer data)
state->new_group_name);
}
}
-
+
if (state->action & MSN_DEL_GROUP) {
GList *l;
-
+
msn_userlist_remove_group_id(session->userlist, state->guid);
for (l = userlist->users; l != NULL; l = l->next) {
msn_user_remove_group_id( (MsnUser *)l->data, state->guid);
}
}
-
+
msn_callback_state_free(state);
}
}
@@ -1396,7 +1396,7 @@ msn_add_group(MsnSession *session, MsnCallbackState *state, const char* group_na
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);
if (state == NULL) {
@@ -1429,13 +1429,13 @@ msn_del_group(MsnSession *session, const gchar *group_name)
const gchar *guid;
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);
-
+
guid = msn_userlist_find_group_id(session->userlist, group_name);
-
- /* if group uid we need to del is NULL,
+
+ /* if group uid we need to del is NULL,
* we need to delete nothing
*/
if (guid == NULL) {
@@ -1451,7 +1451,7 @@ msn_del_group(MsnSession *session, const gchar *group_name)
state = msn_callback_state_new(session);
msn_callback_state_set_action(state, MSN_DEL_GROUP);
msn_callback_state_set_guid(state, guid);
-
+
body = g_strdup_printf(MSN_GROUP_DEL_TEMPLATE, guid);
msn_soap_message_send(session,
@@ -1470,14 +1470,14 @@ msn_contact_rename_group(MsnSession *session, const char *old_group_name, const
gchar *body = NULL;
const gchar * guid;
MsnCallbackState *state;
-
+
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);
-
+
guid = msn_userlist_find_group_id(session->userlist, old_group_name);
if (guid == NULL)
return;
@@ -1492,10 +1492,10 @@ msn_contact_rename_group(MsnSession *session, const char *old_group_name, const
}
msn_callback_state_set_action(state, MSN_RENAME_GROUP);
-
+
body = g_markup_printf_escaped(MSN_GROUP_RENAME_TEMPLATE,
guid, new_group_name);
-
+
msn_soap_message_send(session,
msn_soap_message_new(MSN_GROUP_RENAME_SOAP_ACTION,
xmlnode_from_str(body, -1)),
diff --git a/libpurple/protocols/msn/contact.h b/libpurple/protocols/msn/contact.h
index d1ba9a451a..ed1dca2c3e 100644
--- a/libpurple/protocols/msn/contact.h
+++ b/libpurple/protocols/msn/contact.h
@@ -349,7 +349,7 @@
#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>"
-typedef enum
+typedef enum
{
MSN_ADD_BUDDY = 0x01,
MSN_MOVE_BUDDY = 0x02,
@@ -383,7 +383,7 @@ struct _MsnCallbackState
MsnSession *session;
};
-typedef enum
+typedef enum
{
MSN_PS_INITIAL,
MSN_PS_SAVE_CONTACT,
@@ -404,34 +404,34 @@ void msn_callback_state_set_who(MsnCallbackState *state, const gchar *who);
void msn_callback_state_set_uid(MsnCallbackState *state, const gchar *uid);
void msn_callback_state_set_old_group_name(MsnCallbackState *state,
const gchar *old_group_name);
-void msn_callback_state_set_new_group_name(MsnCallbackState *state,
+void msn_callback_state_set_new_group_name(MsnCallbackState *state,
const gchar *new_group_name);
void msn_callback_state_set_guid(MsnCallbackState *state, const gchar *guid);
void msn_callback_state_set_list_id(MsnCallbackState *state, MsnListId list_id);
-void msn_callback_state_set_action(MsnCallbackState *state,
+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_get_contact_list(MsnContact * contact,
const MsnSoapPartnerScenario partner_scenario,
const char *update);
-void msn_get_address_book(MsnContact *contact,
+void msn_get_address_book(MsnContact *contact,
const MsnSoapPartnerScenario partner_scenario,
const char * update, const char * gupdate);
/* contact SOAP operations */
void msn_update_contact(MsnContact *contact, const char* nickname);
-void msn_add_contact(MsnContact *contact, MsnCallbackState *state,
+void msn_add_contact(MsnContact *contact, MsnCallbackState *state,
const char *passport);
void msn_delete_contact(MsnContact *contact, const char *contactId);
-void msn_add_contact_to_group(MsnContact *contact, MsnCallbackState *state,
+void msn_add_contact_to_group(MsnContact *contact, 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(MsnContact *contact, const char *passport,
const char *group_name);
/* group operations */
-void msn_add_group(MsnSession *session, MsnCallbackState *state,
+void msn_add_group(MsnSession *session, MsnCallbackState *state,
const char* group_name);
void msn_del_group(MsnSession *session, const gchar *group_name);
void msn_contact_rename_group(MsnSession *session, const char *old_group_name,
diff --git a/libpurple/protocols/msn/dialog.c b/libpurple/protocols/msn/dialog.c
index 013491315a..48669d0597 100644
--- a/libpurple/protocols/msn/dialog.c
+++ b/libpurple/protocols/msn/dialog.c
@@ -135,7 +135,7 @@ msn_show_sync_issue(MsnSession *session, const char *passport,
passport);
}
- purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE,
+ purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE,
purple_connection_get_account(gc), data->who, NULL,
data, 2,
_("Yes"), G_CALLBACK(msn_add_cb),
diff --git a/libpurple/protocols/msn/error.c b/libpurple/protocols/msn/error.c
index 0ffb593011..5cb401bf76 100644
--- a/libpurple/protocols/msn/error.c
+++ b/libpurple/protocols/msn/error.c
@@ -259,7 +259,7 @@ msn_error_handle(MsnSession *session, unsigned int type)
{
char buf[MSN_BUF_LEN];
gboolean debug;
-
+
g_snprintf(buf, sizeof(buf), _("MSN Error: %s\n"),
msn_error_get_text(type, &debug));
if (debug)
diff --git a/libpurple/protocols/msn/msn.c b/libpurple/protocols/msn/msn.c
index c05901af4e..ac9b5176b0 100644
--- a/libpurple/protocols/msn/msn.c
+++ b/libpurple/protocols/msn/msn.c
@@ -1594,10 +1594,10 @@ msn_rename_group(PurpleConnection *gc, const char *old_name,
MsnSession *session;
session = gc->proto_data;
-
+
g_return_if_fail(session != NULL);
g_return_if_fail(session->userlist != NULL);
-
+
if (msn_userlist_find_group_with_name(session->userlist, old_name) != NULL)
{
msn_contact_rename_group(session, old_name, group->name);
@@ -1677,7 +1677,7 @@ msn_remove_group(PurpleConnection *gc, PurpleGroup *group)
purple_debug_info("MSN", "This group can't be removed, returning.\n");
return ;
}
-
+
msn_del_group(session, group->name);
}
diff --git a/libpurple/protocols/msn/msnutils.c b/libpurple/protocols/msn/msnutils.c
index 38c3997357..76b53e93a4 100644
--- a/libpurple/protocols/msn/msnutils.c
+++ b/libpurple/protocols/msn/msnutils.c
@@ -169,7 +169,7 @@ msn_encode_mime(const char *str)
gchar *base64, *retval;
g_return_val_if_fail(str != NULL, NULL);
-
+
base64 = purple_base64_encode((guchar *)str, strlen(str));
retval = g_strdup_printf("=?utf-8?B?%s?=", base64);
g_free(base64);
@@ -509,7 +509,7 @@ msn_convert_iso8601(const char *timestr,struct tm tm_time)
*This algorithm reference with http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
*/
#define BUFSIZE 256
-void
+void
msn_handle_chl(char *input, char *output)
{
PurpleCipher *cipher;
@@ -538,7 +538,7 @@ msn_handle_chl(char *input, char *output)
/* Split it into four integers */
md5Parts = (unsigned int *)md5Hash;
- for(i=0; i<4; i++){
+ for(i=0; i<4; i++){
/* adjust endianess */
md5Parts[i] = GUINT_TO_LE(md5Parts[i]);
@@ -578,7 +578,7 @@ msn_handle_chl(char *input, char *output)
/* adjust endianness */
for(i=0; i<4; i++)
- newHashParts[i] = GUINT_TO_LE(newHashParts[i]);
+ newHashParts[i] = GUINT_TO_LE(newHashParts[i]);
/* make a string of the parts */
newHash = (unsigned char *)newHashParts;
diff --git a/libpurple/protocols/msn/notification.c b/libpurple/protocols/msn/notification.c
index e3d9e6df53..0fc7560390 100644
--- a/libpurple/protocols/msn/notification.c
+++ b/libpurple/protocols/msn/notification.c
@@ -34,15 +34,6 @@
static MsnTable *cbs_table;
-/****************************************************************************
- * Local Function Prototype
- ****************************************************************************/
-
-static void msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_len);
-static void
-msn_add_contact_xml(MsnSession *session, xmlnode *mlNode,const char *passport,
- MsnListOp list_op, MsnUserType type);
-
/**************************************************************************
* Main
**************************************************************************/
@@ -336,7 +327,7 @@ ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
}
/*
- * Windows Live Messenger 8.0
+ * Windows Live Messenger 8.0
* Notice :CVR String discriminate!
* reference of http://www.microsoft.com/globaldev/reference/oslocversion.mspx
* to see the Local ID
@@ -430,7 +421,7 @@ uum_send_msg(MsnSession *session,MsnMessage *msg)
char *payload;
gsize payload_len;
int type;
-
+
cmdproc = session->notification->cmdproc;
g_return_if_fail(msg != NULL);
payload = msn_message_gen_payload(msg, &payload_len);
@@ -649,7 +640,7 @@ msn_notification_post_adl(MsnCmdProc *cmdproc, const char *payload, int payload_
{
MsnTransaction *trans;
purple_debug_info("MSN Notification","Sending ADL with payload: %s\n", payload);
- trans = msn_transaction_new(cmdproc, "ADL","%" G_GSIZE_FORMAT, payload_len);
+ trans = msn_transaction_new(cmdproc, "ADL", "%i", payload_len);
msn_transaction_set_payload(trans, payload, payload_len);
msn_cmdproc_send_trans(cmdproc, trans);
}
@@ -709,8 +700,8 @@ msn_notification_dump_contact(MsnSession *session)
}
display_name = purple_connection_get_display_name(session->account->gc);
- if (display_name
- && strcmp(display_name,
+ if (display_name
+ && strcmp(display_name,
purple_account_get_username(session->account))) {
msn_act_id(session->account->gc, display_name);
}
@@ -755,15 +746,15 @@ adl_cmd_parse(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
purple_debug_misc("MSN Notification", "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");
return;
}
for (domain_node = xmlnode_get_child(root, "d"); domain_node; domain_node = xmlnode_get_next_twin(domain_node)) {
- const gchar * domain = NULL;
+ const gchar * domain = NULL;
xmlnode *contact_node = NULL;
domain = xmlnode_get_attrib(domain_node, "n");
@@ -1024,13 +1015,13 @@ fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
MsnSlpLink *slplink;
MsnUser *user;
+ /* Tell libpurple that the user has signed off */
user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]);
-
user->status = "offline";
msn_user_update(user);
+ /* If we have an open MsnSlpLink with the user then close it */
slplink = msn_session_find_slplink(cmdproc->session, cmd->params[0]);
-
if (slplink != NULL)
msn_slplink_destroy(slplink);
@@ -1300,7 +1291,7 @@ prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd)
type = cmd->params[1];
if (!strcmp(type, "MFN")) {
friendlyname = purple_url_decode(cmd->params[2]);
-
+
msn_update_contact(session->contact, friendlyname);
purple_connection_set_display_name(
@@ -1649,12 +1640,12 @@ gcf_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
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(buf);
xmlnode_free(root);
}
@@ -1698,7 +1689,7 @@ ubx_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload,
passport = cmd->params[0];
user = msn_userlist_find_user(session->userlist, passport);
-
+
psm_str = msn_get_psm(cmd->payload,len);
msn_user_set_statusline(user, psm_str);
g_free(psm_str);
@@ -2005,7 +1996,7 @@ system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
{
case 1:
minutes = atoi(g_hash_table_lookup(table, "Arg1"));
- g_snprintf(buf, sizeof(buf), dngettext(PACKAGE,
+ g_snprintf(buf, sizeof(buf), dngettext(PACKAGE,
"The MSN server will shut down for maintenance "
"in %d minute. You will automatically be "
"signed out at that time. Please finish any "
@@ -2032,7 +2023,7 @@ system_msg(MsnCmdProc *cmdproc, MsnMessage *msg)
void
msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list_id,
- const char *who)
+ const char *who)
{
MsnCmdProc *cmdproc;
MsnListOp list_op = 1 << list_id;
@@ -2045,12 +2036,12 @@ msn_notification_add_buddy_to_list(MsnNotification *notification, MsnListId list
adl_node = xmlnode_new("ml");
adl_node->child = NULL;
- msn_add_contact_xml(notification->session, adl_node, who, list_op,
+ msn_add_contact_xml(notification->session, adl_node, who, list_op,
MSN_USER_TYPE_PASSPORT);
payload = xmlnode_to_str(adl_node,&payload_len);
xmlnode_free(adl_node);
-
+
msn_notification_post_adl(notification->servconn->cmdproc,
payload,payload_len);
g_free(payload);
@@ -2155,11 +2146,11 @@ msn_notification_init(void)
/*initial OIM notification*/
msn_table_add_msg_type(cbs_table,
"text/x-msmsgsinitialmdatanotification",
- initial_mdata_msg);
+ initial_mdata_msg);
/*OIM notification when user online*/
msn_table_add_msg_type(cbs_table,
"text/x-msmsgsoimnotification",
- initial_mdata_msg);
+ initial_mdata_msg);
msn_table_add_msg_type(cbs_table,
"text/x-msmsgsinitialemailnotification",
initial_email_msg);
diff --git a/libpurple/protocols/msn/notification.h b/libpurple/protocols/msn/notification.h
index 8ec9589330..493120012c 100644
--- a/libpurple/protocols/msn/notification.h
+++ b/libpurple/protocols/msn/notification.h
@@ -30,7 +30,7 @@
#define MSNP13_WLM_PRODUCT_ID "PROD01065C%ZFN6F"
#define MSNP10_PRODUCT_KEY "VT6PX?UQTM4WM%YR"
-#define MSNP10_PRODUCT_ID "PROD0038W!61ZTF9"
+#define MSNP10_PRODUCT_ID "PROD0038W!61ZTF9"
typedef struct _MsnNotification MsnNotification;
@@ -41,6 +41,11 @@ typedef struct _MsnNotification MsnNotification;
struct _MsnNotification
{
MsnSession *session;
+
+ /**
+ * This is a convenience pointer that always points to
+ * servconn->cmdproc
+ */
MsnCmdProc *cmdproc;
MsnServConn *servconn;
@@ -71,7 +76,7 @@ void msn_notification_dump_contact(MsnSession *session);
* Closes a notification.
*
* It's first closed, and then disconnected.
- *
+ *
* @param notification The notification object to close.
*/
void msn_notification_close(MsnNotification *notification);
diff --git a/libpurple/protocols/msn/object.c b/libpurple/protocols/msn/object.c
index 3192540ca4..e74578c460 100644
--- a/libpurple/protocols/msn/object.c
+++ b/libpurple/protocols/msn/object.c
@@ -172,7 +172,7 @@ msn_object_new_from_image(PurpleStoredImage *img, const char *location,
base64 = purple_base64_encode(digest, sizeof(digest));
msn_object_set_sha1c(msnobj, base64);
g_free(base64);
-
+
return msnobj;
}
diff --git a/libpurple/protocols/msn/oim.c b/libpurple/protocols/msn/oim.c
index 1ec325603a..bd2441fd72 100644
--- a/libpurple/protocols/msn/oim.c
+++ b/libpurple/protocols/msn/oim.c
@@ -1,5 +1,5 @@
/**
- * @file oim.c
+ * @file oim.c
* get and send MSN offline Instant Message via SOAP request
* Author
* MaYuan<mayuan2006@gmail.com>
@@ -91,7 +91,7 @@ msn_oim_new_send_req(const char *from_member, const char*friendname,
const char* to_member, const char *msg)
{
MsnOimSendReq *request;
-
+
request = g_new0(MsnOimSendReq, 1);
request->from_member = g_strdup(from_member);
request->friendname = g_strdup(friendname);
@@ -109,7 +109,7 @@ msn_oim_free_send_req(MsnOimSendReq *req)
g_free(req->friendname);
g_free(req->to_member);
g_free(req->oim_msg);
-
+
g_free(req);
}
@@ -121,10 +121,10 @@ static gchar *
msn_oim_msg_to_str(MsnOim *oim, const char *body)
{
char *oim_body,*oim_base64;
-
- purple_debug_info("MSN OIM","encode OIM Message...\n");
+
+ 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);
+ 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);
@@ -213,7 +213,7 @@ msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
}
/*post send single message request to oim server*/
-void
+void
msn_oim_send_msg(MsnOim *oim)
{
MsnOimSendReq *oim_request;
@@ -333,7 +333,7 @@ msn_oim_parse_timestamp(const char *timestamp)
gboolean offset_positive = TRUE;
int tzhrs;
int tzmins;
-
+
for (t.tm_mon = 0;
months[t.tm_mon] != NULL &&
strcmp(months[t.tm_mon], month_str) != 0; t.tm_mon++);
@@ -462,7 +462,7 @@ msn_oim_get_read_cb(MsnSoapMessage *request, MsnSoapMessage *response,
}
}
-/* parse the oim XML data
+/* parse the oim XML data
* and post it to the soap server to get the Offline Message
* */
void
diff --git a/libpurple/protocols/msn/oim.h b/libpurple/protocols/msn/oim.h
index e15463baf9..1bec1ed30c 100644
--- a/libpurple/protocols/msn/oim.h
+++ b/libpurple/protocols/msn/oim.h
@@ -1,7 +1,7 @@
/**
* @file oim.h Header file for oim.c
* Author
- * MaYuan<mayuan2006@gmail.com>
+ * MaYuan<mayuan2006@gmail.com>
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
@@ -127,4 +127,3 @@ void msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
void msn_oim_send_msg(MsnOim *oim);
#endif/* _MSN_OIM_H_*/
-/*endof oim.h*/
diff --git a/libpurple/protocols/msn/servconn.c b/libpurple/protocols/msn/servconn.c
index 887ea4985c..c69c4353ae 100644
--- a/libpurple/protocols/msn/servconn.c
+++ b/libpurple/protocols/msn/servconn.c
@@ -396,15 +396,15 @@ read_cb(gpointer data, gint source, PurpleInputCondition cond)
if (len <= 0) {
switch (errno) {
- case 0:
+ case 0:
case EBADF:
case EAGAIN: return;
-
+
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_got_error(servconn,
MSN_SERVCONN_ERROR_READ);
return;
}
diff --git a/libpurple/protocols/msn/session.h b/libpurple/protocols/msn/session.h
index 4f005ceba5..4cc9f5627f 100644
--- a/libpurple/protocols/msn/session.h
+++ b/libpurple/protocols/msn/session.h
@@ -104,7 +104,6 @@ struct _MsnSession
int servconns_count; /**< The count of server connections. */
GList *switches; /**< The list of all the switchboards. */
- GList *directconns; /**< The list of all the directconnections. */
GList *slplinks; /**< The list of all the slplinks. */
/*psm info*/
diff --git a/libpurple/protocols/msn/slp.c b/libpurple/protocols/msn/slp.c
index e2ecf40a46..3c0074d7c2 100644
--- a/libpurple/protocols/msn/slp.c
+++ b/libpurple/protocols/msn/slp.c
@@ -947,15 +947,15 @@ static gboolean
msn_release_buddy_icon_request_timeout(gpointer data)
{
MsnUserList *userlist = (MsnUserList *)data;
-
+
/* Free one window slot */
- userlist->buddy_icon_window++;
-
+ userlist->buddy_icon_window++;
+
/* Clear the tag for our former request timer */
userlist->buddy_icon_request_timer = 0;
-
+
msn_release_buddy_icon_request(userlist);
-
+
return FALSE;
}
@@ -1062,7 +1062,7 @@ end_user_display(MsnSlpCall *slpcall, MsnSession *session)
}
/* Wait BUDDY_ICON_DELAY ms before freeing our window slot and requesting the next icon. */
- userlist->buddy_icon_request_timer = purple_timeout_add(BUDDY_ICON_DELAY,
+ userlist->buddy_icon_request_timer = purple_timeout_add(BUDDY_ICON_DELAY,
msn_release_buddy_icon_request_timeout, userlist);
}
diff --git a/libpurple/protocols/msn/slplink.c b/libpurple/protocols/msn/slplink.c
index 7975725e25..078b9000d5 100644
--- a/libpurple/protocols/msn/slplink.c
+++ b/libpurple/protocols/msn/slplink.c
@@ -58,7 +58,7 @@ debug_msg_to_file(MsnMessage *msg, gboolean send)
* Main
**************************************************************************/
-MsnSlpLink *
+static MsnSlpLink *
msn_slplink_new(MsnSession *session, const char *username)
{
MsnSlpLink *slplink;
@@ -593,7 +593,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",
diff --git a/libpurple/protocols/msn/slplink.h b/libpurple/protocols/msn/slplink.h
index 0e820272dd..4c650b1ce0 100644
--- a/libpurple/protocols/msn/slplink.h
+++ b/libpurple/protocols/msn/slplink.h
@@ -59,11 +59,21 @@ struct _MsnSlpLink
GQueue *slp_msg_queue;
};
-MsnSlpLink *msn_slplink_new(MsnSession *session, const char *username);
void msn_slplink_destroy(MsnSlpLink *slplink);
+
+/**
+ * @return An MsnSlpLink for the given user, or NULL if there is no
+ * existing MsnSlpLink.
+ */
MsnSlpLink *msn_session_find_slplink(MsnSession *session,
const char *who);
+
+/**
+ * @return An MsnSlpLink for the given user. One will be created if
+ * it does not already exist.
+ */
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);
diff --git a/libpurple/protocols/msn/soap.c b/libpurple/protocols/msn/soap.c
index e34e7ef987..9f43e836eb 100644
--- a/libpurple/protocols/msn/soap.c
+++ b/libpurple/protocols/msn/soap.c
@@ -1,5 +1,5 @@
/**
- * @file soap.c
+ * @file soap.c
* SOAP connection related process
* Author
* MaYuan<mayuan2006@gmail.com>
@@ -107,7 +107,7 @@ msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc,
/*ssl soap error callback*/
static void
msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data)
-{
+{
MsnSoapConn * soapconn = data;
g_return_if_fail(data != NULL);
@@ -159,7 +159,7 @@ 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");
@@ -250,7 +250,7 @@ msn_soap_read(MsnSoapConn *soapconn)
{
gssize len, requested_len;
char temp_buf[MSN_SOAP_READ_BUFF_SIZE];
-
+
if ( soapconn->need_to_read == 0 || soapconn->need_to_read > MSN_SOAP_READ_BUFF_SIZE) {
requested_len = MSN_SOAP_READ_BUFF_SIZE;
}
@@ -264,7 +264,7 @@ msn_soap_read(MsnSoapConn *soapconn)
len = read(soapconn->fd, temp_buf, requested_len);
}
-
+
if ( len <= 0 ) {
switch (errno) {
@@ -298,7 +298,7 @@ msn_soap_read(MsnSoapConn *soapconn)
soapconn->read_len + len + 1);
exit(EXIT_FAILURE);
}
-
+
}
#if defined(MSN_SOAP_DEBUG)
@@ -312,7 +312,7 @@ msn_soap_read(MsnSoapConn *soapconn)
}
/*read the whole SOAP server response*/
-static void
+static void
msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
{
MsnSoapConn *soapconn = data;
@@ -331,10 +331,10 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
session = soapconn->session;
g_return_if_fail(session != NULL);
-
+
/*read the request header*/
len = msn_soap_read(soapconn);
-
+
if ( len < 0 )
return;
@@ -342,7 +342,7 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
return;
}
- if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
+ if ( (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL)
|| ( strstr(soapconn->read_buf, "HTTP/1.1 301") != NULL ) )
{
/* Redirect. */
@@ -382,14 +382,14 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
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);
@@ -429,7 +429,7 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
g_free(soapconn->login_host);
soapconn->login_host = g_strdup(location);
-
+
msn_soap_close_handler( &(soapconn->input_handler) );
msn_soap_close(soapconn);
@@ -489,7 +489,7 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
}
}
}
-
+
}
else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable"))
{
@@ -539,11 +539,11 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
#if defined(MSN_SOAP_DEBUG) && !defined(_WIN32)
node = xmlnode_from_str(soapconn->body, soapconn->body_len);
-
+
if (node != NULL) {
formattedxml = xmlnode_to_formatted_str(node, NULL);
http_headers = g_strndup(soapconn->read_buf, soapconn->body - soapconn->read_buf);
-
+
purple_debug_info("MSN SOAP","Data with XML payload received from the SOAP server:\n%s%s\n", http_headers, formattedxml);
g_free(http_headers);
g_free(formattedxml);
@@ -572,22 +572,22 @@ msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond)
if ( soapconn->read_cb != NULL ) {
soapconn_is_valid = soapconn->read_cb(soapconn);
}
-
+
if (!soapconn_is_valid) {
return;
}
/* dispatch next request in queue */
msn_soap_post(soapconn, NULL);
- }
+ }
return;
}
-void
+void
msn_soap_free_read_buf(MsnSoapConn *soapconn)
{
g_return_if_fail(soapconn != NULL);
-
+
if (soapconn->read_buf) {
g_free(soapconn->read_buf);
}
@@ -626,7 +626,7 @@ msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond)
}
total_len = strlen(soapconn->write_buf);
- /*
+ /*
* write the content to SSL server,
*/
len = purple_ssl_write(soapconn->gsc,
@@ -690,7 +690,7 @@ msn_soap_write(MsnSoapConn * soapconn, char *write_buf, MsnSoapWrittenCbFunction
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*/
@@ -748,7 +748,7 @@ msn_soap_post_head_request(MsnSoapConn *soapconn)
{
g_return_if_fail(soapconn != NULL);
g_return_if_fail(soapconn->soap_queue != NULL);
-
+
if (soapconn->step == MSN_SOAP_CONNECTED ||
soapconn->step == MSN_SOAP_CONNECTED_IDLE) {
@@ -868,7 +868,7 @@ msn_soap_post_request(MsnSoapConn *soapconn, MsnSoapReq *request)
else
purple_debug_info("MSN SOAP","Failed to parse SOAP request being sent:\n%s\n", request_str);
#endif
-
+
/*free read buffer*/
// msn_soap_free_read_buf(soapconn);
/*post it to server*/
diff --git a/libpurple/protocols/msn/soap.h b/libpurple/protocols/msn/soap.h
index cb17742b9b..ada2a1ab15 100644
--- a/libpurple/protocols/msn/soap.h
+++ b/libpurple/protocols/msn/soap.h
@@ -66,7 +66,7 @@ struct _MsnSoapReq{
char *soap_action;
char *body;
-
+
gpointer data_cb;
MsnSoapReadCbFunction read_cb;
MsnSoapWrittenCbFunction written_cb;
diff --git a/libpurple/protocols/msn/soap2.c b/libpurple/protocols/msn/soap2.c
index f91d5d4101..416c58da93 100644
--- a/libpurple/protocols/msn/soap2.c
+++ b/libpurple/protocols/msn/soap2.c
@@ -82,6 +82,7 @@ static void msn_soap_message_send_internal(MsnSession *session,
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)
@@ -264,45 +265,53 @@ static void
msn_soap_read_cb(gpointer data, gint fd, PurpleInputCondition cond)
{
MsnSoapConnection *conn = data;
- int count = 0, cnt;
- char buf[8192];
- char *linebreak;
- char *cursor;
- gboolean handled = FALSE;
+ 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;
- if (conn->buf == NULL) {
- conn->buf = g_string_new_len(buf, cnt);
- } else {
- g_string_append_len(conn->buf, buf, cnt);
- }
+ g_string_append_len(conn->buf, buf, cnt);
}
- if (cnt < 0) {
- if (errno != EAGAIN) {
- purple_debug_info("soap", "read: %s\n", g_strerror(errno));
+ /* && 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);
- return;
- } else if (count == 0) {
- return;
}
}
+}
- if (cnt == 0 && count == 0) {
- purple_debug_info("soap", "msn_soap_read_cb() called, but no data available?\n");
- purple_ssl_close(conn->ssl);
- conn->ssl = NULL;
- msn_soap_connection_handle_next(conn);
- return;
- }
+static void
+msn_soap_process(MsnSoapConnection *conn) {
+ gboolean handled = FALSE;
+ char *cursor;
+ char *linebreak;
purple_debug_info("soap", "current %s\n", conn->buf->str);
diff --git a/libpurple/protocols/msn/state.c b/libpurple/protocols/msn/state.c
index acb55c3700..4f8aa50ba1 100644
--- a/libpurple/protocols/msn/state.c
+++ b/libpurple/protocols/msn/state.c
@@ -150,7 +150,7 @@ msn_get_currentmedia(char *xml_str, gsize len)
{
xmlnode *payloadNode, *currentmediaNode;
char *currentmedia;
-
+
purple_debug_info("msn","msn get CurrentMedia\n");
payloadNode = xmlnode_from_str(xml_str, len);
if (!payloadNode){
@@ -176,7 +176,7 @@ msn_get_psm(char *xml_str, gsize len)
{
xmlnode *payloadNode, *psmNode;
char *psm;
-
+
purple_debug_info("MSNP14","msn get PSM\n");
payloadNode = xmlnode_from_str(xml_str, len);
if (!payloadNode){
@@ -217,7 +217,7 @@ create_media_string(PurplePresence *presence)
return ret;
}
-/* set the MSN's PSM info,Currently Read from the status Line
+/* set the MSN's PSM info,Currently Read from the status Line
* Thanks for Cris Code
*/
void
diff --git a/libpurple/protocols/msn/transaction.h b/libpurple/protocols/msn/transaction.h
index b7b40329b4..93f6341980 100644
--- a/libpurple/protocols/msn/transaction.h
+++ b/libpurple/protocols/msn/transaction.h
@@ -62,7 +62,7 @@ struct _MsnTransaction
};
MsnTransaction *msn_transaction_new(MsnCmdProc *cmdproc, const char *command,
- const char *format, ...) G_GNUC_PRINTF(3, 4);
+ const char *format, ...) G_GNUC_PRINTF(3, 4);
void msn_transaction_destroy(MsnTransaction *trans);
char *msn_transaction_to_string(MsnTransaction *trans);
diff --git a/libpurple/protocols/msn/user.c b/libpurple/protocols/msn/user.c
index 37e6076f25..a73cec2a8f 100644
--- a/libpurple/protocols/msn/user.c
+++ b/libpurple/protocols/msn/user.c
@@ -122,7 +122,7 @@ msn_user_update(MsnUser *user)
NULL);
} else {
purple_prpl_got_user_status_deactive(account, user->passport, "tune");
- }
+ }
}
if (user->idle)
@@ -239,7 +239,7 @@ void
msn_user_unset_op(MsnUser *user, int list_op)
{
g_return_if_fail(user != NULL);
-
+
user->list_op &= ~list_op;
}
diff --git a/libpurple/protocols/msn/user.h b/libpurple/protocols/msn/user.h
index e9355b2896..220eccc5fa 100644
--- a/libpurple/protocols/msn/user.h
+++ b/libpurple/protocols/msn/user.h
@@ -57,9 +57,6 @@ typedef struct _CurrentMedia
*/
struct _MsnUser
{
-#if 0
- MsnSession *session; /**< The MSN session. */
-#endif
MsnUserList *userlist;
char *passport; /**< The passport account. */
@@ -69,7 +66,7 @@ struct _MsnUser
char * uid; /*< User Id */
const char *status; /**< The state of the user. */
- char *statusline; /**< The state of the user. */
+ char *statusline; /**< The state of the user. */
CurrentMedia media; /**< Current media of the user. */
gboolean idle; /**< The idle state of the user. */
@@ -135,7 +132,7 @@ void msn_user_update(MsnUser *user);
/**
* Sets the new statusline of user.
- *
+ *
* @param user The user.
* @param state The statusline string.
*/
@@ -143,7 +140,7 @@ void msn_user_set_statusline(MsnUser *user, const char *statusline);
/**
* Sets the current media of user.
- *
+ *
* @param user The user.
* @param cmedia Current media.
*/
diff --git a/libpurple/protocols/msn/userlist.c b/libpurple/protocols/msn/userlist.c
index e867c173c1..8a32460f71 100644
--- a/libpurple/protocols/msn/userlist.c
+++ b/libpurple/protocols/msn/userlist.c
@@ -48,7 +48,7 @@ msn_accept_add_cb(gpointer data)
{
MsnSession *session = pa->gc->proto_data;
MsnUserList *userlist = session->userlist;
-
+
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);
@@ -127,7 +127,7 @@ msn_userlist_user_is_in_list(MsnUser *user, MsnListId list_id)
if (user == NULL)
return FALSE;
-
+
list_op = 1 << list_id;
if (user->list_op & list_op)
@@ -237,7 +237,7 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
if (convo) {
PurpleBuddy *buddy;
char *msg;
-
+
buddy = purple_find_buddy(account, passport);
msg = g_strdup_printf(
_("%s has added you to his or her buddy list."),
@@ -246,7 +246,7 @@ msn_got_add_user(MsnSession *session, MsnUser *user,
PURPLE_MESSAGE_SYSTEM, time(NULL));
g_free(msg);
}
-
+
if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
{
/*
@@ -341,7 +341,7 @@ msn_got_lst_user(MsnSession *session, MsnUser *user,
passport = msn_user_get_passport(user);
store = msn_user_get_store_name(user);
-
+
msn_user_set_op(user, list_op);
if (list_op & MSN_LIST_FL_OP)
@@ -407,7 +407,7 @@ msn_userlist_new(MsnSession *session)
userlist->session = session;
userlist->buddy_icon_requests = g_queue_new();
-
+
/* buddy_icon_window is the number of allowed simultaneous buddy icon requests.
* XXX With smarter rate limiting code, we could allow more at once... 5 was the limit set when
* we weren't retrieiving any more than 5 per MSN session. */
@@ -644,12 +644,12 @@ void
msn_userlist_rem_buddy(MsnUserList *userlist, const char *who)
{
MsnUser *user = NULL;
-
+
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);
msn_userlist_rem_buddy_from_list(userlist, who, MSN_LIST_FL);
@@ -669,9 +669,9 @@ msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
MsnListOp list_op = 1 << list_id;
user = msn_userlist_find_user(userlist, who);
-
+
g_return_if_fail(user != NULL);
-
+
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);
@@ -690,14 +690,14 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
MsnUser *user;
MsnCallbackState *state = NULL;
const char *group_id = NULL, *new_group_name;
-
+
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);
@@ -709,9 +709,9 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
/* only notify the user about problems adding to the friends list
* maybe we should do something else for other lists, but it probably
* won't cause too many problems if we just ignore it */
-
+
char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
-
+
purple_notify_error(NULL, NULL, str,
_("The username specified is invalid."));
g_free(str);
@@ -725,7 +725,7 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
{
/* 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);
-
+
msn_callback_state_set_action(state, MSN_ADD_BUDDY);
msn_add_group(userlist->session, state, new_group_name);
@@ -733,9 +733,9 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
} else {
msn_callback_state_set_guid(state, group_id);
}
-
+
/* XXX: adding user here may not be correct (should add them in the
- * ACK to the ADL command), but for now we need to make sure they exist
+ * ACK to the ADL command), but for now we need to make sure they exist
* early enough that the ILN command doesn't screw us up */
user = msn_userlist_find_add_user(userlist, who, who);
@@ -751,7 +751,7 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
return;
}
}
-
+
purple_debug_info("MSN Userlist", "Adding user: %s to group id: %s\n", who, group_id);
msn_callback_state_set_action(state, MSN_ADD_BUDDY);
@@ -762,7 +762,7 @@ msn_userlist_add_buddy(MsnUserList *userlist, const char *who, const char *group
}
void
-msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
+msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
MsnListId list_id)
{
MsnUser *user = NULL;
@@ -770,9 +770,9 @@ msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
MsnListOp list_op = 1 << list_id;
g_return_if_fail(userlist != NULL);
-
+
user = msn_userlist_find_add_user(userlist, who, who);
-
+
/* First we're going to check if it's already there. */
if (msn_userlist_user_is_in_list(user, list_id))
{
@@ -780,16 +780,16 @@ msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
purple_debug_info("MSN Userlist", "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 */
-
+
msn_user_set_op(user, list_op);
-
+
msn_notification_add_buddy_to_list(userlist->session->notification, list_id, who);
}
@@ -799,7 +799,7 @@ msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
{
MsnUser *user;
gchar * group_id;
-
+
g_return_val_if_fail(userlist != NULL, FALSE);
g_return_val_if_fail(group_name != NULL, FALSE);
g_return_val_if_fail(who != NULL, FALSE);
@@ -815,7 +815,7 @@ msn_userlist_add_buddy_to_group(MsnUserList *userlist, const char *who,
purple_debug_error("MSN Userlist", "User %s not found!", who);
return FALSE;
}
-
+
msn_user_add_group_id(user, group_id);
return TRUE;
@@ -832,7 +832,7 @@ msn_userlist_rem_buddy_from_group(MsnUserList *userlist, const char *who,
g_return_val_if_fail(userlist != NULL, FALSE);
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);
if ( (group_id = msn_userlist_find_group_id(userlist, group_name)) == NULL) {
@@ -856,7 +856,7 @@ msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
{
const char *new_group_id;
MsnCallbackState *state;
-
+
g_return_if_fail(userlist != NULL);
g_return_if_fail(userlist->session != NULL);
g_return_if_fail(userlist->session->contact != NULL);
@@ -870,11 +870,11 @@ msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
if (new_group_id == NULL)
- {
+ {
msn_add_group(userlist->session, state, new_group_name);
return;
}
-
+
/* add the contact to the new group, and remove it from the old one in
* the callback
*/
@@ -928,6 +928,6 @@ msn_userlist_load(MsnSession *session)
(char *)l->data,NULL);
msn_user_set_op(user, MSN_LIST_BL_OP);
}
-
+
}
diff --git a/libpurple/protocols/msn/userlist.h b/libpurple/protocols/msn/userlist.h
index d2dac379f7..ea0d03b6a8 100644
--- a/libpurple/protocols/msn/userlist.h
+++ b/libpurple/protocols/msn/userlist.h
@@ -45,11 +45,8 @@ struct _MsnUserList
{
MsnSession *session;
- /* MsnUsers *users; */
- /* MsnGroups *groups; */
-
- GList *users;
- GList *groups;
+ GList *users; /* Contains MsnUsers */
+ GList *groups; /* Contains MsnGroups */
GQueue *buddy_icon_requests;
int buddy_icon_window;
@@ -94,7 +91,7 @@ void msn_userlist_rename_group_id(MsnUserList *userlist, const char *group_id,
void msn_userlist_remove_group_id(MsnUserList *userlist, const char *group_id);
void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who);
-void msn_userlist_add_buddy(MsnUserList *userlist,
+void msn_userlist_add_buddy(MsnUserList *userlist,
const char *who, const char *group_name);
void msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
const char *old_group_name,
@@ -106,7 +103,7 @@ gboolean msn_userlist_rem_buddy_from_group(MsnUserList *userlist,
const char *who,
const char *group_name);
-void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
+void msn_userlist_add_buddy_to_list(MsnUserList *userlist, const char *who,
MsnListId list_id);
void msn_userlist_rem_buddy_from_list(MsnUserList *userlist, const char *who,
MsnListId list_id);
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/myspace.c b/libpurple/protocols/myspace/myspace.c
index c605a8a429..0130e3b215 100644
--- a/libpurple/protocols/myspace/myspace.c
+++ b/libpurple/protocols/myspace/myspace.c
@@ -1328,7 +1328,10 @@ msim_check_alive(gpointer data)
delta = time(NULL) - session->last_comm;
/* purple_debug_info("msim", "msim_check_alive: delta=%d\n", delta); */
if (delta >= MSIM_KEEPALIVE_INTERVAL) {
- errmsg = g_strdup_printf(_("Connection to server lost (no data received within %d seconds)"), (int)delta);
+ errmsg = g_strdup_printf(ngettext("Connection to server lost (no data received within %d second)",
+ "Connection to server lost (no data received within %d seconds)",
+ (int)delta),
+ (int)delta);
purple_debug_info("msim", "msim_check_alive: %s > interval of %d, presumed dead\n",
errmsg, MSIM_KEEPALIVE_INTERVAL);
@@ -2404,7 +2407,7 @@ const char *msim_normalize(const PurpleAccount *account, const char *str) {
const char *username;
/* If the account does not exist, we can't look up the user. */
- if (!account)
+ if (!account || !account->gc)
return str;
id = atol(str);
@@ -2946,7 +2949,10 @@ msim_got_contact_list(MsimSession *session, MsimMessage *reply, gpointer user_da
switch (GPOINTER_TO_UINT(user_data)) {
case MSIM_CONTACT_LIST_IMPORT_ALL_FRIENDS:
- msg = g_strdup_printf(_("%d buddies were added or updated from the server (including buddies already on the server-side list)"), buddy_count);
+ msg = g_strdup_printf(ngettext("%d buddy was added or updated from the server (including buddies already on the server-side list)",
+ "%d buddies were added or updated from the server (including buddies already on the server-side list)",
+ buddy_count),
+ buddy_count);
purple_notify_info(session->account, _("Add contacts from server"), msg, NULL);
g_free(msg);
break;
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_admin.c b/libpurple/protocols/oscar/family_admin.c
index 8dd379fb2f..b111c3dfaf 100644
--- a/libpurple/protocols/oscar/family_admin.c
+++ b/libpurple/protocols/oscar/family_admin.c
@@ -47,8 +47,8 @@ aim_admin_getinfo(OscarData *od, FlapConnection *conn, guint16 info)
byte_stream_put16(&bs, info);
byte_stream_put16(&bs, 0x0000);
- snacid = aim_cachesnac(od, 0x0007, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0007, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -127,8 +127,8 @@ aim_admin_setnick(OscarData *od, FlapConnection *conn, const char *newnick)
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0007, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -154,8 +154,8 @@ aim_admin_changepasswd(OscarData *od, FlapConnection *conn, const char *newpw, c
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0007, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -177,8 +177,8 @@ aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail)
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x0007, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0007, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ADMIN, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -194,11 +194,11 @@ aim_admin_setemail(OscarData *od, FlapConnection *conn, const char *newemail)
void
aim_admin_reqconfirm(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n(od, conn, 0x0007, 0x0006);
+ aim_genericreq_n(od, conn, SNAC_FAMILY_ADMIN, 0x0006);
}
/**
- * Subtype 0x0007 - Account confirmation request acknowledgement.
+ * Subtype SNAC_FAMILY_ADMIN - Account confirmation request acknowledgement.
*/
static int
accountconfirm(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -227,7 +227,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005)) {
infochange(od, conn, mod, frame, snac, bs);
return 1;
- } else if (snac->subtype == 0x0007)
+ } else if (snac->subtype == SNAC_FAMILY_ADMIN)
return accountconfirm(od, conn, mod, frame, snac, bs);
return 0;
@@ -235,7 +235,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int admin_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0007;
+ mod->family = SNAC_FAMILY_ADMIN;
mod->version = 0x0001;
mod->toolid = 0x0010;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_advert.c b/libpurple/protocols/oscar/family_advert.c
index 0dfeafb815..fcd1677ce6 100644
--- a/libpurple/protocols/oscar/family_advert.c
+++ b/libpurple/protocols/oscar/family_advert.c
@@ -28,7 +28,7 @@
void
aim_ads_requestads(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n(od, conn, 0x0005, 0x0002);
+ aim_genericreq_n(od, conn, SNAC_FAMILY_ADVERT, 0x0002);
}
static int snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *rx, aim_modsnac_t *snac, ByteStream *bs)
@@ -39,7 +39,7 @@ static int snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, F
int adverts_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0005;
+ mod->family = SNAC_FAMILY_ADVERT;
mod->version = 0x0001;
mod->toolid = 0x0001;
mod->toolversion = 0x0001;
diff --git a/libpurple/protocols/oscar/family_alert.c b/libpurple/protocols/oscar/family_alert.c
index ae8ff8f82f..f945dec34d 100644
--- a/libpurple/protocols/oscar/family_alert.c
+++ b/libpurple/protocols/oscar/family_alert.c
@@ -72,8 +72,8 @@ aim_email_sendcookies(OscarData *od)
byte_stream_put16(&bs, 0xb0ee);
byte_stream_put16(&bs, 0x0631);
- snacid = aim_cachesnac(od, 0x0018, 0x0006, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0018, 0x0006, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0006, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -188,8 +188,8 @@ aim_email_activate(OscarData *od)
byte_stream_put32(&bs, 0x04000000);
byte_stream_put32(&bs, 0x00000000);
- snacid = aim_cachesnac(od, 0x0018, 0x0016, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0018, 0x0006, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ALERT, 0x0016, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ALERT, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -225,7 +225,7 @@ email_shutdown(OscarData *od, aim_module_t *mod)
int
email_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0018;
+ mod->family = SNAC_FAMILY_ALERT;
mod->version = 0x0001;
mod->toolid = 0x0010;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_auth.c b/libpurple/protocols/oscar/family_auth.c
index 5f4315d367..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));
@@ -200,9 +197,13 @@ goddamnicq2(OscarData *od, FlapConnection *conn, const char *sn, const char *pas
* usually happens for AOL accounts. We are told that we
* should truncate it if the 0x0017/0x0007 SNAC contains
* a TLV of type 0x0026 with data 0x0000.
+ * @param allow_multiple_logins Allow multiple logins? If TRUE, the AIM
+ * server will prompt the user when multiple logins occur. If
+ * FALSE, existing connections (on other clients) will be
+ * disconnected automatically as we connect.
*/
int
-aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key)
+aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins)
{
FlapFrame *frame;
GSList *tlvlist = NULL;
@@ -221,8 +222,8 @@ aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *
frame = flap_frame_new(od, 0x02, 1152);
- snacid = aim_cachesnac(od, 0x0017, 0x0002, 0x0000, NULL, 0);
- aim_putsnac(&frame->data, 0x0017, 0x0002, 0x0000, snacid);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0002, 0x0000, NULL, 0);
+ aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0002, 0x0000, snacid);
aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
@@ -256,7 +257,7 @@ aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *
* If set, old-fashioned buddy lists will not work. You will need
* to use SSI.
*/
- aim_tlvlist_add_8(&tlvlist, 0x004a, 0x01);
+ aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x02));
aim_tlvlist_write(&frame->data, &tlvlist);
@@ -403,7 +404,7 @@ parse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
od->authinfo = info;
- if ((userfunc = aim_callhandler(od, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
+ if ((userfunc = aim_callhandler(od, snac ? snac->family : SNAC_FAMILY_AUTH, snac ? snac->subtype : 0x0003)))
ret = userfunc(od, conn, frame, info);
aim_tlvlist_free(tlvlist);
@@ -449,7 +450,7 @@ goddamnicq(OscarData *od, FlapConnection *conn, const char *sn)
FlapFrame frame;
aim_rxcallback_t userfunc;
- if ((userfunc = aim_callhandler(od, 0x0017, 0x0007)))
+ if ((userfunc = aim_callhandler(od, SNAC_FAMILY_AUTH, 0x0007)))
userfunc(od, conn, &frame, "");
return 0;
@@ -483,8 +484,8 @@ aim_request_login(OscarData *od, FlapConnection *conn, const char *sn)
frame = flap_frame_new(od, 0x02, 10+2+2+strlen(sn)+8);
- snacid = aim_cachesnac(od, 0x0017, 0x0006, 0x0000, NULL, 0);
- aim_putsnac(&frame->data, 0x0017, 0x0006, 0x0000, snacid);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_AUTH, 0x0006, 0x0000, NULL, 0);
+ aim_putsnac(&frame->data, SNAC_FAMILY_AUTH, 0x0006, 0x0000, snacid);
aim_tlvlist_add_str(&tlvlist, 0x0001, sn);
@@ -628,7 +629,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
auth_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0017;
+ mod->family = SNAC_FAMILY_AUTH;
mod->version = 0x0000;
mod->flags = 0;
strncpy(mod->name, "auth", sizeof(mod->name));
diff --git a/libpurple/protocols/oscar/family_bart.c b/libpurple/protocols/oscar/family_bart.c
index 08255ac47f..d699f91617 100644
--- a/libpurple/protocols/oscar/family_bart.c
+++ b/libpurple/protocols/oscar/family_bart.c
@@ -43,7 +43,7 @@ aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen)
ByteStream bs;
aim_snacid_t snacid;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0010)) || !icon || !iconlen)
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !icon || !iconlen)
return -EINVAL;
byte_stream_new(&bs, 2 + 2 + iconlen);
@@ -55,8 +55,8 @@ aim_bart_upload(OscarData *od, const guint8 *icon, guint16 iconlen)
byte_stream_put16(&bs, iconlen);
byte_stream_putraw(&bs, icon, iconlen);
- snacid = aim_cachesnac(od, 0x0010, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0010, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -102,7 +102,7 @@ aim_bart_request(OscarData *od, const char *sn, guint8 iconcsumtype, const guint
ByteStream bs;
aim_snacid_t snacid;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0010)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_BART)) || !sn || !strlen(sn) || !iconcsum || !iconcsumlen)
return -EINVAL;
byte_stream_new(&bs, 1+strlen(sn) + 4 + 1+iconcsumlen);
@@ -120,8 +120,8 @@ aim_bart_request(OscarData *od, const char *sn, guint8 iconcsumtype, const guint
byte_stream_put8(&bs, iconcsumlen);
byte_stream_putraw(&bs, iconcsum, iconcsumlen);
- snacid = aim_cachesnac(od, 0x0010, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0010, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_BART, 0x0004, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BART, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -174,7 +174,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
bart_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0010;
+ mod->family = SNAC_FAMILY_BART;
mod->version = 0x0001;
mod->toolid = 0x0010;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_bos.c b/libpurple/protocols/oscar/family_bos.c
index 2eb6167de5..e34eba43b2 100644
--- a/libpurple/protocols/oscar/family_bos.c
+++ b/libpurple/protocols/oscar/family_bos.c
@@ -32,7 +32,7 @@
void
aim_bos_reqrights(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n_snacid(od, conn, 0x0009, 0x0002);
+ aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_BOS, 0x0002);
}
/* Subtype 0x0003 - BOS Rights. */
@@ -81,7 +81,7 @@ static int rights(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFr
void
aim_bos_setgroupperm(OscarData *od, FlapConnection *conn, guint32 mask)
{
- aim_genericreq_l(od, conn, 0x0009, 0x0004, &mask);
+ aim_genericreq_l(od, conn, SNAC_FAMILY_BOS, 0x0004, &mask);
}
/*
@@ -153,8 +153,8 @@ int aim_bos_changevisibility(OscarData *od, FlapConnection *conn, int changetype
}
g_free(localcpy);
- snacid = aim_cachesnac(od, 0x0009, subtype, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0009, subtype, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_BOS, subtype, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BOS, subtype, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -173,7 +173,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
bos_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0009;
+ mod->family = SNAC_FAMILY_BOS;
mod->version = 0x0001;
mod->toolid = 0x0110;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_buddy.c b/libpurple/protocols/oscar/family_buddy.c
index 5ceb0cd93c..b20e0fc7f5 100644
--- a/libpurple/protocols/oscar/family_buddy.c
+++ b/libpurple/protocols/oscar/family_buddy.c
@@ -108,8 +108,8 @@ aim_buddylist_addbuddy(OscarData *od, FlapConnection *conn, const char *sn)
byte_stream_put8(&bs, strlen(sn));
byte_stream_putstr(&bs, sn);
- snacid = aim_cachesnac(od, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
- flap_connection_send_snac(od, conn, 0x0003, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, sn, strlen(sn)+1);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -159,8 +159,8 @@ aim_buddylist_set(OscarData *od, FlapConnection *conn, const char *buddy_list)
tmpptr = strtok(NULL, "&");
}
- snacid = aim_cachesnac(od, 0x0003, 0x0004, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0003, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -190,8 +190,8 @@ aim_buddylist_removebuddy(OscarData *od, FlapConnection *conn, const char *sn)
byte_stream_put8(&bs, strlen(sn));
byte_stream_putstr(&bs, sn);
- snacid = aim_cachesnac(od, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
- flap_connection_send_snac(od, conn, 0x0003, 0x0005, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_BUDDY, 0x0005, 0x0000, sn, strlen(sn)+1);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_BUDDY, 0x0005, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -243,7 +243,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
buddylist_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0003;
+ mod->family = SNAC_FAMILY_BUDDY;
mod->version = 0x0001;
mod->toolid = 0x0110;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_chat.c b/libpurple/protocols/oscar/family_chat.c
index 1240f1eee7..43f3c48107 100644
--- a/libpurple/protocols/oscar/family_chat.c
+++ b/libpurple/protocols/oscar/family_chat.c
@@ -364,7 +364,7 @@ aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar
byte_stream_new(&bs, 1142);
- snacid = aim_cachesnac(od, 0x000e, 0x0005, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_CHAT, 0x0005, 0x0000, NULL, 0);
/*
* Cookie
@@ -432,7 +432,7 @@ aim_chat_send_im(OscarData *od, FlapConnection *conn, guint16 flags, const gchar
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x000e, 0x0005, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_CHAT, 0x0005, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -594,7 +594,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
chat_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x000e;
+ mod->family = SNAC_FAMILY_CHAT;
mod->version = 0x0001;
mod->toolid = 0x0010;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_chatnav.c b/libpurple/protocols/oscar/family_chatnav.c
index 6ef8be870c..d24c31eb0d 100644
--- a/libpurple/protocols/oscar/family_chatnav.c
+++ b/libpurple/protocols/oscar/family_chatnav.c
@@ -42,7 +42,7 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
return 0;
}
- if (snac2->family != 0x000d) {
+ if (snac2->family != SNAC_FAMILY_CHATNAV) {
purple_debug_warning("oscar", "chatnav error: received response that maps to corrupt request (fam=%04x)\n", snac2->family);
return 0;
}
@@ -80,7 +80,7 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
*/
void aim_chatnav_reqrights(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n_snacid(od, conn, 0x000d, 0x0002);
+ aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_CHATNAV, 0x0002);
}
/*
@@ -97,7 +97,7 @@ int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name
byte_stream_new(&bs, 1142);
- snacid = aim_cachesnac(od, 0x000d, 0x0008, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, NULL, 0);
/* exchange */
byte_stream_put16(&bs, exchange);
@@ -137,7 +137,7 @@ int aim_chatnav_createroom(OscarData *od, FlapConnection *conn, const char *name
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x000d, 0x0008, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_CHATNAV, 0x0008, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -460,7 +460,7 @@ parseinfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fra
return 0;
}
- if (snac2->family != 0x000d) {
+ if (snac2->family != SNAC_FAMILY_CHATNAV) {
purple_debug_misc("oscar", "faim: chatnav_parse_info: received response that maps to corrupt request! (fam=%04x)\n", snac2->family);
return 0;
}
@@ -506,7 +506,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
chatnav_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x000d;
+ mod->family = SNAC_FAMILY_CHATNAV;
mod->version = 0x0001;
mod->toolid = 0x0010;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_icbm.c b/libpurple/protocols/oscar/family_icbm.c
index a08ac7d201..3bab80dcbc 100644
--- a/libpurple/protocols/oscar/family_icbm.c
+++ b/libpurple/protocols/oscar/family_icbm.c
@@ -164,7 +164,7 @@ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
ByteStream bs;
aim_snacid_t snacid;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
if (!params)
@@ -182,8 +182,8 @@ int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
byte_stream_put16(&bs, params->maxrecverwarn);
byte_stream_put32(&bs, params->minmsginterval);
- snacid = aim_cachesnac(od, 0x0004, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0004, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -198,10 +198,10 @@ int aim_im_reqparams(OscarData *od)
{
FlapConnection *conn;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
- aim_genericreq_n_snacid(od, conn, 0x0004, 0x0004);
+ aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
return 0;
}
@@ -237,7 +237,7 @@ static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *m
* Possible flags:
* AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse
* AIM_IMFLAGS_ACK -- Requests that the server send an ack
- * when the message is received (of type 0x0004/0x000c)
+ * when the message is received (of type SNAC_FAMILY_ICBM/0x000c)
* AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
* online (probably ICQ only).
*
@@ -280,7 +280,7 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
int msgtlvlen;
static const guint8 deffeatures[] = { 0x01, 0x01, 0x01, 0x02 };
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
if (!args)
@@ -410,9 +410,9 @@ int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
}
/* XXX - should be optional */
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &data);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &data);
byte_stream_destroy(&data);
/* clean out SNACs over 60sec old */
@@ -462,7 +462,7 @@ int aim_im_sendch2_chatinvite(OscarData *od, const char *sn, const char *msg, gu
GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
ByteStream hdrbs;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
if (!sn || !msg || !roomname)
@@ -472,7 +472,7 @@ int aim_im_sendch2_chatinvite(OscarData *od, const char *sn, const char *msg, gu
byte_stream_new(&bs, 1142+strlen(sn)+strlen(roomname)+strlen(msg));
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, sn, strlen(sn)+1);
/* XXX should be uncached by an unwritten 'invite accept' handler */
priv = g_malloc(sizeof(struct aim_invite_priv));
@@ -519,7 +519,7 @@ int aim_im_sendch2_chatinvite(OscarData *od, const char *sn, const char *msg, gu
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -539,7 +539,7 @@ int aim_im_sendch2_icon(OscarData *od, const char *sn, const guint8 *icon, int i
aim_snacid_t snacid;
guchar cookie[8];
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
@@ -549,7 +549,7 @@ int aim_im_sendch2_icon(OscarData *od, const char *sn, const guint8 *icon, int i
byte_stream_new(&bs, 8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -589,7 +589,7 @@ int aim_im_sendch2_icon(OscarData *od, const char *sn, const guint8 *icon, int i
byte_stream_put16(&bs, 0x0003);
byte_stream_put16(&bs, 0x0000);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -620,7 +620,7 @@ int aim_im_sendch2_rtfmsg(OscarData *od, struct aim_sendrtfmsg_args *args)
const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* OSCAR_CAPABILITY_ICQRTF capability in string form */
int servdatalen;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
if (!args || !args->destsn || !args->rtfmsg)
@@ -632,7 +632,7 @@ int aim_im_sendch2_rtfmsg(OscarData *od, struct aim_sendrtfmsg_args *args)
byte_stream_new(&bs, 128+servdatalen);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, args->destsn);
@@ -682,7 +682,7 @@ int aim_im_sendch2_rtfmsg(OscarData *od, struct aim_sendrtfmsg_args *args)
byte_stream_putle32(&bs, strlen(rtfcap)+1);
byte_stream_putraw(&bs, (const guint8 *)rtfcap, strlen(rtfcap)+1);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -704,13 +704,13 @@ aim_im_sendch2_cancel(PeerConnection *peer_conn)
ByteStream hdrbs;
od = peer_conn->od;
- conn = flap_connection_findbygroup(od, 0x0004);
+ conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
byte_stream_new(&bs, 118+strlen(peer_conn->sn));
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->sn);
@@ -735,7 +735,7 @@ aim_im_sendch2_cancel(PeerConnection *peer_conn)
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -753,13 +753,13 @@ aim_im_sendch2_connected(PeerConnection *peer_conn)
aim_snacid_t snacid;
od = peer_conn->od;
- conn = flap_connection_findbygroup(od, 0x0004);
+ conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
byte_stream_new(&bs, 11+strlen(peer_conn->sn) + 4+2+8+16);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->sn);
@@ -770,7 +770,7 @@ aim_im_sendch2_connected(PeerConnection *peer_conn)
byte_stream_putraw(&bs, peer_conn->cookie, 8);
byte_stream_putcaps(&bs, peer_conn->type);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -791,13 +791,13 @@ aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *sn,
GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
ByteStream hdrbs;
- conn = flap_connection_findbygroup(od, 0x0004);
+ conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
byte_stream_new(&bs, 246+strlen(sn));
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -825,7 +825,7 @@ aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *sn,
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -844,13 +844,13 @@ aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *sn, c
ByteStream hdrbs;
guint8 ip_comp[4];
- conn = flap_connection_findbygroup(od, 0x0004);
+ conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
byte_stream_new(&bs, 246+strlen(sn));
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -888,7 +888,7 @@ aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *sn, c
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -906,13 +906,13 @@ aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char
GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
ByteStream hdrbs;
- conn = flap_connection_findbygroup(od, 0x0004);
+ conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
byte_stream_new(&bs, 1014);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -971,7 +971,7 @@ aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -990,13 +990,13 @@ aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *
ByteStream hdrbs;
guint8 ip_comp[4];
- conn = flap_connection_findbygroup(od, 0x0004);
+ conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
if (conn == NULL)
return;
byte_stream_new(&bs, 1014);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -1064,7 +1064,7 @@ aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *
aim_tlvlist_free(inner_tlvlist);
aim_tlvlist_free(outer_tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -1085,14 +1085,14 @@ int aim_im_sendch2_geticqaway(OscarData *od, const char *sn, int type)
aim_snacid_t snacid;
guchar cookie[8];
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)) || !sn)
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)) || !sn)
return -EINVAL;
aim_icbm_makecookie(cookie);
byte_stream_new(&bs, 8+2+1+strlen(sn) + 4+0x5e + 4);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
/* ICBM header */
aim_im_puticbm(&bs, cookie, 0x0002, sn);
@@ -1160,7 +1160,7 @@ int aim_im_sendch2_geticqaway(OscarData *od, const char *sn, int type)
byte_stream_put16(&bs, 0x0003);
byte_stream_put16(&bs, 0x0000);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1196,7 +1196,7 @@ int aim_im_sendch4(OscarData *od, const char *sn, guint16 type, const char *mess
byte_stream_new(&bs, 8+3+strlen(sn)+12+strlen(message)+1+4);
- snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
aim_icbm_makecookie(cookie);
@@ -1229,7 +1229,7 @@ int aim_im_sendch4(OscarData *od, const char *sn, guint16 type, const char *mess
byte_stream_put16(&bs, 0x0006);
byte_stream_put16(&bs, 0x0000);
- flap_connection_send_snac(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -2268,13 +2268,13 @@ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *sn, guint32 fla
byte_stream_new(&bs, strlen(sn)+3);
- snacid = aim_cachesnac(od, 0x0004, 0x0008, 0x0000, sn, strlen(sn)+1);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0008, 0x0000, sn, strlen(sn)+1);
byte_stream_put16(&bs, (flags & AIM_WARN_ANON) ? 0x0001 : 0x0000);
byte_stream_put8(&bs, strlen(sn));
byte_stream_putstr(&bs, sn);
- flap_connection_send_snac(od, conn, 0x0004, 0x0008, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0008, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -2321,12 +2321,12 @@ int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, gui
aim_snacid_t snacid;
GSList *tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
return -EINVAL;
byte_stream_new(&bs, 8+2+1+strlen(sn)+6);
- snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
byte_stream_putraw(&bs, cookie, 8);
@@ -2338,7 +2338,7 @@ int aim_im_denytransfer(OscarData *od, const char *sn, const guchar *cookie, gui
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -2660,7 +2660,7 @@ int aim_im_reqofflinemsgs(OscarData *od)
if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
return -EINVAL;
- aim_genericreq_n(od, conn, 0x0004, 0x0010);
+ aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
return 0;
}
@@ -2686,7 +2686,7 @@ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2)
byte_stream_new(&bs, 11+strlen(sn)+2);
- snacid = aim_cachesnac(od, 0x0004, 0x0014, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
/*
* 8 days of light
@@ -2713,7 +2713,7 @@ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *sn, guint16 type2)
*/
byte_stream_put16(&bs, type2);
- flap_connection_send_snac(od, conn, 0x0004, 0x0014, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -2773,7 +2773,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
msg_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0004;
+ mod->family = SNAC_FAMILY_ICBM;
mod->version = 0x0001;
mod->toolid = 0x0110;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_icq.c b/libpurple/protocols/oscar/family_icq.c
index 4645e28e26..ce907db9af 100644
--- a/libpurple/protocols/oscar/family_icq.c
+++ b/libpurple/protocols/oscar/family_icq.c
@@ -33,14 +33,14 @@ int aim_icq_reqofflinemsgs(OscarData *od)
aim_snacid_t snacid;
int bslen;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2 + 4 + 2 + 2;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -51,7 +51,7 @@ int aim_icq_reqofflinemsgs(OscarData *od)
byte_stream_putle16(&bs, 0x003c); /* I command thee. */
byte_stream_putle16(&bs, snacid); /* eh. */
- flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -65,14 +65,14 @@ int aim_icq_ackofflinemsgs(OscarData *od)
aim_snacid_t snacid;
int bslen;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2 + 4 + 2 + 2;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -83,7 +83,7 @@ int aim_icq_ackofflinemsgs(OscarData *od)
byte_stream_putle16(&bs, 0x003e); /* I command thee. */
byte_stream_putle16(&bs, snacid); /* eh. */
- flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -99,14 +99,14 @@ aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
aim_snacid_t snacid;
int bslen;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2+4+2+2+2+2+2+1+1+1+1+1+1;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -126,7 +126,7 @@ aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
byte_stream_putle8(&bs, 0x00);
byte_stream_putle8(&bs, !auth_required);
- flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -151,7 +151,7 @@ int aim_icq_changepasswd(OscarData *od, const char *passwd)
if (!passwd)
return -EINVAL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
passwdlen = strlen(passwd);
@@ -161,7 +161,7 @@ int aim_icq_changepasswd(OscarData *od, const char *passwd)
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -176,7 +176,7 @@ int aim_icq_changepasswd(OscarData *od, const char *passwd)
byte_stream_putstr(&bs, passwd);
byte_stream_putle8(&bs, '\0');
- flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -194,14 +194,14 @@ int aim_icq_getallinfo(OscarData *od, const char *uin)
if (!uin || uin[0] < '0' || uin[0] > '9')
return -EINVAL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2 + 4 + 2 + 2 + 2 + 4;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -214,7 +214,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, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -239,14 +239,14 @@ int aim_icq_getalias(OscarData *od, const char *uin)
if (!uin || uin[0] < '0' || uin[0] > '9')
return -EINVAL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2 + 4 + 2 + 2 + 2 + 4;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -259,7 +259,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, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -283,14 +283,14 @@ int aim_icq_getsimpleinfo(OscarData *od, const char *uin)
if (!uin || uin[0] < '0' || uin[0] > '9')
return -EINVAL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2 + 4 + 2 + 2 + 2 + 4;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -303,7 +303,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, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -321,14 +321,14 @@ int aim_icq_sendxmlreq(OscarData *od, const char *xml)
if (!xml || !strlen(xml))
return -EINVAL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
bslen = 2 + 10 + 2 + strlen(xml) + 1;
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -342,7 +342,7 @@ int aim_icq_sendxmlreq(OscarData *od, const char *xml)
byte_stream_putle16(&bs, strlen(xml) + 1);
byte_stream_putraw(&bs, (guint8 *)xml, strlen(xml) + 1);
- flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -380,7 +380,7 @@ int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char
struct tm *tm;
gchar *stripped;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
return -EINVAL;
if (!name || !msg || !alias)
@@ -411,7 +411,7 @@ int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
/* For simplicity, don't bother using a tlvlist */
byte_stream_put16(&bs, 0x0001);
@@ -436,7 +436,7 @@ int aim_icq_sendsms(OscarData *od, const char *name, const char *msg, const char
byte_stream_putstr(&bs, xml);
byte_stream_put8(&bs, 0x00);
- flap_connection_send_snac(od, conn, 0x0015, 0x0002, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -460,7 +460,7 @@ int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, gui
purple_debug_misc("oscar", "aim_icq_getstatusnote: requesting status note for %s.\n", uin);
- if (!od || !(conn = flap_connection_findbygroup(od, 0x0015)))
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICQ)))
{
purple_debug_misc("oscar", "aim_icq_getstatusnote: no connection.\n");
return -EINVAL;
@@ -469,7 +469,7 @@ int aim_icq_getstatusnote(OscarData *od, const char *uin, guint8 *note_hash, gui
bslen = 2 + 4 + 2 + 2 + 2 + 2 + 58 + strlen(uin);
byte_stream_new(&bs, 4 + bslen);
- snacid = aim_cachesnac(od, 0x0015, 0x0002, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
byte_stream_put16(&bs, 0x0001);
byte_stream_put16(&bs, bslen);
@@ -497,7 +497,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, 0x0015, 0x0002, 0x000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -541,7 +541,7 @@ static void aim_icq_freeinfo(struct aim_icq_info *info) {
}
/**
- * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
+ * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
*/
static int
icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
@@ -940,7 +940,7 @@ icq_shutdown(OscarData *od, aim_module_t *mod)
int
icq_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0015;
+ mod->family = SNAC_FAMILY_ICQ;
mod->version = 0x0001;
mod->toolid = 0x0110;
mod->toolversion = 0x047c;
diff --git a/libpurple/protocols/oscar/family_invite.c b/libpurple/protocols/oscar/family_invite.c
index d3c64338ee..bd9cbd25fb 100644
--- a/libpurple/protocols/oscar/family_invite.c
+++ b/libpurple/protocols/oscar/family_invite.c
@@ -41,7 +41,7 @@
int invite_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0006;
+ mod->family = SNAC_FAMILY_INVITE;
mod->version = 0x0001;
mod->toolid = 0x0110;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_locate.c b/libpurple/protocols/oscar/family_locate.c
index 16097675cd..3d7bf0bd37 100644
--- a/libpurple/protocols/oscar/family_locate.c
+++ b/libpurple/protocols/oscar/family_locate.c
@@ -171,7 +171,7 @@ static const struct {
{0x09, 0x46, 0xf0, 0x03, 0x4c, 0x7f, 0x11, 0xd1,
0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
- {OSCAR_CAPABILITY_GENERICUNKNOWN,
+ {OSCAR_CAPABILITY_ICHAT_SCREENSHARE,
{0x09, 0x46, 0xf0, 0x04, 0x4c, 0x7f, 0x11, 0xd1,
0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
@@ -943,7 +943,7 @@ error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame,
return 0;
}
- if ((snac2->family != 0x0002) && (snac2->type != 0x0015)) {
+ if ((snac2->family != SNAC_FAMILY_LOCATE) && (snac2->type != 0x0015)) {
purple_debug_misc("oscar", "faim: locate.c, error(): received response from invalid request! %d\n", snac2->family);
return 0;
}
@@ -1094,12 +1094,12 @@ aim_locate_setprofile(OscarData *od,
byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
- snacid = aim_cachesnac(od, 0x0002, 0x0004, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x0002, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1124,12 +1124,12 @@ aim_locate_setcaps(OscarData *od, guint32 caps)
byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
- snacid = aim_cachesnac(od, 0x0002, 0x0004, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, NULL, 0);
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x0002, 0x0004, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1157,13 +1157,13 @@ aim_locate_getinfo(OscarData *od, const char *sn, guint16 infotype)
byte_stream_new(&bs, 2+1+strlen(sn));
- snacid = aim_cachesnac(od, 0x0002, 0x0005, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, NULL, 0);
byte_stream_put16(&bs, infotype);
byte_stream_put8(&bs, strlen(sn));
byte_stream_putstr(&bs, sn);
- flap_connection_send_snac(od, conn, 0x0002, 0x0005, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0005, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1274,12 +1274,12 @@ int aim_locate_setdirinfo(OscarData *od, const char *first, const char *middle,
byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
- snacid = aim_cachesnac(od, 0x0002, 0x0009, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, NULL, 0);
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x0002, 0x0009, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x0009, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1302,12 +1302,12 @@ int aim_locate_000b(OscarData *od, const char *sn)
byte_stream_new(&bs, 1+strlen(sn));
- snacid = aim_cachesnac(od, 0x0002, 0x000b, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, NULL, 0);
byte_stream_put8(&bs, strlen(sn));
byte_stream_putstr(&bs, sn);
- flap_connection_send_snac(od, conn, 0x0002, 0x000b, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000b, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1347,12 +1347,12 @@ aim_locate_setinterests(OscarData *od, const char *interest1, const char *intere
byte_stream_new(&bs, aim_tlvlist_size(tlvlist));
- snacid = aim_cachesnac(od, 0x0002, 0x000f, 0x0000, NULL, 0);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, NULL, 0);
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- flap_connection_send_snac(od, conn, 0x0002, 0x000f, 0x0000, snacid, &bs);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_LOCATE, 0x000f, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
return 0;
@@ -1385,8 +1385,8 @@ aim_locate_getinfoshort(OscarData *od, const char *sn, guint32 flags)
byte_stream_put8(&bs, strlen(sn));
byte_stream_putstr(&bs, sn);
- snacid = aim_cachesnac(od, 0x0002, 0x0015, 0x0000, sn, strlen(sn)+1);
- flap_connection_send_snac(od, conn, 0x0002, 0x0015, 0x0000, snacid, &bs);
+ 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);
byte_stream_destroy(&bs);
diff --git a/libpurple/protocols/oscar/family_odir.c b/libpurple/protocols/oscar/family_odir.c
index 73122a5f81..a31768a003 100644
--- a/libpurple/protocols/oscar/family_odir.c
+++ b/libpurple/protocols/oscar/family_odir.c
@@ -45,7 +45,7 @@ int aim_odir_email(OscarData *od, const char *region, const char *email)
aim_snacid_t snacid;
GSList *tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region || !email)
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region || !email)
return -EINVAL;
/* Create a TLV chain, write it to the outgoing frame, then free the chain */
@@ -58,8 +58,8 @@ int aim_odir_email(OscarData *od, const char *region, const char *email)
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x000f, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -94,7 +94,7 @@ int aim_odir_name(OscarData *od, const char *region, const char *first, const ch
aim_snacid_t snacid;
GSList *tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region)
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region)
return -EINVAL;
/* Create a TLV chain, write it to the outgoing frame, then free the chain */
@@ -126,8 +126,8 @@ int aim_odir_name(OscarData *od, const char *region, const char *first, const ch
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x000f, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -149,7 +149,7 @@ int aim_odir_interest(OscarData *od, const char *region, const char *interest)
aim_snacid_t snacid;
GSList *tlvlist = NULL;
- if (!od || !(conn = flap_connection_findbygroup(od, 0x000f)) || !region)
+ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ODIR)) || !region)
return -EINVAL;
/* Create a TLV chain, write it to the outgoing frame, then free the chain */
@@ -163,8 +163,8 @@ int aim_odir_interest(OscarData *od, const char *region, const char *interest)
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x000f, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x000f, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_ODIR, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_ODIR, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -252,7 +252,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
odir_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x000f;
+ mod->family = SNAC_FAMILY_ODIR;
mod->version = 0x0001;
mod->toolid = 0x0010;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_oservice.c b/libpurple/protocols/oscar/family_oservice.c
index b2b31f2a2a..4d95bc8cb2 100644
--- a/libpurple/protocols/oscar/family_oservice.c
+++ b/libpurple/protocols/oscar/family_oservice.c
@@ -54,8 +54,8 @@ aim_clientready(OscarData *od, FlapConnection *conn)
}
}
- snacid = aim_cachesnac(od, 0x0001, 0x0002, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -108,7 +108,7 @@ aim_srv_requestnew(OscarData *od, guint16 serviceid)
if(!conn)
return;
- aim_genericreq_s(od, conn, 0x0001, 0x0004, &serviceid);
+ aim_genericreq_s(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, &serviceid);
}
/*
@@ -146,8 +146,8 @@ aim_chat_join(OscarData *od, guint16 exchange, const char *roomname, guint16 ins
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
- flap_connection_send_snac(od, conn, 0x0001, 0x0004, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, &csi, sizeof(csi));
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0004, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -210,7 +210,7 @@ redirect(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *fram
void
aim_srv_reqrates(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n_snacid(od, conn, 0x0001, 0x0006);
+ aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x0006);
}
/*
@@ -389,8 +389,8 @@ aim_srv_rates_addparam(OscarData *od, FlapConnection *conn)
byte_stream_put16(&bs, rateclass->classid);
}
- snacid = aim_cachesnac(od, 0x0001, 0x0008, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x0008, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0008, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -412,8 +412,8 @@ aim_srv_rates_delparam(OscarData *od, FlapConnection *conn)
byte_stream_put16(&bs, rateclass->classid);
}
- snacid = aim_cachesnac(od, 0x0001, 0x0009, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x0009, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0009, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0009, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -503,8 +503,8 @@ aim_srv_sendpauseack(OscarData *od, FlapConnection *conn)
for (cur = conn->groups; cur != NULL; cur = cur->next)
byte_stream_put16(&bs, GPOINTER_TO_UINT(cur->data));
- snacid = aim_cachesnac(od, 0x0001, 0x000c, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x000c, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x000c, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x000c, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -526,7 +526,7 @@ serverresume(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *
void
aim_srv_reqpersonalinfo(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n_snacid(od, conn, 0x0001, 0x000e);
+ aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_OSERVICE, 0x000e);
}
/* Subtype 0x000f - Self User Info */
@@ -589,7 +589,7 @@ aim_srv_setidle(OscarData *od, guint32 idletime)
if(!conn)
return;
- aim_genericreq_l(od, conn, 0x0001, 0x0011, &idletime);
+ aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0011, &idletime);
}
/*
@@ -698,7 +698,7 @@ motd(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, a
void
aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32 flags)
{
- aim_genericreq_l(od, conn, 0x0001, 0x0014, &flags);
+ aim_genericreq_l(od, conn, SNAC_FAMILY_OSERVICE, 0x0014, &flags);
}
/*
@@ -713,7 +713,7 @@ aim_srv_setprivacyflags(OscarData *od, FlapConnection *conn, guint32 flags)
void
aim_srv_nop(OscarData *od, FlapConnection *conn)
{
- aim_genericreq_n(od, conn, 0x0001, 0x0016);
+ aim_genericreq_n(od, conn, SNAC_FAMILY_OSERVICE, 0x0016);
}
/*
@@ -753,8 +753,8 @@ aim_srv_setversions(OscarData *od, FlapConnection *conn)
}
}
- snacid = aim_cachesnac(od, 0x0001, 0x0017, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x0017, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0017, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
}
@@ -861,8 +861,8 @@ aim_srv_setextrainfo(OscarData *od,
aim_tlvlist_write(&bs, &tlvlist);
aim_tlvlist_free(tlvlist);
- snacid = aim_cachesnac(od, 0x0001, 0x001e, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x001e, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x001e, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -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);
@@ -1012,8 +1006,8 @@ aim_sendmemblock(OscarData *od, FlapConnection *conn, guint32 offset, guint32 le
}
- snacid = aim_cachesnac(od, 0x0001, 0x0020, 0x0000, NULL, 0);
- flap_connection_send_snac(od, conn, 0x0001, 0x0020, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, NULL, 0);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_OSERVICE, 0x0020, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -1100,7 +1094,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int service_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0001;
+ mod->family = SNAC_FAMILY_OSERVICE;
mod->version = 0x0003;
mod->toolid = 0x0110;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/family_popup.c b/libpurple/protocols/oscar/family_popup.c
index 4a89ba5b2b..608ac595ec 100644
--- a/libpurple/protocols/oscar/family_popup.c
+++ b/libpurple/protocols/oscar/family_popup.c
@@ -72,7 +72,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
popups_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x0008;
+ mod->family = SNAC_FAMILY_POPUP;
mod->version = 0x0001;
mod->toolid = 0x0104;
mod->toolversion = 0x0001;
diff --git a/libpurple/protocols/oscar/family_stats.c b/libpurple/protocols/oscar/family_stats.c
index 1ebdd0965f..1eb1187838 100644
--- a/libpurple/protocols/oscar/family_stats.c
+++ b/libpurple/protocols/oscar/family_stats.c
@@ -52,7 +52,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
stats_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x000b;
+ mod->family = SNAC_FAMILY_STATS;
mod->version = 0x0001;
mod->toolid = 0x0104;
mod->toolversion = 0x0001;
diff --git a/libpurple/protocols/oscar/family_translate.c b/libpurple/protocols/oscar/family_translate.c
index 5ef7b1a438..005528f116 100644
--- a/libpurple/protocols/oscar/family_translate.c
+++ b/libpurple/protocols/oscar/family_translate.c
@@ -34,7 +34,7 @@
int translate_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x000c;
+ mod->family = SNAC_FAMILY_TRANSLATE;
mod->version = 0x0001;
mod->toolid = 0x0104;
mod->toolversion = 0x0001;
diff --git a/libpurple/protocols/oscar/family_userlookup.c b/libpurple/protocols/oscar/family_userlookup.c
index adac494b92..9f766c142d 100644
--- a/libpurple/protocols/oscar/family_userlookup.c
+++ b/libpurple/protocols/oscar/family_userlookup.c
@@ -74,8 +74,8 @@ int aim_search_address(OscarData *od, const char *address)
byte_stream_putstr(&bs, address);
- snacid = aim_cachesnac(od, 0x000a, 0x0002, 0x0000, address, strlen(address)+1);
- flap_connection_send_snac(od, conn, 0x000a, 0x0002, 0x0000, snacid, &bs);
+ snacid = aim_cachesnac(od, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, address, strlen(address)+1);
+ flap_connection_send_snac(od, conn, SNAC_FAMILY_USERLOOKUP, 0x0002, 0x0000, snacid, &bs);
byte_stream_destroy(&bs);
@@ -145,7 +145,7 @@ snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *f
int
search_modfirst(OscarData *od, aim_module_t *mod)
{
- mod->family = 0x000a;
+ mod->family = SNAC_FAMILY_USERLOOKUP;
mod->version = 0x0001;
mod->toolid = 0x0110;
mod->toolversion = 0x0629;
diff --git a/libpurple/protocols/oscar/oscar.c b/libpurple/protocols/oscar/oscar.c
index f48aaeb27b..c8129383c9 100644
--- a/libpurple/protocols/oscar/oscar.c
+++ b/libpurple/protocols/oscar/oscar.c
@@ -131,8 +131,8 @@ static const char * const msgerrreason[] = {
N_("Busted SNAC payload"),
N_("Insufficient rights"),
N_("In local permit/deny"),
- N_("Too evil (sender)"),
- N_("Too evil (receiver)"),
+ N_("Warning level too high (sender)"),
+ N_("Warning level too high (receiver)"),
N_("User temporarily unavailable"),
N_("No match"),
N_("List overflow"),
@@ -707,6 +707,9 @@ static gchar *oscar_caps_to_string(OscarCapability caps)
case OSCAR_CAPABILITY_CAMERA:
tmp = _("Camera");
break;
+ case OSCAR_CAPABILITY_ICHAT_SCREENSHARE:
+ tmp = _("Screen Sharing");
+ break;
default:
tmp = NULL;
break;
@@ -753,7 +756,7 @@ oscar_user_info_convert_and_add_pair(PurpleAccount *account, PurpleNotifyUserInf
const char *name, const char *value)
{
gchar *utf8;
-
+
if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
purple_notify_user_info_add_pair(user_info, name, utf8);
g_free(utf8);
@@ -765,14 +768,148 @@ oscar_user_info_convert_and_add(PurpleAccount *account, PurpleNotifyUserInfo *us
const char *name, const char *value)
{
gchar *utf8;
-
+
if (value && value[0] && (utf8 = oscar_utf8_try_convert(account, value))) {
purple_notify_user_info_add_pair(user_info, name, utf8);
g_free(utf8);
}
}
-static void oscar_string_append_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
+/**
+ * @brief Append the status information to a user_info struct
+ *
+ * The returned information is HTML-ready, appropriately escaped, as all information in a user_info struct should be HTML.
+ *
+ * @param gc The PurpleConnection
+ * @param user_info A PurpleNotifyUserInfo object to which status information will be added
+ * @param b The PurpleBuddy whose status is desired. This or the aim_userinfo_t (or both) must be passed to oscar_user_info_append_status().
+ * @param userinfo The aim_userinfo_t of the buddy whose status is desired. This or the PurpleBuddy (or both) must be passed to oscar_user_info_append_status().
+ * @param strip_html_tags If strip_html_tags is TRUE, tags embedded in the status message will be stripped, returning a non-formatted string. The string will still be HTML escaped.
+ */
+static void oscar_user_info_append_status(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo, gboolean strip_html_tags)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ OscarData *od;
+ PurplePresence *presence = NULL;
+ PurpleStatus *status = NULL;
+ gchar *message = NULL, *itmsurl = NULL, *tmp;
+ gboolean is_away;
+
+ od = gc->proto_data;
+
+ if (userinfo == NULL)
+ userinfo = aim_locate_finduserinfo(od, b->name);
+
+ if ((user_info == NULL) || ((b == NULL) && (userinfo == NULL)))
+ return;
+
+ if (b == NULL)
+ b = purple_find_buddy(purple_connection_get_account(gc), userinfo->sn);
+
+ if (b) {
+ presence = purple_buddy_get_presence(b);
+ status = purple_presence_get_active_status(presence);
+
+ message = g_strdup(purple_status_get_attr_string(status, "message"));
+ itmsurl = g_strdup(purple_status_get_attr_string(status, "itmsurl"));
+
+ } else {
+ /* This is an OSCAR contact for whom we don't have a PurpleBuddy but do have information. */
+ if ((userinfo->flags & AIM_FLAG_AWAY)) {
+ /* Away message? */
+ if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
+ tmp = oscar_encoding_extract(userinfo->away_encoding);
+ message = oscar_encoding_to_utf8(account, tmp, userinfo->away,
+ userinfo->away_len);
+ g_free(tmp);
+ }
+ } else {
+ /* Available message? */
+ if ((userinfo->status != NULL) && userinfo->status[0] != '\0') {
+ message = oscar_encoding_to_utf8(account, userinfo->status_encoding,
+ userinfo->status, userinfo->status_len);
+ }
+#if defined (_WIN32) || defined (__APPLE__)
+ if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0'))
+ itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
+ userinfo->itmsurl, userinfo->itmsurl_len);
+#endif
+ }
+ }
+
+ is_away = ((status && !purple_status_is_available(status)) ||
+ (userinfo && (userinfo->flags & AIM_FLAG_AWAY)));
+
+ if (strip_html_tags) {
+ /* Away messges are HTML, but available messages were originally plain text.
+ * We therefore need to strip away messages but not available messages if we're asked to remove HTML tags.
+ */
+ if (is_away && message) {
+ gchar *tmp2;
+ tmp = purple_markup_strip_html(message);
+ g_free(message);
+ tmp2 = g_markup_escape_text(tmp, -1);
+ g_free(tmp);
+ message = tmp2;
+ }
+
+ } else {
+ if (itmsurl) {
+ tmp = g_strdup_printf("<a href=\"%s\">%s</a>",
+ itmsurl, message);
+ g_free(itmsurl);
+ g_free(message);
+ message = tmp;
+ }
+ }
+
+ if (is_away && message) {
+ tmp = purple_str_sub_away_formatters(message, purple_account_get_username(account));
+ g_free(message);
+ message = tmp;
+ }
+
+ if (b) {
+ if (purple_presence_is_online(presence)) {
+ if (aim_snvalid_icq(b->name) || is_away || !message || !(*message)) {
+ /* Append the status name for online ICQ statuses, away AIM statuses, and for all buddies with no message.
+ * If the status name and the message are the same, only show one. */
+ const char *status_name = purple_status_get_name(status);
+ if (status_name && message && !strcmp(status_name, message))
+ status_name = NULL;
+
+ tmp = g_strdup_printf("%s%s%s",
+ status_name,
+ ((status_name && message) && *message) ? ": " : "",
+ (message && *message) ? message : "");
+ g_free(message);
+ message = tmp;
+ }
+
+ } else {
+ if (aim_ssi_waitingforauth(od->ssi.local,
+ aim_ssi_itemlist_findparentname(od->ssi.local, b->name),
+ b->name)) {
+ /* Note if an offline buddy is not authorized */
+ tmp = g_strdup_printf("%s%s%s",
+ _("Not Authorized"),
+ (message && *message) ? ": " : "",
+ (message && *message) ? message : "");
+ g_free(message);
+ message = tmp;
+ } else {
+ g_free(message);
+ message = g_strdup(_("Offline"));
+ }
+ }
+
+ }
+
+ purple_notify_user_info_add_pair(user_info, _("Status"), message);
+ g_free(message);
+}
+
+static void oscar_user_info_append_extra_info(PurpleConnection *gc, PurpleNotifyUserInfo *user_info, PurpleBuddy *b, aim_userinfo_t *userinfo)
{
OscarData *od;
PurpleAccount *account;
@@ -803,21 +940,6 @@ static void oscar_string_append_info(PurpleConnection *gc, PurpleNotifyUserInfo
if (userinfo != NULL)
bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->sn));
- if (b != NULL) {
- if (purple_presence_is_online(presence)) {
- if (aim_snvalid_icq(b->name)) {
- PurpleStatus *status = purple_presence_get_active_status(presence);
- oscar_user_info_add_pair(user_info, _("Status"), purple_status_get_name(status));
- }
- } else {
- tmp = aim_ssi_itemlist_findparentname(od->ssi.local, b->name);
- if (aim_ssi_waitingforauth(od->ssi.local, tmp, b->name))
- oscar_user_info_add_pair(user_info, _("Status"), _("Not Authorized"));
- else
- oscar_user_info_add_pair(user_info, _("Status"), _("Offline"));
- }
- }
-
if ((bi != NULL) && (bi->ipaddr != 0)) {
tmp = g_strdup_printf("%hhu.%hhu.%hhu.%hhu",
(bi->ipaddr & 0xff000000) >> 24,
@@ -828,7 +950,6 @@ static void oscar_string_append_info(PurpleConnection *gc, PurpleNotifyUserInfo
g_free(tmp);
}
-
if ((userinfo != NULL) && (userinfo->warnlevel != 0)) {
tmp = g_strdup_printf("%d", (int)(userinfo->warnlevel/10.0 + .5));
oscar_user_info_add_pair(user_info, _("Warning Level"), tmp);
@@ -976,8 +1097,8 @@ connection_established_cb(gpointer data, gint source, const gchar *error_message
if (source < 0)
{
- purple_debug_error("oscar", "unable to connect FLAP server "
- "of type 0x%04hx\n", conn->type);
+ purple_debug_error("oscar", "unable to connect to FLAP "
+ "server of type 0x%04hx\n", conn->type);
if (conn->type == SNAC_FAMILY_AUTH)
{
gchar *msg;
@@ -1693,7 +1814,8 @@ purple_parse_login(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
aim_send_login(od, conn, purple_account_get_username(account),
purple_connection_get_password(gc), truncate_pass,
- od->icq ? &icqinfo : &aiminfo, key);
+ od->icq ? &icqinfo : &aiminfo, key,
+ /* allow multple logins? */ purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
ck[2] = 0x6c;
@@ -1829,6 +1951,7 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
{
char *message = NULL;
char *itmsurl = NULL;
+ char *tmp;
if (info->status != NULL && info->status[0] != '\0')
/* Grab the available message */
@@ -1840,15 +1963,31 @@ static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame
itmsurl = oscar_encoding_to_utf8(account, info->itmsurl_encoding,
info->itmsurl, info->itmsurl_len);
+ tmp = (message ? g_markup_escape_text(message, -1) : NULL);
+
+ if (message == NULL && itmsurl != NULL)
+ message = "";
+
purple_prpl_got_user_status(account, info->sn, status_id,
- "message", message, "itmsurl", itmsurl, NULL);
+ "message", tmp, "itmsurl", itmsurl, NULL);
+ g_free(tmp);
g_free(message);
g_free(itmsurl);
}
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 */
@@ -2636,7 +2775,7 @@ static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *f
switch(reason) {
case 0: /* Invalid (0) */
buf = g_strdup_printf(
- dngettext(PACKAGE,
+ dngettext(PACKAGE,
"You missed %hu message from %s because it was invalid.",
"You missed %hu messages from %s because they were invalid.",
nummissed),
@@ -2645,7 +2784,7 @@ static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *f
break;
case 1: /* Message too large */
buf = g_strdup_printf(
- dngettext(PACKAGE,
+ dngettext(PACKAGE,
"You missed %hu message from %s because it was too large.",
"You missed %hu messages from %s because they were too large.",
nummissed),
@@ -2654,7 +2793,7 @@ static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *f
break;
case 2: /* Rate exceeded */
buf = g_strdup_printf(
- dngettext(PACKAGE,
+ dngettext(PACKAGE,
"You missed %hu message from %s because the rate limit has been exceeded.",
"You missed %hu messages from %s because the rate limit has been exceeded.",
nummissed),
@@ -2663,25 +2802,25 @@ static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *f
break;
case 3: /* Evil Sender */
buf = g_strdup_printf(
- dngettext(PACKAGE,
- "You missed %hu message from %s because he/she was too evil.",
- "You missed %hu messages from %s because he/she was too evil.",
+ dngettext(PACKAGE,
+ "You missed %hu message from %s because his/her warning level is too high.",
+ "You missed %hu messages from %s because his/her warning level is too high.",
nummissed),
nummissed,
userinfo->sn);
break;
case 4: /* Evil Receiver */
buf = g_strdup_printf(
- dngettext(PACKAGE,
- "You missed %hu message from %s because you are too evil.",
- "You missed %hu messages from %s because you are too evil.",
+ dngettext(PACKAGE,
+ "You missed %hu message from %s because your warning level is too high.",
+ "You missed %hu messages from %s because your warning level is too high.",
nummissed),
nummissed,
userinfo->sn);
break;
default:
buf = g_strdup_printf(
- dngettext(PACKAGE,
+ dngettext(PACKAGE,
"You missed %hu message from %s for an unknown reason.",
"You missed %hu messages from %s for an unknown reason.",
nummissed),
@@ -2737,9 +2876,9 @@ static int purple_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason,
/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
statusmsg = oscar_icqstatus(state);
splitmsg = g_strsplit(msg, "\r\n", 0);
-
+
user_info = purple_notify_user_info_new();
-
+
purple_notify_user_info_add_pair(user_info, _("UIN"), who);
purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
purple_notify_user_info_add_section_break(user_info);
@@ -2920,7 +3059,7 @@ static int purple_parse_userinfo(OscarData *od, FlapConnection *conn, FlapFrame
PurpleConnection *gc = od->gc;
PurpleAccount *account = purple_connection_get_account(gc);
PurpleNotifyUserInfo *user_info;
- gchar *tmp = NULL, *info_utf8 = NULL, *away_utf8 = NULL;
+ gchar *tmp = NULL, *info_utf8 = NULL;
va_list ap;
aim_userinfo_t *userinfo;
@@ -2929,9 +3068,19 @@ static int purple_parse_userinfo(OscarData *od, FlapConnection *conn, FlapFrame
va_end(ap);
user_info = purple_notify_user_info_new();
- purple_notify_user_info_add_pair(user_info, _("Username"), userinfo->sn);
- if (userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) {
+ oscar_user_info_append_status(gc, user_info, /* PurpleBuddy */ NULL, userinfo, /* strip_html_tags */ FALSE);
+
+ if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) {
+ tmp = purple_str_seconds_to_string(userinfo->idletime*60);
+ oscar_user_info_add_pair(user_info, _("Idle"), tmp);
+ g_free(tmp);
+ }
+
+ oscar_user_info_append_extra_info(gc, user_info, NULL, userinfo);
+
+ if ((userinfo->present & AIM_USERINFO_PRESENT_ONLINESINCE) && !aim_snvalid_sms(userinfo->sn)) {
+ /* An SMS contact is always online; its Online Since valid is not useful */
time_t t = userinfo->onlinesince;
oscar_user_info_add_pair(user_info, _("Online Since"), purple_date_format_full(localtime(&t)));
}
@@ -2947,50 +3096,6 @@ static int purple_parse_userinfo(OscarData *od, FlapConnection *conn, FlapFrame
g_free(tmp);
}
- if (userinfo->present & AIM_USERINFO_PRESENT_IDLE) {
- tmp = purple_str_seconds_to_string(userinfo->idletime*60);
- oscar_user_info_add_pair(user_info, _("Idle"), tmp);
- g_free(tmp);
- }
-
- oscar_string_append_info(gc, user_info, NULL, userinfo);
-
- /* Available message */
- if ((userinfo->status != NULL) && !(userinfo->flags & AIM_FLAG_AWAY))
- {
- if (userinfo->status[0] != '\0')
- tmp = oscar_encoding_to_utf8(account, userinfo->status_encoding,
- userinfo->status, userinfo->status_len);
-#if defined (_WIN32) || defined (__APPLE__)
- if (userinfo->itmsurl && (userinfo->itmsurl[0] != '\0')) {
- gchar *itmsurl, *tmp2;
- itmsurl = oscar_encoding_to_utf8(account, userinfo->itmsurl_encoding,
- userinfo->itmsurl, userinfo->itmsurl_len);
- tmp2 = g_strdup_printf("<a href=\"%s\">%s</a>",
- itmsurl, tmp);
- g_free(tmp);
- tmp = tmp2;
- g_free(itmsurl);
- }
-#endif
- oscar_user_info_add_pair(user_info, _("Available Message"), tmp);
- g_free(tmp);
- }
-
- /* Away message */
- if ((userinfo->flags & AIM_FLAG_AWAY) && (userinfo->away_len > 0) && (userinfo->away != NULL) && (userinfo->away_encoding != NULL)) {
- tmp = oscar_encoding_extract(userinfo->away_encoding);
- away_utf8 = oscar_encoding_to_utf8(account, tmp, userinfo->away,
- userinfo->away_len);
- g_free(tmp);
- if (away_utf8 != NULL) {
- tmp = purple_str_sub_away_formatters(away_utf8, purple_account_get_username(account));
- oscar_user_info_add_pair(user_info, _("Away Message"), tmp);
- g_free(tmp);
- g_free(away_utf8);
- }
- }
-
/* Info */
if ((userinfo->info_len > 0) && (userinfo->info != NULL) && (userinfo->info_encoding != NULL)) {
tmp = oscar_encoding_extract(userinfo->info_encoding);
@@ -3429,8 +3534,8 @@ static int purple_parse_ratechange(OscarData *od, FlapConnection *conn, FlapFram
if (code == AIM_RATE_CODE_LIMIT)
{
purple_debug_warning("oscar", _("The last action you attempted could not be "
- "performed because you are over the rate limit. "
- "Please wait 10 seconds and try again."));
+ "performed because you are over the rate limit. "
+ "Please wait 10 seconds and try again."));
}
return 1;
@@ -3687,12 +3792,9 @@ static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
PurpleConnection *gc;
PurpleAccount *account;
PurpleBuddy *buddy;
- PurplePresence *presence;
- PurpleStatus *status;
struct buddyinfo *bi;
gchar who[16];
PurpleNotifyUserInfo *user_info;
- GString *tmp;
gchar *utf8;
gchar *buf;
const gchar *alias;
@@ -3710,7 +3812,7 @@ static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
return 0;
user_info = purple_notify_user_info_new();
-
+
g_snprintf(who, sizeof(who), "%u", info->uin);
buddy = purple_find_buddy(purple_connection_get_account(gc), who);
if (buddy != NULL)
@@ -3782,24 +3884,8 @@ static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
g_free(utf8);
}
- if (buddy != NULL) {
- const gchar *message;
- gchar *utf8, *tmp;
-
- presence = purple_buddy_get_presence(buddy);
- status = purple_presence_get_active_status(presence);
- message = purple_status_get_attr_string(status, "message");
-
- utf8 = message && message[0] ? oscar_utf8_try_convert(account, message) : NULL;
- tmp = g_strdup_printf("%s%s%s",
- purple_status_get_name(status),
- utf8 && *utf8 ? ": " : "",
- utf8 && *utf8 ? utf8 : "");
- g_free(utf8);
-
- purple_notify_user_info_add_pair(user_info, _("Status"), tmp);
- g_free(tmp);
- }
+ if (buddy != NULL)
+ oscar_user_info_append_status(gc, user_info, buddy, /* aim_userinfo_t */ NULL, /* strip_html_tags */ FALSE);
oscar_user_info_convert_and_add(account, user_info, _("Additional Information"), info->info);
purple_notify_user_info_add_section_break(user_info);
@@ -3814,7 +3900,7 @@ static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
}
if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
purple_notify_user_info_add_section_header(user_info, _("Work Address"));
-
+
oscar_user_info_convert_and_add(account, user_info, _("Address"), info->workaddr);
oscar_user_info_convert_and_add(account, user_info, _("City"), info->workcity);
oscar_user_info_convert_and_add(account, user_info, _("State"), info->workstate);
@@ -3822,11 +3908,11 @@ static int purple_icqinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ..
}
if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
purple_notify_user_info_add_section_header(user_info, _("Work Information"));
-
+
oscar_user_info_convert_and_add(account, user_info, _("Company"), info->workcompany);
oscar_user_info_convert_and_add(account, user_info, _("Division"), info->workdivision);
oscar_user_info_convert_and_add(account, user_info, _("Position"), info->workposition);
-
+
if (info->workwebpage && info->workwebpage[0] && (utf8 = oscar_utf8_try_convert(gc->account, info->workwebpage))) {
char *webpage = g_strdup_printf("<a href=\"%s\">%s</a>", utf8, utf8);
purple_notify_user_info_add_pair(user_info, _("Web Page"), webpage);
@@ -4333,14 +4419,14 @@ oscar_send_im(PurpleConnection *gc, const char *name, const char *message, Purpl
/* If the length was too long, try stripping the HTML and then running it back through
* purple_strdup_withhtml() and the encoding process. The result may be shorter. */
g_free((char *)args.msg);
-
+
tmp2 = purple_markup_strip_html(tmp1);
g_free(tmp1);
/* re-escape the entities */
tmp1 = g_markup_escape_text(tmp2, -1);
g_free(tmp2);
-
+
tmp2 = purple_strdup_withhtml(tmp1);
g_free(tmp1);
tmp1 = tmp2;
@@ -5570,7 +5656,7 @@ const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b)
return "aim";
}
-const char* oscar_list_emblem(PurpleBuddy *b)
+const char *oscar_list_emblem(PurpleBuddy *b)
{
PurpleConnection *gc = NULL;
OscarData *od = NULL;
@@ -5600,7 +5686,7 @@ const char* oscar_list_emblem(PurpleBuddy *b)
return "not-authorized";
}
}
-
+
if (userinfo != NULL ) {
if (userinfo->flags & AIM_FLAG_ADMINISTRATOR)
return "admin";
@@ -5616,54 +5702,23 @@ const char* oscar_list_emblem(PurpleBuddy *b)
return NULL;
}
-void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) {
- PurpleConnection *gc = b->account->gc;
- OscarData *od = gc->proto_data;
- aim_userinfo_t *userinfo = aim_locate_finduserinfo(od, b->name);
+void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
+{
+ PurpleConnection *gc;
+ OscarData *od;
+ aim_userinfo_t *userinfo;
- if (PURPLE_BUDDY_IS_ONLINE(b)) {
- PurplePresence *presence;
- PurpleStatus *status;
- const char *message;
+ if (!PURPLE_BUDDY_IS_ONLINE(b))
+ return;
- if (full)
- oscar_string_append_info(gc, user_info, b, userinfo);
+ gc = b->account->gc;
+ od = gc->proto_data;
+ userinfo = aim_locate_finduserinfo(od, b->name);
- presence = purple_buddy_get_presence(b);
- status = purple_presence_get_active_status(presence);
- message = purple_status_get_attr_string(status, "message");
+ oscar_user_info_append_status(gc, user_info, b, userinfo, /* strip_html_tags */ TRUE);
- if (purple_status_is_available(status))
- {
- if (message != NULL)
- {
- /* Available status messages are plain text */
- gchar *tmp;
- tmp = g_markup_escape_text(message, -1);
- purple_notify_user_info_add_pair(user_info, _("Message"), tmp);
- g_free(tmp);
- }
- }
- else
- {
- if (message != NULL)
- {
- /* Away messages are HTML */
- gchar *tmp1, *tmp2;
- tmp2 = purple_markup_strip_html(message);
- tmp1 = g_markup_escape_text(tmp2, -1);
- g_free(tmp2);
- tmp2 = purple_str_sub_away_formatters(tmp1, purple_account_get_username(purple_connection_get_account(gc)));
- g_free(tmp1);
- purple_notify_user_info_add_pair(user_info, _("Away Message"), tmp2);
- g_free(tmp2);
- }
- else
- {
- purple_notify_user_info_add_pair(user_info, _("Away Message"), _("<i>(retrieving)</i>"));
- }
- }
- }
+ if (full)
+ oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
}
char *oscar_status_text(PurpleBuddy *b)
@@ -5698,7 +5753,7 @@ char *oscar_status_text(PurpleBuddy *b)
message = purple_status_get_attr_string(status, "message");
if (message != NULL)
{
- ret = g_markup_escape_text(message, -1);
+ ret = g_strdup(message);
purple_util_chrreplace(ret, '\n', ' ');
}
}
@@ -6369,15 +6424,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);
@@ -6731,6 +6783,10 @@ void oscar_init(PurplePluginProtocolInfo *prpl_info)
OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+ option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
+ OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS);
+ prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
+
if (init)
return;
init = TRUE;
diff --git a/libpurple/protocols/oscar/oscar.h b/libpurple/protocols/oscar/oscar.h
index 176f7bac65..e26100e4d9 100644
--- a/libpurple/protocols/oscar/oscar.h
+++ b/libpurple/protocols/oscar/oscar.h
@@ -353,7 +353,8 @@ typedef enum
OSCAR_CAPABILITY_ICHATAV = 0x02000000,
OSCAR_CAPABILITY_LIVEVIDEO = 0x04000000,
OSCAR_CAPABILITY_CAMERA = 0x08000000,
- OSCAR_CAPABILITY_LAST = 0x10000000
+ OSCAR_CAPABILITY_ICHAT_SCREENSHARE = 0x10000000,
+ OSCAR_CAPABILITY_LAST = 0x20000000
} OscarCapability;
/*
@@ -594,7 +595,7 @@ struct aim_redirect_data
void aim_clientready(OscarData *od, FlapConnection *conn);
int aim_request_login(OscarData *od, FlapConnection *conn, const char *sn);
-int aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key);
+int aim_send_login(OscarData *od, FlapConnection *conn, const char *sn, const char *password, gboolean truncate_pass, ClientInfo *ci, const char *key, gboolean allow_multiple_logins);
/* 0x000b */ int aim_auth_securid_send(OscarData *od, const char *securid);
void aim_cleansnacs(OscarData *, int maxage);
diff --git a/libpurple/protocols/oscar/oscarcommon.h b/libpurple/protocols/oscar/oscarcommon.h
index fcf48e1d13..564daef492 100644
--- a/libpurple/protocols/oscar/oscarcommon.h
+++ b/libpurple/protocols/oscar/oscarcommon.h
@@ -41,6 +41,7 @@
#define OSCAR_DEFAULT_HIDE_IP TRUE
#define OSCAR_DEFAULT_WEB_AWARE FALSE
#define OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY FALSE
+#define OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS TRUE
#ifdef _WIN32
const char *oscar_get_locale_charset(void);
diff --git a/libpurple/protocols/qq/ChangeLog b/libpurple/protocols/qq/ChangeLog
new file mode 100644
index 0000000000..29595f3384
--- /dev/null
+++ b/libpurple/protocols/qq/ChangeLog
@@ -0,0 +1,49 @@
+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..7e310589bb 100644
--- a/libpurple/protocols/qq/Makefile.am
+++ b/libpurple/protocols/qq/Makefile.am
@@ -52,20 +52,14 @@ QQSOURCES = \
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..e3601114ea 100644
--- a/libpurple/protocols/qq/Makefile.mingw
+++ b/libpurple/protocols/qq/Makefile.mingw
@@ -63,13 +63,10 @@ C_SRC = \
login_logout.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..8363c4965c 100644
--- a/libpurple/protocols/qq/buddy_info.c
+++ b/libpurple/protocols/qq/buddy_info.c
@@ -34,7 +34,7 @@
#include "crypt.h"
#include "header_info.h"
#include "keep_alive.h"
-#include "send_core.h"
+#include "qq_network.h"
#define QQ_PRIMARY_INFORMATION _("Primary Information")
#define QQ_ADDITIONAL_INFORMATION _("Additional Information")
@@ -94,6 +94,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 +177,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,14 +201,14 @@ 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 *
+ static PurpleNotifyUserInfo *
info_to_notify_user_info(const contact_info *info)
{
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
@@ -209,25 +249,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 +283,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 +311,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 +561,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 +677,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 +735,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 +764,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 +806,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,7 +821,7 @@ 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;
@@ -728,7 +884,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,39 +933,41 @@ 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;
+ * in asking for anyone else */
+ size = 4 * g_list_length(qd->buddies) + 1;
buf = g_new0(guint8, size);
- tmp = buf + 1;
+ bytes += 1;
- while (node != NULL) {
- guint32 tmp4;
+ while (NULL != node) {
q_bud = (qq_buddy *) node->data;
- if (q_bud != NULL) {
- tmp4 = g_htonl(q_bud->uid);
- memcpy(tmp, &tmp4, 4);
- tmp += 4;
+ if (NULL != q_bud) {
+ bytes += qq_put32(buf + bytes, q_bud->uid);
}
node = node->next;
}
- qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size);
+ qq_send_cmd(qd, QQ_CMD_GET_LEVEL, buf, size);
g_free(buf);
}
}
@@ -822,10 +980,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 +994,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 packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %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,7 +1026,7 @@ 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);
}
diff --git a/libpurple/protocols/qq/buddy_info.h b/libpurple/protocols/qq/buddy_info.h
index 7e121f5d1f..63be1da164 100644
--- a/libpurple/protocols/qq/buddy_info.h
+++ b/libpurple/protocols/qq/buddy_info.h
@@ -44,47 +44,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);
diff --git a/libpurple/protocols/qq/buddy_list.c b/libpurple/protocols/qq/buddy_list.c
index 7c64455d79..de6b4a5010 100644
--- a/libpurple/protocols/qq/buddy_list.c
+++ b/libpurple/protocols/qq/buddy_list.c
@@ -38,13 +38,12 @@
#include "crypt.h"
#include "header_info.h"
#include "keep_alive.h"
-#include "send_core.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 */
@@ -64,25 +63,25 @@ typedef struct _qq_friends_online_entry {
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_FRIENDS_ONLINE, raw_data, 5);
qd->last_get_online = time(NULL);
}
@@ -90,42 +89,38 @@ 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_FRIENDS_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)
@@ -151,8 +146,8 @@ static void _qq_buddies_online_reply_dump_unclear(qq_friends_online_entry *fe)
void 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;
+ guint8 *data, position;
PurpleBuddy *b;
qq_buddy *q_bud;
qq_friends_online_entry *fe;
@@ -162,96 +157,100 @@ void qq_process_get_buddies_online_reply(guint8 *buf, gint buf_len, PurpleConnec
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);
- }
+ if (!qq_decrypt(buf, buf_len, qd->session_key, data, &len)) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+ return;
+ }
+
+ qq_show_packet("Get buddies online reply packet", data, len);
+
+ bytes = 0;
+ bytes += qq_get8(&position, data + bytes);
+
+ fe = g_newa(qq_friends_online_entry, 1);
+ fe->s = g_newa(qq_buddy_status, 1);
+
+ while (bytes < len) {
+ /* set flag */
+ bytes_buddy = bytes;
+ /* based on one online buddy entry */
+ /* ATTTENTION! NEWED in the sub function, but FREED here */
+ /* 000-030 qq_buddy_status */
+ bytes += qq_buddy_status_read(fe->s, data + bytes);
+ /* 031-032: unknown4 */
+ bytes += qq_get16(&fe->unknown1, data + bytes);
+ /* 033-033: flag1 */
+ bytes += qq_get8(&fe->flag1, data + bytes);
+ /* 034-034: comm_flag */
+ bytes += qq_get8(&fe->comm_flag, data + bytes);
+ /* 035-036: */
+ bytes += qq_get16(&fe->unknown2, data + bytes);
+ /* 037-037: */
+ bytes += qq_get8(&fe->ending, data + bytes); /* 0x00 */
+
+ if (fe->s->uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "uid=0 or entry complete len(%d) != %d",
+ (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
g_free(fe->s->ip);
g_free(fe->s->unknown_key);
- }
-
- if(cursor > (data + len)) {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ",
- "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
- }
+ continue;
+ } /* check if it is a valid entry */
- if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
- purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+ if (QQ_DEBUG) {
+ _qq_buddies_online_reply_dump_unclear(fe);
+ }
- qq_send_packet_get_buddies_online(gc, position);
+ /* 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 {
- qq_send_packet_get_buddies_levels(gc);
- qq_refresh_all_buddy_status(gc);
+ 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(bytes > len) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+ }
+
+ if (position != QQ_FRIENDS_ONLINE_POSITION_END) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Has more online buddies, position from %d\n", position);
+
+ qq_send_packet_get_buddies_online(gc, position);
} else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies online");
+ qq_send_packet_get_buddies_levels(gc);
+ qq_refresh_all_buddy_status(gc);
}
}
+
/* process reply for get_buddies_list */
void 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, i;
+ gint bytes, buddy_bytes;
guint16 position, unknown;
- guint8 *data, *cursor, pascal_len;
+ guint8 *data, pascal_len;
gchar *name;
PurpleBuddy *b;
@@ -260,81 +259,84 @@ void qq_process_get_buddies_list_reply(guint8 *buf, gint buf_len, PurpleConnecti
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;
+ }
+ bytes = 0;
+ bytes += qq_get16(&position, data + bytes);
+ /* the following data is buddy list in this packet */
+ i = 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);
+ /* flag1: (0-7)
+ * bit1 => qq show
+ * comm_flag: (0-7)
+ * bit1 => member
+ * bit4 => TCP mode
+ * bit5 => open mobile QQ
+ * bit6 => bind to mobile
+ * bit7 => whether having a video
+ */
+ bytes += qq_get8(&q_bud->flag1, data + bytes);
+ bytes += qq_get8(&q_bud->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 {
+ i++;
+ }
- 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]: flag1=0x%02x, comm_flag=0x%02x\n",
+ q_bud->uid, q_bud->flag1, q_bud->comm_flag);
+ }
- 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);
- }
+ 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!");
+ }
+ 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 {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddies list");
+ qq_send_packet_get_buddies_list(gc, position);
}
}
@@ -342,7 +344,8 @@ void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleC
{
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;
@@ -354,62 +357,66 @@ void qq_process_get_all_list_with_group_reply(guint8 *buf, gint buf_len, PurpleC
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;
+ }
+
+ bytes += qq_get8(&sub_cmd, data + bytes);
+ g_return_if_fail(sub_cmd == 0x01);
+
+ 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);
- } else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt all list with group");
}
+
+ 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", "Get all list done, %d buddies and %d Quns\n", i, j);
}
diff --git a/libpurple/protocols/qq/buddy_opt.c b/libpurple/protocols/qq/buddy_opt.c
index 15ea046a05..6aee8b4374 100644
--- a/libpurple/protocols/qq/buddy_opt.c
+++ b/libpurple/protocols/qq/buddy_opt.c
@@ -36,7 +36,7 @@
#include "im.h"
#include "keep_alive.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_FRIEND, (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_FRIEND_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);
@@ -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
index db90b4027b..d96b65f37a 100644
--- a/libpurple/protocols/qq/buddy_status.c
+++ b/libpurple/protocols/qq/buddy_status.c
@@ -3,7 +3,7 @@
*
* purple
*
- * Purple is the legal property of its developers, whose names are too numerous
+ * Purple is the legal property ofr its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
@@ -33,10 +33,9 @@
#include "header_info.h"
#include "keep_alive.h"
#include "packet_parse.h"
-#include "send_core.h"
#include "utils.h"
-#include "qq_proxy.h"
+#include "qq_network.h"
#define QQ_MISC_STATUS_HAVING_VIIDEO 0x00000001
#define QQ_CHANGE_ONLINE_STATUS_REPLY_OK 0x30 /* ASCII value of "0" */
@@ -57,7 +56,7 @@ void qq_buddy_status_dump_unclear(qq_buddy_status *s)
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);
+ qq_show_packet("Unknown key", s->unknown_key, QQ_KEY_LENGTH);
g_string_free(dump, TRUE);
}
@@ -66,35 +65,33 @@ void qq_buddy_status_dump_unclear(qq_buddy_status *s)
* 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 qq_buddy_status_read(qq_buddy_status *s, guint8 *data)
{
- gint bytes;
+ gint bytes = 0;
- g_return_val_if_fail(data != NULL && *cursor != NULL && s != NULL, -1);
-
- bytes = 0;
+ g_return_val_if_fail(data != NULL && s != NULL, -1);
/* 000-003: uid */
- bytes += read_packet_dw(data, cursor, len, &s->uid);
+ bytes += qq_get32(&s->uid, data + bytes);
/* 004-004: 0x01 */
- bytes += read_packet_b(data, cursor, len, &s->unknown1);
+ bytes += qq_get8(&s->unknown1, data + bytes);
/* this is no longer the IP, it seems QQ (as of 2006) no longer sends
* the buddy's IP in this packet. all 0s */
/* 005-008: ip */
s->ip = g_new0(guint8, 4);
- bytes += read_packet_data(data, cursor, len, s->ip, 4);
+ bytes += qq_getdata(s->ip, 4, data + bytes);
/* port info is no longer here either */
/* 009-010: port */
- bytes += read_packet_w(data, cursor, len, &s->port);
+ bytes += qq_get16(&s->port, data + bytes);
/* 011-011: 0x00 */
- bytes += read_packet_b(data, cursor, len, &s->unknown2);
+ bytes += qq_get8(&s->unknown2, data + bytes);
/* 012-012: status */
- bytes += read_packet_b(data, cursor, len, &s->status);
+ bytes += qq_get8(&s->status, data + bytes);
/* 013-014: client_version */
- bytes += read_packet_w(data, cursor, len, &s->client_version);
+ bytes += qq_get16(&s->client_version, data + bytes);
/* 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);
+ bytes += qq_getdata(s->unknown_key, QQ_KEY_LENGTH, data + bytes);
if (s->uid == 0 || bytes != 31)
return -1;
@@ -106,17 +103,17 @@ gint qq_buddy_status_read(guint8 *data, guint8 **cursor, gint len, qq_buddy_stat
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;
+ 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. */
+/* Help calculate the correct icon index to tell the server. */
gint get_icon_offset(PurpleConnection *gc)
{
PurpleAccount *account;
@@ -131,7 +128,7 @@ gint get_icon_offset(PurpleConnection *gc)
|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
return 1;
- } else {
+ } else {
return 0;
}
}
@@ -140,7 +137,9 @@ gint get_icon_offset(PurpleConnection *gc)
void qq_send_packet_change_status(PurpleConnection *gc)
{
qq_data *qd;
- guint8 *raw_data, *cursor, away_cmd;
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
+ guint8 away_cmd;
guint32 misc_status;
gboolean fake_video;
PurpleAccount *account;
@@ -163,28 +162,24 @@ void qq_send_packet_change_status(PurpleConnection *gc)
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);
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, away_cmd);
+ bytes += qq_put32(raw_data + bytes, misc_status);
- g_free(raw_data);
+ 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;
- guint8 *data, *cursor, reply;
+ gint len, bytes;
+ guint8 *data, reply;
PurpleBuddy *b;
qq_buddy *q_bud;
gchar *name;
@@ -195,21 +190,22 @@ void qq_process_change_status_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_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 {
+ if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt chg status reply\n");
+ return;
+ }
+
+ bytes = 0;
+ bytes = qq_get8(&reply, data + bytes);
+ if (reply != QQ_CHANGE_ONLINE_STATUS_REPLY_OK) {
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Change status fail\n");
+ } else {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "Change status OK\n");
+ name = uid_to_purple_name(qd->uid);
+ b = purple_find_buddy(gc->account, name);
+ g_free(name);
+ q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+ qq_update_buddy_contact(gc, q_bud);
}
}
@@ -219,7 +215,7 @@ void qq_process_friend_change_status(guint8 *buf, gint buf_len, PurpleConnection
qq_data *qd;
gint len, bytes;
guint32 my_uid;
- guint8 *data, *cursor;
+ guint8 *data;
PurpleBuddy *b;
qq_buddy *q_bud;
qq_buddy_status *s;
@@ -230,51 +226,53 @@ void qq_process_friend_change_status(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)) {
- 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);
- }
+ if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &len) ) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
+ return;
+ }
+ s = g_new0(qq_buddy_status, 1);
+ bytes = 0;
+ /* 000-030: qq_buddy_status */
+ bytes += qq_buddy_status_read(s, data + bytes);
+ /* 031-034: my uid */
+ /* This has a value of 0 when we've changed our status to
+ * QQ_BUDDY_ONLINE_INVISIBLE */
+ bytes += qq_get32(&my_uid, data + bytes);
+
+ if (bytes != 35) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "bytes(%d) != 35\n", bytes);
g_free(s->ip);
g_free(s->unknown_key);
g_free(s);
+ return;
+ }
+
+ name = uid_to_purple_name(s->uid);
+ b = purple_find_buddy(gc->account, name);
+ g_free(name);
+ q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
+ if (q_bud) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "s->uid = %d, q_bud->uid = %d\n", s->uid , q_bud->uid);
+ if(0 != *((guint32 *)s->ip)) {
+ g_memmove(q_bud->ip, s->ip, 4);
+ q_bud->port = s->port;
+ }
+ q_bud->status = s->status;
+ if(0 != s->client_version) {
+ q_bud->client_version = s->client_version;
+ }
+ if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) {
+ qq_send_packet_get_level(gc, q_bud->uid);
+ }
+ qq_update_buddy_contact(gc, q_bud);
} else {
- purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Error decrypt buddy status change packet\n");
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "got information of unknown buddy %d\n", s->uid);
}
+
+ g_free(s->ip);
+ g_free(s->unknown_key);
+ g_free(s);
}
diff --git a/libpurple/protocols/qq/buddy_status.h b/libpurple/protocols/qq/buddy_status.h
index e358c9c2e4..18c9b77dab 100644
--- a/libpurple/protocols/qq/buddy_status.h
+++ b/libpurple/protocols/qq/buddy_status.h
@@ -52,7 +52,7 @@ enum {
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 qq_buddy_status_read(qq_buddy_status *s, guint8 *data);
gint get_icon_offset(PurpleConnection *gc);
void qq_send_packet_change_status(PurpleConnection *gc);
diff --git a/libpurple/protocols/qq/char_conv.c b/libpurple/protocols/qq/char_conv.c
index 627aacd86d..93b49b975b 100644
--- a/libpurple/protocols/qq/char_conv.c
+++ b/libpurple/protocols/qq/char_conv.c
@@ -39,9 +39,6 @@
#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);
-
const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x73,
@@ -111,16 +108,19 @@ 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", "%s\n", error->message);
+
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ (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
@@ -142,22 +142,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 +178,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) {
diff --git a/libpurple/protocols/qq/crypt.c b/libpurple/protocols/qq/crypt.c
index d1f2d9af40..296034610d 100644
--- a/libpurple/protocols/qq/crypt.c
+++ b/libpurple/protocols/qq/crypt.c
@@ -296,3 +296,20 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen,
}
return 1;
}
+
+/* return 1 is succeed, otherwise return 0
+gint qq_crypt(gint flag,
+ const guint8 *const instr, gint instrlen,
+ const guint8 *const key,
+ guint8 *outstr, gint *outstrlen_ptr)
+{
+ if (flag == DECRYPT)
+ return qq_decrypt(instr, instrlen, key, outstr, outstrlen_ptr);
+ else if (flag == ENCRYPT)
+ qq_encrypt(instr, instrlen, key, outstr, outstrlen_ptr);
+ else
+ return 0;
+
+ return 1;
+}
+*/
diff --git a/libpurple/protocols/qq/crypt.h b/libpurple/protocols/qq/crypt.h
index 9937ab90ad..49ae65aa8c 100644
--- a/libpurple/protocols/qq/crypt.h
+++ b/libpurple/protocols/qq/crypt.h
@@ -35,4 +35,13 @@ gint qq_decrypt(const guint8 *const instr, gint instrlen,
const guint8 *const key,
guint8 *outstr, gint *outstrlen_ptr);
+/*
+#define DECRYPT 0x00
+#define ENCRYPT 0x01
+
+gint qq_crypt(gint flag,
+ const guint8 *const instr, gint instrlen,
+ const guint8 *const key,
+ guint8 *outstr, gint *outstrlen_ptr);
+*/
#endif
diff --git a/libpurple/protocols/qq/file_trans.c b/libpurple/protocols/qq/file_trans.c
index f6e31e7014..bc6c2f024d 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);
}
@@ -126,16 +125,17 @@ static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5)
purple_cipher_context_destroy(context);
}
-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 +190,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,8 +258,8 @@ 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;
@@ -267,21 +267,19 @@ static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16
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 +290,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,51 +347,53 @@ 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;
gint filename_len, filesize;
@@ -408,17 +407,16 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
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)
@@ -437,44 +435,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 +480,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 +518,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 +531,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 +614,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 +644,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 +660,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 +711,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 +723,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 +735,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 +766,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 +796,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 +829,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_im.c b/libpurple/protocols/qq/group_im.c
index 5053d20465..23e62d87a8 100644
--- a/libpurple/protocols/qq/group_im.c
+++ b/libpurple/protocols/qq/group_im.c
@@ -58,7 +58,7 @@ 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;
@@ -67,19 +67,19 @@ void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar
msg_filtered = purple_markup_strip_html(msg);
purple_debug_info("QQ_MESG", "filterd qq qun mesg: %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,32 @@ 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 +339,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 +355,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 +370,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);
@@ -406,11 +390,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..f9cefcc9d8 100644
--- a/libpurple/protocols/qq/group_info.c
+++ b/libpurple/protocols/qq/group_info.c
@@ -43,7 +43,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 +65,83 @@ 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_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);
+ "Conv windows for \"%s\" is not on, do not 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) {
+ if (num <= 0) {
purple_debug(PURPLE_DEBUG_INFO, "QQ", "No group member needs to to update info 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,16 +151,18 @@ 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;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
- read_packet_dw(data, cursor, len, &(internal_group_id));
+ bytes = 0;
+ bytes += qq_get32(&(internal_group_id), data + bytes);
g_return_if_fail(internal_group_id > 0);
- read_packet_dw(data, cursor, len, &(external_group_id));
+
+ bytes += qq_get32(&(external_group_id), data + bytes);
g_return_if_fail(internal_group_id > 0);
pending_id = qq_get_pending_id(qd->adding_groups_from_server, internal_group_id);
@@ -189,32 +174,30 @@ 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);
+ bytes += qq_get32(&(unknown4), data + bytes); /* versionID */
+
+ /* strlen + <str content> */
+ bytes += convert_as_pascal_string(data + bytes, &(group->group_name_utf8), QQ_CHARSET_DEFAULT);
+ bytes += qq_get16(&unknown, data + bytes); /* 0x0000 */
+ bytes += convert_as_pascal_string(data + bytes, &(group->notice_utf8), QQ_CHARSET_DEFAULT);
+ bytes += convert_as_pascal_string(data + bytes, &(group->group_desc_utf8), QQ_CHARSET_DEFAULT);
+
+ 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);
@@ -223,11 +206,11 @@ void qq_process_group_cmd_get_group_info(guint8 *data, guint8 **cursor, gint len
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 +220,32 @@ 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);
+ 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);
}
}
-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 +257,63 @@ 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;
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, &(member->nickname), QQ_CHARSET_DEFAULT);
+ bytes += qq_get16(&unknown, data + bytes);
+ bytes += qq_get8(&(member->flag1), data + bytes);
+ bytes += qq_get8(&(member->comm_flag), data + bytes);
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..dfa3d8aa19 100644
--- a/libpurple/protocols/qq/group_info.h
+++ b/libpurple/protocols/qq/group_info.h
@@ -32,8 +32,8 @@
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_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..f872478b4f 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 {
@@ -81,12 +81,12 @@ const gchar *qq_group_cmd_get_desc(qq_group_cmd cmd)
}
/* 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 +96,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 +115,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 +135,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 +148,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_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/im.c b/libpurple/protocols/qq/im.c
index f63b7f6dbe..5afe847d18 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,
@@ -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,29 @@ 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));
+ bytes += qq_get16(&(common->sender_ver), data + bytes);
+ bytes += qq_get32(&(common->sender_uid), data + bytes);
+ bytes += qq_get32(&(common->receiver_uid), data + bytes);
- common->session_md5 = g_memdup(*cursor, QQ_KEY_LENGTH);
+ common->session_md5 = g_memdup(data + bytes, 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->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 +252,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,50 +260,52 @@ 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;
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)
@@ -315,9 +315,9 @@ static void _qq_process_recv_normal_im_text
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 +333,68 @@ 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\n",
+ common->sender_uid, common->receiver_uid,
+ qq_get_source_str (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], unknown [0x%02x], len %d\n",
+ common->normal_im_type, im_unprocessed->unknown, im_unprocessed->length);
+ g_free (common->session_md5);
+ 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 +402,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_len;
- len = data + data_len - *cursor;
-
- if (NULL == (segments = split_data(*cursor, len, "\x2f", 2)))
+ if (NULL == (segments = split_data(data, len, "\x2f", 2)))
return;
reply = strtol(segments[0], NULL, 10);
@@ -436,7 +418,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 +482,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 +541,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 +551,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 +569,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 +578,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);
+ }
- switch (im_header->im_type) {
+ /* 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_getdata((guint8 *) & (im_header->sender_ip), 4, 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) {
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
index 02c5f8ec95..8eafb24e00 100644
--- a/libpurple/protocols/qq/keep_alive.c
+++ b/libpurple/protocols/qq/keep_alive.c
@@ -40,7 +40,7 @@
#include "header_info.h"
#include "keep_alive.h"
#include "packet_parse.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "utils.h"
#define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */
@@ -49,18 +49,17 @@
void qq_send_packet_keep_alive(PurpleConnection *gc)
{
qq_data *qd;
- guint8 *raw_data, *cursor;
+ guint8 raw_data[16] = {0};
+ gint bytes= 0;
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);
+ bytes += qq_put32(raw_data + bytes, qd->uid);
- qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, TRUE, 0, TRUE, raw_data, 4);
+ qq_send_cmd(qd, QQ_CMD_KEEP_ALIVE, raw_data, 4);
}
/* parse the return of keep-alive packet, it includes some system information */
diff --git a/libpurple/protocols/qq/login_logout.c b/libpurple/protocols/qq/login_logout.c
index 3d8bfc7389..15fd4c01a6 100644
--- a/libpurple/protocols/qq/login_logout.c
+++ b/libpurple/protocols/qq/login_logout.c
@@ -25,6 +25,7 @@
#include "debug.h"
#include "internal.h"
#include "server.h"
+#include "cipher.h"
#include "buddy_info.h"
#include "buddy_list.h"
@@ -36,8 +37,7 @@
#include "login_logout.h"
#include "packet_parse.h"
#include "qq.h"
-#include "qq_proxy.h"
-#include "send_core.h"
+#include "qq_network.h"
#include "utils.h"
#define QQ_LOGIN_DATA_LENGTH 416
@@ -70,32 +70,36 @@ static const guint8 login_23_51[29] = {
*/
/* 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
+/* 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 gint8 login_53_68[16] = {
- -115, -117, -6, -20, -43, 82, 23, 74, -122, -7,
- -89, 117, -26, 50, -47, 109
+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 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
+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] = {
@@ -138,74 +142,80 @@ struct _qq_login_reply_redirect {
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)
+/* generate a md5 key using uid and session_key */
+static guint8 *gen_session_md5(gint uid, guint8 *session_key)
{
- return (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
+ guint8 *src, md5_str[QQ_KEY_LENGTH];
+ PurpleCipher *cipher;
+ PurpleCipherContext *context;
+
+ src = g_newa(guint8, 20);
+ /* bug found by QuLogic */
+ memcpy(src, &uid, sizeof(uid));
+ memcpy(src + sizeof(uid), session_key, QQ_KEY_LENGTH);
+
+ cipher = purple_ciphers_find_cipher("md5");
+ context = purple_cipher_context_new(cipher, NULL);
+ purple_cipher_context_append(context, src, 20);
+ purple_cipher_context_digest(context, sizeof(md5_str), md5_str, NULL);
+ purple_cipher_context_destroy(context);
+
+ return g_memdup(md5_str, QQ_KEY_LENGTH);
}
/* 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;
+ /* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
bytes = 0;
/* 000-000: reply code */
- bytes += read_packet_b(data, &cursor, len, &lrop.result);
+ bytes += qq_get8(&lrop.result, data + bytes);
/* 001-016: session key */
- lrop.session_key = g_memdup(cursor, QQ_KEY_LENGTH);
- cursor += QQ_KEY_LENGTH;
+ lrop.session_key = g_memdup(data + bytes, 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);
+ bytes += qq_get32(&lrop.uid, data + bytes);
/* 021-024: server detected user public IP */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.client_ip, 4);
+ bytes += qq_getdata((guint8 *) &lrop.client_ip, 4, data + bytes);
/* 025-026: server detected user port */
- bytes += read_packet_w(data, &cursor, len, &lrop.client_port);
+ bytes += qq_get16(&lrop.client_port, data + bytes);
/* 027-030: server detected itself ip 127.0.0.1 ? */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.server_ip, 4);
+ bytes += qq_getdata((guint8 *) &lrop.server_ip, 4, data + bytes);
/* 031-032: server listening port */
- bytes += read_packet_w(data, &cursor, len, &lrop.server_port);
+ bytes += qq_get16(&lrop.server_port, data + bytes);
/* 033-036: login time for current session */
- bytes += read_packet_time(data, &cursor, len, &lrop.login_time);
+ bytes += qq_getime(&lrop.login_time, data + bytes);
/* 037-062: 26 bytes, unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown1, 26);
+ bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
/* 063-066: unknown server1 ip address */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server1_ip, 4);
+ bytes += qq_getdata((guint8 *) &lrop.unknown_server1_ip, 4, data + bytes);
/* 067-068: unknown server1 port */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server1_port);
+ bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
/* 069-072: unknown server2 ip address */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown_server2_ip, 4);
+ bytes += qq_getdata((guint8 *) &lrop.unknown_server2_ip, 4, data + bytes);
/* 073-074: unknown server2 port */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown_server2_port);
+ bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
/* 075-076: 2 bytes unknown */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown2);
+ bytes += qq_get16(&lrop.unknown2, data + bytes);
/* 077-078: 2 bytes unknown */
- bytes += read_packet_w(data, &cursor, len, &lrop.unknown3);
+ bytes += qq_get16(&lrop.unknown3, data + bytes);
/* 079-110: 32 bytes unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown4, 32);
+ bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
/* 111-122: 12 bytes unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown5, 12);
+ bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
/* 123-126: login IP of last session */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.last_client_ip, 4);
+ bytes += qq_getdata((guint8 *) &lrop.last_client_ip, 4, data + bytes);
/* 127-130: login time of last session */
- bytes += read_packet_time(data, &cursor, len, &lrop.last_login_time);
+ bytes += qq_getime(&lrop.last_login_time, data + bytes);
/* 131-138: 8 bytes unknown */
- bytes += read_packet_data(data, &cursor, len, (guint8 *) &lrop.unknown6, 8);
+ 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",
@@ -213,9 +223,15 @@ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
} /* but we still go on as login OK */
+ g_return_val_if_fail(qd->session_key == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
qd->session_key = lrop.session_key;
- qd->session_md5 = _gen_session_md5(qd->uid, qd->session_key);
+
+ g_return_val_if_fail(qd->session_md5 == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
+ qd->session_md5 = gen_session_md5(qd->uid, qd->session_key);
+
+ g_return_val_if_fail(qd->my_ip == NULL, QQ_LOGIN_REPLY_MISC_ERROR);
qd->my_ip = gen_ip_str(lrop.client_ip);
+
qd->my_port = lrop.client_port;
qd->login_time = lrop.login_time;
qd->last_login_time = lrop.last_login_time;
@@ -247,34 +263,40 @@ static gint _qq_process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
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);
+ bytes += qq_get8(&lrrp.result, data + bytes);
/* 001-004: login uid */
- bytes += read_packet_dw(data, &cursor, len, &lrrp.uid);
+ bytes += qq_get32(&lrrp.uid, data + bytes);
/* 005-008: redirected new server IP */
- bytes += read_packet_data(data, &cursor, len, lrrp.new_server_ip, 4);
+ bytes += qq_getdata(lrrp.new_server_ip, 4, data + bytes);
/* 009-010: redirected new server port */
- bytes += read_packet_w(data, &cursor, len, &lrrp.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);
ret = QQ_LOGIN_REPLY_MISC_ERROR;
- } else { /* start new connection */
- new_server_str = gen_ip_str(lrrp.new_server_ip);
+ } else {
+ /* redirect to new server, do not disconnect or connect here
+ * those connect should be called at packet_process */
+ if (qd->real_hostname) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n");
+ g_free(qd->real_hostname);
+ qd->real_hostname = NULL;
+ }
+ qd->real_hostname = gen_ip_str(lrrp.new_server_ip);
+ qd->real_port = lrrp.new_server_port;
+ qd->is_redirect = TRUE;
+
purple_debug(PURPLE_DEBUG_WARNING, "QQ",
- "Redirected to new server: %s:%d\n", 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);
+ "Redirected to new server: %s:%d\n", qd->real_hostname, qd->real_port);
+
ret = QQ_LOGIN_REPLY_REDIRECT;
}
@@ -299,89 +321,78 @@ static gint _qq_process_login_wrong_pwd(PurpleConnection *gc, guint8 *data, gint
void qq_send_packet_request_login_token(PurpleConnection *gc)
{
qq_data *qd;
- guint8 *buf, *cursor;
- guint16 seq_ret;
- gint bytes;
+ guint8 buf[16] = {0};
+ gint bytes = 0;
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
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");
+ bytes += qq_put8(buf + bytes, 0);
+
+ qq_send_data(qd, QQ_CMD_REQUEST_LOGIN_TOKEN, buf, bytes);
}
/* 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;
+ 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;
- buf = g_newa(guint8, MAX_PACKET_SIZE);
+
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 */
- qd->inikey = _gen_login_key();
+ if (qd->inikey) {
+ g_free(qd->inikey);
+ }
+ qd->inikey = (guint8 *) g_strnfill(QQ_KEY_LENGTH, 0x01);
+ bytes = 0;
/* now generate the encrypted data
* 000-015 use pwkey as key to encrypt empty string */
- qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data, &encrypted_len);
+ qq_encrypt((guint8 *) "", 0, qd->pwkey, raw_data + bytes, &encrypted_len);
+ bytes += 16;
/* 016-016 */
- raw_data[16] = 0x00;
+ bytes += qq_put8(raw_data + bytes, 0x00);
/* 017-020, used to be IP, now zero */
- *((guint32 *) (raw_data + 17)) = 0x00000000;
+ bytes += qq_put32(raw_data + bytes, 0x00000000);
/* 021-022, used to be port, now zero */
- *((guint16 *) (raw_data + 21)) = 0x0000;
+ bytes += qq_put16(raw_data + bytes, 0x0000);
/* 023-051, fixed value, unknown */
- g_memmove(raw_data + 23, login_23_51, 29);
+ bytes += qq_putdata(raw_data + bytes, login_23_51, 29);
/* 052-052, login mode */
- raw_data[52] = qd->login_mode;
+ bytes += qq_put8(raw_data + bytes, qd->login_mode);
/* 053-068, fixed value, maybe related to per machine */
- g_memmove(raw_data + 53, login_53_68, 16);
-
+ bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
/* 069, login token length */
- raw_data[69] = token_length;
- pos = 70;
+ bytes += qq_put8(raw_data + bytes, token_length);
/* 070-093, login token, normally 24 bytes */
- g_memmove(raw_data + pos, token, token_length);
- pos += token_length;
+ bytes += qq_putdata(raw_data + bytes, token, token_length);
/* 100 bytes unknown */
- g_memmove(raw_data + pos, login_100_bytes, 100);
- pos += 100;
+ bytes += qq_putdata(raw_data + bytes, login_100_bytes, 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;
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
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");
+ bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+
+ qq_send_data(qd, QQ_CMD_LOGIN, buf, bytes);
}
void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
{
qq_data *qd;
- gchar *hex_dump;
+ gchar *error_msg;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -394,20 +405,24 @@ void qq_process_request_login_token_reply(guint8 *buf, gint buf_len, PurpleConne
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_hex_dump(PURPLE_DEBUG_INFO, "QQ",
+ buf+2, buf_len-2,
+ "<<< got a token -> [default] decrypt and dump");
qq_send_packet_login(gc, buf_len-2, buf+2);
} else {
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"));
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ buf, buf_len,
+ ">>> [default] decrypt and dump");
+ error_msg = try_dump_as_gbk(buf, buf_len);
+ if (error_msg) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+ g_free(error_msg);
+ } else {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Error requesting login token"));
+ }
}
- g_free(hex_dump);
}
/* send logout packets to QQ server */
@@ -418,7 +433,7 @@ void qq_send_packet_logout(PurpleConnection *gc)
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);
+ qq_send_cmd_detail(qd, QQ_CMD_LOGOUT, 0xffff, FALSE, qd->pwkey, QQ_KEY_LENGTH);
qd->logged_in = FALSE; /* update login status AFTER sending logout packets */
}
@@ -429,7 +444,7 @@ void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
gint len, ret, bytes;
guint8 *data;
qq_data *qd;
- gchar *hex_dump;
+ gchar* error_msg;
g_return_if_fail(buf != NULL && buf_len != 0);
@@ -462,13 +477,14 @@ void qq_process_login_reply(guint8 *buf, gint buf_len, PurpleConnection *gc)
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);
-
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ data, len,
+ ">>> [default] decrypt and dump");
+ error_msg = try_dump_as_gbk(data, len);
+ if (error_msg) {
+ purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
+ g_free(error_msg);
+ }
ret = QQ_LOGIN_REPLY_MISC_ERROR;
}
} else { /* no idea how to decrypt */
diff --git a/libpurple/protocols/qq/packet_parse.c b/libpurple/protocols/qq/packet_parse.c
index a26192731b..7248647dd5 100644
--- a/libpurple/protocols/qq/packet_parse.c
+++ b/libpurple/protocols/qq/packet_parse.c
@@ -25,119 +25,147 @@
#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)
+{
+ 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 datalen bytes from buf,
+ * return the number of bytes read if succeeds, otherwise return -1 */
+gint qq_getdata(guint8 *data, gint datalen, guint8 *buf)
{
- if (*cursor <= buf + buflen - sizeof(*dw)) {
- *dw = g_ntohl(**(guint32 **) cursor);
- *cursor += sizeof(*dw);
- return sizeof(*dw);
- } else {
- return -1;
- }
+ 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 read_packet_time(guint8 *buf, guint8 **cursor, gint buflen, time_t *t)
+gint qq_getime(time_t *t, guint8 *buf)
{
- guint32 time;
- gint ret = read_packet_dw(buf, cursor, buflen, &time);
- if (ret != -1 ) {
- *t = time;
- }
- return ret;
-}
-
-/* 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;
- }
+ guint32 dw_dest;
+ memcpy(&dw_dest, buf, sizeof(dw_dest));
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest before 0x%08x\n", dw_dest);
+#endif
+ dw_dest = g_ntohl(dw_dest);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][getime] dw_dest after 0x%08x\n", dw_dest);
+#endif
+ memcpy(t, &dw_dest, sizeof(dw_dest));
+ return sizeof(dw_dest);
}
+/*------------------------------------------------PUT------------------------------------------------*/
/* pack one byte into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_b(guint8 *buf, guint8 **cursor, guint8 b)
+gint qq_put8(guint8 *buf, guint8 b)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint8)) {
- **(guint8 **) cursor = b;
- *cursor += sizeof(guint8);
- return sizeof(guint8);
- } else {
- return -1;
- }
+ memcpy(buf, &b, sizeof(b));
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put8] b 0x%02x\n", b);
+#endif
+ return sizeof(b);
}
+
/* pack two bytes as "guint16" into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_w(guint8 *buf, guint8 **cursor, guint16 w)
+gint qq_put16(guint8 *buf, guint16 w)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint16)) {
- **(guint16 **) cursor = g_htons(w);
- *cursor += sizeof(guint16);
- return sizeof(guint16);
- } else {
- return -1;
- }
+ guint16 w_porter;
+ w_porter = g_htons(w);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put16] w 0x%04x, w_porter 0x%04x\n", w, w_porter);
+#endif
+ memcpy(buf, &w_porter, sizeof(w_porter));
+ return sizeof(w_porter);
}
+
/* pack four bytes as "guint32" into buf
* return the number of bytes packed, otherwise return -1 */
-gint create_packet_dw(guint8 *buf, guint8 **cursor, guint32 dw)
+gint qq_put32(guint8 *buf, guint32 dw)
{
- if (*cursor <= buf + MAX_PACKET_SIZE - sizeof(guint32)) {
- **(guint32 **) cursor = g_htonl(dw);
- *cursor += sizeof(guint32);
- return sizeof(guint32);
- } else {
- return -1;
- }
+ guint32 dw_porter;
+ dw_porter = g_htonl(dw);
+#ifdef PARSER_DEBUG
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] buf %p\n", (void *)buf);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
+#endif
+ memcpy(buf, &dw_porter, sizeof(dw_porter));
+ return sizeof(dw_porter);
}
+
/* 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..20dc7154e4 100644
--- a/libpurple/protocols/qq/packet_parse.h
+++ b/libpurple/protocols/qq/packet_parse.h
@@ -37,14 +37,28 @@
*/
#define MAX_PACKET_SIZE 65535
+gint qq_get8(guint8 *b, guint8 *buf);
+gint qq_get16(guint16 *w, guint8 *buf);
+gint qq_get32(guint32 *dw, 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_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..e243153b17 100644
--- a/libpurple/protocols/qq/qq.c
+++ b/libpurple/protocols/qq/qq.c
@@ -55,48 +55,94 @@
#include "login_logout.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 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;
+ }
-static void _qq_login(PurpleAccount *account)
+ 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;
-
- purple_connection_update_progress(gc, _("Connecting"), 0, QQ_CONNECT_STEPS);
+ server_list_create(account);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "Server list has %d\n", g_list_length(qd->servers));
- 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 */
@@ -442,8 +484,9 @@ static void _qq_menu_show_login_info(PurplePluginAction *action)
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>Real hostname</b>: %s: %d<br>\n"), qd->real_hostname, qd->real_port);
g_string_append_printf(info, _("<b>My Public IP</b>: %s<br>\n"), qd->my_ip);
g_string_append(info, "<hr>\n");
@@ -594,7 +637,7 @@ static GList *_qq_buddy_menu(PurpleBlistNode * node)
}
-static void _qq_keep_alive(PurpleConnection *gc)
+static void qq_keep_alive(PurpleConnection *gc)
{
qq_group *group;
qq_data *qd;
@@ -651,8 +694,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 */
@@ -676,7 +719,7 @@ static PurplePluginProtocolInfo prpl_info = {
NULL, /* chat_leave */
NULL, /* chat_whisper */
_qq_chat_send, /* chat_send */
- _qq_keep_alive, /* keepalive */
+ qq_keep_alive, /* keepalive */
NULL, /* register_user */
_qq_get_chat_buddy_info, /* get_cb_info */
NULL, /* get_cb_away */
@@ -751,13 +794,13 @@ 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_string_new(_("Port"), "port", NULL);
+ option = purple_account_option_bool_new(_("Connect using TCP"), "use_tcp", TRUE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
my_protocol = plugin;
diff --git a/libpurple/protocols/qq/qq.h b/libpurple/protocols/qq/qq.h
index bc34455371..32c2f47a06 100644
--- a/libpurple/protocols/qq/qq.h
+++ b/libpurple/protocols/qq/qq.h
@@ -28,6 +28,9 @@
#include <glib.h>
#include "internal.h"
#include "ft.h"
+#include "circbuffer.h"
+#include "dnsquery.h"
+#include "dnssrv.h"
#include "proxy.h"
#include "roomlist.h"
@@ -66,7 +69,39 @@ 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 */
+
+ GList *send_trans; /* check ack packet and resend */
+ guint resend_timeout;
+
+ guint8 rcv_window[1 << 13]; /* windows for check duplicate packet */
+ GQueue *rcv_trans; /* queue to store packet can not process before login */
+
+ /* 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) */
@@ -76,17 +111,9 @@ struct _qq_data {
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;
@@ -99,9 +126,6 @@ struct _qq_data {
guint32 all_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 +136,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_network.c b/libpurple/protocols/qq/qq_network.c
new file mode 100644
index 0000000000..08013030cc
--- /dev/null
+++ b/libpurple/protocols/qq/qq_network.c
@@ -0,0 +1,1258 @@
+/**
+ * @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 "buddy_status.h"
+#include "group_free.h"
+#include "char_conv.h"
+#include "crypt.h"
+#include "group_network.h"
+#include "header_info.h"
+#include "keep_alive.h"
+#include "im.h"
+#include "login_logout.h"
+#include "packet_parse.h"
+#include "qq_network.h"
+#include "qq_trans.h"
+#include "sys_msg.h"
+#include "utils.h"
+
+/* set QQ_RECONNECT_MAX to 1, when test reconnecting */
+#define QQ_RECONNECT_MAX 4
+#define QQ_RECONNECT_INTERVAL 5000
+
+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;
+}
+
+/* QQ 2003iii uses double MD5 for the pwkey to get the session key */
+static guint8 *encrypt_account_password(const gchar *pwd)
+{
+ PurpleCipher *cipher;
+ PurpleCipherContext *context;
+
+ guchar pwkey_tmp[QQ_KEY_LENGTH];
+
+ cipher = purple_ciphers_find_cipher("md5");
+ context = purple_cipher_context_new(cipher, NULL);
+ purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
+ purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
+ purple_cipher_context_destroy(context);
+ context = purple_cipher_context_new(cipher, NULL);
+ purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
+ purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
+ purple_cipher_context_destroy(context);
+
+ return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
+}
+
+/* default process, decrypt and dump */
+static void process_cmd_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+ qq_data *qd;
+ guint8 *data;
+ gint data_len;
+ gchar *msg_utf8 = NULL;
+
+ g_return_if_fail(buf != NULL && buf_len != 0);
+
+ qq_show_packet("Processing unknown packet", buf, buf_len);
+
+ qd = (qq_data *) gc->proto_data;
+
+ data_len = buf_len;
+ data = g_newa(guint8, data_len);
+ memset(data, 0, data_len);
+ if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
+ return;
+ }
+
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
+ data, data_len,
+ ">>> [%d] %s -> [default] decrypt and dump",
+ seq, qq_get_cmd_desc(cmd));
+
+ msg_utf8 = try_dump_as_gbk(data, data_len);
+ if (msg_utf8) {
+ g_free(msg_utf8);
+ }
+}
+
+static gint packet_get_header(guint8 *header_tag, guint16 *source_tag,
+ guint16 *cmd, guint16 *seq, guint8 *buf)
+{
+ 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;
+}
+
+/* check whether one sequence number is duplicated or not
+ * return TRUE if it is duplicated, otherwise FALSE */
+static gboolean packet_is_dup(qq_data *qd, guint16 seq)
+{
+ guint8 *byte, mask;
+
+ g_return_val_if_fail(qd != NULL, FALSE);
+
+ byte = &(qd->rcv_window[seq / 8]);
+ mask = (1 << (seq % 8));
+
+ if ((*byte) & mask)
+ return TRUE; /* check mask */
+ (*byte) |= mask;
+ return FALSE; /* set mask */
+}
+
+static gboolean packet_check_ack(qq_data *qd, guint16 seq)
+{
+ gpointer trans;
+
+ g_return_val_if_fail(qd != NULL, FALSE);
+
+ trans = qq_send_trans_find(qd, seq);
+ if (trans == NULL) {
+ return FALSE;
+ }
+
+ qq_send_trans_remove(qd, trans);
+ return TRUE;
+}
+
+static gboolean reconnect_later_cb(gpointer data)
+{
+ PurpleConnection *gc;
+ 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);
+}
+
+static void process_cmd_server(
+ PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ /* now process the packet */
+ switch (cmd) {
+ case QQ_CMD_RECV_IM:
+ qq_process_recv_im(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_RECV_MSG_SYS:
+ qq_process_msg_sys(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
+ qq_process_friend_change_status(data, data_len, gc);
+ break;
+ default:
+ process_cmd_unknow(gc, data, data_len, cmd, seq);
+ break;
+ }
+}
+
+static void process_cmd_reply(
+ PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ /* now process the packet */
+ switch (cmd) {
+ case QQ_CMD_KEEP_ALIVE:
+ qq_process_keep_alive_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_UPDATE_INFO:
+ qq_process_modify_info_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_ADD_FRIEND_WO_AUTH:
+ qq_process_add_buddy_reply(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_DEL_FRIEND:
+ qq_process_remove_buddy_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_REMOVE_SELF:
+ qq_process_remove_self_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_BUDDY_AUTH:
+ qq_process_add_buddy_auth_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_USER_INFO:
+ qq_process_get_info_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_CHANGE_ONLINE_STATUS:
+ qq_process_change_status_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_SEND_IM:
+ qq_process_send_im_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_LOGIN:
+ qq_process_login_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_FRIENDS_LIST:
+ qq_process_get_buddies_list_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_FRIENDS_ONLINE:
+ qq_process_get_buddies_online_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GROUP_CMD:
+ qq_process_group_cmd_reply(data, data_len, seq, gc);
+ break;
+ case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
+ qq_process_get_all_list_with_group_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_GET_LEVEL:
+ qq_process_get_level_reply(data, data_len, gc);
+ break;
+ case QQ_CMD_REQUEST_LOGIN_TOKEN:
+ qq_process_request_login_token_reply(data, data_len, gc);
+ break;
+ default:
+ process_cmd_unknow(gc, data, data_len, cmd, seq);
+ break;
+ }
+}
+
+/* process the incoming packet from qq_pending */
+static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
+{
+ qq_data *qd;
+ gint bytes, bytes_not_read;
+
+ gboolean prev_login_status;
+ guint8 *new_data;
+ gint new_data_len;
+
+ guint8 header_tag;
+ guint16 source_tag;
+ guint16 cmd;
+ guint16 seq; /* May be ack_seq or send_seq, depends on cmd */
+
+ gboolean is_reply;
+
+ 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_source_str(source_tag));
+ }
+
+ bytes_not_read = buf_len - bytes - 1;
+
+ /* ack packet, we need to update send tranactions */
+ /* we do not check duplication for server ack */
+ is_reply = packet_check_ack(qd, seq);
+ if ( !is_reply ) {
+ if ( !qd->logged_in ) {
+ /* packets before login */
+ qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read);
+ return; /* do not process it now */
+ }
+
+ /* server intiated packet, we need to send ack and check duplicaion
+ * this must be put after processing b4_packet
+ * as these packets will be passed in twice */
+ if (packet_is_dup(qd, seq)) {
+ purple_debug(PURPLE_DEBUG_WARNING,
+ "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
+ return;
+ }
+ process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
+ return;
+ }
+
+ /* this is the length of all the encrypted data (also remove tail tag */
+ process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
+
+ /* 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 */
+ new_data = g_newa(guint8, MAX_PACKET_SIZE);
+ while (1) {
+ memset(new_data, 0, MAX_PACKET_SIZE);
+ new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE);
+ if (new_data_len < 0) {
+ break;
+ }
+ if (new_data_len == 0) {
+ continue;
+ }
+ process_cmd_reply(gc, seq, cmd, new_data, new_data_len);
+ }
+ }
+}
+
+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;
+ }
+
+ 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;
+ }
+
+ 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, "QQ", "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, "QQ", "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 trans_timeout(gpointer data)
+{
+ PurpleConnection *gc;
+ qq_data *qd;
+ guint8 *buf;
+ gint buf_len = 0;
+ guint16 cmd;
+ gint retries = 0;
+ int index;
+
+ gc = (PurpleConnection *) data;
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
+
+ qd = (qq_data *) gc->proto_data;
+
+ index = 0;
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+
+ while (1) {
+ if (index < 0) {
+ /* next record is NULL */
+ break;
+ }
+ /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */
+ memset(buf, 0, MAX_PACKET_SIZE);
+ buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries);
+ if (buf_len <= 0) {
+ /* curr record is empty, whole trans is NULL */
+ break;
+ }
+ /* index = -1, when get last record of transactions */
+
+ /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */
+ if (retries > 0) {
+ if (qd->use_tcp) {
+ tcp_send_out(qd, buf, buf_len);
+ } else {
+ udp_send_out(qd, buf, buf_len);
+ }
+ continue;
+ }
+
+ /* retries <= 0 */
+ switch (cmd) {
+ case QQ_CMD_KEEP_ALIVE:
+ if (qd->logged_in) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+ qd->logged_in = FALSE;
+ }
+ break;
+ case QQ_CMD_LOGIN:
+ case QQ_CMD_REQUEST_LOGIN_TOKEN:
+ if (!qd->logged_in) {
+ /* cancel login progress */
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
+ }
+ break;
+ default:
+ purple_debug(PURPLE_DEBUG_WARNING, "QQ",
+ "%s packet lost.\n", qq_get_cmd_desc(cmd));
+ }
+ }
+
+ 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;
+
+ 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;
+
+ /* 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));
+ g_return_if_fail(qd->pwkey == NULL);
+ qd->pwkey = encrypt_account_password(passwd);
+
+ g_return_if_fail(qd->resend_timeout == 0);
+ /* call trans_timeout every 5 seconds */
+ qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc);
+
+ 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_request_login_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: %d\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");
+ /* finish all I/O */
+ if (qd->fd >= 0 && qd->logged_in) {
+ qq_send_packet_logout(gc);
+ }
+
+ if (qd->resend_timeout > 0) {
+ purple_timeout_remove(qd->resend_timeout);
+ qd->resend_timeout = 0;
+ }
+
+ if (gc->inpa > 0) {
+ purple_input_remove(gc->inpa);
+ gc->inpa = 0;
+ }
+
+ 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;
+ }
+
+ memset(qd->rcv_window, 0, sizeof(qd->rcv_window));
+ qq_rcv_trans_remove_all(qd);
+ qq_send_trans_remove_all(qd);
+
+ if (qd->inikey) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n");
+ g_free(qd->inikey);
+ qd->inikey = NULL;
+ }
+ if (qd->pwkey) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n");
+ g_free(qd->pwkey);
+ qd->pwkey = NULL;
+ }
+ if (qd->session_key) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n");
+ g_free(qd->session_key);
+ qd->session_key = NULL;
+ }
+ if (qd->session_md5) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n");
+ g_free(qd->session_md5);
+ qd->session_md5 = NULL;
+ }
+ if (qd->my_ip) {
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n");
+ g_free(qd->my_ip);
+ qd->my_ip = NULL;
+ }
+
+ 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;
+}
+
+gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
+{
+ guint8 *buf;
+ gint buf_len;
+ gint bytes_sent;
+ gint seq;
+
+ g_return_val_if_fail(qd != NULL, -1);
+ g_return_val_if_fail(data != NULL && data_len > 0, -1);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ seq = ++(qd->send_seq);
+ buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
+ if (buf_len <= 0) {
+ return -1;
+ }
+
+ if (qd->use_tcp) {
+ bytes_sent = tcp_send_out(qd, buf, buf_len);
+ } else {
+ bytes_sent = udp_send_out(qd, buf, buf_len);
+ }
+
+ /* always need ack */
+ qq_send_trans_append(qd, buf, buf_len, cmd, seq);
+
+ 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;
+}
+
+/* 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_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
+ guint8 *data, gint data_len)
+{
+ guint8 *buf;
+ gint buf_len;
+ guint8 *encrypted_data;
+ gint encrypted_len;
+ gint bytes_sent;
+
+ g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
+ g_return_val_if_fail(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);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len);
+ if (buf_len <= 0) {
+ return -1;
+ }
+
+ if (QQ_DEBUG) {
+ qq_show_packet("QQ_SEND_CMD", buf, buf_len);
+ }
+ if (qd->use_tcp) {
+ bytes_sent = tcp_send_out(qd, buf, buf_len);
+ } else {
+ bytes_sent = udp_send_out(qd, buf, buf_len);
+ }
+
+ /* if it does not need ACK, we send ACK manually several times */
+ if (need_ack) {
+ qq_send_trans_append(qd, buf, buf_len, cmd, seq);
+ }
+
+ if (QQ_DEBUG) {
+ qq_show_packet("QQ_SEND_CMD", buf, buf_len);
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "<== [%05d], %s, total %d bytes is sent %d\n",
+ seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
+ }
+ return bytes_sent;
+}
+
+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/recv_core.h b/libpurple/protocols/qq/qq_network.h
index 6353b48f85..c45aa226de 100644
--- a/libpurple/protocols/qq/recv_core.h
+++ b/libpurple/protocols/qq/qq_network.h
@@ -1,5 +1,5 @@
/**
- * @file recv_core.h
+ * @file qq_network.h
*
* purple
*
@@ -22,15 +22,23 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
-#ifndef _QQ_RECV_CORE_H_
-#define _QQ_RECV_CORE_H_
+#ifndef _QQ_PROXY_H
+#define _QQ_PROXY_H
#include <glib.h>
#include "connection.h"
+
#include "qq.h"
-void qq_b4_packets_free(qq_data *qd);
+#define QQ_CONNECT_STEPS 3 /* steps in connection */
+
+void qq_connect(PurpleAccount *account);
+void qq_disconnect(PurpleConnection *gc);
+void qq_connect_later(PurpleConnection *gc);
-void qq_input_pending(gpointer data, gint source, PurpleInputCondition cond);
+gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint datalen);
+gint qq_send_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_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..9dc5eb52e2
--- /dev/null
+++ b/libpurple/protocols/qq/qq_trans.c
@@ -0,0 +1,246 @@
+/**
+ * @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_trans.h"
+
+#define QQ_RESEND_MAX 8 /* max resend per packet */
+
+typedef struct _transaction {
+ guint16 seq;
+ guint16 cmd;
+ guint8 *buf;
+ gint buf_len;
+
+ gint fd;
+ gint retries;
+ time_t create_time;
+} transaction;
+
+void qq_send_trans_append(qq_data *qd, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
+{
+ transaction *trans = g_new0(transaction, 1);
+
+ g_return_if_fail(trans != NULL);
+
+ trans->fd = qd->fd;
+ trans->cmd = cmd;
+ trans->seq = seq;
+ trans->retries = QQ_RESEND_MAX;
+ trans->create_time = time(NULL);
+ trans->buf = g_memdup(buf, buf_len); /* don't use g_strdup, may have 0x00 */
+ trans->buf_len = buf_len;
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Add to transaction, seq = %d, buf = %p, len = %d\n",
+ trans->seq, trans->buf, trans->buf_len);
+ qd->send_trans = g_list_append(qd->send_trans, trans);
+}
+
+/* Remove a packet with seq from send trans */
+void qq_send_trans_remove(qq_data *qd, gpointer data)
+{
+ transaction *trans = (transaction *)data;
+
+ g_return_if_fail(qd != NULL && data != NULL);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ",
+ "ack [%05d] %s, remove from send tranactions\n",
+ trans->seq, qq_get_cmd_desc(trans->cmd));
+
+ if (trans->buf) g_free(trans->buf);
+ qd->send_trans = g_list_remove(qd->send_trans, trans);
+ g_free(trans);
+}
+
+gpointer qq_send_trans_find(qq_data *qd, guint16 seq)
+{
+ GList *curr;
+ GList *next;
+ transaction *trans;
+
+ curr = qd->send_trans;
+ while(curr) {
+ next = curr->next;
+ trans = (transaction *) (curr->data);
+ if(trans->seq == seq) {
+ return trans;
+ }
+ curr = next;
+ }
+
+ return NULL;
+}
+
+/* clean up send trans and free all contents */
+void qq_send_trans_remove_all(qq_data *qd)
+{
+ GList *curr;
+ GList *next;
+ transaction *trans;
+ gint count = 0;
+
+ curr = qd->send_trans;
+ while(curr) {
+ next = curr->next;
+
+ trans = (transaction *) (curr->data);
+ /*
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Remove to transaction, seq = %d, buf = %p, len = %d\n",
+ trans->seq, trans->buf, trans->len);
+ */
+ qq_send_trans_remove(qd, trans);
+
+ count++;
+ curr = next;
+ }
+ g_list_free(qd->send_trans);
+
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in send tranactions are freed!\n", count);
+}
+
+gint qq_send_trans_scan(qq_data *qd, gint *start,
+ guint8 *buf, gint maxlen, guint16 *cmd, gint *retries)
+{
+ GList *curr;
+ GList *next = NULL;
+ transaction *trans;
+ gint copylen;
+
+ g_return_val_if_fail(qd != NULL && *start >= 0 && maxlen > 0, -1);
+
+ /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Scan from %d\n", *start); */
+ curr = g_list_nth(qd->send_trans, *start);
+ while(curr) {
+ next = curr->next;
+ *start = g_list_position(qd->send_trans, next);
+
+ trans = (transaction *) (curr->data);
+ if (trans->buf == NULL || trans->buf_len <= 0) {
+ qq_send_trans_remove(qd, trans);
+ curr = next;
+ continue;
+ }
+
+ if (trans->retries < 0) {
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Remove transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
+ trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
+ qq_send_trans_remove(qd, trans);
+ curr = next;
+ continue;
+ }
+
+ purple_debug(PURPLE_DEBUG_ERROR, "QQ",
+ "Resend transaction, seq %d, buf %p, len %d, retries %d, next %d\n",
+ trans->seq, trans->buf, trans->buf_len, trans->retries, *start);
+ copylen = MIN(trans->buf_len, maxlen);
+ g_memmove(buf, trans->buf, copylen);
+
+ *cmd = trans->cmd;
+ *retries = trans->retries;
+ trans->retries--;
+ return copylen;
+ }
+
+ /* purple_debug(PURPLE_DEBUG_INFO, "QQ", "Scan finished\n"); */
+ return -1;
+}
+
+void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
+{
+ transaction *trans = g_new0(transaction, 1);
+
+ g_return_if_fail(data != NULL && data_len > 0);
+ g_return_if_fail(trans != NULL);
+
+ trans->cmd = cmd;
+ trans->seq = seq;
+ trans->buf = g_memdup(data, data_len);
+ trans->buf_len = data_len;
+ trans->create_time = time(NULL);
+
+ if (qd->rcv_trans == NULL)
+ qd->rcv_trans = g_queue_new();
+
+ g_queue_push_head(qd->rcv_trans, trans);
+}
+
+gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16 *seq, guint8 *data, gint max_len)
+{
+ transaction *trans = NULL;
+ gint copy_len;
+
+ g_return_val_if_fail(data != NULL && max_len > 0, -1);
+
+ if (g_queue_is_empty(qd->rcv_trans)) {
+ return -1;
+ }
+ trans = (transaction *) g_queue_pop_head(qd->rcv_trans);
+ if (trans == NULL) {
+ return 0;
+ }
+ if (trans->buf == NULL || trans->buf_len <= 0) {
+ return 0;
+ }
+
+ copy_len = MIN(max_len, trans->buf_len);
+ g_memmove(data, trans->buf, copy_len);
+ *cmd = trans->cmd;
+ *seq = trans->seq;
+
+ g_free(trans->buf);
+ g_free(trans);
+ return copy_len;
+}
+
+/* clean up the packets before login */
+void qq_rcv_trans_remove_all(qq_data *qd)
+{
+ transaction *trans = NULL;
+ gint count = 0;
+
+ g_return_if_fail(qd != NULL);
+
+ /* now clean up my own data structures */
+ if (qd->rcv_trans != NULL) {
+ while (NULL != (trans = g_queue_pop_tail(qd->rcv_trans))) {
+ g_free(trans->buf);
+ g_free(trans);
+ count++;
+ }
+ g_queue_free(qd->rcv_trans);
+ }
+ purple_debug(PURPLE_DEBUG_INFO, "QQ", "%d packets in receive tranactions are freed!\n", count);
+}
diff --git a/libpurple/protocols/qq/sendqueue.h b/libpurple/protocols/qq/qq_trans.h
index 8111cfa02b..a6bbf9073f 100644
--- a/libpurple/protocols/qq/sendqueue.h
+++ b/libpurple/protocols/qq/qq_trans.h
@@ -1,5 +1,5 @@
/**
- * @file sendqueue.h
+ * @file qq_trans.h
*
* purple
*
@@ -28,23 +28,15 @@
#include <glib.h>
#include "qq.h"
-#define QQ_SENDQUEUE_TIMEOUT 5000 /* in 1/1000 sec */
+void qq_send_trans_append(qq_data *qd, guint8 *buf, gint bus_len, guint16 cmd, guint16 seq);
+void qq_send_trans_remove(qq_data *qd, gpointer data);
+gpointer qq_send_trans_find(qq_data *qd, guint16 seq);
+void qq_send_trans_remove_all(qq_data *qd);
-typedef struct _qq_sendpacket qq_sendpacket;
+gint qq_send_trans_scan(qq_data *qd, gint *start, guint8 *buf, gint maxlen, guint16 *cmd, gint *retries);
-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);
+void qq_rcv_trans_push(qq_data *qd, guint16 cmd, guint16 seq, guint8 *data, gint data_len);
+gint qq_rcv_trans_pop(qq_data *qd, guint16 *cmd, guint16* seq, guint8 *data, gint max_len);
+void qq_rcv_trans_remove_all(qq_data *qd);
#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..e5a41efc96 100644
--- a/libpurple/protocols/qq/send_file.c
+++ b/libpurple/protocols/qq/send_file.c
@@ -36,7 +36,7 @@
#include "im.h"
#include "keep_alive.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;
@@ -155,7 +156,7 @@ static void _qq_xfer_recv_packet(gpointer data, gint source, PurpleInputConditio
gint size;
/* FIXME: It seems that the transfer never use a packet
* larger than 1500 bytes, so if it happened to be a
- * larger packet, either error occured or protocol should
+ * larger packet, either error occurred or protocol should
* be modified
*/
ft_info *info;
@@ -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;
@@ -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 = g_newa(ft_info, 1);
info->local_internet_ip = g_ntohl(inet_addr(qd->my_ip));
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);
@@ -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..e3ed1723c9 100644
--- a/libpurple/protocols/qq/send_file.h
+++ b/libpurple/protocols/qq/send_file.h
@@ -66,20 +66,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/sys_msg.c b/libpurple/protocols/qq/sys_msg.c
index fe63606c40..008751b983 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"
@@ -120,27 +120,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);
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..5757855f72 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"
@@ -40,6 +39,17 @@
#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));
+ }
+ */
+
gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount)
{
gint index;
@@ -113,26 +123,6 @@ 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)
-{
- 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);
-
- return g_memdup(md5_str, QQ_KEY_LENGTH);
-}
-
/* 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)
@@ -194,7 +184,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 +205,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 +284,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;
@@ -331,6 +321,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..1ba7af2332 100644
--- a/libpurple/protocols/qq/utils.h
+++ b/libpurple/protocols/qq/utils.h
@@ -28,12 +28,13 @@
#include <stdio.h>
#include <glib.h>
+#include "debug.h"
+
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 +45,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/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/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 e8922f0498..0598d60852 100644
--- a/libpurple/protocols/yahoo/yahoo.c
+++ b/libpurple/protocols/yahoo/yahoo.c
@@ -777,6 +777,7 @@ static void yahoo_process_message(PurpleConnection *gc, struct yahoo_packet *pkt
list = g_slist_append(list, im);
im->from = pair->value;
im->time = time(NULL);
+ im->utf8 = TRUE;
}
if (pair->key == 97)
if (im)
diff --git a/libpurple/protocols/yahoo/yahoo_picture.c b/libpurple/protocols/yahoo/yahoo_picture.c
index ef29905f92..90f2bfedac 100644
--- a/libpurple/protocols/yahoo/yahoo_picture.c
+++ b/libpurple/protocols/yahoo/yahoo_picture.c
@@ -137,6 +137,9 @@ void yahoo_process_picture(PurpleConnection *gc, struct yahoo_packet *pkt)
if (url_data != NULL) {
yd = gc->proto_data;
yd->url_datas = g_slist_prepend(yd->url_datas, url_data);
+ } else {
+ g_free(data->who);
+ g_free(data);
}
} else if (who && send_icon_info) {
yahoo_send_picture_info(gc, who);
@@ -244,13 +247,12 @@ void yahoo_process_picture_upload(PurpleConnection *gc, struct yahoo_packet *pkt
}
if (url) {
- if (yd->picture_url)
- g_free(yd->picture_url);
+ g_free(yd->picture_url);
yd->picture_url = g_strdup(url);
purple_account_set_string(account, YAHOO_PICURL_SETTING, url);
purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, yd->picture_checksum);
- yahoo_send_picture_update(gc, 2);
yahoo_send_picture_checksum(gc);
+ yahoo_send_picture_update(gc, 2);
}
}
@@ -402,8 +404,15 @@ static void yahoo_buddy_icon_upload_reading(gpointer data, gint source, PurpleIn
if (ret < 0 && errno == EAGAIN)
return;
- else if (ret <= 0)
+ else if (ret <= 0) {
+ purple_debug_info("yahoo", "Buddy icon upload response (%d) bytes (> ~400 indicates failure):\n%.*s\n",
+ d->str->len, d->str->len, d->str->str);
+
yahoo_buddy_icon_upload_data_free(d);
+ return;
+ }
+
+ g_string_append_len(d->str, buf, ret);
}
static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleInputCondition condition)
@@ -421,6 +430,7 @@ static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleIn
if (wrote < 0 && errno == EAGAIN)
return;
if (wrote <= 0) {
+ purple_debug_info("yahoo", "Error uploading buddy icon.\n");
yahoo_buddy_icon_upload_data_free(d);
return;
}
@@ -428,6 +438,9 @@ static void yahoo_buddy_icon_upload_pending(gpointer data, gint source, PurpleIn
if (d->pos >= d->str->len) {
purple_debug_misc("yahoo", "Finished uploading buddy icon.\n");
purple_input_remove(d->watcher);
+ /* Clean out the sent buffer and reuse it to read the result */
+ g_string_free(d->str, TRUE);
+ d->str = g_string_new("");
d->watcher = purple_input_add(d->fd, PURPLE_INPUT_READ, yahoo_buddy_icon_upload_reading, d);
}
}
@@ -436,16 +449,16 @@ static void yahoo_buddy_icon_upload_connected(gpointer data, gint source, const
{
struct yahoo_buddy_icon_upload_data *d = data;
struct yahoo_packet *pkt;
- gchar *size, *header;
+ gchar *tmp, *header;
guchar *pkt_buf;
const char *host;
int port;
- size_t content_length, pkt_buf_len;
- PurpleConnection *gc;
+ gsize pkt_buf_len;
+ PurpleConnection *gc = d->gc;
PurpleAccount *account;
struct yahoo_data *yd;
+ gboolean use_whole_url = FALSE;
- gc = d->gc;
account = purple_connection_get_account(gc);
yd = gc->proto_data;
@@ -457,44 +470,55 @@ static void yahoo_buddy_icon_upload_connected(gpointer data, gint source, const
yahoo_buddy_icon_upload_data_free(d);
return;
}
+ /* use whole URL if using HTTP Proxy */
+ if ((gc->account->proxy_info)
+ && (gc->account->proxy_info->type == PURPLE_PROXY_HTTP))
+ use_whole_url = TRUE;
- pkt = yahoo_packet_new(0xc2, YAHOO_STATUS_AVAILABLE, yd->session_id);
+ pkt = yahoo_packet_new(YAHOO_SERVICE_PICTURE_UPLOAD, YAHOO_STATUS_AVAILABLE, yd->session_id);
- size = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len);
+ tmp = g_strdup_printf("%" G_GSIZE_FORMAT, d->str->len);
/* 1 = me, 38 = expire time(?), 0 = me, 28 = size, 27 = filename, 14 = NULL, 29 = data */
yahoo_packet_hash_str(pkt, 1, purple_connection_get_display_name(gc));
yahoo_packet_hash_str(pkt, 38, "604800"); /* time til expire */
purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, time(NULL) + 604800);
yahoo_packet_hash_str(pkt, 0, purple_connection_get_display_name(gc));
- yahoo_packet_hash_str(pkt, 28, size);
- g_free(size);
+ yahoo_packet_hash_str(pkt, 28, tmp);
+ g_free(tmp);
yahoo_packet_hash_str(pkt, 27, d->filename);
yahoo_packet_hash_str(pkt, 14, "");
+ /* 4 padding for the 29 key name */
+ pkt_buf_len = yahoo_packet_build(pkt, 4, FALSE, yd->jp, &pkt_buf);
+ yahoo_packet_free(pkt);
- content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
+ /* header + packet + "29" + 0xc0 + 0x80) + pictureblob */
host = purple_account_get_string(account, "xfer_host", YAHOO_XFER_HOST);
port = purple_account_get_int(account, "xfer_port", YAHOO_XFER_PORT);
- header = g_strdup_printf(
- "POST http://%s:%d/notifyft HTTP/1.0\r\n"
- "Content-length: %" G_GSIZE_FORMAT "\r\n"
- "Host: %s:%d\r\n"
- "Cookie: Y=%s; T=%s\r\n"
- "\r\n",
- host, port, content_length + 4 + d->str->len,
- host, port, yd->cookie_y, yd->cookie_t);
+ tmp = g_strdup_printf("%s:%d", host, port);
+ header = g_strdup_printf("POST %s%s/notifyft HTTP/1.1\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
+ "Cookie: T=%s; Y=%s\r\n"
+ "Host: %s\r\n"
+ "Content-Length: %" G_GSIZE_FORMAT "\r\n"
+ "Cache-Control: no-cache\r\n\r\n",
+ use_whole_url ? "http://" : "", use_whole_url ? tmp : "",
+ yd->cookie_t, yd->cookie_y,
+ tmp,
+ pkt_buf_len + 4 + d->str->len);
+ g_free(tmp);
/* There's no magic here, we just need to prepend in reverse order */
g_string_prepend(d->str, "29\xc0\x80");
- pkt_buf_len = yahoo_packet_build(pkt, 8, FALSE, yd->jp, &pkt_buf);
- yahoo_packet_free(pkt);
g_string_prepend_len(d->str, (char *)pkt_buf, pkt_buf_len);
g_free(pkt_buf);
g_string_prepend(d->str, header);
g_free(header);
+ purple_debug_info("yahoo", "Buddy icon upload data:\n%.*s\n", d->str->len, d->str->str);
+
d->fd = source;
d->watcher = purple_input_add(d->fd, PURPLE_INPUT_WRITE, yahoo_buddy_icon_upload_pending, d);
@@ -525,6 +549,28 @@ void yahoo_buddy_icon_upload(PurpleConnection *gc, struct yahoo_buddy_icon_uploa
}
}
+static int yahoo_buddy_icon_calculate_checksum(const guchar *data, gsize len)
+{
+ /* This code is borrowed from Kopete, which seems to be managing to calculate
+ checksums in such a manner that Yahoo!'s servers are happy */
+
+ const guchar *p = data;
+ int checksum = 0, g, i = len;
+
+ while(i--) {
+ checksum = (checksum << 4) + *p++;
+
+ if((g = (checksum & 0xf0000000)) != 0)
+ checksum ^= g >> 23;
+
+ checksum &= ~g;
+ }
+
+ purple_debug_misc("yahoo", "Calculated buddy icon checksum: %d", checksum);
+
+ return checksum;
+}
+
void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
{
struct yahoo_data *yd = gc->proto_data;
@@ -534,6 +580,8 @@ void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
g_free(yd->picture_url);
yd->picture_url = NULL;
+ /* TODO: don't we have to clear it on the server too?! */
+
purple_account_set_string(account, YAHOO_PICURL_SETTING, NULL);
purple_account_set_int(account, YAHOO_PICCKSUM_SETTING, 0);
purple_account_set_int(account, YAHOO_PICEXPIRE_SETTING, 0);
@@ -549,14 +597,8 @@ void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
int oldcksum = purple_account_get_int(account, YAHOO_PICCKSUM_SETTING, 0);
int expire = purple_account_get_int(account, YAHOO_PICEXPIRE_SETTING, 0);
const char *oldurl = purple_account_get_string(account, YAHOO_PICURL_SETTING, NULL);
- char *iconfile;
- /* TODO: At some point, it'd be nice to fix this for real, or
- * TODO: at least change it to be something like:
- * TODO: purple_imgstore_get_filename(img);
- * TODO: But it would be great if we knew how to calculate the
- * TODO: Checksum correctly. */
- yd->picture_checksum = g_string_hash(s);
+ yd->picture_checksum = yahoo_buddy_icon_calculate_checksum(data, len);
if ((yd->picture_checksum == oldcksum) &&
(expire > (time(NULL) + 60*60*24)) && oldurl)
@@ -569,12 +611,11 @@ void yahoo_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
}
/* We use this solely for sending a filename to the server */
- iconfile = g_strdup(purple_imgstore_get_filename(img));
d = g_new0(struct yahoo_buddy_icon_upload_data, 1);
d->gc = gc;
d->str = s;
d->fd = -1;
- d->filename = iconfile;
+ d->filename = g_strdup(purple_imgstore_get_filename(img));
if (!yd->logged_in) {
yd->picture_upload_todo = d;
diff --git a/libpurple/protocols/yahoo/yahoo_profile.c b/libpurple/protocols/yahoo/yahoo_profile.c
index 7c34861139..3656e907b2 100644
--- a/libpurple/protocols/yahoo/yahoo_profile.c
+++ b/libpurple/protocols/yahoo/yahoo_profile.c
@@ -745,6 +745,7 @@ static char *yahoo_get_photo_url(const char *url_text, const char *name) {
p += 1; /* skip only the ' ' */
q = strchr(p, ' ');
if (q) {
+ g_free(it);
it = g_strndup(p, q - p);
}
}
diff --git a/libpurple/proxy.c b/libpurple/proxy.c
index 9979e0b657..cf32de3978 100644
--- a/libpurple/proxy.c
+++ b/libpurple/proxy.c
@@ -265,6 +265,7 @@ purple_gnome_proxy_get_info(void)
"'manual' but no proxy server is specified. Using "
"Pidgin's proxy settings instead.\n");
g_free(info.host);
+ info.host = NULL;
return purple_global_proxy_get_info();
}
@@ -272,6 +273,7 @@ purple_gnome_proxy_get_info(void)
&info.username, NULL, NULL, NULL))
{
g_free(info.host);
+ info.host = NULL;
return purple_global_proxy_get_info();
}
g_strchomp(info.username);
@@ -280,7 +282,9 @@ purple_gnome_proxy_get_info(void)
&info.password, NULL, NULL, NULL))
{
g_free(info.host);
+ info.host = NULL;
g_free(info.username);
+ info.username = NULL;
return purple_global_proxy_get_info();
}
g_strchomp(info.password);
@@ -289,8 +293,11 @@ purple_gnome_proxy_get_info(void)
&tmp, NULL, NULL, NULL))
{
g_free(info.host);
+ info.host = NULL;
g_free(info.username);
+ info.username = NULL;
g_free(info.password);
+ info.password = NULL;
return purple_global_proxy_get_info();
}
info.port = atoi(tmp);
diff --git a/libpurple/proxy.h b/libpurple/proxy.h
index f72fedaaa2..2145f6038f 100644
--- a/libpurple/proxy.h
+++ b/libpurple/proxy.h
@@ -239,9 +239,9 @@ PurpleProxyInfo *purple_proxy_get_setup(PurpleAccount *account);
* to something descriptive (hopefully).
* @param data User-defined data.
*
- * @return NULL if there was an error, or a reference to a data
- * structure that can be used to cancel the pending
- * connection, if needed.
+ * @return NULL if there was an error, or a reference to an
+ * opaque data structure that can be used to cancel
+ * the pending connection, if needed.
*/
PurpleProxyConnectData *purple_proxy_connect(void *handle,
PurpleAccount *account,
@@ -265,9 +265,9 @@ PurpleProxyConnectData *purple_proxy_connect(void *handle,
* to something descriptive (hopefully).
* @param data User-defined data.
*
- * @return NULL if there was an error, or a reference to a data
- * structure that can be used to cancel the pending
- * connection, if needed.
+ * @return NULL if there was an error, or a reference to an
+ * opaque data structure that can be used to cancel
+ * the pending connection, if needed.
*/
PurpleProxyConnectData *purple_proxy_connect_socks5(void *handle,
PurpleProxyInfo *gpi,
diff --git a/libpurple/prpl.h b/libpurple/prpl.h
index 680d018b59..67c945ab56 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 */
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 51ed6a2175..e4739df518 100644
--- a/libpurple/server.c
+++ b/libpurple/server.c
@@ -151,7 +151,6 @@ int serv_send_im(PurpleConnection *gc, const char *name, const char *message,
*/
auto_reply_pref = purple_prefs_get_string("/purple/away/auto_reply");
if((gc->flags & PURPLE_CONNECTION_AUTO_RESP) &&
- flags & PURPLE_MESSAGE_AUTO_RESP &&
!purple_presence_is_available(presence) &&
strcmp(auto_reply_pref, "never")) {
@@ -728,6 +727,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");
@@ -735,9 +735,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/smiley.c b/libpurple/smiley.c
index 5288dc9ec9..e3dd624cc9 100644
--- a/libpurple/smiley.c
+++ b/libpurple/smiley.c
@@ -641,10 +641,9 @@ purple_smiley_set_data_impl(PurpleSmiley *smiley, guchar *smiley_data,
old_filename = purple_imgstore_get_filename(old_img);
new_filename = purple_imgstore_get_filename(smiley->img);
- if (g_ascii_strcasecmp(old_filename, new_filename)) {
+ if (g_ascii_strcasecmp(old_filename, new_filename))
purple_smiley_data_unstore(old_filename);
- purple_imgstore_unref(old_img);
- }
+ purple_imgstore_unref(old_img);
}
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/status.c b/libpurple/status.c
index 95b8605822..9950644b36 100644
--- a/libpurple/status.c
+++ b/libpurple/status.c
@@ -107,8 +107,6 @@ struct _PurpleStatus
PurpleStatusType *type;
PurplePresence *presence;
- const char *title;
-
gboolean active;
/*
diff --git a/libpurple/util.c b/libpurple/util.c
index 85201f3fbd..dbe639de3c 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;
@@ -1357,6 +1362,14 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
GString *cdata = NULL;
GList *tags = NULL, *tag;
const char *c = html;
+ char quote = '\0';
+
+#define CHECK_QUOTE(ptr) if (*(ptr) == '\'' || *(ptr) == '\"') \
+ quote = *(ptr++); \
+ else \
+ quote = '\0';
+
+#define VALID_CHAR(ptr) (*(ptr) && *(ptr) != quote && (quote || (*(ptr) != ' ' && *(ptr) != '>')))
g_return_if_fail(xhtml_out != NULL || plain_out != NULL);
@@ -1514,38 +1527,37 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
xhtml = g_string_append(xhtml, "<span style='vertical-align:super;'>");
continue;
}
- if(!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
- const char *p = c;
+ if (!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) {
+ const char *p = c + 4;
GString *src = NULL, *alt = NULL;
- while(*p && *p != '>') {
- if(!g_ascii_strncasecmp(p, "src=", strlen("src="))) {
- const char *q = p + strlen("src=");
+ while (*p && *p != '>') {
+ if (!g_ascii_strncasecmp(p, "src=", 4)) {
+ const char *q = p + 4;
if (src)
g_string_free(src, TRUE);
src = g_string_new("");
- if(*q == '\'' || *q == '\"')
- q++;
- while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
src = g_string_append_c(src, *q);
q++;
}
p = q;
- } else if(!g_ascii_strncasecmp(p, "alt=", strlen("alt="))) {
- const char *q = p + strlen("alt=");
+ } else if (!g_ascii_strncasecmp(p, "alt=", 4)) {
+ const char *q = p + 4;
if (alt)
g_string_free(alt, TRUE);
alt = g_string_new("");
- if(*q == '\'' || *q == '\"')
- q++;
- while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
alt = g_string_append_c(alt, *q);
q++;
}
p = q;
+ } else {
+ p++;
}
- p++;
}
- if ((c = strchr(c, '>')) != NULL)
+ if ((c = strchr(p, '>')) != NULL)
c++;
else
c = p;
@@ -1562,21 +1574,20 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
g_string_free(src, TRUE);
continue;
}
- if(!g_ascii_strncasecmp(c, "<a", 2) && (*(c+2) == '>' || *(c+2) == ' ')) {
- const char *p = c;
+ if (!g_ascii_strncasecmp(c, "<a", 2) && (*(c+2) == '>' || *(c+2) == ' ')) {
+ const char *p = c + 2;
struct purple_parse_tag *pt;
- while(*p && *p != '>') {
- if(!g_ascii_strncasecmp(p, "href=", strlen("href="))) {
- const char *q = p + strlen("href=");
+ while (*p && *p != '>') {
+ if (!g_ascii_strncasecmp(p, "href=", 5)) {
+ const char *q = p + 5;
if (url)
g_string_free(url, TRUE);
url = g_string_new("");
if (cdata)
g_string_free(cdata, TRUE);
cdata = g_string_new("");
- if(*q == '\'' || *q == '\"')
- q++;
- while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
int len;
if ((*q == '&') && (purple_markup_unescape_entity(q, &len) == NULL))
url = g_string_append(url, "&amp;");
@@ -1585,10 +1596,11 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
q++;
}
p = q;
+ } else {
+ p++;
}
- p++;
}
- if ((c = strchr(c, '>')) != NULL)
+ if ((c = strchr(p, '>')) != NULL)
c++;
else
c = p;
@@ -1601,55 +1613,48 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
continue;
}
if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) {
- const char *p = c;
+ const char *p = c + 5;
GString *style = g_string_new("");
struct purple_parse_tag *pt;
- while(*p && *p != '>') {
- if(!g_ascii_strncasecmp(p, "back=", strlen("back="))) {
- const char *q = p + strlen("back=");
+ while (*p && *p != '>') {
+ if (!g_ascii_strncasecmp(p, "back=", 5)) {
+ const char *q = p + 5;
GString *color = g_string_new("");
- if(*q == '\'' || *q == '\"')
- q++;
- while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
color = g_string_append_c(color, *q);
q++;
}
g_string_append_printf(style, "background: %s; ", color->str);
g_string_free(color, TRUE);
p = q;
- } else if(!g_ascii_strncasecmp(p, "color=", strlen("color="))) {
- const char *q = p + strlen("color=");
+ } else if (!g_ascii_strncasecmp(p, "color=", 6)) {
+ const char *q = p + 6;
GString *color = g_string_new("");
- if(*q == '\'' || *q == '\"')
- q++;
- while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
color = g_string_append_c(color, *q);
q++;
}
g_string_append_printf(style, "color: %s; ", color->str);
g_string_free(color, TRUE);
p = q;
- } else if(!g_ascii_strncasecmp(p, "face=", strlen("face="))) {
- const char *q = p + strlen("face=");
- gboolean space_allowed = FALSE;
+ } else if (!g_ascii_strncasecmp(p, "face=", 5)) {
+ const char *q = p + 5;
GString *face = g_string_new("");
- if(*q == '\'' || *q == '\"') {
- space_allowed = TRUE;
- q++;
- }
- while(*q && *q != '\"' && *q != '\'' && (space_allowed || *q != ' ')) {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
face = g_string_append_c(face, *q);
q++;
}
g_string_append_printf(style, "font-family: %s; ", g_strstrip(face->str));
g_string_free(face, TRUE);
p = q;
- } else if(!g_ascii_strncasecmp(p, "size=", strlen("size="))) {
- const char *q = p + strlen("size=");
+ } else if (!g_ascii_strncasecmp(p, "size=", 5)) {
+ const char *q = p + 5;
int sz;
const char *size = "medium";
- if(*q == '\'' || *q == '\"')
- q++;
+ CHECK_QUOTE(q);
sz = atoi(q);
switch (sz)
{
@@ -1679,10 +1684,11 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
}
g_string_append_printf(style, "font-size: %s; ", size);
p = q;
+ } else {
+ p++;
}
- p++;
}
- if ((c = strchr(c, '>')) != NULL)
+ if ((c = strchr(p, '>')) != NULL)
c++;
else
c = p;
@@ -1697,24 +1703,23 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
g_string_free(style, TRUE);
continue;
}
- if(!g_ascii_strncasecmp(c, "<body ", 6)) {
- const char *p = c;
+ if (!g_ascii_strncasecmp(c, "<body ", 6)) {
+ const char *p = c + 6;
gboolean did_something = FALSE;
- while(*p && *p != '>') {
- if(!g_ascii_strncasecmp(p, "bgcolor=", strlen("bgcolor="))) {
- const char *q = p + strlen("bgcolor=");
+ while (*p && *p != '>') {
+ if (!g_ascii_strncasecmp(p, "bgcolor=", 8)) {
+ const char *q = p + 8;
struct purple_parse_tag *pt = g_new0(struct purple_parse_tag, 1);
GString *color = g_string_new("");
- if(*q == '\'' || *q == '\"')
- q++;
- while(*q && *q != '\"' && *q != '\'' && *q != ' ') {
+ CHECK_QUOTE(q);
+ while (VALID_CHAR(q)) {
color = g_string_append_c(color, *q);
q++;
}
- if(xhtml)
+ if (xhtml)
g_string_append_printf(xhtml, "<span style='background: %s;'>", g_strstrip(color->str));
g_string_free(color, TRUE);
- if ((c = strchr(c, '>')) != NULL)
+ if ((c = strchr(p, '>')) != NULL)
c++;
else
c = p;
@@ -1726,7 +1731,7 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
}
p++;
}
- if(did_something) continue;
+ if (did_something) continue;
}
/* this has to come after the special case for bgcolor */
ALLOW_TAG("body");
@@ -1789,6 +1794,8 @@ purple_markup_html_to_xhtml(const char *html, char **xhtml_out,
g_string_free(url, TRUE);
if (cdata)
g_string_free(cdata, TRUE);
+#undef CHECK_QUOTE
+#undef VALID_CHAR
}
/* The following are probably reasonable changes:
@@ -3529,7 +3536,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;
@@ -3703,7 +3710,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;
diff --git a/libpurple/win32/global.mak b/libpurple/win32/global.mak
index 5157881336..673d125a68 100644
--- a/libpurple/win32/global.mak
+++ b/libpurple/win32/global.mak
@@ -112,4 +112,4 @@ PIDGIN_COMMON_TARGETS := $(PURPLE_TOP)/win32/targets.mak
MINGW_MAKEFILE := Makefile.mingw
INSTALL_PIXMAPS ?= 1
-
+INSTALL_SSL_CERTIFICATES ?= 1
diff --git a/pidgin/gtkaccount.c b/pidgin/gtkaccount.c
index ff504b7e1c..963ea2a520 100644
--- a/pidgin/gtkaccount.c
+++ b/pidgin/gtkaccount.c
@@ -2191,8 +2191,7 @@ create_accounts_list(AccountsWindow *dialog)
G_TYPE_STRING, /* COLUMN_SCREENNAME */
G_TYPE_BOOLEAN, /* COLUMN_ENABLED */
G_TYPE_STRING, /* COLUMN_PROTOCOL */
- G_TYPE_POINTER, /* COLUMN_DATA */
- G_TYPE_POINTER /* COLUMN_PULSE_DATA */
+ G_TYPE_POINTER /* COLUMN_DATA */
);
/* And now the actual treeview */
diff --git a/pidgin/gtkblist.c b/pidgin/gtkblist.c
index e4a81bf99c..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;
@@ -3259,18 +3260,18 @@ static GtkItemFactoryEntry blist_menu[] =
/* Accounts menu */
{ N_("/_Accounts"), NULL, NULL, 0, "<Branch>", NULL },
- { N_("/Accounts/Manage"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
+ { N_("/Accounts/Manage Accounts"), "<CTL>A", pidgin_accounts_window_show, 0, "<Item>", NULL },
/* Tools */
{ N_("/_Tools"), NULL, NULL, 0, "<Branch>", NULL },
{ N_("/Tools/Buddy _Pounces"), NULL, pidgin_pounces_manager_show, 1, "<Item>", NULL },
{ N_("/Tools/_Certificates"), NULL, pidgin_certmgr_show, 0, "<Item>", NULL },
- { N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
{ N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS },
{ N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES },
{ N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL },
+ { N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
{ "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
- { N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<Item>", NULL },
+ { N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER },
{ N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
{ N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 3, "<Item>", NULL },
{ "/Tools/sep3", NULL, NULL, 0, "<Separator>", 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);
@@ -5305,7 +5313,7 @@ static void pidgin_blist_show(PurpleBuddyList *list)
tmp = g_strdup_printf(_("<span weight='bold' size='larger'>Welcome to %s!</span>\n\n"
"You have no accounts enabled. Enable your IM accounts from the "
- "<b>Accounts</b> window at <b>Accounts->Manage</b>. Once you "
+ "<b>Accounts</b> window at <b>Accounts->Manage Accounts</b>. Once you "
"enable accounts, you'll be able to sign on, set your status, "
"and talk to your friends."), PIDGIN_NAME);
pretty = pidgin_make_pretty_arrows(tmp);
@@ -6474,6 +6482,10 @@ add_buddy_cb(GtkWidget *w, int resp, PidginAddBuddyData *data)
purple_blist_add_buddy(b, NULL, g, NULL);
purple_account_add_buddy(data->account, b);
+ /* Offer to merge people with the same alias. */
+ if (whoalias != NULL)
+ gtk_blist_auto_personize((PurpleBlistNode *)g, whoalias);
+
/*
* XXX
* It really seems like it would be better if the call to
@@ -6984,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);
}
@@ -7609,12 +7628,58 @@ pidgin_blist_update_accounts_menu(void)
for (l = gtk_container_get_children(GTK_CONTAINER(accountmenu)); l; l = g_list_delete_link(l, l)) {
menuitem = l->data;
- if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage")))
+ if (menuitem != gtk_item_factory_get_widget(gtkblist->ift, N_("/Accounts/Manage Accounts")))
gtk_widget_destroy(menuitem);
}
for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
char *buf = NULL;
+ GtkWidget *image = NULL;
+ PurpleAccount *account = NULL;
+ GdkPixbuf *pixbuf = NULL;
+
+ account = accounts->data;
+
+ if(!purple_account_get_enabled(account, PIDGIN_UI)) {
+ if (!disabled_accounts) {
+ menuitem = gtk_menu_item_new_with_label(_("Enable Account"));
+ gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
+ gtk_widget_show(menuitem);
+
+ submenu = gtk_menu_new();
+ gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
+ gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account"));
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
+ gtk_widget_show(submenu);
+
+ disabled_accounts = TRUE;
+ }
+
+ buf = g_strconcat(purple_account_get_username(account), " (",
+ purple_account_get_protocol_name(account), ")", NULL);
+ menuitem = gtk_image_menu_item_new_with_label(buf);
+ g_free(buf);
+ pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
+ if (pixbuf != NULL)
+ {
+ if (!purple_account_is_connected(account))
+ gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
+ image = gtk_image_new_from_pixbuf(pixbuf);
+ g_object_unref(G_OBJECT(pixbuf));
+ gtk_widget_show(image);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
+ }
+ g_signal_connect(G_OBJECT(menuitem), "activate",
+ G_CALLBACK(enable_account_cb), account);
+ gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
+ gtk_widget_show(menuitem);
+ }
+ }
+
+ pidgin_separator(accountmenu);
+
+ for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
+ char *buf = NULL;
char *accel_path_buf = NULL;
GtkWidget *image = NULL;
PurpleConnection *gc = NULL;
@@ -7684,51 +7749,6 @@ pidgin_blist_update_accounts_menu(void)
}
}
- if(disabled_accounts) {
- pidgin_separator(accountmenu);
- menuitem = gtk_menu_item_new_with_label(_("Enable Account"));
- gtk_menu_shell_append(GTK_MENU_SHELL(accountmenu), menuitem);
- gtk_widget_show(menuitem);
-
- submenu = gtk_menu_new();
- gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
- gtk_menu_set_accel_path(GTK_MENU(submenu), N_("<PurpleMain>/Accounts/Enable Account"));
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
- gtk_widget_show(submenu);
-
- for (accounts = purple_accounts_get_all(); accounts; accounts = accounts->next) {
- char *buf = NULL;
- GtkWidget *image = NULL;
- PurpleAccount *account = NULL;
- GdkPixbuf *pixbuf = NULL;
-
- account = accounts->data;
-
- if(!purple_account_get_enabled(account, PIDGIN_UI)) {
-
- disabled_accounts = TRUE;
-
- buf = g_strconcat(purple_account_get_username(account), " (",
- purple_account_get_protocol_name(account), ")", NULL);
- menuitem = gtk_image_menu_item_new_with_label(buf);
- g_free(buf);
- pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_SMALL);
- if (pixbuf != NULL)
- {
- if (!purple_account_is_connected(account))
- gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
- image = gtk_image_new_from_pixbuf(pixbuf);
- g_object_unref(G_OBJECT(pixbuf));
- gtk_widget_show(image);
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
- }
- g_signal_connect(G_OBJECT(menuitem), "activate",
- G_CALLBACK(enable_account_cb), account);
- gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
- gtk_widget_show(menuitem);
- }
- }
- }
}
static GList *plugin_submenus = NULL;
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
index 6eb4d21976..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,
@@ -3156,7 +3176,8 @@ populate_menu_with_options(GtkWidget *menu, PidginConversation *gtkconv, gboolea
PurpleAccount *account = purple_conversation_get_account(conv);
PurplePlugin *prpl = purple_find_prpl(purple_account_get_protocol_id(account));
PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
- if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_info_defaults)) {
+ if (purple_account_get_connection(account) != NULL &&
+ PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, chat_info_defaults)) {
components = prpl_info->chat_info_defaults(purple_account_get_connection(account),
purple_conversation_get_name(conv));
} else {
diff --git a/pidgin/gtkdialogs.c b/pidgin/gtkdialogs.c
index 553d9079e7..78b3fbbd45 100644
--- a/pidgin/gtkdialogs.c
+++ b/pidgin/gtkdialogs.c
@@ -75,7 +75,7 @@ static const struct developer developers[] = {
{"Thomas Butter", N_("developer"), NULL},
{"Ka-Hing Cheung", N_("developer"), NULL},
{"Sadrul Habib Chowdhury", N_("developer"), NULL},
- {"Mark 'KingAnt' Doliner", N_("developer"), NULL},
+ {"Mark 'KingAnt' Doliner", N_("developer"), "mark@kingant.net"},
{"Sean Egan", N_("developer"), "sean.egan@gmail.com"},
{"Casey Harkins", N_("developer"), NULL},
{"Gary 'grim' Kramlich", N_("developer"), NULL},
diff --git a/pidgin/gtkft.c b/pidgin/gtkft.c
index bdc1049f12..49890981db 100644
--- a/pidgin/gtkft.c
+++ b/pidgin/gtkft.c
@@ -226,8 +226,10 @@ update_title_progress(PidginXferDialog *dialog)
total_pct = 100 * total_bytes_xferred / total_file_size;
}
- title = g_strdup_printf(_("File Transfers - %d%% of %d files"),
- total_pct, num_active_xfers);
+ title = g_strdup_printf(ngettext("File Transfers - %d%% of %d file",
+ "File Transfers - %d%% of %d files",
+ num_active_xfers),
+ total_pct, num_active_xfers);
gtk_window_set_title(GTK_WINDOW(dialog->window), title);
g_free(title);
} else {
diff --git a/pidgin/gtkimhtml.c b/pidgin/gtkimhtml.c
index 02b6df2a24..5d42fe09db 100644
--- a/pidgin/gtkimhtml.c
+++ b/pidgin/gtkimhtml.c
@@ -514,6 +514,7 @@ gtk_imhtml_tip (gpointer data)
tmp);
g_free(tmp);
+ g_object_unref(layout);
return FALSE;
}
@@ -551,6 +552,7 @@ gtk_imhtml_tip (gpointer data)
gtk_widget_show (imhtml->tip_window);
pango_font_metrics_unref(font_metrics);
+ g_object_unref(font);
g_object_unref(layout);
return FALSE;
@@ -766,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;
@@ -4864,7 +4866,7 @@ void gtk_imhtml_insert_smiley_at_iter(GtkIMHtml *imhtml, const char *sml, char *
gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(imhtml), ebox ? ebox : icon, anchor);
} else if (imhtml_smiley != NULL && (imhtml->format_functions & GTK_IMHTML_SMILEY)) {
anchor = gtk_text_buffer_create_child_anchor(imhtml->text_buffer, iter);
- imhtml_smiley->anchors = g_slist_append(imhtml_smiley->anchors, anchor);
+ imhtml_smiley->anchors = g_slist_append(imhtml_smiley->anchors, g_object_ref(anchor));
if (ebox) {
GtkWidget *img = gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_MENU);
char *text = g_strdup(unescaped);
@@ -5023,9 +5025,9 @@ static const gchar *tag_to_html_start(GtkTextTag *tag)
str += g_snprintf(str, sizeof(buf) - (str - buf),
"color: #%02x%02x%02x;",
color->red >> 8, color->green >> 8, color->blue >> 8);
- gdk_color_free(color);
empty = FALSE;
}
+ gdk_color_free(color);
/* Background color */
g_object_get(obj, "background-set", &isset, "background-gdk", &color, NULL);
@@ -5033,9 +5035,9 @@ static const gchar *tag_to_html_start(GtkTextTag *tag)
str += g_snprintf(str, sizeof(buf) - (str - buf),
"background: #%02x%02x%02x;",
color->red >> 8, color->green >> 8, color->blue >> 8);
- gdk_color_free(color);
empty = FALSE;
}
+ gdk_color_free(color);
/* Underline */
g_object_get(obj, "underline-set", &isset, "underline", &ivalue, NULL);
@@ -5046,6 +5048,7 @@ static const gchar *tag_to_html_start(GtkTextTag *tag)
break;
default:
str += g_snprintf(str, sizeof(buf) - (str - buf), "text-decoration: underline;");
+ empty = FALSE;
}
}
@@ -5097,6 +5100,38 @@ static const gchar *tag_to_html_end(GtkTextTag *tag)
}
}
+typedef struct {
+ GtkTextTag *tag;
+ char *end;
+ char *start;
+} PidginTextTagData;
+
+static PidginTextTagData *text_tag_data_new(GtkTextTag *tag)
+{
+ const char *start, *end;
+ PidginTextTagData *ret = NULL;
+
+ start = tag_to_html_start(tag);
+ if (!start || !*start)
+ return NULL;
+ end = tag_to_html_end(tag);
+ if (!end || !*end)
+ return NULL;
+
+ ret = g_new0(PidginTextTagData, 1);
+ ret->start = g_strdup(start);
+ ret->end = g_strdup(end);
+ ret->tag = tag;
+ return ret;
+}
+
+static void text_tag_data_destroy(PidginTextTagData *data)
+{
+ g_free(data->start);
+ g_free(data->end);
+ g_free(data);
+}
+
static gboolean tag_ends_here(GtkTextTag *tag, GtkTextIter *iter, GtkTextIter *niter)
{
return ((gtk_text_iter_has_tag(iter, GTK_TEXT_TAG(tag)) &&
@@ -5117,12 +5152,11 @@ char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkText
gboolean is_rtl_message = FALSE;
GString *str = g_string_new("");
GSList *tags, *sl;
- GQueue *q, *r;
+ GQueue *q;
GtkTextTag *tag;
+ PidginTextTagData *tagdata;
q = g_queue_new();
- r = g_queue_new();
-
gtk_text_iter_order(start, end);
non_neutral_iter = next_iter = iter = *start;
@@ -5145,9 +5179,11 @@ char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkText
for (sl = tags; sl; sl = sl->next) {
tag = sl->data;
if (!gtk_text_iter_toggles_tag(start, GTK_TEXT_TAG(tag))) {
- if (strlen(tag_to_html_end(tag)) > 0)
- g_string_append(str, tag_to_html_start(tag));
- g_queue_push_tail(q, tag);
+ PidginTextTagData *data = text_tag_data_new(tag);
+ if (data) {
+ g_string_append(str, data->start);
+ g_queue_push_tail(q, data);
+ }
}
}
g_slist_free(tags);
@@ -5159,13 +5195,14 @@ char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkText
for (sl = tags; sl; sl = sl->next) {
tag = sl->data;
if (gtk_text_iter_begins_tag(&iter, GTK_TEXT_TAG(tag))) {
- if (strlen(tag_to_html_end(tag)) > 0)
- g_string_append(str, tag_to_html_start(tag));
- g_queue_push_tail(q, tag);
+ PidginTextTagData *data = text_tag_data_new(tag);
+ if (data) {
+ g_string_append(str, data->start);
+ g_queue_push_tail(q, data);
+ }
}
}
-
if (c == 0xFFFC) {
GtkTextChildAnchor* anchor = gtk_text_iter_get_child_anchor(&iter);
if (anchor) {
@@ -5191,28 +5228,33 @@ char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkText
for (sl = tags; sl; sl = sl->next) {
tag = sl->data;
/** don't worry about non-printing tags ending */
- if (tag_ends_here(tag, &iter, &next_iter) && strlen(tag_to_html_end(tag)) > 0) {
-
- GtkTextTag *tmp;
+ if (tag_ends_here(tag, &iter, &next_iter) &&
+ strlen(tag_to_html_end(tag)) > 0 &&
+ strlen(tag_to_html_start(tag)) > 0) {
- while ((tmp = g_queue_pop_tail(q)) != tag) {
- if (tmp == NULL)
- break;
+ PidginTextTagData *tmp;
+ GQueue *r = g_queue_new();
- if (!tag_ends_here(tmp, &iter, &next_iter) && strlen(tag_to_html_end(tmp)) > 0)
+ while ((tmp = g_queue_pop_tail(q)) && tmp->tag != tag) {
+ g_string_append(str, tmp->end);
+ if (!tag_ends_here(tmp->tag, &iter, &next_iter))
g_queue_push_tail(r, tmp);
- g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tmp)));
+ else
+ text_tag_data_destroy(tmp);
}
if (tmp == NULL)
purple_debug_warning("gtkimhtml", "empty queue, more closing tags than open tags!\n");
- else
- g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tag)));
+ else {
+ g_string_append(str, tmp->end);
+ text_tag_data_destroy(tmp);
+ }
while ((tmp = g_queue_pop_head(r))) {
- g_string_append(str, tag_to_html_start(GTK_TEXT_TAG(tmp)));
+ g_string_append(str, tmp->start);
g_queue_push_tail(q, tmp);
}
+ g_queue_free(r);
}
}
@@ -5221,15 +5263,16 @@ char *gtk_imhtml_get_markup_range(GtkIMHtml *imhtml, GtkTextIter *start, GtkText
gtk_text_iter_forward_char(&next_iter);
}
- while ((tag = g_queue_pop_tail(q)))
- g_string_append(str, tag_to_html_end(GTK_TEXT_TAG(tag)));
+ while ((tagdata = g_queue_pop_tail(q))) {
+ g_string_append(str, tagdata->end);
+ text_tag_data_destroy(tagdata);
+ }
/* Bi-directional text support - close tags */
if (is_rtl_message)
g_string_append(str, "</SPAN>");
g_queue_free(q);
- g_queue_free(r);
return g_string_free(str, FALSE);
}
@@ -5465,8 +5508,11 @@ static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data
}
for (current = smiley->anchors; current; current = g_slist_next(current)) {
-
- icon = gtk_image_new_from_animation(smiley->icon);
+ anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
+ if (gtk_text_child_anchor_get_deleted(anchor))
+ icon = NULL;
+ else
+ icon = gtk_image_new_from_animation(smiley->icon);
#ifdef DEBUG_CUSTOM_SMILEY
purple_debug_info("custom-smiley", "gtk_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n",
@@ -5476,7 +5522,6 @@ static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data
GList *wids;
gtk_widget_show(icon);
- anchor = GTK_TEXT_CHILD_ANCHOR(current->data);
wids = gtk_text_child_anchor_get_widgets(anchor);
g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", purple_unescape_html(smiley->smile), g_free);
@@ -5493,7 +5538,7 @@ static void gtk_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data
}
g_list_free(wids);
}
-
+ g_object_unref(anchor);
}
g_slist_free(smiley->anchors);
diff --git a/pidgin/gtkimhtml.h b/pidgin/gtkimhtml.h
index 9083cdbee4..725e71eddd 100644
--- a/pidgin/gtkimhtml.h
+++ b/pidgin/gtkimhtml.h
@@ -854,11 +854,36 @@ char *gtk_imhtml_get_text(GtkIMHtml *imhtml, GtkTextIter *start, GtkTextIter *st
*/
void gtk_imhtml_setup_entry(GtkIMHtml *imhtml, PurpleConnectionFlags flags);
+/**
+ * Create a new GtkIMHtmlSmiley.
+ *
+ * @param file The image file for the smiley
+ * @param shortcut The key shortcut for the smiley
+ * @param hide @c TRUE if the smiley should be hidden in the smiley dialog, @c FALSE otherwise
+ * @param flags The smiley flags
+ *
+ * @return The newly created smiley
+ * @since 2.5.0
+ */
GtkIMHtmlSmiley *gtk_imhtml_smiley_create(const char *file, const char *shortcut, gboolean hide,
GtkIMHtmlSmileyFlags flags);
+/**
+ * Reload the image data for the smiley.
+ *
+ * @param smiley The smiley to reload
+ *
+ * @since 2.5.0
+ */
void gtk_imhtml_smiley_reload(GtkIMHtmlSmiley *smiley);
+/**
+ * Destroy a GtkIMHtmlSmiley.
+ *
+ * @param smiley The smiley to destroy
+ *
+ * @since 2.5.0
+ */
void gtk_imhtml_smiley_destroy(GtkIMHtmlSmiley *smiley);
/*@}*/
diff --git a/pidgin/gtkimhtmltoolbar.c b/pidgin/gtkimhtmltoolbar.c
index 8983dcedd0..c4156ff3be 100644
--- a/pidgin/gtkimhtmltoolbar.c
+++ b/pidgin/gtkimhtmltoolbar.c
@@ -614,8 +614,7 @@ struct smiley_button_list {
static struct smiley_button_list *
sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar,
- int *width, const GtkIMHtmlSmiley *smiley,
- const GSList *custom_smileys)
+ int *width, const GtkIMHtmlSmiley *smiley)
{
GtkWidget *image;
GtkWidget *button;
@@ -625,6 +624,7 @@ sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar,
const gchar *filename = smiley->file;
gchar *face = smiley->smile;
PurpleSmiley *psmiley = NULL;
+ gboolean supports_custom = (gtk_imhtml_get_format_functions(GTK_IMHTML(toolbar->imhtml)) & GTK_IMHTML_CUSTOM_SMILEY);
cur = g_new0(struct smiley_button_list, 1);
it = ls;
@@ -678,10 +678,12 @@ sort_smileys(struct smiley_button_list *ls, GtkIMHtmlToolbar *toolbar,
/* If this is a "non-custom" smiley, check to see if its shortcut is
"shadowed" by any custom smiley. This can only happen if the connection
is custom smiley-enabled */
- if (psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
- gtk_tooltips_set_tip(toolbar->tooltips, button,
- _("This smiley is disabled because a custom smiley exists for this shortcut."),
- NULL);
+ if (supports_custom && psmiley && !(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) {
+ gchar tip[128];
+ g_snprintf(tip, sizeof(tip),
+ _("This smiley is disabled because a custom smiley exists for this shortcut:\n %s"),
+ face);
+ gtk_tooltips_set_tip(toolbar->tooltips, button, tip, NULL);
gtk_widget_set_sensitive(button, FALSE);
} else if (psmiley) {
/* Remove the button if the smiley is destroyed */
@@ -783,11 +785,14 @@ insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
else
smileys = pidgin_themes_get_proto_smileys(NULL);
+ /* Note: prepend smileys to list to avoid O(n^2) overhead when there is
+ a large number of smileys... need to revers the list after for the dialog
+ work... */
while(smileys) {
GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) smileys->data;
if(!smiley->hidden) {
if(smiley_is_unique(unique_smileys, smiley)) {
- unique_smileys = g_slist_append(unique_smileys, smiley);
+ unique_smileys = g_slist_prepend(unique_smileys, smiley);
}
}
smileys = smileys->next;
@@ -800,9 +805,12 @@ insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
for (iterator = custom_smileys ; iterator ;
iterator = g_slist_next(iterator)) {
GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) iterator->data;
- unique_smileys = g_slist_append(unique_smileys, smiley);
+ unique_smileys = g_slist_prepend(unique_smileys, smiley);
}
}
+
+ /* we need to reverse the list to get the smileys in the correct order */
+ unique_smileys = g_slist_reverse(unique_smileys);
dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE);
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
@@ -834,7 +842,7 @@ insert_smiley_cb(GtkWidget *smiley, GtkIMHtmlToolbar *toolbar)
while (unique_smileys) {
GtkIMHtmlSmiley *smiley = (GtkIMHtmlSmiley *) unique_smileys->data;
if (!smiley->hidden) {
- ls = sort_smileys(ls, toolbar, &max_line_width, smiley, custom_smileys);
+ ls = sort_smileys(ls, toolbar, &max_line_width, smiley);
}
unique_smileys = g_slist_delete_link(unique_smileys, unique_smileys);
}
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/gtksavedstatuses.c b/pidgin/gtksavedstatuses.c
index 6d85551ad6..2ae61933a8 100644
--- a/pidgin/gtksavedstatuses.c
+++ b/pidgin/gtksavedstatuses.c
@@ -59,6 +59,7 @@ enum
STATUS_WINDOW_COLUMN_MESSAGE,
/** A hidden column containing a pointer to the editor for this saved status. */
STATUS_WINDOW_COLUMN_WINDOW,
+ STATUS_WINDOW_COLUMN_ICON,
STATUS_WINDOW_NUM_COLUMNS
};
@@ -80,6 +81,7 @@ enum
STATUS_EDITOR_COLUMN_STATUS_ID,
STATUS_EDITOR_COLUMN_STATUS_NAME,
STATUS_EDITOR_COLUMN_STATUS_MESSAGE,
+ STATUS_EDITOR_COLUMN_STATUS_ICON,
STATUS_EDITOR_NUM_COLUMNS
};
@@ -392,12 +394,35 @@ status_selected_cb(GtkTreeSelection *sel, gpointer user_data)
g_list_free(sel_paths);
}
+static const gchar *
+get_stock_icon_from_primitive(PurpleStatusPrimitive type)
+{
+ switch (type) {
+ case PURPLE_STATUS_AVAILABLE:
+ return PIDGIN_STOCK_STATUS_AVAILABLE;
+ case PURPLE_STATUS_AWAY:
+ return PIDGIN_STOCK_STATUS_AWAY;
+ case PURPLE_STATUS_EXTENDED_AWAY:
+ return PIDGIN_STOCK_STATUS_XA;
+ case PURPLE_STATUS_INVISIBLE:
+ return PIDGIN_STOCK_STATUS_INVISIBLE;
+ case PURPLE_STATUS_OFFLINE:
+ return PIDGIN_STOCK_STATUS_OFFLINE;
+ case PURPLE_STATUS_UNAVAILABLE:
+ return PIDGIN_STOCK_STATUS_BUSY;
+ default:
+ /* this shouldn't happen */
+ return NULL;
+ }
+}
+
static void
add_status_to_saved_status_list(GtkListStore *model, PurpleSavedStatus *saved_status)
{
GtkTreeIter iter;
const char *title;
const char *type;
+ const gchar *icon;
char *message;
if (purple_savedstatus_is_transient(saved_status))
@@ -406,14 +431,16 @@ add_status_to_saved_status_list(GtkListStore *model, PurpleSavedStatus *saved_st
title = purple_savedstatus_get_title(saved_status);
type = purple_primitive_get_name_from_type(purple_savedstatus_get_type(saved_status));
message = purple_markup_strip_html(purple_savedstatus_get_message(saved_status));
+ icon = get_stock_icon_from_primitive(purple_savedstatus_get_type(saved_status));
gtk_list_store_append(model, &iter);
gtk_list_store_set(model, &iter,
+ STATUS_WINDOW_COLUMN_ICON, icon,
STATUS_WINDOW_COLUMN_TITLE, title,
STATUS_WINDOW_COLUMN_TYPE, type,
STATUS_WINDOW_COLUMN_MESSAGE, message,
-1);
- free(message);
+ g_free(message);
}
static void
@@ -479,7 +506,8 @@ create_saved_status_list(StatusWindow *dialog)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
- G_TYPE_POINTER);
+ G_TYPE_POINTER,
+ G_TYPE_STRING);
/* Create the treeview */
treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
@@ -517,6 +545,10 @@ create_saved_status_list(StatusWindow *dialog)
gtk_tree_view_column_set_sort_column_id(column,
STATUS_WINDOW_COLUMN_TYPE);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute(column, renderer, "stock-id",
+ STATUS_WINDOW_COLUMN_ICON);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_add_attribute(column, renderer, "text",
@@ -717,8 +749,8 @@ status_editor_remove_dialog(StatusEditor *dialog)
}
-static gboolean
-status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+static void
+status_editor_destroy_cb(GtkWidget *widget, gpointer user_data)
{
StatusEditor *dialog = user_data;
@@ -726,19 +758,13 @@ status_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
g_free(dialog->original_title);
g_object_unref(G_OBJECT(dialog->model));
g_free(dialog);
-
- return FALSE;
}
static void
status_editor_cancel_cb(GtkButton *button, gpointer user_data)
{
StatusEditor *dialog = user_data;
-
- status_editor_remove_dialog(dialog);
gtk_widget_destroy(dialog->window);
- g_free(dialog->original_title);
- g_free(dialog);
}
static void
@@ -842,20 +868,11 @@ status_editor_ok_cb(GtkButton *button, gpointer user_data)
g_free(message);
g_free(unformatted);
- status_editor_remove_dialog(dialog);
- gtk_widget_destroy(dialog->window);
- g_free(dialog->original_title);
-
-/*
- if (status_window != NULL)
- add_status_to_saved_status_list(status_window->model, saved_status);
-*/
-
/* If they clicked on "Save & Use" or "Use," then activate the status */
if (button != dialog->save_button)
purple_savedstatus_activate(saved_status);
- g_free(dialog);
+ gtk_widget_destroy(dialog->window);
}
static void
@@ -871,6 +888,26 @@ editor_title_changed_cb(GtkWidget *widget, gpointer user_data)
}
static GtkWidget *
+create_stock_item(const gchar *str, const gchar *icon)
+{
+ GtkWidget *menuitem = gtk_menu_item_new();
+ GtkWidget *label = gtk_label_new_with_mnemonic(str);
+ GtkWidget *hbox = gtk_hbox_new(FALSE, 4);
+ GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
+ GtkWidget *image = gtk_image_new_from_stock(icon, icon_size);;
+
+ gtk_widget_show(label);
+ gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
+
+ gtk_container_add(GTK_CONTAINER(menuitem), hbox);
+
+ return menuitem;
+}
+
+static GtkWidget *
create_status_type_menu(PurpleStatusPrimitive type)
{
int i;
@@ -889,7 +926,9 @@ create_status_type_menu(PurpleStatusPrimitive type)
* status types, so don't show them in the list.
*/
continue;
- item = gtk_menu_item_new_with_label(purple_primitive_get_name_from_type(i));
+
+ item = create_stock_item(purple_primitive_get_name_from_type(i),
+ get_stock_icon_from_primitive(i));
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
}
@@ -945,6 +984,7 @@ status_editor_substatus_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpo
STATUS_EDITOR_COLUMN_STATUS_ID, NULL,
STATUS_EDITOR_COLUMN_STATUS_NAME, NULL,
STATUS_EDITOR_COLUMN_STATUS_MESSAGE, NULL,
+ STATUS_EDITOR_COLUMN_STATUS_ICON, NULL,
-1);
}
}
@@ -990,6 +1030,10 @@ status_editor_add_columns(StatusEditor *dialog)
gtk_tree_view_column_set_title(column, _("Status"));
gtk_tree_view_insert_column(GTK_TREE_VIEW(dialog->treeview), column, -1);
gtk_tree_view_column_set_resizable(column, TRUE);
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute(column, renderer, "stock-id",
+ STATUS_EDITOR_COLUMN_STATUS_ICON);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(column, renderer, TRUE);
gtk_tree_view_column_add_attribute(column, renderer, "text",
@@ -1016,6 +1060,7 @@ status_editor_set_account(GtkListStore *store, PurpleAccount *account,
{
GdkPixbuf *pixbuf;
const char *id = NULL, *name = NULL, *message = NULL;
+ PurpleStatusPrimitive prim = PURPLE_STATUS_UNSET;
pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
if ((pixbuf != NULL) && !purple_account_is_connected(account))
@@ -1030,6 +1075,7 @@ status_editor_set_account(GtkListStore *store, PurpleAccount *account,
type = purple_savedstatus_substatus_get_type(substatus);
id = purple_status_type_get_id(type);
name = purple_status_type_get_name(type);
+ prim = purple_status_type_get_primitive(type);
if (purple_status_type_get_attr(type, "message"))
message = purple_savedstatus_substatus_get_message(substatus);
}
@@ -1042,6 +1088,7 @@ status_editor_set_account(GtkListStore *store, PurpleAccount *account,
STATUS_EDITOR_COLUMN_STATUS_ID, id,
STATUS_EDITOR_COLUMN_STATUS_NAME, name,
STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
+ STATUS_EDITOR_COLUMN_STATUS_ICON, get_stock_icon_from_primitive(prim),
-1);
if (pixbuf != NULL)
@@ -1133,7 +1180,7 @@ pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *saved_status)
dialog->window = win = pidgin_create_dialog(_("Status"), PIDGIN_HIG_BORDER, "status", TRUE);
- g_signal_connect(G_OBJECT(win), "delete_event",
+ g_signal_connect(G_OBJECT(win), "destroy",
G_CALLBACK(status_editor_destroy_cb), dialog);
/* Setup the vbox */
@@ -1198,6 +1245,7 @@ pidgin_status_editor_show(gboolean edit, PurpleSavedStatus *saved_status)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
+ G_TYPE_STRING,
G_TYPE_STRING);
/* Create the treeview */
@@ -1325,25 +1373,20 @@ substatus_editor_remove_dialog(SubStatusEditor *dialog)
}
}
-static gboolean
-substatus_editor_destroy_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+static void
+substatus_editor_destroy_cb(GtkWidget *widget, gpointer user_data)
{
SubStatusEditor *dialog = user_data;
substatus_editor_remove_dialog(dialog);
g_free(dialog);
-
- return FALSE;
}
static void
substatus_editor_cancel_cb(GtkButton *button, gpointer user_data)
{
SubStatusEditor *dialog = user_data;
-
- substatus_editor_remove_dialog(dialog);
gtk_widget_destroy(dialog->window);
- g_free(dialog);
}
@@ -1356,12 +1399,11 @@ substatus_editor_ok_cb(GtkButton *button, gpointer user_data)
PurpleStatusType *type;
char *id = NULL;
char *message = NULL;
- const char *name = NULL;
+ const char *name = NULL, *stock = NULL;
if (!gtk_combo_box_get_active_iter(dialog->box, &iter))
{
gtk_widget_destroy(dialog->window);
- g_free(dialog);
return;
}
@@ -1372,6 +1414,7 @@ substatus_editor_ok_cb(GtkButton *button, gpointer user_data)
if (purple_status_type_get_attr(type, "message") != NULL)
message = gtk_imhtml_get_markup(GTK_IMHTML(dialog->message));
name = purple_status_type_get_name(type);
+ stock = get_stock_icon_from_primitive(purple_status_type_get_primitive(type));
status_editor = dialog->status_editor;
@@ -1383,13 +1426,13 @@ substatus_editor_ok_cb(GtkButton *button, gpointer user_data)
STATUS_EDITOR_COLUMN_STATUS_NAME, name,
STATUS_EDITOR_COLUMN_STATUS_MESSAGE, message,
STATUS_EDITOR_COLUMN_WINDOW, NULL,
+ STATUS_EDITOR_COLUMN_STATUS_ICON, stock,
-1);
}
gtk_widget_destroy(dialog->window);
g_free(id);
g_free(message);
- g_free(dialog);
}
static void
@@ -1438,7 +1481,7 @@ edit_substatus(StatusEditor *status_editor, PurpleAccount *account)
dialog->window = win = pidgin_create_dialog(tmp, PIDGIN_HIG_BORDER, "substatus", TRUE);
g_free(tmp);
- g_signal_connect(G_OBJECT(win), "delete_event",
+ g_signal_connect(G_OBJECT(win), "destroy",
G_CALLBACK(substatus_editor_destroy_cb), dialog);
/* Setup the vbox */
@@ -1679,6 +1722,96 @@ static gboolean pidgin_status_menu_add_primitive(GtkListStore *model, GtkWidget
return currently_selected;
}
+static void
+pidgin_status_menu_update_iter(GtkWidget *combobox, GtkListStore *store, GtkTreeIter *iter,
+ PurpleSavedStatus *status)
+{
+ GdkPixbuf *pixbuf;
+
+ if (store == NULL)
+ store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+
+ pixbuf = pidgin_create_status_icon(purple_savedstatus_get_type(status),
+ combobox, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
+ gtk_list_store_set(store, iter,
+ SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS,
+ SS_MENU_ICON_COLUMN, pixbuf,
+ SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status),
+ SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)),
+ SS_MENU_EMBLEM_COLUMN, GTK_STOCK_SAVE,
+ SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE,
+ -1);
+ if (pixbuf)
+ g_object_unref(G_OBJECT(pixbuf));
+}
+
+static gboolean
+pidgin_status_menu_find_iter(GtkListStore *store, GtkTreeIter *iter, PurpleSavedStatus *find)
+{
+ int type;
+ gpointer data;
+ time_t creation_time = purple_savedstatus_get_creation_time(find);
+ GtkTreeModel *model = GTK_TREE_MODEL(store);
+
+ if (!gtk_tree_model_get_iter_first(model, iter))
+ return FALSE;
+
+ do {
+ gtk_tree_model_get(model, iter,
+ SS_MENU_TYPE_COLUMN, &type,
+ SS_MENU_DATA_COLUMN, &data,
+ -1);
+ if (type == SS_MENU_ENTRY_TYPE_PRIMITIVE)
+ continue;
+ if (GPOINTER_TO_INT(data) == creation_time)
+ return TRUE;
+ } while (gtk_tree_model_iter_next(model, iter));
+
+ return FALSE;
+}
+
+static void
+savedstatus_added_cb(PurpleSavedStatus *status, GtkWidget *combobox)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ if (purple_savedstatus_is_transient(status))
+ return;
+
+ store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+ gtk_list_store_append(store, &iter);
+ pidgin_status_menu_update_iter(combobox, store, &iter, status);
+}
+
+static void
+savedstatus_deleted_cb(PurpleSavedStatus *status, GtkWidget *combobox)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ if (purple_savedstatus_is_transient(status))
+ return;
+
+ store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+ if (pidgin_status_menu_find_iter(store, &iter, status))
+ gtk_list_store_remove(store, &iter);
+}
+
+static void
+savedstatus_modified_cb(PurpleSavedStatus *status, GtkWidget *combobox)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ if (purple_savedstatus_is_transient(status))
+ return;
+
+ store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combobox)));
+ if (pidgin_status_menu_find_iter(store, &iter, status))
+ pidgin_status_menu_update_iter(combobox, store, &iter, status);
+}
+
GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callback)
{
GtkWidget *combobox;
@@ -1686,7 +1819,6 @@ GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callb
GList *sorted, *cur;
int i = 0;
int index = -1;
- GdkPixbuf *pixbuf;
GtkTreeIter iter;
GtkCellRenderer *text_rend;
GtkCellRenderer *icon_rend;
@@ -1720,18 +1852,9 @@ GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callb
PurpleSavedStatus *status = (PurpleSavedStatus *) cur->data;
if (!purple_savedstatus_is_transient(status))
{
- pixbuf = pidgin_create_status_icon(purple_savedstatus_get_type(status),
- combobox, PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL);
gtk_list_store_append(model, &iter);
- gtk_list_store_set(model, &iter,
- SS_MENU_TYPE_COLUMN, SS_MENU_ENTRY_TYPE_SAVEDSTATUS,
- SS_MENU_ICON_COLUMN, pixbuf,
- SS_MENU_TEXT_COLUMN, purple_savedstatus_get_title(status),
- SS_MENU_DATA_COLUMN, GINT_TO_POINTER(purple_savedstatus_get_creation_time(status)),
- SS_MENU_EMBLEM_COLUMN, GTK_STOCK_SAVE,
- SS_MENU_EMBLEM_VISIBLE_COLUMN, TRUE,
- -1);
- g_object_unref(G_OBJECT(pixbuf));
+
+ pidgin_status_menu_update_iter(combobox, model, &iter, status);
if (status == current_status)
index = i;
@@ -1756,6 +1879,17 @@ GtkWidget *pidgin_status_menu(PurpleSavedStatus *current_status, GCallback callb
gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index);
g_signal_connect(G_OBJECT(combobox), "changed", G_CALLBACK(status_menu_cb), callback);
+ /* Make sure the list is updated dynamically when a substatus is changed/deleted
+ * or a new one is added. */
+ purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-added",
+ combobox, G_CALLBACK(savedstatus_added_cb), combobox);
+ purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-deleted",
+ combobox, G_CALLBACK(savedstatus_deleted_cb), combobox);
+ purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-modified",
+ combobox, G_CALLBACK(savedstatus_modified_cb), combobox);
+ g_signal_connect(G_OBJECT(combobox), "destroy",
+ G_CALLBACK(purple_signals_disconnect_by_handle), NULL);
+
return combobox;
}
diff --git a/pidgin/gtksmiley.c b/pidgin/gtksmiley.c
index 9439087d6a..100e562484 100644
--- a/pidgin/gtksmiley.c
+++ b/pidgin/gtksmiley.c
@@ -96,6 +96,18 @@ shortcut_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gt
gtksmiley->smile = g_strdup(purple_smiley_get_shortcut(smiley));
}
+static void
+image_changed_cb(PurpleSmiley *smiley, gpointer dontcare, GtkIMHtmlSmiley *gtksmiley)
+{
+ const char *file;
+
+ g_free(gtksmiley->file);
+
+ file = purple_imgstore_get_filename(purple_smiley_get_stored_image(smiley));
+ gtksmiley->file = g_build_filename(purple_smileys_get_storing_dir(), file, NULL);
+ gtk_imhtml_smiley_reload(gtksmiley);
+}
+
static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
{
GtkIMHtmlSmiley *gtksmiley;
@@ -114,6 +126,10 @@ static GtkIMHtmlSmiley *smiley_purple_to_gtkimhtml(PurpleSmiley *smiley)
g_signal_connect(G_OBJECT(smiley), "notify::shortcut",
G_CALLBACK(shortcut_changed_cb), gtksmiley);
+ /* And update the pixbuf too when the image is changed */
+ g_signal_connect(G_OBJECT(smiley), "notify::image",
+ G_CALLBACK(image_changed_cb), gtksmiley);
+
return gtksmiley;
}
diff --git a/pidgin/gtkstatusbox.c b/pidgin/gtkstatusbox.c
index b5caabf748..9e48209c00 100644
--- a/pidgin/gtkstatusbox.c
+++ b/pidgin/gtkstatusbox.c
@@ -209,7 +209,8 @@ update_to_reflect_account_status(PidginStatusBox *status_box, PurpleAccount *acc
for (l = purple_account_get_status_types(account); l != NULL; l = l->next) {
PurpleStatusType *status_type = (PurpleStatusType *)l->data;
- if (!purple_status_type_is_user_settable(status_type))
+ if (!purple_status_type_is_user_settable(status_type) ||
+ purple_status_type_is_independent(status_type))
continue;
status_no++;
if (statustype == status_type)
@@ -769,7 +770,8 @@ find_status_type_by_index(const PurpleAccount *account, gint active)
for (i = 0; l; l = l->next) {
PurpleStatusType *status_type = l->data;
- if (!purple_status_type_is_user_settable(status_type))
+ if (!purple_status_type_is_user_settable(status_type) ||
+ purple_status_type_is_independent(status_type))
continue;
if (active == i)
@@ -1030,12 +1032,13 @@ add_account_statuses(PidginStatusBox *status_box, PurpleAccount *account)
PurpleStatusType *status_type = (PurpleStatusType *)l->data;
PurpleStatusPrimitive prim;
- if (!purple_status_type_is_user_settable(status_type))
+ if (!purple_status_type_is_user_settable(status_type) ||
+ purple_status_type_is_independent(status_type))
continue;
- prim = purple_status_type_get_primitive(status_type);
+ prim = purple_status_type_get_primitive(status_type);
- pixbuf = pidgin_status_box_get_pixbuf(status_box, prim);
+ pixbuf = pidgin_status_box_get_pixbuf(status_box, prim);
pidgin_status_box_add(PIDGIN_STATUS_BOX(status_box),
PIDGIN_STATUS_BOX_TYPE_PRIMITIVE, pixbuf,
@@ -1706,6 +1709,48 @@ imhtml_cursor_moved_cb(gpointer data, GtkMovementStep step, gint count, gboolean
}
static void
+treeview_cursor_changed_cb(GtkTreeView *treeview, gpointer data)
+{
+ GtkTreeSelection *sel = gtk_tree_view_get_selection (treeview);
+ GtkTreeModel *model = GTK_TREE_MODEL (data);
+ GtkTreeIter iter;
+ GtkTreePath *cursor;
+ GtkTreePath *selection;
+ gint cmp;
+
+ if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
+ if ((selection = gtk_tree_model_get_path (model, &iter)) == NULL) {
+ /* Shouldn't happen, but ignore anyway */
+ return;
+ }
+ } else {
+ /* I don't think this can happen, but we'll just ignore it */
+ return;
+ }
+
+ gtk_tree_view_get_cursor (treeview, &cursor, NULL);
+ if (cursor == NULL) {
+ /* Probably won't happen in a 'cursor-changed' event? */
+ gtk_tree_path_free (selection);
+ return;
+ }
+
+ cmp = gtk_tree_path_compare (cursor, selection);
+ if (cmp < 0) {
+ /* The cursor moved up without moving the selection, so move it up again */
+ gtk_tree_path_prev (cursor);
+ gtk_tree_view_set_cursor (treeview, cursor, NULL, FALSE);
+ } else if (cmp > 0) {
+ /* The cursor moved down without moving the selection, so move it down again */
+ gtk_tree_path_next (cursor);
+ gtk_tree_view_set_cursor (treeview, cursor, NULL, FALSE);
+ }
+
+ gtk_tree_path_free (selection);
+ gtk_tree_path_free (cursor);
+}
+
+static void
pidgin_status_box_init (PidginStatusBox *status_box)
{
GtkCellRenderer *text_rend;
@@ -1869,6 +1914,8 @@ pidgin_status_box_init (PidginStatusBox *status_box)
G_CALLBACK(imhtml_scroll_event_cb), status_box->imhtml);
g_signal_connect(G_OBJECT(status_box->popup_window), "button_release_event", G_CALLBACK(treeview_button_release_cb), status_box);
g_signal_connect(G_OBJECT(status_box->popup_window), "key_press_event", G_CALLBACK(treeview_key_press_event), status_box);
+ g_signal_connect(G_OBJECT(status_box->tree_view), "cursor-changed",
+ G_CALLBACK(treeview_cursor_changed_cb), status_box->dropdown_store);
#if GTK_CHECK_VERSION(2,6,0)
gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(status_box->tree_view), dropdown_store_row_separator_func, NULL, 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/pidginstock.c b/pidgin/pidginstock.c
index 8dffd5bea3..c73d421a05 100644
--- a/pidgin/pidginstock.c
+++ b/pidgin/pidginstock.c
@@ -168,6 +168,7 @@ static struct SizedStockIcon {
{ PIDGIN_STOCK_TOOLBAR_UNBLOCK, "toolbar", "unblock.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR, "toolbar", "select-avatar.png", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TOOLBAR_SEND_FILE, "toolbar", "send-file.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
+ { PIDGIN_STOCK_TOOLBAR_TRANSFER, "toolbar", "transfer.png", FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TRAY_AVAILABLE, "tray", "tray-online.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
{ PIDGIN_STOCK_TRAY_INVISIBLE, "tray", "tray-invisible.png", FALSE, TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, NULL },
diff --git a/pidgin/pidginstock.h b/pidgin/pidginstock.h
index eb4c895a50..4958a3e10c 100644
--- a/pidgin/pidginstock.h
+++ b/pidgin/pidginstock.h
@@ -129,6 +129,7 @@
#define PIDGIN_STOCK_TOOLBAR_UNBLOCK "pidgin-unblock"
#define PIDGIN_STOCK_TOOLBAR_SELECT_AVATAR "pidgin-select-avatar"
#define PIDGIN_STOCK_TOOLBAR_SEND_FILE "pidgin-send-file"
+#define PIDGIN_STOCK_TOOLBAR_TRANSFER "pidgin-transfer"
/* Tray icons */
#define PIDGIN_STOCK_TRAY_AVAILABLE "pidgin-tray-available"
diff --git a/pidgin/pixmaps/Makefile.am b/pidgin/pixmaps/Makefile.am
index 607623b7e3..bf0343fd21 100644
--- a/pidgin/pixmaps/Makefile.am
+++ b/pidgin/pixmaps/Makefile.am
@@ -565,6 +565,7 @@ TOOLBAR_16 = \
toolbar/16/message-new.png \
toolbar/16/plugins.png \
toolbar/16/send-file.png \
+ toolbar/16/transfer.png \
toolbar/16/unblock.png
TOOLBAR_22_SCALABLE = \
diff --git a/pidgin/pixmaps/status/16/available.png b/pidgin/pixmaps/status/16/available.png
index 9f5eb6fd9c..34bacb712b 100644
--- a/pidgin/pixmaps/status/16/available.png
+++ b/pidgin/pixmaps/status/16/available.png
Binary files differ
diff --git a/pidgin/pixmaps/status/16/away.png b/pidgin/pixmaps/status/16/away.png
index 30725e8ba4..b0df165d75 100644
--- a/pidgin/pixmaps/status/16/away.png
+++ b/pidgin/pixmaps/status/16/away.png
Binary files differ
diff --git a/pidgin/pixmaps/status/16/busy.png b/pidgin/pixmaps/status/16/busy.png
index 8481cf7e2e..0b0ce85a4d 100644
--- a/pidgin/pixmaps/status/16/busy.png
+++ b/pidgin/pixmaps/status/16/busy.png
Binary files differ
diff --git a/pidgin/pixmaps/status/16/chat.png b/pidgin/pixmaps/status/16/chat.png
index 2943558631..6c6b7deb63 100644
--- a/pidgin/pixmaps/status/16/chat.png
+++ b/pidgin/pixmaps/status/16/chat.png
Binary files differ
diff --git a/pidgin/pixmaps/status/16/offline.png b/pidgin/pixmaps/status/16/offline.png
index 423d4f97cd..44a0737e8f 100644
--- a/pidgin/pixmaps/status/16/offline.png
+++ b/pidgin/pixmaps/status/16/offline.png
Binary files differ
diff --git a/pidgin/pixmaps/status/16/person.png b/pidgin/pixmaps/status/16/person.png
index 5c0035386c..905b82fe74 100644
--- a/pidgin/pixmaps/status/16/person.png
+++ b/pidgin/pixmaps/status/16/person.png
Binary files differ
diff --git a/pidgin/pixmaps/status/16/scalable/available.svg b/pidgin/pixmaps/status/16/scalable/available.svg
index 7c33344d9f..ab60f368b0 100644
--- a/pidgin/pixmaps/status/16/scalable/available.svg
+++ b/pidgin/pixmaps/status/16/scalable/available.svg
@@ -2,7 +2,7 @@
<!-- 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"
@@ -13,9 +13,9 @@
height="16"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="0.45"
+ inkscape:version="0.46+devel"
version="1.0"
- inkscape:export-filename="/home/hbons/GUI/Tango/Gaim Refresh/status/16/available.png"
+ inkscape:export-filename="/home/hbons/Desktop/available.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable"
@@ -26,6 +26,25 @@
id="defs4">
<linearGradient
inkscape:collect="always"
+ id="linearGradient3286">
+ <stop
+ style="stop-color:#459000;stop-opacity:1"
+ offset="0"
+ id="stop3288" />
+ <stop
+ style="stop-color:#204300;stop-opacity:1"
+ offset="1"
+ id="stop3290" />
+ </linearGradient>
+ <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="perspective43" />
+ <linearGradient
+ inkscape:collect="always"
id="linearGradient2898">
<stop
style="stop-color:white;stop-opacity:1;"
@@ -50,10 +69,10 @@
xlink:href="#linearGradient3149"
id="linearGradient4740"
gradientUnits="userSpaceOnUse"
- x1="15.498499"
- y1="9.4211226"
- x2="24.240097"
- y2="36.603138" />
+ x1="11.127699"
+ y1="10.823074"
+ x2="30.341434"
+ y2="31.325201" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3149"
@@ -230,6 +249,39 @@
fx="31.112698"
fy="19.008621"
r="8.6620579" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3286"
+ id="linearGradient3292"
+ x1="15.893391"
+ y1="15.213944"
+ x2="26.533659"
+ y2="28.579245"
+ gradientUnits="userSpaceOnUse" />
+ <filter
+ inkscape:collect="always"
+ id="filter3360"
+ x="-0.13093077"
+ width="1.2618615"
+ y="-0.11042095"
+ height="1.2208419">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.19314814"
+ id="feGaussianBlur3362" />
+ </filter>
+ <filter
+ inkscape:collect="always"
+ id="filter3374"
+ x="-0.13117394"
+ width="1.2623479"
+ y="-0.11015608"
+ height="1.2203122">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.19309804"
+ id="feGaussianBlur3376" />
+ </filter>
</defs>
<sodipodi:namedview
id="base"
@@ -239,16 +291,27 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="25.992076"
- inkscape:cx="4.1907826"
- inkscape:cy="8.6773979"
+ inkscape:cx="12.058565"
+ inkscape:cy="10.562588"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
fill="#eeeeec"
- inkscape:window-width="1274"
- inkscape:window-height="844"
- inkscape:window-x="3"
- inkscape:window-y="25" />
+ inkscape:window-width="1440"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="22"
+ inkscape:snap-bbox="true"
+ inkscape:snap-nodes="false"
+ objecttolerance="13"
+ gridtolerance="10">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3284"
+ empspacing="5"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
@@ -265,14 +328,24 @@
inkscape:groupmode="layer"
id="layer1">
<path
- transform="matrix(0.538297,0,0,0.538297,-1.630177,-1.459246)"
- style="fill:url(#linearGradient4738);fill-opacity:1;fill-rule:evenodd;stroke:#306300;stroke-width:1.85770929;stroke-miterlimit:4;stroke-opacity:1"
+ style="fill:url(#linearGradient4738);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3292);stroke-width:1.85770929000000007;stroke-miterlimit:4;stroke-opacity:1"
d="M 31.822886,17.572527 C 31.822886,25.263442 25.580983,31.505344 17.890068,31.505344 C 10.199153,31.505344 3.9572506,25.263442 3.9572506,17.572527 C 3.9572506,9.8816117 10.199153,3.6397095 17.890068,3.6397095 C 25.580983,3.6397095 31.822886,9.8816117 31.822886,17.572527 z "
- id="path4331" />
+ id="path4331"
+ transform="matrix(0.538297,0,0,0.538297,-1.630177,-1.459246)" />
<path
- transform="matrix(0.466524,0,0,0.466525,-0.346154,-0.198015)"
- style="opacity:0.6;fill:url(#linearGradient4740);fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:2.14350748;stroke-miterlimit:4;stroke-opacity:1"
+ style="opacity:0.59999999999999998;fill:url(#linearGradient4740);fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:2.14350747999999980;stroke-miterlimit:4;stroke-opacity:1"
d="M 31.822886,17.572527 C 31.822886,25.263442 25.580983,31.505344 17.890068,31.505344 C 10.199153,31.505344 3.9572506,25.263442 3.9572506,17.572527 C 3.9572506,9.8816117 10.199153,3.6397095 17.890068,3.6397095 C 25.580983,3.6397095 31.822886,9.8816117 31.822886,17.572527 z "
- id="path4333" />
+ id="path4333"
+ transform="matrix(0.466524,0,0,0.466525,-0.346154,-0.198015)" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.19499996000000000;fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3374)"
+ id="path3302"
+ sodipodi:cx="11.484269"
+ sodipodi:cy="4.7465701"
+ sodipodi:rx="1.8659533"
+ sodipodi:ry="1.9428998"
+ d="m 13.350222,4.7465701 a 1.8659533,1.9428998 0 1 1 -3.7319067,0 A 1.8659533,1.9428998 0 1 1 13.350222,4.7465701 z"
+ transform="matrix(2.2014717,-1.281888,0.9447394,1.6503281,-23.266565,12.888149)" />
</g>
</svg>
diff --git a/pidgin/pixmaps/status/16/scalable/away.svg b/pidgin/pixmaps/status/16/scalable/away.svg
index 16c30f2043..cb8339ee7c 100644
--- a/pidgin/pixmaps/status/16/scalable/away.svg
+++ b/pidgin/pixmaps/status/16/scalable/away.svg
@@ -2,7 +2,7 @@
<!-- 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"
@@ -13,9 +13,9 @@
height="16"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="0.45"
+ inkscape:version="0.46+devel"
version="1.0"
- inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/status/16/available16.png"
+ inkscape:export-filename="/home/hbons/Desktop/away.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable"
@@ -26,6 +26,37 @@
id="defs4">
<linearGradient
inkscape:collect="always"
+ id="linearGradient3284">
+ <stop
+ style="stop-color:#173867;stop-opacity:1;"
+ offset="0"
+ id="stop3286" />
+ <stop
+ style="stop-color:#173867;stop-opacity:0;"
+ offset="1"
+ id="stop3288" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3276">
+ <stop
+ style="stop-color:#2863b7;stop-opacity:1"
+ offset="0"
+ id="stop3278" />
+ <stop
+ style="stop-color:#14325c;stop-opacity:1"
+ offset="1"
+ id="stop3280" />
+ </linearGradient>
+ <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="perspective35" />
+ <linearGradient
+ inkscape:collect="always"
id="linearGradient2812">
<stop
style="stop-color:#2e3436;stop-opacity:1;"
@@ -94,9 +125,9 @@
xlink:href="#linearGradient2804"
id="linearGradient2810"
x1="4.5264969"
- y1="2.7991772"
- x2="10.623409"
- y2="11.024895"
+ y1="2.6807978"
+ x2="9.7444448"
+ y2="9.9594812"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
@@ -108,6 +139,24 @@
fy="19.008621"
r="8.6620579"
gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3276"
+ id="linearGradient3282"
+ x1="15.377563"
+ y1="12.744186"
+ x2="22.868998"
+ y2="29.821121"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3284"
+ id="linearGradient3290"
+ x1="23.221344"
+ y1="24.700239"
+ x2="8.2601509"
+ y2="0.92288947"
+ gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
@@ -117,8 +166,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="25.992076"
- inkscape:cx="14.729231"
- inkscape:cy="2.7799575"
+ inkscape:cx="13.286484"
+ inkscape:cy="9.6281985"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
@@ -126,11 +175,22 @@
inkscape:window-width="1434"
inkscape:window-height="840"
inkscape:window-x="3"
- inkscape:window-y="25"
+ inkscape:window-y="27"
inkscape:object-paths="false"
inkscape:grid-bbox="true"
inkscape:guide-bbox="false"
- inkscape:grid-points="true" />
+ inkscape:grid-points="true"
+ objecttolerance="14"
+ gridtolerance="18"
+ inkscape:snap-bbox="true"
+ inkscape:snap-nodes="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3274"
+ empspacing="5"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
@@ -148,7 +208,7 @@
id="layer1">
<path
sodipodi:type="arc"
- style="opacity:1;color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:evenodd;stroke:#173867;stroke-width:1.91314828px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ style="opacity:1;color:#000000;fill:#729fcf;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3282);stroke-width:1.91314827999999992px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
id="path1339"
sodipodi:cx="15.590227"
sodipodi:cy="16.57217"
@@ -168,7 +228,7 @@
transform="matrix(2.192102,0,0,2.091316,16.34939,1.090661)" />
<path
sodipodi:type="arc"
- style="opacity:0.40340911;color:black;fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:#173867;stroke-width:2.60821199px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ style="opacity:0.40340911000000002;color:black;fill:#babdb6;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3290);stroke-width:2.60821199000000004px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
id="path1341"
sodipodi:cx="15.590227"
sodipodi:cy="16.57217"
@@ -188,7 +248,7 @@
transform="matrix(3.094296,0,0,3.766968,-10.69048,-20.45989)" />
<path
sodipodi:type="arc"
- style="opacity:0.76704544;fill:none;fill-opacity:1;stroke:url(#linearGradient2810);stroke-width:0.80677563;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ style="opacity:0.76704543999999986;fill:none;fill-opacity:1;stroke:url(#linearGradient2810);stroke-width:0.80677562999999985;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path2802"
sodipodi:cx="7.5"
sodipodi:cy="7"
@@ -197,7 +257,7 @@
d="M 13 7 A 5.5 5 0 1 1 2,7 A 5.5 5 0 1 1 13 7 z"
transform="matrix(1.18182,0,0,1.3,-0.86365,-1.1)" />
<rect
- style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ style="opacity:1;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect2820"
width="1"
height="1"
@@ -231,7 +291,7 @@
y="-10"
transform="matrix(0,1,-1,0,0,0)" />
<rect
- style="opacity:1;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ style="opacity:1;fill:#888a85;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect2846"
width="1"
height="1"
diff --git a/pidgin/pixmaps/status/16/scalable/busy.svg b/pidgin/pixmaps/status/16/scalable/busy.svg
index 52291fc922..803f633d36 100644
--- a/pidgin/pixmaps/status/16/scalable/busy.svg
+++ b/pidgin/pixmaps/status/16/scalable/busy.svg
@@ -2,7 +2,7 @@
<!-- 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"
@@ -13,9 +13,9 @@
height="16"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="0.45"
+ inkscape:version="0.46+devel"
version="1.0"
- inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/status/16/available16.png"
+ inkscape:export-filename="/home/hbons/Desktop/busy.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
sodipodi:docbase="/home/hbons/Desktop/2.1.1/status/16/scalable"
@@ -26,6 +26,37 @@
id="defs4">
<linearGradient
inkscape:collect="always"
+ id="linearGradient3290">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3292" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3294" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3280">
+ <stop
+ style="stop-color:#a60000;stop-opacity:1"
+ offset="0"
+ id="stop3282" />
+ <stop
+ style="stop-color:#460000;stop-opacity:1"
+ offset="1"
+ id="stop3284" />
+ </linearGradient>
+ <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="perspective39" />
+ <linearGradient
+ inkscape:collect="always"
id="linearGradient2898">
<stop
style="stop-color:white;stop-opacity:1;"
@@ -71,28 +102,6 @@
r="8.6620579" />
<linearGradient
inkscape:collect="always"
- id="linearGradient2186">
- <stop
- style="stop-color:#ffffff;stop-opacity:1;"
- offset="0"
- id="stop2188" />
- <stop
- style="stop-color:#ffffff;stop-opacity:0;"
- offset="1"
- id="stop2190" />
- </linearGradient>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient2186"
- id="linearGradient2194"
- x1="9.2594385"
- y1="-1.5641226"
- x2="11.226587"
- y2="17.697369"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.684526,0,0,0.687171,-0.20455,-0.253325)" />
- <linearGradient
- inkscape:collect="always"
id="linearGradient2239">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
@@ -107,12 +116,12 @@
inkscape:collect="always"
xlink:href="#linearGradient2239"
id="linearGradient2245"
- x1="8.7505674"
- y1="4.5934086"
- x2="31.18539"
- y2="39.834526"
+ x1="-1.5418521"
+ y1="-6.2826729"
+ x2="63.127094"
+ y2="59.183727"
gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(0.482882,0,0,0.482874,0.269812,0.26982)" />
+ gradientTransform="matrix(0.482882,0,0,0.482874,0.269812,0.2698205)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3149"
@@ -177,6 +186,240 @@
x2="12.233074"
y2="27.77807"
gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3280"
+ id="linearGradient3286"
+ x1="11.549973"
+ y1="7.078577"
+ x2="33.836056"
+ y2="45.494511"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3290"
+ id="linearGradient3282"
+ x1="5.8424845"
+ y1="-1.2794704"
+ x2="10.945268"
+ y2="11.145247"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="28.579245"
+ x2="26.533659"
+ y1="15.213944"
+ x1="15.893391"
+ id="linearGradient3292"
+ xlink:href="#linearGradient3280"
+ inkscape:collect="always" />
+ <radialGradient
+ r="8.6620579"
+ fy="19.008621"
+ fx="31.112698"
+ cy="19.008621"
+ cx="31.112698"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient2621"
+ xlink:href="#linearGradient2812"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="11.024895"
+ x2="10.623409"
+ y1="2.7991772"
+ x1="4.5264969"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3007"
+ xlink:href="#linearGradient2804"
+ inkscape:collect="always" />
+ <radialGradient
+ r="8.6620579"
+ fy="19.008621"
+ fx="31.112698"
+ cy="19.008621"
+ cx="31.112698"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3005"
+ xlink:href="#linearGradient2812"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2804">
+ <stop
+ style="stop-color:white;stop-opacity:1;"
+ offset="0"
+ id="stop2806" />
+ <stop
+ style="stop-color:white;stop-opacity:0;"
+ offset="1"
+ id="stop2808" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3149"
+ id="linearGradient2949"
+ gradientUnits="userSpaceOnUse"
+ x1="17.890068"
+ y1="8.0617304"
+ x2="17.890068"
+ y2="40.032413" />
+ <linearGradient
+ id="linearGradient2951">
+ <stop
+ style="stop-color:#73d216;stop-opacity:1;"
+ offset="0"
+ id="stop2953" />
+ <stop
+ style="stop-color:#5ca911;stop-opacity:1;"
+ offset="1"
+ id="stop2955" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2851"
+ id="linearGradient2963"
+ x1="6.878005"
+ y1="11.789385"
+ x2="12.233074"
+ y2="27.77807"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2804"
+ id="linearGradient2810"
+ x1="4.5264969"
+ y1="2.7991772"
+ x2="10.623409"
+ y2="11.024895"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2812"
+ id="radialGradient2818"
+ cx="31.112698"
+ cy="19.008621"
+ fx="31.112698"
+ fy="19.008621"
+ r="8.6620579"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(3.018423,0.664359,-1.388844,4.257661,-0.134567,-26.02469)"
+ r="0.8078171"
+ fy="6.9473476"
+ fx="4.8470273"
+ cy="6.9473476"
+ cx="4.8470273"
+ id="radialGradient2601"
+ xlink:href="#linearGradient2898"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientUnits="userSpaceOnUse"
+ y2="27.77807"
+ x2="12.233074"
+ y1="11.789385"
+ x1="6.878005"
+ id="linearGradient2599"
+ xlink:href="#linearGradient2851"
+ inkscape:collect="always" />
+ <radialGradient
+ gradientTransform="matrix(0.914124,-3.896132e-15,-2.475021e-18,1.631747,2.671799,-12.00863)"
+ gradientUnits="userSpaceOnUse"
+ r="8.6620579"
+ fy="19.008621"
+ fx="31.112698"
+ cy="19.008621"
+ cx="31.112698"
+ id="radialGradient2597"
+ xlink:href="#linearGradient3816"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2579">
+ <stop
+ id="stop2581"
+ offset="0"
+ style="stop-color:#73d216;stop-opacity:1;" />
+ <stop
+ id="stop2583"
+ offset="1"
+ style="stop-color:#5ca911;stop-opacity:1;" />
+ </linearGradient>
+ <linearGradient
+ y2="40.032413"
+ x2="17.890068"
+ y1="8.0617304"
+ x1="17.890068"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2577"
+ xlink:href="#linearGradient3149"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="31.325201"
+ x2="30.341434"
+ y1="10.823074"
+ x1="11.127699"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4740"
+ xlink:href="#linearGradient3149"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="27.77807"
+ x2="12.233074"
+ y1="11.789385"
+ x1="6.878005"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient4738"
+ xlink:href="#linearGradient2851"
+ inkscape:collect="always" />
+ <inkscape:perspective
+ id="perspective43"
+ 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" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3149"
+ id="linearGradient2635"
+ gradientUnits="userSpaceOnUse"
+ x1="11.127699"
+ y1="10.823074"
+ x2="30.341434"
+ y2="31.325201"
+ gradientTransform="matrix(0.466524,0,0,0.466525,-23.253129,0.8019768)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2851"
+ id="linearGradient2638"
+ gradientUnits="userSpaceOnUse"
+ x1="6.878005"
+ y1="11.789385"
+ x2="12.233074"
+ y2="27.77807"
+ gradientTransform="matrix(0.538297,0,0,0.538297,-24.537152,-0.4592542)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3280"
+ id="linearGradient2640"
+ gradientUnits="userSpaceOnUse"
+ x1="15.893391"
+ y1="15.213944"
+ x2="26.533659"
+ y2="28.579245"
+ gradientTransform="matrix(0.538297,0,0,0.538297,-24.537152,-0.4592542)" />
+ <filter
+ inkscape:collect="always"
+ id="filter3416"
+ x="-0.13117394"
+ width="1.2623479"
+ y="-0.11015608"
+ height="1.2203122">
+ <feGaussianBlur
+ inkscape:collect="always"
+ stdDeviation="0.19309804"
+ id="feGaussianBlur3418" />
+ </filter>
</defs>
<sodipodi:namedview
id="base"
@@ -185,17 +428,21 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="25.992076"
- inkscape:cx="8.3811422"
- inkscape:cy="5.1075896"
+ inkscape:zoom="18.379173"
+ inkscape:cx="0.35485832"
+ inkscape:cy="7.0130435"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
fill="#eeeeec"
- inkscape:window-width="1268"
- inkscape:window-height="844"
- inkscape:window-x="3"
- inkscape:window-y="25" />
+ inkscape:window-width="1440"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="22">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2508" />
+ </sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
@@ -223,7 +470,7 @@
transform="matrix(0.468971,0,0,0.468971,0.730372,0.26987)" />
<path
sodipodi:type="arc"
- style="opacity:1;color:#000000;fill:#f24747;fill-opacity:1;fill-rule:evenodd;stroke:#820000;stroke-width:1.91298747px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ style="opacity:1;color:#000000;fill:#f13d3d;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3286);stroke-width:1.91298747000000002px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
id="path1339"
sodipodi:cx="15.590227"
sodipodi:cy="16.57217"
@@ -232,17 +479,27 @@
d="M 29.935402 16.57217 A 14.345175 14.345175 0 1 1 1.2450523,16.57217 A 14.345175 14.345175 0 1 1 29.935402 16.57217 z"
transform="matrix(0.522706,0,0,0.522779,-0.14857,-0.663013)" />
<path
- style="opacity:0.6;color:#000000;fill:url(#linearGradient2194);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2245);stroke-width:1.00000012px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- d="M 14.525974,7.9894993 C 14.525974,11.598577 11.608166,14.527685 8.0130095,14.527685 C 4.4178543,14.527685 1.500047,11.598577 1.500047,7.9894993 C 1.500047,4.3804219 4.4178543,1.4513155 8.0130095,1.4513155 C 11.608166,1.4513155 14.525974,4.3804219 14.525974,7.9894993 z "
+ style="opacity:0.75000000000000000;color:#000000;fill:url(#linearGradient3282);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2245);stroke-width:1.00000011999999994px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ d="m 14.525974,7.9894993 c 0,3.6090777 -2.917808,6.5381857 -6.5129645,6.5381857 -3.5951552,0 -6.5129625,-2.929108 -6.5129625,-6.5381857 0,-3.6090774 2.9178073,-6.5381838 6.5129625,-6.5381838 3.5951565,0 6.5129645,2.9291064 6.5129645,6.5381838 z"
id="path2220" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:0.19499996000000000;fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3416)"
+ id="path3302"
+ sodipodi:cx="11.484269"
+ sodipodi:cy="4.7465701"
+ sodipodi:rx="1.8659533"
+ sodipodi:ry="1.9428998"
+ d="m 13.350222,4.7465701 a 1.8659533,1.9428998 0 1 1 -3.7319067,0 A 1.8659533,1.9428998 0 1 1 13.350222,4.7465701 z"
+ transform="matrix(2.2014717,-1.281888,0.9447394,1.6503281,-23.212768,12.908772)" />
<rect
- style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#a40000;stroke-width:1.00000024;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#a40000;stroke-width:1.00000024000000010;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3207"
width="11.000004"
height="2.9999959"
x="2.500001"
y="6.5000038"
- rx="0.96183157"
- ry="0.96183157" />
+ rx="1.0387781"
+ ry="1.0387781" />
</g>
</svg>
diff --git a/pidgin/pixmaps/status/16/scalable/chat.svg b/pidgin/pixmaps/status/16/scalable/chat.svg
index 56dc06bd0d..faae24676e 100644
--- a/pidgin/pixmaps/status/16/scalable/chat.svg
+++ b/pidgin/pixmaps/status/16/scalable/chat.svg
@@ -2,92 +2,313 @@
<!-- 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://inkscape.sourceforge.net/DTD/s odipodi-0.dtd"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
- id="svg13306"
+ id="svg7380"
sodipodi:version="0.32"
- inkscape:version="0.43"
- version="1.0"
- inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/status/16/irc16.png"
+ inkscape:version="0.46+devel"
+ sodipodi:docbase="/home/hbons/Desktop"
+ sodipodi:docname="person.svg"
+ inkscape:export-filename="/home/hbons/Desktop/person.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
- sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/status/16/scalable"
- sodipodi:docname="irc16.svg">
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
<defs
- id="defs13308">
+ id="defs7382">
<linearGradient
inkscape:collect="always"
- id="linearGradient2280">
+ id="linearGradient3301">
<stop
- style="stop-color:#d3d7cf;stop-opacity:1;"
+ style="stop-color:#46284e;stop-opacity:1;"
offset="0"
- id="stop2282" />
+ id="stop3303" />
<stop
- style="stop-color:#d3d7cf;stop-opacity:0;"
+ style="stop-color:#7a4588;stop-opacity:1"
offset="1"
- id="stop2284" />
+ id="stop3305" />
</linearGradient>
<linearGradient
inkscape:collect="always"
- id="linearGradient3150">
+ id="linearGradient3483">
<stop
- style="stop-color:#2e3436;stop-opacity:1;"
+ style="stop-color:#c17802;stop-opacity:1"
offset="0"
- id="stop3152" />
+ id="stop3485" />
<stop
- style="stop-color:#2e3436;stop-opacity:0;"
+ style="stop-color:#935a00;stop-opacity:1"
offset="1"
- id="stop3154" />
+ id="stop3487" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3475">
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:1;"
+ offset="0"
+ id="stop3477" />
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:0;"
+ offset="1"
+ id="stop3479" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3451">
+ <stop
+ style="stop-color:#3465a4;stop-opacity:1"
+ offset="0"
+ id="stop3453" />
+ <stop
+ style="stop-color:#15325b;stop-opacity:1"
+ offset="1"
+ id="stop3455" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3335">
+ <stop
+ style="stop-color:#b2730d;stop-opacity:1;"
+ offset="0"
+ id="stop3337" />
+ <stop
+ style="stop-color:#935f0a;stop-opacity:1"
+ offset="1"
+ id="stop3339" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3327">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3329" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3331" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 5.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="11 : 5.5 : 1"
+ inkscape:persp3d-origin="5.5 : 3.6666667 : 1"
+ id="perspective28" />
+ <linearGradient
+ id="linearGradient3800">
+ <stop
+ style="stop-color:#f4d9b1;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop3802" />
+ <stop
+ style="stop-color:#df9725;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop3804" />
</linearGradient>
<radialGradient
inkscape:collect="always"
- xlink:href="#linearGradient3150"
- id="radialGradient3156"
- cx="10.748654"
- cy="10.457643"
- fx="10.748654"
- fy="10.457643"
- r="6.6449099"
- gradientTransform="matrix(-0.842757,5.698892e-16,-4.565819e-9,-0.35721,19.80716,14.19321)"
- gradientUnits="userSpaceOnUse" />
+ xlink:href="#linearGradient3800"
+ id="radialGradient4171"
+ gradientUnits="userSpaceOnUse"
+ cx="27.702486"
+ cy="14.540437"
+ fx="27.702486"
+ fy="14.540437"
+ r="9.1620579"
+ gradientTransform="matrix(1.191087,0,0,1.124022,-5.086983,-1.361697)" />
<linearGradient
inkscape:collect="always"
- id="linearGradient3131">
+ id="linearGradient7300">
<stop
style="stop-color:#eeeeec;stop-opacity:1;"
offset="0"
- id="stop3133" />
+ id="stop7302" />
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:0;"
+ offset="1"
+ id="stop7304" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7300"
+ id="radialGradient7306"
+ cx="24.248138"
+ cy="27.184834"
+ fx="24.248138"
+ fy="27.184834"
+ r="12.499089"
+ gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3816">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3818" />
<stop
- style="stop-color:#d3d7cf"
+ style="stop-color:#000000;stop-opacity:0;"
offset="1"
- id="stop3135" />
+ id="stop3820" />
</linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3816"
+ id="radialGradient4179"
+ gradientUnits="userSpaceOnUse"
+ cx="31.112698"
+ cy="19.008621"
+ fx="31.112698"
+ fy="19.008621"
+ r="8.6620579" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7300"
+ id="radialGradient4244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+ cx="24.248138"
+ cy="27.184834"
+ fx="24.248138"
+ fy="27.184834"
+ r="12.499089" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3800"
+ id="radialGradient4246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.191087,0,0,1.124022,-5.086983,-1.361697)"
+ cx="27.702486"
+ cy="14.540437"
+ fx="27.702486"
+ fy="14.540437"
+ r="9.1620579" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7300"
+ id="radialGradient2631"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+ cx="24.248138"
+ cy="27.184834"
+ fx="24.248138"
+ fy="27.184834"
+ r="12.499089" />
+ <inkscape:perspective
+ id="perspective2506"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
- xlink:href="#linearGradient3131"
- id="linearGradient3137"
- x1="18.206755"
- y1="4.8468447"
- x2="18.150391"
- y2="13.775416"
+ xlink:href="#linearGradient3327"
+ id="linearGradient3333"
+ x1="32.26284"
+ y1="18.39094"
+ x2="40.463146"
+ y2="28.908117"
gradientUnits="userSpaceOnUse" />
+ <inkscape:perspective
+ id="perspective3358"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <radialGradient
+ r="9.1620579"
+ fy="14.809424"
+ fx="26.819485"
+ cy="14.809424"
+ cx="26.819485"
+ gradientTransform="matrix(0.9647715,0.3755394,-0.3764009,0.966985,7.9289748,-9.623708)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3376"
+ xlink:href="#linearGradient3800"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="25.307449"
+ x2="33.637684"
+ y1="20.449879"
+ x1="30.189112"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3378"
+ xlink:href="#linearGradient3335"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3327"
+ id="linearGradient3409"
+ gradientUnits="userSpaceOnUse"
+ x1="32.26284"
+ y1="18.39094"
+ x2="40.463146"
+ y2="28.908117" />
+ <inkscape:perspective
+ id="perspective3418"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
<radialGradient
inkscape:collect="always"
- xlink:href="#linearGradient2280"
- id="radialGradient2286"
- cx="10.332581"
- cy="6.9103003"
- fx="10.332581"
- fy="6.9103003"
- r="9.5"
- gradientTransform="matrix(1.895669,2.346468e-16,-3.526656e-16,2.238626,-11.89066,-12.56638)"
+ xlink:href="#linearGradient7300"
+ id="radialGradient3449"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+ cx="24.248138"
+ cy="27.184834"
+ fx="24.248138"
+ fy="27.184834"
+ r="12.499089" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3451"
+ id="linearGradient3457"
+ x1="5.0000005"
+ y1="11.446214"
+ x2="8.2252016"
+ y2="16.493296"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3475"
+ id="radialGradient3481"
+ cx="28.779234"
+ cy="14.68485"
+ fx="28.779234"
+ fy="14.68485"
+ r="9.8994964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1189106,0,0,1.1189106,-3.422157,-1.7461848)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3483"
+ id="linearGradient3489"
+ x1="30.669531"
+ y1="17.247086"
+ x2="34.812038"
+ y2="24.987169"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3301"
+ id="linearGradient3307"
+ x1="16.510134"
+ y1="13.467242"
+ x2="13.781501"
+ y2="9.1689024"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
@@ -97,20 +318,34 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="39.59798"
- inkscape:cx="14.115129"
- inkscape:cy="9.9583634"
+ inkscape:zoom="36.060436"
+ inkscape:cx="12.472661"
+ inkscape:cy="7.0601917"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
- fill="#eeeeec"
- inkscape:window-width="1268"
- inkscape:window-height="971"
- inkscape:window-x="6"
- inkscape:window-y="21" />
+ inkscape:window-width="1440"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="22"
+ width="11px"
+ height="11px"
+ inkscape:snap-nodes="false"
+ inkscape:snap-bbox="true"
+ objecttolerance="7"
+ gridtolerance="7"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2497"
+ empspacing="5"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
<metadata
- id="metadata13311">
+ id="metadata7385">
<rdf:RDF>
<cc:Work
rdf:about="">
@@ -125,91 +360,80 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
- style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.00000095;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 0.49919572,3.436028 L 0.49919572,8.024108 C 0.49919572,10.030365 0.43094041,12.50277 3.4779698,12.50277 L 3.5190997,14.938304 L 6.2391563,12.547388 L 10.144168,12.531827 C 13.477975,12.531827 15.5,11.518177 15.5,9.379431 L 15.484468,3.556577 C 15.484468,1.4311219 14.423292,0.51556128 12.453894,0.51556128 L 3.4753207,0.50000048 C 1.4741104,0.50000048 0.49919572,1.561826 0.49919572,3.436028 z "
- id="path2199"
- sodipodi:nodetypes="ccccccccccc" />
+ style="opacity:1;color:#000000;fill:#855b8c;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3307);stroke-width:0.99999987999999995px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ d="m 9.2852706,13.499999 3.6531493,0 c 1.0350584,0 2.059811,-0.3777864 2.4354322,-1.4545452 C 15.730548,11.022944 15.434737,9.0757587 13.121077,7.5000022 l -4.3228932,0 C 6.4845226,8.9545462 6.1953886,10.943768 6.7280666,12.106061 7.2707356,13.290156 8.1893256,13.5 9.2852706,13.5 z"
+ id="path3443"
+ sodipodi:nodetypes="cczcczc" />
<path
sodipodi:type="inkscape:offset"
- inkscape:radius="-1.0020103"
- inkscape:original="M 10.03125 5.5 C 5.2809556 5.6861502 1.5 8.7231084 1.5 12.4375 C 1.5 14.570281 2.4139213 16.850503 4.375 18.125 C 4.790921 19.906271 3.5825788 21.282326 3.375 21.5 C 3.7506605 21.398222 6.7302843 20.58004 7.84375 19.375 C 8.9660824 19.328744 9.5914383 19.40625 10.5 19.40625 C 15.465015 19.40625 19.500001 16.271711 19.5 12.4375 C 19.499999 8.6032883 15.465015 5.5 10.5 5.5 C 10.344844 5.4999998 10.184486 5.4939951 10.03125 5.5 z "
- xlink:href="#path13316"
- style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- id="path2288"
- inkscape:href="#path13316"
- d="M 10.0625,6.5 C 7.8900031,6.5851338 5.9762716,7.3427975 4.625,8.4375 C 3.2737284,9.5322025 2.5,10.920293 2.5,12.4375 C 2.5,14.284032 3.2837785,16.226812 4.90625,17.28125 C 5.1287835,17.423752 5.2860248,17.648382 5.34375,17.90625 C 5.4955807,18.556498 5.4707531,19.125743 5.375,19.65625 C 6.0781419,19.333618 6.8270886,18.976092 7.09375,18.6875 C 7.2809756,18.490079 7.540428,18.377274 7.8125,18.375 C 8.9961476,18.326217 9.6484235,18.40625 10.5,18.40625 C 12.771643,18.40625 14.815021,17.674738 16.25,16.5625 C 17.684979,15.450262 18.5,14.003112 18.5,12.4375 C 18.5,10.871887 17.684227,9.4172787 16.25,8.3125 C 14.815773,7.2077213 12.773745,6.5 10.5,6.5 C 10.318992,6.4999998 10.162289,6.4960896 10.0625,6.5 z "
- transform="matrix(-1,0,0,1,23.99247,-3.990738)" />
+ inkscape:radius="-1.1784238"
+ inkscape:original="M 24.5625 24.125 C 17.844986 28.367641 17.015916 34.172303 18.5625 37.5625 C 20.138096 41.016289 22.818019 41.625 26 41.625 L 36.59375 41.625 C 39.598953 41.624999 42.565667 40.546959 43.65625 37.40625 C 44.691891 34.423774 43.842514 28.721194 37.125 24.125 L 24.5625 24.125 z "
+ style="opacity:0.5;color:#000000;fill:url(#radialGradient3449);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:3.58186674000000016px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ id="path3445"
+ d="m 24.96875,25.3125 c -6.080867,3.980556 -6.594767,9.007702 -5.34375,11.75 0.693936,1.521145 1.541625,2.278662 2.5625,2.75 1.020875,0.471338 2.296653,0.625 3.8125,0.625 l 10.59375,0 c 1.361692,0 2.658712,-0.23791 3.6875,-0.78125 1.028788,-0.54334 1.79962,-1.327976 2.25,-2.625 0.804003,-2.315397 0.274744,-7.39869 -5.8125,-11.71875 z"
+ transform="matrix(0.2947246,0,0,0.2644629,1.8788978,1.8057826)" />
<path
- style="opacity:1;fill:url(#radialGradient2286);fill-opacity:1;stroke:#555753;stroke-width:1.00000095;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 0.49919572,3.436028 L 0.49919572,8.024108 C 0.49919572,10.030365 0.43094041,12.50277 3.4779698,12.50277 L 3.5190997,14.938304 L 6.2391563,12.547388 L 10.144168,12.531827 C 13.477975,12.531827 15.5,11.518177 15.5,9.379431 L 15.484468,3.556577 C 15.484468,1.4311219 14.423292,0.51556128 12.453894,0.51556128 L 3.4753207,0.50000048 C 1.4741104,0.50000048 0.49919572,1.561826 0.49919572,3.436028 z "
- id="rect1326"
- sodipodi:nodetypes="ccccccccccc" />
+ style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none"
+ d="m 12.999999,10.150001 c 0,1.0212 -1.053115,1.320578 -2,1.85 -1.339673,-0.49828 -2,-0.8288 -2,-1.85 0,-1.0212014 0.8959998,-1.8500014 2,-1.8500014 1.104,0 2,0.8288 2,1.8500014 z"
+ id="path3447"
+ sodipodi:nodetypes="ccssc" />
<path
- sodipodi:type="inkscape:offset"
- inkscape:radius="-0.99553573"
- inkscape:original="M 3.46875 0.5 C 1.4675396 0.5 0.5 1.563298 0.5 3.4375 L 0.5 8.03125 C 0.5 10.037507 0.42172061 12.5 3.46875 12.5 L 3.53125 14.9375 L 6.25 12.5625 L 10.15625 12.53125 C 13.490057 12.53125 15.5 11.513746 15.5 9.375 L 15.5 3.5625 C 15.5 1.4370449 14.438148 0.5 12.46875 0.5 L 3.46875 0.5 z "
- xlink:href="#rect1326"
- style="opacity:1;fill:none;fill-opacity:1;stroke:#eeeeec;stroke-width:1.03363752;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path2248"
- inkscape:href="#rect1326"
- d="M 3.96875,9.0625 C 3.1444632,9.0625002 2.7022172,9.2621947 2.4375,9.53125 C 2.1727828,9.8003053 2,10.234211 2,11 L 2,15.59375 C 2,16.609826 2.0106197,17.613475 2.25,18.1875 C 2.4893803,18.761525 2.71342,19.0625 3.96875,19.0625 C 4.5099878,19.05993 4.9541408,19.490203 4.96875,20.03125 L 4.96875,20.34375 L 6.09375,19.34375 C 6.2748748,19.183476 6.5081463,19.094611 6.75,19.09375 L 10.65625,19.09375 C 12.221872,19.09375 13.413842,18.823397 14.09375,18.4375 C 14.773658,18.051603 15,17.695013 15,16.9375 L 15,11.125 C 15,10.228058 14.802621,9.7651678 14.53125,9.5 C 14.259879,9.2348322 13.795556,9.0625 12.96875,9.0625 L 3.96875,9.0625 z "
- transform="matrix(0.997403,0,0,0.938411,3.636372e-2,0.123235)" />
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:#d28812;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3489);stroke-width:1.92490505999999995px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ id="path2549"
+ sodipodi:cx="31.112698"
+ sodipodi:cy="19.008621"
+ sodipodi:rx="8.6620579"
+ sodipodi:ry="8.6620579"
+ d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+ transform="matrix(0.5195068,0,0,0.5195069,-5.1632599,-4.8751084)" />
<path
+ d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+ sodipodi:ry="8.6620579"
+ sodipodi:rx="8.6620579"
+ sodipodi:cy="19.008621"
+ sodipodi:cx="31.112698"
+ id="path2551"
+ style="opacity:0.625;color:#000000;fill:url(#radialGradient3481);fill-opacity:1;stroke:url(#linearGradient3333);stroke-width:2.47487712px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc"
- style="opacity:0.2;fill:url(#radialGradient3156);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="path3140"
- sodipodi:cx="10.748654"
- sodipodi:cy="10.457643"
- sodipodi:rx="6.6449099"
- sodipodi:ry="2.3675451"
- d="M 17.393564 10.457643 A 6.6449099 2.3675451 0 1 1 4.1037445,10.457643 A 6.6449099 2.3675451 0 1 1 17.393564 10.457643 z"
- transform="matrix(1.655402,0,0,1.267134,-5.793347,6.748769)" />
+ transform="matrix(0.4040609,0,0,0.4040609,-1.5714252,-2.6806412)" />
<path
- sodipodi:type="inkscape:offset"
- inkscape:radius="-1.0020103"
- inkscape:original="M 10.03125 5.5 C 5.2809556 5.6861502 1.5 8.7231084 1.5 12.4375 C 1.5 14.570281 2.4139213 16.850503 4.375 18.125 C 4.790921 19.906271 3.5825788 21.282326 3.375 21.5 C 3.7506605 21.398222 6.7302843 20.58004 7.84375 19.375 C 8.9660824 19.328744 9.5914383 19.40625 10.5 19.40625 C 15.465015 19.40625 19.500001 16.271711 19.5 12.4375 C 19.499999 8.6032883 15.465015 5.5 10.5 5.5 C 10.344844 5.4999998 10.184486 5.4939951 10.03125 5.5 z "
- xlink:href="#path13316"
- style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- id="path13434"
- inkscape:href="#path13316"
- d="M 10.0625,6.5 C 7.8900031,6.5851338 5.9762716,7.3427975 4.625,8.4375 C 3.2737284,9.5322025 2.5,10.920293 2.5,12.4375 C 2.5,14.284032 3.2837785,16.226812 4.90625,17.28125 C 5.1287835,17.423752 5.2860248,17.648382 5.34375,17.90625 C 5.4955807,18.556498 5.4707531,19.125743 5.375,19.65625 C 6.0781419,19.333618 6.8270886,18.976092 7.09375,18.6875 C 7.2809756,18.490079 7.540428,18.377274 7.8125,18.375 C 8.9961476,18.326217 9.6484235,18.40625 10.5,18.40625 C 12.771643,18.40625 14.815021,17.674738 16.25,16.5625 C 17.684979,15.450262 18.5,14.003112 18.5,12.4375 C 18.5,10.871887 17.684227,9.4172787 16.25,8.3125 C 14.815773,7.2077213 12.773745,6.5 10.5,6.5 C 10.318992,6.4999998 10.162289,6.4960896 10.0625,6.5 z "
- transform="matrix(-1,0,0,1,32,-8.84375)" />
+ style="opacity:1;color:#000000;fill:#3465a4;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3457);stroke-width:0.99999987999999995px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ d="m 3.2852716,15.499999 3.6531493,0 c 1.0350584,0 2.059811,-0.3777864 2.4354322,-1.4545452 C 9.7305489,13.022944 9.4347379,11.075758 7.1210779,9.5000014 l -4.3228932,0 c -2.3136607,1.454544 -2.6027951,3.443766 -2.0701172,4.606059 0.54266971,1.184095 1.4612592,1.393939 2.5572041,1.393939 z"
+ id="path4308"
+ sodipodi:nodetypes="cczcczc" />
<path
sodipodi:type="inkscape:offset"
- inkscape:radius="-1.0020103"
- inkscape:original="M 10.03125 5.5 C 5.2809556 5.6861502 1.5 8.7231084 1.5 12.4375 C 1.5 14.570281 2.4139213 16.850503 4.375 18.125 C 4.790921 19.906271 3.5825788 21.282326 3.375 21.5 C 3.7506605 21.398222 6.7302843 20.58004 7.84375 19.375 C 8.9660824 19.328744 9.5914383 19.40625 10.5 19.40625 C 15.465015 19.40625 19.500001 16.271711 19.5 12.4375 C 19.499999 8.6032883 15.465015 5.5 10.5 5.5 C 10.344844 5.4999998 10.184486 5.4939951 10.03125 5.5 z "
- xlink:href="#path13316"
- style="color:#000000;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- id="path13323"
- inkscape:href="#path13316"
- d="M 10.0625,6.5 C 7.8900031,6.5851338 5.9762716,7.3427975 4.625,8.4375 C 3.2737284,9.5322025 2.5,10.920293 2.5,12.4375 C 2.5,14.284032 3.2837785,16.226812 4.90625,17.28125 C 5.1287835,17.423752 5.2860248,17.648382 5.34375,17.90625 C 5.4955807,18.556498 5.4707531,19.125743 5.375,19.65625 C 6.0781419,19.333618 6.8270886,18.976092 7.09375,18.6875 C 7.2809756,18.490079 7.540428,18.377274 7.8125,18.375 C 8.9961476,18.326217 9.6484235,18.40625 10.5,18.40625 C 12.771643,18.40625 14.815021,17.674738 16.25,16.5625 C 17.684979,15.450262 18.5,14.003112 18.5,12.4375 C 18.5,10.871887 17.684227,9.4172787 16.25,8.3125 C 14.815773,7.2077213 12.773745,6.5 10.5,6.5 C 10.318992,6.4999998 10.162289,6.4960896 10.0625,6.5 z "
- transform="matrix(0.750603,0,0,0.750603,0.123492,-2.625632)" />
- <rect
- style="opacity:1;fill:#9a9b96;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="rect2207"
- width="9"
- height="1"
- x="3"
- y="4"
- rx="0.5"
- ry="0.5" />
- <rect
- style="opacity:1;fill:#9a9b96;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="rect1343"
- width="8"
- height="1"
- x="3"
- y="8"
- rx="0.5"
- ry="0.5" />
- <rect
- style="opacity:1;fill:#9a9b96;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- id="rect1345"
- width="7"
- height="1"
- x="3"
- y="6"
- rx="0.5"
- ry="0.5" />
+ inkscape:radius="-1.1784238"
+ inkscape:original="M 24.5625 24.125 C 17.844986 28.367641 17.015916 34.172303 18.5625 37.5625 C 20.138096 41.016289 22.818019 41.625 26 41.625 L 36.59375 41.625 C 39.598953 41.624999 42.565667 40.546959 43.65625 37.40625 C 44.691891 34.423774 43.842514 28.721194 37.125 24.125 L 24.5625 24.125 z "
+ style="opacity:0.5;color:#000000;fill:url(#radialGradient2631);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:3.58186674000000016px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ id="path7281"
+ d="m 24.96875,25.3125 c -6.080867,3.980556 -6.594767,9.007702 -5.34375,11.75 0.693936,1.521145 1.541625,2.278662 2.5625,2.75 1.020875,0.471338 2.296653,0.625 3.8125,0.625 l 10.59375,0 c 1.361692,0 2.658712,-0.23791 3.6875,-0.78125 1.028788,-0.54334 1.79962,-1.327976 2.25,-2.625 0.804003,-2.315397 0.274744,-7.39869 -5.8125,-11.71875 z"
+ transform="matrix(0.2947246,0,0,0.2644629,-4.1211012,3.8057819)" />
+ <path
+ style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none"
+ d="M 7,12.15 C 7,13.1712 5.9468852,13.470578 5.0000003,14 3.6603268,13.50172 2.9999998,13.1712 2.9999998,12.15 c 0,-1.021201 0.896,-1.850001 2.000001,-1.850001 1.1039998,0 1.9999997,0.8288 1.9999997,1.850001 z"
+ id="path7285"
+ sodipodi:nodetypes="ccssc" />
+ <path
+ sodipodi:type="arc"
+ style="opacity:1;color:#000000;fill:url(#radialGradient3376);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3378);stroke-width:1.92490506px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ id="path3405"
+ sodipodi:cx="31.112698"
+ sodipodi:cy="19.008621"
+ sodipodi:rx="8.6620579"
+ sodipodi:ry="8.6620579"
+ d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+ transform="matrix(0.5195068,0,0,0.5195069,-11.163257,-2.8751094)" />
+ <path
+ d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+ sodipodi:ry="8.6620579"
+ sodipodi:rx="8.6620579"
+ sodipodi:cy="19.008621"
+ sodipodi:cx="31.112698"
+ id="path3407"
+ style="opacity:0.625;color:#000000;fill:none;stroke:url(#linearGradient3409);stroke-width:2.47487712px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ sodipodi:type="arc"
+ transform="matrix(0.4040609,0,0,0.4040609,-7.5714222,-0.6806422)" />
</g>
</svg>
diff --git a/pidgin/pixmaps/status/16/scalable/offline.svg b/pidgin/pixmaps/status/16/scalable/offline.svg
index 921bf13016..ba9c1ef4be 100644
--- a/pidgin/pixmaps/status/16/scalable/offline.svg
+++ b/pidgin/pixmaps/status/16/scalable/offline.svg
@@ -2,7 +2,7 @@
<!-- 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"
@@ -13,7 +13,7 @@
height="16"
id="svg2"
sodipodi:version="0.32"
- inkscape:version="0.45"
+ inkscape:version="0.46+devel"
version="1.0"
inkscape:export-filename="/home/hbons/Desktop/offline.png"
inkscape:export-xdpi="90"
@@ -26,25 +26,47 @@
id="defs4">
<linearGradient
inkscape:collect="always"
- id="linearGradient2225">
+ id="linearGradient3303">
<stop
- style="stop-color:#eeeeec;stop-opacity:1;"
+ style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
- id="stop2227" />
+ id="stop3305" />
<stop
- style="stop-color:#eeeeec;stop-opacity:0;"
+ style="stop-color:#ffffff;stop-opacity:0;"
offset="1"
- id="stop2229" />
+ id="stop3307" />
</linearGradient>
<linearGradient
inkscape:collect="always"
- xlink:href="#linearGradient2225"
- id="linearGradient2231"
- x1="11.802028"
- y1="1.9986149"
- x2="11.802028"
- y2="14.895812"
- gradientUnits="userSpaceOnUse" />
+ id="linearGradient3295">
+ <stop
+ style="stop-color:#babdb6;stop-opacity:1"
+ offset="0"
+ id="stop3297" />
+ <stop
+ style="stop-color:#888a85;stop-opacity:1"
+ offset="1"
+ id="stop3299" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3275">
+ <stop
+ style="stop-color:#2e3436;stop-opacity:1;"
+ offset="0"
+ id="stop3277" />
+ <stop
+ style="stop-color:#61635f;stop-opacity:1"
+ offset="1"
+ id="stop3279" />
+ </linearGradient>
+ <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="perspective34" />
<linearGradient
inkscape:collect="always"
id="linearGradient2186">
@@ -153,6 +175,33 @@
x2="12.233074"
y2="27.77807"
gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3275"
+ id="linearGradient3281"
+ x1="14.345539"
+ y1="14.69435"
+ x2="6.8097677"
+ y2="4.3450422"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3295"
+ id="linearGradient3301"
+ x1="4.8374491"
+ y1="3.565635"
+ x2="12.060304"
+ y2="13.821809"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3303"
+ id="linearGradient3309"
+ x1="6.8490844"
+ y1="8.6799088"
+ x2="-3.3852992"
+ y2="-4.1349444"
+ gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
@@ -161,17 +210,28 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="25.992076"
- inkscape:cx="26.27121"
- inkscape:cy="5.5692688"
+ inkscape:zoom="8"
+ inkscape:cx="1.3444167"
+ inkscape:cy="3.0859953"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
fill="#eeeeec"
- inkscape:window-width="1434"
- inkscape:window-height="844"
- inkscape:window-x="3"
- inkscape:window-y="25" />
+ inkscape:window-width="1440"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="22"
+ inkscape:snap-nodes="false"
+ inkscape:snap-bbox="true"
+ objecttolerance="11"
+ gridtolerance="10">
+ <inkscape:grid
+ type="xygrid"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ id="grid2503" />
+ </sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
@@ -188,7 +248,7 @@
inkscape:groupmode="layer"
id="layer1">
<path
- style="fill:#888a85;fill-opacity:1;stroke:#2e3436;stroke-width:0.99999827;stroke-miterlimit:4;stroke-opacity:1"
+ style="fill:url(#linearGradient3301);fill-opacity:1;stroke:url(#linearGradient3281);stroke-width:0.99999826999999997;stroke-miterlimit:4;stroke-opacity:1"
d="M 13.307074,13.307079 C 10.376958,16.237198 5.6213214,16.237693 2.6918157,13.308187 C -0.23769028,10.378679 -0.23719421,5.623042 2.692923,2.6929237 C 5.62304,-0.23719442 10.378675,-0.23769056 13.308181,2.6918165 C 16.237687,5.6213234 16.237192,10.376962 13.307074,13.307079 z "
id="path2187" />
<path
@@ -196,15 +256,22 @@
inkscape:radius="-1.0137641"
inkscape:original="M 8 0.5 C 6.0786384 0.50020041 4.1525585 1.2224409 2.6875 2.6875 C -0.24261721 5.6176183 -0.24200589 10.382992 2.6875 13.3125 C 5.6170057 16.242006 10.382384 16.242619 13.3125 13.3125 C 16.242618 10.382383 16.242006 5.6170068 13.3125 2.6875 C 11.847747 1.2227465 9.9213616 0.49979959 8 0.5 z "
xlink:href="#path2187"
- style="opacity:0.4;fill:url(#linearGradient2231);fill-opacity:1;stroke:#ffffff;stroke-width:0.99995583;stroke-miterlimit:4;stroke-opacity:1"
+ style="opacity:0.40000000000000002;fill:none;fill-opacity:1;stroke:url(#linearGradient3309);stroke-width:0.99995583000000021;stroke-miterlimit:4;stroke-opacity:1"
id="path2215"
inkscape:href="#path2187"
- d="M 8,1.5 C 6.3326173,1.5001739 4.674966,2.1375335 3.40625,3.40625 C 0.86479124,5.9477097 0.86538373,10.052882 3.40625,12.59375 C 5.9471154,15.134616 10.052293,15.135209 12.59375,12.59375 C 15.135209,10.052292 15.134616,5.9471167 12.59375,3.40625 C 11.325315,2.1378146 9.6669929,1.4998261 8,1.5 z "
- transform="matrix(1.000056,0,0,1.000028,-4.353349e-4,-2.926381e-5)" />
+ d="M 8,1.5 C 6.3326173,1.5001739 4.674966,2.1375335 3.40625,3.40625 c -2.5414588,2.5414597 -2.5408663,6.646632 0,9.1875 2.5408654,2.540866 6.646043,2.541459 9.1875,0 2.541459,-2.541458 2.540866,-6.6466333 0,-9.1875 C 11.325315,2.1378146 9.6669929,1.4998261 8,1.5 z"
+ transform="matrix(-1.000056,0,0,-1.000028,16.000461,16.000041)" />
<path
- style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:1.00000036;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
- d="M 11.54182,4.2658182 C 10.931116,3.6551131 10.447965,3.347327 9.8372602,3.9580321 L 8.0000001,5.7952921 L 6.1627401,3.9580321 C 5.5520351,3.3473271 4.9919392,3.7705329 4.381234,4.381238 C 3.770529,4.991943 3.347323,5.5520393 3.9580281,6.1627441 L 5.7952881,8.0000041 L 3.9580281,9.8372641 C 3.3473229,10.447969 3.6935827,10.969592 4.3042875,11.580298 C 4.9149927,12.191002 5.5520349,12.652681 6.1627401,12.041977 L 8.0000001,10.204716 L 9.8372602,12.041977 C 10.447965,12.652681 11.046535,12.306422 11.657239,11.695718 C 12.267944,11.085012 12.652677,10.447969 12.041972,9.8372641 L 10.204713,8.0000041 L 12.041972,6.1627441 C 12.652677,5.552039 12.152526,4.8765236 11.54182,4.2658182 z "
+ style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:#555753;stroke-width:1.00000036;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="M 11.85704,4.0822721 C 11.226174,3.4531102 10.534706,3.2129687 9.903839,3.8421305 L 8.0059223,5.7349162 6.1080057,3.8421305 C 5.4771385,3.2129688 4.7446581,3.5335449 4.1137906,4.1627068 3.4829234,4.7918685 3.1996385,5.4843118 3.8305058,6.1134733 L 5.7284224,8.006259 3.8305058,9.8990447 C 3.1996384,10.528207 3.4803833,11.334907 4.1112504,11.96407 4.7421178,12.59323 5.4771383,12.799549 6.1080057,12.170389 L 8.0059223,10.277602 9.903839,12.170389 C 10.534706,12.799549 11.306931,12.596719 11.937797,11.967558 12.568664,11.338395 12.812206,10.528207 12.181339,9.8990449 L 10.283423,8.0062592 12.181339,6.1134736 C 12.812206,5.4843115 12.487908,4.7114342 11.85704,4.0822721 z"
id="rect2920"
sodipodi:nodetypes="ccccscccscccscccc" />
+ <rect
+ style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none"
+ id="rect3285"
+ width="4"
+ height="4"
+ x="6"
+ y="6" />
</g>
</svg>
diff --git a/pidgin/pixmaps/status/16/scalable/person.svg b/pidgin/pixmaps/status/16/scalable/person.svg
index 1bf818de84..66fbe820df 100644
--- a/pidgin/pixmaps/status/16/scalable/person.svg
+++ b/pidgin/pixmaps/status/16/scalable/person.svg
@@ -2,26 +2,107 @@
<!-- 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"
+ width="16"
+ height="16"
id="svg7380"
sodipodi:version="0.32"
- inkscape:version="0.44.1"
- sodipodi:docbase="/home/hbons/GUI/Tango/Gaim Refresh/status/16/scalable"
+ inkscape:version="0.46+devel"
+ sodipodi:docbase="/home/hbons/Desktop"
sodipodi:docname="person.svg"
- inkscape:export-filename="/home/hbons/GUI/Tango/Gaim Refresh/status/16/person.png"
+ inkscape:export-filename="/home/hbons/Desktop/person.png"
inkscape:export-xdpi="90"
- inkscape:export-ydpi="90">
+ inkscape:export-ydpi="90"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0">
<defs
id="defs7382">
<linearGradient
+ inkscape:collect="always"
+ id="linearGradient3301">
+ <stop
+ style="stop-color:#46284e;stop-opacity:1;"
+ offset="0"
+ id="stop3303" />
+ <stop
+ style="stop-color:#7a4588;stop-opacity:1"
+ offset="1"
+ id="stop3305" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3483">
+ <stop
+ style="stop-color:#c17802;stop-opacity:1"
+ offset="0"
+ id="stop3485" />
+ <stop
+ style="stop-color:#935a00;stop-opacity:1"
+ offset="1"
+ id="stop3487" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3475">
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:1;"
+ offset="0"
+ id="stop3477" />
+ <stop
+ style="stop-color:#eeeeec;stop-opacity:0;"
+ offset="1"
+ id="stop3479" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3451">
+ <stop
+ style="stop-color:#3465a4;stop-opacity:1"
+ offset="0"
+ id="stop3453" />
+ <stop
+ style="stop-color:#15325b;stop-opacity:1"
+ offset="1"
+ id="stop3455" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3335">
+ <stop
+ style="stop-color:#b2730d;stop-opacity:1;"
+ offset="0"
+ id="stop3337" />
+ <stop
+ style="stop-color:#935f0a;stop-opacity:1"
+ offset="1"
+ id="stop3339" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3327">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1;"
+ offset="0"
+ id="stop3329" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0;"
+ offset="1"
+ id="stop3331" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 5.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="11 : 5.5 : 1"
+ inkscape:persp3d-origin="5.5 : 3.6666667 : 1"
+ id="perspective28" />
+ <linearGradient
id="linearGradient3800">
<stop
style="stop-color:#f4d9b1;stop-opacity:1.0000000;"
@@ -110,6 +191,160 @@
fx="27.702486"
fy="14.540437"
r="9.1620579" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7300"
+ id="radialGradient2631"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.964825,0,0,0.631898,0.954495,11.94073)"
+ cx="24.248138"
+ cy="27.184834"
+ fx="24.248138"
+ fy="27.184834"
+ r="12.499089" />
+ <inkscape:perspective
+ id="perspective2506"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3327"
+ id="linearGradient3333"
+ x1="32.26284"
+ y1="18.39094"
+ x2="40.463146"
+ y2="28.908117"
+ gradientUnits="userSpaceOnUse" />
+ <inkscape:perspective
+ id="perspective3358"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <radialGradient
+ r="9.1620579"
+ fy="14.809424"
+ fx="26.819485"
+ cy="14.809424"
+ cx="26.819485"
+ gradientTransform="matrix(0.9647715,0.3755394,-0.3764009,0.966985,7.9289748,-9.623708)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3376"
+ xlink:href="#linearGradient3800"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="25.307449"
+ x2="33.637684"
+ y1="20.449879"
+ x1="30.189112"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient3378"
+ xlink:href="#linearGradient3335"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3327"
+ id="linearGradient3409"
+ gradientUnits="userSpaceOnUse"
+ x1="32.26284"
+ y1="18.39094"
+ x2="40.463146"
+ y2="28.908117" />
+ <inkscape:perspective
+ id="perspective3418"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3451"
+ id="linearGradient3457"
+ x1="5.0000005"
+ y1="11.446214"
+ x2="8.2252016"
+ y2="16.493296"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3475"
+ id="radialGradient3481"
+ cx="28.779234"
+ cy="14.68485"
+ fx="28.779234"
+ fy="14.68485"
+ r="9.8994964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1189106,0,0,1.1189106,-3.422157,-1.7461848)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3483"
+ id="linearGradient3489"
+ x1="30.669531"
+ y1="17.247086"
+ x2="34.812038"
+ y2="24.987169"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3301"
+ id="linearGradient3307"
+ x1="13.753093"
+ y1="16.35816"
+ x2="11.875512"
+ y2="10.748822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1111113,0,0,1.1666667,-4.7222232,-0.2500063)" />
+ <inkscape:perspective
+ id="perspective2538"
+ inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+ inkscape:vp_z="1 : 0.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 0.5 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ <radialGradient
+ r="9.1620579"
+ fy="14.809424"
+ fx="26.819485"
+ cy="14.809424"
+ cx="26.819485"
+ gradientTransform="matrix(0.9647715,0.3755394,-0.3764009,0.966985,7.9289748,-9.623708)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient2556"
+ xlink:href="#linearGradient3800"
+ inkscape:collect="always" />
+ <linearGradient
+ y2="25.307449"
+ x2="33.637684"
+ y1="20.449879"
+ x1="30.189112"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2558"
+ xlink:href="#linearGradient3335"
+ inkscape:collect="always" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3327"
+ id="linearGradient2587"
+ gradientUnits="userSpaceOnUse"
+ x1="32.26284"
+ y1="18.39094"
+ x2="40.463146"
+ y2="28.908117" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient7300"
+ id="linearGradient3300"
+ x1="17.57398"
+ y1="32.875"
+ x2="44.321774"
+ y2="32.875"
+ gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
@@ -118,17 +353,32 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="36.060436"
- inkscape:cx="13.280605"
- inkscape:cy="9.4853742"
+ inkscape:zoom="25.498579"
+ inkscape:cx="8.3447229"
+ inkscape:cy="9.4891345"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
- inkscape:window-width="1274"
- inkscape:window-height="972"
- inkscape:window-x="6"
- inkscape:window-y="25" />
+ inkscape:window-width="1440"
+ inkscape:window-height="847"
+ inkscape:window-x="0"
+ inkscape:window-y="22"
+ width="11px"
+ height="11px"
+ inkscape:snap-nodes="false"
+ inkscape:snap-bbox="true"
+ objecttolerance="7"
+ gridtolerance="7"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2497"
+ empspacing="5"
+ visible="true"
+ enabled="true" />
+ </sodipodi:namedview>
<metadata
id="metadata7385">
<rdf:RDF>
@@ -145,44 +395,42 @@
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<path
- style="opacity:1;color:black;fill:#ad7fa8;fill-opacity:1;fill-rule:evenodd;stroke:#5c3566;stroke-width:2.39089775px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- d="M 25.986174,41.636039 L 36.592776,41.636039 C 39.59798,41.636039 42.57326,40.534107 43.663843,37.393398 C 44.699482,34.410922 43.84062,28.73134 37.123106,24.135146 L 24.57196,24.135146 C 17.854446,28.377786 17.014969,34.179977 18.561553,37.570174 C 20.137148,41.023964 22.804193,41.636039 25.986174,41.636039 z "
- id="path4308"
- sodipodi:nodetypes="cczcczc"
- transform="matrix(0.382691,0,0,0.457119,-3.349933,-3.532635)" />
+ style="opacity:1;color:#000000;fill:#855b8c;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient3307);stroke-width:0.99999963999999997px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ d="m 5.5947456,15.499992 4.0590553,0 c 1.1500651,0 2.2886792,-0.44075079 2.7060362,-1.6969693 C 12.756166,12.610095 12.427487,10.338379 9.856753,8.4999962 l -4.8032154,0 C 2.4828029,10.196964 2.1615429,12.517723 2.7534074,13.873731 c 0.6029656,1.381444 1.6236213,1.626262 2.8413382,1.626262 z"
+ id="path3443"
+ sodipodi:nodetypes="cczcczc" />
<path
sodipodi:type="inkscape:offset"
inkscape:radius="-1.1784238"
inkscape:original="M 24.5625 24.125 C 17.844986 28.367641 17.015916 34.172303 18.5625 37.5625 C 20.138096 41.016289 22.818019 41.625 26 41.625 L 36.59375 41.625 C 39.598953 41.624999 42.565667 40.546959 43.65625 37.40625 C 44.691891 34.423774 43.842514 28.721194 37.125 24.125 L 24.5625 24.125 z "
- style="opacity:0.45454544;color:black;fill:url(#radialGradient7306);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:2.73569775px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- id="path7281"
- d="M 24.96875,25.3125 C 18.887883,29.293056 18.373983,34.320202 19.625,37.0625 C 20.318936,38.583645 21.166625,39.341162 22.1875,39.8125 C 23.208375,40.283838 24.484153,40.4375 26,40.4375 L 36.59375,40.4375 C 37.955442,40.4375 39.252462,40.19959 40.28125,39.65625 C 41.310038,39.11291 42.08087,38.328274 42.53125,37.03125 C 43.335253,34.715853 42.805994,29.63256 36.71875,25.3125 L 24.96875,25.3125 z "
- transform="matrix(0.336828,0,0,0.396695,-1.924113,-1.54134)" />
+ style="opacity:0.5;color:#000000;fill:url(#linearGradient3300);fill-opacity:1;fill-rule:evenodd;stroke:#eeeeec;stroke-width:2.99680495px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ id="path3445"
+ d="m 24.96875,25.3125 c -6.080867,3.980556 -6.594767,9.007702 -5.34375,11.75 0.693936,1.521145 1.541625,2.278662 2.5625,2.75 1.020875,0.471338 2.296653,0.625 3.8125,0.625 l 10.59375,0 c 1.361692,0 2.658712,-0.23791 3.6875,-0.78125 1.028788,-0.54334 1.79962,-1.327976 2.25,-2.625 0.804003,-2.315397 0.274744,-7.39869 -5.8125,-11.71875 z"
+ transform="matrix(-0.3368281,0,0,0.3305786,17.924115,1.1322288)" />
<path
- transform="matrix(0.761596,0,0,0.870395,-3.301291,-7.391088)"
- style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
- d="M 20.091094,19.980694 C 20.091094,21.249084 17.671238,21.620929 15.495478,22.278502 C 12.417166,21.65961 10.899861,21.249084 10.899861,19.980694 C 10.899861,18.712304 12.958697,17.682885 15.495478,17.682885 C 18.032258,17.682885 20.091094,18.712304 20.091094,19.980694 z "
- id="path7285"
+ style="opacity:0.78977272;fill:#eeeeec;fill-opacity:1;stroke:none"
+ d="M 10,10.500001 C 10,11.88 8.6836063,12.284565 7.5,13 5.8254087,12.326649 5,11.88 5,10.500001 5,9.1200001 6.1199997,8 7.5,8 8.88,8 10,9.1199996 10,10.500001 z"
+ id="path3447"
sodipodi:nodetypes="ccssc" />
<path
sodipodi:type="arc"
- style="opacity:1;color:black;fill:url(#radialGradient4171);fill-opacity:1;fill-rule:evenodd;stroke:#b2730d;stroke-width:1.73241472px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
- id="path4320"
+ style="opacity:1;color:#000000;fill:url(#radialGradient2556);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient2558);stroke-width:1.73241425px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
+ id="path3405"
sodipodi:cx="31.112698"
sodipodi:cy="19.008621"
sodipodi:rx="8.6620579"
sodipodi:ry="8.6620579"
- d="M 39.774755 19.008621 A 8.6620579 8.6620579 0 1 1 22.45064,19.008621 A 8.6620579 8.6620579 0 1 1 39.774755 19.008621 z"
- transform="matrix(0.57723,0,0,0.57723,-9.459179,-5.472346)" />
+ d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
+ transform="matrix(0.57723,0,0,0.5772299,-10.459182,-5.4723453)" />
<path
- d="M 39.774755 19.008621 A 8.6620579 8.6620579 0 1 1 22.45064,19.008621 A 8.6620579 8.6620579 0 1 1 39.774755 19.008621 z"
+ d="m 39.774755,19.008621 a 8.6620579,8.6620579 0 1 1 -17.324116,0 A 8.6620579,8.6620579 0 1 1 39.774755,19.008621 z"
sodipodi:ry="8.6620579"
sodipodi:rx="8.6620579"
sodipodi:cy="19.008621"
sodipodi:cx="31.112698"
- id="path4322"
- style="opacity:0.25;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:white;stroke-width:2.165519px;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
+ id="path3407"
+ style="opacity:0.625;color:#000000;fill:none;stroke:url(#linearGradient2587);stroke-width:2.16551685px;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible"
sodipodi:type="arc"
- transform="matrix(0.461783,0,0,0.461784,-5.867326,-3.277881)" />
+ transform="matrix(0.461784,0,0,0.461784,-6.8673454,-3.2778773)" />
</g>
</svg>
diff --git a/pidgin/pixmaps/toolbar/16/send-file.png b/pidgin/pixmaps/toolbar/16/send-file.png
index a16c36e4ea..1796675d2c 100644
--- a/pidgin/pixmaps/toolbar/16/send-file.png
+++ b/pidgin/pixmaps/toolbar/16/send-file.png
Binary files differ
diff --git a/pidgin/pixmaps/toolbar/16/transfer.png b/pidgin/pixmaps/toolbar/16/transfer.png
new file mode 100644
index 0000000000..a16c36e4ea
--- /dev/null
+++ b/pidgin/pixmaps/toolbar/16/transfer.png
Binary files differ
diff --git a/pidgin/plugins/Makefile.mingw b/pidgin/plugins/Makefile.mingw
index eb3d003173..478d73e5e7 100644
--- a/pidgin/plugins/Makefile.mingw
+++ b/pidgin/plugins/Makefile.mingw
@@ -84,6 +84,7 @@ plugins: \
notify.dll \
pidginrc.dll \
relnot.dll \
+ sendbutton.dll \
spellchk.dll \
timestamp_format.dll \
timestamp.dll
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/spellchk.c b/pidgin/plugins/spellchk.c
index b6b9bae3ae..af86e50ad0 100644
--- a/pidgin/plugins/spellchk.c
+++ b/pidgin/plugins/spellchk.c
@@ -1740,6 +1740,7 @@ static void load_conf(void)
"BAD wroking\nGOOD working\n"
"BAD wtih\nGOOD with\n"
"BAD wuould\nGOOD would\n"
+ "BAD wud\nGOOD would\n"
"BAD wut\nGOOD what\n"
"BAD wya\nGOOD way\n"
"BAD y\nGOOD why\n"
diff --git a/pidgin/win32/nsis/pidgin-installer.nsi b/pidgin/win32/nsis/pidgin-installer.nsi
index 6eda25ff0e..faa6cb099c 100644
--- a/pidgin/win32/nsis/pidgin-installer.nsi
+++ b/pidgin/win32/nsis/pidgin-installer.nsi
@@ -748,6 +748,7 @@ Section Uninstall
Delete "$INSTDIR\plugins\pidginrc.dll"
Delete "$INSTDIR\plugins\psychic.dll"
Delete "$INSTDIR\plugins\relnot.dll"
+ Delete "$INSTDIR\plugins\sendbutton.dll"
Delete "$INSTDIR\plugins\spellchk.dll"
Delete "$INSTDIR\plugins\ssl-nss.dll"
Delete "$INSTDIR\plugins\ssl.dll"
@@ -1387,67 +1388,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/po/ar.po b/po/ar.po
index 535715f56b..e4eff98e74 100644
--- a/po/ar.po
+++ b/po/ar.po
@@ -12918,12 +12918,18 @@ msgstr "احفظ باسم..."
#, c-format
msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
-msgstr[0] "‏%1$s لا يطلب من %2$s أن يقبل أيّة ملفات : %4$s (%5$.2f %6$s)%7$s%8$s"
-msgstr[1] "‏%1$s يطلب من %2$s أن يقبل ملفا واحدا: %4$s (%5$.2f %6$s)%7$s%8$s"
-msgstr[2] "‏%1$s يطلب من %2$s أن يقبل ملفين: %4$s (%5$.2f %6$s)%7$s%8$s"
-msgstr[3] "‏%s يطلب من %s أن يقبل %Id ملفات: %s (%.2f %s)%s%s"
-msgstr[4] "‏%s يطلب من %s أن يقبل %Id ملفا: %s (%.2f %s)%s%s"
-msgstr[5] "‏%s يطلب من %s أن يقبل %Id ملف: %s (%.2f %s)%s%s"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+#msgstr[0] "‏%1$s لا يطلب من %2$s أن يقبل أيّة ملفات : %4$s (%5$.2f %6$s)%7$s%8$s"
+#msgstr[1] "‏%1$s يطلب من %2$s أن يقبل ملفا واحدا: %4$s (%5$.2f %6$s)%7$s%8$s"
+#msgstr[2] "‏%1$s يطلب من %2$s أن يقبل ملفين: %4$s (%5$.2f %6$s)%7$s%8$s"
+#msgstr[3] "‏%s يطلب من %s أن يقبل %Id ملفات: %s (%.2f %s)%s%s"
+#msgstr[4] "‏%s يطلب من %s أن يقبل %Id ملفا: %s (%.2f %s)%s%s"
+#msgstr[5] "‏%s يطلب من %s أن يقبل %Id ملف: %s (%.2f %s)%s%s"
#: ../libpurple/protocols/toc/toc.c:2216
#, c-format
@@ -14108,12 +14114,18 @@ msgstr ""
#, c-format
msgid "You have %d contact named %s. Would you like to merge them?"
msgid_plural "You currently have %d contacts named %s. Would you like to merge them?"
-msgstr[0] "لا مراسلين لديك باسم %2$s. أتريد دمجهم؟"
-msgstr[1] "لديك مراسل واحد باسم %2$s. أتريد دمجه؟"
-msgstr[2] "لديك مراسليْن باسم %2$s. أتريد دمجهما؟"
-msgstr[3] "لديك %Id مراسلين باسم %s. أتريد دمجهم؟"
-msgstr[4] "لديك %Id مراسلا باسم %s. أتريد دمجهم؟"
-msgstr[5] "لديك %Id مراسل باسم %s. أتريد دمجهم؟"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+#msgstr[0] "لا مراسلين لديك باسم %2$s. أتريد دمجهم؟"
+#msgstr[1] "لديك مراسل واحد باسم %2$s. أتريد دمجه؟"
+#msgstr[2] "لديك مراسليْن باسم %2$s. أتريد دمجهما؟"
+#msgstr[3] "لديك %Id مراسلين باسم %s. أتريد دمجهم؟"
+#msgstr[4] "لديك %Id مراسلا باسم %s. أتريد دمجهم؟"
+#msgstr[5] "لديك %Id مراسل باسم %s. أتريد دمجهم؟"
#: ../pidgin/gtkblist.c:550
msgid ""
@@ -14476,12 +14488,18 @@ msgstr "/الأدوات/قائمة الغرف"
#, c-format
msgid "%d unread message from %s\n"
msgid_plural "%d unread messages from %s\n"
-msgstr[0] "لا رسائل غير مقروءة من %2$s\n"
-msgstr[1] "رسالة واحدة غير مقروءة من %2$s\n"
-msgstr[2] "رسالتان غير مقروءتان من %2$s\n"
-msgstr[3] "%Id رسائل غير مقروءة من %s\n"
-msgstr[4] "%Id رسالة غير مقروءة من %s\n"
-msgstr[5] "%Id رسالة غير مقروءة من %s\n"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+#msgstr[0] "لا رسائل غير مقروءة من %2$s\n"
+#msgstr[1] "رسالة واحدة غير مقروءة من %2$s\n"
+#msgstr[2] "رسالتان غير مقروءتان من %2$s\n"
+#msgstr[3] "%Id رسائل غير مقروءة من %s\n"
+#msgstr[4] "%Id رسالة غير مقروءة من %s\n"
+#msgstr[5] "%Id رسالة غير مقروءة من %s\n"
#: ../pidgin/gtkblist.c:4238
msgid "Manually"
diff --git a/po/bn.po b/po/bn.po
index d80e30d5b1..5a44cbce7b 100644
--- a/po/bn.po
+++ b/po/bn.po
@@ -6296,8 +6296,9 @@ msgid ""
"%s on the local list is inside the group \"%s\" but not on the server list. "
"Do you want this buddy to be added?"
msgstr ""
-"স্থানীয় তালিকার %s গ্রুপ এ আছেন কিন্তু সার্ভার তালিকায় নেই।আপনি কি এই বন্ধুটিকে যোগ "
-"করতে চান?"
+#msgstr ""
+#"স্থানীয় তালিকার %s গ্রুপ এ আছেন কিন্তু সার্ভার তালিকায় নেই।আপনি কি এই বন্ধুটিকে যোগ "
+#"করতে চান?"
#: ../libpurple/protocols/msn/dialog.c:124
#, c-format
@@ -7833,7 +7834,8 @@ msgstr ""
#: ../libpurple/protocols/novell/novell.c:705
#, c-format
msgid "Could not get details for user %s (%s)."
-msgstr "ব্যবহারকারী %s এর বিবরন পাওয়া যাচ্ছে না"
+msgstr ""
+#msgstr "ব্যবহারকারী %s এর বিবরন পাওয়া যাচ্ছে না"
#: ../libpurple/protocols/novell/novell.c:751
#: ../libpurple/protocols/novell/novell.c:897
@@ -13730,8 +13732,8 @@ msgstr "/টুল/আসর তালিকা"
#, c-format
msgid "%d unread message from %s\n"
msgid_plural "%d unread messages from %s\n"
-msgstr[0] "%s এর %d টি না পড়া বার্তা রয়েছে\n"
-msgstr[1] "%s এর %d টি না পড়া বার্তা রয়েছে\n"
+msgstr[0] "%2$s এর %1$d টি না পড়া বার্তা রয়েছে\n"
+msgstr[1] "%2$s এর %1$d টি না পড়া বার্তা রয়েছে\n"
#: ../pidgin/gtkblist.c:3822
msgid "Manually"
diff --git a/po/bs.po b/po/bs.po
index dee9e9d89a..78747248b4 100644
--- a/po/bs.po
+++ b/po/bs.po
@@ -8858,7 +8858,7 @@ msgstr[1] "Propustili ste %hu poruke od %s, jer nisu bile validne."
msgid "You missed %hu message from %s because it was too large."
msgid_plural "You missed %hu messages from %s because they were too large."
msgstr[0] "Propustili ste %hu poruku od %s, jer je bila prevelika."
-msgstr[1] "Propustili ste %hu poruke, jer su bile prevelike."
+msgstr[1] "Propustili ste %hu poruke od %s, jer su bile prevelike."
#: ../libpurple/protocols/oscar/oscar.c:2614
#, c-format
diff --git a/po/ca@valencia.po b/po/ca@valencia.po
index 8c18a5c8d0..bdb6d9164c 100644
--- a/po/ca@valencia.po
+++ b/po/ca@valencia.po
@@ -13740,15 +13740,16 @@ msgid ""
"You can come back to this window to add, edit, or remove accounts from "
"<b>Accounts->Add/Edit</b> in the Buddy List window"
msgstr ""
-"<span size='larger' weight='bold'>Benvinguts al %s!</span>\n"
-"\n"
-"No teniu cap compte de MI configurat. Per a connectar-vos amb el %s premeu "
-"el botó <b>Afig</b> de davall, i configureu el vostre primer compte. Si "
-"voleu connectar-vos amb més d'un compte de MI, torneu a prémer <b>Afig</b> "
-"fins a configurar-los tots.\n"
-"\n"
-"Podeu tornar a esta finestra per afegir, editar o suprimir comptes, a partir "
-"del menú <b>Comptes->Afig/Edita</b> de la finestra de la llista d'amics."
+#msgstr ""
+#"<span size='larger' weight='bold'>Benvinguts al %s!</span>\n"
+#"\n"
+#"No teniu cap compte de MI configurat. Per a connectar-vos amb el %s premeu "
+#"el botó <b>Afig</b> de davall, i configureu el vostre primer compte. Si "
+#"voleu connectar-vos amb més d'un compte de MI, torneu a prémer <b>Afig</b> "
+#"fins a configurar-los tots.\n"
+#"\n"
+#"Podeu tornar a esta finestra per afegir, editar o suprimir comptes, a partir "
+#"del menú <b>Comptes->Afig/Edita</b> de la finestra de la llista d'amics."
#: ../pidgin/gtkblist.c:767
msgid "Join a Chat"
diff --git a/po/de.po b/po/de.po
index 9bd6c3f13e..ec22c0299c 100644
--- a/po/de.po
+++ b/po/de.po
@@ -11,9 +11,9 @@ msgid ""
msgstr ""
"Project-Id-Version: de\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-26 21:14+0200\n"
-"PO-Revision-Date: 2008-05-26 21:14+0200\n"
-"Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
+"POT-Creation-Date: 2008-06-25 23:30+0200\n"
+"PO-Revision-Date: 2008-06-25 23:27+0200\n"
+"Last-Translator: Bjoern Voigt <bjoern@cs.tu-berlin.de>\n"
"Language-Team: Deutsch <de@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -413,6 +413,9 @@ msgstr "Chat betreten..."
msgid "View Log..."
msgstr "Mitschnitt anzeigen..."
+msgid "View All Logs"
+msgstr "Alle Mitschnitte anzeigen"
+
msgid "Show"
msgstr "Anzeigen"
@@ -612,6 +615,19 @@ msgstr ""
msgid "Send To"
msgstr "Senden an"
+msgid "Invite message"
+msgstr "Einladungsnachricht"
+
+msgid "Invite"
+msgstr "Einladen"
+
+msgid ""
+"Please enter the name of the user you wish to invite,\n"
+"along with an optional invite message."
+msgstr ""
+"Bitte geben Sie den Benutzernamen der Person ein, die Sie einladen möchten "
+"zusammen mit einer optionalen Einladungsnachricht."
+
msgid "Conversation"
msgstr "Unterhaltung"
@@ -624,6 +640,9 @@ msgstr "Zeige Zeitstempel"
msgid "Add Buddy Pounce..."
msgstr "Buddy-Alarm hinzufügen..."
+msgid "Invite..."
+msgstr "Einladen..."
+
msgid "Enable Logging"
msgstr "Mitschnitt einschalten"
@@ -651,6 +670,20 @@ msgstr ""
"zu erhalten.\n"
"Die folgenden Kommandos sind in diesem Kontext verfügbar:\n"
+#, c-format
+msgid ""
+"%s is not a valid message class. See '/help msgcolor' for valid message "
+"classes."
+msgstr ""
+"%s ist keine gültige Nachrichtenklasse. Die gültigen Nachrichtenklassen "
+"finden Sie unter '/help msgcolor'."
+
+#, c-format
+msgid "%s is not a valid color. See '/help msgcolor' for valid colors."
+msgstr ""
+"%s ist keine gültige Farbe. Die gültigen Farben finden Sie unter '/help "
+"msgcolor'."
+
msgid ""
"say &lt;message&gt;: Send a message normally as if you weren't using a "
"command."
@@ -697,6 +730,20 @@ msgstr "prefs: Das Einstellungs-Fenster anzeigen."
msgid "statuses: Show the savedstatuses window."
msgstr "statuses: Das Fenster mit gespeicherten Status-Infos anzeigen."
+msgid ""
+"msgcolor &lt;class&gt; &lt;foreground&gt; &lt;background&gt;: Set the color "
+"for different classes of messages in the conversation window.<br> &lt;"
+"class&gt;: receive, send, highlight, action, timestamp<br> &lt;foreground/"
+"background&gt;: black, red, green, blue, white, gray, darkgray, magenta, "
+"cyan, default<br><br>EXAMPLE:<br> msgcolor send cyan default"
+msgstr ""
+"msgcolor &lt;Klasse&gt; &lt;Vordergrund&gt; &lt;Hintergrund&gt;: Die Farbe "
+"für verschiedene Klassen von Nachrichten im Gesprächsfenster festlegen."
+"<br> &lt;Klasse&gt;: receive, send, highlight, action, timestamp<br> "
+"&lt;Vordergrund/Hintergrund&gt;: black, red, green, blue, white, gray, "
+"darkgray, magenta, cyan, default<br><br>BEISPIEL:<br> msgcolor send cyan "
+"default"
+
msgid "Unable to open file."
msgstr "Konnte die Datei nicht öffnen."
@@ -717,8 +764,10 @@ msgid "Pause"
msgstr "Pause"
#, c-format
-msgid "File Transfers - %d%% of %d files"
-msgstr "Dateiübertragungen - %d%% von %d Dateien"
+msgid "File Transfers - %d%% of %d file"
+msgid_plural "File Transfers - %d%% of %d files"
+msgstr[0] "Dateiübertragungen - %d%% von %d Datei"
+msgstr[1] "Dateiübertragungen - %d%% von %d Dateien"
#. Create the window.
msgid "File Transfers"
@@ -833,11 +882,14 @@ msgstr "Unterhaltung in %s"
msgid "Conversations with %s"
msgstr "Unterhaltung mit %s"
+msgid "All Conversations"
+msgstr "Alle Unterhaltungen"
+
msgid "System Log"
msgstr "System-Mitschnitt"
msgid "Emails"
-msgstr "Emails"
+msgstr "E-Mails"
msgid "You have mail!"
msgstr "Sie haben Post!"
@@ -870,9 +922,6 @@ msgstr "Fortfahren"
msgid "IM"
msgstr "Nachricht"
-msgid "Invite"
-msgstr "Einladen"
-
msgid "(none)"
msgstr "(kein)"
@@ -2785,7 +2834,7 @@ msgid "Last name"
msgstr "Nachname"
msgid "Email"
-msgstr "Email"
+msgstr "E-Mail"
msgid "AIM Account"
msgstr "AIM-Konto"
@@ -2875,6 +2924,9 @@ msgstr "Buddy-Liste erfolgreich geladen!"
msgid "Save buddylist..."
msgstr "Buddy-Liste speichern..."
+msgid "Load buddylist from file..."
+msgstr "Buddy-Liste aus Datei laden..."
+
msgid "Fill in the registration fields."
msgstr "Füllen Sie die Registrierungsfelder aus."
@@ -3051,9 +3103,6 @@ msgstr "Löschen der Buddy-Liste vom Server"
msgid "Save buddylist to file..."
msgstr "Buddy-Liste in Datei speichern..."
-msgid "Load buddylist from file..."
-msgstr "Buddy-Liste aus Datei laden..."
-
#. magic
#. major_version
#. minor_version
@@ -3183,8 +3232,10 @@ msgid "Bad mode"
msgstr "Falscher Modus"
#, c-format
-msgid "Ban on %s by %s, set %ld seconds ago"
-msgstr "Verbot zu %s von %s, gesetzt vor %ld Sekunden"
+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"
#, c-format
msgid "Ban on %s"
@@ -3620,6 +3671,8 @@ msgstr "Postleitzahl"
msgid "Country"
msgstr "Land"
+#. lots of clients (including purple) do this, but it's
+#. * out of spec
msgid "Telephone"
msgstr "Telefon"
@@ -3807,12 +3860,12 @@ msgstr "Hop-Überprüfung"
msgid "Capabilities"
msgstr "Fähigkeiten"
-msgid "Resource"
-msgstr "Ressource"
-
msgid "Priority"
msgstr "Priorität"
+msgid "Resource"
+msgstr "Ressource"
+
msgid "Middle Name"
msgstr "Zweiter Name"
@@ -3897,7 +3950,7 @@ msgstr ""
"Benutzern zu suchen."
msgid "Email Address"
-msgstr "Email-Adresse"
+msgstr "E-Mail-Adresse"
msgid "Search for XMPP users"
msgstr "Suche nach XMPP-Benutzern"
@@ -4534,14 +4587,14 @@ msgid "_Accept Defaults"
msgstr "Standards _akzeptieren"
#, c-format
-msgid "Error in chat %s"
-msgstr "Fehler im Chat %s"
-
-#, c-format
msgid "Error joining chat %s"
msgstr "Fehler beim Betreten des Chats %s"
#, c-format
+msgid "Error in chat %s"
+msgstr "Fehler im Chat %s"
+
+#, c-format
msgid "Unable to send file to %s, user does not support file transfers"
msgstr ""
"Kann die Datei nicht an %s senden, da der Client des Benutzers keine "
@@ -4635,7 +4688,7 @@ msgid "Syntax Error (probably a client bug)"
msgstr "Syntaxfehler (wahrscheinlich ein Client-Bug)"
msgid "Invalid email address"
-msgstr "Ungültige Email-Adresse"
+msgstr "Ungültige E-Mail-Adresse"
msgid "User does not exist"
msgstr "Benutzer existiert nicht"
@@ -4808,7 +4861,7 @@ msgid "Nudging %s..."
msgstr "%s anstoßen..."
msgid "Email Address..."
-msgstr "Email-Adresse..."
+msgstr "E-Mail-Adresse..."
msgid "Your new MSN friendly name is too long."
msgstr "Ihr neuer MSN-Benutzername zu lang."
@@ -4997,7 +5050,7 @@ msgid "Home Fax"
msgstr "Fax (privat)"
msgid "Personal Email"
-msgstr "Email (privat)"
+msgstr "E-Mail (privat)"
msgid "Personal IM"
msgstr "IM (privat)"
@@ -5040,7 +5093,7 @@ msgid "Work Fax"
msgstr "Fax (geschäftlich)"
msgid "Work Email"
-msgstr "Email (geschäftlich)"
+msgstr "E-Mail (geschäftlich)"
msgid "Work IM"
msgstr "IM (geschäftlich)"
@@ -5367,8 +5420,11 @@ msgid "Logging in"
msgstr "Logge ein"
#, c-format
-msgid "Connection to server lost (no data received within %d seconds)"
-msgstr ""
+msgid "Connection to server lost (no data received within %d second)"
+msgid_plural "Connection to server lost (no data received within %d seconds)"
+msgstr[0] ""
+"Verbindung zum Server verloren (seit %d Sekunde keine Daten empfangen)"
+msgstr[1] ""
"Verbindung zum Server verloren (seit %d Sekunden keine Daten empfangen)"
#. Can't write _()'d strings in array initializers. Workaround.
@@ -5468,9 +5524,15 @@ msgstr "IM-Freunde"
#, c-format
msgid ""
+"%d buddy was added or updated from the server (including buddies already on "
+"the server-side list)"
+msgid_plural ""
"%d buddies were added or updated from the server (including buddies already "
"on the server-side list)"
-msgstr ""
+msgstr[0] ""
+"%d Buddy wurde vom Server hinzugefügt oder aktualisiert (inklusive der "
+"Buddys, die schon auf der Serverliste sind)"
+msgstr[1] ""
"%d Buddys wurden vom Server hinzugefügt oder aktualisiert (inklusive der "
"Buddys, die schon auf der Serverliste sind)"
@@ -6110,11 +6172,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"
@@ -6218,6 +6280,9 @@ msgstr "Live-Video"
msgid "Camera"
msgstr "Kamera"
+msgid "Screen Sharing"
+msgstr "Gemeinsamer Bildschirm"
+
msgid "Free For Chat"
msgstr "Bereit zum Chatten"
@@ -6275,7 +6340,7 @@ msgid ""
"only letters, numbers and spaces, or contain only numbers."
msgstr ""
"Anmeldung fehlgeschlagen: Sie konnten nicht als %s angemeldet werden, da der "
-"Benutzername fehlerhaft ist. Benutzernamen müssen gültige Email-Adressen "
+"Benutzername fehlerhaft ist. Benutzernamen müssen gültige E-Mail-Adressen "
"sein oder mit einem Buchstaben beginnen und nur Buchstaben, Ziffern und "
"Leerzeichen enthalten oder nur aus Ziffern bestehen."
@@ -6350,6 +6415,7 @@ msgstr ""
msgid "Unable to get a valid login hash."
msgstr "Konnte keinen gültigen Login-Hash bekommen."
+#. allow multple logins?
msgid "Password sent"
msgstr "Passwort gesendet"
@@ -6424,7 +6490,7 @@ msgid ""
"Message is:\n"
"%s"
msgstr ""
-"Sie haben eine ICQ-Email empfangen von %s [%s]\n"
+"Sie haben eine ICQ-E-Mail empfangen von %s [%s]\n"
"\n"
"Nachricht:\n"
"%s"
@@ -6469,20 +6535,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."
@@ -6514,9 +6586,6 @@ msgstr "Online seit"
msgid "Member Since"
msgstr "Mitglied seit"
-msgid "Available Message"
-msgstr "Verfügbarkeitsnachricht"
-
msgid "Your AIM connection may be lost."
msgstr "Ihre AIM-Verbindung könnte unterbrochen sein."
@@ -6546,12 +6615,17 @@ msgstr "Handynummer"
msgid "Personal Web Page"
msgstr "Persönliche Webseite"
+#. aim_userinfo_t
+#. strip_html_tags
msgid "Additional Information"
msgstr "Zusätzliche Informationen"
msgid "Zip Code"
msgstr "PLZ"
+msgid "Work Information"
+msgstr "Information (Arbeit)"
+
msgid "Division"
msgstr "Abteilung"
@@ -6561,9 +6635,6 @@ msgstr "Position"
msgid "Web Page"
msgstr "Webseite"
-msgid "Work Information"
-msgstr "Information (Arbeit)"
-
msgid "Pop-Up Message"
msgstr "Pop-Up Nachricht"
@@ -6575,12 +6646,12 @@ msgstr[1] "Die folgenden Benutzernamen sind verbunden mit %s"
#, c-format
msgid "No results found for email address %s"
-msgstr "Keine Ergebnisse für die Email-Adresse %s gefunden"
+msgstr "Keine Ergebnisse für die E-Mail-Adresse %s gefunden"
#, c-format
msgid "You should receive an email asking to confirm %s."
msgstr ""
-"Sie sollten eine Email erhalten, in der Sie aufgefordert werden, %s zu "
+"Sie sollten eine E-Mail erhalten, in der Sie aufgefordert werden, %s zu "
"bestätigen."
msgid "Account Confirmation Requested"
@@ -6613,7 +6684,7 @@ msgid ""
"Error 0x%04x: Unable to change email address because there is already a "
"request pending for this username."
msgstr ""
-"Error 0x%04x: Kann die Email-Adresse nicht ändern, weil es schon eine "
+"Error 0x%04x: Kann die E-Mail-Adresse nicht ändern, weil es schon eine "
"laufende Anfrage für diesen Benutzernamen gibt."
#, c-format
@@ -6621,7 +6692,7 @@ msgid ""
"Error 0x%04x: Unable to change email address because the given address has "
"too many usernames associated with it."
msgstr ""
-"Fehler 0x%04x: Kann die Email-Adresse nicht ändern, weil zu dieser Adresse "
+"Fehler 0x%04x: Kann die E-Mail-Adresse nicht ändern, weil zu dieser Adresse "
"schon zu viele Benutzernamen gehören."
#, c-format
@@ -6629,7 +6700,7 @@ msgid ""
"Error 0x%04x: Unable to change email address because the given address is "
"invalid."
msgstr ""
-"Fehler 0x%04x: Kann die Email-Adresse nicht ändern, weil die angegebene "
+"Fehler 0x%04x: Kann die E-Mail-Adresse nicht ändern, weil die angegebene "
"Adresse falsch ist."
#, c-format
@@ -6641,7 +6712,7 @@ msgstr "Fehler beim Ändern der Konten-Information"
#, c-format
msgid "The email address for %s is %s"
-msgstr "Die Email-Adresse für %s ist %s"
+msgstr "Die E-Mail-Adresse für %s ist %s"
msgid "Account Info"
msgstr "Konto-Info"
@@ -6705,7 +6776,7 @@ msgid ""
"numbers and spaces, or contain only numbers."
msgstr ""
"Konnte den Buddy %s nicht hinzufügen, da der Benutzername falsch ist. "
-"Benutzernamen müssen gültige Email-Adressen sein oder mit einem Buchstaben "
+"Benutzernamen müssen gültige E-Mail-Adressen sein oder mit einem Buchstaben "
"beginnen und nur Buchstaben, Ziffern und Leerzeichen enthalten oder nur aus "
"Ziffern bestehen."
@@ -6784,12 +6855,6 @@ msgstr ""
"Ihr IM-Bild wurde nicht gesendet. Sie können keine IM-Bilder in AIM-Chats "
"senden."
-msgid "Away Message"
-msgstr "Abwesenheitsnachricht"
-
-msgid "<i>(retrieving)</i>"
-msgstr "<i>(empfange)</i>"
-
msgid "iTunes Music Store Link"
msgstr "iTunes Music Store Link"
@@ -6864,13 +6929,13 @@ msgstr ""
"fragen“ auswählen."
msgid "Find Buddy by Email"
-msgstr "Suche Buddys nach Email-Adresse"
+msgstr "Suche Buddys nach E-Mail-Adresse"
msgid "Search for a buddy by email address"
-msgstr "Suche nach einem Buddy mit einer bestimmten Email-Adresse"
+msgstr "Suche nach einem Buddy mit einer bestimmten E-Mail-Adresse"
msgid "Type the email address of the buddy you are searching for."
-msgstr "Geben Sie die Email-Adresse des Buddys ein, nach dem Sie suchen."
+msgstr "Geben Sie die E-Mail-Adresse des Buddys ein, nach dem Sie suchen."
msgid "_Search"
msgstr "_Suchen"
@@ -6893,16 +6958,16 @@ msgid "Confirm Account"
msgstr "Konto bestätigen"
msgid "Display Currently Registered Email Address"
-msgstr "Zeige die aktuell registrierte Email-Adresse"
+msgstr "Zeige die aktuell registrierte E-Mail-Adresse"
msgid "Change Currently Registered Email Address..."
-msgstr "Ändere die aktuell registrierte Email-Adresse..."
+msgstr "Ändere die aktuell registrierte E-Mail-Adresse..."
msgid "Show Buddies Awaiting Authorization"
msgstr "Zeige Buddys, von denen Sie Autorisierung erwarten"
msgid "Search for Buddy by Email Address..."
-msgstr "Suche Buddys nach Email-Adresse..."
+msgstr "Suche Buddys nach E-Mail-Adresse..."
msgid "Search for Buddy by Information"
msgstr "Suche Buddy nach Information"
@@ -6916,6 +6981,9 @@ msgstr ""
"Dateiübertragungen und Direkt-IM (langsamer,\n"
"aber zeigt Ihre IP-Adresse nicht)"
+msgid "Allow multiple simultaneous logins"
+msgstr "Mehrere gleichzeitige Logins erlauben"
+
#, c-format
msgid "Asking %s to connect to us at %s:%hu for Direct IM."
msgstr "Frage %s, ob er sich zu uns auf %s:%hu für Direkt-IM verbinden möchte."
@@ -8740,7 +8808,7 @@ msgstr "Benutzername: \t%s\n"
#, c-format
msgid "Email: \t\t%s\n"
-msgstr "Email: \t\t%s\n"
+msgstr "E-Mail: \t\t%s\n"
#, c-format
msgid "Host Name: \t%s\n"
@@ -8988,7 +9056,7 @@ msgid "Dir service temporarily unavailable."
msgstr "Verzeichnis-Dienst ist zur Zeit nicht verfügbar."
msgid "Email lookup restricted."
-msgstr "Email-Suche eingeschränkt."
+msgstr "E-Mail-Suche eingeschränkt."
msgid "Keyword ignored."
msgstr "Stichwort ignoriert."
@@ -9695,7 +9763,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)"
@@ -10185,8 +10252,8 @@ msgstr "/Buddys/_Beenden"
msgid "/_Accounts"
msgstr "/_Konten"
-msgid "/Accounts/Manage"
-msgstr "/Konten/Verwalten"
+msgid "/Accounts/Manage Accounts"
+msgstr "/Konten/Konten verwalten"
#. Tools
msgid "/_Tools"
@@ -10198,9 +10265,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"
@@ -10210,6 +10274,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"
@@ -10376,13 +10443,13 @@ msgid ""
"<span weight='bold' size='larger'>Welcome to %s!</span>\n"
"\n"
"You have no accounts enabled. Enable your IM accounts from the <b>Accounts</"
-"b> window at <b>Accounts->Manage</b>. Once you enable accounts, you'll be "
-"able to sign on, set your status, and talk to your friends."
+"b> window at <b>Accounts->Manage Accounts</b>. Once you enable accounts, "
+"you'll be able to sign on, set your status, and talk to your friends."
msgstr ""
"<span weight='bold' size='larger'>Willkommen bei %s!</span>\n"
"\n"
"Sie haben keine Konten aktiviert. Aktivieren Sie Ihre IM-Konten vom "
-"<b>Konten</b>-Fenster über <b>Konten->Verwalten</b>. Wenn Sie Konten "
+"<b>Konten</b>-Fenster über <b>Konten->Konten verwalten</b>. Wenn Sie Konten "
"aktiviert haben, können Sie sich anmelden, Ihren Status setzen und mit Ihren "
"Freunden reden."
@@ -10443,6 +10510,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/"
@@ -10455,12 +10528,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"
@@ -11375,9 +11442,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"
@@ -11400,7 +11466,7 @@ msgid "Enable typing notification"
msgstr "Tipp-Benachrichtigung aktivieren"
msgid "_Copy Email Address"
-msgstr "Kopiere _Email-Adresse"
+msgstr "Kopiere _E-Mail-Adresse"
msgid "_Open Link in Browser"
msgstr "Ö_ffne Link im Browser"
@@ -11452,7 +11518,6 @@ msgstr "Bild speichern"
msgid "_Save Image..."
msgstr "Bild _speichern..."
-#, fuzzy
msgid "_Add Custom Smiley..."
msgstr "Benutzerdefinierten Smiley _hinzufügen..."
@@ -11494,16 +11559,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"
@@ -11771,8 +11838,8 @@ msgstr[1] "%s hat %d neue Nachrichten."
#, c-format
msgid "<b>%d new email.</b>"
msgid_plural "<b>%d new emails.</b>"
-msgstr[0] "<b>%d neue Email.</b>"
-msgstr[1] "<b>%d neue Emails.</b>"
+msgstr[0] "<b>%d neue E-Mail.</b>"
+msgstr[1] "<b>%d neue E-Mails.</b>"
#, c-format
msgid "The browser command \"%s\" is invalid."
@@ -12857,19 +12924,19 @@ msgid "_Associate Buddy"
msgstr "_Assoziiere den Buddy"
msgid "Unable to send email"
-msgstr "Email konnte nicht gesendet werden"
+msgstr "E-Mail konnte nicht gesendet werden"
msgid "The evolution executable was not found in the PATH."
msgstr "Die ausführbare Evolution-Datei wurde nicht im Pfad (PATH) gefunden."
msgid "An email address was not found for this buddy."
-msgstr "Für diesen Buddy wurde keine Email-Adresse gefunden."
+msgstr "Für diesen Buddy wurde keine E-Mail-Adresse gefunden."
msgid "Add to Address Book"
msgstr "Zum Adressbuch hinzufügen"
msgid "Send Email"
-msgstr "Email senden"
+msgstr "E-Mail senden"
#. Configuration frame
msgid "Evolution Integration Configuration"
@@ -12919,7 +12986,7 @@ msgid "Last name:"
msgstr "Nachname:"
msgid "Email:"
-msgstr "Email:"
+msgstr "E-Mail:"
#. *< type
#. *< ui_requirement
@@ -12972,7 +13039,7 @@ msgstr "Überprüft, ob es neue lokale Mails gibt."
msgid "Adds a small box to the buddy list that shows if you have new mail."
msgstr ""
-"Fügt eine kleine Box zur Buddy-Liste hinzu, die zeigt, ob Sie neue Emails "
+"Fügt eine kleine Box zur Buddy-Liste hinzu, die zeigt, ob Sie neue E-Mails "
"haben."
msgid "Markerline"
diff --git a/po/en_AU.po b/po/en_AU.po
index 7ac722f1a6..0667a04966 100644
--- a/po/en_AU.po
+++ b/po/en_AU.po
@@ -14896,7 +14896,7 @@ msgstr "0 people in room"
msgid "%d person in room"
msgid_plural "%d people in room"
msgstr[0] "%d person in room"
-msgstr[1] "%d person in room"
+msgstr[1] "%d people in room"
#: ../pidgin/gtkconv.c:6486 ../pidgin/gtkstatusbox.c:660
#, fuzzy
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/fi.po b/po/fi.po
index 13fb3513a8..bc55450e63 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -16488,7 +16488,6 @@ msgid ""
"<span foreground=\"red\" weight=\"bold\">Error: %s\n"
"Check the plugin website for an update.</span>"
msgstr ""
-"%s\n"
"<span foreground=\"red\" weight=\"bold\">Virhe: %s\n"
"Tarkista onko liitännäisen WWW-sivustolla päivitystä.</span>"
diff --git a/po/gl.po b/po/gl.po
index 5f74f172a8..bbd7ab46a5 100644
--- a/po/gl.po
+++ b/po/gl.po
@@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
"X-Generator: KBabel 1.11.4\n"
#: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
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/ko.po b/po/ko.po
index 153b3163d1..43a59930a4 100644
--- a/po/ko.po
+++ b/po/ko.po
@@ -913,7 +913,7 @@ msgstr "닫기"
msgid "%s (%s) has %d new message."
msgid_plural "%s (%s) has %d new messages."
msgstr[0] "%s (%s) 에는 %d 개의 새 메일이 있습니다."
-msgstr[1] "%s (%s) 에는 %d 개의 새 메일이 있습니다."
+#msgstr[1] "%s (%s) 에는 %d 개의 새 메일이 있습니다."
#: ../console/gntnotify.c:206
#: ../gtk/gtknotify.c:322
@@ -1875,7 +1875,7 @@ msgstr "/도구/대화실 목록"
msgid "%d unread message from %s\n"
msgid_plural "%d unread messages from %s\n"
msgstr[0] "%2$s 님으로부터 %1$d개의 읽지 않은 메일이 있습니다.\n"
-msgstr[1] "%2$s さんから %1$d個の未?のメッセ?ジがあります\n"
+#msgstr[1] "%2$s さんから %1$d個の未?のメッセ?ジがあります\n"
#: ../gtk/gtkblist.c:3654
msgid "Manually"
@@ -2357,7 +2357,7 @@ msgstr "아무도 없습니다."
msgid "%d person in room"
msgid_plural "%d people in room"
msgstr[0] "대화실에 %d 명이 있습니다."
-msgstr[1] "대화실에 %d 명이 있습니다."
+#msgstr[1] "대화실에 %d 명이 있습니다."
#: ../gtk/gtkconv.c:5855
#: ../gtk/gtkstatusbox.c:567
@@ -2902,7 +2902,7 @@ msgstr "이 대화의 별칭을 입력해 주십시오."
msgid "You are about to remove the contact containing %s and %d other buddy from your buddy list. Do you want to continue?"
msgid_plural "You are about to remove the contact containing %s and %d other buddies from your buddy list. Do you want to continue?"
msgstr[0] "친구 목록에서 %s 님을 포함한 %d 명의 친구의 삭제하려고 합니다. 계속 하시겠습니까?"
-msgstr[1] "친구 목록에서 %s 님을 포함한 %d 명의 친구의 삭제하려고 합니다. 계속 하시겠습니까?"
+#msgstr[1] "친구 목록에서 %s 님을 포함한 %d 명의 친구의 삭제하려고 합니다. 계속 하시겠습니까?"
#: ../gtk/gtkdialogs.c:1016
msgid "Remove Contact"
@@ -3598,14 +3598,14 @@ msgstr "보낸 사람"
msgid "%s has %d new message."
msgid_plural "%s has %d new messages."
msgstr[0] "%s 에는 %d 개의 새로운 메시지가 있습니다."
-msgstr[1] "%s 에는 %d 개의 새로운 메시지가 있습니다."
+#msgstr[1] "%s 에는 %d 개의 새로운 메시지가 있습니다."
#: ../gtk/gtknotify.c:511
#, c-format
msgid "<b>You have %d new email.</b>"
msgid_plural "<b>You have %d new emails.</b>"
msgstr[0] "<b>%d 개의 새로운 메시지가 있습니다.</b>"
-msgstr[1] "<b>%d 개의 새로운 메시지가 있습니다.</b>"
+#msgstr[1] "<b>%d 개의 새로운 메시지가 있습니다.</b>"
#: ../gtk/gtknotify.c:699
#: ../libgaim/protocols/sametime/sametime.c:5548
@@ -5919,7 +5919,7 @@ msgstr "친구 목록"
msgid "%d buddy from group %s was not removed because it belongs to an account which is disabled or offline. This buddy and the group were not removed.\n"
msgid_plural "%d buddies from group %s were not removed because they belong to accounts which are currently disabled or offline. These buddies and the group were not removed.\n"
msgstr[0] "%d 개의 계정이 사용 안 함 또는 오프라인 상태이기 때문에 그 계정을 그룹 %s (으)로부터 삭제하지 못했습니다. 그 친구와 그룹은 삭제할 수 없습니다.\n"
-msgstr[1] "%d 개의 계정이 사용 안 함 또는 오프라인 상태이기 때문에 그 계정을 그룹 %s (으)로부터 삭제하지 못했습니다. 그 친구와 그룹은 삭제할 수 없습니다.\n"
+#msgstr[1] "%d 개의 계정이 사용 안 함 또는 오프라인 상태이기 때문에 그 계정을 그룹 %s (으)로부터 삭제하지 못했습니다. 그 친구와 그룹은 삭제할 수 없습니다.\n"
#: ../libgaim/blist.c:1929
msgid "Group not removed"
@@ -9869,10 +9869,10 @@ msgstr[0] ""
"MSN 서비스가 유지보수를 위해 %d 분간 정지하고 있습니다. 자동으로 접속이 끊길 예정이므로, 지금 수행되고 있는 대화를 종료해 주십시오.\n"
"\n"
"유지보수가 완료되면 다시 접속이 가능합니다."
-msgstr[1] ""
-"MSN 서비스가 유지보수를 위해 %d 분간 정지하고 있습니다. 자동으로 접속이 끊길 예정이므로, 지금 수행되고 있는 대화를 종료해 주십시오.\n"
-"\n"
-"유지보수가 완료되면 다시 접속이 가능합니다."
+#msgstr[1] ""
+#"MSN 서비스가 유지보수를 위해 %d 분간 정지하고 있습니다. 자동으로 접속이 끊길 예정이므로, 지금 수행되고 있는 대화를 종료해 주십시오.\n"
+#"\n"
+#"유지보수가 완료되면 다시 접속이 가능합니다."
#: ../libgaim/protocols/msn/servconn.c:135
msgid "Writing error"
@@ -10921,42 +10921,42 @@ msgstr "거절(_D)"
msgid "You missed %hu message from %s because it was invalid."
msgid_plural "You missed %hu messages from %s because they were invalid."
msgstr[0] "%2$s 님으로부터의 %1$hu 개의 메시지는 타당하지 않아 받지 못했습니다."
-msgstr[1] "%2$s 님으로부터의 %1$hu 개의 메시지는 타당하지 않아 받지 못했습니다."
+#msgstr[1] "%2$s 님으로부터의 %1$hu 개의 메시지는 타당하지 않아 받지 못했습니다."
#: ../libgaim/protocols/oscar/oscar.c:2560
#, c-format
msgid "You missed %hu message from %s because it was too large."
msgid_plural "You missed %hu messages from %s because they were too large."
msgstr[0] "%2$s 님으로부터의 %1$hu개의 메시지는 너무 커서 받지 못했습니다."
-msgstr[1] "%2$s 님으로부터의 %1$hu개의 메시지는 너무 커서 받지 못했습니다."
+#msgstr[1] "%2$s 님으로부터의 %1$hu개의 메시지는 너무 커서 받지 못했습니다."
#: ../libgaim/protocols/oscar/oscar.c:2569
#, c-format
msgid "You missed %hu message from %s because the rate limit has been exceeded."
msgid_plural "You missed %hu messages from %s because the rate limit has been exceeded."
msgstr[0] "속도 제한을 상회하여, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "속도 제한을 상회하여, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "속도 제한을 상회하여, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
#: ../libgaim/protocols/oscar/oscar.c:2578
#, 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."
msgstr[0] "유해한 상대방이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "유해한 상대방이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "유해한 상대방이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
#: ../libgaim/protocols/oscar/oscar.c:2587
#, 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."
msgstr[0] "유해한 자신이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "유해한 자신이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "유해한 자신이어서, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
#: ../libgaim/protocols/oscar/oscar.c:2596
#, c-format
msgid "You missed %hu message from %s for an unknown reason."
msgid_plural "You missed %hu messages from %s for an unknown reason."
msgstr[0] "원인은 알 수 없지만, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
-msgstr[1] "원인은 알 수 없지만, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
+#msgstr[1] "원인은 알 수 없지만, %2$s 님으로부터의 %1$hu개의 메시지를 못했습니다."
#: ../libgaim/protocols/oscar/oscar.c:2718
#, c-format
@@ -11178,7 +11178,7 @@ msgstr "로그인 수속이 완료되기 전에 사용자 프로파일을 설정
msgid "The maximum profile length of %d byte has been exceeded. It has been truncated for you."
msgid_plural "The maximum profile length of %d bytes has been exceeded. It has been truncated for you."
msgstr[0] "프로파일 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
-msgstr[1] "프로파일 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
+#msgstr[1] "프로파일 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
#: ../libgaim/protocols/oscar/oscar.c:4461
msgid "Profile too long."
@@ -11189,7 +11189,7 @@ msgstr "프로파일이 너무 깁니다."
msgid "The maximum away message length of %d byte has been exceeded. It has been truncated for you."
msgid_plural "The maximum away message length of %d bytes has been exceeded. It has been truncated for you."
msgstr[0] "자리 비움 메시지 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
-msgstr[1] "자리 비움 메시지 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
+#msgstr[1] "자리 비움 메시지 길이가 최대값 %d 바이트를 초과하여 일부가 잘렸습니다."
#: ../libgaim/protocols/oscar/oscar.c:4510
msgid "Away message too long."
@@ -14183,7 +14183,7 @@ msgstr "다른 이름으로 저장..."
msgid "%s requests %s to accept %d file: %s (%.2f %s)%s%s"
msgid_plural "%s requests %s to accept %d files: %s (%.2f %s)%s%s"
msgstr[0] "%s 님으로부터 %s 님에게 %d 개의 파일 요청: %s (%.2f %s)%s%s"
-msgstr[1] "%s 님으로부터 %s 님에게 %d 개의 파일 요청: %s (%.2f %s)%s%s"
+#msgstr[1] "%s 님으로부터 %s 님에게 %d 개의 파일 요청: %s (%.2f %s)%s%s"
#: ../libgaim/protocols/toc/toc.c:2236
#, c-format
@@ -14945,42 +14945,42 @@ msgstr "알 수 없음."
msgid "%d second"
msgid_plural "%d seconds"
msgstr[0] "%d 초"
-msgstr[1] "%d 초"
+#msgstr[1] "%d 초"
#: ../libgaim/util.c:2939
#, c-format
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d 일"
-msgstr[1] "%d 일"
+#msgstr[1] "%d 일"
#: ../libgaim/util.c:2947
#, c-format
msgid "%s, %d hour"
msgid_plural "%s, %d hours"
msgstr[0] "%s %d 시간"
-msgstr[1] "%s %d 시간"
+#msgstr[1] "%s %d 시간"
#: ../libgaim/util.c:2953
#, c-format
msgid "%d hour"
msgid_plural "%d hours"
msgstr[0] "%d 시간"
-msgstr[1] "%d 시간"
+#msgstr[1] "%d 시간"
#: ../libgaim/util.c:2961
#, c-format
msgid "%s, %d minute"
msgid_plural "%s, %d minutes"
msgstr[0] "%s %d 분"
-msgstr[1] "%s %d 분"
+#msgstr[1] "%s %d 분"
#: ../libgaim/util.c:2967
#, c-format
msgid "%d minute"
msgid_plural "%d minutes"
msgstr[0] "%d 분"
-msgstr[1] "%d 분"
+#msgstr[1] "%d 분"
#: ../libgaim/util.c:3166
#: ../libgaim/util.c:3464
diff --git a/po/lo.po b/po/lo.po
index d78c417654..8ae0661573 100644
--- a/po/lo.po
+++ b/po/lo.po
@@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+#"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
#: ../finch/finch.c:418
diff --git a/po/lt.po b/po/lt.po
index c8c7036484..a8bfc9c1a4 100644
--- a/po/lt.po
+++ b/po/lt.po
@@ -721,8 +721,11 @@ msgid "Pause"
msgstr "Pristabdyti"
#, c-format
-msgid "File Transfers - %d%% of %d files"
-msgstr "Failų perdavimai – %d%% iš %d"
+msgid "File Transfers - %d%% of %d file"
+msgid_plural "File Transfers - %d%% of %d files"
+msgstr[0] "Failų perdavimai – %d%% iš %d failo"
+msgstr[1] "Failų perdavimai – %d%% iš %d failų"
+msgstr[2] "Failų perdavimai – %d%% iš %d failų"
#. Create the window.
msgid "File Transfers"
@@ -3258,8 +3261,11 @@ msgid "Bad mode"
msgstr "Bloga būsena"
#, c-format
-msgid "Ban on %s by %s, set %ld seconds ago"
-msgstr "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundžių"
+msgid "Ban on %s by %s, set %ld second ago"
+msgid_plural "Ban on %s by %s, set %ld seconds ago"
+msgstr[0] "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundę"
+msgstr[1] "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundes"
+msgstr[2] "Vartotojui %s uždraudė prisijungti %s prieš %ld sekundžių"
#, c-format
msgid "Ban on %s"
@@ -5439,8 +5445,11 @@ msgid "Logging in"
msgstr "Prisijungiama"
#, c-format
-msgid "Connection to server lost (no data received within %d seconds)"
-msgstr "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundžių)"
+msgid "Connection to server lost (no data received within %d second)"
+msgid_plural "Connection to server lost (no data received within %d seconds)"
+msgstr[0] "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundę)"
+msgstr[1] "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundes)"
+msgstr[2] "Nutrūko ryšys su serveriu (negauta jokių duomenų per %d sekundžių)"
#. Can't write _()'d strings in array initializers. Workaround.
msgid "New mail messages"
@@ -5538,9 +5547,18 @@ msgstr "IM draugai"
#, c-format
msgid ""
+"%d buddy was added or updated from the server (including buddies already on "
+"the server-side list)"
+msgid_plural ""
"%d buddies were added or updated from the server (including buddies already "
"on the server-side list)"
-msgstr ""
+msgstr[0] ""
+"%d bičiulis buvo pridėtas ar atnaujintas iš serverio (įskaitant ir "
+"bičiulius, jau esančius serverio sąraše)"
+msgstr[1] ""
+"%d bičiuliai buvo pridėti ar atnaujinti iš serverio (įskaitant ir bičiulius, "
+"jau esančius serverio sąraše)"
+msgstr[2] ""
"%d bičiulių buvo pridėta ar atnaujinta iš serverio (įskaitant ir bičiulius, "
"jau esančius serverio sąraše)"
diff --git a/po/nb.po b/po/nb.po
index a48a98d18c..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
@@ -10311,7 +10311,7 @@ msgstr "Ønsker du å legge denne kontakten?"
#: ../libpurple/protocols/qq/sys_msg.c:166
#, c-format
msgid "You have been added by %s"
-msgstr "Du har blitt lagt til av %s (%s)"
+msgstr "Du har blitt lagt til av %s"
#: ../libpurple/protocols/qq/sys_msg.c:169
#: ../libpurple/protocols/qq/sys_msg.c:263
diff --git a/po/nl.po b/po/nl.po
index 33eca61fb5..07fb0ee22a 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -2489,10 +2489,11 @@ msgid ""
"Activation date: %s\n"
"Expiration date: %s\n"
msgstr ""
-"Gemeenschappelijke naam: %s\n"
-"\n"
-"Vingerafdruk (SHA1): %s\n"
-"\n"
+#msgstr ""
+#"Gemeenschappelijke naam: %s\n"
+#"\n"
+#"Vingerafdruk (SHA1): %s\n"
+#"\n"
#. TODO: Find what the handle ought to be
#: ../libpurple/certificate.c:1905
@@ -6982,7 +6983,7 @@ msgstr "Aanstoten"
#: ../libpurple/protocols/msn/msn.c:129 ../libpurple/protocols/msnp9/msn.c:130
#, c-format
msgid "%s has nudged you!"
-msgstr "%s heeft je aangestoten [%s]"
+msgstr "%s heeft je aangestoten"
#: ../libpurple/protocols/msn/msn.c:129 ../libpurple/protocols/msnp9/msn.c:130
#, c-format
@@ -8177,7 +8178,8 @@ msgstr "Fakkel"
#: ../libpurple/protocols/myspace/zap.c:59
#, c-format
msgid "%s has torched you!"
-msgstr "De gebruiker heeft u befakkeld"
+msgstr ""
+#msgstr "De gebruiker heeft u befakkeld"
#: ../libpurple/protocols/myspace/zap.c:59
#, c-format
@@ -8876,9 +8878,10 @@ msgid ""
"(There was an error receiving this message. Either you and %s have "
"different encodings selected, or %s has a buggy client.)"
msgstr ""
-"(Er is een fout opgetreden bij het ontvangen van het bericht. Of jij en %s "
-"hebben verschillende coderingen geselecteerd of de andere persoon gebruikt "
-"een chatprogramma met fouten.)"
+#msgstr ""
+#"(Er is een fout opgetreden bij het ontvangen van het bericht. Of jij en %s "
+#"hebben verschillende coderingen geselecteerd of de andere persoon gebruikt "
+#"een chatprogramma met fouten.)"
#. Label
#: ../libpurple/protocols/oscar/oscar.c:640 ../pidgin/gtkutils.c:2449
@@ -10452,7 +10455,7 @@ msgstr "Kan niet lezen van socket"
#: ../libpurple/protocols/qq/send_file.c:707
#, c-format
msgid "%d has declined the file %s"
-msgstr "%s heeft het onderwerp veranderd naar: %sx"
+msgstr "%d heeft het onderwerp veranderd naar: %s"
#: ../libpurple/protocols/qq/send_file.c:710
#: ../libpurple/protocols/qq/send_file.c:739
@@ -14558,7 +14561,7 @@ msgid ""
"<b>Occupants:</b> %d"
msgstr ""
"\n"
-"<b>Deelnemers:</b> %s"
+"<b>Deelnemers:</b> %d"
#: ../pidgin/gtkblist.c:3253
#, c-format
diff --git a/po/ps.po b/po/ps.po
index a39e31c9c3..857e098eea 100644
--- a/po/ps.po
+++ b/po/ps.po
@@ -15,7 +15,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+#"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
#: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
#: ../finch/finch.c:418
diff --git a/po/sq.po b/po/sq.po
index 5d7a4dc5ef..b7e0ff2192 100644
--- a/po/sq.po
+++ b/po/sq.po
@@ -14,6 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: KBabel 1.0.2\n"
"X-Poedit-Language: Albanian\n"
"X-Poedit-Country: ALBANIA\n"
diff --git a/po/ta.po b/po/ta.po
index 360ba309a4..463467800d 100644
--- a/po/ta.po
+++ b/po/ta.po
@@ -17,7 +17,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-Country: INDIA\n"
"X-Generator: KBabel 1.11.4\n"
"X-Poedit-Bookmarks: -1,-1,-1,-1,-1,-1,-1,-1,-1,1715\n"
diff --git a/po/te.po b/po/te.po
index d4cbf3697e..aa581f6578 100644
--- a/po/te.po
+++ b/po/te.po
@@ -9,6 +9,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Translate Toolkit 0.10\n"
"X-Poedit-Language: Telugu\n"
"X-Poedit-SourceCharset: utf-8\n"
@@ -349,7 +350,7 @@ msgstr "సవరించు"
#: ../pidgin/gtkaccount.c:2447
#, c-format
msgid "%s%s%s%s has made %s his or her buddy%s%s"
-msgstr " %s%s%s తన మిత్రుడు లేదా స్నేహితురాలు %s%s గా %s ను చేర్చుకున్నారు "
+msgstr "%s%s%s%s తన మిత్రుడు లేదా స్నేహితురాలు %s%s గా %s ను చేర్చుకున్నారు "
#: ../finch/gntaccount.c:884
#: ../pidgin/gtkaccount.c:2499
@@ -1044,7 +1045,7 @@ msgid ""
msgstr ""
"%s\n"
"\n"
-"%s లోపాన్ని సరిచేసి అకౌంటును తిరిగి క్రియాశీలం చేసేదాకా అకౌంటుకు మళ్ళీ కనెక్ట్ చేయడానికి ప్రయత్నించదు."
+"లోపాన్ని సరిచేసి అకౌంటును తిరిగి క్రియాశీలం చేసేదాకా అకౌంటుకు మళ్ళీ కనెక్ట్ చేయడానికి ప్రయత్నించదు."
#: ../finch/gntconn.c:138
msgid "Re-enable Account"
@@ -1427,8 +1428,8 @@ msgstr "విషయం"
#, c-format
msgid "%s (%s) has %d new message."
msgid_plural "%s (%s) has %d new messages."
-msgstr[0] "%s has %d new message."
-msgstr[1] "%s के %d लिए नये संदेश हैं।"
+msgstr[0] "%s (%s) has %d new message."
+msgstr[1] "%s (%s) के %d लिए नये संदेश हैं।"
#: ../finch/gntnotify.c:226
#: ../pidgin/gtknotify.c:342
@@ -2496,7 +2497,8 @@ msgstr ""
#: ../libpurple/certificate.c:1185
#, c-format
msgid "Accept certificate for %s?"
-msgstr "సంభాషణ ఆహ్వానాన్ని అంగీకరించారా?"
+msgstr ""
+#msgstr "సంభాషణ ఆహ్వానాన్ని అంగీకరించారా?"
#. TODO: Find what the handle ought to be
#: ../libpurple/certificate.c:1191
@@ -3122,7 +3124,8 @@ msgstr "మీ ప్లగ్ ఇన్ ను లోడ్ చేయలేక
#: ../libpurple/plugin.c:663
#, c-format
msgid "%s requires %s, but it failed to unload."
-msgstr "%s డిపెండర్ ప్లగ్ ఇన్ అన్ లోడ్ కావడంలో వైఫల్యం."
+msgstr ""
+#msgstr "%s డిపెండర్ ప్లగ్ ఇన్ అన్ లోడ్ కావడంలో వైఫల్యం."
#: ../libpurple/plugins/autoaccept.c:23
msgid "Autoaccept"
@@ -3508,7 +3511,7 @@ msgstr "ఆటో-రెస్పాన్స్ పంపబడినది:"
#: ../libpurple/plugins/statenotify.c:80
#, c-format
msgid "%s has signed off."
-msgstr "%s సైన్ ఆఫ్ చేశారు. (%s)"
+msgstr "%s సైన్ ఆఫ్ చేశారు."
#: ../libpurple/plugins/log_reader.c:1587
msgid "One or more messages may have been undeliverable."
@@ -4056,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
@@ -4197,7 +4200,8 @@ msgstr "మిత్రుల జాబితా విజయవంతంగా
#: ../libpurple/protocols/gg/gg.c:278
#, c-format
msgid "Couldn't write buddy list for %s to %s"
-msgstr "మిత్రుల జాబితాను లోడ్ చేయలేకపోతోంది"
+msgstr ""
+#msgstr "మిత్రుల జాబితాను లోడ్ చేయలేకపోతోంది"
#: ../libpurple/protocols/gg/gg.c:303
#: ../libpurple/protocols/gg/gg.c:304
@@ -5024,7 +5028,8 @@ msgstr "PING సమాధానం -- Lag: %lu సెకండ్లు"
#: ../libpurple/protocols/irc/msgs.c:1113
#, c-format
msgid "Cannot join %s: Registration is required."
-msgstr "నమోదు అవసరం"
+msgstr ""
+#msgstr "నమోదు అవసరం"
#: ../libpurple/protocols/irc/msgs.c:1114
#: ../libpurple/protocols/silc/ops.c:1093
@@ -5234,7 +5239,8 @@ msgstr "సర్వర్‌కు సంకేతభాషకన్నా స
#: ../libpurple/protocols/jabber/auth.c:518
#, c-format
msgid "%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"
-msgstr "ఈ సర్వర్‌కు ఎన్ క్రిప్ట్ కాని కనెక్షన్ కన్నా సాధారణ టెక్స్ట్ ప్రమాణీకరణ అవసరం. దీనికి అనుమతించి ప్రమాణీకరణను కొనసాగించమంటారా?"
+msgstr ""
+#msgstr "ఈ సర్వర్‌కు ఎన్ క్రిప్ట్ కాని కనెక్షన్ కన్నా సాధారణ టెక్స్ట్ ప్రమాణీకరణ అవసరం. దీనికి అనుమతించి ప్రమాణీకరణను కొనసాగించమంటారా?"
#: ../libpurple/protocols/jabber/auth.c:325
#: ../libpurple/protocols/jabber/auth.c:326
@@ -6007,7 +6013,8 @@ msgstr "%s@%s నమోదు విజయవంతమైనది"
#: ../libpurple/protocols/jabber/jabber.c:689
#, c-format
msgid "Registration to %s successful"
-msgstr "%s@%s నమోదు విజయవంతమైనది"
+msgstr ""
+#msgstr "%s@%s నమోదు విజయవంతమైనది"
#: ../libpurple/protocols/jabber/jabber.c:691
#: ../libpurple/protocols/jabber/jabber.c:692
@@ -6022,7 +6029,8 @@ msgstr " నమోదు విఫలమైనది"
#: ../libpurple/protocols/jabber/jabber.c:719
#, c-format
msgid "Registration from %s successfully removed"
-msgstr "%s@%s నమోదు విజయవంతమైనది"
+msgstr ""
+#msgstr "%s@%s నమోదు విజయవంతమైనది"
#: ../libpurple/protocols/jabber/jabber.c:721
#: ../libpurple/protocols/jabber/jabber.c:722
@@ -6101,7 +6109,8 @@ msgstr "%s కోసం వినియోగదారు సమాచారా
#: ../libpurple/protocols/jabber/jabber.c:1040
#, c-format
msgid "Register New Account at %s"
-msgstr "కొత్త జాబర్ అకౌంట్‌ను నమోదు చేయండి"
+msgstr ""
+#msgstr "కొత్త జాబర్ అకౌంట్‌ను నమోదు చేయండి"
#: ../libpurple/protocols/jabber/jabber.c:1043
msgid "Change Registration"
@@ -6582,7 +6591,8 @@ msgstr "బజ్!!"
#: ../libpurple/protocols/yahoo/yahoo.c:4131
#, c-format
msgid "%s has buzzed you!"
-msgstr "%s మీకు [%s] ను చేర్చారు"
+msgstr ""
+#msgstr "%s మీకు [%s] ను చేర్చారు"
#: ../libpurple/protocols/jabber/jabber.c:2281
#: ../libpurple/protocols/yahoo/yahoo.c:4132
@@ -7185,7 +7195,8 @@ msgstr ""
#: ../libpurple/protocols/msnp9/msn.c:131
#, c-format
msgid "%s has nudged you!"
-msgstr "%s మీకు [%s] ను చేర్చారు"
+msgstr ""
+#msgstr "%s మీకు [%s] ను చేర్చారు"
#: ../libpurple/protocols/msn/msn.c:132
#: ../libpurple/protocols/msnp9/msn.c:132
@@ -7767,7 +7778,8 @@ msgstr "%s ఇప్పుడే మీకొక పలకరింపును
#: ../libpurple/protocols/msn/notification.c:836
#, c-format
msgid "Unknown error (%d)"
-msgstr "అజ్ఞాత పొరపాటు"
+msgstr ""
+#msgstr "అజ్ఞాత పొరపాటు"
#: ../libpurple/protocols/msn/notification.c:837
#: ../libpurple/protocols/sametime/sametime.c:4471
@@ -8151,7 +8163,8 @@ msgstr ""
#: ../libpurple/protocols/myspace/myspace.c:1794
#, c-format
msgid "Protocol error, code %d: %s"
-msgstr "లోపం కోడ్ %d ను ప్రక్రియ తిప్పి పంపింది "
+msgstr ""
+#msgstr "లోపం కోడ్ %d ను ప్రక్రియ తిప్పి పంపింది "
#: ../libpurple/protocols/myspace/myspace.c:1990
#: ../libpurple/protocols/myspace/myspace.c:2024
@@ -8211,7 +8224,8 @@ msgstr "సందేశాన్ని పద విశ్లేషణ చేయ
#: ../libpurple/protocols/myspace/myspace.c:2499
#, c-format
msgid "Couldn't connect to host: %s (%d)"
-msgstr "హోస్ట్‌కు కనెక్ట్ చేయలేదు"
+msgstr ""
+#msgstr "హోస్ట్‌కు కనెక్ట్ చేయలేదు"
#: ../libpurple/protocols/myspace/myspace.c:2670
#, fuzzy
@@ -9503,8 +9517,10 @@ msgstr[1] "మీరు %hu సందేశాన్ని %s నుండి
#, c-format
msgid "You missed %hu message from %s for an unknown reason."
msgid_plural "You missed %hu messages from %s for an unknown reason."
-msgstr[0] "అజ్ఞాత కారణములచేత మీరు %hu సందేశాలను %s నుండి కోల్పోయినారు. "
-msgstr[1] "आपको अज्ञात कारनों से साइन-ऑफ करा गया है।"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "అజ్ఞాత కారణములచేత మీరు %hu సందేశాలను %s నుండి కోల్పోయినారు. "
+#msgstr[1] "आपको अज्ञात कारनों से साइन-ऑफ करा गया है।"
# Data is assumed to be the destination sn
#. Data is assumed to be the destination sn
@@ -9757,7 +9773,8 @@ msgstr "(పేరు లేదు)"
#: ../libpurple/protocols/oscar/oscar.c:5109
#, c-format
msgid "Could not add the buddy %s for an unknown reason."
-msgstr "అజ్ఞాత కారణంవల్ల మీ కమాండ్ విఫలమైనది."
+msgstr ""
+#msgstr "అజ్ఞాత కారణంవల్ల మీ కమాండ్ విఫలమైనది."
#: ../libpurple/protocols/oscar/oscar.c:5226
#, c-format
@@ -10169,7 +10186,8 @@ msgstr "మీ సమాచారం నవీకరించబడింది"
#: ../libpurple/protocols/qq/buddy_info.c:601
#, c-format
msgid "Setting custom faces is not currently supported. Please choose an image from %s."
-msgstr "మీకిష్టమైన ముఖారవిందాలను సెట్ చేయడానికి ఇప్పుడు అవకాశంలేదు. ఇక్కడున్న బొమ్మల్లో ఒకదాన్ని దయచేసి ఎంపికచేసుకోండి"
+msgstr ""
+#msgstr "మీకిష్టమైన ముఖారవిందాలను సెట్ చేయడానికి ఇప్పుడు అవకాశంలేదు. ఇక్కడున్న బొమ్మల్లో ఒకదాన్ని దయచేసి ఎంపికచేసుకోండి"
#: ../libpurple/protocols/qq/buddy_info.c:618
#: ../libpurple/protocols/qq/buddy_info.c:631
@@ -10598,7 +10616,7 @@ msgstr "సాకెట్‌ నుంచి రీడ్ చేయడం స
#: ../libpurple/protocols/qq/send_file.c:707
#, c-format
msgid "%d has declined the file %s"
-msgstr "ఫైలు %s ను %d నిరాకరించింది "
+msgstr "ఫైలు %2$s ను %1$d నిరాకరించింది "
#: ../libpurple/protocols/qq/send_file.c:710
#: ../libpurple/protocols/qq/send_file.c:739
@@ -10608,7 +10626,8 @@ msgstr "ఫైలును పంపడంజరిగింది"
#: ../libpurple/protocols/qq/send_file.c:736
#, c-format
msgid "%d canceled the transfer of %s"
-msgstr "%sను ట్రాన్స్‌ఫర్ చేయడాన్ని %s రద్దు చేశారు."
+msgstr ""
+#msgstr "%sను ట్రాన్స్‌ఫర్ చేయడాన్ని %s రద్దు చేశారు."
#: ../libpurple/protocols/qq/sendqueue.c:124
msgid "Connection lost"
@@ -10637,7 +10656,8 @@ msgstr "వీరిని మీరు చేర్చదలచుకున్
#: ../libpurple/protocols/qq/sys_msg.c:176
#, c-format
msgid "%s has added you [%s] to his or her buddy list"
-msgstr "%s మిమ్మల్ని తన మిత్రుల జాబితాలో చేర్చారు."
+msgstr ""
+#msgstr "%s మిమ్మల్ని తన మిత్రుల జాబితాలో చేర్చారు."
#: ../libpurple/protocols/qq/sys_msg.c:192
#, c-format
@@ -13318,8 +13338,9 @@ msgid ""
"Lost connection with %s:\n"
"%s"
msgstr ""
-"సర్వర్ తో కనెక్షన్ పోయింది:\n"
-"%s"
+#msgstr ""
+#"సర్వర్ తో కనెక్షన్ పోయింది:\n"
+#"%s"
#: ../libpurple/protocols/yahoo/yahoo.c:2733
#, c-format
@@ -13327,8 +13348,9 @@ msgid ""
"Could not establish a connection with %s:\n"
"%s"
msgstr ""
-"సర్వర్ తో కనెక్షన్ సాధ్యపడలేదు: \n"
-"%s"
+#msgstr ""
+#"సర్వర్ తో కనెక్షన్ సాధ్యపడలేదు: \n"
+#"%s"
#: ../libpurple/protocols/yahoo/yahoo.c:3092
#: ../libpurple/protocols/yahoo/yahoo.c:3778
@@ -13836,7 +13858,8 @@ msgstr "చూపించుము."
msgid ""
"Unable to create socket:\n"
"%s"
-msgstr "సాకెట్‌ను సృష్టించలేదు. "
+msgstr ""
+#msgstr "సాకెట్‌ను సృష్టించలేదు. "
#: ../libpurple/proxy.c:662
#, c-format
@@ -14012,7 +14035,8 @@ msgstr "%s స్థితిని %s నుంచి %s కు మార్చ
#: ../libpurple/status.c:613
#, c-format
msgid "%s (%s) changed status from %s to %s"
-msgstr "%s స్థితిని %s నుంచి %s కు మార్చబడింది"
+msgstr ""
+#msgstr "%s స్థితిని %s నుంచి %s కు మార్చబడింది"
#: ../libpurple/status.c:624
#, c-format
@@ -14022,17 +14046,20 @@ msgstr "%s ఇప్పుడు %s"
#: ../libpurple/status.c:626
#, c-format
msgid "%s (%s) is now %s"
-msgstr "%s ఇప్పుడు %s"
+msgstr ""
+#msgstr "%s ఇప్పుడు %s"
#: ../libpurple/status.c:632
#, c-format
msgid "%s is no longer %s"
-msgstr "ఇప్పుడు %s దూరంలో లేరు/దు."
+msgstr ""
+#msgstr "ఇప్పుడు %s దూరంలో లేరు/దు."
#: ../libpurple/status.c:634
#, c-format
msgid "%s (%s) is no longer %s"
-msgstr "ఇప్పుడు %s దూరంలో లేరు/దు."
+msgstr ""
+#msgstr "ఇప్పుడు %s దూరంలో లేరు/దు."
#: ../libpurple/status.c:1247
#, c-format
@@ -14131,7 +14158,8 @@ msgstr ""
#: ../libpurple/util.c:3943
#, c-format
msgid "Unable to connect to %s"
-msgstr ",ర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
+msgstr ""
+#msgstr ",ర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
#: ../libpurple/util.c:3770
#, c-format
@@ -14151,7 +14179,8 @@ msgstr "%sకు రాయడంలో లోపం: %s"
#: ../libpurple/util.c:3861
#, c-format
msgid "Unable to connect to %s: %s"
-msgstr "సర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
+msgstr ""
+#msgstr "సర్వర్‌కు కనెక్ట్‌చేయలేని స్థితి. "
#: ../pidgin.desktop.in.h:1
msgid "Internet Messenger"
@@ -14345,9 +14374,10 @@ msgid ""
"\n"
"You can come back to this window to add, edit, or remove accounts from <b>Accounts->Add/Edit</b> in the Buddy List window"
msgstr ""
-"<స్పాన్ పరిమాణం='పెద్ద' బరువు='బోల్డ్'>స్వాగతం %s!</స్పాన్>\n"
-"\n"
-"మీ IM అకౌంట్లు కన్ఫిగర్ కాలేదు. %s తో కనెక్ట్ కావడం ప్రారంభించడానికి <b>Add</b> ప్రెస్ చేయండి"
+#msgstr ""
+#"<స్పాన్ పరిమాణం='పెద్ద' బరువు='బోల్డ్'>స్వాగతం %s!</స్పాన్>\n"
+#"\n"
+#"మీ IM అకౌంట్లు కన్ఫిగర్ కాలేదు. %s తో కనెక్ట్ కావడం ప్రారంభించడానికి <b>Add</b> ప్రెస్ చేయండి"
#: ../pidgin/gtkblist.c:543
#, c-format
@@ -14679,7 +14709,8 @@ msgstr "సుదృఢం"
#: ../pidgin/gtkblist.c:3693
#, c-format
msgid "Idle %dd %dh %02dm"
-msgstr "ఖాళీ %dh %02dm"
+msgstr ""
+#msgstr "ఖాళీ %dh %02dm"
#: ../pidgin/gtkblist.c:3695
#, c-format
@@ -14754,7 +14785,8 @@ msgstr "%s డిస్కనెక్ట్అయింది"
#: ../pidgin/gtkblist.c:4494
#, c-format
msgid "%s disabled"
-msgstr "కమాండ్ పని చేయడం లేదు"
+msgstr ""
+#msgstr "కమాండ్ పని చేయడం లేదు"
#: ../pidgin/gtkblist.c:4498
msgid "Reconnect"
@@ -14778,8 +14810,10 @@ msgstr "ఉపేక్ష "
#, c-format
msgid "%d account was disabled because you signed on from another location."
msgid_plural "%d accounts were disabled because you signed on from another location."
-msgstr[0] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
-msgstr[1] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
+#msgstr[1] "మీరు మరో ప్రదేశంనుండి సైన్ ఆన్ చేశారు."
#: ../pidgin/gtkblist.c:4860
msgid "<b>Username:</b>"
@@ -15277,8 +15311,10 @@ msgstr "గదిలో 0 మంది ఉన్నారు"
#, c-format
msgid "%d person in room"
msgid_plural "%d people in room"
-msgstr[0] "గదిలో %d మనుషులున్నారు "
-msgstr[1] "कक्ष में 0 लोग हैं"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "గదిలో %d మనుషులున్నారు "
+#msgstr[1] "कक्ष में 0 लोग हैं"
#: ../pidgin/gtkconv.c:6574
#: ../pidgin/gtkstatusbox.c:660
@@ -16545,7 +16581,7 @@ msgstr "సిస్టం లాగ్"
#: ../pidgin/gtkmain.c:398
#, c-format
msgid "%s %s. Try `%s -h' for more information.\n"
-msgstr "Gaim %s. ఇంకా వివరములకు `%s -h' ప్రయత్నించండి.\n"
+msgstr "%s %s. ఇంకా వివరములకు `%s -h' ప్రయత్నించండి.\n"
#: ../pidgin/gtkmain.c:400
#, c-format
@@ -16595,23 +16631,6 @@ msgid ""
"on other protocols is at\n"
"%swiki/DeveloperPages\n"
msgstr ""
-"%s has segfaulted and attempted to dump a core file.\n"
-"This is a bug in the software and has happened through\n"
-"no fault of your own.\n"
-"\n"
-"If you can reproduce the crash, please notify the Pidgin\n"
-"developers by reporting a bug at\n"
-"%sbug.php\n"
-"\n"
-"Please make sure to specify what you were doing at the time\n"
-"and post the backtrace from the core file. If you do not know\n"
-"how to get the backtrace, please read the instructions at\n"
-"%sgdb.php\n"
-"\n"
-"If you need further assistance, please IM either SeanEgn or \n"
-"LSchiere (via AIM). Contact information for Sean and Luke \n"
-"on other protocols is at\n"
-"%scontactinfo.php\n"
#. Translators may want to transliterate the name.
#. It is not to be translated.
@@ -16644,8 +16663,10 @@ msgstr[1] "%s के %d लिए नये संदेश हैं।"
#, c-format
msgid "<b>%d new email.</b>"
msgid_plural "<b>%d new emails.</b>"
-msgstr[0] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
-msgstr[1] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
+#msgstr[1] "<b>ప్లగ్ ఇన్ వివరాలు</b>"
#: ../pidgin/gtknotify.c:998
#, c-format
@@ -18136,8 +18157,8 @@ msgid ""
"\n"
"<b>Buddy Note</b>: %s"
msgstr ""
-"\n"
-"<b>సోమరి:</b>"
+#"\n"
+#"<b>సోమరి:</b>"
#: ../pidgin/plugins/history.c:195
msgid "History"
@@ -18560,7 +18581,8 @@ msgstr "టెక్‌స్ట్‌పై ఆధారపడిన ప్ర
#: ../pidgin/plugins/relnot.c:71
#, c-format
msgid "You are using %s version %s. The current version is %s. You can get it from <a href=\"%s\">%s</a><hr>"
-msgstr "మీరు %s వెర్షన్‌ అనువాదాన్ని ఉపయోగిస్తున్నారు. ప్రస్తుత వెర్షన్ %s.<hr>"
+msgstr ""
+#msgstr "మీరు %s వెర్షన్‌ అనువాదాన్ని ఉపయోగిస్తున్నారు. ప్రస్తుత వెర్షన్ %s.<hr>"
#: ../pidgin/plugins/relnot.c:79
#, c-format
diff --git a/po/th.po b/po/th.po
index f1a4794576..b1c169f809 100644
--- a/po/th.po
+++ b/po/th.po
@@ -16,7 +16,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
#: ../finch/finch.c:64 ../finch/finch.c:301 ../finch/finch.c:330
#: ../finch/finch.c:418
@@ -15016,8 +15016,9 @@ msgid ""
"\n"
"%s"
msgstr ""
-"เกิดข้อผิดพลาดขณะบันทึกรูป: %s\n"
-"%s"
+#msgstr ""
+#"เกิดข้อผิดพลาดขณะบันทึกรูป: %s\n"
+#"%s"
#: ../pidgin/gtkimhtml.c:3482 ../pidgin/gtkimhtml.c:3494
msgid "Save Image"
diff --git a/po/ur.po b/po/ur.po
index 1f34e6e696..3887431ffa 100644
--- a/po/ur.po
+++ b/po/ur.po
@@ -13,6 +13,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Translate Toolkit 0.10\n"
"X-Poedit-Language: Urdu\n"
"X-Poedit-SourceCharset: utf.8\n"
@@ -1398,8 +1399,10 @@ msgstr "مضمون"
#, c-format
msgid "%s (%s) has %d new message."
msgid_plural "%s (%s) has %d new messages."
-msgstr[0] "%s کے پاس %dنیا پیام ہے۔"
-msgstr[1] "%sک ےپاس %dنئے پیامات ہیں۔"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "%s کے پاس %dنیا پیام ہے۔"
+#msgstr[1] "%sک ےپاس %dنئے پیامات ہیں۔"
#: ../finch/gntnotify.c:226
#: ../pidgin/gtknotify.c:342
@@ -2466,7 +2469,8 @@ msgstr ""
#: ../libpurple/certificate.c:1185
#, c-format
msgid "Accept certificate for %s?"
-msgstr "دعوت گفتگو قبول ؟"
+msgstr ""
+#msgstr "دعوت گفتگو قبول ؟"
#. TODO: Find what the handle ought to be
#: ../libpurple/certificate.c:1191
@@ -3083,7 +3087,8 @@ msgstr "گائم آپ کا پلگ ان لوڈ کرنے میں ناقابل تھ
#: ../libpurple/plugin.c:663
#, c-format
msgid "%s requires %s, but it failed to unload."
-msgstr "انحصاری پلگ ان%sان لوڈہونے میں ناكام ہوا۔"
+msgstr ""
+#msgstr "انحصاری پلگ ان%sان لوڈہونے میں ناكام ہوا۔"
#: ../libpurple/plugins/autoaccept.c:23
#, fuzzy
@@ -3099,7 +3104,8 @@ msgstr "%s سے ٹرانسفردرخواست فائل قبول؟"
#: ../libpurple/plugins/autoaccept.c:80
#, c-format
msgid "Autoaccepted file transfer of \"%s\" from \"%s\" completed."
-msgstr "%s سے ٹرانسفردرخواست فائل قبول؟"
+msgstr ""
+#msgstr "%s سے ٹرانسفردرخواست فائل قبول؟"
#: ../libpurple/plugins/autoaccept.c:82
msgid "Autoaccept complete"
@@ -3885,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
@@ -4025,7 +4031,8 @@ msgstr "بڈی فہرست کامیابی سے محفوظ ہوئی!"
#: ../libpurple/protocols/gg/gg.c:278
#, c-format
msgid "Couldn't write buddy list for %s to %s"
-msgstr "بڈّی لسٹ لوڈ نہیں كرسكا"
+msgstr ""
+#msgstr "بڈّی لسٹ لوڈ نہیں كرسكا"
#: ../libpurple/protocols/gg/gg.c:303
#: ../libpurple/protocols/gg/gg.c:304
@@ -4821,7 +4828,8 @@ msgstr "PING ری پلاے-- لیگ: %lu سیکنڈس"
#: ../libpurple/protocols/irc/msgs.c:1113
#, c-format
msgid "Cannot join %s: Registration is required."
-msgstr "رجسٹریشن کی ضروت تھی"
+msgstr ""
+#msgstr "رجسٹریشن کی ضروت تھی"
#: ../libpurple/protocols/irc/msgs.c:1114
#: ../libpurple/protocols/silc/ops.c:1093
@@ -5028,7 +5036,8 @@ msgstr "سرور کو ان اینکریپٹیڈ اسٹریم پر سادہ مت
#: ../libpurple/protocols/jabber/auth.c:518
#, c-format
msgid "%s requires plaintext authentication over an unencrypted connection. Allow this and continue authentication?"
-msgstr "اس سرور کو ان اینکریپٹیڈ کنیکشن پر سادہ متن تصدیق کی ضرورت ہے ۔ اس کو اجازت ہے اور تصدیق جاری رکھنا ہے؟"
+msgstr ""
+#msgstr "اس سرور کو ان اینکریپٹیڈ کنیکشن پر سادہ متن تصدیق کی ضرورت ہے ۔ اس کو اجازت ہے اور تصدیق جاری رکھنا ہے؟"
#: ../libpurple/protocols/jabber/auth.c:325
#: ../libpurple/protocols/jabber/auth.c:326
@@ -5789,7 +5798,7 @@ msgstr "کا رجسٹریشن %s@%s کامیاب"
#: ../libpurple/protocols/jabber/jabber.c:689
#, c-format
msgid "Registration to %s successful"
-msgstr "کا رجسٹریشن %s@%s کامیاب"
+msgstr "کا رجسٹریشن %s کامیاب"
#: ../libpurple/protocols/jabber/jabber.c:691
#: ../libpurple/protocols/jabber/jabber.c:692
@@ -5804,7 +5813,7 @@ msgstr "رجسٹریشن نا کام ہوا"
#: ../libpurple/protocols/jabber/jabber.c:719
#, c-format
msgid "Registration from %s successfully removed"
-msgstr "کا رجسٹریشن %s@%s کامیاب"
+msgstr "کا رجسٹریشن %s کامیاب"
#: ../libpurple/protocols/jabber/jabber.c:721
#: ../libpurple/protocols/jabber/jabber.c:722
@@ -5880,7 +5889,8 @@ msgstr " %sکے لئے صارف معلومات تبدیل کریں"
#: ../libpurple/protocols/jabber/jabber.c:1040
#, c-format
msgid "Register New Account at %s"
-msgstr "نیا مہمل اکاؤنٹ رجسٹر کریں"
+msgstr ""
+#msgstr "نیا مہمل اکاؤنٹ رجسٹر کریں"
#: ../libpurple/protocols/jabber/jabber.c:1043
msgid "Change Registration"
@@ -6342,7 +6352,8 @@ msgstr "Buzz!!"
#: ../libpurple/protocols/yahoo/yahoo.c:4131
#, c-format
msgid "%s has buzzed you!"
-msgstr "%s has added you [%s]"
+msgstr ""
+#msgstr "%s has added you [%s]"
#: ../libpurple/protocols/jabber/jabber.c:2281
#: ../libpurple/protocols/yahoo/yahoo.c:4132
@@ -6935,7 +6946,8 @@ msgstr ""
#: ../libpurple/protocols/msnp9/msn.c:131
#, c-format
msgid "%s has nudged you!"
-msgstr "%s has added you [%s]"
+msgstr ""
+#msgstr "%s has added you [%s]"
#: ../libpurple/protocols/msn/msn.c:132
#: ../libpurple/protocols/msnp9/msn.c:132
@@ -7495,7 +7507,8 @@ msgstr "%sنے ابھی آپ کو نج بھیجا!"
#: ../libpurple/protocols/msn/notification.c:836
#, c-format
msgid "Unknown error (%d)"
-msgstr "نامعلوم خامی "
+msgstr ""
+#msgstr "نامعلوم خامی "
#: ../libpurple/protocols/msn/notification.c:837
#: ../libpurple/protocols/sametime/sametime.c:4471
@@ -7864,7 +7877,8 @@ msgstr ""
#: ../libpurple/protocols/myspace/myspace.c:1794
#, c-format
msgid "Protocol error, code %d: %s"
-msgstr "پروسیس لوٹایا گيا خامی کوڈ %d"
+msgstr ""
+#msgstr "پروسیس لوٹایا گيا خامی کوڈ %d"
#: ../libpurple/protocols/myspace/myspace.c:1990
#: ../libpurple/protocols/myspace/myspace.c:2024
@@ -7923,7 +7937,8 @@ msgstr "پیام کی تصریف میں ناقابل"
#: ../libpurple/protocols/myspace/myspace.c:2499
#, c-format
msgid "Couldn't connect to host: %s (%d)"
-msgstr "ہاسٹ سے کنیکٹ نہیں ہو سکا"
+msgstr ""
+#msgstr "ہاسٹ سے کنیکٹ نہیں ہو سکا"
#: ../libpurple/protocols/myspace/myspace.c:2670
#, fuzzy
@@ -8728,7 +8743,8 @@ msgstr "(اس پیام كو حاصل كرنے میں كچھ رخنہ تھا۔ہ
#: ../libpurple/protocols/oscar/oscar.c:457
#, c-format
msgid "(There was an error receiving this message. Either you and %s have different encodings selected, or %s has a buggy client.)"
-msgstr "(پیام موصول ہونے میں خامی تھی۔ جس بڈی سے آپ بات کررہے تھے بہت حد تک قیاس ہے کہ کلائنٹ بگی تھا۔)"
+msgstr ""
+#msgstr "(پیام موصول ہونے میں خامی تھی۔ جس بڈی سے آپ بات کررہے تھے بہت حد تک قیاس ہے کہ کلائنٹ بگی تھا۔)"
#. Label
#: ../libpurple/protocols/oscar/oscar.c:639
@@ -9123,22 +9139,24 @@ msgstr "_گھٹاؤ"
#, c-format
msgid "You missed %hu message from %s because it was invalid."
msgid_plural "You missed %hu messages from %s because they were invalid."
-msgstr[0] "آپ نے %huپیام"
-msgstr[1] "آپ نے "
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "آپ نے %huپیام"
+#msgstr[1] "آپ نے "
#: ../libpurple/protocols/oscar/oscar.c:2615
#, c-format
msgid "You missed %hu message from %s because it was too large."
msgid_plural "You missed %hu messages from %s because they were too large."
-msgstr[0] "آپ نے %sسے %hu پیام کو مس کردیا کیونکہ یہ بہت بڑا تھا۔"
-msgstr[1] "آپ نے %sسے %hu پیامات کو مس کردیا کیونکہ یہ بہت بڑےتھے۔"
+msgstr[0] "آپ نے %2$sسے %1$hu پیام کو مس کردیا کیونکہ یہ بہت بڑا تھا۔"
+msgstr[1] "آپ نے %2$sسے %1$hu پیامات کو مس کردیا کیونکہ یہ بہت بڑےتھے۔"
#: ../libpurple/protocols/oscar/oscar.c:2624
#, c-format
msgid "You missed %hu message from %s because the rate limit has been exceeded."
msgid_plural "You missed %hu messages from %s because the rate limit has been exceeded."
-msgstr[0] "آپ نے %sسے %huپیام مس کردیا کیونکہ قیمت حد گذر گئی۔"
-msgstr[1] "آپ نے%sسے %hu پیامات مس کردیئے کیونکہ قیمت حد گذر گئی۔"
+msgstr[0] "آپ نے %2$sسے %1$huپیام مس کردیا کیونکہ قیمت حد گذر گئی۔"
+msgstr[1] "آپ نے%2$sسے %1$hu پیامات مس کردیئے کیونکہ قیمت حد گذر گئی۔"
#: ../libpurple/protocols/oscar/oscar.c:2633
#, c-format
@@ -9151,15 +9169,15 @@ msgstr[1] "You missed %hu messages from %s because he/she was too evil."
#, 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."
-msgstr[0] "آپ نے %sسے%huپیام مس کردیا کیونکہ آپ بہت ناشائستہ ہیں۔"
-msgstr[1] "آپ نے %sسے%huپیامات مس کردیئے کیونکہ آپ بہت ناشائستہ ہیں ۔"
+msgstr[0] "آپ نے %2$sسے%1$huپیام مس کردیا کیونکہ آپ بہت ناشائستہ ہیں۔"
+msgstr[1] "آپ نے %2$sسے%1$huپیامات مس کردیئے کیونکہ آپ بہت ناشائستہ ہیں ۔"
#: ../libpurple/protocols/oscar/oscar.c:2651
#, c-format
msgid "You missed %hu message from %s for an unknown reason."
msgid_plural "You missed %hu messages from %s for an unknown reason."
-msgstr[0] " آپ نے نا معلوم وجہ کےلئے %sسے%hu پیام مس کردیا ۔"
-msgstr[1] " آپ نے نا معلوم وجہ کےلئے %sسے%hu پیامات مس کردیئے ۔"
+msgstr[0] " آپ نے نا معلوم وجہ کےلئے %2$sسے%1$hu پیام مس کردیا ۔"
+msgstr[1] " آپ نے نا معلوم وجہ کےلئے %2$sسے%1$hu پیامات مس کردیئے ۔"
# Data is assumed to be the destination sn
#. Data is assumed to be the destination sn
@@ -9405,7 +9423,8 @@ msgstr "(کوئی نام نہیں)"
#: ../libpurple/protocols/oscar/oscar.c:5109
#, c-format
msgid "Could not add the buddy %s for an unknown reason."
-msgstr "آپ کا کمانڈ نا معلوم سبب کے لئے ناکام ہوا۔"
+msgstr ""
+#msgstr "آپ کا کمانڈ نا معلوم سبب کے لئے ناکام ہوا۔"
#: ../libpurple/protocols/oscar/oscar.c:5226
#, c-format
@@ -9810,7 +9829,8 @@ msgstr "آپ كی معلومات اپ ڈیٹ كی گئی ہے"
#: ../libpurple/protocols/qq/buddy_info.c:601
#, c-format
msgid "Setting custom faces is not currently supported. Please choose an image from %s."
-msgstr "آپ كسٹم فیس سیٹ كرنے كی كوشش كررہے ہیں ۔ گائم جاری طورپر صرف معیاری فیسیس كو اجازت دیتا ہے۔ براہ كرم ایك امیج منتخب كیجیے"
+msgstr ""
+#msgstr "آپ كسٹم فیس سیٹ كرنے كی كوشش كررہے ہیں ۔ گائم جاری طورپر صرف معیاری فیسیس كو اجازت دیتا ہے۔ براہ كرم ایك امیج منتخب كیجیے"
#: ../libpurple/protocols/qq/buddy_info.c:618
#: ../libpurple/protocols/qq/buddy_info.c:631
@@ -10271,7 +10291,8 @@ msgstr "كیا آپ انھیں ملانا چاہتے ہیں ؟"
#: ../libpurple/protocols/qq/sys_msg.c:176
#, c-format
msgid "%s has added you [%s] to his or her buddy list"
-msgstr "%sنے آپ كواپنی بڈی لسٹ میں شامل كردیا۔"
+msgstr ""
+#msgstr "%sنے آپ كواپنی بڈی لسٹ میں شامل كردیا۔"
#: ../libpurple/protocols/qq/sys_msg.c:192
#, c-format
@@ -12897,8 +12918,9 @@ msgid ""
"Lost connection with %s:\n"
"%s"
msgstr ""
-"سرور كے ساتھ كنیكشن گم ہوا:\n"
-"%s"
+#msgstr ""
+#"سرور كے ساتھ كنیكشن گم ہوا:\n"
+#"%s"
#: ../libpurple/protocols/yahoo/yahoo.c:2733
#, c-format
@@ -12906,8 +12928,9 @@ msgid ""
"Could not establish a connection with %s:\n"
"%s"
msgstr ""
-"سرور كے ساتھ كوئی كنیكشن اسٹابلیش نہیں كیا جاسكتا:\n"
-"%s"
+#msgstr ""
+#"سرور كے ساتھ كوئی كنیكشن اسٹابلیش نہیں كیا جاسكتا:\n"
+#"%s"
#: ../libpurple/protocols/yahoo/yahoo.c:3092
#: ../libpurple/protocols/yahoo/yahoo.c:3778
@@ -13551,12 +13574,12 @@ msgstr ""
#: ../libpurple/status.c:610
#, c-format
msgid "%s changed status from %s to %s"
-msgstr "%s changed status from %s to %s"
+msgstr ""
#: ../libpurple/status.c:613
#, c-format
msgid "%s (%s) changed status from %s to %s"
-msgstr "%s changed status from %s to %s"
+msgstr ""
#: ../libpurple/status.c:624
#, c-format
@@ -13566,7 +13589,8 @@ msgstr "%sابھی ہے %s"
#: ../libpurple/status.c:626
#, c-format
msgid "%s (%s) is now %s"
-msgstr "%sابھی ہے %s"
+msgstr ""
+#msgstr "%sابھی ہے %s"
#: ../libpurple/status.c:632
#, c-format
@@ -13576,7 +13600,8 @@ msgstr "%s اوریادہ نہیں ہےr %s"
#: ../libpurple/status.c:634
#, c-format
msgid "%s (%s) is no longer %s"
-msgstr "%s اوریادہ نہیں ہےr %s"
+msgstr ""
+#msgstr "%s اوریادہ نہیں ہےr %s"
#: ../libpurple/status.c:1247
#, c-format
@@ -13673,7 +13698,8 @@ msgstr ""
#: ../libpurple/util.c:3943
#, c-format
msgid "Unable to connect to %s"
-msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
+msgstr ""
+#msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
#: ../libpurple/util.c:3770
#, c-format
@@ -13689,7 +13715,8 @@ msgstr ""
#: ../libpurple/util.c:3861
#, c-format
msgid "Unable to connect to %s: %s"
-msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
+msgstr ""
+#msgstr "ہوسٹ سےكنیكٹ ہونے میں نااہل"
#: ../pidgin.desktop.in.h:1
msgid "Internet Messenger"
@@ -13873,11 +13900,12 @@ msgid ""
"\n"
"You can come back to this window to add, edit, or remove accounts from <b>Accounts->Add/Edit</b> in the Buddy List window"
msgstr ""
-"<span size='larger' weight='bold'> گائم میں خوش آمدید!</span>\n"
-"\n"
-"آپ كوئي ہیئت والاIM اكاؤنٹس نہیں ركھتے ہیں ۔ گائم كے ساتھ جوڑنا شروع كرنے كے لیے <b>ملاؤ</b> بٹن پر نیچے كلك كیجیےا ور آپ كا پہلا اكاؤنٹ كو ہیئت دیجیے۔ اگر آپ گائم كو متعدد IM اكاؤنٹس میں چاہتے ہیں ، مال كو ہیئت دینے كے لیے دوبارہ <b>ملاؤ</b> پر كلك كیجیے۔\n"
-"\n"
-"آپ ملانے ، مرتب، یا اكاؤنٹس نكالنے كے لیے اس ونڈو كو واپس جاسكتے ہیں ،بڈی فہرست ونڈو میں <b>اكاؤنٹس->ملاؤ/مرتب</b>"
+#msgstr ""
+#"<span size='larger' weight='bold'> گائم میں خوش آمدید!</span>\n"
+#"\n"
+#"آپ كوئي ہیئت والاIM اكاؤنٹس نہیں ركھتے ہیں ۔ گائم كے ساتھ جوڑنا شروع كرنے كے لیے <b>ملاؤ</b> بٹن پر نیچے كلك كیجیےا ور آپ كا پہلا اكاؤنٹ كو ہیئت دیجیے۔ اگر آپ گائم كو متعدد IM اكاؤنٹس میں چاہتے ہیں ، مال كو ہیئت دینے كے لیے دوبارہ <b>ملاؤ</b> پر كلك كیجیے۔\n"
+#"\n"
+#"آپ ملانے ، مرتب، یا اكاؤنٹس نكالنے كے لیے اس ونڈو كو واپس جاسكتے ہیں ،بڈی فہرست ونڈو میں <b>اكاؤنٹس->ملاؤ/مرتب</b>"
#: ../pidgin/gtkblist.c:543
#, c-format
@@ -14203,7 +14231,7 @@ msgstr "راك ان"
#: ../pidgin/gtkblist.c:3693
#, c-format
msgid "Idle %dd %dh %02dm"
-msgstr "Idle %dh %02dm"
+msgstr ""
#: ../pidgin/gtkblist.c:3695
#, c-format
@@ -14278,7 +14306,8 @@ msgstr "%s منقطع کیا گیا"
#: ../pidgin/gtkblist.c:4494
#, c-format
msgid "%s disabled"
-msgstr "نااہل كیاگیا"
+msgstr ""
+#msgstr "نااہل كیاگیا"
#: ../pidgin/gtkblist.c:4498
msgid "Reconnect"
@@ -14300,8 +14329,10 @@ msgstr "نظراندازکرو"
#, c-format
msgid "%d account was disabled because you signed on from another location."
msgid_plural "%d accounts were disabled because you signed on from another location."
-msgstr[0] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
-msgstr[1] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
+msgstr[0] ""
+msgstr[1] ""
+#msgstr[0] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
+#msgstr[1] "آپ نے دوسرے لوکیشن سے سائنڈ آن کیا ہے۔"
#: ../pidgin/gtkblist.c:4860
msgid "<b>Username:</b>"
@@ -14327,9 +14358,10 @@ msgid ""
"\n"
"You have no accounts enabled. Enable your IM accounts from the <b>Accounts</b> window at <b>Accounts->Manage</b>. Once you enable accounts, you'll be able to sign on, set your status, and talk to your friends."
msgstr ""
-"<span weight='bold' size='larger'>گائم كو خوش آمدید!</span>\n"
-"\n"
-" آپ كوئی اكاؤنٹ ممكن نہيں ركھتے ہیں ۔<b>Accounts</b> اكاؤنٹس<b>Accounts->Add/Edit</b>ونڈو سے آپ كا IM اكاؤنٹس ممكن كرو۔ایك بار آپ اكاؤنٹس ممكن كرتے ہیں ،آپ سائن آن كو قابل كرتے ہیں ،آپ كا اسٹیٹس سیٹ كیجیے ، اورآپ كے فرینڈس سے بات كیجیے۔"
+#msgstr ""
+#"<span weight='bold' size='larger'>گائم كو خوش آمدید!</span>\n"
+#"\n"
+#" آپ كوئی اكاؤنٹ ممكن نہيں ركھتے ہیں ۔<b>Accounts</b> اكاؤنٹس<b>Accounts->Add/Edit</b>ونڈو سے آپ كا IM اكاؤنٹس ممكن كرو۔ایك بار آپ اكاؤنٹس ممكن كرتے ہیں ،آپ سائن آن كو قابل كرتے ہیں ،آپ كا اسٹیٹس سیٹ كیجیے ، اورآپ كے فرینڈس سے بات كیجیے۔"
#. set the Show Offline Buddies option. must be done
#. * after the treeview or faceprint gets mad. -Robot101
@@ -14756,8 +14788,9 @@ msgstr "0 لوگ کمرہ میں"
#, c-format
msgid "%d person in room"
msgid_plural "%d people in room"
-msgstr[0] "%d فرد کمرے میں"
+msgstr[0] ""
msgstr[1] ""
+#msgstr[0] "%d فرد کمرے میں"
#: ../pidgin/gtkconv.c:6574
#: ../pidgin/gtkstatusbox.c:660
@@ -15310,12 +15343,14 @@ msgstr "Amharic"
#: ../pidgin/gtkdialogs.c:368
#, c-format
msgid "About %s"
-msgstr "گائم کےبارےمیں"
+msgstr ""
+#msgstr "گائم کےبارےمیں"
#: ../pidgin/gtkdialogs.c:411
#, c-format
msgid "%s is a graphical modular messaging client based on libpurple which is capable of connecting to AIM, MSN, Yahoo!, XMPP, ICQ, IRC, SILC, SIP/SIMPLE, Novell GroupWise, Lotus Sametime, Bonjour, Zephyr, MySpaceIM, Gadu-Gadu, and QQ all at once. It is written using GTK+.<BR><BR>You may modify and redistribute the program under the terms of the GPL (version 2 or later). A copy of the GPL is contained in the 'COPYING' file distributed with %s. %s is copyrighted by its contributors. See the 'COPYRIGHT' file for the complete list of contributors. We provide no warranty for this program.<BR><BR>"
-msgstr "گائم AIM, MSN, Yahoo!, كو استعمال كرتے ہوئے ایك ماڈیولر میسیجینگ كلائںٹ كیبل ہے ۔Jabber, ICQ, IRC, SILC, SIP/SIMPLE, ناول گروپ وائس، لوٹس مشابہ ٹائم،بونزور، زیفیر،گڈو گڈو، اورQQایك بار میں ۔یہ GTK+.<BR><BR> استعمال كرتے ہوئے لكھا جاتا ہے آپ ترمیم كرینگے اور پروگرام كوGPL شرطوں كے ماتحٹ دوبارہ تقسیم كرینگے (version 2 or later).GPLكی ایك كاپی 'COPYING' فائل میں ركھتی ہے گائم سے تقسیم كی جاتی ہے۔ گائم اسے شراكت داروں سے كاپی رائٹیڈ ہے ۔ شراكت داروں كی مكمل فہرست كے لیے گائم كی 'COPYRIGHT' فائل دیكھیے۔ ہم اس پروگرام كے لیے كوئي وارنٹی مہیا نہیں كرتے ہیں ۔<BR><BR>"
+msgstr ""
+#msgstr "گائم AIM, MSN, Yahoo!, كو استعمال كرتے ہوئے ایك ماڈیولر میسیجینگ كلائںٹ كیبل ہے ۔Jabber, ICQ, IRC, SILC, SIP/SIMPLE, ناول گروپ وائس، لوٹس مشابہ ٹائم،بونزور، زیفیر،گڈو گڈو، اورQQایك بار میں ۔یہ GTK+.<BR><BR> استعمال كرتے ہوئے لكھا جاتا ہے آپ ترمیم كرینگے اور پروگرام كوGPL شرطوں كے ماتحٹ دوبارہ تقسیم كرینگے (version 2 or later).GPLكی ایك كاپی 'COPYING' فائل میں ركھتی ہے گائم سے تقسیم كی جاتی ہے۔ گائم اسے شراكت داروں سے كاپی رائٹیڈ ہے ۔ شراكت داروں كی مكمل فہرست كے لیے گائم كی 'COPYRIGHT' فائل دیكھیے۔ ہم اس پروگرام كے لیے كوئي وارنٹی مہیا نہیں كرتے ہیں ۔<BR><BR>"
#: ../pidgin/gtkdialogs.c:429
#, fuzzy
@@ -15898,17 +15933,18 @@ msgstr "كیا آپ واقعی %s for %sپرجھپٹنا خارج كرناچاہ
#: ../pidgin/gtklog.c:309
#, c-format
msgid "Are you sure you want to permanently delete the system log which started at %s?"
-msgstr "كیا آپ واقعی %s for %sپرجھپٹنا خارج كرناچاہتےہیں؟"
+msgstr ""
+#msgstr "كیا آپ واقعی %s for %sپرجھپٹنا خارج كرناچاہتےہیں؟"
#: ../pidgin/gtklog.c:453
#, c-format
msgid "<span size='larger' weight='bold'>Conversation in %s on %s</span>"
-msgstr "<span size='larger' weight='bold'>Conversation in %s on %s</span>"
+msgstr ""
#: ../pidgin/gtklog.c:456
#, c-format
msgid "<span size='larger' weight='bold'>Conversation with %s on %s</span>"
-msgstr "<span size='larger' weight='bold'>Conversation with %s on %s</span>"
+msgstr ""
#: ../pidgin/gtklog.c:503
msgid "%B %Y"
@@ -15957,7 +15993,7 @@ msgstr "سسٹم لاگ"
#: ../pidgin/gtkmain.c:398
#, c-format
msgid "%s %s. Try `%s -h' for more information.\n"
-msgstr "%s. كوشش `%s -h' مزید معلومات كے لیے.\n"
+msgstr "%s %s. كوشش `%s -h' مزید معلومات كے لیے.\n"
#: ../pidgin/gtkmain.c:400
#, c-format
@@ -15975,16 +16011,6 @@ msgid ""
" --display=DISPLAY X display to use\n"
" -v, --version display the current version and exit\n"
msgstr ""
-"Gaim %s\n"
-"Usage: %s [OPTION]...\n"
-"\n"
-" -c, --config=DIR use DIR for config files\n"
-" -d, --debug print debugging messages to stdout\n"
-" -h, --help display this help and exit\n"
-" -n, --nologin don't automatically login\n"
-" -l, --login[=NAME] automatically login (optional argument NAME specifies\n"
-" account(s) to use, separated by commas)\n"
-" -v, --version display the current version and exit\n"
#: ../pidgin/gtkmain.c:528
#, c-format
@@ -16007,23 +16033,24 @@ msgid ""
"on other protocols is at\n"
"%swiki/DeveloperPages\n"
msgstr ""
-"گائم سیگافالٹ ہوگیاہےاوراہم فائل كوپھینكنےكی كوشش كی۔ \n"
-"یہ سافٹ وئرمیں بگ ہے اوراس كے ذریعے ہواہے \n"
-"آپ كی كوئی غلطی نہیں۔\n"
-"\n"
-"اگرآپ دوبارہ كریش پروڈیوس كرسكتے ہیں تو، براہ كرم گائم\n"
-"ڈیولپرز كو بگ كی رپورٹ كریں\n"
-"%sbug.php\n"
-"\n"
-"اس بات كا خیال ركھیں یہ بتانانہ بھولیں كہ آپ اس وقت كیاكررہےتھے\n"
-"اور اہم فائل سےبیك ٹریس پوسٹ كریں۔ اگرآپ كو معلوم نہیں ہے \n"
-"بیك ٹریس كس طرح حاصل كریں تو،براہ كرم ہدایات پڑھیں\n"
-"%sgdb.php\n"
-"\n"
-"اگرآپ كومزیدمددكی ضرورت ہےتو، براہ كرم IM either SeanEgn or \n"
-"LSchiere (via AIM)٫ سین اورلیوك معلومات كےلیےرابطہ كریں \n"
-"دیگرپروٹوكالزہے\n"
-"%scontactinfo.php\n"
+#msgstr ""
+#"گائم سیگافالٹ ہوگیاہےاوراہم فائل كوپھینكنےكی كوشش كی۔ \n"
+#"یہ سافٹ وئرمیں بگ ہے اوراس كے ذریعے ہواہے \n"
+#"آپ كی كوئی غلطی نہیں۔\n"
+#"\n"
+#"اگرآپ دوبارہ كریش پروڈیوس كرسكتے ہیں تو، براہ كرم گائم\n"
+#"ڈیولپرز كو بگ كی رپورٹ كریں\n"
+#"%sbug.php\n"
+#"\n"
+#"اس بات كا خیال ركھیں یہ بتانانہ بھولیں كہ آپ اس وقت كیاكررہےتھے\n"
+#"اور اہم فائل سےبیك ٹریس پوسٹ كریں۔ اگرآپ كو معلوم نہیں ہے \n"
+#"بیك ٹریس كس طرح حاصل كریں تو،براہ كرم ہدایات پڑھیں\n"
+#"%sgdb.php\n"
+#"\n"
+#"اگرآپ كومزیدمددكی ضرورت ہےتو، براہ كرم IM either SeanEgn or \n"
+#"LSchiere (via AIM)٫ سین اورلیوك معلومات كےلیےرابطہ كریں \n"
+#"دیگرپروٹوكالزہے\n"
+#"%scontactinfo.php\n"
#. Translators may want to transliterate the name.
#. It is not to be translated.
@@ -16052,8 +16079,8 @@ msgstr[1] "%sک ےپاس %dنئے پیامات ہیں۔"
#, c-format
msgid "<b>%d new email.</b>"
msgid_plural "<b>%d new emails.</b>"
-msgstr[0] "<b>Plugin Details</b>"
-msgstr[1] "<b>Plugin Details</b>"
+msgstr[0] ""
+msgstr[1] ""
#: ../pidgin/gtknotify.c:998
#, c-format
@@ -16891,7 +16918,8 @@ msgstr "فولڈر نہیں بھیج سکتا%s۔"
#: ../pidgin/gtkutils.c:1501
#, c-format
msgid "%s cannot transfer a folder. You will need to send the files within individually."
-msgstr "گائم فولڈ رٹرانسفر نہیں کر سکا ۔آپ کو انفرادی طور پر فائلیں بھیجنے کی ضرورت ہے"
+msgstr ""
+#msgstr "گائم فولڈ رٹرانسفر نہیں کر سکا ۔آپ کو انفرادی طور پر فائلیں بھیجنے کی ضرورت ہے"
#: ../pidgin/gtkutils.c:1535
#: ../pidgin/gtkutils.c:1547
@@ -17806,7 +17834,8 @@ msgstr "آپ متن بیسڈ- پروٹوکالس کے لئے ڈراؤ ان پٹ
#: ../pidgin/plugins/relnot.c:71
#, c-format
msgid "You are using %s version %s. The current version is %s. You can get it from <a href=\"%s\">%s</a><hr>"
-msgstr "آپ گائم ورجن %s استعمال کررہے ہیں۔حالیہ ورجن ہے%s.<hr>"
+msgstr ""
+#msgstr "آپ گائم ورجن %s استعمال کررہے ہیں۔حالیہ ورجن ہے%s.<hr>"
#: ../pidgin/plugins/relnot.c:79
#, c-format
@@ -18091,7 +18120,8 @@ msgstr "شروع کرو"
#: ../pidgin/plugins/win32/winprefs/winprefs.c:312
#, c-format
msgid "_Start %s on Windows startup"
-msgstr "_ ونڈوز اسٹارٹ اپ پر گائم شروع کریں"
+msgstr ""
+#msgstr "_ ونڈوز اسٹارٹ اپ پر گائم شروع کریں"
#: ../pidgin/plugins/win32/winprefs/winprefs.c:327
msgid "_Dockable Buddy List"
diff --git a/po/vi.po b/po/vi.po
index ac297fcfab..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"
@@ -5860,7 +5860,8 @@ msgstr "Đăng ký thành công %s@%s"
#: ../libpurple/protocols/jabber/jabber.c:710
#, c-format
msgid "Registration to %s successful"
-msgstr "Đăng ký thành công với %s@%"
+msgstr ""
+#msgstr "Đăng ký thành công với %s@%"
#: ../libpurple/protocols/jabber/jabber.c:712
#: ../libpurple/protocols/jabber/jabber.c:713
@@ -11668,7 +11669,7 @@ msgstr "<I>%s</I> đã gỡ bỏ tất cả các chế độ của <I>%s's</I>"
#: ../libpurple/protocols/silc10/ops.c:712
#, c-format
msgid "You have been kicked off <I>%s</I> by <I>%s</I> (%s)"
-msgstr "Bạn đã bị <I>%2$s</I> đã khỏi <I>%1$s</I> (%s)"
+msgstr "Bạn đã bị <I>%2$s</I> đã khỏi <I>%1$s</I> (%3$s)"
#: ../libpurple/protocols/silc/ops.c:718
#: ../libpurple/protocols/silc/ops.c:723
diff --git a/po/zh_CN.po b/po/zh_CN.po
index 622ece2d1b..0555798a39 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -13383,7 +13383,7 @@ msgstr ""
msgid "You have %d contact named %s. Would you like to merge them?"
msgid_plural ""
"You currently have %d contacts named %s. Would you like to merge them?"
-msgstr[0] "您已经有名为 %s 的 %d 位联系人。您是否想要合并?"
+msgstr[0] "您已经有名为 %2$s 的 %1$d 位联系人。您是否想要合并?"
#: ../pidgin/gtkblist.c:525
msgid ""
diff --git a/share/ca-certs/Makefile.am b/share/ca-certs/Makefile.am
index e8e5aa52a3..9994491538 100644
--- a/share/ca-certs/Makefile.am
+++ b/share/ca-certs/Makefile.am
@@ -1,5 +1,4 @@
-cacertsdir = $(datadir)/purple/ca-certs
-cacerts_DATA = \
+CERTIFICATES = \
Equifax_Secure_CA.pem \
GTE_CyberTrust_Global_Root.pem \
Microsoft_Secure_Server_Authority.pem \
@@ -7,7 +6,12 @@ cacerts_DATA = \
Verisign_RSA_Secure_Server_CA.pem \
Verisign_Class3_Primary_CA.pem
+if INSTALL_SSL_CERTIFICATES
+cacertsdir = $(datadir)/purple/ca-certs
+cacerts_DATA = $(CERTIFICATES)
+endif
+
EXTRA_DIST = \
Makefile.mingw \
- $(cacerts_DATA)
+ $(CERTIFICATES)
diff --git a/share/ca-certs/Makefile.mingw b/share/ca-certs/Makefile.mingw
index 1febdd298b..2472de98bf 100644
--- a/share/ca-certs/Makefile.mingw
+++ b/share/ca-certs/Makefile.mingw
@@ -8,14 +8,17 @@ PIDGIN_TREE_TOP := ../..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
datadir := $(PIDGIN_INSTALL_DIR)
-include ./Makefile.am
+-include ./Makefile.am.mingw
cacertsdir := $(PIDGIN_INSTALL_DIR)/ca-certs
.PHONY: install
-install:
+install: ./Makefile.am.mingw
if test '$(cacerts_DATA)'; then \
mkdir -p $(cacertsdir); \
cp $(cacerts_DATA) $(cacertsdir); \
fi;
+./Makefile.am.mingw: ./Makefile.am
+ sed -e 's/^if\ INSTALL_SSL_CERTIFICATES/ifeq (\$$(INSTALL_SSL_CERTIFICATES), 1)/' ./Makefile.am > $@
+