jabberd2  2.2.16
sm/mod_roster.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #include "sm.h"
00022 
00030 typedef struct _mod_roster_st {
00031     int maxitems;
00032 } *mod_roster_t;
00033 
00034 typedef struct _roster_walker_st {
00035     pkt_t  pkt;
00036     int    req_ver;
00037     int    ver;
00038     sess_t sess;
00039 } *roster_walker_t;
00040 
00042 static void _roster_freeuser_walker(const char *key, int keylen, void *val, void *arg)
00043 {
00044     item_t item = (item_t) val;
00045     int i;
00046 
00047     jid_free(item->jid);
00048     
00049     if(item->name != NULL)
00050         free(item->name);
00051 
00052     for(i = 0; i < item->ngroups; i++)
00053         free(item->groups[i]);
00054     free(item->groups);
00055 
00056     free(item);
00057 }
00058 
00060 static void _roster_freeuser(user_t user)
00061 {
00062     if(user->roster == NULL)
00063         return;
00064 
00065     log_debug(ZONE, "freeing roster for %s", jid_user(user->jid));
00066 
00067     xhash_walk(user->roster, _roster_freeuser_walker, NULL);
00068 
00069     xhash_free(user->roster);
00070     user->roster = NULL;
00071 }
00072 
00073 static void _roster_save_item(user_t user, item_t item) {
00074     os_t os;
00075     os_object_t o;
00076     char filter[4096];
00077     int i;
00078 
00079     log_debug(ZONE, "saving roster item %s for %s", jid_full(item->jid), jid_user(user->jid));
00080 
00081     os = os_new();
00082     o = os_object_new(os);
00083 
00084     os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
00085 
00086     if(item->name != NULL)
00087         os_object_put(o, "name", item->name, os_type_STRING);
00088 
00089     os_object_put(o, "to", &item->to, os_type_BOOLEAN);
00090     os_object_put(o, "from", &item->from, os_type_BOOLEAN);
00091     os_object_put(o, "ask", &item->ask, os_type_INTEGER);
00092 
00093     snprintf(filter, 4096, "(jid=%zu:%s)", strlen(jid_full(item->jid)), jid_full(item->jid));
00094 
00095     storage_replace(user->sm->st, "roster-items", jid_user(user->jid), filter, os);
00096 
00097     os_free(os);
00098 
00099     if(item->ngroups == 0) {
00100         storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
00101         return;
00102     }
00103 
00104     os = os_new();
00105     
00106     for(i = 0; i < item->ngroups; i++) {
00107         o = os_object_new(os);
00108 
00109         os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
00110         os_object_put(o, "group", item->groups[i], os_type_STRING);
00111     }
00112 
00113     storage_replace(user->sm->st, "roster-groups", jid_user(user->jid), filter, os);
00114 
00115     os_free(os);
00116 }
00117 
00119 static void _roster_insert_item(pkt_t pkt, item_t item, int elem)
00120 {
00121     int ns, i;
00122     char *sub;
00123 
00124     ns = nad_add_namespace(pkt->nad, uri_CLIENT, NULL);
00125     elem = nad_insert_elem(pkt->nad, elem, ns, "item", NULL);
00126     nad_set_attr(pkt->nad, elem, -1, "jid", jid_full(item->jid), 0);
00127 
00128     if(item->to && item->from)
00129         sub = "both";
00130     else if(item->to)
00131         sub = "to";
00132     else if(item->from)
00133         sub = "from";
00134     else
00135         sub = "none";
00136 
00137     nad_set_attr(pkt->nad, elem, -1, "subscription", sub, 0);
00138 
00139     if(item->ask == 1)
00140         nad_set_attr(pkt->nad, elem, -1, "ask", "subscribe", 9);
00141     else if(item->ask == 2) /* XXX there is no ask='unsubscribe' in RFC bis anymore */
00142         nad_set_attr(pkt->nad, elem, -1, "ask", "unsubscribe", 11);
00143 
00144     if(item->name != NULL)
00145         nad_set_attr(pkt->nad, elem, -1, "name", item->name, 0);
00146 
00147     for(i = 0; i < item->ngroups; i++)
00148         nad_insert_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", item->groups[i]);
00149 }
00150 
00152 static int _roster_push(user_t user, pkt_t pkt, int mod_index)
00153 {
00154     sess_t scan;
00155     pkt_t push;
00156     int pushes = 0;
00157 
00158     /* do the push */
00159     for(scan = user->sessions; scan != NULL; scan = scan->next)
00160     {
00161         /* don't push to us or to anyone who hasn't loaded the roster */
00162         if(scan->module_data[mod_index] == NULL)
00163             continue;
00164 
00165         push = pkt_dup(pkt, jid_full(scan->jid), NULL);
00166         pkt_sess(push, scan);
00167         pushes++;
00168     }
00169 
00170     /* return the pushed packets count */
00171     return pushes;
00172 }
00173 
00174 static mod_ret_t _roster_in_sess_s10n(mod_instance_t mi, sess_t sess, pkt_t pkt)
00175 {
00176     mod_roster_t mroster = (mod_roster_t) mi->mod->private;
00177     module_t mod = mi->mod;
00178     item_t item;
00179     pkt_t push;
00180     int ns, elem, ret, items = -1;
00181 
00182     log_debug(ZONE, "got s10n packet");
00183 
00184     /* s10ns have to go to someone */
00185     if(pkt->to == NULL)
00186         return -stanza_err_BAD_REQUEST;
00187 
00188     /* add a proper from address (no resource) */
00189     if(pkt->from != NULL)
00190         jid_free(pkt->from);
00191 
00192     pkt->from = jid_new(jid_user(sess->jid), -1);
00193     nad_set_attr(pkt->nad, 1, -1, "from", jid_full(pkt->from), 0);
00194 
00195     /* see if they're already on the roster */
00196     item = xhash_get(sess->user->roster, jid_full(pkt->to));
00197     if(item == NULL)
00198     {
00199         /* if they're not on the roster, there's no subscription,
00200          * so quietly pass it on */
00201         if(pkt->type == pkt_S10N_UN || pkt->type == pkt_S10N_UNED)
00202             return mod_PASS;
00203 
00204         /* check if user exceedes maximum roster items */
00205         if(mroster->maxitems > 0) {
00206             ret = storage_count(sess->user->sm->st, "roster-items", jid_user(sess->user->jid), NULL, &items);
00207 
00208             log_debug(ZONE, "user has %i roster-items, maximum is %i", items, mroster->maxitems);
00209 
00210             /* if the limit is reached, return an error */
00211             if (ret == st_SUCCESS && items >= mroster->maxitems)
00212                 return -stanza_err_NOT_ACCEPTABLE;
00213         }
00214 
00215         /* make a new one */
00216         item = (item_t) calloc(1, sizeof(struct item_st));
00217 
00218         item->jid = jid_dup(pkt->to);
00219 
00220         /* remember it */
00221         xhash_put(sess->user->roster, jid_full(item->jid), (void *) item);
00222 
00223         log_debug(ZONE, "made new empty roster item for %s", jid_full(item->jid));
00224     }
00225 
00226     /* a request */
00227     if(pkt->type == pkt_S10N && ! item->to)
00228         item->ask = 1;
00229     else if(pkt->type == pkt_S10N_UN && item->to)
00230         item->ask = 2;
00231 
00232     /* changing states */
00233     else if(pkt->type == pkt_S10N_ED)
00234     {
00235         /* they're allowed to see us, send them presence */
00236         item->from = 1;
00237         pres_roster(sess, item);
00238     }
00239     else if(pkt->type == pkt_S10N_UNED)
00240     {
00241         /* they're not allowed to see us anymore */
00242         item->from = 0;
00243         pres_roster(sess, item);
00244     }
00245 
00246     if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->user->jid)))
00247         return -stanza_err_RESOURCE_CONSTRAINT;
00248 
00249     /* save changes */
00250     _roster_save_item(sess->user, item);
00251     
00252     /* build a new packet to push out to everyone */
00253     push = pkt_create(sess->user->sm, "iq", "set", NULL, NULL);
00254     pkt_id_new(push);
00255     ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
00256     elem = nad_append_elem(push->nad, ns, "query", 3);
00257 
00258     _roster_insert_item(push, item, elem);
00259 
00260     /* tell everyone */
00261     _roster_push(sess->user, push, mod->index);
00262 
00263     /* everyone knows */
00264     pkt_free(push);
00265 
00266     /* pass it on */
00267     return mod_PASS;
00268 }
00269 
00271 static void _roster_get_walker(const char *id, int idlen, void *val, void *arg)
00272 {
00273     item_t item = (item_t) val;
00274     roster_walker_t rw = (roster_walker_t) arg;
00275 
00276     _roster_insert_item(rw->pkt, item, 2);
00277 
00278     /* remember largest item version */
00279     if(item->ver > rw->ver) rw->ver = item->ver;
00280 }
00281 
00283 static void _roster_update_walker(const char *id, int idlen, void *val, void *arg)
00284 {
00285     pkt_t push;
00286     char *buf;
00287     int elem, ns;
00288     item_t item = (item_t) val;
00289     roster_walker_t rw = (roster_walker_t) arg;
00290 
00291     /* skip unneded roster items */
00292     if(item->ver <= rw->req_ver) return;
00293 
00294     /* build a interim roster push packet */
00295     push = pkt_create(rw->sess->user->sm, "iq", "set", NULL, NULL);
00296     pkt_id_new(push);
00297     ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
00298     elem = nad_append_elem(push->nad, ns, "query", 3);
00299 
00300     buf = (char *) malloc(sizeof(char) * 128);
00301     sprintf(buf, "%d", item->ver);
00302     nad_set_attr(push->nad, elem, -1, "ver", buf, 0);
00303     free(buf);
00304 
00305     _roster_insert_item(push, item, elem);
00306 
00307     pkt_sess(push, rw->sess);
00308 }
00309 
00310 static void _roster_set_item(pkt_t pkt, int elem, sess_t sess, mod_instance_t mi)
00311 {
00312     mod_roster_t mroster = (mod_roster_t) mi->mod->private;
00313     module_t mod = mi->mod;
00314     int attr, ns, i, ret, items = -1;
00315     jid_t jid;
00316     item_t item;
00317     pkt_t push;
00318     char filter[4096];
00319 
00320     /* extract the jid */
00321     attr = nad_find_attr(pkt->nad, elem, -1, "jid", NULL);
00322     jid = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00323     if(jid == NULL) {
00324         log_debug(ZONE, "jid failed prep check, skipping");
00325         return;
00326     }
00327 
00328     /* check for removals */
00329     if(nad_find_attr(pkt->nad, elem, -1, "subscription", "remove") >= 0)
00330     {
00331         /* trash the item */
00332         item = xhash_get(sess->user->roster, jid_full(jid));
00333         if(item != NULL)
00334         {
00335             /* tell them they're unsubscribed */
00336             if(item->from) {
00337                 log_debug(ZONE, "telling %s that they're unsubscribed", jid_user(item->jid));
00338                 pkt_router(pkt_create(sess->user->sm, "presence", "unsubscribed", jid_user(item->jid), jid_user(sess->jid)));
00339             }
00340             item->from = 0;
00341 
00342             /* tell them to unsubscribe us */
00343             if(item->to) {
00344                 log_debug(ZONE, "unsubscribing from %s", jid_user(item->jid));
00345                 pkt_router(pkt_create(sess->user->sm, "presence", "unsubscribe", jid_user(item->jid), jid_user(sess->jid)));
00346             }
00347             item->to = 0;
00348         
00349             /* send unavailable */
00350             pres_roster(sess, item);
00351 
00352             /* kill it */
00353             xhash_zap(sess->user->roster, jid_full(jid));
00354             _roster_freeuser_walker((const char *) jid_full(jid), strlen(jid_full(jid)), (void *) item, NULL);
00355 
00356             snprintf(filter, 4096, "(jid=%zu:%s)", strlen(jid_full(jid)), jid_full(jid));
00357             storage_delete(sess->user->sm->st, "roster-items", jid_user(sess->jid), filter);
00358             storage_delete(sess->user->sm->st, "roster-groups", jid_user(sess->jid), filter);
00359         }
00360 
00361         log_debug(ZONE, "removed %s from roster", jid_full(jid));
00362 
00363         /* build a new packet to push out to everyone */
00364         push = pkt_create(sess->user->sm, "iq", "set", NULL, NULL);
00365         pkt_id_new(push);
00366         ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
00367 
00368         nad_append_elem(push->nad, ns, "query", 3);
00369         elem = nad_append_elem(push->nad, ns, "item", 4);
00370         nad_set_attr(push->nad, elem, -1, "jid", jid_full(jid), 0);
00371         nad_set_attr(push->nad, elem, -1, "subscription", "remove", 6);
00372 
00373         /* tell everyone */
00374         _roster_push(sess->user, push, mod->index);
00375 
00376         /* we're done */
00377         pkt_free(push);
00378 
00379         jid_free(jid);
00380 
00381         return;
00382     }
00383 
00384     /* find a pre-existing one */
00385     item = xhash_get(sess->user->roster, jid_full(jid));
00386     if(item == NULL)
00387     {
00388         /* check if user exceedes maximum roster items */
00389         if(mroster->maxitems > 0) {
00390             ret = storage_count(sess->user->sm->st, "roster-items", jid_user(sess->user->jid), NULL, &items);
00391 
00392             log_debug(ZONE, "user has %i roster-items, maximum is %i", items, mroster->maxitems);
00393 
00394             /* if the limit is reached, skip it */
00395             if (ret == st_SUCCESS && items >= mroster->maxitems)
00396                 return;
00397         }
00398 
00399         /* make a new one */
00400         item = (item_t) calloc(1, sizeof(struct item_st));
00401 
00402         /* add the jid */
00403         item->jid = jid;
00404 
00405         /* add it to the roster */
00406         xhash_put(sess->user->roster, jid_full(item->jid), (void *) item);
00407 
00408         log_debug(ZONE, "created new roster item %s", jid_full(item->jid));
00409     }
00410 
00411     else
00412         jid_free(jid);
00413 
00414     /* extract the name */
00415     attr = nad_find_attr(pkt->nad, elem, -1, "name", NULL);
00416     if(attr >= 0)
00417     {
00418         /* free the old name */
00419         if(item->name != NULL) {
00420             free(item->name);
00421             item->name = NULL;
00422         }
00423 
00424         if (NAD_AVAL_L(pkt->nad, attr) > 0)
00425         {
00426             item->name = (char *) malloc(sizeof(char) * (NAD_AVAL_L(pkt->nad, attr) + 1));
00427             sprintf(item->name, "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00428         }
00429     }
00430 
00431     /* free the old groups */
00432     if(item->groups != NULL)
00433     {
00434         for(i = 0; i < item->ngroups; i++)
00435             free(item->groups[i]);
00436         free(item->groups);
00437         item->ngroups = 0;
00438         item->groups = NULL;
00439     }
00440 
00441     /* loop over the groups, adding them to the array */
00442     elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", 1);
00443     while(elem >= 0)
00444     {
00445         /* empty group tags get skipped */
00446         if(NAD_CDATA_L(pkt->nad, elem) >= 0)
00447         {
00448             /* make room and shove it in */
00449             item->groups = (char **) realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
00450 
00451             item->groups[item->ngroups] = (char *) malloc(sizeof(char) * (NAD_CDATA_L(pkt->nad, elem) + 1));
00452             sprintf(item->groups[item->ngroups], "%.*s", NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem));
00453 
00454             item->ngroups++;
00455         }
00456 
00457         elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "group", 0);
00458     }
00459 
00460     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);
00461 
00462     if (sm_storage_rate_limit(sess->user->sm, jid_user(sess->user->jid)))
00463         return -stanza_err_RESOURCE_CONSTRAINT;
00464 
00465     /* save changes */
00466     _roster_save_item(sess->user, item);
00467 
00468     /* build a new packet to push out to everyone */
00469     push = pkt_create(sess->user->sm, "iq", "set", NULL, NULL);
00470     pkt_id_new(push);
00471     ns = nad_add_namespace(push->nad, uri_ROSTER, NULL);
00472     elem = nad_append_elem(push->nad, ns, "query", 3);
00473 
00474     _roster_insert_item(push, item, elem);
00475 
00476     /* tell everyone */
00477     _roster_push(sess->user, push, mod->index);
00478 
00479     /* we're done */
00480     pkt_free(push);
00481 }
00482 
00484 static mod_ret_t _roster_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
00485 {
00486     module_t mod = mi->mod;
00487     int elem, attr, ver = 0;
00488     pkt_t result;
00489     char *buf;
00490     roster_walker_t rw;
00491 
00492     /* handle s10ns in a different function */
00493     if(pkt->type & pkt_S10N)
00494         return _roster_in_sess_s10n(mi, sess, pkt);
00495 
00496     /* we only want to play with iq:roster packets */
00497     if(pkt->ns != ns_ROSTER)
00498         return mod_PASS;
00499 
00500     /* quietly drop results, its probably them responding to a push */
00501     if(pkt->type == pkt_IQ_RESULT) {
00502         pkt_free(pkt);
00503         return mod_HANDLED;
00504     }
00505 
00506     /* need gets or sets */
00507     if(pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET)
00508         return mod_PASS;
00509 
00510     /* get */
00511     if(pkt->type == pkt_IQ)
00512     {
00513         /* check for "XEP-0237: Roster Versioning request" */
00514         if((elem = nad_find_elem(pkt->nad, 1, -1, "query", 1)) >= 0
00515          &&(attr = nad_find_attr(pkt->nad, elem, -1, "ver", NULL)) >= 0) {
00516             if (NAD_AVAL_L(pkt->nad, attr) > 0)
00517             {
00518                 buf = (char *) malloc(sizeof(char) * (NAD_AVAL_L(pkt->nad, attr) + 1));
00519                 sprintf(buf, "%.*s", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00520                 ver = j_atoi(buf, 0);
00521                 free(buf);
00522             }
00523         }
00524 
00525         /* build the packet */
00526         rw = (roster_walker_t) calloc(1, sizeof(struct _roster_walker_st));
00527         rw->pkt = pkt;
00528         rw->req_ver = ver;
00529         rw->sess = sess;
00530 
00531         nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
00532 
00533         if(ver > 0) {
00534             /* send XEP-0237 empty result */
00535             nad_drop_elem(pkt->nad, elem);
00536             pkt_sess(pkt_tofrom(pkt), sess);
00537             xhash_walk(sess->user->roster, _roster_update_walker, (void *) rw);
00538         }
00539         else {
00540             xhash_walk(sess->user->roster, _roster_get_walker, (void *) rw);
00541             if(elem >= 0 && attr >= 0) {
00542                 buf = (char *) malloc(sizeof(char) * 128);
00543                 sprintf(buf, "%d", rw->ver);
00544                 nad_set_attr(pkt->nad, elem, -1, "ver", buf, 0);
00545                 free(buf);
00546             }
00547             pkt_sess(pkt_tofrom(pkt), sess);
00548         }
00549 
00550         free(rw);
00551 
00552         /* remember that they loaded it, so we know to push updates to them */
00553         sess->module_data[mod->index] = (void *) 1;
00554         
00555         return mod_HANDLED;
00556     }
00557 
00558     /* set, find the item */
00559     elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), "item", 1);
00560     if(elem < 0)
00561         /* no item, abort */
00562         return -stanza_err_BAD_REQUEST;
00563 
00564     /* loop over items and stick them in */
00565     while(elem >= 0)
00566     {
00567         /* extract the jid */
00568         attr = nad_find_attr(pkt->nad, elem, -1, "jid", NULL);
00569         if(attr < 0 || NAD_AVAL_L(pkt->nad, attr) == 0)
00570         {
00571             log_debug(ZONE, "no jid on this item, aborting");
00572 
00573             /* no jid, abort */
00574             return -stanza_err_BAD_REQUEST;
00575         }
00576 
00577         /* utility */
00578         _roster_set_item(pkt, elem, sess, mi);
00579 
00580         /* next one */
00581         elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, elem), "item", 0);
00582     }
00583 
00584     /* send the result */
00585     result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
00586 
00587     pkt_id(pkt, result);
00588 
00589     /* tell them */
00590     pkt_sess(result, sess);
00591 
00592     /* free the request */
00593     pkt_free(pkt);
00594 
00595     return mod_HANDLED;
00596 }
00597 
00599 static mod_ret_t _roster_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt)
00600 {
00601     module_t mod = mi->mod;
00602     item_t item;
00603     int ns, elem;
00604 
00605     /* only want s10ns */
00606     if(!(pkt->type & pkt_S10N))
00607         return mod_PASS;
00608 
00609     /* drop route errors */
00610     if(pkt->rtype & route_ERROR) {
00611         pkt_free(pkt);
00612         return mod_HANDLED;
00613     }
00614 
00615     /* get the roster item */
00616     item = (item_t) xhash_get(user->roster, jid_full(pkt->from));
00617     if(item == NULL) {
00618         /* subs are handled by the client */
00619         if(pkt->type == pkt_S10N) {
00620             /* if the user is online broadcast it like roster push */
00621             if(user->top != NULL && _roster_push(user, pkt, mod->index) > 0) {
00622                 /* pushed, thus handled */
00623                 pkt_free(pkt);
00624                 return mod_HANDLED;
00625             }
00626             else {
00627                 /* not pushed to any online resource - pass it on (to mod_offline) */
00628                 return mod_PASS;
00629             }
00630         }
00631 
00632         /* other S10Ns: we didn't ask for this, so we don't care */
00633         pkt_free(pkt);
00634         return mod_HANDLED;
00635     }
00636 
00637     /* ignore bogus answers */
00638     if( (pkt->type == pkt_S10N_ED && (item->ask != 1 || item->to) )
00639      || (pkt->type == pkt_S10N_UNED && ! item->to) )
00640     {
00641         /* remove pending ask */
00642         if( (pkt->type == pkt_S10N_ED && item->ask == 1)
00643          || (pkt->type == pkt_S10N_UNED && item->ask == 2) )
00644         {
00645             item->ask = 0;
00646             /* save changes */
00647             _roster_save_item(user, item);
00648         }
00649         
00650         pkt_free(pkt);
00651         return mod_HANDLED;
00652     }
00653 
00654     /* trying to subscribe */
00655     if(pkt->type == pkt_S10N)
00656     {
00657         if(item->from)
00658         {
00659             /* already subscribed, tell them */
00660             nad_set_attr(pkt->nad, 1, -1, "type", "subscribed", 10);
00661             pkt_router(pkt_tofrom(pkt));
00662             
00663             /* update their presence from the leading session */
00664             if(user->top != NULL)
00665                 pres_roster(user->top, item);
00666 
00667             return mod_HANDLED;
00668         }
00669 
00670         return mod_PASS;
00671     }
00672 
00673     /* handle unsubscribe */
00674     if(pkt->type == pkt_S10N_UN)
00675     {
00676         if(!item->from)
00677         {
00678             /* already unsubscribed, tell them */
00679             nad_set_attr(pkt->nad, 1, -1, "type", "unsubscribed", 12);
00680             pkt_router(pkt_tofrom(pkt));
00681 
00682             return mod_HANDLED;
00683         }
00684 
00685         /* change state */
00686         item->from = 0;
00687 
00688         /* confirm unsubscription */
00689         pkt_router(pkt_create(user->sm, "presence", "unsubscribed", jid_user(pkt->from), jid_user(user->jid)));
00690 
00691         /* update their presence from the leading session */
00692         if(user->top != NULL)
00693             pres_roster(user->top, item);
00694     }
00695 
00696     /* update our s10n */
00697     if(pkt->type == pkt_S10N_ED)
00698     {
00699         item->to = 1;
00700         if(item->ask == 1)
00701             item->ask = 0;
00702     }
00703     if(pkt->type == pkt_S10N_UNED)
00704     {
00705         item->to = 0;
00706         if(item->ask == 2)
00707             item->ask = 0;
00708     }
00709 
00710     if (sm_storage_rate_limit(user->sm, pkt->from))
00711         return -stanza_err_RESOURCE_CONSTRAINT;
00712 
00713     /* save changes */
00714     _roster_save_item(user, item);
00715 
00716     /* if there's no sessions, then we're done */
00717     if(user->sessions == NULL)
00718         return mod_PASS;
00719 
00720     /* build a new packet to push out to everyone */
00721     pkt = pkt_create(user->sm, "iq", "set", NULL, NULL);
00722     pkt_id_new(pkt);
00723     ns = nad_add_namespace(pkt->nad, uri_ROSTER, NULL);
00724     elem = nad_append_elem(pkt->nad, ns, "query", 3);
00725 
00726     _roster_insert_item(pkt, item, elem);
00727 
00728     /* tell everyone */
00729     _roster_push(user, pkt, mod->index);
00730 
00731     /* everyone knows */
00732     pkt_free(pkt);
00733 
00734     return mod_PASS;
00735 }
00736 
00738 static int _roster_user_load(mod_instance_t mi, user_t user) {
00739     os_t os;
00740     os_object_t o;
00741     char *str;
00742     item_t item, olditem;
00743 
00744     log_debug(ZONE, "loading roster for %s", jid_user(user->jid));
00745 
00746     user->roster = xhash_new(101);
00747 
00748     /* pull all the items */
00749     if(storage_get(user->sm->st, "roster-items", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
00750         if(os_iter_first(os))
00751             do {
00752                 o = os_iter_object(os);
00753 
00754                 if(os_object_get_str(os, o, "jid", &str)) {
00755                     /* new one */
00756                     item = (item_t) calloc(1, sizeof(struct item_st));
00757 
00758                     item->jid = jid_new(str, -1);
00759                     if(item->jid == NULL) {
00760                         log_debug(ZONE, "eek! invalid jid %s, skipping it", str);
00761                         free(item);
00762 
00763                     } else {
00764                         if(os_object_get_str(os, o, "name", &str))
00765                             item->name = strdup(str);
00766                         
00767                         os_object_get_bool(os, o, "to", &item->to);
00768                         os_object_get_bool(os, o, "from", &item->from);
00769                         os_object_get_int(os, o, "ask", &item->ask);
00770                         os_object_get_int(os, o, "object-sequence", &item->ver);
00771 
00772                         olditem = xhash_get(user->roster, jid_full(item->jid));
00773                         if(olditem) {
00774                             log_debug(ZONE, "removing old %s roster entry", jid_full(item->jid));
00775                             xhash_zap(user->roster, jid_full(item->jid));
00776                             _roster_freeuser_walker(jid_full(item->jid), strlen(jid_full(item->jid)), (void *) olditem, NULL);
00777                         }
00778 
00779                         /* its good */
00780                         xhash_put(user->roster, jid_full(item->jid), (void *) item);
00781 
00782                         log_debug(ZONE, "added %s to roster (to %d from %d ask %d ver %d name %s)",
00783                                   jid_full(item->jid), item->to, item->from, item->ask, item->ver, item->name);
00784                     }
00785                 }
00786             } while(os_iter_next(os));
00787 
00788        os_free(os);
00789     }
00790 
00791     /* pull the groups and match them up */
00792     if(storage_get(user->sm->st, "roster-groups", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
00793         if(os_iter_first(os))
00794             do {
00795                 o = os_iter_object(os);
00796 
00797                 if(os_object_get_str(os, o, "jid", &str)) {
00798                     item = xhash_get(user->roster, str);
00799 
00800                     if(item != NULL && os_object_get_str(os, o, "group", &str)) {
00801                         item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
00802                         item->groups[item->ngroups] = strdup(str);
00803                         item->ngroups++;
00804 
00805                         log_debug(ZONE, "added group %s to item %s", str, jid_full(item->jid));
00806                     }
00807                 }
00808             } while(os_iter_next(os));
00809 
00810         os_free(os);
00811     }
00812 
00813     pool_cleanup(user->p, (void (*))(void *) _roster_freeuser, user);
00814 
00815     return 0;
00816 }
00817 
00818 static void _roster_user_delete(mod_instance_t mi, jid_t jid) {
00819     log_debug(ZONE, "deleting roster data for %s", jid_user(jid));
00820 
00821     storage_delete(mi->sm->st, "roster-items", jid_user(jid), NULL);
00822     storage_delete(mi->sm->st, "roster-groups", jid_user(jid), NULL);
00823 }
00824 
00825 static void _roster_free(module_t mod)
00826 {
00827     mod_roster_t mroster = (mod_roster_t) mod->private;
00828     free(mroster);
00829 }
00830 
00831 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
00832     module_t mod = mi->mod;
00833     mod_roster_t mroster;
00834 
00835     if(mod->init) return 0;
00836 
00837     mroster = (mod_roster_t) calloc(1, sizeof(struct _mod_roster_st));
00838 
00839     mroster->maxitems = j_atoi(config_get_one(mod->mm->sm->config, "roster.maxitems", 0), 0);
00840 
00841     mod->private = mroster;
00842 
00843     mod->in_sess = _roster_in_sess;
00844     mod->pkt_user = _roster_pkt_user;
00845     mod->user_load = _roster_user_load;
00846     mod->user_delete = _roster_user_delete;
00847     mod->free = _roster_free;
00848 
00849     feature_register(mod->mm->sm, uri_ROSTER);
00850 
00851     return 0;
00852 }