changeset 15138:e952a5fc97b3

[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 <tailor@pidgin.im>
author Mark Huetsch <markhuetsch>
date Sat, 09 Dec 2006 06:34:48 +0000
parents 2409a2508650
children 63d554c2675d
files libgaim/protocols/qq/Makefile.am libgaim/protocols/qq/buddy_info.c libgaim/protocols/qq/buddy_info.h libgaim/protocols/qq/buddy_list.c libgaim/protocols/qq/buddy_status.c libgaim/protocols/qq/header_info.c libgaim/protocols/qq/header_info.h libgaim/protocols/qq/keep_alive.c libgaim/protocols/qq/qq.c libgaim/protocols/qq/qq.h libgaim/protocols/qq/recv_core.c libgaim/protocols/qq/send_core.h libgaim/protocols/qq/utils.c libgaim/protocols/qq/utils.h
diffstat 14 files changed, 177 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- 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)
--- 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
--- 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
--- 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);
 		}
 
--- 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", 
--- 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:
--- 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 */
 };
--- 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);
 	}
--- 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, "\n<b>Gender:</b> ERROR(%d)", q_bud->gender);
 		}
+		if (q_bud->level)
+			g_string_append_printf(tooltip, "\n<b>Level:</b> %d", q_bud->level);
 		/* For debugging */
 		/*
 		g_string_append_printf(tooltip, "\n<b>Flag:</b> %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 */
--- 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;
--- 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;
--- 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 <glib.h>
 #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);
--- 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
--- 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