static void _fill_file_md5(const gchar *filename, gint filelen, guint8 *md5) { FILE *fp; guint8 *buffer; size_t wc; const gint QQ_MAX_FILE_MD5_LENGTH = 10002432; g_return_if_fail(filename != NULL && md5 != NULL); if (filelen > QQ_MAX_FILE_MD5_LENGTH) filelen = QQ_MAX_FILE_MD5_LENGTH; fp = g_fopen(filename, "rb"); g_return_if_fail(fp != NULL); buffer = g_newa(guint8, filelen); g_return_if_fail(buffer != NULL); wc = fread(buffer, filelen, 1, fp); fclose(fp); if (wc != 1) { purple_debug_error("qq", "Unable to read file: %s\n", filename); /* TODO: XXX: Really, the caller should be modified to deal with this properly. */ return; } qq_get_md5(md5, QQ_KEY_LENGTH, buffer, filelen); }
static void set_all_keys(PurpleConnection *gc) { qq_data *qd; const gchar *passwd; guint8 *dest; int dest_len = QQ_KEY_LENGTH; #ifndef DEBUG int bytes; #endif /* _qq_show_socket("Got login socket", source); */ g_return_if_fail(gc != NULL && gc->proto_data != NULL); qd = (qq_data *) gc->proto_data; /* QQ use random seq, to minimize duplicated packets */ srand(time(NULL)); qd->send_seq = rand() & 0xffff; qd->is_login = FALSE; qd->uid = strtoul(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); #ifdef DEBUG memset(qd->ld.random_key, 0x01, sizeof(qd->ld.random_key)); #else for (bytes = 0; bytes < sizeof(qd->ld.random_key); bytes++) { qd->ld.random_key[bytes] = (guint8) (rand() & 0xff); } #endif /* now generate md5 processed passwd */ passwd = purple_account_get_password(purple_connection_get_account(gc)); /* use twice-md5 of user password as session key since QQ 2003iii */ dest = qd->ld.pwd_md5; qq_get_md5(dest, dest_len, (guint8 *)passwd, strlen(passwd)); dest = qd->ld.pwd_twice_md5; qq_get_md5(dest, dest_len, qd->ld.pwd_md5, dest_len); }
/* 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); }