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 00028 #ifndef NO_SM_CACHE 00029 typedef struct _roster_publish_active_cache_st *_roster_publish_active_cache_t; 00030 struct _roster_publish_active_cache_st { 00031 time_t time; // when cache was updated 00032 time_t active; 00033 char *jid_user; 00034 }; 00035 typedef struct _roster_publish_group_cache_st *_roster_publish_group_cache_t; 00036 struct _roster_publish_group_cache_st { 00037 time_t time; // when cache was updated 00038 char *groupid; 00039 char *groupname; 00040 }; 00041 #endif 00042 00043 typedef struct _roster_publish_st { 00044 int publish, forcegroups, fixsubs, overridenames, mappedgroups, fixexist; 00045 char *fetchdomain, *fetchuser, *fetchfixed, *dbtable; 00046 char *groupprefix, *groupsuffix, *removedomain; 00047 int groupprefixlen, groupsuffixlen; 00048 time_t active_cache_ttl; 00049 time_t group_cache_ttl; 00050 #ifndef NO_SM_CACHE 00051 xht active_cache; // cache of values from 'active' storage, 00052 // used to check that user exists in sm database 00053 xht group_cache; // cache of values from published-roster-groups storage 00054 // used to map group id to group name 00055 #endif 00056 } *roster_publish_t; 00057 00058 #ifndef NO_SM_CACHE 00059 /* free single item of active cache */ 00060 static void _roster_publish_free_active_cache_walker(const char *key, int keylen, void *val, void *arg) { 00061 _roster_publish_active_cache_t item = (_roster_publish_active_cache_t)val; 00062 free(item->jid_user); 00063 free(item); 00064 } 00065 /* free single item of group cache */ 00066 static void _roster_publish_free_group_cache_walker(const char *key, int keylen, void *val, void *arg) { 00067 _roster_publish_group_cache_t item = (_roster_publish_group_cache_t)val; 00068 free(item->groupid); 00069 free(item->groupname); 00070 free(item); 00071 } 00072 #endif 00073 00074 /* 00075 * get group's descriptive name by it's text id 00076 * returned value needs to be freed by caller 00077 */ 00078 static char *_roster_publish_get_group_name(sm_t sm, roster_publish_t rp, char *groupid) 00079 { 00080 os_t os; 00081 os_object_t o; 00082 char *str; 00083 char *group; 00084 00085 #ifndef NO_SM_CACHE 00086 _roster_publish_group_cache_t group_cached; 00087 #endif 00088 00089 if(!groupid) return groupid; 00090 00091 #ifndef NO_SM_CACHE 00092 /* check for remembered group value in cache */ 00093 if( rp->group_cache_ttl ) { 00094 if( rp->group_cache ) { 00095 group_cached = xhash_get(rp->group_cache, groupid); 00096 if( group_cached != NULL ) { 00097 if( (time(NULL) - group_cached->time) >= rp->group_cache_ttl ) { 00098 log_debug(ZONE,"group cache: expiring cached value for %s",groupid); 00099 xhash_zap(rp->group_cache, groupid); 00100 free(group_cached); 00101 } else { 00102 log_debug(ZONE,"group cache: returning cached value for %s",groupid); 00103 return strdup(group_cached->groupname); 00104 } 00105 } 00106 } else { 00107 log_debug(ZONE,"group cache: creating cache"); 00108 rp->group_cache = xhash_new(401); 00109 } 00110 } 00111 #endif 00112 00113 if(storage_get(sm->st, "published-roster-groups", groupid, NULL, &os) == st_SUCCESS && os_iter_first(os)) { 00114 o = os_iter_object(os); 00115 os_object_get_str(os, o, "groupname", &str); 00116 if( str ) { 00117 group=strdup(str); 00118 } else { 00119 group=NULL; 00120 } 00121 os_free(os); 00122 #ifndef NO_SM_CACHE 00123 if( rp->group_cache_ttl && group ) { 00124 log_debug(ZONE,"group cache: updating cache value for %s",groupid); 00125 group_cached = calloc(1, sizeof(struct _roster_publish_group_cache_st)); 00126 group_cached->time = time(NULL); 00127 group_cached->groupid = strdup(groupid); 00128 group_cached->groupname = strdup(group); 00129 xhash_put(rp->group_cache, group_cached->groupid, group_cached); 00130 } 00131 #endif 00132 return group; 00133 } else { 00134 return NULL; 00135 } 00136 } 00137 00138 /* free a single roster item */ 00139 static void _roster_publish_free_walker(xht roster, const char *key, void *val, void *arg) 00140 { 00141 item_t item = (item_t) val; 00142 int i; 00143 00144 jid_free(item->jid); 00145 00146 if(item->name != NULL) 00147 free(item->name); 00148 00149 for(i = 0; i < item->ngroups; i++) 00150 free(item->groups[i]); 00151 free(item->groups); 00152 00153 free(item); 00154 } 00155 00156 static void _roster_publish_save_item(user_t user, item_t item) { 00157 os_t os; 00158 os_object_t o; 00159 char filter[4096]; 00160 int i; 00161 00162 log_debug(ZONE, "saving roster item %s for %s", jid_full(item->jid), jid_user(user->jid)); 00163 00164 os = os_new(); 00165 o = os_object_new(os); 00166 00167 os_object_put(o, "jid", jid_full(item->jid), os_type_STRING); 00168 00169 if(item->name != NULL) 00170 os_object_put(o, "name", item->name, os_type_STRING); 00171 00172 os_object_put(o, "to", &item->to, os_type_BOOLEAN); 00173 os_object_put(o, "from", &item->from, os_type_BOOLEAN); 00174 os_object_put(o, "ask", &item->ask, os_type_INTEGER); 00175 00176 snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid)); 00177 00178 storage_replace(user->sm->st, "roster-items", jid_user(user->jid), filter, os); 00179 00180 os_free(os); 00181 00182 snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid)); 00183 00184 if(item->ngroups == 0) { 00185 storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter); 00186 return; 00187 } 00188 00189 os = os_new(); 00190 00191 for(i = 0; i < item->ngroups; i++) { 00192 o = os_object_new(os); 00193 00194 os_object_put(o, "jid", jid_full(item->jid), os_type_STRING); 00195 os_object_put(o, "group", item->groups[i], os_type_STRING); 00196 } 00197 00198 storage_replace(user->sm->st, "roster-groups", jid_user(user->jid), filter, os); 00199 00200 os_free(os); 00201 } 00202 00204 static int _roster_publish_user_load(mod_instance_t mi, user_t user) { 00205 roster_publish_t roster_publish = (roster_publish_t) mi->mod->private; 00206 os_t os, os_active; 00207 os_object_t o, o_active; 00208 char *str, *group, filter[4096]; 00209 const char *fetchkey; 00210 int i,j,gpos,found,delete,checksm,tmp_to,tmp_from,tmp_do_change; 00211 item_t item; 00212 jid_t jid; 00213 00214 /* update roster to match published roster */ 00215 if( roster_publish->publish) { 00216 /* free if necessary */ 00217 if(user->roster == NULL) { 00218 log_write(user->sm->log, LOG_NOTICE, "roster_publish: no roster for %s",jid_user(user->jid)); 00219 return 0; 00220 } 00221 00222 log_debug(ZONE, "publishing roster for %s",jid_user(user->jid)); 00223 /* get published roster */ 00224 if(roster_publish->fetchfixed) 00225 fetchkey = roster_publish->fetchfixed; 00226 else if(roster_publish->fetchuser) 00227 fetchkey = jid_user(user->jid); 00228 else if(roster_publish->fetchdomain) 00229 fetchkey = user->jid->domain; 00230 else 00231 fetchkey = ""; 00232 00233 if( storage_get(user->sm->st, (roster_publish->dbtable ? roster_publish->dbtable : "published-roster"), fetchkey, NULL, &os) == st_SUCCESS ) { 00234 if(os_iter_first(os)) { 00235 /* iterate on published roster */ 00236 jid = NULL; 00237 do { 00238 o = os_iter_object(os); 00239 if(os_object_get_str(os, o, "jid", &str)) { 00240 #ifndef NO_SM_CACHE 00241 int userinsm; 00242 _roster_publish_active_cache_t active_cached = 0; 00243 #endif 00244 log_debug(ZONE, "got %s item for inserting in", str); 00245 if( strcmp(str,jid_user(user->jid)) == 0 ) { 00246 /* not adding self */ 00247 continue; /* do { } while( os_iter_next ) */ 00248 } 00249 /* check that published item exists in sm database */ 00250 checksm=0; 00251 if( jid ) jid_free(jid); 00252 jid = jid_new(str, -1); 00253 if( roster_publish->removedomain ) { 00254 if( strcmp("1", roster_publish->removedomain) == 0 || /* XXX HACKY!!! "1" is very config.c dependant */ 00255 strcmp(jid->domain, roster_publish->removedomain) == 0 ) { 00256 checksm = 1; 00257 } 00258 } 00259 if( checksm ) { 00260 /* is this a hack? but i want to know was the user activated in sm or no? */ 00261 #ifndef NO_SM_CACHE 00262 /* check for remembered active value in cache */ 00263 userinsm = -1; 00264 if( roster_publish->active_cache_ttl ) { 00265 if( roster_publish->active_cache ) { 00266 active_cached = xhash_get(roster_publish->active_cache, jid_user(jid)); 00267 if( active_cached != NULL ) { 00268 if( (time(NULL) - active_cached->time) >= roster_publish->active_cache_ttl ) { 00269 xhash_zap(roster_publish->active_cache, jid_user(jid)); 00270 free(active_cached); 00271 } else { 00272 if( active_cached->active ) { 00273 userinsm = 1; 00274 } else { 00275 userinsm = 0; 00276 } 00277 } 00278 } 00279 } else { 00280 roster_publish->active_cache = xhash_new(401); 00281 } 00282 } 00283 if( userinsm == -1 ) { 00284 if( roster_publish->active_cache_ttl ) { 00285 active_cached = calloc(1, sizeof(struct _roster_publish_active_cache_st)); 00286 active_cached->time = time(NULL); 00287 } 00288 #endif 00289 if(storage_get(user->sm->st, "active", jid_user(jid), NULL, &os_active) == st_SUCCESS 00290 && os_iter_first(os_active)) { 00291 #ifndef NO_SM_CACHE 00292 if( roster_publish->active_cache_ttl ) { 00293 o_active = os_iter_object(os_active); 00294 os_object_get_time(os_active, o_active, "time", &active_cached->active); 00295 } 00296 #endif 00297 os_free(os_active); 00298 userinsm = 1; 00299 } else { 00300 #ifndef NO_SM_CACHE 00301 if( roster_publish->active_cache_ttl ) { 00302 active_cached->active = 0; 00303 } 00304 #endif 00305 userinsm = 0; 00306 } 00307 #ifndef NO_SM_CACHE 00308 if( roster_publish->active_cache_ttl ) { 00309 active_cached->jid_user = strdup(jid_user(jid)); 00310 xhash_put(roster_publish->active_cache, active_cached->jid_user, active_cached); 00311 } 00312 } // if( userinsm == -1 ) 00313 #endif 00314 } else userinsm = 0; // if( checksm ) 00315 item = xhash_get(user->roster,jid_user(jid)); 00316 if( item == NULL ) { 00317 /* user has no this jid in his roster */ 00318 /* if we checking sm database and user is not in it, not adding */ 00319 if( checksm && !userinsm ) { 00320 log_debug(ZONE, "published user %s has no record in sm, not adding", jid_user(jid)); 00321 continue; /* do { } while( os_iter_next ) */ 00322 } 00323 log_debug(ZONE, "user has no %s in roster, adding", jid_user(jid)); 00324 item = (item_t) calloc(1, sizeof(struct item_st)); 00325 00326 item->jid = jid_new(jid_user(jid), -1); 00327 if(item->jid == NULL) { 00328 log_debug(ZONE, "eek! invalid jid %s, skipping it", jid_user(jid)); 00329 log_write(user->sm->log, LOG_ERR, "roster_publish: eek! invalid jid %s, skipping it", jid_user(jid)); 00330 /* nvs: is it needed? */ 00331 free(item); 00332 /* nvs: is it needed? */ 00333 } else { 00334 os_object_get_str(os, o, "group", &str); 00335 if( roster_publish->mappedgroups ) { 00336 group = _roster_publish_get_group_name(user->sm, roster_publish, str); // don't forget to free group 00337 } else { 00338 if(str) 00339 group = strdup(str); 00340 else 00341 group = NULL; 00342 } 00343 if( group ) { 00344 item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1)); 00345 item->groups[item->ngroups] = group; 00346 item->ngroups++; 00347 00348 if(os_object_get_str(os, o, "name", &str)) 00349 item->name = strdup(str); 00350 00351 os_object_get_bool(os, o, "to", &item->to); 00352 os_object_get_bool(os, o, "from", &item->from); 00353 os_object_get_int(os, o, "ask", &item->ask); 00354 00355 log_debug(ZONE, "adding %s to roster from template (to %d from %d ask %d name %s)", jid_full(item->jid), item->to, item->from, item->ask, item->name); 00356 00357 /* its good */ 00358 xhash_put(user->roster, jid_full(item->jid), (void *) item); 00359 _roster_publish_save_item(user,item); 00360 } else { 00361 log_write(user->sm->log, LOG_ERR, "roster_publish: unknown published group id '%s' for %s",str,jid_full(item->jid)); 00362 free(item); 00363 } 00364 if (roster_publish->fixexist && 00365 ( (checksm && !userinsm) || 00366 (!checksm && storage_get(user->sm->st, "active", jid_user(jid), NULL, &os_active) == st_SUCCESS && os_iter_first(os_active)) 00367 ) 00368 ) { 00369 /* Add thise jid to active table*/ 00370 log_debug(ZONE, "adding published user %s to sm", jid_user(jid)); 00371 time_t tfe; 00372 os_t osfe; 00373 00374 os_object_t ofe; 00375 tfe = time(NULL); 00376 osfe = os_new(); 00377 ofe = os_object_new(osfe); 00378 os_object_put_time(ofe, "time", &tfe); 00379 storage_put(mi->sm->st, "active", jid_user(jid), osfe); 00380 os_free(osfe); 00381 } 00382 } 00383 } 00384 else /* if( item == NULL ) else ... : here item != NULL : user has this jid in his roster */ 00385 { 00386 /* if we checking sm database and user is not in it, remove it from roster */ 00387 if( checksm && !userinsm ) { 00388 log_debug(ZONE, "published user %s has no record in sm, deleting from roster", jid_user(jid)); 00389 snprintf(filter, 4096, "(jid=%s)", jid_full(jid)); 00390 storage_delete(user->sm->st, "roster-items", jid_user(user->jid), filter); 00391 snprintf(filter, 4096, "(jid=%s)", jid_full(jid)); 00392 storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter); 00393 00394 xhash_zap(user->roster, jid_full(jid)); 00395 _roster_publish_free_walker(NULL, (const char *) jid_full(jid), (void *) item, NULL); 00396 continue; /* do { } while( os_iter_next ) */ 00397 } 00398 if( roster_publish->fixsubs ) { 00399 /* check subscriptions and correct if needed */ 00400 os_object_get_bool(os, o, "to", &tmp_to); 00401 os_object_get_bool(os, o, "from", &tmp_from); 00402 if( item->to != tmp_to || item->from != tmp_from ) { 00403 item->to = tmp_to; 00404 item->from = tmp_from; 00405 log_debug(ZONE, "fixsubs in roster %s, item %s",jid_user(user->jid),jid_user(item->jid)); 00406 xhash_put(user->roster, jid_full(item->jid), (void *) item); 00407 _roster_publish_save_item(user,item); 00408 } 00409 } 00410 if( roster_publish->overridenames ) { 00411 /* override display name if it differs */ 00412 if(os_object_get_str(os, o, "name", &str)) { 00413 if( str ) { 00414 tmp_do_change = 0; 00415 if( ! item->name ) { 00416 tmp_do_change = 1; 00417 } else { 00418 if( strcmp(item->name,str) != 0 ) { 00419 tmp_do_change = 1; 00420 } 00421 } 00422 if( tmp_do_change ) { 00423 log_debug(ZONE, "replacing name for %s in roster of %s", jid_full(item->jid),jid_user(user->jid)); 00424 item->name = strdup(str); 00425 xhash_put(user->roster, jid_full(item->jid), (void *) item); 00426 _roster_publish_save_item(user,item); 00427 } 00428 } else { 00429 log_debug(ZONE,"warning: name is null in published roster for item %s",jid_full(item->jid)); 00430 } 00431 } 00432 } 00433 if( roster_publish->forcegroups ) { 00434 /* item already in roster, check groups if needed */ 00435 os_object_get_str(os, o, "group", &str); 00436 if( roster_publish->mappedgroups ) { 00437 group = _roster_publish_get_group_name(user->sm, roster_publish, str); // don't forget to free group 00438 if( !group ) { 00439 log_write(user->sm->log, LOG_ERR, "roster_publish: unknown published group id '%s' for %s",str, jid_full(item->jid)); 00440 continue; /* do { } while( os_iter_next ) */ 00441 } 00442 } else { 00443 group = strdup(str); 00444 } 00445 /* find published roster item's group in user's roster */ 00446 found = 0; 00447 for(i = 0; i < item->ngroups; i++) { 00448 if( strcmp(item->groups[i],group) == 0 ) { 00449 found = 1; 00450 /* do not break loop, give groups that matches 00451 * prefix and suffix to be deleted 00452 */ 00453 } else { 00454 /* check if user's roster group matches 00455 * prefix or suffix given in config 00456 * and delete such groups (and thus they will be replaced) 00457 */ 00458 delete = 0; 00459 if( roster_publish->groupprefix ) { 00460 if( strncmp(item->groups[i],roster_publish->groupprefix,roster_publish->groupprefixlen) == 0 ) { 00461 delete = 1; 00462 } 00463 } 00464 if( !delete && roster_publish->groupsuffix ) { 00465 gpos=strlen(item->groups[i])-roster_publish->groupsuffixlen; 00466 if( gpos > 0 ) { 00467 if( strcmp(item->groups[i]+gpos,roster_publish->groupsuffix) == 0 ) { 00468 delete = 1; 00469 } 00470 } 00471 } 00472 /* remove group from roster item */ 00473 if( delete ) { 00474 free(item->groups[i]); 00475 for(j = i; j < item->ngroups-1; j++) { 00476 item->groups[j]=item->groups[j+1]; 00477 } 00478 item->ngroups--; 00479 item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups)); 00480 } 00481 } 00482 } /* for(i... */ 00483 if( !found ) { 00484 log_debug(ZONE, "adding group %s to item %s for user %s",group,jid_user(item->jid),jid_user(user->jid)); 00485 item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1)); 00486 item->groups[item->ngroups] = group; // will be freed 00487 item->ngroups++; 00488 /* replace item */ 00489 xhash_put(user->roster, jid_full(item->jid), (void *) item); 00490 _roster_publish_save_item(user,item); 00491 } else { 00492 free(group); 00493 } 00494 } /* else if( roster_publish->forcegroups ) */ 00495 } /* end of if if( item == NULL ) */ 00496 } /* if( os_object_get(...) */ 00497 } while(os_iter_next(os)); 00498 if( jid ) jid_free(jid); 00499 } 00500 os_free(os); 00501 } 00502 } 00503 return 0; 00504 } 00505 00506 static void _roster_publish_free(module_t mod) { 00507 roster_publish_t roster_publish = (roster_publish_t) mod->private; 00508 00509 #ifndef NO_SM_CACHE 00510 if( roster_publish->active_cache ) { 00511 xhash_walk(roster_publish->active_cache,_roster_publish_free_active_cache_walker,NULL); 00512 xhash_free(roster_publish->active_cache); 00513 } 00514 if( roster_publish->group_cache ) { 00515 xhash_walk(roster_publish->group_cache,_roster_publish_free_group_cache_walker,NULL); 00516 xhash_free(roster_publish->group_cache); 00517 } 00518 #endif 00519 free(roster_publish); 00520 } 00521 00522 DLLEXPORT int module_init(mod_instance_t mi, char *arg) { 00523 module_t mod = mi->mod; 00524 roster_publish_t roster_publish; 00525 00526 if(mod->init) return 0; 00527 00528 roster_publish = (roster_publish_t) calloc(1, sizeof(struct _roster_publish_st)); 00529 00530 if( config_get_one(mod->mm->sm->config, "user.template.publish", 0) ) { 00531 roster_publish->publish = 1; 00532 roster_publish->fetchdomain = config_get_one(mod->mm->sm->config, "user.template.publish.fetch-key.domain", 0); 00533 roster_publish->fetchuser = config_get_one(mod->mm->sm->config, "user.template.publish.fetch-key.user", 0); 00534 roster_publish->fetchfixed = config_get_one(mod->mm->sm->config, "user.template.publish.fetch-key.fixed", 0); 00535 roster_publish->dbtable = config_get_one(mod->mm->sm->config, "user.template.publish.db-table", 0); 00536 roster_publish->removedomain = config_get_one(mod->mm->sm->config, "user.template.publish.check-remove-domain", 0); 00537 roster_publish->fixsubs = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.fix-subscriptions", 0), 0); 00538 roster_publish->overridenames = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.override-names", 0), 0); 00539 roster_publish->mappedgroups = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.mapped-groups.map-groups", 0), 0); 00540 roster_publish->fixexist = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.force-create-contacts", 0), 0); 00541 #ifndef NO_SM_CACHE 00542 roster_publish->active_cache_ttl = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.active-cache-ttl", 0), 0); 00543 roster_publish->group_cache_ttl = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.mapped-groups.group-cache-ttl", 0), 0); 00544 #endif 00545 if( config_get_one(mod->mm->sm->config, "user.template.publish.force-groups", 0) ) { 00546 roster_publish->forcegroups = 1; 00547 roster_publish->groupprefix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.prefix", 0); 00548 if( roster_publish->groupprefix ) { 00549 roster_publish->groupprefixlen = strlen(roster_publish->groupprefix); 00550 } 00551 roster_publish->groupsuffix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.suffix", 0); 00552 if( roster_publish->groupsuffix ) { 00553 roster_publish->groupsuffixlen = strlen(roster_publish->groupsuffix); 00554 } 00555 } else { 00556 roster_publish->forcegroups = 0; 00557 } 00558 } else { 00559 roster_publish->publish = 0; 00560 } 00561 mod->private = roster_publish; 00562 00563 mod->user_load = _roster_publish_user_load; 00564 mod->free = _roster_publish_free; 00565 00566 return 0; 00567 } 00568 00569 // vim: shiftwidth=4