jabberd2  2.3.3
mod_offline.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 
30 typedef struct _mod_offline_st {
34  int userquota;
35 } *mod_offline_t;
36 
38  st_ret_t ret;
39  os_t os;
40  os_object_t o;
41  nad_t nad;
42  pkt_t queued;
43  int ns, elem, attr;
44  char cttl[15], cstamp[18];
45  time_t ttl, stamp;
46 
47  /* if they're becoming available for the first time */
48  if(pkt->type == pkt_PRESENCE && sess->pri >= 0 && pkt->to == NULL && sess->user->top == NULL) {
49 
50  ret = storage_get(pkt->sm->st, "queue", jid_user(sess->jid), NULL, &os);
51  if(ret != st_SUCCESS) {
52  log_debug(ZONE, "storage_get returned %d", ret);
53  return mod_PASS;
54  }
55 
56  if(os_iter_first(os))
57  do {
58  o = os_iter_object(os);
59 
60  if(os_object_get_nad(os, o, "xml", &nad)) {
61  queued = pkt_new(pkt->sm, nad_copy(nad));
62  if(queued == NULL) {
63  log_debug(ZONE, "invalid queued packet, not delivering");
64  } else {
65  /* check expiry as necessary */
66  if((ns = nad_find_scoped_namespace(queued->nad, uri_EXPIRE, NULL)) >= 0 &&
67  (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
68  (attr = nad_find_attr(queued->nad, elem, -1, "seconds", NULL)) >= 0) {
69  snprintf(cttl, 15, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
70  ttl = atoi(cttl);
71 
72  /* it should have a x:delay stamp, because we stamp everything we store */
73  if((ns = nad_find_scoped_namespace(queued->nad, uri_DELAY, NULL)) >= 0 &&
74  (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
75  (attr = nad_find_attr(queued->nad, elem, -1, "stamp", NULL)) >= 0) {
76  snprintf(cstamp, 18, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
77  stamp = datetime_in(cstamp);
78 
79  if(stamp + ttl <= time(NULL)) {
80  log_debug(ZONE, "queued packet has expired, dropping");
81  pkt_free(queued);
82  continue;
83  }
84  }
85  }
86 
87  log_debug(ZONE, "delivering queued packet to %s", jid_full(sess->jid));
88  pkt_sess(queued, sess);
89  }
90  }
91  } while(os_iter_next(os));
92 
93  os_free(os);
94 
95  /* drop the spool */
96  storage_delete(pkt->sm->st, "queue", jid_user(sess->jid), NULL);
97  }
98 
99  /* pass it so that other modules and mod_presence can get it */
100  return mod_PASS;
101 }
102 
104  mod_offline_t offline = (mod_offline_t) mi->mod->private;
105  int ns, elem, attr;
106  os_t os;
107  os_object_t o;
108  pkt_t event;
109  st_ret_t ret;
110  int queuesize;
111 
112  /* send messages to the top sessions */
113  if(user->top != NULL && (pkt->type & pkt_MESSAGE || pkt->type & pkt_S10N)) {
114  sess_t scan;
115 
116  /* loop over each session */
117  for(scan = user->sessions; scan != NULL; scan = scan->next) {
118  /* don't deliver to unavailable sessions */
119  if(!scan->available)
120  continue;
121 
122  /* skip negative priorities */
123  if(scan->pri < 0)
124  continue;
125 
126  /* headlines go to all, other to top priority */
127  if(pkt->type != pkt_MESSAGE_HEADLINE && scan->pri < user->top->pri)
128  continue;
129 
130  /* deliver to session */
131  log_debug(ZONE, "delivering message to %s", jid_full(scan->jid));
132  pkt_sess(pkt_dup(pkt, jid_full(scan->jid), jid_full(pkt->from)), scan);
133  }
134 
135  pkt_free(pkt);
136  return mod_HANDLED;
137  }
138 
139  /* if user quotas are enabled, count the number of offline messages this user has in the queue */
140  if(offline->userquota > 0) {
141  ret = storage_count(user->sm->st, "queue", jid_user(user->jid), NULL, &queuesize);
142 
143  log_debug(ZONE, "storage_count ret is %i queue size is %i", ret, queuesize);
144 
145  /* if the user's quota is exceeded, return an error */
146  if (ret == st_SUCCESS && (pkt->type & pkt_MESSAGE) && queuesize >= offline->userquota)
148  }
149 
150  /* save messages and s10ns for later */
151  if((pkt->type & pkt_MESSAGE && !offline->dropmessages) ||
152  (pkt->type & pkt_S10N && !offline->dropsubscriptions)) {
153 
154  /* check type of the message and drop headlines and groupchat */
155  if((((pkt->type & pkt_MESSAGE_HEADLINE) == pkt_MESSAGE_HEADLINE) && !offline->storeheadlines) ||
157  log_debug(ZONE, "not saving message (type 0x%X) for later", pkt->type);
158  pkt_free(pkt);
159  return mod_HANDLED;
160  }
161 
162  log_debug(ZONE, "saving packet for later");
163 
164  pkt_delay(pkt, time(NULL), user->jid->domain);
165 
166  /* new object */
167  os = os_new();
168  o = os_object_new(os);
169 
170  os_object_put(o, "xml", pkt->nad, os_type_NAD);
171 
172  /* store it */
173  switch(storage_put(user->sm->st, "queue", jid_user(user->jid), os)) {
174  case st_FAILED:
175  os_free(os);
177 
178  case st_NOTIMPL:
179  os_free(os);
180  return -stanza_err_SERVICE_UNAVAILABLE; /* xmpp-im 9.5#4 */
181 
182  default:
183  os_free(os);
184 
185  /* XEP-0022 - send offline events if they asked for it */
186  /* if there's an id element, then this is a notification, not a request, so ignore it */
187 
188  if((ns = nad_find_scoped_namespace(pkt->nad, uri_EVENT, NULL)) >= 0 &&
189  (elem = nad_find_elem(pkt->nad, 1, ns, "x", 1)) >= 0 &&
190  nad_find_elem(pkt->nad, elem, ns, "offline", 1) >= 0 &&
191  nad_find_elem(pkt->nad, elem, ns, "id", 1) < 0) {
192 
193  event = pkt_create(user->sm, "message", NULL, jid_full(pkt->from), jid_full(pkt->to));
194 
195  attr = nad_find_attr(pkt->nad, 1, -1, "type", NULL);
196  if(attr >= 0)
197  nad_set_attr(event->nad, 1, -1, "type", NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
198 
199  ns = nad_add_namespace(event->nad, uri_EVENT, NULL);
200  nad_append_elem(event->nad, ns, "x", 2);
201  nad_append_elem(event->nad, ns, "offline", 3);
202 
203  nad_append_elem(event->nad, ns, "id", 3);
204  attr = nad_find_attr(pkt->nad, 1, -1, "id", NULL);
205  if(attr >= 0)
206  nad_append_cdata(event->nad, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr), 4);
207 
208  pkt_router(event);
209  }
210 
211  pkt_free(pkt);
212  return mod_HANDLED;
213  }
214  }
215 
216  return mod_PASS;
217 }
218 
220  os_t os;
221  os_object_t o;
222  nad_t nad;
223  pkt_t queued;
224  int ns, elem, attr;
225  char cttl[15], cstamp[18];
226  time_t ttl, stamp;
227 
228  log_debug(ZONE, "deleting queue for %s", jid_user(jid));
229 
230  /* bounce the queue */
231  if(storage_get(mi->mod->mm->sm->st, "queue", jid_user(jid), NULL, &os) == st_SUCCESS) {
232  if(os_iter_first(os))
233  do {
234  o = os_iter_object(os);
235 
236  if(os_object_get_nad(os, o, "xml", &nad)) {
237  queued = pkt_new(mi->mod->mm->sm, nad_copy(nad));
238  if(queued == NULL) {
239  log_debug(ZONE, "invalid queued packet, not delivering");
240  } else {
241  /* check expiry as necessary */
242  if((ns = nad_find_scoped_namespace(queued->nad, uri_EXPIRE, NULL)) >= 0 &&
243  (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
244  (attr = nad_find_attr(queued->nad, elem, -1, "seconds", NULL)) >= 0) {
245  snprintf(cttl, 15, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
246  ttl = atoi(cttl);
247 
248  /* it should have a x:delay stamp, because we stamp everything we store */
249  if((ns = nad_find_scoped_namespace(queued->nad, uri_DELAY, NULL)) >= 0 &&
250  (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
251  (attr = nad_find_attr(queued->nad, elem, -1, "stamp", NULL)) >= 0) {
252  snprintf(cstamp, 18, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
253  stamp = datetime_in(cstamp);
254 
255  if(stamp + ttl <= time(NULL)) {
256  log_debug(ZONE, "queued packet has expired, dropping");
257  pkt_free(queued);
258  continue;
259  }
260  }
261  }
262 
263  log_debug(ZONE, "bouncing queued packet from %s", jid_full(queued->from));
265  }
266  }
267  } while(os_iter_next(os));
268 
269  os_free(os);
270  }
271 
272  storage_delete(mi->sm->st, "queue", jid_user(jid), NULL);
273 }
274 
275 static void _offline_free(module_t mod) {
276  mod_offline_t offline = (mod_offline_t) mod->private;
277 
278  free(offline);
279 }
280 
281 DLLEXPORT int module_init(mod_instance_t mi, const char *arg) {
282  module_t mod = mi->mod;
283  const char *configval;
284  mod_offline_t offline;
285 
286  if(mod->init) return 0;
287 
288  offline = (mod_offline_t) calloc(1, sizeof(struct _mod_offline_st));
289 
290  configval = config_get_one(mod->mm->sm->config, "offline.dropmessages", 0);
291  if (configval != NULL)
292  offline->dropmessages = 1;
293 
294  configval = config_get_one(mod->mm->sm->config, "offline.storeheadlines", 0);
295  if (configval != NULL)
296  offline->storeheadlines = 1;
297 
298  configval = config_get_one(mod->mm->sm->config, "offline.dropsubscriptions", 0);
299  if (configval != NULL)
300  offline->dropsubscriptions = 1;
301 
302  offline->userquota = j_atoi(config_get_one(mod->mm->sm->config, "offline.userquota", 0), 0);
303 
304  mod->private = offline;
305 
306  mod->in_sess = _offline_in_sess;
309  mod->free = _offline_free;
310 
311  feature_register(mod->mm->sm, "msgoffline");
312 
313  return 0;
314 }
pkt_t pkt_error(pkt_t pkt, int err)
Definition: pkt.c:30
user_t user
user this session belongs to
Definition: sm.h:255
pkt_type_t type
packet type
Definition: sm.h:138
jid_t jid
session jid (user@host/res)
Definition: sm.h:257
Definition: nad.h:93
data structures and prototypes for the session manager
subscribe request
Definition: sm.h:102
int pri
current priority of this session
Definition: sm.h:267
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
single instance of a module in a chain
Definition: sm.h:445
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
void nad_append_cdata(nad_t nad, const char *cdata, int len, int depth)
append new cdata to the last elem
Definition: nad.c:709
static void _offline_user_delete(mod_instance_t mi, jid_t jid)
Definition: mod_offline.c:219
config_t config
config context
Definition: sm.h:197
int init
number of times the module intialiser has been called
Definition: sm.h:415
message (groupchat)
Definition: sm.h:98
int nad_add_namespace(nad_t nad, const char *uri, const char *prefix)
bring a new namespace into scope
Definition: nad.c:734
sm_t sm
sm context
Definition: sm.h:236
static mod_ret_t _offline_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt)
Definition: mod_offline.c:37
pkt_t pkt_dup(pkt_t pkt, const char *to, const char *from)
duplicate pkt, replacing addresses
Definition: pkt.c:84
int j_atoi(const char *a, int def)
Definition: str.c:87
sm_t sm
sm context
Definition: sm.h:130
int nad_append_elem(nad_t nad, int ns, const char *name, int depth)
create a new elem on the list
Definition: nad.c:667
nad_t nad_copy(nad_t nad)
copy a nad
Definition: nad.c:145
mm_t mm
module manager
Definition: sm.h:403
#define DLLEXPORT
Definition: c2s.h:47
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
sess_t next
next session (in a list of sessions)
Definition: sm.h:275
pkt_t pkt_new(sm_t sm, nad_t nad)
Definition: pkt.c:113
sess_t top
top priority session
Definition: sm.h:243
sm_t sm
sm context
Definition: sm.h:365
mod_ret_t(* in_sess)(mod_instance_t mi, sess_t sess, pkt_t pkt)
in-sess handler
Definition: sm.h:422
sess_t sessions
list of action sessions
Definition: sm.h:242
module_t mod
module that this is an instance of
Definition: sm.h:448
jid_t from
packet addressing (not used for routing)
Definition: sm.h:140
void * private
module private data
Definition: sm.h:417
packet summary data wrapper
Definition: sm.h:129
storage_t st
storage subsystem
Definition: sm.h:210
nad_t nad
nad of the entire packet
Definition: sm.h:146
char * domain
Definition: jid.h:45
Definition: jid.h:42
#define NAD_AVAL_L(N, A)
Definition: nad.h:190
mod_ret_t(* pkt_user)(mod_instance_t mi, user_t user, pkt_t pkt)
pkt-user handler
Definition: sm.h:429
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
void feature_register(sm_t sm, const char *feature)
register a feature
Definition: feature.c:37
#define NAD_AVAL(N, A)
Definition: nad.h:189
presence
Definition: sm.h:99
packet was unhandled, should be passed to the next module
Definition: sm.h:339
#define stanza_err_ITEM_NOT_FOUND
Definition: util.h:373
packet was handled (and freed)
Definition: sm.h:338
There is one instance of this struct per user who is logged in to this c2s instance.
Definition: c2s.h:74
#define uri_DELAY
Definition: uri.h:64
time_t datetime_in(char *date)
Definition: datetime.c:34
void(* user_delete)(mod_instance_t mi, jid_t jid)
user-delete handler
Definition: sm.h:437
DLLEXPORT int module_init(mod_instance_t mi, const char *arg)
Definition: mod_offline.c:281
int available
true if this session is available
Definition: sm.h:266
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:238
static mod_ret_t _offline_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt)
Definition: mod_offline.c:103
#define ZONE
Definition: mio_impl.h:76
message (headline)
Definition: sm.h:97
const char * config_get_one(config_t c, const char *key, int num)
get config value n for this key
Definition: config.c:277
void(* free)(module_t mod)
called when module is freed
Definition: sm.h:441
data for a single module
Definition: sm.h:402
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
static void _offline_free(module_t mod)
Definition: mod_offline.c:275
struct _mod_offline_st * mod_offline_t
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from)
Definition: pkt.c:328
#define uri_EXPIRE
Definition: uri.h:72
#define uri_EVENT
Definition: uri.h:70
mod_ret_t
module return values
Definition: sm.h:337
#define stanza_err_SERVICE_UNAVAILABLE
Definition: util.h:384
sm_t sm
sm context
Definition: sm.h:446
#define stanza_err_INTERNAL_SERVER_ERROR
Definition: util.h:372
void pkt_sess(pkt_t pkt, sess_t sess)
Definition: pkt.c:459
data for a single user
Definition: sm.h:233
int nad_find_scoped_namespace(nad_t nad, const char *uri, const char *prefix)
find a namespace in scope
Definition: nad.c:290