#include "sm.h"
typedef struct moddata_st {
nad_t nad;
int loaded;
time_t t;
os_t tos;
int index;
} *moddata_t;
static void _announce_load(module_t mod, moddata_t data) {
st_ret_t ret;
os_t os;
os_object_t o;
nad_t nad;
int ns, elem, attr;
char timestamp[18], telem[5];
struct tm tm;
memset(&tm, 0, sizeof(struct tm));
data->loaded = 1;
if((ret = storage_get(mod->mm->sm->st, "motd-message", mod->mm->sm->id, NULL, &os)) == st_SUCCESS) {
os_iter_first(os);
o = os_iter_object(os);
if(os_object_get_nad(os, o, "xml", &nad)) {
data->nad = nad_copy(nad);
if((ns = nad_find_scoped_namespace(data->nad, uri_DELAY, NULL)) >= 0 &&
(elem = nad_find_elem(data->nad, 1, ns, "x", 1)) >= 0 &&
(attr = nad_find_attr(data->nad, elem, -1, "stamp", NULL)) >= 0) {
snprintf(timestamp, 18, "%.*s", NAD_AVAL_L(data->nad, attr), NAD_AVAL(data->nad, attr));
telem[0] = timestamp[0];
telem[1] = timestamp[1];
telem[2] = timestamp[2];
telem[3] = timestamp[3];
telem[4] = '\0';
tm.tm_year = atoi(telem) - 1900;
telem[0] = timestamp[4];
telem[1] = timestamp[5];
telem[2] = '\0';
tm.tm_mon = atoi(telem) - 1;
telem[0] = timestamp[6];
telem[1] = timestamp[7];
tm.tm_mday = atoi(telem);
telem[0] = timestamp[9];
telem[1] = timestamp[10];
tm.tm_hour = atoi(telem);
telem[0] = timestamp[12];
telem[1] = timestamp[13];
tm.tm_min = atoi(telem);
telem[0] = timestamp[15];
telem[1] = timestamp[16];
tm.tm_sec = atoi(telem);
data->t = mktime(&tm);
}
}
os_free(os);
}
if(data->tos != NULL)
os_free(data->tos);
data->tos = os_new();
os_object_put(os_object_new(data->tos), "time", &data->t, os_type_INTEGER);
}
static mod_ret_t _announce_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
module_t mod = mi->mod;
moddata_t data = (moddata_t) mod->private;
time_t t;
nad_t nad;
pkt_t motd;
os_t os;
os_object_t o;
if(data->nad == NULL) {
if(data->loaded)
return mod_PASS;
_announce_load(mod, data);
if(data->nad == NULL)
return mod_PASS;
}
if(pkt->type == pkt_PRESENCE && pkt->to == NULL && sess->user->top == NULL) {
if((time_t) sess->user->module_data[mod->index] == 0 &&
storage_get(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, &os) == st_SUCCESS) {
os_iter_first(os);
o = os_iter_object(os);
os_object_get_time(os, o, "time", &t);
sess->user->module_data[mod->index] = (void *) t;
os_free(os);
}
if((time_t) sess->user->module_data[mod->index] >= data->t)
return mod_PASS;
log_debug(ZONE, "delivering stored motd to %s", jid_full(sess->jid));
nad = nad_copy(data->nad);
nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
nad_set_attr(nad, 1, -1, "from", mod->mm->sm->id, strlen(mod->mm->sm->id));
motd = pkt_new(mod->mm->sm, nad);
if(motd == NULL) {
log_debug(ZONE, "invalid stored motd, not delivering");
nad_free(nad);
} else
pkt_router(motd);
sess->user->module_data[mod->index] = (void *) data->t;
storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
}
return mod_PASS;
}
static void _announce_broadcast_user(xht users, const char *key, void *val, void *arg) {
user_t user = (user_t) val;
moddata_t data = (moddata_t) arg;
sess_t sess;
nad_t nad;
for(sess = user->sessions; sess != NULL; sess = sess->next) {
if((!sess->available && !sess->invisible) || sess->pri < 0)
continue;
log_debug(ZONE, "resending to '%s'", jid_full(sess->jid));
nad = nad_copy(data->nad);
nad_set_attr(nad, 1, -1, "to", jid_full(sess->jid), strlen(jid_full(sess->jid)));
nad_set_attr(nad, 1, -1, "from", user->sm->id, strlen(user->sm->id));
pkt_router(pkt_new(user->sm, nad));
sess->user->module_data[data->index] = (void *) data->t;
storage_replace(sess->user->sm->st, "motd-times", jid_user(sess->jid), NULL, data->tos);
}
}
static mod_ret_t _announce_pkt_sm(mod_instance_t mi, pkt_t pkt) {
module_t mod = mi->mod;
moddata_t data = (moddata_t) mod->private;
pkt_t store;
nad_t nad;
time_t t;
os_t os;
os_object_t o;
st_ret_t ret;
t = time(NULL);
if(pkt->type != pkt_MESSAGE || strlen(pkt->to->resource) < 8 || strncmp(pkt->to->resource, "announce", 8) != 0)
return mod_PASS;
if(!aci_check(mod->mm->sm->acls, "broadcast", pkt->from)) {
log_debug(ZONE, "not allowing broadcast from %s", jid_full(pkt->from));
return -stanza_err_FORBIDDEN;
}
if(pkt->to->resource[8] == '\0') {
log_debug(ZONE, "storing message for announce later");
store = pkt_dup(pkt, NULL, NULL);
pkt_delay(store, t, mod->mm->sm->id);
os = os_new();
o = os_object_new(os);
os_object_put(o, "xml", store->nad, os_type_NAD);
ret = storage_replace(mod->mm->sm->st, "motd-message", mod->mm->sm->id, NULL, os);
os_free(os);
switch(ret) {
case st_FAILED:
pkt_free(store);
return -stanza_err_INTERNAL_SERVER_ERROR;
case st_NOTIMPL:
pkt_free(store);
return -stanza_err_FEATURE_NOT_IMPLEMENTED;
default:
break;
}
if(data->nad != NULL)
nad_free(data->nad);
data->nad = store->nad;
store->nad = NULL;
pkt_free(store);
data->t = t;
if(data->tos != NULL)
os_free(data->tos);
data->tos = os_new();
os_object_put(os_object_new(data->tos), "time", &t, os_type_INTEGER);
}
else if(strcmp(&(pkt->to->resource[8]), "/online") != 0) {
log_debug(ZONE, "unknown announce resource '%s'", pkt->to->resource);
pkt_free(pkt);
return mod_HANDLED;
}
log_debug(ZONE, "broadcasting message to all sessions");
nad = data->nad;
data->nad = pkt->nad;
xhash_walk(mod->mm->sm->users, _announce_broadcast_user, (void *) data);
data->nad = nad;
pkt_free(pkt);
return mod_HANDLED;
}
static void _announce_user_delete(mod_instance_t mi, jid_t jid) {
log_debug(ZONE, "deleting motd time for %s", jid_user(jid));
storage_delete(mi->sm->st, "motd-times", jid_user(jid), NULL);
}
static void _announce_free(module_t mod) {
moddata_t data = (moddata_t) mod->private;
if(data->nad != NULL) nad_free(data->nad);
if(data->tos != NULL) os_free(data->tos);
free(data);
}
int announce_init(mod_instance_t mi) {
module_t mod = mi->mod;
moddata_t data;
if(mod->init) return 0;
data = (moddata_t) malloc(sizeof(struct moddata_st));
memset(data, 0, sizeof(struct moddata_st));
mod->private = (void *) data;
data->index = mod->index;
mod->in_sess = _announce_in_sess;
mod->pkt_sm = _announce_pkt_sm;
mod->user_delete = _announce_user_delete;
mod->free = _announce_free;
return 0;
}