Mercurial > pidgin
comparison libpurple/protocols/qq/qq_network.c @ 23878: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
23695:5f454b975a99 | 23878: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 } |