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 
23 /** @file sm/mod_offline.c
24   * @brief offline storage
25   * @author Robert Norris
26   * $Date: 2005/08/17 07:48:28 $
27   * $Revision: 1.26 $
28   */
29 
30 typedef struct _mod_offline_st {
31     int dropmessages;
32     int storeheadlines;
33     int dropsubscriptions;
34     int userquota;
35 } *mod_offline_t;
36 
_offline_in_sess(mod_instance_t mi,sess_t sess,pkt_t pkt)37 static mod_ret_t _offline_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
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 
_offline_pkt_user(mod_instance_t mi,user_t user,pkt_t pkt)103 static mod_ret_t _offline_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
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)
147            return -stanza_err_SERVICE_UNAVAILABLE;
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) ||
156             (pkt->type & pkt_MESSAGE_GROUPCHAT) == pkt_MESSAGE_GROUPCHAT) {
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);
176                 return -stanza_err_INTERNAL_SERVER_ERROR;
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 
_offline_user_delete(mod_instance_t mi,jid_t jid)219 static void _offline_user_delete(mod_instance_t mi, jid_t jid) {
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));
264                         pkt_router(pkt_error(queued, stanza_err_ITEM_NOT_FOUND));
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 
_offline_free(module_t mod)275 static void _offline_free(module_t mod) {
276     mod_offline_t offline = (mod_offline_t) mod->private;
277 
278     free(offline);
279 }
280 
module_init(mod_instance_t mi,const char * arg)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;
307     mod->pkt_user = _offline_pkt_user;
308     mod->user_delete = _offline_user_delete;
309     mod->free = _offline_free;
310 
311     feature_register(mod->mm->sm, "msgoffline");
312 
313     return 0;
314 }
315