comparison libpurple/protocols/msn/userlist.c @ 20394:4a099e4d0d09

propagate from branch 'im.pidgin.pidgin' (head 98b6b547b29ea1192b73cc4e1de1e674edef4328) to branch 'im.pidgin.rlaager.merging.msnp13-and-pidgin' (head 4d82c29e56bd33cd6f94302e343dfeb5d68ab3eb)
author Richard Laager <rlaager@wiktel.com>
date Sun, 15 Apr 2007 03:43:17 +0000
parents 32c366eeeb99
children 61d6a3dfbb3c
comparison
equal deleted inserted replaced
20393:40a04930b233 20394:4a099e4d0d09
1 /**
2 * @file userlist.c MSN user list support
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24 #include "msn.h"
25 #include "userlist.h"
26
27 const char *lists[] = { "FL", "AL", "BL", "RL" };
28
29 typedef struct
30 {
31 PurpleConnection *gc;
32 char *who;
33 char *friendly;
34
35 } MsnPermitAdd;
36
37 /**************************************************************************
38 * Callbacks
39 **************************************************************************/
40 static void
41 msn_accept_add_cb(MsnPermitAdd *pa)
42 {
43 MsnSession *session = pa->gc->proto_data;
44 MsnUserList *userlist = session->userlist;
45
46 msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL);
47
48 g_free(pa->who);
49 g_free(pa->friendly);
50 g_free(pa);
51 }
52
53 static void
54 msn_cancel_add_cb(MsnPermitAdd *pa)
55 {
56 if (g_list_find(purple_connections_get_all(), pa->gc) != NULL)
57 {
58 MsnSession *session = pa->gc->proto_data;
59 MsnUserList *userlist = session->userlist;
60
61 msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL);
62 }
63
64 g_free(pa->who);
65 g_free(pa->friendly);
66 g_free(pa);
67 }
68
69 static void
70 got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly)
71 {
72 MsnPermitAdd *pa;
73
74 pa = g_new0(MsnPermitAdd, 1);
75 pa->who = g_strdup(passport);
76 pa->friendly = g_strdup(friendly);
77 pa->gc = gc;
78
79 purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL,
80 purple_find_buddy(purple_connection_get_account(gc), passport) != NULL,
81 G_CALLBACK(msn_accept_add_cb), G_CALLBACK(msn_cancel_add_cb), pa);
82
83 }
84
85 /**************************************************************************
86 * Utility functions
87 **************************************************************************/
88
89 static gboolean
90 user_is_in_group(MsnUser *user, const char * group_id)
91 {
92 if (user == NULL)
93 return FALSE;
94
95 if (group_id == NULL)
96 return FALSE;
97
98 if (g_list_find(user->group_ids, group_id))
99 return TRUE;
100
101 return FALSE;
102 }
103
104 static gboolean
105 user_is_there(MsnUser *user, int list_id, const char * group_id)
106 {
107 int list_op;
108
109 if (user == NULL)
110 return FALSE;
111
112 list_op = 1 << list_id;
113
114 if (!(user->list_op & list_op))
115 return FALSE;
116
117 if (list_id == MSN_LIST_FL){
118 if (group_id != NULL)
119 return user_is_in_group(user, group_id);
120 }
121
122 return TRUE;
123 }
124
125 static const char*
126 get_store_name(MsnUser *user)
127 {
128 const char *store_name;
129
130 g_return_val_if_fail(user != NULL, NULL);
131
132 store_name = msn_user_get_store_name(user);
133
134 if (store_name != NULL)
135 store_name = purple_url_encode(store_name);
136 else
137 store_name = msn_user_get_passport(user);
138
139 /* this might be a bit of a hack, but it should prevent notification server
140 * disconnections for people who have buddies with insane friendly names
141 * who added you to their buddy list from being disconnected. Stu. */
142 /* Shx: What? Isn't the store_name obtained from the server, and hence it's
143 * below the BUDDY_ALIAS_MAXLEN ? */
144 /* Stu: yeah, that's why it's a bit of a hack, as you pointed out, we're
145 * probably decoding the incoming store_name wrong, or something. bleh. */
146
147 if (strlen(store_name) > BUDDY_ALIAS_MAXLEN)
148 store_name = msn_user_get_passport(user);
149
150 return store_name;
151 }
152
153 static void
154 msn_request_add_group(MsnUserList *userlist, const char *who,
155 const char *old_group_name, const char *new_group_name)
156 {
157 MsnSession *session;
158 MsnCmdProc *cmdproc;
159 MsnMoveBuddy *data;
160
161 session = userlist->session;
162 cmdproc = session->notification->cmdproc;
163 data = g_new0(MsnMoveBuddy, 1);
164
165 data->who = g_strdup(who);
166
167 if (old_group_name){
168 data->old_group_name = g_strdup(old_group_name);
169 /*delete the old group via SOAP action*/
170 msn_del_group(session,old_group_name);
171 }
172
173 /*add new group via SOAP action*/
174 msn_add_group(session, new_group_name);
175
176 }
177
178 /**************************************************************************
179 * Server functions
180 **************************************************************************/
181
182 MsnListId
183 msn_get_list_id(const char *list)
184 {
185 if (list[0] == 'F')
186 return MSN_LIST_FL;
187 else if (list[0] == 'A')
188 return MSN_LIST_AL;
189 else if (list[0] == 'B')
190 return MSN_LIST_BL;
191 else if (list[0] == 'R')
192 return MSN_LIST_RL;
193
194 return -1;
195 }
196
197 void
198 msn_got_add_user(MsnSession *session, MsnUser *user,
199 MsnListId list_id, const char * group_id)
200 {
201 PurpleAccount *account;
202 const char *passport;
203 const char *friendly;
204
205 purple_debug_info("MaYuan","got add user...\n");
206 account = session->account;
207
208 passport = msn_user_get_passport(user);
209 friendly = msn_user_get_friendly_name(user);
210
211 if (list_id == MSN_LIST_FL)
212 {
213 PurpleConnection *gc;
214
215 gc = purple_account_get_connection(account);
216
217 serv_got_alias(gc, passport, friendly);
218
219 if (group_id != NULL)
220 {
221 msn_user_add_group_id(user, group_id);
222 }
223 else
224 {
225 /* session->sync->fl_users_count++; */
226 }
227 }
228 else if (list_id == MSN_LIST_AL)
229 {
230 purple_privacy_permit_add(account, passport, TRUE);
231 }
232 else if (list_id == MSN_LIST_BL)
233 {
234 purple_privacy_deny_add(account, passport, TRUE);
235 }
236 else if (list_id == MSN_LIST_RL)
237 {
238 PurpleConnection *gc;
239 PurpleConversation *convo;
240
241 gc = purple_account_get_connection(account);
242
243 purple_debug_info("msn",
244 "%s has added you to his or her buddy list.\n",
245 passport);
246
247 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account);
248 if (convo) {
249 PurpleBuddy *buddy;
250 char *msg;
251
252 buddy = purple_find_buddy(account, passport);
253 msg = g_strdup_printf(
254 _("%s has added you to his or her buddy list."),
255 buddy ? purple_buddy_get_contact_alias(buddy) : passport);
256 purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg,
257 PURPLE_MESSAGE_SYSTEM, time(NULL));
258 g_free(msg);
259 }
260
261 if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
262 {
263 /*
264 * TODO: The friendly name was NULL for me when I
265 * looked at this. Maybe we should use the store
266 * name instead? --KingAnt
267 */
268 // got_new_entry(gc, passport, friendly);
269 }
270 }
271
272 user->list_op |= (1 << list_id);
273 /* purple_user_add_list_id (user, list_id); */
274 }
275
276 void
277 msn_got_rem_user(MsnSession *session, MsnUser *user,
278 MsnListId list_id, const char * group_id)
279 {
280 PurpleAccount *account;
281 const char *passport;
282
283 account = session->account;
284
285 passport = msn_user_get_passport(user);
286
287 if (list_id == MSN_LIST_FL)
288 {
289 /* TODO: When is the user totally removed? */
290 if (group_id >= 0)
291 {
292 msn_user_remove_group_id(user, group_id);
293 return;
294 }
295 else
296 {
297 /* session->sync->fl_users_count--; */
298 }
299 }
300 else if (list_id == MSN_LIST_AL)
301 {
302 purple_privacy_permit_remove(account, passport, TRUE);
303 }
304 else if (list_id == MSN_LIST_BL)
305 {
306 purple_privacy_deny_remove(account, passport, TRUE);
307 }
308 else if (list_id == MSN_LIST_RL)
309 {
310 PurpleConversation *convo;
311
312 purple_debug_info("msn",
313 "%s has removed you from his or her buddy list.\n",
314 passport);
315
316 convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account);
317 if (convo) {
318 PurpleBuddy *buddy;
319 char *msg;
320
321 buddy = purple_find_buddy(account, passport);
322 msg = g_strdup_printf(
323 _("%s has removed you from his or her buddy list."),
324 buddy ? purple_buddy_get_contact_alias(buddy) : passport);
325 purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg,
326 PURPLE_MESSAGE_SYSTEM, time(NULL));
327 g_free(msg);
328 }
329 }
330
331 user->list_op &= ~(1 << list_id);
332 /* purple_user_remove_list_id (user, list_id); */
333
334 if (user->list_op == 0)
335 {
336 purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n",
337 passport);
338 }
339 }
340
341 void
342 msn_got_lst_user(MsnSession *session, MsnUser *user,
343 int list_op, GSList *group_ids)
344 {
345 PurpleConnection *gc;
346 PurpleAccount *account;
347 const char *passport;
348 const char *store;
349
350 account = session->account;
351 gc = purple_account_get_connection(account);
352
353 passport = msn_user_get_passport(user);
354 store = msn_user_get_store_name(user);
355
356 if (list_op & MSN_LIST_FL_OP)
357 {
358 GSList *c;
359 for (c = group_ids; c != NULL; c = g_slist_next(c)) {
360 char *group_id;
361 group_id = c->data;
362 msn_user_add_group_id(user, group_id);
363 }
364
365 /* FIXME: It might be a real alias */
366 /* Umm, what? This might fix bug #1385130 */
367 serv_got_alias(gc, passport, store);
368 }
369
370 if (list_op & MSN_LIST_AL_OP)
371 {
372 /* These are users who are allowed to see our status. */
373 purple_privacy_deny_remove(account, passport, TRUE);
374 purple_privacy_permit_add(account, passport, TRUE);
375 }
376
377 if (list_op & MSN_LIST_BL_OP)
378 {
379 /* These are users who are not allowed to see our status. */
380 purple_privacy_permit_remove(account, passport, TRUE);
381 purple_privacy_deny_add(account, passport, TRUE);
382 }
383
384 if (list_op & MSN_LIST_RL_OP)
385 {
386 /* These are users who have us on their buddy list. */
387 /*
388 * TODO: What is store name set to when this happens?
389 * For one of my accounts "something@hotmail.com"
390 * the store name was "something." Maybe we
391 * should use the friendly name, instead? --KingAnt
392 */
393
394 if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP)))
395 {
396 got_new_entry(gc, passport, store);
397 }
398 }
399
400 user->list_op |= list_op;
401 }
402
403 /**************************************************************************
404 * UserList functions
405 **************************************************************************/
406
407 MsnUserList*
408 msn_userlist_new(MsnSession *session)
409 {
410 MsnUserList *userlist;
411
412 userlist = g_new0(MsnUserList, 1);
413
414 userlist->session = session;
415 userlist->buddy_icon_requests = g_queue_new();
416
417 /* buddy_icon_window is the number of allowed simultaneous buddy icon requests.
418 * XXX With smarter rate limiting code, we could allow more at once... 5 was the limit set when
419 * we weren't retrieiving any more than 5 per MSN session. */
420 userlist->buddy_icon_window = 1;
421
422 return userlist;
423 }
424
425 void
426 msn_userlist_destroy(MsnUserList *userlist)
427 {
428 GList *l;
429
430 /*destroy userlist*/
431 for (l = userlist->users; l != NULL; l = l->next)
432 {
433 msn_user_destroy(l->data);
434 }
435 g_list_free(userlist->users);
436
437 /*destroy group list*/
438 for (l = userlist->groups; l != NULL; l = l->next)
439 {
440 msn_group_destroy(l->data);
441 }
442 g_list_free(userlist->groups);
443
444 g_queue_free(userlist->buddy_icon_requests);
445
446 if (userlist->buddy_icon_request_timer)
447 purple_timeout_remove(userlist->buddy_icon_request_timer);
448
449 g_free(userlist);
450 }
451
452 MsnUser *
453 msn_userlist_find_add_user(MsnUserList *userlist,const char *passport,const char *userName)
454 {
455 MsnUser *user;
456
457 user = msn_userlist_find_user(userlist, passport);
458 if (user == NULL){
459 user = msn_user_new(userlist, passport, userName);
460 msn_userlist_add_user(userlist, user);
461 }
462 msn_user_set_store_name(user, userName);
463 return user;
464 }
465
466 void
467 msn_userlist_add_user(MsnUserList *userlist, MsnUser *user)
468 {
469 userlist->users = g_list_append(userlist->users, user);
470 }
471
472 void
473 msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user)
474 {
475 userlist->users = g_list_remove(userlist->users, user);
476 }
477
478 MsnUser *
479 msn_userlist_find_user(MsnUserList *userlist, const char *passport)
480 {
481 GList *l;
482
483 g_return_val_if_fail(passport != NULL, NULL);
484
485 for (l = userlist->users; l != NULL; l = l->next)
486 {
487 MsnUser *user = (MsnUser *)l->data;
488 // purple_debug_info("MsnUserList","user passport:%s,passport:%s\n",user->passport,passport);
489 g_return_val_if_fail(user->passport != NULL, NULL);
490
491 if (!g_strcasecmp(passport, user->passport)){
492 // purple_debug_info("MsnUserList","return:%p\n",user);
493 return user;
494 }
495 }
496
497 return NULL;
498 }
499
500 void
501 msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group)
502 {
503 userlist->groups = g_list_append(userlist->groups, group);
504 }
505
506 void
507 msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group)
508 {
509 userlist->groups = g_list_remove(userlist->groups, group);
510 }
511
512 MsnGroup *
513 msn_userlist_find_group_with_id(MsnUserList *userlist, const char * id)
514 {
515 GList *l;
516
517 g_return_val_if_fail(userlist != NULL, NULL);
518 g_return_val_if_fail(id != NULL, NULL);
519
520 for (l = userlist->groups; l != NULL; l = l->next){
521 MsnGroup *group = l->data;
522
523 if (!g_strcasecmp(group->id,id))
524 return group;
525 }
526
527 return NULL;
528 }
529
530 MsnGroup *
531 msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name)
532 {
533 GList *l;
534
535 g_return_val_if_fail(userlist != NULL, NULL);
536 g_return_val_if_fail(name != NULL, NULL);
537
538 for (l = userlist->groups; l != NULL; l = l->next)
539 {
540 MsnGroup *group = l->data;
541
542 if ((group->name != NULL) && !g_strcasecmp(name, group->name))
543 return group;
544 }
545
546 return NULL;
547 }
548
549 const char *
550 msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name)
551 {
552 MsnGroup *group;
553
554 group = msn_userlist_find_group_with_name(userlist, group_name);
555
556 if (group != NULL){
557 return msn_group_get_id(group);
558 else
559 return NULL;
560 }
561
562 const char *
563 msn_userlist_find_group_name(MsnUserList *userlist, const char * group_id)
564 {
565 MsnGroup *group;
566
567 group = msn_userlist_find_group_with_id(userlist, group_id);
568
569 if (group != NULL){
570 return msn_group_get_name(group);
571 }else{
572 return NULL;
573 }
574 }
575
576 void
577 msn_userlist_rename_group_id(MsnUserList *userlist, const char * group_id,
578 const char *new_name)
579 {
580 MsnGroup *group;
581
582 group = msn_userlist_find_group_with_id(userlist, group_id);
583
584 if (group != NULL)
585 msn_group_set_name(group, new_name);
586 }
587
588 void
589 msn_userlist_remove_group_id(MsnUserList *userlist, const char * group_id)
590 {
591 MsnGroup *group;
592
593 group = msn_userlist_find_group_with_id(userlist, group_id);
594
595 if (group != NULL)
596 {
597 msn_userlist_remove_group(userlist, group);
598 msn_group_destroy(group);
599 }
600 }
601
602 void
603 msn_userlist_rem_buddy(MsnUserList *userlist,
604 const char *who, int list_id, const char *group_name)
605 {
606 MsnUser *user;
607 const char *group_id;
608 const char *list;
609
610 user = msn_userlist_find_user(userlist, who);
611
612 g_return_if_fail(user != NULL);
613
614 /*delete the contact from address book via soap action*/
615 msn_delete_contact(userlist->session->contact,user->uid);
616
617 group_id = NULL;
618
619 if (group_name != NULL){
620 group_id = msn_userlist_find_group_id(userlist, group_name);
621
622 if (group_id == NULL){
623 /* Whoa, there is no such group. */
624 purple_debug_error("msn", "Group doesn't exist: %s\n", group_name);
625 return;
626 }
627 }
628
629 /* First we're going to check if not there. */
630 if (!(user_is_there(user, list_id, group_id))){
631 list = lists[list_id];
632 purple_debug_error("msn", "User '%s' is not there: %s\n",
633 who, list);
634 return;
635 }
636
637 /* Then request the rem to the server. */
638 list = lists[list_id];
639
640 msn_notification_rem_buddy(userlist->session->notification, list, who, group_id);
641 }
642
643 /*add buddy*/
644 void
645 msn_userlist_add_buddy(MsnUserList *userlist,
646 const char *who, int list_id,
647 const char *group_name)
648 {
649 MsnUser *user;
650 const char *group_id;
651 const char *list;
652 const char *store_name;
653
654 purple_debug_info("MaYuan", "userlist add buddy,name:{%s},group:{%s}\n",who ,group_name);
655 group_id = NULL;
656
657 if (!purple_email_is_valid(who))
658 {
659 /* only notify the user about problems adding to the friends list
660 * maybe we should do something else for other lists, but it probably
661 * won't cause too many problems if we just ignore it */
662 if (list_id == MSN_LIST_FL) {
663 char *str = g_strdup_printf(_("Unable to add \"%s\"."), who);
664 purple_notify_error(NULL, NULL, str,
665 _("The screen name specified is invalid."));
666 g_free(str);
667 }
668
669 return;
670 }
671
672 if (group_name != NULL)
673 {
674 group_id = msn_userlist_find_group_id(userlist, group_name);
675
676 if (group_id == NULL)
677 {
678 /* Whoa, we must add that group first. */
679 msn_request_add_group(userlist, who, NULL, group_name);
680 return;
681 }
682 }
683
684 user = msn_userlist_find_user(userlist, who);
685
686 /* First we're going to check if it's already there. */
687 if (user_is_there(user, list_id, group_id)){
688 list = lists[list_id];
689 purple_debug_error("msn", "User '%s' is already there: %s\n", who, list);
690 return;
691 }
692
693 store_name = (user != NULL) ? get_store_name(user) : who;
694
695 /* Then request the add to the server. */
696 list = lists[list_id];
697
698 purple_debug_info("MaYuan", "add user:{%s} to group id {%s}\n",store_name ,group_id);
699 msn_add_contact(userlist->session->contact,who,group_id);
700 msn_notification_add_buddy(userlist->session->notification, list, who,
701 store_name, group_id);
702 }
703
704 void
705 msn_userlist_move_buddy(MsnUserList *userlist, const char *who,
706 const char *old_group_name, const char *new_group_name)
707 {
708 const char *new_group_id;
709
710 new_group_id = msn_userlist_find_group_id(userlist, new_group_name);
711
712 if (new_group_id == NULL)
713 {
714 msn_request_add_group(userlist, who, old_group_name, new_group_name);
715 return;
716 }
717
718 msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name);
719 msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name);
720 }
721
722 /*load userlist from the Blist file cache*/
723 void
724 msn_userlist_load(MsnSession *session)
725 {
726 PurpleBlistNode *gnode, *cnode, *bnode;
727 PurpleConnection *gc = purple_account_get_connection(session->account);
728 GSList *l;
729 MsnUser * user;
730
731 g_return_if_fail(gc != NULL);
732
733 for (gnode = purple_get_blist()->root; gnode; gnode = gnode->next){
734 if(!PURPLE_BLIST_NODE_IS_GROUP(gnode))
735 continue;
736 for(cnode = gnode->child; cnode; cnode = cnode->next) {
737 if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode))
738 continue;
739 for(bnode = cnode->child; bnode; bnode = bnode->next) {
740 PurpleBuddy *b;
741 if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode))
742 continue;
743 b = (PurpleBuddy *)bnode;
744 if(b->account == gc->account){
745 user = msn_userlist_find_add_user(session->userlist,
746 b->name,NULL);
747 b->proto_data = user;
748 msn_user_set_op(user, MSN_LIST_FL_OP);
749 }
750 }
751 }
752 }
753 for (l = session->account->permit; l != NULL; l = l->next) {
754 user = msn_userlist_find_add_user(session->userlist,
755 (char *)l->data,NULL);
756 msn_user_set_op(user, MSN_LIST_AL_OP);
757 }
758 for (l = session->account->deny; l != NULL; l = l->next) {
759 user = msn_userlist_find_add_user(session->userlist,
760 (char *)l->data,NULL);
761 msn_user_set_op(user, MSN_LIST_BL_OP);
762 }
763
764 }
765