jabberd2  2.3.1
mod_roster.c
Go to the documentation of this file.
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 
30 typedef struct _mod_roster_st {
31  int maxitems;
32 } *mod_roster_t;
33 
34 typedef struct _roster_walker_st {
36  int req_ver;
37  int ver;
40 
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 
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 
68 
69  xhash_free(user->roster);
70  user->roster = NULL;
71 }
72 
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 
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 
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 
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)
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)))
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 
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 
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 
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 
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 
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)))
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 
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 
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 
825 static void _roster_free(module_t mod)
826 {
827  mod_roster_t mroster = (mod_roster_t) mod->private;
828  free(mroster);
829 }
830 
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;
847  mod->free = _roster_free;
848 
850 
851  return 0;
852 }
static void _roster_get_walker(const char *id, int idlen, void *val, void *arg)
build the iq:roster packet from the hash
Definition: mod_roster.c:271
user_t user
user this session belongs to
Definition: sm.h:255
xht roster
roster for this user (key is full jid of item, value is item_t)
Definition: sm.h:240
static void _roster_set_item(pkt_t pkt, int elem, sess_t sess, mod_instance_t mi)
Definition: mod_roster.c:310
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:257
data structures and prototypes for the session manager
int nad_insert_elem(nad_t nad, int parent, int ns, const char *name, const char *cdata)
shove in a new child elem after the given one
Definition: nad.c:405
subscribe request
Definition: sm.h:102
#define NAD_CDATA_L(N, E)
Definition: nad.h:186
static mod_ret_t _roster_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
our main handler for packets arriving from a session
Definition: mod_roster.c:484
void xhash_free(xht h)
Definition: xhash.c:241
const char * jid_user(jid_t jid)
expand and return the user
Definition: jid.c:339
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:347
jid_t jid_new(const char *id, int len)
make a new jid
Definition: jid.c:81
pkt_t pkt_tofrom(pkt_t pkt)
swap a packet&#39;s to and from attributes
Definition: pkt.c:57
single instance of a module in a chain
Definition: sm.h:445
int nad_find_elem(nad_t nad, int elem, int ns, const char *name, int depth)
locate the next elem at a given depth with an optional matching name
Definition: nad.c:204
config_t config
config context
Definition: sm.h:197
int init
number of times the module intialiser has been called
Definition: sm.h:415
int ask
pending subscription (0 == none, 1 == subscribe, 2 == unsubscribe)
Definition: sm.h:161
route error
Definition: sm.h:125
info/query (set)
Definition: sm.h:107
void ** module_data
per-session module data
Definition: sm.h:273
int nad_add_namespace(nad_t nad, const char *uri, const char *prefix)
bring a new namespace into scope
Definition: nad.c:734
sm_t sm
sm context
Definition: sm.h:236
pkt_t pkt_dup(pkt_t pkt, const char *to, const char *from)
duplicate pkt, replacing addresses
Definition: pkt.c:84
info/query (result)
Definition: sm.h:108
int j_atoi(const char *a, int def)
Definition: str.c:87
void pkt_id_new(pkt_t pkt)
create an id value for new iq packets
Definition: pkt.c:364
int nad_append_elem(nad_t nad, int ns, const char *name, int depth)
create a new elem on the list
Definition: nad.c:667
int index
module index.
Definition: sm.h:407
static void _roster_save_item(user_t user, item_t item)
Definition: mod_roster.c:73
int(* user_load)(mod_instance_t mi, user_t user)
user-load handler
Definition: sm.h:433
mm_t mm
module manager
Definition: sm.h:403
#define DLLEXPORT
Definition: c2s.h:47
sess_t next
next session (in a list of sessions)
Definition: sm.h:275
void nad_set_attr(nad_t nad, int elem, int ns, const char *name, const char *val, int vallen)
create, update, or zap any matching attr on this elem
Definition: nad.c:375
sess_t top
top priority session
Definition: sm.h:243
void pool_cleanup(pool_t p, pool_cleanup_t f, void *arg)
public cleanup utils, insert in a way that they are run FIFO, before mem frees
Definition: pool.c:251
sm_t sm
sm context
Definition: sm.h:365
static int _roster_push(user_t user, pkt_t pkt, int mod_index)
push this packet to all sessions except the given one
Definition: mod_roster.c:152
#define ns_ROSTER
Definition: sm.h:71
pool_t p
memory pool this user is allocated off
Definition: sm.h:234
sess_t sessions
list of action sessions
Definition: sm.h:242
module_t mod
module that this is an instance of
Definition: sm.h:448
jid_t from
packet addressing (not used for routing)
Definition: sm.h:140
void * private
module private data
Definition: sm.h:417
packet summary data wrapper
Definition: sm.h:129
#define uri_ROSTER
Definition: uri.h:62
storage_t st
storage subsystem
Definition: sm.h:210
nad_t nad
nad of the entire packet
Definition: sm.h:146
void jid_free(jid_t jid)
free a jid
Definition: jid.c:286
#define stanza_err_RESOURCE_CONSTRAINT
Definition: util.h:383
mod_ret_t(* in_sess)(mod_instance_t mi, sess_t sess, pkt_t pkt)
in-sess handler
Definition: sm.h:422
void xhash_put(xht h, const char *key, void *val)
Definition: xhash.c:163
static void _roster_freeuser_walker(const char *key, int keylen, void *val, void *arg)
free a single roster item
Definition: mod_roster.c:42
static int _roster_user_load(mod_instance_t mi, user_t user)
load the roster from the database
Definition: mod_roster.c:738
#define stanza_err_BAD_REQUEST
Definition: util.h:367
int ngroups
number of groups in groups array
Definition: sm.h:157
Definition: jid.h:42
const char ** groups
groups this item is in
Definition: sm.h:155
#define NAD_AVAL_L(N, A)
Definition: nad.h:190
void pkt_id(pkt_t src, pkt_t dest)
convenience - copy the packet id from src to dest
Definition: pkt.c:353
static void _roster_user_delete(mod_instance_t mi, jid_t jid)
Definition: mod_roster.c:818
void pkt_router(pkt_t pkt)
Definition: pkt.c:379
void xhash_zap(xht h, const char *key)
Definition: xhash.c:235
void pkt_free(pkt_t pkt)
Definition: pkt.c:315
#define log_debug(...)
Definition: log.h:65
static void _roster_freeuser(user_t user)
free the roster
Definition: mod_roster.c:60
DLLEXPORT int module_init(mod_instance_t mi, const char *arg)
Definition: mod_active.c:70
roster items
Definition: sm.h:150
void feature_register(sm_t sm, const char *feature)
register a feature
Definition: feature.c:37
info/query (get)
Definition: sm.h:106
#define uri_CLIENT
Definition: uri.h:35
#define NAD_AVAL(N, A)
Definition: nad.h:189
static mod_ret_t _roster_in_sess_s10n(mod_instance_t mi, sess_t sess, pkt_t pkt)
Definition: mod_roster.c:174
struct _roster_walker_st * roster_walker_t
struct item_st * item_t
roster items
void(* user_delete)(mod_instance_t mi, jid_t jid)
user-delete handler
Definition: sm.h:437
packet was unhandled, should be passed to the next module
Definition: sm.h:339
int ns
iq sub-namespace
Definition: sm.h:142
packet was handled (and freed)
Definition: sm.h:338
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
unsubscribe request
Definition: sm.h:104
struct _mod_roster_st * mod_roster_t
subscribed response
Definition: sm.h:103
void xhash_walk(xht h, xhash_walker w, void *arg)
Definition: xhash.c:268
void(* free)(module_t mod)
called when module is freed
Definition: sm.h:441
jid_t to
Definition: sm.h:140
int from
subscription to this item (they get presence FROM us, they send presence TO us)
Definition: sm.h:159
jid_t jid
id of this item
Definition: sm.h:151
static void _roster_free(module_t mod)
Definition: mod_roster.c:825
void nad_drop_elem(nad_t nad, int elem)
remove an element (and its subelements)
Definition: nad.c:452
int to
Definition: sm.h:159
mod_ret_t(* pkt_user)(mod_instance_t mi, user_t user, pkt_t pkt)
pkt-user handler
Definition: sm.h:429
void pres_roster(sess_t sess, item_t item)
send presence based on roster changes
Definition: pres.c:359
#define stanza_err_NOT_ACCEPTABLE
Definition: util.h:375
#define NAD_CDATA(N, E)
Definition: nad.h:185
jid_t jid_dup(jid_t jid)
duplicate a jid
Definition: jid.c:373
jid_t jid
user jid (user@host)
Definition: sm.h:238
void * xhash_get(xht h, const char *key)
Definition: xhash.c:184
#define ZONE
Definition: mio_impl.h:76
static void _roster_insert_item(pkt_t pkt, item_t item, int elem)
insert a roster item into this pkt, starting at elem
Definition: mod_roster.c:119
const char * config_get_one(config_t c, const char *key, int num)
get config value n for this key
Definition: config.c:277
xht xhash_new(int prime)
Definition: xhash.c:96
data for a single module
Definition: sm.h:402
int nad_find_attr(nad_t nad, int elem, int ns, const char *name, const char *val)
get a matching attr on this elem, both name and optional val
Definition: nad.c:235
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from)
Definition: pkt.c:328
static mod_ret_t _roster_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt)
handle incoming s10ns
Definition: mod_roster.c:599
mod_ret_t
module return values
Definition: sm.h:337
sm_t sm
sm context
Definition: sm.h:446
void pkt_sess(pkt_t pkt, sess_t sess)
Definition: pkt.c:459
int sm_storage_rate_limit(sm_t sm, const char *owner)
Definition: sm.c:355
int ver
roster item version number
Definition: sm.h:163
#define NAD_ENS(N, E)
Definition: nad.h:196
data for a single user
Definition: sm.h:233
const char * name
display name
Definition: sm.h:153
static void _roster_update_walker(const char *id, int idlen, void *val, void *arg)
push roster XEP-0237 updates to client
Definition: mod_roster.c:283
unsubscribed response
Definition: sm.h:105
route_type_t rtype
type of enclosing route
Definition: sm.h:136