comparison libpurple/protocols/qq/buddy_opt.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 32c366eeeb99
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
1 /**
2 * @file buddy_opt.c
3 *
4 * gaim
5 *
6 * Gaim 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #include "debug.h"
26 #include "internal.h"
27 #include "notify.h"
28 #include "request.h"
29
30 #include "buddy_info.h"
31 #include "buddy_list.h"
32 #include "buddy_opt.h"
33 #include "char_conv.h"
34 #include "crypt.h"
35 #include "header_info.h"
36 #include "im.h"
37 #include "keep_alive.h"
38 #include "packet_parse.h"
39 #include "send_core.h"
40 #include "utils.h"
41
42 #define GAIM_GROUP_QQ_FORMAT "QQ (%s)"
43 #define GAIM_GROUP_QQ_UNKNOWN "QQ Unknown"
44 #define GAIM_GROUP_QQ_BLOCKED "QQ Blocked"
45
46 #define QQ_REMOVE_BUDDY_REPLY_OK 0x00
47 #define QQ_REMOVE_SELF_REPLY_OK 0x00
48 #define QQ_ADD_BUDDY_AUTH_REPLY_OK 0x30 /* ASCII value of "0" */
49
50 enum {
51 QQ_MY_AUTH_APPROVE = 0x30, /* ASCII value of "0" */
52 QQ_MY_AUTH_REJECT = 0x31, /* ASCII value of "1" */
53 QQ_MY_AUTH_REQUEST = 0x32, /* ASCII value of "2" */
54 };
55
56 typedef struct _qq_add_buddy_request {
57 guint32 uid;
58 guint16 seq;
59 } qq_add_buddy_request;
60
61 /* send packet to remove a buddy from my buddy list */
62 static void _qq_send_packet_remove_buddy(GaimConnection *gc, guint32 uid)
63 {
64 gchar uid_str[11];
65
66 g_return_if_fail(uid > 0);
67
68 g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
69 qq_send_cmd(gc, QQ_CMD_DEL_FRIEND, TRUE, 0,
70 TRUE, (guint8 *) uid_str, strlen(uid_str));
71 }
72
73 /* try to remove myself from someone's buddy list */
74 static void _qq_send_packet_remove_self_from(GaimConnection *gc, guint32 uid)
75 {
76 guint8 *raw_data, *cursor;
77
78 g_return_if_fail(uid > 0);
79
80 raw_data = g_newa(guint8, 4);
81 cursor = raw_data;
82 create_packet_dw(raw_data, &cursor, uid);
83
84 qq_send_cmd(gc, QQ_CMD_REMOVE_SELF, TRUE, 0, TRUE, raw_data, 4);
85 }
86
87 /* try to add a buddy without authentication */
88 static void _qq_send_packet_add_buddy(GaimConnection *gc, guint32 uid)
89 {
90 qq_data *qd;
91 qq_add_buddy_request *req;
92 gchar uid_str[11];
93
94 g_return_if_fail(uid > 0);
95
96 /* we need to send the ascii code of this uid to qq server */
97 g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
98 qq_send_cmd(gc, QQ_CMD_ADD_FRIEND_WO_AUTH, TRUE, 0,
99 TRUE, (guint8 *) uid_str, strlen(uid_str));
100
101 /* must be set after sending packet to get the correct send_seq */
102 qd = (qq_data *) gc->proto_data;
103 req = g_new0(qq_add_buddy_request, 1);
104 req->seq = qd->send_seq;
105 req->uid = uid;
106 qd->add_buddy_request = g_list_append(qd->add_buddy_request, req);
107 }
108
109 /* this buddy needs authentication, text conversion is done at lowest level */
110 static void _qq_send_packet_buddy_auth(GaimConnection *gc, guint32 uid, const gchar response, const gchar *text)
111 {
112 gchar *text_qq, uid_str[11];
113 guint8 bar, *cursor, *raw_data;
114
115 g_return_if_fail(uid != 0);
116
117 g_snprintf(uid_str, sizeof(uid_str), "%d", uid);
118 bar = 0x1f;
119 raw_data = g_newa(guint8, QQ_MSG_IM_MAX);
120 cursor = raw_data;
121
122 create_packet_data(raw_data, &cursor, (guint8 *) uid_str, strlen(uid_str));
123 create_packet_b(raw_data, &cursor, bar);
124 create_packet_b(raw_data, &cursor, response);
125
126 if (text != NULL) {
127 text_qq = utf8_to_qq(text, QQ_CHARSET_DEFAULT);
128 create_packet_b(raw_data, &cursor, bar);
129 create_packet_data(raw_data, &cursor, (guint8 *) text_qq, strlen(text_qq));
130 g_free(text_qq);
131 }
132
133 qq_send_cmd(gc, QQ_CMD_BUDDY_AUTH, TRUE, 0, TRUE, raw_data, cursor - raw_data);
134 }
135
136 static void _qq_send_packet_add_buddy_auth_with_gc_and_uid(gc_and_uid *g, const gchar *text)
137 {
138 GaimConnection *gc;
139 guint32 uid;
140 g_return_if_fail(g != NULL);
141
142 gc = g->gc;
143 uid = g->uid;
144 g_return_if_fail(uid != 0);
145
146 _qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_REQUEST, text);
147 g_free(g);
148 }
149
150 /* the real packet to reject and request is sent from here */
151 static void _qq_reject_add_request_real(gc_and_uid *g, const gchar *reason)
152 {
153 gint uid;
154 GaimConnection *gc;
155
156 g_return_if_fail(g != NULL);
157
158 gc = g->gc;
159 uid = g->uid;
160 g_return_if_fail(uid != 0);
161
162 _qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_REJECT, reason);
163 g_free(g);
164 }
165
166 /* we approve other's request of adding me as friend */
167 void qq_approve_add_request_with_gc_and_uid(gc_and_uid *g)
168 {
169 gint uid;
170 GaimConnection *gc;
171
172 g_return_if_fail(g != NULL);
173
174 gc = g->gc;
175 uid = g->uid;
176 g_return_if_fail(uid != 0);
177
178 _qq_send_packet_buddy_auth(gc, uid, QQ_MY_AUTH_APPROVE, NULL);
179 g_free(g);
180 }
181
182 void qq_do_nothing_with_gc_and_uid(gc_and_uid *g, const gchar *msg)
183 {
184 g_free(g);
185 }
186
187 /* we reject other's request of adding me as friend */
188 void qq_reject_add_request_with_gc_and_uid(gc_and_uid *g)
189 {
190 gint uid;
191 gchar *msg1, *msg2;
192 GaimConnection *gc;
193 gc_and_uid *g2;
194
195 g_return_if_fail(g != NULL);
196
197 gc = g->gc;
198 uid = g->uid;
199 g_return_if_fail(uid != 0);
200
201 g_free(g);
202
203 g2 = g_new0(gc_and_uid, 1);
204 g2->gc = gc;
205 g2->uid = uid;
206
207 msg1 = g_strdup_printf(_("You rejected %d's request"), uid);
208 msg2 = g_strdup(_("Input your reason:"));
209
210 gaim_request_input(gc, _("Reject request"), msg1, msg2,
211 _("Sorry, you are not my type..."), TRUE, FALSE,
212 NULL, _("Reject"), G_CALLBACK(_qq_reject_add_request_real), _("Cancel"), NULL, g2);
213 }
214
215 void qq_add_buddy_with_gc_and_uid(gc_and_uid *g)
216 {
217 gint uid;
218 GaimConnection *gc;
219
220 g_return_if_fail(g != NULL);
221
222 gc = g->gc;
223 uid = g->uid;
224 g_return_if_fail(uid != 0);
225
226 _qq_send_packet_add_buddy(gc, uid);
227 g_free(g);
228 }
229
230 void qq_block_buddy_with_gc_and_uid(gc_and_uid *g)
231 {
232 guint32 uid;
233 GaimConnection *gc;
234 GaimBuddy buddy;
235 GaimGroup group;
236
237 g_return_if_fail(g != NULL);
238
239 gc = g->gc;
240 uid = g->uid;
241 g_return_if_fail(uid > 0);
242
243 buddy.name = uid_to_gaim_name(uid);
244 group.name = GAIM_GROUP_QQ_BLOCKED;
245
246 qq_remove_buddy(gc, &buddy, &group);
247 _qq_send_packet_remove_self_from(gc, uid);
248 }
249
250 /* process reply to add_buddy_auth request */
251 void qq_process_add_buddy_auth_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
252 {
253 qq_data *qd;
254 gint len;
255 guint8 *data, *cursor, reply;
256 gchar **segments, *msg_utf8;
257
258 g_return_if_fail(buf != NULL && buf_len != 0);
259
260 qd = (qq_data *) gc->proto_data;
261 len = buf_len;
262 data = g_newa(guint8, len);
263 cursor = data;
264
265 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
266 read_packet_b(data, &cursor, len, &reply);
267 if (reply != QQ_ADD_BUDDY_AUTH_REPLY_OK) {
268 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Add buddy with auth request fails\n");
269 if (NULL == (segments = split_data(data, len, "\x1f", 2)))
270 return;
271 msg_utf8 = qq_to_utf8(segments[1], QQ_CHARSET_DEFAULT);
272 gaim_notify_error(gc, NULL, _("Add buddy with auth request fails"), msg_utf8);
273 g_free(msg_utf8);
274 } else {
275 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Add buddy with auth request OK\n");
276 }
277 } else {
278 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt add buddy with auth reply\n");
279 }
280 }
281
282 /* process the server reply for my request to remove a buddy */
283 void qq_process_remove_buddy_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
284 {
285 qq_data *qd;
286 gint len;
287 guint8 *data, *cursor, reply;
288
289 g_return_if_fail(buf != NULL && buf_len != 0);
290
291 qd = (qq_data *) gc->proto_data;
292 len = buf_len;
293 data = g_newa(guint8, len);
294
295 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
296 cursor = data;
297 read_packet_b(data, &cursor, len, &reply);
298 if (reply != QQ_REMOVE_BUDDY_REPLY_OK) {
299 /* there is no reason return from server */
300 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Remove buddy fails\n");
301 } else { /* if reply */
302 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Remove buddy OK\n");
303 gaim_notify_info(gc, NULL, _("You have successfully removed a buddy"), NULL);
304 }
305 } else {
306 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt remove buddy reply\n");
307 }
308 }
309
310 /* process the server reply for my request to remove myself from a buddy */
311 void qq_process_remove_self_reply(guint8 *buf, gint buf_len, GaimConnection *gc)
312 {
313 qq_data *qd;
314 gint len;
315 guint8 *data, *cursor, reply;
316
317 g_return_if_fail(buf != NULL && buf_len != 0);
318
319 qd = (qq_data *) gc->proto_data;
320 len = buf_len;
321 data = g_newa(guint8, len);
322
323 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
324 cursor = data;
325 read_packet_b(data, &cursor, len, &reply);
326 if (reply != QQ_REMOVE_SELF_REPLY_OK)
327 /* there is no reason return from server */
328 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Remove self fails\n");
329 else { /* if reply */
330 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Remove self from a buddy OK\n");
331 gaim_notify_info(gc, NULL, _("You have successfully removed yourself from a buddy"), NULL);
332 }
333 } else {
334 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt remove self reply\n");
335 }
336 }
337
338 void qq_process_add_buddy_reply(guint8 *buf, gint buf_len, guint16 seq, GaimConnection *gc)
339 {
340 qq_data *qd;
341 gint len, for_uid;
342 gchar *msg, **segments, *uid, *reply;
343 guint8 *data;
344 GList *list;
345 GaimBuddy *b;
346 gc_and_uid *g;
347 qq_add_buddy_request *req;
348
349 g_return_if_fail(buf != NULL && buf_len != 0);
350
351 for_uid = 0;
352 qd = (qq_data *) gc->proto_data;
353 len = buf_len;
354
355 list = qd->add_buddy_request;
356 while (list != NULL) {
357 req = (qq_add_buddy_request *) list->data;
358 if (req->seq == seq) { /* reply to this */
359 for_uid = req->uid;
360 qd->add_buddy_request = g_list_remove(qd->add_buddy_request, qd->add_buddy_request->data);
361 g_free(req);
362 break;
363 }
364 list = list->next;
365 }
366
367 if (for_uid == 0) { /* we have no record for this */
368 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "We have no record for add buddy reply [%d], discard\n", seq);
369 return;
370 } else {
371 gaim_debug(GAIM_DEBUG_INFO, "QQ", "Add buddy reply [%d] is for id [%d]\n", seq, for_uid);
372 }
373
374 data = g_newa(guint8, len);
375
376 if (qq_crypt(DECRYPT, buf, buf_len, qd->session_key, data, &len)) {
377 if (NULL == (segments = split_data(data, len, "\x1f", 2)))
378 return;
379 uid = segments[0];
380 reply = segments[1];
381 if (strtol(uid, NULL, 10) != qd->uid) { /* should not happen */
382 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Add buddy reply is to [%s], not me!", uid);
383 g_strfreev(segments);
384 return;
385 }
386
387 if (strtol(reply, NULL, 10) > 0) { /* need auth */
388 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Add buddy attempt fails, need authentication\n");
389 b = gaim_find_buddy(gc->account, uid_to_gaim_name(for_uid));
390 if (b != NULL)
391 gaim_blist_remove_buddy(b);
392 g = g_new0(gc_and_uid, 1);
393 g->gc = gc;
394 g->uid = for_uid;
395 msg = g_strdup_printf(_("User %d needs authentication"), for_uid);
396 gaim_request_input(gc, NULL, msg,
397 _("Input request here"),
398 _("Would you be my friend?"),
399 TRUE, FALSE, NULL, _("Send"),
400 G_CALLBACK
401 (_qq_send_packet_add_buddy_auth_with_gc_and_uid),
402 _("Cancel"), G_CALLBACK(qq_do_nothing_with_gc_and_uid), g);
403 g_free(msg);
404 } else { /* add OK */
405 qq_add_buddy_by_recv_packet(gc, for_uid, TRUE, TRUE);
406 msg = g_strdup_printf(_("You have added %d in buddy list"), for_uid);
407 gaim_notify_info(gc, NULL, msg, NULL);
408 g_free(msg);
409 }
410 g_strfreev(segments);
411 } else {
412 gaim_debug(GAIM_DEBUG_ERROR, "QQ", "Error decrypt add buddy reply\n");
413 }
414 }
415
416 GaimGroup *qq_get_gaim_group(const gchar *group_name)
417 {
418 GaimGroup *g;
419
420 g_return_val_if_fail(group_name != NULL, NULL);
421
422 g = gaim_find_group(group_name);
423 if (g == NULL) {
424 g = gaim_group_new(group_name);
425 gaim_blist_add_group(g, NULL);
426 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Add new group: %s\n", group_name);
427 }
428
429 return g;
430 }
431
432 /* we add new buddy, if the received packet is from someone not in my list
433 * return the GaimBuddy that is just created */
434 GaimBuddy *qq_add_buddy_by_recv_packet(GaimConnection *gc, guint32 uid, gboolean is_known, gboolean create)
435 {
436 GaimAccount *a;
437 GaimBuddy *b;
438 GaimGroup *g;
439 qq_data *qd;
440 qq_buddy *q_bud;
441 gchar *name, *group_name;
442
443 a = gc->account;
444 qd = (qq_data *) gc->proto_data;
445 g_return_val_if_fail(a != NULL && uid != 0, NULL);
446
447 group_name = is_known ?
448 g_strdup_printf(GAIM_GROUP_QQ_FORMAT, gaim_account_get_username(a)) : g_strdup(GAIM_GROUP_QQ_UNKNOWN);
449
450 g = qq_get_gaim_group(group_name);
451
452 name = uid_to_gaim_name(uid);
453 b = gaim_find_buddy(gc->account, name);
454 /* remove old, we can not simply return here
455 * because there might be old local copy of this buddy */
456 if (b != NULL)
457 gaim_blist_remove_buddy(b);
458
459 b = gaim_buddy_new(a, name, NULL);
460
461 if (!create)
462 b->proto_data = NULL;
463 else {
464 q_bud = g_new0(qq_buddy, 1);
465 q_bud->uid = uid;
466 b->proto_data = q_bud;
467 qd->buddies = g_list_append(qd->buddies, q_bud);
468 qq_send_packet_get_info(gc, q_bud->uid, FALSE);
469 qq_send_packet_get_buddies_online(gc, QQ_FRIENDS_ONLINE_POSITION_START);
470 }
471
472 gaim_blist_add_buddy(b, NULL, g, NULL);
473 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "Add new buddy: [%s]\n", name);
474
475 g_free(name);
476 g_free(group_name);
477
478 return b;
479 }
480
481 /* add a buddy and send packet to QQ server
482 * note that when gaim load local cached buddy list into its blist
483 * it also calls this funtion, so we have to
484 * define qd->logged_in=TRUE AFTER serv_finish_login(gc) */
485 void qq_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
486 {
487 qq_data *qd;
488 guint32 uid;
489 GaimBuddy *b;
490
491 qd = (qq_data *) gc->proto_data;
492 if (!qd->logged_in)
493 return; /* IMPORTANT ! */
494
495 uid = gaim_name_to_uid(buddy->name);
496 if (uid > 0)
497 _qq_send_packet_add_buddy(gc, uid);
498 else {
499 b = gaim_find_buddy(gc->account, buddy->name);
500 if (b != NULL)
501 gaim_blist_remove_buddy(b);
502 gaim_notify_error(gc, NULL,
503 _("QQid Error"),
504 _("Invalid QQid"));
505 }
506 }
507
508 /* remove a buddy and send packet to QQ server accordingly */
509 void qq_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
510 {
511 qq_data *qd;
512 GaimBuddy *b;
513 qq_buddy *q_bud;
514 guint32 uid;
515
516 qd = (qq_data *) gc->proto_data;
517 uid = gaim_name_to_uid(buddy->name);
518
519 if (!qd->logged_in)
520 return;
521
522 if (uid > 0)
523 _qq_send_packet_remove_buddy(gc, uid);
524
525 b = gaim_find_buddy(gc->account, buddy->name);
526 if (b != NULL) {
527 q_bud = (qq_buddy *) b->proto_data;
528 if (q_bud != NULL)
529 qd->buddies = g_list_remove(qd->buddies, q_bud);
530 else
531 gaim_debug(GAIM_DEBUG_WARNING, "QQ", "We have no qq_buddy record for %s\n", buddy->name);
532 /* remove buddy on blist, this does not trigger qq_remove_buddy again
533 * do this only if the request comes from block request,
534 * otherwise gaim segmentation fault */
535 if (g_ascii_strcasecmp(group->name, GAIM_GROUP_QQ_BLOCKED) == 0)
536 gaim_blist_remove_buddy(b);
537 }
538 }
539
540 /* free add buddy request queue */
541 void qq_add_buddy_request_free(qq_data *qd)
542 {
543 gint i;
544 qq_add_buddy_request *p;
545
546 i = 0;
547 while (qd->add_buddy_request) {
548 p = (qq_add_buddy_request *) (qd->add_buddy_request->data);
549 qd->add_buddy_request = g_list_remove(qd->add_buddy_request, p);
550 g_free(p);
551 i++;
552 }
553 gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d add buddy requests are freed!\n", i);
554 }
555
556 /* free up all qq_buddy */
557 void qq_buddies_list_free(GaimAccount *account, qq_data *qd)
558 {
559 gint i;
560 qq_buddy *p;
561 gchar *name;
562 GaimBuddy *b;
563
564 i = 0;
565 while (qd->buddies) {
566 p = (qq_buddy *) (qd->buddies->data);
567 qd->buddies = g_list_remove(qd->buddies, p);
568 name = uid_to_gaim_name(p->uid);
569 b = gaim_find_buddy(account, name);
570 if(b != NULL)
571 b->proto_data = NULL;
572 else
573 gaim_debug(GAIM_DEBUG_INFO, "QQ", "qq_buddy %s not found in gaim proto_data\n", name);
574 g_free(name);
575
576 g_free(p);
577 i++;
578 }
579 gaim_debug(GAIM_DEBUG_INFO, "QQ", "%d qq_buddy structures are freed!\n", i);
580 }