2
* jabberd - Jabber Open Source Server
3
* Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4
* Ryan Eatmon, Robert Norris
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.
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.
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
23
/** @file sm/mod_roster_publish.c
24
* @brief roster publishing
25
* @author Nikita Smirnov
29
typedef struct _roster_publish_active_cache_st *_roster_publish_active_cache_t;
30
struct _roster_publish_active_cache_st {
31
time_t time; // when cache was updated
35
typedef struct _roster_publish_group_cache_st *_roster_publish_group_cache_t;
36
struct _roster_publish_group_cache_st {
37
time_t time; // when cache was updated
43
typedef struct _roster_publish_st {
44
int publish, forcegroups, fixsubs, overridenames, mappedgroups;
45
char *groupprefix, *groupsuffix, *removedomain;
46
int groupprefixlen, groupsuffixlen;
47
time_t active_cache_ttl;
48
time_t group_cache_ttl;
50
xht active_cache; // cache of values from 'active' storage,
51
// used to check that user exists in sm database
52
xht group_cache; // cache of values from published-roster-groups storage
53
// used to map group id to group name
58
/* free single item of active cache */
59
static void _roster_publish_free_active_cache_walker(xht cache, const char *key, void *val, void *arg) {
60
_roster_publish_active_cache_t item = (_roster_publish_active_cache_t)val;
64
/* free single item of group cache */
65
static void _roster_publish_free_group_cache_walker(xht cache, const char *key, void *val, void *arg) {
66
_roster_publish_group_cache_t item = (_roster_publish_group_cache_t)val;
68
free(item->groupname);
74
* get group's descriptive name by it's text id
75
* returned value needs to be freed by caller
77
static char *_roster_publish_get_group_name(sm_t sm, roster_publish_t rp, const char *groupid)
85
_roster_publish_group_cache_t group_cached;
87
/* check for remembered group value in cache */
88
if( rp->group_cache_ttl ) {
89
if( rp->group_cache ) {
90
group_cached = xhash_get(rp->group_cache, groupid);
91
if( group_cached != NULL ) {
92
if( (time(NULL) - group_cached->time) >= rp->group_cache_ttl ) {
93
log_debug(ZONE,"group cache: expiring cached value for %s",groupid);
94
xhash_zap(rp->group_cache, groupid);
97
log_debug(ZONE,"group cache: returning cached value for %s",groupid);
98
return strdup(group_cached->groupname);
102
log_debug(ZONE,"group cache: creating cache");
103
rp->group_cache = xhash_new(401);
108
if(storage_get(sm->st, "published-roster-groups", groupid, NULL, &os) == st_SUCCESS && os_iter_first(os)) {
109
o = os_iter_object(os);
110
os_object_get_str(os, o, "groupname", &str);
118
if( rp->group_cache_ttl && group ) {
119
log_debug(ZONE,"group cache: updating cache value for %s",groupid);
120
group_cached = calloc(1, sizeof(struct _roster_publish_group_cache_st));
121
group_cached->time = time(NULL);
122
group_cached->groupid = strdup(groupid);
123
group_cached->groupname = strdup(group);
124
xhash_put(rp->group_cache, group_cached->groupid, group_cached);
133
/* free a single roster item */
134
static void _roster_publish_free_walker(xht roster, const char *key, void *val, void *arg)
136
item_t item = (item_t) val;
141
if(item->name != NULL)
144
for(i = 0; i < item->ngroups; i++)
145
free(item->groups[i]);
151
static void _roster_publish_save_item(user_t user, item_t item) {
157
log_debug(ZONE, "saving roster item %s for %s", jid_full(item->jid), jid_user(user->jid));
160
o = os_object_new(os);
162
os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
164
if(item->name != NULL)
165
os_object_put(o, "name", item->name, os_type_STRING);
167
os_object_put(o, "to", &item->to, os_type_BOOLEAN);
168
os_object_put(o, "from", &item->from, os_type_BOOLEAN);
169
os_object_put(o, "ask", &item->ask, os_type_INTEGER);
171
snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid));
173
storage_replace(user->sm->st, "roster-items", jid_user(user->jid), filter, os);
177
snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid));
179
if(item->ngroups == 0) {
180
storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
186
for(i = 0; i < item->ngroups; i++) {
187
o = os_object_new(os);
189
os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
190
os_object_put(o, "group", item->groups[i], os_type_STRING);
193
storage_replace(user->sm->st, "roster-groups", jid_user(user->jid), filter, os);
198
/** publish the roster from the database */
199
static int _roster_publish_user_load(mod_instance_t mi, user_t user) {
200
roster_publish_t roster_publish = (roster_publish_t) mi->mod->private;
202
os_object_t o, o_active;
203
os_type_t ot, ot_active;
204
char *str, *group, filter[4096];
205
int i,j,gpos,found,delete,checksm,userinsm,tmp_to,tmp_from,tmp_do_change;
209
_roster_publish_active_cache_t active_cached;
212
/* update roster to match published roster */
213
if( roster_publish->publish) {
214
/* free if necessary */
215
if(user->roster == NULL) {
216
log_write(user->sm->log, LOG_NOTICE, "roster_publish: no roster for %s",jid_user(user->jid));
220
log_debug(ZONE, "publishing roster for %s",jid_user(user->jid));
221
/* get published roster */
222
if( storage_get(user->sm->st, "published-roster", "", NULL, &os) == st_SUCCESS ) {
223
if(os_iter_first(os)) {
224
/* iterate on published roster */
227
o = os_iter_object(os);
228
if(os_object_get_str(os, o, "jid", &str)) {
229
if( strcmp(str,jid_user(user->jid)) == 0 ) {
230
/* not adding self */
231
continue; /* do { } while( os_iter_next ) */
233
/* check that published item exists in sm database */
235
if( jid ) jid_free(jid);
236
jid = jid_new(str, -1);
237
if( roster_publish->removedomain ) {
238
if( strcmp(jid->domain,roster_publish->removedomain) == 0 ) {
243
/* is this a hack? but i want to know was the user activated in sm or no? */
245
/* check for remembered active value in cache */
247
if( roster_publish->active_cache_ttl ) {
248
if( roster_publish->active_cache ) {
249
active_cached = xhash_get(roster_publish->active_cache, jid_user(jid));
250
if( active_cached != NULL ) {
251
if( (time(NULL) - active_cached->time) >= roster_publish->active_cache_ttl ) {
252
xhash_zap(roster_publish->active_cache, jid_user(jid));
255
if( active_cached->active ) {
263
roster_publish->active_cache = xhash_new(401);
266
if( userinsm == -1 ) {
267
if( roster_publish->active_cache_ttl ) {
268
active_cached = calloc(1, sizeof(struct _roster_publish_active_cache_st));
269
active_cached->time = time(NULL);
272
if(storage_get(user->sm->st, "active", jid_user(jid), NULL, &os_active) == st_SUCCESS
273
&& os_iter_first(os_active)) {
275
if( roster_publish->active_cache_ttl ) {
276
o_active = os_iter_object(os_active);
277
os_object_get_time(os_active, o_active, "time", &active_cached->active);
284
if( roster_publish->active_cache_ttl ) {
285
active_cached->active = 0;
291
if( roster_publish->active_cache_ttl ) {
292
active_cached->jid_user = strdup(jid_user(jid));
293
xhash_put(roster_publish->active_cache, active_cached->jid_user, active_cached);
295
} // if( userinsm == -1 )
298
item = xhash_get(user->roster,jid_user(jid));
300
/* user has no this jid in his roster */
301
/* if we checking sm database and user is not in it, not adding */
302
if( checksm && !userinsm ) {
303
log_debug(ZONE, "published user %s has no record in sm, not adding", jid_user(jid));
304
continue; /* do { } while( os_iter_next ) */
306
log_debug(ZONE, "user has no %s in roster, adding", jid_user(jid));
307
item = (item_t) calloc(1, sizeof(struct item_st));
309
item->jid = jid_new(jid_user(jid), -1);
310
if(item->jid == NULL) {
311
log_debug(ZONE, "eek! invalid jid %s, skipping it", jid_user(jid));
312
log_write(user->sm->log, LOG_ERR, "roster_publish: eek! invalid jid %s, skipping it", jid_user(jid));
313
/* nvs: is it needed? */
315
/* nvs: is it needed? */
317
os_object_get_str(os, o, "group", &str);
318
if( roster_publish->mappedgroups ) {
319
group = _roster_publish_get_group_name(user->sm, roster_publish, str); // don't forget to free group
324
item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
325
item->groups[item->ngroups] = group;
328
if(os_object_get_str(os, o, "name", &str))
329
item->name = strdup(str);
331
os_object_get_bool(os, o, "to", &item->to);
332
os_object_get_bool(os, o, "from", &item->from);
333
os_object_get_int(os, o, "ask", &item->ask);
335
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);
338
xhash_put(user->roster, jid_full(item->jid), (void *) item);
339
_roster_publish_save_item(user,item);
341
log_write(user->sm->log, LOG_ERR, "roster_publish: unknown published group id '%s' for %s",str,jid_full(item->jid));
346
else /* if( item == NULL ) else ... : here item != NULL : user has this jid in his roster */
348
/* if we checking sm database and user is not in it, remove it from roster */
349
if( checksm && !userinsm ) {
350
log_debug(ZONE, "published user %s has no record in sm, deleting from roster", jid_user(jid));
351
snprintf(filter, 4096, "(jid=%s)", jid_full(jid));
352
storage_delete(user->sm->st, "roster-items", jid_user(user->jid), filter);
353
snprintf(filter, 4096, "(jid=%s)", jid_full(jid));
354
storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
356
xhash_zap(user->roster, jid_full(jid));
357
_roster_publish_free_walker(NULL, (const char *) jid_full(jid), (void *) item, NULL);
358
continue; /* do { } while( os_iter_next ) */
360
if( roster_publish->fixsubs ) {
361
/* check subscriptions and correct if needed */
362
os_object_get_bool(os, o, "to", &tmp_to);
363
os_object_get_bool(os, o, "from", &tmp_from);
364
if( item->to != tmp_to || item->from != tmp_from ) {
366
item->from = tmp_from;
367
log_debug(ZONE, "fixsubs in roster %s, item %s",jid_user(user->jid),jid_user(item->jid));
368
xhash_put(user->roster, jid_full(item->jid), (void *) item);
369
_roster_publish_save_item(user,item);
372
if( roster_publish->overridenames ) {
373
/* override display name if it differs */
374
if(os_object_get_str(os, o, "name", &str)) {
380
if( strcmp(item->name,str) != 0 ) {
384
if( tmp_do_change ) {
385
log_debug(ZONE, "replacing name for %s in roster of %s", jid_full(item->jid),jid_user(user->jid));
386
item->name = strdup(str);
387
xhash_put(user->roster, jid_full(item->jid), (void *) item);
388
_roster_publish_save_item(user,item);
391
log_debug(ZONE,"warning: name is null in published roster for item %s",jid_full(item->jid));
395
if( roster_publish->forcegroups ) {
396
/* item already in roster, check groups if needed */
397
os_object_get_str(os, o, "group", &str);
398
if( roster_publish->mappedgroups ) {
399
group = _roster_publish_get_group_name(user->sm, roster_publish, str); // don't forget to free group
401
log_write(user->sm->log, LOG_ERR, "roster_publish: unknown published group id '%s' for %s",str, jid_full(item->jid));
402
continue; /* do { } while( os_iter_next ) */
407
/* find published roster item's group in user's roster */
409
for(i = 0; i < item->ngroups; i++) {
410
if( strcmp(item->groups[i],group) == 0 ) {
412
/* do not break loop, give groups that matches
413
* prefix and suffix to be deleted
416
/* check if user's roster group matches
417
* prefix or suffix given in config
418
* and delete such groups (and thus they will be replaced)
421
if( roster_publish->groupprefix ) {
422
if( strncmp(item->groups[i],roster_publish->groupprefix,roster_publish->groupprefixlen) == 0 ) {
426
if( !delete && roster_publish->groupsuffix ) {
427
gpos=strlen(item->groups[i])-roster_publish->groupsuffixlen;
429
if( strcmp(item->groups[i]+gpos,roster_publish->groupsuffix) == 0 ) {
434
/* remove group from roster item */
436
free(item->groups[i]);
437
for(j = i; j < item->ngroups-1; j++) {
438
item->groups[j]=item->groups[j+1];
441
item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups));
446
log_debug(ZONE, "adding group %s to item %s for user %s",group,jid_user(item->jid),jid_user(user->jid));
447
item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
448
item->groups[item->ngroups] = group; // will be freed
451
xhash_put(user->roster, jid_full(item->jid), (void *) item);
452
_roster_publish_save_item(user,item);
456
} /* else if( roster_publish->forcegroups ) */
457
} /* end of if if( item == NULL ) */
458
} /* if( os_object_get(...) */
459
} while(os_iter_next(os));
460
if( jid ) jid_free(jid);
468
static void _roster_publish_free(module_t mod) {
469
roster_publish_t roster_publish = (roster_publish_t) mod->private;
472
if( roster_publish->active_cache ) {
473
xhash_walk(roster_publish->active_cache,_roster_publish_free_active_cache_walker,NULL);
474
xhash_free(roster_publish->active_cache);
476
if( roster_publish->group_cache ) {
477
xhash_walk(roster_publish->group_cache,_roster_publish_free_group_cache_walker,NULL);
478
xhash_free(roster_publish->group_cache);
481
free(roster_publish);
484
DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
485
module_t mod = mi->mod;
486
roster_publish_t roster_publish;
488
if(mod->init) return 0;
490
roster_publish = (roster_publish_t) calloc(1, sizeof(struct _roster_publish_st));
492
if( config_get_one(mod->mm->sm->config, "user.template.publish", 0) ) {
493
roster_publish->publish = 1;
494
roster_publish->removedomain = config_get_one(mod->mm->sm->config, "user.template.publish.check-remove-domain", 0);
495
roster_publish->fixsubs = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.fix-subscriptions", 0), 0);
496
roster_publish->overridenames = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.override-names", 0), 0);
497
roster_publish->mappedgroups = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.mapped-groups.map-groups", 0), 0);
499
roster_publish->active_cache_ttl = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.active-cache-ttl", 0), 0);
500
roster_publish->group_cache_ttl = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.mapped-groups.group-cache-ttl", 0), 0);
502
if( config_get_one(mod->mm->sm->config, "user.template.publish.force-groups", 0) ) {
503
roster_publish->forcegroups = 1;
504
if( roster_publish->groupprefix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.prefix", 0) ) {
505
roster_publish->groupprefixlen = strlen(roster_publish->groupprefix);
507
if( roster_publish->groupsuffix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.suffix", 0) ) {
508
roster_publish->groupsuffixlen = strlen(roster_publish->groupsuffix);
511
roster_publish->forcegroups = 0;
514
roster_publish->publish = 0;
516
mod->private = roster_publish;
518
mod->user_load = _roster_publish_user_load;
519
mod->free = _roster_publish_free;