#include "sm.h"
pkt_t pkt_error(pkt_t pkt, int err) {
if(pkt == NULL) return NULL;
if(pkt->type & pkt_ERROR) {
log_debug(ZONE, "dropping error pkt");
pkt_free(pkt);
return NULL;
}
stanza_error(pkt->nad, 1, err);
pkt_tofrom(pkt);
pkt->type |= pkt_ERROR;
log_debug(ZONE, "processed %d error pkt", err);
return pkt;
}
pkt_t pkt_tofrom(pkt_t pkt) {
jid_t tmp;
if(pkt == NULL) return NULL;
tmp = pkt->from;
pkt->from = pkt->to;
pkt->to = tmp;
if(pkt->to != NULL)
nad_set_attr(pkt->nad, 1, -1, "to", jid_full(pkt->to), 0);
if(pkt->from != NULL)
nad_set_attr(pkt->nad, 1, -1, "from", jid_full(pkt->from), 0);
return pkt;
}
pkt_t pkt_dup(pkt_t pkt, const char *to, const char *from) {
pkt_t pnew;
if(pkt == NULL) return NULL;
pnew = (pkt_t) malloc(sizeof(struct pkt_st));
memset(pnew, 0, sizeof(struct pkt_st));
pnew->sm = pkt->sm;
pnew->type = pkt->type;
pnew->nad = nad_copy(pkt->nad);
if(to != NULL) {
pnew->to = jid_new(pkt->sm->pc, to, -1);
nad_set_attr(pnew->nad, 1, -1, "to", jid_full(pnew->to), 0);
} else if(pkt->to != NULL)
pnew->to = jid_dup(pkt->to);
if(from != NULL) {
pnew->from = jid_new(pkt->sm->pc, from, -1);
nad_set_attr(pnew->nad, 1, -1, "from", jid_full(pnew->from), 0);
} else if(pkt->from != NULL)
pnew->from = jid_dup(pkt->from);
log_debug(ZONE, "duplicated packet");
return pnew;
}
pkt_t pkt_new(sm_t sm, nad_t nad) {
pkt_t pkt;
int ns, attr, elem;
char pri[20];
log_debug(ZONE, "creating new packet");
ns = nad_find_namespace(nad, 0, uri_COMPONENT, NULL);
if(ns < 0) {
log_debug(ZONE, "packet not in component namespace");
return NULL;
}
pkt = (pkt_t) malloc(sizeof(struct pkt_st));
memset(pkt, 0, sizeof(struct pkt_st));
pkt->sm = sm;
pkt->nad = nad;
if(NAD_ENAME_L(nad, 0) == 5 && strncmp("route", NAD_ENAME(nad, 0), 5) == 0) {
if((attr = nad_find_attr(nad, 0, -1, "to", NULL)) >= 0)
pkt->rto = jid_new(sm->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
if((attr = nad_find_attr(nad, 0, -1, "from", NULL)) >= 0)
pkt->rfrom = jid_new(sm->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
attr = nad_find_attr(nad, 0, -1, "type", NULL);
if(attr < 0)
pkt->rtype = route_UNICAST;
else if(NAD_AVAL_L(nad, attr) == 9 && strncmp("broadcast", NAD_AVAL(nad, attr), 9) == 0)
pkt->rtype = route_BROADCAST;
if(nad_find_attr(nad, 0, -1, "error", NULL) >= 0)
pkt->rtype |= route_ERROR;
ns = nad_find_namespace(nad, 1, uri_CLIENT, NULL);
if(ns >= 0) {
if((attr = nad_find_attr(pkt->nad, 1, -1, "to", NULL)) >= 0 && NAD_AVAL_L(pkt->nad, attr) > 0)
pkt->to = jid_new(sm->pc, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
if((attr = nad_find_attr(pkt->nad, 1, -1, "from", NULL)) >= 0 && NAD_AVAL_L(pkt->nad, attr) > 0)
pkt->from = jid_new(sm->pc, NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
attr = nad_find_attr(pkt->nad, 1, -1, "type", NULL);
if(NAD_ENAME_L(pkt->nad, 1) == 7 && strncmp("message", NAD_ENAME(pkt->nad, 1), 7) == 0) {
pkt->type = pkt_MESSAGE;
if(attr >= 0 && NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("error", NAD_AVAL(pkt->nad, attr), 5) == 0)
pkt->type |= pkt_ERROR;
return pkt;
}
if(NAD_ENAME_L(pkt->nad, 1) == 8 && strncmp("presence", NAD_ENAME(pkt->nad, 1), 8) == 0) {
pkt->type = pkt_PRESENCE;
if(attr >= 0) {
if(NAD_AVAL_L(pkt->nad, attr) == 11 && strncmp("unavailable", NAD_AVAL(pkt->nad, attr), 11) == 0)
pkt->type = pkt_PRESENCE_UN;
else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("probe", NAD_AVAL(pkt->nad, attr), 5) == 0)
pkt->type = pkt_PRESENCE_PROBE;
else if(NAD_AVAL_L(pkt->nad, attr) == 9 && strncmp("invisible", NAD_AVAL(pkt->nad, attr), 9) == 0)
pkt->type = pkt_PRESENCE_INVIS;
else if(NAD_AVAL_L(pkt->nad, attr) == 9 && strncmp("subscribe", NAD_AVAL(pkt->nad, attr), 9) == 0)
pkt->type = pkt_S10N;
else if(NAD_AVAL_L(pkt->nad, attr) == 10 && strncmp("subscribed", NAD_AVAL(pkt->nad, attr), 10) == 0)
pkt->type = pkt_S10N_ED;
else if(NAD_AVAL_L(pkt->nad, attr) == 11 && strncmp("unsubscribe", NAD_AVAL(pkt->nad, attr), 11) == 0)
pkt->type = pkt_S10N_UN;
else if(NAD_AVAL_L(pkt->nad, attr) == 12 && strncmp("unsubscribed", NAD_AVAL(pkt->nad, attr), 12) == 0)
pkt->type = pkt_S10N_UNED;
else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("error", NAD_AVAL(pkt->nad, attr), 5) == 0)
pkt->type = pkt_PRESENCE | pkt_ERROR;
}
if((elem = nad_find_elem(pkt->nad, 1, NAD_ENS(pkt->nad, 1), "priority", 1)) < 0)
return pkt;
if(NAD_CDATA_L(pkt->nad, elem) <= 0 || NAD_CDATA_L(pkt->nad, elem) > 19)
return pkt;
memcpy(pri, NAD_CDATA(pkt->nad, elem), NAD_CDATA_L(pkt->nad, elem));
pri[NAD_CDATA_L(pkt->nad, elem)] = '\0';
pkt->pri = atoi(pri);
if(pkt->pri > 127) pkt->pri = 127;
if(pkt->pri < -128) pkt->pri = -128;
return pkt;
}
if(NAD_ENAME_L(pkt->nad, 1) == 2 && strncmp("iq", NAD_ENAME(pkt->nad, 1), 2) == 0) {
pkt->type = pkt_IQ;
if(attr >= 0) {
if(NAD_AVAL_L(pkt->nad, attr) == 3 && strncmp("set", NAD_AVAL(pkt->nad, attr), 3) == 0)
pkt->type = pkt_IQ_SET;
else if(NAD_AVAL_L(pkt->nad, attr) == 6 && strncmp("result", NAD_AVAL(pkt->nad, attr), 6) == 0)
pkt->type = pkt_IQ_RESULT;
else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("error", NAD_AVAL(pkt->nad, attr), 5) == 0)
pkt->type = pkt_IQ | pkt_ERROR;
}
if(pkt->nad->ecur > 2 && (ns = NAD_ENS(pkt->nad, 2)) >= 0)
pkt->ns = (int) xhash_getx(pkt->sm->xmlns, NAD_NURI(pkt->nad, ns), NAD_NURI_L(pkt->nad, ns));
return pkt;
}
log_debug(ZONE, "unknown client namespace packet");
return pkt;
}
ns = nad_find_namespace(nad, 1, uri_SESSION, NULL);
if(ns >= 0) {
if(NAD_ENAME_L(pkt->nad, 1) == 7 && strncmp("session", NAD_ENAME(pkt->nad, 1), 7) == 0) {
attr = nad_find_attr(pkt->nad, 1, -1, "action", NULL);
if(attr >= 0) {
if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("start", NAD_AVAL(pkt->nad, attr), 5) >= 0)
pkt->type = pkt_SESS;
else if(NAD_AVAL_L(pkt->nad, attr) == 3 && strncmp("end", NAD_AVAL(pkt->nad, attr), 3) >= 0)
pkt->type = pkt_SESS_END;
else if(NAD_AVAL_L(pkt->nad, attr) == 6 && strncmp("create", NAD_AVAL(pkt->nad, attr), 6) >= 0)
pkt->type = pkt_SESS_CREATE;
else if(NAD_AVAL_L(pkt->nad, attr) == 6 && strncmp("delete", NAD_AVAL(pkt->nad, attr), 6) >= 0)
pkt->type = pkt_SESS_DELETE;
else if(NAD_AVAL_L(pkt->nad, attr) == 7 && strncmp("started", NAD_AVAL(pkt->nad, attr), 7) >= 0)
pkt->type = pkt_SESS | pkt_SESS_FAILED;
else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("ended", NAD_AVAL(pkt->nad, attr), 5) >= 0)
pkt->type = pkt_SESS_END | pkt_SESS_FAILED;
else if(NAD_AVAL_L(pkt->nad, attr) == 7 && strncmp("created", NAD_AVAL(pkt->nad, attr), 7) >= 0)
pkt->type = pkt_SESS_CREATE | pkt_SESS_FAILED;
else if(NAD_AVAL_L(pkt->nad, attr) == 7 && strncmp("deleted", NAD_AVAL(pkt->nad, attr), 7) >= 0)
pkt->type = pkt_SESS_DELETE | pkt_SESS_FAILED;
return pkt;
} else {
log_debug(ZONE, "missing action on session packet");
return pkt;
}
}
log_debug(ZONE, "unknown session namespace packet");
return pkt;
}
log_debug(ZONE, "unknown packet");
return pkt;
}
if(NAD_ENAME_L(nad, 0) == 8 && strncmp("presence", NAD_ENAME(nad, 0), 8) == 0) {
if(nad_find_attr(nad, 0, -1, "type", "unavailable") >= 0)
pkt->rtype = route_ADV_UN;
else
pkt->rtype = route_ADV;
attr = nad_find_attr(nad, 0, -1, "from", NULL);
if(attr >= 0)
pkt->from = jid_new(sm->pc, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
return pkt;
}
log_debug(ZONE, "invalid component packet");
free(pkt);
return NULL;
}
void pkt_free(pkt_t pkt) {
log_debug(ZONE, "freeing pkt");
if(pkt->rto != NULL) jid_free(pkt->rto);
if(pkt->rfrom != NULL) jid_free(pkt->rfrom);
if(pkt->to != NULL) jid_free(pkt->to);
if(pkt->from != NULL) jid_free(pkt->from);
if(pkt->nad != NULL) nad_free(pkt->nad);
free(pkt);
}
pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from) {
nad_t nad;
int ns;
nad = nad_new(sm->router->nad_cache);
ns = nad_add_namespace(nad, uri_COMPONENT, NULL);
nad_append_elem(nad, ns, "route", 0);
nad_add_namespace(nad, uri_SESSION, "sm");
ns = nad_add_namespace(nad, uri_CLIENT, NULL);
nad_append_elem(nad, ns, elem, 1);
if(type != NULL)
nad_append_attr(nad, -1, "type", type);
if(to != NULL)
nad_append_attr(nad, -1, "to", to);
if(from != NULL)
nad_append_attr(nad, -1, "from", from);
return pkt_new(sm, nad);
}
void pkt_id(pkt_t src, pkt_t dest) {
int attr;
attr = nad_find_attr(src->nad, 1, -1, "id", NULL);
if(attr >= 0)
nad_set_attr(dest->nad, 1, -1, "id", NAD_AVAL(src->nad, attr), NAD_AVAL_L(src->nad, attr));
else
nad_set_attr(dest->nad, 1, -1, "id", NULL, 0);
}
void pkt_router(pkt_t pkt) {
mod_ret_t ret;
int ns, scan;
if(pkt == NULL) return;
log_debug(ZONE, "delivering pkt to router");
if(pkt->to == NULL) {
log_debug(ZONE, "no to address on packet, unable to route");
pkt_free(pkt);
return;
}
if(pkt->rto != NULL)
jid_free(pkt->rto);
pkt->rto = jid_new(pkt->sm->pc, pkt->to->domain, -1);
if(pkt->rto == NULL) {
log_debug(ZONE, "invalid to address on packet, unable to route");
pkt_free(pkt);
return;
}
nad_set_attr(pkt->nad, 0, -1, "to", pkt->rto->domain, 0);
if(pkt->rfrom != NULL)
jid_free(pkt->rfrom);
pkt->rfrom = jid_new(pkt->sm->pc, pkt->sm->id, -1);
if(pkt->rfrom == NULL) {
log_debug(ZONE, "invalid from address on packet, unable to route");
pkt_free(pkt);
return;
}
nad_set_attr(pkt->nad, 0, -1, "from", pkt->rfrom->domain, 0);
ret = mm_out_router(pkt->sm->mm, pkt);
switch(ret) {
case mod_HANDLED:
return;
case mod_PASS:
ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL);
if(ns > 0) {
nad_set_attr(pkt->nad, 1, ns, "c2s", NULL, 0);
nad_set_attr(pkt->nad, 1, ns, "sm", NULL, 0);
if(pkt->nad->elems[1].ns == ns)
pkt->nad->elems[1].ns = pkt->nad->nss[ns].next;
else {
for(scan = pkt->nad->elems[1].ns; pkt->nad->nss[scan].next != -1 && pkt->nad->nss[scan].next != ns; scan = pkt->nad->nss[scan].next);
if(pkt->nad->nss[scan].next != -1)
pkt->nad->nss[scan].next = pkt->nad->nss[ns].next;
}
}
sx_nad_write(pkt->sm->router, pkt->nad);
pkt->nad = NULL;
pkt_free(pkt);
break;
default:
pkt_router(pkt_error(pkt, -ret));
break;
}
}
void pkt_sess(pkt_t pkt, sess_t sess) {
mod_ret_t ret;
if(pkt == NULL) return;
log_debug(ZONE, "delivering pkt to session %s", jid_full(sess->jid));
if(pkt->rto != NULL)
jid_free(pkt->rto);
pkt->rto = jid_new(pkt->sm->pc, sess->c2s, -1);
nad_set_attr(pkt->nad, 0, -1, "to", pkt->rto->domain, 0);
if(pkt->rfrom != NULL)
jid_free(pkt->rfrom);
pkt->rfrom = jid_new(pkt->sm->pc, pkt->sm->id, -1);
nad_set_attr(pkt->nad, 0, -1, "from", pkt->rfrom->domain, 0);
ret = mm_out_sess(pkt->sm->mm, sess, pkt);
switch(ret) {
case mod_HANDLED:
return;
case mod_PASS:
sess_route(sess, pkt);
break;
default:
pkt_router(pkt_error(pkt, -ret));
break;
}
}
void pkt_delay(pkt_t pkt, time_t t, const char *from) {
char timestamp[18];
int ns, elem;
datetime_out(t, dt_LEGACY, timestamp, 18);
ns = nad_add_namespace(pkt->nad, uri_DELAY, NULL);
elem = nad_insert_elem(pkt->nad, 1, ns, "x", NULL);
nad_set_attr(pkt->nad, elem, -1, "stamp", timestamp, 0);
if(from != NULL)
nad_set_attr(pkt->nad, elem, -1, "from", from, 0);
log_debug(ZONE, "added pkt delay stamp %s", timestamp);
}