libpurple/protocols/qq/qq_base.c

branch
cpw.masca.webkit
changeset 32503
ab886d3a38ae
parent 32502
e64e49502c79
parent 31944
77d17906f1c3
child 32504
8243b910ed4c
equal deleted inserted replaced
32502:e64e49502c79 32503:ab886d3a38ae
1 /**
2 * @file qq_base.c
3 *
4 * purple
5 *
6 * Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 */
24
25 #include "internal.h"
26 #include "debug.h"
27 #include "server.h"
28 #include "cipher.h"
29 #include "request.h"
30
31 #include "buddy_info.h"
32 #include "buddy_list.h"
33 #include "char_conv.h"
34 #include "qq_crypt.h"
35 #include "group.h"
36 #include "qq_define.h"
37 #include "qq_network.h"
38 #include "qq_base.h"
39 #include "packet_parse.h"
40 #include "qq.h"
41 #include "qq_network.h"
42 #include "utils.h"
43
44 /* generate a md5 key using uid and session_key */
45 static void get_session_md5(guint8 *session_md5, guint32 uid, guint8 *session_key)
46 {
47 guint8 src[QQ_KEY_LENGTH + QQ_KEY_LENGTH];
48 gint bytes = 0;
49
50 bytes += qq_put32(src + bytes, uid);
51 bytes += qq_putdata(src + bytes, session_key, QQ_KEY_LENGTH);
52
53 qq_get_md5(session_md5, QQ_KEY_LENGTH, src, bytes);
54 }
55
56 /* process login reply which says OK */
57 static gint8 process_login_ok(PurpleConnection *gc, guint8 *data, gint len)
58 {
59 qq_data *qd;
60 gint bytes;
61
62 guint8 ret;
63 guint32 uid;
64 struct in_addr ip;
65 guint16 port;
66 struct tm *tm_local;
67
68 qd = (qq_data *) gc->proto_data;
69 /* qq_show_packet("Login reply", data, len); */
70
71 if (len < 148) {
72 qq_show_packet("Login reply OK, but length < 139", data, len);
73 purple_connection_error_reason(gc,
74 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
75 _("Unable to decrypt server reply"));
76 return QQ_LOGIN_REPLY_ERR;
77 }
78
79 bytes = 0;
80 bytes += qq_get8(&ret, data + bytes);
81 bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
82 get_session_md5(qd->session_md5, qd->uid, qd->session_key);
83 purple_debug_info("QQ", "Got session_key\n");
84 bytes += qq_get32(&uid, data + bytes);
85 if (uid != qd->uid) {
86 purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid);
87 }
88 bytes += qq_getIP(&qd->my_ip, data + bytes);
89 bytes += qq_get16(&qd->my_port, data + bytes);
90 purple_debug_info("QQ", "Internet IP: %s, %d\n", inet_ntoa(qd->my_ip), qd->my_port);
91
92 bytes += qq_getIP(&qd->my_local_ip, data + bytes);
93 bytes += qq_get16(&qd->my_local_port, data + bytes);
94 purple_debug_info("QQ", "Local IP: %s, %d\n", inet_ntoa(qd->my_local_ip), qd->my_local_port);
95
96 bytes += qq_getime(&qd->login_time, data + bytes);
97 tm_local = localtime(&qd->login_time);
98 purple_debug_info("QQ", "Login time: %d-%d-%d, %d:%d:%d\n",
99 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
100 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
101 /* skip unknown 2 bytes, 0x(03 0a) */
102 bytes += 2;
103 /* skip unknown 24 bytes, maybe token to access Qun shared files */
104 bytes += 24;
105 /* unknow ip and port */
106 bytes += qq_getIP(&ip, data + bytes);
107 bytes += qq_get16(&port, data + bytes);
108 purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port);
109 /* unknow ip and port */
110 bytes += qq_getIP(&ip, data + bytes);
111 bytes += qq_get16(&port, data + bytes);
112 purple_debug_info("QQ", "Unknow IP: %s, %d\n", inet_ntoa(ip), port);
113 /* unknown 4 bytes, 0x(00 81 00 00)*/
114 bytes += 4;
115 /* skip unknown 32 bytes, maybe key to access QQ Home */
116 bytes += 32;
117 /* skip unknown 16 bytes, 0x(00 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00) */
118 bytes += 16;
119 /* time */
120 bytes += qq_getime(&qd->last_login_time[0], data + bytes);
121 tm_local = localtime(&qd->last_login_time[0]);
122 purple_debug_info("QQ", "Last login time: %d-%d-%d, %d:%d:%d\n",
123 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
124 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
125 /* unknow time */
126 g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 1, QQ_LOGIN_REPLY_OK);
127 bytes += qq_getime(&qd->last_login_time[1], data + bytes);
128 tm_local = localtime(&qd->last_login_time[1]);
129 purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n",
130 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
131 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
132
133 g_return_val_if_fail(sizeof(qd->last_login_time) / sizeof(time_t) > 2, QQ_LOGIN_REPLY_OK);
134 bytes += qq_getime(&qd->last_login_time[2], data + bytes);
135 tm_local = localtime(&qd->last_login_time[2]);
136 purple_debug_info("QQ", "Time: %d-%d-%d, %d:%d:%d\n",
137 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
138 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
139 /* unknow 9 bytes, 0x(00 0a 00 0a 01 00 00 0e 10) */
140
141 if (len > 148) {
142 qq_show_packet("Login reply OK, but length > 139", data, len);
143 }
144 return QQ_LOGIN_REPLY_OK;
145 }
146
147 /* process login reply packet which includes redirected new server address */
148 static gint8 process_login_redirect(PurpleConnection *gc, guint8 *data, gint len)
149 {
150 qq_data *qd;
151 gint bytes;
152 struct {
153 guint8 result;
154 guint32 uid;
155 struct in_addr new_server_ip;
156 guint16 new_server_port;
157 } packet;
158
159
160 if (len < 11) {
161 purple_connection_error_reason(gc,
162 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
163 _("Unable to decrypt server reply"));
164 return QQ_LOGIN_REPLY_ERR;
165 }
166
167 qd = (qq_data *) gc->proto_data;
168 bytes = 0;
169 /* 000-000: reply code */
170 bytes += qq_get8(&packet.result, data + bytes);
171 /* 001-004: login uid */
172 bytes += qq_get32(&packet.uid, data + bytes);
173 /* 005-008: redirected new server IP */
174 bytes += qq_getIP(&packet.new_server_ip, data + bytes);
175 /* 009-010: redirected new server port */
176 bytes += qq_get16(&packet.new_server_port, data + bytes);
177
178 if (len > 11) {
179 purple_debug_error("QQ", "Login redirect more than expected %d bytes, read %d bytes\n", 11, bytes);
180 }
181
182 /* redirect to new server, do not disconnect or connect here
183 * those connect should be called at packet_process */
184 qd->redirect_ip.s_addr = packet.new_server_ip.s_addr;
185 qd->redirect_port = packet.new_server_port;
186 return QQ_LOGIN_REPLY_REDIRECT;
187 }
188
189 /* request before login */
190 void qq_request_token(PurpleConnection *gc)
191 {
192 qq_data *qd;
193 guint8 buf[16] = {0};
194 gint bytes = 0;
195
196 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
197 qd = (qq_data *) gc->proto_data;
198
199 bytes += qq_put8(buf + bytes, 0);
200
201 qd->send_seq++;
202 qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN, qd->send_seq, buf, bytes, TRUE);
203 }
204
205 /* send login packet to QQ server */
206 void qq_request_login(PurpleConnection *gc)
207 {
208 qq_data *qd;
209 guint8 *buf, *raw_data;
210 gint bytes;
211 guint8 *encrypted;
212 gint encrypted_len;
213
214 /* for QQ 2005? copy from lumaqq */
215 static const guint8 login_23_51[29] = {
216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x86, 0xcc, 0x4c, 0x35,
218 0x2c, 0xd3, 0x73, 0x6c, 0x14, 0xf6, 0xf6, 0xaf,
219 0xc3, 0xfa, 0x33, 0xa4, 0x01
220 };
221
222 static const guint8 login_53_68[16] = {
223 0x8D, 0x8B, 0xFA, 0xEC, 0xD5, 0x52, 0x17, 0x4A,
224 0x86, 0xF9, 0xA7, 0x75, 0xE6, 0x32, 0xD1, 0x6D
225 };
226
227 static const guint8 login_100_bytes[100] = {
228 0x40, 0x0B, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
230 0x00, 0x00, 0x00, 0x00, 0x01, 0xE9, 0x03, 0x01,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF3, 0x03,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xED,
233 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
234 0xEC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
237 0x00, 0x00, 0x01, 0xEE, 0x03, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x01, 0xEF, 0x03, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x01, 0xEB, 0x03, 0x00,
240 0x00, 0x00, 0x00, 0x00
241 };
242
243 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
244 qd = (qq_data *) gc->proto_data;
245
246 g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
247
248 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
249 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
250
251 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
252
253 bytes = 0;
254 /* now generate the encrypted data
255 * 000-015 use password_twice_md5 as key to encrypt empty string */
256 encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
257 g_return_if_fail(encrypted_len == 16);
258 bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
259
260 /* 016-016 */
261 bytes += qq_put8(raw_data + bytes, 0x00);
262 /* 017-020, used to be IP, now zero */
263 bytes += qq_put32(raw_data + bytes, 0x00000000);
264 /* 021-022, used to be port, now zero */
265 bytes += qq_put16(raw_data + bytes, 0x0000);
266 /* 023-051, fixed value, unknown */
267 bytes += qq_putdata(raw_data + bytes, login_23_51, 29);
268 /* 052-052, login mode */
269 bytes += qq_put8(raw_data + bytes, qd->login_mode);
270 /* 053-068, fixed value, maybe related to per machine */
271 bytes += qq_putdata(raw_data + bytes, login_53_68, 16);
272 /* 069, login token length */
273 bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
274 /* 070-093, login token, normally 24 bytes */
275 bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
276 /* 100 bytes unknown */
277 bytes += qq_putdata(raw_data + bytes, login_100_bytes, 100);
278 /* all zero left */
279 memset(raw_data + bytes, 0, 416 - bytes);
280 bytes = 416;
281
282 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
283
284 buf = g_newa(guint8, MAX_PACKET_SIZE);
285 memset(buf, 0, MAX_PACKET_SIZE);
286 bytes = 0;
287 bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
288 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
289
290 qd->send_seq++;
291 qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
292 }
293
294 guint8 qq_process_token(PurpleConnection *gc, guint8 *buf, gint buf_len)
295 {
296 qq_data *qd;
297 gint bytes;
298 guint8 ret;
299 guint8 token_len;
300 gchar *msg;
301
302 g_return_val_if_fail(buf != NULL && buf_len != 0, QQ_LOGIN_REPLY_ERR);
303
304 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
305 qd = (qq_data *) gc->proto_data;
306
307 bytes = 0;
308 bytes += qq_get8(&ret, buf + bytes);
309 bytes += qq_get8(&token_len, buf + bytes);
310
311 if (ret != QQ_LOGIN_REPLY_OK) {
312 qq_show_packet("Failed requesting token", buf, buf_len);
313
314 msg = g_strdup_printf( _("Failed requesting token, 0x%02X"), ret );
315 purple_connection_error_reason(gc,
316 PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
317 msg);
318 g_free(msg);
319 return QQ_LOGIN_REPLY_ERR;
320 }
321
322 if (bytes + token_len < buf_len) {
323 msg = g_strdup_printf( _("Invalid token len, %d"), token_len);
324 purple_connection_error_reason(gc,
325 PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
326 msg);
327 g_free(msg);
328 return QQ_LOGIN_REPLY_ERR;
329 }
330
331 if (bytes + token_len > buf_len) {
332 purple_debug_info("QQ", "Extra token data, %d %d\n", token_len, buf_len - bytes);
333 }
334 /* qq_show_packet("Got token", buf + bytes, buf_len - bytes); */
335
336 if (qd->ld.token != NULL) {
337 g_free(qd->ld.token);
338 qd->ld.token = NULL;
339 qd->ld.token_len = 0;
340 }
341 qd->ld.token = g_new0(guint8, token_len);
342 qd->ld.token_len = token_len;
343 g_memmove(qd->ld.token, buf + 2, qd->ld.token_len);
344 return ret;
345 }
346
347 /* send logout packets to QQ server */
348 void qq_request_logout(PurpleConnection *gc)
349 {
350 gint i;
351 qq_data *qd;
352
353 qd = (qq_data *) gc->proto_data;
354 for (i = 0; i < 4; i++)
355 qq_send_cmd(gc, QQ_CMD_LOGOUT, qd->ld.pwd_twice_md5, QQ_KEY_LENGTH);
356
357 qd->is_login = FALSE; /* update login status AFTER sending logout packets */
358 }
359
360 /* for QQ 2003iii 0117, fixed value */
361 /* static const guint8 login_23_51[29] = {
362 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
363 0x00, 0x00, 0x00, 0x00, 0xbf, 0x14, 0x11, 0x20,
364 0x03, 0x9d, 0xb2, 0xe6, 0xb3, 0x11, 0xb7, 0x13,
365 0x95, 0x67, 0xda, 0x2c, 0x01
366 }; */
367
368 /* for QQ 2003iii 0304, fixed value */
369 /*
370 static const guint8 login_23_51[29] = {
371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x00, 0x00, 0x00, 0x00, 0x9a, 0x93, 0xfe, 0x85,
373 0xd3, 0xd9, 0x2a, 0x41, 0xc8, 0x0d, 0xff, 0xb6,
374 0x40, 0xb8, 0xac, 0x32, 0x01
375 };
376 */
377
378 /* fixed value, not affected by version, or mac address */
379 /*
380 static const guint8 login_53_68[16] = {
381 0x82, 0x2a, 0x91, 0xfd, 0xa5, 0xca, 0x67, 0x4c,
382 0xac, 0x81, 0x1f, 0x6f, 0x52, 0x05, 0xa7, 0xbf
383 };
384 */
385
386 /* process the login reply packet */
387 guint8 qq_process_login( PurpleConnection *gc, guint8 *data, gint data_len)
388 {
389 qq_data *qd;
390 guint8 ret = data[0];
391 gchar *msg, *msg_utf8;
392 gchar *error;
393 PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
394
395 g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
396
397 qd = (qq_data *) gc->proto_data;
398
399 switch (ret) {
400 case QQ_LOGIN_REPLY_OK:
401 purple_debug_info("QQ", "Login OK\n");
402 return process_login_ok(gc, data, data_len);
403 case QQ_LOGIN_REPLY_REDIRECT:
404 purple_debug_info("QQ", "Redirect new server\n");
405 return process_login_redirect(gc, data, data_len);
406
407 case 0x0A: /* extend redirect used in QQ2006 */
408 error = g_strdup( _("Redirect_EX is not currently supported") );
409 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
410 break;
411 case 0x05: /* invalid password */
412 if (!purple_account_get_remember_password(gc->account)) {
413 purple_account_set_password(gc->account, NULL);
414 }
415 error = g_strdup( _("Incorrect password"));
416 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
417 break;
418 case 0x06: /* need activation */
419 error = g_strdup( _("Activation required"));
420 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
421 break;
422
423 default:
424 qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
425 ">>> [default] decrypt and dump");
426 error = g_strdup_printf(
427 _("Unknown reply code when logging in (0x%02X)"),
428 ret );
429 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
430 break;
431 }
432
433 msg = g_strndup((gchar *)data + 1, data_len - 1);
434 msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
435
436 purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
437 purple_connection_error_reason(gc, reason, msg_utf8);
438
439 g_free(error);
440 g_free(msg);
441 g_free(msg_utf8);
442 return QQ_LOGIN_REPLY_ERR;
443 }
444
445 /* send keep-alive packet to QQ server (it is a heart-beat) */
446 void qq_request_keep_alive(PurpleConnection *gc)
447 {
448 qq_data *qd;
449 guint8 raw_data[16] = {0};
450 gint bytes= 0;
451
452 qd = (qq_data *) gc->proto_data;
453
454 /* In fact, we can send whatever we like to server
455 * with this command, server return the same result including
456 * the amount of online QQ users, my ip and port */
457 bytes += qq_put32(raw_data + bytes, qd->uid);
458 qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
459 }
460
461 /* parse the return ofqq_process_keep_alive keep-alive packet, it includes some system information */
462 gboolean qq_process_keep_alive(guint8 *data, gint data_len, PurpleConnection *gc)
463 {
464 qq_data *qd;
465 gchar **segments;
466
467 g_return_val_if_fail(data != NULL, FALSE);
468 g_return_val_if_fail(data_len != 0, FALSE);
469
470 qd = (qq_data *) gc->proto_data;
471
472 /* qq_show_packet("Keep alive reply packet", data, len); */
473
474 /* the last one is 60, don't know what it is */
475 segments = split_data(data, data_len, "\x1f", 6);
476 if (segments == NULL)
477 return TRUE;
478
479 /* segments[0] and segment[1] are all 0x30 ("0") */
480 qd->online_total = strtol(segments[2], NULL, 10);
481 if(0 == qd->online_total) {
482 purple_connection_error_reason(gc,
483 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
484 _("Lost connection with server"));
485 }
486 qd->my_ip.s_addr = inet_addr(segments[3]);
487 qd->my_port = strtol(segments[4], NULL, 10);
488
489 purple_debug_info("QQ", "keep alive, %s:%d\n",
490 inet_ntoa(qd->my_ip), qd->my_port);
491
492 g_strfreev(segments);
493 return TRUE;
494 }
495
496 void qq_request_keep_alive_2007(PurpleConnection *gc)
497 {
498 qq_data *qd;
499 guint8 raw_data[32] = {0};
500 gint bytes= 0;
501 gchar *uid_str;
502
503 qd = (qq_data *) gc->proto_data;
504
505 /* In fact, we can send whatever we like to server
506 * with this command, server return the same result including
507 * the amount of online QQ users, my ip and port */
508 uid_str = g_strdup_printf("%u", qd->uid);
509 bytes += qq_putdata(raw_data + bytes, (guint8 *)uid_str, strlen(uid_str));
510 qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
511
512 g_free(uid_str);
513 }
514
515 gboolean qq_process_keep_alive_2007(guint8 *data, gint data_len, PurpleConnection *gc)
516 {
517 qq_data *qd;
518 gint bytes= 0;
519 guint8 ret;
520
521 g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
522
523 qd = (qq_data *) gc->proto_data;
524
525 /* qq_show_packet("Keep alive reply packet", data, len); */
526
527 bytes = 0;
528 bytes += qq_get8(&ret, data + bytes);
529 bytes += qq_get32(&qd->online_total, data + bytes);
530 if(0 == qd->online_total) {
531 purple_connection_error_reason(gc,
532 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
533 _("Lost connection with server"));
534 }
535
536 bytes += qq_getIP(&qd->my_ip, data + bytes);
537 bytes += qq_get16(&qd->my_port, data + bytes);
538 return TRUE;
539 }
540
541 void qq_request_keep_alive_2008(PurpleConnection *gc)
542 {
543 qq_data *qd;
544 guint8 raw_data[16] = {0};
545 gint bytes= 0;
546
547 qd = (qq_data *) gc->proto_data;
548
549 /* In fact, we can send whatever we like to server
550 * with this command, server return the same result including
551 * the amount of online QQ users, my ip and port */
552 bytes += qq_put32(raw_data + bytes, qd->uid);
553 bytes += qq_putime(raw_data + bytes, &qd->login_time);
554 qq_send_cmd(gc, QQ_CMD_KEEP_ALIVE, raw_data, bytes);
555 }
556
557 gboolean qq_process_keep_alive_2008(guint8 *data, gint data_len, PurpleConnection *gc)
558 {
559 qq_data *qd;
560 gint bytes= 0;
561 guint8 ret;
562 time_t server_time;
563 struct tm *tm_local;
564
565 g_return_val_if_fail(data != NULL && data_len != 0, FALSE);
566
567 qd = (qq_data *) gc->proto_data;
568
569 /* qq_show_packet("Keep alive reply packet", data, len); */
570
571 bytes = 0;
572 bytes += qq_get8(&ret, data + bytes);
573 bytes += qq_get32(&qd->online_total, data + bytes);
574 if(0 == qd->online_total) {
575 purple_connection_error_reason(gc,
576 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
577 _("Lost connection with server"));
578 }
579
580 bytes += qq_getIP(&qd->my_ip, data + bytes);
581 bytes += qq_get16(&qd->my_port, data + bytes);
582 /* skip 2 byytes, 0x(00 3c) */
583 bytes += 2;
584 bytes += qq_getime(&server_time, data + bytes);
585 /* skip 5 bytes, all are 0 */
586
587 purple_debug_info("QQ", "keep alive, %s:%d\n",
588 inet_ntoa(qd->my_ip), qd->my_port);
589
590 tm_local = localtime(&server_time);
591 purple_debug_info("QQ", "Server time: %d-%d-%d, %d:%d:%d\n",
592 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
593 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
594 return TRUE;
595 }
596
597 /* For QQ2007/2008 */
598 void qq_request_get_server(PurpleConnection *gc)
599 {
600 qq_data *qd;
601 guint8 *buf, *raw_data;
602 gint bytes;
603 guint8 *encrypted;
604 gint encrypted_len;
605
606 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
607 qd = (qq_data *) gc->proto_data;
608
609 raw_data = g_newa(guint8, 128);
610 memset(raw_data, 0, 128);
611
612 encrypted = g_newa(guint8, 128 + 17); /* 17 bytes more */
613
614 bytes = 0;
615 if (qd->redirect == NULL) {
616 /* first packet to get server */
617 qd->redirect_len = 15;
618 qd->redirect = g_realloc(qd->redirect, qd->redirect_len);
619 memset(qd->redirect, 0, qd->redirect_len);
620 }
621 bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
622
623 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
624
625 buf = g_newa(guint8, MAX_PACKET_SIZE);
626 memset(buf, 0, MAX_PACKET_SIZE);
627 bytes = 0;
628 bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
629 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
630
631 qd->send_seq++;
632 qq_send_cmd_encrypted(gc, QQ_CMD_GET_SERVER, qd->send_seq, buf, bytes, TRUE);
633 }
634
635 guint16 qq_process_get_server(PurpleConnection *gc, guint8 *data, gint data_len)
636 {
637 qq_data *qd;
638 gint bytes;
639 guint16 ret;
640
641 g_return_val_if_fail (gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
642 qd = (qq_data *) gc->proto_data;
643
644 g_return_val_if_fail (data != NULL, QQ_LOGIN_REPLY_ERR);
645
646 /* qq_show_packet("Get Server", data, data_len); */
647 bytes = 0;
648 bytes += qq_get16(&ret, data + bytes);
649 if (ret == 0) {
650 /* Notice: do not clear redirect_data here. It will be used in login*/
651 qd->redirect_ip.s_addr = 0;
652 return QQ_LOGIN_REPLY_OK;
653 }
654
655 if (data_len < 15) {
656 purple_connection_error_reason(gc,
657 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR,
658 _("Unable to decrypt server reply"));
659 return QQ_LOGIN_REPLY_ERR;
660 }
661
662 qd->redirect_len = data_len;
663 qd->redirect = g_realloc(qd->redirect, qd->redirect_len);
664 qq_getdata(qd->redirect, qd->redirect_len, data);
665 /* qq_show_packet("Redirect to", qd->redirect, qd->redirect_len); */
666
667 qq_getIP(&qd->redirect_ip, data + 11);
668 purple_debug_info("QQ", "Get server %s\n", inet_ntoa(qd->redirect_ip));
669 return QQ_LOGIN_REPLY_REDIRECT;
670 }
671
672 void qq_request_token_ex(PurpleConnection *gc)
673 {
674 qq_data *qd;
675 guint8 *buf, *raw_data;
676 gint bytes;
677 guint8 *encrypted;
678 gint encrypted_len;
679
680 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
681 qd = (qq_data *) gc->proto_data;
682
683 g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
684
685 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
686 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
687
688 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
689
690 bytes = 0;
691 bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
692 bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
693 bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */
694 bytes += qq_put16(raw_data + bytes, 5);
695 bytes += qq_put32(raw_data + bytes, 0);
696 bytes += qq_put8(raw_data + bytes, 0); /* fragment index */
697 bytes += qq_put16(raw_data + bytes, 0); /* captcha token */
698
699 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
700
701 buf = g_newa(guint8, MAX_PACKET_SIZE);
702 memset(buf, 0, MAX_PACKET_SIZE);
703 bytes = 0;
704 bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
705 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
706
707 qd->send_seq++;
708 qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
709 }
710
711 void qq_request_token_ex_next(PurpleConnection *gc)
712 {
713 qq_data *qd;
714 guint8 *buf, *raw_data;
715 gint bytes;
716 guint8 *encrypted;
717 gint encrypted_len;
718
719 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
720 qd = (qq_data *) gc->proto_data;
721
722 g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
723
724 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
725 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
726
727 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
728
729 bytes = 0;
730 bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
731 bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
732 bytes += qq_put8(raw_data + bytes, 3); /* Subcommand */
733 bytes += qq_put16(raw_data + bytes, 5);
734 bytes += qq_put32(raw_data + bytes, 0);
735 bytes += qq_put8(raw_data + bytes, qd->captcha.next_index); /* fragment index */
736 bytes += qq_put16(raw_data + bytes, qd->captcha.token_len); /* captcha token */
737 bytes += qq_putdata(raw_data + bytes, qd->captcha.token, qd->captcha.token_len);
738
739 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
740
741 buf = g_newa(guint8, MAX_PACKET_SIZE);
742 memset(buf, 0, MAX_PACKET_SIZE);
743 bytes = 0;
744 bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
745 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
746
747 qd->send_seq++;
748 qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
749
750 purple_connection_update_progress(gc, _("Requesting captcha"), 3, QQ_CONNECT_STEPS);
751 }
752
753 static void request_token_ex_code(PurpleConnection *gc,
754 guint8 *token, guint16 token_len, guint8 *code, guint16 code_len)
755 {
756 qq_data *qd;
757 guint8 *buf, *raw_data;
758 gint bytes;
759 guint8 *encrypted;
760 gint encrypted_len;
761
762 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
763 qd = (qq_data *) gc->proto_data;
764
765 g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
766 g_return_if_fail(code != NULL && code_len > 0);
767
768 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
769 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
770
771 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
772
773 bytes = 0;
774 bytes += qq_put8(raw_data + bytes, qd->ld.token_len);
775 bytes += qq_putdata(raw_data + bytes, qd->ld.token, qd->ld.token_len);
776 bytes += qq_put8(raw_data + bytes, 4); /* Subcommand */
777 bytes += qq_put16(raw_data + bytes, 5);
778 bytes += qq_put32(raw_data + bytes, 0);
779 bytes += qq_put16(raw_data + bytes, code_len);
780 bytes += qq_putdata(raw_data + bytes, code, code_len);
781 bytes += qq_put16(raw_data + bytes, qd->ld.token_ex_len); /* login token ex */
782 bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
783
784 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
785
786 buf = g_newa(guint8, MAX_PACKET_SIZE);
787 memset(buf, 0, MAX_PACKET_SIZE);
788 bytes = 0;
789 bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
790 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
791
792 qd->send_seq++;
793 qq_send_cmd_encrypted(gc, QQ_CMD_TOKEN_EX, qd->send_seq, buf, bytes, TRUE);
794
795 purple_connection_update_progress(gc, _("Checking captcha"), 3, QQ_CONNECT_STEPS);
796 }
797
798 typedef struct {
799 PurpleConnection *gc;
800 guint8 *token;
801 guint16 token_len;
802 } qq_captcha_request;
803
804 static void captcha_request_destory(qq_captcha_request *captcha_req)
805 {
806 g_return_if_fail(captcha_req != NULL);
807 if (captcha_req->token) g_free(captcha_req->token);
808 g_free(captcha_req);
809 }
810
811 static void captcha_input_cancel_cb(qq_captcha_request *captcha_req,
812 PurpleRequestFields *fields)
813 {
814 captcha_request_destory(captcha_req);
815
816 purple_connection_error_reason(captcha_req->gc,
817 PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
818 _("Failed captcha verification"));
819 }
820
821 static void captcha_input_ok_cb(qq_captcha_request *captcha_req,
822 PurpleRequestFields *fields)
823 {
824 gchar *code;
825
826 g_return_if_fail(captcha_req != NULL && captcha_req->gc != NULL);
827
828 code = utf8_to_qq(
829 purple_request_fields_get_string(fields, "captcha_code"),
830 QQ_CHARSET_DEFAULT);
831
832 if (strlen(code) <= 0) {
833 captcha_input_cancel_cb(captcha_req, fields);
834 return;
835 }
836
837 request_token_ex_code(captcha_req->gc,
838 captcha_req->token, captcha_req->token_len,
839 (guint8 *)code, strlen(code));
840
841 captcha_request_destory(captcha_req);
842 }
843
844 void qq_captcha_input_dialog(PurpleConnection *gc,qq_captcha_data *captcha)
845 {
846 PurpleAccount *account;
847 PurpleRequestFields *fields;
848 PurpleRequestFieldGroup *group;
849 PurpleRequestField *field;
850 qq_captcha_request *captcha_req;
851
852 g_return_if_fail(captcha->token != NULL && captcha->token_len > 0);
853 g_return_if_fail(captcha->data != NULL && captcha->data_len > 0);
854
855 captcha_req = g_new0(qq_captcha_request, 1);
856 captcha_req->gc = gc;
857 captcha_req->token = g_new0(guint8, captcha->token_len);
858 g_memmove(captcha_req->token, captcha->token, captcha->token_len);
859 captcha_req->token_len = captcha->token_len;
860
861 account = purple_connection_get_account(gc);
862
863 fields = purple_request_fields_new();
864 group = purple_request_field_group_new(NULL);
865 purple_request_fields_add_group(fields, group);
866
867 field = purple_request_field_image_new("captcha_img",
868 _("Captcha Image"), (char *)captcha->data, captcha->data_len);
869 purple_request_field_group_add_field(group, field);
870
871 field = purple_request_field_string_new("captcha_code",
872 _("Enter code"), "", FALSE);
873 purple_request_field_string_set_masked(field, FALSE);
874 purple_request_field_group_add_field(group, field);
875
876 purple_request_fields(account,
877 _("QQ Captcha Verification"),
878 _("QQ Captcha Verification"),
879 _("Enter the text from the image"),
880 fields,
881 _("OK"), G_CALLBACK(captcha_input_ok_cb),
882 _("Cancel"), G_CALLBACK(captcha_input_cancel_cb),
883 purple_connection_get_account(gc), NULL, NULL,
884 captcha_req);
885 }
886
887 guint8 qq_process_token_ex(PurpleConnection *gc, guint8 *data, gint data_len)
888 {
889 qq_data *qd;
890 int bytes;
891 guint8 ret;
892 guint8 sub_cmd;
893 guint8 reply;
894 guint16 captcha_len;
895 guint8 curr_index;
896
897 g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
898
899 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
900 qd = (qq_data *) gc->proto_data;
901
902 ret = data[0];
903
904 bytes = 0;
905 bytes += qq_get8(&sub_cmd, data + bytes); /* 03: ok; 04: need verifying */
906 bytes += 2; /* 0x(00 05) */
907 bytes += qq_get8(&reply, data + bytes);
908
909 bytes += qq_get16(&(qd->ld.token_ex_len), data + bytes);
910 qd->ld.token_ex = g_realloc(qd->ld.token_ex, qd->ld.token_ex_len);
911 bytes += qq_getdata(qd->ld.token_ex, qd->ld.token_ex_len, data + bytes);
912 /* qq_show_packet("Get token ex", qd->ld.token_ex, qd->ld.token_ex_len); */
913
914 if(reply != 1)
915 {
916 purple_debug_info("QQ", "Captcha verified, result %d\n", reply);
917 return QQ_LOGIN_REPLY_OK;
918 }
919
920 bytes += qq_get16(&captcha_len, data + bytes);
921
922 qd->captcha.data = g_realloc(qd->captcha.data, qd->captcha.data_len + captcha_len);
923 bytes += qq_getdata(qd->captcha.data + qd->captcha.data_len, captcha_len, data + bytes);
924 qd->captcha.data_len += captcha_len;
925
926 bytes += qq_get8(&curr_index, data + bytes);
927 bytes += qq_get8(&qd->captcha.next_index, data + bytes);
928
929 bytes += qq_get16(&qd->captcha.token_len, data + bytes);
930 qd->captcha.token = g_realloc(qd->captcha.token, qd->captcha.token_len);
931 bytes += qq_getdata(qd->captcha.token, qd->captcha.token_len, data + bytes);
932 /* qq_show_packet("Get captcha token", qd->captcha.token, qd->captcha.token_len); */
933
934 purple_debug_info("QQ", "Request next captcha %d, new %d, total %d\n",
935 qd->captcha.next_index, captcha_len, qd->captcha.data_len);
936 if(qd->captcha.next_index > 0)
937 {
938 return QQ_LOGIN_REPLY_NEXT_TOKEN_EX;
939 }
940
941 return QQ_LOGIN_REPLY_CAPTCHA_DLG;
942 }
943
944 /* source copy from gg's common.c */
945 static guint32 crc32_table[256];
946 static int crc32_initialized = 0;
947
948 static void crc32_make_table()
949 {
950 guint32 h = 1;
951 unsigned int i, j;
952
953 memset(crc32_table, 0, sizeof(crc32_table));
954
955 for (i = 128; i; i >>= 1) {
956 h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
957
958 for (j = 0; j < 256; j += 2 * i)
959 crc32_table[i + j] = crc32_table[j] ^ h;
960 }
961
962 crc32_initialized = 1;
963 }
964
965 static guint32 crc32(guint32 crc, const guint8 *buf, int len)
966 {
967 if (!crc32_initialized)
968 crc32_make_table();
969
970 if (!buf || len < 0)
971 return crc;
972
973 crc ^= 0xffffffffL;
974
975 while (len--)
976 crc = (crc >> 8) ^ crc32_table[(crc ^ *buf++) & 0xff];
977
978 return crc ^ 0xffffffffL;
979 }
980
981 void qq_request_check_pwd(PurpleConnection *gc)
982 {
983 qq_data *qd;
984 guint8 *buf, *raw_data;
985 gint bytes;
986 guint8 *encrypted;
987 gint encrypted_len;
988 static guint8 header[] = {
989 0x00, 0x5F, 0x00, 0x00, 0x08, 0x04, 0x01, 0xE0
990 };
991 static guint8 unknown[] = {
992 0xDB, 0xB9, 0xF3, 0x0B, 0xF9, 0x13, 0x87, 0xB2,
993 0xE6, 0x20, 0x43, 0xBE, 0x53, 0xCA, 0x65, 0x03
994 };
995
996 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
997 qd = (qq_data *) gc->proto_data;
998
999 g_return_if_fail(qd->ld.token_ex != NULL && qd->ld.token_ex_len > 0);
1000
1001 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
1002 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
1003
1004 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
1005
1006 /* Encrypted password and put in encrypted */
1007 bytes = 0;
1008 bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
1009 bytes += qq_put16(raw_data + bytes, 0);
1010 bytes += qq_put16(raw_data + bytes, rand() & 0xffff);
1011
1012 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
1013
1014 /* create packet */
1015 bytes = 0;
1016 bytes += qq_putdata(raw_data + bytes, header, sizeof(header));
1017 /* token get from qq_request_token_ex */
1018 bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
1019 bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
1020 /* password encrypted */
1021 bytes += qq_put16(raw_data + bytes, encrypted_len);
1022 bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
1023 /* len of unknown + len of CRC32 */
1024 bytes += qq_put16(raw_data + bytes, sizeof(unknown) + 4);
1025 bytes += qq_putdata(raw_data + bytes, unknown, sizeof(unknown));
1026 bytes += qq_put32(
1027 raw_data + bytes, crc32(0xFFFFFFFF, unknown, sizeof(unknown)));
1028
1029 /* put length into first 2 bytes */
1030 qq_put8(raw_data + 1, bytes - 2);
1031
1032 /* tail */
1033 bytes += qq_put16(raw_data + bytes, 0x0003);
1034 bytes += qq_put8(raw_data + bytes, 0);
1035 bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[1]);
1036 bytes += qq_put8(raw_data + bytes, qd->ld.pwd_md5[2]);
1037
1038 /* qq_show_packet("Check password", raw_data, bytes); */
1039 /* Encrypted by random key*/
1040 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.random_key);
1041
1042 buf = g_newa(guint8, MAX_PACKET_SIZE);
1043 memset(buf, 0, MAX_PACKET_SIZE);
1044 bytes = 0;
1045 bytes += qq_putdata(buf + bytes, qd->ld.random_key, QQ_KEY_LENGTH);
1046 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
1047
1048 qd->send_seq++;
1049 qq_send_cmd_encrypted(gc, QQ_CMD_CHECK_PWD, qd->send_seq, buf, bytes, TRUE);
1050 }
1051
1052 guint8 qq_process_check_pwd( PurpleConnection *gc, guint8 *data, gint data_len)
1053 {
1054 qq_data *qd;
1055 int bytes;
1056 guint8 ret;
1057 gchar *error = NULL;
1058 guint16 unknow_token_len;
1059 gchar *msg, *msg_utf8;
1060 guint16 msg_len;
1061 PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1062
1063 g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
1064
1065 g_return_val_if_fail(gc != NULL && gc->proto_data != NULL, QQ_LOGIN_REPLY_ERR);
1066 qd = (qq_data *) gc->proto_data;
1067
1068 /* qq_show_packet("Check password reply", data, data_len); */
1069
1070 bytes = 0;
1071 bytes += qq_get16(&unknow_token_len, data + bytes); /* maybe total length */
1072 bytes += qq_get8(&ret, data + bytes);
1073 bytes += 4; /* 0x(00 00 6d b9) */
1074 /* unknow_token_len may 0 when not reply ok*/
1075 bytes += qq_get16(&unknow_token_len, data + bytes); /* 0x0020 */
1076 bytes += unknow_token_len;
1077 bytes += qq_get16(&unknow_token_len, data + bytes); /* 0x0020 */
1078 bytes += unknow_token_len;
1079
1080 if (ret == 0) {
1081 /* get login_token */
1082 bytes += qq_get16(&qd->ld.login_token_len, data + bytes);
1083 if (qd->ld.login_token != NULL) g_free(qd->ld.login_token);
1084 qd->ld.login_token = g_new0(guint8, qd->ld.login_token_len);
1085 bytes += qq_getdata(qd->ld.login_token, qd->ld.login_token_len, data + bytes);
1086 /* qq_show_packet("Get login token", qd->ld.login_token, qd->ld.login_token_len); */
1087
1088 /* get login_key */
1089 bytes += qq_getdata(qd->ld.login_key, sizeof(qd->ld.login_key), data + bytes);
1090 /* qq_show_packet("Get login key", qd->ld.login_key, sizeof(qd->ld.login_key)); */
1091 return QQ_LOGIN_REPLY_OK;
1092 }
1093
1094 switch (ret)
1095 {
1096 case 0x34: /* invalid password */
1097 if (!purple_account_get_remember_password(gc->account)) {
1098 purple_account_set_password(gc->account, NULL);
1099 }
1100 error = g_strdup(_("Incorrect password"));
1101 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1102 break;
1103 case 0x33: /* need activation */
1104 case 0x51: /* need activation */
1105 error = g_strdup(_("Activation required"));
1106 reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
1107 break;
1108 case 0xBF: /* uid is not exist */
1109 error = g_strdup(_("Username does not exist"));
1110 reason = PURPLE_CONNECTION_ERROR_INVALID_USERNAME;
1111 break;
1112 default:
1113 qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len,
1114 ">>> [default] decrypt and dump");
1115 error = g_strdup_printf(
1116 _("Unknown reply when checking password (0x%02X)"),
1117 ret );
1118 reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR;
1119 break;
1120 }
1121
1122 bytes += qq_get16(&msg_len, data + bytes);
1123
1124 msg = g_strndup((gchar *)data + bytes, msg_len);
1125 msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
1126
1127 purple_debug_error("QQ", "%s: %s\n", error, msg_utf8);
1128 purple_connection_error_reason(gc, reason, msg_utf8);
1129
1130 g_free(error);
1131 g_free(msg);
1132 g_free(msg_utf8);
1133 return QQ_LOGIN_REPLY_ERR;
1134 }
1135
1136 void qq_request_login_2007(PurpleConnection *gc)
1137 {
1138 qq_data *qd;
1139 guint8 *buf, *raw_data;
1140 gint bytes;
1141 guint8 *encrypted;
1142 gint encrypted_len;
1143 static const guint8 login_1_16[] = {
1144 0x56, 0x4E, 0xC8, 0xFB, 0x0A, 0x4F, 0xEF, 0xB3,
1145 0x7A, 0x5D, 0xD8, 0x86, 0x0F, 0xAC, 0xE5, 0x1A
1146 };
1147 static const guint8 login_2_16[] = {
1148 0x5E, 0x22, 0x3A, 0xBE, 0x13, 0xBF, 0xDA, 0x4C,
1149 0xA9, 0xB7, 0x0B, 0x43, 0x63, 0x51, 0x8E, 0x28
1150 };
1151 static const guint8 login_3_83[] = {
1152 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
1153 0x00, 0x00, 0x01, 0x40, 0x01, 0x01, 0x58, 0x83,
1154 0xD0, 0x00, 0x10, 0x9D, 0x14, 0x64, 0x0A, 0x2E,
1155 0xE2, 0x11, 0xF7, 0x90, 0xF0, 0xB5, 0x5F, 0x16,
1156 0xFB, 0x41, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
1157 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1159 0x00, 0x00, 0x00, 0x00, 0x02, 0x76, 0x3C, 0xEE,
1160 0x4A, 0x00, 0x10, 0x86, 0x81, 0xAD, 0x1F, 0xC8,
1161 0xC9, 0xCC, 0xCF, 0xCA, 0x9F, 0xFF, 0x88, 0xC0,
1162 0x5C, 0x88, 0xD5
1163 };
1164 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
1165 qd = (qq_data *) gc->proto_data;
1166
1167 g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
1168
1169 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
1170 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
1171
1172 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
1173
1174 /* Encrypted password and put in encrypted */
1175 bytes = 0;
1176 bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
1177 bytes += qq_put16(raw_data + bytes, 0);
1178 bytes += qq_put16(raw_data + bytes, 0xffff);
1179
1180 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
1181
1182 /* create packet */
1183 bytes = 0;
1184 bytes += qq_put16(raw_data + bytes, 0); /* Unknow */
1185 /* password encrypted */
1186 bytes += qq_put16(raw_data + bytes, encrypted_len);
1187 bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
1188 /* put data which NULL string encrypted by key pwd_twice_md5 */
1189 encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
1190 g_return_if_fail(encrypted_len == 16);
1191 bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
1192 /* unknow fill 0 */
1193 memset(raw_data + bytes, 0, 19);
1194 bytes += 19;
1195 bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16));
1196
1197 bytes += qq_put8(raw_data + bytes, rand() & 0xff);
1198 bytes += qq_put8(raw_data + bytes, qd->login_mode);
1199 /* unknow 10 bytes zero filled*/
1200 memset(raw_data + bytes, 0, 10);
1201 bytes += 10;
1202 /* redirect data, 15 bytes */
1203 /* qq_show_packet("Redirect", qd->redirect, qd->redirect_len); */
1204 bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
1205 /* unknow fill */
1206 bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16));
1207 /* captcha token get from qq_process_token_ex */
1208 bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
1209 bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
1210 /* unknow fill */
1211 bytes += qq_putdata(raw_data + bytes, login_3_83, sizeof(login_3_83));
1212 memset(raw_data + bytes, 0, 332 - sizeof(login_3_83));
1213 bytes += 332 - sizeof(login_3_83);
1214
1215 /* qq_show_packet("Login", raw_data, bytes); */
1216
1217 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
1218
1219 buf = g_newa(guint8, MAX_PACKET_SIZE);
1220 memset(buf, 0, MAX_PACKET_SIZE);
1221 bytes = 0;
1222 /* logint token get from qq_process_check_pwd_2007 */
1223 bytes += qq_put16(buf + bytes, qd->ld.login_token_len);
1224 bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len);
1225 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
1226
1227 qd->send_seq++;
1228 qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
1229 }
1230
1231 /* process the login reply packet */
1232 guint8 qq_process_login_2007( PurpleConnection *gc, guint8 *data, gint data_len)
1233 {
1234 qq_data *qd;
1235 gint bytes;
1236 guint8 ret;
1237 guint32 uid;
1238 gchar *error;
1239 gchar *msg;
1240 gchar *msg_utf8;
1241
1242 g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
1243
1244 qd = (qq_data *) gc->proto_data;
1245
1246 bytes = 0;
1247 bytes += qq_get8(&ret, data + bytes);
1248 if (ret != 0) {
1249 msg = g_strndup((gchar *)data + bytes, data_len - bytes);
1250 msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
1251 g_free(msg);
1252
1253 switch (ret) {
1254 case 0x05:
1255 purple_debug_error("QQ", "Server busy for %s\n", msg_utf8);
1256 return QQ_LOGIN_REPLY_REDIRECT;
1257 case 0x0A:
1258 /* 0a 2d 9a 4b 9a 01 01 00 00 00 05 00 00 00 00 79 0e 5f fd */
1259 /* Missing get server before login*/
1260 default:
1261 error = g_strdup_printf(
1262 _("Unknown reply code when logging in (0x%02X):\n%s"),
1263 ret, msg_utf8);
1264 break;
1265 }
1266
1267 purple_debug_error("QQ", "%s\n", error);
1268 purple_connection_error_reason(gc,
1269 PURPLE_CONNECTION_ERROR_OTHER_ERROR,
1270 error);
1271
1272 qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error);
1273
1274 g_free(error);
1275 g_free(msg_utf8);
1276 return QQ_LOGIN_REPLY_ERR;
1277 }
1278
1279 bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
1280 purple_debug_info("QQ", "Got session_key\n");
1281 get_session_md5(qd->session_md5, qd->uid, qd->session_key);
1282
1283 bytes += qq_get32(&uid, data + bytes);
1284 if (uid != qd->uid) {
1285 purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid);
1286 }
1287 bytes += qq_getIP(&qd->my_ip, data + bytes);
1288 bytes += qq_get16(&qd->my_port, data + bytes);
1289 bytes += qq_getIP(&qd->my_local_ip, data + bytes);
1290 bytes += qq_get16(&qd->my_local_port, data + bytes);
1291 bytes += qq_getime(&qd->login_time, data + bytes);
1292 /* skip unknow 50 byte */
1293 bytes += 50;
1294 /* skip client key 32 byte */
1295 bytes += 32;
1296 /* skip unknow 12 byte */
1297 bytes += 12;
1298 /* last login */
1299 bytes += qq_getIP(&qd->last_login_ip, data + bytes);
1300 bytes += qq_getime(&qd->last_login_time[0], data + bytes);
1301 purple_debug_info("QQ", "Last Login: %s, %s\n",
1302 inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0]));
1303 return QQ_LOGIN_REPLY_OK;
1304 }
1305
1306 void qq_request_login_2008(PurpleConnection *gc)
1307 {
1308 qq_data *qd;
1309 guint8 *buf, *raw_data;
1310 gint bytes;
1311 guint8 *encrypted;
1312 gint encrypted_len;
1313 guint8 index, count;
1314
1315 static const guint8 login_1_16[] = {
1316 0xD2, 0x41, 0x75, 0x12, 0xC2, 0x86, 0x57, 0x10,
1317 0x78, 0x57, 0xDC, 0x24, 0x8C, 0xAA, 0x8F, 0x4E
1318 };
1319
1320 static const guint8 login_2_16[] = {
1321 0x94, 0x0B, 0x73, 0x7A, 0xA2, 0x51, 0xF0, 0x4B,
1322 0x95, 0x2F, 0xC6, 0x0A, 0x5B, 0xF6, 0x76, 0x52
1323 };
1324 static const guint8 login_3_18[] = {
1325 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
1326 0x00, 0x00, 0x01, 0x40, 0x01, 0x1b, 0x02, 0x84,
1327 0x50, 0x00
1328 };
1329 static const guint8 login_4_16[] = {
1330 0x2D, 0x49, 0x15, 0x55, 0x78, 0xFC, 0xF3, 0xD4,
1331 0x53, 0x55, 0x60, 0x9C, 0x37, 0x9F, 0xE9, 0x59
1332 };
1333 static const guint8 login_5_6[] = {
1334 0x02, 0x68, 0xe8, 0x07, 0x83, 0x00
1335 };
1336 static const guint8 login_6_16[] = {
1337 0x3B, 0xCE, 0x43, 0xF1, 0x8B, 0xA4, 0x2B, 0xB5,
1338 0xB3, 0x51, 0x57, 0xF7, 0x06, 0x4B, 0x18, 0xFC
1339 };
1340 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
1341 qd = (qq_data *) gc->proto_data;
1342
1343 g_return_if_fail(qd->ld.token != NULL && qd->ld.token_len > 0);
1344
1345 raw_data = g_newa(guint8, MAX_PACKET_SIZE - 17);
1346 memset(raw_data, 0, MAX_PACKET_SIZE - 17);
1347
1348 encrypted = g_newa(guint8, MAX_PACKET_SIZE); /* 17 bytes more */
1349
1350 /* Encrypted password and put in encrypted */
1351 bytes = 0;
1352 bytes += qq_putdata(raw_data + bytes, qd->ld.pwd_md5, sizeof(qd->ld.pwd_md5));
1353 bytes += qq_put16(raw_data + bytes, 0);
1354 bytes += qq_put16(raw_data + bytes, 0xffff);
1355
1356 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.pwd_twice_md5);
1357
1358 /* create packet */
1359 bytes = 0;
1360 bytes += qq_put16(raw_data + bytes, 0); /* Unknow */
1361 /* password encrypted */
1362 bytes += qq_put16(raw_data + bytes, encrypted_len);
1363 bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
1364 /* put data which NULL string encrypted by key pwd_twice_md5 */
1365 encrypted_len = qq_encrypt(encrypted, (guint8 *) "", 0, qd->ld.pwd_twice_md5);
1366 g_return_if_fail(encrypted_len == 16);
1367 bytes += qq_putdata(raw_data + bytes, encrypted, encrypted_len);
1368 /* unknow 19 bytes zero filled*/
1369 memset(raw_data + bytes, 0, 19);
1370 bytes += 19;
1371 bytes += qq_putdata(raw_data + bytes, login_1_16, sizeof(login_1_16));
1372
1373 index = rand() % 3; /* can be set as 1 */
1374 for( count = 0; count < encrypted_len; count++ )
1375 index ^= encrypted[count];
1376 for( count = 0; count < sizeof(login_1_16); count++ )
1377 index ^= login_1_16[count];
1378 bytes += qq_put8(raw_data + bytes, index); /* random in QQ 2007*/
1379
1380 bytes += qq_put8(raw_data + bytes, qd->login_mode);
1381 /* unknow 10 bytes zero filled*/
1382 memset(raw_data + bytes, 0, 10);
1383 bytes += 10;
1384 /* redirect data, 15 bytes */
1385 bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
1386 /* unknow fill */
1387 bytes += qq_putdata(raw_data + bytes, login_2_16, sizeof(login_2_16));
1388 /* captcha token get from qq_process_token_ex */
1389 bytes += qq_put8(raw_data + bytes, (guint8)(qd->ld.token_ex_len & 0xff));
1390 bytes += qq_putdata(raw_data + bytes, qd->ld.token_ex, qd->ld.token_ex_len);
1391 /* unknow fill */
1392 bytes += qq_putdata(raw_data + bytes, login_3_18, sizeof(login_3_18));
1393 bytes += qq_put8(raw_data + bytes , sizeof(login_4_16));
1394 bytes += qq_putdata(raw_data + bytes, login_4_16, sizeof(login_4_16));
1395 /* unknow 10 bytes zero filled*/
1396 memset(raw_data + bytes, 0, 10);
1397 bytes += 10;
1398 /* redirect data, 15 bytes */
1399 bytes += qq_putdata(raw_data + bytes, qd->redirect, qd->redirect_len);
1400 /* unknow fill */
1401 bytes += qq_putdata(raw_data + bytes, login_5_6, sizeof(login_5_6));
1402 bytes += qq_put8(raw_data + bytes , sizeof(login_6_16));
1403 bytes += qq_putdata(raw_data + bytes, login_6_16, sizeof(login_6_16));
1404 /* unknow 249 bytes zero filled*/
1405 memset(raw_data + bytes, 0, 249);
1406 bytes += 249;
1407
1408 /* qq_show_packet("Login request", raw_data, bytes); */
1409 encrypted_len = qq_encrypt(encrypted, raw_data, bytes, qd->ld.login_key);
1410
1411 buf = g_newa(guint8, MAX_PACKET_SIZE);
1412 memset(buf, 0, MAX_PACKET_SIZE);
1413 bytes = 0;
1414 /* logint token get from qq_process_check_pwd_2007 */
1415 bytes += qq_put16(buf + bytes, qd->ld.login_token_len);
1416 bytes += qq_putdata(buf + bytes, qd->ld.login_token, qd->ld.login_token_len);
1417 bytes += qq_putdata(buf + bytes, encrypted, encrypted_len);
1418
1419 qd->send_seq++;
1420 qq_send_cmd_encrypted(gc, QQ_CMD_LOGIN, qd->send_seq, buf, bytes, TRUE);
1421 }
1422
1423 guint8 qq_process_login_2008( PurpleConnection *gc, guint8 *data, gint data_len)
1424 {
1425 qq_data *qd;
1426 gint bytes;
1427 guint8 ret;
1428 guint32 uid;
1429 gchar *error;
1430 gchar *msg;
1431 gchar *msg_utf8;
1432
1433 g_return_val_if_fail(data != NULL && data_len != 0, QQ_LOGIN_REPLY_ERR);
1434
1435 qd = (qq_data *) gc->proto_data;
1436
1437 bytes = 0;
1438 bytes += qq_get8(&ret, data + bytes);
1439 if (ret != 0) {
1440 msg = g_strndup((gchar *)data + bytes, data_len - bytes);
1441 msg_utf8 = qq_to_utf8(msg, QQ_CHARSET_DEFAULT);
1442 g_free(msg);
1443
1444 switch (ret) {
1445 case 0x05:
1446 purple_debug_error("QQ", "Server busy for %s\n", msg_utf8);
1447 return QQ_LOGIN_REPLY_REDIRECT;
1448 break;
1449 default:
1450 error = g_strdup_printf(
1451 _("Unknown reply code when logging in (0x%02X):\n%s"),
1452 ret, msg_utf8);
1453 break;
1454 }
1455
1456 purple_debug_error("QQ", "%s\n", error);
1457 purple_connection_error_reason(gc,
1458 PURPLE_CONNECTION_ERROR_OTHER_ERROR,
1459 error);
1460
1461 qq_hex_dump(PURPLE_DEBUG_WARNING, "QQ", data, data_len, error);
1462
1463 g_free(error);
1464 g_free(msg_utf8);
1465 return QQ_LOGIN_REPLY_ERR;
1466 }
1467
1468 bytes += qq_getdata(qd->session_key, sizeof(qd->session_key), data + bytes);
1469 purple_debug_info("QQ", "Got session_key\n");
1470 get_session_md5(qd->session_md5, qd->uid, qd->session_key);
1471
1472 bytes += qq_get32(&uid, data + bytes);
1473 if (uid != qd->uid) {
1474 purple_debug_warning("QQ", "My uid in login reply is %u, not %u\n", uid, qd->uid);
1475 }
1476 bytes += qq_getIP(&qd->my_ip, data + bytes);
1477 bytes += qq_get16(&qd->my_port, data + bytes);
1478 bytes += qq_getIP(&qd->my_local_ip, data + bytes);
1479 bytes += qq_get16(&qd->my_local_port, data + bytes);
1480 bytes += qq_getime(&qd->login_time, data + bytes);
1481 /* skip 1 byte, always 0x03 */
1482 /* skip 1 byte, login mode */
1483 bytes = 131;
1484 bytes += qq_getIP(&qd->last_login_ip, data + bytes);
1485 bytes += qq_getime(&qd->last_login_time[0], data + bytes);
1486 purple_debug_info("QQ", "Last Login: %s, %s\n",
1487 inet_ntoa(qd->last_login_ip), ctime(&qd->last_login_time[0]));
1488 return QQ_LOGIN_REPLY_OK;
1489 }

mercurial