comparison libpurple/protocols/jabber/roster.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 2c81b0a81790
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
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"
24 #include "util.h"
25
26 #include "buddy.h"
27 #include "google.h"
28 #include "presence.h"
29 #include "roster.h"
30 #include "iq.h"
31
32 #include <string.h>
33
34
35 void jabber_roster_request(JabberStream *js)
36 {
37 JabberIq *iq;
38
39 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "jabber:iq:roster");
40
41 jabber_iq_send(iq);
42 }
43
44 static void remove_gaim_buddies(JabberStream *js, const char *jid)
45 {
46 GSList *buddies, *l;
47
48 buddies = gaim_find_buddies(js->gc->account, jid);
49
50 for(l = buddies; l; l = l->next)
51 gaim_blist_remove_buddy(l->data);
52
53 g_slist_free(buddies);
54 }
55
56 static void add_gaim_buddies_in_groups(JabberStream *js, const char *jid,
57 const char *alias, GSList *groups)
58 {
59 GSList *buddies, *g2, *l;
60 gchar *my_bare_jid;
61
62 buddies = gaim_find_buddies(js->gc->account, jid);
63
64 g2 = groups;
65
66 if(!groups) {
67 if(!buddies)
68 g2 = g_slist_append(g2, g_strdup(_("Buddies")));
69 else
70 return;
71 }
72
73 my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
74
75 while(buddies) {
76 GaimBuddy *b = buddies->data;
77 GaimGroup *g = gaim_buddy_get_group(b);
78
79 buddies = g_slist_remove(buddies, b);
80
81 if((l = g_slist_find_custom(g2, g->name, (GCompareFunc)strcmp))) {
82 const char *servernick;
83
84 if((servernick = gaim_blist_node_get_string((GaimBlistNode*)b, "servernick")))
85 serv_got_alias(js->gc, jid, servernick);
86
87 if(alias && (!b->alias || strcmp(b->alias, alias)))
88 gaim_blist_alias_buddy(b, alias);
89 g_free(l->data);
90 g2 = g_slist_delete_link(g2, l);
91 } else {
92 gaim_blist_remove_buddy(b);
93 }
94 }
95
96 while(g2) {
97 GaimBuddy *b = gaim_buddy_new(js->gc->account, jid, alias);
98 GaimGroup *g = gaim_find_group(g2->data);
99
100 if(!g) {
101 g = gaim_group_new(g2->data);
102 gaim_blist_add_group(g, NULL);
103 }
104
105 gaim_blist_add_buddy(b, NULL, g, NULL);
106 gaim_blist_alias_buddy(b, alias);
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 g_free(g2->data);
121 g2 = g_slist_delete_link(g2, g2);
122 }
123
124 g_free(my_bare_jid);
125 g_slist_free(buddies);
126 }
127
128 void jabber_roster_parse(JabberStream *js, xmlnode *packet)
129 {
130 xmlnode *query, *item, *group;
131 const char *from = xmlnode_get_attrib(packet, "from");
132
133 if(from) {
134 char *from_norm;
135 gboolean invalid;
136
137 from_norm = g_strdup(jabber_normalize(js->gc->account, from));
138
139 if(!from_norm)
140 return;
141
142 invalid = g_utf8_collate(from_norm,
143 jabber_normalize(js->gc->account,
144 gaim_account_get_username(js->gc->account)));
145
146 g_free(from_norm);
147
148 if(invalid)
149 return;
150 }
151
152 query = xmlnode_get_child(packet, "query");
153 if(!query)
154 return;
155
156 js->roster_parsed = TRUE;
157
158 for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
159 {
160 const char *jid, *name, *subscription, *ask;
161 JabberBuddy *jb;
162
163 subscription = xmlnode_get_attrib(item, "subscription");
164 jid = xmlnode_get_attrib(item, "jid");
165 name = xmlnode_get_attrib(item, "name");
166 ask = xmlnode_get_attrib(item, "ask");
167
168 if(!jid)
169 continue;
170
171 if(!(jb = jabber_buddy_find(js, jid, TRUE)))
172 continue;
173
174 if(subscription) {
175 gint me = -1;
176 char *jid_norm;
177 const char *username;
178
179 jid_norm = g_strdup(jabber_normalize(js->gc->account, jid));
180 username = gaim_account_get_username(js->gc->account);
181 me = g_utf8_collate(jid_norm,
182 jabber_normalize(js->gc->account,
183 username));
184
185 if(me == 0)
186 jb->subscription = JABBER_SUB_BOTH;
187 else if(!strcmp(subscription, "none"))
188 jb->subscription = JABBER_SUB_NONE;
189 else if(!strcmp(subscription, "to"))
190 jb->subscription = JABBER_SUB_TO;
191 else if(!strcmp(subscription, "from"))
192 jb->subscription = JABBER_SUB_FROM;
193 else if(!strcmp(subscription, "both"))
194 jb->subscription = JABBER_SUB_BOTH;
195 else if(!strcmp(subscription, "remove"))
196 jb->subscription = JABBER_SUB_REMOVE;
197 /* XXX: if subscription is now "from" or "none" we need to
198 * fake a signoff, since we won't get any presence from them
199 * anymore */
200 /* YYY: I was going to use this, but I'm not sure it's necessary
201 * anymore, but it's here in case it is. */
202 /*
203 if ((jb->subscription & JABBER_SUB_FROM) ||
204 (jb->subscription & JABBER_SUB_NONE)) {
205 gaim_prpl_got_user_status(js->gc->account, jid, "offline", NULL);
206 }
207 */
208 }
209
210 if(ask && !strcmp(ask, "subscribe"))
211 jb->subscription |= JABBER_SUB_PENDING;
212 else
213 jb->subscription &= ~JABBER_SUB_PENDING;
214
215 if(jb->subscription == JABBER_SUB_REMOVE) {
216 remove_gaim_buddies(js, jid);
217 } else {
218 GSList *groups = NULL;
219 for(group = xmlnode_get_child(item, "group"); group; group = xmlnode_get_next_twin(group)) {
220 char *group_name;
221
222 if(!(group_name = xmlnode_get_data(group)))
223 group_name = g_strdup("");
224
225 if (g_slist_find_custom(groups, group_name, (GCompareFunc)gaim_utf8_strcasecmp) == NULL)
226 groups = g_slist_append(groups, group_name);
227 }
228 if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER)
229 jabber_google_roster_incoming(js, item);
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;
252 g = gaim_buddy_get_group(b);
253 groups = g_slist_append(groups, g->name);
254 buddies = g_slist_remove(buddies, b);
255 }
256 }
257
258 if(!(b = gaim_find_buddy(js->gc->account, name)))
259 return;
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 xmlnode_set_attrib(item, "name", b->alias ? b->alias : "");
269
270 for(l = groups; l; l = l->next) {
271 group = xmlnode_new_child(item, "group");
272 xmlnode_insert_data(group, l->data, -1);
273 }
274
275 if(!grps)
276 g_slist_free(groups);
277
278 if (js->server_caps & JABBER_CAP_GOOGLE_ROSTER) {
279 jabber_google_roster_outgoing(js, query, item);
280 xmlnode_set_attrib(query, "xmlns:gr", "google:roster");
281 xmlnode_set_attrib(query, "gr:ext", "2");
282 }
283 jabber_iq_send(iq);
284 }
285
286 void jabber_roster_add_buddy(GaimConnection *gc, GaimBuddy *buddy,
287 GaimGroup *group)
288 {
289 JabberStream *js = gc->proto_data;
290 char *who;
291 GSList *groups = NULL;
292 JabberBuddy *jb;
293 JabberBuddyResource *jbr;
294 char *my_bare_jid;
295
296 if(!js->roster_parsed)
297 return;
298
299 if(!(who = jabber_get_bare_jid(buddy->name)))
300 return;
301
302 jb = jabber_buddy_find(js, buddy->name, FALSE);
303
304 if(!jb || !(jb->subscription & JABBER_SUB_TO)) {
305 groups = g_slist_append(groups, group->name);
306 }
307
308 jabber_roster_update(js, who, groups);
309
310 my_bare_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain);
311 if(!strcmp(who, my_bare_jid)) {
312 GaimPresence *gpresence;
313 GaimStatus *status;
314
315 gpresence = gaim_account_get_presence(js->gc->account);
316 status = gaim_presence_get_active_status(gpresence);
317 jabber_presence_fake_to_self(js, status);
318 } else if(!jb || !(jb->subscription & JABBER_SUB_TO)) {
319 jabber_presence_subscription_set(js, who, "subscribe");
320 } else if((jbr =jabber_buddy_find_resource(jb, NULL))) {
321 gaim_prpl_got_user_status(gc->account, who,
322 jabber_buddy_state_get_status_id(jbr->state),
323 "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
324 }
325
326 g_free(my_bare_jid);
327 g_free(who);
328 }
329
330 void jabber_roster_alias_change(GaimConnection *gc, const char *name, const char *alias)
331 {
332 GaimBuddy *b = gaim_find_buddy(gc->account, name);
333
334 if(b != NULL) {
335 gaim_blist_alias_buddy(b, alias);
336
337 jabber_roster_update(gc->proto_data, name, NULL);
338 }
339 }
340
341 void jabber_roster_group_change(GaimConnection *gc, const char *name,
342 const char *old_group, const char *new_group)
343 {
344 GSList *buddies, *groups = NULL;
345 GaimBuddy *b;
346 GaimGroup *g;
347
348 if(!old_group || !new_group || !strcmp(old_group, new_group))
349 return;
350
351 buddies = gaim_find_buddies(gc->account, name);
352 while(buddies) {
353 b = buddies->data;
354 g = gaim_buddy_get_group(b);
355 if(!strcmp(g->name, old_group))
356 groups = g_slist_append(groups, (char*)new_group); /* ick */
357 else
358 groups = g_slist_append(groups, g->name);
359 buddies = g_slist_remove(buddies, b);
360 }
361 jabber_roster_update(gc->proto_data, name, groups);
362 g_slist_free(groups);
363 }
364
365 void jabber_roster_group_rename(GaimConnection *gc, const char *old_name,
366 GaimGroup *group, GList *moved_buddies)
367 {
368 GList *l;
369 for(l = moved_buddies; l; l = l->next) {
370 GaimBuddy *buddy = l->data;
371 jabber_roster_group_change(gc, buddy->name, old_name, group->name);
372 }
373 }
374
375 void jabber_roster_remove_buddy(GaimConnection *gc, GaimBuddy *buddy,
376 GaimGroup *group) {
377 GSList *buddies = gaim_find_buddies(gc->account, buddy->name);
378 GSList *groups = NULL;
379
380 buddies = g_slist_remove(buddies, buddy);
381 if(g_slist_length(buddies)) {
382 GaimBuddy *tmpbuddy;
383 GaimGroup *tmpgroup;
384
385 while(buddies) {
386 tmpbuddy = buddies->data;
387 tmpgroup = gaim_buddy_get_group(tmpbuddy);
388 groups = g_slist_append(groups, tmpgroup->name);
389 buddies = g_slist_remove(buddies, tmpbuddy);
390 }
391
392 jabber_roster_update(gc->proto_data, buddy->name, groups);
393 } else {
394 JabberIq *iq = jabber_iq_new_query(gc->proto_data, JABBER_IQ_SET,
395 "jabber:iq:roster");
396 xmlnode *query = xmlnode_get_child(iq->node, "query");
397 xmlnode *item = xmlnode_new_child(query, "item");
398
399 xmlnode_set_attrib(item, "jid", buddy->name);
400 xmlnode_set_attrib(item, "subscription", "remove");
401
402 jabber_iq_send(iq);
403 }
404
405 if(buddies)
406 g_slist_free(buddies);
407 if(groups)
408 g_slist_free(groups);
409 }