void qq_process_search_uid( PurpleConnection *gc, guint8 *data, gint data_len, qq_buddy_opt_req *opt_req ) { gint bytes; guint32 uid; guint8 status; gchar * name; guint16 icon; g_return_if_fail(data != NULL && data_len != 0); g_return_if_fail(opt_req && opt_req->uid != 0); //qq_show_packet("qq_process_search_uid", data, data_len); bytes = 7; bytes += qq_get32(&uid, data + bytes); g_return_if_fail(uid == opt_req->uid); bytes ++; bytes += qq_get8(&status, data + bytes); bytes += 4; bytes += qq_get_vstr(&name, NULL, sizeof(guint8), data + bytes); bytes += qq_get16(&icon, data + bytes); bytes += 13; bytes += qq_get16(&opt_req->no_auth_len, data + bytes); if (opt_req->no_auth) { opt_req->no_auth = g_new0(guint8, opt_req->no_auth_len); bytes += qq_getdata(opt_req->no_auth, opt_req->no_auth_len, data + bytes); } qq_request_add_buddy_post(gc, opt_req, NULL); }
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; }
static void process_level(PurpleConnection *gc, guint8 *data, gint data_len) { gint bytes = 0; guint32 uid, onlineTime; guint16 level, timeRemainder; qq_buddy_data *bd; while (data_len - bytes >= 12) { bytes += qq_get32(&uid, data + bytes); bytes += qq_get32(&onlineTime, data + bytes); bytes += qq_get16(&level, data + bytes); bytes += qq_get16(&timeRemainder, data + bytes); purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n", level, uid, onlineTime, timeRemainder); bd = qq_buddy_data_find(gc, uid); if (bd == NULL) { purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid); continue; } bd->onlineTime = onlineTime; bd->level = level; bd->timeRemainder = timeRemainder; } if (bytes != data_len) { purple_debug_error("QQ", "Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes); } }
void qq_process_auth_token( PurpleConnection *gc, guint8 *data, gint data_len, guint32 dataptr, qq_buddy_opt_req *opt_req ) { 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(opt_req && opt_req->uid != 0); //qq_show_packet("qq_process_auth_token", data, data_len); bytes = 0; bytes += qq_get8(&cmd, data + bytes); bytes += qq_get16(&sub_cmd, data + bytes); bytes += qq_get8(&reply, data + bytes); /* if reply == 0x01, we need request captcha */ if (reply) { /* if this is end, means you have submitted the wrong captcha */ if (bytes>=data_len) { qq_request_auth_token(gc, QQ_AUTH_INFO_BUDDY, QQ_AUTH_INFO_ADD_BUDDY, 0, opt_req); return; } bytes += qq_get_vstr(&code, NULL, sizeof(guint16), data + bytes); purple_util_fetch_url_request( (gchar *)code, TRUE, NULL, TRUE, NULL, TRUE, auth_token_captcha_input_cb, opt_req); return; } bytes += qq_get16(&opt_req->auth_len, data + bytes); g_return_if_fail(opt_req->auth_len > 0); g_return_if_fail(bytes + opt_req->auth_len <= data_len); opt_req->auth = g_new0(guint8, opt_req->auth_len); bytes += qq_getdata(opt_req->auth, opt_req->auth_len, data + bytes); if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_REMOVE_BUDDY) { qq_request_remove_buddy(gc, opt_req); return; } if (sub_cmd == QQ_AUTH_INFO_ADD_BUDDY) { if (opt_req->auth_type == 0x01) add_buddy_authorize_input(gc, opt_req); else if (opt_req->auth_type == 0x00) qq_request_search_uid(gc, opt_req); return; } if (cmd == QQ_AUTH_INFO_BUDDY && sub_cmd == QQ_AUTH_INFO_UPDATE_BUDDY_INFO) { request_change_info(gc, (guint8 *)dataptr, 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); }
/* this header is NOT tcp header, it is QQ package header. * combines header_tag, source_tag, cmd and seq */ static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, guint16 *cmd, guint16 *seq, guint8 *buf) { gint bytes = 0; bytes += qq_get8(header_tag, buf + bytes); bytes += qq_get16(source_tag, buf + bytes); bytes += qq_get16(cmd, buf + bytes); bytes += qq_get16(seq, buf + bytes); return bytes; }
/* read the common parts of the normal_im, * returns the bytes read if succeed, or -1 if there is any error */ static gint get_im_header(qq_im_header *im_header, guint8 *data, gint len) { gint bytes; g_return_val_if_fail(data != NULL && len > 0, -1); bytes = 0; bytes += qq_get16(&(im_header->version_from), data + bytes); bytes += qq_get32(&(im_header->uid_from), data + bytes); bytes += qq_get32(&(im_header->uid_to), data + bytes); bytes += qq_getdata(im_header->session_md5, QQ_KEY_LENGTH, data + bytes); bytes += qq_get16(&(im_header->im_type), data + bytes); return bytes; }
static gint server_buddy_check_code(PurpleConnection *gc, gchar *from, guint8 *data, gint data_len) { gint bytes; guint16 code_len; guint8 *code; g_return_val_if_fail(data != NULL && data_len > 0, 0); bytes = 0; bytes += qq_get16(&code_len, data + bytes); if (code_len <= 0) { purple_debug_info("QQ", "Server msg for buddy has no code\n"); return bytes; } if (bytes + code_len < data_len) { purple_debug_error("QQ", "Code len error in server msg for buddy\n"); qq_show_packet("server_buddy_check_code", data, data_len); code_len = data_len - bytes; } code = g_newa(guint8, code_len); bytes += qq_getdata(code, code_len, data + bytes); request_buddy_check_code(gc, from, code, code_len); return bytes; }
/* data includes text msg and font attr*/ gint qq_get_im_tail(qq_im_format *fmt, guint8 *data, gint data_len) { gint bytes, text_len; guint8 tail_len; guint8 font_len; g_return_val_if_fail(fmt != NULL && data != NULL, 0); g_return_val_if_fail(data_len > 1, 0); tail_len = data[data_len - 1]; g_return_val_if_fail(tail_len > 2, 0); text_len = data_len - tail_len; g_return_val_if_fail(text_len >= 0, 0); bytes = text_len; /* qq_show_packet("IM tail", data + bytes, tail_len); */ bytes += 1; /* skip 0x00 */ bytes += qq_get8(&fmt->attr, data + bytes); bytes += qq_getdata(fmt->rgb, sizeof(fmt->rgb), data + bytes); /* red,green,blue */ bytes += 1; /* skip 0x00 */ bytes += qq_get16(&fmt->charset, data + bytes); font_len = data_len - bytes - 1; g_return_val_if_fail(font_len > 0, bytes + 1); fmt->font_len = font_len; if (fmt->font != NULL) g_free(fmt->font); fmt->font = g_strndup((gchar *)data + bytes, fmt->font_len); return tail_len; }
static void process_level_2007(PurpleConnection *gc, guint8 *data, gint data_len) { gint bytes; guint32 uid, onlineTime; guint16 level, timeRemainder; qq_buddy_data *bd; guint16 str_len; gchar *str; gchar *str_utf8; bytes = 0; bytes += qq_get32(&uid, data + bytes); bytes += qq_get32(&onlineTime, data + bytes); bytes += qq_get16(&level, data + bytes); bytes += qq_get16(&timeRemainder, data + bytes); purple_debug_info("QQ", "level: %d, uid %u, tmOnline: %d, tmRemainder: %d\n", level, uid, onlineTime, timeRemainder); bd = qq_buddy_data_find(gc, uid); if (bd == NULL) { purple_debug_error("QQ", "Got levels of %u not in my buddy list\n", uid); return; } bd->onlineTime = onlineTime; bd->level = level; bd->timeRemainder = timeRemainder; /* extend bytes in qq2007*/ bytes += 4; /* skip 8 bytes */ /* qq_show_packet("Buddies level", data + bytes, data_len - bytes); */ do { bytes += qq_get16(&str_len, data + bytes); if (str_len <= 0 || bytes + str_len > data_len) { purple_debug_error("QQ", "Wrong format of Get levels. Truncate %d bytes.\n", data_len - bytes); break; } str = g_strndup((gchar *)data + bytes, str_len); bytes += str_len; str_utf8 = qq_to_utf8(str, QQ_CHARSET_DEFAULT); purple_debug_info("QQ", "%s\n", str_utf8); g_free(str_utf8); g_free(str); } while (bytes < data_len); }
static gint _qq_get_file_header(qq_file_header *fh, guint8 *buf) { gint bytes = 0; bytes += qq_get16(&(fh->client_ver), buf + bytes); bytes += qq_get8(&fh->file_key, buf + bytes); bytes += qq_get32(&(fh->sender_uid), buf + bytes); bytes += qq_get32(&(fh->receiver_uid), buf + bytes); fh->sender_uid = _decrypt_qq_uid(fh->sender_uid, _get_file_key(fh->file_key)); fh->receiver_uid = _decrypt_qq_uid(fh->receiver_uid, _get_file_key(fh->file_key)); return bytes; }
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); }
/* process group cmd reply "search group" */ void qq_process_room_search(PurpleConnection *gc, guint8 *data, gint len, guint32 ship32) { qq_data *qd; qq_room_data rmd; PurpleChat *chat; gint bytes; guint8 search_type; guint16 unknown; g_return_if_fail(data != NULL && len > 0); qd = (qq_data *) gc->proto_data; bytes = 0; bytes += qq_get8(&search_type, data + bytes); /* now it starts with group_info_entry */ bytes += qq_get32(&(rmd.id), data + bytes); bytes += qq_get32(&(rmd.ext_id), data + bytes); bytes += qq_get8(&(rmd.type8), data + bytes); bytes += qq_get16(&(unknown), data + bytes); bytes += qq_get16(&(unknown), data + bytes); bytes += qq_get32(&(rmd.creator_uid), data + bytes); bytes += qq_get16(&(unknown), data + bytes); bytes += qq_get16(&(unknown), data + bytes); bytes += qq_get16(&(unknown), data + bytes); bytes += qq_get32(&(rmd.category), data + bytes); bytes += qq_get_vstr(&(rmd.title_utf8), QQ_CHARSET_DEFAULT, data + bytes); bytes += qq_get16(&(unknown), data + bytes); bytes += qq_get8(&(rmd.auth_type), data + bytes); bytes += qq_get_vstr(&(rmd.desc_utf8), QQ_CHARSET_DEFAULT, data + bytes); /* end of one qq_group */ if(bytes != len) { purple_debug_error("QQ", "group_cmd_search_group: Dangerous error! maybe protocol changed, notify developers!"); } if (ship32 == QQ_ROOM_SEARCH_FOR_JOIN) { chat = qq_room_find_or_new(gc, rmd.id, rmd.ext_id); g_return_if_fail(chat != NULL); qq_room_update_chat_info(chat, &rmd); qq_request_room_join(gc, &rmd); } else { add_to_roomlist(qd, &rmd); } }
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(); }
/* process the reply to get_members_info packet */ void qq_process_room_cmd_get_members_info( guint8 *data, gint len, guint32 index, PurpleConnection *gc ) { gint bytes; gint num; guint32 id, member_uid; guint16 unknown; qq_room_data *rmd; qq_buddy_data *bd; gchar *nick; g_return_if_fail(data != NULL && len > 0); /* qq_show_packet("qq_process_room_cmd_get_members_info", data, len); */ bytes = 0; bytes += qq_get32(&id, data + bytes); g_return_if_fail(id > 0); rmd = qq_room_data_find(gc, id); g_return_if_fail(rmd != NULL); num = 0; while (bytes < len) { bytes += qq_get32(&member_uid, data + bytes); g_return_if_fail(member_uid > 0); bd = qq_room_buddy_find_or_new(gc, rmd, member_uid); g_return_if_fail(bd != NULL); num++; bytes += qq_get16(&(bd->face), data + bytes); bytes += qq_get8(&(bd->age), data + bytes); bytes += qq_get8(&(bd->gender), data + bytes); /* only here use old charset GB18030 */ bytes += qq_get_vstr(&nick, QQ_CHARSET_DEFAULT, sizeof(guint8), data + bytes); bytes += qq_get16(&unknown, data + bytes); bytes += qq_get8(&(bd->ext_flag), data + bytes); bytes += qq_get8(&(bd->comm_flag), data + bytes); qq_filter_str(nick); bd->nickname = g_strdup(nick); g_free(nick); #if 0 purple_debug_info("QQ", "member [%d]: ext_flag=0x%02x, comm_flag=0x%02x, nick=%s\n", member_uid, bd->ext_flag, bd->comm_flag, bd->nickname); #endif bd->last_update = time(NULL); } if (bytes > len) { purple_debug_error("QQ", "group_cmd_get_members_info: Dangerous error! maybe protocol changed, notify developers!"); } purple_debug_info("QQ", "Group \"%s\" got %d member info\n", rmd->name, num); if (index) { qq_request_room_get_members_info(gc, id, 0, index); return; } rmd->has_got_members_info = TRUE; qq_room_conv_set_onlines(gc, rmd); }
static void _qq_process_recv_file_ctl_packet(PurpleConnection *gc, guint8 *data, gint data_len) { gint bytes ; gint decryped_bytes; qq_file_header fh; guint8 *decrypted_data; gint decrypted_len; qq_data *qd = (qq_data *) gc->proto_data; guint16 packet_type; guint16 seq; guint8 hellobyte; ft_info *info = (ft_info *) qd->xfer->data; bytes = 0; bytes += _qq_get_file_header(&fh, data + bytes); decrypted_data = g_newa(guint8, data_len); decrypted_len = qq_decrypt(decrypted_data, data, data_len, qd->session_md5); if ( decrypted_len <= 0 ) { purple_debug_error("QQ", "Error decrypt rcv file ctrl packet\n"); return; } /* only for debug info */ decryped_bytes = 16; /* skip md5 section */ decryped_bytes += qq_get16(&packet_type, decrypted_data + decryped_bytes); decryped_bytes += qq_get16(&seq, decrypted_data + decryped_bytes); decryped_bytes += 4+1+1+19+1; /* skip something */ purple_debug_info("QQ", "==> [%d] receive %s packet\n", seq, qq_get_file_cmd_desc(packet_type)); qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", decrypted_data, decrypted_len, "decrypted control packet received:"); switch (packet_type) { case QQ_FILE_CMD_NOTIFY_IP_ACK: decryped_bytes = 0; qq_get_conn_info(info, decrypted_data + decryped_bytes); /* qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PING, fh->sender_uid, 0); */ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); break; case QQ_FILE_CMD_SENDER_SAY_HELLO: /* I'm receiver, if we receive SAY_HELLO from sender, we send back the ACK */ decryped_bytes += 47; decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO, fh.sender_uid, 0); break; case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: /* I'm sender, do nothing */ break; case QQ_FILE_CMD_RECEIVER_SAY_HELLO: /* I'm sender, ack the hello packet and send the first data */ decryped_bytes += 47; decryped_bytes += qq_get8(&hellobyte, decrypted_data + decryped_bytes); qq_send_file_ctl_packet(gc, QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK, fh.sender_uid, hellobyte); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_BASIC_INFO, 0, 0, NULL, 0); break; case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: /* I'm receiver, do nothing */ break; case QQ_FILE_CMD_PING: /* I'm receiver, ack the PING */ qq_send_file_ctl_packet(gc, QQ_FILE_CMD_PONG, fh.sender_uid, 0); break; case QQ_FILE_CMD_PONG: qq_send_file_ctl_packet(gc, QQ_FILE_CMD_SENDER_SAY_HELLO, fh.sender_uid, 0); break; default: purple_debug_info("QQ", "unprocess file command %d\n", packet_type); } }
/* recv an IM from a group chat */ void qq_process_room_im(guint8 *data, gint data_len, guint32 id, PurpleConnection *gc, guint16 msg_type) { qq_data *qd; gchar *msg_smiley, *msg_fmt, *msg_utf8; gint bytes, tail_len; struct { guint32 ext_id; guint8 type8; guint32 member_uid; guint16 unknown; guint16 msg_seq; time_t send_time; guint32 version; guint16 msg_len; gchar *msg; } im_text; guint32 temp_id; guint16 content_type; guint8 frag_count, frag_index; guint16 msg_id; qq_im_format *fmt = NULL; /* at least include im_text.msg_len */ g_return_if_fail(data != NULL && data_len > 23); qd = (qq_data *) gc->proto_data; /* qq_show_packet("ROOM_IM", data, data_len); */ memset(&im_text, 0, sizeof(im_text)); bytes = 0; bytes += qq_get32(&(im_text.ext_id), data + bytes); bytes += qq_get8(&(im_text.type8), data + bytes); if(QQ_MSG_TEMP_QUN_IM == msg_type) { bytes += qq_get32(&temp_id, data + bytes); } bytes += qq_get32(&(im_text.member_uid), bytes + data); bytes += qq_get16(&im_text.unknown, data + bytes); /* 0x0001? */ bytes += qq_get16(&(im_text.msg_seq), data + bytes); bytes += qq_getime(&im_text.send_time, data + bytes); bytes += qq_get32(&im_text.version, data + bytes); bytes += qq_get16(&(im_text.msg_len), data + bytes); purple_debug_info("QQ", "Room IM, ext id %u, seq %u, version 0x%04X, len %u\n", im_text.ext_id, im_text.msg_seq, im_text.version, im_text.msg_len); if (im_text.msg_len != data_len - bytes) { purple_debug_warning("QQ", "Room IM length %d should be %d\n", im_text.msg_len, data_len - bytes); im_text.msg_len = data_len - bytes; } g_return_if_fail(im_text.msg_len > 0 && bytes + im_text.msg_len <= data_len); if(msg_type != QQ_MSG_QUN_IM_UNKNOWN) { g_return_if_fail(im_text.msg_len >= 10); bytes += qq_get16(&content_type, data + bytes); bytes += qq_get8(&frag_count, data + bytes); bytes += qq_get8(&frag_index, data + bytes); bytes += qq_get16(&msg_id, data + bytes); bytes += 4; /* skip 0x(00 00 00 00) */ purple_debug_info("QQ", "Room IM, content %d, frag %d-%d, msg id %u\n", content_type, frag_count, frag_index, msg_id); im_text.msg_len -= 10; } g_return_if_fail(im_text.msg_len > 0); /* qq_show_packet("Message", data + bytes, data_len - bytes); */ if (frag_count <= 1 || frag_count == frag_index + 1) { fmt = qq_im_fmt_new(); tail_len = qq_get_im_tail(fmt, data + bytes, data_len - bytes); im_text.msg = g_strndup((gchar *)(data + bytes), data_len - tail_len); } else { im_text.msg = g_strndup((gchar *)(data + bytes), data_len - bytes); } /* group im_group has no flag to indicate whether it has font_attr or not */ msg_smiley = qq_emoticon_to_purple(im_text.msg); if (fmt != NULL) { msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); g_free(msg_fmt); qq_im_fmt_free(fmt); } else { msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); } g_free(msg_smiley); purple_debug_info("QQ", "Room (%u) IM from %u: %s\n", im_text.ext_id, im_text.member_uid, msg_utf8); qq_room_got_chat_in(gc, id, im_text.member_uid, msg_utf8, im_text.send_time); g_free(msg_utf8); g_free(im_text.msg); }
/* process received extended (2007) text IM */ static void process_extend_im_text(PurpleConnection *gc, guint8 *data, gint len, qq_im_header *im_header) { qq_data *qd; guint16 purple_msg_type; gchar *who; gchar *msg_smiley, *msg_fmt, *msg_utf8; PurpleBuddy *buddy; qq_buddy_data *bd; gint bytes, tail_len; qq_im_format *fmt = NULL; struct { /* now comes the part for text only */ guint16 msg_seq; guint32 send_time; guint16 sender_icon; guint32 has_font_attr; guint8 unknown1[8]; guint8 fragment_count; guint8 fragment_index; guint8 msg_id; guint8 unknown2; guint8 msg_type; gchar *msg; /* no fixed length, ends with 0x00 */ guint8 fromMobileQQ; } im_text; g_return_if_fail (data != NULL && len > 0); g_return_if_fail(im_header != NULL); qd = (qq_data *) gc->proto_data; memset(&im_text, 0, sizeof(im_text)); /* qq_show_packet("Extend IM text", data, len); */ bytes = 0; bytes += qq_get16(&(im_text.msg_seq), data + bytes); bytes += qq_get32(&(im_text.send_time), data + bytes); bytes += qq_get16(&(im_text.sender_icon), data + bytes); bytes += qq_get32(&(im_text.has_font_attr), data + bytes); bytes += qq_getdata(im_text.unknown1, sizeof(im_text.unknown1), data + bytes); bytes += qq_get8(&(im_text.fragment_count), data + bytes); bytes += qq_get8(&(im_text.fragment_index), data + bytes); bytes += qq_get8(&(im_text.msg_id), data + bytes); bytes += 1; /* skip 0x00 */ bytes += qq_get8(&(im_text.msg_type), data + bytes); purple_debug_info("QQ", "IM Seq %u, id %04X, fragment %d-%d, type %d, %s\n", im_text.msg_seq, im_text.msg_id, im_text.fragment_count, im_text.fragment_index, im_text.msg_type, im_text.has_font_attr ? "exist font atrr" : ""); if (im_text.has_font_attr) { fmt = qq_im_fmt_new(); tail_len = qq_get_im_tail(fmt, data + bytes, len - bytes); im_text.msg = g_strndup((gchar *)(data + bytes), len - tail_len); } else { im_text.msg = g_strndup((gchar *)(data + bytes), len - bytes); } /* qq_show_packet("IM text", (guint8 *)im_text.msg , strlen(im_text.msg)); */ if(im_text.fragment_count == 0) im_text.fragment_count = 1; who = uid_to_purple_name(im_header->uid_from); buddy = purple_find_buddy(gc->account, who); if (buddy == NULL) { /* create no-auth buddy */ buddy = qq_buddy_new(gc, im_header->uid_from); } bd = (buddy == NULL) ? NULL : purple_buddy_get_protocol_data(buddy); if (bd != NULL) { bd->client_tag = im_header->version_from; bd->face = im_text.sender_icon; qq_update_buddy_icon(gc->account, who, bd->face); } purple_msg_type = 0; msg_smiley = qq_emoticon_to_purple(im_text.msg); if (fmt != NULL) { msg_fmt = qq_im_fmt_to_purple(fmt, msg_smiley); msg_utf8 = qq_to_utf8(msg_fmt, QQ_CHARSET_DEFAULT); g_free(msg_fmt); qq_im_fmt_free(fmt); } else { msg_utf8 = qq_to_utf8(msg_smiley, QQ_CHARSET_DEFAULT); } g_free(msg_smiley); /* send encoded to purple, note that we use im_text.send_time, * not the time we receive the message * as it may have been delayed when I am not online. */ serv_got_im(gc, who, msg_utf8, purple_msg_type, (time_t) im_text.send_time); g_free(msg_utf8); g_free(who); g_free(im_text.msg); }
static void _qq_process_recv_file_data(PurpleConnection *gc, guint8 *data, gint len) { gint bytes ; qq_file_header fh; guint16 packet_type; guint16 packet_seq; guint8 sub_type; guint32 fragment_index; guint16 fragment_len; guint32 fragment_offset; qq_data *qd = (qq_data *) gc->proto_data; ft_info *info = (ft_info *) qd->xfer->data; bytes = 0; bytes += _qq_get_file_header(&fh, data + bytes); bytes += 1; /* skip an unknown byte */ bytes += qq_get16(&packet_type, data + bytes); switch(packet_type) { case QQ_FILE_CMD_FILE_OP: bytes += qq_get16(&packet_seq, data + bytes); bytes += qq_get8(&sub_type, data + bytes); switch (sub_type) { case QQ_FILE_BASIC_INFO: bytes += 4; /* file length, we have already known it from xfer */ bytes += qq_get32(&info->fragment_num, data + bytes); bytes += qq_get32(&info->fragment_len, data + bytes); /* FIXME: We must check the md5 here, * if md5 doesn't match we will ignore * the packet or send sth as error number */ info->max_fragment_index = 0; info->window = 0; purple_debug_info("QQ", "start receiving data, %d fragments with %d length each\n", info->fragment_num, info->fragment_len); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, 0, 0, NULL, 0); break; case QQ_FILE_DATA_INFO: bytes += qq_get32(&fragment_index, data + bytes); bytes += qq_get32(&fragment_offset, data + bytes); bytes += qq_get16(&fragment_len, data + bytes); purple_debug_info("QQ", "received %dth fragment with length %d, offset %d\n", fragment_index, fragment_len, fragment_offset); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, fragment_index, packet_seq, NULL, 0); _qq_recv_file_progess(gc, data + bytes, fragment_len, fragment_index, fragment_offset); break; case QQ_FILE_EOF: purple_debug_info("QQ", "end of receiving\n"); _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP_ACK, sub_type, 0, 0, NULL, 0); break; } break; case QQ_FILE_CMD_FILE_OP_ACK: bytes += qq_get16(&packet_seq, data + bytes); bytes += qq_get8(&sub_type, data + bytes); switch (sub_type) { case QQ_FILE_BASIC_INFO: info->max_fragment_index = 0; info->window = 0; /* It is ready to send file data */ _qq_send_file_progess(gc); break; case QQ_FILE_DATA_INFO: bytes += qq_get32(&fragment_index, data + bytes); _qq_update_send_progess(gc, fragment_index); if (purple_xfer_is_completed(qd->xfer)) _qq_send_file_data_packet(gc, QQ_FILE_CMD_FILE_OP, QQ_FILE_EOF, 0, 0, NULL, 0); /* else _qq_send_file_progess(gc); */ break; case QQ_FILE_EOF: /* FIXME: OK, we can end the connection successfully */ _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0); purple_xfer_set_completed(qd->xfer, TRUE); break; } break; case QQ_FILE_EOF: _qq_send_file_data_packet(gc, QQ_FILE_EOF, 0, 0, 0, NULL, 0); purple_xfer_set_completed(qd->xfer, TRUE); purple_xfer_end(qd->xfer); break; case QQ_FILE_BASIC_INFO: purple_debug_info("QQ", "here\n"); _qq_send_file_data_packet(gc, QQ_FILE_DATA_INFO, 0, 0, 0, NULL, 0); break; default: purple_debug_info("QQ", "_qq_process_recv_file_data: unknown packet type [%d]\n", packet_type); 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; } } }
/* 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; } }
void qq_process_room_cmd_get_info(guint8 *data, gint data_len, guint32 action, PurpleConnection *gc) { qq_data *qd; qq_room_data *rmd; qq_buddy_data *bd; PurpleChat *chat; PurpleConversation *conv; guint8 organization, role; guint16 max_members; guint32 resend_flag, member_uid, id, qun_id, last_uid; gint bytes; guint num=0; guint8 has_more=0; gchar *topic; g_return_if_fail(data != NULL && data_len > 0); qd = (qq_data *) gc->proto_data; /* qq_show_packet("Room Info", data, data_len); */ bytes = 0; bytes += qq_get32(&id, data + bytes); g_return_if_fail(id > 0); bytes += qq_get32(&qun_id, data + bytes); g_return_if_fail(qun_id > 0); chat = qq_room_find_or_new(gc, id, qun_id); g_return_if_fail(chat != NULL); rmd = qq_room_data_find(gc, id); g_return_if_fail(rmd != NULL); bytes += qq_get32(&resend_flag, data + bytes); //first 00 00 00 03, then 00 00 00 02 if (resend_flag == 0x00000003) { bytes += qq_get8(&(rmd->type8), data + bytes); bytes += 4; //maybe vip sign bytes += qq_get32(&(rmd->creator_uid), data + bytes); if (rmd->creator_uid == qd->uid) rmd->my_role = QQ_ROOM_ROLE_ADMIN; bytes += qq_get8(&(rmd->auth_type), data + bytes); bytes += 4 ; /* oldCategory */ bytes += 2; // 00 00 bytes += qq_get32(&(rmd->category), data + bytes); bytes += qq_get16(&max_members, data + bytes); bytes += 1; bytes += 8; purple_debug_info("QQ", "type: %u creator: %u category: %u max_members: %u\n", rmd->type8, rmd->creator_uid, rmd->category, max_members); bytes += qq_get_vstr(&(rmd->name), NULL, sizeof(guint8), data + bytes); bytes += 2; /* 0x0000 */ bytes += qq_get_vstr(&(rmd->bulletin), NULL, sizeof(guint8), data + bytes); bytes += qq_get_vstr(&(rmd->intro), NULL, sizeof(guint8), data + bytes); bytes += qq_get_vstr(&(rmd->token), NULL, sizeof(guint16), data + bytes); purple_debug_info("QQ", "room [%s] bulletin [%s] intro [%s] \n", rmd->name, rmd->bulletin, rmd->intro); bytes += 2; //Unknown bytes += qq_get32(&last_uid, data + bytes); /* last_uid of this recv, request more with it */ bytes += qq_get8(&has_more, data + bytes); /* if there are more, request again */ /* now comes the member list separated by 0x00 */ } else { /* resend_flag 00 00 00 02 is special, start with random one only 5 bytes */ bytes += qq_get32(&member_uid, data + bytes); num++; bytes += qq_get8(&organization, data + bytes); bd = qq_room_buddy_find_or_new(gc, rmd, member_uid); } while (bytes < data_len) { bytes += qq_get32(&member_uid, data + bytes); num++; bytes += qq_get8(&organization, data + bytes); bytes += qq_get8(&role, data + bytes); #if 0 if(organization != 0 || role != 0) { purple_debug_info("QQ", "%u, organization=%d, role=%d\n", member_uid, organization, role); } #endif bd = qq_room_buddy_find_or_new(gc, rmd, member_uid); if (bd != NULL) bd->role = role; } purple_debug_info("QQ", "Qun \"%s\" has received %d members\n", rmd->name, num); if (has_more) { qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, id, NULL, 0, 0, last_uid); } else { qq_room_update_chat_info(chat, rmd); if (action == QQ_ROOM_INFO_DISPLAY) { room_info_display(gc, rmd); } conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, rmd->name, purple_connection_get_account(gc)); if(NULL == conv) { purple_debug_warning("QQ", "Conversation \"%s\" is not opened\n", rmd->name); return; } topic = g_strdup_printf("%u %s", rmd->qun_id, rmd->bulletin); purple_debug_info("QQ", "Set chat topic to %s\n", topic); purple_conv_chat_set_topic(PURPLE_CONV_CHAT(conv), NULL, topic); g_free(topic); } }