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 }