1 /*
2 * Copyright (C) 2007 Colin DIDIER
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "module.h"
22 #include "signals.h"
23
24 #include "xmpp-servers.h"
25 #include "rosters-tools.h"
26 #include "tools.h"
27
28 #define XMLNS_ROSTER "jabber:iq:roster"
29
30 const char *xmpp_presence_show[] = {
31 "-",
32 "X",
33 "xa",
34 "dnd",
35 "away",
36 "+",
37 "chat",
38 "online",
39 NULL
40 };
41
42 const char *xmpp_subscription[] = {
43 "remove",
44 "none",
45 "to",
46 "from",
47 "both",
48 NULL
49 };
50
51 static int
func_find_group(gconstpointer group,gconstpointer name)52 func_find_group(gconstpointer group, gconstpointer name)
53 {
54 char *group_name;
55
56 group_name = ((XMPP_ROSTER_GROUP_REC *)group)->name;
57 if (group_name == name)
58 return 0;
59 if (group_name == NULL || name == NULL)
60 return -1;
61 return strcmp(group_name, name);
62 }
63
64 static int
func_sort_group(gconstpointer group1,gconstpointer group2)65 func_sort_group(gconstpointer group1, gconstpointer group2)
66 {
67 char *group1_name, *group2_name;
68
69 group1_name = ((XMPP_ROSTER_GROUP_REC *)group1)->name;
70 group2_name = ((XMPP_ROSTER_GROUP_REC *)group2)->name;
71 if (group1_name == NULL)
72 return -1;
73 if (group2_name == NULL)
74 return 1;
75 return strcmp(group1_name, group2_name);
76 }
77
78 static int
func_sort_resource(gconstpointer resource1_ptr,gconstpointer resource2_ptr)79 func_sort_resource(gconstpointer resource1_ptr, gconstpointer resource2_ptr)
80 {
81 int cmp;
82 XMPP_ROSTER_RESOURCE_REC *resource1, *resource2;
83
84 resource1 = (XMPP_ROSTER_RESOURCE_REC *)resource1_ptr;
85 resource2 = (XMPP_ROSTER_RESOURCE_REC *)resource2_ptr;
86 if ((cmp = resource2->priority - resource1->priority) == 0
87 && (cmp = resource2->show - resource1->show) == 0
88 && resource1->name && resource2->name)
89 return strcmp(resource1->name, resource2->name);
90 return cmp;
91 }
92
93 static int
func_sort_user_by_name(XMPP_ROSTER_USER_REC * user1,XMPP_ROSTER_USER_REC * user2)94 func_sort_user_by_name(XMPP_ROSTER_USER_REC *user1, XMPP_ROSTER_USER_REC *user2)
95 {
96 if (user1->name == NULL && user2->name != NULL)
97 return strcmp(user1->jid, user2->name);
98 if (user1->name != NULL && user2->name == NULL)
99 return strcmp(user1->name, user2->jid);
100 if (user1->name != NULL && user2->name != NULL)
101 return strcmp(user1->name, user2->name);
102 return strcmp(user1->jid, user2->jid);
103 }
104
105 static int
func_sort_user(gconstpointer user1_ptr,gconstpointer user2_ptr)106 func_sort_user(gconstpointer user1_ptr, gconstpointer user2_ptr)
107 {
108 GSList *resources1_list, *resources2_list;
109 XMPP_ROSTER_USER_REC *user1, *user2;
110 XMPP_ROSTER_RESOURCE_REC *fisrt_resources1, *fisrt_resources2;
111
112 user1 = (XMPP_ROSTER_USER_REC *)user1_ptr;
113 resources1_list = user1->resources;
114 user2 = (XMPP_ROSTER_USER_REC *)user2_ptr;
115 resources2_list = user2->resources;
116 if (resources1_list == NULL && resources2_list == NULL
117 && user1->error == user2->error)
118 return func_sort_user_by_name(user1, user2);
119 if (user1->error || resources1_list == NULL)
120 return 1;
121 if (user2->error || resources2_list == NULL)
122 return -1;
123 fisrt_resources1 = (XMPP_ROSTER_RESOURCE_REC *)resources1_list->data;
124 fisrt_resources2 = (XMPP_ROSTER_RESOURCE_REC *)resources2_list->data;
125 if (fisrt_resources1->show == fisrt_resources2->show)
126 return func_sort_user_by_name(user1, user2);
127 return fisrt_resources2->show - fisrt_resources1->show;
128 }
129
130 static XMPP_ROSTER_RESOURCE_REC *
create_resource(const char * name)131 create_resource(const char *name)
132 {
133 XMPP_ROSTER_RESOURCE_REC *resource;
134
135 resource = g_new(XMPP_ROSTER_RESOURCE_REC, 1);
136 resource->name = g_strdup(name == NULL ? "" : name);
137 resource->priority = 0;
138 resource->show= XMPP_PRESENCE_UNAVAILABLE;
139 resource->status = NULL;
140 resource->composing_id = NULL;
141 return resource;
142 }
143
144 static void
cleanup_resource(gpointer data,gpointer user_data)145 cleanup_resource(gpointer data, gpointer user_data)
146 {
147 XMPP_ROSTER_RESOURCE_REC *resource;
148
149 if (data == NULL)
150 return;
151 resource = (XMPP_ROSTER_RESOURCE_REC *)data;
152 g_free(resource->name);
153 g_free(resource->status);
154 g_free(resource->composing_id);
155 g_free(resource);
156 }
157
158 static XMPP_ROSTER_USER_REC *
create_user(const char * jid,const char * name)159 create_user(const char *jid, const char *name)
160 {
161 XMPP_ROSTER_USER_REC *user;
162
163 g_return_val_if_fail(jid != NULL, NULL);
164 user = g_new(XMPP_ROSTER_USER_REC, 1);
165 user->jid = g_strdup(jid);
166 user->name = g_strdup(name);
167 user->subscription = XMPP_SUBSCRIPTION_NONE;
168 user->error = FALSE;
169 user->resources = NULL;
170 return user;
171 }
172
173 static void
cleanup_user(gpointer data,gpointer user_data)174 cleanup_user(gpointer data, gpointer user_data)
175 {
176 XMPP_ROSTER_USER_REC *user;
177
178 if (data == NULL)
179 return;
180 user = (XMPP_ROSTER_USER_REC *)data;
181 g_slist_foreach(user->resources, cleanup_resource, NULL);
182 g_slist_free(user->resources);
183 g_free(user->name);
184 g_free(user->jid);
185 g_free(user);
186 }
187
188 static XMPP_ROSTER_GROUP_REC *
create_group(const char * name)189 create_group(const char *name)
190 {
191 XMPP_ROSTER_GROUP_REC *group;
192
193 group = g_new(XMPP_ROSTER_GROUP_REC, 1);
194 group->name = g_strdup(name);
195 group->users = NULL;
196 return group;
197 }
198
199 static void
cleanup_group(gpointer data,gpointer user_data)200 cleanup_group(gpointer data, gpointer user_data)
201 {
202 XMPP_ROSTER_GROUP_REC *group;
203
204 if (data == NULL)
205 return;
206 group = (XMPP_ROSTER_GROUP_REC *)data;
207 g_slist_foreach(group->users, cleanup_user, group);
208 g_slist_free(group->users);
209 g_free(group->name);
210 g_free(group);
211 }
212
213 static void
roster_cleanup(XMPP_SERVER_REC * server)214 roster_cleanup(XMPP_SERVER_REC *server)
215 {
216 if (!IS_XMPP_SERVER(server) || server->roster == NULL)
217 return;
218 g_slist_foreach(server->roster, cleanup_group, server);
219 g_slist_free(server->roster);
220 server->roster = NULL;
221 g_slist_foreach(server->my_resources, cleanup_resource, NULL);
222 g_slist_free(server->my_resources);
223 server->my_resources = NULL;
224 }
225
226 static XMPP_ROSTER_GROUP_REC *
find_or_add_group(XMPP_SERVER_REC * server,const char * group_name)227 find_or_add_group(XMPP_SERVER_REC *server, const char *group_name)
228 {
229 GSList *group_list;
230 XMPP_ROSTER_GROUP_REC *group;
231
232 g_return_val_if_fail(IS_XMPP_SERVER(server), NULL);
233 group_list = g_slist_find_custom(server->roster, group_name,
234 func_find_group);
235 if (group_list == NULL) {
236 group = create_group(group_name);
237 server->roster = g_slist_insert_sorted(server->roster, group,
238 func_sort_group);
239 } else
240 group = group_list->data;
241 return group;
242 }
243
244 static XMPP_ROSTER_USER_REC *
add_user(XMPP_SERVER_REC * server,const char * jid,const char * name,const char * group_name,XMPP_ROSTER_GROUP_REC ** return_group)245 add_user(XMPP_SERVER_REC *server, const char *jid, const char *name,
246 const char *group_name, XMPP_ROSTER_GROUP_REC **return_group)
247 {
248 XMPP_ROSTER_GROUP_REC *group;
249 XMPP_ROSTER_USER_REC *user;
250
251 g_return_val_if_fail(IS_XMPP_SERVER(server), NULL);
252 g_return_val_if_fail(jid != NULL, NULL);
253 group = find_or_add_group(server, group_name);
254 user = create_user(jid, name);
255 group->users = g_slist_append(group->users, user);
256 if (return_group != NULL)
257 *return_group = group;
258 return user;
259 }
260
261 static XMPP_ROSTER_GROUP_REC *
move_user(XMPP_SERVER_REC * server,XMPP_ROSTER_USER_REC * user,XMPP_ROSTER_GROUP_REC * group,const char * group_name)262 move_user(XMPP_SERVER_REC *server, XMPP_ROSTER_USER_REC *user,
263 XMPP_ROSTER_GROUP_REC *group, const char *group_name)
264 {
265 XMPP_ROSTER_GROUP_REC *new_group;
266
267 g_return_val_if_fail(IS_XMPP_SERVER(server), group);
268 g_return_val_if_fail(user != NULL, group);
269 new_group = find_or_add_group(server, group_name);
270 group->users = g_slist_remove(group->users, user);
271 new_group->users = g_slist_append(new_group->users, user);
272 return new_group;
273 }
274
275 static void
update_subscription(XMPP_SERVER_REC * server,XMPP_ROSTER_USER_REC * user,XMPP_ROSTER_GROUP_REC * group,const char * subscription)276 update_subscription(XMPP_SERVER_REC *server, XMPP_ROSTER_USER_REC *user,
277 XMPP_ROSTER_GROUP_REC *group, const char *subscription)
278 {
279 g_return_if_fail(IS_XMPP_SERVER(server));
280 g_return_if_fail(user != NULL);
281 g_return_if_fail(group != NULL);
282 g_return_if_fail(subscription != NULL);
283 if (g_ascii_strcasecmp(subscription,
284 xmpp_subscription[XMPP_SUBSCRIPTION_NONE]) == 0)
285 user->subscription = XMPP_SUBSCRIPTION_NONE;
286 else if (g_ascii_strcasecmp(subscription,
287 xmpp_subscription[XMPP_SUBSCRIPTION_FROM]) == 0)
288 user->subscription = XMPP_SUBSCRIPTION_FROM;
289 else if (g_ascii_strcasecmp(subscription,
290 xmpp_subscription[XMPP_SUBSCRIPTION_TO]) == 0)
291 user->subscription = XMPP_SUBSCRIPTION_TO;
292 else if (g_ascii_strcasecmp(subscription,
293 xmpp_subscription[XMPP_SUBSCRIPTION_BOTH]) == 0)
294 user->subscription = XMPP_SUBSCRIPTION_BOTH;
295 else if (g_ascii_strcasecmp(subscription,
296 xmpp_subscription[XMPP_SUBSCRIPTION_REMOVE]) == 0) {
297 group->users = g_slist_remove(group->users, user);
298 cleanup_user(user, server);
299 /* remove empty group */
300 if (group->users == NULL) {
301 server->roster = g_slist_remove(server->roster, group);
302 cleanup_group(group, server);
303 }
304 }
305 }
306
307 static void
update_user(XMPP_SERVER_REC * server,const char * jid,const char * subscription,const char * name,const char * group_name)308 update_user(XMPP_SERVER_REC *server, const char *jid, const char *subscription,
309 const char *name, const char *group_name)
310 {
311 XMPP_ROSTER_GROUP_REC *group;
312 XMPP_ROSTER_USER_REC *user;
313
314 g_return_if_fail(IS_XMPP_SERVER(server));
315 g_return_if_fail(jid != NULL);
316 user = rosters_find_user(server->roster, jid, &group, NULL);
317 if (user == NULL)
318 user = add_user(server, jid, name, group_name, &group);
319 else {
320 /* move to another group and sort it */
321 if ((group->name == NULL && group_name != NULL)
322 || (group->name != NULL && group_name == NULL)
323 || (group->name != NULL && group_name != NULL
324 && strcmp(group->name, group_name) != 0)) {
325 group = move_user(server, user, group, group_name);
326 group->users = g_slist_sort(group->users,
327 func_sort_user);
328 }
329 /* change name */
330 if ((user->name == NULL && name != NULL)
331 || (user->name != NULL && name == NULL)
332 || (user->name != NULL && name != NULL
333 && strcmp(user->name, name) != 0)) {
334 g_free(user->name);
335 user->name = g_strdup(name);
336 group->users = g_slist_sort(group->users,
337 func_sort_user);
338 }
339 }
340 update_subscription(server, user, group, subscription);
341 }
342
343 static void
update_user_presence(XMPP_SERVER_REC * server,const char * full_jid,const char * show_str,const char * status,const char * priority_str)344 update_user_presence(XMPP_SERVER_REC *server, const char *full_jid,
345 const char *show_str, const char *status, const char *priority_str)
346 {
347 XMPP_ROSTER_GROUP_REC *group;
348 XMPP_ROSTER_USER_REC *user;
349 XMPP_ROSTER_RESOURCE_REC *resource;
350 char *jid, *res;
351 int show, priority;
352 gboolean new, own;
353
354 g_return_if_fail(IS_XMPP_SERVER(server));
355 g_return_if_fail(full_jid != NULL);
356 new = own = FALSE;
357 jid = xmpp_strip_resource(full_jid);
358 res = xmpp_extract_resource(full_jid);
359 user = rosters_find_user(server->roster, jid, &group, NULL);
360 if (user == NULL) {
361 if (!(own = strcmp(jid, server->jid) == 0
362 && strcmp(res, server->resource) != 0))
363 goto out;
364 } else
365 user->error = FALSE;
366 /* find resource or create it if it doesn't exist */
367 resource = rosters_find_resource(!own ?
368 user->resources : server->my_resources, res);
369 if (resource == NULL) {
370 resource = create_resource(res);
371 new = TRUE;
372 if (!own)
373 user->resources =
374 g_slist_prepend(user->resources, resource);
375 else
376 server->my_resources =
377 g_slist_prepend(server->my_resources, resource);
378 signal_emit("xmpp presence online", 4, server, full_jid,
379 jid, res);
380 }
381 show = xmpp_get_show(show_str);
382 priority = (priority_str != NULL) ?
383 atoi(priority_str) : resource->priority;
384 if (new || xmpp_presence_changed(show, resource->show, status,
385 resource->status, priority, resource->priority)) {
386 resource->show = show;
387 resource->status = g_strdup(status);
388 resource->priority = priority;
389 if (!own) {
390 user->resources = g_slist_sort(
391 user->resources, func_sort_resource);
392 group->users = g_slist_sort(group->users,
393 func_sort_user);
394 } else
395 server->my_resources = g_slist_sort(
396 server->my_resources, func_sort_resource);
397 signal_emit("xmpp presence changed", 4, server, full_jid,
398 resource->show, resource->status);
399 }
400
401 out:
402 g_free(jid);
403 g_free(res);
404 }
405
406 static void
user_unavailable(XMPP_SERVER_REC * server,const char * full_jid,const char * status)407 user_unavailable(XMPP_SERVER_REC *server, const char *full_jid,
408 const char *status)
409 {
410 XMPP_ROSTER_GROUP_REC *group;
411 XMPP_ROSTER_USER_REC *user;
412 XMPP_ROSTER_RESOURCE_REC *resource;
413 char *jid, *res;
414 gboolean own;
415
416 g_return_if_fail(IS_XMPP_SERVER(server));
417 g_return_if_fail(full_jid != NULL);
418 own = FALSE;
419 jid = xmpp_strip_resource(full_jid);
420 res = xmpp_extract_resource(full_jid);
421 user = rosters_find_user(server->roster, jid, &group, NULL);
422 if (user == NULL) {
423 if (!(own = strcmp(jid, server->jid) == 0))
424 goto out;
425 } else
426 user->error = FALSE;
427 resource = rosters_find_resource(!own ?
428 user->resources : server->my_resources, res);
429 if (resource == NULL)
430 goto out;
431 signal_emit("xmpp presence offline", 4, server, full_jid, jid, res);
432 signal_emit("xmpp presence changed", 4, server, full_jid,
433 XMPP_PRESENCE_UNAVAILABLE, status);
434 if (!own)
435 user->resources = g_slist_remove(user->resources, resource);
436 else
437 server->my_resources = g_slist_remove(server->my_resources,
438 resource);
439 cleanup_resource(resource, NULL);
440 if (!own) /* sort the group */
441 group->users = g_slist_sort(group->users, func_sort_user);
442
443 out:
444 g_free(jid);
445 g_free(res);
446 }
447
448 static void
user_presence_error(XMPP_SERVER_REC * server,const char * full_jid)449 user_presence_error(XMPP_SERVER_REC *server, const char *full_jid)
450 {
451 XMPP_ROSTER_GROUP_REC *group;
452 XMPP_ROSTER_USER_REC *user;
453 XMPP_ROSTER_RESOURCE_REC *resource;
454 char *jid, *res;
455 gboolean own;
456
457 g_return_if_fail(IS_XMPP_SERVER(server));
458 g_return_if_fail(full_jid != NULL);
459 own = FALSE;
460 jid = xmpp_strip_resource(full_jid);
461 res = xmpp_extract_resource(full_jid);
462 user = rosters_find_user(server->roster, jid, &group, NULL);
463 if (user == NULL && !(own = strcmp(jid, server->jid) == 0))
464 goto out;
465 resource = rosters_find_resource(!own ?
466 user->resources : server->my_resources, res);
467 if (resource != NULL) {
468 resource->show = XMPP_PRESENCE_ERROR;
469 if (!own) /* sort the group */
470 group->users = g_slist_sort(group->users,
471 func_sort_user);
472 signal_emit("xmpp presence changed", 4, server, full_jid,
473 XMPP_PRESENCE_ERROR, NULL);
474 } else if (user != NULL)
475 user->error = TRUE;
476
477 out:
478 g_free(jid);
479 g_free(res);
480 }
481
482
483 static void
sig_recv_presence(XMPP_SERVER_REC * server,LmMessage * lmsg,const int type,const char * id,const char * from,const char * to)484 sig_recv_presence(XMPP_SERVER_REC *server, LmMessage *lmsg, const int type,
485 const char *id, const char *from, const char *to)
486 {
487 LmMessageNode *node, *node_show, *node_priority;
488 char *status;
489
490 if (server->ischannel(SERVER(server), from))
491 return;
492 switch(type) {
493 case LM_MESSAGE_SUB_TYPE_AVAILABLE:
494 node_show = lm_message_node_get_child(lmsg->node, "show");
495 node = lm_message_node_get_child(lmsg->node, "status");
496 status = node != NULL ? xmpp_recode_in(node->value) : NULL;
497 node_priority = lm_message_node_get_child(lmsg->node, "priority");
498 update_user_presence(server, from,
499 node_show != NULL ? node_show->value : NULL, status,
500 node_priority != NULL ? node_priority->value : NULL);
501 g_free(status);
502 break;
503 case LM_MESSAGE_SUB_TYPE_UNAVAILABLE:
504 node = lm_message_node_get_child(lmsg->node, "status");
505 status = node != NULL ? xmpp_recode_in(node->value) : NULL;
506 user_unavailable(server, from, status);
507 g_free(status);
508 break;
509 case LM_MESSAGE_SUB_TYPE_SUBSCRIBE:
510 node = lm_message_node_get_child(lmsg->node, "status");
511 status = node != NULL ? xmpp_recode_in(node->value) : NULL;
512 signal_emit("xmpp presence subscribe", 3, server, from, status);
513 g_free(status);
514 break;
515 case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE:
516 signal_emit("xmpp presence unsubscribe", 2, server, from);
517 break;
518 case LM_MESSAGE_SUB_TYPE_SUBSCRIBED:
519 signal_emit("xmpp presence subscribed", 2, server, from);
520 break;
521 case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED:
522 signal_emit("xmpp presence unsubscribed", 2, server, from);
523 break;
524 case LM_MESSAGE_SUB_TYPE_ERROR:
525 user_presence_error(server, from);
526 break;
527 }
528 }
529
530 static void
sig_recv_iq(XMPP_SERVER_REC * server,LmMessage * lmsg,const int type,const char * id,const char * from,const char * to)531 sig_recv_iq(XMPP_SERVER_REC *server, LmMessage *lmsg, const int type,
532 const char *id, const char *from, const char *to)
533 {
534 LmMessageNode *node, *item, *group_node;
535 char *jid, *name, *group;
536 const char *subscription;
537
538 if (type != LM_MESSAGE_SUB_TYPE_RESULT
539 && type != LM_MESSAGE_SUB_TYPE_SET)
540 return;
541 node = lm_find_node(lmsg->node, "query", "xmlns", XMLNS_ROSTER);
542 if (node == NULL)
543 return;
544 for (item = node->children; item != NULL; item = item->next) {
545 if (strcmp(item->name, "item") != 0)
546 continue;
547 jid = xmpp_recode_in(lm_message_node_get_attribute(item, "jid"));
548 name = xmpp_recode_in(lm_message_node_get_attribute(item, "name"));
549 group_node = lm_message_node_get_child(item, "group");
550 group = group_node != NULL ?
551 xmpp_recode_in(group_node->value) : NULL;
552 subscription = lm_message_node_get_attribute(item,
553 "subscription");
554 update_user(server, jid, subscription, name, group);
555 g_free(jid);
556 g_free(name);
557 g_free(group);
558 }
559 }
560
561 static void
sig_connected(XMPP_SERVER_REC * server)562 sig_connected(XMPP_SERVER_REC *server)
563 {
564 LmMessage *lmsg;
565 LmMessageNode *node;
566
567 if (!IS_XMPP_SERVER(server))
568 return;
569 signal_emit("xmpp server status", 2, server, "Requesting the roster.");
570 lmsg = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
571 LM_MESSAGE_SUB_TYPE_GET);
572 node = lm_message_node_add_child(lmsg->node, "query", NULL);
573 lm_message_node_set_attribute(node, "xmlns", "jabber:iq:roster");
574 signal_emit("xmpp send iq", 2, server, lmsg);
575 lm_message_unref(lmsg);
576 }
577
578 void
rosters_init(void)579 rosters_init(void)
580 {
581 signal_add("server connected", sig_connected);
582 signal_add_first("server disconnected", roster_cleanup);
583 signal_add("xmpp recv presence", sig_recv_presence);
584 signal_add("xmpp recv iq", sig_recv_iq);
585 }
586
587 void
rosters_deinit(void)588 rosters_deinit(void)
589 {
590 signal_remove("server connected", sig_connected);
591 signal_remove("server disconnected", roster_cleanup);
592 signal_remove("xmpp recv presence", sig_recv_presence);
593 signal_remove("xmpp recv iq", sig_recv_iq);
594 }
595