jabberd2  2.2.17
mod_roster_publish.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
19  */
20 
21 #include "sm.h"
22 
28 #ifndef NO_SM_CACHE
31  time_t time; // when cache was updated
32  time_t active;
33  char *jid_user;
34 };
37  time_t time; // when cache was updated
38  char *groupid;
39  char *groupname;
40 };
41 #endif
42 
43 typedef struct _roster_publish_st {
50 #ifndef NO_SM_CACHE
51  xht active_cache; // cache of values from 'active' storage,
52  // used to check that user exists in sm database
53  xht group_cache; // cache of values from published-roster-groups storage
54  // used to map group id to group name
55 #endif
57 
58 #ifndef NO_SM_CACHE
59 /* free single item of active cache */
60 static void _roster_publish_free_active_cache_walker(const char *key, int keylen, void *val, void *arg) {
61  _roster_publish_active_cache_t item = (_roster_publish_active_cache_t)val;
62  free(item->jid_user);
63  free(item);
64 }
65 /* free single item of group cache */
66 static void _roster_publish_free_group_cache_walker(const char *key, int keylen, void *val, void *arg) {
67  _roster_publish_group_cache_t item = (_roster_publish_group_cache_t)val;
68  free(item->groupid);
69  free(item->groupname);
70  free(item);
71 }
72 #endif
73 
74 /*
75  * get group's descriptive name by it's text id
76  * returned value needs to be freed by caller
77  */
78 static char *_roster_publish_get_group_name(sm_t sm, roster_publish_t rp, char *groupid)
79 {
80  os_t os;
81  os_object_t o;
82  char *str;
83  char *group;
84 
85 #ifndef NO_SM_CACHE
86  _roster_publish_group_cache_t group_cached;
87 #endif
88 
89  if(!groupid) return groupid;
90 
91 #ifndef NO_SM_CACHE
92  /* check for remembered group value in cache */
93  if( rp->group_cache_ttl ) {
94  if( rp->group_cache ) {
95  group_cached = xhash_get(rp->group_cache, groupid);
96  if( group_cached != NULL ) {
97  if( (time(NULL) - group_cached->time) >= rp->group_cache_ttl ) {
98  log_debug(ZONE,"group cache: expiring cached value for %s",groupid);
99  xhash_zap(rp->group_cache, groupid);
100  free(group_cached);
101  } else {
102  log_debug(ZONE,"group cache: returning cached value for %s",groupid);
103  return strdup(group_cached->groupname);
104  }
105  }
106  } else {
107  log_debug(ZONE,"group cache: creating cache");
108  rp->group_cache = xhash_new(401);
109  }
110  }
111 #endif
112 
113  if(storage_get(sm->st, "published-roster-groups", groupid, NULL, &os) == st_SUCCESS && os_iter_first(os)) {
114  o = os_iter_object(os);
115  os_object_get_str(os, o, "groupname", &str);
116  if( str ) {
117  group=strdup(str);
118  } else {
119  group=NULL;
120  }
121  os_free(os);
122 #ifndef NO_SM_CACHE
123  if( rp->group_cache_ttl && group ) {
124  log_debug(ZONE,"group cache: updating cache value for %s",groupid);
125  group_cached = calloc(1, sizeof(struct _roster_publish_group_cache_st));
126  group_cached->time = time(NULL);
127  group_cached->groupid = strdup(groupid);
128  group_cached->groupname = strdup(group);
129  xhash_put(rp->group_cache, group_cached->groupid, group_cached);
130  }
131 #endif
132  return group;
133  } else {
134  return NULL;
135  }
136 }
137 
138 /* free a single roster item */
139 static void _roster_publish_free_walker(xht roster, const char *key, void *val, void *arg)
140 {
141  item_t item = (item_t) val;
142  int i;
143 
144  jid_free(item->jid);
145 
146  if(item->name != NULL)
147  free(item->name);
148 
149  for(i = 0; i < item->ngroups; i++)
150  free(item->groups[i]);
151  free(item->groups);
152 
153  free(item);
154 }
155 
156 static void _roster_publish_save_item(user_t user, item_t item) {
157  os_t os;
158  os_object_t o;
159  char filter[4096];
160  int i;
161 
162  log_debug(ZONE, "saving roster item %s for %s", jid_full(item->jid), jid_user(user->jid));
163 
164  os = os_new();
165  o = os_object_new(os);
166 
167  os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
168 
169  if(item->name != NULL)
170  os_object_put(o, "name", item->name, os_type_STRING);
171 
172  os_object_put(o, "to", &item->to, os_type_BOOLEAN);
173  os_object_put(o, "from", &item->from, os_type_BOOLEAN);
174  os_object_put(o, "ask", &item->ask, os_type_INTEGER);
175 
176  snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid));
177 
178  storage_replace(user->sm->st, "roster-items", jid_user(user->jid), filter, os);
179 
180  os_free(os);
181 
182  snprintf(filter, 4096, "(jid=%s)", jid_full(item->jid));
183 
184  if(item->ngroups == 0) {
185  storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
186  return;
187  }
188 
189  os = os_new();
190 
191  for(i = 0; i < item->ngroups; i++) {
192  o = os_object_new(os);
193 
194  os_object_put(o, "jid", jid_full(item->jid), os_type_STRING);
195  os_object_put(o, "group", item->groups[i], os_type_STRING);
196  }
197 
198  storage_replace(user->sm->st, "roster-groups", jid_user(user->jid), filter, os);
199 
200  os_free(os);
201 }
202 
205  roster_publish_t roster_publish = (roster_publish_t) mi->mod->private;
206  os_t os, os_active;
207  os_object_t o, o_active;
208  char *str, *group, filter[4096];
209  const char *fetchkey;
210  int i,j,gpos,found,delete,checksm,tmp_to,tmp_from,tmp_do_change;
211  item_t item;
212  jid_t jid;
213 
214  /* update roster to match published roster */
215  if( roster_publish->publish) {
216  /* free if necessary */
217  if(user->roster == NULL) {
218  log_write(user->sm->log, LOG_NOTICE, "roster_publish: no roster for %s",jid_user(user->jid));
219  return 0;
220  }
221 
222  log_debug(ZONE, "publishing roster for %s",jid_user(user->jid));
223  /* get published roster */
224  if(roster_publish->fetchfixed)
225  fetchkey = roster_publish->fetchfixed;
226  else if(roster_publish->fetchuser)
227  fetchkey = jid_user(user->jid);
228  else if(roster_publish->fetchdomain)
229  fetchkey = user->jid->domain;
230  else
231  fetchkey = "";
232 
233  if( storage_get(user->sm->st, (roster_publish->dbtable ? roster_publish->dbtable : "published-roster"), fetchkey, NULL, &os) == st_SUCCESS ) {
234  if(os_iter_first(os)) {
235  /* iterate on published roster */
236  jid = NULL;
237  do {
238  o = os_iter_object(os);
239  if(os_object_get_str(os, o, "jid", &str)) {
240 #ifndef NO_SM_CACHE
241  int userinsm;
242  _roster_publish_active_cache_t active_cached = 0;
243 #endif
244  log_debug(ZONE, "got %s item for inserting in", str);
245  if( strcmp(str,jid_user(user->jid)) == 0 ) {
246  /* not adding self */
247  continue; /* do { } while( os_iter_next ) */
248  }
249  /* check that published item exists in sm database */
250  checksm=0;
251  if( jid ) jid_free(jid);
252  jid = jid_new(str, -1);
253  if( roster_publish->removedomain ) {
254  if( strcmp("1", roster_publish->removedomain) == 0 || /* XXX HACKY!!! "1" is very config.c dependant */
255  strcmp(jid->domain, roster_publish->removedomain) == 0 ) {
256  checksm = 1;
257  }
258  }
259  if( checksm ) {
260  /* is this a hack? but i want to know was the user activated in sm or no? */
261 #ifndef NO_SM_CACHE
262  /* check for remembered active value in cache */
263  userinsm = -1;
264  if( roster_publish->active_cache_ttl ) {
265  if( roster_publish->active_cache ) {
266  active_cached = xhash_get(roster_publish->active_cache, jid_user(jid));
267  if( active_cached != NULL ) {
268  if( (time(NULL) - active_cached->time) >= roster_publish->active_cache_ttl ) {
269  xhash_zap(roster_publish->active_cache, jid_user(jid));
270  free(active_cached);
271  } else {
272  if( active_cached->active ) {
273  userinsm = 1;
274  } else {
275  userinsm = 0;
276  }
277  }
278  }
279  } else {
280  roster_publish->active_cache = xhash_new(401);
281  }
282  }
283  if( userinsm == -1 ) {
284  if( roster_publish->active_cache_ttl ) {
285  active_cached = calloc(1, sizeof(struct _roster_publish_active_cache_st));
286  active_cached->time = time(NULL);
287  }
288 #endif
289  if(storage_get(user->sm->st, "active", jid_user(jid), NULL, &os_active) == st_SUCCESS
290  && os_iter_first(os_active)) {
291 #ifndef NO_SM_CACHE
292  if( roster_publish->active_cache_ttl ) {
293  o_active = os_iter_object(os_active);
294  os_object_get_time(os_active, o_active, "time", &active_cached->active);
295  }
296 #endif
297  os_free(os_active);
298  userinsm = 1;
299  } else {
300 #ifndef NO_SM_CACHE
301  if( roster_publish->active_cache_ttl ) {
302  active_cached->active = 0;
303  }
304 #endif
305  userinsm = 0;
306  }
307 #ifndef NO_SM_CACHE
308  if( roster_publish->active_cache_ttl ) {
309  active_cached->jid_user = strdup(jid_user(jid));
310  xhash_put(roster_publish->active_cache, active_cached->jid_user, active_cached);
311  }
312  } // if( userinsm == -1 )
313 #endif
314  } else userinsm = 0; // if( checksm )
315  item = xhash_get(user->roster,jid_user(jid));
316  if( item == NULL ) {
317  /* user has no this jid in his roster */
318  /* if we checking sm database and user is not in it, not adding */
319  if( checksm && !userinsm ) {
320  log_debug(ZONE, "published user %s has no record in sm, not adding", jid_user(jid));
321  continue; /* do { } while( os_iter_next ) */
322  }
323  log_debug(ZONE, "user has no %s in roster, adding", jid_user(jid));
324  item = (item_t) calloc(1, sizeof(struct item_st));
325 
326  item->jid = jid_new(jid_user(jid), -1);
327  if(item->jid == NULL) {
328  log_debug(ZONE, "eek! invalid jid %s, skipping it", jid_user(jid));
329  log_write(user->sm->log, LOG_ERR, "roster_publish: eek! invalid jid %s, skipping it", jid_user(jid));
330  /* nvs: is it needed? */
331  free(item);
332  /* nvs: is it needed? */
333  } else {
334  os_object_get_str(os, o, "group", &str);
335  if( roster_publish->mappedgroups ) {
336  group = _roster_publish_get_group_name(user->sm, roster_publish, str); // don't forget to free group
337  } else {
338  if(str)
339  group = strdup(str);
340  else
341  group = NULL;
342  }
343  if( group ) {
344  item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
345  item->groups[item->ngroups] = group;
346  item->ngroups++;
347 
348  if(os_object_get_str(os, o, "name", &str))
349  item->name = strdup(str);
350 
351  os_object_get_bool(os, o, "to", &item->to);
352  os_object_get_bool(os, o, "from", &item->from);
353  os_object_get_int(os, o, "ask", &item->ask);
354 
355  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);
356 
357  /* its good */
358  xhash_put(user->roster, jid_full(item->jid), (void *) item);
359  _roster_publish_save_item(user,item);
360  } else {
361  log_write(user->sm->log, LOG_ERR, "roster_publish: unknown published group id '%s' for %s",str,jid_full(item->jid));
362  free(item);
363  }
364  if (roster_publish->fixexist &&
365  ( (checksm && !userinsm) ||
366  (!checksm && storage_get(user->sm->st, "active", jid_user(jid), NULL, &os_active) == st_SUCCESS && os_iter_first(os_active))
367  )
368  ) {
369  /* Add thise jid to active table*/
370  log_debug(ZONE, "adding published user %s to sm", jid_user(jid));
371  time_t tfe;
372  os_t osfe;
373 
374  os_object_t ofe;
375  tfe = time(NULL);
376  osfe = os_new();
377  ofe = os_object_new(osfe);
378  os_object_put_time(ofe, "time", &tfe);
379  storage_put(mi->sm->st, "active", jid_user(jid), osfe);
380  os_free(osfe);
381  }
382  }
383  }
384  else /* if( item == NULL ) else ... : here item != NULL : user has this jid in his roster */
385  {
386  /* if we checking sm database and user is not in it, remove it from roster */
387  if( checksm && !userinsm ) {
388  log_debug(ZONE, "published user %s has no record in sm, deleting from roster", jid_user(jid));
389  snprintf(filter, 4096, "(jid=%s)", jid_full(jid));
390  storage_delete(user->sm->st, "roster-items", jid_user(user->jid), filter);
391  snprintf(filter, 4096, "(jid=%s)", jid_full(jid));
392  storage_delete(user->sm->st, "roster-groups", jid_user(user->jid), filter);
393 
394  xhash_zap(user->roster, jid_full(jid));
395  _roster_publish_free_walker(NULL, (const char *) jid_full(jid), (void *) item, NULL);
396  continue; /* do { } while( os_iter_next ) */
397  }
398  if( roster_publish->fixsubs ) {
399  /* check subscriptions and correct if needed */
400  os_object_get_bool(os, o, "to", &tmp_to);
401  os_object_get_bool(os, o, "from", &tmp_from);
402  if( item->to != tmp_to || item->from != tmp_from ) {
403  item->to = tmp_to;
404  item->from = tmp_from;
405  log_debug(ZONE, "fixsubs in roster %s, item %s",jid_user(user->jid),jid_user(item->jid));
406  xhash_put(user->roster, jid_full(item->jid), (void *) item);
407  _roster_publish_save_item(user,item);
408  }
409  }
410  if( roster_publish->overridenames ) {
411  /* override display name if it differs */
412  if(os_object_get_str(os, o, "name", &str)) {
413  if( str ) {
414  tmp_do_change = 0;
415  if( ! item->name ) {
416  tmp_do_change = 1;
417  } else {
418  if( strcmp(item->name,str) != 0 ) {
419  tmp_do_change = 1;
420  }
421  }
422  if( tmp_do_change ) {
423  log_debug(ZONE, "replacing name for %s in roster of %s", jid_full(item->jid),jid_user(user->jid));
424  item->name = strdup(str);
425  xhash_put(user->roster, jid_full(item->jid), (void *) item);
426  _roster_publish_save_item(user,item);
427  }
428  } else {
429  log_debug(ZONE,"warning: name is null in published roster for item %s",jid_full(item->jid));
430  }
431  }
432  }
433  if( roster_publish->forcegroups ) {
434  /* item already in roster, check groups if needed */
435  os_object_get_str(os, o, "group", &str);
436  if( roster_publish->mappedgroups ) {
437  group = _roster_publish_get_group_name(user->sm, roster_publish, str); // don't forget to free group
438  if( !group ) {
439  log_write(user->sm->log, LOG_ERR, "roster_publish: unknown published group id '%s' for %s",str, jid_full(item->jid));
440  continue; /* do { } while( os_iter_next ) */
441  }
442  } else {
443  group = strdup(str);
444  }
445  /* find published roster item's group in user's roster */
446  found = 0;
447  for(i = 0; i < item->ngroups; i++) {
448  if( strcmp(item->groups[i],group) == 0 ) {
449  found = 1;
450  /* do not break loop, give groups that matches
451  * prefix and suffix to be deleted
452  */
453  } else {
454  /* check if user's roster group matches
455  * prefix or suffix given in config
456  * and delete such groups (and thus they will be replaced)
457  */
458  delete = 0;
459  if( roster_publish->groupprefix ) {
460  if( strncmp(item->groups[i],roster_publish->groupprefix,roster_publish->groupprefixlen) == 0 ) {
461  delete = 1;
462  }
463  }
464  if( !delete && roster_publish->groupsuffix ) {
465  gpos=strlen(item->groups[i])-roster_publish->groupsuffixlen;
466  if( gpos > 0 ) {
467  if( strcmp(item->groups[i]+gpos,roster_publish->groupsuffix) == 0 ) {
468  delete = 1;
469  }
470  }
471  }
472  /* remove group from roster item */
473  if( delete ) {
474  free(item->groups[i]);
475  for(j = i; j < item->ngroups-1; j++) {
476  item->groups[j]=item->groups[j+1];
477  }
478  item->ngroups--;
479  item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups));
480  }
481  }
482  } /* for(i... */
483  if( !found ) {
484  log_debug(ZONE, "adding group %s to item %s for user %s",group,jid_user(item->jid),jid_user(user->jid));
485  item->groups = realloc(item->groups, sizeof(char *) * (item->ngroups + 1));
486  item->groups[item->ngroups] = group; // will be freed
487  item->ngroups++;
488  /* replace item */
489  xhash_put(user->roster, jid_full(item->jid), (void *) item);
490  _roster_publish_save_item(user,item);
491  } else {
492  free(group);
493  }
494  } /* else if( roster_publish->forcegroups ) */
495  } /* end of if if( item == NULL ) */
496  } /* if( os_object_get(...) */
497  } while(os_iter_next(os));
498  if( jid ) jid_free(jid);
499  }
500  os_free(os);
501  }
502  }
503  return 0;
504 }
505 
506 static void _roster_publish_free(module_t mod) {
507  roster_publish_t roster_publish = (roster_publish_t) mod->private;
508 
509 #ifndef NO_SM_CACHE
510  if( roster_publish->active_cache ) {
512  xhash_free(roster_publish->active_cache);
513  }
514  if( roster_publish->group_cache ) {
516  xhash_free(roster_publish->group_cache);
517  }
518 #endif
519  free(roster_publish);
520 }
521 
523  module_t mod = mi->mod;
524  roster_publish_t roster_publish;
525 
526  if(mod->init) return 0;
527 
528  roster_publish = (roster_publish_t) calloc(1, sizeof(struct _roster_publish_st));
529 
530  if( config_get_one(mod->mm->sm->config, "user.template.publish", 0) ) {
531  roster_publish->publish = 1;
532  roster_publish->fetchdomain = config_get_one(mod->mm->sm->config, "user.template.publish.fetch-key.domain", 0);
533  roster_publish->fetchuser = config_get_one(mod->mm->sm->config, "user.template.publish.fetch-key.user", 0);
534  roster_publish->fetchfixed = config_get_one(mod->mm->sm->config, "user.template.publish.fetch-key.fixed", 0);
535  roster_publish->dbtable = config_get_one(mod->mm->sm->config, "user.template.publish.db-table", 0);
536  roster_publish->removedomain = config_get_one(mod->mm->sm->config, "user.template.publish.check-remove-domain", 0);
537  roster_publish->fixsubs = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.fix-subscriptions", 0), 0);
538  roster_publish->overridenames = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.override-names", 0), 0);
539  roster_publish->mappedgroups = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.mapped-groups.map-groups", 0), 0);
540  roster_publish->fixexist = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.force-create-contacts", 0), 0);
541 #ifndef NO_SM_CACHE
542  roster_publish->active_cache_ttl = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.active-cache-ttl", 0), 0);
543  roster_publish->group_cache_ttl = j_atoi(config_get_one(mod->mm->sm->config, "user.template.publish.mapped-groups.group-cache-ttl", 0), 0);
544 #endif
545  if( config_get_one(mod->mm->sm->config, "user.template.publish.force-groups", 0) ) {
546  roster_publish->forcegroups = 1;
547  roster_publish->groupprefix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.prefix", 0);
548  if( roster_publish->groupprefix ) {
549  roster_publish->groupprefixlen = strlen(roster_publish->groupprefix);
550  }
551  roster_publish->groupsuffix = config_get_one(mod->mm->sm->config, "user.template.publish.force-groups.suffix", 0);
552  if( roster_publish->groupsuffix ) {
553  roster_publish->groupsuffixlen = strlen(roster_publish->groupsuffix);
554  }
555  } else {
556  roster_publish->forcegroups = 0;
557  }
558  } else {
559  roster_publish->publish = 0;
560  }
561  mod->private = roster_publish;
562 
564  mod->free = _roster_publish_free;
565 
566  return 0;
567 }
568 
569 // vim: shiftwidth=4