jabberd2  2.7.0
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  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 
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 
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 
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);
267 
268  case st_NOTIMPL:
269  pkt_free(store);
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 
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 
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 
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;
344  mod->free = _announce_free;
345 
346  return 0;
347 }
user_t user
user this session belongs to
Definition: sm.h:256
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:258
Definition: nad.h:93
data structures and prototypes for the session manager
static mod_ret_t _announce_pkt_sm(mod_instance_t mi, pkt_t pkt)
Definition: mod_announce.c:198
subscribe request
Definition: sm.h:102
DLLEXPORT int module_init(mod_instance_t mi, const char *arg)
Definition: mod_announce.c:326
int pri
current priority of this session
Definition: sm.h:268
const char * jid_user(jid_t jid)
expand and return the user
Definition: jid.c:338
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:346
jid_t jid_new(const char *id, int len)
make a new jid
Definition: jid.c:81
int nad_find_attr(nad_t nad, unsigned int elem, int ns, const char *name, const char *val)
get a matching attr on this elem, both name and optional val
Definition: nad.c:237
single instance of a module in a chain
Definition: sm.h:446
void nad_set_attr(nad_t nad, unsigned int elem, int ns, const char *name, const char *val, int vallen)
create, update, or zap any matching attr on this elem
Definition: nad.c:407
void nad_drop_elem(nad_t nad, unsigned int elem)
remove an element (and its subelements)
Definition: nad.c:484
int init
number of times the module intialiser has been called
Definition: sm.h:416
#define stanza_err_FEATURE_NOT_IMPLEMENTED
Definition: util.h:369
sm_t sm
sm context
Definition: sm.h:237
pkt_t pkt_dup(pkt_t pkt, const char *to, const char *from)
duplicate pkt, replacing addresses
Definition: pkt.c:84
#define stanza_err_FORBIDDEN
Definition: util.h:370
char * resource
Definition: jid.h:46
void nad_free(nad_t nad)
free that nad
Definition: nad.c:180
nad_t nad_copy(nad_t nad)
copy a nad
Definition: nad.c:147
int index
module index.
Definition: sm.h:408
xht users
pointers to currently loaded users (key is user@domain)
Definition: sm.h:189
mm_t mm
module manager
Definition: sm.h:404
#define DLLEXPORT
Definition: c2s.h:47
sess_t next
next session (in a list of sessions)
Definition: sm.h:276
pkt_t pkt_new(sm_t sm, nad_t nad)
Definition: pkt.c:113
sess_t top
top priority session
Definition: sm.h:244
sm_t sm
sm context
Definition: sm.h:366
mod_ret_t(* in_sess)(mod_instance_t mi, sess_t sess, pkt_t pkt)
in-sess handler
Definition: sm.h:423
static void _announce_user_delete(mod_instance_t mi, jid_t jid)
Definition: mod_announce.c:312
jid_t jid_reset_components(jid_t jid, const char *node, const char *domain, const char *resource)
build a jid from components
Definition: jid.c:281
sess_t sessions
list of action sessions
Definition: sm.h:243
module_t mod
module that this is an instance of
Definition: sm.h:449
jid_t from
packet addressing (not used for routing)
Definition: sm.h:140
static mod_ret_t _announce_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
Definition: mod_announce.c:120
void * private
module private data
Definition: sm.h:418
packet summary data wrapper
Definition: sm.h:129
storage_t st
storage subsystem
Definition: sm.h:211
nad_t nad
nad of the entire packet
Definition: sm.h:146
void jid_free(jid_t jid)
free a jid
Definition: jid.c:286
int aci_check(xht aci, const char *type, const char *name)
see if a username is in an acl
Definition: aci.c:93
char * domain
Definition: jid.h:45
Definition: jid.h:42
#define NAD_AVAL_L(N, A)
Definition: nad.h:190
void pkt_router(pkt_t pkt)
Definition: pkt.c:379
void pkt_free(pkt_t pkt)
Definition: pkt.c:315
#define log_debug(...)
Definition: log.h:65
#define NAD_AVAL(N, A)
Definition: nad.h:189
char * online_resource
Definition: mod_announce.c:43
presence
Definition: sm.h:99
struct user_st * user_t
Definition: sm.h:60
packet was unhandled, should be passed to the next module
Definition: sm.h:340
time_t t
Definition: mod_announce.c:39
packet was handled (and freed)
Definition: sm.h:339
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
struct moddata_st * moddata_t
int nad_find_elem(nad_t nad, unsigned int elem, int ns, const char *name, int depth)
locate the next elem at a given depth with an optional matching name
Definition: nad.c:206
#define uri_DELAY
Definition: uri.h:65
void xhash_walk(xht h, xhash_walker w, void *arg)
Definition: xhash.c:268
static void _announce_free(module_t mod)
Definition: mod_announce.c:318
void(* user_delete)(mod_instance_t mi, jid_t jid)
user-delete handler
Definition: sm.h:438
mod_ret_t(* pkt_sm)(mod_instance_t mi, pkt_t pkt)
pkt-sm handler
Definition: sm.h:429
int available
true if this session is available
Definition: sm.h:267
message
Definition: sm.h:95
jid_t to
Definition: sm.h:140
void pkt_delay(pkt_t pkt, time_t t, const char *from)
add an x:delay stamp
Definition: pkt.c:508
jid_t jid
user jid (user@host)
Definition: sm.h:239
xht acls
access control lists (key is list name, value is jid_t list)
Definition: sm.h:215
#define ZONE
Definition: mio_impl.h:76
void(* free)(module_t mod)
called when module is freed
Definition: sm.h:442
data for a single module
Definition: sm.h:403
void ** module_data
per-user module data
Definition: sm.h:249
presence (probe)
Definition: sm.h:101
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from)
Definition: pkt.c:328
mod_ret_t
module return values
Definition: sm.h:338
static void _announce_load(module_t mod, moddata_t data, const char *domain)
Definition: mod_announce.c:46
static void _announce_broadcast_user(const char *key, int keylen, void *val, void *arg)
Definition: mod_announce.c:175
sm_t sm
sm context
Definition: sm.h:447
#define stanza_err_INTERNAL_SERVER_ERROR
Definition: util.h:372
char * announce_resource
Definition: mod_announce.c:42
data for a single user
Definition: sm.h:234
int nad_find_scoped_namespace(nad_t nad, const char *uri, const char *prefix)
find a namespace in scope
Definition: nad.c:292