#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "sieve_interface.h"
#include "bytecode.h"
#include "comparator.h"
#include "tree.h"
#include "sieve.h"
#define HEADERCACHESIZE 1019
typedef struct Header {
char *name;
int ncontents;
char *contents[1];
} header_t;
typedef struct message_data {
char *name;
FILE *data;
int size;
int cache_full;
header_t *cache[HEADERCACHESIZE];
} message_data_t;
int hashheader(char *header)
{
int x = 0;
for (; !iscntrl(*header) && (*header != ' ') && (*header != ':');
header++) {
x *= 256;
x += *header;
x %= HEADERCACHESIZE;
}
return x;
}
typedef enum {
NAME_START,
NAME,
COLON,
BODY_START,
BODY
} state;
int parseheader(FILE *f, char **headname, char **contents) {
char c;
char name[80], body[1024];
int off = 0;
state s = NAME_START;
while ((c = getc(f))) {
switch (s) {
case NAME_START:
if (c == '\r' || c == '\n') {
goto ph_error;
}
if (!isalpha(c))
goto ph_error;
name[0] = tolower(c);
off = 1;
s = NAME;
break;
case NAME:
if (c == ' ' || c == '\t' || c == ':') {
name[off] = '\0';
s = (c == ':' ? BODY_START : COLON);
break;
}
if (iscntrl(c)) {
goto ph_error;
}
name[off++] = tolower(c);
break;
case COLON:
if (c == ':') {
s = BODY_START;
} else if (c != ' ' && c != '\t') {
goto ph_error;
}
break;
case BODY_START:
if (c == ' ' || c == '\t')
break;
off = 0;
s = BODY;
case BODY:
if (c == '\r' || c == '\n') {
int peek = getc(f);
if (c == '\r' && peek == '\n') {
c = getc(f);
} else {
c = peek;
}
if (c != ' ' && c != '\t') {
body[off] = '\0';
ungetc(c, f);
goto got_header;
}
break;
} else {
body[off++] = c;
}
}
}
ph_error:
if (headname != NULL) *headname = NULL;
if (contents != NULL) *contents = NULL;
return -1;
got_header:
if (headname != NULL) *headname = strdup(name);
if (contents != NULL) *contents = strdup(body);
return 0;
}
void fill_cache(message_data_t *m)
{
rewind(m->data);
for (;;) {
char *name, *body;
int cl, clinit;
if (parseheader(m->data, &name, &body) < 0) {
break;
}
clinit = cl = hashheader(name);
while (m->cache[cl] != NULL && strcmp(name, m->cache[cl]->name)) {
cl++;
cl %= HEADERCACHESIZE;
if (cl == clinit) break;
}
if (m->cache[cl]) {
m->cache[cl]->contents[m->cache[cl]->ncontents++] = body;
if (!(m->cache[cl]->ncontents % 8)) {
m->cache[cl] = (header_t *)
realloc(m->cache[cl],sizeof(header_t) +
((8 + m->cache[cl]->ncontents) * sizeof(char *)));
if (m->cache[cl] == NULL) {
fprintf(stderr, "realloc() returned NULL\n");
exit(1);
}
}
} else {
m->cache[cl] = (header_t *) malloc(sizeof(header_t) +
8 * sizeof(char*));
if (m->cache[cl] == NULL) {
fprintf(stderr, "malloc() returned NULL\n");
exit(1);
}
m->cache[cl]->name = name;
m->cache[cl]->contents[0] = body;
m->cache[cl]->ncontents = 1;
}
m->cache[cl]->contents[m->cache[cl]->ncontents] = NULL;
}
m->cache_full = 1;
}
int getheader(void *v, const char *phead, const char ***body)
{
message_data_t *m = (message_data_t *) v;
int cl, clinit;
char *h;
char *head;
*body = NULL;
if (!m->cache_full) {
fill_cache(m);
}
head = malloc(strlen(phead)+1);
if (!head) return SIEVE_FAIL;
strcpy(head, phead);
h = head;
while (*h != '\0') {
*h = tolower(*h);
h++;
}
clinit = cl = hashheader(head);
while (m->cache[cl] != NULL) {
if (!strcmp(head, m->cache[cl]->name)) {
*body = (const char **) m->cache[cl]->contents;
break;
}
cl++;
cl %= HEADERCACHESIZE;
if (cl == clinit) break;
}
free(head);
if (*body) {
return SIEVE_OK;
} else {
return SIEVE_FAIL;
}
}
message_data_t *new_msg(FILE *msg, int size, char *name)
{
int i;
message_data_t *m;
m = (message_data_t *) malloc(sizeof(message_data_t));
if (m == NULL) {
fprintf(stderr, "malloc() returned NULL\n");
exit(1);
}
m->data = msg;
m->size = size;
m->name = name;
for (i = 0; i < HEADERCACHESIZE; i++) {
m->cache[i] = NULL;
}
m->cache_full = 0;
return m;
}
int getsize(void *mc, int *size)
{
message_data_t *m = (message_data_t *) mc;
*size = m->size;
return SIEVE_OK;
}
int getenvelope(void *v, const char *head, const char ***body)
{
static const char *buf[2];
if (buf[0] == NULL) { buf[0] = malloc(sizeof(char) * 256); buf[1] = NULL; }
printf("Envelope body of '%s'? ", head);
scanf("%s", (char*) buf[0]);
*body = buf;
return SIEVE_OK;
}
int redirect(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_redirect_context_t *rc = (sieve_redirect_context_t *) ac;
message_data_t *m = (message_data_t *) mc;
int *force_fail = (int*) ic;
printf("redirecting message '%s' to '%s'\n", m->name, rc->addr);
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
int discard(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
message_data_t *m = (message_data_t *) mc;
int *force_fail = (int*) ic;
printf("discarding message '%s'\n", m->name);
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
int reject(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_reject_context_t *rc = (sieve_reject_context_t *) ac;
message_data_t *m = (message_data_t *) mc;
int *force_fail = (int*) ic;
printf("rejecting message '%s' with '%s'\n", m->name, rc->msg);
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
int fileinto(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_fileinto_context_t *fc = (sieve_fileinto_context_t *) ac;
message_data_t *m = (message_data_t *) mc;
int *force_fail = (int*) ic;
printf("filing message '%s' into '%s'\n", m->name, fc->mailbox);
if (fc->imapflags->flag) {
int n;
printf("\twith flags");
for (n = 0; n < fc->imapflags->nflags; n++)
printf(" '%s'", fc->imapflags->flag[n]);
printf("\n");
}
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
int keep(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_keep_context_t *kc = (sieve_keep_context_t *) ac;
message_data_t *m = (message_data_t *) mc;
int *force_fail = (int*) ic;
printf("keeping message '%s'\n", m->name);
if (kc->imapflags->flag) {
int n;
printf("\twith flags");
for (n = 0; n < kc->imapflags->nflags; n++)
printf(" '%s'", kc->imapflags->flag[n]);
printf("\n");
}
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
int notify(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_notify_context_t *nc = (sieve_notify_context_t *) ac;
int *force_fail = (int*) ic;
int flag = 0;
printf("notify ");
if (nc->method) {
const char **opts = nc->options;
printf("%s(", nc->method);
while (opts && *opts) {
if (flag) printf(", ");
printf("%s", *opts);
opts++;
flag = 1;
}
printf("), ");
}
printf("msg = '%s' with priority = %s\n",nc->message, nc->priority);
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
int mysieve_error(int lineno, const char *msg, void *i, void *s)
{
fprintf(stderr, "line %d: %s\r\n", lineno, msg);
return SIEVE_OK;
}
int mysieve_execute_error(const char *msg, void *i, void *s, void *m)
{
fprintf(stderr, "%s\r\n", msg);
return SIEVE_OK;
}
int autorespond(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_autorespond_context_t *arc = (sieve_autorespond_context_t *) ac;
char yn;
int i;
printf("Have I already responded to '");
for (i = 0; i < arc->len; i++) {
printf("%x", arc->hash[i]);
}
printf("' in %d days? ", arc->days);
scanf(" %c", &yn);
if (tolower(yn) == 'y') return SIEVE_DONE;
if (tolower(yn) == 'n') return SIEVE_OK;
return SIEVE_FAIL;
}
int send_response(void *ac, void *ic, void *sc, void *mc, const char **errmsg)
{
sieve_send_response_context_t *src = (sieve_send_response_context_t *) ac;
message_data_t *m = (message_data_t *) mc;
int *force_fail = (int*) ic;
printf("echo '%s' | mail -s '%s' '%s' for message '%s' (from: %s)\n",
src->msg, src->subj, src->addr, m->name, src->fromaddr);
return (*force_fail ? SIEVE_FAIL : SIEVE_OK);
}
sieve_vacation_t vacation = {
0,
0,
&autorespond,
&send_response
};
char *markflags[] = { "\\flagged" };
sieve_imapflags_t mark = { markflags, 1 };
struct testcase {
int comp;
int mode;
const char *pat;
const char *text;
int result;
};
struct testcase tc[] =
{ { B_OCTET, B_IS, "", "", 1 },
{ B_OCTET, B_IS, "a", "", 0 },
{ B_OCTET, B_IS, "", "a", 0 },
{ B_OCTET, B_IS, "a", "a", 1 },
{ B_OCTET, B_IS, "a", "A", 0 },
{ B_ASCIICASEMAP, B_IS, "", "", 1 },
{ B_ASCIICASEMAP, B_IS, "a", "", 0 },
{ B_ASCIICASEMAP, B_IS, "", "a", 0 },
{ B_ASCIICASEMAP, B_IS, "a", "a", 1 },
{ B_ASCIICASEMAP, B_IS, "a", "A", 1 },
{ B_OCTET, B_CONTAINS, "", "", 1 },
{ B_OCTET, B_CONTAINS, "", "a", 1 },
{ B_OCTET, B_CONTAINS, "a", "", 0 },
{ B_OCTET, B_CONTAINS, "a", "a", 1 },
{ B_OCTET, B_CONTAINS, "a", "ab", 1 },
{ B_OCTET, B_CONTAINS, "a", "ba", 1 },
{ B_OCTET, B_CONTAINS, "a", "aba", 1 },
{ B_OCTET, B_CONTAINS, "a", "bab", 1 },
{ B_OCTET, B_CONTAINS, "a", "bb", 0 },
{ B_OCTET, B_CONTAINS, "a", "bbb", 0 },
{ B_OCTET, B_MATCHES, "", "", 1 },
{ B_OCTET, B_MATCHES, "", "a", 0 },
{ B_OCTET, B_MATCHES, "a", "", 0 },
{ B_OCTET, B_MATCHES, "a", "a", 1 },
{ B_OCTET, B_MATCHES, "a", "ab", 0 },
{ B_OCTET, B_MATCHES, "a", "ba", 0 },
{ B_OCTET, B_MATCHES, "a", "aba", 0 },
{ B_OCTET, B_MATCHES, "a", "bab", 0 },
{ B_OCTET, B_MATCHES, "a", "bb", 0 },
{ B_OCTET, B_MATCHES, "a", "bbb", 0 },
{ B_OCTET, B_MATCHES, "*", "", 1 },
{ B_OCTET, B_MATCHES, "*", "a", 1 },
{ B_OCTET, B_MATCHES, "*a*", "", 0 },
{ B_OCTET, B_MATCHES, "*a*", "a", 1 },
{ B_OCTET, B_MATCHES, "*a*", "ab", 1 },
{ B_OCTET, B_MATCHES, "*a*", "ba", 1 },
{ B_OCTET, B_MATCHES, "*a*", "aba", 1 },
{ B_OCTET, B_MATCHES, "*a*", "bab", 1 },
{ B_OCTET, B_MATCHES, "*a*", "bb", 0 },
{ B_OCTET, B_MATCHES, "*a*", "bbb", 0 },
{ B_OCTET, B_MATCHES, "*a", "", 0 },
{ B_OCTET, B_MATCHES, "*a", "a", 1 },
{ B_OCTET, B_MATCHES, "*a", "ab", 0 },
{ B_OCTET, B_MATCHES, "*a", "ba", 1 },
{ B_OCTET, B_MATCHES, "*a", "aba", 1 },
{ B_OCTET, B_MATCHES, "*a", "bab", 0 },
{ B_OCTET, B_MATCHES, "*a", "bb", 0 },
{ B_OCTET, B_MATCHES, "*a", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a*", "", 0 },
{ B_OCTET, B_MATCHES, "a*", "a", 1 },
{ B_OCTET, B_MATCHES, "a*", "ab", 1 },
{ B_OCTET, B_MATCHES, "a*", "ba", 0 },
{ B_OCTET, B_MATCHES, "a*", "aba", 1 },
{ B_OCTET, B_MATCHES, "a*", "bab", 0 },
{ B_OCTET, B_MATCHES, "a*", "bb", 0 },
{ B_OCTET, B_MATCHES, "a*", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a*b", "", 0 },
{ B_OCTET, B_MATCHES, "a*b", "a", 0 },
{ B_OCTET, B_MATCHES, "a*b", "ab", 1 },
{ B_OCTET, B_MATCHES, "a*b", "ba", 0 },
{ B_OCTET, B_MATCHES, "a*b", "aba", 0 },
{ B_OCTET, B_MATCHES, "a*b", "bab", 0 },
{ B_OCTET, B_MATCHES, "a*b", "bb", 0 },
{ B_OCTET, B_MATCHES, "a*b", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a*b", "abbb", 1 },
{ B_OCTET, B_MATCHES, "a*b", "acb", 1 },
{ B_OCTET, B_MATCHES, "a*b", "acbc", 0 },
{ B_OCTET, B_MATCHES, "a?b", "", 0 },
{ B_OCTET, B_MATCHES, "a?b", "a", 0 },
{ B_OCTET, B_MATCHES, "a?b", "ab", 0 },
{ B_OCTET, B_MATCHES, "a?b", "ba", 0 },
{ B_OCTET, B_MATCHES, "a?b", "aba", 0 },
{ B_OCTET, B_MATCHES, "a?b", "bab", 0 },
{ B_OCTET, B_MATCHES, "a?b", "bb", 0 },
{ B_OCTET, B_MATCHES, "a?b", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a?b", "abbb", 0 },
{ B_OCTET, B_MATCHES, "a?b", "acb", 1 },
{ B_OCTET, B_MATCHES, "a?b", "acbc", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "a", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "ab", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "ba", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "aba", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "bab", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "bb", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a*?b", "abbb", 1 },
{ B_OCTET, B_MATCHES, "a*?b", "acb", 1 },
{ B_OCTET, B_MATCHES, "a*?b", "acbc", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "a", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "ab", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "ba", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "aba", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "bab", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "bb", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a?*b", "abbb", 1 },
{ B_OCTET, B_MATCHES, "a?*b", "acb", 1 },
{ B_OCTET, B_MATCHES, "a?*b", "acbc", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "a", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "ab", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "ba", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "aba", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "bab", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "bb", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "bbb", 0 },
{ B_OCTET, B_MATCHES, "a*?*b", "abbb", 1 },
{ B_OCTET, B_MATCHES, "a*?*b", "acb", 1 },
{ B_OCTET, B_MATCHES, "a*?*b?", "acbc", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "a", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "ab", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "ba", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "aba", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "bab", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "bb", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "bbb", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "abbb", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "acb", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "acbc", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "A", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "Ab", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "BA", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "ABA", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "BAb", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "BB", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "BBB", 0 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "aBBB", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "ACB", 1 },
{ B_ASCIICASEMAP, B_MATCHES, "a*b", "ACBC", 0 },
{ 0, 0, NULL, NULL, 0 } };
static int test_comparator(void)
{
struct testcase *t;
int didfail = 0;
for (t = tc; t->comp != 0; t++) {
void *comprock = NULL;
comparator_t *c = lookup_comp(t->comp, t->mode, -1, &comprock);
int res;
char *comp, *mode;
if (t->comp == B_OCTET) comp = "i;octet";
else if (t->comp == B_ASCIICASEMAP) comp = "i;ascii-casemap";
else if (t->comp == B_ASCIINUMERIC) comp = "i;ascii-numeric";
else comp = "<unknown comp>";
if (t->mode == B_IS) mode = "IS";
else if (t->mode == B_CONTAINS) mode = "CONTAINS";
else if (t->mode == B_MATCHES) mode = "MATCHES";
else if (t->mode == B_REGEX) mode = "REGEX";
else mode = "<unknown mode>";
if (!c) {
printf("FAIL: can't find a comparator %s/%s\n",
comp, mode);
didfail++;
continue;
}
res = c(t->text, t->pat, comprock);
if (res != t->result) {
printf("FAIL: %s/%s(%s, %s) = %d, not %d\n",
comp, mode, t->pat, t->text, res, t->result);
didfail++;
}
}
if (didfail) {
fprintf(stderr, "failed %d tests\n", didfail);
exit(1);
} else {
exit(0);
}
}
int config_need_data = 0;
int main(int argc, char *argv[])
{
sieve_interp_t *i;
sieve_bytecode_t *bc;
message_data_t *m;
char *script = NULL, *message = NULL;
int c, force_fail = 0, usage_error = 0;
int fd, res;
struct stat sbuf;
while ((c = getopt(argc, argv, "v:cf")) != EOF)
switch (c) {
case 'v':
script = optarg;
break;
case 'c':
test_comparator();
break;
case 'f':
force_fail = 1;
break;
default:
usage_error = 1;
break;
}
if (!script) {
if ((argc - optind) < 2)
usage_error = 1;
else {
message = argv[optind];
script = argv[optind+1];
}
}
if (usage_error) {
fprintf(stderr, "usage:\n");
fprintf(stderr, "%s message script\n", argv[0]);
fprintf(stderr, "%s -v script\n", argv[0]);
exit(1);
}
res = sieve_interp_alloc(&i, &force_fail);
if (res != SIEVE_OK) {
printf("sieve_interp_alloc() returns %d\n", res);
exit(1);
}
res = sieve_register_redirect(i, &redirect);
if (res != SIEVE_OK) {
printf("sieve_register_redirect() returns %d\n", res);
exit(1);
}
res = sieve_register_discard(i, &discard);
if (res != SIEVE_OK) {
printf("sieve_register_discard() returns %d\n", res);
exit(1);
}
res = sieve_register_reject(i, &reject);
if (res != SIEVE_OK) {
printf("sieve_register_reject() returns %d\n", res);
exit(1);
}
res = sieve_register_fileinto(i, &fileinto);
if (res != SIEVE_OK) {
printf("sieve_register_fileinto() returns %d\n", res);
exit(1);
}
res = sieve_register_keep(i, &keep);
if (res != SIEVE_OK) {
printf("sieve_register_keep() returns %d\n", res);
exit(1);
}
res = sieve_register_size(i, &getsize);
if (res != SIEVE_OK) {
printf("sieve_register_size() returns %d\n", res);
exit(1);
}
res = sieve_register_header(i, &getheader);
if (res != SIEVE_OK) {
printf("sieve_register_header() returns %d\n", res);
exit(1);
}
res = sieve_register_envelope(i, &getenvelope);
if (res != SIEVE_OK) {
printf("sieve_register_envelope() returns %d\n", res);
exit(1);
}
res = sieve_register_vacation(i, &vacation);
if (res != SIEVE_OK) {
printf("sieve_register_vacation() returns %d\n", res);
exit(1);
}
res = sieve_register_imapflags(i, &mark);
if (res != SIEVE_OK) {
printf("sieve_register_imapflags() returns %d\n", res);
exit(1);
}
res = sieve_register_notify(i, ¬ify);
if (res != SIEVE_OK) {
printf("sieve_register_notify() returns %d\n", res);
exit(1);
}
res = sieve_register_parse_error(i, &mysieve_error);
if (res != SIEVE_OK) {
printf("sieve_register_parse_error() returns %d\n", res);
exit(1);
}
res = sieve_register_execute_error(i, &mysieve_execute_error);
if (res != SIEVE_OK) {
printf("sieve_register_execute_error() returns %d\n", res);
exit(1);
}
res = sieve_script_load(argv[2], &bc);
if (res != SIEVE_OK) {
printf("sieve_script_load() returns %d\n", res);
exit(1);
}
if (message) {
fd = open(message, O_RDONLY);
res = fstat(fd, &sbuf);
if (res != 0) {
perror("fstat");
}
m = new_msg(fdopen(fd, "r"), sbuf.st_size, message);
if (res != SIEVE_OK) {
printf("sieve_msg_parse() returns %d\n", res);
exit(1);
}
res = sieve_execute_bytecode(bc, i, NULL, m);
if (res != SIEVE_OK) {
printf("sieve_execute_bytecode() returns %d\n", res);
exit(1);
}
close(fd);
}
res = sieve_script_unload(&bc);
if (res != SIEVE_OK) {
printf("sieve_script_unload() returns %d\n", res);
exit(1);
}
res = sieve_interp_free(&i);
if (res != SIEVE_OK) {
printf("sieve_interp_free() returns %d\n", res);
exit(1);
}
return 0;
}
void fatal(char* message, int rc) {
fprintf(stderr, "fatal error: %s\n", message);
exit(rc);
}