comparison libpurple/protocols/qq/qq_network.c @ 23638:1c50f12b1c52

2008.08.02 - csyfek <csyfek(at)gmail.com> * Commit to Pidgin * Tickets: Fixes #1861 Fixes #1902 References #5112 2008.08.02 - ccpaging <ecc_hy(at)hotmail.com> * Store all keys and md5 values of qq_data in char[QQ_KEY_LENGTH] * Use random value in inikey * TEA header padding in crypt.c * Rewrite login part of qq_process 2008.07.31 - ccpaging <ecc_hy(at)hotmail.com> * Fixed: send reply when get duplicate server command. The server may not get our reply before. * Tag custom picture as text "(Broken)" 2008.07.30 - ccpaging <ecc_hy(at)hotmail.com>, csyfek <csyfek(at)gmail.com> * Change some debug message * Modify buddy status flag according to eva for QQ2006 * Modify buddy status parse and correspond to eva2 * Add getIP/putIP functions to packet_parse.c, and replace some gen_ip_str * Replace guint32 *ip with struct in_addr, and reduce g_new/g_free operation * Source file changed: Merge buddy_status into buddy_list Change login_logout to qq_base Merge keep_alive into qq_base New qq_process extract from qq_network * Fixed: Byte alignment bug in crypt.c, tested in ARM PDA * Fixed: group chat message may get in before getting group info, and so group info is empty * Add qq_send_cmd_group_get_group_info when joined a group chat in group_im.c * Add some new group command identify according eva but further program * Add some new QQ client version identify * Fixed: Identify buddy's client version by IM packet, and not by status * Add some new info in buddy's tooltip text * Add video falg to buddy's emblem. But those flag in buddy status may not prasing correctly * Use new timeout function to handle send keep_alive, resend packet, update buddy status * Add new advanced options: The end user may change interval of keep_alive, resend packet, update buddy status to feed their need. For example, saving network flow when use mobile phone. Keep alive packet must be sent in 60-120 seconds whatever client rcved data of not. The intervals of keep alive and update status should be multiple of resend's interval, Since we use counter not time() in a single timeout function for efficiency. * Rewrite qq_trans.c, and use one g_list to manage: Store server packet before login, and prase all of them when get login Store client send packet for resend scanning, confirm server reply, filter duplicate server reply Store server packet for filter out duplicate * Add QQ_MSG_SYS_NOTICE = 0x06 in sys_msg.c * Rewrite qq_proc_cmd_reply and qq_proc_cmd_server: In QQ protocol, one packet reply may need a new packet send later. We may call it packet trigger. The triggers always is hided in every qq_process_reply. Now we try to extract those triggers and put into a single function, and then every trigger should be obviously and easy to manage.
author SHiNE CsyFeK <csyfek@gmail.com>
date Sat, 02 Aug 2008 15:00:46 +0000
parents bdb38a8bf721
children 5f454b975a99
comparison
equal deleted inserted replaced
23637:21e591b55339 23638:1c50f12b1c52
30 #define random rand 30 #define random rand
31 #define srandom srand 31 #define srandom srand
32 #endif 32 #endif
33 33
34 #include "buddy_info.h" 34 #include "buddy_info.h"
35 #include "group_info.h"
36 #include "group_free.h"
37 #include "crypt.h"
38 #include "header_info.h"
39 #include "qq_base.h"
35 #include "buddy_list.h" 40 #include "buddy_list.h"
36 #include "buddy_opt.h"
37 #include "buddy_status.h"
38 #include "group_free.h"
39 #include "char_conv.h"
40 #include "crypt.h"
41 #include "group_network.h"
42 #include "header_info.h"
43 #include "keep_alive.h"
44 #include "im.h"
45 #include "login_logout.h"
46 #include "packet_parse.h" 41 #include "packet_parse.h"
47 #include "qq_network.h" 42 #include "qq_network.h"
48 #include "qq_trans.h" 43 #include "qq_trans.h"
49 #include "sys_msg.h"
50 #include "utils.h" 44 #include "utils.h"
45 #include "qq_process.h"
51 46
52 /* set QQ_RECONNECT_MAX to 1, when test reconnecting */ 47 /* set QQ_RECONNECT_MAX to 1, when test reconnecting */
53 #define QQ_RECONNECT_MAX 4 48 #define QQ_RECONNECT_MAX 4
54 #define QQ_RECONNECT_INTERVAL 5000 49 #define QQ_RECONNECT_INTERVAL 5000
50 #define QQ_KEEP_ALIVE_INTERVAL 60000
51 #define QQ_TRANS_INTERVAL 10000
55 52
56 static gboolean set_new_server(qq_data *qd) 53 static gboolean set_new_server(qq_data *qd)
57 { 54 {
58 gint count; 55 gint count;
59 gint index; 56 gint index;
105 purple_debug(PURPLE_DEBUG_INFO, "QQ", 102 purple_debug(PURPLE_DEBUG_INFO, "QQ",
106 "set new server to %s:%d\n", qd->real_hostname, qd->real_port); 103 "set new server to %s:%d\n", qd->real_hostname, qd->real_port);
107 return TRUE; 104 return TRUE;
108 } 105 }
109 106
110 /* QQ 2003iii uses double MD5 for the pwkey to get the session key */
111 static guint8 *encrypt_account_password(const gchar *pwd)
112 {
113 PurpleCipher *cipher;
114 PurpleCipherContext *context;
115
116 guchar pwkey_tmp[QQ_KEY_LENGTH];
117
118 cipher = purple_ciphers_find_cipher("md5");
119 context = purple_cipher_context_new(cipher, NULL);
120 purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd));
121 purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
122 purple_cipher_context_destroy(context);
123 context = purple_cipher_context_new(cipher, NULL);
124 purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH);
125 purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL);
126 purple_cipher_context_destroy(context);
127
128 return g_memdup(pwkey_tmp, QQ_KEY_LENGTH);
129 }
130
131 /* default process, decrypt and dump */
132 static void process_cmd_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq)
133 {
134 qq_data *qd;
135 guint8 *data;
136 gint data_len;
137 gchar *msg_utf8 = NULL;
138
139 g_return_if_fail(buf != NULL && buf_len != 0);
140
141 qq_show_packet("Processing unknown packet", buf, buf_len);
142
143 qd = (qq_data *) gc->proto_data;
144
145 data_len = buf_len;
146 data = g_newa(guint8, data_len);
147 memset(data, 0, data_len);
148 if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) {
149 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n");
150 return;
151 }
152
153 qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ",
154 data, data_len,
155 ">>> [%d] %s -> [default] decrypt and dump",
156 seq, qq_get_cmd_desc(cmd));
157
158 msg_utf8 = try_dump_as_gbk(data, data_len);
159 if (msg_utf8) {
160 g_free(msg_utf8);
161 }
162 }
163
164 static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, 107 static gint packet_get_header(guint8 *header_tag, guint16 *source_tag,
165 guint16 *cmd, guint16 *seq, guint8 *buf) 108 guint16 *cmd, guint16 *seq, guint8 *buf)
166 { 109 {
167 gint bytes = 0; 110 gint bytes = 0;
168 bytes += qq_get8(header_tag, buf + bytes); 111 bytes += qq_get8(header_tag, buf + bytes);
170 bytes += qq_get16(cmd, buf + bytes); 113 bytes += qq_get16(cmd, buf + bytes);
171 bytes += qq_get16(seq, buf + bytes); 114 bytes += qq_get16(seq, buf + bytes);
172 return bytes; 115 return bytes;
173 } 116 }
174 117
175 /* check whether one sequence number is duplicated or not
176 * return TRUE if it is duplicated, otherwise FALSE */
177 static gboolean packet_is_dup(qq_data *qd, guint16 seq)
178 {
179 guint8 *byte, mask;
180
181 g_return_val_if_fail(qd != NULL, FALSE);
182
183 byte = &(qd->rcv_window[seq / 8]);
184 mask = (1 << (seq % 8));
185
186 if ((*byte) & mask)
187 return TRUE; /* check mask */
188 (*byte) |= mask;
189 return FALSE; /* set mask */
190 }
191
192 static gboolean packet_check_ack(qq_data *qd, guint16 cmd, guint16 seq)
193 {
194 gpointer trans;
195
196 g_return_val_if_fail(qd != NULL, FALSE);
197
198 trans = qq_send_trans_find(qd, cmd, seq);
199 if (trans == NULL) {
200 return FALSE;
201 }
202
203 qq_send_trans_remove(qd, trans);
204 return TRUE;
205 }
206
207 static gboolean reconnect_later_cb(gpointer data) 118 static gboolean reconnect_later_cb(gpointer data)
208 { 119 {
209 PurpleConnection *gc; 120 PurpleConnection *gc;
210 qq_data *qd; 121 qq_data *qd;
211 122
242 153
243 qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL, 154 qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL,
244 reconnect_later_cb, gc); 155 reconnect_later_cb, gc);
245 } 156 }
246 157
247 static void process_cmd_server(
248 PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
249 {
250 /* now process the packet */
251 switch (cmd) {
252 case QQ_CMD_RECV_IM:
253 qq_process_recv_im(data, data_len, seq, gc);
254 break;
255 case QQ_CMD_RECV_MSG_SYS:
256 qq_process_msg_sys(data, data_len, seq, gc);
257 break;
258 case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS:
259 qq_process_friend_change_status(data, data_len, gc);
260 break;
261 default:
262 process_cmd_unknow(gc, data, data_len, cmd, seq);
263 break;
264 }
265 }
266
267 static void process_cmd_reply(
268 PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len)
269 {
270 /* now process the packet */
271 switch (cmd) {
272 case QQ_CMD_KEEP_ALIVE:
273 qq_process_keep_alive_reply(data, data_len, gc);
274 break;
275 case QQ_CMD_UPDATE_INFO:
276 qq_process_modify_info_reply(data, data_len, gc);
277 break;
278 case QQ_CMD_ADD_FRIEND_WO_AUTH:
279 qq_process_add_buddy_reply(data, data_len, seq, gc);
280 break;
281 case QQ_CMD_DEL_FRIEND:
282 qq_process_remove_buddy_reply(data, data_len, gc);
283 break;
284 case QQ_CMD_REMOVE_SELF:
285 qq_process_remove_self_reply(data, data_len, gc);
286 break;
287 case QQ_CMD_BUDDY_AUTH:
288 qq_process_add_buddy_auth_reply(data, data_len, gc);
289 break;
290 case QQ_CMD_GET_USER_INFO:
291 qq_process_get_info_reply(data, data_len, gc);
292 break;
293 case QQ_CMD_CHANGE_ONLINE_STATUS:
294 qq_process_change_status_reply(data, data_len, gc);
295 break;
296 case QQ_CMD_SEND_IM:
297 qq_process_send_im_reply(data, data_len, gc);
298 break;
299 case QQ_CMD_LOGIN:
300 qq_process_login_reply(data, data_len, gc);
301 break;
302 case QQ_CMD_GET_FRIENDS_LIST:
303 qq_process_get_buddies_list_reply(data, data_len, gc);
304 break;
305 case QQ_CMD_GET_FRIENDS_ONLINE:
306 qq_process_get_buddies_online_reply(data, data_len, gc);
307 break;
308 case QQ_CMD_GROUP_CMD:
309 qq_process_group_cmd_reply(data, data_len, seq, gc);
310 break;
311 case QQ_CMD_GET_ALL_LIST_WITH_GROUP:
312 qq_process_get_all_list_with_group_reply(data, data_len, gc);
313 break;
314 case QQ_CMD_GET_LEVEL:
315 qq_process_get_level_reply(data, data_len, gc);
316 break;
317 case QQ_CMD_REQUEST_LOGIN_TOKEN:
318 qq_process_request_login_token_reply(data, data_len, gc);
319 break;
320 default:
321 process_cmd_unknow(gc, data, data_len, cmd, seq);
322 break;
323 }
324 }
325
326 /* process the incoming packet from qq_pending */ 158 /* process the incoming packet from qq_pending */
327 static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) 159 static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len)
328 { 160 {
329 qq_data *qd; 161 qq_data *qd;
330 gint bytes, bytes_not_read; 162 gint bytes, bytes_not_read;
331 163
332 gboolean prev_login_status; 164 gboolean prev_login_status;
333 guint8 *new_data;
334 gint new_data_len;
335 165
336 guint8 header_tag; 166 guint8 header_tag;
337 guint16 source_tag; 167 guint16 source_tag;
338 guint16 cmd; 168 guint16 cmd;
339 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ 169 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */
340 170
341 gboolean is_reply; 171 qq_transaction *trans;
342 172
343 g_return_if_fail(buf != NULL && buf_len > 0); 173 g_return_if_fail(buf != NULL && buf_len > 0);
344 174
345 qd = (qq_data *) gc->proto_data; 175 qd = (qq_data *) gc->proto_data;
346 176
351 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); 181 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes);
352 182
353 if (QQ_DEBUG) { 183 if (QQ_DEBUG) {
354 purple_debug(PURPLE_DEBUG_INFO, "QQ", 184 purple_debug(PURPLE_DEBUG_INFO, "QQ",
355 "==> [%05d] 0x%04X %s, from (0x%04X %s)\n", 185 "==> [%05d] 0x%04X %s, from (0x%04X %s)\n",
356 seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag)); 186 seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag));
357 } 187 }
358 188
359 bytes_not_read = buf_len - bytes - 1; 189 bytes_not_read = buf_len - bytes - 1;
360 190
361 /* ack packet, we need to update send tranactions */ 191 /* ack packet, we need to update send tranactions */
362 /* we do not check duplication for server ack */ 192 /* we do not check duplication for server ack */
363 is_reply = packet_check_ack(qd, cmd, seq); 193 trans = qq_trans_find_rcved(qd, cmd, seq);
364 if ( !is_reply ) { 194 if (trans == NULL) {
365 if ( !qd->logged_in ) { 195 /* new server command */
366 /* packets before login */ 196 qq_trans_add_server_cmd(qd, cmd, seq, buf + bytes, bytes_not_read);
367 qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read); 197 if ( qd->logged_in ) {
368 return; /* do not process it now */ 198 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
369 } 199 }
370 200 return;
371 /* server intiated packet, we need to send ack and check duplicaion 201 }
372 * this must be put after processing b4_packet 202
373 * as these packets will be passed in twice */ 203 if (qq_trans_is_dup(trans)) {
374 if (packet_is_dup(qd, seq)) { 204 purple_debug(PURPLE_DEBUG_WARNING,
375 purple_debug(PURPLE_DEBUG_WARNING, 205 "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd));
376 "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); 206 return;
377 return; 207 }
378 } 208
379 process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); 209 if (qq_trans_is_server(trans)) {
210 if ( qd->logged_in ) {
211 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read);
212 }
380 return; 213 return;
381 } 214 }
382 215
383 /* this is the length of all the encrypted data (also remove tail tag */ 216 /* this is the length of all the encrypted data (also remove tail tag */
384 process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read); 217 qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read);
385 218
386 /* check is redirect or not, and do it now */ 219 /* check is redirect or not, and do it now */
387 if (qd->is_redirect) { 220 if (qd->is_redirect) {
388 /* free resource except real_hostname and port */ 221 /* free resource except real_hostname and port */
389 qq_disconnect(gc); 222 qq_disconnect(gc);
392 return; 225 return;
393 } 226 }
394 227
395 if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { 228 if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) {
396 /* logged_in, but we have packets before login */ 229 /* logged_in, but we have packets before login */
397 new_data = g_newa(guint8, MAX_PACKET_SIZE); 230 qq_trans_process_before_login(qd);
398 while (1) {
399 memset(new_data, 0, MAX_PACKET_SIZE);
400 new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE);
401 if (new_data_len < 0) {
402 break;
403 }
404 if (new_data_len == 0) {
405 continue;
406 }
407 process_cmd_reply(gc, seq, cmd, new_data, new_data_len);
408 }
409 } 231 }
410 } 232 }
411 233
412 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) 234 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond)
413 { 235 {
462 284
463 /* keep alive will be sent in 30 seconds since last_receive 285 /* keep alive will be sent in 30 seconds since last_receive
464 * QQ need a keep alive packet in every 60 seconds 286 * QQ need a keep alive packet in every 60 seconds
465 gc->last_received = time(NULL); 287 gc->last_received = time(NULL);
466 */ 288 */
289 /*
467 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", 290 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
468 "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); 291 "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen);
292 */
469 qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); 293 qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen);
470 memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); 294 memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len);
471 qd->tcp_rxlen += buf_len; 295 qd->tcp_rxlen += buf_len;
472 296
473 pkt = g_newa(guint8, MAX_PACKET_SIZE); 297 pkt = g_newa(guint8, MAX_PACKET_SIZE);
480 bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); 304 bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes);
481 if (qd->tcp_rxlen < pkt_len) { 305 if (qd->tcp_rxlen < pkt_len) {
482 break; 306 break;
483 } 307 }
484 308
309 /*
485 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", 310 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING",
486 "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); 311 "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen);
487 312 */
488 if ( pkt_len < QQ_TCP_HEADER_LENGTH 313 if ( pkt_len < QQ_TCP_HEADER_LENGTH
489 || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG 314 || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG
490 || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { 315 || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) {
491 /* HEY! This isn't even a QQ. What are you trying to pull? */ 316 /* HEY! This isn't even a QQ. What are you trying to pull? */
492 317
516 g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); 341 g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes);
517 342
518 /* jump to next packet */ 343 /* jump to next packet */
519 qd->tcp_rxlen -= pkt_len; 344 qd->tcp_rxlen -= pkt_len;
520 if (qd->tcp_rxlen) { 345 if (qd->tcp_rxlen) {
521 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", 346 /*
522 "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); 347 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen);
348 */
523 jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); 349 jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen);
524 g_free(qd->tcp_rxqueue); 350 g_free(qd->tcp_rxqueue);
525 qd->tcp_rxqueue = jump; 351 qd->tcp_rxqueue = jump;
526 } else { 352 } else {
527 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", 353 /* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */
528 "free tcp_rxqueue\n");
529 g_free(qd->tcp_rxqueue); 354 g_free(qd->tcp_rxqueue);
530 qd->tcp_rxqueue = NULL; 355 qd->tcp_rxqueue = NULL;
531 } 356 }
532 357
533 if (pkt == NULL) { 358 if (pkt == NULL) {
589 { 414 {
590 gint ret; 415 gint ret;
591 416
592 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); 417 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
593 418
594 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd); 419 /*
595 420 purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
421 */
422
596 errno = 0; 423 errno = 0;
597 ret = send(qd->fd, data, data_len, 0); 424 ret = send(qd->fd, data, data_len, 0);
598 if (ret < 0 && errno == EAGAIN) { 425 if (ret < 0 && errno == EAGAIN) {
599 return ret; 426 return ret;
600 } 427 }
601 428
602 if (ret < 0) { 429 if (ret < 0) {
603 /* TODO: what to do here - do we really have to disconnect? */ 430 /* TODO: what to do here - do we really have to disconnect? */
604 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno)); 431 purple_debug(PURPLE_DEBUG_ERROR, "UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno));
605 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); 432 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno));
606 } 433 }
607 return ret; 434 return ret;
608 } 435 }
609 436
640 gint ret; 467 gint ret;
641 468
642 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); 469 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1);
643 470
644 /* 471 /*
645 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); 472 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd);
646 */ 473 */
647 474
648 if (qd->tx_handler == 0) { 475 if (qd->tx_handler == 0) {
649 ret = write(qd->fd, data, data_len); 476 ret = write(qd->fd, data, data_len);
650 } else { 477 } else {
651 ret = -1; 478 ret = -1;
652 errno = EAGAIN; 479 errno = EAGAIN;
653 } 480 }
654 481
482 /*
655 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", 483 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT",
656 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); 484 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret);
485 */
657 if (ret < 0 && errno == EAGAIN) { 486 if (ret < 0 && errno == EAGAIN) {
658 /* socket is busy, send later */ 487 /* socket is busy, send later */
659 /* 488 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n");
660 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n");
661 */
662 ret = 0; 489 ret = 0;
663 } else if (ret <= 0) { 490 } else if (ret <= 0) {
664 /* TODO: what to do here - do we really have to disconnect? */ 491 /* TODO: what to do here - do we really have to disconnect? */
665 purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", 492 purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT",
666 "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); 493 "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno));
677 purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); 504 purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret);
678 } 505 }
679 return ret; 506 return ret;
680 } 507 }
681 508
682 static gboolean trans_timeout(gpointer data) 509 static gboolean network_timeout(gpointer data)
683 { 510 {
684 PurpleConnection *gc; 511 PurpleConnection *gc = (PurpleConnection *) data;
685 qq_data *qd; 512 qq_data *qd;
686 guint8 *buf; 513 gboolean is_lost_conn;
687 gint buf_len = 0; 514
688 guint16 cmd;
689 gint retries = 0;
690 int index;
691
692 gc = (PurpleConnection *) data;
693 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); 515 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE);
694
695 qd = (qq_data *) gc->proto_data; 516 qd = (qq_data *) gc->proto_data;
696 517
697 index = 0; 518 is_lost_conn = qq_trans_scan(qd);
698 buf = g_newa(guint8, MAX_PACKET_SIZE); 519 if (is_lost_conn) {
699 520 purple_connection_error_reason(gc,
700 while (1) { 521 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
701 if (index < 0) { 522 return TRUE;
702 /* next record is NULL */ 523 }
703 break; 524
704 } 525 if ( !qd->logged_in ) {
705 /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */ 526 return TRUE;
706 memset(buf, 0, MAX_PACKET_SIZE); 527 }
707 buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries); 528
708 if (buf_len <= 0) { 529 qd->itv_count.keep_alive--;
709 /* curr record is empty, whole trans is NULL */ 530 if (qd->itv_count.keep_alive <= 0) {
710 break; 531 qd->itv_count.keep_alive = qd->itv_config.keep_alive;
711 } 532 qq_send_packet_keep_alive(gc);
712 /* index = -1, when get last record of transactions */ 533 return TRUE;
713 534 }
714 /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */ 535
715 if (retries > 0) { 536 if (qd->itv_config.update <= 0) {
716 if (qd->use_tcp) { 537 return TRUE;
717 tcp_send_out(qd, buf, buf_len); 538 }
718 } else { 539
719 udp_send_out(qd, buf, buf_len); 540 qd->itv_count.update--;
720 } 541 if (qd->itv_count.update <= 0) {
721 continue; 542 qd->itv_count.update = qd->itv_config.update;
722 } 543 qq_send_packet_get_buddies_online(gc, 0);
723 544
724 /* retries <= 0 */ 545 qq_send_cmd_group_all_get_online_members(gc);
725 switch (cmd) { 546 return TRUE;
726 case QQ_CMD_KEEP_ALIVE:
727 if (qd->logged_in) {
728 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n");
729 purple_connection_error_reason(gc,
730 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost"));
731 qd->logged_in = FALSE;
732 }
733 break;
734 case QQ_CMD_LOGIN:
735 case QQ_CMD_REQUEST_LOGIN_TOKEN:
736 if (!qd->logged_in) {
737 /* cancel login progress */
738 purple_connection_error_reason(gc,
739 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply"));
740 }
741 break;
742 default:
743 purple_debug(PURPLE_DEBUG_WARNING, "QQ",
744 "%s packet lost.\n", qq_get_cmd_desc(cmd));
745 }
746 } 547 }
747 548
748 return TRUE; /* if return FALSE, timeout callback stops */ 549 return TRUE; /* if return FALSE, timeout callback stops */
749 } 550 }
750 551
754 { 555 {
755 qq_data *qd; 556 qq_data *qd;
756 PurpleConnection *gc; 557 PurpleConnection *gc;
757 gchar *conn_msg; 558 gchar *conn_msg;
758 const gchar *passwd; 559 const gchar *passwd;
560 PurpleAccount *account ;
759 561
760 gc = (PurpleConnection *) data; 562 gc = (PurpleConnection *) data;
761 563
762 if (!PURPLE_CONNECTION_IS_VALID(gc)) { 564 if (!PURPLE_CONNECTION_IS_VALID(gc)) {
763 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); 565 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n");
766 } 568 }
767 569
768 g_return_if_fail(gc != NULL && gc->proto_data != NULL); 570 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
769 571
770 qd = (qq_data *) gc->proto_data; 572 qd = (qq_data *) gc->proto_data;
573 account = purple_connection_get_account(gc);
771 574
772 /* Connect is now complete; clear the PurpleProxyConnectData */ 575 /* Connect is now complete; clear the PurpleProxyConnectData */
773 qd->connect_data = NULL; 576 qd->connect_data = NULL;
774 577
775 if (source < 0) { /* socket returns -1 */ 578 if (source < 0) { /* socket returns -1 */
789 qd->channel = 1; 592 qd->channel = 1;
790 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); 593 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10);
791 594
792 /* now generate md5 processed passwd */ 595 /* now generate md5 processed passwd */
793 passwd = purple_account_get_password(purple_connection_get_account(gc)); 596 passwd = purple_account_get_password(purple_connection_get_account(gc));
794 g_return_if_fail(qd->pwkey == NULL); 597
795 qd->pwkey = encrypt_account_password(passwd); 598 /* use twice-md5 of user password as session key since QQ 2003iii */
796 599 qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
797 g_return_if_fail(qd->resend_timeout == 0); 600 (guint8 *)passwd, strlen(passwd));
798 /* call trans_timeout every 5 seconds */ 601 qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5),
799 qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc); 602 qd->password_twice_md5, sizeof(qd->password_twice_md5));
603
604 g_return_if_fail(qd->network_timeout == 0);
605 qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10);
606 if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10;
607
608 qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60);
609 if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30;
610 qd->itv_config.keep_alive /= qd->itv_config.resend;
611 qd->itv_count.keep_alive = qd->itv_config.keep_alive;
612
613 qd->itv_config.update = purple_account_get_int(account, "update_interval", 300);
614 if (qd->itv_config.update > 0) {
615 if (qd->itv_config.update < qd->itv_config.keep_alive) {
616 qd->itv_config.update = qd->itv_config.keep_alive;
617 }
618 qd->itv_config.update /= qd->itv_config.resend;
619 qd->itv_count.update = qd->itv_config.update;
620 } else {
621 qd->itv_config.update = 0;
622 }
623
624 qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc);
800 625
801 if (qd->use_tcp) 626 if (qd->use_tcp)
802 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); 627 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc);
803 else 628 else
804 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); 629 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc);
806 /* Update the login progress status display */ 631 /* Update the login progress status display */
807 conn_msg = g_strdup_printf("Login as %d", qd->uid); 632 conn_msg = g_strdup_printf("Login as %d", qd->uid);
808 purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); 633 purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS);
809 g_free(conn_msg); 634 g_free(conn_msg);
810 635
811 qq_send_packet_request_login_token(gc); 636 qq_send_packet_token(gc);
812 } 637 }
813 638
814 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) 639 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond)
815 { 640 {
816 PurpleConnection *gc; 641 PurpleConnection *gc;
1037 862
1038 g_return_if_fail(gc != NULL && gc->proto_data != NULL); 863 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
1039 qd = (qq_data *) gc->proto_data; 864 qd = (qq_data *) gc->proto_data;
1040 865
1041 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); 866 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n");
867
868 if (qd->network_timeout > 0) {
869 purple_timeout_remove(qd->network_timeout);
870 qd->network_timeout = 0;
871 }
872
1042 /* finish all I/O */ 873 /* finish all I/O */
1043 if (qd->fd >= 0 && qd->logged_in) { 874 if (qd->fd >= 0 && qd->logged_in) {
1044 qq_send_packet_logout(gc); 875 qq_send_packet_logout(gc);
1045 }
1046
1047 if (qd->resend_timeout > 0) {
1048 purple_timeout_remove(qd->resend_timeout);
1049 qd->resend_timeout = 0;
1050 } 876 }
1051 877
1052 if (gc->inpa > 0) { 878 if (gc->inpa > 0) {
1053 purple_input_remove(gc->inpa); 879 purple_input_remove(gc->inpa);
1054 gc->inpa = 0; 880 gc->inpa = 0;
1090 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); 916 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n");
1091 purple_dnsquery_destroy(qd->udp_query_data); 917 purple_dnsquery_destroy(qd->udp_query_data);
1092 qd->udp_query_data = NULL; 918 qd->udp_query_data = NULL;
1093 } 919 }
1094 920
1095 memset(qd->rcv_window, 0, sizeof(qd->rcv_window)); 921 qq_trans_remove_all(qd);
1096 qq_rcv_trans_remove_all(qd); 922
1097 qq_send_trans_remove_all(qd); 923 if (qd->token) {
1098 924 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n");
1099 if (qd->inikey) { 925 g_free(qd->token);
1100 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n"); 926 qd->token = NULL;
1101 g_free(qd->inikey); 927 qd->token_len = 0;
1102 qd->inikey = NULL; 928 }
1103 } 929 memset(qd->inikey, 0, sizeof(qd->inikey));
1104 if (qd->pwkey) { 930 memset(qd->password_twice_md5, 0, sizeof(qd->password_twice_md5));
1105 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n"); 931 memset(qd->session_key, 0, sizeof(qd->session_key));
1106 g_free(qd->pwkey); 932 memset(qd->session_md5, 0, sizeof(qd->session_md5));
1107 qd->pwkey = NULL; 933
1108 } 934 qd->my_ip.s_addr = 0;
1109 if (qd->session_key) {
1110 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n");
1111 g_free(qd->session_key);
1112 qd->session_key = NULL;
1113 }
1114 if (qd->session_md5) {
1115 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n");
1116 g_free(qd->session_md5);
1117 qd->session_md5 = NULL;
1118 }
1119 if (qd->my_ip) {
1120 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n");
1121 g_free(qd->my_ip);
1122 qd->my_ip = NULL;
1123 }
1124 935
1125 qq_group_packets_free(qd); 936 qq_group_packets_free(qd);
1126 qq_group_free_all(qd); 937 qq_group_free_all(qd);
1127 qq_add_buddy_request_free(qd); 938 qq_add_buddy_request_free(qd);
1128 qq_info_query_free(qd); 939 qq_info_query_free(qd);
1166 } 977 }
1167 978
1168 return bytes; 979 return bytes;
1169 } 980 }
1170 981
1171 gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) 982 /* data has been encrypted before */
983 gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
984 guint8 *data, gint data_len)
1172 { 985 {
1173 guint8 *buf; 986 guint8 *buf;
1174 gint buf_len; 987 gint buf_len;
1175 gint bytes_sent; 988 gint bytes_sent;
1176 gint seq;
1177 989
1178 g_return_val_if_fail(qd != NULL, -1); 990 g_return_val_if_fail(qd != NULL, -1);
1179 g_return_val_if_fail(data != NULL && data_len > 0, -1); 991 g_return_val_if_fail(data != NULL && data_len > 0, -1);
1180 992
1181 buf = g_newa(guint8, MAX_PACKET_SIZE); 993 buf = g_newa(guint8, MAX_PACKET_SIZE);
1182 memset(buf, 0, MAX_PACKET_SIZE); 994 memset(buf, 0, MAX_PACKET_SIZE);
1183 seq = ++(qd->send_seq);
1184 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); 995 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len);
1185 if (buf_len <= 0) { 996 if (buf_len <= 0) {
1186 return -1; 997 return -1;
1187 } 998 }
1188 999
1190 bytes_sent = tcp_send_out(qd, buf, buf_len); 1001 bytes_sent = tcp_send_out(qd, buf, buf_len);
1191 } else { 1002 } else {
1192 bytes_sent = udp_send_out(qd, buf, buf_len); 1003 bytes_sent = udp_send_out(qd, buf, buf_len);
1193 } 1004 }
1194 1005
1195 /* always need ack */ 1006 if (need_ack) {
1196 qq_send_trans_append(qd, buf, buf_len, cmd, seq); 1007 qq_trans_add_client_cmd(qd, cmd, seq, data, data_len);
1197 1008 }
1009
1198 if (QQ_DEBUG) { 1010 if (QQ_DEBUG) {
1199 qq_show_packet("QQ_SEND_DATA", buf, buf_len); 1011 /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */
1200 purple_debug(PURPLE_DEBUG_INFO, "QQ", 1012 purple_debug(PURPLE_DEBUG_INFO, "QQ",
1201 "<== [%05d], %s, total %d bytes is sent %d\n", 1013 "<== [%05d], %s, total %d bytes is sent %d\n",
1202 seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); 1014 seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
1203 } 1015 }
1204 return bytes_sent; 1016 return bytes_sent;
1205 } 1017 }
1206 1018
1207 /* send the packet generated with the given cmd and data 1019 /* Encrypt data with session_key, then call qq_send_data */
1208 * return the number of bytes sent to socket if succeeds
1209 * return -1 if there is any error */
1210 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, 1020 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack,
1211 guint8 *data, gint data_len) 1021 guint8 *data, gint data_len)
1212 { 1022 {
1213 guint8 *buf;
1214 gint buf_len;
1215 guint8 *encrypted_data; 1023 guint8 *encrypted_data;
1216 gint encrypted_len; 1024 gint encrypted_len;
1217 gint bytes_sent; 1025
1218 1026 g_return_val_if_fail(qd != NULL, -1);
1219 g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1);
1220 g_return_val_if_fail(data != NULL && data_len > 0, -1); 1027 g_return_val_if_fail(data != NULL && data_len > 0, -1);
1221 1028
1222 encrypted_len = data_len + 16; /* at most 16 bytes more */ 1029 encrypted_len = data_len + 16; /* at most 16 bytes more */
1223 encrypted_data = g_newa(guint8, encrypted_len); 1030 encrypted_data = g_newa(guint8, encrypted_len);
1224 1031
1225 qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); 1032 qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len);
1226 1033
1227 buf = g_newa(guint8, MAX_PACKET_SIZE); 1034 return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len);
1228 memset(buf, 0, MAX_PACKET_SIZE); 1035 }
1229 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len); 1036
1230 if (buf_len <= 0) { 1037 /* set seq and need_ack, then call qq_send_cmd_detail */
1231 return -1;
1232 }
1233
1234 if (QQ_DEBUG) {
1235 qq_show_packet("QQ_SEND_CMD", buf, buf_len);
1236 }
1237 if (qd->use_tcp) {
1238 bytes_sent = tcp_send_out(qd, buf, buf_len);
1239 } else {
1240 bytes_sent = udp_send_out(qd, buf, buf_len);
1241 }
1242
1243 /* if it does not need ACK, we send ACK manually several times */
1244 if (need_ack) {
1245 qq_send_trans_append(qd, buf, buf_len, cmd, seq);
1246 }
1247
1248 if (QQ_DEBUG) {
1249 purple_debug(PURPLE_DEBUG_INFO, "QQ",
1250 "<== [%05d], %s, total %d bytes is sent %d\n",
1251 seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent);
1252 }
1253 return bytes_sent;
1254 }
1255
1256 gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) 1038 gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len)
1257 { 1039 {
1258 g_return_val_if_fail(qd != NULL, -1); 1040 g_return_val_if_fail(qd != NULL, -1);
1259 g_return_val_if_fail(data != NULL && data_len > 0, -1); 1041 g_return_val_if_fail(data != NULL && data_len > 0, -1);
1260 1042