/* it is a normal IM, maybe text or video request */ void qq_process_im(PurpleConnection *gc, guint8 *data, gint len) { gint bytes = 0; qq_im_header im_header; g_return_if_fail (data != NULL && len > 0); bytes = get_im_header(&im_header, data, len); if (bytes < 0) { purple_debug_error("QQ", "Fail read im header, len %d\n", len); qq_show_packet ("IM Header", data, len); return; } purple_debug_info("QQ", "Got IM to %u, type: %02X from: %u ver: %s (%04X)\n", im_header.uid_to, im_header.im_type, im_header.uid_from, qq_get_ver_desc(im_header.version_from), im_header.version_from); switch (im_header.im_type) { case QQ_NORMAL_IM_TEXT: if (bytes >= len - 1) { purple_debug_warning("QQ", "Received normal IM text is empty\n"); return; } process_im_text(gc, data + bytes, len - bytes, &im_header); break; case QQ_NORMAL_IM_FILE_REJECT_UDP: qq_process_recv_file_reject(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_APPROVE_UDP: qq_process_recv_file_accept(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_REQUEST_UDP: qq_process_recv_file_request(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_CANCEL: qq_process_recv_file_cancel(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_NOTIFY: qq_process_recv_file_notify(data + bytes, len - bytes, im_header.uid_from, gc); break; case QQ_NORMAL_IM_FILE_REQUEST_TCP: /* Check ReceivedFileIM::parseContents in eva*/ /* some client use this function for detect invisable buddy*/ case QQ_NORMAL_IM_FILE_APPROVE_TCP: case QQ_NORMAL_IM_FILE_REJECT_TCP: case QQ_NORMAL_IM_FILE_PASV: case QQ_NORMAL_IM_FILE_EX_REQUEST_UDP: case QQ_NORMAL_IM_FILE_EX_REQUEST_ACCEPT: case QQ_NORMAL_IM_FILE_EX_REQUEST_CANCEL: case QQ_NORMAL_IM_FILE_EX_NOTIFY_IP: qq_show_packet ("Not support", data, len); break; default: /* a simple process here, maybe more later */ qq_show_packet ("Unknow", data + bytes, len - bytes); return; } }
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; }
void qq_process_add_buddy_no_auth(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid) { qq_data *qd; gchar **segments; gchar *dest_uid, *reply; PurpleBuddy *buddy; g_return_if_fail(data != NULL && data_len != 0); g_return_if_fail(uid != 0); qd = (qq_data *) gc->proto_data; purple_debug_info("QQ", "Process buddy add for id [%u]\n", uid); qq_show_packet("buddy_add_no_auth", data, data_len); if (NULL == (segments = split_data(data, data_len, "\x1f", 2))) return; dest_uid = segments[0]; reply = segments[1]; if (strtoul(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) { /* add OK */ 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); purple_debug_info("QQ", "Successed adding into %u's buddy list", uid); g_strfreev(segments); return; } /* 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; } add_buddy_authorize_input(gc, uid, NULL, 0); g_strfreev(segments); }
static void do_got_sms(PurpleConnection *gc, guint8 *data, gint data_len) { gint bytes; gchar *mobile = NULL; gchar *msg = NULL; gchar *msg_utf8 = NULL; gchar *msg_formated; g_return_if_fail(data != NULL && data_len > 26); qq_show_packet("Rcv sms", data, data_len); bytes = 0; bytes += 1; /* skip 0x00 */ mobile = g_strndup((gchar *)data + bytes, 20); bytes += 20; bytes += 5; /* skip 0x(49 11 98 d5 03)*/ if (bytes < data_len) { msg = g_strndup((gchar *)data + bytes, data_len - bytes); msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT); g_free(msg); } else { msg_utf8 = g_strdup(""); } msg_formated = g_strdup_printf(_("%s:%s"), mobile, msg_utf8); qq_got_message(gc, msg_formated); g_free(msg_formated); g_free(msg_utf8); g_free(mobile); }
static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, guint8 *data, gint data_len) { gint bytes = 0; g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); g_return_val_if_fail(data != NULL && data_len > 0, -1); /* QQ TCP packet has two bytes in the begining defines packet length * so leave room here to store packet size */ if (qd->use_tcp) { bytes += qq_put16(buf + bytes, 0x0000); } /* now comes the normal QQ packet as UDP */ bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); bytes += qq_put16(buf + bytes, qd->client_tag); bytes += qq_put16(buf + bytes, cmd); bytes += qq_put16(buf + bytes, seq); bytes += qq_put32(buf + bytes, qd->uid); if(qd->client_version == 2010) { bytes += qq_putdata(buf + bytes, qd->vd.sig1, VD_SIG1_LEN); } bytes += qq_putdata(buf + bytes, data, data_len); bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); /* set TCP packet length at begin of the packet */ if (qd->use_tcp) { qq_put16(buf, bytes); } #if 1 qq_show_packet("packet_encap", buf, bytes); #endif return bytes; }
void qq_process_buddy_check_code(PurpleConnection *gc, guint8 *data, gint data_len) { qq_data *qd; gint bytes; guint8 cmd; guint8 reply; guint32 uid; guint16 flag1, flag2; g_return_if_fail(data != NULL && data_len >= 5); qd = (qq_data *) gc->proto_data; 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 %u, flag 0x%04X-0x%04X\n", uid, flag1, flag2); return; }
void qq_process_add_buddy_touch( PurpleConnection *gc, guint8 *data, gint data_len, qq_buddy_opt_req *opt_req ) { qq_data *qd; gint bytes; guint32 dest_uid; guint8 reply; g_return_if_fail(data != NULL && data_len >= 5); g_return_if_fail(opt_req && opt_req->uid != 0); qd = (qq_data *) gc->proto_data; purple_debug_info("QQ", "Process buddy add no auth for id [%u]\n", opt_req->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 == opt_req->uid); if (reply == 0x99) { purple_debug_info("QQ", "Successfully added buddy %u\n", opt_req->uid); qq_buddy_find_or_new(gc, opt_req->uid, opt_req->group_id); qq_request_get_buddy_info(gc, opt_req->uid, 0, 0); qq_request_get_level(gc, opt_req->uid); qq_request_get_buddies_online(gc, 0, 0); return; } if (reply != 0) { purple_debug_info("QQ", "Failed adding buddy %u, Unknown reply 0x%02X\n", opt_req->uid, reply); } /* need auth */ g_return_if_fail(data_len > bytes); bytes += qq_get8(&opt_req->auth_type, data + bytes); purple_debug_warning("QQ", "Adding buddy needs authorize 0x%02X\n", opt_req->auth_type); switch (opt_req->auth_type) { case 0x00: /* no authorize */ case 0x01: /* authorize */ qq_request_auth_token(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, 0, opt_req); break; case 0x02: /* disable */ break; case 0x03: /* answer question */ qq_request_question(gc, QQ_QUESTION_REQUEST, opt_req->uid, NULL, NULL); break; case 0x04: /* deny! */ break; default: g_return_if_reached(); break; } return; }
/* from QQ2010 on, extra info should be taken out */ static gint packet_get_header_ex(UID *uid, guint8 *header_ex_fixed, guint8 *buf) { gint bytes = 0; bytes += qq_get32(uid, buf + bytes); bytes += qq_getdata(header_ex_fixed, 3, buf + bytes); qq_show_packet("packet_get_header_ex", header_ex_fixed, 3); purple_debug_info("QQ", "packet_get_header_ex - UID:%u\n", *uid); return bytes; }
/* you are rejected by the person */ static void server_buddy_rejected_me(PurpleConnection *gc, gchar *from, gchar *to, guint8 *data, gint data_len) { guint32 uid; PurpleBuddy *buddy; gchar *msg, *msg_utf8; gint bytes; gchar **segments; gchar *primary, *secondary; qq_buddy_data *bd; g_return_if_fail(from != NULL && to != NULL); qq_show_packet("server_buddy_rejected_me", data, data_len); 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 = strtoul(from, NULL, 10); g_return_if_fail(uid != 0); buddy = qq_buddy_find(gc, uid); if (buddy != NULL && (bd = purple_buddy_get_protocol_data(buddy)) != NULL) { /* Not authorized now, free buddy data */ qq_buddy_data_free(bd); purple_buddy_set_protocol_data(buddy, NULL); } }
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); }
/* default process, decrypt and dump */ static void process_unknown_cmd(PurpleConnection *gc,const gchar *title, guint8 *data, gint data_len, guint16 cmd, guint16 seq) { gchar *msg; g_return_if_fail(data != NULL && data_len != 0); qq_show_packet(title, data, data_len); qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, ">>> [%d] %s -> [default] decrypt and dump", seq, qq_get_cmd_desc(cmd)); msg = g_strdup_printf("Unknown command 0x%02X, %s", cmd, qq_get_cmd_desc(cmd)); //purple_notify_info(gc, _("QQ Error"), title, msg); g_free(msg); }
void qq_proc_server_cmd(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len) { qq_data *qd; guint8 *data; gint data_len; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; data = g_newa(guint8, rcved_len); data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); if (data_len < 0) { purple_debug_warning("QQ", "Can not decrypt server cmd by session key, [%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); return; } if (data_len <= 0) { purple_debug_warning("QQ", "Server cmd decrypted is empty, [%05d], 0x%04X %s, len %d\n", seq, cmd, qq_get_cmd_desc(cmd), rcved_len); return; } /* now process the packet */ switch (cmd) { case QQ_CMD_RECV_IM_CE: case QQ_CMD_RECV_IM: process_private_msg(data, data_len, cmd, seq, gc); break; case QQ_CMD_RECV_MSG_SYS: 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_unknown_cmd(gc, _("Unknown SERVER CMD"), data, data_len, cmd, seq); break; } }
static void server_buddy_added(PurpleConnection *gc, gchar *from, gchar *to, guint8 *data, gint data_len) { guint32 uid; g_return_if_fail(from != NULL && to != NULL); g_return_if_fail(data != NULL); qq_show_packet("server_buddy_added", data, data_len); purple_debug_info("QQ", "Buddy %s added \n", from); //server_buddy_check_code(gc, from, data + bytes, data_len - bytes); uid = purple_name_to_uid(from); qq_buddy_find_or_new(gc, uid, 0xFF); qq_request_get_buddy_info(gc, uid, 0, 0); qq_request_get_buddies_online(gc, 0, 0); qq_request_get_level(gc, uid); }
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); }
/* Warning: do not return NULL */ static gchar *do_convert(const gchar *str, gssize len, const gchar *to_charset, const gchar *from_charset) { GError *error = NULL; gchar *ret; gsize byte_read, byte_write; g_return_val_if_fail(str != NULL && to_charset != NULL && from_charset != NULL, g_strdup(QQ_NULL_MSG)); ret = g_convert(str, len, to_charset, from_charset, &byte_read, &byte_write, &error); if (error == NULL) { return ret; /* convert is OK */ } /* convert error */ purple_debug_error("QQ_CONVERT", "%s\n", error->message); qq_show_packet("Dump failed text", (guint8 *) str, (len == -1) ? strlen(str) : len); g_error_free(error); return g_strdup(QQ_NULL_MSG); }
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); }
guint8 qq_proc_login_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len, guint32 update_class, guintptr ship_value) { qq_data *qd; guint8 *data = NULL; gint data_len = 0; guint ret_8 = QQ_LOGIN_REPLY_ERR; 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); switch (cmd) { case QQ_CMD_TOUCH_SERVER: case QQ_CMD_CAPTCHA: data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.random_key); break; case QQ_CMD_AUTH: 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.keys[4]); if (data_len >= 0) { purple_debug_warning("QQ", "Decrypt login packet by auth_key1, %d bytes\n", data_len); } } break; case QQ_CMD_VERIFY_DE: data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.keys[0]); break; case QQ_CMD_VERIFY_E5: data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.keys[1]); break; case QQ_CMD_VERIFY_E3: data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.keys[3]); break; case QQ_CMD_LOGIN: data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.keys[2]); if (data_len >= 0) { purple_debug_info("QQ", "Decrypt login packet by Key0_VerifyE5\n"); } else { /* network condition may has changed. please sign in again. */ data_len = qq_decrypt(data, rcved, rcved_len, qd->ld.keys[0]); if (data_len >= 0) { purple_debug_info("QQ", "Decrypt login packet rarely by Key2_Auth\n"); } } break; case QQ_CMD_LOGIN_E9: case QQ_CMD_LOGIN_EA: case QQ_CMD_LOGIN_GETLIST: case QQ_CMD_LOGIN_ED: case QQ_CMD_LOGIN_EC: default: data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); break; } 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, _("Unable to decrypt login reply")); return QQ_LOGIN_REPLY_ERR; } switch (cmd) { case QQ_CMD_TOUCH_SERVER: ret_8 = qq_process_touch_server(gc, data, data_len); if ( ret_8 == QQ_LOGIN_REPLY_OK) { qq_request_captcha(gc); } else if ( ret_8 == QQ_TOUCH_REPLY_REDIRECT) { return QQ_TOUCH_REPLY_REDIRECT; } break; case QQ_CMD_CAPTCHA: ret_8 = qq_process_captcha(gc, data, data_len); if (ret_8 == QQ_LOGIN_REPLY_OK) { qq_request_auth(gc); } else if (ret_8 == QQ_LOGIN_REPLY_NEXT_CAPTCHA) { qq_request_captcha_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_AUTH: ret_8 = qq_process_auth(gc, data, data_len); if (ret_8 == QQ_LOGIN_REPLY_DE) { qq_request_verify_DE(gc); } else if (ret_8 == QQ_LOGIN_REPLY_OK) { qq_request_verify_E5(gc); } else return ret_8; break; case QQ_CMD_VERIFY_DE: ret_8 = qq_process_verify_DE(gc, data, data_len); if (ret_8 != QQ_LOGIN_REPLY_OK) { return ret_8; } qq_request_verify_E5(gc); break; case QQ_CMD_VERIFY_E5: ret_8 = qq_process_verify_E5(gc, data, data_len); if (ret_8 != QQ_LOGIN_REPLY_OK) { return ret_8; } qq_request_verify_E3(gc); break; case QQ_CMD_VERIFY_E3: ret_8 = qq_process_verify_E3(gc, data, data_len); if (ret_8 != QQ_LOGIN_REPLY_OK) { return ret_8; } qq_request_login(gc); break; case QQ_CMD_LOGIN: ret_8 = qq_process_login(gc, data, data_len); if ( ret_8 == QQ_TOUCH_REPLY_REDIRECT) { qq_request_touch_server(gc); return QQ_LOGIN_REPLY_OK; } if (ret_8 == QQ_LOGIN_REPLY_OK) { qq_request_login_E9(gc); } else { return ret_8; } break; case QQ_CMD_LOGIN_E9: qq_request_login_EA(gc); break; case QQ_CMD_LOGIN_EA: qq_request_login_getlist(gc, 0x0001); break; case QQ_CMD_LOGIN_GETLIST: ret_8 = qq_process_login_getlist(gc, data, data_len); if (ret_8 == QQ_LOGIN_REPLY_OK) { qq_request_login_ED(gc); } break; case QQ_CMD_LOGIN_EC: break; case QQ_CMD_LOGIN_ED: qq_request_login_EC(gc); purple_connection_update_progress(gc, _("Logging in"), QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); purple_debug_info("QQ", "Login replies OK; everything is fine\n"); purple_connection_set_state(gc, PURPLE_CONNECTED); qd->is_login = TRUE; /* must be defined after sev_finish_login */ /* is_login, but we have packets before login */ qq_trans_process_remained(gc); qq_update_all(gc, 0); break; default: process_unknown_cmd(gc, _("Unknown LOGIN CMD"), data, data_len, cmd, seq); return QQ_LOGIN_REPLY_ERR; } return QQ_LOGIN_REPLY_OK; }
void qq_proc_room_cmds(PurpleConnection *gc, guint16 seq, guint8 room_cmd, guint32 room_id, guint8 *rcved, gint rcved_len, guint32 update_class, guintptr ship_value) { qq_data *qd; guint8 *data; gint data_len; qq_room_data *rmd; gint bytes; guint8 reply_cmd, reply; g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; data = g_newa(guint8, rcved_len); data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); if (data_len < 0) { purple_debug_warning("QQ", "Can not decrypt room cmd by session key, [%05d], 0x%02X %s for %d, len %d\n", seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); qq_show_packet("Can not decrypted", rcved, rcved_len); return; } if (room_id <= 0) { purple_debug_warning("QQ", "room id is 0, [%05d], 0x%02X %s for %d, len %d\n", seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); /* Some room cmd has no room id, like QQ_ROOM_CMD_SEARCH */ } if (data_len <= 2) { purple_debug_warning("QQ", "Invaild len of room cmd decrypted, [%05d], 0x%02X %s for %d, len %d\n", seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); return; } bytes = 0; bytes += qq_get8(&reply_cmd, data + bytes); bytes += qq_get8(&reply, data + bytes); if (reply_cmd != room_cmd) { purple_debug_warning("QQ", "Missing room cmd in reply 0x%02X %s, [%05d], 0x%02X %s for %d, len %d\n", reply_cmd, qq_get_room_cmd_desc(reply_cmd), seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, rcved_len); } /* now process the packet */ if (reply != QQ_ROOM_CMD_REPLY_OK) { switch (reply) { /* this should be all errors */ case QQ_ROOM_CMD_REPLY_NOT_MEMBER: 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", "Not a member of room \"%s\"\n", rmd->name); rmd->my_role = QQ_ROOM_ROLE_NO; } break; case QQ_ROOM_CMD_REPLY_SEARCH_ERROR: if (qd->roomlist != NULL) { if (purple_roomlist_get_in_progress(qd->roomlist)) purple_roomlist_set_in_progress(qd->roomlist, FALSE); } default: process_room_cmd_notify(gc, reply_cmd, room_id, reply, data + bytes, data_len - bytes); } return; } /* seems ok so far, so we process the reply according to sub_cmd */ switch (reply_cmd) { case QQ_ROOM_CMD_GET_QUN_LIST: qq_process_room_cmd_get_qun_list(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_GET_INFO: qq_process_room_cmd_get_info(data + bytes, data_len - bytes, ship_value, gc); break; case QQ_ROOM_CMD_CREATE: qq_group_process_create_group_reply(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_CHANGE_INFO: qq_group_process_modify_info_reply(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_MEMBER_OPT: qq_group_process_modify_members_reply(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_ACTIVATE: qq_group_process_activate_group_reply(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_SEARCH: qq_process_room_search(gc, data + bytes, data_len - bytes, ship_value); break; case QQ_ROOM_CMD_JOIN: qq_process_group_cmd_join_group(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_AUTH: qq_process_group_cmd_join_group_auth(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_QUIT: qq_process_group_cmd_exit_group(data + bytes, data_len - bytes, gc); break; case QQ_ROOM_CMD_SEND_IM: 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); break; case QQ_ROOM_CMD_GET_MEMBERS_INFO: qq_process_room_cmd_get_members_info(data + bytes, data_len - bytes, ship_value, gc); break; default: purple_debug_warning("QQ", "Unknown room cmd 0x%02X %s\n", reply_cmd, qq_get_room_cmd_desc(reply_cmd)); } if (update_class == QQ_CMD_CLASS_NONE) return; if (update_class == QQ_CMD_CLASS_UPDATE_ALL) { qq_update_all_rooms(gc, room_cmd, room_id); return; } if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) { update_all_rooms_online(gc, room_cmd, room_id); return; } if (update_class == QQ_CMD_CLASS_UPDATE_ROOM) { qq_update_room(gc, room_cmd, room_id); } }
static void process_server_msg(PurpleConnection *gc, guint8 *data, gint data_len, guint16 seq) { qq_data *qd; guint8 *data_str, i = 0; gchar **segments, **seg; 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((gchar *) data_str, "\x1f", 0); g_return_if_fail(segments != NULL); for (seg = segments; *seg != NULL; seg++) i++; if (i < 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; /* qq_show_packet("Server MSG", data, data_len); */ if (strtoul(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_DEPRECATED: case QQ_SERVER_BUDDY_ADDED_ME: case QQ_SERVER_BUDDY_ADD_REQUEST_DEPRECATED: case QQ_SERVER_BUDDY_REJECTED_ME: case QQ_SERVER_BUDDY_ADD_REQUEST: case QQ_SERVER_BUDDY_ADDING_EX: case QQ_SERVER_BUDDY_ADDED_ANSWER: case QQ_SERVER_BUDDY_ADDED: 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); }
/* I receive a message, mainly it is text msg, * but we need to process other types (group etc) */ static void process_private_msg(guint8 *data, gint data_len, guint16 cmd, guint16 seq, PurpleConnection *gc) { qq_data *qd; gint bytes; guint16 len; guint8 tmp8; 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, cmd, 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 %u, 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_BUDDY_84: case QQ_MSG_BUDDY_85: purple_debug_info("QQ", "MSG from buddy [%d]\n", header.uid_from); qq_process_im(gc, data + bytes, data_len - bytes, header.msg_type); break; case QQ_MSG_TO_UNKNOWN: case QQ_MSG_BUDDY_09: case QQ_MSG_BUDDY_A6: case QQ_MSG_BUDDY_A7: case QQ_MSG_BUDDY_78: purple_debug_info("QQ ", "MSG from buddy [%d]\n", header.uid_from); bytes += 1; qq_get8(&tmp8, data+bytes); while (tmp8 == 0) { bytes += 1; bytes += qq_get16(&len, data+bytes); bytes += len; qq_get8(&tmp8, data+bytes); } qq_process_im(gc, data + bytes, data_len - bytes, header.msg_type); break; case QQ_MSG_TYPING: qq_process_typing(gc, data+bytes, data_len-bytes, header.uid_from); break; case QQ_MSG_NEWS: do_server_news(gc, data + bytes, data_len - bytes); break; case QQ_MSG_SMS: do_got_sms(gc, data + bytes, data_len - bytes); break; case QQ_MSG_ROOM_IM_UNKNOWN: case QQ_MSG_TEMP_ROOM_IM: case QQ_MSG_ROOM_IM: case QQ_MSG_ROOM_IM_52: 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_ROOM: 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_ROOM: 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_ROOM: 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_ROOM: 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_ROOM: 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: do_msg_sys(gc, data + bytes, data_len - bytes); break; default: purple_debug_warning("QQ", "MSG from %u, unknown type %s [0x%04X]\n", header.uid_from, get_im_type_desc(header.msg_type), header.msg_type); qq_show_packet("MSG header", data, bytes); if (data_len - bytes > 0) { qq_show_packet("MSG data", data + bytes, data_len - bytes); } break; } }
static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) { PurpleConnection *gc = (PurpleConnection *) data; qq_data *qd; qq_connection *conn; guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ gint buf_len; gint bytes; guint8 *pkt; guint16 pkt_len; gchar *error_msg; guint8 *jump; gint jump_len; g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; if(cond != PURPLE_INPUT_READ) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Socket error")); return; } conn = connection_find(qd, source); g_return_if_fail(conn != NULL); /* test code, not using tcp_rxqueue memset(pkt,0, sizeof(pkt)); buf_len = read(qd->fd, pkt, sizeof(pkt)); if (buf_len > 2) { packet_process(gc, pkt + 2, buf_len - 2); } return; */ buf_len = read(source, buf, sizeof(buf)); if (buf_len < 0) { if (errno == EAGAIN) /* No worries */ return; error_msg = g_strdup_printf(_("Lost connection with server: %s"), g_strerror(errno)); purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); g_free(error_msg); return; } else if (buf_len == 0) { purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Server closed the connection")); return; } /* keep alive will be sent in 30 seconds since last_receive * QQ need a keep alive packet in every 60 seconds gc->last_received = time(NULL); */ #if 1 purple_debug_info("TCP_PENDING", "Read %d bytes, tcp_rxlen is %d\n", buf_len, conn->tcp_rxlen); #endif conn->tcp_rxqueue = g_realloc(conn->tcp_rxqueue, buf_len + conn->tcp_rxlen); memcpy(conn->tcp_rxqueue + conn->tcp_rxlen, buf, buf_len); conn->tcp_rxlen += buf_len; pkt = g_newa(guint8, MAX_PACKET_SIZE); while (PURPLE_CONNECTION_IS_VALID(gc)) { if (qd->openconns == NULL) { break; } if (conn->tcp_rxqueue == NULL) { conn->tcp_rxlen = 0; break; } if (conn->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { break; } bytes = 0; bytes += qq_get16(&pkt_len, conn->tcp_rxqueue + bytes); if (conn->tcp_rxlen < pkt_len) { break; } #if 1 qq_show_packet("tcp_pending", conn->tcp_rxqueue, pkt_len); #endif /* purple_debug_info("TCP_PENDING", "Packet len=%d, rxlen=%d\n", pkt_len, conn->tcp_rxlen); */ if ( pkt_len < QQ_TCP_HEADER_LENGTH || *(conn->tcp_rxqueue + bytes) != QQ_PACKET_TAG || *(conn->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { /* HEY! This isn't even a QQ. What are you trying to pull? */ purple_debug_warning("TCP_PENDING", "Packet error, no header or tail tag\n"); jump = memchr(conn->tcp_rxqueue + 1, QQ_PACKET_TAIL, conn->tcp_rxlen - 1); if ( !jump ) { purple_debug_warning("TCP_PENDING", "Failed to find next tail, clear receive buffer\n"); g_free(conn->tcp_rxqueue); conn->tcp_rxqueue = NULL; conn->tcp_rxlen = 0; return; } /* jump and over QQ_PACKET_TAIL */ jump_len = (jump - conn->tcp_rxqueue) + 1; purple_debug_warning("TCP_PENDING", "Find next tail at %d, jump %d\n", jump_len, jump_len + 1); g_memmove(conn->tcp_rxqueue, jump, conn->tcp_rxlen - jump_len); conn->tcp_rxlen -= jump_len; continue; } /* get packet */ memset(pkt, 0, MAX_PACKET_SIZE); g_memmove(pkt, conn->tcp_rxqueue + bytes, pkt_len - bytes); /* jump to next packet */ conn->tcp_rxlen -= pkt_len; if (conn->tcp_rxlen) { /* purple_debug_info("TCP_PENDING", "shrink tcp_rxqueue to %d\n", conn->tcp_rxlen); */ jump = g_memdup(conn->tcp_rxqueue + pkt_len, conn->tcp_rxlen); g_free(conn->tcp_rxqueue); conn->tcp_rxqueue = jump; } else { /* purple_debug_info("TCP_PENDING", "free tcp_rxqueue\n"); */ g_free(conn->tcp_rxqueue); conn->tcp_rxqueue = NULL; } /* packet_process may call disconnect and destory data like conn * do not call packet_process before jump, * break if packet_process return FALSE */ if (packet_process(gc, pkt, pkt_len - bytes) == FALSE) { purple_debug_info("TCP_PENDING", "Connection has been destory\n"); break; } } }
void qq_proc_client_cmds(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *rcved, gint rcved_len, guint32 update_class, guintptr ship_value) { qq_data *qd; guint8 *data; gint data_len; guint8 ret_8 = 0; guint16 ret_16 = 0; guint32 ret_32 = 0; gboolean not_to_update = FALSE; g_return_if_fail(rcved_len > 0); g_return_if_fail (gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; data = g_newa(guint8, rcved_len); data_len = qq_decrypt(data, rcved, rcved_len, qd->session_key); if (data_len < 0) { purple_debug_warning("QQ", "Reply can not be decrypted by session key, [%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); return; } if (data_len <= 0) { purple_debug_warning("QQ", "Reply decrypted is empty, [%05d], 0x%04X %s, len %d\n", seq, cmd, qq_get_cmd_desc(cmd), rcved_len); return; } switch (cmd) { case QQ_CMD_UPDATE_INFO: qq_process_change_info(gc, data, data_len); break; case QQ_CMD_REMOVE_BUDDY: qq_process_remove_buddy(gc, data, data_len, ship_value); break; case QQ_CMD_REMOVE_ME: qq_process_buddy_remove_me(gc, data, data_len, ship_value); break; case QQ_CMD_GET_BUDDY_INFO: qq_process_get_buddy_info(data, data_len, ship_value, gc); break; case QQ_CMD_CHANGE_STATUS: qq_process_change_status(data, data_len, gc); break; case QQ_CMD_SEND_IM: do_im_ack(data, data_len, gc); break; case QQ_CMD_KEEP_ALIVE: if (qd->client_version >= 2010) { qq_process_keep_alive(data, data_len, gc); } break; case QQ_CMD_GET_BUDDIES_ONLINE: 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_update_buddies_status(gc); break; case QQ_CMD_GET_LEVEL: qq_process_get_level_reply(data, data_len, gc); if (ship_value) { purple_debug_info("QQ", "Requesting Buddy Level pos: %d\n", ship_value); qq_request_get_buddies_level(gc, 0, ship_value); } break; case QQ_CMD_GET_BUDDIES_SIGN: qq_process_get_buddies_sign(data, data_len, gc); if (ship_value) { purple_debug_info("QQ", "Requesting Buddy Signature pos: %d\n", ship_value); qq_request_get_buddies_sign(gc, 0, ship_value); } break; case QQ_CMD_GET_GROUP_LIST: ret_32 = qq_process_get_group_list(data, data_len, gc); /* if still have remained group name */ if (ret_32) { purple_debug_info("QQ", "Requesting for Group pos: %d\n", ret_32); qq_request_get_group_list(gc, ret_32, 0); not_to_update = TRUE; //not to update else when get_group not finished } break; case QQ_CMD_GET_BUDDIES_LIST: ret_16 = qq_process_get_buddies_list(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); return; } purple_debug_info("QQ", "All buddies received. Requesting buddies' levels\n"); break; case QQ_CMD_SEARCH_UID: qq_process_search_uid(gc, data, data_len, ship_value); break; case QQ_CMD_AUTH_TOKEN: qq_process_auth_token(gc, data, data_len, update_class, ship_value); break; case QQ_CMD_BUDDY_QUESTION: qq_process_question(gc, data, data_len, ship_value); break; case QQ_CMD_ADD_BUDDY_TOUCH: qq_process_add_buddy_touch(gc, data, data_len, ship_value); break; case QQ_CMD_ADD_BUDDY_POST: qq_process_add_buddy_post(gc, data, data_len, ship_value); break; /*case QQ_CMD_BUDDY_CHECK_CODE: qq_process_buddy_check_code(gc, data, data_len); break;*/ case QQ_CMD_BUDDY_MEMO: purple_debug_info("QQ", "Receive memo from server!\n"); qq_process_get_buddy_memo(gc, data, data_len, update_class, ship_value); break; default: process_unknown_cmd(gc, _("Unknown CLIENT CMD"), data, data_len, cmd, seq); not_to_update = TRUE; break; } if (not_to_update) return; 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) { qq_update_all(gc, cmd); return; } if (update_class == QQ_CMD_CLASS_UPDATE_ONLINE) { qq_update_online(gc, cmd); return; } }
void qq_process_add_buddy_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 ship32) { g_return_if_fail(data != NULL && data_len != 0); qq_show_packet("qq_process_question", data, data_len); }
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(); }
void qq_process_add_buddy_no_auth_ex(PurpleConnection *gc, guint8 *data, gint data_len, guint32 uid) { qq_data *qd; 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); qd = (qq_data *) gc->proto_data; purple_debug_info("QQ", "Process buddy add no auth for id [%u]\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 %u\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; } if (reply != 0) { purple_debug_info("QQ", "Failed adding buddy %u, Unknow reply 0x%02X\n", uid, reply); } /* 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); 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; }