Mercurial > pidgin
comparison libpurple/protocols/qq/qq_network.c @ 24095:25f62d21b3f8
disapproval of revision '8cebefbc6cd5d84acb69c74e69e8821f11dd225d'
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Mon, 15 Sep 2008 03:04:07 +0000 |
parents | 147ada94a1d8 |
children | 225e0e9e1055 |
comparison
equal
deleted
inserted
replaced
24088:147ada94a1d8 | 24095:25f62d21b3f8 |
---|---|
24 | 24 |
25 #include "cipher.h" | 25 #include "cipher.h" |
26 #include "debug.h" | 26 #include "debug.h" |
27 #include "internal.h" | 27 #include "internal.h" |
28 | 28 |
29 #ifdef _WIN32 | |
30 #define random rand | |
31 #define srandom srand | |
32 #endif | |
33 | |
29 #include "buddy_info.h" | 34 #include "buddy_info.h" |
30 #include "group_info.h" | 35 #include "group_info.h" |
31 #include "group_free.h" | 36 #include "group_free.h" |
32 #include "qq_crypt.h" | 37 #include "qq_crypt.h" |
33 #include "header_info.h" | 38 #include "header_info.h" |
37 #include "qq_network.h" | 42 #include "qq_network.h" |
38 #include "qq_trans.h" | 43 #include "qq_trans.h" |
39 #include "utils.h" | 44 #include "utils.h" |
40 #include "qq_process.h" | 45 #include "qq_process.h" |
41 | 46 |
42 #define QQ_DEFAULT_PORT 8000 | 47 /* set QQ_RECONNECT_MAX to 1, when test reconnecting */ |
43 | 48 #define QQ_RECONNECT_MAX 4 |
44 /* set QQ_CONNECT_MAX to 1, when test reconnecting */ | 49 #define QQ_RECONNECT_INTERVAL 5000 |
45 #define QQ_CONNECT_MAX 3 | 50 #define QQ_KEEP_ALIVE_INTERVAL 60000 |
46 #define QQ_CONNECT_INTERVAL 2 | 51 #define QQ_TRANS_INTERVAL 10000 |
47 #define QQ_CONNECT_CHECK 5 | 52 |
48 #define QQ_KEEP_ALIVE_INTERVAL 60 | |
49 #define QQ_TRANS_INTERVAL 10 | |
50 | |
51 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port); | |
52 | |
53 static qq_connection *connection_find(qq_data *qd, int fd) { | |
54 qq_connection *ret = NULL; | |
55 GSList *entry = qd->openconns; | |
56 while(entry) { | |
57 ret = entry->data; | |
58 if(ret->fd == fd) return ret; | |
59 entry = entry->next; | |
60 } | |
61 return NULL; | |
62 } | |
63 | |
64 static qq_connection *connection_create(qq_data *qd, int fd) { | |
65 qq_connection *ret = g_new0(qq_connection, 1); | |
66 ret->fd = fd; | |
67 qd->openconns = g_slist_append(qd->openconns, ret); | |
68 return ret; | |
69 } | |
70 | |
71 static void connection_remove(qq_data *qd, int fd) { | |
72 qq_connection *conn = connection_find(qd, fd); | |
73 qd->openconns = g_slist_remove(qd->openconns, conn); | |
74 | |
75 g_return_if_fail( conn != NULL ); | |
76 | |
77 purple_debug_info("QQ", "Close socket %d\n", conn->fd); | |
78 if(conn->input_handler > 0) purple_input_remove(conn->input_handler); | |
79 if(conn->can_write_handler > 0) purple_input_remove(conn->can_write_handler); | |
80 | |
81 if (conn->fd >= 0) close(conn->fd); | |
82 if(conn->tcp_txbuf != NULL) purple_circ_buffer_destroy(conn->tcp_txbuf); | |
83 if (conn->tcp_rxqueue != NULL) g_free(conn->tcp_rxqueue); | |
84 | |
85 g_free(conn); | |
86 } | |
87 | |
88 static void connection_free_all(qq_data *qd) { | |
89 qq_connection *ret = NULL; | |
90 GSList *entry = qd->openconns; | |
91 while(entry) { | |
92 ret = entry->data; | |
93 connection_remove(qd, ret->fd); | |
94 entry = qd->openconns; | |
95 } | |
96 } | |
97 static gboolean set_new_server(qq_data *qd) | 53 static gboolean set_new_server(qq_data *qd) |
98 { | 54 { |
99 gint count; | 55 gint count; |
100 gint index; | 56 gint index; |
101 GList *it = NULL; | 57 GList *it = NULL; |
102 | 58 |
103 g_return_val_if_fail(qd != NULL, FALSE); | 59 g_return_val_if_fail(qd != NULL, FALSE); |
104 | 60 |
105 if (qd->servers == NULL) { | 61 if (qd->servers == NULL) { |
106 purple_debug_info("QQ", "Server list is NULL\n"); | 62 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list is NULL\n"); |
107 return FALSE; | 63 return FALSE; |
108 } | 64 } |
109 | 65 |
66 if (qd->real_hostname) { | |
67 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free real_hostname\n"); | |
68 g_free(qd->real_hostname); | |
69 qd->real_hostname = NULL; | |
70 } | |
71 | |
110 /* remove server used before */ | 72 /* remove server used before */ |
111 if (qd->curr_server != NULL) { | 73 if (qd->server_name != NULL) { |
112 purple_debug_info("QQ", | 74 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
113 "Remove current [%s] from server list\n", qd->curr_server); | 75 "Remove previous server [%s]\n", qd->server_name); |
114 qd->servers = g_list_remove(qd->servers, qd->curr_server); | 76 qd->servers = g_list_remove(qd->servers, qd->server_name); |
115 qd->curr_server = NULL; | 77 qd->server_name = NULL; |
116 } | 78 } |
117 | 79 |
118 count = g_list_length(qd->servers); | 80 count = g_list_length(qd->servers); |
119 purple_debug_info("QQ", "Server list has %d\n", count); | 81 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server list has %d\n", count); |
120 if (count <= 0) { | 82 if (count <= 0) { |
121 /* no server left, disconnect when result is false */ | 83 /* no server left, disconnect when result is false */ |
122 qd->servers = NULL; | 84 qd->servers = NULL; |
123 return FALSE; | 85 return FALSE; |
124 } | 86 } |
125 | 87 |
126 /* get new server */ | 88 /* get new server */ |
127 index = rand() % count; | 89 index = random() % count; |
128 it = g_list_nth(qd->servers, index); | 90 it = g_list_nth(qd->servers, index); |
129 qd->curr_server = it->data; /* do not free server_name */ | 91 qd->server_name = it->data; /* do not free server_name */ |
130 if (qd->curr_server == NULL || strlen(qd->curr_server) <= 0 ) { | 92 if (qd->server_name == NULL || strlen(qd->server_name) <= 0 ) { |
131 purple_debug_info("QQ", "Server name at %d is empty\n", index); | 93 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Server name at %d is empty\n", index); |
132 return FALSE; | 94 return FALSE; |
133 } | 95 } |
134 | 96 |
135 purple_debug_info("QQ", "set new server to %s\n", qd->curr_server); | 97 qd->real_hostname = g_strdup(qd->server_name); |
98 qd->real_port = qd->user_port; | |
99 | |
100 qd->reconnect_times = QQ_RECONNECT_MAX; | |
101 | |
102 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
103 "set new server to %s:%d\n", qd->real_hostname, qd->real_port); | |
136 return TRUE; | 104 return TRUE; |
137 } | 105 } |
138 | 106 |
139 static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, | 107 static gint packet_get_header(guint8 *header_tag, guint16 *source_tag, |
140 guint16 *cmd, guint16 *seq, guint8 *buf) | 108 guint16 *cmd, guint16 *seq, guint8 *buf) |
145 bytes += qq_get16(cmd, buf + bytes); | 113 bytes += qq_get16(cmd, buf + bytes); |
146 bytes += qq_get16(seq, buf + bytes); | 114 bytes += qq_get16(seq, buf + bytes); |
147 return bytes; | 115 return bytes; |
148 } | 116 } |
149 | 117 |
150 static gboolean connect_check(gpointer data) | 118 static gboolean reconnect_later_cb(gpointer data) |
151 { | 119 { |
152 PurpleConnection *gc = (PurpleConnection *) data; | 120 PurpleConnection *gc; |
153 qq_data *qd; | 121 qq_data *qd; |
154 | 122 |
123 gc = (PurpleConnection *) data; | |
155 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); | 124 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); |
156 qd = (qq_data *) gc->proto_data; | 125 qd = (qq_data *) gc->proto_data; |
157 | 126 |
158 if (qd->connect_watcher > 0) { | 127 qd->reconnect_timeout = 0; |
159 purple_timeout_remove(qd->connect_watcher); | 128 |
160 qd->connect_watcher = 0; | 129 qq_connect(gc->account); |
161 } | 130 return FALSE; /* timeout callback stops */ |
162 | 131 } |
163 if (qd->fd >= 0 && qd->token != NULL && qd->token_len >= 0) { | 132 |
164 purple_debug_info("QQ", "Connect ok\n"); | 133 static void reconnect_later(PurpleConnection *gc) |
165 return FALSE; | 134 { |
166 } | |
167 | |
168 qd->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc); | |
169 return FALSE; | |
170 } | |
171 | |
172 /* Warning: qq_connect_later destory all connection | |
173 * Any function should be care of use qq_data after call this function | |
174 * Please conside tcp_pending and udp_pending */ | |
175 gboolean qq_connect_later(gpointer data) | |
176 { | |
177 PurpleConnection *gc = (PurpleConnection *) data; | |
178 qq_data *qd; | 135 qq_data *qd; |
179 char *server; | 136 |
180 int port; | 137 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
181 gchar **segments; | |
182 | |
183 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); | |
184 qd = (qq_data *) gc->proto_data; | 138 qd = (qq_data *) gc->proto_data; |
185 | 139 |
186 if (qd->check_watcher > 0) { | 140 qd->reconnect_times--; |
187 purple_timeout_remove(qd->check_watcher); | 141 if (qd->reconnect_times < 0) { |
188 qd->check_watcher = 0; | |
189 } | |
190 qq_disconnect(gc); | |
191 | |
192 if (qd->redirect_ip.s_addr != 0) { | |
193 /* redirect to new server */ | |
194 server = g_strdup_printf("%s:%d", inet_ntoa(qd->redirect_ip), qd->redirect_port); | |
195 qd->servers = g_list_append(qd->servers, server); | |
196 qd->curr_server = server; | |
197 | |
198 qd->redirect_ip.s_addr = 0; | |
199 qd->redirect_port = 0; | |
200 qd->connect_retry = QQ_CONNECT_MAX; | |
201 } | |
202 | |
203 if (qd->curr_server == NULL || strlen (qd->curr_server) == 0 || qd->connect_retry <= 0) { | |
204 if ( set_new_server(qd) != TRUE) { | 142 if ( set_new_server(qd) != TRUE) { |
205 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 143 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
206 _("Failed to connect all servers")); | 144 _("Failed to connect server")); |
207 return FALSE; | 145 return; |
208 } | 146 } |
209 qd->connect_retry = QQ_CONNECT_MAX; | 147 } |
210 } | 148 |
211 | 149 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
212 segments = g_strsplit_set(qd->curr_server, ":", 0); | 150 "Reconnect to server %s:%d next retries %d in %d ms\n", |
213 server = g_strdup(segments[0]); | 151 qd->real_hostname, qd->real_port, |
214 port = atoi(segments[1]); | 152 qd->reconnect_times, QQ_RECONNECT_INTERVAL); |
215 if (port <= 0) { | 153 |
216 purple_debug_info("QQ", "Port not define in %s\n", qd->curr_server); | 154 qd->reconnect_timeout = purple_timeout_add(QQ_RECONNECT_INTERVAL, |
217 port = QQ_DEFAULT_PORT; | 155 reconnect_later_cb, gc); |
218 } | |
219 g_strfreev(segments); | |
220 | |
221 qd->connect_retry--; | |
222 if ( !connect_to_server(gc, server, port) ) { | |
223 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
224 _("Unable to connect.")); | |
225 } | |
226 | |
227 qd->check_watcher = purple_timeout_add_seconds(QQ_CONNECT_CHECK, connect_check, gc); | |
228 return FALSE; /* timeout callback stops */ | |
229 } | 156 } |
230 | 157 |
231 /* process the incoming packet from qq_pending */ | 158 /* process the incoming packet from qq_pending */ |
232 static gboolean packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) | 159 static void packet_process(PurpleConnection *gc, guint8 *buf, gint buf_len) |
233 { | 160 { |
234 qq_data *qd; | 161 qq_data *qd; |
235 gint bytes, bytes_not_read; | 162 gint bytes, bytes_not_read; |
236 | 163 |
237 gboolean prev_update_status; | 164 gboolean prev_login_status; |
238 | 165 |
239 guint8 header_tag; | 166 guint8 header_tag; |
240 guint16 source_tag; | 167 guint16 source_tag; |
241 guint16 cmd; | 168 guint16 cmd; |
242 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 */ |
170 | |
243 guint8 room_cmd; | 171 guint8 room_cmd; |
244 guint32 room_id; | 172 guint32 room_id; |
245 gint update_class; | |
246 guint32 ship32; | |
247 | 173 |
248 qq_transaction *trans; | 174 qq_transaction *trans; |
249 | 175 |
250 g_return_val_if_fail(buf != NULL && buf_len > 0, TRUE); | 176 g_return_if_fail(buf != NULL && buf_len > 0); |
251 | 177 |
252 qd = (qq_data *) gc->proto_data; | 178 qd = (qq_data *) gc->proto_data; |
179 | |
180 prev_login_status = qd->logged_in; | |
253 | 181 |
254 /* Len, header and tail tag have been checked before */ | 182 /* Len, header and tail tag have been checked before */ |
255 bytes = 0; | 183 bytes = 0; |
256 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); | 184 bytes += packet_get_header(&header_tag, &source_tag, &cmd, &seq, buf + bytes); |
257 | 185 |
258 #if 1 | 186 #if 1 |
259 purple_debug_info("QQ", "==> [%05d] 0x%04X %s, source tag 0x%04X len %d\n", | 187 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
260 seq, cmd, qq_get_cmd_desc(cmd), source_tag, buf_len); | 188 "==> [%05d] 0x%04X %s, from (0x%04X %s) len %d\n", |
261 #endif | 189 seq, cmd, qq_get_cmd_desc(cmd), source_tag, qq_get_ver_desc(source_tag), buf_len); |
262 /* this is the length of all the encrypted data (also remove tail tag) */ | 190 #endif |
263 bytes_not_read = buf_len - bytes - 1; | 191 bytes_not_read = buf_len - bytes - 1; |
264 | 192 |
265 /* ack packet, we need to update send tranactions */ | 193 /* ack packet, we need to update send tranactions */ |
266 /* we do not check duplication for server ack */ | 194 /* we do not check duplication for server ack */ |
267 trans = qq_trans_find_rcved(gc, cmd, seq); | 195 trans = qq_trans_find_rcved(qd, cmd, seq); |
268 if (trans == NULL) { | 196 if (trans == NULL) { |
269 /* new server command */ | 197 /* new server command */ |
270 qq_trans_add_server_cmd(gc, cmd, seq, buf + bytes, bytes_not_read); | 198 qq_trans_add_server_cmd(qd, cmd, seq, buf + bytes, bytes_not_read); |
271 if ( qd->is_finish_update ) { | 199 if ( qd->logged_in ) { |
272 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); | 200 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); |
273 } | 201 } |
274 return TRUE; | 202 return; |
275 } | 203 } |
276 | 204 |
277 if (qq_trans_is_dup(trans)) { | 205 if (qq_trans_is_dup(trans)) { |
278 purple_debug_info("QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); | 206 purple_debug(PURPLE_DEBUG_WARNING, |
279 return TRUE; | 207 "QQ", "dup [%05d] %s, discard...\n", seq, qq_get_cmd_desc(cmd)); |
208 return; | |
280 } | 209 } |
281 | 210 |
282 if (qq_trans_is_server(trans)) { | 211 if (qq_trans_is_server(trans)) { |
283 if ( qd->is_finish_update ) { | 212 if ( qd->logged_in ) { |
284 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); | 213 qq_proc_cmd_server(gc, cmd, seq, buf + bytes, bytes_not_read); |
285 } | 214 } |
286 return TRUE; | 215 return; |
287 } | 216 } |
288 | 217 |
289 update_class = qq_trans_get_class(trans); | 218 /* this is the length of all the encrypted data (also remove tail tag */ |
290 ship32 = qq_trans_get_ship(trans); | 219 if (cmd == QQ_CMD_ROOM) { |
291 | 220 room_cmd = qq_trans_get_room_cmd(trans); |
292 prev_update_status = qd->is_finish_update; | 221 room_id = qq_trans_get_room_id(trans); |
293 switch (cmd) { | |
294 case QQ_CMD_TOKEN: | |
295 if (qq_process_token_reply(gc, buf + bytes, bytes_not_read) == QQ_TOKEN_REPLY_OK) { | |
296 qq_send_packet_login(gc); | |
297 } | |
298 break; | |
299 case QQ_CMD_LOGIN: | |
300 qq_proc_cmd_login(gc, buf + bytes, bytes_not_read); | |
301 /* check is redirect or not, and do it now */ | |
302 if (qd->redirect_ip.s_addr != 0) { | |
303 if (qd->check_watcher > 0) { | |
304 purple_timeout_remove(qd->check_watcher); | |
305 qd->check_watcher = 0; | |
306 } | |
307 if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); | |
308 qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); | |
309 return FALSE; /* do nothing after this function and return now */ | |
310 } | |
311 break; | |
312 case QQ_CMD_ROOM: | |
313 room_cmd = qq_trans_get_room_cmd(trans); | |
314 room_id = qq_trans_get_room_id(trans); | |
315 #if 1 | 222 #if 1 |
316 purple_debug_info("QQ", "%s (0x%02X) for room %d, len %d\n", | 223 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
317 qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); | 224 "%s (0x%02X ) for room %d, len %d\n", |
318 #endif | 225 qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); |
319 qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read, update_class, ship32); | 226 #endif |
320 break; | 227 qq_proc_room_cmd_reply(gc, seq, room_cmd, room_id, buf + bytes, bytes_not_read); |
321 default: | 228 } else { |
322 qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read, update_class, ship32); | 229 qq_proc_cmd_reply(gc, cmd, seq, buf + bytes, bytes_not_read); |
323 break; | 230 } |
324 } | 231 |
325 | 232 /* check is redirect or not, and do it now */ |
326 if (prev_update_status != qd->is_finish_update && qd->is_finish_update == TRUE) { | 233 if (qd->is_redirect) { |
327 /* is_login, but we have packets before login */ | 234 /* free resource except real_hostname and port */ |
328 qq_trans_process_before_login(gc); | 235 qq_disconnect(gc); |
329 return TRUE; | 236 qd->reconnect_times = QQ_RECONNECT_MAX; |
330 } | 237 reconnect_later(gc); |
331 return TRUE; | 238 return; |
239 } | |
240 | |
241 if (prev_login_status != qd->logged_in && qd->logged_in == TRUE) { | |
242 /* logged_in, but we have packets before login */ | |
243 qq_trans_process_before_login(qd); | |
244 } | |
332 } | 245 } |
333 | 246 |
334 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) | 247 static void tcp_pending(gpointer data, gint source, PurpleInputCondition cond) |
335 { | 248 { |
336 PurpleConnection *gc = (PurpleConnection *) data; | 249 PurpleConnection *gc; |
337 qq_data *qd; | 250 qq_data *qd; |
338 qq_connection *conn; | |
339 guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ | 251 guint8 buf[1024]; /* set to 16 when test tcp_rxqueue */ |
340 gint buf_len; | 252 gint buf_len; |
341 gint bytes; | 253 gint bytes; |
342 | 254 |
343 guint8 *pkt; | 255 guint8 *pkt; |
344 guint16 pkt_len; | 256 guint16 pkt_len; |
345 | 257 |
346 gchar *error_msg; | 258 gchar *error_msg; |
347 guint8 *jump; | 259 guint8 *jump; |
348 gint jump_len; | 260 gint jump_len; |
349 | 261 |
262 gc = (PurpleConnection *) data; | |
350 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 263 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
351 qd = (qq_data *) gc->proto_data; | |
352 | 264 |
353 if(cond != PURPLE_INPUT_READ) { | 265 if(cond != PURPLE_INPUT_READ) { |
354 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 266 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
355 _("Socket error")); | 267 _("Socket error")); |
356 return; | 268 return; |
357 } | 269 } |
358 | 270 |
359 conn = connection_find(qd, source); | 271 qd = (qq_data *) gc->proto_data; |
360 g_return_if_fail(conn != NULL); | 272 |
361 | |
362 /* test code, not using tcp_rxqueue | 273 /* test code, not using tcp_rxqueue |
363 memset(pkt,0, sizeof(pkt)); | 274 memset(pkt,0, sizeof(pkt)); |
364 buf_len = read(qd->fd, pkt, sizeof(pkt)); | 275 buf_len = read(qd->fd, pkt, sizeof(pkt)); |
365 if (buf_len > 2) { | 276 if (buf_len > 2) { |
366 packet_process(gc, pkt + 2, buf_len - 2); | 277 packet_process(gc, pkt + 2, buf_len - 2); |
367 } | 278 } |
368 return; | 279 return; |
369 */ | 280 */ |
370 | 281 |
371 buf_len = read(source, buf, sizeof(buf)); | 282 buf_len = read(qd->fd, buf, sizeof(buf)); |
372 if (buf_len < 0) { | 283 if (buf_len < 0) { |
373 if (errno == EAGAIN) | 284 if (errno == EAGAIN) |
374 /* No worries */ | 285 /* No worries */ |
375 return; | 286 return; |
376 | 287 |
386 | 297 |
387 /* keep alive will be sent in 30 seconds since last_receive | 298 /* keep alive will be sent in 30 seconds since last_receive |
388 * QQ need a keep alive packet in every 60 seconds | 299 * QQ need a keep alive packet in every 60 seconds |
389 gc->last_received = time(NULL); | 300 gc->last_received = time(NULL); |
390 */ | 301 */ |
391 /* purple_debug_info("TCP_PENDING", "Read %d bytes, rxlen is %d\n", buf_len, conn->tcp_rxlen); */ | 302 /* |
392 conn->tcp_rxqueue = g_realloc(conn->tcp_rxqueue, buf_len + conn->tcp_rxlen); | 303 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", |
393 memcpy(conn->tcp_rxqueue + conn->tcp_rxlen, buf, buf_len); | 304 "Read %d bytes from socket, rxlen is %d\n", buf_len, qd->tcp_rxlen); |
394 conn->tcp_rxlen += buf_len; | 305 */ |
395 | 306 qd->tcp_rxqueue = g_realloc(qd->tcp_rxqueue, buf_len + qd->tcp_rxlen); |
307 memcpy(qd->tcp_rxqueue + qd->tcp_rxlen, buf, buf_len); | |
308 qd->tcp_rxlen += buf_len; | |
309 | |
396 pkt = g_newa(guint8, MAX_PACKET_SIZE); | 310 pkt = g_newa(guint8, MAX_PACKET_SIZE); |
397 while (PURPLE_CONNECTION_IS_VALID(gc)) { | 311 while (1) { |
398 if (qd->openconns == NULL) { | 312 if (qd->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { |
399 break; | 313 break; |
400 } | 314 } |
401 if (conn->tcp_rxqueue == NULL) { | 315 |
402 conn->tcp_rxlen = 0; | 316 bytes = 0; |
317 bytes += qq_get16(&pkt_len, qd->tcp_rxqueue + bytes); | |
318 if (qd->tcp_rxlen < pkt_len) { | |
403 break; | 319 break; |
404 } | 320 } |
405 if (conn->tcp_rxlen < QQ_TCP_HEADER_LENGTH) { | 321 |
406 break; | 322 /* |
407 } | 323 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", |
408 | 324 "Packet len is %d bytes, rxlen is %d\n", pkt_len, qd->tcp_rxlen); |
409 bytes = 0; | 325 */ |
410 bytes += qq_get16(&pkt_len, conn->tcp_rxqueue + bytes); | |
411 if (conn->tcp_rxlen < pkt_len) { | |
412 break; | |
413 } | |
414 | |
415 /* purple_debug_info("TCP_PENDING", "Packet len=%d, rxlen=%d\n", pkt_len, conn->tcp_rxlen); */ | |
416 if ( pkt_len < QQ_TCP_HEADER_LENGTH | 326 if ( pkt_len < QQ_TCP_HEADER_LENGTH |
417 || *(conn->tcp_rxqueue + bytes) != QQ_PACKET_TAG | 327 || *(qd->tcp_rxqueue + bytes) != QQ_PACKET_TAG |
418 || *(conn->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { | 328 || *(qd->tcp_rxqueue + pkt_len - 1) != QQ_PACKET_TAIL) { |
419 /* HEY! This isn't even a QQ. What are you trying to pull? */ | 329 /* HEY! This isn't even a QQ. What are you trying to pull? */ |
420 purple_debug_warning("TCP_PENDING", "Packet error, no header or tail tag\n"); | 330 |
421 | 331 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", |
422 jump = memchr(conn->tcp_rxqueue + 1, QQ_PACKET_TAIL, conn->tcp_rxlen - 1); | 332 "Packet error, failed to check header and tail tag\n"); |
333 | |
334 jump = memchr(qd->tcp_rxqueue + 1, QQ_PACKET_TAIL, qd->tcp_rxlen - 1); | |
423 if ( !jump ) { | 335 if ( !jump ) { |
424 purple_debug_warning("TCP_PENDING", "Failed to find next tail, clear receive buffer\n"); | 336 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", |
425 g_free(conn->tcp_rxqueue); | 337 "Failed to find next QQ_PACKET_TAIL, clear receive buffer\n"); |
426 conn->tcp_rxqueue = NULL; | 338 g_free(qd->tcp_rxqueue); |
427 conn->tcp_rxlen = 0; | 339 qd->tcp_rxqueue = NULL; |
340 qd->tcp_rxlen = 0; | |
428 return; | 341 return; |
429 } | 342 } |
430 | 343 |
431 /* jump and over QQ_PACKET_TAIL */ | 344 /* jump and over QQ_PACKET_TAIL */ |
432 jump_len = (jump - conn->tcp_rxqueue) + 1; | 345 jump_len = (jump - qd->tcp_rxqueue) + 1; |
433 purple_debug_warning("TCP_PENDING", "Find next tail at %d, jump %d\n", jump_len, jump_len + 1); | 346 purple_debug(PURPLE_DEBUG_INFO, "TCP_PENDING", |
434 g_memmove(conn->tcp_rxqueue, jump, conn->tcp_rxlen - jump_len); | 347 "Find next QQ_PACKET_TAIL at %d, jump %d bytes\n", jump_len, jump_len + 1); |
435 conn->tcp_rxlen -= jump_len; | 348 g_memmove(qd->tcp_rxqueue, jump, qd->tcp_rxlen - jump_len); |
349 qd->tcp_rxlen -= jump_len; | |
436 continue; | 350 continue; |
437 } | 351 } |
438 | 352 |
439 memset(pkt, 0, MAX_PACKET_SIZE); | 353 memset(pkt, 0, MAX_PACKET_SIZE); |
440 g_memmove(pkt, conn->tcp_rxqueue + bytes, pkt_len - bytes); | 354 g_memmove(pkt, qd->tcp_rxqueue + bytes, pkt_len - bytes); |
441 | 355 |
442 /* jump to next packet */ | 356 /* jump to next packet */ |
443 conn->tcp_rxlen -= pkt_len; | 357 qd->tcp_rxlen -= pkt_len; |
444 if (conn->tcp_rxlen) { | 358 if (qd->tcp_rxlen) { |
445 /* purple_debug_info("TCP_PENDING", "shrink tcp_rxqueue to %d\n", conn->tcp_rxlen); */ | 359 /* |
446 jump = g_memdup(conn->tcp_rxqueue + pkt_len, conn->tcp_rxlen); | 360 purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "shrink tcp_rxqueue to %d\n", qd->tcp_rxlen); |
447 g_free(conn->tcp_rxqueue); | 361 */ |
448 conn->tcp_rxqueue = jump; | 362 jump = g_memdup(qd->tcp_rxqueue + pkt_len, qd->tcp_rxlen); |
363 g_free(qd->tcp_rxqueue); | |
364 qd->tcp_rxqueue = jump; | |
449 } else { | 365 } else { |
450 /* purple_debug_info("TCP_PENDING", "free tcp_rxqueue\n"); */ | 366 /* purple_debug(PURPLE_DEBUG_ERROR, "TCP_PENDING", "free tcp_rxqueue\n"); */ |
451 g_free(conn->tcp_rxqueue); | 367 g_free(qd->tcp_rxqueue); |
452 conn->tcp_rxqueue = NULL; | 368 qd->tcp_rxqueue = NULL; |
453 } | 369 } |
454 | 370 |
455 if (pkt == NULL) { | 371 if (pkt == NULL) { |
456 continue; | 372 continue; |
457 } | 373 } |
458 /* packet_process may call disconnect and destory data like conn | 374 /* do not call packet_process before jump |
459 * do not call packet_process before jump, | 375 * packet_process may call disconnect and destory tcp_rxqueue */ |
460 * break if packet_process return FALSE */ | 376 packet_process(gc, pkt, pkt_len - bytes); |
461 if (packet_process(gc, pkt, pkt_len - bytes) == FALSE) { | |
462 purple_debug_info("TCP_PENDING", "Connection has been destory\n"); | |
463 break; | |
464 } | |
465 } | 377 } |
466 } | 378 } |
467 | 379 |
468 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) | 380 static void udp_pending(gpointer data, gint source, PurpleInputCondition cond) |
469 { | 381 { |
470 PurpleConnection *gc = (PurpleConnection *) data; | 382 PurpleConnection *gc; |
471 qq_data *qd; | 383 qq_data *qd; |
472 guint8 *buf; | 384 guint8 *buf; |
473 gint buf_len; | 385 gint buf_len; |
474 | 386 |
475 gc = (PurpleConnection *) data; | 387 gc = (PurpleConnection *) data; |
476 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 388 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
477 qd = (qq_data *) gc->proto_data; | |
478 | 389 |
479 if(cond != PURPLE_INPUT_READ) { | 390 if(cond != PURPLE_INPUT_READ) { |
480 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 391 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
481 _("Socket error")); | 392 _("Socket error")); |
482 return; | 393 return; |
483 } | 394 } |
484 | 395 |
396 qd = (qq_data *) gc->proto_data; | |
397 g_return_if_fail(qd->fd >= 0); | |
398 | |
485 buf = g_newa(guint8, MAX_PACKET_SIZE); | 399 buf = g_newa(guint8, MAX_PACKET_SIZE); |
486 | 400 |
487 /* here we have UDP proxy suppport */ | 401 /* here we have UDP proxy suppport */ |
488 buf_len = read(source, buf, MAX_PACKET_SIZE); | 402 buf_len = read(qd->fd, buf, MAX_PACKET_SIZE); |
489 if (buf_len <= 0) { | 403 if (buf_len <= 0) { |
490 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 404 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
491 _("Unable to read from socket")); | 405 _("Unable to read from socket")); |
492 return; | 406 return; |
493 } | 407 } |
503 buf, buf_len, | 417 buf, buf_len, |
504 "Received packet is too short, or no header and tail tag"); | 418 "Received packet is too short, or no header and tail tag"); |
505 return; | 419 return; |
506 } | 420 } |
507 } | 421 } |
508 | 422 |
509 /* packet_process may call disconnect and destory data like conn | |
510 * do not call packet_process before jump, | |
511 * break if packet_process return FALSE */ | |
512 packet_process(gc, buf, buf_len); | 423 packet_process(gc, buf, buf_len); |
513 } | 424 } |
514 | 425 |
515 static gint udp_send_out(PurpleConnection *gc, guint8 *data, gint data_len) | 426 static gint udp_send_out(qq_data *qd, guint8 *data, gint data_len) |
516 { | 427 { |
517 qq_data *qd; | |
518 gint ret; | 428 gint ret; |
519 | 429 |
520 g_return_val_if_fail(data != NULL && data_len > 0, -1); | 430 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); |
521 | 431 |
522 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | 432 /* |
523 qd = (qq_data *) gc->proto_data; | 433 purple_debug(PURPLE_DEBUG_INFO, "UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); |
524 | 434 */ |
525 #if 0 | 435 |
526 purple_debug_info("UDP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); | |
527 #endif | |
528 | |
529 errno = 0; | 436 errno = 0; |
530 ret = send(qd->fd, data, data_len, 0); | 437 ret = send(qd->fd, data, data_len, 0); |
531 if (ret < 0 && errno == EAGAIN) { | 438 if (ret < 0 && errno == EAGAIN) { |
532 return ret; | 439 return ret; |
533 } | 440 } |
534 | 441 |
535 if (ret < 0) { | 442 if (ret < 0) { |
536 /* TODO: what to do here - do we really have to disconnect? */ | 443 /* TODO: what to do here - do we really have to disconnect? */ |
537 purple_debug_error("UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno)); | 444 purple_debug(PURPLE_DEBUG_ERROR, "UDP_SEND_OUT", "Send failed: %d, %s\n", errno, g_strerror(errno)); |
538 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); | 445 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); |
539 } | 446 } |
540 return ret; | 447 return ret; |
541 } | 448 } |
542 | 449 |
543 static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) | 450 static void tcp_can_write(gpointer data, gint source, PurpleInputCondition cond) |
544 { | 451 { |
545 PurpleConnection *gc = (PurpleConnection *) data; | 452 qq_data *qd = data; |
546 qq_data *qd; | |
547 qq_connection *conn; | |
548 int ret, writelen; | 453 int ret, writelen; |
549 | 454 |
550 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 455 writelen = purple_circ_buffer_get_max_read(qd->tcp_txbuf); |
551 qd = (qq_data *) gc->proto_data; | |
552 | |
553 conn = connection_find(qd, source); | |
554 g_return_if_fail(conn != NULL); | |
555 | |
556 writelen = purple_circ_buffer_get_max_read(conn->tcp_txbuf); | |
557 if (writelen == 0) { | 456 if (writelen == 0) { |
558 purple_input_remove(conn->can_write_handler); | 457 purple_input_remove(qd->tx_handler); |
559 conn->can_write_handler = 0; | 458 qd->tx_handler = 0; |
560 return; | 459 return; |
561 } | 460 } |
562 | 461 |
563 ret = write(source, conn->tcp_txbuf->outptr, writelen); | 462 ret = write(qd->fd, qd->tcp_txbuf->outptr, writelen); |
564 purple_debug_info("TCP_CAN_WRITE", "total %d bytes is sent %d\n", writelen, ret); | 463 purple_debug(PURPLE_DEBUG_ERROR, "TCP_CAN_WRITE", |
464 "total %d bytes is sent %d\n", writelen, ret); | |
565 | 465 |
566 if (ret < 0 && errno == EAGAIN) | 466 if (ret < 0 && errno == EAGAIN) |
567 return; | 467 return; |
568 else if (ret < 0) { | 468 else if (ret < 0) { |
569 /* TODO: what to do here - do we really have to disconnect? */ | 469 /* TODO: what to do here - do we really have to disconnect? */ |
570 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 470 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
571 _("Write Error")); | 471 _("Write Error")); |
572 return; | 472 return; |
573 } | 473 } |
574 | 474 |
575 purple_circ_buffer_mark_read(conn->tcp_txbuf, ret); | 475 purple_circ_buffer_mark_read(qd->tcp_txbuf, ret); |
576 } | 476 } |
577 | 477 |
578 static gint tcp_send_out(PurpleConnection *gc, guint8 *data, gint data_len) | 478 static gint tcp_send_out(qq_data *qd, guint8 *data, gint data_len) |
579 { | 479 { |
580 qq_data *qd; | |
581 qq_connection *conn; | |
582 gint ret; | 480 gint ret; |
583 | 481 |
584 g_return_val_if_fail(data != NULL && data_len > 0, -1); | 482 g_return_val_if_fail(qd != NULL && qd->fd >= 0 && data != NULL && data_len > 0, -1); |
585 | 483 |
586 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | 484 /* |
587 qd = (qq_data *) gc->proto_data; | 485 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); |
588 | 486 */ |
589 conn = connection_find(qd, qd->fd); | 487 |
590 g_return_val_if_fail(conn, -1); | 488 if (qd->tx_handler == 0) { |
591 | |
592 #if 0 | |
593 purple_debug_info("TCP_SEND_OUT", "Send %d bytes to socket %d\n", data_len, qd->fd); | |
594 #endif | |
595 | |
596 if (conn->can_write_handler == 0) { | |
597 ret = write(qd->fd, data, data_len); | 489 ret = write(qd->fd, data, data_len); |
598 } else { | 490 } else { |
599 ret = -1; | 491 ret = -1; |
600 errno = EAGAIN; | 492 errno = EAGAIN; |
601 } | 493 } |
602 | 494 |
603 /* | 495 /* |
604 purple_debug_info("TCP_SEND_OUT", | 496 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", |
605 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); | 497 "Socket %d, total %d bytes is sent %d\n", qd->fd, data_len, ret); |
606 */ | 498 */ |
607 if (ret < 0 && errno == EAGAIN) { | 499 if (ret < 0 && errno == EAGAIN) { |
608 /* socket is busy, send later */ | 500 /* socket is busy, send later */ |
609 purple_debug_info("TCP_SEND_OUT", "Socket is busy and send later\n"); | 501 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", "Socket is busy and send later\n"); |
610 ret = 0; | 502 ret = 0; |
611 } else if (ret <= 0) { | 503 } else if (ret <= 0) { |
612 /* TODO: what to do here - do we really have to disconnect? */ | 504 /* TODO: what to do here - do we really have to disconnect? */ |
613 purple_debug_error("TCP_SEND_OUT", | 505 purple_debug(PURPLE_DEBUG_ERROR, "TCP_SEND_OUT", |
614 "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); | 506 "Send to socket %d failed: %d, %s\n", qd->fd, errno, g_strerror(errno)); |
615 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); | 507 purple_connection_error_reason(qd->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, g_strerror(errno)); |
616 return ret; | 508 return ret; |
617 } | 509 } |
618 | 510 |
619 if (ret < data_len) { | 511 if (ret < data_len) { |
620 purple_debug_info("TCP_SEND_OUT", | 512 purple_debug(PURPLE_DEBUG_INFO, "TCP_SEND_OUT", |
621 "Add %d bytes to buffer\n", data_len - ret); | 513 "Add %d bytes to buffer\n", data_len - ret); |
622 if (conn->can_write_handler == 0) { | 514 if (qd->tx_handler == 0) { |
623 conn->can_write_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, gc); | 515 qd->tx_handler = purple_input_add(qd->fd, PURPLE_INPUT_WRITE, tcp_can_write, qd); |
624 } | 516 } |
625 purple_circ_buffer_append(conn->tcp_txbuf, data + ret, data_len - ret); | 517 purple_circ_buffer_append(qd->tcp_txbuf, data + ret, data_len - ret); |
626 } | 518 } |
627 return ret; | 519 return ret; |
628 } | 520 } |
629 | 521 |
630 static gboolean network_timeout(gpointer data) | 522 static gboolean network_timeout(gpointer data) |
634 gboolean is_lost_conn; | 526 gboolean is_lost_conn; |
635 | 527 |
636 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); | 528 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, TRUE); |
637 qd = (qq_data *) gc->proto_data; | 529 qd = (qq_data *) gc->proto_data; |
638 | 530 |
639 is_lost_conn = qq_trans_scan(gc); | 531 is_lost_conn = qq_trans_scan(qd); |
640 if (is_lost_conn) { | 532 if (is_lost_conn) { |
641 purple_connection_error_reason(gc, | 533 purple_connection_error_reason(gc, |
642 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); | 534 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Connection lost")); |
643 return TRUE; | 535 return TRUE; |
644 } | 536 } |
645 | 537 |
646 if ( !qd->is_login ) { | 538 if ( !qd->logged_in ) { |
647 return TRUE; | 539 return TRUE; |
648 } | 540 } |
649 | 541 |
650 qd->itv_count.keep_alive--; | 542 qd->itv_count.keep_alive--; |
651 if (qd->itv_count.keep_alive <= 0) { | 543 if (qd->itv_count.keep_alive <= 0) { |
652 qd->itv_count.keep_alive = qd->itv_config.keep_alive; | 544 qd->itv_count.keep_alive = qd->itv_config.keep_alive; |
653 qq_send_packet_keep_alive(gc); | 545 qq_send_packet_keep_alive(gc); |
654 return TRUE; | 546 return TRUE; |
659 } | 551 } |
660 | 552 |
661 qd->itv_count.update--; | 553 qd->itv_count.update--; |
662 if (qd->itv_count.update <= 0) { | 554 if (qd->itv_count.update <= 0) { |
663 qd->itv_count.update = qd->itv_config.update; | 555 qd->itv_count.update = qd->itv_config.update; |
664 qq_update_online(gc, 0); | 556 qq_send_packet_get_buddies_online(gc, 0); |
557 | |
558 qq_send_cmd_group_all_get_online_members(gc); | |
665 return TRUE; | 559 return TRUE; |
666 } | 560 } |
667 | 561 |
668 return TRUE; /* if return FALSE, timeout callback stops */ | 562 return TRUE; /* if return FALSE, timeout callback stops */ |
669 } | 563 } |
670 | 564 |
671 static void do_request_token(PurpleConnection *gc) | 565 /* the callback function after socket is built |
566 * we setup the qq protocol related configuration here */ | |
567 static void qq_connect_cb(gpointer data, gint source, const gchar *error_message) | |
672 { | 568 { |
673 qq_data *qd; | 569 qq_data *qd; |
570 PurpleConnection *gc; | |
674 gchar *conn_msg; | 571 gchar *conn_msg; |
675 const gchar *passwd; | 572 const gchar *passwd; |
573 PurpleAccount *account ; | |
574 | |
575 gc = (PurpleConnection *) data; | |
576 | |
577 if (!PURPLE_CONNECTION_IS_VALID(gc)) { | |
578 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection\n"); | |
579 close(source); | |
580 return; | |
581 } | |
582 | |
583 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
584 | |
585 qd = (qq_data *) gc->proto_data; | |
586 account = purple_connection_get_account(gc); | |
587 | |
588 /* Connect is now complete; clear the PurpleProxyConnectData */ | |
589 qd->connect_data = NULL; | |
590 | |
591 if (source < 0) { /* socket returns -1 */ | |
592 purple_debug(PURPLE_DEBUG_INFO, "QQ_CONN", "Invalid connection, source is < 0\n"); | |
593 qq_disconnect(gc); | |
594 reconnect_later(gc); | |
595 return; | |
596 } | |
676 | 597 |
677 /* _qq_show_socket("Got login socket", source); */ | 598 /* _qq_show_socket("Got login socket", source); */ |
678 | 599 |
679 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | |
680 qd = (qq_data *) gc->proto_data; | |
681 | |
682 /* QQ use random seq, to minimize duplicated packets */ | 600 /* QQ use random seq, to minimize duplicated packets */ |
683 srand(time(NULL)); | 601 srandom(time(NULL)); |
684 qd->send_seq = rand() & 0xffff; | 602 qd->send_seq = random() & 0x0000ffff; |
685 | 603 qd->fd = source; |
686 qd->is_login = FALSE; | 604 qd->logged_in = FALSE; |
687 qd->is_finish_update = FALSE; | |
688 qd->channel = 1; | 605 qd->channel = 1; |
689 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); | 606 qd->uid = strtol(purple_account_get_username(purple_connection_get_account(gc)), NULL, 10); |
690 | 607 |
691 /* now generate md5 processed passwd */ | 608 /* now generate md5 processed passwd */ |
692 passwd = purple_account_get_password(purple_connection_get_account(gc)); | 609 passwd = purple_account_get_password(purple_connection_get_account(gc)); |
695 qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), | 612 qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), |
696 (guint8 *)passwd, strlen(passwd)); | 613 (guint8 *)passwd, strlen(passwd)); |
697 qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), | 614 qq_get_md5(qd->password_twice_md5, sizeof(qd->password_twice_md5), |
698 qd->password_twice_md5, sizeof(qd->password_twice_md5)); | 615 qd->password_twice_md5, sizeof(qd->password_twice_md5)); |
699 | 616 |
700 g_return_if_fail(qd->network_watcher == 0); | 617 g_return_if_fail(qd->network_timeout == 0); |
701 qd->network_watcher = purple_timeout_add_seconds(qd->itv_config.resend, network_timeout, gc); | 618 qd->itv_config.resend = purple_account_get_int(account, "resend_interval", 10); |
619 if (qd->itv_config.resend <= 0) qd->itv_config.resend = 10; | |
620 | |
621 qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60); | |
622 if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30; | |
623 qd->itv_config.keep_alive /= qd->itv_config.resend; | |
624 qd->itv_count.keep_alive = qd->itv_config.keep_alive; | |
625 | |
626 qd->itv_config.update = purple_account_get_int(account, "update_interval", 300); | |
627 if (qd->itv_config.update > 0) { | |
628 if (qd->itv_config.update < qd->itv_config.keep_alive) { | |
629 qd->itv_config.update = qd->itv_config.keep_alive; | |
630 } | |
631 qd->itv_config.update /= qd->itv_config.resend; | |
632 qd->itv_count.update = qd->itv_config.update; | |
633 } else { | |
634 qd->itv_config.update = 0; | |
635 } | |
636 | |
637 qd->network_timeout = purple_timeout_add(qd->itv_config.resend *1000, network_timeout, gc); | |
638 | |
639 if (qd->use_tcp) | |
640 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, tcp_pending, gc); | |
641 else | |
642 gc->inpa = purple_input_add(qd->fd, PURPLE_INPUT_READ, udp_pending, gc); | |
702 | 643 |
703 /* Update the login progress status display */ | 644 /* Update the login progress status display */ |
704 conn_msg = g_strdup_printf(_("Request token")); | 645 conn_msg = g_strdup_printf("Login as %d", qd->uid); |
705 purple_connection_update_progress(gc, conn_msg, 2, QQ_CONNECT_STEPS); | 646 purple_connection_update_progress(gc, conn_msg, QQ_CONNECT_STEPS - 1, QQ_CONNECT_STEPS); |
706 g_free(conn_msg); | 647 g_free(conn_msg); |
707 | 648 |
708 qq_send_packet_token(gc); | 649 qq_send_packet_token(gc); |
709 } | 650 } |
710 | 651 |
711 /* the callback function after socket is built | 652 static void udp_can_write(gpointer data, gint source, PurpleInputCondition cond) |
712 * we setup the qq protocol related configuration here */ | |
713 static void connect_cb(gpointer data, gint source, const gchar *error_message) | |
714 { | 653 { |
715 PurpleConnection *gc; | 654 PurpleConnection *gc; |
716 qq_data *qd; | 655 qq_data *qd; |
717 PurpleAccount *account ; | 656 socklen_t len; |
718 qq_connection *conn; | 657 int error=0, ret; |
719 | 658 |
720 gc = (PurpleConnection *) data; | 659 gc = (PurpleConnection *) data; |
721 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 660 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
722 | 661 |
723 qd = (qq_data *) gc->proto_data; | 662 qd = (qq_data *) gc->proto_data; |
724 account = purple_connection_get_account(gc); | 663 |
725 | 664 |
726 /* conn_data will be destoryed */ | 665 purple_debug_info("proxy", "Connected.\n"); |
727 qd->conn_data = NULL; | 666 |
728 | 667 /* |
729 if (!PURPLE_CONNECTION_IS_VALID(gc)) { | 668 * getsockopt after a non-blocking connect returns -1 if something is |
730 purple_debug_info("QQ_CONN", "Invalid connection\n"); | 669 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and |
670 * error holds what connect would have returned if it blocked until now. | |
671 * Thus, error == 0 is success, error == EINPROGRESS means "try again", | |
672 * and anything else is a real error. | |
673 * | |
674 * (error == EINPROGRESS can happen after a select because the kernel can | |
675 * be overly optimistic sometimes. select is just a hint that you might be | |
676 * able to do something.) | |
677 */ | |
678 len = sizeof(error); | |
679 ret = getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len); | |
680 if (ret == 0 && error == EINPROGRESS) | |
681 return; /* we'll be called again later */ | |
682 | |
683 purple_input_remove(qd->tx_handler); | |
684 qd->tx_handler = 0; | |
685 if (ret < 0 || error != 0) { | |
686 if(ret != 0) | |
687 error = errno; | |
688 | |
731 close(source); | 689 close(source); |
732 return; | 690 |
733 } | 691 purple_debug_error("proxy", "getsockopt SO_ERROR check: %s\n", g_strerror(error)); |
734 | 692 |
735 if (source < 0) { /* socket returns -1 */ | 693 qq_connect_cb(gc, -1, _("Unable to connect")); |
736 purple_debug_info("QQ_CONN", | 694 return; |
737 "Could not establish a connection with the server:\n%s\n", | 695 } |
738 error_message); | 696 |
739 if (qd->connect_watcher > 0) purple_timeout_remove(qd->connect_watcher); | 697 qq_connect_cb(gc, source, NULL); |
740 qd->connect_watcher = purple_timeout_add_seconds(QQ_CONNECT_INTERVAL, qq_connect_later, gc); | 698 } |
741 return; | 699 |
742 } | 700 static void udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { |
743 | 701 PurpleConnection *gc; |
744 /* _qq_show_socket("Got login socket", source); */ | 702 qq_data *qd; |
745 qd->fd = source; | 703 struct sockaddr server_addr; |
746 conn = connection_create(qd, source); | 704 int addr_size; |
747 if (qd->use_tcp) { | 705 gint fd = -1; |
748 conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, tcp_pending, gc); | 706 int flags; |
749 } else { | 707 |
750 conn->input_handler = purple_input_add(source, PURPLE_INPUT_READ, udp_pending, gc); | 708 gc = (PurpleConnection *) data; |
751 } | 709 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
752 | 710 |
753 do_request_token( gc ); | 711 qd = (qq_data *) gc->proto_data; |
754 } | 712 |
755 | 713 /* udp_query_data must be set as NULL. |
756 gboolean connect_to_server(PurpleConnection *gc, gchar *server, gint port) | 714 * Otherwise purple_dnsquery_destroy in qq_disconnect cause glib double free error */ |
757 { | 715 qd->udp_query_data = NULL; |
758 PurpleAccount *account ; | 716 |
717 if (!hosts || !hosts->data) { | |
718 purple_connection_error_reason(gc, | |
719 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
720 _("Couldn't resolve host")); | |
721 return; | |
722 } | |
723 | |
724 addr_size = GPOINTER_TO_INT(hosts->data); | |
725 hosts = g_slist_remove(hosts, hosts->data); | |
726 memcpy(&server_addr, hosts->data, addr_size); | |
727 g_free(hosts->data); | |
728 | |
729 hosts = g_slist_remove(hosts, hosts->data); | |
730 while(hosts) { | |
731 hosts = g_slist_remove(hosts, hosts->data); | |
732 g_free(hosts->data); | |
733 hosts = g_slist_remove(hosts, hosts->data); | |
734 } | |
735 | |
736 fd = socket(PF_INET, SOCK_DGRAM, 0); | |
737 if (fd < 0) { | |
738 purple_debug(PURPLE_DEBUG_ERROR, "QQ", | |
739 "Unable to create socket: %s\n", g_strerror(errno)); | |
740 return; | |
741 } | |
742 | |
743 /* we use non-blocking mode to speed up connection */ | |
744 flags = fcntl(fd, F_GETFL); | |
745 fcntl(fd, F_SETFL, flags | O_NONBLOCK); | |
746 #ifndef _WIN32 | |
747 fcntl(fd, F_SETFD, FD_CLOEXEC); | |
748 #endif | |
749 | |
750 /* From Unix-socket-FAQ: http://www.faqs.org/faqs/unix-faq/socket/ | |
751 * | |
752 * If a UDP socket is unconnected, which is the normal state after a | |
753 * bind() call, then send() or write() are not allowed, since no | |
754 * destination is available; only sendto() can be used to send data. | |
755 * | |
756 * Calling connect() on the socket simply records the specified address | |
757 * and port number as being the desired communications partner. That | |
758 * means that send() or write() are now allowed; they use the destination | |
759 * address and port given on the connect call as the destination of packets. | |
760 */ | |
761 if (connect(fd, &server_addr, addr_size) >= 0) { | |
762 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Connected.\n"); | |
763 flags = fcntl(fd, F_GETFL); | |
764 fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); | |
765 qq_connect_cb(gc, fd, NULL); | |
766 return; | |
767 } | |
768 | |
769 /* [EINPROGRESS] | |
770 * The socket is marked as non-blocking and the connection cannot be | |
771 * completed immediately. It is possible to select for completion by | |
772 * selecting the socket for writing. | |
773 * [EINTR] | |
774 * A signal interrupted the call. | |
775 * The connection is established asynchronously. | |
776 */ | |
777 if ((errno == EINPROGRESS) || (errno == EINTR)) { | |
778 purple_debug(PURPLE_DEBUG_WARNING, "QQ", "Connect in asynchronous mode.\n"); | |
779 qd->tx_handler = purple_input_add(fd, PURPLE_INPUT_WRITE, udp_can_write, gc); | |
780 return; | |
781 } | |
782 | |
783 purple_debug(PURPLE_DEBUG_ERROR, "QQ", "Connection failed: %s\n", g_strerror(errno)); | |
784 close(fd); | |
785 } | |
786 | |
787 /* establish a generic QQ connection | |
788 * TCP/UDP, and direct/redirected */ | |
789 void qq_connect(PurpleAccount *account) | |
790 { | |
791 PurpleConnection *gc; | |
759 qq_data *qd; | 792 qq_data *qd; |
760 gchar *conn_msg; | 793 gchar *conn_msg; |
761 | 794 |
762 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, FALSE); | 795 gc = purple_account_get_connection(account); |
763 account = purple_connection_get_account(gc); | 796 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
797 | |
764 qd = (qq_data *) gc->proto_data; | 798 qd = (qq_data *) gc->proto_data; |
765 | 799 |
766 if (server == NULL || strlen(server) == 0 || port == 0) { | 800 |
801 /* test set_new_server | |
802 while (set_new_server(qd)) { | |
803 purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", | |
804 "New server %s:%d Real server %s:%d\n", | |
805 qd->server_name, qd->user_port, qd->real_hostname, qd->real_port); | |
806 } | |
807 purple_debug(PURPLE_DEBUG_INFO, "QQ_TEST", "qd->servers %lu\n", | |
808 qd->servers); | |
809 exit(1); | |
810 */ | |
811 if (qd->server_name == NULL) { | |
812 /* must be first call this function */ | |
813 if ( set_new_server(qd) != TRUE) { | |
814 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
815 _("Failed to connect server")); | |
816 return; | |
817 } | |
818 } | |
819 | |
820 if (qd->real_hostname == NULL || qd->real_port == 0) { | |
767 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 821 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
768 _("Invalid server or port")); | 822 _("hostname is NULL or port is 0")); |
769 return FALSE; | 823 return; |
770 } | 824 } |
771 | 825 |
772 conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), server, port); | 826 conn_msg = g_strdup_printf( _("Connecting server %s, retries %d"), |
827 qd->real_hostname, qd->reconnect_times); | |
773 purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); | 828 purple_connection_update_progress(gc, conn_msg, 1, QQ_CONNECT_STEPS); |
774 g_free(conn_msg); | 829 g_free(conn_msg); |
775 | 830 |
776 purple_debug_info("QQ", "Connect to %s:%d\n", server, port); | 831 if (qd->is_redirect) { |
777 | 832 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Redirect to %s:%d\n", |
778 if (qd->conn_data != NULL) { | 833 qd->real_hostname, qd->real_port); |
779 purple_proxy_connect_cancel(qd->conn_data); | 834 } |
780 qd->conn_data = NULL; | 835 qd->is_redirect = FALSE; |
781 } | 836 |
782 qd->conn_data = purple_proxy_connect(gc, account, server, port, connect_cb, gc); | 837 qd->fd = -1; |
783 if ( qd->conn_data == NULL ) { | 838 qd->tx_handler = 0; |
784 purple_debug_error("QQ", _("Couldn't create socket")); | 839 |
785 return FALSE; | 840 /* QQ connection via UDP/TCP. |
786 } | 841 * Now use Purple proxy function to provide TCP proxy support, |
787 return TRUE; | 842 * and qq_udp_proxy.c to add UDP proxy support (thanks henry) */ |
843 if(qd->use_tcp) { | |
844 purple_debug(PURPLE_DEBUG_INFO, "QQ", "TCP Connect to %s:%d\n", | |
845 qd->real_hostname, qd->real_port); | |
846 | |
847 /* TODO: is there a good default grow size? */ | |
848 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Create tcp_txbuf\n"); | |
849 qd->tcp_txbuf = purple_circ_buffer_new(0); | |
850 | |
851 qd->connect_data = purple_proxy_connect(NULL, account, | |
852 qd->real_hostname, qd->real_port, qq_connect_cb, gc); | |
853 if (qd->connect_data == NULL) { | |
854 purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
855 _("Unable to connect.")); | |
856 } | |
857 return; | |
858 } | |
859 | |
860 purple_debug(PURPLE_DEBUG_INFO, "QQ", "UDP Connect to %s:%d\n", | |
861 qd->real_hostname, qd->real_port); | |
862 | |
863 g_return_if_fail(qd->udp_query_data == NULL); | |
864 qd->udp_query_data = purple_dnsquery_a(qd->real_hostname, qd->real_port, | |
865 udp_host_resolved, gc); | |
866 if (qd->udp_query_data == NULL) { | |
867 purple_connection_error_reason(qd->gc, | |
868 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
869 _("Could not resolve hostname")); | |
870 } | |
788 } | 871 } |
789 | 872 |
790 /* clean up qq_data structure and all its components | 873 /* clean up qq_data structure and all its components |
791 * always used before a redirectly connection */ | 874 * always used before a redirectly connection */ |
792 void qq_disconnect(PurpleConnection *gc) | 875 void qq_disconnect(PurpleConnection *gc) |
794 qq_data *qd; | 877 qq_data *qd; |
795 | 878 |
796 g_return_if_fail(gc != NULL && gc->proto_data != NULL); | 879 g_return_if_fail(gc != NULL && gc->proto_data != NULL); |
797 qd = (qq_data *) gc->proto_data; | 880 qd = (qq_data *) gc->proto_data; |
798 | 881 |
799 purple_debug_info("QQ", "Disconnecting ...\n"); | 882 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Disconnecting ...\n"); |
800 | 883 |
801 if (qd->network_watcher > 0) { | 884 if (qd->network_timeout > 0) { |
802 purple_debug_info("QQ", "Remove network watcher\n"); | 885 purple_timeout_remove(qd->network_timeout); |
803 purple_timeout_remove(qd->network_watcher); | 886 qd->network_timeout = 0; |
804 qd->network_watcher = 0; | |
805 } | 887 } |
806 | 888 |
807 /* finish all I/O */ | 889 /* finish all I/O */ |
808 if (qd->fd >= 0 && qd->is_login) { | 890 if (qd->fd >= 0 && qd->logged_in) { |
809 qq_send_packet_logout(gc); | 891 qq_send_packet_logout(gc); |
810 } | 892 } |
811 | 893 |
812 /* not connected */ | 894 if (gc->inpa > 0) { |
813 if (qd->conn_data != NULL) { | 895 purple_input_remove(gc->inpa); |
814 purple_debug_info("QQ", "Connect cancel\n"); | 896 gc->inpa = 0; |
815 purple_proxy_connect_cancel(qd->conn_data); | 897 } |
816 qd->conn_data = NULL; | 898 |
817 } | 899 if (qd->fd >= 0) { |
818 connection_free_all(qd); | 900 close(qd->fd); |
819 qd->fd = -1; | 901 qd->fd = -1; |
820 | 902 } |
821 qq_trans_remove_all(gc); | 903 |
822 | 904 if (qd->reconnect_timeout > 0) { |
905 purple_timeout_remove(qd->reconnect_timeout); | |
906 qd->reconnect_timeout = 0; | |
907 } | |
908 | |
909 if (qd->connect_data != NULL) { | |
910 purple_debug(PURPLE_DEBUG_INFO, "QQ", "Cancel connect_data\n"); | |
911 purple_proxy_connect_cancel(qd->connect_data); | |
912 } | |
913 | |
914 if(qd->tcp_txbuf != NULL) { | |
915 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_txbuf\n"); | |
916 purple_circ_buffer_destroy(qd->tcp_txbuf); | |
917 qd->tcp_txbuf = NULL; | |
918 } | |
919 | |
920 if (qd->tx_handler) { | |
921 purple_input_remove(qd->tx_handler); | |
922 qd->tx_handler = 0; | |
923 } | |
924 if (qd->tcp_rxqueue != NULL) { | |
925 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy tcp_rxqueue\n"); | |
926 g_free(qd->tcp_rxqueue); | |
927 qd->tcp_rxqueue = NULL; | |
928 qd->tcp_rxlen = 0; | |
929 } | |
930 | |
931 if (qd->udp_query_data != NULL) { | |
932 purple_debug(PURPLE_DEBUG_INFO, "QQ", "destroy udp_query_data\n"); | |
933 purple_dnsquery_destroy(qd->udp_query_data); | |
934 qd->udp_query_data = NULL; | |
935 } | |
936 | |
937 qq_trans_remove_all(qd); | |
938 | |
823 if (qd->token) { | 939 if (qd->token) { |
824 purple_debug_info("QQ", "free token\n"); | 940 purple_debug(PURPLE_DEBUG_INFO, "QQ", "free token\n"); |
825 g_free(qd->token); | 941 g_free(qd->token); |
826 qd->token = NULL; | 942 qd->token = NULL; |
827 qd->token_len = 0; | 943 qd->token_len = 0; |
828 } | 944 } |
829 memset(qd->inikey, 0, sizeof(qd->inikey)); | 945 memset(qd->inikey, 0, sizeof(qd->inikey)); |
837 qq_add_buddy_request_free(qd); | 953 qq_add_buddy_request_free(qd); |
838 qq_info_query_free(qd); | 954 qq_info_query_free(qd); |
839 qq_buddies_list_free(gc->account, qd); | 955 qq_buddies_list_free(gc->account, qd); |
840 } | 956 } |
841 | 957 |
842 static gint packet_encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, | 958 static gint encap(qq_data *qd, guint8 *buf, gint maxlen, guint16 cmd, guint16 seq, |
843 guint8 *data, gint data_len) | 959 guint8 *data, gint data_len) |
844 { | 960 { |
845 gint bytes = 0; | 961 gint bytes = 0; |
846 g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); | 962 g_return_val_if_fail(qd != NULL && buf != NULL && maxlen > 0, -1); |
847 g_return_val_if_fail(data != NULL && data_len > 0, -1); | 963 g_return_val_if_fail(data != NULL && data_len > 0, -1); |
848 | 964 |
849 /* QQ TCP packet has two bytes in the begining defines packet length | 965 /* QQ TCP packet has two bytes in the begining defines packet length |
850 * so leave room here to store packet size */ | 966 * so leave room here to store packet size */ |
851 if (qd->use_tcp) { | 967 if (qd->use_tcp) { |
852 bytes += qq_put16(buf + bytes, 0x0000); | 968 bytes += qq_put16(buf + bytes, 0x0000); |
853 } | 969 } |
854 /* now comes the normal QQ packet as UDP */ | 970 /* now comes the normal QQ packet as UDP */ |
855 bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); | 971 bytes += qq_put8(buf + bytes, QQ_PACKET_TAG); |
856 bytes += qq_put16(buf + bytes, QQ_CLIENT); | 972 bytes += qq_put16(buf + bytes, QQ_CLIENT); |
857 bytes += qq_put16(buf + bytes, cmd); | 973 bytes += qq_put16(buf + bytes, cmd); |
858 | 974 |
859 bytes += qq_put16(buf + bytes, seq); | 975 bytes += qq_put16(buf + bytes, seq); |
860 | 976 |
861 bytes += qq_put32(buf + bytes, qd->uid); | 977 bytes += qq_put32(buf + bytes, qd->uid); |
862 bytes += qq_putdata(buf + bytes, data, data_len); | 978 bytes += qq_putdata(buf + bytes, data, data_len); |
863 bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); | 979 bytes += qq_put8(buf + bytes, QQ_PACKET_TAIL); |
869 | 985 |
870 return bytes; | 986 return bytes; |
871 } | 987 } |
872 | 988 |
873 /* data has been encrypted before */ | 989 /* data has been encrypted before */ |
874 static gint packet_send_out(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) | 990 gint qq_send_data(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, |
875 { | 991 guint8 *data, gint data_len) |
876 qq_data *qd; | 992 { |
877 guint8 *buf; | 993 guint8 *buf; |
878 gint buf_len; | 994 gint buf_len; |
879 gint bytes_sent; | 995 gint bytes_sent; |
880 | 996 |
881 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | 997 g_return_val_if_fail(qd != NULL, -1); |
882 qd = (qq_data *)gc->proto_data; | |
883 g_return_val_if_fail(data != NULL && data_len > 0, -1); | 998 g_return_val_if_fail(data != NULL && data_len > 0, -1); |
884 | 999 |
885 buf = g_newa(guint8, MAX_PACKET_SIZE); | 1000 buf = g_newa(guint8, MAX_PACKET_SIZE); |
886 memset(buf, 0, MAX_PACKET_SIZE); | 1001 memset(buf, 0, MAX_PACKET_SIZE); |
887 buf_len = packet_encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); | 1002 buf_len = encap(qd, buf, MAX_PACKET_SIZE, cmd, seq, data, data_len); |
888 if (buf_len <= 0) { | 1003 if (buf_len <= 0) { |
889 return -1; | 1004 return -1; |
890 } | 1005 } |
891 | 1006 |
892 if (qd->use_tcp) { | 1007 if (qd->use_tcp) { |
893 bytes_sent = tcp_send_out(gc, buf, buf_len); | 1008 bytes_sent = tcp_send_out(qd, buf, buf_len); |
894 } else { | 1009 } else { |
895 bytes_sent = udp_send_out(gc, buf, buf_len); | 1010 bytes_sent = udp_send_out(qd, buf, buf_len); |
896 } | 1011 } |
897 | 1012 |
1013 if (need_ack) { | |
1014 qq_trans_add_client_cmd(qd, cmd, seq, data, data_len); | |
1015 } | |
1016 | |
1017 #if 1 | |
1018 /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */ | |
1019 purple_debug(PURPLE_DEBUG_INFO, "QQ", | |
1020 "<== [%05d], 0x%04X %s, total %d bytes is sent %d\n", | |
1021 seq, cmd, qq_get_cmd_desc(cmd), buf_len, bytes_sent); | |
1022 #endif | |
898 return bytes_sent; | 1023 return bytes_sent; |
899 } | 1024 } |
900 | 1025 |
901 gint qq_send_cmd_encrypted(PurpleConnection *gc, guint16 cmd, guint16 seq, | 1026 /* Encrypt data with session_key, then call qq_send_data */ |
902 guint8 *data, gint data_len, gboolean need_ack) | 1027 gint qq_send_cmd_detail(qq_data *qd, guint16 cmd, guint16 seq, gboolean need_ack, |
903 { | 1028 guint8 *data, gint data_len) |
904 gint send_len; | 1029 { |
905 | |
906 #if 1 | |
907 purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", | |
908 seq, qq_get_cmd_desc(cmd), cmd, data_len); | |
909 #endif | |
910 | |
911 send_len = packet_send_out(gc, cmd, seq, data, data_len); | |
912 if (need_ack) { | |
913 qq_trans_add_client_cmd(gc, cmd, seq, data, data_len, 0, 0); | |
914 } | |
915 return send_len; | |
916 } | |
917 | |
918 /* Encrypt data with session_key, and send packet out */ | |
919 static gint send_cmd_detail(PurpleConnection *gc, guint16 cmd, guint16 seq, | |
920 guint8 *data, gint data_len, gboolean need_ack, gint update_class, guint32 ship32) | |
921 { | |
922 qq_data *qd; | |
923 guint8 *encrypted_data; | 1030 guint8 *encrypted_data; |
924 gint encrypted_len; | 1031 gint encrypted_len; |
925 gint bytes_sent; | 1032 |
926 | 1033 g_return_val_if_fail(qd != NULL, -1); |
927 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | |
928 qd = (qq_data *)gc->proto_data; | |
929 g_return_val_if_fail(data != NULL && data_len > 0, -1); | 1034 g_return_val_if_fail(data != NULL && data_len > 0, -1); |
930 | 1035 |
931 /* at most 16 bytes more */ | 1036 /* at most 16 bytes more */ |
932 encrypted_data = g_newa(guint8, data_len + 16); | 1037 encrypted_data = g_newa(guint8, data_len + 16); |
1038 #if 0 | |
1039 purple_debug(PURPLE_DEBUG_INFO, "QQ_ENCRYPT", | |
1040 "Before %d: [%05d] 0x%04X %s\n", | |
1041 data_len, seq, cmd, qq_get_cmd_desc(cmd)); | |
1042 #endif | |
933 encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key); | 1043 encrypted_len = qq_encrypt(encrypted_data, data, data_len, qd->session_key); |
934 if (encrypted_len < 16) { | 1044 if (encrypted_len < 16) { |
935 purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] 0x%04X %s\n", | 1045 purple_debug(PURPLE_DEBUG_ERROR, "QQ_ENCRYPT", |
1046 "Error len %d: [%05d] 0x%04X %s\n", | |
936 encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); | 1047 encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); |
937 return -1; | 1048 return -1; |
938 } | 1049 } |
939 | 1050 |
940 bytes_sent = packet_send_out(gc, cmd, seq, encrypted_data, encrypted_len); | 1051 #if 0 |
941 | 1052 purple_debug(PURPLE_DEBUG_INFO, "QQ_ENCRYPT", |
942 if (need_ack) { | 1053 "After %d: [%05d] 0x%04X %s\n", |
943 qq_trans_add_client_cmd(gc, cmd, seq, encrypted_data, encrypted_len, update_class, ship32); | 1054 encrypted_len, seq, cmd, qq_get_cmd_desc(cmd)); |
944 } | 1055 #endif |
945 return bytes_sent; | 1056 return qq_send_data(qd, cmd, seq, need_ack, encrypted_data, encrypted_len); |
946 } | 1057 } |
947 | 1058 |
948 gint qq_send_cmd_mess(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len, | 1059 /* set seq and need_ack, then call qq_send_cmd_detail */ |
949 gint update_class, guint32 ship32) | 1060 gint qq_send_cmd(qq_data *qd, guint16 cmd, guint8 *data, gint data_len) |
1061 { | |
1062 g_return_val_if_fail(qd != NULL, -1); | |
1063 g_return_val_if_fail(data != NULL && data_len > 0, -1); | |
1064 | |
1065 qd->send_seq++; | |
1066 return qq_send_cmd_detail(qd, cmd, qd->send_seq, TRUE, data, data_len); | |
1067 } | |
1068 | |
1069 gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, | |
1070 guint8 *data, gint data_len) | |
1071 { | |
1072 return qq_send_room_cmd(gc, room_cmd, 0, data, data_len); | |
1073 } | |
1074 | |
1075 gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) | |
1076 { | |
1077 g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1); | |
1078 return qq_send_room_cmd(gc, room_cmd, room_id, NULL, 0); | |
1079 } | |
1080 | |
1081 gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, | |
1082 guint8 *data, gint data_len) | |
950 { | 1083 { |
951 qq_data *qd; | 1084 qq_data *qd; |
952 guint16 seq; | 1085 |
953 | |
954 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | |
955 qd = (qq_data *) gc->proto_data; | |
956 g_return_val_if_fail(data != NULL && data_len > 0, -1); | |
957 | |
958 seq = ++qd->send_seq; | |
959 #if 1 | |
960 purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", | |
961 seq, qq_get_cmd_desc(cmd), cmd, data_len); | |
962 #endif | |
963 return send_cmd_detail(gc, cmd, seq, data, data_len, TRUE, update_class, ship32); | |
964 } | |
965 | |
966 /* set seq and need_ack, then call send_cmd_detail */ | |
967 gint qq_send_cmd(PurpleConnection *gc, guint16 cmd, guint8 *data, gint data_len) | |
968 { | |
969 qq_data *qd; | |
970 guint16 seq; | |
971 gboolean need_ack; | |
972 | |
973 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | |
974 qd = (qq_data *) gc->proto_data; | |
975 g_return_val_if_fail(data != NULL && data_len > 0, -1); | |
976 | |
977 if (cmd != QQ_CMD_LOGOUT) { | |
978 seq = ++qd->send_seq; | |
979 need_ack = TRUE; | |
980 } else { | |
981 seq = 0xFFFF; | |
982 need_ack = FALSE; | |
983 } | |
984 #if 1 | |
985 purple_debug_info("QQ", "<== [%05d], %s(0x%04X), datalen %d\n", | |
986 seq, qq_get_cmd_desc(cmd), cmd, data_len); | |
987 #endif | |
988 return send_cmd_detail(gc, cmd, seq, data, data_len, need_ack, 0, 0); | |
989 } | |
990 | |
991 /* set seq and need_ack, then call send_cmd_detail */ | |
992 gint qq_send_server_reply(PurpleConnection *gc, guint16 cmd, guint16 seq, guint8 *data, gint data_len) | |
993 { | |
994 #if 1 | |
995 purple_debug_info("QQ", "<== [SRV-%05d], %s(0x%04X), datalen %d\n", | |
996 seq, qq_get_cmd_desc(cmd), cmd, data_len); | |
997 #endif | |
998 return send_cmd_detail(gc, cmd, seq, data, data_len, FALSE, 0, 0); | |
999 } | |
1000 | |
1001 static gint send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, | |
1002 guint8 *data, gint data_len, gint update_class, guint32 ship32) | |
1003 { | |
1004 qq_data *qd; | |
1005 guint8 *buf; | 1086 guint8 *buf; |
1006 gint buf_len; | 1087 gint buf_len; |
1007 guint8 *encrypted_data; | 1088 guint8 *encrypted_data; |
1008 gint encrypted_len; | 1089 gint encrypted_len; |
1009 gint bytes_sent; | 1090 gint bytes_sent; |
1010 guint16 seq; | 1091 guint16 seq; |
1011 | 1092 |
1012 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); | 1093 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, -1); |
1013 qd = (qq_data *) gc->proto_data; | 1094 qd = (qq_data *) gc->proto_data; |
1014 | 1095 |
1015 buf = g_newa(guint8, MAX_PACKET_SIZE); | 1096 buf = g_newa(guint8, MAX_PACKET_SIZE); |
1016 memset(buf, 0, MAX_PACKET_SIZE); | 1097 memset(buf, 0, MAX_PACKET_SIZE); |
1023 buf_len += qq_put32(buf + buf_len, room_id); | 1104 buf_len += qq_put32(buf + buf_len, room_id); |
1024 } | 1105 } |
1025 if (data != NULL && data_len > 0) { | 1106 if (data != NULL && data_len > 0) { |
1026 buf_len += qq_putdata(buf + buf_len, data, data_len); | 1107 buf_len += qq_putdata(buf + buf_len, data, data_len); |
1027 } | 1108 } |
1028 | |
1029 qd->send_seq++; | 1109 qd->send_seq++; |
1030 seq = qd->send_seq; | 1110 seq = qd->send_seq; |
1031 | 1111 |
1032 /* Encrypt to encrypted_data with session_key */ | 1112 /* Encrypt to encrypted_data with session_key */ |
1033 /* at most 16 bytes more */ | 1113 /* at most 16 bytes more */ |
1034 encrypted_data = g_newa(guint8, buf_len + 16); | 1114 encrypted_data = g_newa(guint8, buf_len + 16); |
1035 encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key); | 1115 encrypted_len = qq_encrypt(encrypted_data, buf, buf_len, qd->session_key); |
1036 if (encrypted_len < 16) { | 1116 if (encrypted_len < 16) { |
1037 purple_debug_error("QQ_ENCRYPT", "Error len %d: [%05d] %s (0x%02X)\n", | 1117 purple_debug(PURPLE_DEBUG_ERROR, "QQ_ENCRYPT", |
1038 encrypted_len, seq, qq_get_room_cmd_desc(room_cmd), room_cmd); | 1118 "Error len %d: [%05d] QQ_CMD_ROOM.(0x%02X %s)\n", |
1119 encrypted_len, seq, room_cmd, qq_get_room_cmd_desc(room_cmd)); | |
1039 return -1; | 1120 return -1; |
1040 } | 1121 } |
1041 | 1122 |
1042 bytes_sent = packet_send_out(gc, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len); | 1123 /* Encap header to buf */ |
1124 buf_len = encap(qd, buf, MAX_PACKET_SIZE, QQ_CMD_ROOM, seq, encrypted_data, encrypted_len); | |
1125 if (buf_len <= 0) { | |
1126 return -1; | |
1127 } | |
1128 | |
1129 if (qd->use_tcp) { | |
1130 bytes_sent = tcp_send_out(qd, buf, buf_len); | |
1131 } else { | |
1132 bytes_sent = udp_send_out(qd, buf, buf_len); | |
1133 } | |
1134 | |
1135 qq_trans_add_room_cmd(qd, seq, room_cmd, room_id, buf, buf_len); | |
1136 | |
1043 #if 1 | 1137 #if 1 |
1044 /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */ | 1138 /* qq_show_packet("QQ_SEND_DATA", buf, buf_len); */ |
1045 purple_debug_info("QQ", | 1139 purple_debug(PURPLE_DEBUG_INFO, "QQ", |
1046 "<== [%05d], %s (0x%02X) to room %d, datalen %d\n", | 1140 "<== [%05d], QQ_CMD_ROOM.(0x%02X %s) to room %d, total %d bytes is sent %d\n", |
1047 seq, qq_get_room_cmd_desc(room_cmd), room_cmd, room_id, buf_len); | 1141 seq, room_cmd, qq_get_room_cmd_desc(room_cmd), room_id, |
1142 buf_len, bytes_sent); | |
1048 #endif | 1143 #endif |
1049 | |
1050 qq_trans_add_room_cmd(gc, seq, room_cmd, room_id, buf, buf_len, update_class, ship32); | |
1051 return bytes_sent; | 1144 return bytes_sent; |
1052 } | 1145 } |
1053 | |
1054 gint qq_send_room_cmd_mess(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, | |
1055 guint8 *data, gint data_len, gint update_class, guint32 ship32) | |
1056 { | |
1057 return send_room_cmd(gc, room_cmd, room_id, data, data_len, update_class, ship32); | |
1058 } | |
1059 | |
1060 gint qq_send_room_cmd(PurpleConnection *gc, guint8 room_cmd, guint32 room_id, | |
1061 guint8 *data, gint data_len) | |
1062 { | |
1063 return send_room_cmd(gc, room_cmd, room_id, data, data_len, 0, 0); | |
1064 } | |
1065 | |
1066 gint qq_send_room_cmd_noid(PurpleConnection *gc, guint8 room_cmd, | |
1067 guint8 *data, gint data_len) | |
1068 { | |
1069 return send_room_cmd(gc, room_cmd, 0, data, data_len, 0, 0); | |
1070 } | |
1071 | |
1072 gint qq_send_room_cmd_only(PurpleConnection *gc, guint8 room_cmd, guint32 room_id) | |
1073 { | |
1074 g_return_val_if_fail(room_cmd > 0 && room_id > 0, -1); | |
1075 return send_room_cmd(gc, room_cmd, room_id, NULL, 0, 0, 0); | |
1076 } |