comparison libpurple/protocols/qq/qq_network.c @ 23754:967344bc404d

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