summaryrefslogtreecommitdiff
path: root/libpurple/protocols/qq/buddy_opt.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpurple/protocols/qq/buddy_opt.c')
-rw-r--r--libpurple/protocols/qq/buddy_opt.c1365
1 files changed, 1049 insertions, 316 deletions
diff --git a/libpurple/protocols/qq/buddy_opt.c b/libpurple/protocols/qq/buddy_opt.c
index 59c7f03131..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,131 +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 ! */
- /* XXX: This looks very wrong */
- 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;
@@ -258,305 +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;
- const char *bname;
+
+ 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;
- bname = purple_buddy_get_name(buddy);
- uid = purple_name_to_uid(bname);
- if (uid > 0)
- _qq_send_packet_add_buddy(gc, uid);
- else {
- b = purple_find_buddy(gc->account, bname);
- if (b != NULL)
- purple_blist_remove_buddy(b);
- purple_notify_error(gc, NULL,
- _("QQ Number Error"),
- _("Invalid QQ Number"));
+ uid = purple_name_to_uid(buddy->name);
+ 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;
- const char *bname;
+ guint16 flag1, flag2;
+
+ g_return_if_fail(data != NULL && data_len >= 5);
+ g_return_if_fail(uid != 0);
qd = (qq_data *) gc->proto_data;
- bname = purple_buddy_get_name(buddy);
- uid = purple_name_to_uid(bname);
- 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, bname);
- 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", bname);
- /* 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(purple_group_get_name(group), 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;
}
}