jabberd2  2.2.16
sm/mod_offline.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #include "sm.h"
00022 
00030 typedef struct _mod_offline_st {
00031     int dropmessages;
00032     int storeheadlines;
00033     int dropsubscriptions;
00034     int userquota;
00035 } *mod_offline_t;
00036 
00037 static mod_ret_t _offline_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
00038     st_ret_t ret;
00039     os_t os;
00040     os_object_t o;
00041     nad_t nad;
00042     pkt_t queued;
00043     int ns, elem, attr;
00044     char cttl[15], cstamp[18];
00045     time_t ttl, stamp;
00046 
00047     /* if they're becoming available for the first time */
00048     if(pkt->type == pkt_PRESENCE && sess->pri >= 0 && pkt->to == NULL && sess->user->top == NULL) {
00049 
00050         ret = storage_get(pkt->sm->st, "queue", jid_user(sess->jid), NULL, &os);
00051         if(ret != st_SUCCESS) {
00052             log_debug(ZONE, "storage_get returned %d", ret);
00053             return mod_PASS;
00054         }
00055         
00056         if(os_iter_first(os))
00057             do {
00058                 o = os_iter_object(os);
00059 
00060                 if(os_object_get_nad(os, o, "xml", &nad)) {
00061                     queued = pkt_new(pkt->sm, nad_copy(nad));
00062                     if(queued == NULL) {
00063                         log_debug(ZONE, "invalid queued packet, not delivering");
00064                     } else {
00065                         /* check expiry as necessary */
00066                         if((ns = nad_find_scoped_namespace(queued->nad, uri_EXPIRE, NULL)) >= 0 &&
00067                            (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
00068                            (attr = nad_find_attr(queued->nad, elem, -1, "seconds", NULL)) >= 0) {
00069                             snprintf(cttl, 15, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
00070                             ttl = atoi(cttl);
00071 
00072                             /* it should have a x:delay stamp, because we stamp everything we store */
00073                             if((ns = nad_find_scoped_namespace(queued->nad, uri_DELAY, NULL)) >= 0 &&
00074                                (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
00075                                (attr = nad_find_attr(queued->nad, elem, -1, "stamp", NULL)) >= 0) {
00076                                 snprintf(cstamp, 18, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
00077                                 stamp = datetime_in(cstamp);
00078 
00079                                 if(stamp + ttl <= time(NULL)) {
00080                                     log_debug(ZONE, "queued packet has expired, dropping");
00081                                     pkt_free(queued);
00082                                     continue;
00083                                 }
00084                             }
00085                         }
00086 
00087                         log_debug(ZONE, "delivering queued packet to %s", jid_full(sess->jid));
00088                         pkt_sess(queued, sess);
00089                     }
00090                 }
00091             } while(os_iter_next(os));
00092 
00093         os_free(os);
00094 
00095         /* drop the spool */
00096         storage_delete(pkt->sm->st, "queue", jid_user(sess->jid), NULL);
00097     }
00098 
00099     /* pass it so that other modules and mod_presence can get it */
00100     return mod_PASS;
00101 }
00102 
00103 static mod_ret_t _offline_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
00104     mod_offline_t offline = (mod_offline_t) mi->mod->private;
00105     int ns, elem, attr;
00106     os_t os;
00107     os_object_t o;
00108     pkt_t event;
00109     st_ret_t ret;
00110     int queuesize;
00111 
00112     /* send messages to the top sessions */
00113     if(user->top != NULL && (pkt->type & pkt_MESSAGE || pkt->type & pkt_S10N)) {
00114         sess_t scan;
00115     
00116         /* loop over each session */
00117         for(scan = user->sessions; scan != NULL; scan = scan->next) {
00118             /* don't deliver to unavailable sessions */
00119             if(!scan->available)
00120                 continue;
00121 
00122             /* skip negative priorities */
00123             if(scan->pri < 0)
00124                 continue;
00125 
00126             /* headlines go to all, other to top priority */
00127             if(pkt->type != pkt_MESSAGE_HEADLINE && scan->pri < user->top->pri)
00128                 continue;
00129     
00130             /* deliver to session */
00131             log_debug(ZONE, "delivering message to %s", jid_full(scan->jid));
00132             pkt_sess(pkt_dup(pkt, jid_full(scan->jid), jid_full(pkt->from)), scan);
00133         }
00134 
00135         pkt_free(pkt);
00136         return mod_HANDLED;
00137     }
00138 
00139     /* if user quotas are enabled, count the number of offline messages this user has in the queue */
00140     if(offline->userquota > 0) {
00141         ret = storage_count(user->sm->st, "queue", jid_user(user->jid), NULL, &queuesize);
00142 
00143         log_debug(ZONE, "storage_count ret is %i queue size is %i", ret, queuesize);
00144 
00145         /* if the user's quota is exceeded, return an error */
00146         if (ret == st_SUCCESS && (pkt->type & pkt_MESSAGE) && queuesize >= offline->userquota)
00147            return -stanza_err_SERVICE_UNAVAILABLE;
00148     }
00149 
00150     /* save messages and s10ns for later */
00151     if((pkt->type & pkt_MESSAGE && !offline->dropmessages) ||
00152        (pkt->type & pkt_S10N && !offline->dropsubscriptions)) {
00153 
00154         /* check type of the message and drop headlines and groupchat */
00155         if((((pkt->type & pkt_MESSAGE_HEADLINE) == pkt_MESSAGE_HEADLINE) && !offline->storeheadlines) ||
00156             (pkt->type & pkt_MESSAGE_GROUPCHAT) == pkt_MESSAGE_GROUPCHAT) {
00157             log_debug(ZONE, "not saving message (type 0x%X) for later", pkt->type);
00158             pkt_free(pkt);
00159             return mod_HANDLED;
00160         }
00161 
00162     log_debug(ZONE, "saving packet for later");
00163 
00164         pkt_delay(pkt, time(NULL), user->jid->domain);
00165 
00166         /* new object */
00167         os = os_new();
00168         o = os_object_new(os);
00169 
00170         os_object_put(o, "xml", pkt->nad, os_type_NAD);
00171 
00172         /* store it */
00173         switch(storage_put(user->sm->st, "queue", jid_user(user->jid), os)) {
00174             case st_FAILED:
00175                 os_free(os);
00176                 return -stanza_err_INTERNAL_SERVER_ERROR;
00177 
00178             case st_NOTIMPL:
00179                 os_free(os);
00180                 return -stanza_err_SERVICE_UNAVAILABLE;     /* xmpp-im 9.5#4 */
00181 
00182             default:
00183                 os_free(os);
00184 
00185                 /* XEP-0022 - send offline events if they asked for it */
00186                 /* if there's an id element, then this is a notification, not a request, so ignore it */
00187 
00188                 if((ns = nad_find_scoped_namespace(pkt->nad, uri_EVENT, NULL)) >= 0 &&
00189                    (elem = nad_find_elem(pkt->nad, 1, ns, "x", 1)) >= 0 &&
00190                    nad_find_elem(pkt->nad, elem, ns, "offline", 1) >= 0 && 
00191                    nad_find_elem(pkt->nad, elem, ns, "id", 1) < 0) {
00192 
00193                     event = pkt_create(user->sm, "message", NULL, jid_full(pkt->from), jid_full(pkt->to));
00194 
00195                     attr = nad_find_attr(pkt->nad, 1, -1, "type", NULL);
00196                     if(attr >= 0)
00197                         nad_set_attr(event->nad, 1, -1, "type", NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00198 
00199                     ns = nad_add_namespace(event->nad, uri_EVENT, NULL);
00200                     nad_append_elem(event->nad, ns, "x", 2);
00201                     nad_append_elem(event->nad, ns, "offline", 3);
00202 
00203                     nad_append_elem(event->nad, ns, "id", 3);
00204                     attr = nad_find_attr(pkt->nad, 1, -1, "id", NULL);
00205                     if(attr >= 0)
00206                         nad_append_cdata(event->nad, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr), 4);
00207 
00208                     pkt_router(event);
00209                 }
00210 
00211                 pkt_free(pkt);
00212                 return mod_HANDLED;
00213         }
00214     }
00215 
00216     return mod_PASS;
00217 }
00218 
00219 static void _offline_user_delete(mod_instance_t mi, jid_t jid) {
00220     os_t os;
00221     os_object_t o;
00222     nad_t nad;
00223     pkt_t queued;
00224     int ns, elem, attr;
00225     char cttl[15], cstamp[18];
00226     time_t ttl, stamp;
00227 
00228     log_debug(ZONE, "deleting queue for %s", jid_user(jid));
00229 
00230     /* bounce the queue */
00231     if(storage_get(mi->mod->mm->sm->st, "queue", jid_user(jid), NULL, &os) == st_SUCCESS) {
00232         if(os_iter_first(os))
00233             do {
00234                 o = os_iter_object(os);
00235 
00236                 if(os_object_get_nad(os, o, "xml", &nad)) {
00237                     queued = pkt_new(mi->mod->mm->sm, nad_copy(nad));
00238                     if(queued == NULL) {
00239                         log_debug(ZONE, "invalid queued packet, not delivering");
00240                     } else {
00241                         /* check expiry as necessary */
00242                         if((ns = nad_find_scoped_namespace(queued->nad, uri_EXPIRE, NULL)) >= 0 &&
00243                            (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
00244                            (attr = nad_find_attr(queued->nad, elem, -1, "seconds", NULL)) >= 0) {
00245                             snprintf(cttl, 15, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
00246                             ttl = atoi(cttl);
00247 
00248                             /* it should have a x:delay stamp, because we stamp everything we store */
00249                             if((ns = nad_find_scoped_namespace(queued->nad, uri_DELAY, NULL)) >= 0 &&
00250                                (elem = nad_find_elem(queued->nad, 1, ns, "x", 1)) >= 0 &&
00251                                (attr = nad_find_attr(queued->nad, elem, -1, "stamp", NULL)) >= 0) {
00252                                 snprintf(cstamp, 18, "%.*s", NAD_AVAL_L(queued->nad, attr), NAD_AVAL(queued->nad, attr));
00253                                 stamp = datetime_in(cstamp);
00254 
00255                                 if(stamp + ttl <= time(NULL)) {
00256                                     log_debug(ZONE, "queued packet has expired, dropping");
00257                                     pkt_free(queued);
00258                                     continue;
00259                                 }
00260                             }
00261                         }
00262 
00263                         log_debug(ZONE, "bouncing queued packet from %s", jid_full(queued->from));
00264                         pkt_router(pkt_error(queued, stanza_err_ITEM_NOT_FOUND));
00265                     }
00266                 }
00267             } while(os_iter_next(os));
00268 
00269         os_free(os);
00270     }
00271     
00272     storage_delete(mi->sm->st, "queue", jid_user(jid), NULL);
00273 }
00274 
00275 static void _offline_free(module_t mod) {
00276     mod_offline_t offline = (mod_offline_t) mod->private;
00277 
00278     free(offline);
00279 }
00280 
00281 DLLEXPORT int module_init(mod_instance_t mi, char *arg) {
00282     module_t mod = mi->mod;
00283     char *configval;
00284     mod_offline_t offline;
00285 
00286     if(mod->init) return 0;
00287 
00288     offline = (mod_offline_t) calloc(1, sizeof(struct _mod_offline_st));
00289 
00290     configval = config_get_one(mod->mm->sm->config, "offline.dropmessages", 0);
00291     if (configval != NULL)
00292         offline->dropmessages = 1;
00293 
00294     configval = config_get_one(mod->mm->sm->config, "offline.storeheadlines", 0);
00295     if (configval != NULL)
00296         offline->storeheadlines = 1;
00297 
00298     configval = config_get_one(mod->mm->sm->config, "offline.dropsubscriptions", 0);
00299     if (configval != NULL)
00300         offline->dropsubscriptions = 1;
00301 
00302     offline->userquota = j_atoi(config_get_one(mod->mm->sm->config, "offline.userquota", 0), 0);
00303 
00304     mod->private = offline;
00305 
00306     mod->in_sess = _offline_in_sess;
00307     mod->pkt_user = _offline_pkt_user;
00308     mod->user_delete = _offline_user_delete;
00309     mod->free = _offline_free;
00310 
00311     feature_register(mod->mm->sm, "msgoffline");
00312 
00313     return 0;
00314 }