#include "jabberd.h"
extern pool jabberd__runtime;
int deliver__flag=0;
pth_msgport_t deliver__mp=NULL;
typedef struct deliver_mp_st
{
pth_message_t head;
instance i;
dpacket p;
} _deliver_msg,*deliver_msg;
typedef struct ilist_struct
{
instance i;
struct ilist_struct *next;
} *ilist, _ilist;
ilist ilist_add(ilist il, instance i)
{
ilist cur, ilnew;
for(cur = il; cur != NULL; cur = cur->next)
if(cur->i == i)
return cur;
ilnew = pmalloco(i->p, sizeof(_ilist));
ilnew->i = i;
ilnew->next = il;
return ilnew;
}
ilist ilist_rem(ilist il, instance i)
{
ilist cur;
if(il == NULL) return NULL;
if(il->i == i) return il->next;
for(cur = il; cur->next != NULL; cur = cur->next)
if(cur->next->i == i)
{
cur->next = cur->next->next;
return il;
}
return il;
}
HASHTABLE deliver__hnorm = NULL;
HASHTABLE deliver__hxdb = NULL;
HASHTABLE deliver__hlog = NULL;
HASHTABLE deliver__ns = NULL;
HASHTABLE deliver__logtype = NULL;
ilist deliver__all = NULL;
instance deliver__uplink = NULL;
HASHTABLE deliver_hashtable(ptype type)
{
switch(type)
{
case p_LOG:
return deliver__hlog;
case p_XDB:
return deliver__hxdb;
default:
return deliver__hnorm;
}
}
ilist deliver_hashmatch(HASHTABLE ht, char *key)
{
ilist l;
l = ghash_get(ht, key);
if(l == NULL)
{
l = ghash_get(ht, "*");
}
return l;
}
instance deliver_intersect(ilist a, ilist b)
{
ilist cur = NULL, cur2;
instance i = NULL;
if(a == NULL)
cur = b;
if(b == NULL)
cur = a;
if(cur != NULL)
{
if(cur->next != NULL)
return NULL;
else
return cur->i;
}
for(cur = a; cur != NULL; cur = cur->next)
{
for(cur2 = b; cur2 != NULL; cur2 = cur2->next)
{
if(cur->i == cur2->i)
{
if(i != NULL)
return NULL;
i = cur->i;
}
}
}
if(i == NULL)
return deliver__uplink;
return i;
}
void deliver_internal(dpacket p, instance i)
{
xmlnode x;
char *ns = xmlnode_get_attrib(p->x, "ns");
log_debug(ZONE,"@-internal processing %s",xmlnode2str(p->x));
if(j_strcmp(p->id->user,"config") == 0)
{
for(x = xmlnode_get_firstchild(i->x); x != NULL; x = xmlnode_get_nextsibling(x))
{
if(j_strcmp(xmlnode_get_attrib(x,"xmlns"),ns) != 0)
continue;
xmlnode_insert_tag_node(p->x, x);
}
xmlnode_put_attrib(p->x,"type","result");
jutil_tofrom(p->x);
p->type = p_NORM;
deliver_instance(i, p);
return;
}
if(j_strcmp(p->id->user,"host") == 0)
{
register_instance(i,p->id->resource);
return;
}
if(j_strcmp(p->id->user,"unhost") == 0)
{
unregister_instance(i,p->id->resource);
return;
}
}
void register_instance(instance i, char *host)
{
ilist l;
HASHTABLE ht;
log_debug(ZONE,"Registering %s with instance %s",host,i->id);
if(i->type == p_XDB && deliver__ns != NULL && xmlnode_get_tag(i->x, "ns") == NULL)
{
fprintf(stderr, "Configuration Error! If <ns> is used in any xdb section, it must be used in all sections for correct packet routing.");
exit(1);
}
if(i->type == p_LOG && deliver__logtype != NULL && xmlnode_get_tag(i->x, "logtype") == NULL)
{
fprintf(stderr, "Configuration Error! If <logtype> is used in any log section, it must be used in all sections for correct packet routing.");
exit(1);
}
ht = deliver_hashtable(i->type);
l = ghash_get(ht, host);
l = ilist_add(l, i);
ghash_put(ht, pstrdup(i->p,host), (void *)l);
}
void unregister_instance(instance i, char *host)
{
ilist l;
HASHTABLE ht;
log_debug(ZONE,"Unregistering %s with instance %s",host,i->id);
ht = deliver_hashtable(i->type);
l = ghash_get(ht, host);
l = ilist_rem(l, i);
if(l == NULL)
ghash_remove(ht, host);
else
ghash_put(ht, pstrdup(i->p,host), (void *)l);
}
result deliver_config_host(instance i, xmlnode x, void *arg)
{
char *host;
int c;
if(i == NULL)
return r_PASS;
host = xmlnode_get_data(x);
if(host == NULL)
{
register_instance(i, "*");
return r_DONE;
}
for(c = 0; host[c] != '\0'; c++)
if(isspace((int)host[c]))
{
xmlnode_put_attrib(x,"error","The host tag contains illegal whitespace.");
return r_ERR;
}
register_instance(i, host);
return r_DONE;
}
result deliver_config_ns(instance i, xmlnode x, void *arg)
{
ilist l;
char *ns, star[] = "*";
if(i == NULL)
return r_PASS;
if(i->type != p_XDB)
return r_ERR;
ns = xmlnode_get_data(x);
if(ns == NULL)
ns = pstrdup(xmlnode_pool(x),star);
log_debug(ZONE,"Registering namespace %s with instance %s",ns,i->id);
if(deliver__ns == NULL)
deliver__ns = ghash_create_pool(jabberd__runtime, 401,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
l = ghash_get(deliver__ns, ns);
l = ilist_add(l, i);
ghash_put(deliver__ns, ns, (void *)l);
return r_DONE;
}
result deliver_config_logtype(instance i, xmlnode x, void *arg)
{
ilist l;
char *type, star[] = "*";
if(i == NULL)
return r_PASS;
if(i->type != p_LOG)
return r_ERR;
type = xmlnode_get_data(x);
if(type == NULL)
type = pstrdup(xmlnode_pool(x),star);
log_debug(ZONE,"Registering logtype %s with instance %s",type,i->id);
if(deliver__logtype == NULL)
deliver__logtype = ghash_create_pool(jabberd__runtime, 401,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
l = ghash_get(deliver__logtype, type);
l = ilist_add(l, i);
ghash_put(deliver__logtype, type, (void *)l);
return r_DONE;
}
result deliver_config_uplink(instance i, xmlnode x, void *arg)
{
if(i == NULL)
return r_PASS;
if(deliver__uplink != NULL)
return r_ERR;
deliver__uplink = i;
return r_DONE;
}
result deliver_null(instance i, dpacket p, void* arg)
{
pool_free(p->p);
return r_DONE;
}
result deliver_config_null(instance i, xmlnode x, void *arg)
{
if(i == NULL)
return r_PASS;
register_phandler(i, o_DELIVER, deliver_null, NULL);
return r_DONE;
}
void deliver(dpacket p, instance i)
{
ilist a, b;
if(deliver__flag == 1 && p == NULL && i == NULL)
{
deliver_msg d;
while((d=(deliver_msg)pth_msgport_get(deliver__mp))!=NULL)
{
deliver(d->p,d->i);
}
pth_msgport_destroy(deliver__mp);
deliver__mp = NULL;
deliver__flag = -1;
}
if (p == NULL)
return;
if(p->type == p_XDB && *(p->host) == '-')
{
deliver_internal(p, i);
return;
}
if(deliver__flag == 0)
{
deliver_msg d = pmalloco(xmlnode_pool(p->x) ,sizeof(_deliver_msg));
if(deliver__mp == NULL)
deliver__mp = pth_msgport_create("deliver__");
d->i = i;
d->p = p;
pth_msgport_put(deliver__mp, (void*)d);
return;
}
log_debug(ZONE,"DELIVER %d:%s %s",p->type,p->host,xmlnode2str(p->x));
b = NULL;
a = deliver_hashmatch(deliver_hashtable(p->type), p->host);
if(p->type == p_XDB)
b = deliver_hashmatch(deliver__ns, xmlnode_get_attrib(p->x,"ns"));
else if(p->type == p_LOG)
b = deliver_hashmatch(deliver__logtype, xmlnode_get_attrib(p->x,"type"));
deliver_instance(deliver_intersect(a, b), p);
}
instance deliver_hostcheck(char *host)
{
ilist l;
if(host == NULL) return NULL;
if((l = deliver_hashmatch(deliver__hnorm,host)) == NULL || l->next != NULL) return NULL;
return l->i;
}
void deliver_init(void)
{
deliver__hnorm = ghash_create_pool(jabberd__runtime, 401,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
deliver__hlog = ghash_create_pool(jabberd__runtime, 401,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
deliver__hxdb = ghash_create_pool(jabberd__runtime, 401,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
register_config("host",deliver_config_host,NULL);
register_config("ns",deliver_config_ns,NULL);
register_config("logtype",deliver_config_logtype,NULL);
register_config("uplink",deliver_config_uplink,NULL);
register_config("null",deliver_config_null,NULL);
}
void register_phandler(instance id, order o, phandler f, void *arg)
{
handel newh, h1, last;
pool p;
p = pool_new();
newh = pmalloc_x(p, sizeof(_handel), 0);
newh->p = p;
newh->f = f;
newh->arg = arg;
newh->o = o;
if(id->hds == NULL)
{
id->hds = newh;
return;
}
switch(o)
{
case o_PRECOND:
newh->next = id->hds;
id->hds = newh;
break;
case o_COND:
h1 = id->hds;
last = NULL;
while(h1->o < o_PREDELIVER)
{
last = h1;
h1 = h1->next;
if(h1 == NULL)
break;
}
if(last == NULL)
{
newh->next = h1;
id->hds = newh;
}
else if(h1 == NULL)
{
last->next = newh;
}
else
{
newh->next = h1;
last->next = newh;
}
break;
case o_PREDELIVER:
h1 = id->hds;
last = NULL;
while(h1->o < o_DELIVER)
{
last = h1;
h1 = h1->next;
if(h1 == NULL)
break;
}
if(last == NULL)
{
newh->next = h1;
id->hds = newh;
}
else if(h1 == NULL)
{
last->next = newh;
}
else
{
newh->next = h1;
last->next = newh;
}
break;
case o_DELIVER:
for(h1 = id->hds; h1->next != NULL; h1 = h1->next);
h1->next = newh;
break;
default:
;
}
}
void deliver_fail(dpacket p, char *err)
{
terror t;
char message[MAX_LOG_SIZE];
log_debug(ZONE,"delivery failed (%s)",err);
if(p==NULL) return;
switch(p->type)
{
case p_LOG:
snprintf(message, MAX_LOG_SIZE, "WARNING! Logging Failed: %s\n",xmlnode2str(p->x));
fprintf(stderr, "%s\n", message);
pool_free(p->p);
break;
case p_XDB:
log_warn(p->host,"dropping a %s xdb request to %s for %s",xmlnode_get_attrib(p->x,"type"),xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"ns"));
case p_ROUTE:
if(j_strcmp(xmlnode_get_attrib(p->x,"type"),"error") == 0)
{
log_warn(p->host,"dropping a routed packet to %s from %s: %s",xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"from"),err);
pool_free(p->p);
}else{
log_notice(p->host,"bouncing a routed packet to %s from %s: %s",xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"from"),err);
jutil_tofrom(p->x);
xmlnode_put_attrib(p->x,"type","error");
xmlnode_put_attrib(p->x,"error",err);
deliver(dpacket_new(p->x),NULL);
}
break;
case p_NORM:
if(j_strcmp(xmlnode_get_attrib(p->x,"type"),"error") == 0)
{
log_warn(p->host,"dropping a packet to %s from %s: %s",xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"from"),err);
pool_free(p->p);
}else{
log_notice(p->host,"bouncing a packet to %s from %s: %s",xmlnode_get_attrib(p->x,"to"),xmlnode_get_attrib(p->x,"from"),err);
if(err == NULL)
{
jutil_error(p->x,TERROR_EXTERNAL);
}else{
t.code = 502;
t.msg[0] = '\0';
strcat(t.msg,err);
jutil_error(p->x,t);
}
deliver(dpacket_new(p->x),NULL);
}
break;
default:
;
}
}
void deliver_instance(instance i, dpacket p)
{
handel h, hlast;
result r;
dpacket pig = p;
if(i == NULL)
{
deliver_fail(p, "Unable to deliver, destination unknown");
return;
}
log_debug(ZONE,"delivering to instance '%s'",i->id);
hlast = h = i->hds;
while(h != NULL)
{
if(h->o == o_DELIVER && h->next != NULL)
pig = dpacket_copy(p);
if((r = (h->f)(i,p,h->arg)) == r_ERR)
{
deliver_fail(p, "Internal Delivery Error");
break;
}
if(h->o != o_DELIVER && r == r_DONE)
break;
if(h->o == o_COND && r == r_LAST)
break;
if(h->o == o_DELIVER && h->next != NULL)
{
if(r == r_DONE)
p = pig;
else
pool_free(pig->p);
}
if(r == r_UNREG)
{
if(h == i->hds)
{
i->hds = h->next;
pool_free(h->p);
hlast = h = i->hds;
}else{
hlast->next = h->next;
pool_free(h->p);
h = hlast->next;
}
continue;
}
hlast = h;
h = h->next;
}
}
dpacket dpacket_new(xmlnode x)
{
dpacket p;
char *str;
if(x == NULL)
return NULL;
p = pmalloc_x(xmlnode_pool(x),sizeof(_dpacket),0);
p->x = x;
p->p = xmlnode_pool(x);
p->type = p_NORM;
if(*(xmlnode_get_name(x)) == 'r')
p->type = p_ROUTE;
else if(*(xmlnode_get_name(x)) == 'x')
p->type = p_XDB;
else if(*(xmlnode_get_name(x)) == 'l')
p->type = p_LOG;
if(p->type == p_XDB && (str = xmlnode_get_attrib(p->x,"type")) != NULL && (*str == 'r' || *str == 'e' ))
p->type = p_NORM;
if(p->type == p_LOG)
p->id = jid_new(p->p, xmlnode_get_attrib(x, "from"));
else
p->id = jid_new(p->p, xmlnode_get_attrib(x, "to"));
if(p->id == NULL)
{
log_warn(NULL,"Packet Delivery Failed, invalid packet, dropping %s",xmlnode2str(x));
xmlnode_free(x);
return NULL;
}
switch(p->type)
{
case p_LOG:
if(xmlnode_get_attrib(x,"type")==NULL)
p=NULL;
break;
case p_XDB:
if(xmlnode_get_attrib(x,"ns") == NULL)
p=NULL;
case p_NORM:
if(xmlnode_get_attrib(x,"to")==NULL||xmlnode_get_attrib(x,"from")==NULL)
p=NULL;
break;
case p_ROUTE:
if(xmlnode_get_attrib(x,"to")==NULL)
p=NULL;
break;
case p_NONE:
p=NULL;
break;
}
if(p==NULL)
{
log_warn(NULL,"Packet Delivery Failed, invalid packet, dropping %s",xmlnode2str(x));
xmlnode_free(x);
return NULL;
}
p->host = p->id->server;
return p;
}
dpacket dpacket_copy(dpacket p)
{
dpacket p2;
p2 = dpacket_new(xmlnode_dup(p->x));
return p2;
}