1 /*
2 * jabberd - Jabber Open Source Server
3 * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4 * Ryan Eatmon, Robert Norris
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, MA02111-1307USA
19 */
20
21 #include "sm.h"
22
23 /** @file sm/mod_roster.c
24 * @brief roster managment & subscriptions
25 * @author Robert Norris
26 * $Date: 2005/09/09 05:34:13 $
27 * $Revision: 1.61 $
28 */
29
30 typedef struct _mod_roster_st {
31 int maxitems;
32 } *mod_roster_t;
33
34 typedef struct _roster_walker_st {
35 pkt_t pkt;
36 int req_ver;
37 int ver;
38 sess_t sess;
39 } *roster_walker_t;
40
41 /** free a single roster item */
_roster_freeuser_walker(const char * key,int keylen,void * val,void * arg)42 static void _roster_freeuser_walker(const char *key, int keylen, void *val, void *arg)
43 {
44 item_t item = (item_t) val;
45 int i;
46
47 jid_free(item->jid);
48
49 if(item->name != NULL)
50 free((void*)item->name);
51
52 for(i = 0; i < item->ngroups; i++)
53 free((void*)item->groups[i]);
54 free(item->groups);
55
56 free(item);
57 }
58
59 /** free the roster */
_roster_freeuser(user_t user)60 static void _roster_freeuser(user_t user)
61 {
62 if(user->roster == NULL)
63 return;
64
65 log_debug(ZONE, "freeing roster for %s", jid_user(user->jid));
66
67 xhash_walk(user->roster, _roster_freeuser_walker, NULL);
68
69 xhash_free(user->roster);
70 user->roster = NULL;
71 }
72
_roster_save_item(user_t user,item_t item)73 static void _roster_save_item(user_t user, item_t item) {
74 os_t os;
75 os_object_t o;
76 char filter[4096];
77 int i;
78
79 log_debug(ZONE, "saving roster item %s for %s", jid_full(item->jid), jid_user(user->jid));
80
81 os = os_new();
82 o = os_object_new(os);
83
84 os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
85
86 if(item->name != NULL)
87 os_object_put(o, "name", item->name, os_type_STRING);
88
89 os_object_put(o, "to", &item->to, os_type_BOOLEAN);
90 os_object_put(o, "from", &item->from, os_type_BOOLEAN);
91 os_object_put(o, "ask", &item->ask, os_type_INTEGER);
92
93 snprintf(filter, 4096, "(jid=%zu:%s)", strlen(jid_full(item->jid)), jid_full(item->jid));
94
95 storage_replace(user->sm->st, "roster-items", jid_user(user->jid), filter, os);
96
97 os_free(os);
98
99 if(item->ngroups == 0) {
100 storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
101 return;
102 }
103
104 os = os_new();
105
106 for(i = 0; i < item->ngroups; i++) {
107 o = os_object_new(os);
108
109 os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
110 os_object_put(o, "group", item->groups[i], os_type_STRING);
111 }
112
113 storage_replace(user->sm->st, "roster-groups", jid_user(user->jid), filter, os);
114
115 os_free(os);
116 }
117
118 /** insert a roster item into this pkt, starting at elem */
_roster_insert_item(pkt_t pkt,item_t item,int elem)119 static void _roster_insert_item(pkt_t pkt, item_t item, int elem)
120 {
121 int ns, i;
122 char *sub;
123
124 ns = nad_add_namespace(pkt->nad, uri_CLIENT, NULL);
125 elem = nad_insert_elem(pkt->nad, elem, ns, "item", NULL);
126 nad_set_attr(pkt->nad, elem, -1, "jid", jid_full(item->jid), 0);
127
128 if(item->to && item->from)
129 sub = "both";
130 else if(item->to)
131 sub = "to";
132 else if(item->from)
133 sub = "from";
134 else
135 sub = "none";
136
137 nad_set_attr(pkt->nad, elem, -1, "subscription", sub, 0);
138
139 if(item->ask == 1)
140 nad_set_attr(pkt->nad, elem, -1, "ask", "subscribe", 9);
141 else if(item->ask == 2) /* XXX there is no ask='unsubscribe' in RFC bis anymore */
142 nad_set_attr(pkt->nad, elem, -1, "ask", "unsubscribe", 11);
143
144 if(item->name != NULL)
145 nad_set_attr(pkt->nad, elem, -1, "name", item->name, 0);
146
147 for(i = 0; i < item->ngroups; i++)
148 nad_insert_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", item->groups[i]);
149 }
150
151 /** push this packet to all sessions except the given one */
_roster_push(user_t user,pkt_t pkt,int mod_index)152 static int _roster_push(user_t user, pkt_t pkt, int mod_index)
153 {
154 sess_t scan;
155 pkt_t push;
156 int pushes = 0;
157
158 /* do the push */
159 for(scan = user->sessions; scan != NULL; scan = scan->next)
160 {
161 /* don't push to us or to anyone who hasn't loaded the roster */
162 if(scan->module_data[mod_index] == NULL)
163 continue;
164
165 push = pkt_dup(pkt, jid_full(scan->jid), NULL);
166 pkt_sess(push, scan);
167 pushes++;
168 }
169
170 /* return the pushed packets count */
171 return pushes;
172 }
173
_roster_in_sess_s10n(mod_instance_t mi,sess_t sess,pkt_t pkt)174 static mod_ret_t _roster_in_sess_s10n(mod_instance_t mi, sess_t sess, pkt_t pkt)
175 {
176 mod_roster_t mroster = (mod_roster_t) mi->mod->private;
177 module_t mod = mi->mod;
178 item_t item;
179 pkt_t push;
180 int ns, elem, ret, items = -1;
181
182 log_debug(ZONE, "got s10n packet");
183
184 /* s10ns have to go to someone */
185 if(pkt->to == NULL)
186 return -stanza_err_BAD_REQUEST;
187
188 /* add a proper from address (no resource) */
189 if(pkt->from != NULL)
190 jid_free(pkt->from);
191
192 pkt->from = jid_new(jid_user(sess->jid), -1);
193 nad_set_attr(pkt->nad, 1, -1, "from", jid_full(pkt->from), 0);
194
195 /* see if they're already on the roster */
196 item = xhash_get(sess->user->roster, jid_full(pkt->to));
197 if(item == NULL)
198 {
199 /* if they're not on the roster, there's no subscription,
200 * so quietly pass it on */
201 if(pkt->type == pkt_S10N_UN || pkt->type == pkt_S10N_UNED)
202 return mod_PASS;
203
204 /* check if user exceedes maximum roster items */
205 if(mroster->maxitems > 0) {
206 ret = storage_count(sess->user->sm->st, "roster-items", jid_user(sess->user->jid), NULL, &items);
207
208 log_debug(ZONE, "user has %i roster-items, maximum is %i", items, mroster->maxitems);
209
210 /* if the limit is reached, return an error */
211 if (ret == st_SUCCESS && items >= mroster->maxitems)
212 return -stanza_err_NOT_ACCEPTABLE;
213 }
214
215 /* make a new one */
216 item = (item_t) calloc(1, sizeof(struct item_st));
217
218 item->jid = jid_dup(pkt->to);
219
220 /* remember it */
221 xhash_put(sess->user->roster, jid_full(item->jid), (void *) item);
222
223 log_debug(ZONE, "made new empty roster item for %s", jid_full(item->jid));
224 }
225
226 /* a request */
227 if(pkt->type == pkt_S10N && ! item->to)
228 item->ask = 1;
229 else if(pkt->type == pkt_S10N_UN && item->to)
230 item->ask = 2;
231
232 /* changing states */
233 else if(pkt->type == pkt_S10N_ED)
234 {
235 /* they're allowed to see us, send them presence */
236 item->from = 1;
237 pres_roster(sess, item);
238 }
239 else if(pkt->type == pkt_S10N_UNED)
240 {
241 /* they're not allowed to see us anymore */
242 item->from = 0;
243 pres_roster(sess, item);
244 }
245
246 if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->user->jid)))
247 return -stanza_err_RESOURCE_CONSTRAINT;
248
249 /* save changes */
250 _roster_save_item(sess->user, item);
251
252 /* build a new packet to push out to everyone */
253 push = pkt_create(sess->user->sm, "iq", "set", NULL, NULL);
254 pkt_id_new(push);
255 ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
256 elem = nad_append_elem(push->nad, ns, "query", 3);
257
258 _roster_insert_item(push, item, elem);
259
260 /* tell everyone */
261 _roster_push(sess->user, push, mod->index);
262
263 /* everyone knows */
264 pkt_free(push);
265
266 /* pass it on */
267 return mod_PASS;
268 }
269
270 /** build the iq:roster packet from the hash */
_roster_get_walker(const char * id,int idlen,void * val,void * arg)271 static void _roster_get_walker(const char *id, int idlen, void *val, void *arg)
272 {
273 item_t item = (item_t) val;
274 roster_walker_t rw = (roster_walker_t) arg;
275
276 _roster_insert_item(rw->pkt, item, 2);
277
278 /* remember largest item version */
279 if(item->ver > rw->ver) rw->ver = item->ver;
280 }
281
282 /** push roster XEP-0237 updates to client */
_roster_update_walker(const char * id,int idlen,void * val,void * arg)283 static void _roster_update_walker(const char *id, int idlen, void *val, void *arg)
284 {
285 pkt_t push;
286 char *buf;
287 int elem, ns;
288 item_t item = (item_t) val;
289 roster_walker_t rw = (roster_walker_t) arg;
290
291 /* skip unneded roster items */
292 if(item->ver <= rw->req_ver) return;
293
294 /* build a interim roster push packet */
295 push = pkt_create(rw->sess->user->sm, "iq", "set", NULL, NULL);
296 pkt_id_new(push);
297 ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
298 elem = nad_append_elem(push->nad, ns, "query", 3);
299
300 buf = (char *) malloc(sizeof(char) * 128);
301 sprintf(buf, "%d", item->ver);
302 nad_set_attr(push->nad, elem, -1, "ver", buf, 0);
303 free(buf);
304
305 _roster_insert_item(push, item, elem);
306
307 pkt_sess(push, rw->sess);
308 }
309
_roster_set_item(pkt_t pkt,int elem,sess_t sess,mod_instance_t mi)310 static void _roster_set_item(pkt_t pkt, int elem, sess_t sess, mod_instance_t mi)
311 {
312 mod_roster_t mroster = (mod_roster_t) mi->mod->private;
313 module_t mod = mi->mod;
314 int attr, ns, i, ret, items = -1;
315 jid_t jid;
316 item_t item;
317 pkt_t push;
318 char filter[4096];
319
320 /* extract the jid */
321 attr = nad_find_attr(pkt->nad, elem, -1, "jid", NULL);
322 jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
323 if(jid == NULL) {
324 log_debug(ZONE, "jid failed prep check, skipping");
325 return;
326 }
327
328 /* check for removals */
329 if(nad_find_attr(pkt->nad, elem, -1, "subscription", "remove") >= 0)
330 {
331 /* trash the item */
332 item = xhash_get(sess->user->roster, jid_full(jid));
333 if(item != NULL)
334 {
335 /* tell them they're unsubscribed */
336 if(item->from) {
337 log_debug(ZONE, "telling %s that they're unsubscribed", jid_user(item->jid));
338 pkt_router(pkt_create(sess->user->sm, "presence", "unsubscribed", jid_user(item->jid), jid_user(sess->jid)));
339 }
340 item->from = 0;
341
342 /* tell them to unsubscribe us */
343 if(item->to) {
344 log_debug(ZONE, "unsubscribing from %s", jid_user(item->jid));
345 pkt_router(pkt_create(sess->user->sm, "presence", "unsubscribe", jid_user(item->jid), jid_user(sess->jid)));
346 }
347 item->to = 0;
348
349 /* send unavailable */
350 pres_roster(sess, item);
351
352 /* kill it */
353 xhash_zap(sess->user->roster, jid_full(jid));
354 _roster_freeuser_walker((const char *) jid_full(jid), strlen(jid_full(jid)), (void *) item, NULL);
355
356 snprintf(filter, 4096, "(jid=%zu:%s)", strlen(jid_full(jid)), jid_full(jid));
357 storage_delete(sess->user->sm->st, "roster-items", jid_user(sess->jid), filter);
358 storage_delete(sess->user->sm->st, "roster-groups", jid_user(sess->jid), filter);
359 }
360
361 log_debug(ZONE, "removed %s from roster", jid_full(jid));
362
363 /* build a new packet to push out to everyone */
364 push = pkt_create(sess->user->sm, "iq", "set", NULL, NULL);
365 pkt_id_new(push);
366 ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
367
368 nad_append_elem(push->nad, ns, "query", 3);
369 elem = nad_append_elem(push->nad, ns, "item", 4);
370 nad_set_attr(push->nad, elem, -1, "jid", jid_full(jid), 0);
371 nad_set_attr(push->nad, elem, -1, "subscription", "remove", 6);
372
373 /* tell everyone */
374 _roster_push(sess->user, push, mod->index);
375
376 /* we're done */
377 pkt_free(push);
378
379 jid_free(jid);
380
381 return;
382 }
383
384 /* find a pre-existing one */
385 item = xhash_get(sess->user->roster, jid_full(jid));
386 if(item == NULL)
387 {
388 /* check if user exceedes maximum roster items */
389 if(mroster->maxitems > 0) {
390 ret = storage_count(sess->user->sm->st, "roster-items", jid_user(sess->user->jid), NULL, &items);
391
392 log_debug(ZONE, "user has %i roster-items, maximum is %i", items, mroster->maxitems);
393
394 /* if the limit is reached, skip it */
395 if (ret == st_SUCCESS && items >= mroster->maxitems)
396 return;
397 }
398
399 /* make a new one */
400 item = (item_t) calloc(1, sizeof(struct item_st));
401
402 /* add the jid */
403 item->jid = jid;
404
405 /* add it to the roster */
406 xhash_put(sess->user->roster, jid_full(item->jid), (void *) item);
407
408 log_debug(ZONE, "created new roster item %s", jid_full(item->jid));
409 }
410
411 else
412 jid_free(jid);
413
414 /* extract the name */
415 attr = nad_find_attr(pkt->nad, elem, -1, "name", NULL);
416 if(attr >= 0)
417 {
418 /* free the old name */
419 if(item->name != NULL) {
420 free((void*)item->name);
421 item->name = NULL;
422 }
423
424 if (NAD_AVAL_L(pkt->nad, attr) > 0)
425 {
426 item->name = (const char *) malloc(sizeof(char) * (NAD_AVAL_L(pkt->nad, attr) + 1));
427 sprintf((char *)item->name, "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
428 }
429 }
430
431 /* free the old groups */
432 if(item->groups != NULL)
433 {
434 for(i = 0; i < item->ngroups; i++)
435 free((void*)item->groups[i]);
436 free(item->groups);
437 item->ngroups = 0;
438 item->groups = NULL;
439 }
440
441 /* loop over the groups, adding them to the array */
442 elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", 1);
443 while(elem >= 0)
444 {
445 /* empty group tags get skipped */
446 if(NAD_CDATA_L(pkt->nad, elem) >= 0)
447 {
448 /* make room and shove it in */
449 item->groups = (const char **) realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
450
451 item->groups[item->ngroups] = (const char *) malloc(sizeof(char) * (NAD_CDATA_L(pkt->nad, elem) + 1));
452 sprintf((char *)(item->groups[item->ngroups]), "%.*s", NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem));
453
454 item->ngroups++;
455 }
456
457 elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", 0);
458 }
459
460 log_debug(ZONE, "added %s to roster (to %d from %d ask %d name %s ngroups %d)", jid_full(item->jid), item->to, item->from, item->ask, item->name, item->ngroups);
461
462 if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->user->jid)))
463 return;
464
465 /* save changes */
466 _roster_save_item(sess->user, item);
467
468 /* build a new packet to push out to everyone */
469 push = pkt_create(sess->user->sm, "iq", "set", NULL, NULL);
470 pkt_id_new(push);
471 ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
472 elem = nad_append_elem(push->nad, ns, "query", 3);
473
474 _roster_insert_item(push, item, elem);
475
476 /* tell everyone */
477 _roster_push(sess->user, push, mod->index);
478
479 /* we're done */
480 pkt_free(push);
481 }
482
483 /** our main handler for packets arriving from a session */
_roster_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)484 static mod_ret_t _roster_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
485 {
486 module_t mod = mi->mod;
487 int elem, attr, ver = 0;
488 pkt_t result;
489 char *buf;
490 roster_walker_t rw;
491
492 /* handle s10ns in a different function */
493 if(pkt->type & pkt_S10N)
494 return _roster_in_sess_s10n(mi, sess, pkt);
495
496 /* we only want to play with iq:roster packets */
497 if(pkt->ns != ns_ROSTER)
498 return mod_PASS;
499
500 /* quietly drop results, its probably them responding to a push */
501 if(pkt->type == pkt_IQ_RESULT) {
502 pkt_free(pkt);
503 return mod_HANDLED;
504 }
505
506 /* need gets or sets */
507 if(pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET)
508 return mod_PASS;
509
510 /* get */
511 if(pkt->type == pkt_IQ)
512 {
513 /* check for "XEP-0237: Roster Versioning request" */
514 if((elem = nad_find_elem(pkt->nad, 1, -1, "query", 1)) >= 0
515 &&(attr = nad_find_attr(pkt->nad, elem, -1, "ver", NULL)) >= 0) {
516 if (NAD_AVAL_L(pkt->nad, attr) > 0)
517 {
518 buf = (char *) malloc(sizeof(char) * (NAD_AVAL_L(pkt->nad, attr) + 1));
519 sprintf(buf, "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
520 ver = j_atoi(buf, 0);
521 free(buf);
522 }
523 }
524
525 /* build the packet */
526 rw = (roster_walker_t) calloc(1, sizeof(struct _roster_walker_st));
527 rw->pkt = pkt;
528 rw->req_ver = ver;
529 rw->sess = sess;
530
531 nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
532
533 if(ver > 0) {
534 /* send XEP-0237 empty result */
535 nad_drop_elem(pkt->nad, elem);
536 pkt_sess(pkt_tofrom(pkt), sess);
537 xhash_walk(sess->user->roster, _roster_update_walker, (void *) rw);
538 }
539 else {
540 xhash_walk(sess->user->roster, _roster_get_walker, (void *) rw);
541 if(elem >= 0 && attr >= 0) {
542 buf = (char *) malloc(sizeof(char) * 128);
543 sprintf(buf, "%d", rw->ver);
544 nad_set_attr(pkt->nad, elem, -1, "ver", buf, 0);
545 free(buf);
546 }
547 pkt_sess(pkt_tofrom(pkt), sess);
548 }
549
550 free(rw);
551
552 /* remember that they loaded it, so we know to push updates to them */
553 sess->module_data[mod->index] = (void *) 1;
554
555 return mod_HANDLED;
556 }
557
558 /* set, find the item */
559 elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), "item", 1);
560 if(elem < 0)
561 /* no item, abort */
562 return -stanza_err_BAD_REQUEST;
563
564 /* loop over items and stick them in */
565 while(elem >= 0)
566 {
567 /* extract the jid */
568 attr = nad_find_attr(pkt->nad, elem, -1, "jid", NULL);
569 if(attr < 0 || NAD_AVAL_L(pkt->nad, attr) == 0)
570 {
571 log_debug(ZONE, "no jid on this item, aborting");
572
573 /* no jid, abort */
574 return -stanza_err_BAD_REQUEST;
575 }
576
577 /* utility */
578 _roster_set_item(pkt, elem, sess, mi);
579
580 /* next one */
581 elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "item", 0);
582 }
583
584 /* send the result */
585 result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
586
587 pkt_id(pkt, result);
588
589 /* tell them */
590 pkt_sess(result, sess);
591
592 /* free the request */
593 pkt_free(pkt);
594
595 return mod_HANDLED;
596 }
597
598 /** handle incoming s10ns */
_roster_pkt_user(mod_instance_t mi,user_t user,pkt_t pkt)599 static mod_ret_t _roster_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt)
600 {
601 module_t mod = mi->mod;
602 item_t item;
603 int ns, elem;
604
605 /* only want s10ns */
606 if(!(pkt->type & pkt_S10N))
607 return mod_PASS;
608
609 /* drop route errors */
610 if(pkt->rtype & route_ERROR) {
611 pkt_free(pkt);
612 return mod_HANDLED;
613 }
614
615 /* get the roster item */
616 item = (item_t) xhash_get(user->roster, jid_full(pkt->from));
617 if(item == NULL) {
618 /* subs are handled by the client */
619 if(pkt->type == pkt_S10N) {
620 /* if the user is online broadcast it like roster push */
621 if(user->top != NULL && _roster_push(user, pkt, mod->index) > 0) {
622 /* pushed, thus handled */
623 pkt_free(pkt);
624 return mod_HANDLED;
625 }
626 else {
627 /* not pushed to any online resource - pass it on (to mod_offline) */
628 return mod_PASS;
629 }
630 }
631
632 /* other S10Ns: we didn't ask for this, so we don't care */
633 pkt_free(pkt);
634 return mod_HANDLED;
635 }
636
637 /* ignore bogus answers */
638 if( (pkt->type == pkt_S10N_ED && (item->ask != 1 || item->to) )
639 || (pkt->type == pkt_S10N_UNED && ! item->to) )
640 {
641 /* remove pending ask */
642 if( (pkt->type == pkt_S10N_ED && item->ask == 1)
643 || (pkt->type == pkt_S10N_UNED && item->ask == 2) )
644 {
645 item->ask = 0;
646 /* save changes */
647 _roster_save_item(user, item);
648 }
649
650 pkt_free(pkt);
651 return mod_HANDLED;
652 }
653
654 /* trying to subscribe */
655 if(pkt->type == pkt_S10N)
656 {
657 if(item->from)
658 {
659 /* already subscribed, tell them */
660 nad_set_attr(pkt->nad, 1, -1, "type", "subscribed", 10);
661 pkt_router(pkt_tofrom(pkt));
662
663 /* update their presence from the leading session */
664 if(user->top != NULL)
665 pres_roster(user->top, item);
666
667 return mod_HANDLED;
668 }
669
670 return mod_PASS;
671 }
672
673 /* handle unsubscribe */
674 if(pkt->type == pkt_S10N_UN)
675 {
676 if(!item->from)
677 {
678 /* already unsubscribed, tell them */
679 nad_set_attr(pkt->nad, 1, -1, "type", "unsubscribed", 12);
680 pkt_router(pkt_tofrom(pkt));
681
682 return mod_HANDLED;
683 }
684
685 /* change state */
686 item->from = 0;
687
688 /* confirm unsubscription */
689 pkt_router(pkt_create(user->sm, "presence", "unsubscribed", jid_user(pkt->from), jid_user(user->jid)));
690
691 /* update their presence from the leading session */
692 if(user->top != NULL)
693 pres_roster(user->top, item);
694 }
695
696 /* update our s10n */
697 if(pkt->type == pkt_S10N_ED)
698 {
699 item->to = 1;
700 if(item->ask == 1)
701 item->ask = 0;
702 }
703 if(pkt->type == pkt_S10N_UNED)
704 {
705 item->to = 0;
706 if(item->ask == 2)
707 item->ask = 0;
708 }
709
710 if (sm_storage_rate_limit(user->sm, jid_user(pkt->from)))
711 return -stanza_err_RESOURCE_CONSTRAINT;
712
713 /* save changes */
714 _roster_save_item(user, item);
715
716 /* if there's no sessions, then we're done */
717 if(user->sessions == NULL)
718 return mod_PASS;
719
720 /* build a new packet to push out to everyone */
721 pkt = pkt_create(user->sm, "iq", "set", NULL, NULL);
722 pkt_id_new(pkt);
723 ns = nad_add_namespace(pkt->nad, uri_ROSTER, NULL);
724 elem = nad_append_elem(pkt->nad, ns, "query", 3);
725
726 _roster_insert_item(pkt, item, elem);
727
728 /* tell everyone */
729 _roster_push(user, pkt, mod->index);
730
731 /* everyone knows */
732 pkt_free(pkt);
733
734 return mod_PASS;
735 }
736
737 /** load the roster from the database */
_roster_user_load(mod_instance_t mi,user_t user)738 static int _roster_user_load(mod_instance_t mi, user_t user) {
739 os_t os;
740 os_object_t o;
741 char *str;
742 item_t item, olditem;
743
744 log_debug(ZONE, "loading roster for %s", jid_user(user->jid));
745
746 user->roster = xhash_new(101);
747
748 /* pull all the items */
749 if(storage_get(user->sm->st, "roster-items", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
750 if(os_iter_first(os))
751 do {
752 o = os_iter_object(os);
753
754 if(os_object_get_str(os, o, "jid", &str)) {
755 /* new one */
756 item = (item_t) calloc(1, sizeof(struct item_st));
757
758 item->jid = jid_new(str, -1);
759 if(item->jid == NULL) {
760 log_debug(ZONE, "eek! invalid jid %s, skipping it", str);
761 free(item);
762
763 } else {
764 if(os_object_get_str(os, o, "name", &str))
765 item->name = strdup(str);
766
767 os_object_get_bool(os, o, "to", &item->to);
768 os_object_get_bool(os, o, "from", &item->from);
769 os_object_get_int(os, o, "ask", &item->ask);
770 os_object_get_int(os, o, "object-sequence", &item->ver);
771
772 olditem = xhash_get(user->roster, jid_full(item->jid));
773 if(olditem) {
774 log_debug(ZONE, "removing old %s roster entry", jid_full(item->jid));
775 xhash_zap(user->roster, jid_full(item->jid));
776 _roster_freeuser_walker(jid_full(item->jid), strlen(jid_full(item->jid)), (void *) olditem, NULL);
777 }
778
779 /* its good */
780 xhash_put(user->roster, jid_full(item->jid), (void *) item);
781
782 log_debug(ZONE, "added %s to roster (to %d from %d ask %d ver %d name %s)",
783 jid_full(item->jid), item->to, item->from, item->ask, item->ver, item->name);
784 }
785 }
786 } while(os_iter_next(os));
787
788 os_free(os);
789 }
790
791 /* pull the groups and match them up */
792 if(storage_get(user->sm->st, "roster-groups", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
793 if(os_iter_first(os))
794 do {
795 o = os_iter_object(os);
796
797 if(os_object_get_str(os, o, "jid", &str)) {
798 item = xhash_get(user->roster, str);
799
800 if(item != NULL && os_object_get_str(os, o, "group", &str)) {
801 item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
802 item->groups[item->ngroups] = strdup(str);
803 item->ngroups++;
804
805 log_debug(ZONE, "added group %s to item %s", str, jid_full(item->jid));
806 }
807 }
808 } while(os_iter_next(os));
809
810 os_free(os);
811 }
812
813 pool_cleanup(user->p, (void (*))(void *) _roster_freeuser, user);
814
815 return 0;
816 }
817
_roster_user_delete(mod_instance_t mi,jid_t jid)818 static void _roster_user_delete(mod_instance_t mi, jid_t jid) {
819 log_debug(ZONE, "deleting roster data for %s", jid_user(jid));
820
821 storage_delete(mi->sm->st, "roster-items", jid_user(jid), NULL);
822 storage_delete(mi->sm->st, "roster-groups", jid_user(jid), NULL);
823 }
824
_roster_free(module_t mod)825 static void _roster_free(module_t mod)
826 {
827 mod_roster_t mroster = (mod_roster_t) mod->private;
828 free(mroster);
829 }
830
module_init(mod_instance_t mi,const char * arg)831 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
832 module_t mod = mi->mod;
833 mod_roster_t mroster;
834
835 if(mod->init) return 0;
836
837 mroster = (mod_roster_t) calloc(1, sizeof(struct _mod_roster_st));
838
839 mroster->maxitems = j_atoi(config_get_one(mod->mm->sm->config, "roster.maxitems", 0), 0);
840
841 mod->private = mroster;
842
843 mod->in_sess = _roster_in_sess;
844 mod->pkt_user = _roster_pkt_user;
845 mod->user_load = _roster_user_load;
846 mod->user_delete = _roster_user_delete;
847 mod->free = _roster_free;
848
849 feature_register(mod->mm->sm, uri_ROSTER);
850
851 return 0;
852 }
853