#include "sm.h"
typedef struct zebra_st *zebra_t;
typedef struct zebra_list_st *zebra_list_t;
typedef struct zebra_item_st *zebra_item_t;
typedef enum {
zebra_NONE,
zebra_JID,
zebra_GROUP,
zebra_S10N
} zebra_item_type_t;
typedef enum {
block_NONE = 0x00,
block_MESSAGE = 0x01,
block_PRES_IN = 0x02,
block_PRES_OUT = 0x04,
block_IQ = 0x08
} zebra_block_type_t;
struct zebra_st {
xht lists;
zebra_list_t def;
};
struct zebra_list_st {
pool p;
char *name;
zebra_item_t items, last;
};
struct zebra_item_st {
zebra_item_type_t type;
jid_t jid;
char *group;
int to;
int from;
int deny;
int order;
zebra_block_type_t block;
zebra_item_t next, prev;
};
union xhashv
{
void **val;
zebra_list_t *z_val;
};
static void _privacy_free(zebra_t z) {
zebra_list_t zlist;
union xhashv xhv;
log_debug(ZONE, "freeing zebra ctx");
if(xhash_iter_first(z->lists))
do {
xhv.z_val = &zlist;
xhash_iter_get(z->lists, NULL, xhv.val);
pool_free(zlist->p);
} while(xhash_iter_next(z->lists));
xhash_free(z->lists);
free(z);
}
static void _privacy_user_free(zebra_t *z) {
if(*z != NULL)
_privacy_free(*z);
}
static int _privacy_user_load(mod_instance_t mi, user_t user) {
module_t mod = mi->mod;
zebra_t z;
os_t os;
os_object_t o;
zebra_list_t zlist;
pool p;
zebra_item_t zitem, scan;
char *str;
log_debug(ZONE, "loading privacy lists for %s", jid_user(user->jid));
z = user->module_data[mod->index];
if(z != NULL)
_privacy_free(z);
z = (zebra_t) malloc(sizeof(struct zebra_st));
memset(z, 0, sizeof(struct zebra_st));
z->lists = xhash_new(101);
user->module_data[mod->index] = z;
pool_cleanup(user->p, (void (*))(void *) _privacy_user_free, &(user->module_data[mod->index]));
if(storage_get(user->sm->st, "privacy-items", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
if(os_iter_first(os))
do {
o = os_iter_object(os);
if(!os_object_get_str(os, o, "list", &str)) {
log_debug(ZONE, "item with no list field, skipping");
continue;
}
log_debug(ZONE, "got item for list %s", str);
zlist = xhash_get(z->lists, str);
if(zlist == NULL) {
log_debug(ZONE, "creating list %s", str);
p = pool_new();
zlist = (zebra_list_t) pmalloco(p, sizeof(struct zebra_list_st));
zlist->p = p;
zlist->name = pstrdup(p, str);
xhash_put(z->lists, zlist->name, (void *) zlist);
}
zitem = (zebra_item_t) pmalloco(zlist->p, sizeof(struct zebra_item_st));
if(os_object_get_str(os, o, "type", &str))
switch(str[0]) {
case 'j':
zitem->type = zebra_JID;
break;
case 'g':
zitem->type = zebra_GROUP;
break;
case 's':
zitem->type = zebra_S10N;
break;
}
if(zitem->type != zebra_NONE) {
if(!os_object_get_str(os, o, "value", &str)) {
log_debug(ZONE, "no value on non-fall-through item, dropping this item");
free(zitem);
continue;
}
switch(zitem->type) {
case zebra_JID:
zitem->jid = jid_new(user->sm->pc, str, strlen(str));
if(zitem->jid == NULL) {
log_debug(ZONE, "invalid jid '%s' on item, dropping this item", str);
free(zitem);
continue;
}
pool_cleanup(zlist->p, free, zitem->jid);
log_debug(ZONE, "jid item with value '%s'", jid_full(zitem->jid));
break;
case zebra_GROUP:
zitem->group = pstrdup(zlist->p, str);
log_debug(ZONE, "group item with value '%s'", zitem->group);
break;
case zebra_S10N:
if(strcmp(str, "to") == 0)
zitem->to = 1;
else if(strcmp(str, "from") == 0)
zitem->from = 1;
else if(strcmp(str, "both") == 0)
zitem->to = zitem->from = 1;
else if(strcmp(str, "none") != 0) {
log_debug(ZONE, "invalid value '%s' on s10n item, dropping this item", str);
free(zitem);
continue;
}
log_debug(ZONE, "s10n item with value '%s' (to %d from %d)", str, zitem->to, zitem->from);
break;
case zebra_NONE:
break;
}
}
os_object_get_bool(os, o, "deny", &zitem->deny);
if(zitem->deny) {
log_debug(ZONE, "deny rule");
} else {
log_debug(ZONE, "accept rule");
}
os_object_get_int(os, o, "order", &(zitem->order));
log_debug(ZONE, "order %d", zitem->order);
os_object_get_int(os, o, "block", (int *) &(zitem->block));
log_debug(ZONE, "block 0x%x", zitem->block);
for(scan = zlist->items; scan != NULL; scan = scan->next)
if(zitem->order < scan->order)
break;
if(scan == NULL) {
if(zlist->last == NULL)
zlist->items = zlist->last = zitem;
else {
zlist->last->next = zitem;
zitem->prev = zlist->last;
zlist->last = zitem;
}
}
else {
if(zlist->items == scan) {
zitem->next = zlist->items;
zlist->items = zitem;
scan->prev = zitem;
} else {
zitem->next = scan;
zitem->prev = scan->prev;
scan->prev->next = zitem;
scan->prev = zitem;
}
}
} while(os_iter_next(os));
os_free(os);
}
if(storage_get(user->sm->st, "privacy-default", jid_user(user->jid), NULL, &os) == st_SUCCESS) {
if(os_iter_first(os))
do {
o = os_iter_object(os);
if(os_object_get_str(os, o, "default", &str)) {
z->def = (zebra_list_t) xhash_get(z->lists, str);
if(z->def == NULL) {
log_debug(ZONE, "storage says the default list for %s is %s, but it doesn't exist!", jid_user(user->jid), str);
} else {
log_debug(ZONE, "user %s has default list %s", jid_user(user->jid), str);
}
}
} while(os_iter_next(os));
os_free(os);
}
return 0;
}
static int _privacy_action(user_t user, zebra_list_t zlist, jid_t jid, pkt_type_t ptype, int in) {
zebra_item_t scan;
int match, i;
item_t ritem;
unsigned char domres[2048];
log_debug(ZONE, "running match on list %s for %s (packet type 0x%x) (%s)", zlist->name, jid_full(jid), ptype, in ? "incoming" : "outgoing");
for(scan = zlist->items; scan != NULL; scan = scan->next) {
match = 0;
switch(scan->type) {
case zebra_NONE:
match = 1;
break;
case zebra_JID:
sprintf(domres, "%s/%s", jid->domain, jid->resource);
if(jid_compare_full(scan->jid, jid) == 0 ||
strcmp(jid_full(scan->jid), jid_user(jid)) == 0 ||
strcmp(jid_full(scan->jid), domres) == 0 ||
strcmp(jid_full(scan->jid), jid->domain) == 0)
match = 1;
break;
case zebra_GROUP:
ritem = xhash_get(user->roster, jid_full(jid));
if(ritem == NULL) ritem = xhash_get(user->roster, jid_user(jid));
if(ritem == NULL) ritem = xhash_get(user->roster, jid->domain);
if(ritem != NULL)
for(i = 0; i < ritem->ngroups; i++)
if(strcmp(scan->group, ritem->groups[i]) == 0)
match = 1;
break;
case zebra_S10N:
ritem = xhash_get(user->roster, jid_full(jid));
if(ritem == NULL) ritem = xhash_get(user->roster, jid_user(jid));
if(ritem == NULL) ritem = xhash_get(user->roster, jid->domain);
if(ritem != NULL)
if(scan->to == ritem->to && scan->from == ritem->from)
match = 1;
break;
}
if(match) {
if(scan->block == block_NONE)
return scan->deny;
if(in) {
if(ptype & pkt_MESSAGE && scan->block & block_MESSAGE)
return scan->deny;
if(ptype & pkt_PRESENCE && scan->block & block_PRES_IN)
return scan->deny;
if(ptype & pkt_IQ && scan->block & block_IQ)
return scan->deny;
} else if(ptype & pkt_PRESENCE && scan->block & block_PRES_OUT && ptype != pkt_PRESENCE_PROBE) {
return scan->deny;
}
}
}
return 0;
}
static mod_ret_t _privacy_in_router(mod_instance_t mi, pkt_t pkt) {
module_t mod = mi->mod;
user_t user;
zebra_t z;
sess_t sess = NULL;
zebra_list_t zlist = NULL;
if(pkt->to == NULL || pkt->to->node[0] == '\0')
return mod_PASS;
user = user_load(mod->mm->sm, pkt->to);
if(user == NULL) {
log_debug(ZONE, "no user %s, passing packet", jid_user(pkt->to));
return mod_PASS;
}
z = (zebra_t) user->module_data[mod->index];
if(*pkt->to->resource != '\0')
sess = sess_match(user, pkt->to->resource);
if(sess == NULL)
sess = user->top;
if(sess != NULL)
zlist = (zebra_list_t) sess->module_data[mod->index];
if(zlist == NULL)
zlist = z->def;
if(zlist == NULL)
return mod_PASS;
if(_privacy_action(user, zlist, pkt->from, pkt->type, 1) == 0)
return mod_PASS;
log_debug(ZONE, "denying incoming packet based on privacy policy");
if(pkt->type == pkt_IQ || pkt->type == pkt_IQ_SET)
return -stanza_err_FEATURE_NOT_IMPLEMENTED;
pkt_free(pkt);
return mod_HANDLED;
}
static mod_ret_t _privacy_out_router(mod_instance_t mi, pkt_t pkt) {
module_t mod = mi->mod;
user_t user;
zebra_t z;
sess_t sess = NULL;
zebra_list_t zlist = NULL;
if(pkt->from == NULL || pkt->from->node[0] == '\0')
return mod_PASS;
user = user_load(mod->mm->sm, pkt->from);
if(user == NULL) {
log_debug(ZONE, "no user %s, passing packet", jid_user(pkt->to));
return mod_PASS;
}
z = (zebra_t) user->module_data[mod->index];
if(*pkt->from->resource != '\0')
sess = sess_match(user, pkt->from->resource);
if(sess != NULL)
zlist = (zebra_list_t) sess->module_data[mod->index];
if(zlist == NULL)
zlist = z->def;
if(zlist == NULL)
return mod_PASS;
if(_privacy_action(user, zlist, pkt->to, pkt->type, 0) == 0)
return mod_PASS;
log_debug(ZONE, "denying outgoing packet based on privacy policy");
pkt_free(pkt);
return mod_HANDLED;
}
static void _privacy_result_builder(xht zhash, const char *name, void *val, void *arg) {
zebra_list_t zlist = (zebra_list_t) val;
pkt_t pkt = (pkt_t) arg;
int ns, query, list, item;
zebra_item_t zitem;
char order[14];
ns = nad_find_scoped_namespace(pkt->nad, uri_PRIVACY, NULL);
query = nad_find_elem(pkt->nad, 1, ns, "query", 1);
list = nad_insert_elem(pkt->nad, query, ns, "list", NULL);
nad_set_attr(pkt->nad, list, -1, "name", zlist->name, 0);
for(zitem = zlist->items; zitem != NULL; zitem = zitem->next) {
item = nad_insert_elem(pkt->nad, list, ns, "item", NULL);
switch(zitem->type) {
case zebra_JID:
nad_set_attr(pkt->nad, item, -1, "type", "jid", 0);
nad_set_attr(pkt->nad, item, -1, "value", jid_full(zitem->jid), 0);
break;
case zebra_GROUP:
nad_set_attr(pkt->nad, item, -1, "type", "group", 0);
nad_set_attr(pkt->nad, item, -1, "value", zitem->group, 0);
break;
case zebra_S10N:
nad_set_attr(pkt->nad, item, -1, "type", "subscription", 0);
if(zitem->to == 1 && zitem->from == 1)
nad_set_attr(pkt->nad, item, -1, "value", "both", 4);
else if(zitem->to == 1)
nad_set_attr(pkt->nad, item, -1, "value", "to", 2);
else if(zitem->from == 1)
nad_set_attr(pkt->nad, item, -1, "value", "from", 4);
else
nad_set_attr(pkt->nad, item, -1, "value", "none", 4);
break;
case zebra_NONE:
break;
}
if(zitem->deny)
nad_set_attr(pkt->nad, item, -1, "action", "deny", 4);
else
nad_set_attr(pkt->nad, item, -1, "action", "allow", 5);
snprintf(order, 14, "%d", zitem->order);
order[13] = '\0';
nad_set_attr(pkt->nad, item, -1, "order", order, 0);
if(zitem->block & block_MESSAGE)
nad_insert_elem(pkt->nad, item, ns, "message", NULL);
if(zitem->block & block_PRES_IN)
nad_insert_elem(pkt->nad, item, ns, "presence-in", NULL);
if(zitem->block & block_PRES_OUT)
nad_insert_elem(pkt->nad, item, ns, "presence-out", NULL);
if(zitem->block & block_IQ)
nad_insert_elem(pkt->nad, item, ns, "iq", NULL);
}
}
static void _privacy_lists_result_builder(xht zhash, const char *name, void *val, void *arg) {
zebra_list_t zlist = (zebra_list_t) val;
pkt_t pkt = (pkt_t) arg;
int ns, query, list;
ns = nad_find_scoped_namespace(pkt->nad, uri_PRIVACY, NULL);
query = nad_find_elem(pkt->nad, 1, ns, "query", 1);
list = nad_insert_elem(pkt->nad, query, ns, "list", NULL);
nad_set_attr(pkt->nad, list, -1, "name", zlist->name, 0);
}
static mod_ret_t _privacy_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
module_t mod = mi->mod;
int ns, query, list, name, active, def, item, type, value, action, order;
char corder[14], str[256], filter[1024];
zebra_t z;
zebra_list_t zlist, old;
pool p;
zebra_item_t zitem, scan;
sess_t sscan;
pkt_t result;
os_t os;
os_object_t o;
st_ret_t ret;
if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_PRIVACY)
return mod_PASS;
if(pkt->to != NULL)
return -stanza_err_BAD_REQUEST;
ns = nad_find_scoped_namespace(pkt->nad, uri_PRIVACY, NULL);
query = nad_find_elem(pkt->nad, 1, ns, "query", 1);
if(query < 0)
return -stanza_err_BAD_REQUEST;
z = (zebra_t) sess->user->module_data[mod->index];
if(pkt->type == pkt_IQ_SET) {
list = nad_find_elem(pkt->nad, query, ns, "list", 1);
active = nad_find_elem(pkt->nad, query, ns, "active", 1);
def = nad_find_elem(pkt->nad, query, ns, "default", 1);
if((list < 0 && active < 0 && def < 0) || (list >= 0 && (active >=0 || def >= 0)))
return -stanza_err_BAD_REQUEST;
if(list >= 0) {
if(nad_find_elem(pkt->nad, list, ns, "list", 0) >= 0) {
pkt_error(pkt, stanza_err_BAD_REQUEST);
pkt_sess(pkt, sess);
return mod_HANDLED;
}
name = nad_find_attr(pkt->nad, list, -1, "name", NULL);
if(name < 0) {
log_debug(ZONE, "no list name specified, failing request");
return -stanza_err_BAD_REQUEST;
}
snprintf(str, 256, "%.*s", NAD_AVAL_L(pkt->nad, name), NAD_AVAL(pkt->nad, name));
str[255] = '\0';
log_debug(ZONE, "updating list %s", str);
p = pool_new();
zlist = (zebra_list_t) pmalloco(p, sizeof(struct zebra_list_st));
zlist->p = p;
zlist->name = pstrdup(p, str);
os = os_new();
item = nad_find_elem(pkt->nad, list, ns, "item", 1);
while(item >= 0) {
type = nad_find_attr(pkt->nad, item, -1, "type", 0);
value = nad_find_attr(pkt->nad, item, -1, "value", 0);
action = nad_find_attr(pkt->nad, item, -1, "action", 0);
order = nad_find_attr(pkt->nad, item, -1, "order", 0);
if(action < 0 || order < 0 || (type >= 0 && value < 0)) {
pool_free(p);
os_free(os);
return -stanza_err_BAD_REQUEST;
}
zitem = (zebra_item_t) pmalloco(p, sizeof(struct zebra_item_st));
o = os_object_new(os);
os_object_put(o, "list", zlist->name, os_type_STRING);
if(type >= 0) {
if(NAD_AVAL_L(pkt->nad, type) == 3 && strncmp("jid", NAD_AVAL(pkt->nad, type), 3) == 0) {
zitem->type = zebra_JID;
zitem->jid = jid_new(mod->mm->sm->pc, NAD_AVAL(pkt->nad, value), NAD_AVAL_L(pkt->nad, value));
if(zitem->jid == NULL) {
log_debug(ZONE, "invalid jid '%.*s', failing request", NAD_AVAL_L(pkt->nad, value), NAD_AVAL(pkt->nad, value));
pool_free(p);
os_free(os);
return -stanza_err_BAD_REQUEST;
}
pool_cleanup(p, free, zitem->jid);
log_debug(ZONE, "jid item with value '%s'", jid_full(zitem->jid));
os_object_put(o, "type", "jid", os_type_STRING);
os_object_put(o, "value", jid_full(zitem->jid), os_type_STRING);
}
else if(NAD_AVAL_L(pkt->nad, type) == 5 && strncmp("group", NAD_AVAL(pkt->nad, type), 5) == 0) {
zitem->type = zebra_GROUP;
zitem->group = pstrdupx(zlist->p, NAD_AVAL(pkt->nad, value), NAD_AVAL_L(pkt->nad, value));
log_debug(ZONE, "group item with value '%s'", zitem->group);
os_object_put(o, "type", "group", os_type_STRING);
os_object_put(o, "value", zitem->group, os_type_STRING);
}
else if(NAD_AVAL_L(pkt->nad, type) == 12 && strncmp("subscription", NAD_AVAL(pkt->nad, type), 12) == 0) {
zitem->type = zebra_S10N;
os_object_put(o, "type", "subscription", os_type_STRING);
if(NAD_AVAL_L(pkt->nad, value) == 2 && strncmp("to", NAD_AVAL(pkt->nad, value), 2) == 0) {
zitem->to = 1;
os_object_put(o, "value", "to", os_type_STRING);
} else if(NAD_AVAL_L(pkt->nad, value) == 4 && strncmp("from", NAD_AVAL(pkt->nad, value), 4) == 0) {
zitem->from = 1;
os_object_put(o, "value", "from", os_type_STRING);
} else if(NAD_AVAL_L(pkt->nad, value) == 4 && strncmp("both", NAD_AVAL(pkt->nad, value), 4) == 0) {
zitem->to = zitem->from = 1;
os_object_put(o, "value", "both", os_type_STRING);
} else if(NAD_AVAL_L(pkt->nad, value) == 4 && strncmp("none", NAD_AVAL(pkt->nad, value), 4) == 0)
os_object_put(o, "value", "none", os_type_STRING);
else {
log_debug(ZONE, "invalid value '%.*s' on s10n item, failing request", NAD_AVAL_L(pkt->nad, value), NAD_AVAL(pkt->nad, value));
pool_free(p);
os_free(os);
return -stanza_err_BAD_REQUEST;
}
log_debug(ZONE, "s10n item with value '%.*s' (to %d from %d)", NAD_AVAL_L(pkt->nad, value), NAD_AVAL(pkt->nad, value), zitem->to, zitem->from);
}
}
if(NAD_AVAL_L(pkt->nad, action) == 4 && strncmp("deny", NAD_AVAL(pkt->nad, action), 4) == 0) {
zitem->deny = 1;
log_debug(ZONE, "deny rule");
} else if(NAD_AVAL_L(pkt->nad, action) == 5 && strncmp("allow", NAD_AVAL(pkt->nad, action), 5) == 0) {
zitem->deny = 0;
log_debug(ZONE, "allow rule");
} else {
log_debug(ZONE, "unknown action '%.*s', failing request", NAD_AVAL_L(pkt->nad, action), NAD_AVAL(pkt->nad, action));
pool_free(p);
os_free(os);
return -stanza_err_BAD_REQUEST;
}
os_object_put(o, "deny", &zitem->deny, os_type_BOOLEAN);
snprintf(corder, 14, "%.*s", NAD_AVAL_L(pkt->nad, order), NAD_AVAL(pkt->nad, order));
corder[13] = '\0';
zitem->order = atoi(corder);
os_object_put(o, "order", &zitem->order, os_type_INTEGER);
if(nad_find_elem(pkt->nad, item, ns, "message", 1) >= 0)
zitem->block |= block_MESSAGE;
if(nad_find_elem(pkt->nad, item, ns, "presence-in", 1) >= 0)
zitem->block |= block_PRES_IN;
if(nad_find_elem(pkt->nad, item, ns, "presence-out", 1) >= 0)
zitem->block |= block_PRES_OUT;
if(nad_find_elem(pkt->nad, item, ns, "iq", 1) >= 0)
zitem->block |= block_IQ;
os_object_put(o, "block", &zitem->block, os_type_INTEGER);
for(scan = zlist->items; scan != NULL; scan = scan->next)
if(zitem->order < scan->order)
break;
if(scan == NULL) {
if(zlist->last == NULL)
zlist->items = zlist->last = zitem;
else {
zlist->last->next = zitem;
zitem->prev = zlist->last;
zlist->last = zitem;
}
}
else {
if(zlist->items == scan) {
zitem->next = zlist->items;
zlist->items = zitem;
scan->prev = zitem;
} else {
zitem->next = scan;
zitem->prev = scan->prev;
scan->prev->next = zitem;
scan->prev = zitem;
}
}
item = nad_find_elem(pkt->nad, item, ns, "item", 0);
}
sprintf(filter, "(list=%i:%s)", strlen(zlist->name), zlist->name);
ret = storage_replace(mod->mm->sm->st, "privacy-items", jid_user(sess->user->jid), filter, os);
os_free(os);
if(ret != st_SUCCESS) {
pool_free(zlist->p);
return -stanza_err_INTERNAL_SERVER_ERROR;
}
old = xhash_get(z->lists, zlist->name);
if(zlist->items == NULL) {
log_debug(ZONE, "removed list %s", zlist->name);
xhash_zap(z->lists, zlist->name);
pool_free(zlist->p);
if(old != NULL) pool_free(old->p);
zlist = NULL;
} else {
log_debug(ZONE, "updated list %s", zlist->name);
xhash_put(z->lists, zlist->name, (void *) zlist);
if(old != NULL) pool_free(old->p);
}
if(old != NULL) {
log_debug(ZONE, "relinking sessions");
for(sscan = sess->user->sessions; sscan != NULL; sscan = sscan->next)
if(sscan->module_data[mod->index] == old) {
sscan->module_data[mod->index] = (void *) zlist;
log_debug(ZONE, "session '%s' now has active list '%s'", jid_full(sscan->jid), (zlist != NULL) ? zlist->name : "(NONE)");
}
if(z->def == old) {
z->def = zlist;
if(zlist == NULL) {
storage_delete(mod->mm->sm->st, "privacy-default", jid_user(sess->user->jid), NULL);
log_debug(ZONE, "removed default list");
}
else {
os = os_new();
o = os_object_new(os);
os_object_put(o, "default", zlist->name, os_type_STRING);
storage_replace(mod->mm->sm->st, "privacy-default", jid_user(sess->user->jid), NULL, os);
os_free(os);
log_debug(ZONE, "default list is now '%s'", (zlist != NULL) ? zlist->name : "(NONE)");
}
}
}
}
if(active >= 0) {
name = nad_find_attr(pkt->nad, active, -1, "name", NULL);
if(name < 0) {
log_debug(ZONE, "clearing active list for session '%s'", jid_full(sess->jid));
sess->module_data[mod->index] = NULL;
}
else {
snprintf(str, 256, "%.*s", NAD_AVAL_L(pkt->nad, name), NAD_AVAL(pkt->nad, name));
str[255] = '\0';
zlist = xhash_get(z->lists, str);
if(zlist == NULL) {
log_debug(ZONE, "request to make list '%s' active, but there's no such list", str);
pkt_error(pkt, stanza_err_ITEM_NOT_FOUND);
pkt_sess(pkt, sess);
return mod_HANDLED;
}
sess->module_data[mod->index] = zlist;
log_debug(ZONE, "session '%s' now has active list '%s'", jid_full(sess->jid), str);
}
}
if(def >= 0) {
name = nad_find_attr(pkt->nad, def, -1, "name", NULL);
if(name < 0) {
log_debug(ZONE, "clearing default list for '%s'", jid_user(sess->user->jid));
z->def = NULL;
}
else {
snprintf(str, 256, "%.*s", NAD_AVAL_L(pkt->nad, name), NAD_AVAL(pkt->nad, name));
str[255] = '\0';
zlist = xhash_get(z->lists, str);
if(zlist == NULL) {
log_debug(ZONE, "request to make list '%s' default, but there's no such list");
pkt_error(pkt, stanza_err_ITEM_NOT_FOUND);
pkt_sess(pkt, sess);
return mod_HANDLED;
}
z->def = zlist;
os = os_new();
o = os_object_new(os);
os_object_put(o, "default", zlist->name, os_type_STRING);
storage_replace(mod->mm->sm->st, "privacy-default", jid_user(sess->user->jid), NULL, os);
os_free(os);
log_debug(ZONE, "'%s' now has default list '%s'", jid_user(sess->user->jid), str);
}
}
result = pkt_create(pkt->sm, "iq", "result", NULL, NULL);
pkt_id(pkt, result);
pkt_free(pkt);
pkt_sess(result, sess);
return mod_HANDLED;
}
list = nad_find_elem(pkt->nad, query, ns, "list", 1);
if(list >= 0 && nad_find_elem(pkt->nad, list, ns, "list", 0) >= 0) {
pkt_error(pkt, stanza_err_BAD_REQUEST);
pkt_sess(pkt, sess);
return mod_HANDLED;
}
result = pkt_create(pkt->sm, "iq", "result", NULL, NULL);
pkt_id(pkt, result);
ns = nad_add_namespace(result->nad, uri_PRIVACY, NULL);
query = nad_insert_elem(result->nad, 1, ns, "query", NULL);
if(list >= 0) {
name = nad_find_attr(pkt->nad, list, -1, "name", NULL);
zlist = xhash_getx(z->lists, NAD_AVAL(pkt->nad, name), NAD_AVAL_L(pkt->nad, name));
if(zlist == NULL) {
pkt_error(pkt, stanza_err_ITEM_NOT_FOUND);
pkt_sess(pkt, sess);
return mod_HANDLED;
}
_privacy_result_builder(z->lists, zlist->name, (void *) zlist, (void *) result);
}
else {
xhash_walk(z->lists, _privacy_lists_result_builder, (void *) result);
}
if(list < 0) {
if(sess->module_data[mod->index] != NULL) {
active = nad_insert_elem(result->nad, query, ns, "active", NULL);
nad_set_attr(result->nad, active, -1, "name", ((zebra_list_t) sess->module_data[mod->index])->name, 0);
}
if(z->def != NULL) {
def = nad_insert_elem(result->nad, query, ns, "default", NULL);
nad_set_attr(result->nad, def, -1, "name", z->def->name, 0);
}
}
pkt_sess(result, sess);
pkt_free(pkt);
return mod_HANDLED;
}
static void _privacy_user_delete(mod_instance_t mi, jid_t jid) {
log_debug(ZONE, "deleting privacy data for %s", jid_user(jid));
storage_delete(mi->sm->st, "privacy-items", jid_user(jid), NULL);
storage_delete(mi->sm->st, "privacy-default", jid_user(jid), NULL);
}
int privacy_init(mod_instance_t mi, char *arg) {
module_t mod = mi->mod;
if(mod->init) return 0;
mod->user_load = _privacy_user_load;
mod->in_router = _privacy_in_router;
mod->out_router = _privacy_out_router;
mod->in_sess = _privacy_in_sess;
mod->user_delete = _privacy_user_delete;
feature_register(mod->mm->sm, uri_PRIVACY);
return 0;
}