jabberd2  2.2.17
mod_announce.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 #include <time.h>
23 
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 {
38  int loaded;
39  time_t t;
40  os_t tos;
41  int index;
44 } *moddata_t;
45 
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  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  os_free(os);
111  }
112 
113  if(data->tos != NULL)
114  os_free(data->tos);
115  data->tos = os_new();
116  os_object_put(os_object_new(data->tos), "time", &data->t, os_type_INTEGER);
117 }
118 
120  module_t mod = mi->mod;
121  moddata_t data = (moddata_t) mod->private;
122  time_t t;
123  nad_t nad;
124  pkt_t motd;
125  os_t os;
126  os_object_t o;
127 
128  /* try to load data if we haven't yet */
129  if(data->nad == NULL) {
130  if(data->loaded)
131  return mod_PASS; /* nothing to give them */
132  _announce_load(mod, data, sess->user->jid->domain);
133  if(data->nad == NULL)
134  return mod_PASS;
135  }
136 
137  /* if they're becoming available for the first time */
138  if(pkt->type == pkt_PRESENCE && pkt->to == NULL && sess->user->top == NULL) {
139  /* load the time of the last motd they got */
140  if((time_t) sess->user->module_data[mod->index] == 0 &&
141  storage_get(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, &os) == st_SUCCESS) {
142  os_iter_first(os);
143  o = os_iter_object(os);
144  os_object_get_time(os, o, "time", &t);
145  sess->user->module_data[mod->index] = (void *) t;
146  os_free(os);
147  }
148 
149  /* they've seen this one */
150  if((time_t) sess->user->module_data[mod->index] >= data->t)
151  return mod_PASS;
152 
153  /* a-delivering we go */
154  log_debug(ZONE, "delivering stored motd to %s", jid_full(sess->jid));
155 
156  nad = nad_copy(data->nad);
157  nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
158  nad_set_attr(nad, 1, -1, "from", sess->user->jid->domain, strlen(sess->user->jid->domain));
159 
160  motd = pkt_new(mod->mm->sm, nad);
161  if(motd == NULL) {
162  log_debug(ZONE, "invalid stored motd, not delivering");
163  nad_free(nad);
164  } else
165  pkt_router(motd);
166 
167  sess->user->module_data[mod->index] = (void *) data->t;
168  storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
169  }
170 
171  return mod_PASS;
172 }
173 
174 static void _announce_broadcast_user(const char *key, int keylen, void *val, void *arg) {
175  user_t user = (user_t) val;
176  moddata_t data = (moddata_t) arg;
177  sess_t sess;
178  nad_t nad;
179 
180  for(sess = user->sessions; sess != NULL; sess = sess->next) {
181  if(!sess->available || sess->pri < 0)
182  continue;
183 
184  log_debug(ZONE, "resending to '%s'", jid_full(sess->jid));
185 
186  nad = nad_copy(data->nad);
187  nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
188  nad_set_attr(nad, 1, -1, "from", sess->jid->domain, strlen(sess->jid->domain));
189 
190  pkt_router(pkt_new(user->sm, nad));
191 
192  sess->user->module_data[data->index] = (void *) data->t;
193  storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
194  }
195 }
196 
198  module_t mod = mi->mod;
199  moddata_t data = (moddata_t) mod->private;
200  pkt_t store;
201  nad_t nad;
202  jid_t jid;
203  time_t t;
204  os_t os;
205  os_object_t o;
206  st_ret_t ret;
207  int elem;
208 
209  /* time of this packet */
210  t = time(NULL);
211 
212  /* answer to probes and subscription requests if admin */
213  if((pkt->type == pkt_PRESENCE_PROBE || pkt->type == pkt_S10N) && aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
214  log_debug(ZONE, "answering presence probe/sub from %s with /announce resources", jid_full(pkt->from));
215 
216  /* send presences */
217  jid = jid_new(pkt->from->domain, -1);
218  jid_reset_components(jid, jid->node, jid->domain, data->announce_resource);
219  pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
220  jid_free(jid);
221 
222  jid = jid_new(pkt->from->domain, -1);
223  jid_reset_components(jid, jid->node, jid->domain, data->online_resource);
224  pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid)));
225  jid_free(jid);
226  }
227 
228  /* we want messages addressed to /announce */
229  if(!(pkt->type & pkt_MESSAGE) || strlen(pkt->to->resource) < 8 || strncmp(pkt->to->resource, data->announce_resource, 8) != 0)
230  return mod_PASS;
231 
232  /* make sure they're allowed */
233  if(!aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
234  log_debug(ZONE, "not allowing broadcast from %s", jid_full(pkt->from));
235  return -stanza_err_FORBIDDEN;
236  }
237 
238  /* "fix" packet a bit */
239  /* force type normal */
240  nad_set_attr(pkt->nad, 1, -1, "type", NULL, 0);
241  /* remove sender nick */
242  elem = nad_find_elem(pkt->nad, 1, -1, "nick", 1);
243  if(elem >= 0) nad_drop_elem(pkt->nad, elem);
244 
245  if(pkt->to->resource[8] == '\0') {
246  log_debug(ZONE, "storing message for announce later");
247 
248  store = pkt_dup(pkt, NULL, NULL);
249 
250  pkt_delay(store, t, pkt->to->domain);
251 
252  /* prepare for storage */
253  os = os_new();
254  o = os_object_new(os);
255 
256  os_object_put(o, "xml", store->nad, os_type_NAD);
257 
258  /* store it */
259  ret = storage_replace(mod->mm->sm->st, "motd-message", pkt->to->domain, NULL, os);
260  os_free(os);
261 
262  switch(ret) {
263  case st_FAILED:
264  pkt_free(store);
266 
267  case st_NOTIMPL:
268  pkt_free(store);
270 
271  default:
272  break;
273  }
274 
275  /* replace our local copy */
276  if(data->nad != NULL)
277  nad_free(data->nad);
278  data->nad = store->nad;
279 
280  store->nad = NULL;
281  pkt_free(store);
282 
283  /* update timestamp */
284  data->t = t;
285  if(data->tos != NULL)
286  os_free(data->tos);
287  data->tos = os_new();
288  os_object_put(os_object_new(data->tos), "time", &t, os_type_INTEGER);
289  }
290 
291  else if(strcmp(&(pkt->to->resource[8]), "/online") != 0) {
292  log_debug(ZONE, "unknown announce resource '%s'", pkt->to->resource);
293  pkt_free(pkt);
294  return mod_HANDLED;
295  }
296 
297  log_debug(ZONE, "broadcasting message to all sessions");
298 
299  /* hack */
300  nad = data->nad;
301  data->nad = pkt->nad;
302  xhash_walk(mod->mm->sm->users, _announce_broadcast_user, (void *) data);
303  data->nad = nad;
304 
305  /* done */
306  pkt_free(pkt);
307 
308  return mod_HANDLED;
309 }
310 
312  log_debug(ZONE, "deleting motd time for %s", jid_user(jid));
313 
314  storage_delete(mi->sm->st, "motd-times", jid_user(jid), NULL);
315 }
316 
317 static void _announce_free(module_t mod) {
318  moddata_t data = (moddata_t) mod->private;
319 
320  if(data->nad != NULL) nad_free(data->nad);
321  if(data->tos != NULL) os_free(data->tos);
322  free(data);
323 }
324 
326  module_t mod = mi->mod;
327  moddata_t data;
328 
329  if(mod->init) return 0;
330 
331  data = (moddata_t) calloc(1, sizeof(struct moddata_st));
332 
333  mod->private = (void *) data;
334 
335  data->index = mod->index;
336 
337  data->announce_resource = "announce";
338  data->online_resource = "announce/online";
339 
340  mod->in_sess = _announce_in_sess;
341  mod->pkt_sm = _announce_pkt_sm;
343  mod->free = _announce_free;
344 
345  return 0;
346 }