jabberd2
2.2.16
|
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 }