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 #include <time.h>
23 
24 /** @file sm/mod_announce.c
25   * @brief announce (broadcast) messages
26   * @author Robert Norris
27   * $Date: 2005/08/17 07:48:28 $
28   * $Revision: 1.23 $
29   */
30 
31 /*
32  * message to host/announce goes to all online sessions and to offline users next time they connect
33  * message to host/announce/online goes to all online sessions
34  */
35 
36 typedef struct moddata_st {
37     nad_t       nad;
38     int         loaded;
39     time_t      t;
40     os_t        tos;
41     int         index;
42     char        *announce_resource;
43     char        *online_resource;
44 } *moddata_t;
45 
_announce_load(module_t mod,moddata_t data,const char * domain)46 static void _announce_load(module_t mod, moddata_t data, const char *domain) {
47     st_ret_t ret;
48     os_t os;
49     os_object_t o;
50     nad_t nad;
51     int ns, elem, attr;
52     char timestamp[18], telem[5];
53     struct tm tm;
54 
55     /* struct tm can vary in size depending on platform */
56     memset(&tm, 0, sizeof(struct tm));
57 
58     data->loaded = 1;
59 
60     /* load the current message */
61     if((ret = storage_get(mod->mm->sm->st, "motd-message", domain, NULL, &os)) == st_SUCCESS) {
62         if(os_iter_first(os)) {
63             o = os_iter_object(os);
64             if(os_object_get_nad(os, o, "xml", &nad)) {
65                 /* Copy the nad, as the original is freed when the os is freed below */
66                 data->nad = nad_copy(nad);
67                 if((ns = nad_find_scoped_namespace(data->nad, uri_DELAY, NULL)) >= 0 &&
68                         (elem = nad_find_elem(data->nad, 1, ns, "x", 1)) >= 0 &&
69                         (attr = nad_find_attr(data->nad, elem, -1, "stamp", NULL)) >= 0) {
70                     snprintf(timestamp, 18, "%.*s", NAD_AVAL_L(data->nad, attr), NAD_AVAL(data->nad, attr));
71 
72                     /* year */
73                     telem[0] = timestamp[0];
74                     telem[1] = timestamp[1];
75                     telem[2] = timestamp[2];
76                     telem[3] = timestamp[3];
77                     telem[4] = '\0';
78                     tm.tm_year = atoi(telem) - 1900;
79 
80                     /* month */
81                     telem[0] = timestamp[4];
82                     telem[1] = timestamp[5];
83                     telem[2] = '\0';
84                     tm.tm_mon = atoi(telem) - 1;
85 
86                     /* day */
87                     telem[0] = timestamp[6];
88                     telem[1] = timestamp[7];
89                     tm.tm_mday = atoi(telem);
90 
91                     /* hour */
92                     telem[0] = timestamp[9];
93                     telem[1] = timestamp[10];
94                     tm.tm_hour = atoi(telem);
95 
96                     /* minute */
97                     telem[0] = timestamp[12];
98                     telem[1] = timestamp[13];
99                     tm.tm_min = atoi(telem);
100 
101                     /* second */
102                     telem[0] = timestamp[15];
103                     telem[1] = timestamp[16];
104                     tm.tm_sec = atoi(telem);
105 
106                     data->t = timegm(&tm);
107                 }
108             }
109         }
110 
111         os_free(os);
112     }
113 
114     if(data->tos != NULL)
115         os_free(data->tos);
116     data->tos = os_new();
117     os_object_put(os_object_new(data->tos), "time", &data->t, os_type_INTEGER);
118 }
119 
_announce_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)120 static mod_ret_t _announce_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
121     module_t mod = mi->mod;
122     moddata_t data = (moddata_t) mod->private;
123     time_t t;
124     nad_t nad;
125     pkt_t motd;
126     os_t os;
127     os_object_t o;
128 
129     /* try to load data if we haven't yet */
130     if(data->nad == NULL) {
131         if(data->loaded)
132             return mod_PASS;        /* nothing to give them */
133         _announce_load(mod, data, sess->user->jid->domain);
134         if(data->nad == NULL)
135             return mod_PASS;
136     }
137 
138     /* if they're becoming available for the first time */
139     if(pkt->type == pkt_PRESENCE && pkt->to == NULL && sess->user->top == NULL) {
140         /* load the time of the last motd they got */
141         if((time_t) sess->user->module_data[mod->index] == 0 &&
142            storage_get(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, &os) == st_SUCCESS) {
143             if(os_iter_first(os)) {
144                 o = os_iter_object(os);
145                 os_object_get_time(os, o, "time", &t);
146                 sess->user->module_data[mod->index] = (void *) t;
147             }
148             os_free(os);
149         }
150 
151         /* they've seen this one */
152         if((time_t) sess->user->module_data[mod->index] >= data->t)
153             return mod_PASS;
154 
155         /* a-delivering we go */
156         log_debug(ZONE, "delivering stored motd to %s", jid_full(sess->jid));
157 
158         nad = nad_copy(data->nad);
159         nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
160         nad_set_attr(nad, 1, -1, "from", sess->user->jid->domain, strlen(sess->user->jid->domain));
161 
162         motd = pkt_new(mod->mm->sm, nad); // pkt_new takes ownership of given nad
163         if(motd == NULL) {
164             log_debug(ZONE, "invalid stored motd, not delivering");
165         } else
166             pkt_router(motd);
167 
168         sess->user->module_data[mod->index] = (void *) data->t;
169         storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
170     }
171 
172     return mod_PASS;
173 }
174 
_announce_broadcast_user(const char * key,int keylen,void * val,void * arg)175 static void _announce_broadcast_user(const char *key, int keylen, void *val, void *arg) {
176     user_t user = (user_t) val;
177     moddata_t data = (moddata_t) arg;
178     sess_t sess;
179     nad_t nad;
180 
181     for(sess = user->sessions; sess != NULL; sess = sess->next) {
182         if(!sess->available || sess->pri < 0)
183             continue;
184 
185         log_debug(ZONE, "resending to '%s'", jid_full(sess->jid));
186 
187         nad = nad_copy(data->nad);
188         nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
189         nad_set_attr(nad, 1, -1, "from", sess->jid->domain, strlen(sess->jid->domain));
190 
191         pkt_router(pkt_new(user->sm, nad));
192 
193         sess->user->module_data[data->index] = (void *) data->t;
194         storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
195     }
196 }
197 
_announce_pkt_sm(mod_instance_t mi,pkt_t pkt)198 static mod_ret_t _announce_pkt_sm(mod_instance_t mi, pkt_t pkt) {
199     module_t mod = mi->mod;
200     moddata_t data = (moddata_t) mod->private;
201     pkt_t store;
202     nad_t nad;
203     jid_t jid;
204     time_t t;
205     os_t os;
206     os_object_t o;
207     st_ret_t ret;
208     int elem;
209 
210     /* time of this packet */
211     t = time(NULL);
212 
213     /* answer to probes and subscription requests if admin */
214     if((pkt->type == pkt_PRESENCE_PROBE || pkt->type == pkt_S10N) && aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
215         log_debug(ZONE, "answering presence probe/sub from %s with /announce resources", jid_full(pkt->from));
216 
217         /* send presences */
218         jid = jid_new(pkt->from->domain, -1);
219         jid_reset_components(jid, jid->node, jid->domain, data->announce_resource);
220         pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
221         jid_free(jid);
222 
223         jid = jid_new(pkt->from->domain, -1);
224         jid_reset_components(jid, jid->node, jid->domain, data->online_resource);
225         pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
226         jid_free(jid);
227     }
228 
229     /* we want messages addressed to /announce */
230     if(!(pkt->type & pkt_MESSAGE) || strlen(pkt->to->resource) < 8 || strncmp(pkt->to->resource, data->announce_resource, 8) != 0)
231         return mod_PASS;
232 
233     /* make sure they're allowed */
234     if(!aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
235         log_debug(ZONE, "not allowing broadcast from %s", jid_full(pkt->from));
236         return -stanza_err_FORBIDDEN;
237     }
238 
239     /* "fix" packet a bit */
240     /* force type normal */
241     nad_set_attr(pkt->nad, 1, -1, "type", NULL, 0);
242     /* remove sender nick */
243     elem = nad_find_elem(pkt->nad, 1, -1, "nick", 1);
244     if(elem >= 0) nad_drop_elem(pkt->nad, elem);
245 
246     if(pkt->to->resource[8] == '\0') {
247         log_debug(ZONE, "storing message for announce later");
248 
249         store = pkt_dup(pkt, NULL, NULL);
250 
251         pkt_delay(store, t, pkt->to->domain);
252 
253         /* prepare for storage */
254         os = os_new();
255         o = os_object_new(os);
256 
257         os_object_put(o, "xml", store->nad, os_type_NAD);
258 
259         /* store it */
260         ret = storage_replace(mod->mm->sm->st, "motd-message", pkt->to->domain, NULL, os);
261         os_free(os);
262 
263         switch(ret) {
264             case st_FAILED:
265                 pkt_free(store);
266                 return -stanza_err_INTERNAL_SERVER_ERROR;
267 
268             case st_NOTIMPL:
269                 pkt_free(store);
270                 return -stanza_err_FEATURE_NOT_IMPLEMENTED;
271 
272             default:
273                 break;
274         }
275 
276         /* replace our local copy */
277         if(data->nad != NULL)
278             nad_free(data->nad);
279         data->nad = store->nad;
280 
281         store->nad = NULL;
282         pkt_free(store);
283 
284         /* update timestamp */
285         data->t = t;
286         if(data->tos != NULL)
287             os_free(data->tos);
288         data->tos = os_new();
289         os_object_put(os_object_new(data->tos), "time", &t, os_type_INTEGER);
290     }
291 
292     else if(strcmp(&(pkt->to->resource[8]), "/online") != 0) {
293         log_debug(ZONE, "unknown announce resource '%s'", pkt->to->resource);
294         pkt_free(pkt);
295         return mod_HANDLED;
296     }
297 
298     log_debug(ZONE, "broadcasting message to all sessions");
299 
300     /* hack */
301     nad = data->nad;
302     data->nad = pkt->nad;
303     xhash_walk(mod->mm->sm->users, _announce_broadcast_user, (void *) data);
304     data->nad = nad;
305 
306     /* done */
307     pkt_free(pkt);
308 
309     return mod_HANDLED;
310 }
311 
_announce_user_delete(mod_instance_t mi,jid_t jid)312 static void _announce_user_delete(mod_instance_t mi, jid_t jid) {
313     log_debug(ZONE, "deleting motd time for %s", jid_user(jid));
314 
315     storage_delete(mi->sm->st, "motd-times", jid_user(jid), NULL);
316 }
317 
_announce_free(module_t mod)318 static void _announce_free(module_t mod) {
319     moddata_t data = (moddata_t) mod->private;
320 
321     if(data->nad != NULL) nad_free(data->nad);
322     if(data->tos != NULL) os_free(data->tos);
323     free(data);
324 }
325 
module_init(mod_instance_t mi,const char * arg)326 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
327     module_t mod = mi->mod;
328     moddata_t data;
329 
330     if(mod->init) return 0;
331 
332     data = (moddata_t) calloc(1, sizeof(struct moddata_st));
333 
334     mod->private = (void *) data;
335 
336     data->index = mod->index;
337 
338     data->announce_resource = "announce";
339     data->online_resource = "announce/online";
340 
341     mod->in_sess = _announce_in_sess;
342     mod->pkt_sm = _announce_pkt_sm;
343     mod->user_delete = _announce_user_delete;
344     mod->free = _announce_free;
345 
346     return 0;
347 }
348