Mercurial > pidgin
annotate src/protocols/jabber/roster.c @ 13967:99b9b58b19dd
[gaim-migrate @ 16523]
Fix a crazy MSN crash. Basically it's possible to have more than one
slplink associated with a given switchboard, but our code did not
allow for that. I think it happens when you're in a multi-user
chat and you do stuff with multiple users that involves slplinks.
Like maybe file transfer and buddy icon related stuff.
Tracking this down took an ungodly amount of time, but thanks to
Meebo for letting me do it :-)
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 20 Jul 2006 07:31:15 +0000 |
parents | 60f39c405dff |
children | 6fc412e59214 |
rev | line source |
---|---|
7014 | 1 /* |
2 * gaim - Jabber Protocol Plugin | |
3 * | |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 * | |
20 */ | |
21 #include "internal.h" | |
22 #include "debug.h" | |
23 #include "server.h" | |
13807 | 24 #include "util.h" |
7014 | 25 |
26 #include "buddy.h" | |
27 #include "presence.h" | |
28 #include "roster.h" | |
29 #include "iq.h" | |
30 | |
31 #include <string.h> | |
32 | |
33 | |
34 void jabber_roster_request(JabberStream *js) | |
35 { | |
36 JabberIq *iq; | |
37 | |
38 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:roster"); | |
39 | |
40 jabber_iq_send(iq); | |
41 } | |
42 | |
43 static void remove_gaim_buddies(JabberStream *js, const char *jid) | |
44 { | |
45 GSList *buddies, *l; | |
46 | |
47 buddies = gaim_find_buddies(js->gc->account, jid); | |
48 | |
49 for(l = buddies; l; l = l->next) | |
50 gaim_blist_remove_buddy(l->data); | |
51 | |
52 g_slist_free(buddies); | |
53 } | |
54 | |
55 static void add_gaim_buddies_in_groups(JabberStream *js, const char *jid, | |
56 const char *alias, GSList *groups) | |
57 { | |
58 GSList *buddies, *g2, *l; | |
13925 | 59 gchar *my_bare_jid; |
7014 | 60 |
61 buddies = gaim_find_buddies(js->gc->account, jid); | |
62 | |
63 g2 = groups; | |
64 | |
65 if(!groups) { | |
66 if(!buddies) | |
67 g2 = g_slist_append(g2, g_strdup(_("Buddies"))); | |
68 else | |
69 return; | |
70 } | |
71 | |
13925 | 72 my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); |
73 | |
7014 | 74 while(buddies) { |
75 GaimBuddy *b = buddies->data; | |
12088 | 76 GaimGroup *g = gaim_buddy_get_group(b); |
7014 | 77 |
78 buddies = g_slist_remove(buddies, b); | |
79 | |
80 if((l = g_slist_find_custom(g2, g->name, (GCompareFunc)strcmp))) { | |
7955 | 81 const char *servernick; |
82 | |
83 if((servernick = gaim_blist_node_get_string((GaimBlistNode*)b, "servernick"))) | |
84 serv_got_alias(js->gc, jid, servernick); | |
85 | |
7014 | 86 if(alias && (!b->alias || strcmp(b->alias, alias))) |
87 gaim_blist_alias_buddy(b, alias); | |
88 g_free(l->data); | |
89 g2 = g_slist_delete_link(g2, l); | |
90 } else { | |
91 gaim_blist_remove_buddy(b); | |
92 } | |
93 } | |
94 | |
95 while(g2) { | |
96 GaimBuddy *b = gaim_buddy_new(js->gc->account, jid, alias); | |
97 GaimGroup *g = gaim_find_group(g2->data); | |
98 | |
99 if(!g) { | |
100 g = gaim_group_new(g2->data); | |
101 gaim_blist_add_group(g, NULL); | |
102 } | |
103 | |
104 gaim_blist_add_buddy(b, NULL, g, NULL); | |
7955 | 105 gaim_blist_alias_buddy(b, alias); |
13925 | 106 |
107 | |
108 /* If we just learned about ourself, then fake our status, | |
109 * because we won't be receiving a normal presence message | |
110 * about ourself. */ | |
111 if(!strcmp(b->name, my_bare_jid)) { | |
112 GaimPresence *gpresence; | |
113 GaimStatus *status; | |
114 | |
115 gpresence = gaim_account_get_presence(js->gc->account); | |
116 status = gaim_presence_get_active_status(gpresence); | |
117 jabber_presence_fake_to_self(js, status); | |
118 } | |
119 | |
120 | |
121 | |
7014 | 122 g_free(g2->data); |
123 g2 = g_slist_delete_link(g2, g2); | |
124 } | |
125 | |
13925 | 126 g_free(my_bare_jid); |
7014 | 127 g_slist_free(buddies); |
128 } | |
129 | |
130 void jabber_roster_parse(JabberStream *js, xmlnode *packet) | |
131 { | |
132 xmlnode *query, *item, *group; | |
133 const char *from = xmlnode_get_attrib(packet, "from"); | |
7310 | 134 |
135 if(from) { | |
7445 | 136 char *from_norm; |
7310 | 137 gboolean invalid; |
7175 | 138 |
7445 | 139 from_norm = g_strdup(jabber_normalize(js->gc->account, from)); |
140 | |
141 if(!from_norm) | |
7310 | 142 return; |
143 | |
7445 | 144 invalid = g_utf8_collate(from_norm, |
145 jabber_normalize(js->gc->account, | |
146 gaim_account_get_username(js->gc->account))); | |
7175 | 147 |
7310 | 148 g_free(from_norm); |
149 | |
150 if(invalid) | |
151 return; | |
7175 | 152 } |
153 | |
7014 | 154 query = xmlnode_get_child(packet, "query"); |
155 if(!query) | |
156 return; | |
157 | |
158 js->roster_parsed = TRUE; | |
159 | |
8135 | 160 for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item)) |
7014 | 161 { |
162 const char *jid, *name, *subscription, *ask; | |
163 JabberBuddy *jb; | |
164 | |
165 subscription = xmlnode_get_attrib(item, "subscription"); | |
166 jid = xmlnode_get_attrib(item, "jid"); | |
167 name = xmlnode_get_attrib(item, "name"); | |
168 ask = xmlnode_get_attrib(item, "ask"); | |
169 | |
8347 | 170 if(!jid) |
171 continue; | |
172 | |
173 if(!(jb = jabber_buddy_find(js, jid, TRUE))) | |
174 continue; | |
7014 | 175 |
10289 | 176 if(subscription) { |
12285
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
177 gint me = -1; |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
178 char *jid_norm; |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
179 const char *username; |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
180 |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
181 jid_norm = g_strdup(jabber_normalize(js->gc->account, jid)); |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
182 username = gaim_account_get_username(js->gc->account); |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
183 me = g_utf8_collate(jid_norm, |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
184 jabber_normalize(js->gc->account, |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
185 username)); |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
186 |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
187 if(me == 0) |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
188 jb->subscription = JABBER_SUB_BOTH; |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
189 else if(!strcmp(subscription, "none")) |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
190 jb->subscription = JABBER_SUB_NONE; |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
191 else if(!strcmp(subscription, "to")) |
10289 | 192 jb->subscription = JABBER_SUB_TO; |
193 else if(!strcmp(subscription, "from")) | |
194 jb->subscription = JABBER_SUB_FROM; | |
195 else if(!strcmp(subscription, "both")) | |
196 jb->subscription = JABBER_SUB_BOTH; | |
197 else if(!strcmp(subscription, "remove")) | |
198 jb->subscription = JABBER_SUB_REMOVE; | |
10941 | 199 /* XXX: if subscription is now "from" or "none" we need to |
200 * fake a signoff, since we won't get any presence from them | |
201 * anymore */ | |
12285
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
202 /* YYY: I was going to use this, but I'm not sure it's necessary |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
203 * anymore, but it's here in case it is. */ |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
204 /* |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
205 if ((jb->subscription & JABBER_SUB_FROM) || |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
206 (jb->subscription & JABBER_SUB_NONE)) { |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
207 gaim_prpl_got_user_status(js->gc->account, jid, "offline", NULL); |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
208 } |
af257d8679fe
[gaim-migrate @ 14589]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12088
diff
changeset
|
209 */ |
10289 | 210 } |
7014 | 211 |
212 if(ask && !strcmp(ask, "subscribe")) | |
213 jb->subscription |= JABBER_SUB_PENDING; | |
214 else | |
215 jb->subscription &= ~JABBER_SUB_PENDING; | |
216 | |
8194 | 217 if(jb->subscription == JABBER_SUB_REMOVE) { |
7014 | 218 remove_gaim_buddies(js, jid); |
219 } else { | |
220 GSList *groups = NULL; | |
8135 | 221 for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_next_twin(group)) { |
7316 | 222 char *group_name; |
223 | |
224 if(!(group_name = xmlnode_get_data(group))) | |
225 group_name = g_strdup(""); | |
13807 | 226 |
227 if (g_slist_find_custom(groups, group_name, (GCompareFunc)gaim_utf8_strcasecmp) == NULL) | |
228 groups = g_slist_append(groups, group_name); | |
7014 | 229 } |
230 add_gaim_buddies_in_groups(js, jid, name, groups); | |
231 } | |
232 } | |
233 } | |
234 | |
235 static void jabber_roster_update(JabberStream *js, const char *name, | |
236 GSList *grps) | |
237 { | |
238 GaimBuddy *b; | |
239 GaimGroup *g; | |
240 GSList *groups = NULL, *l; | |
241 JabberIq *iq; | |
242 xmlnode *query, *item, *group; | |
243 | |
244 if(grps) { | |
245 groups = grps; | |
246 } else { | |
247 GSList *buddies = gaim_find_buddies(js->gc->account, name); | |
248 if(!buddies) | |
249 return; | |
250 while(buddies) { | |
251 b = buddies->data; | |
12088 | 252 g = gaim_buddy_get_group(b); |
7014 | 253 groups = g_slist_append(groups, g->name); |
254 buddies = g_slist_remove(buddies, b); | |
255 } | |
256 } | |
257 | |
8120 | 258 if(!(b = gaim_find_buddy(js->gc->account, name))) |
259 return; | |
7014 | 260 |
261 iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:roster"); | |
262 | |
263 query = xmlnode_get_child(iq->node, "query"); | |
264 item = xmlnode_new_child(query, "item"); | |
265 | |
266 xmlnode_set_attrib(item, "jid", name); | |
267 | |
268 if(b->alias) | |
269 xmlnode_set_attrib(item, "name", b->alias); | |
270 | |
271 for(l = groups; l; l = l->next) { | |
272 group = xmlnode_new_child(item, "group"); | |
273 xmlnode_insert_data(group, l->data, -1); | |
274 } | |
275 | |
276 if(!grps) | |
277 g_slist_free(groups); | |
278 | |
279 jabber_iq_send(iq); | |
280 } | |
281 | |
9285 | 282 void jabber_roster_add_buddy(GaimConnection *gc, GaimBuddy *buddy, |
283 GaimGroup *group) | |
7014 | 284 { |
285 JabberStream *js = gc->proto_data; | |
286 char *who; | |
7425 | 287 GSList *groups = NULL; |
7014 | 288 JabberBuddy *jb; |
7488 | 289 JabberBuddyResource *jbr; |
8194 | 290 char *my_bare_jid; |
7014 | 291 |
292 if(!js->roster_parsed) | |
293 return; | |
294 | |
9285 | 295 if(!(who = jabber_get_bare_jid(buddy->name))) |
7425 | 296 return; |
7014 | 297 |
9285 | 298 jb = jabber_buddy_find(js, buddy->name, FALSE); |
7425 | 299 |
300 if(!jb || !(jb->subscription & JABBER_SUB_TO)) { | |
9285 | 301 groups = g_slist_append(groups, group->name); |
7425 | 302 } |
7014 | 303 |
7449 | 304 jabber_roster_update(js, who, groups); |
7014 | 305 |
8194 | 306 my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); |
9954 | 307 if(!strcmp(who, my_bare_jid)) { |
308 GaimPresence *gpresence; | |
309 GaimStatus *status; | |
310 | |
311 gpresence = gaim_account_get_presence(js->gc->account); | |
312 status = gaim_presence_get_active_status(gpresence); | |
313 jabber_presence_fake_to_self(js, status); | |
314 } else if(!jb || !(jb->subscription & JABBER_SUB_TO)) { | |
7014 | 315 jabber_presence_subscription_set(js, who, "subscribe"); |
9954 | 316 } else if((jbr =jabber_buddy_find_resource(jb, NULL))) { |
317 gaim_prpl_got_user_status(gc->account, who, | |
318 jabber_buddy_state_get_status_id(jbr->state), | |
9990 | 319 "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL); |
9954 | 320 } |
7425 | 321 |
8194 | 322 g_free(my_bare_jid); |
7014 | 323 g_free(who); |
324 } | |
325 | |
326 void jabber_roster_alias_change(GaimConnection *gc, const char *name, const char *alias) | |
327 { | |
7449 | 328 GaimBuddy *b = gaim_find_buddy(gc->account, name); |
7482 | 329 char *a; |
7449 | 330 |
7482 | 331 a = g_strdup(alias); |
332 gaim_blist_alias_buddy(b, a); | |
333 g_free(a); | |
7449 | 334 |
7014 | 335 jabber_roster_update(gc->proto_data, name, NULL); |
336 } | |
337 | |
338 void jabber_roster_group_change(GaimConnection *gc, const char *name, | |
339 const char *old_group, const char *new_group) | |
340 { | |
341 GSList *buddies, *groups = NULL; | |
342 GaimBuddy *b; | |
343 GaimGroup *g; | |
344 | |
345 if(!old_group || !new_group || !strcmp(old_group, new_group)) | |
346 return; | |
347 | |
348 buddies = gaim_find_buddies(gc->account, name); | |
349 while(buddies) { | |
350 b = buddies->data; | |
12088 | 351 g = gaim_buddy_get_group(b); |
7014 | 352 if(!strcmp(g->name, old_group)) |
353 groups = g_slist_append(groups, (char*)new_group); /* ick */ | |
354 else | |
355 groups = g_slist_append(groups, g->name); | |
356 buddies = g_slist_remove(buddies, b); | |
357 } | |
358 jabber_roster_update(gc->proto_data, name, groups); | |
359 g_slist_free(groups); | |
360 } | |
361 | |
9285 | 362 void jabber_roster_group_rename(GaimConnection *gc, const char *old_name, |
363 GaimGroup *group, GList *moved_buddies) | |
7014 | 364 { |
365 GList *l; | |
9285 | 366 for(l = moved_buddies; l; l = l->next) { |
367 GaimBuddy *buddy = l->data; | |
368 jabber_roster_group_change(gc, buddy->name, old_name, group->name); | |
7014 | 369 } |
370 } | |
371 | |
9285 | 372 void jabber_roster_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, |
373 GaimGroup *group) { | |
374 GSList *buddies = gaim_find_buddies(gc->account, buddy->name); | |
7014 | 375 GSList *groups = NULL; |
376 | |
9285 | 377 buddies = g_slist_remove(buddies, buddy); |
7014 | 378 if(g_slist_length(buddies)) { |
9285 | 379 GaimBuddy *tmpbuddy; |
380 GaimGroup *tmpgroup; | |
381 | |
7014 | 382 while(buddies) { |
9285 | 383 tmpbuddy = buddies->data; |
12088 | 384 tmpgroup = gaim_buddy_get_group(tmpbuddy); |
9285 | 385 groups = g_slist_append(groups, tmpgroup->name); |
386 buddies = g_slist_remove(buddies, tmpbuddy); | |
7014 | 387 } |
9285 | 388 |
389 jabber_roster_update(gc->proto_data, buddy->name, groups); | |
7014 | 390 } else { |
7171 | 391 JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET, |
392 "jabber:iq:roster"); | |
393 xmlnode *query = xmlnode_get_child(iq->node, "query"); | |
394 xmlnode *item = xmlnode_new_child(query, "item"); | |
395 | |
9285 | 396 xmlnode_set_attrib(item, "jid", buddy->name); |
7171 | 397 xmlnode_set_attrib(item, "subscription", "remove"); |
398 | |
399 jabber_iq_send(iq); | |
7014 | 400 } |
401 | |
402 if(buddies) | |
403 g_slist_free(buddies); | |
404 if(groups) | |
405 g_slist_free(groups); | |
406 } |