#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "xmalloc.h"
#include "sieve_interface.h"
#include "script.h"
#include "tree.h"
#include "sieve.h"
#include "bytecode.h"
#include <assert.h>
#include <string.h>
struct bytecode_info
{
bytecode_t *data;
size_t scriptend;
size_t reallen;
};
static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t);
static int atleast(bytecode_info_t *arr, size_t len)
{
if(arr->reallen < len) {
arr->reallen = (len > arr->reallen * 2 ? len : arr->reallen * 2);
arr->data = xrealloc(arr->data, arr->reallen*sizeof(bytecode_t));
if(!arr->data)
{
return 0;
}
}
return 1;
}
static int bc_stringlist_generate(int codep, bytecode_info_t *retval,
stringlist_t *sl)
{
int len_codep = codep;
int strcount = 0;
stringlist_t *cur;
codep++;
if(!atleast(retval,codep+1))
return -1;
for(cur=sl; cur; cur=cur->next)
{
strcount++;
assert((cur->s)!=NULL);
if(!atleast(retval,codep+2))
return -1;
retval->data[codep++].len = strlen(cur->s);
retval->data[codep++].str = cur->s;
}
retval->data[len_codep].listlen = strcount;
return codep;
}
static int bc_testlist_generate(int codep, bytecode_info_t *retval,
testlist_t *tl)
{
int len_codep = codep;
int testcount = 0;
testlist_t *cur;
codep++;
if(!atleast(retval,codep+1))
return -1;
for(cur=tl; cur; cur=cur->next) {
int oldcodep = codep;
if(!atleast(retval,codep+1))
return -1;
testcount++;
codep = bc_test_generate(codep+1, retval, cur->t);
retval->data[oldcodep].jump = codep;
}
retval->data[len_codep].listlen = testcount;
return codep;
}
static int bc_relation_generate(int codep, bytecode_info_t *retval, int relat)
{
if (!atleast(retval, codep + 1)) return -1;
switch (relat)
{
case GT:
retval->data[codep++].value= B_GT;
break;
case GE:
retval->data[codep++].value= B_GE;
break;
case LT:
retval->data[codep++].value= B_LT;
break;
case LE:
retval->data[codep++].value= B_LE;
break;
case EQ:
retval->data[codep++].value= B_EQ;
break;
case NE:
retval->data[codep++].value= B_NE;
break;
default:
retval->data[codep++].value= -1;
break;
}
return codep;
}
static int bc_comparator_generate(int codep, bytecode_info_t *retval,
int comptag, int relat,
const char *comparator)
{
assert(retval != NULL);
if (!atleast(retval, codep + 1)) return -1;
switch (comptag) {
case IS:
retval->data[codep++].value = B_IS;
break;
case CONTAINS:
retval->data[codep++].value = B_CONTAINS;
break;
case MATCHES:
retval->data[codep++].value = B_MATCHES;
break;
#ifdef ENABLE_REGEX
case REGEX:
retval->data[codep++].value = B_REGEX;
break;
#endif
case COUNT:
retval->data[codep++].value = B_COUNT;
break;
case VALUE:
retval->data[codep++].value = B_VALUE;
break;
default:
return -1;
}
codep = bc_relation_generate(codep, retval, relat);
if (!atleast(retval, codep + 1)) return -1;
if (!strcmp (comparator, "i;octet"))
retval->data[codep++].value = B_OCTET;
else if (!strcmp (comparator, "i;ascii-casemap"))
retval->data[codep++].value = B_ASCIICASEMAP;
else if (!strcmp (comparator, "i;ascii-numeric"))
retval->data[codep++].value = B_ASCIINUMERIC;
return codep;
}
static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t)
{
if(!retval) return -1;
switch(t->type) {
case STRUE:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_TRUE;
break;
case SFALSE:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_FALSE;
break;
case NOT:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_NOT;
codep = bc_test_generate(codep, retval, t->u.t);
if (codep == -1) return -1;
break;
case SIZE:
if(!atleast(retval,codep+3)) return -1;
retval->data[codep++].op = BC_SIZE;
retval->data[codep++].value = (t->u.sz.t == OVER
? B_OVER : B_UNDER);
retval->data[codep++].value = t->u.sz.n;
break;
case EXISTS:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_EXISTS;
codep= bc_stringlist_generate(codep, retval, t->u.sl);
break;
case ANYOF:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_ANYOF;
codep=bc_testlist_generate(codep, retval, t->u.tl);
if (codep == -1) return -1;
break;
case ALLOF:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_ALLOF;
codep= bc_testlist_generate(codep, retval, t->u.tl);
if (codep == -1) return -1;
break;
case HEADER:
if(!atleast(retval,codep + 1)) return -1;
retval->data[codep++].op = BC_HEADER;
codep = bc_comparator_generate(codep, retval,
t->u.h.comptag,
t->u.h.relation,
t->u.h.comparator);
if (codep == -1) return -1;
codep = bc_stringlist_generate(codep, retval, t->u.h.sl);
if (codep == -1) return -1;
codep = bc_stringlist_generate(codep, retval, t->u.h.pl);
if (codep == -1) return -1;
break;
case ADDRESS:
case ENVELOPE:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = (t->type == ADDRESS)
? BC_ADDRESS : BC_ENVELOPE;
codep = bc_comparator_generate(codep, retval,t->u.ae.comptag,
t->u.ae.relation,
t->u.ae.comparator);
if (codep == -1) return -1;
if(!atleast(retval,codep+1)) return -1;
switch(t->u.ae.addrpart) {
case ALL:
retval->data[codep++].value = B_ALL;
break;
case LOCALPART:
retval->data[codep++].value = B_LOCALPART;
break;
case DOMAIN:
retval->data[codep++].value = B_DOMAIN;
break;
case USER:
retval->data[codep++].value = B_USER;
break;
case DETAIL:
retval->data[codep++].value = B_DETAIL;
break;
default:
return -1;
}
codep = bc_stringlist_generate(codep, retval, t->u.ae.sl);
if (codep == -1) return -1;
codep = bc_stringlist_generate(codep, retval, t->u.ae.pl);
if (codep == -1) return -1;
break;
case BODY:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = BC_BODY;
codep = bc_comparator_generate(codep, retval,t->u.b.comptag,
t->u.b.relation,
t->u.b.comparator);
if (codep == -1) return -1;
if(!atleast(retval,codep+2)) return -1;
switch(t->u.b.transform) {
case RAW:
retval->data[codep++].value = B_RAW;
break;
case TEXT:
retval->data[codep++].value = B_TEXT;
break;
case CONTENT:
retval->data[codep++].value = B_CONTENT;
break;
default:
return -1;
}
retval->data[codep++].value = t->u.b.offset;
codep = bc_stringlist_generate(codep, retval, t->u.b.content_types);
if (codep == -1) return -1;
codep = bc_stringlist_generate(codep, retval, t->u.b.pl);
if (codep == -1) return -1;
break;
default:
return -1;
}
return codep;
}
static int bc_action_generate(int codep, bytecode_info_t *retval,
commandlist_t *c)
{
int jumploc,baseloc;
if(!retval) return -1;
if (c==NULL)
{
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_NULL;
}
else
{
do {
switch(c->type) {
case STOP:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_STOP;
break;
case DISCARD:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_DISCARD;
break;
case KEEP:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_KEEP;
break;
case MARK:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_MARK;
break;
case UNMARK:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_UNMARK;
break;
case RETURN:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_RETURN;
break;
case DENOTIFY:
if(!atleast(retval,codep+6)) return -1;
retval->data[codep++].op = B_DENOTIFY;
switch(c->u.d.priority) {
case LOW:
retval->data[codep++].value = B_LOW;
break;
case NORMAL:
retval->data[codep++].value = B_NORMAL;
break;
case HIGH:
retval->data[codep++].value = B_HIGH;
break;
case ANY:
retval->data[codep++].value = B_ANY;
break;
default:
return -1;
}
switch(c->u.d.comptag) {
case IS:
retval->data[codep++].value = B_IS;
break;
case CONTAINS:
retval->data[codep++].value = B_CONTAINS;
break;
case MATCHES:
retval->data[codep++].value = B_MATCHES;
break;
#ifdef ENABLE_REGEX
case REGEX:
retval->data[codep++].value = B_REGEX;
break;
#endif
case ANY:
retval->data[codep++].value = B_ANY;
break;
default:
return -1;
}
codep = bc_relation_generate(codep, retval, c->u.d.relation);
if(c->u.d.pattern)
{
retval->data[codep++].len = strlen(c->u.d.pattern);
retval->data[codep++].str = c->u.d.pattern;
} else {
retval->data[codep++].len = -1;
retval->data[codep++].str = NULL;
}
break;
case REJCT:
if(!atleast(retval,codep+3)) return -1;
retval->data[codep++].op = B_REJECT;
retval->data[codep++].len = strlen(c->u.str);
retval->data[codep++].str = c->u.str;
break;
case FILEINTO:
if(!atleast(retval,codep+4)) return -1;
retval->data[codep++].op = B_FILEINTO;
retval->data[codep++].value = c->u.f.copy;
retval->data[codep++].len = strlen(c->u.f.folder);
retval->data[codep++].str = c->u.f.folder;
break;
case REDIRECT:
if(!atleast(retval,codep+4)) return -1;
retval->data[codep++].op = B_REDIRECT;
retval->data[codep++].value = c->u.r.copy;
retval->data[codep++].len = strlen(c->u.r.address);
retval->data[codep++].str = c->u.r.address;
break;
case ADDFLAG:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_ADDFLAG;
codep = bc_stringlist_generate(codep,retval,c->u.sl);
if(codep == -1) return -1;
break;
case SETFLAG:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_SETFLAG;
codep = bc_stringlist_generate(codep,retval,c->u.sl);
if(codep == -1) return -1;
break;
case REMOVEFLAG:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_REMOVEFLAG;
codep = bc_stringlist_generate(codep,retval,c->u.sl);
if(codep == -1) return -1;
break;
case NOTIFY:
if(!atleast(retval,codep+5)) return -1;
retval->data[codep++].op = B_NOTIFY;
retval->data[codep++].len = strlen(c->u.n.method);
retval->data[codep++].str = c->u.n.method;
if (c->u.n.id)
{
retval->data[codep++].len = strlen(c->u.n.id);
retval->data[codep++].str = c->u.n.id;
}
else
{
retval->data[codep++].len = -1;
retval->data[codep++].str = NULL;
}
codep = bc_stringlist_generate(codep,retval,c->u.n.options);
if(codep == -1) return -1;
if(!atleast(retval,codep+3)) return -1;
switch(c->u.n.priority) {
case LOW:
retval->data[codep++].value = B_LOW;
break;
case NORMAL:
retval->data[codep++].value = B_NORMAL;
break;
case HIGH:
retval->data[codep++].value = B_HIGH;
break;
case ANY:
retval->data[codep++].value = B_ANY;
break;
default:
return -1;
}
retval->data[codep++].len = strlen(c->u.n.message);
retval->data[codep++].str = c->u.n.message;
break;
case VACATION:
if(!atleast(retval,codep+1)) return -1;
retval->data[codep++].op = B_VACATION;
codep = bc_stringlist_generate(codep,retval,c->u.v.addresses);
if (codep == -1) return -1;
if (!atleast(retval,codep+2)) return -1;
if(c->u.v.subject) {
retval->data[codep++].len = strlen(c->u.v.subject);
retval->data[codep++].str = c->u.v.subject;
} else {
retval->data[codep++].len = -1;
retval->data[codep++].str = NULL;
}
if (!atleast(retval,codep+2)) return -1;
if(c->u.v.message) {
retval->data[codep++].len = strlen(c->u.v.message);
retval->data[codep++].str = c->u.v.message;
} else {
retval->data[codep++].len = -1;
retval->data[codep++].str = NULL;
}
if (!atleast(retval,codep+2)) return -1;
retval->data[codep++].value = c->u.v.days;
retval->data[codep++].value = c->u.v.mime;
if (!atleast(retval,codep+2)) return -1;
if(c->u.v.from) {
retval->data[codep++].len = strlen(c->u.v.from);
retval->data[codep++].str = c->u.v.from;
} else {
retval->data[codep++].len = -1;
retval->data[codep++].str = NULL;
}
if (!atleast(retval,codep+2)) return -1;
if(c->u.v.handle) {
retval->data[codep++].len = strlen(c->u.v.handle);
retval->data[codep++].str = c->u.v.handle;
} else {
retval->data[codep++].len = -1;
retval->data[codep++].str = NULL;
}
if(codep == -1) return -1;
break;
case INCLUDE:
if(!atleast(retval,codep+4)) return -1;
retval->data[codep++].op = B_INCLUDE;
switch(c->u.inc.location) {
case PERSONAL:
retval->data[codep++].value = B_PERSONAL;
break;
case GLOBAL:
retval->data[codep++].value = B_GLOBAL;
break;
default:
return -1;
}
retval->data[codep++].len = strlen(c->u.inc.script);
retval->data[codep++].str = c->u.inc.script;
break;
case IF:
{
int jumpVal;
baseloc = codep;
if(!atleast(retval,codep+4)) return -1;
jumploc = codep+4;
retval->data[codep++].op = B_IF;
jumpVal= bc_test_generate(jumploc,retval,c->u.i.t);
if(jumpVal == -1)
return -1;
else {
retval->data[codep].jump = jumpVal;
codep++;
}
jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_then);
if(jumpVal == -1)
return -1;
else
retval->data[codep].jump = jumpVal;
codep++;
if(c->u.i.do_else) {
jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_else);
if(jumpVal == -1)
{
return -1;
} else
{
retval->data[codep].jump = jumpVal;
}
codep = retval->data[codep].jump;
} else {
retval->data[codep].jump = -1;
codep = retval->data[codep-1].jump;
}
break;
}
default:
return -1;
}
c = c->next;
} while(c);
}
retval->scriptend=codep;
return codep;
}
int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s)
{
commandlist_t *c;
if(!retval) return -1;
if(!s) return -1;
c = s->cmds;
*retval = xmalloc(sizeof(bytecode_info_t));
if(!(*retval)) return -1;
memset(*retval, 0, sizeof(bytecode_info_t));
return bc_action_generate(0, *retval, c);
}
void sieve_free_bytecode(bytecode_info_t **p)
{
if(!p || !*p) return;
if((*p)->data) free((*p)->data);
free(*p);
*p = NULL;
}