jabberd2  2.2.16
sm/mod_roster_publish.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 
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