summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Bailey <rekkanoryo@rekkanoryo.org>2008-10-29 04:37:14 +0000
committerJohn Bailey <rekkanoryo@rekkanoryo.org>2008-10-29 04:37:14 +0000
commit6bddf58bfe4b9df5fcf6e8217c65df2f0229aa31 (patch)
tree2d549670fb5de37d91aca7abdeab79bbb3b57050
parentb95a8cf36ef344b2838d6e5a16a96cfd78961d84 (diff)
parentc9b9fa095bd549c1fb43f3406bc0d61d9d5bf8f9 (diff)
downloadpidgin-6bddf58bfe4b9df5fcf6e8217c65df2f0229aa31.tar.gz
merge of '5f136b8043e3940b5aa98ece1842c009efc828bd'
and '48c5fa5ad966b7b6593240073e800084372d7e2f'
-rw-r--r--libpurple/protocols/qq/AUTHORS1
-rw-r--r--libpurple/protocols/qq/ChangeLog123
-rw-r--r--libpurple/protocols/qq/Makefile.am22
-rw-r--r--libpurple/protocols/qq/Makefile.mingw13
-rw-r--r--libpurple/protocols/qq/buddy_info.c1423
-rw-r--r--libpurple/protocols/qq/buddy_info.h27
-rw-r--r--libpurple/protocols/qq/buddy_list.c425
-rw-r--r--libpurple/protocols/qq/buddy_list.h27
-rw-r--r--libpurple/protocols/qq/buddy_opt.c1358
-rw-r--r--libpurple/protocols/qq/buddy_opt.h60
-rw-r--r--libpurple/protocols/qq/char_conv.c44
-rw-r--r--libpurple/protocols/qq/char_conv.h6
-rw-r--r--libpurple/protocols/qq/file_trans.c56
-rw-r--r--libpurple/protocols/qq/group.c61
-rw-r--r--libpurple/protocols/qq/group.h12
-rw-r--r--libpurple/protocols/qq/group_conv.c128
-rw-r--r--libpurple/protocols/qq/group_conv.h35
-rw-r--r--libpurple/protocols/qq/group_find.c249
-rw-r--r--libpurple/protocols/qq/group_find.h43
-rw-r--r--libpurple/protocols/qq/group_free.c81
-rw-r--r--libpurple/protocols/qq/group_free.h35
-rw-r--r--libpurple/protocols/qq/group_im.c450
-rw-r--r--libpurple/protocols/qq/group_im.h19
-rw-r--r--libpurple/protocols/qq/group_info.c247
-rw-r--r--libpurple/protocols/qq/group_info.h9
-rw-r--r--libpurple/protocols/qq/group_internal.c508
-rw-r--r--libpurple/protocols/qq/group_internal.h28
-rw-r--r--libpurple/protocols/qq/group_join.c347
-rw-r--r--libpurple/protocols/qq/group_join.h15
-rw-r--r--libpurple/protocols/qq/group_opt.c450
-rw-r--r--libpurple/protocols/qq/group_opt.h19
-rw-r--r--libpurple/protocols/qq/group_search.c133
-rw-r--r--libpurple/protocols/qq/group_search.h34
-rw-r--r--libpurple/protocols/qq/im.c665
-rw-r--r--libpurple/protocols/qq/im.h45
-rw-r--r--libpurple/protocols/qq/packet_parse.c25
-rw-r--r--libpurple/protocols/qq/packet_parse.h1
-rw-r--r--libpurple/protocols/qq/qq.c689
-rw-r--r--libpurple/protocols/qq/qq.h70
-rw-r--r--libpurple/protocols/qq/qq_base.c1531
-rw-r--r--libpurple/protocols/qq/qq_base.h52
-rw-r--r--libpurple/protocols/qq/qq_define.c (renamed from libpurple/protocols/qq/header_info.c)56
-rw-r--r--libpurple/protocols/qq/qq_define.h (renamed from libpurple/protocols/qq/header_info.h)51
-rw-r--r--libpurple/protocols/qq/qq_network.c217
-rw-r--r--libpurple/protocols/qq/qq_network.h2
-rw-r--r--libpurple/protocols/qq/qq_process.c759
-rw-r--r--libpurple/protocols/qq/qq_process.h9
-rw-r--r--libpurple/protocols/qq/qq_trans.c3
-rw-r--r--libpurple/protocols/qq/send_file.c64
-rw-r--r--libpurple/protocols/qq/sys_msg.c357
-rw-r--r--libpurple/protocols/qq/sys_msg.h33
-rw-r--r--libpurple/protocols/qq/utils.c62
-rw-r--r--libpurple/protocols/qq/utils.h8
53 files changed, 6519 insertions, 4668 deletions
diff --git a/libpurple/protocols/qq/AUTHORS b/libpurple/protocols/qq/AUTHORS
index 0ec81447b9..b33b2a29a5 100644
--- a/libpurple/protocols/qq/AUTHORS
+++ b/libpurple/protocols/qq/AUTHORS
@@ -35,4 +35,5 @@ LumaQQ Team : http://lumaqq.linuxsir.org
khc@pidgin.im
qulogic@pidgin.im
rlaager@pidgin.im
+Huang Guan : http://home.xxsyzx.com
OpenQ Google Group : http://groups.google.com/group/openq
diff --git a/libpurple/protocols/qq/ChangeLog b/libpurple/protocols/qq/ChangeLog
index 4b97d0aed6..59aae305c2 100644
--- a/libpurple/protocols/qq/ChangeLog
+++ b/libpurple/protocols/qq/ChangeLog
@@ -1,3 +1,126 @@
+2008.10.28 - flos <lonicerae(at)gmail.com>
+ * Updated AUTHORS
+
+2008.10.27 - ccpaging <ccpaging(at)gmail.com>
+ * Fixed a bug in buddy_info.c
+
+2008.10.27 - ccpaging <ccpaging(at)gmail.com>
+ * Update 'buddy_adding' protocol
+
+2008.10.22 - ccpaging <ccpaging(at)gmail.com>
+ * 20081022
+
+2008.10.20 - ccpaging <ccpaging(at)gmail.com>
+ * Support incoming authorization of 'buddy_adding' protocol of QQ2007/2008
+
+2008.10.14 - ccpaging <ccpaging(at)gmail.com>
+ * 2007 remove buddy ok
+ * Removed group_search.c/h
+
+2008.10.10 - ccpaging <ccpaging(at)gmail.com>
+ * Support part of 'buddy' protocol of QQ2007/2008
+
+2008.10.10 - ccpaging <ccpaging(at)gmail.com>
+ * Keep group_search.c/h for later use
+ * Update 'group'
+
+2008.10.09 - ccpaging <ccpaging(at)gmail.com>
+ * 20081009-1
+
+2008.10.09 - ccpaging <ccpaging(at)gmail.com>
+ * Update 'group' protocol
+ * Functions of group_find, group_free, group_search merged into group_join and group_internal
+ * Removed group_find.c/h, group_free.c/h, group_search.c/h
+
+2008.10.08 - ccpaging <ccpaging(at)gmail.com>
+ * Update 'group' protocol
+
+2008.10.08 - ccpaging <ccpaging(at)gmail.com>
+ * 20081008-1
+
+2008.10.08 - ccpaging <ccpaging(at)gmail.com>
+ * Update group part
+ * Delete some meaningless functions and data
+ * Added 'change my icon'
+
+2008.10.08 - lonicerae <lonicerae(at)gmail.com>
+ * Update Makefile.mingw
+
+2008.10.08 - ccpaging <ccpaging(at)gmail.com>
+ * Fixed QQ_BUDDY_ICON_DIR problem
+
+2008.10.07 - lonicerae <lonicerae(at)gmail.com>
+ * Update 'version display'
+
+2008.10.07 - lonicerae <lonicerae(at)gmail.com>
+ * Added some defensive code for 'action' series functions of qq.c
+
+2008.10.07 - ccpaging <ccpaging(at)gmail.com>
+ * Update buddy icon
+
+2008.10.07 - ccpaging <ccpaging(at)gmail.com>
+ * Update qq_buddy
+
+2008.10.07 - ccpaging <ccpaging(at)gmail.com>
+ * Update qun conversation
+
+2008.10.05 - lonicerae <lonicerae(at)gmail.com>
+ * Bug fix in 'About OpenQ' dialog
+
+2008.10.05 - lonicerae <lonicerae(at)gmail.com>
+ * Added 'About OpenQ' dialog
+
+2008.10.05 - ccpaging <ccpagint(at)gmail.com>
+ * Add my uid into buddy list
+ * Fixed a minor bug in qq_create_buddy. Not get new buddy's info.
+ * There are 38 fields in protocol 2008, one more than 2005/2007.
+ * The packet of Modifing buddy info is changed. Need sample to fix it.
+
+2008.10.04 - ccpaging <ccpagint(at)gmail.com>
+ * Update protocol for 2007
+ * Code cleanup
+
+2008.10.04 - lonicerae <lonicerae(at)gmail.com>
+ * fixed a bug in qq_base.c
+
+2008.10.03 - ccpaging <ccpaging(at)gmail.com>
+ * 2007 protocol:
+ 1. fixed 'get room info'
+ 2. fixed 'get buddy level'
+
+2008.10.02 - ccpaging <ccpaging(at)gmail.com>
+ * Added 'Captcha Display' function
+ * QQ2007 for openq, programed by Emil Alexiev:
+ 1. Most functions from patch written by Emil Alexiev merged into trunk, except 'buddy operations'
+ 2. 'online buddy status' and 'qun buddies' still have problems
+ * QQ2008 console client, programed by Shrimp:
+ 1. 'before login' and 'keep alive' parts merged into trunk
+
+2008.09.30 - ccpaging <ccpaging(at)gmail.com>
+ * Successfully login using 2007/2008 protocols
+
+2008.09.29 - ccpaging <ccpaging(at)gmail.com>
+ * 'Check Password' function for protocol 2007/2008
+
+2008.09.28 - ccpaging <ccpaging(at)gmail.com>
+ * The source is only for debug, not for user:
+ 1. Implement new QQ protocol 2007/2008, include login and change status
+ 2. Check 2005's login reply packet, get last 3 login time.
+ 3. Server's notice and news is displayed in self buddy (The new buddy created in buddy list).
+ 4. The notice messages when adding/removing QQ Qun's buddy displayed in char conversation. They are displayed as purple notify windows in the past.
+ 5. The notice messages when adding/removing buddy displayed in self buddy's conversation. They are displayed as purple notify windows in the past.
+ 6. Client version can be selected in account option. Now only qq2005 is working, other new version is only for debug.
+
+2008.09.26 - ccpaging <ccpaging(at)gmail.com>
+ * Added 'Request/Add/Remove Buddy' functions
+
+2008.09.19 - ccpaging <ccpaging(at)gmail.com>
+ * Rewrite buddy modify info, there is a ticket for this problem
+ * Use ship32 to trans action code between request packet and reply packet process
+
+2008.09.15 - csyfek <csyfek(at)gmail.com>
+ * im.pidgin.pidgin.openq branch
+
2008.09.05 - ccpaging <ccpaging(at)gmail.com>
* Filter chars 0x01-0x20 in nickname
diff --git a/libpurple/protocols/qq/Makefile.am b/libpurple/protocols/qq/Makefile.am
index 6bc8fa3314..366f71224a 100644
--- a/libpurple/protocols/qq/Makefile.am
+++ b/libpurple/protocols/qq/Makefile.am
@@ -18,12 +18,6 @@ QQSOURCES = \
file_trans.h \
group.c \
group.h \
- group_conv.c \
- group_conv.h \
- group_find.c \
- group_find.h \
- group_free.c \
- group_free.h \
group_internal.c \
group_internal.h \
group_im.c \
@@ -34,10 +28,8 @@ QQSOURCES = \
group_join.h \
group_opt.c \
group_opt.h \
- group_search.c \
- group_search.h \
- header_info.c \
- header_info.h \
+ qq_define.c \
+ qq_define.h \
im.c \
im.h \
qq_process.c \
@@ -54,8 +46,6 @@ QQSOURCES = \
send_file.h \
qq_trans.c \
qq_trans.h \
- sys_msg.c \
- sys_msg.h \
utils.c \
utils.h
@@ -63,6 +53,11 @@ AM_CFLAGS = $(st)
libqq_la_LDFLAGS = -module -avoid-version
+CURRENT_REVISION=$(shell \
+ awk 'BEGIN {"grep node .hg_archival.txt" | getline rev; \
+ rev=substr(rev,7,6); \
+ print rev}')
+
if STATIC_QQ
st = -DPURPLE_STATIC_PRPL
@@ -84,4 +79,5 @@ AM_CPPFLAGS = \
-I$(top_builddir)/libpurple \
-DQQ_BUDDY_ICON_DIR=\"$(datadir)/pixmaps/purple/buddy_icons/qq\" \
$(DEBUG_CFLAGS) \
- $(GLIB_CFLAGS)
+ $(GLIB_CFLAGS) \
+ -DOPENQ_VERSION=\"$(CURRENT_REVISION)\"
diff --git a/libpurple/protocols/qq/Makefile.mingw b/libpurple/protocols/qq/Makefile.mingw
index 527ab6978c..22e2d2ee6a 100644
--- a/libpurple/protocols/qq/Makefile.mingw
+++ b/libpurple/protocols/qq/Makefile.mingw
@@ -6,7 +6,10 @@
PIDGIN_TREE_TOP := ../../..
include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak
-
+CURRENT_REVISION=$(shell \
+ awk 'BEGIN {"grep node .hg_archival.txt" | getline rev; \
+ rev=substr(rev,7,6); \
+ print rev}')
TARGET = libqq
TYPE = PLUGIN
@@ -16,6 +19,7 @@ ifeq ($(TYPE),STATIC)
DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR)
else
ifeq ($(TYPE),PLUGIN)
+ DEFINES += -DOPENQ_VERSION=\"$(CURRENT_REVISION)\"
DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR)
endif
endif
@@ -46,16 +50,12 @@ C_SRC = \
qq_crypt.c \
file_trans.c \
group.c \
- group_conv.c \
- group_find.c \
- group_free.c \
group_internal.c \
group_im.c \
group_info.c \
group_join.c \
group_opt.c \
- group_search.c \
- header_info.c \
+ qq_define.c \
im.c \
packet_parse.c \
qq.c \
@@ -64,7 +64,6 @@ C_SRC = \
qq_process.c \
qq_trans.c \
send_file.c \
- sys_msg.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 40071df7cc..1e53b10089 100644
--- a/libpurple/protocols/qq/buddy_info.c
+++ b/libpurple/protocols/qq/buddy_info.c
@@ -32,33 +32,11 @@
#include "buddy_list.h"
#include "buddy_info.h"
#include "char_conv.h"
-#include "header_info.h"
+#include "im.h"
+#include "qq_define.h"
#include "qq_base.h"
#include "qq_network.h"
-#define QQ_PRIMARY_INFORMATION _("Primary Information")
-#define QQ_ADDITIONAL_INFORMATION _("Additional Information")
-#define QQ_INTRO _("Personal Introduction")
-#define QQ_NUMBER _("QQ Number")
-#define QQ_NICKNAME _("Nickname")
-#define QQ_NAME _("Name")
-#define QQ_AGE _("Age")
-#define QQ_GENDER _("Gender")
-#define QQ_COUNTRY _("Country/Region")
-#define QQ_PROVINCE _("Province/State")
-#define QQ_CITY _("City")
-#define QQ_HOROSCOPE _("Horoscope Symbol")
-#define QQ_OCCUPATION _("Occupation")
-#define QQ_ZODIAC _("Zodiac Sign")
-#define QQ_BLOOD _("Blood Type")
-#define QQ_COLLEGE _("College")
-#define QQ_EMAIL _("Email")
-#define QQ_ADDRESS _("Address")
-#define QQ_ZIPCODE _("Zipcode")
-#define QQ_CELL _("Cellphone Number")
-#define QQ_TELEPHONE _("Phone Number")
-#define QQ_HOMEPAGE _("Homepage")
-
#define QQ_HOROSCOPE_SIZE 13
static const gchar *horoscope_names[] = {
"-", N_("Aquarius"), N_("Pisces"), N_("Aries"), N_("Taurus"),
@@ -78,223 +56,171 @@ static const gchar *blood_types[] = {
"-", "A", "B", "O", "AB", N_("Other")
};
-#define QQ_GENDER_SIZE 2
+#define QQ_PUBLISH_SIZE 3
+static const gchar *publish_types[] = {
+ N_("Visible"), N_("Firend Only"), N_("Private")
+};
+
+#define QQ_GENDER_SIZE 3
static const gchar *genders[] = {
+ N_("Private"),
N_("Male"),
- N_("Female")
+ N_("Female"),
};
-#define QQ_CONTACT_FIELDS 37
-#define QQ_FACES 100
+static const gchar *genders_zh[] = {
+ N_("-"),
+ N_("\xc4\xd0"),
+ N_("\xc5\xae"),
+};
-/* There is no user id stored in the reply packet for information query
- * we have to manually store the query, so that we know the query source */
-typedef struct _qq_info_query {
- guint32 uid;
- gboolean show_window;
- 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 */
-typedef struct _modify_info_data {
- PurpleConnection *gc;
- contact_info *info;
-} modify_info_data;
+#define QQ_FACES 134
+#define QQ_ICON_PREFIX "qq_"
+#define QQ_ICON_SUFFIX ".png"
+
+enum {
+ QQ_INFO_UID = 0, QQ_INFO_NICK, QQ_INFO_COUNTRY, QQ_INFO_PROVINCE, QQ_INFO_ZIPCODE,
+ QQ_INFO_ADDR, QQ_INFO_TEL, QQ_INFO_AGE, QQ_INFO_GENDER, QQ_INFO_NAME, QQ_INFO_EMAIL,
+ QQ_INFO_PG_SN, QQ_INFO_PG_NUM, QQ_INFO_PG_SP, QQ_INFO_PG_BASE_NUM, QQ_INFO_PG_TYPE,
+ QQ_INFO_OCCU, QQ_INFO_HOME_PAGE, QQ_INFO_AUTH_TYPE, QQ_INFO_UNKNOW1, QQ_INFO_UNKNOW2,
+ QQ_INFO_FACE, QQ_INFO_MOBILE, QQ_INFO_MOBILE_TYPE, QQ_INFO_INTRO, QQ_INFO_CITY,
+ QQ_INFO_UNKNOW3, QQ_INFO_UNKNOW4, QQ_INFO_UNKNOW5,
+ QQ_INFO_IS_PUB_MOBILE, QQ_INFO_IS_PUB_CONTACT, QQ_INFO_COLLEGE, QQ_INFO_HOROSCOPE,
+ QQ_INFO_ZODIAC, QQ_INFO_BLOOD, QQ_INFO_SHOW, QQ_INFO_UNKNOW6,
+ QQ_INFO_LAST_2007, QQ_INFO_LAST,
+};
-/* return -1 as a sentinel */
-static gint choice_index(const gchar *value, const gchar **choice, gint choice_size)
-{
- gint len, i;
+enum {
+ QQ_FIELD_UNUSED = 0, QQ_FIELD_BASE, QQ_FIELD_EXT, QQ_FIELD_CONTACT, QQ_FIELD_ADDR
+};
- len = strlen(value);
- if (len > 3 || len == 0) return -1;
- for (i = 0; i < len; i++) {
- if (!g_ascii_isdigit(value[i]))
- return -1;
- }
- i = strtol(value, NULL, 10);
- if (i >= choice_size)
- return -1;
+enum {
+ QQ_FIELD_LABEL = 0, QQ_FIELD_STRING, QQ_FIELD_MULTI, QQ_FIELD_BOOL, QQ_FIELD_CHOICE,
+};
- return i;
-}
+typedef struct {
+ int iclass;
+ int type;
+ char *id;
+ char *text;
+ const gchar **choice;
+ int choice_size;
+} QQ_FIELD_INFO;
+
+static const QQ_FIELD_INFO field_infos[] = {
+ { QQ_FIELD_BASE, QQ_FIELD_STRING, "uid", N_("QQ Number"), NULL, 0 },
+ { QQ_FIELD_BASE, QQ_FIELD_STRING, "nick", N_("Nickname"), NULL, 0 },
+ { QQ_FIELD_ADDR, QQ_FIELD_STRING, "country", N_("Country/Region"), NULL, 0 },
+ { QQ_FIELD_ADDR, QQ_FIELD_STRING, "province", N_("Province/State"), NULL, 0 },
+ { QQ_FIELD_ADDR, QQ_FIELD_STRING, "zipcode", N_("Zipcode"), NULL, 0 },
+ { QQ_FIELD_ADDR, QQ_FIELD_STRING, "address", N_("Address"), NULL, 0 },
+ { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "tel", N_("Phone Number"), NULL, 0 },
+ { QQ_FIELD_BASE, QQ_FIELD_STRING, "age", N_("Age"), NULL, 0 },
+ { QQ_FIELD_BASE, QQ_FIELD_CHOICE, "gender", N_("Gender"), genders, QQ_GENDER_SIZE },
+ { QQ_FIELD_BASE, QQ_FIELD_STRING, "name", N_("Name"), NULL, 0 },
+ { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "email", N_("Email"), NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_sn", "Pager Serial Num", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_num", "Pager Num", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_sp", "Pager Serivce Provider", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_sta", "Pager Station Num", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "pg_type", "Pager Type", NULL, 0 },
+ { QQ_FIELD_BASE, QQ_FIELD_STRING, "occupation", N_("Occupation"), NULL, 0 },
+ { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "homepage", N_("Homepage"), NULL, 0 },
+ { QQ_FIELD_BASE, QQ_FIELD_BOOL, "auth", N_("Authorize adding"), NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow1", "Unknow1", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow2", "Unknow2", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "face", "Face", NULL, 0 },
+ { QQ_FIELD_CONTACT, QQ_FIELD_STRING, "mobile", N_("Cellphone Number"), NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "mobile_type","Cellphone Type", NULL, 0 },
+ { QQ_FIELD_BASE, QQ_FIELD_MULTI, "intro", N_("Personal Introduction"), NULL, 0 },
+ { QQ_FIELD_ADDR, QQ_FIELD_STRING, "city", N_("City/Area"), NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow3", "Unknow3", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow4", "Unknow4", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow5", "Unknow5", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_CHOICE, "pub_mobile", N_("Publish Mobile"), publish_types, QQ_PUBLISH_SIZE },
+ { QQ_FIELD_CONTACT, QQ_FIELD_CHOICE, "pub_contact", N_("Publish Contact"), publish_types, QQ_PUBLISH_SIZE },
+ { QQ_FIELD_EXT, QQ_FIELD_STRING, "college", N_("College"), NULL, 0 },
+ { QQ_FIELD_EXT, QQ_FIELD_CHOICE, "horoscope", N_("Horoscope"), horoscope_names, QQ_HOROSCOPE_SIZE },
+ { QQ_FIELD_EXT, QQ_FIELD_CHOICE, "zodiac", N_("Zodiac"), zodiac_names, QQ_ZODIAC_SIZE },
+ { QQ_FIELD_EXT, QQ_FIELD_CHOICE, "blood", N_("Blood"), blood_types, QQ_BLOOD_SIZE },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "qq_show", "QQ Show", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "unknow6", "Unknow6", NULL, 0 },
+ { QQ_FIELD_UNUSED, QQ_FIELD_STRING, "LAST_2005", "LAST_2005", NULL, 0 }
+};
-/* return should be freed */
-static gchar *field_value(const gchar *field, const gchar **choice, gint choice_size)
+typedef struct _modify_info_request {
+ PurpleConnection *gc;
+ int iclass;
+ gchar **segments;
+} modify_info_request;
+
+#ifdef DEBUG
+static void info_debug(gchar **segments)
{
- gint index, len;
-
- len = strlen(field);
- if (len == 0) {
- return NULL;
- } else if (choice != NULL) {
- /* some choice fields are also customizable */
- index = choice_index(field, choice, choice_size);
- if (index == -1) {
- if (strcmp(field, "-") != 0) {
- return qq_to_utf8(field, QQ_CHARSET_DEFAULT);
- } else {
- return NULL;
- }
- /* else ASCIIized index */
- } else {
- if (strcmp(choice[index], "-") != 0)
- return g_strdup(choice[index]);
- else
- return NULL;
- }
- } else {
- if (strcmp(field, "-") != 0) {
- return qq_to_utf8(field, QQ_CHARSET_DEFAULT);
- } else {
- return NULL;
+#if 0
+ int index;
+ gchar *utf8_str;
+ for (index = 0; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+ if (field_infos[index].type == QQ_FIELD_STRING
+ || field_infos[index].type == QQ_FIELD_LABEL
+ || field_infos[index].type == QQ_FIELD_MULTI
+ || index == QQ_INFO_GENDER) {
+ utf8_str = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT);
+ purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index].text, utf8_str);
+ g_free(utf8_str);
+ continue;
}
+ purple_debug_info("QQ_BUDDY_INFO", "%s: %s\n", field_infos[index].text, segments[index]);
}
+#endif
}
+#endif
-static gboolean append_field_value(PurpleNotifyUserInfo *user_info, const gchar *field,
- const gchar *title, const gchar **choice, gint choice_size)
+static void info_display_only(PurpleConnection *gc, gchar **segments)
{
- gchar *value = field_value(field, choice, choice_size);
-
- if (value != NULL) {
- purple_notify_user_info_add_pair(user_info, title, value);
- g_free(value);
+ PurpleNotifyUserInfo *user_info;
+ gchar *utf8_value;
+ int index;
+ int choice_num;
- return TRUE;
- }
+ user_info = purple_notify_user_info_new();
- return FALSE;
-}
-
-static PurpleNotifyUserInfo *
- info_to_notify_user_info(const contact_info *info)
-{
- PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
- const gchar *intro;
- gboolean has_extra_info = FALSE;
-
- purple_notify_user_info_add_pair(user_info, QQ_NUMBER, info->uid);
-
- append_field_value(user_info, info->nick, QQ_NICKNAME, NULL, 0);
- append_field_value(user_info, info->name, QQ_NAME, NULL, 0);
- append_field_value(user_info, info->age, QQ_AGE, NULL, 0);
- append_field_value(user_info, info->gender, QQ_GENDER, genders, QQ_GENDER_SIZE);
- append_field_value(user_info, info->country, QQ_COUNTRY, NULL, 0);
- append_field_value(user_info, info->province, QQ_PROVINCE, NULL, 0);
- append_field_value(user_info, info->city, QQ_CITY, NULL, 0);
-
- purple_notify_user_info_add_section_header(user_info, QQ_ADDITIONAL_INFORMATION);
-
- has_extra_info |= append_field_value(user_info, info->horoscope, QQ_HOROSCOPE, horoscope_names, QQ_HOROSCOPE_SIZE);
- has_extra_info |= append_field_value(user_info, info->occupation, QQ_OCCUPATION, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->zodiac, QQ_ZODIAC, zodiac_names, QQ_ZODIAC_SIZE);
- has_extra_info |= append_field_value(user_info, info->blood, QQ_BLOOD, blood_types, QQ_BLOOD_SIZE);
- has_extra_info |= append_field_value(user_info, info->college, QQ_COLLEGE, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->email, QQ_EMAIL, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->address, QQ_ADDRESS, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->zipcode, QQ_ZIPCODE, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->hp_num, QQ_CELL, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->tel, QQ_TELEPHONE, NULL, 0);
- has_extra_info |= append_field_value(user_info, info->homepage, QQ_HOMEPAGE, NULL, 0);
-
- if (!has_extra_info)
- purple_notify_user_info_remove_last_item(user_info);
-
- intro = field_value(info->intro, NULL, 0);
- if (intro) {
- purple_notify_user_info_add_pair(user_info, QQ_INTRO, intro);
+ for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+ if (field_infos[index].iclass == QQ_FIELD_UNUSED) {
+ continue;
+ }
+ switch (field_infos[index].type) {
+ case QQ_FIELD_BOOL:
+ purple_notify_user_info_add_pair(user_info, field_infos[index].text,
+ strtol(segments[index], NULL, 10) ? _("True") : _("False"));
+ break;
+ case QQ_FIELD_CHOICE:
+ choice_num = strtol(segments[index], NULL, 10);
+ if (choice_num < 0 || choice_num >= field_infos[index].choice_size) choice_num = 0;
+
+ purple_notify_user_info_add_pair(user_info, field_infos[index].text, field_infos[index].choice[choice_num]);
+ break;
+ case QQ_FIELD_LABEL:
+ case QQ_FIELD_STRING:
+ case QQ_FIELD_MULTI:
+ default:
+ if (strlen(segments[index]) != 0) {
+ utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT);
+ purple_notify_user_info_add_pair(user_info, field_infos[index].text, utf8_value);
+ g_free(utf8_value);
+ }
+ break;
+ }
}
- /* 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);
- */
-
- return user_info;
-}
+ purple_notify_userinfo(gc, segments[0], user_info, NULL, NULL);
-/* send a packet to get detailed information of uid */
-void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window)
-{
- qq_data *qd;
- gchar uid_str[11];
- qq_info_query *query;
-
- g_return_if_fail(uid != 0);
-
- qd = (qq_data *) gc->proto_data;
- g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
- qq_send_cmd(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) uid_str, strlen(uid_str));
-
- query = g_new0(qq_info_query, 1);
- query->uid = uid;
- query->show_window = show_window;
- query->modify_info = FALSE;
- qd->info_query = g_list_append(qd->info_query, query);
+ purple_notify_user_info_destroy(user_info);
+ g_strfreev(segments);
}
void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
- gint update_class, guint32 ship32)
+ gint update_class, int action)
{
qq_data *qd;
gchar raw_data[16] = {0};
@@ -304,418 +230,238 @@ void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
qd = (qq_data *) gc->proto_data;
g_snprintf(raw_data, sizeof(raw_data), "%d", uid);
qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDY_INFO, (guint8 *) raw_data, strlen(raw_data),
- update_class, ship32);
-}
-
-/* set up the fields requesting personal information and send a get_info packet
- * for myself */
-void qq_prepare_modify_info(PurpleConnection *gc)
-{
- qq_data *qd;
- GList *ql;
- qq_info_query *query;
-
- qd = (qq_data *) gc->proto_data;
- qq_send_packet_get_info(gc, qd->uid, FALSE);
- /* traverse backwards so we get the most recent info_query */
- for (ql = g_list_last(qd->info_query); ql != NULL; ql = g_list_previous(ql)) {
- query = ql->data;
- if (query->uid == qd->uid)
- query->modify_info = TRUE;
- }
+ update_class, action);
}
/* send packet to modify personal information */
-static void qq_send_packet_modify_info(PurpleConnection *gc, contact_info *info)
+static void request_change_info(PurpleConnection *gc, gchar **segments)
{
gint bytes = 0;
guint8 raw_data[MAX_PACKET_SIZE - 128] = {0};
guint8 bar;
+ gchar *join;
- g_return_if_fail(info != NULL);
+ g_return_if_fail(segments != NULL);
bar = 0x1f;
bytes += qq_put8(raw_data + bytes, 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]));
- }
- */
- /* 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));
+ join = g_strjoinv("\x1f", segments + 1);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)join, strlen(join));
+ g_free(join);
bytes += qq_put8(raw_data + bytes, bar);
+ /* qq_show_packet("request_modify_info", raw_data, bytes); */
qq_send_cmd(gc, QQ_CMD_UPDATE_INFO, raw_data, bytes);
-
-}
-
-static void modify_info_cancel_cb(modify_info_data *mid)
-{
- qq_data *qd;
-
- qd = (qq_data *) mid->gc->proto_data;
- qd->modifying_info = FALSE;
-
- g_strfreev((gchar **) mid->info);
- g_free(mid);
}
-static gchar *parse_field(PurpleRequestField *field, gboolean choice)
+static void info_modify_cancel_cb(modify_info_request *info_request)
{
- gchar *value;
-
- if (choice) {
- value = g_strdup_printf("%d", purple_request_field_choice_get_value(field));
- } else {
- value = (gchar *) purple_request_field_string_get_value(field);
- if (value == NULL)
- value = g_strdup("-");
- else
- value = utf8_to_qq(value, QQ_CHARSET_DEFAULT);
- }
-
- return value;
+ g_strfreev(info_request->segments);
+ g_free(info_request);
}
/* parse fields and send info packet */
-static void modify_info_ok_cb(modify_info_data *mid, PurpleRequestFields *fields)
+static void info_modify_ok_cb(modify_info_request *info_request, PurpleRequestFields *fields)
{
PurpleConnection *gc;
qq_data *qd;
- GList *groups;
- contact_info *info;
+ gchar **segments;
+ int index;
+ const char *utf8_str;
+ gchar *value;
+ int choice_num;
- gc = mid->gc;
+ gc = info_request->gc;
+ g_return_if_fail(gc != NULL && info_request->gc);
qd = (qq_data *) gc->proto_data;
- qd->modifying_info = FALSE;
-
- info = mid->info;
-
- groups = purple_request_fields_get_groups(fields);
- while (groups != NULL) {
- PurpleRequestFieldGroup *group = groups->data;
- const char *g_name = purple_request_field_group_get_title(group);
- GList *fields = purple_request_field_group_get_fields(group);
+ segments = info_request->segments;
+ g_return_if_fail(segments != NULL);
- if (g_name == NULL)
+ for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+ if (field_infos[index].iclass == QQ_FIELD_UNUSED) {
continue;
-
- while (fields != NULL) {
- PurpleRequestField *field = fields->data;
- const char *f_id = purple_request_field_get_id(field);
-
- if (!strcmp(QQ_PRIMARY_INFORMATION, g_name)) {
-
- if (!strcmp(f_id, "uid"))
- info->uid = parse_field(field, FALSE);
- else if (!strcmp(f_id, "nick"))
- info->nick = parse_field(field, FALSE);
- else if (!strcmp(f_id, "name"))
- info->name = parse_field(field, FALSE);
- else if (!strcmp(f_id, "age"))
- info->age = parse_field(field, FALSE);
- else if (!strcmp(f_id, "gender"))
- info->gender = parse_field(field, TRUE);
- else if (!strcmp(f_id, "country"))
- info->country = parse_field(field, FALSE);
- else if (!strcmp(f_id, "province"))
- info->province = parse_field(field, FALSE);
- else if (!strcmp(f_id, "city"))
- info->city = parse_field(field, FALSE);
-
- } else if (!strcmp(QQ_ADDITIONAL_INFORMATION, g_name)) {
-
- if (!strcmp(f_id, "horoscope"))
- info->horoscope = parse_field(field, TRUE);
- else if (!strcmp(f_id, "occupation"))
- info->occupation = parse_field(field, FALSE);
- else if (!strcmp(f_id, "zodiac"))
- info->zodiac = parse_field(field, TRUE);
- else if (!strcmp(f_id, "blood"))
- info->blood = parse_field(field, TRUE);
- else if (!strcmp(f_id, "college"))
- info->college = parse_field(field, FALSE);
- else if (!strcmp(f_id, "email"))
- info->email = parse_field(field, FALSE);
- else if (!strcmp(f_id, "address"))
- info->address = parse_field(field, FALSE);
- else if (!strcmp(f_id, "zipcode"))
- info->zipcode = parse_field(field, FALSE);
- else if (!strcmp(f_id, "hp_num"))
- info->hp_num = parse_field(field, FALSE);
- else if (!strcmp(f_id, "tel"))
- info->tel = parse_field(field, FALSE);
- else if (!strcmp(f_id, "homepage"))
- info->homepage = parse_field(field, FALSE);
-
- } else if (!strcmp(QQ_INTRO, g_name)) {
-
- if (!strcmp(f_id, "intro"))
- info->intro = parse_field(field, FALSE);
-
- }
-
- fields = fields->next;
}
-
- groups = groups->next;
+ if (!purple_request_fields_exists(fields, field_infos[index].id)) {
+ continue;
+ }
+ switch (field_infos[index].type) {
+ case QQ_FIELD_BOOL:
+ value = purple_request_fields_get_bool(fields, field_infos[index].id)
+ ? g_strdup("1") : g_strdup("0");
+ g_free(segments[index]);
+ segments[index] = value;
+ break;
+ case QQ_FIELD_CHOICE:
+ choice_num = purple_request_fields_get_choice(fields, field_infos[index].id);
+ if (choice_num < 0 || choice_num >= field_infos[index].choice_size) choice_num = 0;
+
+ if (index == QQ_INFO_GENDER) {
+ /* QQ Server only recept gender in Chinese */
+ value = g_strdup(genders_zh[choice_num]);
+ } else {
+ value = g_strdup_printf("%d", choice_num);
+ }
+ g_free(segments[index]);
+ segments[index] = value;
+ break;
+ case QQ_FIELD_LABEL:
+ case QQ_FIELD_STRING:
+ case QQ_FIELD_MULTI:
+ default:
+ utf8_str = purple_request_fields_get_string(fields, field_infos[index].id);
+ if (utf8_str == NULL) {
+ value = g_strdup("-");
+ } else {
+ value = utf8_to_qq(utf8_str, QQ_CHARSET_DEFAULT);
+ if (value == NULL) value = g_strdup("-");
+ }
+ g_free(segments[index]);
+ segments[index] = value;
+ break;
+ }
}
+ request_change_info(gc, segments);
- /* 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);
-}
-
-static PurpleRequestFieldGroup *setup_field_group(PurpleRequestFields *fields, const gchar *title)
-{
- PurpleRequestFieldGroup *group;
-
- group = purple_request_field_group_new(title);
- purple_request_fields_add_group(fields, group);
-
- return group;
+ g_strfreev(segments);
+ g_free(info_request);
}
-static void add_string_field_to_group(PurpleRequestFieldGroup *group,
- const gchar *id, const gchar *title, const gchar *value)
+static void field_request_new(PurpleRequestFieldGroup *group, gint index, gchar **segments)
{
PurpleRequestField *field;
gchar *utf8_value;
-
- utf8_value = qq_to_utf8(value, QQ_CHARSET_DEFAULT);
- field = purple_request_field_string_new(id, title, utf8_value, FALSE);
- purple_request_field_group_add_field(group, field);
- g_free(utf8_value);
-}
-
-static void add_choice_field_to_group(PurpleRequestFieldGroup *group,
- const gchar *id, const gchar *title, const gchar *value,
- const gchar **choice, gint choice_size)
-{
- PurpleRequestField *field;
- gint i, index;
-
- index = choice_index(value, choice, choice_size);
- field = purple_request_field_choice_new(id, title, index);
- for (i = 0; i < choice_size; i++)
- purple_request_field_choice_add(field, choice[i]);
- purple_request_field_group_add_field(group, field);
+ int choice_num;
+ int i;
+
+ g_return_if_fail(index >=0 && segments[index] != NULL && index < QQ_INFO_LAST);
+
+ switch (field_infos[index].type) {
+ case QQ_FIELD_STRING:
+ case QQ_FIELD_MULTI:
+ utf8_value = qq_to_utf8(segments[index], QQ_CHARSET_DEFAULT);
+ if (field_infos[index].type == QQ_FIELD_STRING) {
+ field = purple_request_field_string_new(
+ field_infos[index].id, field_infos[index].text, utf8_value, FALSE);
+ } else {
+ field = purple_request_field_string_new(
+ field_infos[index].id, field_infos[index].text, utf8_value, TRUE);
+ }
+ purple_request_field_group_add_field(group, field);
+ g_free(utf8_value);
+ break;
+ case QQ_FIELD_BOOL:
+ field = purple_request_field_bool_new(
+ field_infos[index].id, field_infos[index].text,
+ strtol(segments[index], NULL, 10) ? TRUE : FALSE);
+ purple_request_field_group_add_field(group, field);
+ break;
+ case QQ_FIELD_CHOICE:
+ choice_num = strtol(segments[index], NULL, 10);
+ if (choice_num < 0 || choice_num >= field_infos[index].choice_size) choice_num = 0;
+
+ if (index == QQ_INFO_GENDER && strlen(segments[index]) != 0) {
+ for (i = 0; i < QQ_GENDER_SIZE; i++) {
+ if (strcmp(segments[index], genders_zh[i]) == 0) {
+ choice_num = i;
+ }
+ }
+ }
+ field = purple_request_field_choice_new(
+ field_infos[index].id, field_infos[index].text, choice_num);
+ for (i = 0; i < field_infos[index].choice_size; i++) {
+ purple_request_field_choice_add(field, field_infos[index].choice[i]);
+ }
+ purple_request_field_group_add_field(group, field);
+ break;
+ case QQ_FIELD_LABEL:
+ default:
+ field = purple_request_field_label_new(field_infos[index].id, segments[index]);
+ purple_request_field_group_add_field(group, field);
+ break;
+ }
}
-/* take the info returned by a get_info packet for myself and set up a request form */
-static void create_modify_info_dialogue(PurpleConnection *gc, const contact_info *info)
+static void info_modify_dialogue(PurpleConnection *gc, gchar **segments, int iclass)
{
qq_data *qd;
PurpleRequestFieldGroup *group;
PurpleRequestFields *fields;
- PurpleRequestField *field;
- modify_info_data *mid;
+ modify_info_request *info_request;
+ gchar *utf8_title, *utf8_prim;
+ int index;
- /* so we only have one dialog open at a time */
qd = (qq_data *) gc->proto_data;
- if (!qd->modifying_info) {
- qd->modifying_info = TRUE;
-
- fields = purple_request_fields_new();
-
- group = setup_field_group(fields, QQ_PRIMARY_INFORMATION);
- field = purple_request_field_string_new("uid", QQ_NUMBER, info->uid, FALSE);
- purple_request_field_group_add_field(group, field);
- purple_request_field_string_set_editable(field, FALSE);
- add_string_field_to_group(group, "nick", QQ_NICKNAME, info->nick);
- add_string_field_to_group(group, "name", QQ_NAME, info->name);
- add_string_field_to_group(group, "age", QQ_AGE, info->age);
- add_choice_field_to_group(group, "gender", QQ_GENDER, info->gender, genders, QQ_GENDER_SIZE);
- add_string_field_to_group(group, "country", QQ_COUNTRY, info->country);
- add_string_field_to_group(group, "province", QQ_PROVINCE, info->province);
- add_string_field_to_group(group, "city", QQ_CITY, info->city);
-
- group = setup_field_group(fields, QQ_ADDITIONAL_INFORMATION);
- add_choice_field_to_group(group, "horoscope", QQ_HOROSCOPE, info->horoscope, horoscope_names, QQ_HOROSCOPE_SIZE);
- add_string_field_to_group(group, "occupation", QQ_OCCUPATION, info->occupation);
- add_choice_field_to_group(group, "zodiac", QQ_ZODIAC, info->zodiac, zodiac_names, QQ_ZODIAC_SIZE);
- add_choice_field_to_group(group, "blood", QQ_BLOOD, info->blood, blood_types, QQ_BLOOD_SIZE);
- add_string_field_to_group(group, "college", QQ_COLLEGE, info->college);
- add_string_field_to_group(group, "email", QQ_EMAIL, info->email);
- add_string_field_to_group(group, "address", QQ_ADDRESS, info->address);
- add_string_field_to_group(group, "zipcode", QQ_ZIPCODE, info->zipcode);
- add_string_field_to_group(group, "hp_num", QQ_CELL, info->hp_num);
- add_string_field_to_group(group, "tel", QQ_TELEPHONE, info->tel);
- add_string_field_to_group(group, "homepage", QQ_HOMEPAGE, info->homepage);
-
- group = setup_field_group(fields, QQ_INTRO);
- field = purple_request_field_string_new("intro", QQ_INTRO, info->intro, TRUE);
- purple_request_field_group_add_field(group, field);
-
- /* prepare unmodifiable info */
- mid = g_new0(modify_info_data, 1);
- mid->gc = gc;
- /* QQ_CONTACT_FIELDS+1 so that the array is NULL-terminated and can be g_strfreev()'ed later */
- mid->info = (contact_info *) g_new0(gchar *, QQ_CONTACT_FIELDS+1);
- mid->info->pager_sn = g_strdup(info->pager_sn);
- mid->info->pager_num = g_strdup(info->pager_num);
- mid->info->pager_sp = g_strdup(info->pager_sp);
- mid->info->pager_base_num = g_strdup(info->pager_base_num);
- mid->info->pager_type = g_strdup(info->pager_type);
- mid->info->auth_type = g_strdup(info->auth_type);
- mid->info->unknown1 = g_strdup(info->unknown1);
- mid->info->unknown2 = g_strdup(info->unknown2);
- mid->info->face = g_strdup(info->face);
- mid->info->hp_type = g_strdup(info->hp_type);
- mid->info->unknown3 = g_strdup(info->unknown3);
- mid->info->unknown4 = g_strdup(info->unknown4);
- mid->info->unknown5 = g_strdup(info->unknown5);
- /* TODO stop hiding these 2 */
- mid->info->is_open_hp = g_strdup(info->is_open_hp);
- mid->info->is_open_contact = g_strdup(info->is_open_contact);
- mid->info->qq_show = g_strdup(info->qq_show);
- mid->info->unknown6 = g_strdup(info->unknown6);
-
- purple_request_fields(gc, _("Modify information"),
- _("Modify information"), NULL, fields,
- _("Update information"), G_CALLBACK(modify_info_ok_cb),
- _("Cancel"), G_CALLBACK(modify_info_cancel_cb),
- purple_connection_get_account(gc), NULL, NULL,
- mid);
+ /* Keep one dialog once a time */
+ purple_request_close_with_handle(gc);
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ for (index = 1; segments[index] != NULL && index < QQ_INFO_LAST; index++) {
+ if (field_infos[index].iclass != iclass) {
+ continue;
+ }
+ field_request_new(group, index, segments);
}
+
+ switch (iclass) {
+ case QQ_FIELD_CONTACT:
+ utf8_title = g_strdup(_("Modify Contact"));
+ utf8_prim = g_strdup_printf("%s for %s", _("Modify Contact"), segments[0]);
+ case QQ_FIELD_ADDR:
+ utf8_title = g_strdup(_("Modify Address"));
+ utf8_prim = g_strdup_printf("%s for %s", _("Modify Address"), segments[0]);
+ case QQ_FIELD_EXT:
+ utf8_title = g_strdup(_("Modify Extend Information"));
+ utf8_prim = g_strdup_printf("%s for %s", _("Modify Extend Information"), segments[0]);
+ break;
+ case QQ_FIELD_BASE:
+ default:
+ utf8_title = g_strdup(_("Modify Information"));
+ utf8_prim = g_strdup_printf("%s for %s", _("Modify Information"), segments[0]);
+ }
+
+ info_request = g_new0(modify_info_request, 1);
+ info_request->gc = gc;
+ info_request->iclass = iclass;
+ info_request->segments = segments;
+
+ purple_request_fields(gc,
+ utf8_title,
+ utf8_prim,
+ NULL,
+ fields,
+ _("Update"), G_CALLBACK(info_modify_ok_cb),
+ _("Cancel"), G_CALLBACK(info_modify_cancel_cb),
+ purple_connection_get_account(gc), NULL, NULL,
+ info_request);
+
+ g_free(utf8_title);
+ g_free(utf8_prim);
}
/* process the reply of modify_info packet */
-void qq_process_modify_info_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_change_info(PurpleConnection *gc, guint8 *data, gint data_len)
{
qq_data *qd;
-
g_return_if_fail(data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
data[data_len] = '\0';
- if (qd->uid == atoi((gchar *) data)) { /* return should be my uid */
- purple_debug_info("QQ", "Update info ACK OK\n");
- purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), _("Change buddy information."));
+ if (qd->uid != atoi((gchar *) data)) { /* return should be my uid */
+ purple_debug_info("QQ", "Failed Updating info\n");
+ qq_got_attention(gc, _("Failed changing buddy information."));
}
}
-static void _qq_send_packet_modify_face(PurpleConnection *gc, gint face_num)
+static void request_set_buddy_icon(PurpleConnection *gc, gint face_num)
{
PurpleAccount *account = purple_connection_get_account(gc);
PurplePresence *presence = purple_account_get_presence(account);
@@ -732,307 +478,428 @@ static void _qq_send_packet_modify_face(PurpleConnection *gc, gint face_num)
}
qd->my_icon = 3 * (face_num - 1) + offset;
- qd->modifying_face = TRUE;
- qq_send_packet_get_info(gc, qd->uid, FALSE);
+ qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_SET_ICON);
}
-void qq_set_buddy_icon_for_user(PurpleAccount *account, const gchar *who, const gchar *icon_num, const gchar *iconfile)
+void qq_change_icon_cb(PurpleConnection *gc, const char *filepath)
{
- gchar *data;
- gsize len;
+ gchar **segments;
+ const gchar *filename;
+ gint index;
+ gint face;
+ gchar *error;
- if (!g_file_get_contents(iconfile, &data, &len, NULL)) {
- g_return_if_reached();
- } else {
- purple_buddy_icons_set_for_user(account, who, data, len, icon_num);
+ g_return_if_fail(filepath != NULL);
+
+ purple_debug_info("QQ", "Change my icon to %s\n", filepath);
+ segments = g_strsplit_set(filepath, G_DIR_SEPARATOR_S, 0);
+
+#if 0
+ for (index = 0; segments[index] != NULL; index++) {
+ purple_debug_info("QQ", "Split to %s\n", segments[index]);
+ }
+#endif
+
+ index = g_strv_length(segments) - 1;
+ if (index < 0) {
+ g_strfreev(segments);
+ return;
}
+
+ filename = segments[index];
+ index = strcspn (filename, "0123456789");
+ if (index < 0 || index >= strlen(filename)) {
+ error = g_strdup_printf(_("Can not get face number in file name (%s)"), filename);
+ purple_notify_error(gc, _("QQ Buddy"), _("Failed change icon"), error);
+ g_free(error);
+ return;
+ }
+ face = strtol(filename+index, NULL, 10);
+ purple_debug_info("QQ", "Set face to %d\n", face);
+
+ request_set_buddy_icon(gc, face);
+
+ g_strfreev(segments);
}
-/* TODO: custom faces for QQ members and users with level >= 16 */
-void qq_set_my_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img)
+void qq_set_custom_icon(PurpleConnection *gc, PurpleStoredImage *img)
{
- gchar *icon;
- gint icon_num;
- gint icon_len;
PurpleAccount *account = purple_connection_get_account(gc);
const gchar *icon_path = purple_account_get_buddy_icon_path(account);
- const gchar *buddy_icon_dir = qq_buddy_icon_dir();
- gint prefix_len = strlen(QQ_ICON_PREFIX);
- gint suffix_len = strlen(QQ_ICON_SUFFIX);
- gint dir_len = buddy_icon_dir ? strlen(buddy_icon_dir) : 0;
- gchar *errmsg = g_strdup_printf(_("Setting custom faces is not currently supported. Please choose an image from %s."), buddy_icon_dir ? buddy_icon_dir : "(null)");
- gboolean icon_global = purple_account_get_bool(gc->account, "use-global-buddyicon", TRUE);
-
- if (!icon_path)
- icon_path = "";
-
- icon_len = strlen(icon_path) - dir_len - 1 - prefix_len - suffix_len;
-
- /* make sure we're using an appropriate icon */
- if (buddy_icon_dir && !(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)) {
- if (icon_global)
- purple_debug_error("QQ", "%s\n", errmsg);
- else
- purple_notify_error(gc, _("Invalid QQ Face"), errmsg, NULL);
- g_free(errmsg);
- return;
+ gchar **segments;
+ gint index;
+
+ g_return_if_fail(icon_path != NULL);
+
+ /* Fixme:
+ * icon_path is always null
+ * purple_imgstore_get_filename is always new file
+ * QQ buddy may set custom icon if level is over 16 */
+ purple_debug_info("QQ", "Change my icon to %s\n", icon_path);
+ segments = g_strsplit_set(icon_path, G_DIR_SEPARATOR_S, 0);
+ for (index = 0; segments[index] != NULL; index++) {
+ purple_debug_info("QQ", "Split to %s\n", segments[index]);
}
- /* strip everything but number */
- icon = g_strndup(icon_path + dir_len + 1 + prefix_len, icon_len);
- icon_num = strtol(icon, NULL, 10);
- g_free(icon);
- /* ensure face number in proper range */
- if (icon_num > QQ_FACES) {
- if (icon_global)
- purple_debug_error("QQ", "%s\n", errmsg);
- else
- purple_notify_error(gc, _("Invalid QQ Face"), errmsg, NULL);
- g_free(errmsg);
- return;
+
+ g_strfreev(segments);
+}
+
+gchar *qq_get_icon_name(gint face)
+{
+ gint icon;
+ gchar *icon_name;
+
+ icon = face / 3 + 1;
+ if (icon < 1 || icon > QQ_FACES) {
+ icon = 1;
}
- g_free(errmsg);
- /* tell server my icon changed */
- _qq_send_packet_modify_face(gc, icon_num);
- /* display in blist */
- qq_set_buddy_icon_for_user(account, account->username, icon, icon_path);
+
+ icon_name = g_strdup_printf("%s%d%s", QQ_ICON_PREFIX, icon, QQ_ICON_SUFFIX);
+ return icon_name;
}
+gchar *qq_get_icon_path(gchar *icon_name)
+{
+ gchar *icon_path;
+ const gchar *icon_dir;
+#ifdef _WIN32
+ static char *dir = NULL;
+ if (dir == NULL) {
+ dir = g_build_filename(wpurple_install_dir(), "pixmaps",
+ "purple", "buddy_icons", "qq", NULL);
+ }
+#endif
+
+ icon_dir = purple_prefs_get_string("/plugins/prpl/qq/icon_dir");
+ if ( icon_dir == NULL || strlen(icon_dir) == 0) {
+#ifdef _WIN32
+ icon_dir = dir;
+#else
+ icon_dir = QQ_BUDDY_ICON_DIR;
+#endif
+ }
+ icon_path = g_strdup_printf("%s%c%s", icon_dir, G_DIR_SEPARATOR, icon_name);
-static void _qq_update_buddy_icon(PurpleAccount *account, const gchar *name, gint face)
+ return icon_path;
+}
+
+static void update_buddy_icon(PurpleAccount *account, const gchar *who, gint face)
{
PurpleBuddy *buddy;
- gchar *icon_num_str = face_to_icon_str(face);
- const gchar *old_icon_num = NULL;
+ const gchar *icon_name_prev = NULL;
+ gchar *icon_name;
+ gchar *icon_path;
+ gchar *icon_file_content;
+ gsize icon_file_size;
- if ((buddy = purple_find_buddy(account, name)))
- old_icon_num = purple_buddy_icons_get_checksum_for_user(buddy);
+ g_return_if_fail(account != NULL && who != NULL);
- if ((old_icon_num == NULL ||
- strcmp(icon_num_str, old_icon_num)) && (qq_buddy_icon_dir() != NULL))
- {
- gchar *icon_path;
+ purple_debug_info("QQ", "Update %s icon to %d\n", who, face);
- icon_path = g_strconcat(qq_buddy_icon_dir(), G_DIR_SEPARATOR_S,
- QQ_ICON_PREFIX, icon_num_str,
- QQ_ICON_SUFFIX, NULL);
+ icon_name = qq_get_icon_name(face);
+ purple_debug_info("QQ", "icon file name is %s\n", icon_name);
- qq_set_buddy_icon_for_user(account, name, icon_num_str, icon_path);
- g_free(icon_path);
+ if ((buddy = purple_find_buddy(account, who))) {
+ icon_name_prev = purple_buddy_icons_get_checksum_for_user(buddy);
+ if (icon_name_prev != NULL) {
+ purple_debug_info("QQ", "Previous icon is %s\n", icon_name_prev);
+ }
}
- g_free(icon_num_str);
+ if (icon_name_prev != NULL && !strcmp(icon_name, icon_name_prev)) {
+ purple_debug_info("QQ", "Icon is not changed\n");
+ g_free(icon_name);
+ return;
+ }
+
+ icon_path = qq_get_icon_path(icon_name);
+ if (icon_path == NULL) {
+ g_free(icon_name);
+ return;
+ }
+
+ if (!g_file_get_contents(icon_path, &icon_file_content, &icon_file_size, NULL)) {
+ purple_debug_error("QQ", "Failed reading icon file %s\n", icon_path);
+ } else {
+ purple_buddy_icons_set_for_user(account, who,
+ icon_file_content, icon_file_size, icon_name);
+ }
+ g_free(icon_name);
+ g_free(icon_path);
}
/* after getting info or modify myself, refresh the buddy list accordingly */
-static void qq_refresh_buddy_and_myself(contact_info *info, PurpleConnection *gc)
+static void update_buddy_info(PurpleConnection *gc, gchar **segments)
{
- PurpleBuddy *b;
+ PurpleBuddy *buddy;
qq_data *qd;
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
+ guint32 uid;
+ gchar *who;
gchar *alias_utf8;
- gchar *purple_name;
PurpleAccount *account = purple_connection_get_account(gc);
qd = (qq_data *) gc->proto_data;
- purple_name = uid_to_purple_name(strtol(info->uid, NULL, 10));
- alias_utf8 = qq_to_utf8(info->nick, QQ_CHARSET_DEFAULT);
- if (qd->uid == strtol(info->uid, NULL, 10)) { /* it is me */
- qd->my_icon = strtol(info->face, NULL, 10);
- if (alias_utf8 != NULL)
+ uid = strtol(segments[QQ_INFO_UID], NULL, 10);
+ who = uid_to_purple_name(uid);
+
+ qq_filter_str(segments[QQ_INFO_NICK]);
+ alias_utf8 = qq_to_utf8(segments[QQ_INFO_NICK], QQ_CHARSET_DEFAULT);
+ if (uid == qd->uid) { /* it is me */
+ purple_debug_info("QQ", "Got my info\n");
+ qd->my_icon = strtol(segments[QQ_INFO_FACE], NULL, 10);
+ if (alias_utf8 != NULL) {
purple_account_set_alias(account, alias_utf8);
+ }
+ /* add me to buddy list */
+ buddy = qq_buddy_find_or_new(gc, uid);
+ } else {
+ buddy = purple_find_buddy(gc->account, who);
+ }
+
+ if (buddy == NULL || buddy->proto_data == NULL) {
+ g_free(who);
+ g_free(alias_utf8);
+ return;
}
+
/* update buddy list (including myself, if myself is the buddy) */
- b = purple_find_buddy(gc->account, purple_name);
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (q_bud != NULL) { /* I have this buddy */
- q_bud->age = strtol(info->age, NULL, 10);
- q_bud->gender = strtol(info->gender, NULL, 10);
- q_bud->face = strtol(info->face, NULL, 10);
- if (alias_utf8 != NULL)
- q_bud->nickname = g_strdup(alias_utf8);
- qq_update_buddy_contact(gc, q_bud);
- _qq_update_buddy_icon(gc->account, purple_name, q_bud->face);
+ bd = (qq_buddy_data *)buddy->proto_data;
+
+ bd->age = strtol(segments[QQ_INFO_AGE], NULL, 10);
+ bd->gender = strtol(segments[QQ_INFO_GENDER], NULL, 10);
+ bd->face = strtol(segments[QQ_INFO_FACE], NULL, 10);
+ if (alias_utf8 != NULL) {
+ if (bd->nickname) g_free(bd->nickname);
+ bd->nickname = g_strdup(alias_utf8);
}
- g_free(purple_name);
+ bd->last_update = time(NULL);
+
+ purple_blist_server_alias_buddy(buddy, bd->nickname);
+
+ /* convert face num from packet (0-299) to local face (1-100) */
+ update_buddy_icon(gc->account, who, bd->face);
+
+ g_free(who);
g_free(alias_utf8);
}
/* process reply to get_info packet */
-void qq_process_get_buddy_info(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_get_buddy_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc)
{
- gchar **segments;
- qq_info_query *query;
qq_data *qd;
- contact_info *info;
- GList *list, *query_list;
- PurpleNotifyUserInfo *user_info;
+ gchar **segments;
+ gint field_count;
+ gchar *icon_name;
g_return_if_fail(data != NULL && data_len != 0);
qd = (qq_data *) gc->proto_data;
- list = query_list = NULL;
- info = NULL;
- if (NULL == (segments = split_data(data, data_len, "\x1e", QQ_CONTACT_FIELDS)))
+ if (qd->client_version >= 2008) {
+ field_count = QQ_INFO_LAST;
+ } else {
+ field_count = QQ_INFO_LAST_2007;
+ }
+ if (NULL == (segments = split_data(data, data_len, "\x1e", field_count)))
return;
- info = (contact_info *) segments;
- if (qd->modifying_face && strtol(info->face, NULL, 10) != qd->my_icon) {
- gchar *icon = g_strdup_printf("%d", qd->my_icon);
- qd->modifying_face = FALSE;
- g_free(info->face);
- info->face = icon;
- qq_send_packet_modify_info(gc, (contact_info *)segments);
- }
+#ifdef DEBUG
+ info_debug(segments);
+#endif
- qq_refresh_buddy_and_myself(info, gc);
-
- query_list = qd->info_query;
- /* ensure we're processing the right query */
- while (query_list) {
- query = (qq_info_query *) query_list->data;
- if (query->uid == atoi(info->uid)) {
- if (query->show_window) {
- user_info = info_to_notify_user_info(info);
- purple_notify_userinfo(gc, info->uid, user_info, NULL, NULL);
- purple_notify_user_info_destroy(user_info);
- } else if (query->modify_info) {
- create_modify_info_dialogue(gc, info);
- }
- qd->info_query = g_list_remove(qd->info_query, qd->info_query->data);
- g_free(query);
- break;
+ if (action == QQ_BUDDY_INFO_SET_ICON) {
+ if (strtol(segments[QQ_INFO_FACE], NULL, 10) != qd->my_icon) {
+ icon_name = g_strdup_printf("%d", qd->my_icon);
+ g_free(segments[QQ_INFO_FACE]);
+ segments[QQ_INFO_FACE] = icon_name;
+
+ /* Update me in buddy list */
+ update_buddy_info(gc, segments);
+ /* send new face to server */
+ request_change_info(gc, segments);
}
- query_list = query_list->next;
+ g_strfreev(segments);
+ return;
}
- g_strfreev(segments);
+ update_buddy_info(gc, segments);
+ switch (action) {
+ case QQ_BUDDY_INFO_DISPLAY:
+ info_display_only(gc, segments);
+ break;
+ case QQ_BUDDY_INFO_SET_ICON:
+ g_return_if_reached();
+ break;
+ case QQ_BUDDY_INFO_MODIFY_BASE:
+ info_modify_dialogue(gc, segments, QQ_FIELD_BASE);
+ break;
+ case QQ_BUDDY_INFO_MODIFY_EXT:
+ info_modify_dialogue(gc, segments, QQ_FIELD_EXT);
+ break;
+ case QQ_BUDDY_INFO_MODIFY_ADDR:
+ info_modify_dialogue(gc, segments, QQ_FIELD_ADDR);
+ break;
+ case QQ_BUDDY_INFO_MODIFY_CONTACT:
+ info_modify_dialogue(gc, segments, QQ_FIELD_CONTACT);
+ break;
+ default:
+ g_strfreev(segments);
+ break;
+ }
+ return;
}
-void qq_info_query_free(qq_data *qd)
+void qq_request_get_level(PurpleConnection *gc, guint32 uid)
{
- gint count;
- qq_info_query *p;
-
- g_return_if_fail(qd != NULL);
+ qq_data *qd = (qq_data *) gc->proto_data;
+ guint8 buf[16] = {0};
+ gint bytes = 0;
- count = 0;
- while (qd->info_query != NULL) {
- p = (qq_info_query *) (qd->info_query->data);
- qd->info_query = g_list_remove(qd->info_query, p);
- g_free(p);
- count++;
- }
- if (count > 0) {
- purple_debug_info("QQ", "%d info queries are freed!\n", count);
+ if (qd->client_version >= 2007) {
+ bytes += qq_put8(buf + bytes, 0x02);
+ } else {
+ bytes += qq_put8(buf + bytes, 0x00);
}
+ bytes += qq_put32(buf + bytes, uid);
+ qq_send_cmd(gc, QQ_CMD_GET_LEVEL, buf, bytes);
}
-void qq_request_get_level(PurpleConnection *gc, guint32 uid)
+void qq_request_get_level_2007(PurpleConnection *gc, guint32 uid)
{
- qq_data *qd = (qq_data *) gc->proto_data;
guint8 buf[16] = {0};
gint bytes = 0;
- bytes += qq_put8(buf + bytes, 0x00);
+ bytes += qq_put8(buf + bytes, 0x08);
bytes += qq_put32(buf + bytes, uid);
-
- qd = (qq_data *) gc->proto_data;
+ bytes += qq_put8(buf + bytes, 0x00);
qq_send_cmd(gc, QQ_CMD_GET_LEVEL, buf, bytes);
}
void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class)
{
- guint8 *buf;
- guint16 size;
- qq_buddy *q_bud;
qq_data *qd = (qq_data *) gc->proto_data;
- GList *node = qd->buddies;
- gint bytes = 0;
+ PurpleBuddy *buddy;
+ qq_buddy_data *bd;
+ guint8 *buf;
+ GSList *buddies, *it;
+ gint bytes;
- if ( qd->buddies == NULL) {
- return;
- }
/* server only reply levels for online buddies */
- size = 4 * g_list_length(qd->buddies) + 1 + 4;
- buf = g_newa(guint8, size);
- bytes += qq_put8(buf + bytes, 0x00);
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
- while (NULL != node) {
- q_bud = (qq_buddy *) node->data;
- if (NULL != q_bud) {
- bytes += qq_put32(buf + bytes, q_bud->uid);
- }
- node = node->next;
+ bytes = 0;
+ bytes += qq_put8(buf + bytes, 0x00);
+ buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+ for (it = buddies; it; it = it->next) {
+ buddy = it->data;
+ if (buddy == NULL) continue;
+ if (buddy->proto_data == NULL) continue;
+ bd = (qq_buddy_data *)buddy->proto_data;
+ if (bd->uid == 0) continue; /* keep me as end of packet*/
+ if (bd->uid == qd->uid) continue;
+ bytes += qq_put32(buf + bytes, bd->uid);
}
-
- /* my id should be the end if included */
bytes += qq_put32(buf + bytes, qd->uid);
- qq_send_cmd_mess(gc, QQ_CMD_GET_LEVEL, buf, size, update_class, 0);
+ qq_send_cmd_mess(gc, QQ_CMD_GET_LEVEL, buf, bytes, update_class, 0);
}
-void qq_process_get_level_reply(guint8 *decr_buf, gint decr_len, PurpleConnection *gc)
+static void process_level(PurpleConnection *gc, guint8 *data, gint data_len)
{
+ gint bytes = 0;
guint32 uid, onlineTime;
guint16 level, timeRemainder;
- gchar *purple_name;
- PurpleBuddy *b;
- qq_buddy *q_bud;
- gint i;
- PurpleAccount *account = purple_connection_get_account(gc);
- qq_data *qd = (qq_data *) gc->proto_data;
- gint bytes = 0;
+ qq_buddy_data *bd;
- decr_len--;
- if (decr_len % 12 != 0) {
- purple_debug_error("QQ",
- "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12);
- decr_len -= (decr_len % 12);
- }
-
- bytes += 1;
- /* this byte seems random */
- /*
- purple_debug_info("QQ", "Byte one of get_level packet: %d\n", buf[0]);
- */
- for (i = 0; i < decr_len; i += 12) {
- 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);
+ while (data_len - bytes >= 12) {
+ bytes += qq_get32(&uid, data + bytes);
+ bytes += qq_get32(&onlineTime, data + bytes);
+ bytes += qq_get16(&level, data + bytes);
+ bytes += qq_get16(&timeRemainder, data + bytes);
purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
uid, onlineTime, level, timeRemainder);
- if (uid == qd->uid) {
- qd->my_level = level;
- purple_debug_warning("QQ", "Got my levels as %d\n", qd->my_level);
- continue;
- }
- purple_name = uid_to_purple_name(uid);
- if (purple_name == NULL) {
+ bd = qq_buddy_data_find(gc, uid);
+ if (bd == NULL) {
+ purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
continue;
}
- b = purple_find_buddy(account, purple_name);
- g_free(purple_name);
+ bd->onlineTime = onlineTime;
+ bd->level = level;
+ bd->timeRemainder = timeRemainder;
+ }
- q_bud = NULL;
- if (b != NULL) {
- q_bud = (qq_buddy *) b->proto_data;
- }
+ if (bytes != data_len) {
+ purple_debug_error("QQ",
+ "Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes);
+ }
+}
- if (q_bud == NULL) {
- purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
- continue;
+static void process_level_2007(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ gint bytes;
+ guint32 uid, onlineTime;
+ guint16 level, timeRemainder;
+ qq_buddy_data *bd;
+ guint16 str_len;
+ gchar *str;
+ gchar *str_utf8;
+
+ bytes = 0;
+ bytes += qq_get32(&uid, data + bytes);
+ bytes += qq_get32(&onlineTime, data + bytes);
+ bytes += qq_get16(&level, data + bytes);
+ bytes += qq_get16(&timeRemainder, data + bytes);
+ purple_debug_info("QQ_LEVEL", "%d, tmOnline: %d, level: %d, tmRemainder: %d\n",
+ uid, onlineTime, level, timeRemainder);
+
+ bd = qq_buddy_data_find(gc, uid);
+ if (bd == NULL) {
+ purple_debug_error("QQ", "Got levels of %d not in my buddy list\n", uid);
+ return;
+ }
+
+ bd->onlineTime = onlineTime;
+ bd->level = level;
+ bd->timeRemainder = timeRemainder;
+
+ /* extend bytes in qq2007*/
+ bytes += 4; /* skip 8 bytes */
+ /* qq_show_packet("Buddies level", data + bytes, data_len - bytes); */
+
+ do {
+ bytes += qq_get16(&str_len, data + bytes);
+ if (str_len <= 0 || bytes + str_len > data_len) {
+ purple_debug_error("QQ",
+ "Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes);
+ break;
}
+ str = g_strndup((gchar *)data + bytes, str_len);
+ bytes += str_len;
+ str_utf8 = qq_to_utf8(str, QQ_CHARSET_DEFAULT);
+ purple_debug_info("QQ", "%s\n", str_utf8);
+ g_free(str_utf8);
+ g_free(str);
+ } while (bytes < data_len);
+}
- q_bud->onlineTime = onlineTime;
- q_bud->level = level;
- q_bud->timeRemainder = timeRemainder;
+void qq_process_get_level_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+ gint bytes;
+ guint8 sub_cmd;
+
+ bytes = 0;
+ bytes += qq_get8(&sub_cmd, data + bytes);
+ switch (sub_cmd) {
+ case 0x08:
+ process_level_2007(gc, data + bytes, data_len - bytes);
+ break;
+ case 0x00:
+ case 0x02:
+ default:
+ process_level(gc, data + bytes, data_len - bytes);
+ break;
}
}
diff --git a/libpurple/protocols/qq/buddy_info.h b/libpurple/protocols/qq/buddy_info.h
index 8fd4edafe1..a3cd40b673 100644
--- a/libpurple/protocols/qq/buddy_info.h
+++ b/libpurple/protocols/qq/buddy_info.h
@@ -64,25 +64,28 @@
#define QQ_BUDDY_GENDER_MM 0x01
#define QQ_BUDDY_GENDER_UNKNOWN 0xff
-#define QQ_ICON_PREFIX "qq_"
-#define QQ_ICON_SUFFIX ".png"
-
enum {
QQ_BUDDY_INFO_UPDATE_ONLY = 0,
QQ_BUDDY_INFO_DISPLAY,
- QQ_BUDDY_INFO_MODIFY,
+ QQ_BUDDY_INFO_SET_ICON,
+ QQ_BUDDY_INFO_MODIFY_BASE,
+ QQ_BUDDY_INFO_MODIFY_EXT,
+ QQ_BUDDY_INFO_MODIFY_ADDR,
+ QQ_BUDDY_INFO_MODIFY_CONTACT,
};
-void qq_send_packet_get_info(PurpleConnection *gc, guint32 uid, gboolean show_window);
+gchar *qq_get_icon_name(gint face);
+gchar *qq_get_icon_path(gchar *icon_name);
+void qq_change_icon_cb(PurpleConnection *gc, const char *filepath);
+
void qq_request_buddy_info(PurpleConnection *gc, guint32 uid,
- gint update_class, guint32 ship32);
-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);
-void qq_prepare_modify_info(PurpleConnection *gc);
-void qq_process_modify_info_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_process_get_buddy_info(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_info_query_free(qq_data *qd);
+ gint update_class, int action);
+void qq_set_custom_icon(PurpleConnection *gc, PurpleStoredImage *img);
+void qq_process_change_info(PurpleConnection *gc, guint8 *data, gint data_len);
+void qq_process_get_buddy_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc);
+
void qq_request_get_level(PurpleConnection *gc, guint32 uid);
+void qq_request_get_level_2007(PurpleConnection *gc, guint32 uid);
void qq_request_get_buddies_level(PurpleConnection *gc, gint update_class);
void qq_process_get_level_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/buddy_list.c b/libpurple/protocols/qq/buddy_list.c
index 8a41970f81..de9c85272f 100644
--- a/libpurple/protocols/qq/buddy_list.c
+++ b/libpurple/protocols/qq/buddy_list.c
@@ -34,10 +34,9 @@
#include "buddy_list.h"
#include "buddy_opt.h"
#include "char_conv.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "qq_base.h"
#include "group.h"
-#include "group_find.h"
#include "group_internal.h"
#include "group_info.h"
@@ -78,16 +77,18 @@ void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint u
bytes += qq_put16(raw_data + bytes, 0x0000);
qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_ONLINE, raw_data, 5, update_class, 0);
- qd->last_get_online = time(NULL);
}
/* position starts with 0x0000,
* server may return a position tag if list is too long for one packet */
-void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class)
+void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class)
{
+ qq_data *qd;
guint8 raw_data[16] = {0};
gint bytes = 0;
+ qd = (qq_data *) gc->proto_data;
+
/* 000-001 starting position, can manually specify */
bytes += qq_put16(raw_data + bytes, position);
/* before Mar 18, 2004, any value can work, and we sent 00
@@ -96,6 +97,9 @@ void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint up
* Now I tested that 00,00,00,00,00,01 work perfectly
* March 22, found the 00,00,00 starts to work as well */
bytes += qq_put8(raw_data + bytes, 0x00);
+ if (qd->client_version >= 2007) {
+ bytes += qq_put16(raw_data + bytes, 0x0000);
+ }
qq_send_cmd_mess(gc, QQ_CMD_GET_BUDDIES_LIST, raw_data, bytes, update_class, 0);
}
@@ -139,7 +143,7 @@ static gint get_buddy_status(qq_buddy_status *bs, guint8 *data)
bytes += qq_get8(&bs->unknown2, data + bytes);
/* 012-012: status */
bytes += qq_get8(&bs->status, data + bytes);
- /* 013-014: client_version */
+ /* 013-014: client tag */
bytes += qq_get16(&bs->unknown3, data + bytes);
/* 015-030: unknown key */
bytes += qq_getdata(&(bs->unknown_key[0]), QQ_KEY_LENGTH, data + bytes);
@@ -152,117 +156,118 @@ static gint get_buddy_status(qq_buddy_status *bs, guint8 *data)
return bytes;
}
-#define QQ_ONLINE_BUDDY_ENTRY_LEN 38
-
/* process the reply packet for get_buddies_online packet */
-guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc)
{
qq_data *qd;
- gint bytes, bytes_buddy;
+ gint bytes, bytes_start;
gint count;
guint8 position;
- PurpleBuddy *b;
- qq_buddy *q_bud;
- qq_buddy_online bo;
- gchar *purple_name;
+ qq_buddy_data *bd;
+ int entry_len = 38;
+
+ qq_buddy_status bs;
+ struct {
+ guint16 unknown1;
+ guint8 ext_flag;
+ guint8 comm_flag;
+ guint16 unknown2;
+ guint8 ending; /* 0x00 */
+ } packet;
g_return_val_if_fail(data != NULL && data_len != 0, -1);
qd = (qq_data *) gc->proto_data;
/* qq_show_packet("Get buddies online reply packet", data, len); */
+ if (qd->client_version >= 2007) entry_len += 4;
bytes = 0;
bytes += qq_get8(&position, data + bytes);
count = 0;
while (bytes < data_len) {
- if (data_len - bytes < QQ_ONLINE_BUDDY_ENTRY_LEN) {
- purple_debug_error("QQ", "[buddies online] only %d, need %d",
- (data_len - bytes), QQ_ONLINE_BUDDY_ENTRY_LEN);
+ if (data_len - bytes < entry_len) {
+ purple_debug_error("QQ", "[buddies online] only %d, need %d\n",
+ (data_len - bytes), entry_len);
break;
}
- memset(&bo, 0 ,sizeof(bo));
+ memset(&bs, 0 ,sizeof(bs));
+ memset(&packet, 0 ,sizeof(packet));
/* set flag */
- bytes_buddy = bytes;
+ bytes_start = bytes;
/* based on one online buddy entry */
/* 000-030 qq_buddy_status */
- bytes += get_buddy_status(&(bo.bs), data + bytes);
+ bytes += get_buddy_status(&bs, data + bytes);
/* 031-032: */
- bytes += qq_get16(&bo.unknown1, data + bytes);
+ bytes += qq_get16(&packet.unknown1, data + bytes);
/* 033-033: ext_flag */
- bytes += qq_get8(&bo.ext_flag, data + bytes);
+ bytes += qq_get8(&packet.ext_flag, data + bytes);
/* 034-034: comm_flag */
- bytes += qq_get8(&bo.comm_flag, data + bytes);
+ bytes += qq_get8(&packet.comm_flag, data + bytes);
/* 035-036: */
- bytes += qq_get16(&bo.unknown2, data + bytes);
+ bytes += qq_get16(&packet.unknown2, data + bytes);
/* 037-037: */
- bytes += qq_get8(&bo.ending, data + bytes); /* 0x00 */
+ bytes += qq_get8(&packet.ending, data + bytes); /* 0x00 */
+ /* skip 4 bytes in qq2007 */
+ if (qd->client_version >= 2007) bytes += 4;
- if (bo.bs.uid == 0 || (bytes - bytes_buddy) != QQ_ONLINE_BUDDY_ENTRY_LEN) {
+ if (bs.uid == 0 || (bytes - bytes_start) != entry_len) {
purple_debug_error("QQ", "uid=0 or entry complete len(%d) != %d",
- (bytes - bytes_buddy), QQ_ONLINE_BUDDY_ENTRY_LEN);
+ (bytes - bytes_start), entry_len);
continue;
} /* check if it is a valid entry */
- if (bo.bs.uid == qd->uid) {
- purple_debug_warning("QQ", "I am in online list %d\n", bo.bs.uid);
- continue;
+ if (bs.uid == qd->uid) {
+ purple_debug_warning("QQ", "I am in online list %d\n", bs.uid);
}
/* update buddy information */
- purple_name = uid_to_purple_name(bo.bs.uid);
- if (purple_name == NULL) {
- purple_debug_error("QQ",
- "Got an online buddy %d, but not find purple name\n", bo.bs.uid);
- continue;
- }
- b = purple_find_buddy(purple_connection_get_account(gc), purple_name);
- g_free(purple_name);
-
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (q_bud == NULL) {
+ bd = qq_buddy_data_find(gc, bs.uid);
+ if (bd == NULL) {
purple_debug_error("QQ",
- "Got an online buddy %d, but not in my buddy list\n", bo.bs.uid);
+ "Got an online buddy %d, but not in my buddy list\n", bs.uid);
continue;
}
- /* we find one and update qq_buddy */
+ /* we find one and update qq_buddy_data */
/*
- if(0 != fe->s->client_version)
- q_bud->client_version = fe->s->client_version;
+ if(0 != fe->s->client_tag)
+ q_bud->client_tag = fe->s->client_tag;
*/
- q_bud->ip.s_addr = bo.bs.ip.s_addr;
- q_bud->port = bo.bs.port;
- q_bud->status = bo.bs.status;
- q_bud->ext_flag = bo.ext_flag;
- q_bud->comm_flag = bo.comm_flag;
- qq_update_buddy_contact(gc, q_bud);
+ if (bd->status != bs.status || bd->comm_flag != packet.comm_flag) {
+ bd->status = bs.status;
+ bd->comm_flag = packet.comm_flag;
+ qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
+ }
+ bd->ip.s_addr = bs.ip.s_addr;
+ bd->port = bs.port;
+ bd->ext_flag = packet.ext_flag;
+ bd->last_update = time(NULL);
count++;
}
if(bytes > data_len) {
purple_debug_error("QQ",
- "qq_process_get_buddies_online_reply: Dangerous error! maybe protocol changed, notify developers!\n");
+ "qq_process_get_buddies_online: Dangerous error! maybe protocol changed, notify developers!\n");
}
purple_debug_info("QQ", "Received %d online buddies, nextposition=%u\n",
- count, (guint) position);
+ count, (guint) position);
return position;
}
/* process reply for get_buddies_list */
-guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc)
{
qq_data *qd;
- qq_buddy *q_bud;
+ qq_buddy_data bd;
gint bytes_expected, count;
gint bytes, buddy_bytes;
+ gint nickname_len;
guint16 position, unknown;
- guint8 pascal_len;
- gchar *name;
- PurpleBuddy *b;
+ PurpleBuddy *buddy;
g_return_val_if_fail(data != NULL && data_len != 0, -1);
@@ -278,60 +283,66 @@ guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleCon
/* the following data is buddy list in this packet */
count = 0;
while (bytes < data_len) {
- q_bud = g_new0(qq_buddy, 1);
+ memset(&bd, 0, sizeof(bd));
/* set flag */
buddy_bytes = bytes;
/* 000-003: uid */
- bytes += qq_get32(&q_bud->uid, data + bytes);
+ bytes += qq_get32(&bd.uid, data + bytes);
/* 004-005: icon index (1-255) */
- bytes += qq_get16(&q_bud->face, data + bytes);
+ bytes += qq_get16(&bd.face, data + bytes);
/* 006-006: age */
- bytes += qq_get8(&q_bud->age, data + bytes);
+ bytes += qq_get8(&bd.age, data + bytes);
/* 007-007: gender */
- bytes += qq_get8(&q_bud->gender, data + bytes);
+ bytes += qq_get8(&bd.gender, data + bytes);
- pascal_len = convert_as_pascal_string(data + bytes, &q_bud->nickname, QQ_CHARSET_DEFAULT);
- bytes += pascal_len;
- qq_filter_str(q_bud->nickname);
+ nickname_len = qq_get_vstr(&bd.nickname, QQ_CHARSET_DEFAULT, data + bytes);
+ bytes += nickname_len;
+ qq_filter_str(bd.nickname);
+ /* Fixme: merge following as 32bit flag */
bytes += qq_get16(&unknown, data + bytes);
- bytes += qq_get8(&q_bud->ext_flag, data + bytes);
- bytes += qq_get8(&q_bud->comm_flag, data + bytes);
+ bytes += qq_get8(&bd.ext_flag, data + bytes);
+ bytes += qq_get8(&bd.comm_flag, data + bytes);
- bytes_expected = 12 + pascal_len;
+ if (qd->client_version >= 2007) {
+ bytes += 4; /* skip 4 bytes */
+ bytes_expected = 16 + nickname_len;
+ } else {
+ bytes_expected = 12 + nickname_len;
+ }
- if (q_bud->uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
+ if (bd.uid == 0 || (bytes - buddy_bytes) != bytes_expected) {
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);
+ g_free(bd.nickname);
continue;
} else {
count++;
}
#if 1
- purple_debug_info("QQ",
- "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
- q_bud->uid, q_bud->ext_flag, q_bud->comm_flag, q_bud->nickname);
+ purple_debug_info("QQ", "buddy [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
+ bd.uid, bd.ext_flag, bd.comm_flag, bd.nickname);
#endif
- name = uid_to_purple_name(q_bud->uid);
- b = purple_find_buddy(gc->account, name);
- g_free(name);
-
- if (b == NULL) {
- b = qq_add_buddy_by_recv_packet(gc, q_bud->uid, TRUE, FALSE);
+ buddy = qq_buddy_find_or_new(gc, bd.uid);
+ if (buddy == NULL || buddy->proto_data == NULL) {
+ g_free(bd.nickname);
+ continue;
}
+ purple_blist_server_alias_buddy(buddy, bd.nickname);
+ bd.last_update = time(NULL);
+ qq_update_buddy_status(gc, bd.uid, bd.status, bd.comm_flag);
- b->proto_data = q_bud;
- qd->buddies = g_list_append(qd->buddies, q_bud);
- qq_update_buddy_contact(gc, q_bud);
+ g_memmove(buddy->proto_data, &bd, sizeof(qq_buddy_data));
+ /* nickname has been copy to buddy_data do not free
+ g_free(bd.nickname);
+ */
}
if(bytes > data_len) {
purple_debug_error("QQ",
- "qq_process_get_buddies_list_reply: Dangerous error! maybe protocol changed, notify developers!");
+ "qq_process_get_buddies: Dangerous error! maybe protocol changed, notify developers!");
}
purple_debug_info("QQ", "Received %d buddies, nextposition=%u\n",
@@ -347,8 +358,8 @@ guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConn
guint8 sub_cmd, reply_code;
guint32 unknown, position;
guint32 uid;
- guint8 type, groupid;
- qq_group *group;
+ guint8 type;
+ qq_room_data *rmd;
g_return_val_if_fail(data != NULL && data_len != 0, -1);
@@ -360,7 +371,7 @@ guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConn
bytes += qq_get8(&reply_code, data + bytes);
if(0 != reply_code) {
- purple_debug_warning("QQ", "qq_process_get_buddies_and_rooms, %d", reply_code);
+ purple_debug_warning("QQ", "qq_process_get_buddies_and_rooms, %d\n", reply_code);
}
bytes += qq_get32(&unknown, data + bytes);
@@ -373,29 +384,23 @@ guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConn
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_info("QQ", "groupid: %i\n", groupid);
- groupid >>= 2;
- */
+ /* 05: skip unknow 0x00 */
+ bytes += 1;
if (uid == 0 || (type != 0x1 && type != 0x4)) {
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_request_get_buddies_list */
+ * qq_request_get_buddies */
++i;
} else { /* a group */
- group = qq_room_search_id(gc, uid);
- if(group == NULL) {
- purple_debug_info("QQ",
- "Not find room id %d in qq_process_get_buddies_and_rooms\n", uid);
- qq_set_pending_id(&qd->adding_groups_from_server, uid, TRUE);
+ rmd = qq_room_data_find(gc, uid);
+ if(rmd == NULL) {
+ purple_debug_info("QQ", "Unknow room id %d", uid);
+ qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_INFO, uid);
} else {
- group->my_role = QQ_ROOM_ROLE_YES;
- qq_group_refresh(gc, group);
+ rmd->my_role = QQ_ROOM_ROLE_YES;
}
++j;
}
@@ -416,39 +421,34 @@ guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConn
/* TODO: figure out what's going on with the IP region. Sometimes I get valid IP addresses,
* but the port number's weird, other times I get 0s. I get these simultaneously on the same buddy,
* using different accounts to get info. */
-
-/* check if status means online or offline */
-gboolean is_online(guint8 status)
-{
- switch(status) {
- case QQ_BUDDY_ONLINE_NORMAL:
- case QQ_BUDDY_ONLINE_AWAY:
- case QQ_BUDDY_ONLINE_INVISIBLE:
- return TRUE;
- case QQ_BUDDY_CHANGE_TO_OFFLINE:
- return FALSE;
- }
- return FALSE;
-}
-
-/* Help calculate the correct icon index to tell the server. */
-gint get_icon_offset(PurpleConnection *gc)
+static guint8 get_status_from_purple(PurpleConnection *gc)
{
+ qq_data *qd;
PurpleAccount *account;
PurplePresence *presence;
+ guint8 ret;
+ qd = (qq_data *) gc->proto_data;
account = purple_connection_get_account(gc);
presence = purple_account_get_presence(account);
if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
- return 2;
+ ret = QQ_BUDDY_ONLINE_INVISIBLE;
+ } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE))
+ {
+ if (qd->client_version >= 2007) {
+ ret = QQ_BUDDY_ONLINE_BUSY;
+ } else {
+ ret = QQ_BUDDY_ONLINE_INVISIBLE;
+ }
} else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
|| purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
- return 1;
+ ret = QQ_BUDDY_ONLINE_AWAY;
} else {
- return 0;
+ ret = QQ_BUDDY_ONLINE_NORMAL;
}
+ return ret;
}
/* send a packet to change my online status */
@@ -470,37 +470,37 @@ void qq_request_change_status(PurpleConnection *gc, gint update_class)
if (!qd->is_login)
return;
- if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
- away_cmd = QQ_BUDDY_ONLINE_INVISIBLE;
- } else if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
- || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)
- || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_UNAVAILABLE)) {
- away_cmd = QQ_BUDDY_ONLINE_AWAY;
- } else {
- away_cmd = QQ_BUDDY_ONLINE_NORMAL;
- }
+ away_cmd = get_status_from_purple(gc);
misc_status = 0x00000000;
fake_video = purple_prefs_get_bool("/plugins/prpl/qq/show_fake_video");
if (fake_video)
misc_status |= QQ_MISC_STATUS_HAVING_VIIDEO;
- bytes = 0;
- bytes += qq_put8(raw_data + bytes, away_cmd);
- bytes += qq_put32(raw_data + bytes, misc_status);
-
+ if (qd->client_version >= 2007) {
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, away_cmd);
+ /* status version */
+ bytes += qq_put16(raw_data + bytes, 0);
+ bytes += qq_put16(raw_data + bytes, 0);
+ bytes += qq_put32(raw_data + bytes, misc_status);
+ /* Fixme: custom status message, now is empty */
+ bytes += qq_put16(raw_data + bytes, 0);
+ } else {
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, away_cmd);
+ bytes += qq_put32(raw_data + bytes, misc_status);
+ }
qq_send_cmd_mess(gc, QQ_CMD_CHANGE_STATUS, raw_data, bytes, update_class, 0);
}
/* parse the reply packet for change_status */
-void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc)
{
qq_data *qd;
gint bytes;
guint8 reply;
- PurpleBuddy *b;
- qq_buddy *q_bud;
- gchar *name;
+ qq_buddy_data *bd;
g_return_if_fail(data != NULL && data_len != 0);
@@ -514,12 +514,11 @@ void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnectio
}
/* purple_debug_info("QQ", "Change status OK\n"); */
- name = uid_to_purple_name(qd->uid);
- b = purple_find_buddy(gc->account, name);
- g_free(name);
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (q_bud != NULL) {
- qq_update_buddy_contact(gc, q_bud);
+ bd = qq_buddy_data_find(gc, qd->uid);
+ if (bd != NULL) {
+ bd->status = get_status_from_purple(gc);
+ bd->last_update = time(NULL);
+ qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
}
}
@@ -529,10 +528,8 @@ void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnectio
qq_data *qd;
gint bytes;
guint32 my_uid;
- PurpleBuddy *b;
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
qq_buddy_status bs;
- gchar *name;
g_return_if_fail(data != NULL && data_len != 0);
@@ -552,57 +549,46 @@ void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnectio
* QQ_BUDDY_ONLINE_INVISIBLE */
bytes += qq_get32(&my_uid, data + bytes);
- name = uid_to_purple_name(bs.uid);
- b = purple_find_buddy(gc->account, name);
- g_free(name);
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (q_bud == NULL) {
+ bd = qq_buddy_data_find(gc, bs.uid);
+ if (bd == NULL) {
purple_debug_warning("QQ", "Get status of unknown buddy %d\n", bs.uid);
return;
}
if(bs.ip.s_addr != 0) {
- q_bud->ip.s_addr = bs.ip.s_addr;
- q_bud->port = bs.port;
+ bd->ip.s_addr = bs.ip.s_addr;
+ bd->port = bs.port;
+ }
+ if (bd->status != bs.status) {
+ bd->status = bs.status;
+ qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
}
- q_bud->status =bs.status;
+ bd->last_update = time(NULL);
- if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL && q_bud->level <= 0) {
- qq_request_get_level(gc, q_bud->uid);
+ if (bd->status == QQ_BUDDY_ONLINE_NORMAL && bd->level <= 0) {
+ if (qd->client_version >= 2007) {
+ qq_request_get_level_2007(gc, bd->uid);
+ } else {
+ qq_request_get_level(gc, bd->uid);
+ }
}
- qq_update_buddy_contact(gc, q_bud);
}
/*TODO: maybe this should be qq_update_buddy_status() ?*/
-void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
+void qq_update_buddy_status(PurpleConnection *gc, guint32 uid, guint8 status, guint8 flag)
{
- gchar *purple_name;
- PurpleBuddy *bud;
+ gchar *who;
gchar *status_id;
- g_return_if_fail(q_bud != NULL);
+ g_return_if_fail(uid != 0);
- purple_name = uid_to_purple_name(q_bud->uid);
- if (purple_name == NULL) {
- purple_debug_error("QQ", "Not find purple name: %d\n", q_bud->uid);
- return;
- }
-
- bud = purple_find_buddy(gc->account, purple_name);
- if (bud == NULL) {
- purple_debug_error("QQ", "Not find buddy: %d\n", q_bud->uid);
- g_free(purple_name);
- return;
- }
-
- purple_blist_server_alias_buddy(bud, q_bud->nickname); /* server */
- q_bud->last_update = time(NULL);
+ who = uid_to_purple_name(uid);
/* purple supports signon and idle time
* but it is not much use for QQ, I do not use them */
/* serv_got_update(gc, name, online, 0, q_bud->signon, q_bud->idle, bud->uc); */
status_id = "available";
- switch(q_bud->status) {
+ switch(status) {
case QQ_BUDDY_OFFLINE:
status_id = "offline";
break;
@@ -618,42 +604,81 @@ void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud)
case QQ_BUDDY_ONLINE_INVISIBLE:
status_id = "invisible";
break;
+ case QQ_BUDDY_ONLINE_BUSY:
+ status_id = "busy";
+ break;
default:
status_id = "invisible";
- purple_debug_error("QQ", "unknown status: %x\n", q_bud->status);
+ purple_debug_error("QQ", "unknown status: 0x%X\n", status);
break;
}
- purple_debug_info("QQ", "buddy %d %s\n", q_bud->uid, status_id);
- purple_prpl_got_user_status(gc->account, purple_name, status_id, NULL);
+ purple_debug_info("QQ", "Update buddy %s status as %s\n", who, status_id);
+ purple_prpl_got_user_status(gc->account, who, status_id, NULL);
- if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE && q_bud->status != QQ_BUDDY_OFFLINE)
- purple_prpl_got_user_status(gc->account, purple_name, "mobile", NULL);
+ if (flag & QQ_COMM_FLAG_MOBILE && status != QQ_BUDDY_OFFLINE)
+ purple_prpl_got_user_status(gc->account, who, "mobile", NULL);
else
- purple_prpl_got_user_status_deactive(gc->account, purple_name, "mobile");
+ purple_prpl_got_user_status_deactive(gc->account, who, "mobile");
- g_free(purple_name);
+ g_free(who);
}
/* refresh all buddies online/offline,
* after receiving reply for get_buddies_online packet */
-void qq_refresh_all_buddy_status(PurpleConnection *gc)
+void qq_update_buddyies_status(PurpleConnection *gc)
{
- time_t now;
- GList *list;
qq_data *qd;
- qq_buddy *q_bud;
+ PurpleBuddy *buddy;
+ qq_buddy_data *bd;
+ GSList *buddies, *it;
+ time_t tm_limit = time(NULL);
qd = (qq_data *) (gc->proto_data);
- now = time(NULL);
- list = qd->buddies;
-
- while (list != NULL) {
- q_bud = (qq_buddy *) list->data;
- if (q_bud != NULL && now > q_bud->last_update + QQ_UPDATE_ONLINE_INTERVAL
- && q_bud->status != QQ_BUDDY_ONLINE_INVISIBLE) {
- q_bud->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
- qq_update_buddy_contact(gc, q_bud);
- }
- list = list->next;
+
+ tm_limit -= QQ_UPDATE_ONLINE_INTERVAL;
+
+ buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+ for (it = buddies; it; it = it->next) {
+ buddy = it->data;
+ if (buddy == NULL) continue;
+ if (buddy->proto_data == NULL) continue;
+
+ bd = (qq_buddy_data *)buddy->proto_data;
+ if (bd->uid == 0) continue;
+ if (bd->uid == qd->uid) continue; /* my status is always online in my buddy list */
+ if (tm_limit < bd->last_update) continue;
+ if (bd->status == QQ_BUDDY_ONLINE_INVISIBLE) continue;
+ if (bd->status == QQ_BUDDY_CHANGE_TO_OFFLINE) continue;
+
+ bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
+ bd->last_update = time(NULL);
+ qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
}
}
+
+void qq_buddy_data_free_all(PurpleConnection *gc)
+{
+ qq_data *qd;
+ PurpleBuddy *buddy;
+ GSList *buddies, *it;
+ gint count = 0;
+
+ qd = (qq_data *) (gc->proto_data);
+
+ buddies = purple_find_buddies(purple_connection_get_account(gc), NULL);
+ for (it = buddies; it; it = it->next) {
+ buddy = it->data;
+ if (buddy == NULL) continue;
+ if (buddy->proto_data == NULL) continue;
+
+ qq_buddy_data_free(buddy->proto_data);
+ buddy->proto_data = NULL;
+
+ count++;
+ }
+
+ if (count > 0) {
+ purple_debug_info("QQ", "%d buddies' data are freed\n", count);
+ }
+}
+
diff --git a/libpurple/protocols/qq/buddy_list.h b/libpurple/protocols/qq/buddy_list.h
index 8226bacf74..c6f5cc839c 100644
--- a/libpurple/protocols/qq/buddy_list.h
+++ b/libpurple/protocols/qq/buddy_list.h
@@ -40,33 +40,20 @@ typedef struct _qq_buddy_status {
guint8 unknown_key[QQ_KEY_LENGTH];
} qq_buddy_status;
-enum {
- QQ_BUDDY_OFFLINE = 0x00,
- QQ_BUDDY_ONLINE_NORMAL = 10,
- QQ_BUDDY_CHANGE_TO_OFFLINE = 20,
- QQ_BUDDY_ONLINE_AWAY = 30,
- QQ_BUDDY_ONLINE_INVISIBLE = 40
-};
-
void qq_request_get_buddies_online(PurpleConnection *gc, guint8 position, gint update_class);
-guint8 qq_process_get_buddies_online_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+guint8 qq_process_get_buddies_online(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_request_get_buddies_list(PurpleConnection *gc, guint16 position, gint update_class);
-guint16 qq_process_get_buddies_list_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_request_get_buddies(PurpleConnection *gc, guint16 position, gint update_class);
+guint16 qq_process_get_buddies(guint8 *data, gint data_len, PurpleConnection *gc);
void qq_request_get_buddies_and_rooms(PurpleConnection *gc, guint32 position, gint update_class);
guint32 qq_process_get_buddies_and_rooms(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_refresh_all_buddy_status(PurpleConnection *gc);
-
-gboolean is_online(guint8 status);
-
-gint get_icon_offset(PurpleConnection *gc);
-
void qq_request_change_status(PurpleConnection *gc, gint update_class);
-void qq_process_change_status_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_process_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
void qq_process_buddy_change_status(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_refresh_all_buddy_status(PurpleConnection *gc);
-void qq_update_buddy_contact(PurpleConnection *gc, qq_buddy *q_bud);
+void qq_update_buddyies_status(PurpleConnection *gc);
+void qq_update_buddy_status(PurpleConnection *gc, guint32 uid, guint8 status, guint8 flag);
+void qq_buddy_data_free_all(PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/buddy_opt.c b/libpurple/protocols/qq/buddy_opt.c
index b77081b821..5404fc0d76 100644
--- a/libpurple/protocols/qq/buddy_opt.c
+++ b/libpurple/protocols/qq/buddy_opt.c
@@ -26,12 +26,13 @@
#include "internal.h"
#include "notify.h"
#include "request.h"
+#include "privacy.h"
#include "buddy_info.h"
#include "buddy_list.h"
#include "buddy_opt.h"
#include "char_conv.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "im.h"
#include "qq_base.h"
#include "packet_parse.h"
@@ -39,12 +40,8 @@
#include "utils.h"
#define PURPLE_GROUP_QQ_FORMAT "QQ (%s)"
-#define PURPLE_GROUP_QQ_UNKNOWN "QQ Unknown"
-#define PURPLE_GROUP_QQ_BLOCKED "QQ Blocked"
-#define QQ_REMOVE_BUDDY_REPLY_OK 0x00
#define QQ_REMOVE_SELF_REPLY_OK 0x00
-#define QQ_ADD_BUDDY_AUTH_REPLY_OK 0x30 /* ASCII value of "0" */
enum {
QQ_MY_AUTH_APPROVE = 0x30, /* ASCII value of "0" */
@@ -52,24 +49,419 @@ enum {
QQ_MY_AUTH_REQUEST = 0x32, /* ASCII value of "2" */
};
-typedef struct _qq_add_buddy_request {
+typedef struct _qq_buddy_req {
+ PurpleConnection *gc;
guint32 uid;
- guint16 seq;
-} qq_add_buddy_request;
+ guint8 *auth;
+ guint8 auth_len;
+} qq_buddy_req;
+
+void add_buddy_authorize_input(PurpleConnection *gc, guint32 uid,
+ guint8 *auth, guint8 auth_len);
+
+static void buddy_req_free(qq_buddy_req *add_req)
+{
+ g_return_if_fail(add_req != NULL);
+ if (add_req->auth) g_free(add_req->auth);
+ g_free(add_req);
+}
+
+static void buddy_req_cancel_cb(qq_buddy_req *add_req, const gchar *msg)
+{
+ g_return_if_fail(add_req != NULL);
+ buddy_req_free(add_req);
+}
+
+PurpleGroup *qq_group_find_or_new(const gchar *group_name)
+{
+ PurpleGroup *g;
+
+ g_return_val_if_fail(group_name != NULL, NULL);
+
+ g = purple_find_group(group_name);
+ if (g == NULL) {
+ g = purple_group_new(group_name);
+ purple_blist_add_group(g, NULL);
+ purple_debug_warning("QQ", "Add new group: %s\n", group_name);
+ }
+
+ return g;
+}
+
+static qq_buddy_data *qq_buddy_data_new(guint32 uid)
+{
+ qq_buddy_data *bd = g_new0(qq_buddy_data, 1);
+ memset(bd, 0, sizeof(qq_buddy_data));
+ bd->uid = uid;
+ bd->status = QQ_BUDDY_OFFLINE;
+ return bd;
+}
+
+qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, guint32 uid)
+{
+ gchar *who;
+ PurpleBuddy *buddy;
+
+ g_return_val_if_fail(gc != NULL, NULL);
+
+ who = uid_to_purple_name(uid);
+ if (who == NULL) return NULL;
+ buddy = purple_find_buddy(purple_connection_get_account(gc), who);
+ g_free(who);
+
+ if (buddy == NULL) {
+ purple_debug_error("QQ", "Can not find purple buddy of %d\n", uid);
+ return NULL;
+ }
+ if (buddy->proto_data == NULL) {
+ purple_debug_error("QQ", "Can not find buddy data of %d\n", uid);
+ return NULL;
+ }
+ return (qq_buddy_data *)buddy->proto_data;
+}
+
+void qq_buddy_data_free(qq_buddy_data *bd)
+{
+ g_return_if_fail(bd != NULL);
+
+ if (bd->nickname) g_free(bd->nickname);
+ g_free(bd);
+}
+
+/* create purple buddy without data and display with no-auth icon */
+PurpleBuddy *qq_buddy_new(PurpleConnection *gc, guint32 uid)
+{
+ PurpleBuddy *buddy;
+ PurpleGroup *group;
+ gchar *who;
+ gchar *group_name;
+
+ g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+ group_name = g_strdup_printf(PURPLE_GROUP_QQ_FORMAT,
+ purple_account_get_username(gc->account));
+ group = qq_group_find_or_new(group_name);
+ if (group == NULL) {
+ purple_debug_error("QQ", "Failed creating group %s\n", group_name);
+ return NULL;
+ }
+
+ who = uid_to_purple_name(uid);
+
+ purple_debug_info("QQ", "Add new purple buddy: [%s]\n", who);
+ buddy = purple_buddy_new(gc->account, who, NULL); /* alias is NULL */
+ buddy->proto_data = NULL;
+
+ g_free(who);
+
+ purple_blist_add_buddy(buddy, NULL, group, NULL);
+
+ g_free(group_name);
+
+ return buddy;
+}
+
+static void qq_buddy_free(PurpleBuddy *buddy)
+{
+ g_return_if_fail(buddy);
+ if (buddy->proto_data) {
+ qq_buddy_data_free(buddy->proto_data);
+ }
+ buddy->proto_data = NULL;
+ purple_blist_remove_buddy(buddy);
+}
+
+PurpleBuddy *qq_buddy_find(PurpleConnection *gc, guint32 uid)
+{
+ PurpleBuddy *buddy;
+ gchar *who;
+
+ g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+ who = uid_to_purple_name(uid);
+ buddy = purple_find_buddy(gc->account, who);
+ g_free(who);
+ return buddy;
+}
+
+PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid)
+{
+ PurpleBuddy *buddy;
+
+ g_return_val_if_fail(gc->account != NULL && uid != 0, NULL);
+
+ buddy = qq_buddy_find(gc, uid);
+ if (buddy == NULL) {
+ buddy = qq_buddy_new(gc, uid);
+ if (buddy == NULL) {
+ return NULL;
+ }
+ }
+
+ if (buddy->proto_data != NULL) {
+ return buddy;
+ }
+
+ buddy->proto_data = qq_buddy_data_new(uid);
+ return buddy;
+}
/* send packet to remove a buddy from my buddy list */
-static void _qq_send_packet_remove_buddy(PurpleConnection *gc, guint32 uid)
+static void request_remove_buddy(PurpleConnection *gc, guint32 uid)
{
gchar uid_str[11];
+ gint bytes;
g_return_if_fail(uid > 0);
g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
- qq_send_cmd(gc, QQ_CMD_DEL_BUDDY, (guint8 *) uid_str, strlen(uid_str));
+ bytes = strlen(uid_str);
+ qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, (guint8 *) uid_str, bytes, 0, uid);
+}
+
+static void request_remove_buddy_ex(PurpleConnection *gc,
+ guint32 uid, guint8 *auth, guint8 auth_len)
+{
+ gint bytes;
+ guint8 *raw_data;
+ gchar uid_str[16];
+
+ g_return_if_fail(uid != 0);
+ g_return_if_fail(auth != NULL && auth_len > 0);
+
+ raw_data = g_newa(guint8, auth_len + sizeof(uid_str) );
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, auth_len);
+ bytes += qq_putdata(raw_data + bytes, auth, auth_len);
+
+ g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
+
+ qq_send_cmd_mess(gc, QQ_CMD_REMOVE_BUDDY, raw_data, bytes, 0, uid);
+}
+
+void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid)
+{
+ guint8 raw_data[16];
+ gint bytes;
+
+ g_return_if_fail(uid > 0);
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, cmd);
+ bytes += qq_put16(raw_data + bytes, sub_cmd);
+ bytes += qq_put32(raw_data + bytes, uid);
+
+ qq_send_cmd_mess(gc, QQ_CMD_AUTH_CODE, raw_data, bytes, 0, uid);
+}
+
+void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
+{
+ qq_data *qd;
+ gint bytes;
+ guint8 cmd, reply;
+ guint16 sub_cmd;
+ guint8 *code = NULL;
+ guint16 code_len = 0;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+ g_return_if_fail(uid != 0);
+
+ qd = (qq_data *) gc->proto_data;
+
+ qq_show_packet("qq_process_auth_code", data, data_len);
+ bytes = 0;
+ bytes += qq_get8(&cmd, data + bytes);
+ bytes += qq_get16(&sub_cmd, data + bytes);
+ bytes += qq_get8(&reply, data + bytes);
+ g_return_if_fail(bytes + 2 <= data_len);
+
+ bytes += qq_get16(&code_len, data + bytes);
+ g_return_if_fail(code_len > 0);
+ g_return_if_fail(bytes + code_len <= data_len);
+ code = g_newa(guint8, code_len);
+ bytes += qq_getdata(code, code_len, data + bytes);
+
+ if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_REMOVE_BUDDY) {
+ request_remove_buddy_ex(gc, uid, code, code_len);
+ return;
+ }
+ if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_ADD_BUDDY) {
+ add_buddy_authorize_input(gc, uid, code, code_len);
+ return;
+ }
+ purple_debug_info("QQ", "Got auth info cmd 0x%x, sub 0x%x, reply 0x%x\n",
+ cmd, sub_cmd, reply);
+}
+
+static void add_buddy_question_cb(qq_buddy_req *add_req, const gchar *text)
+{
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->uid == 0) {
+ buddy_req_free(add_req);
+ return;
+ }
+
+ qq_request_question(add_req->gc, QQ_QUESTION_ANSWER, add_req->uid, NULL, text);
+ buddy_req_free(add_req);
+}
+
+static void add_buddy_question_input(PurpleConnection *gc, guint32 uid, gchar *question)
+{
+ gchar *who, *msg;
+ qq_buddy_req *add_req;
+ g_return_if_fail(uid != 0);
+
+ add_req = g_new0(qq_buddy_req, 1);
+ add_req->gc = gc;
+ add_req->uid = uid;
+ add_req->auth = NULL;
+ add_req->auth_len = 0;
+
+ who = uid_to_purple_name(uid);
+ msg = g_strdup_printf(_("%d needs Q&A"), uid);
+ purple_request_input(gc, _("Add buddy Q&A"), msg,
+ _("Input answer here"),
+ NULL,
+ TRUE, FALSE, NULL,
+ _("Send"), G_CALLBACK(add_buddy_question_cb),
+ _("Cancel"), G_CALLBACK(buddy_req_cancel_cb),
+ purple_connection_get_account(gc), who, NULL,
+ add_req);
+
+ g_free(msg);
+ g_free(who);
+}
+
+void qq_request_question(PurpleConnection *gc,
+ guint8 cmd, guint32 uid, const gchar *question_utf8, const gchar *answer_utf8)
+{
+ guint8 raw_data[MAX_PACKET_SIZE - 16];
+ gint bytes;
+
+ g_return_if_fail(uid > 0);
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, cmd);
+ if (cmd == QQ_QUESTION_GET) {
+ bytes += qq_put8(raw_data + bytes, 0);
+ qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+ return;
+ }
+ if (cmd == QQ_QUESTION_SET) {
+ bytes += qq_put_vstr(raw_data + bytes, question_utf8, QQ_CHARSET_DEFAULT);
+ bytes += qq_put_vstr(raw_data + bytes, answer_utf8, QQ_CHARSET_DEFAULT);
+ bytes += qq_put8(raw_data + bytes, 0);
+ qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+ return;
+ }
+ /* Unknow 2 bytes, 0x(00 01) */
+ bytes += qq_put8(raw_data + bytes, 0x00);
+ bytes += qq_put8(raw_data + bytes, 0x01);
+ g_return_if_fail(uid != 0);
+ bytes += qq_put32(raw_data + bytes, uid);
+ if (cmd == QQ_QUESTION_REQUEST) {
+ qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+ return;
+ }
+ bytes += qq_put_vstr(raw_data + bytes, answer_utf8, QQ_CHARSET_DEFAULT);
+ bytes += qq_put8(raw_data + bytes, 0);
+ qq_send_cmd_mess(gc, QQ_CMD_BUDDY_QUESTION, raw_data, bytes, 0, uid);
+ return;
+}
+
+static void request_add_buddy_by_question(PurpleConnection *gc, guint32 uid,
+ guint8 *code, guint16 code_len)
+{
+ guint8 raw_data[MAX_PACKET_SIZE - 16];
+ gint bytes = 0;
+
+ g_return_if_fail(uid != 0 && code_len > 0);
+
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, 0x10);
+ bytes += qq_put32(raw_data + bytes, uid);
+ bytes += qq_put16(raw_data + bytes, 0);
+
+ bytes += qq_put8(raw_data + bytes, 0);
+ bytes += qq_put8(raw_data + bytes, 0); /* no auth code */
+
+ bytes += qq_put16(raw_data + bytes, code_len);
+ bytes += qq_putdata(raw_data + bytes, code, code_len);
+
+ bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */
+ bytes += qq_put8(raw_data + bytes, 0); /* group number? */
+ qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes);
+}
+
+void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
+{
+ qq_data *qd;
+ gint bytes;
+ guint8 cmd, reply;
+ gchar *question, *answer;
+ guint16 code_len;
+ guint8 *code;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+
+ qd = (qq_data *) gc->proto_data;
+
+ qq_show_packet("qq_process_question", data, data_len);
+ bytes = 0;
+ bytes += qq_get8(&cmd, data + bytes);
+ if (cmd == QQ_QUESTION_GET) {
+ bytes += qq_get_vstr(&question, QQ_CHARSET_DEFAULT, data + bytes);
+ bytes += qq_get_vstr(&answer, QQ_CHARSET_DEFAULT, data + bytes);
+ purple_debug_info("QQ", "Get buddy adding Q&A:\n%s\n%s\n", question, answer);
+ g_free(question);
+ g_free(answer);
+ return;
+ }
+ if (cmd == QQ_QUESTION_SET) {
+ bytes += qq_get8(&reply, data + bytes);
+ if (reply == 0) {
+ purple_debug_info("QQ", "Successed setting Q&A\n");
+ } else {
+ purple_debug_warning("QQ", "Failed setting Q&A, reply %d\n", reply);
+ }
+ return;
+ }
+
+ g_return_if_fail(uid != 0);
+ bytes += 2; /* skip 2 bytes, 0x(00 01)*/
+ if (cmd == QQ_QUESTION_REQUEST) {
+ bytes += qq_get8(&reply, data + bytes);
+ if (reply == 0x01) {
+ purple_debug_warning("QQ", "Failed getting question, reply %d\n", reply);
+ return;
+ }
+ bytes += qq_get_vstr(&question, QQ_CHARSET_DEFAULT, data + bytes);
+ purple_debug_info("QQ", "Get buddy question:\n%s\n", question);
+ add_buddy_question_input(gc, uid, question);
+ g_free(question);
+ return;
+ }
+
+ if (cmd == QQ_QUESTION_ANSWER) {
+ bytes += qq_get8(&reply, data + bytes);
+ if (reply == 0x01) {
+ purple_notify_error(gc, _("Add Buddy"), _("Invalid answer."), NULL);
+ return;
+ }
+ bytes += qq_get16(&code_len, data + bytes);
+ g_return_if_fail(code_len > 0);
+ g_return_if_fail(bytes + code_len <= data_len);
+
+ code = g_newa(guint8, code_len);
+ bytes += qq_getdata(code, code_len, data + bytes);
+ request_add_buddy_by_question(gc, uid, code, code_len);
+ return;
+ }
+
+ g_return_if_reached();
}
/* try to remove myself from someone's buddy list */
-static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
+static void request_buddy_remove_me(PurpleConnection *gc, guint32 uid)
{
guint8 raw_data[16] = {0};
gint bytes = 0;
@@ -78,31 +470,36 @@ static void _qq_send_packet_remove_self_from(PurpleConnection *gc, guint32 uid)
bytes += qq_put32(raw_data + bytes, uid);
- qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, raw_data, bytes);
+ qq_send_cmd_mess(gc, QQ_CMD_REMOVE_ME, raw_data, bytes, 0, uid);
}
/* try to add a buddy without authentication */
-static void _qq_send_packet_add_buddy(PurpleConnection *gc, guint32 uid)
+static void request_add_buddy_no_auth(PurpleConnection *gc, guint32 uid)
{
- qq_data *qd = (qq_data *) gc->proto_data;
- qq_add_buddy_request *req;
gchar uid_str[11];
g_return_if_fail(uid > 0);
/* 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_BUDDY_WO_AUTH, (guint8 *) uid_str, strlen(uid_str));
+ qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_NO_AUTH,
+ (guint8 *) uid_str, strlen(uid_str), 0, uid);
+}
- /* must be set after sending packet to get the correct send_seq */
- req = g_new0(qq_add_buddy_request, 1);
- req->seq = qd->send_seq;
- req->uid = uid;
- qd->add_buddy_request = g_list_append(qd->add_buddy_request, req);
+static void request_add_buddy_no_auth_ex(PurpleConnection *gc, guint32 uid)
+{
+ guint bytes;
+ guint8 raw_data[16];
+
+ g_return_if_fail(uid != 0);
+
+ bytes = 0;
+ bytes += qq_put32(raw_data + bytes, uid);
+ qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_NO_AUTH_EX, raw_data, bytes, 0, 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)
+static void request_add_buddy_auth(PurpleConnection *gc, guint32 uid, const gchar response, const gchar *text)
{
gchar *text_qq, uid_str[11];
guint8 bar, *raw_data;
@@ -125,130 +522,196 @@ static void _qq_send_packet_buddy_auth(PurpleConnection *gc, guint32 uid, const
g_free(text_qq);
}
- qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, raw_data, bytes);
+ qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH, raw_data, bytes);
}
-static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
+static void request_add_buddy_auth_ex(PurpleConnection *gc, guint32 uid,
+ const gchar *text, guint8 *auth, guint8 auth_len)
{
- PurpleConnection *gc;
- guint32 uid;
- g_return_if_fail(g != NULL);
+ guint8 raw_data[MAX_PACKET_SIZE - 16];
+ gint bytes = 0;
- gc = g->gc;
- uid = g->uid;
g_return_if_fail(uid != 0);
- _qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_REQUEST, text);
- g_free(g);
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, 0x02);
+ bytes += qq_put32(raw_data + bytes, uid);
+ bytes += qq_put16(raw_data + bytes, 0);
+
+ bytes += qq_put8(raw_data + bytes, 0);
+ if (auth == NULL || auth_len <= 0) {
+ bytes += qq_put8(raw_data + bytes, 0);
+ } else {
+ bytes += qq_put8(raw_data + bytes, auth_len);
+ bytes += qq_putdata(raw_data + bytes, auth, auth_len);
+ }
+ bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */
+ bytes += qq_put8(raw_data + bytes, 0); /* group number? */
+ bytes += qq_put_vstr(raw_data + bytes, text, QQ_CHARSET_DEFAULT);
+ qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes);
}
-/* the real packet to reject and request is sent from here */
-static void _qq_reject_add_request_real(gc_and_uid *g, const gchar *reason)
+void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32)
{
- gint uid;
- PurpleConnection *gc;
-
- g_return_if_fail(g != NULL);
-
- gc = g->gc;
- uid = g->uid;
- g_return_if_fail(uid != 0);
+ g_return_if_fail(data != NULL && data_len != 0);
- _qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_REJECT, reason);
- g_free(g);
+ qq_show_packet("qq_process_question", data, data_len);
}
-/* we approve other's request of adding me as friend */
-void qq_approve_add_request_with_gc_and_uid(gc_and_uid *g)
+static void add_buddy_auth_cb(qq_buddy_req *add_req, const gchar *text)
{
- gint uid;
- PurpleConnection *gc;
+ qq_data *qd;
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->uid == 0) {
+ buddy_req_free(add_req);
+ return;
+ }
- g_return_if_fail(g != NULL);
+ qd = (qq_data *)add_req->gc->proto_data;
+ if (qd->client_version > 2005) {
+ request_add_buddy_auth_ex(add_req->gc, add_req->uid,
+ text, add_req->auth, add_req->auth_len);
+ } else {
+ request_add_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_REQUEST, text);
+ }
+ buddy_req_free(add_req);
+}
- gc = g->gc;
- uid = g->uid;
- g_return_if_fail(uid != 0);
+/* the real packet to reject and request is sent from here */
+static void buddy_add_deny_reason_cb(qq_buddy_req *add_req, const gchar *reason)
+{
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->uid == 0) {
+ buddy_req_free(add_req);
+ return;
+ }
- _qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_APPROVE, NULL);
- g_free(g);
+ request_add_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_REJECT, reason);
+ buddy_req_free(add_req);
}
-void qq_do_nothing_with_gc_and_uid(gc_and_uid *g, const gchar *msg)
+static void buddy_add_deny_noreason_cb(qq_buddy_req *add_req)
{
- g_free(g);
+ buddy_add_deny_reason_cb(add_req, NULL);
}
-/* we reject other's request of adding me as friend */
-void qq_reject_add_request_with_gc_and_uid(gc_and_uid *g)
+/* we approve other's request of adding me as friend */
+static void buddy_add_authorize_cb(gpointer data)
{
- gint uid;
- gchar *msg1, *msg2;
- PurpleConnection *gc;
- gc_and_uid *g2;
- gchar *nombre;
-
- g_return_if_fail(g != NULL);
-
- gc = g->gc;
- uid = g->uid;
- g_return_if_fail(uid != 0);
-
- g_free(g);
+ qq_buddy_req *add_req = (qq_buddy_req *)data;
- g2 = g_new0(gc_and_uid, 1);
- g2->gc = gc;
- g2->uid = uid;
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->uid == 0) {
+ buddy_req_free(add_req);
+ return;
+ }
- msg1 = g_strdup_printf(_("You rejected %d's request"), uid);
- msg2 = g_strdup(_("Message:"));
+ request_add_buddy_auth(add_req->gc, add_req->uid, QQ_MY_AUTH_APPROVE, NULL);
+ buddy_req_free(add_req);
+}
- nombre = uid_to_purple_name(uid);
- purple_request_input(gc, _("Reject request"), msg1, msg2,
- _("Sorry, you are not my style..."), TRUE, FALSE,
- NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL,
- purple_connection_get_account(gc), nombre, NULL,
- g2);
- g_free(nombre);
+/* we reject other's request of adding me as friend */
+static void buddy_add_deny_cb(gpointer data)
+{
+ qq_buddy_req *add_req = (qq_buddy_req *)data;
+ gchar *who = uid_to_purple_name(add_req->uid);
+ purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
+ NULL, _("Sorry, You are not my style."), TRUE, FALSE, NULL,
+ _("OK"), G_CALLBACK(buddy_add_deny_reason_cb),
+ _("Cancel"), G_CALLBACK(buddy_add_deny_noreason_cb),
+ purple_connection_get_account(add_req->gc), who, NULL,
+ add_req);
+ g_free(who);
}
-void qq_add_buddy_with_gc_and_uid(gc_and_uid *g)
+static void add_buddy_no_auth_cb(qq_buddy_req *add_req)
{
- gint uid;
- PurpleConnection *gc;
+ qq_data *qd;
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->uid == 0) {
+ buddy_req_free(add_req);
+ return;
+ }
- g_return_if_fail(g != NULL);
+ qd = (qq_data *) add_req->gc->proto_data;
+ if (qd->client_version > 2005) {
+ request_add_buddy_no_auth_ex(add_req->gc, add_req->uid);
+ } else {
+ request_add_buddy_no_auth(add_req->gc, add_req->uid);
+ }
+ buddy_req_free(add_req);
+}
- gc = g->gc;
- uid = g->uid;
+void add_buddy_authorize_input(PurpleConnection *gc, guint32 uid,
+ guint8 *auth, guint8 auth_len)
+{
+ gchar *who, *msg;
+ qq_buddy_req *add_req;
g_return_if_fail(uid != 0);
- _qq_send_packet_add_buddy(gc, uid);
- g_free(g);
+ add_req = g_new0(qq_buddy_req, 1);
+ add_req->gc = gc;
+ add_req->uid = uid;
+ add_req->auth = NULL;
+ add_req->auth_len = 0;
+ if (auth != NULL && auth_len > 0) {
+ add_req->auth = g_new0(guint8, auth_len);
+ g_memmove(add_req->auth, auth, auth_len);
+ add_req->auth_len = auth_len;
+ }
+
+ who = uid_to_purple_name(uid);
+ msg = g_strdup_printf(_("%d needs authentication"), uid);
+ purple_request_input(gc, _("Add buddy authorize"), msg,
+ _("Input request here"),
+ _("Would you be my friend?"),
+ TRUE, FALSE, NULL,
+ _("Send"), G_CALLBACK(add_buddy_auth_cb),
+ _("Cancel"), G_CALLBACK(buddy_req_cancel_cb),
+ purple_connection_get_account(gc), who, NULL,
+ add_req);
+
+ g_free(msg);
+ g_free(who);
}
-void qq_block_buddy_with_gc_and_uid(gc_and_uid *g)
+/* add a buddy and send packet to QQ server
+ * note that when purple load local cached buddy list into its blist
+ * it also calls this funtion, so we have to
+ * define qd->is_login=TRUE AFTER LOGIN */
+void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
+ qq_data *qd;
guint32 uid;
- PurpleConnection *gc;
- PurpleBuddy buddy;
- PurpleGroup group;
- g_return_if_fail(g != NULL);
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ g_return_if_fail(buddy != NULL);
- gc = g->gc;
- uid = g->uid;
- g_return_if_fail(uid > 0);
+ qd = (qq_data *) gc->proto_data;
+ if (!qd->is_login)
+ return; /* IMPORTANT ! */
- buddy.name = uid_to_purple_name(uid);
- group.name = PURPLE_GROUP_QQ_BLOCKED;
+ uid = purple_name_to_uid(buddy->name);
+ if (uid > 0) {
+ if (qd->client_version > 2005) {
+ request_add_buddy_no_auth_ex(gc, uid);
+ } else {
+ request_add_buddy_no_auth(gc, uid);
+ }
+ return;
+ }
+
+ purple_notify_error(gc, _("QQ Buddy"), _("Add buddy"), _("Invalid QQ Number"));
+ if (buddy == NULL) {
+ return;
+ }
- qq_remove_buddy(gc, &buddy, &group);
- _qq_send_packet_remove_self_from(gc, uid);
+ purple_debug_info("QQ", "Remove buddy with invalid QQ number %d\n", uid);
+ qq_buddy_free(buddy);
}
/* process reply to add_buddy_auth request */
-void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_add_buddy_auth(guint8 *data, gint data_len, PurpleConnection *gc)
{
qq_data *qd;
gchar **segments, *msg_utf8;
@@ -257,301 +720,576 @@ void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnecti
qd = (qq_data *) gc->proto_data;
- if (data[0] != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
- purple_debug_warning("QQ", "Add buddy with auth request failed\n");
- if (NULL == (segments = split_data(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_info("QQ", "Add buddy with auth request OK\n");
+ if (data[0] == '0') {
+ purple_debug_info("QQ", "Reply OK for sending authorize\n");
+ return;
}
+
+ if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) {
+ purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), NULL);
+ return;
+ }
+ msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+ purple_notify_error(gc, _("QQ Buddy"), _("Failed sending authorize"), msg_utf8);
+ g_free(msg_utf8);
}
/* process the server reply for my request to remove a buddy */
-void qq_process_remove_buddy_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_remove_buddy(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
- qq_data *qd;
+ PurpleBuddy *buddy = NULL;
+ gchar *msg;
g_return_if_fail(data != NULL && data_len != 0);
+ g_return_if_fail(uid != 0);
- qd = (qq_data *) gc->proto_data;
+ buddy = qq_buddy_find(gc, uid);
+ if (data[0] != 0) {
+ msg = g_strdup_printf(_("Failed removing buddy %d"), uid);
+ purple_notify_info(gc, _("QQ Buddy"), msg, NULL);
+ g_free(msg);
+ }
- if (data[0] != QQ_REMOVE_BUDDY_REPLY_OK) {
- /* there is no reason return from server */
- purple_debug_warning("QQ", "Remove buddy fails\n");
- purple_notify_info(gc, _("QQ Buddy"), _("Failed:"), _("Remove buddy"));
- } else { /* if reply */
- 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, _("QQ Buddy"), _("Successed:"), _("Remove buddy"));
+ purple_debug_info("QQ", "Reply OK for removing buddy\n");
+ /* remove buddy again */
+ if (buddy != NULL) {
+ qq_buddy_free(buddy);
}
}
/* process the server reply for my request to remove myself from a buddy */
-void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid)
{
qq_data *qd;
+ gchar *msg;
g_return_if_fail(data != NULL && data_len != 0);
-
qd = (qq_data *) gc->proto_data;
- if (data[0] != QQ_REMOVE_SELF_REPLY_OK) {
- /* there is no reason return from server */
- purple_debug_warning("QQ", "Remove self fails\n");
- purple_notify_info(gc, _("QQ Buddy"), _("Failed:"), _("Remove from other's buddy list"));
- } else { /* if reply */
- purple_debug_info("QQ", "Remove from a buddy OK\n");
- /* TODO: Does the user really need to be notified about this? */
- purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), _("Remove from other's buddy list"));
+ if (data[0] == 0) {
+ purple_debug_info("QQ", "Reply OK for removing me from %d's buddy list\n", uid);
+ return;
}
+ msg = g_strdup_printf(_("Failed removing me from %d's buddy list"), uid);
+ purple_notify_info(gc, _("QQ Buddy"), msg, NULL);
+ g_free(msg);
}
-void qq_process_add_buddy_reply(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
+void qq_process_add_buddy_no_auth(PurpleConnection *gc,
+ guint8 *data, gint data_len, guint32 uid)
{
qq_data *qd;
- gint for_uid;
- gchar *msg, **segments, *uid, *reply;
- GList *list;
- PurpleBuddy *b;
- gc_and_uid *g;
- qq_add_buddy_request *req;
- gchar *nombre;
+ gchar **segments;
+ gchar *dest_uid, *reply;
+ PurpleBuddy *buddy;
g_return_if_fail(data != NULL && data_len != 0);
+ g_return_if_fail(uid != 0);
- for_uid = 0;
qd = (qq_data *) gc->proto_data;
- list = qd->add_buddy_request;
- while (list != NULL) {
- req = (qq_add_buddy_request *) list->data;
- if (req->seq == seq) { /* reply to this */
- for_uid = req->uid;
- qd->add_buddy_request = g_list_remove(qd->add_buddy_request, qd->add_buddy_request->data);
- g_free(req);
- break;
- }
- list = list->next;
- }
-
- if (for_uid == 0) { /* we have no record for this */
- purple_debug_error("QQ", "We have no record for add buddy reply [%d], discard\n", seq);
- return;
- } else {
- purple_debug_info("QQ", "Add buddy reply [%d] is for id [%d]\n", seq, for_uid);
- }
+ purple_debug_info("QQ", "Process buddy add for id [%d]\n", uid);
+ qq_show_packet("buddy_add_no_auth", data, data_len);
if (NULL == (segments = split_data(data, data_len, "\x1f", 2)))
return;
- uid = segments[0];
+ dest_uid = segments[0];
reply = segments[1];
- if (strtol(uid, NULL, 10) != qd->uid) { /* should not happen */
- purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", uid);
+ if (strtol(dest_uid, NULL, 10) != qd->uid) { /* should not happen */
+ purple_debug_error("QQ", "Add buddy reply is to [%s], not me!", dest_uid);
g_strfreev(segments);
return;
}
- if (strtol(reply, NULL, 10) > 0) { /* need auth */
- purple_debug_warning("QQ", "Add buddy attempt fails, need authentication\n");
- nombre = uid_to_purple_name(for_uid);
- b = purple_find_buddy(gc->account, nombre);
- if (b != NULL)
- purple_blist_remove_buddy(b);
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = for_uid;
- msg = g_strdup_printf(_("%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);
- g_free(msg);
- g_free(nombre);
- } else { /* add OK */
- qq_add_buddy_by_recv_packet(gc, for_uid, TRUE, TRUE);
- msg = g_strdup_printf(_("Add into %d's buddy list"), for_uid);
- purple_notify_info(gc, _("QQ Buddy"), _("Successed:"), msg);
- g_free(msg);
- }
- g_strfreev(segments);
-}
+ if (strtol(reply, NULL, 10) == 0) {
+ /* add OK */
+ qq_buddy_find_or_new(gc, uid);
-PurpleGroup *qq_get_purple_group(const gchar *group_name)
-{
- PurpleGroup *g;
+ qq_request_buddy_info(gc, uid, 0, 0);
+ if (qd->client_version >= 2007) {
+ qq_request_get_level_2007(gc, uid);
+ } else {
+ qq_request_get_level(gc, uid);
+ }
+ qq_request_get_buddies_online(gc, 0, 0);
- g_return_val_if_fail(group_name != NULL, NULL);
+ purple_debug_info("QQ", "Successed adding into %d's buddy list", uid);
+ g_strfreev(segments);
+ return;
+ }
- g = purple_find_group(group_name);
- if (g == NULL) {
- g = purple_group_new(group_name);
- purple_blist_add_group(g, NULL);
- purple_debug_warning("QQ", "Add new group: %s\n", group_name);
+ /* need auth */
+ purple_debug_warning("QQ", "Failed adding buddy, need authorize\n");
+
+ buddy = qq_buddy_find(gc, uid);
+ if (buddy == NULL) {
+ buddy = qq_buddy_new(gc, uid);
+ }
+ if (buddy != NULL && buddy->proto_data != NULL) {
+ /* Not authorized now, free buddy data */
+ qq_buddy_data_free(buddy->proto_data);
+ buddy->proto_data = NULL;
}
- return g;
+ add_buddy_authorize_input(gc, uid, NULL, 0);
+ g_strfreev(segments);
}
-/* we add new buddy, if the received packet is from someone not in my list
- * return the PurpleBuddy that is just created */
-PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create)
+void qq_process_add_buddy_no_auth_ex(PurpleConnection *gc,
+ guint8 *data, gint data_len, guint32 uid)
{
- PurpleAccount *a;
- PurpleBuddy *b;
- PurpleGroup *g;
qq_data *qd;
- qq_buddy *q_bud;
- gchar *name, *group_name;
+ gint bytes;
+ guint32 dest_uid;
+ guint8 reply;
+ guint8 auth_type;
+
+ g_return_if_fail(data != NULL && data_len >= 5);
+ g_return_if_fail(uid != 0);
- a = gc->account;
qd = (qq_data *) gc->proto_data;
- 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 = qq_get_purple_group(group_name);
-
- name = uid_to_purple_name(uid);
- b = purple_find_buddy(gc->account, name);
- /* remove old, we can not simply return here
- * because there might be old local copy of this buddy */
- if (b != NULL)
- purple_blist_remove_buddy(b);
-
- b = purple_buddy_new(a, name, NULL);
-
- if (!create)
- b->proto_data = NULL;
- else {
- q_bud = g_new0(qq_buddy, 1);
- q_bud->uid = uid;
- b->proto_data = q_bud;
- qd->buddies = g_list_append(qd->buddies, q_bud);
- qq_send_packet_get_info(gc, q_bud->uid, FALSE);
+
+ purple_debug_info("QQ", "Process buddy add no auth for id [%d]\n", uid);
+ qq_show_packet("buddy_add_no_auth_ex", data, data_len);
+
+ bytes = 0;
+ bytes += qq_get32(&dest_uid, data + bytes);
+ bytes += qq_get8(&reply, data + bytes);
+
+ g_return_if_fail(dest_uid == uid);
+
+ if (reply == 0x99) {
+ purple_debug_info("QQ", "Successed adding buddy %d\n", uid);
+ qq_buddy_find_or_new(gc, uid);
+
+ qq_request_buddy_info(gc, uid, 0, 0);
+ if (qd->client_version >= 2007) {
+ qq_request_get_level_2007(gc, uid);
+ } else {
+ qq_request_get_level(gc, uid);
+ }
qq_request_get_buddies_online(gc, 0, 0);
+ return;
}
- purple_blist_add_buddy(b, NULL, g, NULL);
- purple_debug_warning("QQ", "Add new buddy: [%s]\n", name);
+ if (reply != 0) {
+ purple_debug_info("QQ", "Failed adding buddy %d, Unknow reply 0x%02X\n",
+ uid, reply);
+ }
- g_free(name);
- g_free(group_name);
+ /* need auth */
+ g_return_if_fail(data_len > bytes);
+ bytes += qq_get8(&auth_type, data + bytes);
+ purple_debug_warning("QQ", "Adding buddy needs authorize 0x%02X\n", auth_type);
- return b;
+ switch (auth_type) {
+ case 0x00: /* no authorize */
+ break;
+ case 0x01: /* authorize */
+ qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, uid);
+ break;
+ case 0x02: /* disable */
+ break;
+ case 0x03: /* answer question */
+ qq_request_question(gc, QQ_QUESTION_REQUEST, uid, NULL, NULL);
+ break;
+ default:
+ g_return_if_reached();
+ break;
+ }
+ return;
}
-/* add a buddy and send packet to QQ server
- * note that when purple load local cached buddy list into its blist
- * it also calls this funtion, so we have to
- * define qd->is_login=TRUE AFTER serv_finish_login(gc) */
-void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+/* remove a buddy and send packet to QQ server accordingly */
+void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
qq_data *qd;
guint32 uid;
- PurpleBuddy *b;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ g_return_if_fail(buddy != NULL);
qd = (qq_data *) gc->proto_data;
if (!qd->is_login)
- return; /* IMPORTANT ! */
+ return;
uid = purple_name_to_uid(buddy->name);
- if (uid > 0)
- _qq_send_packet_add_buddy(gc, uid);
- else {
- b = purple_find_buddy(gc->account, buddy->name);
- if (b != NULL)
- purple_blist_remove_buddy(b);
- purple_notify_error(gc, NULL,
- _("QQ Number Error"),
- _("Invalid QQ Number"));
+ if (uid > 0 && uid != qd->uid) {
+ if (qd->client_version > 2005) {
+ qq_request_auth_code(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_REMOVE_BUDDY, uid);
+ } else {
+ request_remove_buddy(gc, uid);
+ request_buddy_remove_me(gc, uid);
+ }
}
+
+ if (buddy->proto_data) {
+ qq_buddy_data_free(buddy->proto_data);
+ buddy->proto_data = NULL;
+ } else {
+ purple_debug_warning("QQ", "Empty buddy data of %s\n", buddy->name);
+ }
+
+ /* Do not call purple_blist_remove_buddy,
+ * otherwise purple segmentation fault */
}
-/* remove a buddy and send packet to QQ server accordingly */
-void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
+static void buddy_add_input(PurpleConnection *gc, guint32 uid, gchar *reason)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ qq_buddy_req *add_req;
+ gchar *who;
+
+ g_return_if_fail(uid != 0 && reason != NULL);
+
+ purple_debug_info("QQ", "Buddy %d request adding, msg: %s\n", uid, reason);
+
+ add_req = g_new0(qq_buddy_req, 1);
+ add_req->gc = gc;
+ add_req->uid = uid;
+
+ if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) {
+ qq_request_buddy_info(gc, add_req->uid, 0, QQ_BUDDY_INFO_DISPLAY);
+ }
+ who = uid_to_purple_name(add_req->uid);
+
+ purple_account_request_authorization(account,
+ who, NULL,
+ NULL, reason,
+ purple_find_buddy(account, who) != NULL,
+ buddy_add_authorize_cb,
+ buddy_add_deny_cb,
+ add_req);
+
+ g_free(who);
+}
+
+/* someone wants to add you to his buddy list */
+static void server_buddy_add_request(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
+{
+ guint32 uid;
+ gchar *msg, *reason;
+
+ g_return_if_fail(from != NULL && to != NULL);
+ uid = strtol(from, NULL, 10);
+ g_return_if_fail(uid != 0);
+
+ if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) {
+ qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
+ }
+
+ if (data_len <= 0) {
+ reason = g_strdup( _("No reason given") );
+ } else {
+ msg = g_strndup((gchar *)data, data_len);
+ reason = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+ if (reason == NULL) reason = g_strdup( _("Unknown reason") );
+ g_free(msg);
+ }
+
+ buddy_add_input(gc, uid, reason);
+ g_free(reason);
+}
+
+void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len)
{
qq_data *qd;
- PurpleBuddy *b;
- qq_buddy *q_bud;
+ gint bytes;
+ guint8 cmd;
+ guint8 reply;
guint32 uid;
+ guint16 flag1, flag2;
+
+ g_return_if_fail(data != NULL && data_len >= 5);
+ g_return_if_fail(uid != 0);
qd = (qq_data *) gc->proto_data;
- uid = purple_name_to_uid(buddy->name);
- if (!qd->is_login)
+ qq_show_packet("buddy_check_code", data, data_len);
+
+ bytes = 0;
+ bytes += qq_get8(&cmd, data + bytes); /* 0x03 */
+ bytes += qq_get8(&reply, data + bytes);
+
+ if (reply == 0) {
+ purple_debug_info("QQ", "Failed checking code\n");
return;
+ }
+
+ bytes += qq_get32(&uid, data + bytes);
+ g_return_if_fail(uid != 0);
+ bytes += qq_get16(&flag1, data + bytes);
+ bytes += qq_get16(&flag2, data + bytes);
+ purple_debug_info("QQ", "Check code reply Ok, uid %d, flag 0x%04X-0x%04X\n",
+ uid, flag1, flag2);
+ return;
+}
+
+static void request_buddy_check_code(PurpleConnection *gc,
+ gchar *from, guint8 *code, gint code_len)
+{
+ guint8 *raw_data;
+ gint bytes;
+ guint32 uid;
+
+ g_return_if_fail(code != NULL && code_len > 0 && from != NULL);
+
+ uid = strtol(from, NULL, 10);
+ raw_data = g_newa(guint8, code_len + 16);
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, 0x03);
+ bytes += qq_put8(raw_data + bytes, 0x01);
+ bytes += qq_put32(raw_data + bytes, uid);
+ bytes += qq_put16(raw_data + bytes, code_len);
+ bytes += qq_putdata(raw_data + bytes, code, code_len);
+
+ qq_send_cmd(gc, QQ_CMD_BUDDY_CHECK_CODE, raw_data, bytes);
+}
+
+static gint server_buddy_check_code(PurpleConnection *gc,
+ gchar *from, guint8 *data, gint data_len)
+{
+ gint bytes;
+ guint16 code_len;
+ guint8 *code;
+
+ g_return_val_if_fail(data != NULL && data_len > 0, 0);
+
+ bytes = 0;
+ bytes += qq_get16(&code_len, data + bytes);
+ if (code_len <= 0) {
+ purple_debug_info("QQ", "Server msg for buddy has no code\n");
+ return bytes;
+ }
+ if (bytes + code_len < data_len) {
+ purple_debug_error("QQ", "Code len error in server msg for buddy\n");
+ qq_show_packet("server_buddy_check_code", data, data_len);
+ code_len = data_len - bytes;
+ }
+ code = g_newa(guint8, code_len);
+ bytes += qq_getdata(code, code_len, data + bytes);
+
+ request_buddy_check_code(gc, from, code, code_len);
+ return bytes;
+}
+
+static void server_buddy_add_request_ex(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
+{
+ gint bytes;
+ guint32 uid;
+ gchar *msg;
+ guint8 allow_reverse;
+
+ g_return_if_fail(from != NULL && to != NULL);
+ g_return_if_fail(data != NULL && data_len >= 3);
+ uid = strtol(from, NULL, 10);
+ g_return_if_fail(uid != 0);
+
+ /* qq_show_packet("server_buddy_add_request_ex", data, data_len); */
- if (uid > 0)
- _qq_send_packet_remove_buddy(gc, uid);
+ bytes = 0;
+ bytes += qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data+bytes);
+ bytes += qq_get8(&allow_reverse, data + bytes); /* allow_reverse = 0x01, allowed */
+ server_buddy_check_code(gc, from, data + bytes, data_len - bytes);
- b = purple_find_buddy(gc->account, buddy->name);
- if (b != NULL) {
- q_bud = (qq_buddy *) b->proto_data;
- if (q_bud != NULL)
- qd->buddies = g_list_remove(qd->buddies, q_bud);
- else
- purple_debug_warning("QQ", "We have no qq_buddy record for %s\n", buddy->name);
- /* remove buddy on blist, this does not trigger qq_remove_buddy again
- * do this only if the request comes from block request,
- * otherwise purple segmentation fault */
- if (g_ascii_strcasecmp(group->name, PURPLE_GROUP_QQ_BLOCKED) == 0)
- purple_blist_remove_buddy(b);
+ if (strlen(msg) <= 0) {
+ g_free(msg);
+ msg = g_strdup( _("No reason given") );
}
+ buddy_add_input(gc, uid, msg);
+ g_free(msg);
}
-/* free add buddy request queue */
-void qq_add_buddy_request_free(qq_data *qd)
+/* when you are added by a person, QQ server will send sys message */
+static void server_buddy_added(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
{
- gint count;
- qq_add_buddy_request *p;
+ PurpleAccount *account = purple_connection_get_account(gc);
+ PurpleBuddy *buddy;
+ guint32 uid;
+ qq_buddy_req *add_req;
+ gchar *who;
+ gchar *primary;
+
+ g_return_if_fail(from != NULL && to != NULL);
- count = 0;
- while (qd->add_buddy_request != NULL) {
- p = (qq_add_buddy_request *) (qd->add_buddy_request->data);
- qd->add_buddy_request = g_list_remove(qd->add_buddy_request, p);
- g_free(p);
- count++;
+ uid = strtol(from, NULL, 10);
+ who = uid_to_purple_name(uid);
+
+ buddy = purple_find_buddy(account, who);
+ if (buddy != NULL) {
+ purple_account_notify_added(account, from, to, NULL, NULL);
}
- if (count > 0) {
- purple_debug_info("QQ", "%d add buddy requests are freed!\n", count);
+
+ add_req = g_new0(qq_buddy_req, 1);
+ add_req->gc = gc;
+ add_req->uid = uid; /* only need to get value */
+ primary = g_strdup_printf(_("You have been added by %s"), from);
+ purple_request_action(gc, NULL, primary,
+ _("Would you like to add him?"),
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(gc), who, NULL,
+ add_req, 2,
+ _("Add"), G_CALLBACK(add_buddy_no_auth_cb),
+ _("Cancel"), G_CALLBACK(buddy_req_cancel_cb));
+
+ g_free(who);
+ g_free(primary);
+}
+
+static void server_buddy_added_ex(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
+{
+ gint bytes;
+ guint8 allow_reverse;
+ gchar *msg;
+
+ g_return_if_fail(from != NULL && to != NULL);
+ g_return_if_fail(data != NULL && data_len >= 3);
+
+ qq_show_packet("server_buddy_added_ex", data, data_len);
+
+ bytes = 0;
+ bytes += qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data+bytes); /* always empty msg */
+ purple_debug_info("QQ", "Buddy added msg: %s\n", msg);
+ bytes += qq_get8(&allow_reverse, data + bytes); /* allow_reverse = 0x01, allowed */
+ server_buddy_check_code(gc, from, data + bytes, data_len - bytes);
+
+ g_free(msg);
+}
+
+static void server_buddy_adding_ex(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
+{
+ gint bytes;
+ guint8 allow_reverse;
+
+ g_return_if_fail(from != NULL && to != NULL);
+ g_return_if_fail(data != NULL && data_len >= 3);
+
+ qq_show_packet("server_buddy_adding_ex", data, data_len);
+
+ bytes = 0;
+ bytes += qq_get8(&allow_reverse, data + bytes); /* allow_reverse = 0x01, allowed */
+ server_buddy_check_code(gc, from, data + bytes, data_len - bytes);
+}
+
+/* the buddy approves your request of adding him/her as your friend */
+static void server_buddy_added_me(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
+{
+ PurpleAccount *account = purple_connection_get_account(gc);
+ qq_data *qd;
+ guint32 uid;
+
+ g_return_if_fail(from != NULL && to != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+
+ uid = strtol(from, NULL, 10);
+ g_return_if_fail(uid > 0);
+
+ server_buddy_check_code(gc, from, data, data_len);
+
+ qq_buddy_find_or_new(gc, uid);
+ qq_request_buddy_info(gc, uid, 0, 0);
+ qq_request_get_buddies_online(gc, 0, 0);
+ if (qd->client_version >= 2007) {
+ qq_request_get_level_2007(gc, uid);
+ } else {
+ qq_request_get_level(gc, uid);
}
+
+ purple_account_notify_added(account, to, from, NULL, NULL);
}
-/* free up all qq_buddy */
-void qq_buddies_list_free(PurpleAccount *account, qq_data *qd)
+/* you are rejected by the person */
+static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
{
- gint count;
- qq_buddy *p;
- gchar *name;
- PurpleBuddy *b;
+ guint32 uid;
+ PurpleBuddy *buddy;
+ gchar *msg, *msg_utf8;
+ gint bytes;
+ gchar **segments;
+ gchar *primary, *secondary;
+
+ g_return_if_fail(from != NULL && to != NULL);
- count = 0;
- while (qd->buddies) {
- p = (qq_buddy *) (qd->buddies->data);
- qd->buddies = g_list_remove(qd->buddies, p);
- name = uid_to_purple_name(p->uid);
- b = purple_find_buddy(account, name);
- if(b != NULL)
- b->proto_data = NULL;
- else
- purple_debug_info("QQ", "qq_buddy %s not found in purple proto_data\n", name);
- g_free(name);
+ qq_show_packet("server_buddy_rejected_me", data, data_len);
- g_free(p);
- count++;
+ if (data_len <= 0) {
+ msg = g_strdup( _("No reason given") );
+ } else {
+ segments = g_strsplit((gchar *)data, "\x1f", 1);
+ if (segments != NULL && segments[0] != NULL) {
+ msg = g_strdup(segments[0]);
+ g_strfreev(segments);
+ bytes = strlen(msg) + 1;
+ if (bytes < data_len) {
+ server_buddy_check_code(gc, from, data + bytes, data_len - bytes);
+ }
+ } else {
+ msg = g_strdup( _("No reason given") );
+ }
+ }
+ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+ if (msg_utf8 == NULL) {
+ msg_utf8 = g_strdup( _("Unknown reason") );
+ }
+ g_free(msg);
+
+ primary = g_strdup_printf(_("Rejected by %s"), from);
+ secondary = g_strdup_printf(_("Message: %s"), msg_utf8);
+
+ purple_notify_info(gc, _("QQ Buddy"), primary, secondary);
+
+ g_free(msg_utf8);
+ g_free(primary);
+ g_free(secondary);
+
+ uid = strtol(from, NULL, 10);
+ g_return_if_fail(uid != 0);
+
+ buddy = qq_buddy_find(gc, uid);
+ if (buddy != NULL && buddy->proto_data != NULL) {
+ /* Not authorized now, free buddy data */
+ qq_buddy_data_free(buddy->proto_data);
+ buddy->proto_data = NULL;
}
- if (count > 0) {
- purple_debug_info("QQ", "%d qq_buddy structures are freed!\n", count);
+}
+
+void qq_process_buddy_from_server(PurpleConnection *gc, int funct,
+ gchar *from, gchar *to, guint8 *data, gint data_len)
+{
+ switch (funct) {
+ case QQ_SERVER_BUDDY_ADDED:
+ server_buddy_added(gc, from, to, data, data_len);
+ break;
+ case QQ_SERVER_BUDDY_ADD_REQUEST:
+ server_buddy_add_request(gc, from, to, data, data_len);
+ break;
+ case QQ_SERVER_BUDDY_ADD_REQUEST_EX:
+ server_buddy_add_request_ex(gc, from, to, data, data_len);
+ break;
+ case QQ_SERVER_BUDDY_ADDED_ME:
+ server_buddy_added_me(gc, from, to, data, data_len);
+ break;
+ case QQ_SERVER_BUDDY_REJECTED_ME:
+ server_buddy_rejected_me(gc, from, to, data, data_len);
+ break;
+ case QQ_SERVER_BUDDY_ADDED_EX:
+ server_buddy_added_ex(gc, from, to, data, data_len);
+ break;
+ case QQ_SERVER_BUDDY_ADDING_EX:
+ case QQ_SERVER_BUDDY_ADDED_ANSWER:
+ server_buddy_adding_ex(gc, from, to, data, data_len);
+ break;
+ default:
+ purple_debug_warning("QQ", "Unknow buddy operate (%d) from server\n", funct);
+ break;
}
}
diff --git a/libpurple/protocols/qq/buddy_opt.h b/libpurple/protocols/qq/buddy_opt.h
index af6cc7d8cb..f485a95422 100644
--- a/libpurple/protocols/qq/buddy_opt.h
+++ b/libpurple/protocols/qq/buddy_opt.h
@@ -30,33 +30,55 @@
#include "qq.h"
-typedef struct _gc_and_uid gc_and_uid;
+enum {
+ QQ_AUTH_INFO_BUDDY = 0x01,
+ QQ_AUTH_INFO_ROOM = 0x02,
-struct _gc_and_uid {
- guint32 uid;
- PurpleConnection *gc;
+ QQ_AUTH_INFO_ADD_BUDDY = 0x0001,
+ QQ_AUTH_INFO_TEMP_SESSION = 0x0003,
+ QQ_AUTH_INFO_CLUSTER = 0x0002,
+ QQ_AUTH_INFO_REMOVE_BUDDY = 0x0006,
+ QQ_AUTH_INFO_UPDATE_BUDDY_INFO = 0x0007,
};
-void qq_approve_add_request_with_gc_and_uid(gc_and_uid *g);
-void qq_reject_add_request_with_gc_and_uid(gc_and_uid *g);
+enum {
+ QQ_QUESTION_GET = 0x01,
+ QQ_QUESTION_SET = 0x02,
+ QQ_QUESTION_REQUEST = 0x03, /* get question only*/
+ QQ_QUESTION_ANSWER = 0x04,
+};
-void qq_add_buddy_with_gc_and_uid(gc_and_uid *g);
-void qq_block_buddy_with_gc_and_uid(gc_and_uid *g);
+void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void qq_change_buddys_group(PurpleConnection *gc, const char *who,
+ const char *old_group, const char *new_group);
+void qq_remove_buddy_and_me(PurpleBlistNode * node);
+void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
-void qq_do_nothing_with_gc_and_uid(gc_and_uid *g, const gchar *msg);
+void qq_process_remove_buddy(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+void qq_process_buddy_remove_me(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+void qq_process_add_buddy_no_auth(PurpleConnection *gc,
+ guint8 *data, gint data_len, guint32 uid);
+void qq_process_add_buddy_no_auth_ex(PurpleConnection *gc,
+ guint8 *data, gint data_len, guint32 uid);
+void qq_process_add_buddy_auth(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_process_buddy_from_server(PurpleConnection *gc, int funct,
+ gchar *from, gchar *to, guint8 *data, gint data_len);
-void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, PurpleConnection *gc);
-void qq_process_remove_self_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-void qq_process_add_buddy_reply(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
-void qq_process_add_buddy_auth_reply(guint8 *data, gint data_len, PurpleConnection *gc);
-PurpleBuddy *qq_add_buddy_by_recv_packet(PurpleConnection *gc, guint32 uid, gboolean is_known, gboolean create);
-void qq_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
+void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len);
-PurpleGroup *qq_get_purple_group(const gchar *group_name);
+void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid);
+void qq_process_auth_code(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
+void qq_request_question(PurpleConnection *gc,
+ guint8 cmd, guint32 uid, const gchar *question_utf8, const gchar *answer_utf8);
+void qq_process_question(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid);
-void qq_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group);
-void qq_add_buddy_request_free(qq_data *qd);
+void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32);
-void qq_buddies_list_free(PurpleAccount *account, qq_data *qd);
+qq_buddy_data *qq_buddy_data_find(PurpleConnection *gc, guint32 uid);
+void qq_buddy_data_free(qq_buddy_data *bd);
+PurpleBuddy *qq_buddy_new(PurpleConnection *gc, guint32 uid);
+PurpleBuddy *qq_buddy_find_or_new(PurpleConnection *gc, guint32 uid);
+PurpleBuddy *qq_buddy_find(PurpleConnection *gc, guint32 uid);
+PurpleGroup *qq_group_find_or_new(const gchar *group_name);
#endif
diff --git a/libpurple/protocols/qq/char_conv.c b/libpurple/protocols/qq/char_conv.c
index c29a273a5e..c121bb0160 100644
--- a/libpurple/protocols/qq/char_conv.c
+++ b/libpurple/protocols/qq/char_conv.c
@@ -37,7 +37,7 @@
#define QQ_CHARSET_ENG "ISO-8859-1"
#define QQ_NULL_MSG "(NULL)" /* return this if conversion fails */
-#define QQ_NULL_SMILEY "(Broken)" /* return this if smiley conversion fails */
+#define QQ_NULL_SMILEY "<IMG ID=\"0\">" /* return this if smiley conversion fails */
const gchar qq_smiley_map[QQ_SMILEY_AMOUNT] = {
0x41, 0x43, 0x42, 0x44, 0x45, 0x46, 0x47, 0x48,
@@ -98,7 +98,7 @@ static gboolean _check_underline(gchar font_attr)
}
/* convert a string from from_charset to to_charset, using g_convert */
-static gchar *_my_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset)
+static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset)
{
GError *error = NULL;
gchar *ret;
@@ -128,20 +128,42 @@ static gchar *_my_convert(const gchar *str, gssize len, const gchar *to_charset,
* returns the number of bytes read, return -1 if fatal error
* the converted UTF-8 will be saved in ret
*/
-gint convert_as_pascal_string(guint8 *data, gchar **ret, const gchar *from_charset)
+gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data)
{
guint8 len;
g_return_val_if_fail(data != NULL && from_charset != NULL, -1);
len = data[0];
- *ret = _my_convert((gchar *) (data + 1), (gssize) len, UTF8, from_charset);
+ if (len == 0) {
+ *ret = g_strdup("");
+ return 1;
+ }
+ *ret = do_convert((gchar *) (data + 1), (gssize) len, UTF8, from_charset);
return len + 1;
}
+gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset)
+{
+ gchar *str;
+ guint8 len;
+
+ if (str_utf8 == NULL || (len = strlen(str_utf8)) == 0) {
+ buf[0] = 0;
+ return 1;
+ }
+ str = do_convert(str_utf8, -1, to_charset, UTF8);
+ len = strlen(str_utf8);
+ buf[0] = len;
+ if (len > 0) {
+ memcpy(buf + 1, str, len);
+ }
+ return 1 + len;
+}
+
/* convert QQ formatted msg to Purple formatted msg (and UTF-8) */
-gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
+gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg, const gint client_version)
{
GString *encoded;
guint8 font_attr, font_size, color[3], bar;
@@ -211,15 +233,15 @@ gchar *qq_encode_to_purple(guint8 *data, gint len, const gchar *msg)
return ret;
}
-/* two convenience methods, using _my_convert */
+/* two convenience methods, using do_convert */
gchar *utf8_to_qq(const gchar *str, const gchar *to_charset)
{
- return _my_convert(str, -1, to_charset, UTF8);
+ return do_convert(str, -1, to_charset, UTF8);
}
gchar *qq_to_utf8(const gchar *str, const gchar *from_charset)
{
- return _my_convert(str, -1, UTF8, from_charset);
+ return do_convert(str, -1, UTF8, from_charset);
}
/* QQ uses binary code for smiley, while purple uses strings.
@@ -232,8 +254,10 @@ gchar *qq_smiley_to_purple(gchar *text)
converted = g_string_new("");
segments = split_data((guint8 *) text, strlen(text), "\x14\x15", 0);
- g_string_append(converted, segments[0]);
+ if(segments == NULL)
+ return NULL;
+ g_string_append(converted, segments[0]);
while ((*(++segments)) != NULL) {
cur_seg = *segments;
qq_smiley = cur_seg[0];
@@ -291,5 +315,3 @@ void qq_filter_str(gchar *str) {
}
g_strstrip(str);
}
-
-
diff --git a/libpurple/protocols/qq/char_conv.h b/libpurple/protocols/qq/char_conv.h
index 1d6c90bd38..1d358ebf7c 100644
--- a/libpurple/protocols/qq/char_conv.h
+++ b/libpurple/protocols/qq/char_conv.h
@@ -29,15 +29,15 @@
#define QQ_CHARSET_DEFAULT "GB18030"
-gint convert_as_pascal_string(guint8 *data, gchar **ret, const gchar *from_charset);
+gint qq_get_vstr(gchar **ret, const gchar *from_charset, guint8 *data);
+gint qq_put_vstr(guint8 *buf, const gchar *str_utf8, const gchar *to_charset);
gchar *qq_smiley_to_purple(gchar *text);
-
gchar *purple_smiley_to_qq(gchar *text);
gchar *utf8_to_qq(const gchar *str, const gchar *to_charset);
gchar *qq_to_utf8(const gchar *str, const gchar *from_charset);
-gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg);
+gchar *qq_encode_to_purple(guint8 *font_attr_data, gint len, const gchar *msg, const gint client_version);
gchar *qq_im_filter_html(const gchar *text);
void qq_filter_str(gchar *str);
diff --git a/libpurple/protocols/qq/file_trans.c b/libpurple/protocols/qq/file_trans.c
index 5f0993ca5c..bbcdb8da68 100644
--- a/libpurple/protocols/qq/file_trans.c
+++ b/libpurple/protocols/qq/file_trans.c
@@ -30,7 +30,7 @@
#include "qq_crypt.h"
#include "file_trans.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "im.h"
#include "packet_parse.h"
#include "proxy.h"
@@ -81,7 +81,7 @@ static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5)
const gint QQ_MAX_FILE_MD5_LENGTH = 10002432;
g_return_if_fail(filename != NULL && md5 != NULL);
- if (filelen > QQ_MAX_FILE_MD5_LENGTH)
+ if (filelen > QQ_MAX_FILE_MD5_LENGTH)
filelen = QQ_MAX_FILE_MD5_LENGTH;
fp = fopen(filename, "rb");
@@ -161,7 +161,7 @@ static int _qq_xfer_open_file(const gchar *filename, const gchar *method, Purple
fd = open(purple_xfer_get_local_filename(xfer), O_RDONLY);
info->buffer = mmap(0, purple_xfer_get_size(xfer), PROT_READ, MAP_PRIVATE, fd, 0);
}
- else
+ else
{
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);
@@ -248,7 +248,7 @@ static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16
file_key = _gen_file_key();
bytes += qq_put8(raw_data + bytes, packet_type);
- bytes += qq_put16(raw_data + bytes, QQ_CLIENT);
+ bytes += qq_put16(raw_data + bytes, qd->client_tag);
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));
@@ -266,7 +266,7 @@ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32
{
qq_data *qd;
gint bytes, bytes_expected, encrypted_len;
- guint8 *raw_data, *encrypted_data;
+ guint8 *raw_data, *encrypted;
time_t now;
ft_info *info;
@@ -334,19 +334,19 @@ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32
raw_data, bytes,
"sending packet[%s]:", qq_get_file_cmd_desc(packet_type));
- encrypted_data = g_newa(guint8, bytes + 16);
- encrypted_len = qq_encrypt(encrypted_data, raw_data, bytes, info->file_session_key);
+ encrypted = g_newa(guint8, bytes + 16);
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, info->file_session_key);
/*debug: try to decrypt it */
#if 0
guint8 *buf;
int buflen;
- hex_dump = hex_dump_to_str(encrypted_data, encrypted_len);
+ hex_dump = hex_dump_to_str(encrypted, encrypted_len);
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)) {
+ if (qq_crypt(DECRYPT, encrypted, encrypted_len, info->file_session_key, buf, &buflen)) {
purple_debug_info("QQ", "decrypt success\n");
if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0)
purple_debug_info("QQ", "checksum ok\n");
@@ -360,11 +360,11 @@ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32
#endif
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);
+ _qq_send_file(gc, encrypted, 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,
+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];
@@ -402,11 +402,11 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
_fill_file_md5(purple_xfer_get_local_filename(qd->xfer),
purple_xfer_get_size(qd->xfer),
file_md5);
-
+
info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1;
info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN;
- purple_debug_info("QQ",
+ purple_debug_info("QQ",
"start transfering data, %d fragments with %d length each\n",
info->fragment_num, info->fragment_len);
/* Unknown */
@@ -431,7 +431,7 @@ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type,
filename_len);
break;
case QQ_FILE_DATA_INFO:
- purple_debug_info("QQ",
+ purple_debug_info("QQ",
"sending %dth fragment with length %d, offset %d\n",
fragment_index, len, (fragment_index-1)*fragment_size);
/* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */
@@ -532,7 +532,7 @@ static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data,
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);
+ 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 */
@@ -573,8 +573,8 @@ static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16
ft_info *info = (ft_info *) xfer->data;
guint32 mask;
- purple_debug_info("QQ",
- "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n",
+ purple_debug_info("QQ",
+ "receiving %dth fragment with length %d, slide window status %o, max_fragment_index %d\n",
index, len, info->window, info->max_fragment_index);
if (info->window == 0 && info->max_fragment_index == 0) {
if (_qq_xfer_open_file(purple_xfer_get_local_filename(xfer), "wb", xfer) == -1) {
@@ -605,7 +605,7 @@ static void _qq_recv_file_progess(PurpleConnection *gc, guint8 *buffer, guint16
if (mask & 0x8000) mask = 0x0001;
else mask = mask << 1;
}
- purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n",
+ purple_debug_info("QQ", "procceed %dth fragment, slide window status %o, max_fragment_index %d\n",
index, info->window, info->max_fragment_index);
}
@@ -650,10 +650,10 @@ static void _qq_update_send_progess(PurpleConnection *gc, guint32 fragment_index
PurpleXfer *xfer = qd->xfer;
ft_info *info = (ft_info *) xfer->data;
- purple_debug_info("QQ",
- "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n",
+ purple_debug_info("QQ",
+ "receiving %dth fragment ack, slide window status %o, max_fragment_index %d\n",
fragment_index, info->window, info->max_fragment_index);
- if (fragment_index < info->max_fragment_index ||
+ if (fragment_index < info->max_fragment_index ||
fragment_index >= info->max_fragment_index + sizeof(info->window)) {
purple_debug_info("QQ", "duplicate %dth fragment, drop it!\n", fragment_index+1);
return;
@@ -681,7 +681,7 @@ static void _qq_update_send_progess(PurpleConnection *gc, guint32 fragment_index
info->window &= ~mask;
buffer = g_newa(guint8, info->fragment_len);
- readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window),
+ readbytes = _qq_xfer_read_file(buffer, info->max_fragment_index + sizeof(info->window),
info->fragment_len, xfer);
if (readbytes > 0)
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_DATA_INFO,
@@ -692,8 +692,8 @@ static void _qq_update_send_progess(PurpleConnection *gc, guint32 fragment_index
else mask = mask << 1;
}
}
- purple_debug_info("QQ",
- "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n",
+ purple_debug_info("QQ",
+ "procceed %dth fragment ack, slide window status %o, max_fragment_index %d\n",
fragment_index, info->window, info->max_fragment_index);
}
@@ -727,13 +727,13 @@ static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint
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
+ /* 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;
- purple_debug_info("QQ",
+ purple_debug_info("QQ",
"start receiving data, %d fragments with %d length each\n",
info->fragment_num, info->fragment_len);
_qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type,
@@ -743,7 +743,7 @@ static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint
bytes += qq_get32(&fragment_index, data + bytes);
bytes += qq_get32(&fragment_offset, data + bytes);
bytes += qq_get16(&fragment_len, data + bytes);
- purple_debug_info("QQ",
+ purple_debug_info("QQ",
"received %dth fragment with length %d, offset %d\n",
fragment_index, fragment_len, fragment_offset);
diff --git a/libpurple/protocols/qq/group.c b/libpurple/protocols/qq/group.c
index 206b86c236..8962e12c45 100644
--- a/libpurple/protocols/qq/group.c
+++ b/libpurple/protocols/qq/group.c
@@ -30,20 +30,19 @@
#include "group_internal.h"
#include "group_info.h"
-#include "group_search.h"
+#include "group_join.h"
#include "utils.h"
#include "qq_network.h"
-#include "header_info.h"
-#include "group.h"
+#include "qq_define.h"
static void _qq_group_search_callback(PurpleConnection *gc, const gchar *input)
{
guint32 ext_id;
g_return_if_fail(input != NULL);
- ext_id = qq_string_to_dec_value(input);
+ ext_id = strtol(input, NULL, 10);
/* 0x00000000 means search for demo group */
- qq_send_cmd_group_search_group(gc, ext_id);
+ qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_ONLY);
}
static void _qq_group_search_cancel_callback(PurpleConnection *gc, const gchar *input)
@@ -98,20 +97,8 @@ PurpleRoomlist *qq_roomlist_get_list(PurpleConnection *gc)
f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Group ID"), QQ_ROOM_KEY_EXTERNAL_ID, FALSE);
fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Creator"), QQ_ROOM_KEY_CREATOR_UID, FALSE);
- fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING,
- _("Group Description"), QQ_ROOM_KEY_DESC_UTF8, FALSE);
- fields = g_list_append(fields, f);
f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_INTERNAL_ID, TRUE);
fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_TYPE, TRUE);
- fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Auth"), QQ_ROOM_KEY_AUTH_TYPE, TRUE);
- fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_CATEGORY, TRUE);
- fields = g_list_append(fields, f);
- f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", QQ_ROOM_KEY_TITLE_UTF8, TRUE);
fields = g_list_append(fields, f);
purple_roomlist_set_fields(rl, fields);
@@ -142,43 +129,3 @@ void qq_roomlist_cancel(PurpleRoomlist *list)
purple_roomlist_set_in_progress(list, FALSE);
purple_roomlist_unref(list);
}
-
-/* this should be called upon signin, even when we did not open group chat window */
-void qq_group_init(PurpleConnection *gc)
-{
- PurpleAccount *account;
- PurpleChat *chat;
- PurpleGroup *purple_group;
- PurpleBlistNode *node;
- qq_group *group;
- gint count;
-
- account = purple_connection_get_account(gc);
-
- purple_group = purple_find_group(PURPLE_GROUP_QQ_QUN);
- if (purple_group == NULL) {
- purple_debug_info("QQ", "We have no QQ Qun\n");
- return;
- }
-
- count = 0;
- for (node = ((PurpleBlistNode *) purple_group)->child; node != NULL; node = node->next) {
- if ( !PURPLE_BLIST_NODE_IS_CHAT(node)) {
- continue;
- }
- /* got one */
- chat = (PurpleChat *) node;
- if (account != chat->account) /* not qq account*/
- continue;
- group = qq_room_create_by_hashtable(gc, chat->components);
- if (group == NULL)
- continue;
-
- if (group->id <= 0)
- continue;
-
- count++;
- }
-
- purple_debug_info("QQ", "Load %d QQ Qun configurations\n", count);
-}
diff --git a/libpurple/protocols/qq/group.h b/libpurple/protocols/qq/group.h
index f36aed7af1..8688205a05 100644
--- a/libpurple/protocols/qq/group.h
+++ b/libpurple/protocols/qq/group.h
@@ -40,10 +40,10 @@ typedef enum {
QQ_ROOM_ROLE_ADMIN,
} qq_room_role;
-typedef struct _qq_group {
+typedef struct _qq_room_data qq_room_data;
+struct _qq_room_data {
/* all these will be saved when we exit Purple */
qq_room_role my_role; /* my role for this room */
- gchar *my_role_desc; /* my role description */
guint32 id;
guint32 ext_id;
guint8 type8; /* permanent or temporory */
@@ -54,16 +54,14 @@ typedef struct _qq_group {
gchar *desc_utf8;
/* all these will be loaded from the network */
gchar *notice_utf8; /* group notice by admin */
- GList *members;
- gboolean is_got_info;
-} qq_group;
+ gboolean is_got_buddies;
+ GList *members;
+};
GList *qq_chat_info(PurpleConnection *gc);
GHashTable *qq_chat_info_defaults(PurpleConnection *gc, const gchar *chat_name);
-void qq_group_init(PurpleConnection *gc);
-
PurpleRoomlist *qq_roomlist_get_list(PurpleConnection *gc);
void qq_roomlist_cancel(PurpleRoomlist *list);
diff --git a/libpurple/protocols/qq/group_conv.c b/libpurple/protocols/qq/group_conv.c
deleted file mode 100644
index 59e204a751..0000000000
--- a/libpurple/protocols/qq/group_conv.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/**
- * @file group_conv.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 <glib.h>
-#include "qq.h"
-
-#include "group_conv.h"
-#include "buddy_list.h"
-#include "header_info.h"
-#include "qq_network.h"
-#include "qq_process.h"
-#include "utils.h"
-
-/* show group conversation window */
-PurpleConversation *qq_room_conv_create(PurpleConnection *gc, qq_group *group)
-{
- PurpleConversation *conv;
- qq_data *qd;
-
- g_return_val_if_fail(group != NULL, NULL);
- qd = (qq_data *) gc->proto_data;
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- group->title_utf8, purple_connection_get_account(gc));
- if (conv != NULL) {
- /* show only one conversation per group */
- return conv;
- }
-
- serv_got_joined_chat(gc, qd->channel++, group->title_utf8);
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
- if (conv != NULL) {
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, group->notice_utf8);
- if (group->is_got_info)
- qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, group->id);
- else
- qq_update_room(gc, 0, group->id);
- return conv;
- }
- return NULL;
-}
-
-/* refresh online member in group conversation window */
-void qq_group_conv_refresh_online_member(PurpleConnection *gc, qq_group *group)
-{
- GList *names, *list, *flags;
- qq_buddy *member;
- gchar *member_name, *member_uid;
- PurpleConversation *conv;
- gint flag;
- g_return_if_fail(group != NULL);
-
- names = NULL;
- flags = NULL;
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- group->title_utf8, purple_connection_get_account(gc));
- if (conv != NULL && group->members != NULL) {
- list = group->members;
- while (list != NULL) {
- member = (qq_buddy *) list->data;
-
- /* we need unique identifiers for everyone in the chat or else we'll
- * run into problems with functions like get_cb_real_name from qq.c */
- member_name = (member->nickname != NULL && *(member->nickname) != '\0') ?
- g_strdup_printf("%s (qq-%u)", member->nickname, member->uid) :
- g_strdup_printf("(qq-%u)", member->uid);
- member_uid = g_strdup_printf("(qq-%u)", member->uid);
-
- flag = 0;
- /* TYPING to put online above OP and FOUNDER */
- if (is_online(member->status))
- flag |= (PURPLE_CBFLAGS_TYPING | PURPLE_CBFLAGS_VOICE);
- if(1 == (member->role & 1)) flag |= PURPLE_CBFLAGS_OP;
- if(member->uid == group->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER;
-
- if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_name))
- {
- purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
- member_name,
- flag);
- } else if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_uid))
- {
- purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
- member_uid,
- flag);
- purple_conv_chat_rename_user(PURPLE_CONV_CHAT(conv), member_uid, member_name);
- } else {
- /* always put it even offline */
- names = g_list_append(names, member_name);
- flags = g_list_append(flags, GINT_TO_POINTER(flag));
- }
- g_free(member_uid);
- list = list->next;
- }
-
- if (names != NULL && flags != NULL) {
- purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE);
- }
- }
- /* clean up names */
- while (names != NULL) {
- member_name = (gchar *) names->data;
- names = g_list_remove(names, member_name);
- g_free(member_name);
- }
- g_list_free(flags);
-}
diff --git a/libpurple/protocols/qq/group_conv.h b/libpurple/protocols/qq/group_conv.h
deleted file mode 100644
index 4824ad454c..0000000000
--- a/libpurple/protocols/qq/group_conv.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * @file group_conv.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_GROUP_CONV_H_
-#define _QQ_GROUP_CONV_H_
-
-#include "connection.h"
-#include "conversation.h"
-#include "group.h"
-
-PurpleConversation *qq_room_conv_create(PurpleConnection *gc, qq_group *group);
-void qq_group_conv_refresh_online_member(PurpleConnection *gc, qq_group *group);
-
-#endif
diff --git a/libpurple/protocols/qq/group_find.c b/libpurple/protocols/qq/group_find.c
deleted file mode 100644
index 0e26a3d18d..0000000000
--- a/libpurple/protocols/qq/group_find.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/**
- * @file group_find.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 "qq.h"
-
-#include "conversation.h"
-#include "debug.h"
-#include "util.h"
-
-#include "group_find.h"
-#include "utils.h"
-
-/* find a qq_buddy by uid, called by im.c */
-qq_buddy *qq_group_find_member_by_uid(qq_group *group, guint32 uid)
-{
- GList *list;
- qq_buddy *member;
- g_return_val_if_fail(group != NULL && uid > 0, NULL);
-
- list = group->members;
- while (list != NULL) {
- member = (qq_buddy *) list->data;
- if (member->uid == uid)
- return member;
- else
- list = list->next;
- }
-
- return NULL;
-}
-
-/* remove a qq_buddy by uid, called by qq_group_opt.c */
-void qq_group_remove_member_by_uid(qq_group *group, guint32 uid)
-{
- GList *list;
- qq_buddy *member;
- g_return_if_fail(group != NULL && uid > 0);
-
- list = group->members;
- while (list != NULL) {
- member = (qq_buddy *) list->data;
- if (member->uid == uid) {
- group->members = g_list_remove(group->members, member);
- return;
- } else {
- list = list->next;
- }
- }
-}
-
-qq_buddy *qq_group_find_or_add_member(PurpleConnection *gc, qq_group *group, guint32 member_uid)
-{
- qq_buddy *member, *q_bud;
- PurpleBuddy *buddy;
- g_return_val_if_fail(group != NULL && member_uid > 0, NULL);
-
- member = qq_group_find_member_by_uid(group, member_uid);
- if (member == NULL) { /* first appear during my session */
- member = g_new0(qq_buddy, 1);
- member->uid = member_uid;
- buddy = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(member_uid));
- if (buddy != NULL) {
- q_bud = (qq_buddy *) buddy->proto_data;
- if (q_bud != NULL && q_bud->nickname != NULL)
- member->nickname = g_strdup(q_bud->nickname);
- else if (buddy->alias != NULL)
- member->nickname = g_strdup(buddy->alias);
- }
- group->members = g_list_append(group->members, member);
- }
-
- return member;
-}
-
-/* find a qq_group by chatroom channel */
-qq_group *qq_group_find_by_channel(PurpleConnection *gc, gint channel)
-{
- PurpleConversation *conv;
- qq_data *qd;
- qq_group *group;
- GList *list;
-
- qd = (qq_data *) gc->proto_data;
-
- conv = purple_find_chat(gc, channel);
- g_return_val_if_fail(conv != NULL, NULL);
-
- list = qd->groups;
- group = NULL;
- while (list != NULL) {
- group = (qq_group *) list->data;
- if (group->title_utf8 == NULL) {
- continue;
- }
- if (!g_ascii_strcasecmp(purple_conversation_get_name(conv), group->title_utf8))
- break;
- list = list->next;
- }
-
- return group;
-}
-
-/* find a qq_group by its id, flag is QQ_INTERNAL_ID or QQ_EXTERNAL_ID */
-qq_group *qq_room_search_ext_id(PurpleConnection *gc, guint32 ext_id)
-{
- GList *list;
- qq_group *group;
- qq_data *qd;
-
- qd = (qq_data *) gc->proto_data;
-
- if (qd->groups == NULL || ext_id <= 0)
- return NULL;
-
- list = qd->groups;
- while (list != NULL) {
- group = (qq_group *) list->data;
- if (group->ext_id == ext_id) {
- return group;
- }
- list = list->next;
- }
-
- return NULL;
-}
-
-qq_group *qq_room_search_id(PurpleConnection *gc, guint32 room_id)
-{
- GList *list;
- qq_group *group;
- qq_data *qd;
-
- qd = (qq_data *) gc->proto_data;
-
- if (qd->groups == NULL || room_id <= 0)
- return NULL;
-
- list = qd->groups;
- while (list != NULL) {
- group = (qq_group *) list->data;
- if (group->id == room_id) {
- return group;
- }
- list = list->next;
- }
-
- return NULL;
-}
-
-qq_group *qq_room_get_next(PurpleConnection *gc, guint32 room_id)
-{
- GList *list;
- qq_group *group;
- qq_data *qd;
- gboolean is_find = FALSE;
-
- qd = (qq_data *) gc->proto_data;
-
- if (qd->groups == NULL) {
- return NULL;
- }
-
- if (room_id <= 0) {
- return (qq_group *) qd->groups->data;
- }
-
- list = qd->groups;
- while (list != NULL) {
- group = (qq_group *) list->data;
- list = list->next;
- if (group->id == room_id) {
- is_find = TRUE;
- break;
- }
- }
-
- if ( !is_find || list == NULL) {
- return NULL;
- }
-
- return (qq_group *)list->data;
-}
-
-qq_group *qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id)
-{
- GList *list;
- qq_group *group;
- qq_data *qd;
- gboolean is_find;
-
- qd = (qq_data *) gc->proto_data;
-
- list = qd->groups;
- if (room_id > 0) {
- /* search next room */
- is_find = FALSE;
- while (list != NULL) {
- group = (qq_group *) list->data;
- list = list->next;
- if (group->id == room_id) {
- is_find = TRUE;
- break;
- }
- }
- if ( !is_find || list == NULL) {
- return NULL;
- }
- }
-
- is_find = FALSE;
- while (list != NULL) {
- group = (qq_group *) list->data;
- if (group->my_role == QQ_ROOM_ROLE_YES || group->my_role == QQ_ROOM_ROLE_ADMIN) {
- if (NULL != purple_find_conversation_with_account(
- PURPLE_CONV_TYPE_CHAT,group->title_utf8, purple_connection_get_account(gc))) {
- /* In convseration*/
- is_find = TRUE;
- break;
- }
- }
- list = list->next;
- }
-
- if ( !is_find) {
- return NULL;
- }
- return group;
-}
diff --git a/libpurple/protocols/qq/group_find.h b/libpurple/protocols/qq/group_find.h
deleted file mode 100644
index 47b3b35051..0000000000
--- a/libpurple/protocols/qq/group_find.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * @file group_find.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_GROUP_FIND_H_
-#define _QQ_GROUP_FIND_H_
-
-#include <glib.h>
-#include "connection.h"
-#include "group.h"
-
-qq_buddy *qq_group_find_member_by_uid(qq_group *group, guint32 uid);
-void qq_group_remove_member_by_uid(qq_group *group, guint32 uid);
-qq_buddy *qq_group_find_or_add_member(PurpleConnection *gc, qq_group *group, guint32 member_uid);
-qq_group *qq_group_find_by_channel(PurpleConnection *gc, gint channel);
-
-qq_group *qq_room_search_ext_id(PurpleConnection *gc, guint32 ext_id);
-qq_group *qq_room_search_id(PurpleConnection *gc, guint32 room_id);
-
-qq_group *qq_room_get_next(PurpleConnection *gc, guint32 room_id);
-qq_group *qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id);
-
-#endif
diff --git a/libpurple/protocols/qq/group_free.c b/libpurple/protocols/qq/group_free.c
deleted file mode 100644
index 7417697c05..0000000000
--- a/libpurple/protocols/qq/group_free.c
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * @file group_free.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 "debug.h"
-
-#include "buddy_list.h"
-#include "group_free.h"
-
-/* gracefully free all members in a group */
-static void qq_group_free_member(qq_group *group)
-{
- gint i;
- GList *list;
- qq_buddy *member;
-
- g_return_if_fail(group != NULL);
- i = 0;
- while (NULL != (list = group->members)) {
- member = (qq_buddy *) list->data;
- i++;
- group->members = g_list_remove(group->members, member);
- g_free(member->nickname);
- g_free(member);
- }
-
- group->members = NULL;
-}
-
-/* gracefully free the memory for one qq_group */
-void qq_group_free(qq_group *group)
-{
- g_return_if_fail(group != NULL);
- qq_group_free_member(group);
- g_free(group->my_role_desc);
- g_free(group->title_utf8);
- g_free(group->desc_utf8);
- g_free(group->notice_utf8);
- g_free(group);
-}
-
-void qq_group_free_all(qq_data *qd)
-{
- qq_group *group;
- gint count;
-
- g_return_if_fail(qd != NULL);
- count = 0;
- while (qd->groups != NULL) {
- group = (qq_group *) qd->groups->data;
- qd->groups = g_list_remove(qd->groups, group);
- qq_group_free(group);
- count++;
- }
-
- if (count > 0) {
- purple_debug_info("QQ", "%d rooms are freed\n", count);
- }
-}
diff --git a/libpurple/protocols/qq/group_free.h b/libpurple/protocols/qq/group_free.h
deleted file mode 100644
index 759decd310..0000000000
--- a/libpurple/protocols/qq/group_free.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * @file group_free.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_GROUP_FREE_H_
-#define _QQ_GROUP_FREE_H_
-
-#include <glib.h>
-#include "qq.h"
-#include "group.h"
-
-void qq_group_free(qq_group *group);
-void qq_group_free_all(qq_data *qd);
-
-#endif
diff --git a/libpurple/protocols/qq/group_im.c b/libpurple/protocols/qq/group_im.c
index 5848b53cc8..81f6c49ebf 100644
--- a/libpurple/protocols/qq/group_im.c
+++ b/libpurple/protocols/qq/group_im.c
@@ -32,43 +32,143 @@
#include "util.h"
#include "char_conv.h"
-#include "group_find.h"
#include "group_internal.h"
#include "group_info.h"
+#include "group_join.h"
#include "group_im.h"
#include "group_opt.h"
-#include "group_conv.h"
#include "im.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "qq_process.h"
#include "utils.h"
-typedef struct _qq_recv_group_im {
- guint32 ext_id;
- guint8 type8;
- guint32 member_uid;
- guint16 msg_seq;
- time_t send_time;
- guint16 msg_len;
- gchar *msg;
- guint8 *font_attr;
- gint font_attr_len;
-} qq_recv_group_im;
+/* show group conversation window */
+PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd)
+{
+ PurpleConversation *conv;
+ qq_data *qd;
+ gchar *topic_utf8;
+
+ g_return_val_if_fail(rmd != NULL, NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ rmd->title_utf8, purple_connection_get_account(gc));
+ if (conv != NULL) {
+ /* show only one conversation per room */
+ return conv;
+ }
+
+ serv_got_joined_chat(gc, rmd->id, rmd->title_utf8);
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, rmd->title_utf8, purple_connection_get_account(gc));
+ if (conv != NULL) {
+ topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8);
+ purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
+ g_free(topic_utf8);
+
+ if (rmd->is_got_buddies)
+ qq_send_room_cmd_only(gc, QQ_ROOM_CMD_GET_ONLINES, rmd->id);
+ else
+ qq_update_room(gc, 0, rmd->id);
+ return conv;
+ }
+ return NULL;
+}
+
+/* refresh online member in group conversation window */
+void qq_room_conv_set_onlines(PurpleConnection *gc, qq_room_data *rmd)
+{
+ GList *names, *list, *flags;
+ qq_buddy_data *bd;
+ gchar *member_name, *member_uid;
+ PurpleConversation *conv;
+ gint flag;
+ gboolean is_find;
+
+ g_return_if_fail(rmd != NULL);
+
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ rmd->title_utf8, purple_connection_get_account(gc));
+ if (conv == NULL) {
+ purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->title_utf8);
+ return;
+ }
+ g_return_if_fail(rmd->members != NULL);
+
+ names = NULL;
+ flags = NULL;
+
+ list = rmd->members;
+ while (list != NULL) {
+ bd = (qq_buddy_data *) list->data;
+
+ /* we need unique identifiers for everyone in the chat or else we'll
+ * run into problems with functions like get_cb_real_name from qq.c */
+ member_name = (bd->nickname != NULL && *(bd->nickname) != '\0') ?
+ g_strdup_printf("%s (%u)", bd->nickname, bd->uid) :
+ g_strdup_printf("(%u)", bd->uid);
+ member_uid = g_strdup_printf("(%u)", bd->uid);
+
+ flag = 0;
+ /* TYPING to put online above OP and FOUNDER */
+ if (is_online(bd->status)) flag |= (PURPLE_CBFLAGS_TYPING | PURPLE_CBFLAGS_VOICE);
+ if(1 == (bd->role & 1)) flag |= PURPLE_CBFLAGS_OP;
+ if(bd->uid == rmd->creator_uid) flag |= PURPLE_CBFLAGS_FOUNDER;
+
+ is_find = TRUE;
+ if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_name))
+ {
+ purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
+ member_name,
+ flag);
+ } else if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(conv), member_uid))
+ {
+ purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(conv),
+ member_uid,
+ flag);
+ purple_conv_chat_rename_user(PURPLE_CONV_CHAT(conv), member_uid, member_name);
+ } else {
+ is_find = FALSE;
+ }
+ if (!is_find) {
+ /* always put it even offline */
+ names = g_list_append(names, member_name);
+ flags = g_list_append(flags, GINT_TO_POINTER(flag));
+ } else {
+ g_free(member_name);
+ }
+ g_free(member_uid);
+ list = list->next;
+ }
+
+ if (names != NULL && flags != NULL) {
+ purple_conv_chat_add_users(PURPLE_CONV_CHAT(conv), names, NULL, flags, FALSE);
+ }
+
+ /* clean up names */
+ while (names != NULL) {
+ member_name = (gchar *) names->data;
+ names = g_list_remove(names, member_name);
+ g_free(member_name);
+ }
+ g_list_free(flags);
+}
/* send IM to a group */
-void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg)
+void qq_request_room_send_im(PurpleConnection *gc, guint32 room_id, const gchar *msg)
{
gint data_len, bytes;
guint8 *raw_data, *send_im_tail;
guint16 msg_len;
gchar *msg_filtered;
- g_return_if_fail(group != NULL && msg != NULL);
+ g_return_if_fail(room_id != 0 && msg != NULL);
msg_filtered = purple_markup_strip_html(msg);
- purple_debug_info("QQ_MESG", "Send qun mesg filterd: %s\n", msg_filtered);
+ /* purple_debug_info("QQ", "Send qun mesg filterd: %s\n", msg_filtered); */
msg_len = strlen(msg_filtered);
data_len = 2 + msg_len + QQ_SEND_IM_AFTER_MSG_LEN;
@@ -85,226 +185,76 @@ void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar
g_free(msg_filtered);
if (bytes == data_len) /* create OK */
- qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_MSG, group->id, raw_data, data_len);
+ qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_MSG, room_id, raw_data, data_len);
else
purple_debug_error("QQ",
"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, gint len, PurpleConnection *gc)
+void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len)
{
/* return should be the internal group id
* but we have nothing to do with it */
return;
}
-/* receive an application to join the group */
-void qq_process_room_msg_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
-{
- guint32 ext_id, user_uid;
- guint8 type8;
- gchar *reason_utf8, *msg, *reason;
- group_member_opt *g;
- gchar *nombre;
- gint bytes = 0;
-
- g_return_if_fail(id > 0 && data != NULL && len > 0);
-
- /* FIXME: check length here */
-
- bytes += qq_get32(&ext_id, data + bytes);
- bytes += qq_get8(&type8, data + bytes);
- bytes += qq_get32(&user_uid, data + bytes);
-
- g_return_if_fail(ext_id > 0 && user_uid > 0);
-
- bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
-
- msg = g_strdup_printf(_("%d request to join Qun %d"), user_uid, ext_id);
- reason = g_strdup_printf(_("Message: %s"), reason_utf8);
-
- g = g_new0(group_member_opt, 1);
- g->gc = gc;
- g->id = id;
- g->member = user_uid;
-
- 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));
-
- g_free(nombre);
- g_free(reason);
- g_free(msg);
- g_free(reason_utf8);
-}
-
-/* the request to join a group is rejected */
-void qq_process_room_msg_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
-{
- guint32 ext_id, admin_uid;
- guint8 type8;
- gchar *reason_utf8, *msg, *reason;
- qq_group *group;
- gint bytes = 0;
-
- g_return_if_fail(data != NULL && len > 0);
-
- /* FIXME: check length here */
-
- bytes += qq_get32(&ext_id, data + bytes);
- bytes += qq_get8(&type8, data + bytes);
- bytes += qq_get32(&admin_uid, data + bytes);
-
- g_return_if_fail(ext_id > 0 && admin_uid > 0);
-
- bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
-
- msg = g_strdup_printf
- (_("Failed to join Qun %d, operated by admin %d"), ext_id, admin_uid);
- reason = g_strdup_printf(_("Message: %s"), reason_utf8);
-
- purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
-
- group = qq_room_search_id(gc, id);
- if (group != NULL) {
- group->my_role = QQ_ROOM_ROLE_NO;
- qq_group_refresh(gc, group);
- }
-
- g_free(reason);
- g_free(msg);
- g_free(reason_utf8);
-}
-
-/* the request to join a group is approved */
-void qq_process_room_msg_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+void qq_room_got_chat_in(PurpleConnection *gc,
+ guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time)
{
- guint32 ext_id, admin_uid;
- guint8 type8;
- gchar *reason_utf8, *msg;
- qq_group *group;
- gint bytes = 0;
-
- g_return_if_fail(data != NULL && len > 0);
-
- /* FIXME: check length here */
-
- bytes += qq_get32(&ext_id, data + bytes);
- bytes += qq_get8(&type8, data + bytes);
- bytes += qq_get32(&admin_uid, data + bytes);
-
- g_return_if_fail(ext_id > 0 && admin_uid > 0);
- /* it is also a "æ— " here, so do not display */
- bytes += convert_as_pascal_string(data + bytes, &reason_utf8, QQ_CHARSET_DEFAULT);
+ PurpleConversation *conv;
+ qq_buddy_data *bd;
+ qq_room_data *rmd;
+ gchar *from;
- msg = g_strdup_printf
- (_("Successed to join Qun %d, operated by admin %d"), ext_id, admin_uid);
+ g_return_if_fail(gc != NULL && room_id != 0);
- purple_notify_warning(gc, _("QQ Qun Operation"), msg, NULL);
+ conv = purple_find_chat(gc, room_id);
+ rmd = qq_room_data_find(gc, room_id);
+ g_return_if_fail(rmd != NULL);
- group = qq_room_search_id(gc, id);
- if (group != NULL) {
- group->my_role = QQ_ROOM_ROLE_YES;
- qq_group_refresh(gc, group);
+ if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/auto_popup_conversation")) {
+ conv = qq_room_conv_open(gc, rmd);
}
- g_free(msg);
- g_free(reason_utf8);
-}
-
-/* process the packet when removed from a group */
-void qq_process_room_msg_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
-{
- guint32 ext_id, uid;
- guint8 type8;
- gchar *msg;
- qq_group *group;
- gint bytes = 0;
-
- g_return_if_fail(data != NULL && len > 0);
-
- /* FIXME: check length here */
-
- bytes += qq_get32(&ext_id, data + bytes);
- bytes += qq_get8(&type8, data + bytes);
- bytes += qq_get32(&uid, data + bytes);
-
- g_return_if_fail(ext_id > 0 && uid > 0);
-
- msg = g_strdup_printf(_("[%d] removed from Qun \"%d\""), uid, ext_id);
- purple_notify_info(gc, _("QQ Qun Operation"), _("Notice:"), msg);
-
- group = qq_room_search_id(gc, id);
- if (group != NULL) {
- group->my_role = QQ_ROOM_ROLE_NO;
- qq_group_refresh(gc, group);
+ if (conv == NULL) {
+ return;
}
- g_free(msg);
-}
-
-/* process the packet when added to a group */
-void qq_process_room_msg_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
-{
- guint32 ext_id, uid;
- guint8 type8;
- qq_group *group;
- gchar *msg;
- gint bytes = 0;
-
- g_return_if_fail(data != NULL && len > 0);
-
- /* FIXME: check length here */
+ if (uid_from != 0) {
- bytes += qq_get32(&ext_id, data + bytes);
- bytes += qq_get8(&type8, data + bytes);
- bytes += qq_get32(&uid, data + bytes);
-
- g_return_if_fail(ext_id > 0 && uid > 0);
-
- msg = g_strdup_printf(_("[%d] added to Qun \"%d\""), uid, ext_id);
- purple_notify_info(gc, _("QQ Qun Operation"), _("Notice:"), msg);
-
- group = qq_room_search_id(gc, id);
- if (group != NULL) {
- group->my_role = QQ_ROOM_ROLE_YES;
- qq_group_refresh(gc, group);
- } else { /* no such group, try to create a dummy first, and then update */
- group = qq_group_create_internal_record(gc, id, ext_id, NULL);
- group->my_role = QQ_ROOM_ROLE_YES;
- qq_group_refresh(gc, group);
- qq_update_room(gc, 0, group->id);
- /* the return of this cmd will automatically update the group in blist */
+ bd = qq_room_buddy_find(rmd, uid_from);
+ if (bd == NULL || bd->nickname == NULL)
+ from = g_strdup_printf("%d", uid_from);
+ else
+ from = g_strdup(bd->nickname);
+ } else {
+ from = g_strdup("");
}
-
- g_free(msg);
+ serv_got_chat_in(gc, room_id, from, 0, msg, in_time);
+ g_free(from);
}
/* recv an IM from a group chat */
-void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type)
+void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type)
{
- gchar *msg_with_purple_smiley, *msg_utf8_encoded, *im_src_name;
- guint16 unknown;
- guint32 unknown4;
- PurpleConversation *conv;
+ gchar *msg_with_purple_smiley, *msg_utf8_encoded;
qq_data *qd;
- qq_buddy *member;
- qq_group *group;
- qq_recv_group_im *im_group;
gint skip_len;
- gint bytes = 0;
+ gint bytes ;
+ struct {
+ guint32 ext_id;
+ guint8 type8;
+ guint32 member_uid;
+ guint16 unknown;
+ guint16 msg_seq;
+ time_t send_time;
+ guint32 unknown4;
+ guint16 msg_len;
+ gchar *msg;
+ guint8 *font_attr;
+ gint font_attr_len;
+ } packet;
g_return_if_fail(data != NULL && data_len > 0);
@@ -312,24 +262,22 @@ void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleC
qd = (qq_data *) gc->proto_data;
-#if 0
- qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", data, data_len, "group im hex dump");
-#endif
+ /* qq_show_packet("ROOM_IM", data, data_len); */
- im_group = g_newa(qq_recv_group_im, 1);
-
- bytes += qq_get32(&(im_group->ext_id), data + bytes);
- bytes += qq_get8(&(im_group->type8), data + bytes);
+ memset(&packet, 0, sizeof(packet));
+ bytes = 0;
+ bytes += qq_get32(&(packet.ext_id), data + bytes);
+ bytes += qq_get8(&(packet.type8), data + bytes);
- if(QQ_RECV_IM_TEMP_QUN_IM == im_type) {
+ if(QQ_MSG_TEMP_QUN_IM == msg_type) {
bytes += qq_get32(&(id), data + bytes);
}
- 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 */
+ bytes += qq_get32(&(packet.member_uid), bytes + data);
+ bytes += qq_get16(&packet.unknown, data + bytes); /* 0x0001? */
+ bytes += qq_get16(&(packet.msg_seq), data + bytes);
+ bytes += qq_getime(&packet.send_time, data + bytes);
+ bytes += qq_get32(&packet.unknown4, data + bytes); /* versionID */
/*
* length includes font_attr
* this msg_len includes msg and font_attr
@@ -340,9 +288,8 @@ void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleC
* 3. font_attr
*/
- bytes += qq_get16(&(im_group->msg_len), data + bytes);
- g_return_if_fail(im_group->msg_len > 0);
-
+ bytes += qq_get16(&(packet.msg_len), data + bytes);
+ g_return_if_fail(packet.msg_len > 0);
/*
* 10 bytes from lumaqq
* contentType = buf.getChar();
@@ -352,50 +299,37 @@ void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleC
* buf.getInt();
*/
- if(im_type != QQ_RECV_IM_UNKNOWN_QUN_IM)
+ if(msg_type != QQ_MSG_UNKNOWN_QUN_IM)
skip_len = 10;
else
skip_len = 0;
bytes += skip_len;
- im_group->msg = g_strdup((gchar *) data + bytes);
- bytes += strlen(im_group->msg) + 1;
+ /* qq_show_packet("Message", data + bytes, data_len - bytes); */
+
+ packet.msg = g_strdup((gchar *) data + bytes);
+ bytes += strlen(packet.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(data + bytes, im_group->font_attr_len);
- else
- im_group->font_attr = NULL;
+ packet.font_attr_len = data_len - bytes;
+ if (packet.font_attr_len > 0) {
+ packet.font_attr = g_memdup(data + bytes, packet.font_attr_len);
+ /* qq_show_packet("font_attr", packet.font_attr, packet.font_attr_len); */
+ } else {
+ packet.font_attr = NULL;
+ }
/* group im_group has no flag to indicate whether it has font_attr or not */
- 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);
- else
+ msg_with_purple_smiley = qq_smiley_to_purple(packet.msg);
+ if (packet.font_attr_len > 0) {
+ msg_utf8_encoded = qq_encode_to_purple(packet.font_attr,
+ packet.font_attr_len, msg_with_purple_smiley, qd->client_version);
+ } else {
msg_utf8_encoded = qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
-
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
-
- conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, group->title_utf8, purple_connection_get_account(gc));
- if (conv == NULL && purple_prefs_get_bool("/plugins/prpl/qq/show_room_when_newin")) {
- conv = qq_room_conv_create(gc, group);
}
+ qq_room_got_chat_in(gc, id, packet.member_uid, msg_utf8_encoded, packet.send_time);
- if (conv != NULL) {
- member = qq_group_find_member_by_uid(group, im_group->member_uid);
- if (member == NULL || member->nickname == NULL)
- im_src_name = uid_to_purple_name(im_group->member_uid);
- 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);
- g_free(im_src_name);
- }
g_free(msg_with_purple_smiley);
g_free(msg_utf8_encoded);
- g_free(im_group->msg);
- g_free(im_group->font_attr);
+ g_free(packet.msg);
+ g_free(packet.font_attr);
}
diff --git a/libpurple/protocols/qq/group_im.h b/libpurple/protocols/qq/group_im.h
index 09bc4dad07..8cc049e18c 100644
--- a/libpurple/protocols/qq/group_im.h
+++ b/libpurple/protocols/qq/group_im.h
@@ -27,21 +27,18 @@
#include <glib.h>
#include "connection.h"
+#include "conversation.h"
#include "group.h"
-void qq_send_packet_group_im(PurpleConnection *gc, qq_group *group, const gchar *msg);
+PurpleConversation *qq_room_conv_open(PurpleConnection *gc, qq_room_data *rmd);
+void qq_room_conv_set_onlines(PurpleConnection *gc, qq_room_data *rmd);
-void qq_process_group_cmd_im(guint8 *data, gint len, PurpleConnection *gc);
+void qq_room_got_chat_in(PurpleConnection *gc,
+ guint32 room_id, guint32 uid_from, const gchar *msg, time_t in_time);
-void qq_process_room_msg_normal(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 im_type);
+void qq_request_room_send_im(PurpleConnection *gc, guint32 room_id, const gchar *msg);
+void qq_process_room_send_im(PurpleConnection *gc, guint8 *data, gint len);
-void qq_process_room_msg_apply_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type);
-void qq_process_room_msg_been_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
-
-void qq_process_room_msg_been_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
-
-void qq_process_room_msg_been_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
-
-void qq_process_room_msg_been_added(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_info.c b/libpurple/protocols/qq/group_info.c
index b1f3ea6fc6..d3732e2ebb 100644
--- a/libpurple/protocols/qq/group_info.c
+++ b/libpurple/protocols/qq/group_info.c
@@ -28,11 +28,11 @@
#include "debug.h"
#include "char_conv.h"
-#include "group_find.h"
+#include "group_im.h"
#include "group_internal.h"
#include "group_info.h"
#include "buddy_list.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "utils.h"
@@ -41,7 +41,7 @@
* this interval determines if their member info is outdated */
#define QQ_GROUP_CHAT_REFRESH_NICKNAME_INTERNAL 180
-static gboolean check_update_interval(qq_buddy *member)
+static gboolean check_update_interval(qq_buddy_data *member)
{
g_return_val_if_fail(member != NULL, FALSE);
return (member->nickname == NULL) ||
@@ -50,32 +50,37 @@ static gboolean check_update_interval(qq_buddy *member)
/* this is done when we receive the reply to get_online_members sub_cmd
* all member are set offline, and then only those in reply packets are online */
-static void set_all_offline(qq_group *group)
+static void set_all_offline(qq_room_data *rmd)
{
GList *list;
- qq_buddy *member;
- g_return_if_fail(group != NULL);
+ qq_buddy_data *bd;
+ g_return_if_fail(rmd != NULL);
- list = group->members;
+ list = rmd->members;
while (list != NULL) {
- member = (qq_buddy *) list->data;
- member->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
+ bd = (qq_buddy_data *) list->data;
+ bd->status = QQ_BUDDY_CHANGE_TO_OFFLINE;
list = list->next;
}
}
/* send packet to get info for each group member */
-gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class)
+gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class)
{
guint8 *raw_data;
gint bytes, num;
GList *list;
- qq_buddy *member;
+ qq_room_data *rmd;
+ qq_buddy_data *bd;
- g_return_val_if_fail(group != NULL, 0);
- for (num = 0, list = group->members; list != NULL; list = list->next) {
- member = (qq_buddy *) list->data;
- if (check_update_interval(member))
+ g_return_val_if_fail(room_id > 0, 0);
+
+ rmd = qq_room_data_find(gc, room_id);
+ g_return_val_if_fail(rmd != NULL, 0);
+
+ for (num = 0, list = rmd->members; list != NULL; list = list->next) {
+ bd = (qq_buddy_data *) list->data;
+ if (check_update_interval(bd))
num++;
}
@@ -88,37 +93,101 @@ gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint upd
bytes = 0;
- list = group->members;
+ list = rmd->members;
while (list != NULL) {
- member = (qq_buddy *) list->data;
- if (check_update_interval(member))
- bytes += qq_put32(raw_data + bytes, member->uid);
+ bd = (qq_buddy_data *) list->data;
+ if (check_update_interval(bd))
+ bytes += qq_put32(raw_data + bytes, bd->uid);
list = list->next;
}
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_BUDDIES, group->id, raw_data, bytes,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_BUDDIES, rmd->id, raw_data, bytes,
update_class, 0);
return num;
}
-void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection *gc)
+static gchar *get_role_desc(qq_room_role role)
+{
+ const char *role_desc;
+ switch (role) {
+ case QQ_ROOM_ROLE_NO:
+ role_desc = _("Not member");
+ break;
+ case QQ_ROOM_ROLE_YES:
+ role_desc = _("Member");
+ break;
+ case QQ_ROOM_ROLE_REQUESTING:
+ role_desc = _("Requesting");
+ break;
+ case QQ_ROOM_ROLE_ADMIN:
+ role_desc = _("Admin");
+ break;
+ default:
+ role_desc = _("Unknown");
+ }
+
+ return g_strdup(role_desc);
+}
+
+static void room_info_display(PurpleConnection *gc, qq_room_data *rmd)
+{
+ PurpleNotifyUserInfo *room_info;
+ gchar *utf8_value;
+
+ g_return_if_fail(rmd != NULL && rmd->id > 0);
+
+ room_info = purple_notify_user_info_new();
+
+ purple_notify_user_info_add_pair(room_info, _("Title"), rmd->title_utf8);
+ purple_notify_user_info_add_pair(room_info, _("Notice"), rmd->notice_utf8);
+ purple_notify_user_info_add_pair(room_info, _("Detail"), rmd->desc_utf8);
+
+ purple_notify_user_info_add_section_break(room_info);
+
+ utf8_value = g_strdup_printf(("%d"), rmd->creator_uid);
+ purple_notify_user_info_add_pair(room_info, _("Creator"), utf8_value);
+ g_free(utf8_value);
+
+ utf8_value = get_role_desc(rmd->my_role);
+ purple_notify_user_info_add_pair(room_info, _("About me"), utf8_value);
+ g_free(utf8_value);
+
+ utf8_value = g_strdup_printf(("%d"), rmd->category);
+ purple_notify_user_info_add_pair(room_info, _("Category"), utf8_value);
+ g_free(utf8_value);
+
+ utf8_value = g_strdup_printf(("%d"), rmd->auth_type);
+ purple_notify_user_info_add_pair(room_info, _("Authorize"), utf8_value);
+ g_free(utf8_value);
+
+ utf8_value = g_strdup_printf(("%d"), rmd->ext_id);
+ purple_notify_userinfo(gc, utf8_value, room_info, NULL, NULL);
+ g_free(utf8_value);
+
+ purple_notify_user_info_destroy(room_info);
+}
+
+void qq_process_room_cmd_get_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc)
{
- qq_group *group;
- qq_buddy *member;
qq_data *qd;
- PurpleConversation *purple_conv;
+ qq_room_data *rmd;
+ qq_buddy_data *bd;
+ PurpleChat *chat;
+ PurpleConversation *conv;
guint8 organization, role;
guint16 unknown, max_members;
guint32 member_uid, id, ext_id;
- GSList *pending_id;
guint32 unknown4;
guint8 unknown1;
gint bytes, num;
gchar *notice;
+ gchar *topic_utf8;
g_return_if_fail(data != NULL && data_len > 0);
qd = (qq_data *) gc->proto_data;
+ /* qq_show_packet("Room Info", data, data_len); */
+
bytes = 0;
bytes += qq_get32(&id, data + bytes);
g_return_if_fail(id > 0);
@@ -126,22 +195,18 @@ void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection
bytes += qq_get32(&ext_id, data + bytes);
g_return_if_fail(ext_id > 0);
- pending_id = qq_get_pending_id(qd->adding_groups_from_server, id);
- if (pending_id != NULL) {
- qq_set_pending_id(&qd->adding_groups_from_server, id, FALSE);
- qq_group_create_internal_record(gc, id, ext_id, NULL);
- }
-
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
+ chat = qq_room_find_or_new(gc, id, ext_id);
+ g_return_if_fail(chat != NULL);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
- bytes += qq_get8(&(group->type8), data + bytes);
+ bytes += qq_get8(&(rmd->type8), 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(&(rmd->creator_uid), data + bytes);
+ bytes += qq_get8(&(rmd->auth_type), data + bytes);
bytes += qq_get32(&unknown4, data + bytes); /* oldCategory */
bytes += qq_get16(&unknown, data + bytes);
- bytes += qq_get32(&(group->category), data + bytes);
+ bytes += qq_get32(&(rmd->category), data + bytes);
bytes += qq_get16(&max_members, data + bytes);
bytes += qq_get8(&unknown1, data + bytes);
/* the following, while Eva:
@@ -150,16 +215,21 @@ void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection
* qunDestLen(qunDestcontent)) */
bytes += qq_get8(&unknown1, data + bytes);
purple_debug_info("QQ", "type=%u creatorid=%u category=%u maxmembers=%u\n",
- group->type8, group->creator_uid, group->category, max_members);
+ rmd->type8, rmd->creator_uid, rmd->category, max_members);
+ if (qd->client_version >= 2007) {
+ /* skip 7 bytes unknow in qq2007 0x(00 00 01 00 00 00 fc)*/
+ bytes += 7;
+ }
+ /* qq_show_packet("Room Info", data + bytes, data_len - bytes); */
/* strlen + <str content> */
- bytes += convert_as_pascal_string(data + bytes, &(group->title_utf8), QQ_CHARSET_DEFAULT);
+ bytes += qq_get_vstr(&(rmd->title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
bytes += qq_get16(&unknown, data + bytes); /* 0x0000 */
- bytes += convert_as_pascal_string(data + bytes, &notice, QQ_CHARSET_DEFAULT);
- bytes += convert_as_pascal_string(data + bytes, &(group->desc_utf8), QQ_CHARSET_DEFAULT);
+ bytes += qq_get_vstr(&notice, QQ_CHARSET_DEFAULT, data + bytes);
+ bytes += qq_get_vstr(&(rmd->desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
purple_debug_info("QQ", "room [%s] notice [%s] desc [%s] unknow 0x%04X\n",
- group->title_utf8, notice, group->desc_utf8, unknown);
+ rmd->title_utf8, notice, rmd->desc_utf8, unknown);
num = 0;
/* now comes the member list separated by 0x00 */
@@ -175,37 +245,42 @@ void qq_process_room_cmd_get_info(guint8 *data, gint data_len, PurpleConnection
}
#endif
- member = qq_group_find_or_add_member(gc, group, member_uid);
- if (member != NULL)
- member->role = role;
+ bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
+ if (bd != NULL)
+ bd->role = role;
}
if(bytes > data_len) {
purple_debug_error("QQ",
"group_cmd_get_group_info: Dangerous error! maybe protocol changed, notify me!");
}
- purple_debug_info("QQ", "group \"%s\" has %d members\n", group->title_utf8, num);
+ purple_debug_info("QQ", "group \"%s\" has %d members\n", rmd->title_utf8, num);
- if (group->creator_uid == qd->uid)
- group->my_role = QQ_ROOM_ROLE_ADMIN;
+ if (rmd->creator_uid == qd->uid)
+ rmd->my_role = QQ_ROOM_ROLE_ADMIN;
/* filter \r\n in notice */
qq_filter_str(notice);
- group->notice_utf8 = strdup(notice);
+ rmd->notice_utf8 = strdup(notice);
g_free(notice);
- qq_group_refresh(gc, group);
+ qq_room_update_chat_info(chat, rmd);
+
+ if (action == QQ_ROOM_INFO_DISPLAY) {
+ room_info_display(gc, rmd);
+ }
- purple_conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
- group->title_utf8, purple_connection_get_account(gc));
- if(NULL == purple_conv) {
- purple_debug_warning("QQ",
- "Conversation \"%s\" is not open, do not set topic\n", group->title_utf8);
+ conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
+ rmd->title_utf8, purple_connection_get_account(gc));
+ if(NULL == conv) {
+ purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->title_utf8);
return;
}
- purple_debug_info("QQ", "Set chat topic to %s\n", group->notice_utf8);
- purple_conv_chat_set_topic(PURPLE_CONV_CHAT(purple_conv), NULL, group->notice_utf8);
+ topic_utf8 = g_strdup_printf("%d %s", rmd->ext_id, rmd->notice_utf8);
+ purple_debug_info("QQ", "Set chat topic to %s\n", topic_utf8);
+ purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic_utf8);
+ g_free(topic_utf8);
}
void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc)
@@ -213,8 +288,8 @@ void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *g
guint32 id, member_uid;
guint8 unknown;
gint bytes, num;
- qq_group *group;
- qq_buddy *member;
+ qq_room_data *rmd;
+ qq_buddy_data *bd;
g_return_if_fail(data != NULL && len > 0);
@@ -228,28 +303,29 @@ void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *g
bytes += qq_get8(&unknown, data + bytes); /* 0x3c ?? */
g_return_if_fail(id > 0);
- group = qq_room_search_id(gc, id);
- if (group == NULL) {
+ rmd = qq_room_data_find(gc, id);
+ if (rmd == NULL) {
purple_debug_error("QQ", "We have no group info for internal id [%d]\n", id);
return;
}
/* set all offline first, then update those online */
- set_all_offline(group);
+ set_all_offline(rmd);
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;
+ bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
+ if (bd != NULL)
+ bd->status = QQ_BUDDY_ONLINE_NORMAL;
}
if(bytes > len) {
purple_debug_error("QQ",
"group_cmd_get_online_members: Dangerous error! maybe protocol changed, notify developers!");
}
- purple_debug_info("QQ", "Group \"%s\" has %d online members\n", group->title_utf8, num);
+ purple_debug_info("QQ", "Group \"%s\" has %d online members\n", rmd->title_utf8, num);
+ qq_room_conv_set_onlines(gc, rmd);
}
/* process the reply to get_members_info packet */
@@ -259,57 +335,58 @@ void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *g
gint num;
guint32 id, member_uid;
guint16 unknown;
- qq_group *group;
- qq_buddy *member;
+ qq_room_data *rmd;
+ qq_buddy_data *bd;
gchar *nick;
g_return_if_fail(data != NULL && len > 0);
-#if 0
- qq_show_packet("qq_process_room_cmd_get_buddies", data, len);
-#endif
+ /* qq_show_packet("qq_process_room_cmd_get_buddies", data, len); */
bytes = 0;
bytes += qq_get32(&id, data + bytes);
g_return_if_fail(id > 0);
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
num = 0;
/* now starts the member info, as get buddy list reply */
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);
+ bd = qq_room_buddy_find_or_new(gc, rmd, member_uid);
+ g_return_if_fail(bd != NULL);
num++;
- bytes += qq_get16(&(member->face), data + bytes);
- bytes += qq_get8(&(member->age), data + bytes);
- bytes += qq_get8(&(member->gender), data + bytes);
- bytes += convert_as_pascal_string(data + bytes, &nick, QQ_CHARSET_DEFAULT);
+ bytes += qq_get16(&(bd->face), data + bytes);
+ bytes += qq_get8(&(bd->age), data + bytes);
+ bytes += qq_get8(&(bd->gender), data + bytes);
+ bytes += qq_get_vstr(&nick, QQ_CHARSET_DEFAULT, data + bytes);
bytes += qq_get16(&unknown, data + bytes);
- bytes += qq_get8(&(member->ext_flag), data + bytes);
- bytes += qq_get8(&(member->comm_flag), data + bytes);
+ bytes += qq_get8(&(bd->ext_flag), data + bytes);
+ bytes += qq_get8(&(bd->comm_flag), data + bytes);
/* filter \r\n in nick */
qq_filter_str(nick);
- member->nickname = g_strdup(nick);
+ bd->nickname = g_strdup(nick);
g_free(nick);
#if 0
purple_debug_info("QQ",
"member [%09d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n",
- member_uid, member->ext_flag, member->comm_flag, member->nickname);
+ member_uid, bd->ext_flag, bd->comm_flag, bd->nickname);
#endif
- member->last_update = time(NULL);
+ bd->last_update = time(NULL);
}
if (bytes > len) {
purple_debug_error("QQ",
"group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!");
}
- purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", group->title_utf8, num);
+ purple_debug_info("QQ", "Group \"%s\" obtained %d member info\n", rmd->title_utf8, num);
+
+ rmd->is_got_buddies = TRUE;
+ qq_room_conv_set_onlines(gc, rmd);
}
diff --git a/libpurple/protocols/qq/group_info.h b/libpurple/protocols/qq/group_info.h
index cc9f1b44c6..b076935335 100644
--- a/libpurple/protocols/qq/group_info.h
+++ b/libpurple/protocols/qq/group_info.h
@@ -29,9 +29,14 @@
#include "connection.h"
#include "group.h"
-gint qq_request_room_get_buddies(PurpleConnection *gc, qq_group *group, gint update_class);
+enum {
+ QQ_ROOM_INFO_UPDATE_ONLY = 0,
+ QQ_ROOM_INFO_DISPLAY,
+};
-void qq_process_room_cmd_get_info(guint8 *data, gint len, PurpleConnection *gc);
+gint qq_request_room_get_buddies(PurpleConnection *gc, guint32 room_id, gint update_class);
+
+void qq_process_room_cmd_get_info(guint8 *data, gint len, guint32 action, PurpleConnection *gc);
void qq_process_room_cmd_get_onlines(guint8 *data, gint len, PurpleConnection *gc);
void qq_process_room_cmd_get_buddies(guint8 *data, gint len, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_internal.c b/libpurple/protocols/qq/group_internal.c
index bf7849ac05..002e19b875 100644
--- a/libpurple/protocols/qq/group_internal.c
+++ b/libpurple/protocols/qq/group_internal.c
@@ -27,216 +27,396 @@
#include "debug.h"
#include "buddy_opt.h"
-#include "group_free.h"
#include "group_internal.h"
#include "utils.h"
-static gchar *get_role_desc(qq_group *group)
+static qq_room_data *room_data_new(guint32 id, guint32 ext_id, gchar *title)
{
- const char *role_desc;
- g_return_val_if_fail(group != NULL, g_strdup(""));
+ qq_room_data *rmd;
- switch (group->my_role) {
- case QQ_ROOM_ROLE_NO:
- role_desc = _("I am not a member");
- break;
- case QQ_ROOM_ROLE_YES:
- role_desc = _("I am a member");
- break;
- case QQ_ROOM_ROLE_REQUESTING:
- role_desc = _("I am requesting");
- break;
- case QQ_ROOM_ROLE_ADMIN:
- role_desc = _("I am the admin");
- break;
- default:
- role_desc = _("Unknown status");
+ purple_debug_info("QQ", "Created room data: %s, ext id %d, id %d\n",
+ title, ext_id, id);
+ rmd = g_new0(qq_room_data, 1);
+ rmd->my_role = QQ_ROOM_ROLE_NO;
+ rmd->id = id;
+ rmd->ext_id = ext_id;
+ rmd->type8 = 0x01; /* assume permanent Qun */
+ rmd->creator_uid = 10000; /* assume by QQ admin */
+ rmd->category = 0x01;
+ rmd->auth_type = 0x02; /* assume need auth */
+ rmd->title_utf8 = g_strdup(title == NULL ? "" : title);
+ rmd->desc_utf8 = g_strdup("");
+ rmd->notice_utf8 = g_strdup("");
+ rmd->members = NULL;
+ rmd->is_got_buddies = FALSE;
+ return rmd;
+}
+
+/* create a qq_room_data from hashtable */
+static qq_room_data *room_data_new_by_hashtable(PurpleConnection *gc, GHashTable *data)
+{
+ qq_room_data *rmd;
+ guint32 id, ext_id;
+ gchar *value;
+
+ value = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
+ id = value ? strtol(value, NULL, 10) : 0;
+ value= g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
+ ext_id = value ? strtol(value, NULL, 10) : 0;
+ value = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
+
+ rmd = room_data_new(id, ext_id, value);
+ rmd->my_role = QQ_ROOM_ROLE_YES;
+ return rmd;
+}
+
+/* gracefully free all members in a room */
+static void room_buddies_free(qq_room_data *rmd)
+{
+ gint i;
+ GList *list;
+ qq_buddy_data *bd;
+
+ g_return_if_fail(rmd != NULL);
+ i = 0;
+ while (NULL != (list = rmd->members)) {
+ bd = (qq_buddy_data *) list->data;
+ i++;
+ rmd->members = g_list_remove(rmd->members, bd);
+ qq_buddy_data_free(bd);
}
- return g_strdup(role_desc);
+ rmd->members = NULL;
+}
+
+/* gracefully free the memory for one qq_room_data */
+static void room_data_free(qq_room_data *rmd)
+{
+ g_return_if_fail(rmd != NULL);
+ room_buddies_free(rmd);
+ g_free(rmd->title_utf8);
+ g_free(rmd->desc_utf8);
+ g_free(rmd->notice_utf8);
+ g_free(rmd);
}
-static void add_room_to_blist(PurpleConnection *gc, qq_group *group)
+void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd)
+{
+ if (rmd->title_utf8 != NULL && strlen(rmd->title_utf8) > 0) {
+ purple_blist_alias_chat(chat, rmd->title_utf8);
+ }
+ g_hash_table_replace(chat->components,
+ g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
+ g_strdup_printf("%d", rmd->id));
+ g_hash_table_replace(chat->components,
+ g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
+ g_strdup_printf("%d", rmd->ext_id));
+ g_hash_table_replace(chat->components,
+ g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
+}
+
+static PurpleChat *chat_new(PurpleConnection *gc, qq_room_data *rmd)
{
GHashTable *components;
PurpleGroup *g;
PurpleChat *chat;
- components = qq_group_to_hashtable(group);
- chat = purple_chat_new(purple_connection_get_account(gc), group->title_utf8, components);
- g = qq_get_purple_group(PURPLE_GROUP_QQ_QUN);
+
+ purple_debug_info("QQ", "Add new chat: id %d, ext id %d, title %s\n",
+ rmd->id, rmd->ext_id, rmd->title_utf8);
+
+ components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+ g_hash_table_insert(components,
+ g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", rmd->id));
+ g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
+ g_strdup_printf("%d", rmd->ext_id));
+ g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(rmd->title_utf8));
+
+ chat = purple_chat_new(purple_connection_get_account(gc), rmd->title_utf8, components);
+ g = qq_group_find_or_new(PURPLE_GROUP_QQ_QUN);
purple_blist_add_chat(chat, g, NULL);
- purple_debug_info("QQ", "You have added group \"%s\" to blist locally\n", group->title_utf8);
+
+ return chat;
+}
+
+PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 ext_id)
+{
+ qq_data *qd;
+ qq_room_data *rmd;
+ PurpleChat *chat;
+ gchar *num_str;
+
+ g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_val_if_fail(id != 0 && ext_id != 0, NULL);
+
+ purple_debug_info("QQ", "Find or add new room: id %d, ext id %d\n", id, ext_id);
+
+ rmd = qq_room_data_find(gc, id);
+ if (rmd == NULL) {
+ rmd = room_data_new(id, ext_id, NULL);
+ g_return_val_if_fail(rmd != NULL, NULL);
+ rmd->my_role = QQ_ROOM_ROLE_YES;
+ qd->groups = g_list_append(qd->groups, rmd);
+ }
+
+ num_str = g_strdup_printf("%d", ext_id);
+ chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
+ g_free(num_str);
+ if (chat) {
+ return chat;
+ }
+
+ return chat_new(gc, rmd);
}
-/* Create a dummy qq_group, which includes only internal_id, ext_id,
- * and potentially title_utf8, in case we need to call group_conv_show_window
- * right after creation. All other attributes are set to empty.
- * We need to send a get_group_info to the QQ server to update it right away */
-qq_group *qq_group_create_internal_record(PurpleConnection *gc,
- guint32 internal_id, guint32 ext_id, gchar *title_utf8)
+void qq_room_remove(PurpleConnection *gc, guint32 id)
{
- qq_group *group;
- qq_data *qd;
+ qq_data *qd;
+ PurpleChat *chat;
+ qq_room_data *rmd;
+ gchar *num_str;
+ guint32 ext_id;
- g_return_val_if_fail(internal_id > 0, NULL);
- qd = (qq_data *) gc->proto_data;
+ g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ purple_debug_info("QQ", "Find and remove room data, id %d", id);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail (rmd != NULL);
- group = g_new0(qq_group, 1);
- group->my_role = QQ_ROOM_ROLE_NO;
- group->my_role_desc = get_role_desc(group);
- group->id = internal_id;
- group->ext_id = ext_id;
- group->type8 = 0x01; /* assume permanent Qun */
- group->creator_uid = 10000; /* assume by QQ admin */
- group->category = 0x01;
- group->auth_type = 0x02; /* assume need auth */
- group->title_utf8 = g_strdup(title_utf8 == NULL ? "" : title_utf8);
- group->desc_utf8 = g_strdup("");
- group->notice_utf8 = g_strdup("");
- group->members = NULL;
+ ext_id = rmd->ext_id;
+ qd->groups = g_list_remove(qd->groups, rmd);
+ room_data_free(rmd);
- qd->groups = g_list_append(qd->groups, group);
- add_room_to_blist(gc, group);
+ purple_debug_info("QQ", "Find and remove chat, ext_id %d", ext_id);
+ num_str = g_strdup_printf("%d", ext_id);
+ chat = purple_blist_find_chat(purple_connection_get_account(gc), num_str);
+ g_free(num_str);
- return group;
+ g_return_if_fail (chat != NULL);
+
+ purple_blist_remove_chat(chat);
}
-void qq_group_delete_internal_record(qq_data *qd, guint32 id)
+/* find a qq_buddy_data by uid, called by im.c */
+qq_buddy_data *qq_room_buddy_find(qq_room_data *rmd, guint32 uid)
{
- qq_group *group;
- GList *list;
+ GList *list;
+ qq_buddy_data *bd;
+ g_return_val_if_fail(rmd != NULL && uid > 0, NULL);
- list = qd->groups;
- while (list != NULL) {
- group = (qq_group *) qd->groups->data;
- if (id == group->id) {
- qd->groups = g_list_remove(qd->groups, group);
- qq_group_free(group);
- break;
- } else {
- list = list->next;
- }
- }
+ list = rmd->members;
+ while (list != NULL) {
+ bd = (qq_buddy_data *) list->data;
+ if (bd->uid == uid)
+ return bd;
+ else
+ list = list->next;
+ }
+
+ return NULL;
}
-/* convert a qq_group to hash-table, which could be component of PurpleChat */
-GHashTable *qq_group_to_hashtable(qq_group *group)
+/* remove a qq_buddy_data by uid, called by qq_group_opt.c */
+void qq_room_buddy_remove(qq_room_data *rmd, guint32 uid)
{
- GHashTable *components;
- components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_ROLE), g_strdup_printf("%d", group->my_role));
- group->my_role_desc = get_role_desc(group);
+ GList *list;
+ qq_buddy_data *bd;
+ g_return_if_fail(rmd != NULL && uid > 0);
- g_hash_table_insert(components,
- g_strdup(QQ_ROOM_KEY_INTERNAL_ID), g_strdup_printf("%d", group->id));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
- g_strdup_printf("%d", group->ext_id));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TYPE), g_strdup_printf("%d", group->type8));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_CREATOR_UID), g_strdup_printf("%d", group->creator_uid));
- g_hash_table_insert(components,
- g_strdup(QQ_ROOM_KEY_CATEGORY), g_strdup_printf("%d", group->category));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_AUTH_TYPE), g_strdup_printf("%d", group->auth_type));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_ROLE_DESC), g_strdup(group->my_role_desc));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(group->title_utf8));
- g_hash_table_insert(components, g_strdup(QQ_ROOM_KEY_DESC_UTF8), g_strdup(group->desc_utf8));
- return components;
+ list = rmd->members;
+ while (list != NULL) {
+ bd = (qq_buddy_data *) list->data;
+ if (bd->uid == uid) {
+ rmd->members = g_list_remove(rmd->members, bd);
+ return;
+ } else {
+ list = list->next;
+ }
+ }
+}
+
+qq_buddy_data *qq_room_buddy_find_or_new(PurpleConnection *gc, qq_room_data *rmd, guint32 member_uid)
+{
+ qq_buddy_data *member, *bd;
+ PurpleBuddy *buddy;
+ g_return_val_if_fail(rmd != NULL && member_uid > 0, NULL);
+
+ member = qq_room_buddy_find(rmd, member_uid);
+ if (member == NULL) { /* first appear during my session */
+ member = g_new0(qq_buddy_data, 1);
+ member->uid = member_uid;
+ buddy = purple_find_buddy(purple_connection_get_account(gc), uid_to_purple_name(member_uid));
+ if (buddy != NULL) {
+ bd = (qq_buddy_data *) buddy->proto_data;
+ if (bd != NULL && bd->nickname != NULL)
+ member->nickname = g_strdup(bd->nickname);
+ else if (buddy->alias != NULL)
+ member->nickname = g_strdup(buddy->alias);
+ }
+ rmd->members = g_list_append(rmd->members, member);
+ }
+
+ return member;
}
-/* create a qq_group from hashtable */
-qq_group *qq_room_create_by_hashtable(PurpleConnection *gc, GHashTable *data)
+qq_room_data *qq_room_data_find(PurpleConnection *gc, guint32 room_id)
{
+ GList *list;
+ qq_room_data *rmd;
qq_data *qd;
- qq_group *group;
- g_return_val_if_fail(data != NULL, NULL);
qd = (qq_data *) gc->proto_data;
- group = g_new0(qq_group, 1);
- group->my_role =
- qq_string_to_dec_value
- (NULL ==
- g_hash_table_lookup(data,
- QQ_ROOM_KEY_ROLE) ?
- g_strdup_printf("%d", QQ_ROOM_ROLE_NO) :
- g_hash_table_lookup(data, QQ_ROOM_KEY_ROLE));
- group->id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID));
- group->ext_id = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID));
- group->type8 = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_TYPE));
- group->creator_uid = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_CREATOR_UID));
- group->category = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_CATEGORY));
- group->auth_type = qq_string_to_dec_value(g_hash_table_lookup(data, QQ_ROOM_KEY_AUTH_TYPE));
- group->title_utf8 = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_TITLE_UTF8));
- group->desc_utf8 = g_strdup(g_hash_table_lookup(data, QQ_ROOM_KEY_DESC_UTF8));
- group->my_role_desc = get_role_desc(group);
- group->is_got_info = FALSE;
-
- qd->groups = g_list_append(qd->groups, group);
- return group;
-}
-
-/* refresh group local subscription */
-void qq_group_refresh(PurpleConnection *gc, qq_group *group)
+ if (qd->groups == NULL || room_id <= 0)
+ return 0;
+
+ list = qd->groups;
+ while (list != NULL) {
+ rmd = (qq_room_data *) list->data;
+ if (rmd->id == room_id) {
+ return rmd;
+ }
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+guint32 qq_room_get_next(PurpleConnection *gc, guint32 room_id)
{
- PurpleChat *chat;
- gchar *ext_id;
- g_return_if_fail(group != NULL);
-
- ext_id = g_strdup_printf("%d", group->ext_id);
- chat = purple_blist_find_chat(purple_connection_get_account(gc), ext_id);
- g_free(ext_id);
- if (chat == NULL && group->my_role != QQ_ROOM_ROLE_NO) {
- add_room_to_blist(gc, group);
- return;
+ GList *list;
+ qq_room_data *rmd;
+ qq_data *qd;
+ gboolean is_find = FALSE;
+
+ qd = (qq_data *) gc->proto_data;
+
+ if (qd->groups == NULL) {
+ return 0;
}
- if (chat == NULL) {
- return;
+ if (room_id <= 0) {
+ rmd = (qq_room_data *) qd->groups->data;
+ return rmd->id;
}
- /* we have a local record, update its info */
- /* if there is title_utf8, we update the group name */
- if (group->title_utf8 != NULL && strlen(group->title_utf8) > 0)
- purple_blist_alias_chat(chat, group->title_utf8);
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_ROLE), g_strdup_printf("%d", group->my_role));
- group->my_role_desc = get_role_desc(group);
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_ROLE_DESC), g_strdup(group->my_role_desc));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_INTERNAL_ID),
- g_strdup_printf("%d", group->id));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_EXTERNAL_ID),
- g_strdup_printf("%d", group->ext_id));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_TYPE), g_strdup_printf("%d", group->type8));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_CREATOR_UID), g_strdup_printf("%d", group->creator_uid));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_CATEGORY),
- g_strdup_printf("%d", group->category));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_AUTH_TYPE), g_strdup_printf("%d", group->auth_type));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_TITLE_UTF8), g_strdup(group->title_utf8));
- g_hash_table_replace(chat->components,
- g_strdup(QQ_ROOM_KEY_DESC_UTF8), g_strdup(group->desc_utf8));
+ list = qd->groups;
+ while (list != NULL) {
+ rmd = (qq_room_data *) list->data;
+ list = list->next;
+ if (rmd->id == room_id) {
+ is_find = TRUE;
+ break;
+ }
+ }
+
+ g_return_val_if_fail(is_find, 0);
+ if (list == NULL) return 0; /* be the end */
+ rmd = (qq_room_data *) list->data;
+ g_return_val_if_fail(rmd != NULL, 0);
+ return rmd->id;
}
-/* NOTE: If we knew how to convert between an external and internal group id, as the official
- * client seems to, the following would be unnecessary. That would be ideal. */
+guint32 qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id)
+{
+ GList *list;
+ qq_room_data *rmd;
+ qq_data *qd;
+ gboolean is_find;
+
+ qd = (qq_data *) gc->proto_data;
-/* Use list to specify if id's alternate id is pending discovery. */
-void qq_set_pending_id(GSList **list, guint32 id, gboolean pending)
+ list = qd->groups;
+ if (room_id > 0) {
+ /* search next room */
+ is_find = FALSE;
+ while (list != NULL) {
+ rmd = (qq_room_data *) list->data;
+ list = list->next;
+ if (rmd->id == room_id) {
+ is_find = TRUE;
+ break;
+ }
+ }
+ g_return_val_if_fail(is_find, 0);
+ }
+
+ while (list != NULL) {
+ rmd = (qq_room_data *) list->data;
+ g_return_val_if_fail(rmd != NULL, 0);
+
+ if (rmd->my_role == QQ_ROOM_ROLE_YES || rmd->my_role == QQ_ROOM_ROLE_ADMIN) {
+ if (NULL != purple_find_conversation_with_account(
+ PURPLE_CONV_TYPE_CHAT,rmd->title_utf8, purple_connection_get_account(gc))) {
+ /* In convseration*/
+ return rmd->id;
+ }
+ }
+ list = list->next;
+ }
+
+ return 0;
+}
+
+/* this should be called upon signin, even when we did not open group chat window */
+void qq_room_data_initial(PurpleConnection *gc)
{
- if (pending)
- *list = g_slist_prepend(*list, GINT_TO_POINTER(id));
- else
- *list = g_slist_remove(*list, GINT_TO_POINTER(id));
+ PurpleAccount *account;
+ PurpleChat *chat;
+ PurpleGroup *purple_group;
+ PurpleBlistNode *node;
+ qq_data *qd;
+ qq_room_data *rmd;
+ gint count;
+
+ account = purple_connection_get_account(gc);
+ qd = (qq_data *) gc->proto_data;
+
+ purple_debug_info("QQ", "Initial QQ Qun configurations\n");
+ purple_group = purple_find_group(PURPLE_GROUP_QQ_QUN);
+ if (purple_group == NULL) {
+ purple_debug_info("QQ", "We have no QQ Qun\n");
+ return;
+ }
+
+ count = 0;
+ for (node = ((PurpleBlistNode *) purple_group)->child; node != NULL; node = node->next) {
+ if ( !PURPLE_BLIST_NODE_IS_CHAT(node)) {
+ continue;
+ }
+ /* got one */
+ chat = (PurpleChat *) node;
+ if (account != chat->account) /* not qq account*/
+ continue;
+
+ rmd = room_data_new_by_hashtable(gc, chat->components);
+ qd->groups = g_list_append(qd->groups, rmd);
+ count++;
+ }
+
+ purple_debug_info("QQ", "Load %d QQ Qun configurations\n", count);
}
-/* Return the location of id in list, or NULL if not found */
-GSList *qq_get_pending_id(GSList *list, guint32 id)
+void qq_room_data_free_all(PurpleConnection *gc)
{
- return g_slist_find(list, GINT_TO_POINTER(id));
+ qq_data *qd;
+ qq_room_data *rmd;
+ gint count;
+
+ g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ count = 0;
+ while (qd->groups != NULL) {
+ rmd = (qq_room_data *) qd->groups->data;
+ qd->groups = g_list_remove(qd->groups, rmd);
+ room_data_free(rmd);
+ count++;
+ }
+
+ if (count > 0) {
+ purple_debug_info("QQ", "%d rooms are freed\n", count);
+ }
}
diff --git a/libpurple/protocols/qq/group_internal.h b/libpurple/protocols/qq/group_internal.h
index e08d048970..dc8bcc5493 100644
--- a/libpurple/protocols/qq/group_internal.h
+++ b/libpurple/protocols/qq/group_internal.h
@@ -28,27 +28,23 @@
#include <glib.h>
#include "group.h"
-#define QQ_ROOM_KEY_ROLE "my_role"
-#define QQ_ROOM_KEY_ROLE_DESC "my_role_desc"
#define QQ_ROOM_KEY_INTERNAL_ID "id"
#define QQ_ROOM_KEY_EXTERNAL_ID "ext_id"
-#define QQ_ROOM_KEY_TYPE "type"
-#define QQ_ROOM_KEY_CREATOR_UID "creator_uid"
-#define QQ_ROOM_KEY_CATEGORY "category"
-#define QQ_ROOM_KEY_AUTH_TYPE "auth_type"
-#define QQ_ROOM_KEY_TITLE_UTF8 "title_utf8"
-#define QQ_ROOM_KEY_DESC_UTF8 "desc_utf8"
+#define QQ_ROOM_KEY_TITLE_UTF8 "title_utf8"
-qq_group *qq_group_create_internal_record(PurpleConnection *gc,
- guint32 internal_id, guint32 ext_id, gchar *group_name_utf8);
-void qq_group_delete_internal_record(qq_data *qd, guint32 id);
+PurpleChat *qq_room_find_or_new(PurpleConnection *gc, guint32 id, guint32 ext_id);
+void qq_room_remove(PurpleConnection *gc, guint32 id);
+void qq_room_update_chat_info(PurpleChat *chat, qq_room_data *rmd);
-GHashTable *qq_group_to_hashtable(qq_group *group);
-qq_group *qq_room_create_by_hashtable(PurpleConnection *gc, GHashTable *data);
+qq_buddy_data *qq_room_buddy_find(qq_room_data *rmd, guint32 uid);
+void qq_room_buddy_remove(qq_room_data *rmd, guint32 uid);
+qq_buddy_data *qq_room_buddy_find_or_new(PurpleConnection *gc, qq_room_data *rmd, guint32 member_uid);
-void qq_group_refresh(PurpleConnection *gc, qq_group *group);
+void qq_room_data_initial(PurpleConnection *gc);
+void qq_room_data_free_all(PurpleConnection *gc);
+qq_room_data *qq_room_data_find(PurpleConnection *gc, guint32 room_id);
-void qq_set_pending_id(GSList **list, guint32 id, gboolean pending);
-GSList *qq_get_pending_id(GSList *list, guint32 id);
+guint32 qq_room_get_next(PurpleConnection *gc, guint32 room_id);
+guint32 qq_room_get_next_conv(PurpleConnection *gc, guint32 room_id);
#endif
diff --git a/libpurple/protocols/qq/group_join.c b/libpurple/protocols/qq/group_join.c
index cd552a0792..cc3fd98db4 100644
--- a/libpurple/protocols/qq/group_join.c
+++ b/libpurple/protocols/qq/group_join.c
@@ -29,18 +29,14 @@
#include "request.h"
#include "server.h"
-#include "buddy_opt.h"
#include "char_conv.h"
-#include "group_conv.h"
-#include "group_find.h"
-#include "group_free.h"
+#include "im.h"
#include "group_internal.h"
#include "group_info.h"
#include "group_join.h"
#include "group_opt.h"
-#include "group_conv.h"
-#include "group_search.h"
-#include "header_info.h"
+#include "group_im.h"
+#include "qq_define.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "qq_process.h"
@@ -51,129 +47,140 @@ enum {
QQ_ROOM_JOIN_DENIED = 0x03,
};
-static void _qq_group_exit_with_gc_and_id(gc_and_uid *g)
+enum {
+ QQ_ROOM_SEARCH_TYPE_BY_ID = 0x01,
+ QQ_ROOM_SEARCH_TYPE_DEMO = 0x02
+};
+
+static void group_quit_cb(qq_room_req *add_req)
{
PurpleConnection *gc;
guint32 id;
- qq_group *group;
+ qq_room_data *rmd;
- gc = g->gc;
- id = g->uid;
+ if (add_req->gc == NULL || add_req->id == 0) {
+ g_free(add_req);
+ return;
+ }
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
+ gc = add_req->gc;
+ id = add_req->id;
- qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, group->id);
+ rmd = qq_room_data_find(gc, id);
+ if (rmd == NULL) {
+ g_free(add_req);
+ return;
+ }
+
+ qq_send_room_cmd_only(gc, QQ_ROOM_CMD_QUIT, rmd->id);
+ g_free(add_req);
}
/* send packet to join a group without auth */
-void qq_request_room_join(PurpleConnection *gc, qq_group *group)
+void qq_request_room_join(PurpleConnection *gc, qq_room_data *rmd)
{
- g_return_if_fail(group != NULL);
+ g_return_if_fail(rmd != NULL);
- if (group->my_role == QQ_ROOM_ROLE_NO) {
- group->my_role = QQ_ROOM_ROLE_REQUESTING;
- qq_group_refresh(gc, group);
+ if (rmd->my_role == QQ_ROOM_ROLE_NO) {
+ rmd->my_role = QQ_ROOM_ROLE_REQUESTING;
}
- switch (group->auth_type) {
+ switch (rmd->auth_type) {
case QQ_ROOM_AUTH_TYPE_NO_AUTH:
case QQ_ROOM_AUTH_TYPE_NEED_AUTH:
break;
case QQ_ROOM_AUTH_TYPE_NO_ADD:
- if (group->my_role == QQ_ROOM_ROLE_NO
- && group->my_role == QQ_ROOM_ROLE_REQUESTING) {
+ if (rmd->my_role == QQ_ROOM_ROLE_NO
+ && rmd->my_role == QQ_ROOM_ROLE_REQUESTING) {
purple_notify_warning(gc, NULL, _("The Qun does not allow others to join"), NULL);
return;
}
break;
default:
- purple_debug_error("QQ", "Unknown room auth type: %d\n", group->auth_type);
+ purple_debug_error("QQ", "Unknown room auth type: %d\n", rmd->auth_type);
break;
}
- qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, group->id);
+ qq_send_room_cmd_only(gc, QQ_ROOM_CMD_JOIN, rmd->id);
}
-static void _qq_group_join_auth_with_gc_and_id(gc_and_uid *g, const gchar *reason_utf8)
+static void group_join_cb(qq_room_req *add_req, const gchar *reason_utf8)
{
- PurpleConnection *gc;
- qq_group *group;
- guint32 id;
+ qq_room_data *rmd;
- gc = g->gc;
- id = g->uid;
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->id == 0) {
+ g_free(add_req);
+ return;
+ }
- group = qq_room_search_id(gc, id);
- if (group == NULL) {
- purple_debug_error("QQ", "Can not find qq_group by internal_id: %d\n", id);
+ rmd = qq_room_data_find(add_req->gc, add_req->id);
+ if (rmd == NULL) {
+ purple_debug_error("QQ", "Can not find room data of %d\n", add_req->id);
+ g_free(add_req);
return;
- } else { /* everything is OK */
- qq_send_cmd_group_auth(gc, group, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
}
+
+ qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPLY, 0, reason_utf8);
+ g_free(add_req);
}
-static void _qq_group_join_auth(PurpleConnection *gc, qq_group *group)
+static void room_join_cancel_cb(qq_room_req *add_req, const gchar *msg)
+{
+ g_return_if_fail(add_req != NULL);
+ g_free(add_req);
+}
+
+static void do_room_join_request(PurpleConnection *gc, qq_room_data *rmd)
{
gchar *msg;
- gc_and_uid *g;
- g_return_if_fail(group != NULL);
+ qq_room_req *add_req;
+ g_return_if_fail(rmd != NULL);
- purple_debug_info("QQ", "Group (internal id: %d) needs authentication\n", group->id);
+ purple_debug_info("QQ", "Room (internal id: %d) needs authentication\n", rmd->id);
- msg = g_strdup_printf("Group \"%s\" needs authentication\n", group->title_utf8);
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = group->id;
- purple_request_input(gc, NULL, msg,
+ msg = g_strdup_printf("QQ Qun %d needs authentication\n", rmd->ext_id);
+ add_req = g_new0(qq_room_req, 1);
+ add_req->gc = gc;
+ add_req->id = rmd->id;
+ purple_request_input(gc, _("Join QQ Qun"), msg,
_("Input request here"),
_("Would you be my friend?"), TRUE, FALSE, NULL,
_("Send"),
- G_CALLBACK(_qq_group_join_auth_with_gc_and_id),
- _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid),
- purple_connection_get_account(gc), group->title_utf8, NULL,
- g);
+ G_CALLBACK(group_join_cb),
+ _("Cancel"), G_CALLBACK(room_join_cancel_cb),
+ purple_connection_get_account(gc), rmd->title_utf8, NULL,
+ add_req);
g_free(msg);
}
-void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8)
+void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd,
+ guint8 opt, guint32 uid, const gchar *reason_utf8)
{
- guint8 *raw_data;
- gchar *reason_qq;
+ guint8 raw_data[MAX_PACKET_SIZE - 16];
gint bytes;
- g_return_if_fail(group != NULL);
-
- if (reason_utf8 == NULL || strlen(reason_utf8) == 0)
- reason_qq = g_strdup("");
- else
- reason_qq = utf8_to_qq(reason_utf8, QQ_CHARSET_DEFAULT);
+ g_return_if_fail(rmd != NULL);
if (opt == QQ_ROOM_AUTH_REQUEST_APPLY) {
- group->my_role = QQ_ROOM_ROLE_REQUESTING;
- qq_group_refresh(gc, group);
+ rmd->my_role = QQ_ROOM_ROLE_REQUESTING;
uid = 0;
}
- raw_data = g_newa(guint8, 6 + strlen(reason_qq));
-
bytes = 0;
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));
+ bytes += qq_put_vstr(raw_data + bytes, reason_utf8, QQ_CHARSET_DEFAULT);
- qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, group->id, raw_data, bytes);
+ qq_send_room_cmd(gc, QQ_ROOM_CMD_AUTH, rmd->id, raw_data, bytes);
}
/* If comes here, cmd is OK already */
void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *gc)
{
+ qq_data *qd;
gint bytes;
guint32 id;
- PurpleChat *chat;
- qq_group *group;
- qq_data *qd;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
@@ -186,15 +193,7 @@ void qq_process_group_cmd_exit_group(guint8 *data, gint len, PurpleConnection *g
bytes = 0;
bytes += qq_get32(&id, data + bytes);
- group = qq_room_search_id(gc, id);
- if (group != NULL) {
- chat = purple_blist_find_chat
- (purple_connection_get_account(gc), g_strdup_printf("%d", group->ext_id));
- if (chat != NULL)
- purple_blist_remove_chat(chat);
- qq_group_delete_internal_record(qd, id);
- }
- purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Remove from Qun"));
+ qq_room_remove(gc, id);
}
/* Process the reply to group_auth subcmd */
@@ -203,6 +202,8 @@ void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnecti
gint bytes;
guint32 id;
qq_data *qd;
+ qq_room_data *rmd;
+ gchar *msg;
g_return_if_fail(data != NULL && len > 0);
qd = (qq_data *) gc->proto_data;
@@ -216,7 +217,14 @@ void qq_process_group_cmd_join_group_auth(guint8 *data, gint len, PurpleConnecti
bytes += qq_get32(&id, data + bytes);
g_return_if_fail(id > 0);
- purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Join to Qun"));
+ rmd = qq_room_data_find(gc, id);
+ if (rmd != NULL) {
+ msg = g_strdup_printf(_("Successed join to Qun %s (%d)"), rmd->title_utf8, rmd->ext_id);
+ qq_got_attention(gc, msg);
+ g_free(msg);
+ } else {
+ qq_got_attention(gc, _("Successed join to Qun"));
+ }
}
/* process group cmd reply "join group" */
@@ -225,14 +233,14 @@ void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *g
gint bytes;
guint32 id;
guint8 reply;
- qq_group *group;
+ qq_room_data *rmd;
gchar *msg;
g_return_if_fail(data != NULL && len > 0);
if (len < 5) {
purple_debug_error("QQ",
- "Invalid join group reply, expect %d bytes, read %d bytes\n", 5, len);
+ "Invalid join room reply, expect %d bytes, read %d bytes\n", 5, len);
return;
}
@@ -241,34 +249,32 @@ void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *g
bytes += qq_get8(&reply, data + bytes);
/* join group OK */
- group = qq_room_search_id(gc, id);
+ rmd = qq_room_data_find(gc, id);
/* need to check if group is NULL or not. */
- g_return_if_fail(group != NULL);
+ g_return_if_fail(rmd != NULL);
switch (reply) {
case QQ_ROOM_JOIN_OK:
- purple_debug_info("QQ", "Successed in joining group \"%s\"\n", group->title_utf8);
- group->my_role = QQ_ROOM_ROLE_YES;
- qq_group_refresh(gc, group);
+ purple_debug_info("QQ", "Successed in joining group \"%s\"\n", rmd->title_utf8);
+ rmd->my_role = QQ_ROOM_ROLE_YES;
/* this must be shown before getting online members */
- qq_room_conv_create(gc, group);
+ qq_room_conv_open(gc, rmd);
break;
case QQ_ROOM_JOIN_NEED_AUTH:
purple_debug_info("QQ",
"Fail joining group [%d] %s, needs authentication\n",
- group->ext_id, group->title_utf8);
- group->my_role = QQ_ROOM_ROLE_NO;
- qq_group_refresh(gc, group);
- _qq_group_join_auth(gc, group);
+ rmd->ext_id, rmd->title_utf8);
+ rmd->my_role = QQ_ROOM_ROLE_NO;
+ do_room_join_request(gc, rmd);
break;
case QQ_ROOM_JOIN_DENIED:
- msg = g_strdup_printf(_("Qun %d denied to join"), group->ext_id);
+ msg = g_strdup_printf(_("Qun %d denied to join"), rmd->ext_id);
purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), msg);
g_free(msg);
break;
default:
purple_debug_info("QQ",
"Failed joining group [%d] %s, unknown reply: 0x%02x\n",
- group->ext_id, group->title_utf8, reply);
+ rmd->ext_id, rmd->title_utf8, reply);
purple_notify_info(gc, _("QQ Qun Operation"), _("Failed:"), _("Join Qun, Unknow Reply"));
}
@@ -278,55 +284,146 @@ void qq_process_group_cmd_join_group(guint8 *data, gint len, PurpleConnection *g
void qq_group_join(PurpleConnection *gc, GHashTable *data)
{
qq_data *qd;
- gchar *ext_id_ptr;
+ gchar *ext_id_str;
+ gchar *id_str;
guint32 ext_id;
- qq_group *group;
+ guint32 id;
+ qq_room_data *rmd;
g_return_if_fail(data != NULL);
qd = (qq_data *) gc->proto_data;
- ext_id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
- g_return_if_fail(ext_id_ptr != NULL);
- errno = 0;
- ext_id = strtol(ext_id_ptr, NULL, 10);
- if (errno != 0) {
- purple_notify_error(gc, _("Error"),
- _("You entered a group ID outside the acceptable range"), NULL);
- return;
+ ext_id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_EXTERNAL_ID);
+ id_str = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
+ purple_debug_info("QQ", "Join room %s, extend id %s\n", id_str, ext_id_str);
+
+ if (id_str != NULL) {
+ id = strtol(id_str, NULL, 10);
+ if (id != 0) {
+ rmd = qq_room_data_find(gc, id);
+ if (rmd) {
+ qq_request_room_join(gc, rmd);
+ return;
+ }
+ }
}
- group = qq_room_search_ext_id(gc, ext_id);
- if (group) {
- qq_request_room_join(gc, group);
- } else {
- qq_set_pending_id(&qd->joining_groups, ext_id, TRUE);
- qq_send_cmd_group_search_group(gc, ext_id);
+ purple_debug_info("QQ", "Search and join extend id %s\n", ext_id_str);
+ if (ext_id_str == NULL) {
+ return;
+ }
+ ext_id = strtol(ext_id_str, NULL, 10);
+ if (ext_id == 0) {
+ return;
}
+
+ qq_request_room_search(gc, ext_id, QQ_ROOM_SEARCH_FOR_JOIN);
}
-void qq_group_exit(PurpleConnection *gc, GHashTable *data)
+void qq_room_quit(PurpleConnection *gc, guint32 room_id)
{
- gchar *id_ptr;
- guint32 id;
- gc_and_uid *g;
-
- g_return_if_fail(data != NULL);
+ qq_room_req *add_req;
- id_ptr = g_hash_table_lookup(data, QQ_ROOM_KEY_INTERNAL_ID);
- id = strtol(id_ptr, NULL, 10);
-
- g_return_if_fail(id > 0);
-
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = id;
+ add_req = g_new0(qq_room_req, 1);
+ add_req->gc = gc;
+ add_req->id = room_id;
purple_request_action(gc, _("QQ Qun Operation"),
- _("Are you sure you want to leave this Qun?"),
+ _("Quit Qun"),
_("Note, if you are the creator, \nthis operation will eventually remove this Qun."),
1,
purple_connection_get_account(gc), NULL, NULL,
- g, 2, _("Cancel"),
- G_CALLBACK(qq_do_nothing_with_gc_and_uid),
- _("Continue"), G_CALLBACK(_qq_group_exit_with_gc_and_id));
+ add_req, 2, _("Cancel"),
+ G_CALLBACK(room_join_cancel_cb),
+ _("Continue"), G_CALLBACK(group_quit_cb));
+}
+
+/* send packet to search for qq_group */
+void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action)
+{
+ guint8 raw_data[16] = {0};
+ gint bytes = 0;
+ guint8 type;
+
+ purple_debug_info("QQ", "Search QQ Qun %d\n", ext_id);
+ type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID;
+
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, type);
+ bytes += qq_put32(raw_data + bytes, ext_id);
+
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_SEARCH, 0, raw_data, bytes, 0, action);
+}
+
+static void add_to_roomlist(qq_data *qd, qq_room_data *rmd)
+{
+ PurpleRoomlistRoom *room;
+ gchar field[11];
+
+ room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, rmd->title_utf8, NULL);
+ g_snprintf(field, sizeof(field), "%d", rmd->ext_id);
+ purple_roomlist_room_add_field(qd->roomlist, room, field);
+ g_snprintf(field, sizeof(field), "%d", rmd->creator_uid);
+ purple_roomlist_room_add_field(qd->roomlist, room, field);
+ purple_roomlist_room_add_field(qd->roomlist, room, rmd->desc_utf8);
+ g_snprintf(field, sizeof(field), "%d", rmd->id);
+ purple_roomlist_room_add_field(qd->roomlist, room, field);
+ g_snprintf(field, sizeof(field), "%d", rmd->type8);
+ purple_roomlist_room_add_field(qd->roomlist, room, field);
+ g_snprintf(field, sizeof(field), "%d", rmd->auth_type);
+ purple_roomlist_room_add_field(qd->roomlist, room, field);
+ g_snprintf(field, sizeof(field), "%d", rmd->category);
+ purple_roomlist_room_add_field(qd->roomlist, room, field);
+ purple_roomlist_room_add_field(qd->roomlist, room, rmd->title_utf8);
+ purple_roomlist_room_add(qd->roomlist, room);
+
+ purple_roomlist_set_in_progress(qd->roomlist, FALSE);
+}
+
+/* process group cmd reply "search group" */
+void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32)
+{
+ qq_data *qd;
+ qq_room_data rmd;
+ PurpleChat *chat;
+ gint bytes;
+ guint8 search_type;
+ guint16 unknown;
+
+ g_return_if_fail(data != NULL && len > 0);
+ qd = (qq_data *) gc->proto_data;
+
+ bytes = 0;
+ bytes += qq_get8(&search_type, data + bytes);
+
+ /* now it starts with group_info_entry */
+ bytes += qq_get32(&(rmd.id), data + bytes);
+ bytes += qq_get32(&(rmd.ext_id), data + bytes);
+ bytes += qq_get8(&(rmd.type8), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get32(&(rmd.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(&(rmd.category), data + bytes);
+ bytes += qq_get_vstr(&(rmd.title_utf8), QQ_CHARSET_DEFAULT, data + bytes);
+ bytes += qq_get16(&(unknown), data + bytes);
+ bytes += qq_get8(&(rmd.auth_type), data + bytes);
+ bytes += qq_get_vstr(&(rmd.desc_utf8), QQ_CHARSET_DEFAULT, data + bytes);
+ /* end of one qq_group */
+ if(bytes != len) {
+ purple_debug_error("QQ",
+ "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!");
+ }
+
+ if (ship32 == QQ_ROOM_SEARCH_FOR_JOIN) {
+ chat = qq_room_find_or_new(gc, rmd.id, rmd.ext_id);
+ g_return_if_fail(chat != NULL);
+
+ qq_room_update_chat_info(chat, &rmd);
+ qq_request_room_join(gc, &rmd);
+ } else {
+ add_to_roomlist(qd, &rmd);
+ }
}
diff --git a/libpurple/protocols/qq/group_join.h b/libpurple/protocols/qq/group_join.h
index f6b1032e2b..1b4cbb35b8 100644
--- a/libpurple/protocols/qq/group_join.h
+++ b/libpurple/protocols/qq/group_join.h
@@ -41,12 +41,19 @@ enum {
QQ_ROOM_AUTH_REQUEST_REJECT = 0x03
};
-void qq_send_cmd_group_auth(PurpleConnection *gc, qq_group *group, guint8 opt, guint32 uid, const gchar *reason_utf8);
+enum {
+ QQ_ROOM_SEARCH_ONLY = 0,
+ QQ_ROOM_SEARCH_FOR_JOIN
+};
+
+void qq_request_room_search(PurpleConnection *gc, guint32 ext_id, int action);
+void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32);
+
+void qq_send_cmd_group_auth(PurpleConnection *gc, qq_room_data *rmd, guint8 opt, guint32 uid, const gchar *reason_utf8);
void qq_group_join(PurpleConnection *gc, GHashTable *data);
-void qq_request_room_join(PurpleConnection *gc, qq_group *group);
-void qq_group_exit(PurpleConnection *gc, GHashTable *data);
+void qq_request_room_join(PurpleConnection *gc, qq_room_data *rmd);
+void qq_room_quit(PurpleConnection *gc, guint32 room_id);
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_opt.c b/libpurple/protocols/qq/group_opt.c
index 4d8002a97c..b2ae60ffec 100644
--- a/libpurple/protocols/qq/group_opt.c
+++ b/libpurple/protocols/qq/group_opt.c
@@ -30,12 +30,12 @@
#include "buddy_info.h"
#include "char_conv.h"
-#include "group_find.h"
#include "group_internal.h"
#include "group_info.h"
#include "group_join.h"
+#include "group_im.h"
#include "group_opt.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "qq_process.h"
@@ -57,7 +57,7 @@ static void _sort(guint32 *list)
qsort (list, i, sizeof (guint32), _compare_guint32);
}
-static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint operation, guint32 *members)
+static void _qq_group_member_opt(PurpleConnection *gc, qq_room_data *rmd, gint operation, guint32 *members)
{
guint8 *data;
gint i, count, data_len;
@@ -74,80 +74,71 @@ static void _qq_group_member_opt(PurpleConnection *gc, qq_group *group, gint ope
for (i = 0; i < count; i++)
bytes += qq_put32(data + bytes, members[i]);
- qq_send_room_cmd(gc, QQ_ROOM_CMD_MEMBER_OPT, group->id, data, bytes);
+ qq_send_room_cmd(gc, QQ_ROOM_CMD_MEMBER_OPT, rmd->id, data, bytes);
}
-static void _qq_group_do_nothing_with_struct(group_member_opt *g)
+static void room_req_cancel_cb(qq_room_req *add_req)
{
- if (g != NULL)
- g_free(g);
+ if (add_req != NULL)
+ g_free(add_req);
}
-static void _qq_group_reject_application_real(group_member_opt *g, gchar *msg_utf8)
+static void member_join_authorize_cb(gpointer data)
{
- qq_group *group;
- g_return_if_fail(g != NULL && g->gc != NULL && g->id > 0 && g->member > 0);
- group = qq_room_search_id(g->gc, g->id);
- g_return_if_fail(group != NULL);
- qq_send_cmd_group_auth(g->gc, group, QQ_ROOM_AUTH_REQUEST_REJECT, g->member, msg_utf8);
- g_free(g);
+ qq_room_req *add_req = (qq_room_req *)data;
+ qq_room_data *rmd;
+ g_return_if_fail(add_req != NULL && add_req->gc != NULL);
+ g_return_if_fail(add_req->id > 0 && add_req->member > 0);
+ rmd = qq_room_data_find(add_req->gc, add_req->id);
+ g_return_if_fail(rmd != NULL);
+
+ qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, add_req->member, "");
+ qq_room_buddy_find_or_new(add_req->gc, rmd, add_req->member);
+ g_free(add_req);
}
-void qq_group_search_application_with_struct(group_member_opt *g)
+static void member_join_deny_reason_cb(qq_room_req *add_req, gchar *msg_utf8)
{
- g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0);
-
- 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));
+ qq_room_data *rmd;
+ g_return_if_fail(add_req != NULL && add_req->gc != NULL);
+ g_return_if_fail(add_req->id > 0 && add_req->member > 0);
+ rmd = qq_room_data_find(add_req->gc, add_req->id);
+ g_return_if_fail(rmd != NULL);
+ qq_send_cmd_group_auth(add_req->gc, rmd, QQ_ROOM_AUTH_REQUEST_REJECT, add_req->member, msg_utf8);
+ g_free(add_req);
}
-void qq_group_reject_application_with_struct(group_member_opt *g)
+static void member_join_deny_noreason_cb(qq_room_req *add_req, gchar *msg_utf8)
{
- gchar *msg1, *msg2, *nombre;
- g_return_if_fail(g != NULL && g->gc != NULL && g->member > 0);
-
- msg1 = g_strdup_printf(_("You rejected %d's request"), g->member);
- msg2 = g_strdup(_("Message:"));
-
- nombre = uid_to_purple_name(g->member);
- purple_request_input(g->gc, /* title */ NULL, msg1, msg2,
- _("Sorry, you are not my style..."), /* multiline */ TRUE, /* masked */ FALSE,
- /* hint */ NULL,
- _("Send"), G_CALLBACK(_qq_group_reject_application_real),
- _("Cancel"), G_CALLBACK(_qq_group_do_nothing_with_struct),
- purple_connection_get_account(g->gc), nombre, NULL,
- g);
-
- g_free(msg1);
- g_free(msg2);
- g_free(nombre);
+ member_join_deny_reason_cb(add_req, NULL);
}
-void qq_group_approve_application_with_struct(group_member_opt *g)
+static void member_join_deny_cb(gpointer data)
{
- qq_group *group;
- g_return_if_fail(g != NULL && g->gc != NULL && g->id > 0 && g->member > 0);
- group = qq_room_search_id(g->gc, g->id);
- g_return_if_fail(group != NULL);
- qq_send_cmd_group_auth(g->gc, group, QQ_ROOM_AUTH_REQUEST_APPROVE, g->member, "");
- qq_group_find_or_add_member(g->gc, group, g->member);
- g_free(g);
+ qq_room_req *add_req = (qq_room_req *)data;
+ gchar *who;
+ g_return_if_fail(add_req != NULL && add_req->gc != NULL);
+ g_return_if_fail(add_req->id > 0 && add_req->member > 0);
+
+ who = uid_to_purple_name(add_req->member);
+ purple_request_input(add_req->gc, NULL, _("Authorization denied message:"),
+ NULL, _("Sorry, you are not our style ..."), TRUE, FALSE, NULL,
+ _("OK"), G_CALLBACK(member_join_deny_reason_cb),
+ _("Cancel"), G_CALLBACK(member_join_deny_noreason_cb),
+ purple_connection_get_account(add_req->gc), who, NULL,
+ add_req);
+ g_free(who);
}
-void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new_members)
+void qq_group_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members)
{
guint32 *old_members, *del_members, *add_members;
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
qq_data *qd;
gint i = 0, old = 0, new = 0, del = 0, add = 0;
GList *list;
- g_return_if_fail(group != NULL);
+ g_return_if_fail(rmd != NULL);
qd = (qq_data *) gc->proto_data;
if (new_members[0] == 0xffffffff)
return;
@@ -157,11 +148,11 @@ void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new
add_members = g_newa(guint32, QQ_QUN_MEMBER_MAX);
/* construct the old member list */
- list = group->members;
+ list = rmd->members;
while (list != NULL) {
- q_bud = (qq_buddy *) list->data;
- if (q_bud != NULL)
- old_members[i++] = q_bud->uid;
+ bd = (qq_buddy_data *) list->data;
+ if (bd != NULL)
+ old_members[i++] = bd->uid;
list = list->next;
}
old_members[i] = 0xffffffff; /* this is the end */
@@ -185,21 +176,22 @@ void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new
del_members[del] = add_members[add] = 0xffffffff;
for (i = 0; i < del; i++)
- qq_group_remove_member_by_uid(group, del_members[i]);
+ qq_room_buddy_remove(rmd, del_members[i]);
for (i = 0; i < add; i++)
- qq_group_find_or_add_member(gc, group, add_members[i]);
+ qq_room_buddy_find_or_new(gc, rmd, add_members[i]);
if (del > 0)
- _qq_group_member_opt(gc, group, QQ_ROOM_MEMBER_DEL, del_members);
+ _qq_group_member_opt(gc, rmd, QQ_ROOM_MEMBER_DEL, del_members);
if (add > 0)
- _qq_group_member_opt(gc, group, QQ_ROOM_MEMBER_ADD, add_members);
+ _qq_group_member_opt(gc, rmd, QQ_ROOM_MEMBER_ADD, add_members);
}
void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnection *gc)
{
gint bytes;
guint32 id;
- qq_group *group;
+ time_t now = time(NULL);
+ qq_room_data *rmd;
g_return_if_fail(data != NULL);
bytes = 0;
@@ -207,82 +199,60 @@ void qq_group_process_modify_members_reply(guint8 *data, gint len, PurpleConnect
g_return_if_fail(id > 0);
/* we should have its info locally */
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
- purple_debug_info("QQ", "Succeed in modify members for room %d\n", group->ext_id);
+ purple_debug_info("QQ", "Succeed in modify members for room %d\n", rmd->ext_id);
- purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Change Qun member"));
+ qq_room_got_chat_in(gc, id, 0, _("Successed changing Qun member"), now);
}
-void qq_room_change_info(PurpleConnection *gc, qq_group *group)
+void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd)
{
- guint8 *data;
- gint data_len;
+ guint8 data[MAX_PACKET_SIZE - 16];
gint bytes;
- gchar *group_name, *group_desc, *notice;
-
- g_return_if_fail(group != NULL);
- group_name = group->title_utf8 == NULL ? "" : utf8_to_qq(group->title_utf8, QQ_CHARSET_DEFAULT);
- group_desc = group->desc_utf8 == NULL ? "" : utf8_to_qq(group->desc_utf8, QQ_CHARSET_DEFAULT);
- notice = group->notice_utf8 == NULL ? "" : utf8_to_qq(group->notice_utf8, QQ_CHARSET_DEFAULT);
+ g_return_if_fail(rmd != NULL);
- data_len = 64 + strlen(group_name) + strlen(group_desc) + strlen(notice);
- data = g_newa(guint8, data_len);
bytes = 0;
/* 005-005 */
bytes += qq_put8(data + bytes, 0x01);
/* 006-006 */
- bytes += qq_put8(data + bytes, group->auth_type);
+ bytes += qq_put8(data + bytes, rmd->auth_type);
/* 007-008 */
bytes += qq_put16(data + bytes, 0x0000);
/* 009-010 */
- bytes += qq_put16(data + bytes, group->category);
+ bytes += qq_put16(data + bytes, rmd->category);
- bytes += qq_put8(data + bytes, strlen(group_name));
- bytes += qq_putdata(data + bytes, (guint8 *) group_name, strlen(group_name));
+ bytes += qq_put_vstr(data + bytes, rmd->title_utf8, QQ_CHARSET_DEFAULT);
bytes += qq_put16(data + bytes, 0x0000);
- bytes += qq_put8(data + bytes, strlen(notice));
- bytes += qq_putdata(data+ bytes, (guint8 *) notice, strlen(notice));
-
- bytes += qq_put8(data + bytes, strlen(group_desc));
- bytes += qq_putdata(data + bytes, (guint8 *) group_desc, strlen(group_desc));
+ bytes += qq_put_vstr(data + bytes, rmd->notice_utf8, QQ_CHARSET_DEFAULT);
+ bytes += qq_put_vstr(data + bytes, rmd->desc_utf8, QQ_CHARSET_DEFAULT);
- if (bytes > data_len) {
- purple_debug_error("QQ",
- "Overflow in qq_room_change_info, max %d bytes, now %d bytes\n",
- data_len, bytes);
- return;
- }
- qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, group->id, data, bytes);
+ qq_send_room_cmd(gc, QQ_ROOM_CMD_CHANGE_INFO, rmd->id, data, bytes);
}
void qq_group_process_modify_info_reply(guint8 *data, gint len, PurpleConnection *gc)
{
gint bytes;
guint32 id;
- qq_group *group;
+ time_t now = time(NULL);
+
g_return_if_fail(data != NULL);
bytes = 0;
bytes += qq_get32(&id, data + bytes);
g_return_if_fail(id > 0);
- /* we should have its info locally */
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
-
- purple_debug_info("QQ", "Succeed in modify info for Qun %d\n", group->ext_id);
- qq_group_refresh(gc, group);
+ purple_debug_info("QQ", "Succeed modify room info of %d\n", id);
- purple_notify_info(gc, _("QQ Qun Operation"), _("Successed:"), _("Change Qun information"));
+ qq_room_got_chat_in(gc, id, 0, _("Successed changing Qun information"), now);
}
-/* we create a very simple group first, and then let the user to modify */
-void qq_room_create_new(PurpleConnection *gc, const gchar *name)
+/* we create a very simple room first, and then let the user to modify */
+void qq_create_room(PurpleConnection *gc, const gchar *name)
{
guint8 *data;
gint data_len;
@@ -322,25 +292,32 @@ void qq_room_create_new(PurpleConnection *gc, const gchar *name)
qq_send_room_cmd_noid(gc, QQ_ROOM_CMD_CREATE, data, bytes);
}
-static void qq_group_setup_with_gc_and_uid(gc_and_uid *g)
+static void room_create_cb(qq_room_req *add_req)
{
- qq_group *group;
- g_return_if_fail(g != NULL && g->gc != NULL && g->uid > 0);
+ qq_room_data *rmd;
+ g_return_if_fail(add_req != NULL);
+ if (add_req->gc == NULL || add_req->id == 0) {
+ g_free(add_req);
+ return;
+ }
- group = qq_room_search_id(g->gc, g->uid);
- g_return_if_fail(group != NULL);
+ rmd = qq_room_data_find(add_req->gc, add_req->id);
+ if (rmd == NULL) {
+ g_free(add_req);
+ return;
+ }
/* TODO insert UI code here */
- /* qq_group_detail_window_show(g->gc, group); */
- g_free(g);
+ /* qq_group_detail_window_show(g->gc, rmd); */
+ g_free(add_req);
}
void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnection *gc)
{
gint bytes;
guint32 id, ext_id;
- qq_group *group;
- gc_and_uid *g;
+ qq_room_data *rmd;
+ qq_room_req *add_req;
qq_data *qd;
g_return_if_fail(data != NULL);
@@ -352,36 +329,37 @@ void qq_group_process_create_group_reply(guint8 *data, gint len, PurpleConnectio
bytes += qq_get32(&ext_id, data + bytes);
g_return_if_fail(id > 0 && ext_id);
- group = qq_group_create_internal_record(gc, id, ext_id, NULL);
- group->my_role = QQ_ROOM_ROLE_ADMIN;
- group->creator_uid = qd->uid;
- qq_group_refresh(gc, group);
+ qq_room_find_or_new(gc, id, ext_id);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
+
+ rmd->my_role = QQ_ROOM_ROLE_ADMIN;
+ rmd->creator_uid = qd->uid;
qq_send_room_cmd_only(gc, QQ_ROOM_CMD_ACTIVATE, id);
- qq_update_room(gc, 0, group->id);
+ qq_update_room(gc, 0, rmd->id);
- purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", group->ext_id);
+ purple_debug_info("QQ", "Succeed in create Qun, external ID %d\n", rmd->ext_id);
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = id;
+ add_req = g_new0(qq_room_req, 1);
+ add_req->gc = gc;
+ add_req->id = id;
purple_request_action(gc, _("QQ Qun Operation"),
_("You have successfully created a Qun"),
- _
- ("Would you like to set up the detail information now?"),
+ _("Would you like to set up the detail information now?"),
1,
purple_connection_get_account(gc), NULL, NULL,
- g, 2,
- _("Setup"), G_CALLBACK(qq_group_setup_with_gc_and_uid),
- _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid));
+ add_req, 2,
+ _("Setup"), G_CALLBACK(room_create_cb),
+ _("Cancel"), G_CALLBACK(room_req_cancel_cb));
}
void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnection *gc)
{
gint bytes;
guint32 id;
- qq_group *group;
+ qq_room_data *rmd;
g_return_if_fail(data != NULL);
bytes = 0;
@@ -389,17 +367,17 @@ void qq_group_process_activate_group_reply(guint8 *data, gint len, PurpleConnect
g_return_if_fail(id > 0);
/* we should have its info locally */
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
- purple_debug_info("QQ", "Succeed in activate Qun %d\n", group->ext_id);
+ purple_debug_info("QQ", "Succeed in activate Qun %d\n", rmd->ext_id);
}
void qq_group_manage_group(PurpleConnection *gc, GHashTable *data)
{
gchar *id_ptr;
guint32 id;
- qq_group *group;
+ qq_room_data *rmd;
g_return_if_fail(data != NULL);
@@ -407,9 +385,211 @@ void qq_group_manage_group(PurpleConnection *gc, GHashTable *data)
id = strtol(id_ptr, NULL, 10);
g_return_if_fail(id > 0);
- group = qq_room_search_id(gc, id);
- g_return_if_fail(group != NULL);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
/* XXX insert UI code here */
- /* qq_group_detail_window_show(gc, group); */
+ /* qq_group_detail_window_show(gc, rmd); */
+}
+
+/* receive an application to join the group */
+void qq_process_room_buddy_request_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+{
+ guint32 ext_id, member_id;
+ guint8 type8;
+ gchar *msg, *reason;
+ qq_room_req *add_req;
+ gchar *who;
+ gint bytes = 0;
+ qq_room_data *rmd;
+ time_t now = time(NULL);
+
+ g_return_if_fail(id > 0 && data != NULL && len > 0);
+
+ /* FIXME: check length here */
+
+ bytes += qq_get32(&ext_id, data + bytes);
+ bytes += qq_get8(&type8, data + bytes);
+ bytes += qq_get32(&member_id, data + bytes);
+
+ g_return_if_fail(ext_id > 0 && member_id > 0);
+
+ bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
+
+ add_req = g_new0(qq_room_req, 1);
+ add_req->gc = gc;
+ add_req->id = id;
+ add_req->member = member_id;
+
+ purple_debug_info("QQ", "%d requested to join room, ext id %d\n", member_id, ext_id);
+
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
+ if (qq_room_buddy_find(rmd, member_id)) {
+ purple_debug_info("QQ", "Approve join, buddy joined before\n");
+ msg = g_strdup_printf(_("%d requested to join Qun %d for %s"),
+ member_id, ext_id, reason);
+ qq_room_got_chat_in(gc, id, 0, msg, now);
+ qq_send_cmd_group_auth(gc, rmd, QQ_ROOM_AUTH_REQUEST_APPROVE, member_id, "");
+ g_free(msg);
+ g_free(reason);
+ return;
+ }
+
+ if (purple_prefs_get_bool("/plugins/prpl/qq/auto_get_authorize_info")) {
+ qq_request_buddy_info(gc, member_id, 0, QQ_BUDDY_INFO_DISPLAY);
+ }
+ who = uid_to_purple_name(member_id);
+ msg = g_strdup_printf(_("%d request to join Qun %d"), member_id, ext_id);
+
+ purple_request_action(gc, _("QQ Qun Operation"),
+ msg, reason,
+ PURPLE_DEFAULT_ACTION_NONE,
+ purple_connection_get_account(gc), who, NULL,
+ add_req, 2,
+ _("Deny"), G_CALLBACK(member_join_deny_cb),
+ _("Authorize"), G_CALLBACK(member_join_authorize_cb));
+
+ g_free(who);
+ g_free(msg);
+ g_free(reason);
+ g_free(reason);
+}
+
+/* the request to join a group is rejected */
+void qq_process_room_buddy_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+{
+ guint32 ext_id, admin_uid;
+ guint8 type8;
+ gchar *msg, *reason;
+ qq_room_data *rmd;
+ gint bytes;
+
+ g_return_if_fail(data != NULL && len > 0);
+
+ /* FIXME: check length here */
+ bytes = 0;
+ bytes += qq_get32(&ext_id, data + bytes);
+ bytes += qq_get8(&type8, data + bytes);
+ bytes += qq_get32(&admin_uid, data + bytes);
+
+ g_return_if_fail(ext_id > 0 && admin_uid > 0);
+
+ bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
+
+ msg = g_strdup_printf
+ (_("Failed to join Qun %d, operated by admin %d"), ext_id, admin_uid);
+
+ purple_notify_warning(gc, _("QQ Qun Operation"), msg, reason);
+
+ qq_room_find_or_new(gc, id, ext_id);
+ rmd = qq_room_data_find(gc, id);
+ if (rmd != NULL) {
+ rmd->my_role = QQ_ROOM_ROLE_NO;
+ }
+
+ g_free(msg);
+ g_free(reason);
+}
+
+/* the request to join a group is approved */
+void qq_process_room_buddy_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+{
+ guint32 ext_id, admin_uid;
+ guint8 type8;
+ gchar *msg, *reason;
+ qq_room_data *rmd;
+ gint bytes;
+ time_t now;
+
+ g_return_if_fail(data != NULL && len > 0);
+
+ /* FIXME: check length here */
+ bytes = 0;
+ bytes += qq_get32(&ext_id, data + bytes);
+ bytes += qq_get8(&type8, data + bytes);
+ bytes += qq_get32(&admin_uid, data + bytes);
+
+ g_return_if_fail(ext_id > 0 && admin_uid > 0);
+ /* it is also a "æ— " here, so do not display */
+ bytes += qq_get_vstr(&reason, QQ_CHARSET_DEFAULT, data + bytes);
+
+ qq_room_find_or_new(gc, id, ext_id);
+ rmd = qq_room_data_find(gc, id);
+ if (rmd != NULL) {
+ rmd->my_role = QQ_ROOM_ROLE_YES;
+ }
+
+ msg = g_strdup_printf(_("<b>Joinning Qun %d is approved by Admin %d for %s</b>"),
+ ext_id, admin_uid, reason);
+ now = time(NULL);
+ qq_room_got_chat_in(gc, id, 0, msg, now);
+
+ g_free(msg);
+ g_free(reason);
+}
+
+/* process the packet when removed from a group */
+void qq_process_room_buddy_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+{
+ guint32 ext_id, uid;
+ guint8 type8;
+ gchar *msg;
+ qq_room_data *rmd;
+ gint bytes = 0;
+ time_t now = time(NULL);
+
+ g_return_if_fail(data != NULL && len > 0);
+
+ /* FIXME: check length here */
+ bytes = 0;
+ bytes += qq_get32(&ext_id, data + bytes);
+ bytes += qq_get8(&type8, data + bytes);
+ bytes += qq_get32(&uid, data + bytes);
+
+ g_return_if_fail(ext_id > 0 && uid > 0);
+
+ qq_room_find_or_new(gc, id, ext_id);
+ rmd = qq_room_data_find(gc, id);
+ if (rmd != NULL) {
+ rmd->my_role = QQ_ROOM_ROLE_NO;
+ }
+
+ msg = g_strdup_printf(_("<b>Removed buddy %d.</b>"), uid);
+ qq_room_got_chat_in(gc, id, 0, msg, now);
+ g_free(msg);
}
+
+/* process the packet when added to a group */
+void qq_process_room_buddy_joined(guint8 *data, gint len, guint32 id, PurpleConnection *gc)
+{
+ guint32 ext_id, uid;
+ guint8 type8;
+ qq_room_data *rmd;
+ gint bytes;
+ gchar *msg;
+ time_t now = time(NULL);
+
+ g_return_if_fail(data != NULL && len > 0);
+
+ /* FIXME: check length here */
+ bytes = 0;
+ bytes += qq_get32(&ext_id, data + bytes);
+ bytes += qq_get8(&type8, data + bytes);
+ bytes += qq_get32(&uid, data + bytes);
+
+ g_return_if_fail(ext_id > 0 && id > 0);
+
+ qq_room_find_or_new(gc, id, ext_id);
+ rmd = qq_room_data_find(gc, id);
+ g_return_if_fail(rmd != NULL);
+
+ rmd->my_role = QQ_ROOM_ROLE_YES;
+
+ qq_update_room(gc, 0, rmd->id);
+
+ msg = g_strdup_printf(_("<b>New buddy %d joined.</b>"), uid);
+ qq_room_got_chat_in(gc, id, 0, msg, now);
+ g_free(msg);
+}
+
diff --git a/libpurple/protocols/qq/group_opt.h b/libpurple/protocols/qq/group_opt.h
index 3946279006..f8006c532c 100644
--- a/libpurple/protocols/qq/group_opt.h
+++ b/libpurple/protocols/qq/group_opt.h
@@ -31,11 +31,11 @@
#define QQ_QUN_MEMBER_MAX 80 /* max number of the group */
-typedef struct _group_member_opt {
+typedef struct _qq_room_req {
PurpleConnection *gc;
guint32 id;
guint32 member;
-} group_member_opt;
+} qq_room_req;
enum {
QQ_ROOM_TYPE_PERMANENT = 0x01,
@@ -47,18 +47,19 @@ enum {
QQ_ROOM_MEMBER_DEL
};
-void qq_group_modify_members(PurpleConnection *gc, qq_group *group, guint32 *new_members);
-void qq_room_change_info(PurpleConnection *gc, qq_group *group);
-
-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_modify_members(PurpleConnection *gc, qq_room_data *rmd, guint32 *new_members);
+void qq_room_change_info(PurpleConnection *gc, qq_room_data *rmd);
+void qq_create_room(PurpleConnection *gc, const gchar *name);
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_room_create_new(PurpleConnection *gc, const gchar *name);
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);
+void qq_process_room_buddy_request_join(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_buddy_rejected(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_buddy_approved(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_buddy_removed(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
+void qq_process_room_buddy_joined(guint8 *data, gint len, guint32 id, PurpleConnection *gc);
#endif
diff --git a/libpurple/protocols/qq/group_search.c b/libpurple/protocols/qq/group_search.c
deleted file mode 100644
index 9996c70cfb..0000000000
--- a/libpurple/protocols/qq/group_search.c
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * @file group_search.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 "debug.h"
-
-#include "char_conv.h"
-#include "group_find.h"
-#include "group_free.h"
-#include "group_internal.h"
-#include "group_join.h"
-#include "group_search.h"
-#include "utils.h"
-#include "header_info.h"
-#include "packet_parse.h"
-#include "qq_network.h"
-
-enum {
- QQ_ROOM_SEARCH_TYPE_BY_ID = 0x01,
- QQ_ROOM_SEARCH_TYPE_DEMO = 0x02
-};
-
-/* send packet to search for qq_group */
-void qq_send_cmd_group_search_group(PurpleConnection *gc, guint32 ext_id)
-{
- guint8 raw_data[16] = {0};
- gint bytes = 0;
- guint8 type;
-
- type = (ext_id == 0x00000000) ? QQ_ROOM_SEARCH_TYPE_DEMO : QQ_ROOM_SEARCH_TYPE_BY_ID;
-
- bytes = 0;
- bytes += qq_put8(raw_data + bytes, type);
- bytes += qq_put32(raw_data + bytes, ext_id);
-
- qq_send_room_cmd_noid(gc, QQ_ROOM_CMD_SEARCH, raw_data, bytes);
-}
-
-static void _qq_setup_roomlist(qq_data *qd, qq_group *group)
-{
- PurpleRoomlistRoom *room;
- gchar field[11];
-
- room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, group->title_utf8, NULL);
- g_snprintf(field, sizeof(field), "%d", group->ext_id);
- purple_roomlist_room_add_field(qd->roomlist, room, field);
- g_snprintf(field, sizeof(field), "%d", group->creator_uid);
- purple_roomlist_room_add_field(qd->roomlist, room, field);
- purple_roomlist_room_add_field(qd->roomlist, room, group->desc_utf8);
- g_snprintf(field, sizeof(field), "%d", group->id);
- purple_roomlist_room_add_field(qd->roomlist, room, field);
- g_snprintf(field, sizeof(field), "%d", group->type8);
- purple_roomlist_room_add_field(qd->roomlist, room, field);
- g_snprintf(field, sizeof(field), "%d", group->auth_type);
- purple_roomlist_room_add_field(qd->roomlist, room, field);
- g_snprintf(field, sizeof(field), "%d", group->category);
- purple_roomlist_room_add_field(qd->roomlist, room, field);
- purple_roomlist_room_add_field(qd->roomlist, room, group->title_utf8);
- purple_roomlist_room_add(qd->roomlist, room);
-
- purple_roomlist_set_in_progress(qd->roomlist, FALSE);
-}
-
-/* process group cmd reply "search group" */
-void qq_process_group_cmd_search_group(guint8 *data, gint len, PurpleConnection *gc)
-{
- gint bytes;
- guint8 search_type;
- guint16 unknown;
- qq_group group;
- qq_data *qd;
- GSList *pending_id;
-
- g_return_if_fail(data != NULL && len > 0);
- qd = (qq_data *) gc->proto_data;
-
- bytes = 0;
- bytes += qq_get8(&search_type, data + bytes);
-
- /* now it starts with group_info_entry */
- bytes += qq_get32(&(group.id), data + bytes);
- bytes += qq_get32(&(group.ext_id), data + bytes);
- bytes += qq_get8(&(group.type8), 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.category), data + bytes);
- bytes += convert_as_pascal_string(data + bytes, &(group.title_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.desc_utf8), QQ_CHARSET_DEFAULT);
- /* end of one qq_group */
- if(bytes != len) {
- 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.ext_id);
- if (pending_id != NULL) {
- qq_set_pending_id(&qd->joining_groups, group.ext_id, FALSE);
- if (qq_room_search_id(gc, group.id) == NULL)
- qq_group_create_internal_record(gc,
- group.id, group.ext_id, group.title_utf8);
- qq_request_room_join(gc, &group);
- } else {
- _qq_setup_roomlist(qd, &group);
- }
-}
diff --git a/libpurple/protocols/qq/group_search.h b/libpurple/protocols/qq/group_search.h
deleted file mode 100644
index c370a9341d..0000000000
--- a/libpurple/protocols/qq/group_search.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * @file group_search.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_GROUP_SEARCH_H_
-#define _QQ_GROUP_SEARCH_H_
-
-#include <glib.h>
-#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, gint len, PurpleConnection *gc);
-
-#endif
diff --git a/libpurple/protocols/qq/im.c b/libpurple/protocols/qq/im.c
index 845bcc6bed..2dc45265b0 100644
--- a/libpurple/protocols/qq/im.c
+++ b/libpurple/protocols/qq/im.c
@@ -35,15 +35,13 @@
#include "buddy_list.h"
#include "buddy_opt.h"
#include "char_conv.h"
-#include "group_im.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "im.h"
#include "packet_parse.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
enum
@@ -64,52 +62,15 @@ enum
QQ_NORMAL_IM_FILE_EX_NOTIFY_IP = 0x87
};
-enum {
- QQ_RECV_SYS_IM_KICK_OUT = 0x01
-};
-
-typedef struct _qq_recv_im_header qq_recv_im_header;
-typedef struct _qq_recv_normal_im_text qq_recv_normal_im_text;
-typedef struct _qq_recv_normal_im_common qq_recv_normal_im_common;
-typedef struct _qq_recv_normal_im_unprocessed qq_recv_normal_im_unprocessed;
+typedef struct _qq_im_header qq_im_header;
+typedef struct _qq_recv_extended_im_text qq_recv_extended_im_text;
-struct _qq_recv_normal_im_common {
+struct _qq_im_header {
/* this is the common part of normal_text */
- guint16 sender_ver;
- guint32 sender_uid;
- guint32 receiver_uid;
+ guint16 version_from;
+ guint32 uid_from;
+ guint32 uid_to;
guint8 session_md5[QQ_KEY_LENGTH];
- guint16 normal_im_type;
-};
-
-struct _qq_recv_normal_im_text {
- qq_recv_normal_im_common *common;
- /* now comes the part for text only */
- guint16 msg_seq;
- guint32 send_time;
- guint16 sender_icon;
- guint8 unknown2[3];
- guint8 is_there_font_attr;
- guint8 unknown3[4];
- guint8 msg_type;
- gchar *msg; /* no fixed length, ends with 0x00 */
- guint8 *font_attr;
- gint font_attr_len;
-};
-
-struct _qq_recv_normal_im_unprocessed {
- qq_recv_normal_im_common *common;
- /* now comes the part of unprocessed */
- guint8 *unknown; /* no fixed length */
- gint length;
-};
-
-struct _qq_recv_im_header {
- guint32 sender_uid;
- guint32 receiver_uid;
- guint32 server_im_seq;
- struct in_addr sender_ip;
- guint16 sender_port;
guint16 im_type;
};
@@ -182,327 +143,360 @@ guint8 *qq_get_send_im_tail(const gchar *font_color,
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";
- case QQ_RECV_IM_NEWS:
- return "QQ_RECV_IM_NEWS";
- case QQ_RECV_IM_FROM_BUDDY_2006:
- return "QQ_RECV_IM_FROM_BUDDY_2006";
- case QQ_RECV_IM_FROM_UNKNOWN_2006:
- return "QQ_RECV_IM_FROM_UNKNOWN_2006";
- default:
- return "QQ_RECV_IM_UNKNOWN";
- }
-}
-
/* 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, gint len, qq_recv_normal_im_common *common)
+static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len)
{
gint bytes;
- g_return_val_if_fail(data != NULL && len != 0 && common != NULL, -1);
+ g_return_val_if_fail(data != NULL && len > 0, -1);
bytes = 0;
- /* now push data into common header */
- bytes += qq_get16(&(common->sender_ver), data + bytes);
- bytes += qq_get32(&(common->sender_uid), data + bytes);
- bytes += qq_get32(&(common->receiver_uid), data + bytes);
- bytes += qq_getdata(common->session_md5, QQ_KEY_LENGTH, data + bytes);
- bytes += qq_get16(&(common->normal_im_type), data + bytes);
-
- if (bytes != 28) { /* read common place fail */
- purple_debug_error("QQ", "Expect 28 bytes, read %d bytes\n", bytes);
- return -1;
- }
-
+ bytes += qq_get16(&(im_header->version_from), data + bytes);
+ bytes += qq_get32(&(im_header->uid_from), data + bytes);
+ bytes += qq_get32(&(im_header->uid_to), data + bytes);
+ bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes);
+ bytes += qq_get16(&(im_header->im_type), data + bytes);
return bytes;
}
-static void _qq_process_recv_news(guint8 *data, gint data_len, PurpleConnection *gc)
+void qq_got_attention(PurpleConnection *gc, const gchar *msg)
{
- qq_data *qd = (qq_data *) gc->proto_data;
- gint bytes;
- guint8 *temp;
- guint8 temp_len;
- gchar *title, *brief, *url;
- gchar *title_utf8;
- gchar *content, *content_utf8;
-
- g_return_if_fail(data != NULL && data_len != 0);
-
-#if 0
- qq_show_packet("Rcv news", data, data_len);
-#endif
-
- temp = g_newa(guint8, data_len);
- bytes = 4; /* ignore unknown 4 bytes */
-
- bytes += qq_get8(&temp_len, data + bytes);
- g_return_if_fail(bytes + temp_len <= data_len);
- bytes += qq_getdata(temp, temp_len, data+bytes);
- title = g_strndup((gchar *)temp, temp_len);
-
- bytes += qq_get8(&temp_len, data + bytes);
- g_return_if_fail(bytes + temp_len <= data_len);
- bytes += qq_getdata(temp, temp_len, data+bytes);
- brief = g_strndup((gchar *)temp, temp_len);
-
- bytes += qq_get8(&temp_len, data + bytes);
- g_return_if_fail(bytes + temp_len <= data_len);
- bytes += qq_getdata(temp, temp_len, data+bytes);
- url = g_strndup((gchar *)temp, temp_len);
-
- title_utf8 = qq_to_utf8(title, QQ_CHARSET_DEFAULT);
- content = g_strdup_printf(_("%s\n\n%s"), brief, url);
- content_utf8 = qq_to_utf8(content, QQ_CHARSET_DEFAULT);
-
- if (qd->is_show_news) {
- purple_notify_info(gc, _("QQ Server News"), title_utf8, content_utf8);
- } else {
- purple_debug_info("QQ", "QQ Server news:\n%s\n%s", title_utf8, content_utf8);
- }
- g_free(title);
- g_free(title_utf8);
- g_free(brief);
- g_free(url);
- g_free(content);
- g_free(content_utf8);
+ qq_data *qd;
+ gchar *from;
+ time_t now = time(NULL);
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = gc->proto_data;
+
+ g_return_if_fail(qd->uid > 0);
+
+ qq_buddy_find_or_new(gc, qd->uid);
+
+ from = uid_to_purple_name(qd->uid);
+ serv_got_im(gc, from, msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NOTIFY, now);
+ g_free(from);
}
/* process received normal text IM */
-static void _qq_process_recv_normal_im_text(guint8 *data, gint len, qq_recv_normal_im_common *common, PurpleConnection *gc)
+static void process_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
{
guint16 purple_msg_type;
- gchar *name;
+ gchar *who;
gchar *msg_with_purple_smiley;
gchar *msg_utf8_encoded;
qq_data *qd;
- qq_recv_normal_im_text *im_text;
gint bytes = 0;
PurpleBuddy *b;
- qq_buddy *qq_b;
+ qq_buddy_data *bd;
+
+ struct {
+ /* now comes the part for text only */
+ guint16 msg_seq;
+ guint32 send_time;
+ guint16 sender_icon;
+ guint8 unknown2[3];
+ guint8 is_there_font_attr;
+ guint8 unknown3[4];
+ guint8 msg_type;
+ gchar *msg; /* no fixed length, ends with 0x00 */
+ guint8 *font_attr;
+ gint font_attr_len;
+ } im_text;
+
+ g_return_if_fail (data != NULL && len > 0);
+ g_return_if_fail(im_header != NULL);
- 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_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;
+ memset(&im_text, 0, sizeof(im_text));
/* push data into im_text */
- 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);
+ 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();
*/
- bytes += qq_getdata((guint8 *) & (im_text->unknown3), 4, data + bytes);
- bytes += qq_get8(&(im_text->msg_type), data + bytes);
+ 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 *)(data + bytes), len - bytes);
+ 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 *)(data + bytes), len - bytes);
} else { /* it is normal mesasge */
- if (im_text->is_there_font_attr) {
- 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 *)(data + bytes), len - bytes);
- } /* if im_text->msg_type */
-
- name = uid_to_purple_name(common->sender_uid);
- b = purple_find_buddy(gc->account, name);
+ if (im_text.is_there_font_attr) {
+ 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 *)(data + bytes), len - bytes);
+ } /* if im_text.msg_type */
+
+ who = uid_to_purple_name(im_header->uid_from);
+ b = purple_find_buddy(gc->account, who);
+ if (b == NULL) {
+ /* create no-auth buddy */
+ b = qq_buddy_new(gc, im_header->uid_from);
+ }
+ bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data;
+ if (bd != NULL) {
+ bd->client_tag = im_header->version_from;
+ }
+
+ purple_msg_type = (im_text.msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
+
+ msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg);
+ msg_utf8_encoded = im_text.is_there_font_attr ?
+ qq_encode_to_purple(im_text.font_attr,
+ im_text.font_attr_len,
+ msg_with_purple_smiley, qd->client_version)
+ : 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
+ * as it may have been delayed when I am not online. */
+ serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time);
+
+ g_free(msg_utf8_encoded);
+ g_free(msg_with_purple_smiley);
+ g_free(who);
+ g_free(im_text.msg);
+ if (im_text.font_attr) g_free(im_text.font_attr);
+}
+
+/* process received extended (2007) text IM */
+static void process_extend_im_text(
+ PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header)
+{
+ guint16 purple_msg_type;
+ gchar *who;
+ gchar *msg_with_purple_smiley;
+ gchar *msg_utf8_encoded;
+ qq_data *qd;
+ PurpleBuddy *b;
+ qq_buddy_data *bd;
+ gint bytes, text_len;
+
+ struct {
+ /* now comes the part for text only */
+ guint16 sessionId;
+ guint32 send_time;
+ guint16 senderHead;
+ guint32 flag;
+ guint8 unknown2[8];
+ guint8 fragmentCount;
+ guint8 fragmentIndex;
+ guint16 messageId;
+ guint8 replyType;
+ gchar *msg; /* no fixed length, ends with 0x00 */
+ guint8 fromMobileQQ;
+
+ guint8 is_there_font_attr;
+ guint8 *font_attr;
+ gint8 font_attr_len;
+ } im_text;
+
+ g_return_if_fail (data != NULL && len > 0);
+ g_return_if_fail(im_header != NULL);
+
+ qd = (qq_data *) gc->proto_data;
+ memset(&im_text, 0, sizeof(im_text));
+
+ /* push data into im_text */
+ bytes = 0;
+ bytes += qq_get16(&(im_text.sessionId), data + bytes);
+ bytes += qq_get32(&(im_text.send_time), data + bytes);
+ bytes += qq_get16(&(im_text.senderHead), data + bytes);
+ bytes += qq_get32(&(im_text.flag), data + bytes);
+
+ bytes += qq_getdata(im_text.unknown2, 8, data + bytes);
+ bytes += qq_get8(&(im_text.fragmentCount), data + bytes);
+ bytes += qq_get8(&(im_text.fragmentIndex), data + bytes);
+
+ bytes += qq_get16(&(im_text.messageId), data + bytes);
+ bytes += qq_get8(&(im_text.replyType), data + bytes);
+
+ im_text.font_attr_len = data[len-1] & 0xff;
+
+ text_len = len - bytes - im_text.font_attr_len;
+ im_text.msg = g_strndup((gchar *)(data + bytes), text_len);
+ bytes += text_len;
+ if(im_text.font_attr_len >= 0)
+ im_text.font_attr = g_memdup(data + bytes, im_text.font_attr_len);
+ else
+ {
+ purple_debug_error("QQ", "Failed to get IM's font attribute len %d\n",
+ im_text.font_attr_len);
+ return;
+ }
+
+ if(im_text.fragmentCount == 0)
+ im_text.fragmentCount = 1;
+
+ // Filter tail space
+ if(im_text.fragmentIndex == im_text.fragmentCount -1)
+ {
+ gint real_len = text_len;
+ while(real_len > 0 && im_text.msg[real_len - 1] == 0x20)
+ real_len --;
+
+ text_len = real_len;
+ // Null string instaed of space
+ im_text.msg[text_len] = 0;
+ }
+
+ who = uid_to_purple_name(im_header->uid_from);
+ b = purple_find_buddy(gc->account, who);
if (b == NULL) {
- qq_add_buddy_by_recv_packet(gc, common->sender_uid, FALSE, TRUE);
- b = purple_find_buddy(gc->account, name);
+ /* create no-auth buddy */
+ b = qq_buddy_new(gc, im_header->uid_from);
}
- qq_b = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (qq_b != NULL) {
- qq_b->client_version = common->sender_ver;
+ bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data;
+ if (bd != NULL) {
+ bd->client_tag = im_header->version_from;
}
- purple_msg_type = (im_text->msg_type == QQ_IM_AUTO_REPLY) ? PURPLE_MESSAGE_AUTO_RESP : 0;
+ purple_msg_type = 0;
- msg_with_purple_smiley = qq_smiley_to_purple(im_text->msg);
- msg_utf8_encoded = im_text->is_there_font_attr ?
- qq_encode_to_purple(im_text->font_attr,
- im_text->font_attr_len,
- msg_with_purple_smiley) : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
+ msg_with_purple_smiley = qq_smiley_to_purple(im_text.msg);
+ msg_utf8_encoded = im_text.font_attr ?
+ qq_encode_to_purple(im_text.font_attr,
+ im_text.font_attr_len,
+ msg_with_purple_smiley, qd->client_version)
+ : qq_to_utf8(msg_with_purple_smiley, QQ_CHARSET_DEFAULT);
- /* send encoded to purple, note that we use im_text->send_time,
+ /* send encoded to purple, note that we use im_text.send_time,
* not the time we receive the message
* as it may have been delayed when I am not online. */
- serv_got_im(gc, name, msg_utf8_encoded, purple_msg_type, (time_t) im_text->send_time);
+ serv_got_im(gc, who, msg_utf8_encoded, purple_msg_type, (time_t) im_text.send_time);
g_free(msg_utf8_encoded);
g_free(msg_with_purple_smiley);
- g_free(name);
- g_free(im_text->msg);
- if (im_text->is_there_font_attr)
- g_free(im_text->font_attr);
+ g_free(who);
+ g_free(im_text.msg);
+ if (im_text.font_attr) g_free(im_text.font_attr);
}
/* it is a normal IM, maybe text or video request */
-static void _qq_process_recv_normal_im(guint8 *data, gint len, PurpleConnection *gc)
+void qq_process_im(PurpleConnection *gc, guint8 *data, gint len)
{
gint bytes = 0;
- qq_recv_normal_im_common *common;
- qq_recv_normal_im_unprocessed *im_unprocessed;
+ qq_im_header im_header;
- g_return_if_fail (data != NULL && len != 0);
+ g_return_if_fail (data != NULL && len > 0);
- common = g_newa (qq_recv_normal_im_common, 1);
-
- bytes = _qq_normal_im_common_read(data, len, common);
+ bytes = get_im_header(&im_header, data, len);
if (bytes < 0) {
- purple_debug_error("QQ", "Fail read the common part of normal IM\n");
+ purple_debug_error("QQ", "Fail read im header, len %d\n", len);
+ qq_show_packet ("IM Header", data, len);
return;
}
+ purple_debug_info("QQ",
+ "Got IM to %d, type: %02X from: %d ver: %s (%04X)\n",
+ im_header.uid_to, im_header.im_type, im_header.uid_from,
+ qq_get_ver_desc(im_header.version_from), im_header.version_from);
- switch (common->normal_im_type) {
+ switch (im_header.im_type) {
case QQ_NORMAL_IM_TEXT:
- purple_debug_info("QQ",
- "Normal IM, text type:\n [%d] => [%d], src: %s (%04X)\n",
- common->sender_uid, common->receiver_uid,
- qq_get_ver_desc (common->sender_ver), common->sender_ver);
if (bytes >= len - 1) {
purple_debug_warning("QQ", "Received normal IM text is empty\n");
return;
}
- _qq_process_recv_normal_im_text(data + bytes, len - bytes, common, gc);
+ process_im_text(gc, data + bytes, len - bytes, &im_header);
break;
case QQ_NORMAL_IM_FILE_REJECT_UDP:
- qq_process_recv_file_reject(data + bytes, len - bytes, common->sender_uid, gc);
+ qq_process_recv_file_reject(data + bytes, len - bytes, im_header.uid_from, gc);
break;
case QQ_NORMAL_IM_FILE_APPROVE_UDP:
- qq_process_recv_file_accept(data + bytes, len - bytes, common->sender_uid, gc);
+ qq_process_recv_file_accept(data + bytes, len - bytes, im_header.uid_from, gc);
break;
case QQ_NORMAL_IM_FILE_REQUEST_UDP:
- qq_process_recv_file_request(data + bytes, len - bytes, common->sender_uid, gc);
+ qq_process_recv_file_request(data + bytes, len - bytes, im_header.uid_from, gc);
break;
case QQ_NORMAL_IM_FILE_CANCEL:
- qq_process_recv_file_cancel(data + bytes, len - bytes, common->sender_uid, gc);
+ qq_process_recv_file_cancel(data + bytes, len - bytes, im_header.uid_from, gc);
break;
case QQ_NORMAL_IM_FILE_NOTIFY:
- qq_process_recv_file_notify(data + bytes, len - bytes, common->sender_uid, gc);
+ qq_process_recv_file_notify(data + bytes, len - bytes, im_header.uid_from, gc);
break;
case QQ_NORMAL_IM_FILE_REQUEST_TCP:
/* Check ReceivedFileIM::parseContents in eva*/
/* some client use this function for detect invisable buddy*/
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n");
- qq_show_packet ("Not support", data, len);
- break;
case QQ_NORMAL_IM_FILE_APPROVE_TCP:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_APPROVE_TCP\n");
- qq_show_packet ("Not support", data, len);
- break;
case QQ_NORMAL_IM_FILE_REJECT_TCP:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REJECT_TCP\n");
- qq_show_packet ("Not support", data, len);
- break;
case QQ_NORMAL_IM_FILE_PASV:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_PASV\n");
- qq_show_packet ("Not support", data, len);
- break;
case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_REQUEST_TCP\n");
- qq_show_packet ("QQ", data, len);
- break;
case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT\n");
- qq_show_packet ("QQ", data, len);
- break;
case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL\n");
- qq_show_packet ("Not support", data, len);
- break;
case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP:
- purple_debug_warning("QQ", "Normal IM, not support QQ_NORMAL_IM_FILE_EX_NOTIFY_IP\n");
qq_show_packet ("Not support", data, len);
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_warning("QQ",
- "Normal IM, unprocessed type [0x%04x], len %d\n",
- common->normal_im_type, im_unprocessed->length);
- qq_show_packet ("QQ", im_unprocessed->unknown, im_unprocessed->length);
+ qq_show_packet ("Unknow", data + bytes, len - bytes);
return;
}
}
-/* process im from system administrator */
-static void _qq_process_recv_sys_im(guint8 *data, gint data_len, PurpleConnection *gc)
+/* it is a extended IM, maybe text or video request */
+void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len)
{
- gint len;
- guint8 reply;
- gchar **segments, *msg_utf8;
-
- g_return_if_fail(data != NULL && data_len != 0);
+ gint bytes;
+ qq_im_header im_header;
- len = data_len;
+ g_return_if_fail (data != NULL && len > 0);
- if (NULL == (segments = split_data(data, len, "\x2f", 2)))
+ bytes = get_im_header(&im_header, data, len);
+ if (bytes < 0) {
+ purple_debug_error("QQ", "Fail read im header, len %d\n", len);
+ qq_show_packet ("IM Header", data, len);
return;
-
- reply = strtol(segments[0], NULL, 10);
- if (reply == QQ_RECV_SYS_IM_KICK_OUT)
- purple_debug_warning("QQ", "We are kicked out by QQ server\n");
- msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
- purple_notify_warning(gc, NULL, _("System Message"), msg_utf8);
+ }
+ purple_debug_info("QQ",
+ "Got Extend IM to %d, type: %02X from: %d ver: %s (%04X)\n",
+ im_header.uid_to, im_header.im_type, im_header.uid_from,
+ qq_get_ver_desc(im_header.version_from), im_header.version_from);
+
+ switch (im_header.im_type) {
+ case QQ_NORMAL_IM_TEXT:
+ process_extend_im_text(gc, data + bytes, len - bytes, &im_header);
+ break;
+ case QQ_NORMAL_IM_FILE_REJECT_UDP:
+ qq_process_recv_file_reject (data + bytes, len - bytes, im_header.uid_from, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_APPROVE_UDP:
+ qq_process_recv_file_accept (data + bytes, len - bytes, im_header.uid_from, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_REQUEST_UDP:
+ qq_process_recv_file_request (data + bytes, len - bytes, im_header.uid_from, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_CANCEL:
+ qq_process_recv_file_cancel (data + bytes, len - bytes, im_header.uid_from, gc);
+ break;
+ case QQ_NORMAL_IM_FILE_NOTIFY:
+ qq_process_recv_file_notify (data + bytes, len - bytes, im_header.uid_from, gc);
+ break;
+ default:
+ /* a simple process here, maybe more later */
+ qq_show_packet ("Unknow", data + bytes, len - bytes);
+ break;
+ }
}
-/* send an IM to to_uid */
-void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type)
+/* send an IM to uid_to */
+void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type)
{
qq_data *qd;
guint8 *raw_data, *send_im_tail;
- guint16 client_tag, normal_im_type;
+ guint16 im_type;
gint msg_len, raw_len, font_name_len, tail_len, bytes;
time_t now;
gchar *msg_filtered;
@@ -512,8 +506,7 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty
const gchar *start, *end, *last;
qd = (qq_data *) gc->proto_data;
- client_tag = QQ_CLIENT;
- normal_im_type = QQ_NORMAL_IM_TEXT;
+ im_type = QQ_NORMAL_IM_TEXT;
last = msg;
while (purple_markup_find_tag("font", last, &start, &end, &attribs)) {
@@ -570,17 +563,17 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty
/* 000-003: receiver uid */
bytes += qq_put32(raw_data + bytes, qd->uid);
/* 004-007: sender uid */
- bytes += qq_put32(raw_data + bytes, to_uid);
+ bytes += qq_put32(raw_data + bytes, uid_to);
/* 008-009: sender client version */
- bytes += qq_put16(raw_data + bytes, client_tag);
+ bytes += qq_put16(raw_data + bytes, qd->client_tag);
/* 010-013: receiver uid */
bytes += qq_put32(raw_data + bytes, qd->uid);
/* 014-017: sender uid */
- bytes += qq_put32(raw_data + bytes, to_uid);
+ bytes += qq_put32(raw_data + bytes, uid_to);
/* 018-033: md5 of (uid+session_key) */
bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16);
/* 034-035: message type */
- bytes += qq_put16(raw_data + bytes, normal_im_type);
+ bytes += qq_put16(raw_data + bytes, im_type);
/* 036-037: sequence number */
bytes += qq_put16(raw_data + bytes, qd->send_seq);
/* 038-041: send time */
@@ -600,10 +593,10 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty
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_send_im_tail debug", send_im_tail, tail_len);
+ /* qq_show_packet("qq_get_send_im_tail", send_im_tail, tail_len); */
bytes += qq_putdata(raw_data + bytes, send_im_tail, tail_len);
- qq_show_packet("QQ_raw_data debug", raw_data, bytes);
+ /* qq_show_packet("QQ_CMD_SEND_IM, raw_data, bytes); */
if (bytes == raw_len) /* create packet OK */
qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes);
@@ -619,127 +612,5 @@ void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint ty
g_free(msg_filtered);
}
-/* parse the reply to send_im */
-void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc)
-{
- qq_data *qd;
-
- g_return_if_fail(data != NULL && data_len != 0);
-
- qd = gc->proto_data;
-
- if (data[0] != QQ_SEND_IM_REPLY_OK) {
- purple_debug_warning("QQ", "Send IM fail\n");
- purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
- } else {
- purple_debug_info("QQ", "IM ACK OK\n");
- }
-}
-
-/* I receive a message, mainly it is text msg,
- * but we need to proess other types (group etc) */
-void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
-{
- qq_data *qd;
- gint bytes;
- qq_recv_im_header *im_header;
- g_return_if_fail(data != NULL && data_len != 0);
-
- qd = (qq_data *) gc->proto_data;
-
- if (data_len < 16) { /* we need to ack with the first 16 bytes */
- purple_debug_error("QQ", "MSG is too short\n");
- return;
- } else {
- /* when we receive a message,
- * we send an ACK which is the first 16 bytes of incoming packet */
- qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16);
- }
-
- /* check len first */
- if (data_len < 20) { /* length of im_header */
- purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
- return;
- }
-
- bytes = 0;
- im_header = g_newa(qq_recv_im_header, 1);
- bytes += qq_get32(&(im_header->sender_uid), data + bytes);
- bytes += qq_get32(&(im_header->receiver_uid), data + bytes);
- bytes += qq_get32(&(im_header->server_im_seq), data + bytes);
- /* if the message is delivered via server, it is server IP/port */
- bytes += qq_getIP(&(im_header->sender_ip), data + bytes);
- bytes += qq_get16(&(im_header->sender_port), data + bytes);
- bytes += qq_get16(&(im_header->im_type), data + bytes);
- /* im_header prepared */
-
- if (im_header->receiver_uid != qd->uid) { /* should not happen */
- purple_debug_error("QQ", "MSG to [%d], NOT me\n", im_header->receiver_uid);
- return;
- }
-
- /* check bytes */
- if (bytes >= data_len - 1) {
- purple_debug_warning("QQ", "Empty MSG\n");
- return;
- }
-
- switch (im_header->im_type) {
- case QQ_RECV_IM_NEWS:
- _qq_process_recv_news(data + bytes, data_len - bytes, gc);
- break;
- case QQ_RECV_IM_FROM_BUDDY_2006:
- case QQ_RECV_IM_FROM_UNKNOWN_2006:
- case QQ_RECV_IM_TO_UNKNOWN:
- case QQ_RECV_IM_TO_BUDDY:
- purple_debug_info("QQ", "MSG from buddy [%d]\n", im_header->sender_uid);
- _qq_process_recv_normal_im(data + bytes, data_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_info("QQ", "MSG from room [%d]\n", im_header->sender_uid);
- /* sender_uid is in fact id */
- qq_process_room_msg_normal(data + bytes, data_len - bytes, im_header->sender_uid, gc, im_header->im_type);
- break;
- case QQ_RECV_IM_ADD_TO_QUN:
- purple_debug_info("QQ", "Notice from [%d], Added\n", im_header->sender_uid);
- /* sender_uid is group id
- * we need this to create a dummy group and add to blist */
- qq_process_room_msg_been_added(data + bytes, data_len - bytes, im_header->sender_uid, gc);
- break;
- case QQ_RECV_IM_DEL_FROM_QUN:
- purple_debug_info("QQ", "Notice from room [%d], Removed\n", im_header->sender_uid);
- /* sender_uid is group id */
- qq_process_room_msg_been_removed(data + bytes, data_len - bytes, im_header->sender_uid, gc);
- break;
- case QQ_RECV_IM_APPLY_ADD_TO_QUN:
- purple_debug_info("QQ", "Notice from room [%d], Joined\n", im_header->sender_uid);
- /* sender_uid is group id */
- qq_process_room_msg_apply_join(data + bytes, data_len - bytes, im_header->sender_uid, gc);
- break;
- case QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN:
- purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
- im_header->sender_uid);
- /* sender_uid is group id */
- qq_process_room_msg_been_approved(data + bytes, data_len - bytes, im_header->sender_uid, gc);
- break;
- case QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN:
- purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
- im_header->sender_uid);
- /* sender_uid is group id */
- qq_process_room_msg_been_rejected(data + bytes, data_len - bytes, im_header->sender_uid, gc);
- break;
- case QQ_RECV_IM_SYS_NOTIFICATION:
- purple_debug_info("QQ", "Admin notice from [%d]\n", im_header->sender_uid);
- _qq_process_recv_sys_im(data + bytes, data_len - bytes, gc);
- break;
- default:
- purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%02x]\n",
- im_header->sender_uid, qq_get_recv_im_type_str(im_header->im_type),
- im_header->im_type);
- qq_show_packet("Unknown MSG type", data, data_len);
- }
-}
diff --git a/libpurple/protocols/qq/im.h b/libpurple/protocols/qq/im.h
index 4ada57fafd..c7d8f42b0a 100644
--- a/libpurple/protocols/qq/im.h
+++ b/libpurple/protocols/qq/im.h
@@ -39,30 +39,33 @@ enum {
};
enum {
- QQ_RECV_IM_TO_BUDDY = 0x0009,
- QQ_RECV_IM_TO_UNKNOWN = 0x000a,
- QQ_RECV_IM_NEWS = 0x0018,
- QQ_RECV_IM_UNKNOWN_QUN_IM = 0x0020,
- QQ_RECV_IM_ADD_TO_QUN = 0x0021,
- QQ_RECV_IM_DEL_FROM_QUN = 0x0022,
- QQ_RECV_IM_APPLY_ADD_TO_QUN = 0x0023,
- QQ_RECV_IM_APPROVE_APPLY_ADD_TO_QUN = 0x0024,
- QQ_RECV_IM_REJCT_APPLY_ADD_TO_QUN = 0x0025,
- QQ_RECV_IM_CREATE_QUN = 0x0026,
- QQ_RECV_IM_TEMP_QUN_IM = 0x002A,
- QQ_RECV_IM_QUN_IM = 0x002B,
- QQ_RECV_IM_SYS_NOTIFICATION = 0x0030,
- QQ_RECV_IM_FROM_BUDDY_2006 = 0x0084,
- QQ_RECV_IM_FROM_UNKNOWN_2006 = 0x0085,
+ QQ_MSG_TO_BUDDY = 0x0009,
+ QQ_MSG_TO_UNKNOWN = 0x000a,
+ QQ_MSG_NEWS = 0x0018,
+ QQ_MSG_UNKNOWN_QUN_IM = 0x0020,
+ QQ_MSG_ADD_TO_QUN = 0x0021,
+ QQ_MSG_DEL_FROM_QUN = 0x0022,
+ QQ_MSG_APPLY_ADD_TO_QUN = 0x0023,
+ QQ_MSG_APPROVE_APPLY_ADD_TO_QUN = 0x0024,
+ QQ_MSG_REJCT_APPLY_ADD_TO_QUN = 0x0025,
+ QQ_MSG_CREATE_QUN = 0x0026,
+ QQ_MSG_TEMP_QUN_IM = 0x002A,
+ QQ_MSG_QUN_IM = 0x002B,
+ QQ_MSG_SYS_30 = 0x0030,
+ QQ_MSG_SYS_4C = 0x004C,
+ QQ_MSG_EXTEND = 0x0084,
+ QQ_MSG_EXTEND_85 = 0x0085,
};
+void qq_got_attention(PurpleConnection *gc, const gchar *msg);
+
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 len);
+ const gchar *font_size,
+ const gchar *font_name,
+ gboolean is_bold, gboolean is_italic, gboolean is_underline, gint len);
-void qq_send_packet_im(PurpleConnection *gc, guint32 to_uid, gchar *msg, gint type);
-void qq_process_recv_im(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
-void qq_process_send_im_reply(guint8 *data, gint data_len, PurpleConnection *gc);
+void qq_request_send_im(PurpleConnection *gc, guint32 uid_to, gchar *msg, gint type);
+void qq_process_im(PurpleConnection *gc, guint8 *data, gint len);
+void qq_process_extend_im(PurpleConnection *gc, guint8 *data, gint len);
#endif
diff --git a/libpurple/protocols/qq/packet_parse.c b/libpurple/protocols/qq/packet_parse.c
index 7e71960aae..83097c2f2c 100644
--- a/libpurple/protocols/qq/packet_parse.c
+++ b/libpurple/protocols/qq/packet_parse.c
@@ -38,7 +38,7 @@
#define PARSER_DEBUG
#endif
-/* read one byte from buf,
+/* read one byte from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
gint qq_get8(guint8 *b, guint8 *buf)
{
@@ -53,7 +53,7 @@ gint qq_get8(guint8 *b, guint8 *buf)
}
-/* read two bytes as "guint16" from buf,
+/* read two bytes as "guint16" from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
gint qq_get16(guint16 *w, guint8 *buf)
{
@@ -67,7 +67,7 @@ gint qq_get16(guint16 *w, guint8 *buf)
return sizeof(w_dest);
}
-/* read four bytes as "guint32" from buf,
+/* read four bytes as "guint32" from buf,
* return the number of bytes read if succeeds, otherwise return -1 */
gint qq_get32(guint32 *dw, guint8 *buf)
{
@@ -87,7 +87,7 @@ gint qq_getIP(struct in_addr *ip, guint8 *buf)
return sizeof(struct in_addr);
}
-/* read datalen bytes from buf,
+/* 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)
{
@@ -151,7 +151,20 @@ gint qq_put16(guint8 *buf, guint16 w)
* return the number of bytes packed, otherwise return -1 */
gint qq_put32(guint8 *buf, guint32 dw)
{
- guint32 dw_porter;
+ guint32 dw_porter;
+ dw_porter = g_htonl(dw);
+#ifdef PARSER_DEBUG
+ purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf);
+ purple_debug_info("QQ", "[DBG][put32] dw 0x%08x, dw_porter 0x%08x\n", dw, dw_porter);
+#endif
+ memcpy(buf, &dw_porter, sizeof(dw_porter));
+ return sizeof(dw_porter);
+}
+
+gint qq_putime(guint8 *buf, time_t *t)
+{
+ guint32 dw, dw_porter;
+ memcpy(&dw, t, sizeof(dw));
dw_porter = g_htonl(dw);
#ifdef PARSER_DEBUG
purple_debug_info("QQ", "[DBG][put32] buf %p\n", (void *)buf);
@@ -171,7 +184,7 @@ gint qq_putIP(guint8* buf, struct in_addr *ip)
* return the number of bytes packed, otherwise return -1 */
gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen)
{
- memcpy(buf, data, datalen);
+ memcpy(buf, data, datalen);
#ifdef PARSER_DEBUG
purple_debug_info("QQ", "[DBG][putdata] buf %p\n", (void *)buf);
#endif
diff --git a/libpurple/protocols/qq/packet_parse.h b/libpurple/protocols/qq/packet_parse.h
index 6c0cf2b5f0..2cd10c6a3e 100644
--- a/libpurple/protocols/qq/packet_parse.h
+++ b/libpurple/protocols/qq/packet_parse.h
@@ -54,6 +54,7 @@ gint qq_put8(guint8 *buf, guint8 b);
gint qq_put16(guint8 *buf, guint16 w);
gint qq_put32(guint8 *buf, guint32 dw);
gint qq_putIP(guint8* buf, struct in_addr *ip);
+gint qq_putime(guint8 *buf, time_t *t);
gint qq_putdata(guint8 *buf, const guint8 *data, const int datalen);
/*
diff --git a/libpurple/protocols/qq/qq.c b/libpurple/protocols/qq/qq.c
index 58a02c8bbb..08d5fe350d 100644
--- a/libpurple/protocols/qq/qq.c
+++ b/libpurple/protocols/qq/qq.c
@@ -29,6 +29,7 @@
#include "notify.h"
#include "prefs.h"
#include "prpl.h"
+#include "privacy.h"
#include "request.h"
#include "roomlist.h"
#include "server.h"
@@ -39,12 +40,12 @@
#include "buddy_list.h"
#include "char_conv.h"
#include "group.h"
-#include "group_find.h"
#include "group_im.h"
#include "group_info.h"
#include "group_join.h"
#include "group_opt.h"
-#include "header_info.h"
+#include "group_internal.h"
+#include "qq_define.h"
#include "im.h"
#include "qq_process.h"
#include "qq_base.h"
@@ -56,7 +57,11 @@
#include "version.h"
#define OPENQ_AUTHOR "Puzzlebird"
-#define OPENQ_WEBSITE "http://openq.sourceforge.net"
+#define OPENQ_WEBSITE "http://openq.sourceforge.net"
+
+#ifndef OPENQ_VERSION
+#define OPENQ_VERSION DISPLAY_VERSION
+#endif
static GList *server_list_build(gchar select)
{
@@ -128,6 +133,7 @@ static void qq_login(PurpleAccount *account)
PurpleConnection *gc;
qq_data *qd;
PurplePresence *presence;
+ const gchar *version_str;
g_return_if_fail(account != NULL);
@@ -154,6 +160,19 @@ static void qq_login(PurpleAccount *account)
server_list_create(account);
purple_debug_info("QQ", "Server list has %d\n", g_list_length(qd->servers));
+ version_str = purple_account_get_string(account, "client_version", NULL);
+ qd->client_tag = QQ_CLIENT_0D55; /* set default as QQ2005 */
+ qd->client_version = 2005;
+ if (version_str != NULL && strlen(version_str) != 0) {
+ if (strcmp(version_str, "qq2007") == 0) {
+ qd->client_tag = QQ_CLIENT_111D;
+ qd->client_version = 2007;
+ } else if (strcmp(version_str, "qq2008") == 0) {
+ qd->client_tag = QQ_CLIENT_115B;
+ qd->client_version = 2008;
+ }
+ }
+
qd->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE);
qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE);
@@ -203,6 +222,13 @@ static void qq_close(PurpleConnection *gc)
}
qq_disconnect(gc);
+
+ if (qd->redirect) g_free(qd->redirect);
+ if (qd->ld.token) g_free(qd->ld.token);
+ if (qd->ld.token_ex) g_free(qd->ld.token_ex);
+ if (qd->captcha.token) g_free(qd->captcha.token);
+ if (qd->captcha.data) g_free(qd->captcha.data);
+
server_list_remove_all(qd);
g_free(qd);
@@ -210,25 +236,25 @@ static void qq_close(PurpleConnection *gc)
}
/* returns the icon name for a buddy or protocol */
-static const gchar *_qq_list_icon(PurpleAccount *a, PurpleBuddy *b)
+static const gchar *qq_list_icon(PurpleAccount *a, PurpleBuddy *b)
{
return "qq";
}
/* a short status text beside buddy icon*/
-static gchar *_qq_status_text(PurpleBuddy *b)
+static gchar *qq_status_text(PurpleBuddy *b)
{
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
GString *status;
- q_bud = (qq_buddy *) b->proto_data;
- if (q_bud == NULL)
+ bd = (qq_buddy_data *) b->proto_data;
+ if (bd == NULL)
return NULL;
status = g_string_new("");
- switch(q_bud->status) {
+ switch(bd->status) {
case QQ_BUDDY_OFFLINE:
g_string_append(status, _("Offline"));
break;
@@ -245,8 +271,11 @@ static gchar *_qq_status_text(PurpleBuddy *b)
case QQ_BUDDY_ONLINE_INVISIBLE:
g_string_append(status, _("Invisible"));
break;
+ case QQ_BUDDY_ONLINE_BUSY:
+ g_string_append(status, _("Busy"));
+ break;
default:
- g_string_printf(status, _("Unknown-%d"), q_bud->status);
+ g_string_printf(status, _("Unknown-%d"), bd->status);
}
return g_string_free(status, FALSE);
@@ -254,23 +283,23 @@ static gchar *_qq_status_text(PurpleBuddy *b)
/* a floating text when mouse is on the icon, show connection status here */
-static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
+static void qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
{
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
gchar *tmp;
GString *str;
g_return_if_fail(b != NULL);
- q_bud = (qq_buddy *) b->proto_data;
- if (q_bud == NULL)
+ bd = (qq_buddy_data *) b->proto_data;
+ if (bd == NULL)
return;
- /* if (PURPLE_BUDDY_IS_ONLINE(b) && q_bud != NULL) */
- if (q_bud->ip.s_addr != 0) {
+ /* if (PURPLE_BUDDY_IS_ONLINE(b) && bd != NULL) */
+ if (bd->ip.s_addr != 0) {
str = g_string_new(NULL);
- g_string_printf(str, "%s:%d", inet_ntoa(q_bud->ip), q_bud->port);
- if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
+ g_string_printf(str, "%s:%d", inet_ntoa(bd->ip), bd->port);
+ if (bd->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
g_string_append(str, " TCP");
} else {
g_string_append(str, " UDP");
@@ -278,11 +307,11 @@ static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gb
g_string_free(str, TRUE);
}
- tmp = g_strdup_printf("%d", q_bud->age);
+ tmp = g_strdup_printf("%d", bd->age);
purple_notify_user_info_add_pair(user_info, _("Age"), tmp);
g_free(tmp);
- switch (q_bud->gender) {
+ switch (bd->gender) {
case QQ_BUDDY_GENDER_GG:
purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male"));
break;
@@ -293,38 +322,38 @@ static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gb
purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown"));
break;
default:
- tmp = g_strdup_printf("Error (%d)", q_bud->gender);
+ tmp = g_strdup_printf("Error (%d)", bd->gender);
purple_notify_user_info_add_pair(user_info, _("Gender"), tmp);
g_free(tmp);
}
- if (q_bud->level) {
- tmp = g_strdup_printf("%d", q_bud->level);
+ if (bd->level) {
+ tmp = g_strdup_printf("%d", bd->level);
purple_notify_user_info_add_pair(user_info, _("Level"), tmp);
g_free(tmp);
}
str = g_string_new(NULL);
- if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) {
+ if (bd->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) {
g_string_append( str, _("Member") );
}
- if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_VIP) {
+ if (bd->comm_flag & QQ_COMM_FLAG_QQ_VIP) {
g_string_append( str, _(" VIP") );
}
- if (q_bud->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
+ if (bd->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
g_string_append( str, _(" TCP") );
}
- if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE) {
+ if (bd->comm_flag & QQ_COMM_FLAG_MOBILE) {
g_string_append( str, _(" FromMobile") );
}
- if (q_bud->comm_flag & QQ_COMM_FLAG_BIND_MOBILE) {
+ if (bd->comm_flag & QQ_COMM_FLAG_BIND_MOBILE) {
g_string_append( str, _(" BindMobile") );
}
- if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO) {
+ if (bd->comm_flag & QQ_COMM_FLAG_VIDEO) {
g_string_append( str, _(" Video") );
}
- if (q_bud->ext_flag & QQ_EXT_FLAG_ZONE) {
+ if (bd->ext_flag & QQ_EXT_FLAG_ZONE) {
g_string_append( str, _(" Zone") );
}
purple_notify_user_info_add_pair(user_info, _("Flag"), str->str);
@@ -333,40 +362,47 @@ static void _qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gb
#ifdef DEBUG
tmp = g_strdup_printf( "%s (%04X)",
- qq_get_ver_desc(q_bud->client_version),
- q_bud->client_version );
+ qq_get_ver_desc(bd->client_tag),
+ bd->client_tag );
purple_notify_user_info_add_pair(user_info, _("Ver"), tmp);
g_free(tmp);
tmp = g_strdup_printf( "Ext 0x%X, Comm 0x%X",
- q_bud->ext_flag, q_bud->comm_flag );
+ bd->ext_flag, bd->comm_flag );
purple_notify_user_info_add_pair(user_info, _("Flag"), tmp);
g_free(tmp);
#endif
}
/* we can show tiny icons on the four corners of buddy icon, */
-static const char *_qq_list_emblem(PurpleBuddy *b)
+static const char *qq_list_emblem(PurpleBuddy *b)
{
- /* each char** are refering to a filename in pixmaps/purple/status/default/ */
- qq_buddy *q_bud;
+ PurpleAccount *account;
+ PurpleConnection *gc;
+ qq_data *qd;
+ qq_buddy_data *buddy;
- if (!b || !(q_bud = b->proto_data)) {
+ if (!b || !(account = b->account) ||
+ !(gc = purple_account_get_connection(account)) || !(qd = gc->proto_data))
return NULL;
+
+ buddy = (qq_buddy_data *)b->proto_data;
+ if (!buddy) {
+ return "not-authorized";
}
- if (q_bud->comm_flag & QQ_COMM_FLAG_MOBILE)
+ if (buddy->comm_flag & QQ_COMM_FLAG_MOBILE)
return "mobile";
- if (q_bud->comm_flag & QQ_COMM_FLAG_VIDEO)
+ if (buddy->comm_flag & QQ_COMM_FLAG_VIDEO)
return "video";
- if (q_bud->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
+ if (buddy->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
return "qq_member";
return NULL;
}
/* QQ away status (used to initiate QQ away packet) */
-static GList *_qq_away_states(PurpleAccount *ga)
+static GList *qq_status_types(PurpleAccount *ga)
{
PurpleStatusType *status;
GList *types = NULL;
@@ -383,6 +419,10 @@ static GList *_qq_away_states(PurpleAccount *ga)
"invisible", _("Invisible"), FALSE, TRUE, FALSE);
types = g_list_append(types, status);
+ status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
+ "busy", _("Busy"), TRUE, TRUE, FALSE);
+ types = g_list_append(types, status);
+
status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
"offline", _("Offline"), FALSE, TRUE, FALSE);
types = g_list_append(types, status);
@@ -395,18 +435,72 @@ static GList *_qq_away_states(PurpleAccount *ga)
}
/* initiate QQ away with proper change_status packet */
-static void _qq_change_status(PurpleAccount *account, PurpleStatus *status)
+static void qq_change_status(PurpleAccount *account, PurpleStatus *status)
{
PurpleConnection *gc = purple_account_get_connection(account);
qq_request_change_status(gc, 0);
}
+static void qq_add_deny(PurpleConnection *gc, const char *who)
+{
+ qq_data *qd;
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+
+ qd = (qq_data *) gc->proto_data;
+ if (!qd->is_login)
+ return;
+
+ if (!who || who[0] == '\0')
+ return;
+
+ purple_debug_info("QQ", "Add deny for %s\n", who);
+}
+
+static void qq_rem_deny(PurpleConnection *gc, const char *who)
+{
+ qq_data *qd;
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+
+ qd = (qq_data *) gc->proto_data;
+ if (!qd->is_login)
+ return;
+
+ if (!who || who[0] == '\0')
+ return;
+
+ purple_debug_info("QQ", "Rem deny for %s\n", who);
+}
+
+static void qq_set_permit_deny(PurpleConnection *gc)
+{
+ PurpleAccount *account;
+ GSList *deny;
+
+ purple_debug_info("QQ", "Set permit deny\n");
+ account = purple_connection_get_account(gc);
+ switch (account->perm_deny)
+ {
+ case PURPLE_PRIVACY_ALLOW_ALL:
+ for (deny = account->deny; deny; deny = deny->next)
+ qq_rem_deny(gc, deny->data);
+ break;
+
+ case PURPLE_PRIVACY_ALLOW_BUDDYLIST:
+ case PURPLE_PRIVACY_ALLOW_USERS:
+ case PURPLE_PRIVACY_DENY_USERS:
+ case PURPLE_PRIVACY_DENY_ALL:
+ for (deny = account->deny; deny; deny = deny->next)
+ qq_add_deny(gc, deny->data);
+ break;
+ }
+}
+
/* IMPORTANT: PurpleConvImFlags -> PurpleMessageFlags */
/* send an instant msg to a buddy */
-static gint _qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
+static gint qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *message, PurpleMessageFlags flags)
{
- gint type, to_uid;
+ gint type, uid_to;
gchar *msg, *msg_with_qq_smiley;
qq_data *qd;
@@ -417,15 +511,15 @@ static gint _qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *mes
g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG);
type = (flags == PURPLE_MESSAGE_AUTO_RESP ? QQ_IM_AUTO_REPLY : QQ_IM_TEXT);
- to_uid = purple_name_to_uid(who);
+ uid_to = purple_name_to_uid(who);
/* if msg is to myself, bypass the network */
- if (to_uid == qd->uid) {
+ if (uid_to == qd->uid) {
serv_got_im(gc, who, message, flags, time(NULL));
} else {
msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT);
msg_with_qq_smiley = purple_smiley_to_qq(msg);
- qq_send_packet_im(gc, to_uid, msg_with_qq_smiley, type);
+ qq_request_send_im(gc, uid_to, msg_with_qq_smiley, type);
g_free(msg);
g_free(msg_with_qq_smiley);
}
@@ -434,21 +528,18 @@ static gint _qq_send_im(PurpleConnection *gc, const gchar *who, const gchar *mes
}
/* send a chat msg to a QQ Qun */
-static int _qq_chat_send(PurpleConnection *gc, int channel, const char *message, PurpleMessageFlags flags)
+static int qq_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
{
gchar *msg, *msg_with_qq_smiley;
- qq_group *group;
+ guint32 room_id = id;
g_return_val_if_fail(message != NULL, -1);
g_return_val_if_fail(strlen(message) <= QQ_MSG_IM_MAX, -E2BIG);
- group = qq_group_find_by_channel(gc, channel);
- g_return_val_if_fail(group != NULL, -1);
-
purple_debug_info("QQ_MESG", "Send qun mesg in utf8: %s\n", message);
msg = utf8_to_qq(message, QQ_CHARSET_DEFAULT);
msg_with_qq_smiley = purple_smiley_to_qq(msg);
- qq_send_packet_group_im(gc, group, msg_with_qq_smiley);
+ qq_request_room_send_im(gc, room_id, msg_with_qq_smiley);
g_free(msg);
g_free(msg_with_qq_smiley);
@@ -456,7 +547,7 @@ static int _qq_chat_send(PurpleConnection *gc, int channel, const char *message,
}
/* send packet to get who's detailed information */
-static void _qq_get_info(PurpleConnection *gc, const gchar *who)
+static void qq_show_buddy_info(PurpleConnection *gc, const gchar *who)
{
guint32 uid;
qq_data *qd;
@@ -470,78 +561,135 @@ static void _qq_get_info(PurpleConnection *gc, const gchar *who)
return;
}
- qq_request_get_level(gc, uid);
- qq_send_packet_get_info(gc, uid, TRUE);
+ if (qd->client_version >= 2007) {
+ qq_request_get_level_2007(gc, uid);
+ } else {
+ qq_request_get_level(gc, uid);
+ }
+ qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
}
-/* get my own information */
-static void _qq_menu_modify_my_info(PurplePluginAction *action)
+static void action_update_all_rooms(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
qq_data *qd;
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
qd = (qq_data *) gc->proto_data;
- qq_prepare_modify_info(gc);
+
+ if ( !qd->is_login ) {
+ return;
+ }
+
+ qq_update_all_rooms(gc, 0, 0);
}
-static void _qq_menu_change_password(PurplePluginAction *action)
+static void action_change_icon(PurplePluginAction *action)
{
- purple_notify_uri(NULL, "https://password.qq.com");
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ qq_data *qd;
+ gchar *icon_name;
+ gchar *icon_path;
+
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ qd = (qq_data *) gc->proto_data;
+
+ if ( !qd->is_login ) {
+ return;
+ }
+
+ icon_name = qq_get_icon_name(qd->my_icon);
+ icon_path = qq_get_icon_path(icon_name);
+ g_free(icon_name);
+
+ purple_debug_info("QQ", "Change prev icon %s to ...\n", icon_path);
+ purple_request_file(action, _("Select icon..."), icon_path,
+ FALSE,
+ G_CALLBACK(qq_change_icon_cb), NULL,
+ purple_connection_get_account(gc), NULL, NULL,
+ gc);
+ g_free(icon_path);
}
-/* remove a buddy from my list and remove myself from his list */
-/* TODO: re-enable this
-static void _qq_menu_block_buddy(PurpleBlistNode * node)
+static void action_modify_info_base(PurplePluginAction *action)
{
- guint32 uid;
- gc_and_uid *g;
- PurpleBuddy *buddy;
- PurpleConnection *gc;
- const gchar *who;
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ qq_data *qd;
- g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ qd = (qq_data *) gc->proto_data;
+ qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_BASE);
+}
- buddy = (PurpleBuddy *) node;
- gc = purple_account_get_connection(buddy->account);
- who = buddy->name;
- g_return_if_fail(who != NULL);
+static void action_modify_info_ext(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ qq_data *qd;
- uid = purple_name_to_uid(who);
- g_return_if_fail(uid > 0);
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ qd = (qq_data *) gc->proto_data;
+ qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_EXT);
+}
+
+static void action_modify_info_addr(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ qq_data *qd;
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = uid;
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ qd = (qq_data *) gc->proto_data;
+ qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_ADDR);
+}
+
+static void action_modify_info_contact(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ qq_data *qd;
- purple_request_action(gc, _("Block Buddy"),
- _("Are you sure you want to block this buddy?"), NULL,
- 1, g, 2,
- _("Cancel"),
- G_CALLBACK(qq_do_nothing_with_gc_and_uid),
- _("Block"), G_CALLBACK(qq_block_buddy_with_gc_and_uid));
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ qd = (qq_data *) gc->proto_data;
+ qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_CONTACT);
+}
+
+static void action_change_password(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ purple_notify_uri(NULL, "https://password.qq.com");
}
-*/
/* show a brief summary of what we get from login packet */
-static void _qq_menu_account_info(PurplePluginAction *action)
+static void action_show_account_info(PurplePluginAction *action)
{
PurpleConnection *gc = (PurpleConnection *) action->context;
qq_data *qd;
GString *info;
+ struct tm *tm_local;
+ int index;
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
qd = (qq_data *) gc->proto_data;
- info = g_string_new("<html><body>\n");
+ info = g_string_new("<html><body>");
- g_string_append_printf(info, _("<b>Current Online</b>: %d<br>\n"), qd->total_online);
- g_string_append_printf(info, _("<b>Last Refresh</b>: %s<br>\n"), ctime(&qd->last_get_online));
+ tm_local = localtime(&qd->login_time);
+ g_string_append_printf(info, _("<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"),
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ g_string_append_printf(info, _("<b>Total Online Buddies</b>: %d<br>\n"), qd->online_total);
+ tm_local = localtime(&qd->online_last_update);
+ g_string_append_printf(info, _("<b>Last Refresh</b>: %d-%d-%d, %d:%d:%d<br>\n"),
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
- g_string_append(info, "<hr>\n");
+ g_string_append(info, "<hr>");
g_string_append_printf(info, _("<b>Server</b>: %s<br>\n"), qd->curr_server);
+ g_string_append_printf(info, _("<b>Client Tag</b>: %s<br>\n"), qq_get_ver_desc(qd->client_tag));
g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
- g_string_append_printf(info, _("<b>My Internet Address</b>: %s<br>\n"), inet_ntoa(qd->my_ip));
+ g_string_append_printf(info, _("<b>My Internet IP</b>: %s:%d<br>\n"), inet_ntoa(qd->my_ip), qd->my_port);
- g_string_append(info, "<hr>\n");
+ g_string_append(info, "<hr>");
g_string_append(info, "<i>Network Status</i><br>\n");
g_string_append_printf(info, _("<b>Sent</b>: %lu<br>\n"), qd->net_stat.sent);
g_string_append_printf(info, _("<b>Resend</b>: %lu<br>\n"), qd->net_stat.resend);
@@ -549,12 +697,18 @@ static void _qq_menu_account_info(PurplePluginAction *action)
g_string_append_printf(info, _("<b>Received</b>: %lu<br>\n"), qd->net_stat.rcved);
g_string_append_printf(info, _("<b>Received Duplicate</b>: %lu<br>\n"), qd->net_stat.rcved_dup);
- g_string_append(info, "<hr>\n");
- g_string_append(info, "<i>Information below may not be accurate</i><br>\n");
+ g_string_append(info, "<hr>");
+ g_string_append(info, "<i>Last Login Information</i><br>\n");
- g_string_append_printf(info, _("<b>Login Time</b>: %s<br>\n"), ctime(&qd->login_time));
- g_string_append_printf(info, _("<b>Last Login IP</b>: %s<br>\n"), qd->last_login_ip);
- g_string_append_printf(info, _("<b>Last Login Time</b>: %s\n"), ctime(&qd->last_login_time));
+ for (index = 0; index < sizeof(qd->last_login_time) / sizeof(time_t); index++) {
+ tm_local = localtime(&qd->last_login_time[index]);
+ g_string_append_printf(info, _("<b>Time</b>: %d-%d-%d, %d:%d:%d<br>\n"),
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ }
+ if (qd->last_login_ip.s_addr != 0) {
+ g_string_append_printf(info, _("<b>IP</b>: %s<br>\n"), inet_ntoa(qd->last_login_ip));
+ }
g_string_append(info, "</body></html>");
@@ -563,6 +717,65 @@ static void _qq_menu_account_info(PurplePluginAction *action)
g_string_free(info, TRUE);
}
+static void action_about_openq(PurplePluginAction *action)
+{
+ PurpleConnection *gc = (PurpleConnection *) action->context;
+ qq_data *qd;
+ GString *info;
+ gchar *title;
+
+ g_return_if_fail(NULL != gc && NULL != gc->proto_data);
+ qd = (qq_data *) gc->proto_data;
+
+ info = g_string_new("<html><body>");
+ g_string_append(info, _("<p><b>Original Author</b>:<br>\n"));
+ g_string_append(info, "puzzlebird<br>\n");
+ g_string_append(info, "<br>\n");
+ g_string_append(info, _("<p><b>Code Contributors</b>:<br>\n"));
+ g_string_append(info, "gfhuang : patches for libpurple 2.0.0beta2, maintainer<br>\n");
+ g_string_append(info, "Yuan Qingyun : patches for libpurple 1.5.0, maintainer<br>\n");
+ g_string_append(info, "henryouly : file transfer, udp sock5 proxy and qq_show, maintainer<br>\n");
+ g_string_append(info, "hzhr : maintainer<br>\n");
+ g_string_append(info, "joymarquis : maintainer<br>\n");
+ g_string_append(info, "arfankai : fixed bugs in char_conv.c<br>\n");
+ g_string_append(info, "rakescar : provided filter for HTML tag<br>\n");
+ g_string_append(info, "yyw : improved performance on PPC linux<br>\n");
+ g_string_append(info, "lvxiang : provided ip to location original code<br>\n");
+ g_string_append(info, "markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007<br>\n");
+ g_string_append(info, "ccpaging : maintainer since 2007<br>\n");
+ g_string_append(info, "icesky : maintainer since 2007<br>\n");
+ g_string_append(info, "csyfek : faces, maintainer since 2007<br>\n");
+ g_string_append(info, "<br>\n");
+ g_string_append(info, _("<p><b>Lovely Patch Writers</b>:<br>\n"));
+ g_string_append(info, "gnap : message displaying, documentation<br>\n");
+ g_string_append(info, "manphiz : qun processing<br>\n");
+ g_string_append(info, "moo : qun processing<br>\n");
+ g_string_append(info, "Coly Li : qun processing<br>\n");
+ g_string_append(info, "Emil Alexiev : captcha verification on login based on LumaQQ for MAC (2007), login, add buddy, remove buddy, message exchange and logout<br>\n");
+ g_string_append(info, "<br>\n");
+ g_string_append(info, _("<p><b>Acknowledgement</b>:<br>\n"));
+ g_string_append(info, "Shufeng Tan : http://sf.net/projects/perl-oicq<br>\n");
+ g_string_append(info, "Jeff Ye : http://www.sinomac.com<br>\n");
+ g_string_append(info, "Hu Zheng : http://forlinux.yeah.net<br>\n");
+ g_string_append(info, "yunfan : http://www.myswear.net<br>\n");
+ g_string_append(info, "OpenQ Team : http://openq.linuxsir.org<br>\n");
+ g_string_append(info, "LumaQQ Team : http://lumaqq.linuxsir.org<br>\n");
+ g_string_append(info, "khc(at)pidgin.im<br>\n");
+ g_string_append(info, "qulogic(at)pidgin.im<br>\n");
+ g_string_append(info, "rlaager(at)pidgin.im<br>\n");
+ g_string_append(info, "OpenQ Google Group : http://groups.google.com/group/openq<br>\n");
+ g_string_append(info, "<br>\n");
+ g_string_append(info, _("<p><i>And, all the boys in the backroom...</i><br>\n"));
+ g_string_append(info, _("<i>Feel free to join us!</i> :)"));
+ g_string_append(info, "</body></html>");
+
+ title = g_strdup_printf(_("About OpenQ r%s"), OPENQ_VERSION);
+ purple_notify_formatted(gc, NULL, title, NULL, info->str, NULL, NULL);
+
+ g_free(title);
+ g_string_free(info, TRUE);
+}
+
/*
static void _qq_menu_search_or_add_permanent_group(PurplePluginAction *action)
{
@@ -578,35 +791,48 @@ static void _qq_menu_create_permanent_group(PurplePluginAction * action)
_("Input Qun name here"),
_("Only QQ members can create permanent Qun"),
"OpenQ", FALSE, FALSE, NULL,
- _("Create"), G_CALLBACK(qq_room_create_new), _("Cancel"), NULL, gc);
+ _("Create"), G_CALLBACK(qq_create_room), _("Cancel"), NULL, gc);
}
*/
-static void _qq_menu_unsubscribe_group(PurpleBlistNode * node)
+static void action_chat_quit(PurpleBlistNode * node)
{
PurpleChat *chat = (PurpleChat *)node;
PurpleConnection *gc = purple_account_get_connection(chat->account);
GHashTable *components = chat -> components;
+ gchar *num_str;
+ guint32 room_id;
g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
g_return_if_fail(components != NULL);
- qq_group_exit(gc, components);
+
+ num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
+ room_id = strtol(num_str, NULL, 10);
+ g_return_if_fail(room_id != 0);
+
+ qq_room_quit(gc, room_id);
}
-/*
-static void _qq_menu_manage_group(PurpleBlistNode * node)
+static void action_chat_get_info(PurpleBlistNode * node)
{
PurpleChat *chat = (PurpleChat *)node;
PurpleConnection *gc = purple_account_get_connection(chat->account);
GHashTable *components = chat -> components;
+ gchar *num_str;
+ guint32 room_id;
g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
g_return_if_fail(components != NULL);
- qq_group_manage_group(gc, components);
+
+ num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
+ room_id = strtol(num_str, NULL, 10);
+ g_return_if_fail(room_id != 0);
+
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
+ 0, QQ_ROOM_INFO_DISPLAY);
}
-*/
#if 0
/* TODO: re-enable this */
@@ -614,12 +840,12 @@ static void _qq_menu_send_file(PurpleBlistNode * node, gpointer ignored)
{
PurpleBuddy *buddy;
PurpleConnection *gc;
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
g_return_if_fail (PURPLE_BLIST_NODE_IS_BUDDY (node));
buddy = (PurpleBuddy *) node;
- q_bud = (qq_buddy *) buddy->proto_data;
-/* if (is_online (q_bud->status)) { */
+ bd = (qq_buddy_data *) buddy->proto_data;
+/* if (is_online (bd->status)) { */
gc = purple_account_get_connection (buddy->account);
g_return_if_fail (gc != NULL && gc->proto_data != NULL);
qq_send_file(gc, buddy->name, NULL);
@@ -628,21 +854,38 @@ static void _qq_menu_send_file(PurpleBlistNode * node, gpointer ignored)
#endif
/* protocol related menus */
-static GList *_qq_actions(PurplePlugin *plugin, gpointer context)
+static GList *qq_actions(PurplePlugin *plugin, gpointer context)
{
GList *m;
PurplePluginAction *act;
m = NULL;
- act = purple_plugin_action_new(_("Set My Information"), _qq_menu_modify_my_info);
+ act = purple_plugin_action_new(_("Change Icon"), action_change_icon);
+ m = g_list_append(m, act);
+
+ act = purple_plugin_action_new(_("Modify Information"), action_modify_info_base);
+ m = g_list_append(m, act);
+
+ act = purple_plugin_action_new(_("Modify Extend Information"), action_modify_info_ext);
+ m = g_list_append(m, act);
+
+ act = purple_plugin_action_new(_("Modify Address"), action_modify_info_addr);
+ m = g_list_append(m, act);
+
+ act = purple_plugin_action_new(_("Modify Contact"), action_modify_info_contact);
+ m = g_list_append(m, act);
+
+ act = purple_plugin_action_new(_("Change Password"), action_change_password);
m = g_list_append(m, act);
- act = purple_plugin_action_new(_("Change Password"), _qq_menu_change_password);
+ act = purple_plugin_action_new(_("Account Information"), action_show_account_info);
m = g_list_append(m, act);
- act = purple_plugin_action_new(_("Account Information"), _qq_menu_account_info);
+ act = purple_plugin_action_new(_("Update all QQ Quns"), action_update_all_rooms);
m = g_list_append(m, act);
+ act = purple_plugin_action_new(_("About OpenQ"), action_about_openq);
+ m = g_list_append(m, act);
/*
act = purple_plugin_action_new(_("Qun: Search a permanent Qun"), _qq_menu_search_or_add_permanent_group);
m = g_list_append(m, act);
@@ -654,63 +897,131 @@ static GList *_qq_actions(PurplePlugin *plugin, gpointer context)
return m;
}
+static void qq_add_buddy_from_menu_cb(PurpleBlistNode *node, gpointer data)
+{
+ PurpleBuddy *buddy;
+ PurpleConnection *gc;
+
+ g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
+
+ buddy = (PurpleBuddy *) node;
+ gc = purple_account_get_connection(buddy->account);
+
+ qq_add_buddy(gc, buddy, NULL);
+}
+
+static GList *qq_buddy_menu(PurpleBuddy *buddy)
+{
+ GList *m = NULL;
+ PurpleMenuAction *act;
+ /*
+ PurpleConnection *gc = purple_account_get_connection(buddy->account);
+ qq_data *qd = gc->proto_data;
+ */
+ qq_buddy_data *bd = (qq_buddy_data *)buddy->proto_data;
+
+ if (bd == NULL) {
+ act = purple_menu_action_new(_("Add Buddy"),
+ PURPLE_CALLBACK(qq_add_buddy_from_menu_cb),
+ NULL, NULL);
+ m = g_list_append(m, act);
+
+ return m;
+
+ }
+
+/* TODO : not working, temp commented out by gfhuang */
+#if 0
+ if (bd && is_online(bd->status)) {
+ act = purple_menu_action_new(_("Send File"), PURPLE_CALLBACK(_qq_menu_send_file), NULL, NULL); /* add NULL by gfhuang */
+ m = g_list_append(m, act);
+ }
+#endif
+ return m;
+}
+
/* chat-related (QQ Qun) menu shown up with right-click */
-static GList *_qq_chat_menu(PurpleBlistNode *node)
+static GList *qq_chat_menu(PurpleBlistNode *node)
{
GList *m;
PurpleMenuAction *act;
m = NULL;
- act = purple_menu_action_new(_("Leave the QQ Qun"), PURPLE_CALLBACK(_qq_menu_unsubscribe_group), NULL, NULL);
+ act = purple_menu_action_new(_("Get Info"), PURPLE_CALLBACK(action_chat_get_info), NULL, NULL);
m = g_list_append(m, act);
- /* TODO: enable this
- act = purple_menu_action_new(_("Show Details"), PURPLE_CALLBACK(_qq_menu_manage_group), NULL, NULL);
+ act = purple_menu_action_new(_("Quit Qun"), PURPLE_CALLBACK(action_chat_quit), NULL, NULL);
m = g_list_append(m, act);
- */
-
return m;
}
/* buddy-related menu shown up with right-click */
-static GList *_qq_buddy_menu(PurpleBlistNode * node)
+static GList *qq_blist_node_menu(PurpleBlistNode * node)
{
- GList *m;
-
if(PURPLE_BLIST_NODE_IS_CHAT(node))
- return _qq_chat_menu(node);
+ return qq_chat_menu(node);
- m = NULL;
+ if(PURPLE_BLIST_NODE_IS_BUDDY(node))
+ return qq_buddy_menu((PurpleBuddy *) node);
-/* TODO : not working, temp commented out by gfhuang */
-#if 0
+ return NULL;
+}
- act = purple_menu_action_new(_("Block this buddy"), PURPLE_CALLBACK(_qq_menu_block_buddy), NULL, NULL); /* add NULL by gfhuang */
- m = g_list_append(m, act);
-/* if (q_bud && is_online(q_bud->status)) { */
- act = purple_menu_action_new(_("Send File"), PURPLE_CALLBACK(_qq_menu_send_file), NULL, NULL); /* add NULL by gfhuang */
- m = g_list_append(m, act);
-/* } */
-#endif
+/* convert name displayed in a chat channel to original QQ UID */
+static gchar *chat_name_to_purple_name(const gchar *const name)
+{
+ const char *start;
+ const char *end;
+ gchar *ret;
- return m;
+ g_return_val_if_fail(name != NULL, NULL);
+
+ /* Sample: (1234567)*/
+ start = strchr(name, '(');
+ g_return_val_if_fail(start != NULL, NULL);
+ end = strchr(start, ')');
+ g_return_val_if_fail(end != NULL && (end - start) > 1, NULL);
+
+ ret = g_strndup(start + 1, end - start - 1);
+
+ return ret;
}
-/* convert chat nickname to qq-uid to get this buddy info */
+/* convert chat nickname to uid to get this buddy info */
/* who is the nickname of buddy in QQ chat-room (Qun) */
-static void _qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who)
+static void qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who)
{
- gchar *purple_name;
+ qq_data *qd;
+ gchar *uid_str;
+ guint32 uid;
+
+ purple_debug_info("QQ", "Get chat buddy info of %s\n", who);
g_return_if_fail(who != NULL);
- purple_name = chat_name_to_purple_name(who);
- if (purple_name != NULL)
- _qq_get_info(gc, purple_name);
+ uid_str = chat_name_to_purple_name(who);
+ if (uid_str == NULL) {
+ return;
+ }
+
+ qd = gc->proto_data;
+ uid = purple_name_to_uid(uid_str);
+ g_free(uid_str);
+
+ if (uid <= 0) {
+ purple_debug_error("QQ", "Not valid chat name: %s\n", who);
+ purple_notify_error(gc, NULL, _("Invalid name"), NULL);
+ return;
+ }
+
+ if (qd->client_version < 2007) {
+ qq_request_get_level(gc, uid);
+ }
+ qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
}
-/* convert chat nickname to qq-uid to invite individual IM to buddy */
+/* convert chat nickname to uid to invite individual IM to buddy */
/* who is the nickname of buddy in QQ chat-room (Qun) */
-static gchar *_qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, const gchar *who)
+static gchar *qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, const gchar *who)
{
g_return_val_if_fail(who != NULL, NULL);
return chat_name_to_purple_name(who);
@@ -722,21 +1033,21 @@ static PurplePluginProtocolInfo prpl_info =
NULL, /* user_splits */
NULL, /* protocol_options */
{"png", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
- _qq_list_icon, /* list_icon */
- _qq_list_emblem, /* list_emblems */
- _qq_status_text, /* status_text */
- _qq_tooltip_text, /* tooltip_text */
- _qq_away_states, /* away_states */
- _qq_buddy_menu, /* blist_node_menu */
+ qq_list_icon, /* list_icon */
+ qq_list_emblem, /* list_emblems */
+ qq_status_text, /* status_text */
+ qq_tooltip_text, /* tooltip_text */
+ qq_status_types, /* away_states */
+ qq_blist_node_menu, /* blist_node_menu */
qq_chat_info, /* chat_info */
qq_chat_info_defaults, /* chat_info_defaults */
qq_login, /* open */
qq_close, /* close */
- _qq_send_im, /* send_im */
+ qq_send_im, /* send_im */
NULL, /* set_info */
NULL, /* send_typing */
- _qq_get_info, /* get_info */
- _qq_change_status, /* change status */
+ qq_show_buddy_info, /* get_info */
+ qq_change_status, /* change status */
NULL, /* set_idle */
NULL, /* change_passwd */
qq_add_buddy, /* add_buddy */
@@ -744,30 +1055,30 @@ static PurplePluginProtocolInfo prpl_info =
qq_remove_buddy, /* remove_buddy */
NULL, /* remove_buddies */
NULL, /* add_permit */
- NULL, /* add_deny */
+ qq_add_deny, /* add_deny */
NULL, /* rem_permit */
NULL, /* rem_deny */
- NULL, /* set_permit_deny */
+ qq_set_permit_deny, /* set_permit_deny */
qq_group_join, /* join_chat */
NULL, /* reject chat invite */
NULL, /* get_chat_name */
NULL, /* chat_invite */
NULL, /* chat_leave */
NULL, /* chat_whisper */
- _qq_chat_send, /* chat_send */
+ qq_chat_send, /* chat_send */
NULL, /* keepalive */
NULL, /* register_user */
- _qq_get_chat_buddy_info, /* get_cb_info */
+ qq_get_chat_buddy_info, /* get_cb_info */
NULL, /* get_cb_away */
NULL, /* alias_buddy */
- NULL, /* group_buddy */
+ NULL, /* change buddy's group */
NULL, /* rename_group */
NULL, /* buddy_free */
NULL, /* convo_closed */
NULL, /* normalize */
- qq_set_my_buddy_icon, /* set_buddy_icon */
+ qq_set_custom_icon,
NULL, /* remove_group */
- _qq_get_chat_buddy_real_name, /* get_cb_real_name */
+ qq_get_chat_buddy_real_name, /* get_cb_real_name */
NULL, /* set_chat_topic */
NULL, /* find_blist_chat */
qq_roomlist_get_list, /* roomlist_get_list */
@@ -815,7 +1126,7 @@ static PurplePluginInfo info = {
NULL, /**< ui_info */
&prpl_info, /**< extra_info */
NULL, /**< prefs_info */
- _qq_actions,
+ qq_actions,
/* padding */
NULL,
@@ -829,34 +1140,59 @@ static void init_plugin(PurplePlugin *plugin)
{
PurpleAccountOption *option;
PurpleKeyValuePair *kvp;
- GList *list = NULL;
- GList *kvlist = NULL;
- GList *entry;
+ GList *server_list = NULL;
+ GList *server_kv_list = NULL;
+ GList *it;
+//#ifdef DEBUG
+ GList *version_kv_list = NULL;
+//#endif
- list = server_list_build('A');
+ server_list = server_list_build('A');
- purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", list);
- list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist");
+ purple_prefs_add_string_list("/plugins/prpl/qq/serverlist", server_list);
+ server_list = purple_prefs_get_string_list("/plugins/prpl/qq/serverlist");
- kvlist = NULL;
+ server_kv_list = NULL;
kvp = g_new0(PurpleKeyValuePair, 1);
kvp->key = g_strdup(_("Auto"));
kvp->value = g_strdup("auto");
- kvlist = g_list_append(kvlist, kvp);
+ server_kv_list = g_list_append(server_kv_list, kvp);
- entry = list;
- while(entry) {
- if (entry->data != NULL && strlen(entry->data) > 0) {
+ it = server_list;
+ while(it) {
+ if (it->data != NULL && strlen(it->data) > 0) {
kvp = g_new0(PurpleKeyValuePair, 1);
- kvp->key = g_strdup(entry->data);
- kvp->value = g_strdup(entry->data);
- kvlist = g_list_append(kvlist, kvp);
+ kvp->key = g_strdup(it->data);
+ kvp->value = g_strdup(it->data);
+ server_kv_list = g_list_append(server_kv_list, kvp);
}
- entry = entry->next;
+ it = it->next;
}
- option = purple_account_option_list_new(_("Server"), "server", kvlist);
+ g_list_free(server_list);
+
+ option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list);
+ prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+//#ifdef DEBUG
+ kvp = g_new0(PurpleKeyValuePair, 1);
+ kvp->key = g_strdup(_("QQ2005"));
+ kvp->value = g_strdup("qq2005");
+ version_kv_list = g_list_append(version_kv_list, kvp);
+
+ kvp = g_new0(PurpleKeyValuePair, 1);
+ kvp->key = g_strdup(_("QQ2007"));
+ kvp->value = g_strdup("qq2007");
+ version_kv_list = g_list_append(version_kv_list, kvp);
+
+ kvp = g_new0(PurpleKeyValuePair, 1);
+ kvp->key = g_strdup(_("QQ2008"));
+ kvp->value = g_strdup("qq2008");
+ version_kv_list = g_list_append(version_kv_list, kvp);
+
+ option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+//#endif
option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
@@ -876,9 +1212,10 @@ static void init_plugin(PurplePlugin *plugin)
purple_prefs_add_none("/plugins/prpl/qq");
purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
- purple_prefs_add_bool("/plugins/prpl/qq/show_room_when_newin", TRUE);
+ purple_prefs_add_bool("/plugins/prpl/qq/auto_popup_conversation", FALSE);
+ purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE);
purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3);
- purple_prefs_add_int("/plugins/prpl/qq/resend_times", 4);
+ purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10);
}
PURPLE_INIT_PLUGIN(qq, init_plugin, info);
diff --git a/libpurple/protocols/qq/qq.h b/libpurple/protocols/qq/qq.h
index 503dcc3ce6..79e7cdb8b0 100644
--- a/libpurple/protocols/qq/qq.h
+++ b/libpurple/protocols/qq/qq.h
@@ -37,9 +37,34 @@
#define QQ_KEY_LENGTH 16
typedef struct _qq_data qq_data;
-typedef struct _qq_buddy qq_buddy;
+typedef struct _qq_buddy_data qq_buddy_data;
typedef struct _qq_interval qq_interval;
typedef struct _qq_net_stat qq_net_stat;
+typedef struct _qq_login_data qq_login_data;
+typedef struct _qq_captcha_data qq_captcha_data;
+
+struct _qq_captcha_data {
+ guint8 *token;
+ guint16 token_len;
+ guint8 next_index;
+ guint8 *data;
+ guint16 data_len;
+};
+
+struct _qq_login_data {
+ guint8 random_key[QQ_KEY_LENGTH]; /* first encrypt key generated by client */
+ guint8 *token; /* get from server */
+ guint8 token_len;
+ guint8 *token_ex; /* get from server */
+ guint16 token_ex_len;
+
+ guint8 pwd_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */
+ guint8 pwd_twice_md5[QQ_KEY_LENGTH];
+
+ guint8 *login_token;
+ guint16 login_token_len;
+ guint8 login_key[QQ_KEY_LENGTH];
+};
struct _qq_interval {
gint resend;
@@ -55,7 +80,7 @@ struct _qq_net_stat {
glong rcved_dup;
};
-struct _qq_buddy {
+struct _qq_buddy_data {
guint32 uid;
guint16 face; /* index: 0 - 299 */
guint8 age;
@@ -66,7 +91,7 @@ struct _qq_buddy {
guint8 status;
guint8 ext_flag;
guint8 comm_flag; /* details in qq_buddy_list.c */
- guint16 client_version;
+ guint16 client_tag;
guint8 onlineTime;
guint16 level;
guint16 timeRemainder;
@@ -105,8 +130,14 @@ struct _qq_data {
GList *servers;
gchar *curr_server; /* point to servers->data, do not free*/
+ guint16 client_tag;
+ gint client_version;
+
struct in_addr redirect_ip;
guint16 redirect_port;
+ guint8 *redirect;
+ guint8 redirect_len;
+
guint check_watcher;
guint connect_watcher;
gint connect_retry;
@@ -119,46 +150,35 @@ struct _qq_data {
GList *transactions; /* check ack packet and resend */
guint32 uid; /* QQ number */
- guint8 *token; /* get from server*/
- int token_len;
- guint8 inikey[QQ_KEY_LENGTH]; /* initial key to encrypt login packet */
- guint8 password_twice_md5[QQ_KEY_LENGTH]; /* password in md5 (or md5' md5) */
+
+ qq_login_data ld;
+ qq_captcha_data captcha;
+
guint8 session_key[QQ_KEY_LENGTH]; /* later use this as key in this session */
guint8 session_md5[QQ_KEY_LENGTH]; /* concatenate my uid with session_key and md5 it */
guint16 send_seq; /* send sequence number */
guint8 login_mode; /* online of invisible */
- gboolean is_login; /* used by qq-add_buddy */
+ gboolean is_login; /* used by qq_add_buddy */
PurpleXfer *xfer; /* file transfer handler */
/* get from login reply packet */
+ struct in_addr my_local_ip; /* my local ip address detected by server */
+ guint16 my_local_port; /* my lcoal port detected by server */
time_t login_time;
- time_t last_login_time;
- gchar *last_login_ip;
+ time_t last_login_time[3];
+ struct in_addr last_login_ip;
/* get from keep_alive packet */
struct in_addr my_ip; /* my ip address detected by server */
guint16 my_port; /* my port detected by server */
guint16 my_icon; /* my icon index */
- guint16 my_level; /* my level */
- guint32 total_online; /* the number of online QQ users */
- time_t last_get_online; /* last time send get_friends_online packet */
+ guint32 online_total; /* the number of online QQ users */
+ time_t online_last_update; /* last time send get_friends_online packet */
PurpleRoomlist *roomlist;
- gint channel; /* the id for opened chat conversation */
GList *groups;
- GSList *joining_groups;
- GSList *adding_groups_from_server; /* internal ids of groups the server wants in my blist */
- GList *buddies;
- GList *contact_info_window;
- GList *group_info_window;
- GList *info_query;
- GList *add_buddy_request;
-
- /* TODO pass qq_send_packet_get_info() a callback and use signals to get rid of these */
- gboolean modifying_info;
- gboolean modifying_face;
gboolean is_show_notice;
gboolean is_show_news;
diff --git a/libpurple/protocols/qq/qq_base.c b/libpurple/protocols/qq/qq_base.c
index 8517fa9200..10e73c9f85 100644
--- a/libpurple/protocols/qq/qq_base.c
+++ b/libpurple/protocols/qq/qq_base.c
@@ -26,114 +26,21 @@
#include "internal.h"
#include "server.h"
#include "cipher.h"
+#include "request.h"
#include "buddy_info.h"
#include "buddy_list.h"
#include "char_conv.h"
#include "qq_crypt.h"
#include "group.h"
-#include "header_info.h"
+#include "qq_define.h"
+#include "qq_network.h"
#include "qq_base.h"
#include "packet_parse.h"
#include "qq.h"
#include "qq_network.h"
#include "utils.h"
-#define QQ_LOGIN_DATA_LENGTH 416
-#define QQ_LOGIN_REPLY_OK_PACKET_LEN 139
-#define QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN 11
-
-/* for QQ 2003iii 0117, fixed value */
-/* static const guint8 login_23_51[29] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
- 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
- 0x95, 0x67, 0xda, 0x2c, 0x01
-}; */
-
-/* for QQ 2003iii 0304, fixed value */
-/*
-static const guint8 login_23_51[29] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
- 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
- 0x40, 0xb8, 0xac, 0x32, 0x01
-};
-*/
-
-/* for QQ 2005? copy from lumaqq */
-/* FIXME: change to guint8 */
-static const guint8 login_23_51[29] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
- 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
- 0xc3, 0xfa, 0x33, 0xa4, 0x01
-};
-
-static const guint8 login_53_68[16] = {
- 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
- 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
-};
-
-static const guint8 login_100_bytes[100] = {
- 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-
-
-/* fixed value, not affected by version, or mac address */
-/*
-static const guint8 login_53_68[16] = {
- 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
- 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
-};
-*/
-
-
-typedef struct _qq_login_reply_ok qq_login_reply_ok_packet;
-typedef struct _qq_login_reply_redirect qq_login_reply_redirect_packet;
-
-struct _qq_login_reply_ok {
- guint8 result;
- guint8 session_key[QQ_KEY_LENGTH];
- guint32 uid;
- struct in_addr client_ip; /* those detected by server */
- guint16 client_port;
- struct in_addr server_ip;
- guint16 server_port;
- time_t login_time;
- guint8 unknown1[26];
- struct in_addr unknown_server1_ip;
- guint16 unknown_server1_port;
- struct in_addr unknown_server2_ip;
- guint16 unknown_server2_port;
- guint16 unknown2; /* 0x0001 */
- guint16 unknown3; /* 0x0000 */
- guint8 unknown4[32];
- guint8 unknown5[12];
- struct in_addr last_client_ip;
- time_t last_login_time;
- guint8 unknown6[8];
-};
-
-struct _qq_login_reply_redirect {
- guint8 result;
- guint32 uid;
- struct in_addr new_server_ip;
- guint16 new_server_port;
-};
-
/* generate a md5 key using uid and session_key */
static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
{
@@ -149,72 +56,90 @@ static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_ke
/* process login reply which says OK */
static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
{
- gint bytes;
qq_data *qd;
- qq_login_reply_ok_packet lrop;
-
- qd = (qq_data *) gc->proto_data;
- /* FIXME, check QQ_LOGIN_REPLY_OK_PACKET_LEN here */
- bytes = 0;
+ gint bytes;
- /* 000-000: reply code */
- bytes += qq_get8(&lrop.result, data + bytes);
- /* 001-016: session key */
- bytes += qq_getdata(lrop.session_key, sizeof(lrop.session_key), data + bytes);
- purple_debug_info("QQ", "Got session_key\n");
- /* 017-020: login uid */
- bytes += qq_get32(&lrop.uid, data + bytes);
- /* 021-024: server detected user public IP */
- bytes += qq_getIP(&lrop.client_ip, data + bytes);
- /* 025-026: server detected user port */
- bytes += qq_get16(&lrop.client_port, data + bytes);
- /* 027-030: server detected itself ip 127.0.0.1 ? */
- bytes += qq_getIP(&lrop.server_ip, data + bytes);
- /* 031-032: server listening port */
- bytes += qq_get16(&lrop.server_port, data + bytes);
- /* 033-036: login time for current session */
- bytes += qq_getime(&lrop.login_time, data + bytes);
- /* 037-062: 26 bytes, unknown */
- bytes += qq_getdata((guint8 *) &lrop.unknown1, 26, data + bytes);
- /* 063-066: unknown server1 ip address */
- bytes += qq_getIP(&lrop.unknown_server1_ip, data + bytes);
- /* 067-068: unknown server1 port */
- bytes += qq_get16(&lrop.unknown_server1_port, data + bytes);
- /* 069-072: unknown server2 ip address */
- bytes += qq_getIP(&lrop.unknown_server2_ip, data + bytes);
- /* 073-074: unknown server2 port */
- bytes += qq_get16(&lrop.unknown_server2_port, data + bytes);
- /* 075-076: 2 bytes unknown */
- bytes += qq_get16(&lrop.unknown2, data + bytes);
- /* 077-078: 2 bytes unknown */
- bytes += qq_get16(&lrop.unknown3, data + bytes);
- /* 079-110: 32 bytes unknown */
- bytes += qq_getdata((guint8 *) &lrop.unknown4, 32, data + bytes);
- /* 111-122: 12 bytes unknown */
- bytes += qq_getdata((guint8 *) &lrop.unknown5, 12, data + bytes);
- /* 123-126: login IP of last session */
- bytes += qq_getIP(&lrop.last_client_ip, data + bytes);
- /* 127-130: login time of last session */
- bytes += qq_getime(&lrop.last_login_time, data + bytes);
- /* 131-138: 8 bytes unknown */
- bytes += qq_getdata((guint8 *) &lrop.unknown6, 8, data + bytes);
-
- if (bytes != QQ_LOGIN_REPLY_OK_PACKET_LEN) { /* fail parsing login info */
- purple_debug_warning("QQ",
- "Fail parsing login info, expect %d bytes, read %d bytes\n",
- QQ_LOGIN_REPLY_OK_PACKET_LEN, bytes);
- } /* but we still go on as login OK */
-
- memcpy(qd->session_key, lrop.session_key, sizeof(qd->session_key));
- get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+ guint8 ret;
+ guint32 uid;
+ struct in_addr ip;
+ guint16 port;
+ struct tm *tm_local;
- qd->my_ip.s_addr = lrop.client_ip.s_addr;
+ qd = (qq_data *) gc->proto_data;
+ /* qq_show_packet("Login reply", data, len); */
- qd->my_port = lrop.client_port;
- qd->login_time = lrop.login_time;
- qd->last_login_time = lrop.last_login_time;
- qd->last_login_ip = g_strdup( inet_ntoa(lrop.last_client_ip) );
+ if (len < 139) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+ _("Can not decrypt get server reply"));
+ return QQ_LOGIN_REPLY_ERR;
+ }
+ bytes = 0;
+ bytes += qq_get8(&ret, data + bytes);
+ bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
+ get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+ purple_debug_info("QQ", "Got session_key\n");
+ bytes += qq_get32(&uid, data + bytes);
+ if (uid != qd->uid) {
+ purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+ }
+ bytes += qq_getIP(&qd->my_ip, data + bytes);
+ bytes += qq_get16(&qd->my_port, data + bytes);
+ purple_debug_info("QQ", "Internet IP: %s, %d\n", inet_ntoa(qd->my_ip), qd->my_port);
+
+ bytes += qq_getIP(&qd->my_local_ip, data + bytes);
+ bytes += qq_get16(&qd->my_local_port, data + bytes);
+ purple_debug_info("QQ", "Local IP: %s, %d\n", inet_ntoa(qd->my_local_ip), qd->my_local_port);
+
+ bytes += qq_getime(&qd->login_time, data + bytes);
+ tm_local = localtime(&qd->login_time);
+ purple_debug_info("QQ", "Login time: %d-%d-%d, %d:%d:%d\n",
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ /* skip unknown 2 bytes, 0x(03 0a) */
+ bytes += 2;
+ /* skip unknown 24 bytes, maybe token to access Qun shared files */
+ bytes += 24;
+ /* unknow ip and port */
+ bytes += qq_getIP(&ip, data + bytes);
+ bytes += qq_get16(&port, data + bytes);
+ purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port);
+ /* unknow ip and port */
+ bytes += qq_getIP(&ip, data + bytes);
+ bytes += qq_get16(&port, data + bytes);
+ purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port);
+ /* unknown 4 bytes, 0x(00 81 00 00)*/
+ bytes += 4;
+ /* skip unknown 32 bytes, maybe key to access QQ Home */
+ bytes += 32;
+ /* skip unknown 16 bytes, 0x(00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00) */
+ bytes += 16;
+ /* time */
+ bytes += qq_getime(&qd->last_login_time[0], data + bytes);
+ tm_local = localtime(&qd->last_login_time[0]);
+ purple_debug_info("QQ", "Last login time: %d-%d-%d, %d:%d:%d\n",
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ /* unknow time */
+ g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 1, QQ_LOGIN_REPLY_OK);
+ bytes += qq_getime(&qd->last_login_time[1], data + bytes);
+ tm_local = localtime(&qd->last_login_time[1]);
+ purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n",
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+
+ g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 2, QQ_LOGIN_REPLY_OK);
+ bytes += qq_getime(&qd->last_login_time[2], data + bytes);
+ tm_local = localtime(&qd->last_login_time[2]);
+ purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n",
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ /* unknow 9 bytes, 0x(00 0a 00 0a 01 00 00 0e 10) */
+
+ if (len > 139) {
+ purple_debug_warning("QQ", "Login reply more than expected %d bytes, read %d bytes\n", 139, bytes);
+ }
return QQ_LOGIN_REPLY_OK;
}
@@ -223,35 +148,45 @@ static gint8 process_login_redirect(PurpleConnection *gc, guint8 *data, gint len
{
qq_data *qd;
gint bytes;
- qq_login_reply_redirect_packet lrrp;
+ struct {
+ guint8 result;
+ guint32 uid;
+ struct in_addr new_server_ip;
+ guint16 new_server_port;
+ } packet;
+
+
+ if (len < 11) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+ _("Can not decrypt get server reply"));
+ return QQ_LOGIN_REPLY_ERR;
+ }
qd = (qq_data *) gc->proto_data;
bytes = 0;
/* 000-000: reply code */
- bytes += qq_get8(&lrrp.result, data + bytes);
+ bytes += qq_get8(&packet.result, data + bytes);
/* 001-004: login uid */
- bytes += qq_get32(&lrrp.uid, data + bytes);
+ bytes += qq_get32(&packet.uid, data + bytes);
/* 005-008: redirected new server IP */
- bytes += qq_getIP(&lrrp.new_server_ip, data + bytes);
+ bytes += qq_getIP(&packet.new_server_ip, data + bytes);
/* 009-010: redirected new server port */
- bytes += qq_get16(&lrrp.new_server_port, data + bytes);
+ bytes += qq_get16(&packet.new_server_port, data + bytes);
- if (bytes != QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN) {
- purple_debug_error("QQ",
- "Fail parsing login redirect packet, expect %d bytes, read %d bytes\n",
- QQ_LOGIN_REPLY_REDIRECT_PACKET_LEN, bytes);
- return QQ_LOGIN_REPLY_ERR_MISC;
+ if (len > 11) {
+ purple_debug_error("QQ", "Login redirect more than expected %d bytes, read %d bytes\n", 11, bytes);
}
/* redirect to new server, do not disconnect or connect here
* those connect should be called at packet_process */
- qd->redirect_ip.s_addr = lrrp.new_server_ip.s_addr;
- qd->redirect_port = lrrp.new_server_port;
+ qd->redirect_ip.s_addr = packet.new_server_ip.s_addr;
+ qd->redirect_port = packet.new_server_port;
return QQ_LOGIN_REPLY_REDIRECT;
}
/* request before login */
-void qq_send_packet_token(PurpleConnection *gc)
+void qq_request_token(PurpleConnection *gc)
{
qq_data *qd;
guint8 buf[16] = {0};
@@ -267,38 +202,59 @@ void qq_send_packet_token(PurpleConnection *gc)
}
/* send login packet to QQ server */
-void qq_send_packet_login(PurpleConnection *gc)
+void qq_request_login(PurpleConnection *gc)
{
qq_data *qd;
guint8 *buf, *raw_data;
gint bytes;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
+ /* for QQ 2005? copy from lumaqq */
+ static const guint8 login_23_51[29] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
+ 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
+ 0xc3, 0xfa, 0x33, 0xa4, 0x01
+ };
+
+ static const guint8 login_53_68[16] = {
+ 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
+ 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
+ };
+
+ static const guint8 login_100_bytes[100] = {
+ 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+
g_return_if_fail(gc != NULL && gc->proto_data != NULL);
qd = (qq_data *) gc->proto_data;
- g_return_if_fail(qd->token != NULL && qd->token_len > 0);
+ g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
-#ifdef DEBUG
- memset(qd->inikey, 0x01, sizeof(qd->inikey));
-#else
- for (bytes = 0; bytes < sizeof(qd->inikey); bytes++) {
- qd->inikey[bytes] = (guint8) (rand() & 0xff);
- }
-#endif
-
- raw_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH);
- memset(raw_data, 0, QQ_LOGIN_DATA_LENGTH);
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
- encrypted_data = g_newa(guint8, QQ_LOGIN_DATA_LENGTH + 16); /* 16 bytes more */
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
bytes = 0;
/* now generate the encrypted data
* 000-015 use password_twice_md5 as key to encrypt empty string */
- encrypted_len = qq_encrypt(raw_data + bytes, (guint8 *) "", 0, qd->password_twice_md5);
+ encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
g_return_if_fail(encrypted_len == 16);
- bytes += encrypted_len;
+ bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
/* 016-016 */
bytes += qq_put8(raw_data + bytes, 0x00);
@@ -313,97 +269,129 @@ void qq_send_packet_login(PurpleConnection *gc)
/* 053-068, fixed value, maybe related to per machine */
bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
/* 069, login token length */
- bytes += qq_put8(raw_data + bytes, qd->token_len);
+ bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
/* 070-093, login token, normally 24 bytes */
- bytes += qq_putdata(raw_data + bytes, qd->token, qd->token_len);
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
/* 100 bytes unknown */
bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
/* all zero left */
+ memset(raw_data + bytes, 0, 416 - bytes);
+ bytes = 416;
- encrypted_len = qq_encrypt(encrypted_data, raw_data, QQ_LOGIN_DATA_LENGTH, qd->inikey);
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
buf = g_newa(guint8, MAX_PACKET_SIZE);
memset(buf, 0, MAX_PACKET_SIZE);
bytes = 0;
- bytes += qq_putdata(buf + bytes, qd->inikey, QQ_KEY_LENGTH);
- bytes += qq_putdata(buf + bytes, encrypted_data, encrypted_len);
+ bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
qd->send_seq++;
qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
}
-guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len)
+guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len)
{
qq_data *qd;
+ gint bytes;
guint8 ret;
- int token_len;
- gchar *error_msg;
+ guint8 token_len;
+ gchar *msg;
- g_return_val_if_fail(buf != NULL && buf_len != 0, -1);
+ g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR);
- g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1);
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
qd = (qq_data *) gc->proto_data;
- ret = buf[0];
-
- if (ret != QQ_TOKEN_REPLY_OK) {
- purple_debug_error("QQ", "Failed to request token: %d\n", buf[0]);
- qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
- buf, buf_len,
- ">>> [default] decrypt and dump");
- error_msg = try_dump_as_gbk(buf, buf_len);
- if (error_msg == NULL) {
- error_msg = g_strdup_printf( _("Invalid token reply code, 0x%02X"), ret);
- }
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
- g_free(error_msg);
- return ret;
+ bytes = 0;
+ bytes += qq_get8(&ret, buf + bytes);
+ bytes += qq_get8(&token_len, buf + bytes);
+
+ if (ret != QQ_LOGIN_REPLY_OK) {
+ qq_show_packet("Failed requesting token", buf, buf_len);
+
+ msg = g_strdup_printf( _("Failed requesting token, 0x%02X"), ret );
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ msg);
+ g_free(msg);
+ return QQ_LOGIN_REPLY_ERR;
}
- token_len = buf_len-2;
- if (token_len <= 0) {
- error_msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
- g_free(error_msg);
- return -1;
+ if (bytes + token_len < buf_len) {
+ msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ msg);
+ g_free(msg);
+ return QQ_LOGIN_REPLY_ERR;
}
- if (buf[1] != token_len) {
- purple_debug_info("QQ",
- "Invalid token len. Packet specifies length of %d, actual length is %d\n", buf[1], buf_len-2);
+ if (bytes + token_len > buf_len) {
+ purple_debug_info("QQ", "Extra token data, %d %d\n", token_len, buf_len - bytes);
}
- qq_hex_dump(PURPLE_DEBUG_INFO, "QQ",
- buf+2, token_len,
- "<<< got a token -> [default] decrypt and dump");
+ qq_show_packet("Got token", buf + bytes, buf_len - bytes);
- qd->token = g_new0(guint8, token_len);
- qd->token_len = token_len;
- g_memmove(qd->token, buf + 2, qd->token_len);
+ if (qd->ld.token != NULL) {
+ g_free(qd->ld.token);
+ qd->ld.token = NULL;
+ qd->ld.token_len = 0;
+ }
+ qd->ld.token = g_new0(guint8, token_len);
+ qd->ld.token_len = token_len;
+ g_memmove(qd->ld.token, buf + 2, qd->ld.token_len);
return ret;
}
/* send logout packets to QQ server */
-void qq_send_packet_logout(PurpleConnection *gc)
+void qq_request_logout(PurpleConnection *gc)
{
gint i;
qq_data *qd;
qd = (qq_data *) gc->proto_data;
for (i = 0; i < 4; i++)
- qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->password_twice_md5, QQ_KEY_LENGTH);
+ qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_twice_md5, QQ_KEY_LENGTH);
qd->is_login = FALSE; /* update login status AFTER sending logout packets */
}
+/* for QQ 2003iii 0117, fixed value */
+/* static const guint8 login_23_51[29] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
+ 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
+ 0x95, 0x67, 0xda, 0x2c, 0x01
+}; */
+
+/* for QQ 2003iii 0304, fixed value */
+/*
+static const guint8 login_23_51[29] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
+ 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
+ 0x40, 0xb8, 0xac, 0x32, 0x01
+};
+*/
+
+/* fixed value, not affected by version, or mac address */
+/*
+static const guint8 login_53_68[16] = {
+ 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
+ 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
+};
+*/
+
/* process the login reply packet */
-guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len)
+guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len)
{
qq_data *qd;
guint8 ret = data[0];
- gchar *server_reply, *server_reply_utf8;
- gchar *error_msg;
+ gchar *msg, *msg_utf8;
+ gchar *error;
+ PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
- g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR_MISC);
+ g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
qd = (qq_data *) gc->proto_data;
@@ -415,64 +403,46 @@ guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len
purple_debug_info("QQ", "Redirect new server\n");
return process_login_redirect(gc, data, data_len);
- case QQ_LOGIN_REPLY_REDIRECT_EX:
- purple_debug_error("QQ", "Extend redirect new server, not supported yet\n");
- error_msg = g_strdup( _("Unable login for not support Redirect_EX now") );
- return QQ_LOGIN_REPLY_REDIRECT_EX;
-
- case QQ_LOGIN_REPLY_ERR_PWD:
- server_reply = g_strndup((gchar *)data + 1, data_len - 1);
- server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
-
- purple_debug_error("QQ", "Error password: %s\n", server_reply_utf8);
- error_msg = g_strdup_printf( _("Error password: %s"), server_reply_utf8);
-
- g_free(server_reply);
- g_free(server_reply_utf8);
-
+ case 0x0A: /* extend redirect used in QQ2006 */
+ error = g_strdup( _("Not support Redirect_EX now") );
+ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+ break;
+ case 0x05: /* invalid password */
if (!purple_account_get_remember_password(gc->account)) {
purple_account_set_password(gc->account, NULL);
}
-
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, error_msg);
- g_free(error_msg);
-
- return QQ_LOGIN_REPLY_ERR_PWD;
-
- case QQ_LOGIN_REPLY_NEED_REACTIVE:
- server_reply = g_strndup((gchar *)data + 1, data_len - 1);
- server_reply_utf8 = qq_to_utf8(server_reply, QQ_CHARSET_DEFAULT);
-
- purple_debug_error("QQ", "Need active: %s\n", server_reply_utf8);
- error_msg = g_strdup_printf( _("Need active: %s"), server_reply_utf8);
-
- g_free(server_reply);
- g_free(server_reply_utf8);
+ error = g_strdup( _("Error password"));
+ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+ break;
+ case 0x06: /* need activation */
+ error = g_strdup( _("Need active"));
+ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
break;
default:
- purple_debug_error("QQ",
- "Unable login for unknow reply code 0x%02X\n", data[0]);
- qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
- data, data_len,
- ">>> [default] decrypt and dump");
- error_msg = try_dump_as_gbk(data, data_len);
- if (error_msg == NULL) {
- error_msg = g_strdup_printf(
- _("Unable login for unknow reply code 0x%02X"), data[0] );
- }
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+ ">>> [default] decrypt and dump");
+ error = g_strdup_printf(
+ _("Unknow reply code when login (0x%02X)"),
+ ret );
+ reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
break;
}
- purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg);
- g_free(error_msg);
- return ret;
+ msg = g_strndup((gchar *)data + 1, data_len - 1);
+ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+ purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
+ purple_connection_error_reason(gc, reason, msg_utf8);
+
+ g_free(error);
+ g_free(msg);
+ g_free(msg_utf8);
+ return QQ_LOGIN_REPLY_ERR;
}
/* send keep-alive packet to QQ server (it is a heart-beat) */
-void qq_send_packet_keep_alive(PurpleConnection *gc)
+void qq_request_keep_alive(PurpleConnection *gc)
{
qq_data *qd;
guint8 raw_data[16] = {0};
@@ -484,11 +454,10 @@ void qq_send_packet_keep_alive(PurpleConnection *gc)
* with this command, server return the same result including
* the amount of online QQ users, my ip and port */
bytes += qq_put32(raw_data + bytes, qd->uid);
-
- qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, 4);
+ qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
}
-/* parse the return of keep-alive packet, it includes some system information */
+/* parse the return ofqq_process_keep_alive keep-alive packet, it includes some system information */
gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc)
{
qq_data *qd;
@@ -505,9 +474,10 @@ gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc
return TRUE;
/* segments[0] and segment[1] are all 0x30 ("0") */
- qd->total_online = strtol(segments[2], NULL, 10);
- if(0 == qd->total_online) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ qd->online_total = strtol(segments[2], NULL, 10);
+ if(0 == qd->online_total) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Keep alive error"));
}
qd->my_ip.s_addr = inet_addr(segments[3]);
@@ -519,3 +489,998 @@ gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc
g_strfreev(segments);
return TRUE;
}
+
+void qq_request_keep_alive_2007(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 raw_data[32] = {0};
+ gint bytes= 0;
+ gchar *uid_str;
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* In fact, we can send whatever we like to server
+ * with this command, server return the same result including
+ * the amount of online QQ users, my ip and port */
+ uid_str = g_strdup_printf("%u", qd->uid);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
+ qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
+
+ g_free(uid_str);
+}
+
+gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+ qq_data *qd;
+ gint bytes= 0;
+ guint8 ret;
+
+ g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* qq_show_packet("Keep alive reply packet", data, len); */
+
+ bytes = 0;
+ bytes += qq_get8(&ret, data + bytes);
+ bytes += qq_get32(&qd->online_total, data + bytes);
+ if(0 == qd->online_total) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Keep alive error"));
+ }
+
+ bytes += qq_getIP(&qd->my_ip, data + bytes);
+ bytes += qq_get16(&qd->my_port, data + bytes);
+ return TRUE;
+}
+
+void qq_request_keep_alive_2008(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 raw_data[16] = {0};
+ gint bytes= 0;
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* In fact, we can send whatever we like to server
+ * with this command, server return the same result including
+ * the amount of online QQ users, my ip and port */
+ bytes += qq_put32(raw_data + bytes, qd->uid);
+ bytes += qq_putime(raw_data + bytes, &qd->login_time);
+ qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
+}
+
+gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+ qq_data *qd;
+ gint bytes= 0;
+ guint8 ret;
+ time_t server_time;
+ struct tm *tm_local;
+
+ g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
+
+ qd = (qq_data *) gc->proto_data;
+
+ /* qq_show_packet("Keep alive reply packet", data, len); */
+
+ bytes = 0;
+ bytes += qq_get8(&ret, data + bytes);
+ bytes += qq_get32(&qd->online_total, data + bytes);
+ if(0 == qd->online_total) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Keep alive error"));
+ }
+
+ bytes += qq_getIP(&qd->my_ip, data + bytes);
+ bytes += qq_get16(&qd->my_port, data + bytes);
+ /* skip 2 byytes, 0x(00 3c) */
+ bytes += 2;
+ bytes += qq_getime(&server_time, data + bytes);
+ /* skip 5 bytes, all are 0 */
+
+ purple_debug_info("QQ", "keep alive, %s:%d\n",
+ inet_ntoa(qd->my_ip), qd->my_port);
+
+ tm_local = localtime(&server_time);
+ purple_debug_info("QQ", "Server time: %d-%d-%d, %d:%d:%d\n",
+ (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
+ tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
+ return TRUE;
+}
+
+/* For QQ2007/2008 */
+void qq_request_get_server(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ raw_data = g_newa(guint8, 128);
+ memset(raw_data, 0, 128);
+
+ encrypted = g_newa(guint8, 128 + 16); /* 16 bytes more */
+
+ bytes = 0;
+ if (qd->redirect == NULL) {
+ /* first packet to get server */
+ qd->redirect_len = 15;
+ qd->redirect = g_realloc(qd->redirect, qd->redirect_len);
+ memset(qd->redirect, 0, qd->redirect_len);
+ }
+ bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_GET_SERVER, qd->send_seq, buf, bytes, TRUE);
+}
+
+guint16 qq_process_get_server(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ qq_data *qd;
+ gint bytes;
+ guint16 ret;
+
+ g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_val_if_fail (data != NULL, QQ_LOGIN_REPLY_ERR);
+
+ /* qq_show_packet("Get Server", data, data_len); */
+ bytes = 0;
+ bytes += qq_get16(&ret, data + bytes);
+ if (ret == 0) {
+ /* Notice: do not clear redirect_data here. It will be used in login*/
+ qd->redirect_ip.s_addr = 0;
+ return QQ_LOGIN_REPLY_OK;
+ }
+
+ if (data_len < 15) {
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+ _("Can not decrypt get server reply"));
+ return QQ_LOGIN_REPLY_ERR;
+ }
+
+ qd->redirect_len = data_len;
+ qd->redirect = g_realloc(qd->redirect, qd->redirect_len);
+ qq_getdata(qd->redirect, qd->redirect_len, data);
+ /* qq_show_packet("Redirect to", qd->redirect, qd->redirect_len); */
+
+ qq_getIP(&qd->redirect_ip, data + 11);
+ purple_debug_info("QQ", "Get server %s\n", inet_ntoa(qd->redirect_ip));
+ return QQ_LOGIN_REPLY_REDIRECT;
+}
+
+void qq_request_token_ex(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
+
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
+ bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */
+ bytes += qq_put16(raw_data + bytes, 5);
+ bytes += qq_put32(raw_data + bytes, 0);
+ bytes += qq_put8(raw_data + bytes, 0); /* fragment index */
+ bytes += qq_put16(raw_data + bytes, 0); /* captcha token */
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
+}
+
+void qq_request_token_ex_next(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
+
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
+ bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */
+ bytes += qq_put16(raw_data + bytes, 5);
+ bytes += qq_put32(raw_data + bytes, 0);
+ bytes += qq_put8(raw_data + bytes, qd->captcha.next_index); /* fragment index */
+ bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); /* captcha token */
+ bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len);
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
+
+ purple_connection_update_progress(gc, _("Requesting captcha ..."), 3, QQ_CONNECT_STEPS);
+}
+
+static void request_token_ex_code(PurpleConnection *gc,
+ guint8 *token, guint16 token_len, guint8 *code, guint16 code_len)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+ g_return_if_fail(code != NULL && code_len > 0);
+
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
+
+ bytes = 0;
+ bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
+ bytes += qq_put8(raw_data + bytes, 4); /* Subcommand */
+ bytes += qq_put16(raw_data + bytes, 5);
+ bytes += qq_put32(raw_data + bytes, 0);
+ bytes += qq_put16(raw_data + bytes, code_len);
+ bytes += qq_putdata(raw_data + bytes, code, code_len);
+ bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len); /* login token ex */
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
+
+ purple_connection_update_progress(gc, _("Checking code of captcha ..."), 3, QQ_CONNECT_STEPS);
+}
+
+typedef struct {
+ PurpleConnection *gc;
+ guint8 *token;
+ guint16 token_len;
+} qq_captcha_request;
+
+static void captcha_request_destory(qq_captcha_request *captcha_req)
+{
+ g_return_if_fail(captcha_req != NULL);
+ if (captcha_req->token) g_free(captcha_req->token);
+ g_free(captcha_req);
+}
+
+static void captcha_input_cancel_cb(qq_captcha_request *captcha_req,
+ PurpleRequestFields *fields)
+{
+ captcha_request_destory(captcha_req);
+
+ purple_connection_error_reason(captcha_req->gc,
+ PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
+ _("Failed captcha verify"));
+}
+
+static void captcha_input_ok_cb(qq_captcha_request *captcha_req,
+ PurpleRequestFields *fields)
+{
+ gchar *code;
+
+ g_return_if_fail(captcha_req != NULL && captcha_req->gc != NULL);
+
+ code = utf8_to_qq(
+ purple_request_fields_get_string(fields, "captcha_code"),
+ QQ_CHARSET_DEFAULT);
+
+ if (strlen(code) <= 0) {
+ captcha_input_cancel_cb(captcha_req, fields);
+ return;
+ }
+
+ request_token_ex_code(captcha_req->gc,
+ captcha_req->token, captcha_req->token_len,
+ (guint8 *)code, strlen(code));
+
+ captcha_request_destory(captcha_req);
+}
+
+void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha)
+{
+ PurpleAccount *account;
+ PurpleRequestFields *fields;
+ PurpleRequestFieldGroup *group;
+ PurpleRequestField *field;
+ qq_captcha_request *captcha_req;
+
+ g_return_if_fail(captcha->token != NULL && captcha->token_len > 0);
+ g_return_if_fail(captcha->data != NULL && captcha->data_len > 0);
+
+ captcha_req = g_new0(qq_captcha_request, 1);
+ captcha_req->gc = gc;
+ captcha_req->token = g_new0(guint8, captcha->token_len);
+ g_memmove(captcha_req->token, captcha->token, captcha->token_len);
+ captcha_req->token_len = captcha->token_len;
+
+ account = purple_connection_get_account(gc);
+
+ fields = purple_request_fields_new();
+ group = purple_request_field_group_new(NULL);
+ purple_request_fields_add_group(fields, group);
+
+ field = purple_request_field_image_new("captcha_img",
+ _("Captcha Image"), (char *)captcha->data, captcha->data_len);
+ purple_request_field_group_add_field(group, field);
+
+ field = purple_request_field_string_new("captcha_code",
+ _("Enter code"), "", FALSE);
+ purple_request_field_string_set_masked(field, FALSE);
+ purple_request_field_group_add_field(group, field);
+
+ purple_request_fields(account,
+ _("QQ Captcha Verifing"),
+ _("QQ Captcha Verifing"),
+ _("Please fill code according to image"),
+ fields,
+ _("OK"), G_CALLBACK(captcha_input_ok_cb),
+ _("Cancel"), G_CALLBACK(captcha_input_cancel_cb),
+ purple_connection_get_account(gc), NULL, NULL,
+ captcha_req);
+}
+
+guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ qq_data *qd;
+ int bytes;
+ guint8 ret;
+ guint8 sub_cmd;
+ guint8 reply;
+ guint16 captcha_len;
+ guint8 curr_index;
+
+ g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
+ qd = (qq_data *) gc->proto_data;
+
+ ret = data[0];
+
+ bytes = 0;
+ bytes += qq_get8(&sub_cmd, data + bytes); /* 03: ok; 04: need verifying */
+ bytes += 2; /* 0x(00 05) */
+ bytes += qq_get8(&reply, data + bytes);
+
+ bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes);
+ qd->ld.token_ex = g_realloc(qd->ld.token_ex, qd->ld.token_ex_len);
+ bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len, data + bytes);
+ /* qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len); */
+
+ if(reply != 1)
+ {
+ purple_debug_info("QQ", "Captcha verified, result %d\n", reply);
+ return QQ_LOGIN_REPLY_OK;
+ }
+
+ bytes += qq_get16(&captcha_len, data + bytes);
+
+ qd->captcha.data = g_realloc(qd->captcha.data, qd->captcha.data_len + captcha_len);
+ bytes += qq_getdata(qd->captcha.data + qd->captcha.data_len, captcha_len, data + bytes);
+ qd->captcha.data_len += captcha_len;
+
+ bytes += qq_get8(&curr_index, data + bytes);
+ bytes += qq_get8(&qd->captcha.next_index, data + bytes);
+
+ bytes += qq_get16(&qd->captcha.token_len, data + bytes);
+ qd->captcha.token = g_realloc(qd->captcha.token, qd->captcha.token_len);
+ bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, data + bytes);
+ /* qq_show_packet("Get captcha token", qd->captcha.token, qd->captcha.token_len); */
+
+ purple_debug_info("QQ", "Request next captcha %d, new %d, total %d\n",
+ qd->captcha.next_index, captcha_len, qd->captcha.data_len);
+ if(qd->captcha.next_index > 0)
+ {
+ return QQ_LOGIN_REPLY_NEXT_TOKEN_EX;
+ }
+
+ return QQ_LOGIN_REPLY_CAPTCHA_DLG;
+}
+
+/* source copy from gg's common.c */
+static guint32 crc32_table[256];
+static int crc32_initialized = 0;
+
+static void crc32_make_table()
+{
+ guint32 h = 1;
+ unsigned int i, j;
+
+ memset(crc32_table, 0, sizeof(crc32_table));
+
+ for (i = 128; i; i >>= 1) {
+ h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
+
+ for (j = 0; j < 256; j += 2 * i)
+ crc32_table[i + j] = crc32_table[j] ^ h;
+ }
+
+ crc32_initialized = 1;
+}
+
+static guint32 crc32(guint32 crc, const guint8 *buf, int len)
+{
+ if (!crc32_initialized)
+ crc32_make_table();
+
+ if (!buf || len < 0)
+ return crc;
+
+ crc ^= 0xffffffffL;
+
+ while (len--)
+ crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xff];
+
+ return crc ^ 0xffffffffL;
+}
+
+void qq_request_check_pwd(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+ static guint8 header[] = {
+ 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0
+ };
+ static guint8 unknown[] = {
+ 0xDB, 0xB9, 0xF3, 0x0B, 0xF9, 0x13, 0x87, 0xB2,
+ 0xE6, 0x20, 0x43, 0xBE, 0x53, 0xCA, 0x65, 0x03
+ };
+
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->ld.token_ex != NULL && qd->ld.token_ex_len > 0);
+
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
+
+ /* Encrypted password and put in encrypted */
+ bytes = 0;
+ bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
+ bytes += qq_put16(raw_data + bytes, 0);
+ bytes += qq_put16(raw_data + bytes, rand() & 0xffff);
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
+
+ /* create packet */
+ bytes = 0;
+ bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
+ /* token get from qq_request_token_ex */
+ bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
+ /* password encrypted */
+ bytes += qq_put16(raw_data + bytes, encrypted_len);
+ bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+ /* len of unknown + len of CRC32 */
+ bytes += qq_put16(raw_data + bytes, sizeof(unknown) + 4);
+ bytes += qq_putdata(raw_data + bytes, unknown, sizeof(unknown));
+ bytes += qq_put32(
+ raw_data + bytes, crc32(0xFFFFFFFF, unknown, sizeof(unknown)));
+
+ /* put length into first 2 bytes */
+ qq_put8(raw_data + 1, bytes - 2);
+
+ /* tail */
+ bytes += qq_put16(raw_data + bytes, 0x0003);
+ bytes += qq_put8(raw_data + bytes, 0);
+ bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[1]);
+ bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[2]);
+
+ /* qq_show_packet("Check password", raw_data, bytes); */
+ /* Encrypted by random key*/
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE);
+}
+
+guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ qq_data *qd;
+ int bytes;
+ guint8 ret;
+ gchar *error = NULL;
+ guint16 unknow_token_len;
+ gchar *msg, *msg_utf8;
+ guint16 msg_len;
+ PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+
+ g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+ g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
+ qd = (qq_data *) gc->proto_data;
+
+ /* qq_show_packet("Check password reply", data, data_len); */
+
+ bytes = 0;
+ bytes += qq_get16(&unknow_token_len, data + bytes); /* maybe total length */
+ bytes += qq_get8(&ret, data + bytes);
+ bytes += 4; /* 0x(00 00 6d b9) */
+ /* unknow_token_len may 0 when not reply ok*/
+ bytes += qq_get16(&unknow_token_len, data + bytes); /* 0x0020 */
+ bytes += unknow_token_len;
+ bytes += qq_get16(&unknow_token_len, data + bytes); /* 0x0020 */
+ bytes += unknow_token_len;
+
+ if (ret == 0) {
+ /* get login_token */
+ bytes += qq_get16(&qd->ld.login_token_len, data + bytes);
+ if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
+ qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
+ bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
+ /* qq_show_packet("Get login token", qd->ld.login_token, qd->ld.login_token_len); */
+
+ /* get login_key */
+ bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
+ /* qq_show_packet("Get login key", qd->ld.login_key, sizeof(qd->ld.login_key)); */
+ return QQ_LOGIN_REPLY_OK;
+ }
+
+ switch (ret)
+ {
+ case 0x34: /* invalid password */
+ if (!purple_account_get_remember_password(gc->account)) {
+ purple_account_set_password(gc->account, NULL);
+ }
+ error = g_strdup(_("Error password"));
+ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+ break;
+ case 0x33: /* need activation */
+ case 0x51: /* need activation */
+ error = g_strdup(_("Need active"));
+ reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
+ break;
+ case 0xBF: /* uid is not exist */
+ error = g_strdup(_("invalid user name"));
+ reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
+ break;
+ default:
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
+ ">>> [default] decrypt and dump");
+ error = g_strdup_printf(
+ _("Unknow reply code when checking password (0x%02X)"),
+ ret );
+ reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
+ break;
+ }
+
+ bytes += qq_get16(&msg_len, data + bytes);
+
+ msg = g_strndup((gchar *)data + bytes, msg_len);
+ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+
+ purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
+ purple_connection_error_reason(gc, reason, msg_utf8);
+
+ g_free(error);
+ g_free(msg);
+ g_free(msg_utf8);
+ return QQ_LOGIN_REPLY_ERR;
+}
+
+void qq_request_login_2007(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+ static const guint8 login_1_16[] = {
+ 0x56, 0x4E, 0xC8, 0xFB, 0x0A, 0x4F, 0xEF, 0xB3,
+ 0x7A, 0x5D, 0xD8, 0x86, 0x0F, 0xAC, 0xE5, 0x1A
+ };
+ static const guint8 login_2_16[] = {
+ 0x5E, 0x22, 0x3A, 0xBE, 0x13, 0xBF, 0xDA, 0x4C,
+ 0xA9, 0xB7, 0x0B, 0x43, 0x63, 0x51, 0x8E, 0x28
+ };
+ static const guint8 login_3_83[] = {
+ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x40, 0x01, 0x01, 0x58, 0x83,
+ 0xD0, 0x00, 0x10, 0x9D, 0x14, 0x64, 0x0A, 0x2E,
+ 0xE2, 0x11, 0xF7, 0x90, 0xF0, 0xB5, 0x5F, 0x16,
+ 0xFB, 0x41, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x76, 0x3C, 0xEE,
+ 0x4A, 0x00, 0x10, 0x86, 0x81, 0xAD, 0x1F, 0xC8,
+ 0xC9, 0xCC, 0xCF, 0xCA, 0x9F, 0xFF, 0x88, 0xC0,
+ 0x5C, 0x88, 0xD5
+ };
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
+
+ /* Encrypted password and put in encrypted */
+ bytes = 0;
+ bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
+ bytes += qq_put16(raw_data + bytes, 0);
+ bytes += qq_put16(raw_data + bytes, 0xffff);
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
+
+ /* create packet */
+ bytes = 0;
+ bytes += qq_put16(raw_data + bytes, 0); /* Unknow */
+ /* password encrypted */
+ bytes += qq_put16(raw_data + bytes, encrypted_len);
+ bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+ /* put data which NULL string encrypted by key pwd_twice_md5 */
+ encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
+ g_return_if_fail(encrypted_len == 16);
+ bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+ /* unknow fill 0 */
+ memset(raw_data + bytes, 0, 19);
+ bytes += 19;
+ bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16));
+
+ bytes += qq_put8(raw_data + bytes, rand() & 0xff);
+ bytes += qq_put8(raw_data + bytes, qd->login_mode);
+ /* unknow 10 bytes zero filled*/
+ memset(raw_data + bytes, 0, 10);
+ bytes += 10;
+ /* redirect data, 15 bytes */
+ /* qq_show_packet("Redirect", qd->redirect, qd->redirect_len); */
+ bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
+ /* unknow fill */
+ bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16));
+ /* captcha token get from qq_process_token_ex */
+ bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
+ /* unknow fill */
+ bytes += qq_putdata(raw_data + bytes, login_3_83, sizeof(login_3_83));
+ memset(raw_data + bytes, 0, 332 - sizeof(login_3_83));
+ bytes += 332 - sizeof(login_3_83);
+
+ /* qq_show_packet("Login", raw_data, bytes); */
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ /* logint token get from qq_process_check_pwd_2007 */
+ bytes += qq_put16(buf + bytes, qd->ld.login_token_len);
+ bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
+}
+
+/* process the login reply packet */
+guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ qq_data *qd;
+ gint bytes;
+ guint8 ret;
+ guint32 uid;
+ gchar *error;
+ gchar *msg;
+ gchar *msg_utf8;
+
+ g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+ qd = (qq_data *) gc->proto_data;
+
+ bytes = 0;
+ bytes += qq_get8(&ret, data + bytes);
+ if (ret != 0) {
+ msg = g_strndup((gchar *)data + bytes, data_len - bytes);
+ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+ g_free(msg);
+
+ switch (ret) {
+ case 0x05:
+ purple_debug_error("QQ", "Server busy for %s\n", msg_utf8);
+ return QQ_LOGIN_REPLY_REDIRECT;
+ case 0x0A:
+ /* 0a 2d 9a 4b 9a 01 01 00 00 00 05 00 00 00 00 79 0e 5f fd */
+ /* Missing get server before login*/
+ default:
+ error = g_strdup_printf(
+ _("Unknow reply code when login (0x%02X):\n%s"),
+ ret, msg_utf8);
+ break;
+ }
+
+ purple_debug_error("QQ", "%s\n", error);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ error);
+
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error);
+
+ g_free(error);
+ g_free(msg_utf8);
+ return QQ_LOGIN_REPLY_ERR;
+ }
+
+ bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
+ purple_debug_info("QQ", "Got session_key\n");
+ get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+
+ bytes += qq_get32(&uid, data + bytes);
+ if (uid != qd->uid) {
+ purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+ }
+ bytes += qq_getIP(&qd->my_ip, data + bytes);
+ bytes += qq_get16(&qd->my_port, data + bytes);
+ bytes += qq_getIP(&qd->my_local_ip, data + bytes);
+ bytes += qq_get16(&qd->my_local_port, data + bytes);
+ bytes += qq_getime(&qd->login_time, data + bytes);
+ /* skip unknow 50 byte */
+ bytes += 50;
+ /* skip client key 32 byte */
+ bytes += 32;
+ /* skip unknow 12 byte */
+ bytes += 12;
+ /* last login */
+ bytes += qq_getIP(&qd->last_login_ip, data + bytes);
+ bytes += qq_getime(&qd->last_login_time[0], data + bytes);
+ purple_debug_info("QQ", "Last Login: %s, %s\n",
+ inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0]));
+ return QQ_LOGIN_REPLY_OK;
+}
+
+void qq_request_login_2008(PurpleConnection *gc)
+{
+ qq_data *qd;
+ guint8 *buf, *raw_data;
+ gint bytes;
+ guint8 *encrypted;
+ gint encrypted_len;
+ guint8 index, count;
+
+ static const guint8 login_1_16[] = {
+ 0xD2, 0x41, 0x75, 0x12, 0xC2, 0x86, 0x57, 0x10,
+ 0x78, 0x57, 0xDC, 0x24, 0x8C, 0xAA, 0x8F, 0x4E
+ };
+
+ static const guint8 login_2_16[] = {
+ 0x94, 0x0B, 0x73, 0x7A, 0xA2, 0x51, 0xF0, 0x4B,
+ 0x95, 0x2F, 0xC6, 0x0A, 0x5B, 0xF6, 0x76, 0x52
+ };
+ static const guint8 login_3_18[] = {
+ 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x40, 0x01, 0x1b, 0x02, 0x84,
+ 0x50, 0x00
+ };
+ static const guint8 login_4_16[] = {
+ 0x2D, 0x49, 0x15, 0x55, 0x78, 0xFC, 0xF3, 0xD4,
+ 0x53, 0x55, 0x60, 0x9C, 0x37, 0x9F, 0xE9, 0x59
+ };
+ static const guint8 login_5_6[] = {
+ 0x02, 0x68, 0xe8, 0x07, 0x83, 0x00
+ };
+ static const guint8 login_6_16[] = {
+ 0x3B, 0xCE, 0x43, 0xF1, 0x8B, 0xA4, 0x2B, 0xB5,
+ 0xB3, 0x51, 0x57, 0xF7, 0x06, 0x4B, 0x18, 0xFC
+ };
+ g_return_if_fail(gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+ g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
+
+ raw_data = g_newa(guint8, MAX_PACKET_SIZE - 16);
+ memset(raw_data, 0, MAX_PACKET_SIZE - 16);
+
+ encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 16 bytes more */
+
+ /* Encrypted password and put in encrypted */
+ bytes = 0;
+ bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
+ bytes += qq_put16(raw_data + bytes, 0);
+ bytes += qq_put16(raw_data + bytes, 0xffff);
+
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
+
+ /* create packet */
+ bytes = 0;
+ bytes += qq_put16(raw_data + bytes, 0); /* Unknow */
+ /* password encrypted */
+ bytes += qq_put16(raw_data + bytes, encrypted_len);
+ bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+ /* put data which NULL string encrypted by key pwd_twice_md5 */
+ encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
+ g_return_if_fail(encrypted_len == 16);
+ bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
+ /* unknow 19 bytes zero filled*/
+ memset(raw_data + bytes, 0, 19);
+ bytes += 19;
+ bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16));
+
+ index = rand() % 3; /* can be set as 1 */
+ for( count = 0; count < encrypted_len; count++ )
+ index ^= encrypted[count];
+ for( count = 0; count < sizeof(login_1_16); count++ )
+ index ^= login_1_16[count];
+ bytes += qq_put8(raw_data + bytes, index); /* random in QQ 2007*/
+
+ bytes += qq_put8(raw_data + bytes, qd->login_mode);
+ /* unknow 10 bytes zero filled*/
+ memset(raw_data + bytes, 0, 10);
+ bytes += 10;
+ /* redirect data, 15 bytes */
+ bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
+ /* unknow fill */
+ bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16));
+ /* captcha token get from qq_process_token_ex */
+ bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
+ bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
+ /* unknow fill */
+ bytes += qq_putdata(raw_data + bytes, login_3_18, sizeof(login_3_18));
+ bytes += qq_put8(raw_data + bytes , sizeof(login_4_16));
+ bytes += qq_putdata(raw_data + bytes, login_4_16, sizeof(login_4_16));
+ /* unknow 10 bytes zero filled*/
+ memset(raw_data + bytes, 0, 10);
+ bytes += 10;
+ /* redirect data, 15 bytes */
+ bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
+ /* unknow fill */
+ bytes += qq_putdata(raw_data + bytes, login_5_6, sizeof(login_5_6));
+ bytes += qq_put8(raw_data + bytes , sizeof(login_6_16));
+ bytes += qq_putdata(raw_data + bytes, login_6_16, sizeof(login_6_16));
+ /* unknow 249 bytes zero filled*/
+ memset(raw_data + bytes, 0, 249);
+ bytes += 249;
+
+ /* qq_show_packet("Login request", raw_data, bytes); */
+ encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
+
+ buf = g_newa(guint8, MAX_PACKET_SIZE);
+ memset(buf, 0, MAX_PACKET_SIZE);
+ bytes = 0;
+ /* logint token get from qq_process_check_pwd_2007 */
+ bytes += qq_put16(buf + bytes, qd->ld.login_token_len);
+ bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len);
+ bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
+
+ qd->send_seq++;
+ qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
+}
+
+guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ qq_data *qd;
+ gint bytes;
+ guint8 ret;
+ guint32 uid;
+ gchar *error;
+ gchar *msg;
+ gchar *msg_utf8;
+
+ g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
+
+ qd = (qq_data *) gc->proto_data;
+
+ bytes = 0;
+ bytes += qq_get8(&ret, data + bytes);
+ if (ret != 0) {
+ msg = g_strndup((gchar *)data + bytes, data_len - bytes);
+ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+ g_free(msg);
+
+ switch (ret) {
+ case 0x05:
+ purple_debug_error("QQ", "Server busy for %s\n", msg_utf8);
+ return QQ_LOGIN_REPLY_REDIRECT;
+ break;
+ default:
+ error = g_strdup_printf(
+ _("Unknow reply code when login (0x%02X):\n%s"),
+ ret, msg_utf8);
+ break;
+ }
+
+ purple_debug_error("QQ", "%s\n", error);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_OTHER_ERROR,
+ error);
+
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error);
+
+ g_free(error);
+ g_free(msg_utf8);
+ return QQ_LOGIN_REPLY_ERR;
+ }
+
+ bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
+ purple_debug_info("QQ", "Got session_key\n");
+ get_session_md5(qd->session_md5, qd->uid, qd->session_key);
+
+ bytes += qq_get32(&uid, data + bytes);
+ if (uid != qd->uid) {
+ purple_debug_warning("QQ", "My uid in login reply is %d, not %d\n", uid, qd->uid);
+ }
+ bytes += qq_getIP(&qd->my_ip, data + bytes);
+ bytes += qq_get16(&qd->my_port, data + bytes);
+ bytes += qq_getIP(&qd->my_local_ip, data + bytes);
+ bytes += qq_get16(&qd->my_local_port, data + bytes);
+ bytes += qq_getime(&qd->login_time, data + bytes);
+ /* skip 1 byte, always 0x03 */
+ /* skip 1 byte, login mode */
+ bytes = 131;
+ bytes += qq_getIP(&qd->last_login_ip, data + bytes);
+ bytes += qq_getime(&qd->last_login_time[0], data + bytes);
+ purple_debug_info("QQ", "Last Login: %s, %s\n",
+ inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0]));
+ return QQ_LOGIN_REPLY_OK;
+}
diff --git a/libpurple/protocols/qq/qq_base.h b/libpurple/protocols/qq/qq_base.h
index eb2e413f04..d6b2f2d3dd 100644
--- a/libpurple/protocols/qq/qq_base.h
+++ b/libpurple/protocols/qq/qq_base.h
@@ -28,29 +28,51 @@
#include <glib.h>
#include "connection.h"
-#define QQ_TOKEN_REPLY_OK 0x00
-
#define QQ_LOGIN_REPLY_OK 0x00
#define QQ_LOGIN_REPLY_REDIRECT 0x01
-#define QQ_LOGIN_REPLY_ERR_PWD 0x05
-#define QQ_LOGIN_REPLY_NEED_REACTIVE 0x06
-#define QQ_LOGIN_REPLY_REDIRECT_EX 0x0A
-#define QQ_LOGIN_REPLY_ERR_MISC 0xff /* defined by myself */
+/* defined by myself */
+#define QQ_LOGIN_REPLY_CAPTCHA_DLG 0xfd
+#define QQ_LOGIN_REPLY_NEXT_TOKEN_EX 0xfe
+#define QQ_LOGIN_REPLY_ERR 0xff
-#define QQ_LOGIN_MODE_NORMAL 0x0a
-#define QQ_LOGIN_MODE_AWAY 0x1e
-#define QQ_LOGIN_MODE_HIDDEN 0x28
+#define QQ_LOGIN_MODE_NORMAL 0x0a
+#define QQ_LOGIN_MODE_AWAY 0x1e
+#define QQ_LOGIN_MODE_HIDDEN 0x28
#define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */
-void qq_send_packet_token(PurpleConnection *gc);
-guint8 qq_process_token_reply(PurpleConnection *gc, guint8 *buf, gint buf_len);
+void qq_request_token(PurpleConnection *gc);
+guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len);
-void qq_send_packet_login(PurpleConnection *gc);
-guint8 qq_process_login_reply( PurpleConnection *gc, guint8 *data, gint data_len);
+void qq_request_login(PurpleConnection *gc);
+guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len);
-void qq_send_packet_logout(PurpleConnection *gc);
+void qq_request_logout(PurpleConnection *gc);
-void qq_send_packet_keep_alive(PurpleConnection *gc);
+void qq_request_keep_alive(PurpleConnection *gc);
gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc);
+
+void qq_request_keep_alive_2007(PurpleConnection *gc);
+gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc);
+
+void qq_request_keep_alive_2008(PurpleConnection *gc);
+gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc);
+
+/* for QQ2007/2008 */
+void qq_request_get_server(PurpleConnection *gc);
+guint16 qq_process_get_server(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
+
+void qq_request_token_ex(PurpleConnection *gc);
+void qq_request_token_ex_next(PurpleConnection *gc);
+guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *buf, gint buf_len);
+void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha);
+
+void qq_request_check_pwd(PurpleConnection *gc);
+guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len);
+
+void qq_request_login_2007(PurpleConnection *gc);
+guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len);
+
+void qq_request_login_2008(PurpleConnection *gc);
+guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len);
#endif
diff --git a/libpurple/protocols/qq/header_info.c b/libpurple/protocols/qq/qq_define.c
index 5dfe85c54e..985384b629 100644
--- a/libpurple/protocols/qq/header_info.c
+++ b/libpurple/protocols/qq/qq_define.c
@@ -1,5 +1,5 @@
/**
- * @file header_info.c
+ * @file qq_define.c
*
* purple
*
@@ -24,7 +24,7 @@
#include "internal.h"
-#include "header_info.h"
+#include "qq_define.h"
#define QQ_CLIENT_062E 0x062e /* GB QQ2000c build 0630 */
#define QQ_CLIENT_072E 0x072e /* EN QQ2000c build 0305 */
@@ -51,10 +51,8 @@
#define QQ_CLIENT_0F4B 0x0F4B /* QQ2006 Beta 3 */
#define QQ_CLIENT_1105 0x1105 /* QQ2007 beta4*/
-#define QQ_CLIENT_111D 0x111D /* QQ2007 */
-#define QQ_CLIENT_115B 0x115B /* QQ2008 */
#define QQ_CLIENT_1203 0x1203 /* QQ2008 */
-#define QQ_CLIENT_1205 0x1205 /* QQ2008 */
+#define QQ_CLIENT_1205 0x1205 /* QQ2008 Qi Fu */
#define QQ_CLIENT_120B 0x120B /* QQ2008 July 8.0.978.400 */
#define QQ_CLIENT_1412 0x1412 /* QQMac 1.0 preview1 build 670 */
#define QQ_CLIENT_1441 0x1441 /* QQ2009 preview2 */
@@ -94,6 +92,7 @@ const gchar *qq_get_ver_desc(gint source)
return "QQ2005 beta1";
case QQ_CLIENT_0D51:
return "QQ2005 beta2";
+ case QQ_CLIENT_0D55:
case QQ_CLIENT_0D61:
return "QQ2005";
case QQ_CLIENT_0E1B:
@@ -140,12 +139,12 @@ const gchar *qq_get_cmd_desc(gint cmd)
return "QQ_CMD_SEARCH_USER";
case QQ_CMD_GET_BUDDY_INFO:
return "QQ_CMD_GET_BUDDY_INFO";
- case QQ_CMD_ADD_BUDDY_WO_AUTH:
- return "QQ_CMD_ADD_BUDDY_WO_AUTH";
- case QQ_CMD_DEL_BUDDY:
- return "QQ_CMD_DEL_BUDDY";
- case QQ_CMD_BUDDY_AUTH:
- return "QQ_CMD_BUDDY_AUTH";
+ case QQ_CMD_ADD_BUDDY_NO_AUTH:
+ return "QQ_CMD_ADD_BUDDY_NO_AUTH";
+ case QQ_CMD_REMOVE_BUDDY:
+ return "QQ_CMD_REMOVE_BUDDY";
+ case QQ_CMD_ADD_BUDDY_AUTH:
+ return "QQ_CMD_ADD_BUDDY_AUTH";
case QQ_CMD_CHANGE_STATUS:
return "QQ_CMD_CHANGE_STATUS";
case QQ_CMD_ACK_SYS_MSG:
@@ -154,8 +153,8 @@ const gchar *qq_get_cmd_desc(gint cmd)
return "QQ_CMD_SEND_IM";
case QQ_CMD_RECV_IM:
return "QQ_CMD_RECV_IM";
- case QQ_CMD_REMOVE_SELF:
- return "QQ_CMD_REMOVE_SELF";
+ case QQ_CMD_REMOVE_ME:
+ return "QQ_CMD_REMOVE_ME";
case QQ_CMD_LOGIN:
return "QQ_CMD_LOGIN";
case QQ_CMD_GET_BUDDIES_LIST:
@@ -174,6 +173,22 @@ const gchar *qq_get_cmd_desc(gint cmd)
return "QQ_CMD_RECV_MSG_SYS";
case QQ_CMD_BUDDY_CHANGE_STATUS:
return "QQ_CMD_BUDDY_CHANGE_STATUS";
+ case QQ_CMD_GET_SERVER:
+ return "QQ_CMD_GET_SERVER";
+ case QQ_CMD_TOKEN_EX:
+ return "QQ_CMD_TOKEN_EX";
+ case QQ_CMD_CHECK_PWD:
+ return "QQ_CMD_CHECK_PWD";
+ case QQ_CMD_AUTH_CODE:
+ return "QQ_CMD_AUTH_CODE";
+ case QQ_CMD_ADD_BUDDY_NO_AUTH_EX:
+ return "QQ_CMD_ADD_BUDDY_NO_AUTH_EX";
+ case QQ_CMD_ADD_BUDDY_AUTH_EX:
+ return "QQ_CMD_BUDDY_ADD_AUTH_EX";
+ case QQ_CMD_BUDDY_CHECK_CODE:
+ return "QQ_CMD_BUDDY_CHECK_CODE";
+ case QQ_CMD_BUDDY_QUESTION:
+ return "QQ_CMD_BUDDY_QUESTION";
default:
return "Unknown CMD";
}
@@ -234,3 +249,18 @@ const gchar *qq_get_room_cmd_desc(gint room_cmd)
return "Unknown Room Command";
}
}
+
+/* check if status means online or offline */
+gboolean is_online(guint8 status)
+{
+ switch(status) {
+ case QQ_BUDDY_ONLINE_NORMAL:
+ case QQ_BUDDY_ONLINE_AWAY:
+ case QQ_BUDDY_ONLINE_INVISIBLE:
+ case QQ_BUDDY_ONLINE_BUSY:
+ return TRUE;
+ case QQ_BUDDY_CHANGE_TO_OFFLINE:
+ return FALSE;
+ }
+ return FALSE;
+}
diff --git a/libpurple/protocols/qq/header_info.h b/libpurple/protocols/qq/qq_define.h
index 6f792e0c66..ab6c474ad5 100644
--- a/libpurple/protocols/qq/header_info.h
+++ b/libpurple/protocols/qq/qq_define.h
@@ -1,5 +1,5 @@
/**
- * @file header_info.h
+ * @file qq_define.h
*
* purple
*
@@ -30,10 +30,12 @@
#define QQ_UDP_HEADER_LENGTH 7
#define QQ_TCP_HEADER_LENGTH 9
-#define QQ_PACKET_TAG 0x02 /* all QQ text packets starts with it */
-#define QQ_PACKET_TAIL 0x03 /* all QQ text packets end with it */
+#define QQ_PACKET_TAG 0x02 /* all QQ text packets starts with it */
+#define QQ_PACKET_TAIL 0x03 /* all QQ text packets end with it */
-#define QQ_CLIENT 0x0d55
+#define QQ_CLIENT_0D55 0x0d55 /* QQ2005 used by openq before */
+#define QQ_CLIENT_111D 0x111D /* QQ2007 */
+#define QQ_CLIENT_115B 0x115B /* QQ2008 He Sui*/
const gchar *qq_get_ver_desc(gint source);
@@ -44,14 +46,14 @@ enum {
QQ_CMD_UPDATE_INFO = 0x0004, /* update information */
QQ_CMD_SEARCH_USER = 0x0005, /* search for user */
QQ_CMD_GET_BUDDY_INFO = 0x0006, /* get user information */
- QQ_CMD_ADD_BUDDY_WO_AUTH = 0x0009, /* add buddy without auth */
- QQ_CMD_DEL_BUDDY = 0x000a, /* delete a buddy */
- QQ_CMD_BUDDY_AUTH = 0x000b, /* buddy authentication */
+ QQ_CMD_ADD_BUDDY_NO_AUTH = 0x0009, /* add buddy without auth */
+ QQ_CMD_REMOVE_BUDDY = 0x000a, /* delete a buddy */
+ QQ_CMD_ADD_BUDDY_AUTH = 0x000b, /* buddy authentication */
QQ_CMD_CHANGE_STATUS = 0x000d, /* change my online status */
QQ_CMD_ACK_SYS_MSG = 0x0012, /* ack system message */
QQ_CMD_SEND_IM = 0x0016, /* send message */
QQ_CMD_RECV_IM = 0x0017, /* receive message */
- QQ_CMD_REMOVE_SELF = 0x001c, /* remove self */
+ QQ_CMD_REMOVE_ME = 0x001c, /* remove self */
QQ_CMD_REQUEST_KEY = 0x001d, /* request key for file transfer */
QQ_CMD_CELL_PHONE_1 = 0x0021, /* cell phone 1 */
QQ_CMD_LOGIN = 0x0022, /* login */
@@ -64,6 +66,15 @@ enum {
QQ_CMD_TOKEN = 0x0062, /* get login token */
QQ_CMD_RECV_MSG_SYS = 0x0080, /* receive a system message */
QQ_CMD_BUDDY_CHANGE_STATUS = 0x0081, /* buddy change status */
+ /* for QQ2007*/
+ QQ_CMD_GET_SERVER = 0x0091, /* select login server */
+ QQ_CMD_TOKEN_EX = 0x00BA, /* get LOGIN token */
+ QQ_CMD_CHECK_PWD = 0x00DD, /* Password verify */
+ QQ_CMD_AUTH_CODE = 0x00AE, /* the request verification of information */
+ QQ_CMD_ADD_BUDDY_NO_AUTH_EX = 0x00A7, /* add friend without auth */
+ QQ_CMD_ADD_BUDDY_AUTH_EX = 0x00A8, /* add buddy with auth */
+ QQ_CMD_BUDDY_CHECK_CODE = 0x00B5,
+ QQ_CMD_BUDDY_QUESTION = 0x00B7,
};
const gchar *qq_get_cmd_desc(gint type);
@@ -98,4 +109,28 @@ enum {
const gchar *qq_get_room_cmd_desc(gint room_cmd);
+enum {
+ QQ_SERVER_BUDDY_ADDED = 1,
+ QQ_SERVER_BUDDY_ADD_REQUEST = 2,
+ QQ_SERVER_BUDDY_ADDED_ME = 3,
+ QQ_SERVER_BUDDY_REJECTED_ME = 4,
+ QQ_SERVER_NOTICE= 6,
+ QQ_SERVER_NEW_CLIENT = 9,
+ QQ_SERVER_BUDDY_ADDING_EX = 40,
+ QQ_SERVER_BUDDY_ADD_REQUEST_EX = 41,
+ QQ_SERVER_BUDDY_ADDED_ANSWER = 42,
+ QQ_SERVER_BUDDY_ADDED_EX = 43,
+};
+
+enum {
+ QQ_BUDDY_OFFLINE = 0x00,
+ QQ_BUDDY_ONLINE_NORMAL = 10,
+ QQ_BUDDY_CHANGE_TO_OFFLINE = 20,
+ QQ_BUDDY_ONLINE_AWAY = 30,
+ QQ_BUDDY_ONLINE_INVISIBLE = 40,
+ QQ_BUDDY_ONLINE_BUSY = 50,
+};
+
+gboolean is_online(guint8 status);
+
#endif
diff --git a/libpurple/protocols/qq/qq_network.c b/libpurple/protocols/qq/qq_network.c
index aee961c930..e4ea521f72 100644
--- a/libpurple/protocols/qq/qq_network.c
+++ b/libpurple/protocols/qq/qq_network.c
@@ -28,9 +28,9 @@
#include "buddy_info.h"
#include "group_info.h"
-#include "group_free.h"
+#include "group_internal.h"
#include "qq_crypt.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "qq_base.h"
#include "buddy_list.h"
#include "packet_parse.h"
@@ -160,7 +160,7 @@ static gboolean connect_check(gpointer data)
qd->connect_watcher = 0;
}
- if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) {
+ if (qd->fd >= 0 && qd->ld.token != NULL && qd->ld.token_len > 0) {
purple_debug_info("QQ", "Connect ok\n");
return FALSE;
}
@@ -202,7 +202,8 @@ gboolean qq_connect_later(gpointer data)
if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) {
if ( set_new_server(qd) != TRUE) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Failed to connect all servers"));
return FALSE;
}
@@ -220,7 +221,8 @@ gboolean qq_connect_later(gpointer data)
qd->connect_retry--;
if ( !connect_to_server(gc, server, port) ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect."));
}
@@ -228,6 +230,19 @@ gboolean qq_connect_later(gpointer data)
return FALSE; /* timeout callback stops */
}
+static void redirect_server(PurpleConnection *gc)
+{
+ qq_data *qd;
+ qd = (qq_data *) gc->proto_data;
+
+ if (qd->check_watcher > 0) {
+ purple_timeout_remove(qd->check_watcher);
+ qd->check_watcher = 0;
+ }
+ if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher);
+ qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
+}
+
/* process the incoming packet from qq_pending */
static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
{
@@ -242,6 +257,7 @@ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
guint32 room_id;
gint update_class;
guint32 ship32;
+ int ret;
qq_transaction *trans;
@@ -285,23 +301,22 @@ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
update_class = qq_trans_get_class(trans);
ship32 = qq_trans_get_ship(trans);
+ if (update_class != 0 || ship32 != 0) {
+ purple_debug_info("QQ", "Process in Update class %d, ship32 %d\n",
+ update_class, ship32);
+ }
switch (cmd) {
case QQ_CMD_TOKEN:
- if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) {
- qq_send_packet_login(gc);
- }
- break;
+ case QQ_CMD_GET_SERVER:
+ case QQ_CMD_TOKEN_EX:
+ case QQ_CMD_CHECK_PWD:
case QQ_CMD_LOGIN:
- qq_proc_login_cmd(gc, buf + bytes, bytes_not_read);
- /* check is redirect or not, and do it now */
- if (qd->redirect_ip.s_addr != 0) {
- if (qd->check_watcher > 0) {
- purple_timeout_remove(qd->check_watcher);
- qd->check_watcher = 0;
+ ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+ if (ret != QQ_LOGIN_REPLY_OK) {
+ if (ret == QQ_LOGIN_REPLY_REDIRECT) {
+ redirect_server(gc);
}
- if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher);
- qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc);
return FALSE; /* do nothing after this function and return now */
}
break;
@@ -312,10 +327,10 @@ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n",
qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
#endif
- qq_proc_room_cmd(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
+ qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32);
break;
default:
- qq_proc_client_cmd(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
+ qq_proc_client_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32);
break;
}
@@ -342,7 +357,8 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
qd = (qq_data *) gc->proto_data;
if(cond != PURPLE_INPUT_READ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Socket error"));
return;
}
@@ -366,7 +382,9 @@ static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
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);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ error_msg);
g_free(error_msg);
return;
} else if (buf_len == 0) {
@@ -468,7 +486,8 @@ static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
qd = (qq_data *) gc->proto_data;
if(cond != PURPLE_INPUT_READ) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Socket error"));
return;
}
@@ -478,7 +497,8 @@ static void udp_pending(gpointer data, gint source, PurpleInputCondition cond)
/* here we have UDP proxy suppport */
buf_len = read(source, buf, MAX_PACKET_SIZE);
if (buf_len <= 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to read from socket"));
return;
}
@@ -526,7 +546,9 @@ static gint udp_send_out(PurpleConnection *gc, guint8 *data, gint data_len)
if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
purple_debug_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ g_strerror(errno));
}
return ret;
}
@@ -558,8 +580,9 @@ static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond)
return;
else if (ret < 0) {
/* TODO: what to do here - do we really have to disconnect? */
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Write Error"));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Write Error"));
return;
}
@@ -603,7 +626,9 @@ static gint tcp_send_out(PurpleConnection *gc, guint8 *data, gint data_len)
/* TODO: what to do here - do we really have to disconnect? */
purple_debug_error("TCP_SEND_OUT",
"Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ g_strerror(errno));
return ret;
}
@@ -630,7 +655,8 @@ static gboolean network_timeout(gpointer data)
is_lost_conn = qq_trans_scan(gc);
if (is_lost_conn) {
purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Connection lost"));
return TRUE;
}
@@ -641,7 +667,13 @@ static gboolean network_timeout(gpointer data)
qd->itv_count.keep_alive--;
if (qd->itv_count.keep_alive <= 0) {
qd->itv_count.keep_alive = qd->itv_config.keep_alive;
- qq_send_packet_keep_alive(gc);
+ if (qd->client_version >= 2008) {
+ qq_request_keep_alive_2008(gc);
+ } else if (qd->client_version >= 2007) {
+ qq_request_keep_alive_2007(gc);
+ } else {
+ qq_request_keep_alive(gc);
+ }
return TRUE;
}
@@ -659,12 +691,15 @@ static gboolean network_timeout(gpointer data)
return TRUE; /* if return FALSE, timeout callback stops */
}
-static void do_request_token(PurpleConnection *gc)
+static void set_all_keys(PurpleConnection *gc)
{
qq_data *qd;
- gchar *conn_msg;
const gchar *passwd;
-
+ guint8 *dest;
+ int dest_len = QQ_KEY_LENGTH;
+#ifndef DEBUG
+ int bytes;
+#endif
/* _qq_show_socket("Got login socket", source); */
g_return_if_fail(gc != NULL && gc->proto_data != NULL);
@@ -675,27 +710,25 @@ static void do_request_token(PurpleConnection *gc)
qd->send_seq = rand() & 0xffff;
qd->is_login = FALSE;
- qd->channel = 1;
qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
+#ifdef DEBUG
+ memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key));
+#else
+ for (bytes = 0; bytes < sizeof(qd->ld.random_key); bytes++) {
+ qd->ld.random_key[bytes] = (guint8) (rand() & 0xff);
+ }
+#endif
+
/* now generate md5 processed passwd */
passwd = purple_account_get_password(purple_connection_get_account(gc));
/* use twice-md5 of user password as session key since QQ 2003iii */
- qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
- (guint8 *)passwd, strlen(passwd));
- qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
- qd->password_twice_md5, sizeof(qd->password_twice_md5));
-
- g_return_if_fail(qd->network_watcher == 0);
- qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
-
- /* Update the login progress status display */
- conn_msg = g_strdup_printf(_("Request token"));
- purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS);
- g_free(conn_msg);
+ dest = qd->ld.pwd_md5;
+ qq_get_md5(dest, dest_len, (guint8 *)passwd, strlen(passwd));
- qq_send_packet_token(gc);
+ dest = qd->ld.pwd_twice_md5;
+ qq_get_md5(dest, dest_len, qd->ld.pwd_md5, dest_len);
}
/* the callback function after socket is built
@@ -740,7 +773,19 @@ static void connect_cb(gpointer data, gint source, const gchar *error_message)
conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc);
}
- do_request_token( gc );
+ g_return_if_fail(qd->network_watcher == 0);
+ qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc);
+
+ set_all_keys( gc );
+
+ if (qd->client_version >= 2007) {
+ purple_connection_update_progress(gc, _("Get server ..."), 2, QQ_CONNECT_STEPS);
+ qq_request_get_server(gc);
+ return;
+ }
+
+ purple_connection_update_progress(gc, _("Request token"), 2, QQ_CONNECT_STEPS);
+ qq_request_token(gc);
}
#ifndef purple_proxy_connect_udp
@@ -811,8 +856,8 @@ static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_me
if (!hosts || !hosts->data) {
purple_connection_error_reason(gc,
- PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Couldn't resolve host"));
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ _("Couldn't resolve host"));
return;
}
@@ -884,21 +929,19 @@ gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port)
{
PurpleAccount *account ;
qq_data *qd;
- gchar *conn_msg;
g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE);
account = purple_connection_get_account(gc);
qd = (qq_data *) gc->proto_data;
if (server == NULL || strlen(server) == 0 || port == 0) {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Invalid server or port"));
return FALSE;
}
- conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port);
- purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS);
- g_free(conn_msg);
+ purple_connection_update_progress(gc, _("Connecting server ..."), 1, QQ_CONNECT_STEPS);
purple_debug_info("QQ", "Connect to %s:%d\n", server, port);
@@ -959,7 +1002,7 @@ void qq_disconnect(PurpleConnection *gc)
/* finish all I/O */
if (qd->fd >= 0 && qd->is_login) {
- qq_send_packet_logout(gc);
+ qq_request_logout(gc);
}
/* not connected */
@@ -984,23 +1027,20 @@ void qq_disconnect(PurpleConnection *gc)
qq_trans_remove_all(gc);
- if (qd->token) {
- purple_debug_info("QQ", "free token\n");
- g_free(qd->token);
- qd->token = NULL;
- qd->token_len = 0;
- }
- memset(qd->inikey, 0, sizeof(qd->inikey));
- memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5));
+ memset(qd->ld.random_key, 0, sizeof(qd->ld.random_key));
+ memset(qd->ld.pwd_md5, 0, sizeof(qd->ld.pwd_md5));
+ memset(qd->ld.pwd_twice_md5, 0, sizeof(qd->ld.pwd_twice_md5));
+ memset(qd->ld.login_key, 0, sizeof(qd->ld.login_key));
memset(qd->session_key, 0, sizeof(qd->session_key));
memset(qd->session_md5, 0, sizeof(qd->session_md5));
+ qd->my_local_ip.s_addr = 0;
+ qd->my_local_port = 0;
qd->my_ip.s_addr = 0;
+ qd->my_port = 0;
- qq_group_free_all(qd);
- qq_add_buddy_request_free(qd);
- qq_info_query_free(qd);
- qq_buddies_list_free(gc->account, qd);
+ qq_room_data_free_all(gc);
+ qq_buddy_data_free_all(gc);
}
static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq,
@@ -1017,7 +1057,7 @@ static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, gui
}
/* 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, qd->client_tag);
bytes += qq_put16(buf + bytes, cmd);
bytes += qq_put16(buf + bytes, seq);
@@ -1064,7 +1104,7 @@ static gint packet_send_out(PurpleConnection *gc, guint16 cmd, guint16 seq, guin
}
gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
- guint8 *encrypted_data, gint encrypted_len, gboolean is_save2trans)
+ guint8 *encrypted, gint encrypted_len, gboolean is_save2trans)
{
gint sent_len;
@@ -1074,9 +1114,9 @@ gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq,
seq, qq_get_cmd_desc(cmd), cmd, encrypted_len);
#endif
- sent_len = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+ sent_len = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
if (is_save2trans) {
- qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len, 0, 0);
+ qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, 0, 0);
}
return sent_len;
}
@@ -1086,7 +1126,7 @@ static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq,
guint8 *data, gint data_len, gboolean is_save2trans, gint update_class, guint32 ship32)
{
qq_data *qd;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
gint bytes_sent;
@@ -1095,18 +1135,18 @@ static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq,
g_return_val_if_fail(data != NULL && data_len > 0, -1);
/* at most 16 bytes more */
- encrypted_data = g_newa(guint8, data_len + 16);
- encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
+ encrypted = g_newa(guint8, data_len + 16);
+ encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key);
if (encrypted_len < 16) {
purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
return -1;
}
- bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
+ bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
if (is_save2trans) {
- qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len,
+ qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len,
update_class, ship32);
}
return bytes_sent;
@@ -1159,7 +1199,7 @@ gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len)
gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
{
qq_data *qd;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
gint bytes_sent;
@@ -1172,16 +1212,16 @@ gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8
seq, qq_get_cmd_desc(cmd), cmd, data_len);
#endif
/* at most 16 bytes more */
- encrypted_data = g_newa(guint8, data_len + 16);
- encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key);
+ encrypted = g_newa(guint8, data_len + 16);
+ encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key);
if (encrypted_len < 16) {
purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n",
encrypted_len, seq, cmd, qq_get_cmd_desc(cmd));
return -1;
}
- bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len);
- qq_trans_add_server_reply(gc, cmd, seq, encrypted_data, encrypted_len);
+ bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len);
+ qq_trans_add_server_reply(gc, cmd, seq, encrypted, encrypted_len);
return bytes_sent;
}
@@ -1192,7 +1232,7 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
qq_data *qd;
guint8 *buf;
gint buf_len;
- guint8 *encrypted_data;
+ guint8 *encrypted;
gint encrypted_len;
gint bytes_sent;
guint16 seq;
@@ -1217,17 +1257,17 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
qd->send_seq++;
seq = qd->send_seq;
- /* Encrypt to encrypted_data with session_key */
+ /* Encrypt to encrypted with session_key */
/* at most 16 bytes more */
- encrypted_data = g_newa(guint8, buf_len + 16);
- encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key);
+ encrypted = g_newa(guint8, buf_len + 16);
+ encrypted_len = qq_encrypt(encrypted, buf, buf_len, qd->session_key);
if (encrypted_len < 16) {
purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n",
encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd);
return -1;
}
- bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len);
+ bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted, encrypted_len);
#if 1
/* qq_show_packet("send_room_cmd", buf, buf_len); */
purple_debug_info("QQ",
@@ -1235,7 +1275,7 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len);
#endif
- qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted_data, encrypted_len,
+ qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, encrypted, encrypted_len,
update_class, ship32);
return bytes_sent;
}
@@ -1243,18 +1283,21 @@ static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id
gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
guint8 *data, gint data_len, gint update_class, guint32 ship32)
{
+ g_return_val_if_fail(room_cmd > 0, -1);
return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32);
}
gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id,
guint8 *data, gint data_len)
{
+ g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1);
return send_room_cmd(gc, room_cmd, room_id, data, data_len, 0, 0);
}
gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd,
guint8 *data, gint data_len)
{
+ g_return_val_if_fail(room_cmd > 0, -1);
return send_room_cmd(gc, room_cmd, 0, data, data_len, 0, 0);
}
diff --git a/libpurple/protocols/qq/qq_network.h b/libpurple/protocols/qq/qq_network.h
index e150f70639..0e2cd5e96a 100644
--- a/libpurple/protocols/qq/qq_network.h
+++ b/libpurple/protocols/qq/qq_network.h
@@ -30,7 +30,7 @@
#include "qq.h"
-#define QQ_CONNECT_STEPS 3 /* steps in connection */
+#define QQ_CONNECT_STEPS 4 /* steps in connection */
gboolean qq_connect_later(gpointer data);
void qq_disconnect(PurpleConnection *gc);
diff --git a/libpurple/protocols/qq/qq_process.c b/libpurple/protocols/qq/qq_process.c
index e2731fcb5a..f95a718862 100644
--- a/libpurple/protocols/qq/qq_process.c
+++ b/libpurple/protocols/qq/qq_process.c
@@ -30,27 +30,22 @@
#include "buddy_list.h"
#include "buddy_opt.h"
#include "group_info.h"
-#include "group_free.h"
#include "char_conv.h"
#include "qq_crypt.h"
-#include "group_conv.h"
-#include "group_find.h"
#include "group_internal.h"
#include "group_im.h"
#include "group_info.h"
#include "group_join.h"
#include "group_opt.h"
-#include "group_search.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "qq_base.h"
#include "im.h"
#include "qq_process.h"
#include "packet_parse.h"
#include "qq_network.h"
#include "qq_trans.h"
-#include "sys_msg.h"
#include "utils.h"
enum {
@@ -60,10 +55,10 @@ enum {
};
/* default process, decrypt and dump */
-static void process_cmd_unknow(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
+static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq)
{
qq_data *qd;
- gchar *msg_utf8 = NULL;
+ gchar *msg;
g_return_if_fail(data != NULL && data_len != 0);
@@ -76,11 +71,390 @@ static void process_cmd_unknow(PurpleConnection *gc,const gchar *title, guint8 *
">>> [%d] %s -> [default] decrypt and dump",
seq, qq_get_cmd_desc(cmd));
- msg_utf8 = try_dump_as_gbk(data, data_len);
- if (msg_utf8 != NULL) {
- purple_notify_info(gc, _("QQ Error"), title, msg_utf8);
- g_free(msg_utf8);
+ msg = g_strdup_printf("Unknow command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd));
+ purple_notify_info(gc, _("QQ Error"), title, msg);
+ g_free(msg);
+}
+
+/* parse the reply to send_im */
+static void do_im_ack(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+ qq_data *qd;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+
+ qd = gc->proto_data;
+
+ if (data[0] != 0) {
+ purple_debug_warning("QQ", "Failed sent IM\n");
+ purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL);
+ return;
+ }
+
+ purple_debug_info("QQ", "OK sent IM\n");
+}
+
+static void do_server_news(guint8 *data, gint data_len, PurpleConnection *gc)
+{
+ qq_data *qd = (qq_data *) gc->proto_data;
+ gint bytes;
+ gchar *title, *brief, *url;
+ gchar *content;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+
+ /* qq_show_packet("Rcv news", data, data_len); */
+
+ bytes = 4; /* skip unknown 4 bytes */
+
+ bytes += qq_get_vstr(&title, QQ_CHARSET_DEFAULT, data + bytes);
+ bytes += qq_get_vstr(&brief, QQ_CHARSET_DEFAULT, data + bytes);
+ bytes += qq_get_vstr(&url, QQ_CHARSET_DEFAULT, data + bytes);
+
+ content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url);
+
+ if (qd->is_show_news) {
+ qq_got_attention(gc, content);
+ } else {
+ purple_debug_info("QQ", "QQ Server news:\n%s\n", content);
}
+ g_free(title);
+ g_free(brief);
+ g_free(url);
+ g_free(content);
+}
+
+static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ gint len;
+ guint8 reply;
+ gchar **segments, *msg_utf8;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+
+ len = data_len;
+
+ if (NULL == (segments = split_data(data, len, "\x2f", 2)))
+ return;
+
+ reply = strtol(segments[0], NULL, 10);
+ if (reply == 1)
+ purple_debug_warning("QQ", "We are kicked out by QQ server\n");
+
+ msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
+ qq_got_attention(gc, msg_utf8);
+}
+
+static void do_msg_sys_4c(PurpleConnection *gc, guint8 *data, gint data_len)
+{
+ gint bytes;
+ gint msg_len;
+ GString *content;
+ gchar *msg = NULL;
+
+ g_return_if_fail(data != NULL && data_len > 0);
+
+ bytes = 6; /* skip 0x(06 00 01 1e 01 1c)*/
+
+ content = g_string_new("");
+ while (bytes < data_len) {
+ msg_len = qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data + bytes);
+ g_string_append(content, msg);
+ g_string_append(content, "\n");
+ g_free(msg);
+
+ if (msg_len <= 1) {
+ break;
+ }
+ bytes += msg_len;
+ }
+ if (bytes != data_len) {
+ purple_debug_warning("QQ", "Failed to read QQ_MSG_SYS_4C\n");
+ qq_show_packet("do_msg_sys_4c", data, data_len);
+ }
+ qq_got_attention(gc, content->str);
+ g_string_free(content, FALSE);
+}
+
+static const gchar *get_im_type_desc(gint type)
+{
+ switch (type) {
+ case QQ_MSG_TO_BUDDY:
+ return "QQ_MSG_TO_BUDDY";
+ case QQ_MSG_TO_UNKNOWN:
+ return "QQ_MSG_TO_UNKNOWN";
+ case QQ_MSG_UNKNOWN_QUN_IM:
+ return "QQ_MSG_UNKNOWN_QUN_IM";
+ case QQ_MSG_ADD_TO_QUN:
+ return "QQ_MSG_ADD_TO_QUN";
+ case QQ_MSG_DEL_FROM_QUN:
+ return "QQ_MSG_DEL_FROM_QUN";
+ case QQ_MSG_APPLY_ADD_TO_QUN:
+ return "QQ_MSG_APPLY_ADD_TO_QUN";
+ case QQ_MSG_CREATE_QUN:
+ return "QQ_MSG_CREATE_QUN";
+ case QQ_MSG_SYS_30:
+ return "QQ_MSG_SYS_30";
+ case QQ_MSG_SYS_4C:
+ return "QQ_MSG_SYS_4C";
+ case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN:
+ return "QQ_MSG_APPROVE_APPLY_ADD_TO_QUN";
+ case QQ_MSG_REJCT_APPLY_ADD_TO_QUN:
+ return "QQ_MSG_REJCT_APPLY_ADD_TO_QUN";
+ case QQ_MSG_TEMP_QUN_IM:
+ return "QQ_MSG_TEMP_QUN_IM";
+ case QQ_MSG_QUN_IM:
+ return "QQ_MSG_QUN_IM";
+ case QQ_MSG_NEWS:
+ return "QQ_MSG_NEWS";
+ case QQ_MSG_EXTEND:
+ return "QQ_MSG_EXTEND";
+ case QQ_MSG_EXTEND_85:
+ return "QQ_MSG_EXTEND_85";
+ default:
+ return "QQ_MSG_UNKNOWN";
+ }
+}
+
+/* I receive a message, mainly it is text msg,
+ * but we need to proess other types (group etc) */
+static void process_private_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
+{
+ qq_data *qd;
+ gint bytes;
+
+ struct {
+ guint32 uid_from;
+ guint32 uid_to;
+ guint32 seq;
+ struct in_addr ip_from;
+ guint16 port_from;
+ guint16 msg_type;
+ } header;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+
+ qd = (qq_data *) gc->proto_data;
+
+ if (data_len < 16) { /* we need to ack with the first 16 bytes */
+ purple_debug_error("QQ", "MSG is too short\n");
+ return;
+ } else {
+ /* when we receive a message,
+ * we send an ACK which is the first 16 bytes of incoming packet */
+ qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16);
+ }
+
+ /* check len first */
+ if (data_len < 20) { /* length of im_header */
+ purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len);
+ return;
+ }
+
+ bytes = 0;
+ bytes += qq_get32(&(header.uid_from), data + bytes);
+ bytes += qq_get32(&(header.uid_to), data + bytes);
+ bytes += qq_get32(&(header.seq), data + bytes);
+ /* if the message is delivered via server, it is server IP/port */
+ bytes += qq_getIP(&(header.ip_from), data + bytes);
+ bytes += qq_get16(&(header.port_from), data + bytes);
+ bytes += qq_get16(&(header.msg_type), data + bytes);
+ /* im_header prepared */
+
+ if (header.uid_to != qd->uid) { /* should not happen */
+ purple_debug_error("QQ", "MSG to [%d], NOT me\n", header.uid_to);
+ return;
+ }
+
+ /* check bytes */
+ if (bytes >= data_len - 1) {
+ purple_debug_warning("QQ", "Empty MSG\n");
+ return;
+ }
+
+ switch (header.msg_type) {
+ case QQ_MSG_NEWS:
+ do_server_news(data + bytes, data_len - bytes, gc);
+ break;
+ case QQ_MSG_EXTEND:
+ case QQ_MSG_EXTEND_85:
+ purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
+ qq_process_extend_im(gc, data + bytes, data_len - bytes);
+ break;
+ case QQ_MSG_TO_UNKNOWN:
+ case QQ_MSG_TO_BUDDY:
+ purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from);
+ qq_process_im(gc, data + bytes, data_len - bytes);
+ break;
+ case QQ_MSG_UNKNOWN_QUN_IM:
+ case QQ_MSG_TEMP_QUN_IM:
+ case QQ_MSG_QUN_IM:
+ purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from);
+ qq_process_room_im(data + bytes, data_len - bytes, header.uid_from, gc, header.msg_type);
+ break;
+ case QQ_MSG_ADD_TO_QUN:
+ purple_debug_info("QQ", "Notice from [%d], Added\n", header.uid_from);
+ /* uid_from is group id
+ * we need this to create a dummy group and add to blist */
+ qq_process_room_buddy_joined(data + bytes, data_len - bytes, header.uid_from, gc);
+ break;
+ case QQ_MSG_DEL_FROM_QUN:
+ purple_debug_info("QQ", "Notice from room [%d], Removed\n", header.uid_from);
+ /* uid_from is group id */
+ qq_process_room_buddy_removed(data + bytes, data_len - bytes, header.uid_from, gc);
+ break;
+ case QQ_MSG_APPLY_ADD_TO_QUN:
+ purple_debug_info("QQ", "Notice from room [%d], Joined\n", header.uid_from);
+ /* uid_from is group id */
+ qq_process_room_buddy_request_join(data + bytes, data_len - bytes, header.uid_from, gc);
+ break;
+ case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN:
+ purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n",
+ header.uid_from);
+ /* uid_from is group id */
+ qq_process_room_buddy_approved(data + bytes, data_len - bytes, header.uid_from, gc);
+ break;
+ case QQ_MSG_REJCT_APPLY_ADD_TO_QUN:
+ purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n",
+ header.uid_from);
+ /* uid_from is group id */
+ qq_process_room_buddy_rejected(data + bytes, data_len - bytes, header.uid_from, gc);
+ break;
+ case QQ_MSG_SYS_30:
+ do_msg_sys_30(gc, data + bytes, data_len - bytes);
+ break;
+ case QQ_MSG_SYS_4C:
+ do_msg_sys_4c(gc, data + bytes, data_len - bytes);
+ break;
+ default:
+ purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%04X]\n",
+ header.uid_from, get_im_type_desc(header.msg_type), header.msg_type);
+ qq_show_packet("Unknown MSG type", data, data_len);
+ break;
+ }
+}
+
+/* Send ACK if the sys message needs an ACK */
+static void request_server_ack(PurpleConnection *gc, gchar *funct_str, gchar *from, guint16 seq)
+{
+ qq_data *qd;
+ guint8 *raw_data;
+ gint bytes;
+ guint8 bar;
+
+ g_return_if_fail(funct_str != NULL && from != NULL);
+ qd = (qq_data *) gc->proto_data;
+
+
+ bar = 0x1e;
+ raw_data = g_newa(guint8, strlen(funct_str) + strlen(from) + 16);
+
+ bytes = 0;
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)funct_str, strlen(funct_str));
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_putdata(raw_data + bytes, (guint8 *)from, strlen(from));
+ bytes += qq_put8(raw_data + bytes, bar);
+ bytes += qq_put16(raw_data + bytes, seq);
+
+ qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, raw_data, bytes);
+}
+
+static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to,
+ guint8 *data, gint data_len)
+{
+ qq_data *qd = (qq_data *) gc->proto_data;
+ gchar *msg, *msg_utf8;
+ gchar *title, *content;
+
+ g_return_if_fail(from != NULL && to != NULL && data_len > 0);
+
+ msg = g_strndup((gchar *)data, data_len);
+ msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
+ g_free(msg);
+ if (msg_utf8 == NULL) {
+ purple_debug_error("QQ", "Recv NULL sys msg from %s to %s, discard\n",
+ from, to);
+ return;
+ }
+
+ title = g_strdup_printf(_("From %s:"), from);
+ content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8);
+
+ if (qd->is_show_notice) {
+ qq_got_attention(gc, content);
+ } else {
+ purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
+ }
+ g_free(msg_utf8);
+ g_free(title);
+ g_free(content);
+}
+
+static void process_server_msg(PurpleConnection *gc, guint8 *data, gint data_len, guint16 seq)
+{
+ qq_data *qd;
+ guint8 *data_str;
+ gchar **segments;
+ gchar *funct_str, *from, *to;
+ gint bytes, funct;
+
+ g_return_if_fail(data != NULL && data_len != 0);
+
+ qd = (qq_data *) gc->proto_data;
+
+ data_str = g_newa(guint8, data_len + 1);
+ g_memmove(data_str, data, data_len);
+ data_str[data_len] = 0x00;
+
+ segments = g_strsplit_set((gchar *) data_str, "\x1f", 0);
+ g_return_if_fail(segments != NULL);
+ if (g_strv_length(segments) < 3) {
+ purple_debug_warning("QQ", "Server message segments is less than 3\n");
+ g_strfreev(segments);
+ return;
+ }
+
+ bytes = 0;
+ funct_str = segments[0];
+ bytes += strlen(funct_str) + 1;
+ from = segments[1];
+ bytes += strlen(from) + 1;
+ to = segments[2];
+ bytes += strlen(to) + 1;
+
+ request_server_ack(gc, funct_str, from, seq);
+
+ /* qq_show_packet("Server MSG", data, data_len); */
+ if (strtol(to, NULL, 10) != qd->uid) { /* not to me */
+ purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
+ g_strfreev(segments);
+ return;
+ }
+
+ funct = strtol(funct_str, NULL, 10);
+ switch (funct) {
+ case QQ_SERVER_BUDDY_ADDED:
+ case QQ_SERVER_BUDDY_ADD_REQUEST:
+ case QQ_SERVER_BUDDY_ADDED_ME:
+ case QQ_SERVER_BUDDY_REJECTED_ME:
+ case QQ_SERVER_BUDDY_ADD_REQUEST_EX:
+ case QQ_SERVER_BUDDY_ADDING_EX:
+ case QQ_SERVER_BUDDY_ADDED_ANSWER:
+ case QQ_SERVER_BUDDY_ADDED_EX:
+ qq_process_buddy_from_server(gc, funct, from, to, data + bytes, data_len - bytes);
+ break;
+ case QQ_SERVER_NOTICE:
+ do_server_notice(gc, from, to, data + bytes, data_len - bytes);
+ break;
+ case QQ_SERVER_NEW_CLIENT:
+ purple_debug_warning("QQ", "QQ Server has newer client version\n");
+ break;
+ default:
+ qq_show_packet("Unknown sys msg", data, data_len);
+ purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", funct_str);
+ break;
+ }
+ g_strfreev(segments);
}
void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len)
@@ -113,16 +487,16 @@ void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *
/* now process the packet */
switch (cmd) {
case QQ_CMD_RECV_IM:
- qq_process_recv_im(data, data_len, seq, gc);
+ process_private_msg(data, data_len, seq, gc);
break;
case QQ_CMD_RECV_MSG_SYS:
- qq_process_msg_sys(data, data_len, seq, gc);
+ process_server_msg(gc, data, data_len, seq);
break;
case QQ_CMD_BUDDY_CHANGE_STATUS:
qq_process_buddy_change_status(data, data_len, gc);
break;
default:
- process_cmd_unknow(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq);
+ process_unknow_cmd(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq);
break;
}
}
@@ -150,36 +524,25 @@ static void process_room_cmd_notify(PurpleConnection *gc,
void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
{
qq_data *qd;
- qq_group *group;
gint ret;
g_return_if_fail (gc != NULL && gc->proto_data != NULL);
qd = (qq_data *) gc->proto_data;
- group = qq_room_search_id(gc, room_id);
- if (group == NULL && room_id <= 0) {
- purple_debug_info("QQ", "No room, nothing update\n");
- return;
- }
- if (group == NULL ) {
- purple_debug_warning("QQ", "Failed search room id [%d]\n", room_id);
- return;
- }
-
switch (room_cmd) {
case 0:
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ROOM, 0);
break;
case QQ_ROOM_CMD_GET_INFO:
- ret = qq_request_room_get_buddies(gc, group, QQ_CMD_CLASS_UPDATE_ROOM);
+ ret = qq_request_room_get_buddies(gc, room_id, QQ_CMD_CLASS_UPDATE_ROOM);
if (ret <= 0) {
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ROOM, 0);
}
break;
case QQ_ROOM_CMD_GET_BUDDIES:
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ROOM, 0);
break;
case QQ_ROOM_CMD_GET_ONLINES:
@@ -189,43 +552,46 @@ void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
}
}
-static void update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
+void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
{
qq_data *qd;
gboolean is_new_turn = FALSE;
- qq_group *next_group;
+ guint32 next_id;
g_return_if_fail (gc != NULL && gc->proto_data != NULL);
qd = (qq_data *) gc->proto_data;
- next_group = qq_room_get_next(gc, room_id);
- if (next_group == NULL && room_id <= 0) {
- purple_debug_info("QQ", "No room. Finished update\n");
- return;
- }
- if (next_group == NULL ) {
- is_new_turn = TRUE;
- next_group = qq_room_get_next(gc, 0);
- g_return_if_fail(next_group != NULL);
+ next_id = qq_room_get_next(gc, room_id);
+ purple_debug_info("QQ", "Update rooms, next id %d, prev id %d\n", next_id, room_id);
+
+ if (next_id <= 0) {
+ if (room_id > 0) {
+ is_new_turn = TRUE;
+ next_id = qq_room_get_next(gc, 0);
+ purple_debug_info("QQ", "new turn, id %d\n", next_id);
+ } else {
+ purple_debug_info("QQ", "No room. Finished update\n");
+ return;
+ }
}
switch (room_cmd) {
case 0:
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ALL, 0);
break;
case QQ_ROOM_CMD_GET_INFO:
if (!is_new_turn) {
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ALL, 0);
} else {
- qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL);
+ qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL);
}
break;
case QQ_ROOM_CMD_GET_BUDDIES:
/* last command */
if (!is_new_turn) {
- qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL);
+ qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL);
} else {
purple_debug_info("QQ", "Finished update\n");
}
@@ -244,57 +610,63 @@ void qq_update_all(PurpleConnection *gc, guint16 cmd)
switch (cmd) {
case 0:
- qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, QQ_BUDDY_INFO_UPDATE_ONLY);
+ qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, 0);
break;
case QQ_CMD_GET_BUDDY_INFO:
qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL);
break;
case QQ_CMD_CHANGE_STATUS:
- qq_request_get_buddies_list(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
+ qq_request_get_buddies(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
break;
case QQ_CMD_GET_BUDDIES_LIST:
qq_request_get_buddies_and_rooms(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
break;
case QQ_CMD_GET_BUDDIES_AND_ROOMS:
- qq_request_get_buddies_level(gc, QQ_CMD_CLASS_UPDATE_ALL);
+ if (qd->client_version >= 2007) {
+ /* QQ2007/2008 can not get buddies level*/
+ qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
+ } else {
+ qq_request_get_buddies_level(gc, QQ_CMD_CLASS_UPDATE_ALL);
+ }
break;
case QQ_CMD_GET_LEVEL:
qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL);
break;
case QQ_CMD_GET_BUDDIES_ONLINE:
/* last command */
- update_all_rooms(gc, 0, 0);
+ qq_update_all_rooms(gc, 0, 0);
break;
default:
break;
}
+ qd->online_last_update = time(NULL);
}
static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id)
{
qq_data *qd;
- qq_group *next_group;
+ guint32 next_id;
g_return_if_fail (gc != NULL && gc->proto_data != NULL);
qd = (qq_data *) gc->proto_data;
- next_group = qq_room_get_next_conv(gc, room_id);
- if (next_group == NULL && room_id <= 0) {
+ next_id = qq_room_get_next_conv(gc, room_id);
+ if (next_id <= 0 && room_id <= 0) {
purple_debug_info("QQ", "No room in conversation, no update online buddies\n");
return;
}
- if (next_group == NULL ) {
+ if (next_id <= 0 ) {
purple_debug_info("QQ", "finished update rooms' online buddies\n");
return;
}
switch (room_cmd) {
case 0:
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ALL, 0);
break;
case QQ_ROOM_CMD_GET_ONLINES:
- qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0,
+ qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0,
QQ_CMD_CLASS_UPDATE_ALL, 0);
break;
default:
@@ -304,6 +676,10 @@ static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint
void qq_update_online(PurpleConnection *gc, guint16 cmd)
{
+ qq_data *qd;
+ g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+ qd = (qq_data *) gc->proto_data;
+
switch (cmd) {
case 0:
qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE);
@@ -315,16 +691,17 @@ void qq_update_online(PurpleConnection *gc, guint16 cmd)
default:
break;
}
+ qd->online_last_update = time(NULL);
}
-void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
+void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
gint update_class, guint32 ship32)
{
qq_data *qd;
guint8 *data;
gint data_len;
- qq_group *group;
+ qq_room_data *rmd;
gint bytes;
guint8 reply_cmd, reply;
@@ -355,13 +732,6 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
return;
}
- group = qq_room_search_id(gc, room_id);
- if (group == NULL) {
- purple_debug_warning("QQ",
- "Missing room id in [%05d], 0x%02X %s for %d, len %d\n",
- seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
- }
-
bytes = 0;
bytes += qq_get8(&reply_cmd, data + bytes);
bytes += qq_get8(&reply, data + bytes);
@@ -375,17 +745,17 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
/* now process the packet */
if (reply != QQ_ROOM_CMD_REPLY_OK) {
- if (group != NULL) {
- qq_set_pending_id(&qd->joining_groups, group->ext_id, FALSE);
- }
-
switch (reply) { /* this should be all errors */
case QQ_ROOM_CMD_REPLY_NOT_MEMBER:
- if (group != NULL) {
+ rmd = qq_room_data_find(gc, room_id);
+ if (rmd == NULL) {
+ purple_debug_warning("QQ",
+ "Missing room id in [%05d], 0x%02X %s for %d, len %d\n",
+ seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len);
+ } else {
purple_debug_warning("QQ",
- _("You are not a member of QQ Qun \"%s\"\n"), group->title_utf8);
- group->my_role = QQ_ROOM_ROLE_NO;
- qq_group_refresh(gc, group);
+ _("Not a member of room \"%s\"\n"), rmd->title_utf8);
+ rmd->my_role = QQ_ROOM_ROLE_NO;
}
break;
case QQ_ROOM_CMD_REPLY_SEARCH_ERROR:
@@ -402,7 +772,7 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
/* seems ok so far, so we process the reply according to sub_cmd */
switch (reply_cmd) {
case QQ_ROOM_CMD_GET_INFO:
- qq_process_room_cmd_get_info(data + bytes, data_len - bytes, gc);
+ qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship32, gc);
break;
case QQ_ROOM_CMD_CREATE:
qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc);
@@ -417,7 +787,7 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc);
break;
case QQ_ROOM_CMD_SEARCH:
- qq_process_group_cmd_search_group(data + bytes, data_len - bytes, gc);
+ qq_process_room_search(gc, data + bytes, data_len - bytes, ship32);
break;
case QQ_ROOM_CMD_JOIN:
qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc);
@@ -429,19 +799,13 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc);
break;
case QQ_ROOM_CMD_SEND_MSG:
- qq_process_group_cmd_im(data + bytes, data_len - bytes, gc);
+ qq_process_room_send_im(gc, data + bytes, data_len - bytes);
break;
case QQ_ROOM_CMD_GET_ONLINES:
qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc);
- if (group != NULL)
- qq_group_conv_refresh_online_member(gc, group);
break;
case QQ_ROOM_CMD_GET_BUDDIES:
qq_process_room_cmd_get_buddies(data + bytes, data_len - bytes, gc);
- if (group != NULL) {
- group->is_got_info = TRUE;
- qq_group_conv_refresh_online_member(gc, group);
- }
break;
default:
purple_debug_warning("QQ", "Unknow room cmd 0x%02X %s\n",
@@ -451,9 +815,8 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
if (update_class == QQ_CMD_CLASS_NONE)
return;
- purple_debug_info("QQ", "Update class %d\n", update_class);
if (update_class == QQ_CMD_CLASS_UPDATE_ALL) {
- update_all_rooms(gc, room_cmd, room_id);
+ qq_update_all_rooms(gc, room_cmd, room_id);
return;
}
if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) {
@@ -465,58 +828,159 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
}
}
-void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len)
+guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
+ guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
{
qq_data *qd;
- guint8 *data;
- gint data_len;
- guint ret_8;
+ guint8 *data = NULL;
+ gint data_len = 0;
+ guint ret_8 = QQ_LOGIN_REPLY_ERR;
- g_return_if_fail (gc != NULL && gc->proto_data != NULL);
+ g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
qd = (qq_data *) gc->proto_data;
+ g_return_val_if_fail(rcved_len > 0, QQ_LOGIN_REPLY_ERR);
data = g_newa(guint8, rcved_len);
- /* May use password_twice_md5 in the past version like QQ2005*/
- data_len = qq_decrypt(data, rcved, rcved_len, qd->inikey);
- if (data_len >= 0) {
- purple_debug_warning("QQ",
- "Decrypt login reply packet with inikey, %d bytes\n", data_len);
- } else {
- data_len = qq_decrypt(data, rcved, rcved_len, qd->password_twice_md5);
- if (data_len >= 0) {
- purple_debug_warning("QQ",
- "Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len);
- } else {
- purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
- _("Can not decrypt login reply"));
- return;
- }
- }
- ret_8 = qq_process_login_reply(gc, data, data_len);
- if (ret_8 != QQ_LOGIN_REPLY_OK) {
- return;
+ switch (cmd) {
+ case QQ_CMD_TOKEN:
+ if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) {
+ if (qd->client_version >= 2007) {
+ qq_request_token_ex(gc);
+ } else {
+ qq_request_login(gc);
+ }
+ return QQ_LOGIN_REPLY_OK;
+ }
+ return QQ_LOGIN_REPLY_ERR;
+ case QQ_CMD_GET_SERVER:
+ case QQ_CMD_TOKEN_EX:
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
+ break;
+ case QQ_CMD_CHECK_PWD:
+ /* May use password_twice_md5 in the past version like QQ2005 */
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
+ if (data_len >= 0) {
+ purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len);
+ } else {
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
+ if (data_len >= 0) {
+ purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5, %d bytes\n", data_len);
+ }
+ }
+ break;
+ case QQ_CMD_LOGIN:
+ default:
+ if (qd->client_version >= 2007) {
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
+ if (data_len >= 0) {
+ purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
+ } else {
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key);
+ if (data_len >= 0) {
+ purple_debug_warning("QQ", "Decrypt login packet by login_key\n");
+ }
+ }
+ } else {
+ /* May use password_twice_md5 in the past version like QQ2005 */
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key);
+ if (data_len >= 0) {
+ purple_debug_warning("QQ", "Decrypt login packet by random_key\n");
+ } else {
+ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5);
+ if (data_len >= 0) {
+ purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n");
+ }
+ }
+ }
+ break;
}
- purple_debug_info("QQ", "Login repliess OK; everything is fine\n");
+ if (data_len < 0) {
+ purple_debug_warning("QQ",
+ "Can not decrypt login cmd, [%05d], 0x%04X %s, len %d\n",
+ seq, cmd, qq_get_cmd_desc(cmd), rcved_len);
+ qq_show_packet("Can not decrypted", rcved, rcved_len);
+ purple_connection_error_reason(gc,
+ PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
+ _("Can not decrypt login reply"));
+ return QQ_LOGIN_REPLY_ERR;
+ }
- purple_connection_set_state(gc, PURPLE_CONNECTED);
- qd->is_login = TRUE; /* must be defined after sev_finish_login */
+ switch (cmd) {
+ case QQ_CMD_GET_SERVER:
+ ret_8 = qq_process_get_server(gc, data, data_len);
+ if ( ret_8 == QQ_LOGIN_REPLY_OK) {
+ qq_request_token(gc);
+ } else if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+ return QQ_LOGIN_REPLY_REDIRECT;
+ }
+ break;
+ case QQ_CMD_TOKEN_EX:
+ ret_8 = qq_process_token_ex(gc, data, data_len);
+ if (ret_8 == QQ_LOGIN_REPLY_OK) {
+ qq_request_check_pwd(gc);
+ } else if (ret_8 == QQ_LOGIN_REPLY_NEXT_TOKEN_EX) {
+ qq_request_token_ex_next(gc);
+ } else if (ret_8 == QQ_LOGIN_REPLY_CAPTCHA_DLG) {
+ qq_captcha_input_dialog(gc, &(qd->captcha));
+ g_free(qd->captcha.token);
+ g_free(qd->captcha.data);
+ memset(&qd->captcha, 0, sizeof(qd->captcha));
+ }
+ break;
+ case QQ_CMD_CHECK_PWD:
+ ret_8 = qq_process_check_pwd(gc, data, data_len);
+ if (ret_8 != QQ_LOGIN_REPLY_OK) {
+ return ret_8;
+ }
+ if (qd->client_version == 2008) {
+ qq_request_login_2008(gc);
+ } else {
+ qq_request_login_2007(gc);
+ }
+ break;
+ case QQ_CMD_LOGIN:
+ if (qd->client_version == 2008) {
+ ret_8 = qq_process_login_2008(gc, data, data_len);
+ if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+ qq_request_get_server(gc);
+ return QQ_LOGIN_REPLY_OK;
+ }
+ } else if (qd->client_version == 2007) {
+ ret_8 = qq_process_login_2007(gc, data, data_len);
+ if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) {
+ qq_request_get_server(gc);
+ return QQ_LOGIN_REPLY_OK;
+ }
+ } else {
+ ret_8 = qq_process_login(gc, data, data_len);
+ }
+ if (ret_8 != QQ_LOGIN_REPLY_OK) {
+ return ret_8;
+ }
- /* now initiate QQ Qun, do it first as it may take longer to finish */
- qq_group_init(gc);
+ purple_connection_update_progress(gc, _("Logined"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
+ purple_debug_info("QQ", "Login repliess OK; everything is fine\n");
+ purple_connection_set_state(gc, PURPLE_CONNECTED);
+ qd->is_login = TRUE; /* must be defined after sev_finish_login */
- /* Now goes on updating my icon/nickname, not showing info_window */
- qd->modifying_face = FALSE;
+ /* now initiate QQ Qun, do it first as it may take longer to finish */
+ qq_room_data_initial(gc);
- /* is_login, but we have packets before login */
- qq_trans_process_remained(gc);
+ /* is_login, but we have packets before login */
+ qq_trans_process_remained(gc);
- qq_update_all(gc, 0);
- return;
+ qq_update_all(gc, 0);
+ break;
+ default:
+ process_unknow_cmd(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq);
+ return QQ_LOGIN_REPLY_ERR;
+ }
+ return QQ_LOGIN_REPLY_OK;
}
-void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
+void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32)
{
qq_data *qd;
@@ -553,50 +1017,56 @@ void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
switch (cmd) {
case QQ_CMD_UPDATE_INFO:
- qq_process_modify_info_reply(data, data_len, gc);
+ qq_process_change_info(gc, data, data_len);
break;
- case QQ_CMD_ADD_BUDDY_WO_AUTH:
- qq_process_add_buddy_reply(data, data_len, seq, gc);
+ case QQ_CMD_ADD_BUDDY_NO_AUTH:
+ qq_process_add_buddy_no_auth(gc, data, data_len, ship32);
break;
- case QQ_CMD_DEL_BUDDY:
- qq_process_remove_buddy_reply(data, data_len, gc);
+ case QQ_CMD_REMOVE_BUDDY:
+ qq_process_remove_buddy(gc, data, data_len, ship32);
break;
- case QQ_CMD_REMOVE_SELF:
- qq_process_remove_self_reply(data, data_len, gc);
+ case QQ_CMD_REMOVE_ME:
+ qq_process_buddy_remove_me(gc, data, data_len, ship32);
break;
- case QQ_CMD_BUDDY_AUTH:
- qq_process_add_buddy_auth_reply(data, data_len, gc);
+ case QQ_CMD_ADD_BUDDY_AUTH:
+ qq_process_add_buddy_auth(data, data_len, gc);
break;
case QQ_CMD_GET_BUDDY_INFO:
- qq_process_get_buddy_info(data, data_len, gc);
+ qq_process_get_buddy_info(data, data_len, ship32, gc);
break;
case QQ_CMD_CHANGE_STATUS:
- qq_process_change_status_reply(data, data_len, gc);
+ qq_process_change_status(data, data_len, gc);
break;
case QQ_CMD_SEND_IM:
- qq_process_send_im_reply(data, data_len, gc);
+ do_im_ack(data, data_len, gc);
break;
case QQ_CMD_KEEP_ALIVE:
- qq_process_keep_alive(data, data_len, gc);
+ if (qd->client_version >= 2008) {
+ qq_process_keep_alive_2008(data, data_len, gc);
+ } else if (qd->client_version >= 2007) {
+ qq_process_keep_alive_2007(data, data_len, gc);
+ } else {
+ qq_process_keep_alive(data, data_len, gc);
+ }
break;
case QQ_CMD_GET_BUDDIES_ONLINE:
- ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc);
+ ret_8 = qq_process_get_buddies_online(data, data_len, gc);
if (ret_8 > 0 && ret_8 < 0xff) {
purple_debug_info("QQ", "Requesting for more online buddies\n");
qq_request_get_buddies_online(gc, ret_8, update_class);
return;
}
purple_debug_info("QQ", "All online buddies received\n");
- qq_refresh_all_buddy_status(gc);
+ qq_update_buddyies_status(gc);
break;
case QQ_CMD_GET_LEVEL:
qq_process_get_level_reply(data, data_len, gc);
break;
case QQ_CMD_GET_BUDDIES_LIST:
- ret_16 = qq_process_get_buddies_list_reply(data, data_len, gc);
+ ret_16 = qq_process_get_buddies(data, data_len, gc);
if (ret_16 > 0 && ret_16 < 0xffff) {
purple_debug_info("QQ", "Requesting for more buddies\n");
- qq_request_get_buddies_list(gc, ret_16, update_class);
+ qq_request_get_buddies(gc, ret_16, update_class);
return;
}
purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n");
@@ -610,8 +1080,23 @@ void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
}
purple_debug_info("QQ", "All buddies and groups received\n");
break;
+ case QQ_CMD_AUTH_CODE:
+ qq_process_auth_code(gc, data, data_len, ship32);
+ break;
+ case QQ_CMD_BUDDY_QUESTION:
+ qq_process_question(gc, data, data_len, ship32);
+ break;
+ case QQ_CMD_ADD_BUDDY_NO_AUTH_EX:
+ qq_process_add_buddy_no_auth_ex(gc, data, data_len, ship32);
+ break;
+ case QQ_CMD_ADD_BUDDY_AUTH_EX:
+ qq_process_add_buddy_auth_ex(gc, data, data_len, ship32);
+ break;
+ case QQ_CMD_BUDDY_CHECK_CODE:
+ qq_process_buddy_check_code(gc, data, data_len);
+ break;
default:
- process_cmd_unknow(gc, _("Unknow reply CMD"), data, data_len, cmd, seq);
+ process_unknow_cmd(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq);
is_unknow = TRUE;
break;
}
diff --git a/libpurple/protocols/qq/qq_process.h b/libpurple/protocols/qq/qq_process.h
index e529100797..c9d2b130b1 100644
--- a/libpurple/protocols/qq/qq_process.h
+++ b/libpurple/protocols/qq/qq_process.h
@@ -34,13 +34,15 @@ enum {
QQ_CMD_CLASS_NONE = 0,
QQ_CMD_CLASS_UPDATE_ALL,
QQ_CMD_CLASS_UPDATE_ONLINE,
+ QQ_CMD_CLASS_UPDATE_BUDDY,
QQ_CMD_CLASS_UPDATE_ROOM,
};
-void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len);
-void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq,
+guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
-void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq,
+void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq,
+ guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32);
+void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq,
guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len,
gint update_class, guint32 ship32);
@@ -49,5 +51,6 @@ void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *
void qq_update_all(PurpleConnection *gc, guint16 cmd);
void qq_update_online(PurpleConnection *gc, guint16 cmd);
void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
+void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id);
#endif
diff --git a/libpurple/protocols/qq/qq_trans.c b/libpurple/protocols/qq/qq_trans.c
index 3f507c6219..d95d7898ed 100644
--- a/libpurple/protocols/qq/qq_trans.c
+++ b/libpurple/protocols/qq/qq_trans.c
@@ -30,7 +30,7 @@
#include "prefs.h"
#include "request.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "qq_network.h"
#include "qq_process.h"
#include "qq_trans.h"
@@ -131,6 +131,7 @@ static qq_transaction *trans_create(PurpleConnection *gc, gint fd,
}
trans->update_class = update_class;
+ trans->ship32 = ship32;
return trans;
}
diff --git a/libpurple/protocols/qq/send_file.c b/libpurple/protocols/qq/send_file.c
index d7a6b45164..a9df3b95c5 100644
--- a/libpurple/protocols/qq/send_file.c
+++ b/libpurple/protocols/qq/send_file.c
@@ -31,7 +31,7 @@
#include "buddy_list.h"
#include "file_trans.h"
-#include "header_info.h"
+#include "qq_define.h"
#include "im.h"
#include "qq_base.h"
#include "packet_parse.h"
@@ -54,8 +54,8 @@ enum
static int _qq_in_same_lan(ft_info *info)
{
if (info->remote_internet_ip == info->local_internet_ip) return 1;
- purple_debug_info("QQ",
- "Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n",
+ purple_debug_info("QQ",
+ "Not in the same LAN, remote internet ip[%x], local internet ip[%x]\n",
info->remote_internet_ip
, info->local_internet_ip);
return 0;
@@ -87,7 +87,7 @@ static ssize_t _qq_xfer_udp_recv(guint8 *buf, size_t len, PurpleXfer *xfer)
info = (ft_info *) xfer->data;
sinlen = sizeof(sin);
r = recvfrom(info->recv_fd, buf, len, 0, (struct sockaddr *) &sin, &sinlen);
- purple_debug_info("QQ",
+ purple_debug_info("QQ",
"==> recv %d bytes from File UDP Channel, remote ip[%s], remote port[%d]\n",
r, inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port));
return r;
@@ -121,11 +121,8 @@ static ssize_t _qq_xfer_udp_send(const guint8 *buf, size_t len, PurpleXfer *xfer
sin.sin_port = g_htons(info->remote_minor_port);
sin.sin_addr.s_addr = g_htonl(info->remote_real_ip);
}
- purple_debug_info("QQ", "sending to channel: %d.%d.%d.%d:%d\n",
- (int)sin.sin_addr.s_addr & 0xff,
- (int)(sin.sin_addr.s_addr >> 8) & 0xff,
- (int)(sin.sin_addr.s_addr >> 16) & 0xff,
- (int)sin.sin_addr.s_addr >> 24,
+ purple_debug_info("QQ", "sending to channel: %s:%d\n",
+ inet_ntoa(sin.sin_addr),
(int)g_ntohs(sin.sin_port)
);
return sendto(info->sender_fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(sin));
@@ -301,7 +298,7 @@ static gint _qq_create_packet_file_header
/* 004-007: sender uid */
bytes += qq_put32 (raw_data + bytes, to_uid);
/* 008-009: sender client version */
- bytes += qq_put16 (raw_data + bytes, QQ_CLIENT);
+ bytes += qq_put16 (raw_data + bytes, qd->client_tag);
/* 010-013: receiver uid */
bytes += qq_put32 (raw_data + bytes, qd->uid);
/* 014-017: sender uid */
@@ -380,7 +377,7 @@ in_addr_t get_real_ip()
static void _qq_xfer_init_socket(PurpleXfer *xfer)
{
- gint sockfd, listen_port = 0, i;
+ gint sockfd, listen_port = 0, i;
socklen_t sin_len;
struct sockaddr_in sin;
ft_info *info;
@@ -389,7 +386,7 @@ static void _qq_xfer_init_socket(PurpleXfer *xfer)
g_return_if_fail(xfer->data != NULL);
info = (ft_info *) xfer->data;
- /* debug
+ /* debug
info->local_real_ip = 0x7f000001;
*/
info->local_real_ip = g_ntohl(inet_addr(purple_network_get_my_ip(-1)));
@@ -460,7 +457,7 @@ static void _qq_send_packet_file_request (PurpleConnection *gc, guint32 to_uid,
raw_data = g_newa(guint8, packet_len);
bytes = 0;
- bytes += _qq_create_packet_file_header(raw_data + bytes, 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 + bytes, info);
/* 079: 0x20 */
@@ -682,7 +679,7 @@ static void _qq_xfer_recv_init(PurpleXfer *xfer)
}
/* process reject im for file transfer request */
-void qq_process_recv_file_reject (guint8 *data, gint data_len,
+void qq_process_recv_file_reject (guint8 *data, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gchar *msg, *filename;
@@ -711,7 +708,7 @@ void qq_process_recv_file_reject (guint8 *data, gint data_len,
}
/* process cancel im for file transfer request */
-void qq_process_recv_file_cancel (guint8 *data, gint data_len,
+void qq_process_recv_file_cancel (guint8 *data, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gchar *msg, *filename;
@@ -774,7 +771,7 @@ void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_ui
gchar *sender_name, **fileinfo;
ft_info *info;
PurpleBuddy *b;
- qq_buddy *q_bud;
+ qq_buddy_data *bd;
gint bytes;
g_return_if_fail (data != NULL && data_len != 0);
@@ -785,7 +782,7 @@ void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_ui
info->local_internet_port = qd->my_port;
info->local_real_ip = 0x00000000;
info->to_uid = sender_uid;
-
+
if (data_len <= 2 + 30 + QQ_CONN_INFO_LEN) {
purple_debug_warning("QQ", "Received file request message is empty\n");
return;
@@ -804,36 +801,37 @@ void qq_process_recv_file_request(guint8 *data, gint data_len, guint32 sender_ui
/* FACE from IP detector, ignored by gfhuang */
if(g_ascii_strcasecmp(fileinfo[0], "FACE") == 0) {
purple_debug_warning("QQ",
- "Received a FACE ip detect from qq-%d, so he/she must be online :)\n", sender_uid);
+ "Received a FACE ip detect from %d, so he/she must be online :)\n", sender_uid);
b = purple_find_buddy(gc->account, sender_name);
- q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data;
- if (q_bud) {
+ bd = (b == NULL) ? NULL : (qq_buddy_data *) b->proto_data;
+ if (bd) {
if(0 != info->remote_real_ip) {
- g_memmove(&(q_bud->ip), &info->remote_real_ip, sizeof(q_bud->ip));
- q_bud->port = info->remote_minor_port;
+ g_memmove(&(bd->ip), &info->remote_real_ip, sizeof(bd->ip));
+ bd->port = info->remote_minor_port;
}
else if (0 != info->remote_internet_ip) {
- g_memmove(&(q_bud->ip), &info->remote_internet_ip, sizeof(q_bud->ip));
- q_bud->port = info->remote_major_port;
+ g_memmove(&(bd->ip), &info->remote_internet_ip, sizeof(bd->ip));
+ bd->port = info->remote_major_port;
}
- if(!is_online(q_bud->status)) {
- q_bud->status = QQ_BUDDY_ONLINE_INVISIBLE;
- qq_update_buddy_contact(gc, q_bud);
+ if(!is_online(bd->status)) {
+ bd->status = QQ_BUDDY_ONLINE_INVISIBLE;
+ bd->last_update = time(NULL);
+ qq_update_buddy_status(gc, bd->uid, bd->status, bd->comm_flag);
}
- else
+ else
purple_debug_info("QQ", "buddy %d is already online\n", sender_uid);
}
- else
+ else
purple_debug_warning("QQ", "buddy %d is not in list\n", sender_uid);
- g_free(sender_name);
+ g_free(sender_name);
g_strfreev(fileinfo);
return;
}
-
+
xfer = purple_xfer_new(purple_connection_get_account(gc),
PURPLE_XFER_RECEIVE,
sender_name);
@@ -875,7 +873,7 @@ static void _qq_xfer_send_notify_ip_ack(gpointer data, gint source, PurpleInputC
*/
}
-void qq_process_recv_file_notify(guint8 *data, gint data_len,
+void qq_process_recv_file_notify(guint8 *data, gint data_len,
guint32 sender_uid, PurpleConnection *gc)
{
gint bytes;
@@ -892,7 +890,7 @@ void qq_process_recv_file_notify(guint8 *data, gint data_len,
purple_debug_warning("QQ", "Received file notify message is empty\n");
return;
}
-
+
bytes = 0;
bytes += qq_get16(&(info->send_seq), data + bytes);
diff --git a/libpurple/protocols/qq/sys_msg.c b/libpurple/protocols/qq/sys_msg.c
deleted file mode 100644
index 0df850d6f5..0000000000
--- a/libpurple/protocols/qq/sys_msg.c
+++ /dev/null
@@ -1,357 +0,0 @@
-/**
- * @file sys_msg.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 "notify.h"
-#include "request.h"
-
-#include "buddy_info.h"
-#include "buddy_list.h"
-#include "buddy_opt.h"
-#include "char_conv.h"
-#include "header_info.h"
-#include "packet_parse.h"
-#include "qq.h"
-#include "qq_network.h"
-#include "sys_msg.h"
-#include "utils.h"
-
-enum {
- QQ_MSG_SYS_BEING_ADDED = 0x01,
- QQ_MSG_SYS_ADD_CONTACT_REQUEST = 0x02,
- QQ_MSG_SYS_ADD_CONTACT_APPROVED = 0x03,
- QQ_MSG_SYS_ADD_CONTACT_REJECTED = 0x04,
- QQ_MSG_SYS_NOTICE= 0x06,
- QQ_MSG_SYS_NEW_VERSION = 0x09
-};
-
-/* Henry: private function for reading/writing of system log */
-static void _qq_sys_msg_log_write(PurpleConnection *gc, gchar *msg, gchar *from)
-{
- PurpleLog *log;
- PurpleAccount *account;
-
- account = purple_connection_get_account(gc);
-
- log = purple_log_new(PURPLE_LOG_IM,
- "systemim",
- account,
- NULL,
- time(NULL),
- NULL
- );
- purple_log_write(log, PURPLE_MESSAGE_SYSTEM, from,
- time(NULL), msg);
- purple_log_free(log);
-}
-
-/* suggested by rakescar@linuxsir, can still approve after search */
-static void _qq_search_before_auth_with_gc_and_uid(gc_and_uid *g)
-{
- PurpleConnection *gc;
- guint32 uid;
- gchar *nombre;
-
- g_return_if_fail(g != NULL);
-
- gc = g->gc;
- uid = g->uid;
- g_return_if_fail(gc != 0 && uid != 0);
-
- qq_send_packet_get_info(gc, uid, TRUE); /* we want to see window */
-
- nombre = uid_to_purple_name(uid);
- purple_request_action
- (gc, NULL, _("Do you approve the requestion?"), "",
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), nombre, NULL,
- g, 2,
- _("Reject"), G_CALLBACK(qq_reject_add_request_with_gc_and_uid),
- _("Approve"), G_CALLBACK(qq_approve_add_request_with_gc_and_uid));
- g_free(nombre);
-}
-
-static void _qq_search_before_add_with_gc_and_uid(gc_and_uid *g)
-{
- PurpleConnection *gc;
- guint32 uid;
- gchar *nombre;
-
- g_return_if_fail(g != NULL);
-
- gc = g->gc;
- uid = g->uid;
- g_return_if_fail(gc != 0 && uid != 0);
-
- qq_send_packet_get_info(gc, uid, TRUE); /* we want to see window */
- nombre = uid_to_purple_name(uid);
- purple_request_action
- (gc, NULL, _("Do you add the buddy?"), "",
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), nombre, NULL,
- g, 2,
- _("Cancel"), NULL,
- _("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid));
- g_free(nombre);
-}
-
-/* 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)
-{
- 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);
-
- 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_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, ack, ack_len);
- else
- purple_debug_error("QQ",
- "Fail creating sys msg ACK, expect %d bytes, build %d bytes\n", ack_len, bytes);
-}
-
-/* when you are added by a person, QQ server will send sys message */
-static void _qq_process_msg_sys_being_added(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
- gchar *message;
- PurpleBuddy *b;
- guint32 uid;
- gc_and_uid *g;
- gchar *name;
-
- g_return_if_fail(from != NULL && to != NULL);
-
- uid = strtol(from, NULL, 10);
- name = uid_to_purple_name(uid);
- b = purple_find_buddy(gc->account, name);
-
- if (b == NULL) { /* the person is not in my list */
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = uid; /* only need to get value */
- message = g_strdup_printf(_("You have been added by %s"), from);
- _qq_sys_msg_log_write(gc, message, from);
- purple_request_action(gc, NULL, message,
- _("Would you like to add him?"),
- PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), name, NULL,
- g, 3,
- _("Cancel"), NULL,
- _("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid),
- _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid));
- } else {
- message = g_strdup_printf(_("%s added you [%s] to buddy list"), from, to);
- _qq_sys_msg_log_write(gc, message, from);
- purple_notify_info(gc, _("QQ Budy"), _("Successed:"), message);
- }
-
- g_free(name);
- g_free(message);
-}
-
-/* you are rejected by the person */
-static void _qq_process_msg_sys_add_contact_rejected(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
- gchar *message, *reason;
-
- g_return_if_fail(from != NULL && to != NULL);
-
- message = g_strdup_printf(_("Requestion rejected by %s"), from);
- reason = g_strdup_printf(_("Message: %s"), msg_utf8);
- _qq_sys_msg_log_write(gc, message, from);
-
- purple_notify_info(gc, _("QQ Buddy"), message, reason);
- g_free(message);
- g_free(reason);
-}
-
-/* the buddy approves your request of adding him/her as your friend */
-static void _qq_process_msg_sys_add_contact_approved(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
- gchar *message;
- qq_data *qd;
-
- g_return_if_fail(from != NULL && to != NULL);
-
- qd = (qq_data *) gc->proto_data;
- qq_add_buddy_by_recv_packet(gc, strtol(from, NULL, 10), TRUE, TRUE);
-
- message = g_strdup_printf(_("Requestion approved by %s"), from);
- _qq_sys_msg_log_write(gc, message, from);
- purple_notify_info(gc, _("QQ Buddy"), _("Notice:"), message);
-
- g_free(message);
-}
-
-/* someone wants to add you to his buddy list */
-static void _qq_process_msg_sys_add_contact_request(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
- gchar *message, *reason;
- guint32 uid;
- gc_and_uid *g, *g2;
- PurpleBuddy *b;
- gchar *name;
-
- g_return_if_fail(from != NULL && to != NULL);
-
- uid = strtol(from, NULL, 10);
- g = g_new0(gc_and_uid, 1);
- g->gc = gc;
- g->uid = uid;
-
- name = uid_to_purple_name(uid);
-
- /* TODO: this should go through purple_account_request_authorization() */
- message = g_strdup_printf(_("%s wants to add you [%s] as a friend"), from, to);
- reason = g_strdup_printf(_("Message: %s"), msg_utf8);
- _qq_sys_msg_log_write(gc, message, from);
-
- purple_request_action
- (gc, NULL, message, reason, PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), name, NULL,
- g, 3,
- _("Reject"),
- G_CALLBACK(qq_reject_add_request_with_gc_and_uid),
- _("Approve"),
- G_CALLBACK(qq_approve_add_request_with_gc_and_uid),
- _("Search"), G_CALLBACK(_qq_search_before_auth_with_gc_and_uid));
-
- g_free(message);
- g_free(reason);
-
- /* XXX: Is this needed once the above goes through purple_account_request_authorization()? */
- b = purple_find_buddy(gc->account, name);
- if (b == NULL) { /* the person is not in my list */
- g2 = g_new0(gc_and_uid, 1);
- g2->gc = gc;
- g2->uid = strtol(from, NULL, 10);
- message = g_strdup_printf(_("%s is not in buddy list"), from);
- purple_request_action(gc, NULL, message,
- _("Would you add?"), PURPLE_DEFAULT_ACTION_NONE,
- purple_connection_get_account(gc), name, NULL,
- g2, 3,
- _("Cancel"), NULL,
- _("Add"), G_CALLBACK(qq_add_buddy_with_gc_and_uid),
- _("Search"), G_CALLBACK(_qq_search_before_add_with_gc_and_uid));
- g_free(message);
- }
-
- g_free(name);
-}
-
-static void _qq_process_msg_sys_notice(PurpleConnection *gc, gchar *from, gchar *to, gchar *msg_utf8)
-{
- qq_data *qd = (qq_data *) gc->proto_data;
- gchar *title, *content;
-
- g_return_if_fail(from != NULL && to != NULL);
-
- title = g_strdup_printf(_("From %s:"), from);
- content = g_strdup_printf(_("%s"), msg_utf8);
-
- if (qd->is_show_notice) {
- purple_notify_info(gc, _("QQ Server Notice"), title, content);
- } else {
- purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8);
-}
- g_free(title);
- g_free(content);
-}
-
-void qq_process_msg_sys(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc)
-{
- qq_data *qd;
- gchar **segments, *code, *from, *to, *msg, *msg_utf8;
-
- g_return_if_fail(data != NULL && data_len != 0);
-
- qd = (qq_data *) gc->proto_data;
-
- if (NULL == (segments = split_data(data, data_len, "\x1f", 4)))
- return;
- code = segments[0];
- from = segments[1];
- to = segments[2];
- msg = segments[3];
-
- _qq_send_packet_ack_msg_sys(gc, code[0], strtol(from, NULL, 10), seq);
-
- if (strtol(to, NULL, 10) != qd->uid) { /* not to me */
- purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to);
- g_strfreev(segments);
- return;
- }
-
- msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
- if (from == NULL && msg_utf8) {
- purple_debug_error("QQ", "Recv NULL sys msg to [%s], discard\n", to);
- g_strfreev(segments);
- g_free(msg_utf8);
- return;
- }
-
- switch (strtol(code, NULL, 10)) {
- case QQ_MSG_SYS_BEING_ADDED:
- _qq_process_msg_sys_being_added(gc, from, to, msg_utf8);
- break;
- case QQ_MSG_SYS_ADD_CONTACT_REQUEST:
- _qq_process_msg_sys_add_contact_request(gc, from, to, msg_utf8);
- break;
- case QQ_MSG_SYS_ADD_CONTACT_APPROVED:
- _qq_process_msg_sys_add_contact_approved(gc, from, to, msg_utf8);
- break;
- case QQ_MSG_SYS_ADD_CONTACT_REJECTED:
- _qq_process_msg_sys_add_contact_rejected(gc, from, to, msg_utf8);
- break;
- case QQ_MSG_SYS_NOTICE:
- _qq_process_msg_sys_notice(gc, from, to, msg_utf8);
- break;
- case QQ_MSG_SYS_NEW_VERSION:
- purple_debug_warning("QQ",
- "QQ server says there is newer version than %s\n", qq_get_ver_desc(QQ_CLIENT));
- break;
- default:
- purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", code);
- purple_debug_warning("QQ", "the msg is : %s\n", msg_utf8);
- }
- g_free(msg_utf8);
- g_strfreev(segments);
-}
diff --git a/libpurple/protocols/qq/sys_msg.h b/libpurple/protocols/qq/sys_msg.h
deleted file mode 100644
index 9371c1501e..0000000000
--- a/libpurple/protocols/qq/sys_msg.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * @file sys_msg.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_SYS_MSG_H_
-#define _QQ_SYS_MSG_H_
-
-#include <glib.h>
-#include "connection.h"
-
-void qq_process_msg_sys(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc);
-
-#endif
diff --git a/libpurple/protocols/qq/utils.c b/libpurple/protocols/qq/utils.c
index 420e51886e..6fb7ee3c82 100644
--- a/libpurple/protocols/qq/utils.c
+++ b/libpurple/protocols/qq/utils.c
@@ -91,12 +91,6 @@ gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount)
return g_strdup_printf("%d", index);
}
-gint qq_string_to_dec_value(const gchar *str)
-{
- g_return_val_if_fail(str != NULL, 0);
- return strtol(str, NULL, 10);
-}
-
/* split the given data(len) with delimit,
* check the number of field matches the expected_fields (<=0 means all)
* return gchar* array (needs to be freed by g_strfreev later), or NULL */
@@ -104,7 +98,7 @@ gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_f
{
guint8 *input;
gchar **segments;
- gint i, j;
+ gint count, j;
g_return_val_if_fail(data != NULL && len != 0 && delimit != 0, NULL);
@@ -118,22 +112,17 @@ gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_f
if (expected_fields <= 0)
return segments;
- for (i = 0; segments[i] != NULL; i++) {;
- }
- if (i < expected_fields) { /* not enough fields */
- purple_debug_error("QQ", "Invalid data, expect %d fields, found only %d, discard\n",
- expected_fields, i);
- g_strfreev(segments);
+ count = g_strv_length(segments);
+ if (count < expected_fields) { /* not enough fields */
+ purple_debug_error("QQ", "Less fields %d then %d\n", count, expected_fields);
return NULL;
- } else if (i > expected_fields) { /* more fields, OK */
- purple_debug_warning("QQ", "Dangerous data, expect %d fields, found %d, return all\n",
- expected_fields, i);
+ } else if (count > expected_fields) { /* more fields, OK */
+ purple_debug_warning("QQ", "More fields %d than %d\n", count, expected_fields);
/* free up those not used */
- for (j = expected_fields; j < i; j++) {
+ for (j = expected_fields; j < count; j++) {
purple_debug_warning("QQ", "field[%d] is %s\n", j, segments[j]);
g_free(segments[j]);
}
-
segments[expected_fields] = NULL;
}
@@ -183,20 +172,6 @@ gchar *uid_to_purple_name(guint32 uid)
return g_strdup_printf(QQ_NAME_FORMAT, uid);
}
-/* convert name displayed in a chat channel to original QQ UID */
-gchar *chat_name_to_purple_name(const gchar *const name)
-{
- const gchar *tmp;
- gchar *ret;
-
- g_return_val_if_fail(name != NULL, NULL);
-
- tmp = (gchar *) purple_strcasestr(name, "(qq-");
- ret = g_strndup(tmp + 4, strlen(name) - (tmp - name) - 4 - 1);
-
- return ret;
-}
-
/* try to dump the data as GBK */
gchar* try_dump_as_gbk(const guint8 *const data, gint len)
{
@@ -335,7 +310,7 @@ static gchar *hex_dump_to_str(const guint8 *const buffer, gint bytes)
}
void qq_hex_dump(PurpleDebugLevel level, const char *category,
- const guint8 *pdata, gint bytes,
+ const guint8 *pdata, gint bytes,
const char *format, ...)
{
va_list args;
@@ -361,25 +336,6 @@ void qq_hex_dump(PurpleDebugLevel level, const char *category,
void qq_show_packet(const gchar *desc, const guint8 *buf, gint len)
{
- qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", buf, len, desc);
-}
-
-/* convert face num from packet (0-299) to local face (1-100) */
-gchar *face_to_icon_str(gint face)
-{
- gchar *icon_num_str;
- gint icon_num = face / 3 + 1;
- icon_num_str = g_strdup_printf("%d", icon_num);
- return icon_num_str;
+ qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", buf, len, desc);
}
-/* return the location of the buddy icon dir
- * any application using libpurple but not installing the QQ buddy icons
- * under datadir needs to set the pref below, or buddy icons won't work */
-const char *qq_buddy_icon_dir(void)
-{
- if (purple_prefs_exists("/prpl/qq/buddy_icon_dir"))
- return purple_prefs_get_string("/prpl/qq/buddy_icon_dir");
- else
- return NULL;
-}
diff --git a/libpurple/protocols/qq/utils.h b/libpurple/protocols/qq/utils.h
index 7cefde20ec..f768de8a2c 100644
--- a/libpurple/protocols/qq/utils.h
+++ b/libpurple/protocols/qq/utils.h
@@ -34,7 +34,6 @@ void qq_get_md5(guint8 *md5, gint md5_len, const guint8* const src, gint src_len
gchar *get_name_by_index_str(gchar **array, const gchar *index_str, gint amount);
gchar *get_index_str_by_name(gchar **array, const gchar *name, gint amount);
-gint qq_string_to_dec_value(const gchar *str);
gchar **split_data(guint8 *data, gint len, const gchar *delimit, gint expected_fields);
@@ -43,18 +42,13 @@ guint8 *str_ip_gen(gchar *str);
guint32 purple_name_to_uid(const gchar *name);
gchar *uid_to_purple_name(guint32 uid);
-gchar *chat_name_to_purple_name(const gchar *const name);
-
-gchar *face_to_icon_str(gint face);
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 guint8 *pdata, gint bytes,
const char *format, ...);
guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len);
-const gchar *qq_buddy_icon_dir(void);
-
#endif