#include "jabberd.h"
typedef struct dcfg_struct
{
int match;
char *folder;
HASHTABLE cache;
} *dcfg, _dcfg;
typedef struct dfile_struct
{
_instance i;
char *file;
int mtime;
jid id;
} *dfile, _dfile;
void *load_symbol(char *func, char *file);
result base_exec_config(instance id, xmlnode x, void *arg);
result base_dynamic_deliver(instance i, dpacket p, void* arg)
{
dcfg d = (dcfg)arg;
char *match, *file;
struct stat m;
dfile f;
pool pnew;
void (*so)(instance,xmlnode) = NULL;
xmlnode x;
if(d->match)
match = p->id->resource;
else
match = p->id->user;
f = ghash_get(d->cache, match);
if(f == NULL || stat(f->file, &m) != 0 || m.st_mtime > f->mtime)
{
file = spools(p->p, d->folder, "/", match, p->p);
if(stat(file,&m) != 0)
{
file = spools(p->p, d->folder, "/", match, ".so", p->p);
if(stat(file,&m) != 0)
{
log_alert(p->host,"Unable to locate dynamic handler %s in folder %s",match,d->folder);
deliver_fail(p,"Unable to locate handler");
return r_DONE;
}
if((so = load_symbol(match, file)) == NULL)
{
log_alert(p->host,"Unable to load dynamic handler %s in file %s",match,file);
deliver_fail(p,"Unable to start handler");
return r_DONE;
}
}else if(!(S_IXUSR & m.st_mode)){
log_alert(p->host,"Execuate bit is not set on dynamic handler for %s",file);
deliver_fail(p,"Unable to start handler");
return r_DONE;
}
if(f != NULL)
pool_free(f->i.p);
pnew = pool_new();
f = pmalloco(pnew, sizeof(_dfile));
f->file = pstrdup(pnew, file);
f->mtime = m.st_mtime;
f->id = jid_new(pnew, jid_full(p->id));
if(d->match)
{
jid_set(f->id,NULL,JID_USER);
ghash_put(d->cache, f->id->resource, (void *)f);
}else{
jid_set(f->id,NULL,JID_RESOURCE);
ghash_put(d->cache, f->id->user, (void *)f);
}
f->i.p = pnew;
f->i.id = jid_full(f->id);
f->i.type = p_NORM;
f->i.x = i->x;
if(so != NULL)
{
(so)(&(f->i), NULL);
}else{
x = xmlnode_new_tag_pool(f->i.p,"exec");
xmlnode_insert_cdata(x,f->file,-1);
base_exec_config(&(f->i), x, NULL);
}
if(f->i.hds == NULL)
{
log_alert(p->host,"Dynamic initialization failed for %s",file);
deliver_fail(p,"Unable to start handler");
pool_free(pnew);
ghash_remove(d->cache, match);
return r_DONE;
}
}
deliver_instance(&(f->i), p);
return r_DONE;
}
result base_dynamic_config(instance i, xmlnode x, void *arg)
{
dcfg d;
struct stat m;
if(i == NULL)
{
if(xmlnode_get_tag_data(x,"folder") == NULL || stat(xmlnode_get_tag_data(x,"folder"),&m) != 0 || !S_ISDIR(m.st_mode))
{
xmlnode_put_attrib(x, "error", "<dynamic> must contain a <folder>/path/to/folder</folder> with a valid folder in it");
return r_ERR;
}
return r_PASS;
}
log_debug(ZONE, "base_dynamic configuring instance %s", i->id);
if(i->type != p_NORM)
{
log_alert(NULL, "ERROR in instance %s: <dynamic>..</dynamic> element only allowed in service sections", i->id);
return r_ERR;
}
d = pmalloco(i->p, sizeof(_dcfg));
d->folder = xmlnode_get_tag_data(x,"folder");
d->cache = ghash_create(j_atoi(xmlnode_get_tag_data(x,"maxfiles"),101),(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
if(j_strcmp(xmlnode_get_tag_data(x,"match"),"resource") == 0)
d->match = 1;
register_phandler(i, o_DELIVER, base_dynamic_deliver, (void*)d);
return r_DONE;
}
void base_dynamic(void)
{
log_debug(ZONE,"base_dynamic loading...");
register_config("dynamic",base_dynamic_config,NULL);
}