#include "sm.h"
static char *_iq_vcard_map[] = {
"FN", "fn",
"NICKNAME", "nickname",
"URL", "url",
"TEL/NUMBER", "tel",
"EMAIL/USERID", "email",
"TITLE", "title",
"ROLE", "role",
"BDAY", "bday",
"DESC", "desc",
"N/GIVEN", "n-given",
"N/FAMILY", "n-family",
"ADR/STREET", "adr-street",
"ADR/EXTADD", "adr-extadd",
"ADR/LOCALITY", "adr-locality",
"ADR/REGION", "adr-region",
"ADR/PCODE", "adr-pcode",
"ADR/CTRY", "adr-country",
"ORG/ORGNAME", "org-orgname",
"ORG/ORGUNIT", "org-orgunit",
"PHOTO/TYPE", "photo-type",
"PHOTO/BINVAL", "photo-binval",
NULL, NULL
};
const max_vcard_field_length = 32768;
static os_t _iq_vcard_to_object(pkt_t pkt) {
os_t os;
os_object_t o;
int i = 0, elem;
char *vkey, *dkey, *vskey, ekey[10], cdata[max_vcard_field_length];
log_debug(ZONE, "building object from packet");
os = os_new();
o = os_object_new(os);
while(_iq_vcard_map[i] != NULL) {
vkey = _iq_vcard_map[i];
dkey = _iq_vcard_map[i + 1];
i += 2;
vskey = strchr(vkey, '/');
if(vskey == NULL) {
vskey = vkey;
elem = 2;
} else {
sprintf(ekey, "%.*s", vskey - vkey, vkey);
elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
if(elem < 0)
continue;
vskey++;
}
elem = nad_find_elem(pkt->nad, elem, NAD_ENS(pkt->nad, 2), vskey, 1);
if(elem < 0 || NAD_CDATA_L(pkt->nad, elem) == 0)
continue;
log_debug(ZONE, "extracted vcard key %s val '%.*s' for db key %s", vkey, NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem), dkey);
snprintf(cdata, max_vcard_field_length, "%.*s", NAD_CDATA_L(pkt->nad, elem), NAD_CDATA(pkt->nad, elem));
cdata[max_vcard_field_length - 1] = '\0';
os_object_put(o, dkey, cdata, os_type_STRING);
}
return os;
}
static pkt_t _iq_vcard_to_pkt(sm_t sm, os_t os) {
pkt_t pkt;
os_object_t o;
int i = 0, elem;
char *vkey, *dkey, *vskey, ekey[10], *dval;
log_debug(ZONE, "building packet from object");
pkt = pkt_create(sm, "iq", "result", NULL, NULL);
nad_append_elem(pkt->nad, nad_add_namespace(pkt->nad, "vcard-temp", NULL), "vCard", 2);
if(!os_iter_first(os))
return pkt;
o = os_iter_object(os);
while(_iq_vcard_map[i] != NULL) {
vkey = _iq_vcard_map[i];
dkey = _iq_vcard_map[i + 1];
i += 2;
if(!os_object_get_str(os, o, dkey, &dval))
continue;
vskey = strchr(vkey, '/');
if(vskey == NULL) {
vskey = vkey;
elem = 2;
} else {
sprintf(ekey, "%.*s", vskey - vkey, vkey);
elem = nad_find_elem(pkt->nad, 2, NAD_ENS(pkt->nad, 2), ekey, 1);
if(elem < 0)
elem = nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), ekey, 3);
vskey++;
}
log_debug(ZONE, "extracted dbkey %s val '%s' for vcard key %s", dkey, dval, vkey);
nad_append_elem(pkt->nad, NAD_ENS(pkt->nad, 2), vskey, pkt->nad->elems[elem].depth + 1);
nad_append_cdata(pkt->nad, dval, strlen(dval), pkt->nad->elems[elem].depth + 2);
}
return pkt;
}
static mod_ret_t _iq_vcard_in_sess(mod_instance_t mi, sess_t sess, pkt_t pkt) {
os_t os;
st_ret_t ret;
pkt_t result;
if(pkt->to != NULL || (pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
return mod_PASS;
if(pkt->type == pkt_IQ) {
ret = storage_get(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, &os);
switch(ret) {
case st_FAILED:
return -stanza_err_INTERNAL_SERVER_ERROR;
case st_NOTIMPL:
return -stanza_err_FEATURE_NOT_IMPLEMENTED;
case st_NOTFOUND:
nad_set_attr(pkt->nad, 1, -1, "type", "result", 6);
nad_set_attr(pkt->nad, 1, -1, "to", NULL, 0);
nad_set_attr(pkt->nad, 1, -1, "from", NULL, 0);
pkt_sess(pkt, sess);
return mod_HANDLED;
case st_SUCCESS:
result = _iq_vcard_to_pkt(sess->user->sm, os);
os_free(os);
nad_set_attr(result->nad, 1, -1, "type", "result", 6);
pkt_id(pkt, result);
pkt_sess(result, sess);
pkt_free(pkt);
return mod_HANDLED;
}
pkt_free(pkt);
return mod_HANDLED;
}
os = _iq_vcard_to_object(pkt);
ret = storage_replace(sess->user->sm->st, "vcard", jid_user(sess->jid), NULL, os);
os_free(os);
switch(ret) {
case st_FAILED:
return -stanza_err_INTERNAL_SERVER_ERROR;
case st_NOTIMPL:
return -stanza_err_FEATURE_NOT_IMPLEMENTED;
default:
result = pkt_create(sess->user->sm, "iq", "result", NULL, NULL);
pkt_id(pkt, result);
pkt_sess(result, sess);
pkt_free(pkt);
return mod_HANDLED;
}
pkt_free(pkt);
return mod_HANDLED;
}
static mod_ret_t _iq_vcard_pkt_user(mod_instance_t mi, user_t user, pkt_t pkt) {
os_t os;
st_ret_t ret;
pkt_t result;
if((pkt->type != pkt_IQ && pkt->type != pkt_IQ_SET) || pkt->ns != ns_VCARD)
return mod_PASS;
if(pkt->type == pkt_IQ_SET)
return -stanza_err_FORBIDDEN;
ret = storage_get(user->sm->st, "vcard", jid_user(user->jid), NULL, &os);
switch(ret) {
case st_FAILED:
return -stanza_err_INTERNAL_SERVER_ERROR;
case st_NOTIMPL:
return -stanza_err_FEATURE_NOT_IMPLEMENTED;
case st_NOTFOUND:
return -stanza_err_ITEM_NOT_FOUND;
case st_SUCCESS:
result = _iq_vcard_to_pkt(user->sm, os);
os_free(os);
result->to = jid_dup(pkt->from);
result->from = jid_dup(pkt->to);
nad_set_attr(result->nad, 1, -1, "to", jid_full(result->to), 0);
nad_set_attr(result->nad, 1, -1, "from", jid_full(result->from), 0);
pkt_id(pkt, result);
pkt_router(result);
pkt_free(pkt);
return mod_HANDLED;
}
pkt_free(pkt);
return mod_HANDLED;
}
static void _iq_vcard_user_delete(mod_instance_t mi, jid_t jid) {
log_debug(ZONE, "deleting vcard for %s", jid_user(jid));
storage_delete(mi->sm->st, "vcard", jid_user(jid), NULL);
}
int iq_vcard_init(mod_instance_t mi, char *arg) {
module_t mod = mi->mod;
if(mod->init) return 0;
mod->in_sess = _iq_vcard_in_sess;
mod->pkt_user = _iq_vcard_pkt_user;
mod->user_delete = _iq_vcard_user_delete;
feature_register(mod->mm->sm, "vcard-temp");
return 0;
}