#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "xmalloc.h"
#include "sieve_interface.h"
#include "bytecode.h"
#include <syslog.h>
#include <sys/types.h>
#include <unistd.h>
#if DUMPCODE
void dump(bytecode_info_t *d);
#endif
inline int write_int (int fd, int x)
{
int y=htonl(x);
return (write(fd, &y, sizeof(int)));
}
struct bytecode_info
{
bytecode_t *data;
size_t scriptend;
size_t reallen;
};
static int align_string(int fd, int string_len)
{
int needed = sizeof(int) - (string_len % sizeof(int));
if (needed>= 0 && needed <=4)
{
if(write(fd, "\0\0\0\0", needed) == -1) return -1;
}
return needed;
}
static int bc_stringlist_emit(int fd, int *codep, bytecode_info_t *bc)
{
int len = bc->data[(*codep)++].len;
int i;
int ret;
int wrote = 2*sizeof(int);
int begin,end;
if (write_int(fd, len)== -1) return -1 ;
begin=lseek(fd,0,SEEK_CUR);
lseek(fd,sizeof(int),SEEK_CUR);
for(i=0; i < len; i++)
{
int datalen = bc->data[(*codep)++].len;
if(write_int(fd, datalen) == -1) return -1;
wrote += sizeof(int);
if(write(fd, bc->data[(*codep)++].str, datalen) == -1) return -1;
wrote += datalen;
ret = align_string(fd,datalen);
if(ret == -1) return -1;
wrote+=ret;
}
end=lseek(fd,0,SEEK_CUR);
lseek(fd,begin,SEEK_SET);
if(write_int(fd, end) == -1) return -1;
lseek(fd,end,SEEK_SET);
return wrote;
}
static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc);
static int bc_testlist_emit(int fd, int *codep, bytecode_info_t *bc)
{
int len = bc->data[(*codep)++].len;
int i;
int ret;
int begin, end;
int wrote = 2*sizeof(int);
if(write_int(fd, len)== -1) return -1;
begin = lseek(fd, 0, SEEK_CUR);
lseek(fd, sizeof(int), SEEK_CUR);
for(i=0; i < len; i++) {
int nextcodep = bc->data[(*codep)++].jump;
ret = bc_test_emit(fd, codep, bc);
if(ret < 0 ) return -1;
wrote+=ret;
*codep = nextcodep;
}
end = lseek(fd, 0, SEEK_CUR);
lseek(fd,begin,SEEK_SET);
if(write_int(fd, end) == -1) return -1;
lseek(fd,end,SEEK_SET);
return wrote;
}
static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc)
{
int wrote=0;
int ret;
if(write_int(fd, bc->data[(*codep)].op) == -1)
return -1;
wrote += sizeof(int);
switch(bc->data[(*codep)++].op) {
case BC_TRUE:
case BC_FALSE:
break;
case BC_NOT:
{
ret = bc_test_emit(fd, codep, bc);
if(ret < 0)
return -1;
else
wrote+=ret;
break;
}
case BC_ALLOF:
case BC_ANYOF:
ret = bc_testlist_emit(fd, codep, bc);
if(ret < 0)
return -1;
else
wrote+=ret;
break;
case BC_SIZE:
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
if(write_int(fd, bc->data[(*codep)+1].value) == -1)
return -1;
wrote += 2 * sizeof(int);
(*codep) += 2;
break;
case BC_EXISTS:
{
int ret;
ret = bc_stringlist_emit(fd, codep, bc);
if(ret < 0) return -1;
wrote += ret;
break;
}
case BC_HEADER:
{
int ret;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
ret = bc_stringlist_emit(fd, codep, bc);
if(ret < 0) return -1;
wrote+=ret;
ret = bc_stringlist_emit(fd, codep, bc);
if(ret < 0) return -1;
wrote+=ret;
break;
}
case BC_ADDRESS:
case BC_ENVELOPE:
{
int ret;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
if(write_int(fd, bc->data[(*codep)].value) == -1)
return -1;
wrote += sizeof(int);
(*codep)++;
ret = bc_stringlist_emit(fd, codep, bc);
if(ret < 0) return -1;
wrote+=ret;
ret = bc_stringlist_emit(fd, codep, bc);
if(ret < 0) return -1;
wrote+=ret;
break;
}
default:
return -1;
}
return wrote;
}
static int bc_action_emit(int fd, int codep, int stopcodep,
bytecode_info_t *bc, int filelen)
{
int len;
int ret;
int start_filelen = filelen;
int i;
syslog(LOG_DEBUG, "entered bc_action_emit with filelen: %d", filelen);
while(codep < stopcodep) {
if(write_int(fd, bc->data[codep].op) == -1)
return -1;
filelen+=sizeof(int);
switch(bc->data[codep++].op) {
case B_IF:
{
int testEndLoc=-1;
int testdist, thendist, elsedist;
int c;
int jumpFalseLoc=-1;
int jumpEndLoc=-1;
int jumpto=-1;
int jumpop= B_JUMP;
ret = lseek(fd, sizeof(int), SEEK_CUR);
if(ret == -1) return ret;
testEndLoc=filelen;
filelen+=sizeof(int);
c=codep+3;
testdist = bc_test_emit(fd, &c, bc);
if(testdist == -1)return -1;
filelen +=testdist;
jumpto=filelen/4;
if(lseek(fd, testEndLoc, SEEK_SET) == -1)
return -1;
if(write_int(fd,jumpto) == -1)
return -1;
if(lseek(fd,filelen,SEEK_SET) == -1)
return -1;
if(write_int(fd, jumpop) == -1)
return -1;
ret = lseek(fd, sizeof(int), SEEK_CUR);
if(ret == -1)
return ret;
jumpFalseLoc=filelen+sizeof(int);
filelen +=2*sizeof(int);
thendist = bc_action_emit(fd, bc->data[codep].value,
bc->data[codep+1].value, bc,
filelen);
filelen+=thendist;
if(bc->data[codep+2].value != -1)
{
if(write_int(fd, jumpop) == -1)
return -1;
ret = lseek(fd, sizeof(int), SEEK_CUR);
if(ret == -1)
return ret;
jumpEndLoc=filelen+sizeof(int);
filelen+=2*sizeof(int);
}
jumpto=filelen/4;
if(lseek(fd, jumpFalseLoc, SEEK_SET) == -1)
return -1;
if(write_int(fd,jumpto) == -1)
return -1;
if(lseek(fd,filelen,SEEK_SET) == -1)
return -1;
if(bc->data[codep+2].value != -1) {
elsedist = bc_action_emit(fd, bc->data[codep+1].value,
bc->data[codep+2].value, bc,
filelen);
filelen+=elsedist;
jumpto=filelen/4;
if(lseek(fd, jumpEndLoc, SEEK_SET) == -1)
return -1;
if(write_int(fd,jumpto) == -1)
return -1;
if(lseek(fd,filelen,SEEK_SET) == -1)
return -1;
codep = bc->data[codep+2].value;
} else {
codep = bc->data[codep+1].value;
}
break;
}
case B_REJECT:
case B_FILEINTO:
case B_REDIRECT:
len = bc->data[codep++].len;
if(write_int(fd,len) == -1)
return -1;
filelen+=sizeof(int);
if(write(fd,bc->data[codep++].str,len) == -1)
return -1;
ret = align_string(fd, len);
if(ret == -1)
return -1;
filelen += len + ret;
break;
case B_SETFLAG:
case B_ADDFLAG:
case B_REMOVEFLAG:
ret = bc_stringlist_emit(fd, &codep, bc);
if(ret < 0)
return -1;
filelen += ret;
break;
case B_NOTIFY:
for(i=0; i<2; i++) {
len = bc->data[codep++].len;
if(write_int(fd,len) == -1)
return -1;
filelen += sizeof(int);
if(len == -1)
{
codep++;
}
else
{
if(write(fd,bc->data[codep++].str,len) == -1)
return -1;
ret = align_string(fd, len);
if(ret == -1)
return -1;
filelen += len + ret;
}
}
ret = bc_stringlist_emit(fd, &codep, bc);
if(ret < 0)
return -1;
filelen+=ret;
if(write_int(fd, bc->data[codep].value) == -1)
return -1;
codep++;
filelen += sizeof(int);
len = bc->data[codep++].len;
if(write_int(fd,len) == -1)
return -1;
filelen += sizeof(int);
if(write(fd,bc->data[codep++].str,len) == -1)
return -1;
ret = align_string(fd, len);
if(ret == -1) return -1;
filelen += len + ret;
break;
case B_DENOTIFY:
if(write_int(fd, bc->data[codep].value) == -1)
return -1;
filelen += sizeof(int);
codep++;
if(write_int(fd, bc->data[codep].value) == -1)
return -1;
filelen += sizeof(int);
codep++;
if(write_int(fd, bc->data[codep].value) == -1)
return -1;
filelen += sizeof(int);
codep++;
len = bc->data[codep++].len;
if(write_int(fd,len) == -1)
return -1;
filelen += sizeof(int);
if(len == -1)
{
codep++;
}
else
{
if(write(fd,bc->data[codep++].str,len) == -1)
return -1;
ret = align_string(fd, len);
if(ret == -1) return -1;
filelen += len + ret;
}
break;
case B_VACATION:
ret = bc_stringlist_emit(fd, &codep, bc);
if(ret < 0) return -1;
filelen += ret;
for(i=0; i<2; i++) {
len = bc->data[codep++].len;
if(write_int(fd,len) == -1)
return -1;
filelen += sizeof(int);
if(len == -1)
{
codep++;
}
else
{
if(write(fd,bc->data[codep++].str,len) == -1)
return -1;
ret = align_string(fd, len);
if(ret == -1) return -1;
filelen += len + ret;
}
}
if(write_int(fd,bc->data[codep].value) == -1)
return -1;
codep++;
filelen += sizeof(int);
if(write_int(fd,bc->data[codep].value) == -1)
return -1;
codep++;
filelen += sizeof(int);
break;
case B_NULL:
case B_STOP:
case B_DISCARD:
case B_KEEP:
case B_MARK:
case B_UNMARK:
break;
default:
return -1;
}
}
return filelen - start_filelen;
}
int sieve_emit_bytecode(int fd, bytecode_info_t *bc)
{
int data = BYTECODE_VERSION;
if(write(fd, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN) == -1)
return -1;
if(write_int(fd, data) == -1) return -1;
#if DUMPCODE
dump(bc);
#endif
return bc_action_emit(fd, 0, bc->scriptend, bc, sizeof(int) + BYTECODE_MAGIC_LEN);
}