diff options
Diffstat (limited to 'libpurple/protocols/qq/buddy_opt.c')
-rw-r--r-- | libpurple/protocols/qq/buddy_opt.c | 1365 |
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; } } |