/* set seq and is_save2trans, then call send_cmd_detail */ gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) { qq_data *qd; guint8 *encrypted; gint encrypted_len; gint bytes_sent; g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); qd = (qq_data *)gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); #if 1 purple_debug_info("QQ", "<== [SRV-%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, data_len); #endif /* at most 17 bytes more */ encrypted = g_newa(guint8, data_len + 17); encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key); if (encrypted_len < 16) { purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n", encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); return -1; } bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len); qq_trans_add_server_reply(gc, cmd, seq, encrypted, encrypted_len); return bytes_sent; }
/* Encrypt data with session_key, and send packet out */ static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len, gboolean is_save2trans, UPDCLS update_class, guint32 ship32) { qq_data *qd; guint8 *encrypted; gint encrypted_len; gint bytes_sent; g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); qd = (qq_data *)gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); /* at most 17 bytes more */ encrypted = g_newa(guint8, data_len + 17); encrypted_len = qq_encrypt(encrypted, data, data_len, qd->session_key); if (encrypted_len < 16) { purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n", encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); return -1; } bytes_sent = packet_send_out(gc, cmd, seq, encrypted, encrypted_len); if (is_save2trans) { qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, update_class, ship32); } return bytes_sent; }
/* 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; } }
gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len, UPDCLS update_class, guint32 ship32) { qq_data *qd; guint16 seq; g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); qd = (qq_data *) gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); seq = ++qd->send_seq; #if 1 purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, data_len); #endif return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32); }
gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *encrypted, gint encrypted_len, gboolean is_save2trans) { gint sent_len; #if 1 /* qq_show_packet("qq_send_cmd_encrypted", encrypted, encrypted_len); */ purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, encrypted_len); #endif sent_len = packet_send_out(gc, cmd, seq, encrypted, encrypted_len); if (is_save2trans) { qq_trans_add_client_cmd(gc, cmd, seq, encrypted, encrypted_len, 0, 0); } return sent_len; }
/* set seq and is_save2trans, then call send_cmd_detail */ gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) { qq_data *qd; guint16 seq; gboolean is_save2trans; g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); qd = (qq_data *) gc->proto_data; g_return_val_if_fail(data != NULL && data_len > 0, -1); if (cmd != QQ_CMD_LOGOUT) { seq = ++qd->send_seq; is_save2trans = TRUE; } else { seq = 0xFFFF; is_save2trans = FALSE; } #if 1 purple_debug_info("QQ", "<== [%05d] %s(0x%04X), datalen %d\n", seq, qq_get_cmd_desc(cmd), cmd, data_len); #endif return send_cmd_detail(gc, cmd, seq, data, data_len, is_save2trans, 0, 0); }
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_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; } }
/* process the incoming packet from qq_pending */ static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) { qq_data *qd; gint bytes, bytes_not_read; guint8 header_tag; guint16 source_tag; guint16 cmd; guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ UID uid; guint8 header_ex_fixed[3]; /* 3 zeors */ guint8 room_cmd; guint32 room_id; UPDCLS update_class; guint32 ship32; int ret; qq_transaction *trans; g_return_val_if_fail(buf != NULL && buf_len > 0, TRUE); qd = (qq_data *) gc->proto_data; qd->net_stat.rcved++; if (qd->net_stat.rcved <= 0) memset(&(qd->net_stat), 0, sizeof(qd->net_stat)); /* Len, header and tail tag have been checked before */ bytes = 0; bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); if (qd->client_version == 2010) { bytes += packet_get_header_ex(&uid, header_ex_fixed, buf + bytes); } #if 1 purple_debug_info("QQ", "==> [%05d] %s 0x%04X, source tag 0x%04X len %d\n", seq, qq_get_cmd_desc(cmd), cmd, source_tag, buf_len); #endif /* this is the length of all the encrypted data (also remove tail tag) */ bytes_not_read = buf_len - bytes - 1; /* ack packet, we need to update send tranactions */ /* we do not check duplication for server ack */ trans = qq_trans_find_rcved(gc, cmd, seq); if (trans == NULL) { /* new server command */ if ( !qd->is_login ) { qq_trans_add_remain(gc, cmd, seq, buf + bytes, bytes_not_read); } else { qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); qq_proc_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); } return TRUE; } if (qq_trans_is_dup(trans)) { qd->net_stat.rcved_dup++; purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); return TRUE; } update_class = qq_trans_get_class(trans); ship32 = qq_trans_get_ship(trans); if (update_class != 0 || ship32 != 0) { purple_debug_info("QQ", "Update class %d, ship32 %d\n", update_class, ship32); } switch (cmd) { case QQ_CMD_TOKEN: case QQ_CMD_GET_SERVER: case QQ_CMD_TOKEN_EX: case QQ_CMD_CHECK_PWD: case QQ_CMD_LOGIN: ret = qq_proc_login_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); if (ret != QQ_LOGIN_REPLY_OK) { if (ret == QQ_LOGIN_REPLY_REDIRECT) { redirect_server(gc); } return FALSE; /* do nothing after this function and return now */ } break; case QQ_CMD_ROOM: room_cmd = qq_trans_get_room_cmd(trans); room_id = qq_trans_get_room_id(trans); qq_proc_room_cmds(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); break; default: qq_proc_client_cmds(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); break; } return TRUE; }