# HG changeset patch # User Mark Huetsch # Date 1165646088 0 # Node ID e952a5fc97b36046687a411a013f6b293a32cbfc # Parent 2409a2508650ae3b2f6dc85e979cbacd802c7b56 [gaim-migrate @ 17924] Added support for QQ levels Allow 3rd party clients to specify the location of the buddy icon dir Minor code cleanup committer: Tailor Script diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/Makefile.am --- a/libgaim/protocols/qq/Makefile.am Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/Makefile.am Sat Dec 09 06:34:48 2006 +0000 @@ -94,7 +94,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/libgaim \ -DVERSION=\"$(VERSION)\" \ - -DQQBUDDYICONDIR=\"$(datadir)/pixmaps/gaim/buddy_icons/qq\" \ + -DQQ_BUDDY_ICON_DIR=\"$(datadir)/pixmaps/gaim/buddy_icons/qq\" \ $(DEBUG_CFLAGS) \ $(GLIB_CFLAGS) \ $(GAIM_CFLAGS) diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/buddy_info.c --- a/libgaim/protocols/qq/buddy_info.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/buddy_info.c Sat Dec 09 06:34:48 2006 +0000 @@ -548,27 +548,27 @@ } } -/* TODO: figure out how/when we can use a custom face - * for now, only allow the stock icons */ +/* TODO: custom faces */ void qq_set_my_buddy_icon(GaimConnection *gc, const gchar *iconfile) { gchar *icon; gint icon_num; GaimAccount *account = gaim_connection_get_account(gc); const gchar *icon_path = gaim_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 = strlen(QQBUDDYICONDIR); + gint dir_len = strlen(buddy_icon_dir); gint icon_len = strlen(icon_path) - dir_len - 1 - prefix_len - suffix_len; - gchar *errmsg = g_strconcat(_("You are attempting to set a custom face. Gaim currently only allows the standard faces. Please choose an image from "), QQBUDDYICONDIR, ".", NULL); + gchar *errmsg = g_strconcat(_("You are attempting to set a custom face. Gaim currently only allows the standard faces. Please choose an image from "), buddy_icon_dir, ".", NULL); /* make sure we're using an appropriate icon */ - if (!(g_ascii_strncasecmp(icon_path, QQBUDDYICONDIR, dir_len) == 0 + if (!(g_ascii_strncasecmp(icon_path, buddy_icon_dir, dir_len) == 0 && icon_path[dir_len] == G_DIR_SEPARATOR && g_ascii_strncasecmp(icon_path + dir_len + 1, QQ_ICON_PREFIX, prefix_len) == 0 && g_ascii_strncasecmp(icon_path + dir_len + 1 + prefix_len + icon_len, QQ_ICON_SUFFIX, suffix_len) == 0 && icon_len <= 3)) { - gaim_notify_error(gc, _("Invalid QQ Facea"), errmsg, NULL); + gaim_notify_error(gc, _("Invalid QQ Face"), errmsg, NULL); g_free(errmsg); return; } @@ -589,6 +589,24 @@ qq_set_buddy_icon_for_user(account, account->username, icon_path); } + +static void _qq_update_buddy_icon(GaimAccount *account, const gchar *name, gint face) +{ + gchar *icon_path; + GaimBuddyIcon *icon = gaim_buddy_icons_find(account, name); + gchar *icon_num_str = face_to_icon_str(face); + const gchar *old_path = gaim_buddy_icon_get_path(icon); + const gchar *buddy_icon_dir = qq_buddy_icon_dir(); + + icon_path = g_strconcat(buddy_icon_dir, G_DIR_SEPARATOR_S, QQ_ICON_PREFIX, + icon_num_str, QQ_ICON_SUFFIX, NULL); + if (icon == NULL || old_path == NULL + || g_ascii_strcasecmp(icon_path, old_path) != 0) + qq_set_buddy_icon_for_user(account, name, icon_path); + g_free(icon_num_str); + g_free(icon_path); +} + /* after getting info or modify myself, refresh the buddy list accordingly */ void qq_refresh_buddy_and_myself(contact_info *info, GaimConnection *gc) { @@ -617,6 +635,7 @@ if (alias_utf8 != NULL) q_bud->nickname = g_strdup(alias_utf8); qq_update_buddy_contact(gc, q_bud); + _qq_update_buddy_icon(gc->account, gaim_name, q_bud->face); } g_free(gaim_name); g_free(alias_utf8); @@ -699,13 +718,98 @@ gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d info queries are freed!\n", i); } -#ifdef _WIN32 -const char *qq_win32_buddy_icon_dir(void) +void qq_send_packet_get_level(GaimConnection *gc, guint32 uid) +{ + guint8 buf[5]; + guint32 tmp = g_htonl(uid); + buf[0] = 0; + memcpy(buf+1, &tmp, 4); + qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, 5); +} + +void qq_send_packet_get_buddies_levels(GaimConnection *gc) +{ + guint8 *buf, *tmp, size; + qq_buddy *q_bud; + GList *node; + qq_data *qd = (qq_data *) gc->proto_data; + + /* server only sends back levels for online buddies, no point + * in asking for anyone else */ + size = 4*g_list_length(qd->buddies) + 1; + buf = g_new0(guint8, size); + tmp = buf + 1; + + for (node = qd->buddies; node != NULL; node = node->next) { + guint32 tmp4; + q_bud = (qq_buddy *) node->data; + if (q_bud != NULL) { + tmp4 = g_htonl(q_bud->uid); + memcpy(tmp, &tmp4, 4); + tmp += 4; + } + } + qq_send_cmd(gc, QQ_CMD_GET_LEVEL, TRUE, 0, TRUE, buf, size); + qd->last_get_levels = time(NULL); + g_free(buf); +} + +void qq_process_get_level_reply(guint8 *buf, gint buf_len, GaimConnection *gc) { - static char *dir = NULL; - if (dir == NULL) - dir = g_build_filename(wgaim_install_dir(), "pixmaps", - "gaim", "buddy_icons", "qq", NULL); - return dir; + guint32 uid, onlineTime; + guint16 level, timeRemainder; + gchar *gaim_name; + GaimBuddy *b; + qq_buddy *q_bud; + gint decr_len, i; + guint8 *decr_buf, *tmp; + GaimAccount *account = gaim_connection_get_account(gc); + qq_data *qd = (qq_data *) gc->proto_data; + + decr_len = buf_len; + decr_buf = g_new0(guint8, buf_len); + if (!qq_crypt(DECRYPT, buf, buf_len, qd->session_key, decr_buf, &decr_len)) { + gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Couldn't decrypt get level packet\n"); + } + + decr_len--; + if (decr_len % 12 != 0) { + gaim_debug(GAIM_DEBUG_ERROR, "QQ", + "Get levels list of abnormal length. Truncating last %d bytes.\n", decr_len % 12); + decr_len -= (decr_len % 12); + } + + tmp = decr_buf + 1; + /* this byte seems random */ + /* + gaim_debug(GAIM_DEBUG_INFO, "QQ", "Byte one of get_level packet: %d\n", buf[0]); + */ + for (i = 0; i < decr_len; i += 12) { + uid = g_ntohl(*(guint32 *) tmp); + tmp += 4; + onlineTime = g_ntohl(*(guint32 *) tmp); + tmp += 4; + level = g_ntohs(*(guint16 *) tmp); + tmp += 2; + timeRemainder = g_ntohs(*(guint16 *) tmp); + tmp += 2; + /* + gaim_debug(GAIM_DEBUG_INFO, "QQ", "Level packet entry:\nuid: %d\nonlineTime: %d\nlevel: %d\ntimeRemainder: %d\n", + uid, onlineTime, level, timeRemainder); + */ + gaim_name = uid_to_gaim_name(uid); + b = gaim_find_buddy(account, gaim_name); + q_bud = (b == NULL) ? NULL : (qq_buddy *) b->proto_data; + + if (q_bud != NULL) { + q_bud->onlineTime = onlineTime; + q_bud->level = level; + q_bud->timeRemainder = timeRemainder; + } else { + gaim_debug(GAIM_DEBUG_ERROR, "QQ", + "Got an online buddy %d, but not in my buddy list\n", uid); + } + g_free(gaim_name); + } + g_free(decr_buf); } -#endif diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/buddy_info.h --- a/libgaim/protocols/qq/buddy_info.h Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/buddy_info.h Sat Dec 09 06:34:48 2006 +0000 @@ -92,5 +92,8 @@ void qq_process_modify_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc); void qq_process_get_info_reply(guint8 *buf, gint buf_len, GaimConnection *gc); void qq_info_query_free(qq_data *qd); +void qq_send_packet_get_level(GaimConnection *gc, guint32 uid); +void qq_send_packet_get_buddies_levels(GaimConnection *gc); +void qq_process_get_level_reply(guint8 *buf, gint buf_len, GaimConnection *gc); #endif diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/buddy_list.c --- a/libgaim/protocols/qq/buddy_list.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/buddy_list.c Sat Dec 09 06:34:48 2006 +0000 @@ -28,6 +28,7 @@ #include "notify.h" #include "utils.h" #include "packet_parse.h" +#include "buddy_info.h" #include "buddy_list.h" #include "buddy_status.h" #include "buddy_opt.h" @@ -235,6 +236,7 @@ qq_send_packet_get_buddies_online(gc, position); } else { + qq_send_packet_get_buddies_levels(gc); qq_refresh_all_buddy_status(gc); } diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/buddy_status.c --- a/libgaim/protocols/qq/buddy_status.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/buddy_status.c Sat Dec 09 06:34:48 2006 +0000 @@ -26,6 +26,7 @@ #include "debug.h" #include "prefs.h" +#include "buddy_info.h" #include "buddy_status.h" #include "crypt.h" #include "header_info.h" @@ -261,6 +262,8 @@ q_bud->status = s->status; if(0 != s->client_version) q_bud->client_version = s->client_version; + if (q_bud->status == QQ_BUDDY_ONLINE_NORMAL) + qq_send_packet_get_level(gc, q_bud->uid); qq_update_buddy_contact(gc, q_bud); } else { gaim_debug(GAIM_DEBUG_ERROR, "QQ", diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/header_info.c --- a/libgaim/protocols/qq/header_info.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/header_info.c Sat Dec 09 06:34:48 2006 +0000 @@ -32,6 +32,7 @@ #define QQ_CLIENT_0B2F 0x0b2f /* GB QQ2003iii build 0117 */ #define QQ_CLIENT_0B35 0x0b35 /* GB QQ2003iii build 0304 (offical release) */ #define QQ_CLIENT_0B37 0x0b37 /* GB QQ2003iii build 0304 (April 05 updates) */ + #define QQ_SERVER_0100 0x0100 /* server */ /* given command alias, return the command name accordingly */ @@ -74,6 +75,8 @@ return "QQ_CMD_GROUP_CMD"; case QQ_CMD_GET_ALL_LIST_WITH_GROUP: return "QQ_CMD_GET_ALL_LIST_WITH_GROUP"; + case QQ_CMD_GET_LEVEL: + return "QQ_CMD_GET_LEVEL"; case QQ_CMD_REQUEST_LOGIN_TOKEN: return "QQ_CMD_REQUEST_LOGIN_TOKEN"; case QQ_CMD_RECV_MSG_SYS: diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/header_info.h --- a/libgaim/protocols/qq/header_info.h Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/header_info.h Sat Dec 09 06:34:48 2006 +0000 @@ -33,7 +33,7 @@ #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 0x0b37 */ /* QQ2003iii build 0304, Aprili 05 update */ +#define QQ_CLIENT 0x0E1B /* list of known QQ commands */ enum { @@ -57,8 +57,9 @@ QQ_CMD_GET_FRIENDS_ONLINE = 0x0027, /* get my online friends list */ QQ_CMD_CELL_PHONE_2 = 0x0029, /* cell phone 2 */ QQ_CMD_GROUP_CMD = 0x0030, /* group command */ - QQ_CMD_GET_ALL_LIST_WITH_GROUP = 0x58, - QQ_CMD_REQUEST_LOGIN_TOKEN = 0x62, + QQ_CMD_GET_ALL_LIST_WITH_GROUP = 0x0058, + QQ_CMD_GET_LEVEL = 0x005C, /* get level for one or more buddies */ + QQ_CMD_REQUEST_LOGIN_TOKEN = 0x0062, /* get login token */ QQ_CMD_RECV_MSG_SYS = 0x0080, /* receive a system message */ QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS = 0x0081, /* friends change status */ }; diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/keep_alive.c --- a/libgaim/protocols/qq/keep_alive.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/keep_alive.c Sat Dec 09 06:34:48 2006 +0000 @@ -42,6 +42,7 @@ #include "utils.h" #define QQ_UPDATE_ONLINE_INTERVAL 300 /* in sec */ +#define QQ_UPDATE_LEVELS_INTERVAL 600 /* in sec */ /* send keep-alive packet to QQ server (it is a heart-beat) */ void qq_send_packet_keep_alive(GaimConnection *gc) @@ -62,7 +63,8 @@ } /* parse the return of keep-alive packet, it includes some system information */ -void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, GaimConnection *gc) { +void qq_process_keep_alive_reply(guint8 *buf, gint buf_len, GaimConnection *gc) +{ qq_data *qd; gint len; gchar **segments; @@ -90,9 +92,11 @@ gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt keep alive reply\n"); /* we refresh buddies's online status periodically */ - /* qd->lasat_get_online is updated when setting get_buddies_online packet */ + /* qd->last_get_online is updated when setting get_buddies_online packet */ if ((time(NULL) - qd->last_get_online) >= QQ_UPDATE_ONLINE_INTERVAL) qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START); + if ((time(NULL) - qd->last_get_levels) >= QQ_UPDATE_LEVELS_INTERVAL) + qq_send_packet_get_buddies_levels(gc); } /* refresh all buddies online/offline, @@ -119,20 +123,7 @@ } } -static void _qq_update_buddy_icon(GaimAccount *account, const gchar *name, gint face) -{ - GaimBuddyIcon *icon = gaim_buddy_icons_find(account, name); - gchar *icon_num_str = face_to_icon_str(face); - gchar *icon_path = g_strconcat(QQBUDDYICONDIR, G_DIR_SEPARATOR_S, - QQ_ICON_PREFIX, icon_num_str, QQ_ICON_SUFFIX, NULL); - const gchar *old_path = gaim_buddy_icon_get_path(icon); - if (icon == NULL || old_path == NULL - || g_ascii_strcasecmp(icon_path, old_path) != 0) - qq_set_buddy_icon_for_user(account, name, icon_path); - g_free(icon_num_str); - g_free(icon_path); -} - +/*TODO: maybe this should be qq_update_buddy_status() ?*/ void qq_update_buddy_contact(GaimConnection *gc, qq_buddy *q_bud) { gchar *name; @@ -176,7 +167,6 @@ } gaim_debug(GAIM_DEBUG_INFO, "QQ", "set buddy %d to %s\n", q_bud->uid, status_id); gaim_prpl_got_user_status(gc->account, name, status_id, NULL); - _qq_update_buddy_icon(gc->account, name, q_bud->face); } else { gaim_debug(GAIM_DEBUG_ERROR, "QQ", "unknown buddy: %d\n", q_bud->uid); } diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/qq.c --- a/libgaim/protocols/qq/qq.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/qq.c Sat Dec 09 06:34:48 2006 +0000 @@ -224,6 +224,8 @@ default: g_string_append_printf(tooltip, "\nGender: ERROR(%d)", q_bud->gender); } + if (q_bud->level) + g_string_append_printf(tooltip, "\nLevel: %d", q_bud->level); /* For debugging */ /* g_string_append_printf(tooltip, "\nFlag: %01x", q_bud->flag1); @@ -368,6 +370,7 @@ return; } + qq_send_packet_get_level(gc, uid); qq_send_packet_get_info(gc, uid, TRUE); } @@ -609,7 +612,6 @@ } qq_send_packet_keep_alive(gc); - } /* convert chat nickname to qq-uid to get this buddy info */ diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/qq.h --- a/libgaim/protocols/qq/qq.h Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/qq.h Sat Dec 09 06:34:48 2006 +0000 @@ -37,7 +37,7 @@ #ifdef _WIN32 const char *qq_win32_buddy_icon_dir(void); -#define QQBUDDYICONDIR qq_win32_buddy_icon_dir() +#define QQ_BUDDY_ICON_DIR qq_win32_buddy_icon_dir() #endif typedef struct _qq_data qq_data; @@ -55,6 +55,9 @@ guint8 flag1; guint8 comm_flag; /* details in qq_buddy_list.c */ guint16 client_version; + guint8 onlineTime; + guint16 level; + guint16 timeRemainder; time_t signon; time_t idle; time_t last_refresh; @@ -94,6 +97,7 @@ guint16 my_icon; /* my icon index */ guint32 all_online; /* the number of online QQ users */ time_t last_get_online; /* last time send get_friends_online packet */ + time_t last_get_levels; /* last time send get_buddies_levels packet */ guint8 window[1 << 13]; /* check up for duplicated packet */ gint sendqueue_timeout; diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/recv_core.c --- a/libgaim/protocols/qq/recv_core.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/recv_core.c Sat Dec 09 06:34:48 2006 +0000 @@ -263,6 +263,9 @@ case QQ_CMD_GET_ALL_LIST_WITH_GROUP: qq_process_get_all_list_with_group_reply(cursor, len, gc); break; + case QQ_CMD_GET_LEVEL: + qq_process_get_level_reply(cursor, len, gc); + break; case QQ_CMD_REQUEST_LOGIN_TOKEN: qq_process_request_login_token_reply(cursor, len, gc); break; diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/send_core.h --- a/libgaim/protocols/qq/send_core.h Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/send_core.h Sat Dec 09 06:34:48 2006 +0000 @@ -28,10 +28,6 @@ #include #include "connection.h" - -#define QQ_CLIENT 0x0E1B -/* #define QQ_CLIENT 0x0F3F */ - gint qq_send_cmd(GaimConnection *gc, guint16 cmd, gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint len); gint _qq_send_packet(GaimConnection * gc, guint8 *buf, gint len, guint16 cmd); diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/utils.c --- a/libgaim/protocols/qq/utils.c Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/utils.c Sat Dec 09 06:34:48 2006 +0000 @@ -349,3 +349,25 @@ return icon_num; } */ + +/* return the location of the buddy icon dir + * any application using libgaim 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 (gaim_prefs_exists("/prpl/qq/buddy_icon_dir")) + return gaim_prefs_get_string("/prpl/qq/buddy_icon_dir"); + else + return QQ_BUDDY_ICON_DIR; +} + +#ifdef _WIN32 +const char *qq_win32_buddy_icon_dir(void) +{ + static char *dir = NULL; + if (dir == NULL) + dir = g_build_filename(wgaim_install_dir(), "pixmaps", + "gaim", "buddy_icons", "qq", NULL); + return dir; +} +#endif diff -r 2409a2508650 -r e952a5fc97b3 libgaim/protocols/qq/utils.h --- a/libgaim/protocols/qq/utils.h Fri Dec 08 03:03:52 2006 +0000 +++ b/libgaim/protocols/qq/utils.h Sat Dec 09 06:34:48 2006 +0000 @@ -49,4 +49,7 @@ guint8 *hex_str_to_bytes(const gchar *buf, gint *out_len); gchar *hex_dump_to_str(const guint8 *buf, gint buf_len); +const gchar *qq_buddy_icon_dir(void); +const gchar *qq_win32_buddy_icon_dir(void); + #endif