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 #include <time.h> 00023 00031 /* 00032 * message to host/announce goes to all online sessions and to offline users next time they connect 00033 * message to host/announce/online goes to all online sessions 00034 */ 00035 00036 typedef struct moddata_st { 00037 nad_t nad; 00038 int loaded; 00039 time_t t; 00040 os_t tos; 00041 int index; 00042 char *announce_resource; 00043 char *online_resource; 00044 } *moddata_t; 00045 00046 static void _announce_load(module_t mod, moddata_t data, const char *domain) { 00047 st_ret_t ret; 00048 os_t os; 00049 os_object_t o; 00050 nad_t nad; 00051 int ns, elem, attr; 00052 char timestamp[18], telem[5]; 00053 struct tm tm; 00054 00055 /* struct tm can vary in size depending on platform */ 00056 memset(&tm, 0, sizeof(struct tm)); 00057 00058 data->loaded = 1; 00059 00060 /* load the current message */ 00061 if((ret = storage_get(mod->mm->sm->st, "motd-message", domain, NULL, &os)) == st_SUCCESS) { 00062 os_iter_first(os); 00063 o = os_iter_object(os); 00064 if(os_object_get_nad(os, o, "xml", &nad)) { 00065 /* Copy the nad, as the original is freed when the os is freed below */ 00066 data->nad = nad_copy(nad); 00067 if((ns = nad_find_scoped_namespace(data->nad, uri_DELAY, NULL)) >= 0 && 00068 (elem = nad_find_elem(data->nad, 1, ns, "x", 1)) >= 0 && 00069 (attr = nad_find_attr(data->nad, elem, -1, "stamp", NULL)) >= 0) { 00070 snprintf(timestamp, 18, "%.*s", NAD_AVAL_L(data->nad, attr), NAD_AVAL(data->nad, attr)); 00071 00072 /* year */ 00073 telem[0] = timestamp[0]; 00074 telem[1] = timestamp[1]; 00075 telem[2] = timestamp[2]; 00076 telem[3] = timestamp[3]; 00077 telem[4] = '\0'; 00078 tm.tm_year = atoi(telem) - 1900; 00079 00080 /* month */ 00081 telem[0] = timestamp[4]; 00082 telem[1] = timestamp[5]; 00083 telem[2] = '\0'; 00084 tm.tm_mon = atoi(telem) - 1; 00085 00086 /* day */ 00087 telem[0] = timestamp[6]; 00088 telem[1] = timestamp[7]; 00089 tm.tm_mday = atoi(telem); 00090 00091 /* hour */ 00092 telem[0] = timestamp[9]; 00093 telem[1] = timestamp[10]; 00094 tm.tm_hour = atoi(telem); 00095 00096 /* minute */ 00097 telem[0] = timestamp[12]; 00098 telem[1] = timestamp[13]; 00099 tm.tm_min = atoi(telem); 00100 00101 /* second */ 00102 telem[0] = timestamp[15]; 00103 telem[1] = timestamp[16]; 00104 tm.tm_sec = atoi(telem); 00105 00106 data->t = timegm(&tm); 00107 } 00108 } 00109 00110 os_free(os); 00111 } 00112 00113 if(data->tos != NULL) 00114 os_free(data->tos); 00115 data->tos = os_new(); 00116 os_object_put(os_object_new(data->tos), "time", &data->t, os_type_INTEGER); 00117 } 00118 00119 static mod_ret_t _announce_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) { 00120 module_t mod = mi->mod; 00121 moddata_t data = (moddata_t) mod->private; 00122 time_t t; 00123 nad_t nad; 00124 pkt_t motd; 00125 os_t os; 00126 os_object_t o; 00127 00128 /* try to load data if we haven't yet */ 00129 if(data->nad == NULL) { 00130 if(data->loaded) 00131 return mod_PASS; /* nothing to give them */ 00132 _announce_load(mod, data, sess->user->jid->domain); 00133 if(data->nad == NULL) 00134 return mod_PASS; 00135 } 00136 00137 /* if they're becoming available for the first time */ 00138 if(pkt->type == pkt_PRESENCE && pkt->to == NULL && sess->user->top == NULL) { 00139 /* load the time of the last motd they got */ 00140 if((time_t) sess->user->module_data[mod->index] == 0 && 00141 storage_get(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, &os) == st_SUCCESS) { 00142 os_iter_first(os); 00143 o = os_iter_object(os); 00144 os_object_get_time(os, o, "time", &t); 00145 sess->user->module_data[mod->index] = (void *) t; 00146 os_free(os); 00147 } 00148 00149 /* they've seen this one */ 00150 if((time_t) sess->user->module_data[mod->index] >= data->t) 00151 return mod_PASS; 00152 00153 /* a-delivering we go */ 00154 log_debug(ZONE, "delivering stored motd to %s", jid_full(sess->jid)); 00155 00156 nad = nad_copy(data->nad); 00157 nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid))); 00158 nad_set_attr(nad, 1, -1, "from", sess->user->jid->domain, strlen(sess->user->jid->domain)); 00159 00160 motd = pkt_new(mod->mm->sm, nad); 00161 if(motd == NULL) { 00162 log_debug(ZONE, "invalid stored motd, not delivering"); 00163 nad_free(nad); 00164 } else 00165 pkt_router(motd); 00166 00167 sess->user->module_data[mod->index] = (void *) data->t; 00168 storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos); 00169 } 00170 00171 return mod_PASS; 00172 } 00173 00174 static void _announce_broadcast_user(const char *key, int keylen, void *val, void *arg) { 00175 user_t user = (user_t) val; 00176 moddata_t data = (moddata_t) arg; 00177 sess_t sess; 00178 nad_t nad; 00179 00180 for(sess = user->sessions; sess != NULL; sess = sess->next) { 00181 if(!sess->available || sess->pri < 0) 00182 continue; 00183 00184 log_debug(ZONE, "resending to '%s'", jid_full(sess->jid)); 00185 00186 nad = nad_copy(data->nad); 00187 nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid))); 00188 nad_set_attr(nad, 1, -1, "from", sess->jid->domain, strlen(sess->jid->domain)); 00189 00190 pkt_router(pkt_new(user->sm, nad)); 00191 00192 sess->user->module_data[data->index] = (void *) data->t; 00193 storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos); 00194 } 00195 } 00196 00197 static mod_ret_t _announce_pkt_sm(mod_instance_t mi, pkt_t pkt) { 00198 module_t mod = mi->mod; 00199 moddata_t data = (moddata_t) mod->private; 00200 pkt_t store; 00201 nad_t nad; 00202 jid_t jid; 00203 time_t t; 00204 os_t os; 00205 os_object_t o; 00206 st_ret_t ret; 00207 int elem; 00208 00209 /* time of this packet */ 00210 t = time(NULL); 00211 00212 /* answer to probes and subscription requests if admin */ 00213 if((pkt->type == pkt_PRESENCE_PROBE || pkt->type == pkt_S10N) && aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) { 00214 log_debug(ZONE, "answering presence probe/sub from %s with /announce resources", jid_full(pkt->from)); 00215 00216 /* send presences */ 00217 jid = jid_new(pkt->from->domain, -1); 00218 jid_reset_components(jid, jid->node, jid->domain, data->announce_resource); 00219 pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid))); 00220 jid_free(jid); 00221 00222 jid = jid_new(pkt->from->domain, -1); 00223 jid_reset_components(jid, jid->node, jid->domain, data->online_resource); 00224 pkt_router(pkt_create(mod->mm->sm, "presence", NULL, jid_user(pkt->from), jid_full(jid))); 00225 jid_free(jid); 00226 } 00227 00228 /* we want messages addressed to /announce */ 00229 if(!(pkt->type & pkt_MESSAGE) || strlen(pkt->to->resource) < 8 || strncmp(pkt->to->resource, data->announce_resource, 8) != 0) 00230 return mod_PASS; 00231 00232 /* make sure they're allowed */ 00233 if(!aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) { 00234 log_debug(ZONE, "not allowing broadcast from %s", jid_full(pkt->from)); 00235 return -stanza_err_FORBIDDEN; 00236 } 00237 00238 /* "fix" packet a bit */ 00239 /* force type normal */ 00240 nad_set_attr(pkt->nad, 1, -1, "type", NULL, 0); 00241 /* remove sender nick */ 00242 elem = nad_find_elem(pkt->nad, 1, -1, "nick", 1); 00243 if(elem >= 0) nad_drop_elem(pkt->nad, elem); 00244 00245 if(pkt->to->resource[8] == '\0') { 00246 log_debug(ZONE, "storing message for announce later"); 00247 00248 store = pkt_dup(pkt, NULL, NULL); 00249 00250 pkt_delay(store, t, pkt->to->domain); 00251 00252 /* prepare for storage */ 00253 os = os_new(); 00254 o = os_object_new(os); 00255 00256 os_object_put(o, "xml", store->nad, os_type_NAD); 00257 00258 /* store it */ 00259 ret = storage_replace(mod->mm->sm->st, "motd-message", pkt->to->domain, NULL, os); 00260 os_free(os); 00261 00262 switch(ret) { 00263 case st_FAILED: 00264 pkt_free(store); 00265 return -stanza_err_INTERNAL_SERVER_ERROR; 00266 00267 case st_NOTIMPL: 00268 pkt_free(store); 00269 return -stanza_err_FEATURE_NOT_IMPLEMENTED; 00270 00271 default: 00272 break; 00273 } 00274 00275 /* replace our local copy */ 00276 if(data->nad != NULL) 00277 nad_free(data->nad); 00278 data->nad = store->nad; 00279 00280 store->nad = NULL; 00281 pkt_free(store); 00282 00283 /* update timestamp */ 00284 data->t = t; 00285 if(data->tos != NULL) 00286 os_free(data->tos); 00287 data->tos = os_new(); 00288 os_object_put(os_object_new(data->tos), "time", &t, os_type_INTEGER); 00289 } 00290 00291 else if(strcmp(&(pkt->to->resource[8]), "/online") != 0) { 00292 log_debug(ZONE, "unknown announce resource '%s'", pkt->to->resource); 00293 pkt_free(pkt); 00294 return mod_HANDLED; 00295 } 00296 00297 log_debug(ZONE, "broadcasting message to all sessions"); 00298 00299 /* hack */ 00300 nad = data->nad; 00301 data->nad = pkt->nad; 00302 xhash_walk(mod->mm->sm->users, _announce_broadcast_user, (void *) data); 00303 data->nad = nad; 00304 00305 /* done */ 00306 pkt_free(pkt); 00307 00308 return mod_HANDLED; 00309 } 00310 00311 static void _announce_user_delete(mod_instance_t mi, jid_t jid) { 00312 log_debug(ZONE, "deleting motd time for %s", jid_user(jid)); 00313 00314 storage_delete(mi->sm->st, "motd-times", jid_user(jid), NULL); 00315 } 00316 00317 static void _announce_free(module_t mod) { 00318 moddata_t data = (moddata_t) mod->private; 00319 00320 if(data->nad != NULL) nad_free(data->nad); 00321 if(data->tos != NULL) os_free(data->tos); 00322 free(data); 00323 } 00324 00325 DLLEXPORT int module_init(mod_instance_t mi, char *arg) { 00326 module_t mod = mi->mod; 00327 moddata_t data; 00328 00329 if(mod->init) return 0; 00330 00331 data = (moddata_t) calloc(1, sizeof(struct moddata_st)); 00332 00333 mod->private = (void *) data; 00334 00335 data->index = mod->index; 00336 00337 data->announce_resource = "announce"; 00338 data->online_resource = "announce/online"; 00339 00340 mod->in_sess = _announce_in_sess; 00341 mod->pkt_sm = _announce_pkt_sm; 00342 mod->user_delete = _announce_user_delete; 00343 mod->free = _announce_free; 00344 00345 return 0; 00346 }