2
* Copyright (C) 2007 Colin DIDIER
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.
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.
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.
24
#include "xmpp-servers.h"
25
#include "rosters-tools.h"
28
#define XMLNS_ROSTER "jabber:iq:roster"
30
const char *xmpp_presence_show[] = {
42
const char *xmpp_subscription[] = {
52
func_find_group(gconstpointer group, gconstpointer name)
56
group_name = ((XMPP_ROSTER_GROUP_REC *)group)->name;
57
if (group_name == name)
59
if (group_name == NULL || name == NULL)
61
return strcmp(group_name, name);
65
func_sort_group(gconstpointer group1, gconstpointer group2)
67
char *group1_name, *group2_name;
69
group1_name = ((XMPP_ROSTER_GROUP_REC *)group1)->name;
70
group2_name = ((XMPP_ROSTER_GROUP_REC *)group2)->name;
71
if (group1_name == NULL)
73
if (group2_name == NULL)
75
return strcmp(group1_name, group2_name);
79
func_sort_resource(gconstpointer resource1_ptr, gconstpointer resource2_ptr)
82
XMPP_ROSTER_RESOURCE_REC *resource1, *resource2;
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
return strcmp(resource1->name, resource2->name);
93
func_sort_user_by_name(XMPP_ROSTER_USER_REC *user1, XMPP_ROSTER_USER_REC *user2)
95
if (user1->name == NULL && user2->name != NULL)
96
return strcmp(user1->jid, user2->name);
97
if (user1->name != NULL && user2->name == NULL)
98
return strcmp(user1->name, user2->jid);
99
if (user1->name != NULL && user2->name != NULL)
100
return strcmp(user1->name, user2->name);
101
return strcmp(user1->jid, user2->jid);
105
func_sort_user(gconstpointer user1_ptr, gconstpointer user2_ptr)
107
GSList *resources1_list, *resources2_list;
108
XMPP_ROSTER_USER_REC *user1, *user2;
109
XMPP_ROSTER_RESOURCE_REC *fisrt_resources1, *fisrt_resources2;
111
user1 = (XMPP_ROSTER_USER_REC *)user1_ptr;
112
resources1_list = user1->resources;
113
user2 = (XMPP_ROSTER_USER_REC *)user2_ptr;
114
resources2_list = user2->resources;
115
if (resources1_list == NULL && resources2_list == NULL
116
&& user1->error == user2->error)
117
return func_sort_user_by_name(user1, user2);
118
if (user1->error || resources1_list == NULL)
120
if (user2->error || resources2_list == NULL)
122
fisrt_resources1 = (XMPP_ROSTER_RESOURCE_REC *)resources1_list->data;
123
fisrt_resources2 = (XMPP_ROSTER_RESOURCE_REC *)resources2_list->data;
124
if (fisrt_resources1->show == fisrt_resources2->show)
125
return func_sort_user_by_name(user1, user2);
126
return fisrt_resources2->show - fisrt_resources1->show;
129
static XMPP_ROSTER_RESOURCE_REC *
130
create_resource(const char *name)
132
XMPP_ROSTER_RESOURCE_REC *resource;
134
resource = g_new(XMPP_ROSTER_RESOURCE_REC, 1);
135
resource->name = g_strdup(name == NULL ? "" : name);
136
resource->priority = 0;
137
resource->show= XMPP_PRESENCE_UNAVAILABLE;
138
resource->status = NULL;
139
resource->composing_id = NULL;
140
resource->pgp_keyid = NULL;
141
resource->pgp_encrypt = 0;
146
cleanup_resource(gpointer data, gpointer user_data)
148
XMPP_ROSTER_RESOURCE_REC *resource;
152
resource = (XMPP_ROSTER_RESOURCE_REC *)data;
153
g_free(resource->name);
154
g_free(resource->status);
155
g_free(resource->composing_id);
156
if(resource->pgp_keyid) free(resource->pgp_keyid);
160
static XMPP_ROSTER_USER_REC *
161
create_user(const char *jid, const char *name)
163
XMPP_ROSTER_USER_REC *user;
165
g_return_val_if_fail(jid != NULL, NULL);
166
user = g_new(XMPP_ROSTER_USER_REC, 1);
167
user->jid = g_strdup(jid);
168
user->name = g_strdup(name);
169
user->subscription = XMPP_SUBSCRIPTION_NONE;
171
user->resources = NULL;
176
cleanup_user(gpointer data, gpointer user_data)
178
XMPP_ROSTER_USER_REC *user;
182
user = (XMPP_ROSTER_USER_REC *)data;
183
g_slist_foreach(user->resources, cleanup_resource, NULL);
184
g_slist_free(user->resources);
190
static XMPP_ROSTER_GROUP_REC *
191
create_group(const char *name)
193
XMPP_ROSTER_GROUP_REC *group;
195
group = g_new(XMPP_ROSTER_GROUP_REC, 1);
196
group->name = g_strdup(name);
202
cleanup_group(gpointer data, gpointer user_data)
204
XMPP_ROSTER_GROUP_REC *group;
208
group = (XMPP_ROSTER_GROUP_REC *)data;
209
g_slist_foreach(group->users, cleanup_user, group);
210
g_slist_free(group->users);
216
roster_cleanup(XMPP_SERVER_REC *server)
218
if (!IS_XMPP_SERVER(server) || server->roster == NULL)
220
g_slist_foreach(server->roster, cleanup_group, server);
221
g_slist_free(server->roster);
222
server->roster = NULL;
223
g_slist_foreach(server->my_resources, cleanup_resource, NULL);
224
g_slist_free(server->my_resources);
225
server->my_resources = NULL;
228
static XMPP_ROSTER_GROUP_REC *
229
find_or_add_group(XMPP_SERVER_REC *server, const char *group_name)
232
XMPP_ROSTER_GROUP_REC *group;
234
g_return_val_if_fail(IS_XMPP_SERVER(server), NULL);
235
group_list = g_slist_find_custom(server->roster, group_name,
237
if (group_list == NULL) {
238
group = create_group(group_name);
239
server->roster = g_slist_insert_sorted(server->roster, group,
242
group = group_list->data;
246
static XMPP_ROSTER_USER_REC *
247
add_user(XMPP_SERVER_REC *server, const char *jid, const char *name,
248
const char *group_name, XMPP_ROSTER_GROUP_REC **return_group)
250
XMPP_ROSTER_GROUP_REC *group;
251
XMPP_ROSTER_USER_REC *user;
253
g_return_val_if_fail(IS_XMPP_SERVER(server), NULL);
254
g_return_val_if_fail(jid != NULL, NULL);
255
group = find_or_add_group(server, group_name);
256
user = create_user(jid, name);
257
group->users = g_slist_append(group->users, user);
258
if (return_group != NULL)
259
*return_group = group;
263
static XMPP_ROSTER_GROUP_REC *
264
move_user(XMPP_SERVER_REC *server, XMPP_ROSTER_USER_REC *user,
265
XMPP_ROSTER_GROUP_REC *group, const char *group_name)
267
XMPP_ROSTER_GROUP_REC *new_group;
269
g_return_val_if_fail(IS_XMPP_SERVER(server), group);
270
g_return_val_if_fail(user != NULL, group);
271
new_group = find_or_add_group(server, group_name);
272
group->users = g_slist_remove(group->users, user);
273
new_group->users = g_slist_append(new_group->users, user);
278
update_subscription(XMPP_SERVER_REC *server, XMPP_ROSTER_USER_REC *user,
279
XMPP_ROSTER_GROUP_REC *group, const char *subscription)
281
g_return_if_fail(IS_XMPP_SERVER(server));
282
g_return_if_fail(user != NULL);
283
g_return_if_fail(group != NULL);
284
g_return_if_fail(subscription != NULL);
285
if (g_ascii_strcasecmp(subscription,
286
xmpp_subscription[XMPP_SUBSCRIPTION_NONE]) == 0)
287
user->subscription = XMPP_SUBSCRIPTION_NONE;
288
else if (g_ascii_strcasecmp(subscription,
289
xmpp_subscription[XMPP_SUBSCRIPTION_FROM]) == 0)
290
user->subscription = XMPP_SUBSCRIPTION_FROM;
291
else if (g_ascii_strcasecmp(subscription,
292
xmpp_subscription[XMPP_SUBSCRIPTION_TO]) == 0)
293
user->subscription = XMPP_SUBSCRIPTION_TO;
294
else if (g_ascii_strcasecmp(subscription,
295
xmpp_subscription[XMPP_SUBSCRIPTION_BOTH]) == 0)
296
user->subscription = XMPP_SUBSCRIPTION_BOTH;
297
else if (g_ascii_strcasecmp(subscription,
298
xmpp_subscription[XMPP_SUBSCRIPTION_REMOVE]) == 0) {
299
group->users = g_slist_remove(group->users, user);
300
cleanup_user(user, server);
301
/* remove empty group */
302
if (group->users == NULL) {
303
server->roster = g_slist_remove(server->roster, group);
304
cleanup_group(group, server);
310
update_user(XMPP_SERVER_REC *server, const char *jid, const char *subscription,
311
const char *name, const char *group_name)
313
XMPP_ROSTER_GROUP_REC *group;
314
XMPP_ROSTER_USER_REC *user;
316
g_return_if_fail(IS_XMPP_SERVER(server));
317
g_return_if_fail(jid != NULL);
318
user = rosters_find_user(server->roster, jid, &group, NULL);
320
user = add_user(server, jid, name, group_name, &group);
322
/* move to another group and sort it */
323
if ((group->name == NULL && group_name != NULL)
324
|| (group->name != NULL && group_name == NULL)
325
|| (group->name != NULL && group_name != NULL
326
&& strcmp(group->name, group_name) != 0)) {
327
group = move_user(server, user, group, group_name);
328
group->users = g_slist_sort(group->users,
332
if ((user->name == NULL && name != NULL)
333
|| (user->name != NULL && name == NULL)
334
|| (user->name != NULL && name != NULL
335
&& strcmp(user->name, name) != 0)) {
337
user->name = g_strdup(name);
338
group->users = g_slist_sort(group->users,
342
update_subscription(server, user, group, subscription);
346
update_user_presence(XMPP_SERVER_REC *server, const char *full_jid,
347
const char *show_str, const char *status, const char *priority_str,
350
XMPP_ROSTER_GROUP_REC *group;
351
XMPP_ROSTER_USER_REC *user;
352
XMPP_ROSTER_RESOURCE_REC *resource;
357
g_return_if_fail(IS_XMPP_SERVER(server));
358
g_return_if_fail(full_jid != NULL);
360
jid = xmpp_strip_resource(full_jid);
361
res = xmpp_extract_resource(full_jid);
362
user = rosters_find_user(server->roster, jid, &group, NULL);
364
if (!(own = strcmp(jid, server->jid) == 0
365
&& strcmp(res, server->resource) != 0))
369
/* find resource or create it if it doesn't exist */
370
resource = rosters_find_resource(!own ?
371
user->resources : server->my_resources, res);
372
if (resource == NULL) {
373
resource = create_resource(res);
377
g_slist_prepend(user->resources, resource);
379
server->my_resources =
380
g_slist_prepend(server->my_resources, resource);
381
signal_emit("xmpp presence online", 4, server, full_jid,
384
show = xmpp_get_show(show_str);
385
priority = (priority_str != NULL) ?
386
atoi(priority_str) : resource->priority;
387
if (new || xmpp_presence_changed(show, resource->show, status,
388
resource->status, priority, resource->priority)) {
389
resource->show = show;
390
resource->status = g_strdup(status);
391
resource->priority = priority;
392
resource->pgp_keyid = pgp_keyid;
394
user->resources = g_slist_sort(
395
user->resources, func_sort_resource);
396
group->users = g_slist_sort(group->users,
399
server->my_resources = g_slist_sort(
400
server->my_resources, func_sort_resource);
401
signal_emit("xmpp presence changed", 4, server, full_jid,
402
resource->show, resource->status);
411
user_unavailable(XMPP_SERVER_REC *server, const char *full_jid,
414
XMPP_ROSTER_GROUP_REC *group;
415
XMPP_ROSTER_USER_REC *user;
416
XMPP_ROSTER_RESOURCE_REC *resource;
420
g_return_if_fail(IS_XMPP_SERVER(server));
421
g_return_if_fail(full_jid != NULL);
423
jid = xmpp_strip_resource(full_jid);
424
res = xmpp_extract_resource(full_jid);
425
user = rosters_find_user(server->roster, jid, &group, NULL);
427
if (!(own = strcmp(jid, server->jid) == 0))
431
resource = rosters_find_resource(!own ?
432
user->resources : server->my_resources, res);
433
if (resource == NULL)
435
signal_emit("xmpp presence offline", 4, server, full_jid, jid, res);
436
signal_emit("xmpp presence changed", 4, server, full_jid,
437
XMPP_PRESENCE_UNAVAILABLE, status);
439
user->resources = g_slist_remove(user->resources, resource);
441
server->my_resources = g_slist_remove(server->my_resources,
443
cleanup_resource(resource, NULL);
444
if (!own) /* sort the group */
445
group->users = g_slist_sort(group->users, func_sort_user);
453
user_presence_error(XMPP_SERVER_REC *server, const char *full_jid)
455
XMPP_ROSTER_GROUP_REC *group;
456
XMPP_ROSTER_USER_REC *user;
457
XMPP_ROSTER_RESOURCE_REC *resource;
461
g_return_if_fail(IS_XMPP_SERVER(server));
462
g_return_if_fail(full_jid != NULL);
464
jid = xmpp_strip_resource(full_jid);
465
res = xmpp_extract_resource(full_jid);
466
user = rosters_find_user(server->roster, jid, &group, NULL);
467
if (user == NULL && !(own = strcmp(jid, server->jid) == 0))
469
resource = rosters_find_resource(!own ?
470
user->resources : server->my_resources, res);
471
if (resource != NULL) {
472
resource->show = XMPP_PRESENCE_ERROR;
473
if (!own) /* sort the group */
474
group->users = g_slist_sort(group->users,
476
signal_emit("xmpp presence changed", 4, server, full_jid,
477
XMPP_PRESENCE_ERROR, NULL);
478
} else if (user != NULL)
488
sig_recv_presence(XMPP_SERVER_REC *server, LmMessage *lmsg, const int type,
489
const char *id, const char *from, const char *to)
491
LmMessageNode *node, *node_show, *node_priority, *signature;
492
char *status, *pgp_keyid = NULL;
494
if (server->ischannel(SERVER(server), from))
497
case LM_MESSAGE_SUB_TYPE_AVAILABLE:
498
node_show = lm_message_node_get_child(lmsg->node, "show");
499
node = lm_message_node_get_child(lmsg->node, "status");
500
status = node != NULL ? xmpp_recode_in(node->value) : NULL;
501
node_priority = lm_message_node_get_child(lmsg->node, "priority");
502
signature = lm_find_node(lmsg->node, "x", "xmlns", "jabber:x:signed");
504
char *send_to_gpg = malloc(sizeof( \
505
"-----BEGIN PGP SIGNATURE-----\n\n" \
506
"-----END PGP SIGNATURE-----\n")+ \
507
strlen(signature->value)+1 \
509
char *send_status = status ? status : "";
512
send_to_gpg[0] = '\0';
513
strcat(send_to_gpg, "-----BEGIN PGP SIGNATURE-----\n\n");
514
strcat(send_to_gpg, signature->value);
515
strcat(send_to_gpg, "----- END PGP SIGNATURE-----\n");
517
from_gpg = call_gpg("--verify", send_to_gpg, send_status, 1, 0);
520
/* If there is a good signature, grab the key ID */
521
if(from_gpg && strstr(from_gpg, "Good signature from")) {
522
char *s = strstr(from_gpg, "key ID ");
524
pgp_keyid = malloc(sizeof(*pgp_keyid)*9);
525
strncpy(pgp_keyid, s+7, 8);
529
if(from_gpg) free(from_gpg);
531
update_user_presence(server, from,
532
node_show != NULL ? node_show->value : NULL, status,
533
node_priority != NULL ? node_priority->value : NULL,
537
case LM_MESSAGE_SUB_TYPE_UNAVAILABLE:
538
node = lm_message_node_get_child(lmsg->node, "status");
539
status = node != NULL ? xmpp_recode_in(node->value) : NULL;
540
user_unavailable(server, from, status);
543
case LM_MESSAGE_SUB_TYPE_SUBSCRIBE:
544
node = lm_message_node_get_child(lmsg->node, "status");
545
status = node != NULL ? xmpp_recode_in(node->value) : NULL;
546
signal_emit("xmpp presence subscribe", 3, server, from, status);
549
case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBE:
550
signal_emit("xmpp presence unsubscribe", 2, server, from);
552
case LM_MESSAGE_SUB_TYPE_SUBSCRIBED:
553
signal_emit("xmpp presence subscribed", 2, server, from);
555
case LM_MESSAGE_SUB_TYPE_UNSUBSCRIBED:
556
signal_emit("xmpp presence unsubscribed", 2, server, from);
558
case LM_MESSAGE_SUB_TYPE_ERROR:
559
user_presence_error(server, from);
565
sig_recv_iq(XMPP_SERVER_REC *server, LmMessage *lmsg, const int type,
566
const char *id, const char *from, const char *to)
568
LmMessageNode *node, *item, *group_node;
569
char *jid, *name, *group;
570
const char *subscription;
572
if (type != LM_MESSAGE_SUB_TYPE_RESULT
573
&& type != LM_MESSAGE_SUB_TYPE_SET)
575
node = lm_find_node(lmsg->node, "query", "xmlns", XMLNS_ROSTER);
578
for (item = node->children; item != NULL; item = item->next) {
579
if (strcmp(item->name, "item") != 0)
581
jid = xmpp_recode_in(lm_message_node_get_attribute(item, "jid"));
582
name = xmpp_recode_in(lm_message_node_get_attribute(item, "name"));
583
group_node = lm_message_node_get_child(item, "group");
584
group = group_node != NULL ?
585
xmpp_recode_in(group_node->value) : NULL;
586
subscription = lm_message_node_get_attribute(item,
588
update_user(server, jid, subscription, name, group);
596
sig_connected(XMPP_SERVER_REC *server)
601
if (!IS_XMPP_SERVER(server))
603
signal_emit("xmpp server status", 2, server, "Requesting the roster.");
604
lmsg = lm_message_new_with_sub_type(NULL, LM_MESSAGE_TYPE_IQ,
605
LM_MESSAGE_SUB_TYPE_GET);
606
node = lm_message_node_add_child(lmsg->node, "query", NULL);
607
lm_message_node_set_attribute(node, "xmlns", "jabber:iq:roster");
608
signal_emit("xmpp send iq", 2, server, lmsg);
609
lm_message_unref(lmsg);
615
signal_add("server connected", sig_connected);
616
signal_add_first("server disconnected", roster_cleanup);
617
signal_add("xmpp recv presence", sig_recv_presence);
618
signal_add("xmpp recv iq", sig_recv_iq);
624
signal_remove("server connected", sig_connected);
625
signal_remove("server disconnected", roster_cleanup);
626
signal_remove("xmpp recv presence", sig_recv_presence);
627
signal_remove("xmpp recv iq", sig_recv_iq);