libpurple/protocols/qq/qq.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.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
27 #include "accountopt.h"
28 #include "debug.h"
29 #include "notify.h"
30 #include "prefs.h"
31 #include "prpl.h"
32 #include "privacy.h"
33 #include "request.h"
34 #include "roomlist.h"
35 #include "server.h"
36 #include "util.h"
37
38 #include "buddy_info.h"
39 #include "buddy_memo.h"
40 #include "buddy_opt.h"
41 #include "buddy_list.h"
42 #include "char_conv.h"
43 #include "group.h"
44 #include "group_im.h"
45 #include "group_info.h"
46 #include "group_join.h"
47 #include "group_opt.h"
48 #include "group_internal.h"
49 #include "qq_define.h"
50 #include "im.h"
51 #include "qq_process.h"
52 #include "qq_base.h"
53 #include "packet_parse.h"
54 #include "qq.h"
55 #include "qq_network.h"
56 #include "send_file.h"
57 #include "utils.h"
58 #include "version.h"
59
60 #define OPENQ_VERSION "0.3.2-p20"
61
62 static GList *server_list_build(gchar select)
63 {
64 GList *list = NULL;
65
66 if ( select == 'T' || select == 'A') {
67 list = g_list_append(list, "tcpconn.tencent.com:8000");
68 list = g_list_append(list, "tcpconn2.tencent.com:8000");
69 list = g_list_append(list, "tcpconn3.tencent.com:8000");
70 list = g_list_append(list, "tcpconn4.tencent.com:8000");
71 list = g_list_append(list, "tcpconn5.tencent.com:8000");
72 list = g_list_append(list, "tcpconn6.tencent.com:8000");
73 }
74 if ( select == 'U' || select == 'A') {
75 list = g_list_append(list, "sz.tencent.com:8000");
76 list = g_list_append(list, "sz2.tencent.com:8000");
77 list = g_list_append(list, "sz3.tencent.com:8000");
78 list = g_list_append(list, "sz4.tencent.com:8000");
79 list = g_list_append(list, "sz5.tencent.com:8000");
80 list = g_list_append(list, "sz6.tencent.com:8000");
81 list = g_list_append(list, "sz7.tencent.com:8000");
82 list = g_list_append(list, "sz8.tencent.com:8000");
83 list = g_list_append(list, "sz9.tencent.com:8000");
84 }
85 return list;
86 }
87
88 static void server_list_create(PurpleAccount *account)
89 {
90 PurpleConnection *gc;
91 qq_data *qd;
92 PurpleProxyInfo *gpi;
93 const gchar *custom_server;
94
95 gc = purple_account_get_connection(account);
96 g_return_if_fail(gc != NULL && gc->proto_data != NULL);
97 qd = gc->proto_data;
98
99 gpi = purple_proxy_get_setup(account);
100
101 qd->use_tcp = purple_account_get_bool(account, "use_tcp", TRUE);
102
103 custom_server = purple_account_get_string(account, "server", NULL);
104
105 if (custom_server != NULL) {
106 purple_debug_info("QQ", "Select server '%s'\n", custom_server);
107 if (*custom_server != '\0' && g_ascii_strcasecmp(custom_server, "auto") != 0) {
108 qd->servers = g_list_append(qd->servers, g_strdup(custom_server));
109 return;
110 }
111 }
112
113 if (qd->use_tcp) {
114 qd->servers = server_list_build('T');
115 return;
116 }
117
118 qd->servers = server_list_build('U');
119 }
120
121 static void server_list_remove_all(qq_data *qd)
122 {
123 g_return_if_fail(qd != NULL);
124
125 purple_debug_info("QQ", "free server list\n");
126 g_list_free(qd->servers);
127 qd->curr_server = NULL;
128 }
129
130 static void qq_login(PurpleAccount *account)
131 {
132 PurpleConnection *gc;
133 qq_data *qd;
134 PurplePresence *presence;
135 const gchar *version_str;
136
137 g_return_if_fail(account != NULL);
138
139 gc = purple_account_get_connection(account);
140 g_return_if_fail(gc != NULL);
141
142 gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_AUTO_RESP;
143
144 qd = g_new0(qq_data, 1);
145 memset(qd, 0, sizeof(qq_data));
146 qd->gc = gc;
147 gc->proto_data = qd;
148
149 presence = purple_account_get_presence(account);
150 if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_INVISIBLE)) {
151 qd->login_mode = QQ_LOGIN_MODE_HIDDEN;
152 } else if(purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_AWAY)
153 || purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_EXTENDED_AWAY)) {
154 qd->login_mode = QQ_LOGIN_MODE_AWAY;
155 } else {
156 qd->login_mode = QQ_LOGIN_MODE_NORMAL;
157 }
158
159 server_list_create(account);
160 purple_debug_info("QQ", "Server list has %d\n", g_list_length(qd->servers));
161
162 version_str = purple_account_get_string(account, "client_version", NULL);
163 qd->client_tag = QQ_CLIENT_0D55; /* set default as QQ2005 */
164 qd->client_version = 2005;
165 if (version_str != NULL && strlen(version_str) != 0) {
166 if (strcmp(version_str, "qq2007") == 0) {
167 qd->client_tag = QQ_CLIENT_111D;
168 qd->client_version = 2007;
169 } else if (strcmp(version_str, "qq2008") == 0) {
170 qd->client_tag = QQ_CLIENT_115B;
171 qd->client_version = 2008;
172 }
173 }
174
175 qd->is_show_notice = purple_account_get_bool(account, "show_notice", TRUE);
176 qd->is_show_news = purple_account_get_bool(account, "show_news", TRUE);
177 qd->is_show_chat = purple_account_get_bool(account, "show_chat", TRUE);
178
179 qd->resend_times = purple_prefs_get_int("/plugins/prpl/qq/resend_times");
180 if (qd->resend_times <= 1) qd->itv_config.resend = 4;
181
182 qd->itv_config.resend = purple_prefs_get_int("/plugins/prpl/qq/resend_interval");
183 if (qd->itv_config.resend <= 0) qd->itv_config.resend = 3;
184 purple_debug_info("QQ", "Resend interval %d, retries %d\n",
185 qd->itv_config.resend, qd->resend_times);
186
187 qd->itv_config.keep_alive = purple_account_get_int(account, "keep_alive_interval", 60);
188 if (qd->itv_config.keep_alive < 30) qd->itv_config.keep_alive = 30;
189 qd->itv_config.keep_alive /= qd->itv_config.resend;
190 qd->itv_count.keep_alive = qd->itv_config.keep_alive;
191
192 qd->itv_config.update = purple_account_get_int(account, "update_interval", 300);
193 if (qd->itv_config.update > 0) {
194 if (qd->itv_config.update < qd->itv_config.keep_alive) {
195 qd->itv_config.update = qd->itv_config.keep_alive;
196 }
197 qd->itv_config.update /= qd->itv_config.resend;
198 qd->itv_count.update = qd->itv_config.update;
199 } else {
200 qd->itv_config.update = 0;
201 }
202
203 qd->connect_watcher = purple_timeout_add_seconds(0, qq_connect_later, gc);
204 }
205
206 /* clean up the given QQ connection and free all resources */
207 static void qq_close(PurpleConnection *gc)
208 {
209 qq_data *qd;
210
211 g_return_if_fail(gc != NULL && gc->proto_data);
212 qd = gc->proto_data;
213
214 if (qd->check_watcher > 0) {
215 purple_timeout_remove(qd->check_watcher);
216 qd->check_watcher = 0;
217 }
218
219 if (qd->connect_watcher > 0) {
220 purple_timeout_remove(qd->connect_watcher);
221 qd->connect_watcher = 0;
222 }
223
224 qq_disconnect(gc);
225
226 if (qd->redirect) g_free(qd->redirect);
227 if (qd->ld.token) g_free(qd->ld.token);
228 if (qd->ld.token_ex) g_free(qd->ld.token_ex);
229 if (qd->captcha.token) g_free(qd->captcha.token);
230 if (qd->captcha.data) g_free(qd->captcha.data);
231
232 server_list_remove_all(qd);
233
234 g_free(qd);
235 gc->proto_data = NULL;
236 }
237
238 /* returns the icon name for a buddy or protocol */
239 static const gchar *qq_list_icon(PurpleAccount *a, PurpleBuddy *b)
240 {
241 return "qq";
242 }
243
244
245 /* a short status text beside buddy icon*/
246 static gchar *qq_status_text(PurpleBuddy *b)
247 {
248 qq_buddy_data *bd;
249 GString *status;
250
251 bd = purple_buddy_get_protocol_data(b);
252 if (bd == NULL)
253 return NULL;
254
255 status = g_string_new("");
256
257 switch(bd->status) {
258 case QQ_BUDDY_OFFLINE:
259 g_string_append(status, _("Offline"));
260 break;
261 case QQ_BUDDY_ONLINE_NORMAL:
262 g_string_append(status, _("Online"));
263 break;
264 /* TODO What does this status mean? Labelling it as offline... */
265 case QQ_BUDDY_CHANGE_TO_OFFLINE:
266 g_string_append(status, _("Offline"));
267 break;
268 case QQ_BUDDY_ONLINE_AWAY:
269 g_string_append(status, _("Away"));
270 break;
271 case QQ_BUDDY_ONLINE_INVISIBLE:
272 g_string_append(status, _("Invisible"));
273 break;
274 case QQ_BUDDY_ONLINE_BUSY:
275 g_string_append(status, _("Busy"));
276 break;
277 default:
278 g_string_printf(status, _("Unknown-%d"), bd->status);
279 }
280
281 return g_string_free(status, FALSE);
282 }
283
284
285 /* a floating text when mouse is on the icon, show connection status here */
286 static void qq_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
287 {
288 qq_buddy_data *bd;
289 gchar *tmp;
290 GString *str;
291
292 g_return_if_fail(b != NULL);
293
294 bd = purple_buddy_get_protocol_data(b);
295 if (bd == NULL)
296 return;
297
298 /* if (PURPLE_BUDDY_IS_ONLINE(b) && bd != NULL) */
299 if (bd->ip.s_addr != 0) {
300 str = g_string_new(NULL);
301 g_string_printf(str, "%s:%d", inet_ntoa(bd->ip), bd->port);
302 if (bd->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
303 g_string_append(str, " TCP");
304 } else {
305 g_string_append(str, " UDP");
306 }
307 g_string_free(str, TRUE);
308 }
309
310 tmp = g_strdup_printf("%d", bd->age);
311 purple_notify_user_info_add_pair(user_info, _("Age"), tmp);
312 g_free(tmp);
313
314 switch (bd->gender) {
315 case QQ_BUDDY_GENDER_GG:
316 purple_notify_user_info_add_pair(user_info, _("Gender"), _("Male"));
317 break;
318 case QQ_BUDDY_GENDER_MM:
319 purple_notify_user_info_add_pair(user_info, _("Gender"), _("Female"));
320 break;
321 case QQ_BUDDY_GENDER_UNKNOWN:
322 purple_notify_user_info_add_pair(user_info, _("Gender"), _("Unknown"));
323 break;
324 default:
325 tmp = g_strdup_printf("Error (%d)", bd->gender);
326 purple_notify_user_info_add_pair(user_info, _("Gender"), tmp);
327 g_free(tmp);
328 }
329
330 if (bd->level) {
331 tmp = g_strdup_printf("%d", bd->level);
332 purple_notify_user_info_add_pair(user_info, _("Level"), tmp);
333 g_free(tmp);
334 }
335
336 str = g_string_new(NULL);
337 if (bd->comm_flag & QQ_COMM_FLAG_QQ_MEMBER) {
338 g_string_append( str, _("Member") );
339 }
340 if (bd->comm_flag & QQ_COMM_FLAG_QQ_VIP) {
341 g_string_append( str, _(" VIP") );
342 }
343 if (bd->comm_flag & QQ_COMM_FLAG_TCP_MODE) {
344 g_string_append( str, _(" TCP") );
345 }
346 if (bd->comm_flag & QQ_COMM_FLAG_MOBILE) {
347 g_string_append( str, _(" FromMobile") );
348 }
349 if (bd->comm_flag & QQ_COMM_FLAG_BIND_MOBILE) {
350 g_string_append( str, _(" BindMobile") );
351 }
352 if (bd->comm_flag & QQ_COMM_FLAG_VIDEO) {
353 g_string_append( str, _(" Video") );
354 }
355
356 if (bd->ext_flag & QQ_EXT_FLAG_ZONE) {
357 g_string_append( str, _(" Zone") );
358 }
359 purple_notify_user_info_add_pair(user_info, _("Flag"), str->str);
360
361 g_string_free(str, TRUE);
362
363 #ifdef DEBUG
364 tmp = g_strdup_printf( "%s (%04X)",
365 qq_get_ver_desc(bd->client_tag),
366 bd->client_tag );
367 purple_notify_user_info_add_pair(user_info, _("Ver"), tmp);
368 g_free(tmp);
369
370 tmp = g_strdup_printf( "Ext 0x%X, Comm 0x%X",
371 bd->ext_flag, bd->comm_flag );
372 purple_notify_user_info_add_pair(user_info, _("Flag"), tmp);
373 g_free(tmp);
374 #endif
375 }
376
377 /* we can show tiny icons on the four corners of buddy icon, */
378 static const char *qq_list_emblem(PurpleBuddy *b)
379 {
380 PurpleAccount *account;
381 PurpleConnection *gc;
382 qq_data *qd;
383 qq_buddy_data *buddy;
384
385 if (!b || !(account = purple_buddy_get_account(b)) ||
386 !(gc = purple_account_get_connection(account)) ||
387 !(qd = purple_connection_get_protocol_data(gc)))
388 return NULL;
389
390 buddy = purple_buddy_get_protocol_data(b);
391 if (!buddy) {
392 return "not-authorized";
393 }
394
395 if (buddy->comm_flag & QQ_COMM_FLAG_MOBILE)
396 return "mobile";
397 if (buddy->comm_flag & QQ_COMM_FLAG_VIDEO)
398 return "video";
399 if (buddy->comm_flag & QQ_COMM_FLAG_QQ_MEMBER)
400 return "qq_member";
401
402 return NULL;
403 }
404
405 /* QQ away status (used to initiate QQ away packet) */
406 static GList *qq_status_types(PurpleAccount *ga)
407 {
408 PurpleStatusType *status;
409 GList *types = NULL;
410
411 status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE,
412 "available", _("Available"), TRUE, TRUE, FALSE);
413 types = g_list_append(types, status);
414
415 status = purple_status_type_new_full(PURPLE_STATUS_AWAY,
416 "away", _("Away"), TRUE, TRUE, FALSE);
417 types = g_list_append(types, status);
418
419 status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE,
420 "invisible", _("Invisible"), TRUE, TRUE, FALSE);
421 types = g_list_append(types, status);
422
423 status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE,
424 "busy", _("Busy"), TRUE, TRUE, FALSE);
425 types = g_list_append(types, status);
426
427 status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
428 "offline", _("Offline"), TRUE, TRUE, FALSE);
429 types = g_list_append(types, status);
430
431 status = purple_status_type_new_full(PURPLE_STATUS_MOBILE,
432 "mobile", NULL, FALSE, FALSE, TRUE);
433 types = g_list_append(types, status);
434
435 return types;
436 }
437
438 /* initiate QQ away with proper change_status packet */
439 static void qq_change_status(PurpleAccount *account, PurpleStatus *status)
440 {
441 PurpleConnection *gc = purple_account_get_connection(account);
442
443 qq_request_change_status(gc, 0);
444 }
445
446 /* send packet to get who's detailed information */
447 static void qq_show_buddy_info(PurpleConnection *gc, const gchar *who)
448 {
449 guint32 uid;
450 qq_data *qd;
451
452 qd = gc->proto_data;
453 uid = purple_name_to_uid(who);
454
455 if (uid <= 0) {
456 purple_debug_error("QQ", "Not valid QQid: %s\n", who);
457 purple_notify_error(gc, NULL, _("Invalid name"), NULL);
458 return;
459 }
460
461 if (qd->client_version >= 2007) {
462 qq_request_get_level_2007(gc, uid);
463 } else {
464 qq_request_get_level(gc, uid);
465 }
466 qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
467 }
468
469 static void action_update_all_rooms(PurplePluginAction *action)
470 {
471 PurpleConnection *gc = (PurpleConnection *) action->context;
472 qq_data *qd;
473
474 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
475 qd = (qq_data *) gc->proto_data;
476
477 if ( !qd->is_login ) {
478 return;
479 }
480
481 qq_update_all_rooms(gc, 0, 0);
482 }
483
484 static void action_change_icon(PurplePluginAction *action)
485 {
486 PurpleConnection *gc = (PurpleConnection *) action->context;
487 qq_data *qd;
488 gchar *icon_name;
489 gchar *icon_path;
490
491 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
492 qd = (qq_data *) gc->proto_data;
493
494 if ( !qd->is_login ) {
495 return;
496 }
497
498 icon_name = qq_get_icon_name(qd->my_icon);
499 icon_path = qq_get_icon_path(icon_name);
500 g_free(icon_name);
501
502 purple_debug_info("QQ", "Change prev icon %s to...\n", icon_path);
503 purple_request_file(action, _("Select icon..."), icon_path,
504 FALSE,
505 G_CALLBACK(qq_change_icon_cb), NULL,
506 purple_connection_get_account(gc), NULL, NULL,
507 gc);
508 g_free(icon_path);
509 }
510
511 static void action_modify_info_base(PurplePluginAction *action)
512 {
513 PurpleConnection *gc = (PurpleConnection *) action->context;
514 qq_data *qd;
515
516 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
517 qd = (qq_data *) gc->proto_data;
518 qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_BASE);
519 }
520
521 static void action_modify_info_ext(PurplePluginAction *action)
522 {
523 PurpleConnection *gc = (PurpleConnection *) action->context;
524 qq_data *qd;
525
526 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
527 qd = (qq_data *) gc->proto_data;
528 qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_EXT);
529 }
530
531 static void action_modify_info_addr(PurplePluginAction *action)
532 {
533 PurpleConnection *gc = (PurpleConnection *) action->context;
534 qq_data *qd;
535
536 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
537 qd = (qq_data *) gc->proto_data;
538 qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_ADDR);
539 }
540
541 static void action_modify_info_contact(PurplePluginAction *action)
542 {
543 PurpleConnection *gc = (PurpleConnection *) action->context;
544 qq_data *qd;
545
546 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
547 qd = (qq_data *) gc->proto_data;
548 qq_request_buddy_info(gc, qd->uid, 0, QQ_BUDDY_INFO_MODIFY_CONTACT);
549 }
550
551 static void action_change_password(PurplePluginAction *action)
552 {
553 PurpleConnection *gc = (PurpleConnection *) action->context;
554
555 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
556 purple_notify_uri(NULL, "https://password.qq.com");
557 }
558
559 /* show a brief summary of what we get from login packet */
560 static void action_show_account_info(PurplePluginAction *action)
561 {
562 PurpleConnection *gc = (PurpleConnection *) action->context;
563 qq_data *qd;
564 GString *info;
565 struct tm *tm_local;
566 int index;
567
568 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
569 qd = (qq_data *) gc->proto_data;
570 info = g_string_new("<html><body>");
571
572 tm_local = localtime(&qd->login_time);
573 g_string_append_printf(info, _("<b>Login time</b>: %d-%d-%d, %d:%d:%d<br>\n"),
574 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
575 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
576 g_string_append_printf(info, _("<b>Total Online Buddies</b>: %d<br>\n"), qd->online_total);
577 tm_local = localtime(&qd->online_last_update);
578 g_string_append_printf(info, _("<b>Last Refresh</b>: %d-%d-%d, %d:%d:%d<br>\n"),
579 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
580 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
581
582 g_string_append(info, "<hr>");
583
584 g_string_append_printf(info, _("<b>Server</b>: %s<br>\n"), qd->curr_server);
585 g_string_append_printf(info, _("<b>Client Tag</b>: %s<br>\n"), qq_get_ver_desc(qd->client_tag));
586 g_string_append_printf(info, _("<b>Connection Mode</b>: %s<br>\n"), qd->use_tcp ? "TCP" : "UDP");
587 g_string_append_printf(info, _("<b>My Internet IP</b>: %s:%d<br>\n"), inet_ntoa(qd->my_ip), qd->my_port);
588
589 g_string_append(info, "<hr>");
590 g_string_append(info, "<i>Network Status</i><br>\n");
591 g_string_append_printf(info, _("<b>Sent</b>: %lu<br>\n"), qd->net_stat.sent);
592 g_string_append_printf(info, _("<b>Resend</b>: %lu<br>\n"), qd->net_stat.resend);
593 g_string_append_printf(info, _("<b>Lost</b>: %lu<br>\n"), qd->net_stat.lost);
594 g_string_append_printf(info, _("<b>Received</b>: %lu<br>\n"), qd->net_stat.rcved);
595 g_string_append_printf(info, _("<b>Received Duplicate</b>: %lu<br>\n"), qd->net_stat.rcved_dup);
596
597 g_string_append(info, "<hr>");
598 g_string_append(info, "<i>Last Login Information</i><br>\n");
599
600 for (index = 0; index < sizeof(qd->last_login_time) / sizeof(time_t); index++) {
601 tm_local = localtime(&qd->last_login_time[index]);
602 g_string_append_printf(info, _("<b>Time</b>: %d-%d-%d, %d:%d:%d<br>\n"),
603 (1900 +tm_local->tm_year), (1 + tm_local->tm_mon), tm_local->tm_mday,
604 tm_local->tm_hour, tm_local->tm_min, tm_local->tm_sec);
605 }
606 if (qd->last_login_ip.s_addr != 0) {
607 g_string_append_printf(info, _("<b>IP</b>: %s<br>\n"), inet_ntoa(qd->last_login_ip));
608 }
609
610 g_string_append(info, "</body></html>");
611
612 purple_notify_formatted(gc, NULL, _("Login Information"), NULL, info->str, NULL, NULL);
613
614 g_string_free(info, TRUE);
615 }
616
617 static void action_about_openq(PurplePluginAction *action)
618 {
619 PurpleConnection *gc = (PurpleConnection *) action->context;
620 qq_data *qd;
621 GString *info;
622 gchar *title;
623
624 g_return_if_fail(NULL != gc && NULL != gc->proto_data);
625 qd = (qq_data *) gc->proto_data;
626
627 info = g_string_new("<html><body>");
628 g_string_append(info, _("<p><b>Original Author</b>:<br>\n"));
629 g_string_append(info, "puzzlebird<br>\n");
630 g_string_append(info, "<br>\n");
631
632 g_string_append(info, _("<p><b>Code Contributors</b>:<br>\n"));
633 g_string_append(info, "gfhuang(poppyer) : patches for libpurple 2.0.0beta2, maintainer<br>\n");
634 g_string_append(info, "Yuan Qingyun : patches for libpurple 1.5.0, maintainer<br>\n");
635 g_string_append(info, "henryouly : file transfer, udp sock5 proxy and qq_show, maintainer<br>\n");
636 g_string_append(info, "hzhr : maintainer<br>\n");
637 g_string_append(info, "joymarquis : maintainer<br>\n");
638 g_string_append(info, "arfankai : fixed bugs in char_conv.c<br>\n");
639 g_string_append(info, "rakescar : provided filter for HTML tag<br>\n");
640 g_string_append(info, "yyw : improved performance on PPC linux<br>\n");
641 g_string_append(info, "lvxiang : provided ip to location original code<br>\n");
642 g_string_append(info, "markhuetsch : OpenQ merge into libpurple, maintainer 2006-2007<br>\n");
643 g_string_append(info, "ccpaging : maintainer since 2007<br>\n");
644 g_string_append(info, "icesky : maintainer since 2007<br>\n");
645 g_string_append(info, "csyfek : faces, maintainer since 2007<br>\n");
646 g_string_append(info, "<br>\n");
647
648 g_string_append(info, _("<p><b>Lovely Patch Writers</b>:<br>\n"));
649 g_string_append(info, "gnap : message displaying, documentation<br>\n");
650 g_string_append(info, "manphiz : qun processing<br>\n");
651 g_string_append(info, "moo : qun processing<br>\n");
652 g_string_append(info, "Coly Li : qun processing<br>\n");
653 g_string_append(info, "Emil Alexiev : captcha verification on login based on LumaQQ for MAC (2007), login, add buddy, remove buddy, message exchange and logout<br>\n");
654 g_string_append(info, "Chengming Wang : buddy memo<br>\n");
655 g_string_append(info, "lonicerae : chat room window bugfix, server list bugfix, buddy memo<br>\n");
656 g_string_append(info, "<br>\n");
657
658 g_string_append(info, _("<p><b>Acknowledgement</b>:<br>\n"));
659 g_string_append(info, "Shufeng Tan : http://sf.net/projects/perl-oicq<br>\n");
660 g_string_append(info, "Jeff Ye : http://www.sinomac.com<br>\n");
661 g_string_append(info, "Hu Zheng : http://forlinux.yeah.net<br>\n");
662 g_string_append(info, "yunfan : http://www.myswear.net<br>\n");
663 g_string_append(info, "OpenQ Team : http://openq.linuxsir.org<br>\n");
664 g_string_append(info, "LumaQQ Team : http://lumaqq.linuxsir.org<br>\n");
665 g_string_append(info, "Pidgin Team : http://www.pidgin.im<br>\n");
666 g_string_append(info, "Huang Guan : http://home.xxsyzx.com<br>\n");
667 g_string_append(info, "OpenQ Google Group : http://groups.google.com/group/openq<br>\n");
668 g_string_append(info, "<br>\n");
669
670 g_string_append(info, _("<p><b>Scrupulous Testers</b>:<br>\n"));
671 g_string_append(info, "yegle<br>\n");
672 g_string_append(info, "cnzhangbx<br>\n");
673 g_string_append(info, "casparant<br>\n");
674 g_string_append(info, "wd<br>\n");
675 g_string_append(info, "x6719620<br>\n");
676 g_string_append(info, "netelk<br>\n");
677 g_string_append(info, _("and more, please let me know... thank you!))"));
678 g_string_append(info, "<br>\n<br>\n");
679 g_string_append(info, _("<p><i>And, all the boys in the backroom...</i><br>\n"));
680 g_string_append(info, _("<i>Feel free to join us!</i> :)"));
681 g_string_append(info, "</body></html>");
682
683 title = g_strdup_printf(_("About OpenQ %s"), OPENQ_VERSION);
684 purple_notify_formatted(gc, title, title, NULL, info->str, NULL, NULL);
685
686 g_free(title);
687 g_string_free(info, TRUE);
688 }
689
690 /*
691 static void _qq_menu_search_or_add_permanent_group(PurplePluginAction *action)
692 {
693 purple_roomlist_show_with_account(NULL);
694 }
695 */
696
697 /*
698 static void _qq_menu_create_permanent_group(PurplePluginAction * action)
699 {
700 PurpleConnection *gc = (PurpleConnection *) action->context;
701 purple_request_input(gc, _("Create QQ Qun"),
702 _("Input Qun name here"),
703 _("Only QQ members can create permanent Qun"),
704 "OpenQ", FALSE, FALSE, NULL,
705 _("Create"), G_CALLBACK(qq_create_room), _("Cancel"), NULL, gc);
706 }
707 */
708
709 static void action_chat_quit(PurpleBlistNode * node)
710 {
711 PurpleChat *chat = (PurpleChat *)node;
712 PurpleAccount *account = purple_chat_get_account(chat);
713 PurpleConnection *gc = purple_account_get_connection(account);
714 GHashTable *components = purple_chat_get_components(chat);
715 gchar *num_str;
716 guint32 room_id;
717
718 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
719
720 g_return_if_fail(components != NULL);
721
722 num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
723 room_id = strtoul(num_str, NULL, 10);
724 g_return_if_fail(room_id != 0);
725
726 qq_room_quit(gc, room_id);
727 }
728
729 static void action_chat_get_info(PurpleBlistNode * node)
730 {
731 PurpleChat *chat = (PurpleChat *)node;
732 PurpleAccount *account = purple_chat_get_account(chat);
733 PurpleConnection *gc = purple_account_get_connection(account);
734 GHashTable *components = purple_chat_get_components(chat);
735 gchar *num_str;
736 guint32 room_id;
737
738 g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node));
739
740 g_return_if_fail(components != NULL);
741
742 num_str = g_hash_table_lookup(components, QQ_ROOM_KEY_INTERNAL_ID);
743 room_id = strtoul(num_str, NULL, 10);
744 g_return_if_fail(room_id != 0);
745
746 qq_send_room_cmd_mess(gc, QQ_ROOM_CMD_GET_INFO, room_id, NULL, 0,
747 0, QQ_ROOM_INFO_DISPLAY);
748 }
749
750 #if 0
751 /* TODO: re-enable this */
752 static void _qq_menu_send_file(PurpleBlistNode * node, gpointer ignored)
753 {
754 PurpleBuddy *buddy;
755 PurpleConnection *gc;
756 qq_buddy_data *bd;
757
758 g_return_if_fail (PURPLE_BLIST_NODE_IS_BUDDY (node));
759 buddy = (PurpleBuddy *) node;
760 bd = (qq_buddy_data *) buddy->proto_data;
761 /* if (is_online (bd->status)) { */
762 gc = purple_account_get_connection (buddy->account);
763 g_return_if_fail (gc != NULL && gc->proto_data != NULL);
764 qq_send_file(gc, buddy->name, NULL);
765 /* } */
766 }
767 #endif
768
769 /* protocol related menus */
770 static GList *qq_actions(PurplePlugin *plugin, gpointer context)
771 {
772 GList *m;
773 PurplePluginAction *act;
774
775 m = NULL;
776 act = purple_plugin_action_new(_("Change Icon"), action_change_icon);
777 m = g_list_append(m, act);
778
779 act = purple_plugin_action_new(_("Modify Information"), action_modify_info_base);
780 m = g_list_append(m, act);
781
782 act = purple_plugin_action_new(_("Modify Extended Information"), action_modify_info_ext);
783 m = g_list_append(m, act);
784
785 act = purple_plugin_action_new(_("Modify Address"), action_modify_info_addr);
786 m = g_list_append(m, act);
787
788 act = purple_plugin_action_new(_("Modify Contact"), action_modify_info_contact);
789 m = g_list_append(m, act);
790
791 act = purple_plugin_action_new(_("Change Password"), action_change_password);
792 m = g_list_append(m, act);
793
794 act = purple_plugin_action_new(_("Account Information"), action_show_account_info);
795 m = g_list_append(m, act);
796
797 act = purple_plugin_action_new(_("Update all QQ Quns"), action_update_all_rooms);
798 m = g_list_append(m, act);
799
800 act = purple_plugin_action_new(_("About OpenQ"), action_about_openq);
801 m = g_list_append(m, act);
802 /*
803 act = purple_plugin_action_new(_("Qun: Search a permanent Qun"), _qq_menu_search_or_add_permanent_group);
804 m = g_list_append(m, act);
805
806 act = purple_plugin_action_new(_("Qun: Create a permanent Qun"), _qq_menu_create_permanent_group);
807 m = g_list_append(m, act);
808 */
809
810 return m;
811 }
812
813 static void qq_add_buddy_from_menu_cb(PurpleBlistNode *node, gpointer data)
814 {
815 PurpleBuddy *buddy;
816 PurpleConnection *gc;
817
818 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
819
820 buddy = (PurpleBuddy *) node;
821 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
822
823 qq_add_buddy(gc, buddy, NULL);
824 }
825
826 static void qq_modify_buddy_memo_from_menu_cb(PurpleBlistNode *node, gpointer data)
827 {
828 PurpleBuddy *buddy;
829 qq_buddy_data *bd;
830 PurpleConnection *gc;
831 guint32 bd_uid;
832
833 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
834
835 buddy = (PurpleBuddy *)node;
836 g_return_if_fail(NULL != buddy);
837
838 gc = purple_account_get_connection(purple_buddy_get_account(buddy));
839 g_return_if_fail(NULL != gc);
840
841 bd = (qq_buddy_data *)purple_buddy_get_protocol_data(buddy);
842 g_return_if_fail(NULL != bd);
843 bd_uid = bd->uid;
844
845 /* param: gc, uid, update_class, action
846 * here, update_class is set to bd_uid. because some memo packages returned
847 * without uid, which will make us confused */
848 qq_request_buddy_memo(gc, bd_uid, bd_uid, QQ_BUDDY_MEMO_MODIFY);
849 }
850
851 static GList *qq_buddy_menu(PurpleBuddy *buddy)
852 {
853 GList *m = NULL;
854 PurpleMenuAction *act;
855 qq_buddy_data *bd = purple_buddy_get_protocol_data(buddy);
856
857 if (bd == NULL) {
858 act = purple_menu_action_new(_("Add Buddy"),
859 PURPLE_CALLBACK(qq_add_buddy_from_menu_cb),
860 NULL, NULL);
861 m = g_list_append(m, act);
862 return m;
863 }
864
865
866 act = purple_menu_action_new(_("Modify Buddy Memo"),
867 PURPLE_CALLBACK(qq_modify_buddy_memo_from_menu_cb),
868 NULL, NULL);
869 m = g_list_append(m, act);
870
871
872 /* TODO : not working, temp commented out by gfhuang */
873 #if 0
874 if (bd && is_online(bd->status)) {
875 act = purple_menu_action_new(_("Send File"), PURPLE_CALLBACK(_qq_menu_send_file), NULL, NULL); /* add NULL by gfhuang */
876 m = g_list_append(m, act);
877 }
878 #endif
879 return m;
880 }
881
882 /* chat-related (QQ Qun) menu shown up with right-click */
883 static GList *qq_chat_menu(PurpleBlistNode *node)
884 {
885 GList *m;
886 PurpleMenuAction *act;
887
888 m = NULL;
889 act = purple_menu_action_new(_("Get Info"), PURPLE_CALLBACK(action_chat_get_info), NULL, NULL);
890 m = g_list_append(m, act);
891
892 act = purple_menu_action_new(_("Quit Qun"), PURPLE_CALLBACK(action_chat_quit), NULL, NULL);
893 m = g_list_append(m, act);
894 return m;
895 }
896
897 /* buddy-related menu shown up with right-click */
898 static GList *qq_blist_node_menu(PurpleBlistNode * node)
899 {
900 if(PURPLE_BLIST_NODE_IS_CHAT(node))
901 return qq_chat_menu(node);
902
903 if(PURPLE_BLIST_NODE_IS_BUDDY(node))
904 return qq_buddy_menu((PurpleBuddy *) node);
905
906 return NULL;
907 }
908
909 /* convert name displayed in a chat channel to original QQ UID */
910 static gchar *chat_name_to_purple_name(const gchar *const name)
911 {
912 const char *start;
913 const char *end;
914 gchar *ret;
915
916 g_return_val_if_fail(name != NULL, NULL);
917
918 /* Sample: (1234567)*/
919 start = strchr(name, '(');
920 g_return_val_if_fail(start != NULL, NULL);
921 end = strchr(start, ')');
922 g_return_val_if_fail(end != NULL && (end - start) > 1, NULL);
923
924 ret = g_strndup(start + 1, end - start - 1);
925
926 return ret;
927 }
928
929 /* convert chat nickname to uid to get this buddy info */
930 /* who is the nickname of buddy in QQ chat-room (Qun) */
931 static void qq_get_chat_buddy_info(PurpleConnection *gc, gint channel, const gchar *who)
932 {
933 qq_data *qd;
934 gchar *uid_str;
935 guint32 uid;
936
937 purple_debug_info("QQ", "Get chat buddy info of %s\n", who);
938 g_return_if_fail(who != NULL);
939
940 uid_str = chat_name_to_purple_name(who);
941 if (uid_str == NULL) {
942 return;
943 }
944
945 qd = gc->proto_data;
946 uid = purple_name_to_uid(uid_str);
947 g_free(uid_str);
948
949 if (uid <= 0) {
950 purple_debug_error("QQ", "Not valid chat name: %s\n", who);
951 purple_notify_error(gc, NULL, _("Invalid name"), NULL);
952 return;
953 }
954
955 if (qd->client_version < 2007) {
956 qq_request_get_level(gc, uid);
957 }
958 qq_request_buddy_info(gc, uid, 0, QQ_BUDDY_INFO_DISPLAY);
959 }
960
961 /* convert chat nickname to uid to invite individual IM to buddy */
962 /* who is the nickname of buddy in QQ chat-room (Qun) */
963 static gchar *qq_get_chat_buddy_real_name(PurpleConnection *gc, gint channel, const gchar *who)
964 {
965 g_return_val_if_fail(who != NULL, NULL);
966 return chat_name_to_purple_name(who);
967 }
968
969 static PurplePluginProtocolInfo prpl_info =
970 {
971 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_USE_POINTSIZE,
972 NULL, /* user_splits */
973 NULL, /* protocol_options */
974 {"png", 96, 96, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */
975 qq_list_icon, /* list_icon */
976 qq_list_emblem, /* list_emblems */
977 qq_status_text, /* status_text */
978 qq_tooltip_text, /* tooltip_text */
979 qq_status_types, /* away_states */
980 qq_blist_node_menu, /* blist_node_menu */
981 qq_chat_info, /* chat_info */
982 qq_chat_info_defaults, /* chat_info_defaults */
983 qq_login, /* open */
984 qq_close, /* close */
985 qq_send_im, /* send_im */
986 NULL, /* set_info */
987 NULL, /* send_typing */
988 qq_show_buddy_info, /* get_info */
989 qq_change_status, /* change status */
990 NULL, /* set_idle */
991 NULL, /* change_passwd */
992 qq_add_buddy, /* add_buddy */
993 NULL, /* add_buddies */
994 qq_remove_buddy, /* remove_buddy */
995 NULL, /* remove_buddies */
996 NULL, /* add_permit */
997 NULL, /* add_deny */
998 NULL, /* rem_permit */
999 NULL, /* rem_deny */
1000 NULL, /* set_permit_deny */
1001 qq_group_join, /* join_chat */
1002 NULL, /* reject chat invite */
1003 NULL, /* get_chat_name */
1004 NULL, /* chat_invite */
1005 NULL, /* chat_leave */
1006 NULL, /* chat_whisper */
1007 qq_chat_send, /* chat_send */
1008 NULL, /* keepalive */
1009 NULL, /* register_user */
1010 qq_get_chat_buddy_info, /* get_cb_info */
1011 NULL, /* get_cb_away */
1012 NULL, /* alias_buddy */
1013 NULL, /* change buddy's group */
1014 NULL, /* rename_group */
1015 NULL, /* buddy_free */
1016 NULL, /* convo_closed */
1017 NULL, /* normalize */
1018 qq_set_custom_icon,
1019 NULL, /* remove_group */
1020 qq_get_chat_buddy_real_name, /* get_cb_real_name */
1021 NULL, /* set_chat_topic */
1022 NULL, /* find_blist_chat */
1023 qq_roomlist_get_list, /* roomlist_get_list */
1024 qq_roomlist_cancel, /* roomlist_cancel */
1025 NULL, /* roomlist_expand_category */
1026 NULL, /* can_receive_file */
1027 NULL, /* qq_send_file send_file */
1028 NULL, /* new xfer */
1029 NULL, /* offline_message */
1030 NULL, /* PurpleWhiteboardPrplOps */
1031 NULL, /* send_raw */
1032 NULL, /* roomlist_room_serialize */
1033 NULL, /* unregister_user */
1034 NULL, /* send_attention */
1035 NULL, /* get attention_types */
1036
1037 sizeof(PurplePluginProtocolInfo), /* struct_size */
1038 NULL, /* get_account_text_table */
1039 NULL, /* initiate_media */
1040 NULL /* can_do_media */
1041 };
1042
1043 static PurplePluginInfo info = {
1044 PURPLE_PLUGIN_MAGIC,
1045 PURPLE_MAJOR_VERSION,
1046 PURPLE_MINOR_VERSION,
1047 PURPLE_PLUGIN_PROTOCOL, /**< type */
1048 NULL, /**< ui_requirement */
1049 0, /**< flags */
1050 NULL, /**< dependencies */
1051 PURPLE_PRIORITY_DEFAULT, /**< priority */
1052
1053 "prpl-qq", /**< id */
1054 "QQ", /**< name */
1055 DISPLAY_VERSION, /**< version */
1056 /** summary */
1057 N_("QQ Protocol Plugin"),
1058 /** description */
1059 N_("QQ Protocol Plugin"),
1060 NULL, /**< author */
1061 PURPLE_WEBSITE, /**< homepage */
1062
1063 NULL, /**< load */
1064 NULL, /**< unload */
1065 NULL, /**< destroy */
1066
1067 NULL, /**< ui_info */
1068 &prpl_info, /**< extra_info */
1069 NULL, /**< prefs_info */
1070 qq_actions,
1071
1072 /* padding */
1073 NULL,
1074 NULL,
1075 NULL,
1076 NULL
1077 };
1078
1079
1080 static void init_plugin(PurplePlugin *plugin)
1081 {
1082 PurpleAccountOption *option;
1083 PurpleKeyValuePair *kvp;
1084 GList *server_list = NULL;
1085 GList *server_kv_list = NULL;
1086 GList *it;
1087 /* #ifdef DEBUG */
1088 GList *version_kv_list = NULL;
1089 /* #endif */
1090
1091 server_list = server_list_build('A');
1092
1093 purple_prefs_remove("/plugins/prpl/qq/serverlist");
1094
1095 server_kv_list = NULL;
1096 kvp = g_new0(PurpleKeyValuePair, 1);
1097 kvp->key = g_strdup(_("Auto"));
1098 kvp->value = g_strdup("auto");
1099 server_kv_list = g_list_append(server_kv_list, kvp);
1100
1101 it = server_list;
1102 while(it) {
1103 if (it->data != NULL && strlen(it->data) > 0) {
1104 kvp = g_new0(PurpleKeyValuePair, 1);
1105 kvp->key = g_strdup(it->data);
1106 kvp->value = g_strdup(it->data);
1107 server_kv_list = g_list_append(server_kv_list, kvp);
1108 }
1109 it = it->next;
1110 }
1111
1112 g_list_free(server_list);
1113
1114 option = purple_account_option_list_new(_("Select Server"), "server", server_kv_list);
1115 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1116
1117 kvp = g_new0(PurpleKeyValuePair, 1);
1118 kvp->key = g_strdup(_("QQ2005"));
1119 kvp->value = g_strdup("qq2005");
1120 version_kv_list = g_list_append(version_kv_list, kvp);
1121
1122 kvp = g_new0(PurpleKeyValuePair, 1);
1123 kvp->key = g_strdup(_("QQ2007"));
1124 kvp->value = g_strdup("qq2007");
1125 version_kv_list = g_list_append(version_kv_list, kvp);
1126
1127 kvp = g_new0(PurpleKeyValuePair, 1);
1128 kvp->key = g_strdup(_("QQ2008"));
1129 kvp->value = g_strdup("qq2008");
1130 version_kv_list = g_list_append(version_kv_list, kvp);
1131
1132 option = purple_account_option_list_new(_("Client Version"), "client_version", version_kv_list);
1133 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1134
1135 option = purple_account_option_bool_new(_("Connect by TCP"), "use_tcp", TRUE);
1136 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1137
1138 option = purple_account_option_bool_new(_("Show server notice"), "show_notice", TRUE);
1139 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1140
1141 option = purple_account_option_bool_new(_("Show server news"), "show_news", TRUE);
1142 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1143
1144 option = purple_account_option_bool_new(_("Show chat room when msg comes"), "show_chat", TRUE);
1145 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1146
1147 option = purple_account_option_int_new(_("Keep alive interval (seconds)"), "keep_alive_interval", 60);
1148 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1149
1150 option = purple_account_option_int_new(_("Update interval (seconds)"), "update_interval", 300);
1151 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
1152
1153 purple_prefs_add_none("/plugins/prpl/qq");
1154 purple_prefs_add_bool("/plugins/prpl/qq/show_status_by_icon", TRUE);
1155 purple_prefs_add_bool("/plugins/prpl/qq/show_fake_video", FALSE);
1156 purple_prefs_add_bool("/plugins/prpl/qq/auto_get_authorize_info", TRUE);
1157 purple_prefs_add_int("/plugins/prpl/qq/resend_interval", 3);
1158 purple_prefs_add_int("/plugins/prpl/qq/resend_times", 10);
1159 }
1160
1161 PURPLE_INIT_PLUGIN(qq, init_plugin, info);

mercurial