Mercurial > pidgin
comparison libpurple/protocols/qq/qq_network.c @ 23050:51dbe83ebbd3
patch-04-tcp-pending
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Tue, 24 Jun 2008 12:22:40 +0000 |
parents | |
children | 55f986ccbb6a |
comparison
equal
deleted
inserted
replaced
23049:190bc4ecf6c3 | 23050:51dbe83ebbd3 |
---|---|
1 /** | |
2 * @file qq_network.c | |
3 * | |
4 * purple | |
5 * | |
6 * Purple is the legal property of its developers, whose names are too numerous | |
7 * to list here. Please refer to the COPYRIGHT file distributed with this | |
8 * source distribution. | |
9 * | |
10 * This program is free software; you can redistribute it and/or modify | |
11 * it under the terms of the GNU General Public License as published by | |
12 * the Free Software Foundation; either version 2 of the License, or | |
13 * (at your option) any later version. | |
14 * | |
15 * This program is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 * GNU General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU General Public License | |
21 * along with this program; if not, write to the Free Software | |
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA | |
23 */ | |
24 | |
25 #include "cipher.h" | |
26 #include "debug.h" | |
27 #include "internal.h" | |
28 | |
29 #ifdef _WIN32 | |
30 #define random rand | |
31 #define srandom srand | |
32 #endif | |
33 | |
34 #include "buddy_info.h" | |
35 #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" | |
47 #include "qq_network.h" | |
48 #include "sendqueue.h" | |
49 #include "sys_msg.h" | |
50 #include "utils.h" | |
51 | |
52 /* These functions are used only in development phase */ | |
53 /* | |
54 static void _qq_show_socket(gchar *desc, gint fd) { | |
55 struct sockaddr_in sin; | |
56 socklen_t len = sizeof(sin); | |
57 getsockname(fd, (struct sockaddr *)&sin, &len); | |
58 purple_debug(PURPLE_DEBUG_INFO, desc, "%s:%d\n", | |
59 inet_ntoa(sin.sin_addr), g_ntohs(sin.sin_port)); | |
60 } | |
61 */ | |
62 | |
63 /* QQ 2003iii uses double MD5 for the pwkey to get the session key */ | |
64 static guint8 *encrypt_account_password(const gchar *pwd) | |
65 { | |
66 PurpleCipher *cipher; | |
67 PurpleCipherContext *context; | |
68 | |
69 guchar pwkey_tmp[QQ_KEY_LENGTH]; | |
70 | |
71 cipher = purple_ciphers_find_cipher("md5"); | |
72 context = purple_cipher_context_new(cipher, NULL); | |
73 purple_cipher_context_append(context, (guchar *) pwd, strlen(pwd)); | |
74 purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); | |
75 purple_cipher_context_destroy(context); | |
76 context = purple_cipher_context_new(cipher, NULL); | |
77 purple_cipher_context_append(context, pwkey_tmp, QQ_KEY_LENGTH); | |
78 purple_cipher_context_digest(context, sizeof(pwkey_tmp), pwkey_tmp, NULL); | |
79 purple_cipher_context_destroy(context); | |
80 | |
81 return g_memdup(pwkey_tmp, QQ_KEY_LENGTH); | |
82 } | |
83 | |
84 /* default process, decrypt and dump */ | |
85 static void packet_process_unknow(PurpleConnection *gc, guint8 *buf, gint buf_len, guint16 cmd, guint16 seq) | |
86 { | |
87 qq_data *qd; | |
88 guint8 *data; | |
89 gint data_len; | |
90 gchar *msg_utf8 = NULL; | |
91 | |
92 g_return_if_fail(buf != NULL && buf_len != 0); | |
93 | |
94 qq_show_packet("Processing unknown packet", buf, buf_len); | |
95 | |
96 qd = (qq_data *) gc->proto_data; | |
97 | |
98 data_len = buf_len; | |
99 data = g_newa(guint8, data_len); | |
100 memset(data, 0, data_len); | |
101 if ( !qq_decrypt(buf, buf_len, qd->session_key, data, &data_len )) { | |
102 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); | |
103 return; | |
104 } | |
105 | |
106 qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", | |
107 data, data_len, | |
108 ">>> [%d] %s -> [default] decrypt and dump", | |
109 seq, qq_get_cmd_desc(cmd)); | |
110 | |
111 msg_utf8 = try_dump_as_gbk(data, data_len); | |
112 if (msg_utf8) { | |
113 g_free(msg_utf8); | |
114 } | |
115 } | |
116 | |
117 static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, | |
118 guint16 *cmd, guint16 *seq, guint8 *buf) | |
119 { | |
120 gint bytes = 0; | |
121 bytes += qq_get8(header_tag, buf + bytes); | |
122 bytes += qq_get16(source_tag, buf + bytes); | |
123 bytes += qq_get16(cmd, buf + bytes); | |
124 bytes += qq_get16(seq, buf + bytes); | |
125 return bytes; | |
126 } | |
127 | |
128 /* check whether one sequence number is duplicated or not | |
129 * return TRUE if it is duplicated, otherwise FALSE */ | |
130 static gboolean packet_is_dup(qq_data *qd, guint16 seq) | |
131 { | |
132 guint8 *byte, mask; | |
133 | |
134 g_return_val_if_fail(qd != NULL, FALSE); | |
135 | |
136 byte = &(qd->rcv_window[seq / 8]); | |
137 mask = (1 << (seq % 8)); | |
138 | |
139 if ((*byte) & mask) | |
140 return TRUE; /* check mask */ | |
141 (*byte) |= mask; | |
142 return FALSE; /* set mask */ | |
143 } | |
144 | |
145 static gboolean packet_check_ack(qq_data *qd, guint16 seq) | |
146 { | |
147 gpointer trans; | |
148 | |
149 g_return_val_if_fail(qd != NULL, FALSE); | |
150 | |
151 trans = qq_trans_find(qd, seq); | |
152 if (trans == NULL) { | |
153 return FALSE; | |
154 } | |
155 | |
156 qq_trans_remove(qd, trans); | |
157 return TRUE; | |
158 } | |
159 | |
160 static void packet_process_cmd( | |
161 PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) | |
162 { | |
163 /* now process the packet */ | |
164 switch (cmd) { | |
165 case QQ_CMD_KEEP_ALIVE: | |
166 qq_process_keep_alive_reply(data, data_len, gc); | |
167 break; | |
168 case QQ_CMD_UPDATE_INFO: | |
169 qq_process_modify_info_reply(data, data_len, gc); | |
170 break; | |
171 case QQ_CMD_ADD_FRIEND_WO_AUTH: | |
172 qq_process_add_buddy_reply(data, data_len, seq, gc); | |
173 break; | |
174 case QQ_CMD_DEL_FRIEND: | |
175 qq_process_remove_buddy_reply(data, data_len, gc); | |
176 break; | |
177 case QQ_CMD_REMOVE_SELF: | |
178 qq_process_remove_self_reply(data, data_len, gc); | |
179 break; | |
180 case QQ_CMD_BUDDY_AUTH: | |
181 qq_process_add_buddy_auth_reply(data, data_len, gc); | |
182 break; | |
183 case QQ_CMD_GET_USER_INFO: | |
184 qq_process_get_info_reply(data, data_len, gc); | |
185 break; | |
186 case QQ_CMD_CHANGE_ONLINE_STATUS: | |
187 qq_process_change_status_reply(data, data_len, gc); | |
188 break; | |
189 case QQ_CMD_SEND_IM: | |
190 qq_process_send_im_reply(data, data_len, gc); | |
191 break; | |
192 case QQ_CMD_RECV_IM: | |
193 qq_process_recv_im(data, data_len, seq, gc); | |
194 break; | |
195 case QQ_CMD_LOGIN: | |
196 qq_process_login_reply(data, data_len, gc); | |
197 break; | |
198 case QQ_CMD_GET_FRIENDS_LIST: | |
199 qq_process_get_buddies_list_reply(data, data_len, gc); | |
200 break; | |
201 case QQ_CMD_GET_FRIENDS_ONLINE: | |
202 qq_process_get_buddies_online_reply(data, data_len, gc); | |
203 break; | |
204 case QQ_CMD_GROUP_CMD: | |
205 qq_process_group_cmd_reply(data, data_len, seq, gc); | |
206 break; | |
207 case QQ_CMD_GET_ALL_LIST_WITH_GROUP: | |
208 qq_process_get_all_list_with_group_reply(data, data_len, gc); | |
209 break; | |
210 case QQ_CMD_GET_LEVEL: | |
211 qq_process_get_level_reply(data, data_len, gc); | |
212 break; | |
213 case QQ_CMD_REQUEST_LOGIN_TOKEN: | |
214 qq_process_request_login_token_reply(data, data_len, gc); | |
215 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: | |
223 packet_process_unknow(gc, data, data_len, cmd, seq); | |
224 break; | |
225 } | |
226 } | |
227 | |
228 /* process the incoming packet from qq_pending */ | |
229 static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) | |
230 { | |
231 qq_data *qd; | |
232 gint bytes, bytes_not_read; | |
233 | |
234 gboolean prev_login_status; | |
235 guint8 *new_data; | |
236 gint new_data_len; | |
237 | |
238 guint8 header_tag; | |
239 guint16 source_tag; | |
240 guint16 cmd; | |
241 guint16 seq; // May be ack_seq or send_seq, depends on cmd | |
242 | |
243 g_return_if_fail(buf != NULL && buf_len > 0); | |
244 | |
245 qd = (qq_data *) gc->proto_data; | |
246 | |
247 prev_login_status = qd->logged_in; | |
248 | |
249 // Len, header and tail tag have been checked before | |
250 bytes = 0; | |
251 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); | |
252 | |
253 if (QQ_DEBUG) { | |
254 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
255 "==> [%05d] 0x%04X %s, from (0x%04X %s)\n", | |
256 seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_source_str(source_tag)); | |
257 } | |
258 | |
259 bytes_not_read = buf_len - bytes - 1; | |
260 | |
261 if ( !qd->logged_in ) { | |
262 if (cmd != QQ_CMD_LOGIN && cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { | |
263 /* packets before login */ | |
264 qq_packet_push(qd, cmd, seq, buf + bytes, bytes_not_read); | |
265 return; /* do not process it now */ | |
266 } | |
267 } | |
268 | |
269 /* whether it is an ack */ | |
270 switch (cmd) { | |
271 case QQ_CMD_RECV_IM: | |
272 case QQ_CMD_RECV_MSG_SYS: | |
273 case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: | |
274 /* server intiated packet, we need to send ack and check duplicaion | |
275 * this must be put after processing b4_packet | |
276 * as these packets will be passed in twice */ | |
277 if (packet_is_dup(qd, seq)) { | |
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 } | |
288 | |
289 /* 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); | |
291 | |
292 // check is redirect or not, and do it now | |
293 if (qd->is_redirect) { | |
294 // free resource except real_hostname and port | |
295 qq_disconnect(gc); | |
296 qq_connect(gc->account); | |
297 return; | |
298 } | |
299 | |
300 if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { | |
301 /* logged_in, but we have packets before login */ | |
302 new_data = g_newa(guint8, MAX_PACKET_SIZE); | |
303 while (1) { | |
304 new_data_len = qq_packet_pop(qd, &cmd, &seq, new_data, MAX_PACKET_SIZE); | |
305 if (new_data_len < 0) { | |
306 break; | |
307 } | |
308 if (new_data_len == 0) { | |
309 continue; | |
310 } | |
311 packet_process_cmd(gc, seq, cmd, new_data, new_data_len); | |
312 } | |
313 } | |
314 } | |
315 | |
316 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) | |
317 { | |
318 PurpleConnection *gc; | |
319 qq_data *qd; | |
320 guint8 buf[1024]; // set to 16 when test tcp_rxqueue | |
321 gint buf_len; | |
322 gint bytes; | |
323 | |
324 guint8 *pkt; | |
325 guint16 pkt_len; | |
326 | |
327 gchar *error_msg; | |
328 guint8 *jump; | |
329 gint jump_len; | |
330 | |
331 gc = (PurpleConnection *) data; | |
332 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
333 | |
334 if(cond != PURPLE_INPUT_READ) { | |
335 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
336 _("Socket error")); | |
337 return; | |
338 } | |
339 | |
340 qd = (qq_data *) gc->proto_data; | |
341 | |
342 /* test code, not using tcp_rxqueue | |
343 memset(pkt,0, sizeof(pkt)); | |
344 buf_len = read(qd->fd, pkt, sizeof(pkt)); | |
345 if (buf_len > 2) { | |
346 packet_process(gc, pkt + 2, buf_len - 2); | |
347 } | |
348 return; | |
349 */ | |
350 | |
351 buf_len = read(qd->fd, buf, sizeof(buf)); | |
352 if (buf_len < 0) { | |
353 if (errno == EAGAIN) | |
354 /* No worries */ | |
355 return; | |
356 | |
357 error_msg = g_strdup_printf(_("Lost connection with server:\n%d, %s"), errno, g_strerror(errno)); | |
358 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_msg); | |
359 g_free(error_msg); | |
360 return; | |
361 } else if (buf_len == 0) { | |
362 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
363 _("Server closed the connection.")); | |
364 return; | |
365 } | |
366 | |
367 gc->last_received = time(NULL); | |
368 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", | |
369 "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); | |
370 qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); | |
371 memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); | |
372 qd->tcp_rxlen += buf_len; | |
373 | |
374 pkt = g_newa(guint8, MAX_PACKET_SIZE); | |
375 while (1) { | |
376 if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { | |
377 break; | |
378 } | |
379 | |
380 bytes = 0; | |
381 bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); | |
382 if (qd->tcp_rxlen < pkt_len) { | |
383 break; | |
384 } | |
385 | |
386 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", | |
387 "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); | |
388 | |
389 if ( pkt_len < QQ_TCP_HEADER_LENGTH | |
390 || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG | |
391 || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { | |
392 // HEY! This isn't even a QQ. What are you trying to pull? | |
393 | |
394 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", | |
395 "Packet error, failed to check header and tail tag\n"); | |
396 | |
397 jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); | |
398 if ( !jump ) { | |
399 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", | |
400 "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n"); | |
401 g_free(qd->tcp_rxqueue); | |
402 qd->tcp_rxqueue = NULL; | |
403 qd->tcp_rxlen = 0; | |
404 return; | |
405 } | |
406 | |
407 // jump and over QQ_PACKET_TAIL | |
408 jump_len = (jump - qd->tcp_rxqueue) + 1; | |
409 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", | |
410 "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); | |
412 qd->tcp_rxlen -= jump_len; | |
413 continue; | |
414 } | |
415 | |
416 memset(pkt, 0, MAX_PACKET_SIZE); | |
417 g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); | |
418 | |
419 // jump to next packet | |
420 qd->tcp_rxlen -= pkt_len; | |
421 if (qd->tcp_rxlen) { | |
422 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", | |
423 "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); | |
424 jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); | |
425 g_free(qd->tcp_rxqueue); | |
426 qd->tcp_rxqueue = jump; | |
427 } else { | |
428 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", | |
429 "free tcp_rxqueue\n"); | |
430 g_free(qd->tcp_rxqueue); | |
431 qd->tcp_rxqueue = NULL; | |
432 } | |
433 | |
434 if (pkt == NULL) { | |
435 continue; | |
436 } | |
437 // do not call packet_process before jump | |
438 // packet_process may call disconnect and destory tcp_rxqueue | |
439 packet_process(gc, pkt, pkt_len - bytes); | |
440 } | |
441 } | |
442 | |
443 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) | |
444 { | |
445 PurpleConnection *gc; | |
446 qq_data *qd; | |
447 guint8 *buf; | |
448 gint buf_len; | |
449 | |
450 gc = (PurpleConnection *) data; | |
451 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
452 | |
453 if(cond != PURPLE_INPUT_READ) { | |
454 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
455 _("Socket error")); | |
456 return; | |
457 } | |
458 | |
459 qd = (qq_data *) gc->proto_data; | |
460 g_return_if_fail(qd->fd >= 0); | |
461 | |
462 buf = g_newa(guint8, MAX_PACKET_SIZE); | |
463 | |
464 /* here we have UDP proxy suppport */ | |
465 buf_len = read(qd->fd, buf, MAX_PACKET_SIZE); | |
466 if (buf_len <= 0) { | |
467 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
468 _("Unable to read from socket")); | |
469 return; | |
470 } | |
471 | |
472 gc->last_received = time(NULL); | |
473 | |
474 if (buf_len < QQ_UDP_HEADER_LENGTH) { | |
475 if (buf[0] != QQ_PACKET_TAG || buf[buf_len - 1] != QQ_PACKET_TAIL) { | |
476 qq_hex_dump(PURPLE_DEBUG_ERROR, "UDP_PENDING", | |
477 buf, buf_len, | |
478 "Received packet is too short, or no header and tail tag"); | |
479 return; | |
480 } | |
481 } | |
482 | |
483 packet_process(gc, buf, buf_len); | |
484 } | |
485 | |
486 static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len) | |
487 { | |
488 gint ret; | |
489 | |
490 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); | |
491 | |
492 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Send %d bytes to socket %d\n", data_len, qd->fd); | |
493 | |
494 errno = 0; | |
495 ret = send(qd->fd, data, data_len, 0); | |
496 if (ret < 0 && errno == EAGAIN) { | |
497 return ret; | |
498 } | |
499 | |
500 if (ret < 0) { | |
501 /* TODO: what to do here - do we really have to disconnect? */ | |
502 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Send failed: %d, %s\n", errno, g_strerror(errno)); | |
503 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); | |
504 } | |
505 return ret; | |
506 } | |
507 | |
508 static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) | |
509 { | |
510 qq_data *qd = data; | |
511 int ret, writelen; | |
512 | |
513 writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf); | |
514 if (writelen == 0) { | |
515 purple_input_remove(qd->tx_handler); | |
516 qd->tx_handler = 0; | |
517 return; | |
518 } | |
519 | |
520 ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen); | |
521 purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE", | |
522 "total %d bytes is sent %d\n", writelen, ret); | |
523 | |
524 if (ret < 0 && errno == EAGAIN) | |
525 return; | |
526 else if (ret < 0) { | |
527 /* TODO: what to do here - do we really have to disconnect? */ | |
528 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
529 _("Write Error")); | |
530 return; | |
531 } | |
532 | |
533 purple_circ_buffer_mark_read(qd->tcp_txbuf, ret); | |
534 } | |
535 | |
536 static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len) | |
537 { | |
538 gint ret; | |
539 | |
540 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); | |
541 | |
542 // purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); | |
543 | |
544 if (qd->tx_handler == 0) { | |
545 ret = write(qd->fd, data, data_len); | |
546 } else { | |
547 ret = -1; | |
548 errno = EAGAIN; | |
549 } | |
550 | |
551 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", | |
552 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); | |
553 if (ret < 0 && errno == EAGAIN) { | |
554 // socket is busy, send later | |
555 // purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); | |
556 ret = 0; | |
557 } else if (ret <= 0) { | |
558 // TODO: what to do here - do we really have to disconnect? | |
559 purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", | |
560 "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)); | |
562 return ret; | |
563 } | |
564 | |
565 if (ret < data_len) { | |
566 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", | |
567 "Add %d bytes to buffer\n", data_len - ret); | |
568 if (qd->tx_handler == 0) { | |
569 qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd); | |
570 } | |
571 purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); | |
572 } | |
573 return ret; | |
574 } | |
575 | |
576 static gboolean trans_timeout(gpointer data) | |
577 { | |
578 PurpleConnection *gc; | |
579 qq_data *qd; | |
580 guint8 *buf; | |
581 gint buf_len = 0; | |
582 guint16 cmd; | |
583 gint retries = 0; | |
584 int index; | |
585 | |
586 gc = (PurpleConnection *) data; | |
587 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); | |
588 | |
589 qd = (qq_data *) gc->proto_data; | |
590 | |
591 index = 0; | |
592 buf = g_newa(guint8, MAX_PACKET_SIZE); | |
593 | |
594 while (1) { | |
595 if (index < 0) { | |
596 // next record is NULL | |
597 break; | |
598 } | |
599 // purple_debug(PURPLE_DEBUG_ERROR, "QQ", "scan begin %d\n", index); | |
600 memset(buf, 0, MAX_PACKET_SIZE); | |
601 buf_len = qq_trans_scan(qd, &index, buf, MAX_PACKET_SIZE, &cmd, &retries); | |
602 if (buf_len <= 0) { | |
603 // curr record is empty, whole trans is NULL | |
604 break; | |
605 } | |
606 // index = -1, when get last record of transactions | |
607 | |
608 // purple_debug(PURPLE_DEBUG_ERROR, "QQ", "retries %d next index %d\n", retries, index); | |
609 if (retries > 0) { | |
610 if (qd->use_tcp) { | |
611 tcp_send_out(qd, buf, buf_len); | |
612 } else { | |
613 udp_send_out(qd, buf, buf_len); | |
614 } | |
615 continue; | |
616 } | |
617 | |
618 // retries <= 0 | |
619 switch (cmd) { | |
620 case QQ_CMD_KEEP_ALIVE: | |
621 if (qd->logged_in) { | |
622 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection lost!\n"); | |
623 purple_connection_error_reason(gc, | |
624 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); | |
625 qd->logged_in = FALSE; | |
626 } | |
627 break; | |
628 case QQ_CMD_LOGIN: | |
629 case QQ_CMD_REQUEST_LOGIN_TOKEN: | |
630 if (!qd->logged_in) { | |
631 /* cancel login progress */ | |
632 purple_connection_error_reason(gc, | |
633 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Login failed, no reply")); | |
634 } | |
635 break; | |
636 default: | |
637 purple_debug(PURPLE_DEBUG_WARNING, "QQ", | |
638 "%s packet lost.\n", qq_get_cmd_desc(cmd)); | |
639 } | |
640 } | |
641 | |
642 return TRUE; /* if return FALSE, timeout callback stops */ | |
643 } | |
644 | |
645 /* the callback function after socket is built | |
646 * we setup the qq protocol related configuration here */ | |
647 static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) | |
648 { | |
649 qq_data *qd; | |
650 PurpleConnection *gc; | |
651 gchar *buf; | |
652 const gchar *passwd; | |
653 | |
654 gc = (PurpleConnection *) data; | |
655 | |
656 if (!PURPLE_CONNECTION_IS_VALID(gc)) { | |
657 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); | |
658 close(source); | |
659 return; | |
660 } | |
661 | |
662 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
663 | |
664 qd = (qq_data *) gc->proto_data; | |
665 | |
666 /* Connect is now complete; clear the PurpleProxyConnectData */ | |
667 qd->connect_data = NULL; | |
668 | |
669 if (source < 0) { /* socket returns -1 */ | |
670 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Source is < 0\n"); | |
671 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message); | |
672 return; | |
673 } | |
674 | |
675 // _qq_show_socket("Got login socket", source); | |
676 | |
677 /* QQ use random seq, to minimize duplicated packets */ | |
678 srandom(time(NULL)); | |
679 qd->send_seq = random() & 0x0000ffff; | |
680 qd->fd = source; | |
681 qd->logged_in = FALSE; | |
682 qd->channel = 1; | |
683 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); | |
684 | |
685 /* now generate md5 processed passwd */ | |
686 passwd = purple_account_get_password(purple_connection_get_account(gc)); | |
687 g_return_if_fail(qd->pwkey == NULL); | |
688 qd->pwkey = encrypt_account_password(passwd); | |
689 | |
690 g_return_if_fail(qd->resend_timeout == 0); | |
691 /* call trans_timeout every 5 seconds */ | |
692 qd->resend_timeout = purple_timeout_add(5000, trans_timeout, gc); | |
693 | |
694 if (qd->use_tcp) | |
695 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); | |
696 else | |
697 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); | |
698 | |
699 /* Update the login progress status display */ | |
700 buf = g_strdup_printf("Login as %d", qd->uid); | |
701 purple_connection_update_progress(gc, buf, 1, QQ_CONNECT_STEPS); | |
702 g_free(buf); | |
703 | |
704 qq_send_packet_request_login_token(gc); | |
705 } | |
706 | |
707 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) | |
708 { | |
709 PurpleConnection *gc; | |
710 qq_data *qd; | |
711 socklen_t len; | |
712 int error=0, ret; | |
713 | |
714 gc = (PurpleConnection *) data; | |
715 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
716 | |
717 qd = (qq_data *) gc->proto_data; | |
718 | |
719 | |
720 purple_debug_info("proxy", "Connected.\n"); | |
721 | |
722 /* | |
723 * getsockopt after a non-blocking connect returns -1 if something is | |
724 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and | |
725 * error holds what connect would have returned if it blocked until now. | |
726 * Thus, error == 0 is success, error == EINPROGRESS means "try again", | |
727 * and anything else is a real error. | |
728 * | |
729 * (error == EINPROGRESS can happen after a select because the kernel can | |
730 * be overly optimistic sometimes. select is just a hint that you might be | |
731 * able to do something.) | |
732 */ | |
733 len = sizeof(error); | |
734 ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); | |
735 if (ret == 0 && error == EINPROGRESS) | |
736 return; /* we'll be called again later */ | |
737 | |
738 purple_input_remove(qd->tx_handler); | |
739 qd->tx_handler = 0; | |
740 if (ret < 0 || error != 0) { | |
741 if(ret != 0) | |
742 error = errno; | |
743 | |
744 close(source); | |
745 | |
746 purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); | |
747 | |
748 qq_connect_cb(gc, -1, _("Unable to connect")); | |
749 return; | |
750 } | |
751 | |
752 qq_connect_cb(gc, source, NULL); | |
753 } | |
754 | |
755 static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { | |
756 PurpleConnection *gc; | |
757 qq_data *qd; | |
758 struct sockaddr server_addr; | |
759 int addr_size; | |
760 gint fd = -1; | |
761 int flags; | |
762 | |
763 gc = (PurpleConnection *) data; | |
764 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
765 | |
766 qd = (qq_data *) gc->proto_data; | |
767 | |
768 // udp_query_data must be set as NULL. | |
769 // Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error | |
770 qd->udp_query_data = NULL; | |
771 | |
772 if (!hosts || !hosts->data) { | |
773 purple_connection_error_reason(gc, | |
774 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
775 _("Couldn't resolve host")); | |
776 return; | |
777 } | |
778 | |
779 addr_size = GPOINTER_TO_INT(hosts->data); | |
780 hosts = g_slist_remove(hosts, hosts->data); | |
781 memcpy(&server_addr, hosts->data, addr_size); | |
782 g_free(hosts->data); | |
783 | |
784 hosts = g_slist_remove(hosts, hosts->data); | |
785 while(hosts) { | |
786 hosts = g_slist_remove(hosts, hosts->data); | |
787 g_free(hosts->data); | |
788 hosts = g_slist_remove(hosts, hosts->data); | |
789 } | |
790 | |
791 fd = socket(PF_INET, SOCK_DGRAM, 0); | |
792 if (fd < 0) { | |
793 purple_debug(PURPLE_DEBUG_ERROR, "QQ", | |
794 "Unable to create socket: %s\n", g_strerror(errno)); | |
795 return; | |
796 } | |
797 | |
798 /* we use non-blocking mode to speed up connection */ | |
799 flags = fcntl(fd, F_GETFL); | |
800 fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
801 | |
802 /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ | |
803 * | |
804 * If a UDP socket is unconnected, which is the normal state after a | |
805 * bind() call, then send() or write() are not allowed, since no | |
806 * destination is available; only sendto() can be used to send data. | |
807 * | |
808 * Calling connect() on the socket simply records the specified address | |
809 * and port number as being the desired communications partner. That | |
810 * means that send() or write() are now allowed; they use the destination | |
811 * address and port given on the connect call as the destination of packets. | |
812 */ | |
813 if (connect(fd, &server_addr, addr_size) >= 0) { | |
814 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); | |
815 flags = fcntl(fd, F_GETFL); | |
816 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); | |
817 qq_connect_cb(gc, fd, NULL); | |
818 return; | |
819 } | |
820 | |
821 /* [EINPROGRESS] | |
822 * The socket is marked as non-blocking and the connection cannot be | |
823 * completed immediately. It is possible to select for completion by | |
824 * selecting the socket for writing. | |
825 * [EINTR] | |
826 * A signal interrupted the call. | |
827 * The connection is established asynchronously. | |
828 */ | |
829 if ((errno == EINPROGRESS) || (errno == EINTR)) { | |
830 purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); | |
831 qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); | |
832 return; | |
833 } | |
834 | |
835 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %d\n", g_strerror(errno)); | |
836 close(fd); | |
837 } | |
838 | |
839 /* establish a generic QQ connection | |
840 * TCP/UDP, and direct/redirected */ | |
841 void qq_connect(PurpleAccount *account) | |
842 { | |
843 PurpleConnection *gc; | |
844 qq_data *qd; | |
845 | |
846 gc = purple_account_get_connection(account); | |
847 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
848 | |
849 qd = (qq_data *) gc->proto_data; | |
850 | |
851 if (qd->real_hostname == NULL || qd->real_port == 0) { | |
852 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
853 _("hostname is NULL or port is 0")); | |
854 return; | |
855 } | |
856 | |
857 if (qd->is_redirect) { | |
858 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", | |
859 qd->real_hostname, qd->real_port); | |
860 } | |
861 qd->is_redirect = FALSE; | |
862 | |
863 qd->fd = -1; | |
864 qd->tx_handler = 0; | |
865 | |
866 g_return_if_fail(qd->real_hostname != NULL); | |
867 | |
868 /* QQ connection via UDP/TCP. | |
869 * Now use Purple proxy function to provide TCP proxy support, | |
870 * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ | |
871 if(qd->use_tcp) { | |
872 purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", | |
873 qd->real_hostname, qd->real_port); | |
874 | |
875 /* TODO: is there a good default grow size? */ | |
876 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n"); | |
877 qd->tcp_txbuf = purple_circ_buffer_new(0); | |
878 | |
879 qd->connect_data = purple_proxy_connect(NULL, account, | |
880 qd->real_hostname, qd->real_port, qq_connect_cb, gc); | |
881 if (qd->connect_data == NULL) { | |
882 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
883 _("Unable to connect.")); | |
884 } | |
885 return; | |
886 } | |
887 | |
888 purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n", | |
889 qd->real_hostname, qd->real_port); | |
890 | |
891 g_return_if_fail(qd->udp_query_data == NULL); | |
892 qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port, | |
893 udp_host_resolved, gc); | |
894 if (qd->udp_query_data == NULL) { | |
895 purple_connection_error_reason(qd->gc, | |
896 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
897 _("Could not resolve hostname")); | |
898 } | |
899 } | |
900 | |
901 /* clean up qq_data structure and all its components | |
902 * always used before a redirectly connection */ | |
903 void qq_disconnect(PurpleConnection *gc) | |
904 { | |
905 qq_data *qd; | |
906 | |
907 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
908 qd = (qq_data *) gc->proto_data; | |
909 | |
910 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); | |
911 /* finish all I/O */ | |
912 if (qd->fd >= 0 && qd->logged_in) { | |
913 qq_send_packet_logout(gc); | |
914 } | |
915 | |
916 if (qd->resend_timeout > 0) { | |
917 purple_timeout_remove(qd->resend_timeout); | |
918 qd->resend_timeout = 0; | |
919 } | |
920 | |
921 if (gc->inpa > 0) { | |
922 purple_input_remove(gc->inpa); | |
923 gc->inpa = 0; | |
924 } | |
925 | |
926 if (qd->fd >= 0) { | |
927 close(qd->fd); | |
928 qd->fd = -1; | |
929 } | |
930 | |
931 if (qd->connect_data != NULL) { | |
932 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); | |
933 purple_proxy_connect_cancel(qd->connect_data); | |
934 } | |
935 | |
936 if(qd->tcp_txbuf != NULL) { | |
937 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); | |
938 purple_circ_buffer_destroy(qd->tcp_txbuf); | |
939 } | |
940 | |
941 if (qd->tx_handler) { | |
942 purple_input_remove(qd->tx_handler); | |
943 qd->tx_handler = 0; | |
944 } | |
945 if (qd->tcp_rxqueue != NULL) { | |
946 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n"); | |
947 g_free(qd->tcp_rxqueue); | |
948 qd->tcp_rxqueue = NULL; | |
949 qd->tcp_rxlen = 0; | |
950 } | |
951 | |
952 if (qd->udp_query_data != NULL) { | |
953 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); | |
954 purple_dnsquery_destroy(qd->udp_query_data); | |
955 qd->udp_query_data = NULL; | |
956 } | |
957 | |
958 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy transactions\n"); | |
959 qq_trans_remove_all(qd); | |
960 | |
961 if (qd->inikey) { | |
962 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free inikey\n"); | |
963 g_free(qd->inikey); | |
964 qd->inikey = NULL; | |
965 } | |
966 if (qd->pwkey) { | |
967 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free pwkey\n"); | |
968 g_free(qd->pwkey); | |
969 qd->pwkey = NULL; | |
970 } | |
971 if (qd->session_key) { | |
972 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_key\n"); | |
973 g_free(qd->session_key); | |
974 qd->session_key = NULL; | |
975 } | |
976 if (qd->session_md5) { | |
977 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free session_md5\n"); | |
978 g_free(qd->session_md5); | |
979 qd->session_md5 = NULL; | |
980 } | |
981 if (qd->my_ip) { | |
982 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free my_ip\n"); | |
983 g_free(qd->my_ip); | |
984 qd->my_ip = NULL; | |
985 } | |
986 | |
987 qq_packet_remove_all(qd); | |
988 qq_group_packets_free(qd); | |
989 qq_group_free_all(qd); | |
990 qq_add_buddy_request_free(qd); | |
991 qq_info_query_free(qd); | |
992 qq_buddies_list_free(gc->account, qd); | |
993 } | |
994 | |
995 static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, | |
996 guint8 *data, gint data_len) | |
997 { | |
998 gint bytes = 0; | |
999 g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); | |
1000 | |
1001 if (data == NULL) { | |
1002 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data is NULL\n"); | |
1003 return -1; | |
1004 } | |
1005 if (data_len <= 0) { | |
1006 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Fail encap packet, data len <= 0\n"); | |
1007 return -1; | |
1008 } | |
1009 | |
1010 /* QQ TCP packet has two bytes in the begining defines packet length | |
1011 * so leave room here to store packet size */ | |
1012 if (qd->use_tcp) { | |
1013 bytes += qq_put16(buf + bytes, 0x0000); | |
1014 } | |
1015 /* now comes the normal QQ packet as UDP */ | |
1016 bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); | |
1017 bytes += qq_put16(buf + bytes, QQ_CLIENT); | |
1018 bytes += qq_put16(buf + bytes, cmd); | |
1019 | |
1020 bytes += qq_put16(buf + bytes, seq); | |
1021 | |
1022 bytes += qq_put32(buf + bytes, qd->uid); | |
1023 bytes += qq_putdata(buf + bytes, data, data_len); | |
1024 bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); | |
1025 | |
1026 // set TCP packet length at begin of the packet | |
1027 if (qd->use_tcp) { | |
1028 qq_put16(buf, bytes); | |
1029 } | |
1030 | |
1031 return bytes; | |
1032 } | |
1033 | |
1034 gint qq_send_data(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) | |
1035 { | |
1036 qq_data *qd; | |
1037 guint8 *buf; | |
1038 gint buf_len; | |
1039 gint bytes_sent; | |
1040 gint seq; | |
1041 | |
1042 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | |
1043 qd = (qq_data *) gc->proto_data; | |
1044 | |
1045 buf = g_newa(guint8, MAX_PACKET_SIZE); | |
1046 memset(buf, 0, MAX_PACKET_SIZE); | |
1047 seq = ++(qd->send_seq); | |
1048 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); | |
1049 if (buf_len <= 0) { | |
1050 return -1; | |
1051 } | |
1052 | |
1053 if (qd->use_tcp) { | |
1054 bytes_sent = tcp_send_out(qd, buf, buf_len); | |
1055 } else { | |
1056 bytes_sent = udp_send_out(qd, buf, buf_len); | |
1057 } | |
1058 | |
1059 // always need ack | |
1060 qq_trans_append(qd, buf, buf_len, cmd, seq); | |
1061 | |
1062 if (QQ_DEBUG) { | |
1063 qq_show_packet("QQ_SEND_DATA", buf, buf_len); | |
1064 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
1065 "<== [%05d], %s, total %d bytes is sent %d\n", | |
1066 seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); | |
1067 } | |
1068 return bytes_sent; | |
1069 } | |
1070 | |
1071 /* send the packet generated with the given cmd and data | |
1072 * return the number of bytes sent to socket if succeeds | |
1073 * return -1 if there is any error */ | |
1074 gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, | |
1075 gboolean is_auto_seq, guint16 seq, gboolean need_ack, guint8 *data, gint data_len) | |
1076 { | |
1077 qq_data *qd; | |
1078 guint8 *buf; | |
1079 gint buf_len; | |
1080 guint8 *encrypted_data; | |
1081 gint encrypted_len; | |
1082 gint real_seq; | |
1083 gint bytes_sent; | |
1084 | |
1085 qd = (qq_data *) gc->proto_data; | |
1086 g_return_val_if_fail(qd->session_key != NULL, -1); | |
1087 | |
1088 encrypted_len = data_len + 16; /* at most 16 bytes more */ | |
1089 encrypted_data = g_newa(guint8, encrypted_len); | |
1090 | |
1091 qq_encrypt(data, data_len, qd->session_key, encrypted_data, &encrypted_len); | |
1092 | |
1093 real_seq = seq; | |
1094 if (is_auto_seq) real_seq = ++(qd->send_seq); | |
1095 | |
1096 buf = g_newa(guint8, MAX_PACKET_SIZE); | |
1097 memset(buf, 0, MAX_PACKET_SIZE); | |
1098 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, real_seq, encrypted_data, encrypted_len); | |
1099 if (buf_len <= 0) { | |
1100 return -1; | |
1101 } | |
1102 | |
1103 if (QQ_DEBUG) { | |
1104 qq_show_packet("QQ_SEND_CMD", buf, buf_len); | |
1105 } | |
1106 if (qd->use_tcp) { | |
1107 bytes_sent = tcp_send_out(qd, buf, buf_len); | |
1108 } else { | |
1109 bytes_sent = udp_send_out(qd, buf, buf_len); | |
1110 } | |
1111 | |
1112 /* if it does not need ACK, we send ACK manually several times */ | |
1113 if (need_ack) { | |
1114 qq_trans_append(qd, buf, buf_len, cmd, real_seq); | |
1115 } | |
1116 | |
1117 if (QQ_DEBUG) { | |
1118 qq_show_packet("QQ_SEND_CMD", buf, buf_len); | |
1119 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
1120 "<== [%05d], %s, total %d bytes is sent %d\n", | |
1121 real_seq, qq_get_cmd_desc(cmd), buf_len, bytes_sent); | |
1122 } | |
1123 return bytes_sent; | |
1124 } |