diff options
Diffstat (limited to 'libpurple/protocols/qq/qq_process.c')
-rw-r--r-- | libpurple/protocols/qq/qq_process.c | 759 |
1 files changed, 622 insertions, 137 deletions
diff --git a/libpurple/protocols/qq/qq_process.c b/libpurple/protocols/qq/qq_process.c index e2731fcb5a..f95a718862 100644 --- a/libpurple/protocols/qq/qq_process.c +++ b/libpurple/protocols/qq/qq_process.c @@ -30,27 +30,22 @@ #include "buddy_list.h" #include "buddy_opt.h" #include "group_info.h" -#include "group_free.h" #include "char_conv.h" #include "qq_crypt.h" -#include "group_conv.h" -#include "group_find.h" #include "group_internal.h" #include "group_im.h" #include "group_info.h" #include "group_join.h" #include "group_opt.h" -#include "group_search.h" -#include "header_info.h" +#include "qq_define.h" #include "qq_base.h" #include "im.h" #include "qq_process.h" #include "packet_parse.h" #include "qq_network.h" #include "qq_trans.h" -#include "sys_msg.h" #include "utils.h" enum { @@ -60,10 +55,10 @@ enum { }; /* default process, decrypt and dump */ -static void process_cmd_unknow(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq) +static void process_unknow_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq) { qq_data *qd; - gchar *msg_utf8 = NULL; + gchar *msg; g_return_if_fail(data != NULL && data_len != 0); @@ -76,11 +71,390 @@ static void process_cmd_unknow(PurpleConnection *gc,const gchar *title, guint8 * ">>> [%d] %s -> [default] decrypt and dump", seq, qq_get_cmd_desc(cmd)); - msg_utf8 = try_dump_as_gbk(data, data_len); - if (msg_utf8 != NULL) { - purple_notify_info(gc, _("QQ Error"), title, msg_utf8); - g_free(msg_utf8); + msg = g_strdup_printf("Unknow command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd)); + purple_notify_info(gc, _("QQ Error"), title, msg); + g_free(msg); +} + +/* parse the reply to send_im */ +static void do_im_ack(guint8 *data, gint data_len, PurpleConnection *gc) +{ + qq_data *qd; + + g_return_if_fail(data != NULL && data_len != 0); + + qd = gc->proto_data; + + if (data[0] != 0) { + purple_debug_warning("QQ", "Failed sent IM\n"); + purple_notify_error(gc, _("Error"), _("Failed to send IM."), NULL); + return; + } + + purple_debug_info("QQ", "OK sent IM\n"); +} + +static void do_server_news(guint8 *data, gint data_len, PurpleConnection *gc) +{ + qq_data *qd = (qq_data *) gc->proto_data; + gint bytes; + gchar *title, *brief, *url; + gchar *content; + + g_return_if_fail(data != NULL && data_len != 0); + + /* qq_show_packet("Rcv news", data, data_len); */ + + bytes = 4; /* skip unknown 4 bytes */ + + bytes += qq_get_vstr(&title, QQ_CHARSET_DEFAULT, data + bytes); + bytes += qq_get_vstr(&brief, QQ_CHARSET_DEFAULT, data + bytes); + bytes += qq_get_vstr(&url, QQ_CHARSET_DEFAULT, data + bytes); + + content = g_strdup_printf(_("Server News:\n%s\n%s\n%s"), title, brief, url); + + if (qd->is_show_news) { + qq_got_attention(gc, content); + } else { + purple_debug_info("QQ", "QQ Server news:\n%s\n", content); } + g_free(title); + g_free(brief); + g_free(url); + g_free(content); +} + +static void do_msg_sys_30(PurpleConnection *gc, guint8 *data, gint data_len) +{ + gint len; + guint8 reply; + gchar **segments, *msg_utf8; + + g_return_if_fail(data != NULL && data_len != 0); + + len = data_len; + + if (NULL == (segments = split_data(data, len, "\x2f", 2))) + return; + + reply = strtol(segments[0], NULL, 10); + if (reply == 1) + purple_debug_warning("QQ", "We are kicked out by QQ server\n"); + + msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT); + qq_got_attention(gc, msg_utf8); +} + +static void do_msg_sys_4c(PurpleConnection *gc, guint8 *data, gint data_len) +{ + gint bytes; + gint msg_len; + GString *content; + gchar *msg = NULL; + + g_return_if_fail(data != NULL && data_len > 0); + + bytes = 6; /* skip 0x(06 00 01 1e 01 1c)*/ + + content = g_string_new(""); + while (bytes < data_len) { + msg_len = qq_get_vstr(&msg, QQ_CHARSET_DEFAULT, data + bytes); + g_string_append(content, msg); + g_string_append(content, "\n"); + g_free(msg); + + if (msg_len <= 1) { + break; + } + bytes += msg_len; + } + if (bytes != data_len) { + purple_debug_warning("QQ", "Failed to read QQ_MSG_SYS_4C\n"); + qq_show_packet("do_msg_sys_4c", data, data_len); + } + qq_got_attention(gc, content->str); + g_string_free(content, FALSE); +} + +static const gchar *get_im_type_desc(gint type) +{ + switch (type) { + case QQ_MSG_TO_BUDDY: + return "QQ_MSG_TO_BUDDY"; + case QQ_MSG_TO_UNKNOWN: + return "QQ_MSG_TO_UNKNOWN"; + case QQ_MSG_UNKNOWN_QUN_IM: + return "QQ_MSG_UNKNOWN_QUN_IM"; + case QQ_MSG_ADD_TO_QUN: + return "QQ_MSG_ADD_TO_QUN"; + case QQ_MSG_DEL_FROM_QUN: + return "QQ_MSG_DEL_FROM_QUN"; + case QQ_MSG_APPLY_ADD_TO_QUN: + return "QQ_MSG_APPLY_ADD_TO_QUN"; + case QQ_MSG_CREATE_QUN: + return "QQ_MSG_CREATE_QUN"; + case QQ_MSG_SYS_30: + return "QQ_MSG_SYS_30"; + case QQ_MSG_SYS_4C: + return "QQ_MSG_SYS_4C"; + case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN: + return "QQ_MSG_APPROVE_APPLY_ADD_TO_QUN"; + case QQ_MSG_REJCT_APPLY_ADD_TO_QUN: + return "QQ_MSG_REJCT_APPLY_ADD_TO_QUN"; + case QQ_MSG_TEMP_QUN_IM: + return "QQ_MSG_TEMP_QUN_IM"; + case QQ_MSG_QUN_IM: + return "QQ_MSG_QUN_IM"; + case QQ_MSG_NEWS: + return "QQ_MSG_NEWS"; + case QQ_MSG_EXTEND: + return "QQ_MSG_EXTEND"; + case QQ_MSG_EXTEND_85: + return "QQ_MSG_EXTEND_85"; + default: + return "QQ_MSG_UNKNOWN"; + } +} + +/* I receive a message, mainly it is text msg, + * but we need to proess other types (group etc) */ +static void process_private_msg(guint8 *data, gint data_len, guint16 seq, PurpleConnection *gc) +{ + qq_data *qd; + gint bytes; + + struct { + guint32 uid_from; + guint32 uid_to; + guint32 seq; + struct in_addr ip_from; + guint16 port_from; + guint16 msg_type; + } header; + + g_return_if_fail(data != NULL && data_len != 0); + + qd = (qq_data *) gc->proto_data; + + if (data_len < 16) { /* we need to ack with the first 16 bytes */ + purple_debug_error("QQ", "MSG is too short\n"); + return; + } else { + /* when we receive a message, + * we send an ACK which is the first 16 bytes of incoming packet */ + qq_send_server_reply(gc, QQ_CMD_RECV_IM, seq, data, 16); + } + + /* check len first */ + if (data_len < 20) { /* length of im_header */ + purple_debug_error("QQ", "Invald MSG header, len %d < 20\n", data_len); + return; + } + + bytes = 0; + bytes += qq_get32(&(header.uid_from), data + bytes); + bytes += qq_get32(&(header.uid_to), data + bytes); + bytes += qq_get32(&(header.seq), data + bytes); + /* if the message is delivered via server, it is server IP/port */ + bytes += qq_getIP(&(header.ip_from), data + bytes); + bytes += qq_get16(&(header.port_from), data + bytes); + bytes += qq_get16(&(header.msg_type), data + bytes); + /* im_header prepared */ + + if (header.uid_to != qd->uid) { /* should not happen */ + purple_debug_error("QQ", "MSG to [%d], NOT me\n", header.uid_to); + return; + } + + /* check bytes */ + if (bytes >= data_len - 1) { + purple_debug_warning("QQ", "Empty MSG\n"); + return; + } + + switch (header.msg_type) { + case QQ_MSG_NEWS: + do_server_news(data + bytes, data_len - bytes, gc); + break; + case QQ_MSG_EXTEND: + case QQ_MSG_EXTEND_85: + purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from); + qq_process_extend_im(gc, data + bytes, data_len - bytes); + break; + case QQ_MSG_TO_UNKNOWN: + case QQ_MSG_TO_BUDDY: + purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from); + qq_process_im(gc, data + bytes, data_len - bytes); + break; + case QQ_MSG_UNKNOWN_QUN_IM: + case QQ_MSG_TEMP_QUN_IM: + case QQ_MSG_QUN_IM: + purple_debug_info("QQ", "MSG from room [%d]\n", header.uid_from); + qq_process_room_im(data + bytes, data_len - bytes, header.uid_from, gc, header.msg_type); + break; + case QQ_MSG_ADD_TO_QUN: + purple_debug_info("QQ", "Notice from [%d], Added\n", header.uid_from); + /* uid_from is group id + * we need this to create a dummy group and add to blist */ + qq_process_room_buddy_joined(data + bytes, data_len - bytes, header.uid_from, gc); + break; + case QQ_MSG_DEL_FROM_QUN: + purple_debug_info("QQ", "Notice from room [%d], Removed\n", header.uid_from); + /* uid_from is group id */ + qq_process_room_buddy_removed(data + bytes, data_len - bytes, header.uid_from, gc); + break; + case QQ_MSG_APPLY_ADD_TO_QUN: + purple_debug_info("QQ", "Notice from room [%d], Joined\n", header.uid_from); + /* uid_from is group id */ + qq_process_room_buddy_request_join(data + bytes, data_len - bytes, header.uid_from, gc); + break; + case QQ_MSG_APPROVE_APPLY_ADD_TO_QUN: + purple_debug_info("QQ", "Notice from room [%d], Confirm add in\n", + header.uid_from); + /* uid_from is group id */ + qq_process_room_buddy_approved(data + bytes, data_len - bytes, header.uid_from, gc); + break; + case QQ_MSG_REJCT_APPLY_ADD_TO_QUN: + purple_debug_info("QQ", "Notice from room [%d], Refuse add in\n", + header.uid_from); + /* uid_from is group id */ + qq_process_room_buddy_rejected(data + bytes, data_len - bytes, header.uid_from, gc); + break; + case QQ_MSG_SYS_30: + do_msg_sys_30(gc, data + bytes, data_len - bytes); + break; + case QQ_MSG_SYS_4C: + do_msg_sys_4c(gc, data + bytes, data_len - bytes); + break; + default: + purple_debug_warning("QQ", "MSG from [%d], unknown type %s [0x%04X]\n", + header.uid_from, get_im_type_desc(header.msg_type), header.msg_type); + qq_show_packet("Unknown MSG type", data, data_len); + break; + } +} + +/* Send ACK if the sys message needs an ACK */ +static void request_server_ack(PurpleConnection *gc, gchar *funct_str, gchar *from, guint16 seq) +{ + qq_data *qd; + guint8 *raw_data; + gint bytes; + guint8 bar; + + g_return_if_fail(funct_str != NULL && from != NULL); + qd = (qq_data *) gc->proto_data; + + + bar = 0x1e; + raw_data = g_newa(guint8, strlen(funct_str) + strlen(from) + 16); + + bytes = 0; + bytes += qq_putdata(raw_data + bytes, (guint8 *)funct_str, strlen(funct_str)); + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_putdata(raw_data + bytes, (guint8 *)from, strlen(from)); + bytes += qq_put8(raw_data + bytes, bar); + bytes += qq_put16(raw_data + bytes, seq); + + qq_send_server_reply(gc, QQ_CMD_ACK_SYS_MSG, 0, raw_data, bytes); +} + +static void do_server_notice(PurpleConnection *gc, gchar *from, gchar *to, + guint8 *data, gint data_len) +{ + qq_data *qd = (qq_data *) gc->proto_data; + gchar *msg, *msg_utf8; + gchar *title, *content; + + g_return_if_fail(from != NULL && to != NULL && data_len > 0); + + msg = g_strndup((gchar *)data, data_len); + msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); + g_free(msg); + if (msg_utf8 == NULL) { + purple_debug_error("QQ", "Recv NULL sys msg from %s to %s, discard\n", + from, to); + return; + } + + title = g_strdup_printf(_("From %s:"), from); + content = g_strdup_printf(_("Server notice From %s: \n%s"), from, msg_utf8); + + if (qd->is_show_notice) { + qq_got_attention(gc, content); + } else { + purple_debug_info("QQ", "QQ Server notice from %s:\n%s", from, msg_utf8); + } + g_free(msg_utf8); + g_free(title); + g_free(content); +} + +static void process_server_msg(PurpleConnection *gc, guint8 *data, gint data_len, guint16 seq) +{ + qq_data *qd; + guint8 *data_str; + gchar **segments; + gchar *funct_str, *from, *to; + gint bytes, funct; + + g_return_if_fail(data != NULL && data_len != 0); + + qd = (qq_data *) gc->proto_data; + + data_str = g_newa(guint8, data_len + 1); + g_memmove(data_str, data, data_len); + data_str[data_len] = 0x00; + + segments = g_strsplit_set((gchar *) data_str, "\x1f", 0); + g_return_if_fail(segments != NULL); + if (g_strv_length(segments) < 3) { + purple_debug_warning("QQ", "Server message segments is less than 3\n"); + g_strfreev(segments); + return; + } + + bytes = 0; + funct_str = segments[0]; + bytes += strlen(funct_str) + 1; + from = segments[1]; + bytes += strlen(from) + 1; + to = segments[2]; + bytes += strlen(to) + 1; + + request_server_ack(gc, funct_str, from, seq); + + /* qq_show_packet("Server MSG", data, data_len); */ + if (strtol(to, NULL, 10) != qd->uid) { /* not to me */ + purple_debug_error("QQ", "Recv sys msg to [%s], not me!, discard\n", to); + g_strfreev(segments); + return; + } + + funct = strtol(funct_str, NULL, 10); + switch (funct) { + case QQ_SERVER_BUDDY_ADDED: + case QQ_SERVER_BUDDY_ADD_REQUEST: + case QQ_SERVER_BUDDY_ADDED_ME: + case QQ_SERVER_BUDDY_REJECTED_ME: + case QQ_SERVER_BUDDY_ADD_REQUEST_EX: + case QQ_SERVER_BUDDY_ADDING_EX: + case QQ_SERVER_BUDDY_ADDED_ANSWER: + case QQ_SERVER_BUDDY_ADDED_EX: + qq_process_buddy_from_server(gc, funct, from, to, data + bytes, data_len - bytes); + break; + case QQ_SERVER_NOTICE: + do_server_notice(gc, from, to, data + bytes, data_len - bytes); + break; + case QQ_SERVER_NEW_CLIENT: + purple_debug_warning("QQ", "QQ Server has newer client version\n"); + break; + default: + qq_show_packet("Unknown sys msg", data, data_len); + purple_debug_warning("QQ", "Recv unknown sys msg code: %s\n", funct_str); + break; + } + g_strfreev(segments); } void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len) @@ -113,16 +487,16 @@ void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 * /* now process the packet */ switch (cmd) { case QQ_CMD_RECV_IM: - qq_process_recv_im(data, data_len, seq, gc); + process_private_msg(data, data_len, seq, gc); break; case QQ_CMD_RECV_MSG_SYS: - qq_process_msg_sys(data, data_len, seq, gc); + process_server_msg(gc, data, data_len, seq); break; case QQ_CMD_BUDDY_CHANGE_STATUS: qq_process_buddy_change_status(data, data_len, gc); break; default: - process_cmd_unknow(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq); + process_unknow_cmd(gc, _("Unknow SERVER CMD"), data, data_len, cmd, seq); break; } } @@ -150,36 +524,25 @@ static void process_room_cmd_notify(PurpleConnection *gc, void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) { qq_data *qd; - qq_group *group; gint ret; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - group = qq_room_search_id(gc, room_id); - if (group == NULL && room_id <= 0) { - purple_debug_info("QQ", "No room, nothing update\n"); - return; - } - if (group == NULL ) { - purple_debug_warning("QQ", "Failed search room id [%d]\n", room_id); - return; - } - switch (room_cmd) { case 0: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ROOM, 0); break; case QQ_ROOM_CMD_GET_INFO: - ret = qq_request_room_get_buddies(gc, group, QQ_CMD_CLASS_UPDATE_ROOM); + ret = qq_request_room_get_buddies(gc, room_id, QQ_CMD_CLASS_UPDATE_ROOM); if (ret <= 0) { - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ROOM, 0); } break; case QQ_ROOM_CMD_GET_BUDDIES: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, room_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ROOM, 0); break; case QQ_ROOM_CMD_GET_ONLINES: @@ -189,43 +552,46 @@ void qq_update_room(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) } } -static void update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) +void qq_update_all_rooms(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) { qq_data *qd; gboolean is_new_turn = FALSE; - qq_group *next_group; + guint32 next_id; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - next_group = qq_room_get_next(gc, room_id); - if (next_group == NULL && room_id <= 0) { - purple_debug_info("QQ", "No room. Finished update\n"); - return; - } - if (next_group == NULL ) { - is_new_turn = TRUE; - next_group = qq_room_get_next(gc, 0); - g_return_if_fail(next_group != NULL); + next_id = qq_room_get_next(gc, room_id); + purple_debug_info("QQ", "Update rooms, next id %d, prev id %d\n", next_id, room_id); + + if (next_id <= 0) { + if (room_id > 0) { + is_new_turn = TRUE; + next_id = qq_room_get_next(gc, 0); + purple_debug_info("QQ", "new turn, id %d\n", next_id); + } else { + purple_debug_info("QQ", "No room. Finished update\n"); + return; + } } switch (room_cmd) { case 0: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ALL, 0); break; case QQ_ROOM_CMD_GET_INFO: if (!is_new_turn) { - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, next_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ALL, 0); } else { - qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL); + qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL); } break; case QQ_ROOM_CMD_GET_BUDDIES: /* last command */ if (!is_new_turn) { - qq_request_room_get_buddies(gc, next_group, QQ_CMD_CLASS_UPDATE_ALL); + qq_request_room_get_buddies(gc, next_id, QQ_CMD_CLASS_UPDATE_ALL); } else { purple_debug_info("QQ", "Finished update\n"); } @@ -244,57 +610,63 @@ void qq_update_all(PurpleConnection *gc, guint16 cmd) switch (cmd) { case 0: - qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, QQ_BUDDY_INFO_UPDATE_ONLY); + qq_request_buddy_info(gc, qd->uid, QQ_CMD_CLASS_UPDATE_ALL, 0); break; case QQ_CMD_GET_BUDDY_INFO: qq_request_change_status(gc, QQ_CMD_CLASS_UPDATE_ALL); break; case QQ_CMD_CHANGE_STATUS: - qq_request_get_buddies_list(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); + qq_request_get_buddies(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); break; case QQ_CMD_GET_BUDDIES_LIST: qq_request_get_buddies_and_rooms(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); break; case QQ_CMD_GET_BUDDIES_AND_ROOMS: - qq_request_get_buddies_level(gc, QQ_CMD_CLASS_UPDATE_ALL); + if (qd->client_version >= 2007) { + /* QQ2007/2008 can not get buddies level*/ + qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); + } else { + qq_request_get_buddies_level(gc, QQ_CMD_CLASS_UPDATE_ALL); + } break; case QQ_CMD_GET_LEVEL: qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ALL); break; case QQ_CMD_GET_BUDDIES_ONLINE: /* last command */ - update_all_rooms(gc, 0, 0); + qq_update_all_rooms(gc, 0, 0); break; default: break; } + qd->online_last_update = time(NULL); } static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) { qq_data *qd; - qq_group *next_group; + guint32 next_id; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; - next_group = qq_room_get_next_conv(gc, room_id); - if (next_group == NULL && room_id <= 0) { + next_id = qq_room_get_next_conv(gc, room_id); + if (next_id <= 0 && room_id <= 0) { purple_debug_info("QQ", "No room in conversation, no update online buddies\n"); return; } - if (next_group == NULL ) { + if (next_id <= 0 ) { purple_debug_info("QQ", "finished update rooms' online buddies\n"); return; } switch (room_cmd) { case 0: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ALL, 0); break; case QQ_ROOM_CMD_GET_ONLINES: - qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_group->id, NULL, 0, + qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_ONLINES, next_id, NULL, 0, QQ_CMD_CLASS_UPDATE_ALL, 0); break; default: @@ -304,6 +676,10 @@ static void update_all_rooms_online(PurpleConnection *gc, guint8 room_cmd, guint void qq_update_online(PurpleConnection *gc, guint16 cmd) { + qq_data *qd; + g_return_if_fail (gc != NULL && gc->proto_data != NULL); + qd = (qq_data *) gc->proto_data; + switch (cmd) { case 0: qq_request_get_buddies_online(gc, 0, QQ_CMD_CLASS_UPDATE_ONLINE); @@ -315,16 +691,17 @@ void qq_update_online(PurpleConnection *gc, guint16 cmd) default: break; } + qd->online_last_update = time(NULL); } -void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, +void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32) { qq_data *qd; guint8 *data; gint data_len; - qq_group *group; + qq_room_data *rmd; gint bytes; guint8 reply_cmd, reply; @@ -355,13 +732,6 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, return; } - group = qq_room_search_id(gc, room_id); - if (group == NULL) { - purple_debug_warning("QQ", - "Missing room id in [%05d], 0x%02X %s for %d, len %d\n", - seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); - } - bytes = 0; bytes += qq_get8(&reply_cmd, data + bytes); bytes += qq_get8(&reply, data + bytes); @@ -375,17 +745,17 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, /* now process the packet */ if (reply != QQ_ROOM_CMD_REPLY_OK) { - if (group != NULL) { - qq_set_pending_id(&qd->joining_groups, group->ext_id, FALSE); - } - switch (reply) { /* this should be all errors */ case QQ_ROOM_CMD_REPLY_NOT_MEMBER: - if (group != NULL) { + rmd = qq_room_data_find(gc, room_id); + if (rmd == NULL) { + purple_debug_warning("QQ", + "Missing room id in [%05d], 0x%02X %s for %d, len %d\n", + seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); + } else { purple_debug_warning("QQ", - _("You are not a member of QQ Qun \"%s\"\n"), group->title_utf8); - group->my_role = QQ_ROOM_ROLE_NO; - qq_group_refresh(gc, group); + _("Not a member of room \"%s\"\n"), rmd->title_utf8); + rmd->my_role = QQ_ROOM_ROLE_NO; } break; case QQ_ROOM_CMD_REPLY_SEARCH_ERROR: @@ -402,7 +772,7 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, /* seems ok so far, so we process the reply according to sub_cmd */ switch (reply_cmd) { case QQ_ROOM_CMD_GET_INFO: - qq_process_room_cmd_get_info(data + bytes, data_len - bytes, gc); + qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship32, gc); break; case QQ_ROOM_CMD_CREATE: qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc); @@ -417,7 +787,7 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_SEARCH: - qq_process_group_cmd_search_group(data + bytes, data_len - bytes, gc); + qq_process_room_search(gc, data + bytes, data_len - bytes, ship32); break; case QQ_ROOM_CMD_JOIN: qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc); @@ -429,19 +799,13 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_SEND_MSG: - qq_process_group_cmd_im(data + bytes, data_len - bytes, gc); + qq_process_room_send_im(gc, data + bytes, data_len - bytes); break; case QQ_ROOM_CMD_GET_ONLINES: qq_process_room_cmd_get_onlines(data + bytes, data_len - bytes, gc); - if (group != NULL) - qq_group_conv_refresh_online_member(gc, group); break; case QQ_ROOM_CMD_GET_BUDDIES: qq_process_room_cmd_get_buddies(data + bytes, data_len - bytes, gc); - if (group != NULL) { - group->is_got_info = TRUE; - qq_group_conv_refresh_online_member(gc, group); - } break; default: purple_debug_warning("QQ", "Unknow room cmd 0x%02X %s\n", @@ -451,9 +815,8 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, if (update_class == QQ_CMD_CLASS_NONE) return; - purple_debug_info("QQ", "Update class %d\n", update_class); if (update_class == QQ_CMD_CLASS_UPDATE_ALL) { - update_all_rooms(gc, room_cmd, room_id); + qq_update_all_rooms(gc, room_cmd, room_id); return; } if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) { @@ -465,58 +828,159 @@ void qq_proc_room_cmd(PurpleConnection *gc, guint16 seq, } } -void qq_proc_login_cmd(PurpleConnection *gc, guint8 *rcved, gint rcved_len) +guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, + guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32) { qq_data *qd; - guint8 *data; - gint data_len; - guint ret_8; + guint8 *data = NULL; + gint data_len = 0; + guint ret_8 = QQ_LOGIN_REPLY_ERR; - g_return_if_fail (gc != NULL && gc->proto_data != NULL); + g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR); qd = (qq_data *) gc->proto_data; + g_return_val_if_fail(rcved_len > 0, QQ_LOGIN_REPLY_ERR); data = g_newa(guint8, rcved_len); - /* May use password_twice_md5 in the past version like QQ2005*/ - data_len = qq_decrypt(data, rcved, rcved_len, qd->inikey); - if (data_len >= 0) { - purple_debug_warning("QQ", - "Decrypt login reply packet with inikey, %d bytes\n", data_len); - } else { - data_len = qq_decrypt(data, rcved, rcved_len, qd->password_twice_md5); - if (data_len >= 0) { - purple_debug_warning("QQ", - "Decrypt login reply packet with password_twice_md5, %d bytes\n", data_len); - } else { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Can not decrypt login reply")); - return; - } - } - ret_8 = qq_process_login_reply(gc, data, data_len); - if (ret_8 != QQ_LOGIN_REPLY_OK) { - return; + switch (cmd) { + case QQ_CMD_TOKEN: + if (qq_process_token(gc, rcved, rcved_len) == QQ_LOGIN_REPLY_OK) { + if (qd->client_version >= 2007) { + qq_request_token_ex(gc); + } else { + qq_request_login(gc); + } + return QQ_LOGIN_REPLY_OK; + } + return QQ_LOGIN_REPLY_ERR; + case QQ_CMD_GET_SERVER: + case QQ_CMD_TOKEN_EX: + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); + break; + case QQ_CMD_CHECK_PWD: + /* May use password_twice_md5 in the past version like QQ2005 */ + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by random_key, %d bytes\n", data_len); + } else { + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5, %d bytes\n", data_len); + } + } + break; + case QQ_CMD_LOGIN: + default: + if (qd->client_version >= 2007) { + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n"); + } else { + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.login_key); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by login_key\n"); + } + } + } else { + /* May use password_twice_md5 in the past version like QQ2005 */ + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by random_key\n"); + } else { + data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.pwd_twice_md5); + if (data_len >= 0) { + purple_debug_warning("QQ", "Decrypt login packet by pwd_twice_md5\n"); + } + } + } + break; } - purple_debug_info("QQ", "Login repliess OK; everything is fine\n"); + if (data_len < 0) { + purple_debug_warning("QQ", + "Can not decrypt login cmd, [%05d], 0x%04X %s, len %d\n", + seq, cmd, qq_get_cmd_desc(cmd), rcved_len); + qq_show_packet("Can not decrypted", rcved, rcved_len); + purple_connection_error_reason(gc, + PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, + _("Can not decrypt login reply")); + return QQ_LOGIN_REPLY_ERR; + } - purple_connection_set_state(gc, PURPLE_CONNECTED); - qd->is_login = TRUE; /* must be defined after sev_finish_login */ + switch (cmd) { + case QQ_CMD_GET_SERVER: + ret_8 = qq_process_get_server(gc, data, data_len); + if ( ret_8 == QQ_LOGIN_REPLY_OK) { + qq_request_token(gc); + } else if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { + return QQ_LOGIN_REPLY_REDIRECT; + } + break; + case QQ_CMD_TOKEN_EX: + ret_8 = qq_process_token_ex(gc, data, data_len); + if (ret_8 == QQ_LOGIN_REPLY_OK) { + qq_request_check_pwd(gc); + } else if (ret_8 == QQ_LOGIN_REPLY_NEXT_TOKEN_EX) { + qq_request_token_ex_next(gc); + } else if (ret_8 == QQ_LOGIN_REPLY_CAPTCHA_DLG) { + qq_captcha_input_dialog(gc, &(qd->captcha)); + g_free(qd->captcha.token); + g_free(qd->captcha.data); + memset(&qd->captcha, 0, sizeof(qd->captcha)); + } + break; + case QQ_CMD_CHECK_PWD: + ret_8 = qq_process_check_pwd(gc, data, data_len); + if (ret_8 != QQ_LOGIN_REPLY_OK) { + return ret_8; + } + if (qd->client_version == 2008) { + qq_request_login_2008(gc); + } else { + qq_request_login_2007(gc); + } + break; + case QQ_CMD_LOGIN: + if (qd->client_version == 2008) { + ret_8 = qq_process_login_2008(gc, data, data_len); + if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { + qq_request_get_server(gc); + return QQ_LOGIN_REPLY_OK; + } + } else if (qd->client_version == 2007) { + ret_8 = qq_process_login_2007(gc, data, data_len); + if ( ret_8 == QQ_LOGIN_REPLY_REDIRECT) { + qq_request_get_server(gc); + return QQ_LOGIN_REPLY_OK; + } + } else { + ret_8 = qq_process_login(gc, data, data_len); + } + if (ret_8 != QQ_LOGIN_REPLY_OK) { + return ret_8; + } - /* now initiate QQ Qun, do it first as it may take longer to finish */ - qq_group_init(gc); + purple_connection_update_progress(gc, _("Logined"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); + purple_debug_info("QQ", "Login repliess OK; everything is fine\n"); + purple_connection_set_state(gc, PURPLE_CONNECTED); + qd->is_login = TRUE; /* must be defined after sev_finish_login */ - /* Now goes on updating my icon/nickname, not showing info_window */ - qd->modifying_face = FALSE; + /* now initiate QQ Qun, do it first as it may take longer to finish */ + qq_room_data_initial(gc); - /* is_login, but we have packets before login */ - qq_trans_process_remained(gc); + /* is_login, but we have packets before login */ + qq_trans_process_remained(gc); - qq_update_all(gc, 0); - return; + qq_update_all(gc, 0); + break; + default: + process_unknow_cmd(gc, _("Unknow LOGIN CMD"), data, data_len, cmd, seq); + return QQ_LOGIN_REPLY_ERR; + } + return QQ_LOGIN_REPLY_OK; } -void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, +void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len, gint update_class, guint32 ship32) { qq_data *qd; @@ -553,50 +1017,56 @@ void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, switch (cmd) { case QQ_CMD_UPDATE_INFO: - qq_process_modify_info_reply(data, data_len, gc); + qq_process_change_info(gc, data, data_len); break; - case QQ_CMD_ADD_BUDDY_WO_AUTH: - qq_process_add_buddy_reply(data, data_len, seq, gc); + case QQ_CMD_ADD_BUDDY_NO_AUTH: + qq_process_add_buddy_no_auth(gc, data, data_len, ship32); break; - case QQ_CMD_DEL_BUDDY: - qq_process_remove_buddy_reply(data, data_len, gc); + case QQ_CMD_REMOVE_BUDDY: + qq_process_remove_buddy(gc, data, data_len, ship32); break; - case QQ_CMD_REMOVE_SELF: - qq_process_remove_self_reply(data, data_len, gc); + case QQ_CMD_REMOVE_ME: + qq_process_buddy_remove_me(gc, data, data_len, ship32); break; - case QQ_CMD_BUDDY_AUTH: - qq_process_add_buddy_auth_reply(data, data_len, gc); + case QQ_CMD_ADD_BUDDY_AUTH: + qq_process_add_buddy_auth(data, data_len, gc); break; case QQ_CMD_GET_BUDDY_INFO: - qq_process_get_buddy_info(data, data_len, gc); + qq_process_get_buddy_info(data, data_len, ship32, gc); break; case QQ_CMD_CHANGE_STATUS: - qq_process_change_status_reply(data, data_len, gc); + qq_process_change_status(data, data_len, gc); break; case QQ_CMD_SEND_IM: - qq_process_send_im_reply(data, data_len, gc); + do_im_ack(data, data_len, gc); break; case QQ_CMD_KEEP_ALIVE: - qq_process_keep_alive(data, data_len, gc); + if (qd->client_version >= 2008) { + qq_process_keep_alive_2008(data, data_len, gc); + } else if (qd->client_version >= 2007) { + qq_process_keep_alive_2007(data, data_len, gc); + } else { + qq_process_keep_alive(data, data_len, gc); + } break; case QQ_CMD_GET_BUDDIES_ONLINE: - ret_8 = qq_process_get_buddies_online_reply(data, data_len, gc); + ret_8 = qq_process_get_buddies_online(data, data_len, gc); if (ret_8 > 0 && ret_8 < 0xff) { purple_debug_info("QQ", "Requesting for more online buddies\n"); qq_request_get_buddies_online(gc, ret_8, update_class); return; } purple_debug_info("QQ", "All online buddies received\n"); - qq_refresh_all_buddy_status(gc); + qq_update_buddyies_status(gc); break; case QQ_CMD_GET_LEVEL: qq_process_get_level_reply(data, data_len, gc); break; case QQ_CMD_GET_BUDDIES_LIST: - ret_16 = qq_process_get_buddies_list_reply(data, data_len, gc); + ret_16 = qq_process_get_buddies(data, data_len, gc); if (ret_16 > 0 && ret_16 < 0xffff) { purple_debug_info("QQ", "Requesting for more buddies\n"); - qq_request_get_buddies_list(gc, ret_16, update_class); + qq_request_get_buddies(gc, ret_16, update_class); return; } purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n"); @@ -610,8 +1080,23 @@ void qq_proc_client_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, } purple_debug_info("QQ", "All buddies and groups received\n"); break; + case QQ_CMD_AUTH_CODE: + qq_process_auth_code(gc, data, data_len, ship32); + break; + case QQ_CMD_BUDDY_QUESTION: + qq_process_question(gc, data, data_len, ship32); + break; + case QQ_CMD_ADD_BUDDY_NO_AUTH_EX: + qq_process_add_buddy_no_auth_ex(gc, data, data_len, ship32); + break; + case QQ_CMD_ADD_BUDDY_AUTH_EX: + qq_process_add_buddy_auth_ex(gc, data, data_len, ship32); + break; + case QQ_CMD_BUDDY_CHECK_CODE: + qq_process_buddy_check_code(gc, data, data_len); + break; default: - process_cmd_unknow(gc, _("Unknow reply CMD"), data, data_len, cmd, seq); + process_unknow_cmd(gc, _("Unknow CLIENT CMD"), data, data_len, cmd, seq); is_unknow = TRUE; break; } |