Mercurial > pidgin
annotate libgaim/protocols/qq/recv_core.c @ 15042:edd78a0c6698
[gaim-migrate @ 17825]
partition the tests
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Sun, 26 Nov 2006 23:31:16 +0000 |
parents | c039c920e11c |
children | e952a5fc97b3 |
rev | line source |
---|---|
14192 | 1 /** |
15025 | 2 * @file recv_core.c |
14192 | 3 * |
15025 | 4 * gaim |
14192 | 5 * |
15025 | 6 * Gaim 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. | |
14192 | 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 */ | |
24 | |
25 #include "debug.h" | |
26 #include "internal.h" | |
27 | |
28 #include "buddy_info.h" | |
29 #include "buddy_list.h" | |
30 #include "buddy_opt.h" | |
31 #include "buddy_status.h" | |
32 #include "char_conv.h" | |
33 #include "crypt.h" | |
34 #include "group_network.h" | |
35 #include "header_info.h" | |
36 #include "keep_alive.h" | |
37 #include "im.h" | |
38 #include "login_logout.h" | |
39 #include "packet_parse.h" | |
40 #include "qq_proxy.h" | |
41 #include "recv_core.h" | |
42 #include "sendqueue.h" | |
43 #include "sys_msg.h" | |
44 #include "utils.h" | |
45 | |
46 typedef struct _packet_before_login packet_before_login; | |
47 typedef struct _qq_recv_msg_header qq_recv_msg_header; | |
48 | |
49 struct _packet_before_login { | |
50 guint8 *buf; | |
51 gint len; | |
52 }; | |
53 | |
54 struct _qq_recv_msg_header { | |
55 guint8 header_tag; | |
56 guint16 source_tag; | |
57 guint16 cmd; | |
58 guint16 seq; /* can be ack_seq or send_seq, depends on cmd */ | |
59 }; | |
60 | |
61 /* check whether one sequence number is duplicated or not | |
62 * return TRUE if it is duplicated, otherwise FALSE */ | |
63 static gboolean _qq_check_packet_set_window(guint16 seq, GaimConnection *gc) | |
64 { | |
65 qq_data *qd; | |
66 guint8 *byte, mask; | |
67 | |
68 qd = (qq_data *) gc->proto_data; | |
69 byte = &(qd->window[seq / 8]); | |
70 mask = (1 << (seq % 8)); | |
71 | |
72 if ((*byte) & mask) | |
73 return TRUE; /* check mask */ | |
74 (*byte) |= mask; | |
75 return FALSE; /* set mask */ | |
14629 | 76 } |
14192 | 77 |
78 /* default process, decrypt and dump */ | |
79 static void _qq_process_packet_default(guint8 *buf, gint buf_len, guint16 cmd, guint16 seq, GaimConnection *gc) | |
80 { | |
81 qq_data *qd; | |
82 guint8 *data; | |
83 gchar *msg_utf8; | |
84 gint len; | |
85 | |
86 g_return_if_fail(buf != NULL && buf_len != 0); | |
87 | |
88 qd = (qq_data *) gc->proto_data; | |
89 len = buf_len; | |
90 data = g_newa(guint8, len); | |
91 msg_utf8 = NULL; | |
92 | |
93 _qq_show_packet("Processing unknown packet", buf, len); | |
94 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) { | |
14656 | 95 gchar *hex_dump = hex_dump_to_str(data, len); |
14192 | 96 gaim_debug(GAIM_DEBUG_WARNING, "QQ", |
97 ">>> [%d] %s, %d bytes -> [default] decrypt and dump\n%s", | |
14656 | 98 seq, qq_get_cmd_desc(cmd), buf_len, hex_dump); |
99 g_free(hex_dump); | |
14192 | 100 try_dump_as_gbk(data, len); |
101 } else { | |
102 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Fail decrypt packet with default process\n"); | |
103 } | |
104 } | |
105 | |
106 /* process the incoming packet from qq_pending */ | |
107 static void _qq_packet_process(guint8 *buf, gint buf_len, GaimConnection *gc) | |
108 { | |
109 qq_data *qd; | |
110 gint len, bytes_expected, bytes_read; | |
111 guint16 buf_len_read; /* two bytes in the begining of TCP packet */ | |
112 guint8 *cursor; | |
113 qq_recv_msg_header header; | |
114 packet_before_login *b4_packet; | |
115 | |
116 g_return_if_fail(buf != NULL && buf_len > 0); | |
117 | |
118 qd = (qq_data *) gc->proto_data; | |
119 bytes_expected = qd->use_tcp ? QQ_TCP_HEADER_LENGTH : QQ_UDP_HEADER_LENGTH; | |
120 | |
121 if (buf_len < bytes_expected) { | |
14656 | 122 gchar *hex_dump = hex_dump_to_str(buf, buf_len); |
14192 | 123 gaim_debug(GAIM_DEBUG_ERROR, |
14656 | 124 "QQ", "Received packet is too short, dump and drop\n%s", hex_dump); |
125 g_free(hex_dump); | |
14192 | 126 return; |
127 } | |
128 /* initialize */ | |
129 cursor = buf; | |
130 bytes_read = 0; | |
131 | |
132 /* QQ TCP packet returns first 2 bytes the length of this packet */ | |
133 if (qd->use_tcp) { | |
134 bytes_read += read_packet_w(buf, &cursor, buf_len, &buf_len_read); | |
135 if (buf_len_read != buf_len) { /* wrong */ | |
136 gaim_debug | |
137 (GAIM_DEBUG_ERROR, | |
138 "QQ", | |
139 "TCP read %d bytes, header says %d bytes, use header anyway\n", buf_len, buf_len_read); | |
140 buf_len = buf_len_read; /* we believe header is more accurate */ | |
141 } | |
142 } | |
143 | |
144 /* now goes the normal QQ packet as UDP packet */ | |
145 bytes_read += read_packet_b(buf, &cursor, buf_len, &header.header_tag); | |
146 bytes_read += read_packet_w(buf, &cursor, buf_len, &header.source_tag); | |
147 bytes_read += read_packet_w(buf, &cursor, buf_len, &header.cmd); | |
148 bytes_read += read_packet_w(buf, &cursor, buf_len, &header.seq); | |
149 | |
150 if (bytes_read != bytes_expected) { /* read error */ | |
151 gaim_debug(GAIM_DEBUG_ERROR, "QQ", | |
152 "Fail reading packet header, expect %d bytes, read %d bytes\n", | |
153 bytes_expected, bytes_read); | |
154 return; | |
155 } | |
156 | |
157 if ((buf[buf_len - 1] != QQ_PACKET_TAIL) || (header.header_tag != QQ_PACKET_TAG)) { | |
14656 | 158 gchar *hex_dump = hex_dump_to_str(buf, buf_len); |
14192 | 159 gaim_debug(GAIM_DEBUG_ERROR, |
14656 | 160 "QQ", "Unknown QQ proctocol, dump and drop\n%s", hex_dump); |
161 g_free(hex_dump); | |
14192 | 162 return; |
163 } | |
164 | |
165 if (QQ_DEBUG) | |
166 gaim_debug(GAIM_DEBUG_INFO, "QQ", | |
167 "==> [%05d] %s, from (%s)\n", | |
168 header.seq, qq_get_cmd_desc(header.cmd), qq_get_source_str(header.source_tag)); | |
169 | |
170 if (header.cmd != QQ_CMD_LOGIN && header.cmd != QQ_CMD_REQUEST_LOGIN_TOKEN) { | |
171 if (!qd->logged_in) { /* packets before login */ | |
172 b4_packet = g_new0(packet_before_login, 1); | |
173 /* must duplicate, buffer will be freed after exiting this function */ | |
174 b4_packet->buf = g_memdup(buf, buf_len); | |
175 b4_packet->len = buf_len; | |
176 if (qd->before_login_packets == NULL) | |
177 qd->before_login_packets = g_queue_new(); | |
178 g_queue_push_head(qd->before_login_packets, b4_packet); | |
179 return; /* do not process it now */ | |
180 } else if (!g_queue_is_empty(qd->before_login_packets)) { | |
181 /* logged_in, but we have packets before login */ | |
182 b4_packet = (packet_before_login *) | |
183 g_queue_pop_head(qd->before_login_packets); | |
184 _qq_packet_process(b4_packet->buf, b4_packet->len, gc); | |
185 /* in fact this is a recursive call, | |
186 * all packets before login will be processed before goes on */ | |
187 g_free(b4_packet->buf); /* the buf is duplicated, need to be freed */ | |
188 g_free(b4_packet); | |
189 } | |
190 } | |
191 | |
192 /* this is the length of all the encrypted data (also remove tail tag */ | |
193 len = buf_len - (bytes_read) - 1; | |
194 | |
195 /* whether it is an ack */ | |
196 switch (header.cmd) { | |
197 case QQ_CMD_RECV_IM: | |
198 case QQ_CMD_RECV_MSG_SYS: | |
199 case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: | |
200 /* server intiated packet, we need to send ack and check duplicaion | |
201 * this must be put after processing b4_packet | |
202 * as these packets will be passed in twice */ | |
203 if (_qq_check_packet_set_window(header.seq, gc)) { | |
204 gaim_debug(GAIM_DEBUG_WARNING, | |
205 "QQ", "dup [%05d] %s, discard...\n", header.seq, qq_get_cmd_desc(header.cmd)); | |
206 return; | |
207 } | |
208 break; | |
209 default:{ /* ack packet, we need to update sendqueue */ | |
210 /* we do not check duplication for server ack */ | |
211 qq_sendqueue_remove(qd, header.seq); | |
212 if (QQ_DEBUG) | |
213 gaim_debug(GAIM_DEBUG_INFO, "QQ", | |
214 "ack [%05d] %s, remove from sendqueue\n", | |
215 header.seq, qq_get_cmd_desc(header.cmd)); | |
216 } | |
217 } | |
218 | |
219 /* now process the packet */ | |
220 switch (header.cmd) { | |
221 case QQ_CMD_KEEP_ALIVE: | |
222 qq_process_keep_alive_reply(cursor, len, gc); | |
223 break; | |
224 case QQ_CMD_UPDATE_INFO: | |
225 qq_process_modify_info_reply(cursor, len, gc); | |
226 break; | |
227 case QQ_CMD_ADD_FRIEND_WO_AUTH: | |
228 qq_process_add_buddy_reply(cursor, len, header.seq, gc); | |
229 break; | |
230 case QQ_CMD_DEL_FRIEND: | |
231 qq_process_remove_buddy_reply(cursor, len, gc); | |
232 break; | |
233 case QQ_CMD_REMOVE_SELF: | |
234 qq_process_remove_self_reply(cursor, len, gc); | |
235 break; | |
236 case QQ_CMD_BUDDY_AUTH: | |
237 qq_process_add_buddy_auth_reply(cursor, len, gc); | |
238 break; | |
239 case QQ_CMD_GET_USER_INFO: | |
240 qq_process_get_info_reply(cursor, len, gc); | |
241 break; | |
242 case QQ_CMD_CHANGE_ONLINE_STATUS: | |
243 qq_process_change_status_reply(cursor, len, gc); | |
244 break; | |
245 case QQ_CMD_SEND_IM: | |
246 qq_process_send_im_reply(cursor, len, gc); | |
247 break; | |
248 case QQ_CMD_RECV_IM: | |
249 qq_process_recv_im(cursor, len, header.seq, gc); | |
250 break; | |
251 case QQ_CMD_LOGIN: | |
252 qq_process_login_reply(cursor, len, gc); | |
253 break; | |
254 case QQ_CMD_GET_FRIENDS_LIST: | |
255 qq_process_get_buddies_list_reply(cursor, len, gc); | |
256 break; | |
257 case QQ_CMD_GET_FRIENDS_ONLINE: | |
258 qq_process_get_buddies_online_reply(cursor, len, gc); | |
259 break; | |
260 case QQ_CMD_GROUP_CMD: | |
261 qq_process_group_cmd_reply(cursor, len, header.seq, gc); | |
262 break; | |
263 case QQ_CMD_GET_ALL_LIST_WITH_GROUP: | |
264 qq_process_get_all_list_with_group_reply(cursor, len, gc); | |
265 break; | |
266 case QQ_CMD_REQUEST_LOGIN_TOKEN: | |
267 qq_process_request_login_token_reply(cursor, len, gc); | |
268 break; | |
269 case QQ_CMD_RECV_MSG_SYS: | |
270 qq_process_msg_sys(cursor, len, header.seq, gc); | |
271 break; | |
272 case QQ_CMD_RECV_MSG_FRIEND_CHANGE_STATUS: | |
273 qq_process_friend_change_status(cursor, len, gc); | |
274 break; | |
275 default: | |
276 _qq_process_packet_default(cursor, len, header.cmd, header.seq, gc); | |
277 break; | |
278 } | |
279 } | |
280 | |
281 /* clean up the packets before login */ | |
282 void qq_b4_packets_free(qq_data *qd) | |
283 { | |
284 packet_before_login *b4_packet; | |
285 g_return_if_fail(qd != NULL); | |
286 /* now clean up my own data structures */ | |
287 if (qd->before_login_packets != NULL) { | |
288 while (NULL != (b4_packet = g_queue_pop_tail(qd->before_login_packets))) { | |
289 g_free(b4_packet->buf); | |
290 g_free(b4_packet); | |
291 } | |
292 g_queue_free(qd->before_login_packets); | |
293 } | |
294 } | |
295 | |
296 void qq_input_pending(gpointer data, gint source, GaimInputCondition cond) | |
297 { | |
298 GaimConnection *gc; | |
299 qq_data *qd; | |
300 guint8 *buf; | |
301 gint len; | |
302 | |
303 gc = (GaimConnection *) data; | |
14498
26bca01c7076
[gaim-migrate @ 17217]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14195
diff
changeset
|
304 |
26bca01c7076
[gaim-migrate @ 17217]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14195
diff
changeset
|
305 if(cond != GAIM_INPUT_READ) { |
14195 | 306 gaim_connection_error(gc, _("Socket error")); |
307 return; | |
308 } | |
14192 | 309 |
310 qd = (qq_data *) gc->proto_data; | |
311 buf = g_newa(guint8, MAX_PACKET_SIZE); | |
312 | |
313 /* here we have UDP proxy suppport */ | |
314 len = qq_proxy_read(qd, buf, MAX_PACKET_SIZE); | |
315 if (len <= 0) { | |
316 gaim_connection_error(gc, _("Unable to read from socket")); | |
317 return; | |
318 } else { | |
319 _qq_packet_process(buf, len, gc); | |
320 } | |
321 } |