jabberd2
2.2.16
|
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 }