Ejemplo n.º 1
0
/* 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;
}
Ejemplo n.º 2
0
/* 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;
}
Ejemplo n.º 3
0
/* 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);
}
Ejemplo n.º 4
0
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;
	}
}
Ejemplo n.º 5
0
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);
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
/* 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);
}
Ejemplo n.º 8
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;
}
Ejemplo n.º 9
0
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;
	}
}
Ejemplo n.º 10
0
/* 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;
}