static void request_room_send_im_ex(PurpleConnection *gc, guint32 room_id, qq_im_format *fmt, gchar *msg, guint16 msg_id, guint8 frag_count, guint8 frag_index) { guint8 raw_data[MAX_PACKET_SIZE - 16]; gint bytes; g_return_if_fail(room_id != 0 && msg != NULL); bytes = 0; bytes += qq_put16(raw_data + bytes, 0); /* packet len */ /* type 0x0001, text only; 0x0002, with custom emoticon */ bytes += qq_put16(raw_data + bytes, 0x0001); bytes += qq_put8(raw_data + bytes, frag_count); bytes += qq_put8(raw_data + bytes, frag_index); bytes += qq_put16(raw_data + bytes, msg_id); bytes += qq_put32(raw_data + bytes, 0); /* unknow 4 bytes */ bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); if (frag_count == frag_index + 1) { bytes += qq_put8(raw_data + bytes, 0x20); /* add extra SPACE */ bytes += qq_put_im_tail(raw_data + bytes, fmt); } /* reset first two bytes as length */ qq_put16(raw_data, bytes - 2); /*qq_show_packet("QQ_ROOM_CMD_SEND_IM_EX", raw_data, bytes); */ qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM_EX, room_id, raw_data, bytes); }
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; }
/* send an IM to uid_to */ static void request_send_im(PurpleConnection *gc, guint32 uid_to, gint type, qq_im_format *fmt, gchar *msg, guint8 id, guint8 frag_count, guint8 frag_index) { qq_data *qd; guint8 raw_data[MAX_PACKET_SIZE - 16]; guint16 im_type; gint bytes; time_t now; qd = (qq_data *) gc->proto_data; im_type = QQ_NORMAL_IM_TEXT; /* purple_debug_info("QQ", "Send IM %d-%d\n", frag_count, frag_index); */ bytes = 0; /* 000-003: receiver uid */ bytes += qq_put32(raw_data + bytes, qd->uid); /* 004-007: sender uid */ bytes += qq_put32(raw_data + bytes, uid_to); /* 008-009: sender client version */ bytes += qq_put16(raw_data + bytes, qd->client_tag); /* 010-013: receiver uid */ bytes += qq_put32(raw_data + bytes, qd->uid); /* 014-017: sender uid */ bytes += qq_put32(raw_data + bytes, uid_to); /* 018-033: md5 of (uid+session_key) */ bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); /* 034-035: message type */ bytes += qq_put16(raw_data + bytes, QQ_NORMAL_IM_TEXT); /* 036-037: sequence number */ bytes += qq_put16(raw_data + bytes, qd->send_seq); /* 038-041: send time */ now = time(NULL); bytes += qq_put32(raw_data + bytes, (guint32) now); /* 042-043: sender icon */ bytes += qq_put16(raw_data + bytes, qd->my_icon); /* 044-046: always 0x00 */ bytes += qq_put16(raw_data + bytes, 0x0000); bytes += qq_put8(raw_data + bytes, 0x00); /* 047-047: always use font attr */ bytes += qq_put8(raw_data + bytes, 0x01); /* 048-051: always 0x00 */ /* Fixme: frag_count, frag_index not working now */ bytes += qq_put8(raw_data + bytes, frag_count); bytes += qq_put8(raw_data + bytes, frag_index); bytes += qq_put8(raw_data + bytes, id); bytes += qq_put8(raw_data + bytes, 0); /* 052-052: text message type (normal/auto-reply) */ bytes += qq_put8(raw_data + bytes, type); /* 053- : msg ends with 0x00 */ bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); if (frag_count == frag_index + 1) { bytes += qq_put8(raw_data + bytes, 0x20); /* add extra SPACE */ } bytes += qq_put_im_tail(raw_data + bytes, fmt); /* qq_show_packet("QQ_CMD_SEND_IM", raw_data, bytes); */ qq_send_cmd(gc, QQ_CMD_SEND_IM, raw_data, bytes); }
static gint _qq_send_file(PurpleConnection *gc, guint8 *data, gint len, guint16 packet_type, guint32 to_uid) { guint8 *raw_data; gint bytes = 0; guint32 file_key; qq_data *qd; qd = (qq_data *) gc->proto_data; raw_data = g_newa(guint8, MAX_PACKET_SIZE); file_key = _gen_file_key(); bytes += qq_put8(raw_data + bytes, packet_type); bytes += qq_put16(raw_data + bytes, qd->client_tag); bytes += qq_put8(raw_data + bytes, file_key & 0xff); bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(qd->uid, file_key)); bytes += qq_put32(raw_data + bytes, _encrypt_qq_uid(to_uid, file_key)); bytes += qq_putdata(raw_data + bytes, data, len); if (bytes == len + 12) { _qq_xfer_write(raw_data, bytes, qd->xfer); } else purple_debug_info("QQ", "send_file: want %d but got %d\n", len + 12, bytes); return bytes; }
static void request_add_buddy_auth_ex(PurpleConnection *gc, guint32 uid, const gchar *text, guint8 *auth, guint8 auth_len) { guint8 raw_data[MAX_PACKET_SIZE - 16]; gint bytes = 0; g_return_if_fail(uid != 0); bytes = 0; bytes += qq_put8(raw_data + bytes, 0x02); bytes += qq_put32(raw_data + bytes, uid); bytes += qq_put16(raw_data + bytes, 0); bytes += qq_put8(raw_data + bytes, 0); if (auth == NULL || auth_len <= 0) { bytes += qq_put8(raw_data + bytes, 0); } else { bytes += qq_put8(raw_data + bytes, auth_len); bytes += qq_putdata(raw_data + bytes, auth, auth_len); } bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */ bytes += qq_put8(raw_data + bytes, 0); /* group number? */ bytes += qq_put_vstr(raw_data + bytes, text, QQ_CHARSET_DEFAULT); qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes); }
/* send IM to a group */ static void request_room_send_im(PurpleConnection *gc, guint32 room_id, qq_im_format *fmt, const gchar *msg) { guint8 raw_data[MAX_PACKET_SIZE - 16]; gint bytes; g_return_if_fail(room_id != 0 && msg != NULL); bytes = 0; bytes += qq_put16(raw_data + bytes, 0); bytes += qq_putdata(raw_data + bytes, (guint8 *)msg, strlen(msg)); bytes += qq_put_im_tail(raw_data + bytes, fmt); /* reset first two bytes */ qq_put16(raw_data, bytes - 2); qq_send_room_cmd(gc, QQ_ROOM_CMD_SEND_IM, room_id, raw_data, bytes); }
void qq_request_auth_token( PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 dataptr2ship, qq_buddy_opt_req *opt_req ) { guint8 raw_data[128]; gint bytes; g_return_if_fail(opt_req && opt_req->uid > 0); bytes = 0; bytes += qq_put8(raw_data + bytes, cmd); bytes += qq_put16(raw_data + bytes, sub_cmd); bytes += qq_put32(raw_data + bytes, opt_req->uid); if (opt_req->captcha_input && opt_req->session) { bytes += qq_put_vstr(raw_data+bytes, opt_req->captcha_input, sizeof(guint16), NULL); bytes += qq_put16(raw_data+bytes, opt_req->session_len); bytes += qq_putdata(raw_data+bytes, opt_req->session, opt_req->session_len); } qq_send_cmd_mess(gc, QQ_CMD_AUTH_TOKEN, raw_data, bytes, dataptr2ship, (guintptr)opt_req); }
void qq_request_auth_code(PurpleConnection *gc, guint8 cmd, guint16 sub_cmd, guint32 uid) { guint8 raw_data[16]; gint bytes; g_return_if_fail(uid > 0); bytes = 0; bytes += qq_put8(raw_data + bytes, cmd); bytes += qq_put16(raw_data + bytes, sub_cmd); bytes += qq_put32(raw_data + bytes, uid); qq_send_cmd_mess(gc, QQ_CMD_AUTH_CODE, raw_data, bytes, 0, uid); }
static void request_add_buddy_by_question(PurpleConnection *gc, guint32 uid, guint8 *code, guint16 code_len) { guint8 raw_data[MAX_PACKET_SIZE - 16]; gint bytes = 0; g_return_if_fail(uid != 0 && code_len > 0); bytes = 0; bytes += qq_put8(raw_data + bytes, 0x10); bytes += qq_put32(raw_data + bytes, uid); bytes += qq_put16(raw_data + bytes, 0); bytes += qq_put8(raw_data + bytes, 0); bytes += qq_put8(raw_data + bytes, 0); /* no auth code */ bytes += qq_put16(raw_data + bytes, code_len); bytes += qq_putdata(raw_data + bytes, code, code_len); bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */ bytes += qq_put8(raw_data + bytes, 0); /* group number? */ qq_send_cmd(gc, QQ_CMD_ADD_BUDDY_AUTH_EX, raw_data, bytes); }
static void request_buddy_check_code(PurpleConnection *gc, gchar *from, guint8 *code, gint code_len) { guint8 *raw_data; gint bytes; guint32 uid; g_return_if_fail(code != NULL && code_len > 0 && from != NULL); uid = strtoul(from, NULL, 10); raw_data = g_newa(guint8, code_len + 16); bytes = 0; bytes += qq_put8(raw_data + bytes, 0x03); bytes += qq_put8(raw_data + bytes, 0x01); bytes += qq_put32(raw_data + bytes, uid); bytes += qq_put16(raw_data + bytes, code_len); bytes += qq_putdata(raw_data + bytes, code, code_len); qq_send_cmd(gc, QQ_CMD_BUDDY_CHECK_CODE, raw_data, bytes); }
gint qq_put_im_tail(guint8 *buf, qq_im_format *fmt) { gint bytes; g_return_val_if_fail(buf != NULL && fmt != NULL, 0); bytes = 0; bytes += qq_put8(buf + bytes, 0); bytes += qq_put8(buf + bytes, fmt->attr); bytes += qq_putdata(buf + bytes, fmt->rgb, sizeof(fmt->rgb)); bytes += qq_put8(buf + bytes, 0); bytes += qq_put16(buf + bytes, fmt->charset); if (fmt->font != NULL && fmt->font_len > 0) { bytes += qq_putdata(buf + bytes, (guint8 *)fmt->font, fmt->font_len); } else { purple_debug_warning("QQ", "Font name is empty\n"); } bytes += qq_put8(buf + bytes, bytes + 1); /* qq_show_packet("IM tail", buf, bytes); */ return bytes; }
/* send a file to udp channel with QQ_FILE_DATA_PACKET_TAG */ static void _qq_send_file_data_packet(PurpleConnection *gc, guint16 packet_type, guint8 sub_type, guint32 fragment_index, guint16 seq, guint8 *data, gint len) { guint8 *raw_data, filename_md5[QQ_KEY_LENGTH], file_md5[QQ_KEY_LENGTH]; gint bytes; guint32 fragment_size = 1000; const char *filename; gint filename_len, filesize; qq_data *qd; ft_info *info; qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; filename = purple_xfer_get_filename(qd->xfer); filesize = purple_xfer_get_size(qd->xfer); raw_data = g_newa(guint8, MAX_PACKET_SIZE); bytes = 0; bytes += qq_put8(raw_data + bytes, 0x00); bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_BASIC_INFO: case QQ_FILE_DATA_INFO: case QQ_FILE_EOF: bytes += qq_put16(raw_data + bytes, 0x0000); bytes += qq_put8(raw_data + bytes, 0x00); break; case QQ_FILE_CMD_FILE_OP: switch(sub_type) { case QQ_FILE_BASIC_INFO: filename_len = strlen(filename); qq_get_md5(filename_md5, sizeof(filename_md5), (guint8 *)filename, filename_len); _fill_file_md5(purple_xfer_get_local_filename(qd->xfer), purple_xfer_get_size(qd->xfer), file_md5); info->fragment_num = (filesize - 1) / QQ_FILE_FRAGMENT_MAXLEN + 1; info->fragment_len = QQ_FILE_FRAGMENT_MAXLEN; purple_debug_info("QQ", "start transfering data, %d fragments with %d length each\n", info->fragment_num, info->fragment_len); /* Unknown */ bytes += qq_put16(raw_data + bytes, 0x0000); /* Sub-operation type */ bytes += qq_put8(raw_data + bytes, sub_type); /* Length of file */ bytes += qq_put32(raw_data + bytes, filesize); /* Number of fragments */ bytes += qq_put32(raw_data + bytes, info->fragment_num); /* Length of a single fragment */ bytes += qq_put32(raw_data + bytes, info->fragment_len); bytes += qq_putdata(raw_data + bytes, file_md5, 16); bytes += qq_putdata(raw_data + bytes, filename_md5, 16); /* Length of filename */ bytes += qq_put16(raw_data + bytes, filename_len); /* 8 unknown bytes */ bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); /* filename */ bytes += qq_putdata(raw_data + bytes, (guint8 *) filename, filename_len); break; case QQ_FILE_DATA_INFO: purple_debug_info("QQ", "sending %dth fragment with length %d, offset %d\n", fragment_index, len, (fragment_index-1)*fragment_size); /* bytes += qq_put16(raw_data + bytes, ++(qd->send_seq)); */ bytes += qq_put16(raw_data + bytes, info->send_seq); bytes += qq_put8(raw_data + bytes, sub_type); /* bytes += qq_put32(raw_data + bytes, fragment_index); */ bytes += qq_put32(raw_data + bytes, fragment_index - 1); bytes += qq_put32(raw_data + bytes, (fragment_index - 1) * fragment_size); bytes += qq_put16(raw_data + bytes, len); bytes += qq_putdata(raw_data + bytes, data, len); break; case QQ_FILE_EOF: purple_debug_info("QQ", "end of sending data\n"); /* bytes += qq_put16(raw_data + bytes, info->fragment_num + 1); */ bytes += qq_put16(raw_data + bytes, info->fragment_num); bytes += qq_put8(raw_data + bytes, sub_type); /* purple_xfer_set_completed(qd->xfer, TRUE); */ } break; case QQ_FILE_CMD_FILE_OP_ACK: switch (sub_type) { case QQ_FILE_BASIC_INFO: bytes += qq_put16(raw_data + bytes, 0x0000); bytes += qq_put8(raw_data + bytes, sub_type); bytes += qq_put32(raw_data + bytes, 0x00000000); break; case QQ_FILE_DATA_INFO: bytes += qq_put16(raw_data + bytes, seq); bytes += qq_put8(raw_data + bytes, sub_type); bytes += qq_put32(raw_data + bytes, fragment_index); break; case QQ_FILE_EOF: bytes += qq_put16(raw_data + bytes, filesize / QQ_FILE_FRAGMENT_MAXLEN + 2); bytes += qq_put8(raw_data + bytes, sub_type); break; } } purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); _qq_send_file(gc, raw_data, bytes, QQ_FILE_DATA_PACKET_TAG, info->to_uid); }
/* send a file to udp channel with QQ_FILE_CONTROL_PACKET_TAG */ void qq_send_file_ctl_packet(PurpleConnection *gc, guint16 packet_type, guint32 to_uid, guint8 hellobyte) { qq_data *qd; gint bytes, bytes_expected, encrypted_len; guint8 *raw_data, *encrypted; time_t now; ft_info *info; qd = (qq_data *) gc->proto_data; info = (ft_info *) qd->xfer->data; raw_data = g_newa (guint8, 61); bytes = 0; now = time(NULL); bytes += qq_putdata(raw_data + bytes, qd->session_md5, 16); bytes += qq_put16(raw_data + bytes, packet_type); switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: case QQ_FILE_CMD_NOTIFY_IP_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: bytes += qq_put16(raw_data + bytes, info->send_seq); break; default: bytes += qq_put16(raw_data + bytes, ++qd->send_seq); } bytes += qq_put32(raw_data + bytes, (guint32) now); bytes += qq_put8(raw_data + bytes, 0x00); bytes += qq_put8(raw_data + bytes, qd->my_icon); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put32(raw_data + bytes, 0x00000000); bytes += qq_put16(raw_data + bytes, 0x0000); bytes += qq_put8(raw_data + bytes, 0x00); /* 0x65: send a file, 0x6b: send a custom face */ bytes += qq_put8(raw_data + bytes, QQ_FILE_TRANSFER_FILE); /* FIXME temp by gfhuang */ switch (packet_type) { case QQ_FILE_CMD_SENDER_SAY_HELLO: case QQ_FILE_CMD_RECEIVER_SAY_HELLO: case QQ_FILE_CMD_SENDER_SAY_HELLO_ACK: case QQ_FILE_CMD_RECEIVER_SAY_HELLO_ACK: bytes += qq_put8(raw_data + bytes, 0x00); bytes += qq_put8(raw_data + bytes, hellobyte); bytes_expected = 48; break; case QQ_FILE_CMD_PING: case QQ_FILE_CMD_PONG: case QQ_FILE_CMD_NOTIFY_IP_ACK: bytes += qq_fill_conn_info(raw_data, info); bytes_expected = 61; break; default: purple_debug_info("QQ", "qq_send_file_ctl_packet: Unknown packet type[%d]\n", packet_type); bytes_expected = 0; } if (bytes != bytes_expected) { purple_debug_error("QQ", "qq_send_file_ctl_packet: Expected to get %d bytes, but get %d\n", bytes_expected, bytes); return; } qq_hex_dump(PURPLE_DEBUG_INFO, "QQ", raw_data, bytes, "sending packet[%s]:", qq_get_file_cmd_desc(packet_type)); encrypted = g_newa(guint8, bytes + 17); encrypted_len = qq_encrypt(encrypted, raw_data, bytes, info->file_session_key); /*debug: try to decrypt it */ #if 0 guint8 *buf; int buflen; hex_dump = hex_dump_to_str(encrypted, encrypted_len); purple_debug_info("QQ", "encrypted packet: \n%s\n", hex_dump); g_free(hex_dump); buf = g_newa(guint8, MAX_PACKET_SIZE); buflen = encrypted_len; if (qq_crypt(DECRYPT, encrypted, encrypted_len, info->file_session_key, buf, &buflen)) { purple_debug_info("QQ", "decrypt success\n"); if (buflen == bytes && memcmp(raw_data, buf, buflen) == 0) purple_debug_info("QQ", "checksum ok\n"); hex_dump = hex_dump_to_str(buf, buflen); purple_debug_info("QQ", "decrypted packet: \n%s\n", hex_dump); g_free(hex_dump); } else { purple_debug_info("QQ", "decrypt fail\n"); } #endif purple_debug_info("QQ", "<== send %s packet\n", qq_get_file_cmd_desc(packet_type)); _qq_send_file(gc, encrypted, encrypted_len, QQ_FILE_CONTROL_PACKET_TAG, info->to_uid); }
void qq_request_add_buddy_post(PurpleConnection *gc, qq_buddy_opt_req *opt_req, const gchar *text) { static guint8 fill1[] = { 0x00, 0x0A, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static guint8 fill2[] = { 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; guint8 raw_data[256]; gint bytes = 0; guint8 cmd; g_return_if_fail(opt_req && opt_req->uid!= 0); switch (opt_req->auth_type) { case 0x01: //auth cmd = 0x02; break; case 0x02: //question cmd = 0x10; break; case 0x00: //no_auth case 0x03: //approve and add case 0x04: //approve case 0x05: //reject cmd = opt_req->auth_type; break; } bytes = 0; bytes += qq_put8(raw_data + bytes, cmd); bytes += qq_put32(raw_data + bytes, opt_req->uid); if (cmd == 0x03 || cmd == 0x04 || cmd == 0x05) { bytes += qq_put16(raw_data + bytes, 0); } else { if (opt_req->no_auth && opt_req->no_auth_len > 0) { bytes += qq_put16(raw_data + bytes, opt_req->no_auth_len); bytes += qq_putdata(raw_data + bytes, opt_req->no_auth, opt_req->no_auth_len); } else bytes += qq_put16(raw_data + bytes, 0); if (opt_req->auth == NULL || opt_req->auth_len <= 0) { bytes += qq_put16(raw_data + bytes, 0); } else { bytes += qq_put16(raw_data + bytes, opt_req->auth_len); bytes += qq_putdata(raw_data + bytes, opt_req->auth, opt_req->auth_len); } bytes += qq_put8(raw_data + bytes, 1); /* ALLOW ADD ME FLAG */ } bytes += qq_put8(raw_data + bytes, opt_req->group_id); /* group number */ if (text) { bytes += qq_put8(raw_data + bytes, strlen(text)); bytes += qq_putdata(raw_data + bytes, (guint8 *)text, strlen(text)); } if (cmd == 0x03 || cmd == 0x04 || cmd == 0x05) bytes += qq_putdata(raw_data + bytes, fill2, sizeof(fill2)); else bytes += qq_putdata(raw_data + bytes, fill1, sizeof(fill1)); qq_send_cmd_mess(gc, QQ_CMD_ADD_BUDDY_POST, raw_data, bytes, 0, opt_req->auth_type); buddy_opt_req_free(opt_req); }