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