jabberd2  2.2.16
sm/mod_announce.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 #include <time.h>
00023 
00031 /*
00032  * message to host/announce goes to all online sessions and to offline users next time they connect
00033  * message to host/announce/online goes to all online sessions
00034  */
00035 
00036 typedef struct moddata_st {
00037     nad_t       nad;
00038     int         loaded;
00039     time_t      t;
00040     os_t        tos;
00041     int         index;
00042     char        *announce_resource;
00043     char        *online_resource;
00044 } *moddata_t;
00045 
00046 static void _announce_load(module_t mod, moddata_t data, const char *domain) {
00047     st_ret_t ret;
00048     os_t os;
00049     os_object_t o;
00050     nad_t nad;
00051     int ns, elem, attr;
00052     char timestamp[18], telem[5];
00053     struct tm tm;
00054 
00055     /* struct tm can vary in size depending on platform */
00056     memset(&tm, 0, sizeof(struct tm));
00057 
00058     data->loaded = 1;
00059 
00060     /* load the current message */
00061     if((ret = storage_get(mod->mm->sm->st, "motd-message", domain, NULL, &os)) == st_SUCCESS) {
00062         os_iter_first(os);
00063         o = os_iter_object(os);
00064         if(os_object_get_nad(os, o, "xml", &nad)) {
00065             /* Copy the nad, as the original is freed when the os is freed below */
00066             data->nad = nad_copy(nad);
00067             if((ns = nad_find_scoped_namespace(data->nad, uri_DELAY, NULL)) >= 0 &&
00068                (elem = nad_find_elem(data->nad, 1, ns, "x", 1)) >= 0 &&
00069                (attr = nad_find_attr(data->nad, elem, -1, "stamp", NULL)) >= 0) {
00070                 snprintf(timestamp, 18, "%.*s", NAD_AVAL_L(data->nad, attr), NAD_AVAL(data->nad, attr));
00071 
00072                 /* year */
00073                 telem[0] = timestamp[0];
00074                 telem[1] = timestamp[1];
00075                 telem[2] = timestamp[2];
00076                 telem[3] = timestamp[3];
00077                 telem[4] = '\0';
00078                 tm.tm_year = atoi(telem) - 1900;
00079 
00080                 /* month */
00081                 telem[0] = timestamp[4];
00082                 telem[1] = timestamp[5];
00083                 telem[2] = '\0';
00084                 tm.tm_mon = atoi(telem) - 1;
00085 
00086                 /* day */
00087                 telem[0] = timestamp[6];
00088                 telem[1] = timestamp[7];
00089                 tm.tm_mday = atoi(telem);
00090 
00091                 /* hour */
00092                 telem[0] = timestamp[9];
00093                 telem[1] = timestamp[10];
00094                 tm.tm_hour = atoi(telem);
00095 
00096                 /* minute */
00097                 telem[0] = timestamp[12];
00098                 telem[1] = timestamp[13];
00099                 tm.tm_min = atoi(telem);
00100 
00101                 /* second */
00102                 telem[0] = timestamp[15];
00103                 telem[1] = timestamp[16];
00104                 tm.tm_sec = atoi(telem);
00105             
00106                 data->t = timegm(&tm);
00107             }
00108         }
00109 
00110         os_free(os);
00111     }
00112 
00113     if(data->tos != NULL)
00114         os_free(data->tos);
00115     data->tos = os_new();
00116     os_object_put(os_object_new(data->tos), "time", &data->t, os_type_INTEGER);
00117 }
00118 
00119 static mod_ret_t _announce_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
00120     module_t mod = mi->mod;
00121     moddata_t data = (moddata_t) mod->private;
00122     time_t t;
00123     nad_t nad;
00124     pkt_t motd;
00125     os_t os;
00126     os_object_t o;
00127 
00128     /* try to load data if we haven't yet */
00129     if(data->nad == NULL) {
00130         if(data->loaded)
00131             return mod_PASS;        /* nothing to give them */
00132         _announce_load(mod, data, sess->user->jid->domain);
00133         if(data->nad == NULL)
00134             return mod_PASS;
00135     }
00136 
00137     /* if they're becoming available for the first time */
00138     if(pkt->type == pkt_PRESENCE && pkt->to == NULL && sess->user->top == NULL) {
00139         /* load the time of the last motd they got */
00140         if((time_t) sess->user->module_data[mod->index] == 0 &&
00141            storage_get(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, &os) == st_SUCCESS) {
00142             os_iter_first(os);
00143             o = os_iter_object(os);
00144             os_object_get_time(os, o, "time", &t);
00145             sess->user->module_data[mod->index] = (void *) t;
00146             os_free(os);
00147         }
00148 
00149         /* they've seen this one */
00150         if((time_t) sess->user->module_data[mod->index] >= data->t)
00151             return mod_PASS;
00152 
00153         /* a-delivering we go */
00154         log_debug(ZONE, "delivering stored motd to %s", jid_full(sess->jid));
00155 
00156         nad = nad_copy(data->nad);
00157         nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
00158         nad_set_attr(nad, 1, -1, "from", sess->user->jid->domain, strlen(sess->user->jid->domain));
00159 
00160         motd = pkt_new(mod->mm->sm, nad);
00161         if(motd == NULL) {
00162             log_debug(ZONE, "invalid stored motd, not delivering");
00163             nad_free(nad);
00164         } else
00165             pkt_router(motd);
00166 
00167         sess->user->module_data[mod->index] = (void *) data->t;
00168         storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
00169     }
00170 
00171     return mod_PASS;
00172 }
00173 
00174 static void _announce_broadcast_user(const char *key, int keylen, void *val, void *arg) {
00175     user_t user = (user_t) val;
00176     moddata_t data = (moddata_t) arg;
00177     sess_t sess;
00178     nad_t nad;
00179 
00180     for(sess = user->sessions; sess != NULL; sess = sess->next) {
00181         if(!sess->available || sess->pri < 0)
00182             continue;
00183 
00184         log_debug(ZONE, "resending to '%s'", jid_full(sess->jid));
00185 
00186         nad = nad_copy(data->nad);
00187         nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
00188         nad_set_attr(nad, 1, -1, "from", sess->jid->domain, strlen(sess->jid->domain));
00189 
00190         pkt_router(pkt_new(user->sm, nad));
00191 
00192         sess->user->module_data[data->index] = (void *) data->t;
00193         storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
00194     }
00195 }
00196 
00197 static mod_ret_t _announce_pkt_sm(mod_instance_t mi, pkt_t pkt) {
00198     module_t mod = mi->mod;
00199     moddata_t data = (moddata_t) mod->private;
00200     pkt_t store;
00201     nad_t nad;
00202     jid_t jid;
00203     time_t t;
00204     os_t os;
00205     os_object_t o;
00206     st_ret_t ret;
00207     int elem;
00208 
00209     /* time of this packet */
00210     t = time(NULL);
00211 
00212     /* answer to probes and subscription requests if admin */
00213     if((pkt->type == pkt_PRESENCE_PROBE || pkt->type == pkt_S10N) && aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
00214         log_debug(ZONE, "answering presence probe/sub from %s with /announce resources", jid_full(pkt->from));
00215 
00216         /* send presences */
00217         jid = jid_new(pkt->from->domain, -1);
00218         jid_reset_components(jid, jid->node, jid->domain, data->announce_resource);
00219         pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
00220         jid_free(jid);
00221 
00222         jid = jid_new(pkt->from->domain, -1);
00223         jid_reset_components(jid, jid->node, jid->domain, data->online_resource);
00224         pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
00225         jid_free(jid);
00226     }
00227 
00228     /* we want messages addressed to /announce */
00229     if(!(pkt->type & pkt_MESSAGE) || strlen(pkt->to->resource) < 8 || strncmp(pkt->to->resource, data->announce_resource, 8) != 0)
00230         return mod_PASS;
00231     
00232     /* make sure they're allowed */
00233     if(!aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
00234         log_debug(ZONE, "not allowing broadcast from %s", jid_full(pkt->from));
00235         return -stanza_err_FORBIDDEN;
00236     }
00237 
00238     /* "fix" packet a bit */
00239     /* force type normal */
00240     nad_set_attr(pkt->nad, 1, -1, "type", NULL, 0);
00241     /* remove sender nick */
00242     elem = nad_find_elem(pkt->nad, 1, -1, "nick", 1);
00243     if(elem >= 0) nad_drop_elem(pkt->nad, elem);
00244 
00245     if(pkt->to->resource[8] == '\0') {
00246         log_debug(ZONE, "storing message for announce later");
00247 
00248         store = pkt_dup(pkt, NULL, NULL);
00249 
00250         pkt_delay(store, t, pkt->to->domain);
00251 
00252         /* prepare for storage */
00253         os = os_new();
00254         o = os_object_new(os);
00255 
00256         os_object_put(o, "xml", store->nad, os_type_NAD);
00257 
00258         /* store it */
00259         ret = storage_replace(mod->mm->sm->st, "motd-message", pkt->to->domain, NULL, os);
00260         os_free(os);
00261 
00262         switch(ret) {
00263             case st_FAILED:
00264                 pkt_free(store);
00265                 return -stanza_err_INTERNAL_SERVER_ERROR;
00266 
00267             case st_NOTIMPL:
00268                 pkt_free(store);
00269                 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
00270 
00271             default:
00272                 break;
00273         }
00274 
00275         /* replace our local copy */
00276         if(data->nad != NULL)
00277             nad_free(data->nad);
00278         data->nad = store->nad;
00279 
00280         store->nad = NULL;
00281         pkt_free(store);
00282 
00283         /* update timestamp */
00284         data->t = t;
00285         if(data->tos != NULL)
00286             os_free(data->tos);
00287         data->tos = os_new();
00288         os_object_put(os_object_new(data->tos), "time", &t, os_type_INTEGER);
00289     }
00290 
00291     else if(strcmp(&(pkt->to->resource[8]), "/online") != 0) {
00292         log_debug(ZONE, "unknown announce resource '%s'", pkt->to->resource);
00293         pkt_free(pkt);
00294         return mod_HANDLED;
00295     }
00296 
00297     log_debug(ZONE, "broadcasting message to all sessions");
00298 
00299     /* hack */
00300     nad = data->nad;
00301     data->nad = pkt->nad;
00302     xhash_walk(mod->mm->sm->users, _announce_broadcast_user, (void *) data);
00303     data->nad = nad;
00304 
00305     /* done */
00306     pkt_free(pkt);
00307 
00308     return mod_HANDLED;
00309 }
00310 
00311 static void _announce_user_delete(mod_instance_t mi, jid_t jid) {
00312     log_debug(ZONE, "deleting motd time for %s", jid_user(jid));
00313 
00314     storage_delete(mi->sm->st, "motd-times", jid_user(jid), NULL);
00315 }
00316 
00317 static void _announce_free(module_t mod) {
00318     moddata_t data = (moddata_t) mod->private;
00319 
00320     if(data->nad != NULL) nad_free(data->nad);
00321     if(data->tos != NULL) os_free(data->tos);
00322     free(data);
00323 }
00324 
00325 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
00326     module_t mod = mi->mod;
00327     moddata_t data;
00328 
00329     if(mod->init) return 0;
00330 
00331     data = (moddata_t) calloc(1, sizeof(struct moddata_st));
00332 
00333     mod->private = (void *) data;
00334 
00335     data->index = mod->index;
00336 
00337     data->announce_resource = "announce";
00338     data->online_resource = "announce/online";
00339 
00340     mod->in_sess = _announce_in_sess;
00341     mod->pkt_sm = _announce_pkt_sm;
00342     mod->user_delete = _announce_user_delete;
00343     mod->free = _announce_free;
00344 
00345     return 0;
00346 }