Mercurial > pidgin
comparison libpurple/protocols/qq/qq_network.c @ 23051:55f986ccbb6a
patch-05-reconnect-and-code-cleanup
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Tue, 24 Jun 2008 12:28:38 +0000 |
parents | 51dbe83ebbd3 |
children | ebad75b719f5 |
comparison
equal
deleted
inserted
replaced
23050:51dbe83ebbd3 | 23051:55f986ccbb6a |
---|---|
43 #include "keep_alive.h" | 43 #include "keep_alive.h" |
44 #include "im.h" | 44 #include "im.h" |
45 #include "login_logout.h" | 45 #include "login_logout.h" |
46 #include "packet_parse.h" | 46 #include "packet_parse.h" |
47 #include "qq_network.h" | 47 #include "qq_network.h" |
48 #include "sendqueue.h" | 48 #include "qq_trans.h" |
49 #include "sys_msg.h" | 49 #include "sys_msg.h" |
50 #include "utils.h" | 50 #include "utils.h" |
51 | 51 |
52 /* These functions are used only in development phase */ | 52 /* set QQ_RECONNECT_MAX to 1, when test reconnecting */ |
53 /* | 53 #define QQ_RECONNECT_MAX 4 |
54 static void _qq_show_socket(gchar *desc, gint fd) { | 54 #define QQ_RECONNECT_INTERVAL 5000 |
55 struct sockaddr_in sin; | 55 |
56 socklen_t len = sizeof(sin); | 56 static gboolean set_new_server(qq_data *qd) |
57 getsockname(fd, (struct sockaddr *)&sin, &len); | 57 { |
58 purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", | 58 gint count; |
59 inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); | 59 gint index; |
60 } | 60 GList *it = NULL; |
61 */ | 61 |
62 g_return_val_if_fail(qd != NULL, FALSE); | |
63 | |
64 if (qd->servers == NULL) { | |
65 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n"); | |
66 return FALSE; | |
67 } | |
68 | |
69 if (qd->real_hostname) { | |
70 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); | |
71 g_free(qd->real_hostname); | |
72 qd->real_hostname = NULL; | |
73 } | |
74 | |
75 /* remove server used before */ | |
76 if (qd->server_name != NULL) { | |
77 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
78 "Remove previous server [%s]\n", qd->server_name); | |
79 qd->servers = g_list_remove(qd->servers, qd->server_name); | |
80 qd->server_name = NULL; | |
81 } | |
82 | |
83 count = g_list_length(qd->servers); | |
84 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count); | |
85 if (count <= 0) { | |
86 /* no server left, disconnect when result is false */ | |
87 qd->servers = NULL; | |
88 return FALSE; | |
89 } | |
90 | |
91 /* get new server */ | |
92 index = random() % count; | |
93 it = g_list_nth(qd->servers, index); | |
94 qd->server_name = it->data; /* do not free server_name */ | |
95 if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) { | |
96 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index); | |
97 return FALSE; | |
98 } | |
99 | |
100 qd->real_hostname = g_strdup(qd->server_name); | |
101 qd->real_port = qd->user_port; | |
102 | |
103 qd->reconnect_times = QQ_RECONNECT_MAX; | |
104 | |
105 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
106 "set new server to %s:%d\n", qd->real_hostname, qd->real_port); | |
107 return TRUE; | |
108 } | |
62 | 109 |
63 /* QQ 2003iii uses double MD5 for the pwkey to get the session key */ | 110 /* QQ 2003iii uses double MD5 for the pwkey to get the session key */ |
64 static guint8 *encrypt_account_password(const gchar *pwd) | 111 static guint8 *encrypt_account_password(const gchar *pwd) |
65 { | 112 { |
66 PurpleCipher *cipher; | 113 PurpleCipher *cipher; |
80 | 127 |
81 return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); | 128 return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); |
82 } | 129 } |
83 | 130 |
84 /* default process, decrypt and dump */ | 131 /* default process, decrypt and dump */ |
85 static void packet_process_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) | 132 static void process_cmd_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) |
86 { | 133 { |
87 qq_data *qd; | 134 qq_data *qd; |
88 guint8 *data; | 135 guint8 *data; |
89 gint data_len; | 136 gint data_len; |
90 gchar *msg_utf8 = NULL; | 137 gchar *msg_utf8 = NULL; |
146 { | 193 { |
147 gpointer trans; | 194 gpointer trans; |
148 | 195 |
149 g_return_val_if_fail(qd != NULL, FALSE); | 196 g_return_val_if_fail(qd != NULL, FALSE); |
150 | 197 |
151 trans = qq_trans_find(qd, seq); | 198 trans = qq_send_trans_find(qd, seq); |
152 if (trans == NULL) { | 199 if (trans == NULL) { |
153 return FALSE; | 200 return FALSE; |
154 } | 201 } |
155 | 202 |
156 qq_trans_remove(qd, trans); | 203 qq_send_trans_remove(qd, trans); |
157 return TRUE; | 204 return TRUE; |
158 } | 205 } |
159 | 206 |
160 static void packet_process_cmd( | 207 static gboolean reconnect_later_cb(gpointer data) |
208 { | |
209 PurpleConnection *gc; | |
210 qq_data *qd; | |
211 | |
212 gc = (PurpleConnection *) data; | |
213 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); | |
214 qd = (qq_data *) gc->proto_data; | |
215 | |
216 qd->reconnect_timeout = 0; | |
217 | |
218 qq_connect(gc->account); | |
219 return FALSE; /* timeout callback stops */ | |
220 } | |
221 | |
222 static void reconnect_later(PurpleConnection *gc) | |
223 { | |
224 qq_data *qd; | |
225 | |
226 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
227 qd = (qq_data *) gc->proto_data; | |
228 | |
229 qd->reconnect_times--; | |
230 if (qd->reconnect_times < 0) { | |
231 if ( set_new_server(qd) != TRUE) { | |
232 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
233 _("Failed to connect server")); | |
234 return; | |
235 } | |
236 } | |
237 | |
238 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
239 "Reconnect to server %s:%d next retries %d in %d ms\n", | |
240 qd->real_hostname, qd->real_port, | |
241 qd->reconnect_times, QQ_RECONNECT_INTERVAL); | |
242 | |
243 qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL, | |
244 reconnect_later_cb, gc); | |
245 } | |
246 | |
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( | |
161 PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) | 268 PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) |
162 { | 269 { |
163 /* now process the packet */ | 270 /* now process the packet */ |
164 switch (cmd) { | 271 switch (cmd) { |
165 case QQ_CMD_KEEP_ALIVE: | 272 case QQ_CMD_KEEP_ALIVE: |
187 qq_process_change_status_reply(data, data_len, gc); | 294 qq_process_change_status_reply(data, data_len, gc); |
188 break; | 295 break; |
189 case QQ_CMD_SEND_IM: | 296 case QQ_CMD_SEND_IM: |
190 qq_process_send_im_reply(data, data_len, gc); | 297 qq_process_send_im_reply(data, data_len, gc); |
191 break; | 298 break; |
192 case QQ_CMD_RECV_IM: | |
193 qq_process_recv_im(data, data_len, seq, gc); | |
194 break; | |
195 case QQ_CMD_LOGIN: | 299 case QQ_CMD_LOGIN: |
196 qq_process_login_reply(data, data_len, gc); | 300 qq_process_login_reply(data, data_len, gc); |
197 break; | 301 break; |
198 case QQ_CMD_GET_FRIENDS_LIST: | 302 case QQ_CMD_GET_FRIENDS_LIST: |
199 qq_process_get_buddies_list_reply(data, data_len, gc); | 303 qq_process_get_buddies_list_reply(data, data_len, gc); |
211 qq_process_get_level_reply(data, data_len, gc); | 315 qq_process_get_level_reply(data, data_len, gc); |
212 break; | 316 break; |
213 case QQ_CMD_REQUEST_LOGIN_TOKEN: | 317 case QQ_CMD_REQUEST_LOGIN_TOKEN: |
214 qq_process_request_login_token_reply(data, data_len, gc); | 318 qq_process_request_login_token_reply(data, data_len, gc); |
215 break; | 319 break; |
216 case QQ_CMD_RECV_MSG_SYS: | |
217 qq_process_msg_sys(data, data_len, seq, gc); | |
218 break; | |
219 case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: | |
220 qq_process_friend_change_status(data, data_len, gc); | |
221 break; | |
222 default: | 320 default: |
223 packet_process_unknow(gc, data, data_len, cmd, seq); | 321 process_cmd_unknow(gc, data, data_len, cmd, seq); |
224 break; | 322 break; |
225 } | 323 } |
226 } | 324 } |
227 | 325 |
228 /* process the incoming packet from qq_pending */ | 326 /* process the incoming packet from qq_pending */ |
236 gint new_data_len; | 334 gint new_data_len; |
237 | 335 |
238 guint8 header_tag; | 336 guint8 header_tag; |
239 guint16 source_tag; | 337 guint16 source_tag; |
240 guint16 cmd; | 338 guint16 cmd; |
241 guint16 seq; // May be ack_seq or send_seq, depends on cmd | 339 guint16 seq; /* May be ack_seq or send_seq, depends on cmd */ |
340 | |
341 gboolean is_reply; | |
242 | 342 |
243 g_return_if_fail(buf != NULL && buf_len > 0); | 343 g_return_if_fail(buf != NULL && buf_len > 0); |
244 | 344 |
245 qd = (qq_data *) gc->proto_data; | 345 qd = (qq_data *) gc->proto_data; |
246 | 346 |
247 prev_login_status = qd->logged_in; | 347 prev_login_status = qd->logged_in; |
248 | 348 |
249 // Len, header and tail tag have been checked before | 349 /* Len, header and tail tag have been checked before */ |
250 bytes = 0; | 350 bytes = 0; |
251 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); | 351 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); |
252 | 352 |
253 if (QQ_DEBUG) { | 353 if (QQ_DEBUG) { |
254 purple_debug(PURPLE_DEBUG_INFO, "QQ", | 354 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
256 seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag)); | 356 seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag)); |
257 } | 357 } |
258 | 358 |
259 bytes_not_read = buf_len - bytes - 1; | 359 bytes_not_read = buf_len - bytes - 1; |
260 | 360 |
261 if ( !qd->logged_in ) { | 361 /* ack packet, we need to update send tranactions */ |
262 if (cmd != QQ_CMD_LOGIN && cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { | 362 /* we do not check duplication for server ack */ |
363 is_reply = packet_check_ack(qd, seq); | |
364 if ( !is_reply ) { | |
365 if ( !qd->logged_in ) { | |
263 /* packets before login */ | 366 /* packets before login */ |
264 qq_packet_push(qd, cmd, seq, buf + bytes, bytes_not_read); | 367 qq_rcv_trans_push(qd, cmd, seq, buf + bytes, bytes_not_read); |
265 return; /* do not process it now */ | 368 return; /* do not process it now */ |
266 } | 369 } |
267 } | 370 |
268 | 371 /* server intiated packet, we need to send ack and check duplicaion |
269 /* whether it is an ack */ | 372 * this must be put after processing b4_packet |
270 switch (cmd) { | 373 * as these packets will be passed in twice */ |
271 case QQ_CMD_RECV_IM: | 374 if (packet_is_dup(qd, seq)) { |
272 case QQ_CMD_RECV_MSG_SYS: | 375 purple_debug(PURPLE_DEBUG_WARNING, |
273 case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: | 376 "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); |
274 /* server intiated packet, we need to send ack and check duplicaion | 377 return; |
275 * this must be put after processing b4_packet | 378 } |
276 * as these packets will be passed in twice */ | 379 process_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); |
277 if (packet_is_dup(qd, seq)) { | 380 return; |
278 purple_debug(PURPLE_DEBUG_WARNING, | |
279 "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); | |
280 return; | |
281 } | |
282 break; | |
283 default: | |
284 /* ack packet, we need to update sendqueue */ | |
285 /* we do not check duplication for server ack */ | |
286 packet_check_ack(qd, seq); | |
287 } | 381 } |
288 | 382 |
289 /* this is the length of all the encrypted data (also remove tail tag */ | 383 /* this is the length of all the encrypted data (also remove tail tag */ |
290 packet_process_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); | 384 process_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read); |
291 | 385 |
292 // check is redirect or not, and do it now | 386 /* check is redirect or not, and do it now */ |
293 if (qd->is_redirect) { | 387 if (qd->is_redirect) { |
294 // free resource except real_hostname and port | 388 /* free resource except real_hostname and port */ |
295 qq_disconnect(gc); | 389 qq_disconnect(gc); |
296 qq_connect(gc->account); | 390 qd->reconnect_times = QQ_RECONNECT_MAX; |
391 reconnect_later(gc); | |
297 return; | 392 return; |
298 } | 393 } |
299 | 394 |
300 if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { | 395 if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { |
301 /* logged_in, but we have packets before login */ | 396 /* logged_in, but we have packets before login */ |
302 new_data = g_newa(guint8, MAX_PACKET_SIZE); | 397 new_data = g_newa(guint8, MAX_PACKET_SIZE); |
303 while (1) { | 398 while (1) { |
304 new_data_len = qq_packet_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE); | 399 memset(new_data, 0, MAX_PACKET_SIZE); |
400 new_data_len = qq_rcv_trans_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE); | |
305 if (new_data_len < 0) { | 401 if (new_data_len < 0) { |
306 break; | 402 break; |
307 } | 403 } |
308 if (new_data_len == 0) { | 404 if (new_data_len == 0) { |
309 continue; | 405 continue; |
310 } | 406 } |
311 packet_process_cmd(gc, seq, cmd, new_data, new_data_len); | 407 process_cmd_reply(gc, seq, cmd, new_data, new_data_len); |
312 } | 408 } |
313 } | 409 } |
314 } | 410 } |
315 | 411 |
316 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) | 412 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) |
317 { | 413 { |
318 PurpleConnection *gc; | 414 PurpleConnection *gc; |
319 qq_data *qd; | 415 qq_data *qd; |
320 guint8 buf[1024]; // set to 16 when test tcp_rxqueue | 416 guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ |
321 gint buf_len; | 417 gint buf_len; |
322 gint bytes; | 418 gint bytes; |
323 | 419 |
324 guint8 *pkt; | 420 guint8 *pkt; |
325 guint16 pkt_len; | 421 guint16 pkt_len; |
387 "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); | 483 "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); |
388 | 484 |
389 if ( pkt_len < QQ_TCP_HEADER_LENGTH | 485 if ( pkt_len < QQ_TCP_HEADER_LENGTH |
390 || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG | 486 || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG |
391 || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { | 487 || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { |
392 // HEY! This isn't even a QQ. What are you trying to pull? | 488 /* HEY! This isn't even a QQ. What are you trying to pull? */ |
393 | 489 |
394 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", | 490 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", |
395 "Packet error, failed to check header and tail tag\n"); | 491 "Packet error, failed to check header and tail tag\n"); |
396 | 492 |
397 jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); | 493 jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); |
402 qd->tcp_rxqueue = NULL; | 498 qd->tcp_rxqueue = NULL; |
403 qd->tcp_rxlen = 0; | 499 qd->tcp_rxlen = 0; |
404 return; | 500 return; |
405 } | 501 } |
406 | 502 |
407 // jump and over QQ_PACKET_TAIL | 503 /* jump and over QQ_PACKET_TAIL */ |
408 jump_len = (jump - qd->tcp_rxqueue) + 1; | 504 jump_len = (jump - qd->tcp_rxqueue) + 1; |
409 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", | 505 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", |
410 "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); | 506 "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); |
411 g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); | 507 g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); |
412 qd->tcp_rxlen -= jump_len; | 508 qd->tcp_rxlen -= jump_len; |
414 } | 510 } |
415 | 511 |
416 memset(pkt, 0, MAX_PACKET_SIZE); | 512 memset(pkt, 0, MAX_PACKET_SIZE); |
417 g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); | 513 g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); |
418 | 514 |
419 // jump to next packet | 515 /* jump to next packet */ |
420 qd->tcp_rxlen -= pkt_len; | 516 qd->tcp_rxlen -= pkt_len; |
421 if (qd->tcp_rxlen) { | 517 if (qd->tcp_rxlen) { |
422 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", | 518 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", |
423 "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); | 519 "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); |
424 jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); | 520 jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); |
432 } | 528 } |
433 | 529 |
434 if (pkt == NULL) { | 530 if (pkt == NULL) { |
435 continue; | 531 continue; |
436 } | 532 } |
437 // do not call packet_process before jump | 533 /* do not call packet_process before jump |
438 // packet_process may call disconnect and destory tcp_rxqueue | 534 * packet_process may call disconnect and destory tcp_rxqueue */ |
439 packet_process(gc, pkt, pkt_len - bytes); | 535 packet_process(gc, pkt, pkt_len - bytes); |
440 } | 536 } |
441 } | 537 } |
442 | 538 |
443 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) | 539 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) |
537 { | 633 { |
538 gint ret; | 634 gint ret; |
539 | 635 |
540 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); | 636 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); |
541 | 637 |
542 // purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); | 638 /* |
639 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); | |
640 */ | |
543 | 641 |
544 if (qd->tx_handler == 0) { | 642 if (qd->tx_handler == 0) { |
545 ret = write(qd->fd, data, data_len); | 643 ret = write(qd->fd, data, data_len); |
546 } else { | 644 } else { |
547 ret = -1; | 645 ret = -1; |
549 } | 647 } |
550 | 648 |
551 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", | 649 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", |
552 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); | 650 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); |
553 if (ret < 0 && errno == EAGAIN) { | 651 if (ret < 0 && errno == EAGAIN) { |
554 // socket is busy, send later | 652 /* socket is busy, send later */ |
555 // purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); | 653 /* |
654 * purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); | |
655 */ | |
556 ret = 0; | 656 ret = 0; |
557 } else if (ret <= 0) { | 657 } else if (ret <= 0) { |
558 // TODO: what to do here - do we really have to disconnect? | 658 /* TODO: what to do here - do we really have to disconnect? */ |
559 purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", | 659 purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", |
560 "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); | 660 "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); |
561 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); | 661 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); |
562 return ret; | 662 return ret; |
563 } | 663 } |
591 index = 0; | 691 index = 0; |
592 buf = g_newa(guint8, MAX_PACKET_SIZE); | 692 buf = g_newa(guint8, MAX_PACKET_SIZE); |
593 | 693 |
594 while (1) { | 694 while (1) { |
595 if (index < 0) { | 695 if (index < 0) { |
596 // next record is NULL | 696 /* next record is NULL */ |
597 break; | 697 break; |
598 } | 698 } |
599 // purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); | 699 /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); */ |
600 memset(buf, 0, MAX_PACKET_SIZE); | 700 memset(buf, 0, MAX_PACKET_SIZE); |
601 buf_len = qq_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries); | 701 buf_len = qq_send_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries); |
602 if (buf_len <= 0) { | 702 if (buf_len <= 0) { |
603 // curr record is empty, whole trans is NULL | 703 /* curr record is empty, whole trans is NULL */ |
604 break; | 704 break; |
605 } | 705 } |
606 // index = -1, when get last record of transactions | 706 /* index = -1, when get last record of transactions */ |
607 | 707 |
608 // purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); | 708 /* purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); */ |
609 if (retries > 0) { | 709 if (retries > 0) { |
610 if (qd->use_tcp) { | 710 if (qd->use_tcp) { |
611 tcp_send_out(qd, buf, buf_len); | 711 tcp_send_out(qd, buf, buf_len); |
612 } else { | 712 } else { |
613 udp_send_out(qd, buf, buf_len); | 713 udp_send_out(qd, buf, buf_len); |
614 } | 714 } |
615 continue; | 715 continue; |
616 } | 716 } |
617 | 717 |
618 // retries <= 0 | 718 /* retries <= 0 */ |
619 switch (cmd) { | 719 switch (cmd) { |
620 case QQ_CMD_KEEP_ALIVE: | 720 case QQ_CMD_KEEP_ALIVE: |
621 if (qd->logged_in) { | 721 if (qd->logged_in) { |
622 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); | 722 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); |
623 purple_connection_error_reason(gc, | 723 purple_connection_error_reason(gc, |
646 * we setup the qq protocol related configuration here */ | 746 * we setup the qq protocol related configuration here */ |
647 static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) | 747 static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) |
648 { | 748 { |
649 qq_data *qd; | 749 qq_data *qd; |
650 PurpleConnection *gc; | 750 PurpleConnection *gc; |
651 gchar *buf; | 751 gchar *conn_msg; |
652 const gchar *passwd; | 752 const gchar *passwd; |
653 | 753 |
654 gc = (PurpleConnection *) data; | 754 gc = (PurpleConnection *) data; |
655 | 755 |
656 if (!PURPLE_CONNECTION_IS_VALID(gc)) { | 756 if (!PURPLE_CONNECTION_IS_VALID(gc)) { |
665 | 765 |
666 /* Connect is now complete; clear the PurpleProxyConnectData */ | 766 /* Connect is now complete; clear the PurpleProxyConnectData */ |
667 qd->connect_data = NULL; | 767 qd->connect_data = NULL; |
668 | 768 |
669 if (source < 0) { /* socket returns -1 */ | 769 if (source < 0) { /* socket returns -1 */ |
670 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Source is < 0\n"); | 770 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n"); |
671 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); | 771 qq_disconnect(gc); |
672 return; | 772 reconnect_later(gc); |
673 } | 773 return; |
674 | 774 } |
675 // _qq_show_socket("Got login socket", source); | 775 |
776 /* _qq_show_socket("Got login socket", source); */ | |
676 | 777 |
677 /* QQ use random seq, to minimize duplicated packets */ | 778 /* QQ use random seq, to minimize duplicated packets */ |
678 srandom(time(NULL)); | 779 srandom(time(NULL)); |
679 qd->send_seq = random() & 0x0000ffff; | 780 qd->send_seq = random() & 0x0000ffff; |
680 qd->fd = source; | 781 qd->fd = source; |
695 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); | 796 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); |
696 else | 797 else |
697 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); | 798 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); |
698 | 799 |
699 /* Update the login progress status display */ | 800 /* Update the login progress status display */ |
700 buf = g_strdup_printf("Login as %d", qd->uid); | 801 conn_msg = g_strdup_printf("Login as %d", qd->uid); |
701 purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS); | 802 purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); |
702 g_free(buf); | 803 g_free(conn_msg); |
703 | 804 |
704 qq_send_packet_request_login_token(gc); | 805 qq_send_packet_request_login_token(gc); |
705 } | 806 } |
706 | 807 |
707 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) | 808 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) |
763 gc = (PurpleConnection *) data; | 864 gc = (PurpleConnection *) data; |
764 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 865 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
765 | 866 |
766 qd = (qq_data *) gc->proto_data; | 867 qd = (qq_data *) gc->proto_data; |
767 | 868 |
768 // udp_query_data must be set as NULL. | 869 /* udp_query_data must be set as NULL. |
769 // Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error | 870 * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */ |
770 qd->udp_query_data = NULL; | 871 qd->udp_query_data = NULL; |
771 | 872 |
772 if (!hosts || !hosts->data) { | 873 if (!hosts || !hosts->data) { |
773 purple_connection_error_reason(gc, | 874 purple_connection_error_reason(gc, |
774 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 875 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
840 * TCP/UDP, and direct/redirected */ | 941 * TCP/UDP, and direct/redirected */ |
841 void qq_connect(PurpleAccount *account) | 942 void qq_connect(PurpleAccount *account) |
842 { | 943 { |
843 PurpleConnection *gc; | 944 PurpleConnection *gc; |
844 qq_data *qd; | 945 qq_data *qd; |
946 gchar *conn_msg; | |
845 | 947 |
846 gc = purple_account_get_connection(account); | 948 gc = purple_account_get_connection(account); |
847 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 949 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
848 | 950 |
849 qd = (qq_data *) gc->proto_data; | 951 qd = (qq_data *) gc->proto_data; |
952 | |
953 | |
954 /* test set_new_server | |
955 while (set_new_server(qd)) { | |
956 purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", | |
957 "New server %s:%d Real server %s:%d\n", | |
958 qd->server_name, qd->user_port, qd->real_hostname, qd->real_port); | |
959 } | |
960 purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n", | |
961 qd->servers); | |
962 exit(1); | |
963 */ | |
964 if (qd->server_name == NULL) { | |
965 /* must be first call this function */ | |
966 if ( set_new_server(qd) != TRUE) { | |
967 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
968 _("Failed to connect server")); | |
969 return; | |
970 } | |
971 } | |
850 | 972 |
851 if (qd->real_hostname == NULL || qd->real_port == 0) { | 973 if (qd->real_hostname == NULL || qd->real_port == 0) { |
852 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 974 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
853 _("hostname is NULL or port is 0")); | 975 _("hostname is NULL or port is 0")); |
854 return; | 976 return; |
855 } | 977 } |
978 | |
979 conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), | |
980 qd->real_hostname, qd->reconnect_times); | |
981 purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); | |
982 g_free(conn_msg); | |
856 | 983 |
857 if (qd->is_redirect) { | 984 if (qd->is_redirect) { |
858 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", | 985 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", |
859 qd->real_hostname, qd->real_port); | 986 qd->real_hostname, qd->real_port); |
860 } | 987 } |
861 qd->is_redirect = FALSE; | 988 qd->is_redirect = FALSE; |
862 | 989 |
863 qd->fd = -1; | 990 qd->fd = -1; |
864 qd->tx_handler = 0; | 991 qd->tx_handler = 0; |
865 | 992 |
866 g_return_if_fail(qd->real_hostname != NULL); | |
867 | |
868 /* QQ connection via UDP/TCP. | 993 /* QQ connection via UDP/TCP. |
869 * Now use Purple proxy function to provide TCP proxy support, | 994 * Now use Purple proxy function to provide TCP proxy support, |
870 * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ | 995 * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ |
871 if(qd->use_tcp) { | 996 if(qd->use_tcp) { |
872 purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", | 997 purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", |
926 if (qd->fd >= 0) { | 1051 if (qd->fd >= 0) { |
927 close(qd->fd); | 1052 close(qd->fd); |
928 qd->fd = -1; | 1053 qd->fd = -1; |
929 } | 1054 } |
930 | 1055 |
1056 if (qd->reconnect_timeout > 0) { | |
1057 purple_timeout_remove(qd->reconnect_timeout); | |
1058 qd->reconnect_timeout = 0; | |
1059 } | |
1060 | |
931 if (qd->connect_data != NULL) { | 1061 if (qd->connect_data != NULL) { |
932 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); | 1062 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); |
933 purple_proxy_connect_cancel(qd->connect_data); | 1063 purple_proxy_connect_cancel(qd->connect_data); |
934 } | 1064 } |
935 | 1065 |
936 if(qd->tcp_txbuf != NULL) { | 1066 if(qd->tcp_txbuf != NULL) { |
937 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); | 1067 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); |
938 purple_circ_buffer_destroy(qd->tcp_txbuf); | 1068 purple_circ_buffer_destroy(qd->tcp_txbuf); |
1069 qd->tcp_txbuf = NULL; | |
939 } | 1070 } |
940 | 1071 |
941 if (qd->tx_handler) { | 1072 if (qd->tx_handler) { |
942 purple_input_remove(qd->tx_handler); | 1073 purple_input_remove(qd->tx_handler); |
943 qd->tx_handler = 0; | 1074 qd->tx_handler = 0; |
953 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); | 1084 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); |
954 purple_dnsquery_destroy(qd->udp_query_data); | 1085 purple_dnsquery_destroy(qd->udp_query_data); |
955 qd->udp_query_data = NULL; | 1086 qd->udp_query_data = NULL; |
956 } | 1087 } |
957 | 1088 |
958 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy transactions\n"); | 1089 memset(qd->rcv_window, 0, sizeof(qd->rcv_window)); |
959 qq_trans_remove_all(qd); | 1090 qq_rcv_trans_remove_all(qd); |
1091 qq_send_trans_remove_all(qd); | |
960 | 1092 |
961 if (qd->inikey) { | 1093 if (qd->inikey) { |
962 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n"); | 1094 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n"); |
963 g_free(qd->inikey); | 1095 g_free(qd->inikey); |
964 qd->inikey = NULL; | 1096 qd->inikey = NULL; |
982 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n"); | 1114 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n"); |
983 g_free(qd->my_ip); | 1115 g_free(qd->my_ip); |
984 qd->my_ip = NULL; | 1116 qd->my_ip = NULL; |
985 } | 1117 } |
986 | 1118 |
987 qq_packet_remove_all(qd); | |
988 qq_group_packets_free(qd); | 1119 qq_group_packets_free(qd); |
989 qq_group_free_all(qd); | 1120 qq_group_free_all(qd); |
990 qq_add_buddy_request_free(qd); | 1121 qq_add_buddy_request_free(qd); |
991 qq_info_query_free(qd); | 1122 qq_info_query_free(qd); |
992 qq_buddies_list_free(gc->account, qd); | 1123 qq_buddies_list_free(gc->account, qd); |
1021 | 1152 |
1022 bytes += qq_put32(buf + bytes, qd->uid); | 1153 bytes += qq_put32(buf + bytes, qd->uid); |
1023 bytes += qq_putdata(buf + bytes, data, data_len); | 1154 bytes += qq_putdata(buf + bytes, data, data_len); |
1024 bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); | 1155 bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); |
1025 | 1156 |
1026 // set TCP packet length at begin of the packet | 1157 /* set TCP packet length at begin of the packet */ |
1027 if (qd->use_tcp) { | 1158 if (qd->use_tcp) { |
1028 qq_put16(buf, bytes); | 1159 qq_put16(buf, bytes); |
1029 } | 1160 } |
1030 | 1161 |
1031 return bytes; | 1162 return bytes; |
1032 } | 1163 } |
1033 | 1164 |
1034 gint qq_send_data(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) | 1165 gint qq_send_data(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) |
1035 { | 1166 { |
1036 qq_data *qd; | |
1037 guint8 *buf; | 1167 guint8 *buf; |
1038 gint buf_len; | 1168 gint buf_len; |
1039 gint bytes_sent; | 1169 gint bytes_sent; |
1040 gint seq; | 1170 gint seq; |
1041 | 1171 |
1042 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | 1172 g_return_val_if_fail(qd != NULL, -1); |
1043 qd = (qq_data *) gc->proto_data; | 1173 g_return_val_if_fail(data != NULL && data_len > 0, -1); |
1044 | 1174 |
1045 buf = g_newa(guint8, MAX_PACKET_SIZE); | 1175 buf = g_newa(guint8, MAX_PACKET_SIZE); |
1046 memset(buf, 0, MAX_PACKET_SIZE); | 1176 memset(buf, 0, MAX_PACKET_SIZE); |
1047 seq = ++(qd->send_seq); | 1177 seq = ++(qd->send_seq); |
1048 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); | 1178 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); |
1049 if (buf_len <= 0) { | 1179 if (buf_len <= 0) { |
1054 bytes_sent = tcp_send_out(qd, buf, buf_len); | 1184 bytes_sent = tcp_send_out(qd, buf, buf_len); |
1055 } else { | 1185 } else { |
1056 bytes_sent = udp_send_out(qd, buf, buf_len); | 1186 bytes_sent = udp_send_out(qd, buf, buf_len); |
1057 } | 1187 } |
1058 | 1188 |
1059 // always need ack | 1189 /* always need ack */ |
1060 qq_trans_append(qd, buf, buf_len, cmd, seq); | 1190 qq_send_trans_append(qd, buf, buf_len, cmd, seq); |
1061 | 1191 |
1062 if (QQ_DEBUG) { | 1192 if (QQ_DEBUG) { |
1063 qq_show_packet("QQ_SEND_DATA", buf, buf_len); | 1193 qq_show_packet("QQ_SEND_DATA", buf, buf_len); |
1064 purple_debug(PURPLE_DEBUG_INFO, "QQ", | 1194 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
1065 "<== [%05d], %s, total %d bytes is sent %d\n", | 1195 "<== [%05d], %s, total %d bytes is sent %d\n", |
1069 } | 1199 } |
1070 | 1200 |
1071 /* send the packet generated with the given cmd and data | 1201 /* send the packet generated with the given cmd and data |
1072 * return the number of bytes sent to socket if succeeds | 1202 * return the number of bytes sent to socket if succeeds |
1073 * return -1 if there is any error */ | 1203 * return -1 if there is any error */ |
1074 gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, | 1204 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, |
1075 gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint data_len) | 1205 guint8 *data, gint data_len) |
1076 { | 1206 { |
1077 qq_data *qd; | |
1078 guint8 *buf; | 1207 guint8 *buf; |
1079 gint buf_len; | 1208 gint buf_len; |
1080 guint8 *encrypted_data; | 1209 guint8 *encrypted_data; |
1081 gint encrypted_len; | 1210 gint encrypted_len; |
1082 gint real_seq; | |
1083 gint bytes_sent; | 1211 gint bytes_sent; |
1084 | 1212 |
1085 qd = (qq_data *) gc->proto_data; | 1213 g_return_val_if_fail(qd != NULL && qd->session_key != NULL, -1); |
1086 g_return_val_if_fail(qd->session_key != NULL, -1); | 1214 g_return_val_if_fail(data != NULL && data_len > 0, -1); |
1087 | 1215 |
1088 encrypted_len = data_len + 16; /* at most 16 bytes more */ | 1216 encrypted_len = data_len + 16; /* at most 16 bytes more */ |
1089 encrypted_data = g_newa(guint8, encrypted_len); | 1217 encrypted_data = g_newa(guint8, encrypted_len); |
1090 | 1218 |
1091 qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); | 1219 qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); |
1092 | 1220 |
1093 real_seq = seq; | |
1094 if (is_auto_seq) real_seq = ++(qd->send_seq); | |
1095 | |
1096 buf = g_newa(guint8, MAX_PACKET_SIZE); | 1221 buf = g_newa(guint8, MAX_PACKET_SIZE); |
1097 memset(buf, 0, MAX_PACKET_SIZE); | 1222 memset(buf, 0, MAX_PACKET_SIZE); |
1098 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, real_seq, encrypted_data, encrypted_len); | 1223 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, encrypted_data, encrypted_len); |
1099 if (buf_len <= 0) { | 1224 if (buf_len <= 0) { |
1100 return -1; | 1225 return -1; |
1101 } | 1226 } |
1102 | 1227 |
1103 if (QQ_DEBUG) { | 1228 if (QQ_DEBUG) { |
1109 bytes_sent = udp_send_out(qd, buf, buf_len); | 1234 bytes_sent = udp_send_out(qd, buf, buf_len); |
1110 } | 1235 } |
1111 | 1236 |
1112 /* if it does not need ACK, we send ACK manually several times */ | 1237 /* if it does not need ACK, we send ACK manually several times */ |
1113 if (need_ack) { | 1238 if (need_ack) { |
1114 qq_trans_append(qd, buf, buf_len, cmd, real_seq); | 1239 qq_send_trans_append(qd, buf, buf_len, cmd, seq); |
1115 } | 1240 } |
1116 | 1241 |
1117 if (QQ_DEBUG) { | 1242 if (QQ_DEBUG) { |
1118 qq_show_packet("QQ_SEND_CMD", buf, buf_len); | 1243 qq_show_packet("QQ_SEND_CMD", buf, buf_len); |
1119 purple_debug(PURPLE_DEBUG_INFO, "QQ", | 1244 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
1120 "<== [%05d], %s, total %d bytes is sent %d\n", | 1245 "<== [%05d], %s, total %d bytes is sent %d\n", |
1121 real_seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); | 1246 seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); |
1122 } | 1247 } |
1123 return bytes_sent; | 1248 return bytes_sent; |
1124 } | 1249 } |
1250 | |
1251 gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) | |
1252 { | |
1253 g_return_val_if_fail(qd != NULL, -1); | |
1254 g_return_val_if_fail(data != NULL && data_len > 0, -1); | |
1255 | |
1256 qd->send_seq++; | |
1257 return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len); | |
1258 } |