mod_disco_publish.c [plain text]
#include "sm.h"
typedef struct disco_item_st *disco_item_t;
struct disco_item_st {
jid_t jid;
char name[257];
char node[257];
disco_item_t next;
};
static mod_ret_t _disco_publish_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
module_t mod = mi->mod;
disco_item_t list, di, scan, updi;
pkt_t res;
int ns, elem, attr;
char filter[4096];
os_t os;
os_object_t o;
if(pkt->ns != ns_DISCO_ITEMS)
return mod_PASS;
list = user->module_data[mod->index];
if(pkt->type == pkt_IQ) {
res = pkt_create(mod->mm->sm, "iq", "result", jid_full(pkt->from), jid_full(pkt->to));
pkt_id(pkt, res);
ns = nad_add_namespace(res->nad, uri_DISCO_INFO, NULL);
nad_append_elem(res->nad, ns, "query", 2);
pkt_free(pkt);
for(scan = list; scan != NULL; scan = scan->next) {
nad_append_elem(res->nad, ns, "item", 3);
nad_append_attr(res->nad, -1, "jid", jid_full(scan->jid));
if(scan->name[0] != '\0')
nad_append_attr(res->nad, -1, "name", scan->name);
if(scan->node[0] != '\0')
nad_append_attr(res->nad, -1, "node", scan->node);
}
pkt_router(res);
return mod_HANDLED;
}
if(pkt->type != pkt_IQ_SET)
return mod_PASS;
if(jid_compare_user(pkt->from, user->jid) != 0)
return -stanza_err_FORBIDDEN;
ns = nad_find_scoped_namespace(pkt->nad, uri_DISCO_INFO, NULL);
elem = nad_find_elem(pkt->nad, 2, ns, "item", 1);
while(elem >= 0) {
attr = nad_find_attr(pkt->nad, elem, -1, "jid", NULL);
if(attr < 0) {
elem = nad_find_elem(pkt->nad, elem, ns, "item", 0);
continue;
}
di = (disco_item_t) malloc(sizeof(struct disco_item_st));
memset(di, 0, sizeof(struct disco_item_st));
di->jid = jid_new(mod->mm->sm->pc, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
attr = nad_find_attr(pkt->nad, elem, -1, "name", NULL);
if(attr >= 0)
strncpy(di->name, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr) > 256 ? 256 : NAD_AVAL_L(pkt->nad, attr));
attr = nad_find_attr(pkt->nad, elem, -1, "node", NULL);
if(attr >= 0)
strncpy(di->node, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr) > 256 ? 256 : NAD_AVAL_L(pkt->nad, attr));
if(nad_find_attr(pkt->nad, elem, -1, "action", "remove") >= 0) {
if(list != NULL) {
updi = NULL;
if(jid_compare_full(di->jid, list->jid) == 0 && strcmp(di->node, list->node) == 0) {
updi = list;
list = list->next;
user->module_data[mod->index] = list;
}
else {
for(scan = list; scan != NULL && scan->next != NULL && jid_compare_full(di->jid, scan->next->jid) != 0 && strcmp(di->node, scan->next->node) != 0; scan = scan->next);
if(scan->next != NULL) {
updi = scan->next;
scan->next = scan->next->next;
}
}
if(updi != NULL) {
jid_free(updi->jid);
free(updi);
if(di->node[0] == '\0')
sprintf(filter, "(jid=%i:%s)", strlen(jid_full(di->jid)), jid_full(di->jid));
else
sprintf(filter, "(&(jid=%i:%s)(node=%i:%s))", strlen(jid_full(di->jid)), jid_full(di->jid), strlen(di->node), di->node);
storage_delete(mod->mm->sm->st, "disco-items", jid_user(user->jid), filter);
}
}
jid_free(di->jid);
free(di);
}
else {
if(list == NULL)
list = user->module_data[mod->index] = di;
else {
updi = NULL;
if(jid_compare_full(di->jid, list->jid) == 0 && strcmp(di->node, list->node) == 0) {
updi = list;
di->next = list->next;
list = user->module_data[mod->index] = di;
}
else {
for(scan = list; scan != NULL && scan->next != NULL && jid_compare_full(di->jid, scan->next->jid) != 0 && strcmp(di->node, scan->next->node) != 0; scan = scan->next);
if(scan->next != NULL) {
updi = scan->next;
scan->next = di;
di->next = scan->next->next;
}
}
if(updi == NULL) {
di->next = list;
list = user->module_data[mod->index] = di;
}
else {
jid_free(updi->jid);
free(updi);
}
}
if(di->node[0] == '\0')
sprintf(filter, "(jid=%i:%s)", strlen(jid_full(di->jid)), jid_full(di->jid));
else
sprintf(filter, "(&(jid=%i:%s)(node=%i:%s))", strlen(jid_full(di->jid)), jid_full(di->jid), strlen(di->node), di->node);
os = os_new();
o = os_object_new(os);
os_object_put(o, "jid", jid_full(di->jid), os_type_STRING);
if(di->name[0] != '\0')
os_object_put(o, "name", di->name, os_type_STRING);
if(di->node[0] != '\0')
os_object_put(o, "node", di->node, os_type_STRING);
storage_replace(mod->mm->sm->st, "disco-items", jid_user(user->jid), filter, os);
os_free(os);
}
elem = nad_find_elem(pkt->nad, elem, ns, "item", 0);
}
res = pkt_create(mod->mm->sm, "iq", "result", jid_full(pkt->from), jid_full(pkt->to));
pkt_id(pkt, res);
pkt_free(pkt);
pkt_router(res);
return mod_HANDLED;
}
static void _disco_publish_user_free(disco_item_t *list) {
disco_item_t scan, next;
scan = *list;
while(scan != NULL) {
log_debug(ZONE, "freeing published disco item %s node %s", jid_full(scan->jid), scan->node);
next = scan->next;
jid_free(scan->jid);
free(scan);
scan = next;
}
}
static int _disco_publish_user_load(mod_instance_t mi, user_t user) {
module_t mod = mi->mod;
disco_item_t list = user->module_data[mod->index], scan, next, di;
os_t os;
os_object_t o;
char *str;
scan = list;
while(scan != NULL) {
next = scan->next;
jid_free(scan->jid);
free(scan);
scan = next;
}
list = user->module_data[mod->index] = NULL;
pool_cleanup(user->p, (void (*))(void *) _disco_publish_user_free, &(user->module_data[mod->index]));
if(storage_get(mod->mm->sm->st, "disco-items", jid_user(user->jid), NULL, &os) != st_SUCCESS)
return 0;
if(os_iter_first(os))
do {
o = os_iter_object(os);
if(os_object_get_str(os, o, "jid", &str)) {
di = (disco_item_t) malloc(sizeof(struct disco_item_st));
memset(di, 0, sizeof(struct disco_item_st));
di->jid = jid_new(mod->mm->sm->pc, str, -1);
if(os_object_get_str(os, o, "name", &str))
strncpy(di->name, str, 256);
if(os_object_get_str(os, o, "node", &str))
strncpy(di->node, str, 256);
di->next = list;
list = user->module_data[mod->index] = di;
}
} while(os_iter_next(os));
os_free(os);
return 0;
}
static void _disco_publish_user_delete(mod_instance_t mi, jid_t jid) {
log_debug(ZONE, "deleting published disco items for %s", jid_user(jid));
storage_delete(mi->sm->st, "disco-items", jid_user(jid), NULL);
}
int disco_publish_init(mod_instance_t mi, char *arg) {
if(mi->mod->init) return 0;
log_debug(ZONE, "disco publish module init");
mi->mod->pkt_user = _disco_publish_pkt_user;
mi->mod->user_load = _disco_publish_user_load;
mi->mod->user_delete = _disco_publish_user_delete;
return 0;
}