jabberd2  2.3.4
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 
325 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
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 }
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:197
subscribe request
Definition: sm.h:102
DLLEXPORT int module_init(mod_instance_t mi, const char *arg)
Definition: mod_announce.c:325
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:339
const char * jid_full(jid_t jid)
expand and return the full
Definition: jid.c:347
jid_t jid_new(const char *id, int len)
make a new jid
Definition: jid.c:81
single instance of a module in a chain
Definition: sm.h:446
int nad_find_elem(nad_t nad, 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:204
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:178
nad_t nad_copy(nad_t nad)
copy a nad
Definition: nad.c:145
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
void nad_set_attr(nad_t nad, 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:375
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:311
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:119
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
#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:317
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 nad_drop_elem(nad_t nad, int elem)
remove an element (and its subelements)
Definition: nad.c:452
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
int nad_find_attr(nad_t nad, 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:235
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:174
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:290