int sieve_execute_bytecode(sieve_execute_t *exe, sieve_interp_t *interp, void *script_context, void *message_context) { action_list_t *actions = NULL; notify_list_t *notify_list = NULL; /* notify_action_t *notify_action;*/ action_t lastaction = -1; int ret; char actions_string[ACTIONS_STRING_LEN] = ""; const char *errmsg = NULL; strarray_t imapflags = STRARRAY_INITIALIZER; if (!interp) return SIEVE_FAIL; if (interp->notify) { notify_list = new_notify_list(); if (notify_list == NULL) { return do_sieve_error(SIEVE_NOMEM, interp, script_context, message_context, &imapflags, actions, notify_list, lastaction, 0, actions_string, errmsg); } } actions = new_action_list(); if (actions == NULL) { ret = do_sieve_error(SIEVE_NOMEM, interp, script_context, message_context, &imapflags, actions, notify_list, lastaction, 0, actions_string, errmsg); } else { ret = sieve_eval_bc(exe, 0, interp, script_context, message_context, &imapflags, actions, notify_list, &errmsg); if (ret < 0) { ret = do_sieve_error(SIEVE_RUN_ERROR, interp, script_context, message_context, &imapflags, actions, notify_list, lastaction, 0, actions_string, errmsg); } else { ret = do_action_list(interp, script_context, message_context, &imapflags, actions, notify_list, actions_string, errmsg); } } strarray_fini(&imapflags); return ret; }
/* The entrypoint for bytecode evaluation */ int sieve_eval_bc(sieve_execute_t *exe, int is_incl, sieve_interp_t *i, void *sc, void *m, strarray_t *imapflags, action_list_t *actions, notify_list_t *notify_list, const char **errmsg) { const char *data; int res=0; int op; int version; sieve_bytecode_t *bc_cur = exe->bc_cur; bytecode_input_t *bc = (bytecode_input_t *) bc_cur->data; int ip = 0, ip_max = (bc_cur->len/sizeof(bytecode_input_t)); if (bc_cur->is_executing) { *errmsg = "Recursive Include"; return SIEVE_RUN_ERROR; } bc_cur->is_executing = 1; /* Check that we * a) have bytecode * b) it is atleast long enough for the magic number, the version * and one opcode */ if(!bc) return SIEVE_FAIL; if(bc_cur->len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t))) return SIEVE_FAIL; if(memcmp(bc, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) { *errmsg = "Not a bytecode file"; return SIEVE_FAIL; } ip = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t); version= ntohl(bc[ip].op); /* this is because there was a time where integers were not network byte order. all the scripts written then would have version 0x01 written in host byte order.*/ if(version == (int)ntohl(1)) { if(errmsg) { *errmsg = "Incorrect Bytecode Version, please recompile (use sievec)"; } return SIEVE_FAIL; } if((version < BYTECODE_MIN_VERSION) || (version > BYTECODE_VERSION)) { if(errmsg) { *errmsg = "Incorrect Bytecode Version, please recompile (use sievec)"; } return SIEVE_FAIL; } #if VERBOSE printf("version number %d\n",version); #endif for(ip++; ip<ip_max; ) { int copy = 0; op=ntohl(bc[ip].op); switch(op) { case B_STOP:/*0*/ res=1; break; case B_KEEP:/*1*/ res = do_keep(actions, imapflags); if (res == SIEVE_RUN_ERROR) *errmsg = "Keep can not be used with Reject"; ip++; break; case B_DISCARD:/*2*/ res=do_discard(actions); ip++; break; case B_REJECT:/*3*/ ip = unwrap_string(bc, ip+1, &data, NULL); res = do_reject(actions, data); if (res == SIEVE_RUN_ERROR) *errmsg = "Reject can not be used with any other action"; break; case B_FILEINTO:/*19*/ copy = ntohl(bc[ip+1].value); ip+=1; /* fall through */ case B_FILEINTO_ORIG:/*4*/ { ip = unwrap_string(bc, ip+1, &data, NULL); res = do_fileinto(actions, data, !copy, imapflags); if (res == SIEVE_RUN_ERROR) *errmsg = "Fileinto can not be used with Reject"; break; } case B_REDIRECT:/*20*/ copy = ntohl(bc[ip+1].value); ip+=1; /* fall through */ case B_REDIRECT_ORIG:/*5*/ { ip = unwrap_string(bc, ip+1, &data, NULL); res = do_redirect(actions, data, !copy); if (res == SIEVE_RUN_ERROR) *errmsg = "Redirect can not be used with Reject"; break; } case B_IF:/*6*/ { int testend=ntohl(bc[ip+1].value); int result; ip+=2; result=eval_bc_test(i, m, bc, &ip); if (result<0) { *errmsg = "Invalid test"; return SIEVE_FAIL; } else if (result) { /*skip over jump instruction*/ testend+=2; } ip=testend; break; } case B_MARK:/*8*/ res = do_mark(actions); ip++; break; case B_UNMARK:/*9*/ res = do_unmark(actions); ip++; break; case B_ADDFLAG:/*10*/ { int x; int list_len=ntohl(bc[ip+1].len); ip+=3; /* skip opcode, list_len, and list data len */ for (x=0; x<list_len; x++) { ip = unwrap_string(bc, ip, &data, NULL); res = do_addflag(actions, data); if (res == SIEVE_RUN_ERROR) *errmsg = "addflag can not be used with Reject"; } break; } case B_SETFLAG: { int x; int list_len=ntohl(bc[ip+1].len); ip+=3; /* skip opcode, list_len, and list data len */ ip = unwrap_string(bc, ip, &data, NULL); res = do_setflag(actions, data); if (res == SIEVE_RUN_ERROR) { *errmsg = "setflag can not be used with Reject"; } else { for (x=1; x<list_len; x++) { ip = unwrap_string(bc, ip, &data, NULL); res = do_addflag(actions, data); if (res == SIEVE_RUN_ERROR) *errmsg = "setflag can not be used with Reject"; } } break; } case B_REMOVEFLAG: { int x; int list_len=ntohl(bc[ip+1].len); ip+=3; /* skip opcode, list_len, and list data len */ for (x=0; x<list_len; x++) { ip = unwrap_string(bc, ip, &data, NULL); res = do_removeflag(actions, data); if (res == SIEVE_RUN_ERROR) *errmsg = "removeflag can not be used with Reject"; } break; } case B_NOTIFY: { const char * id; const char * method; const char **options = NULL; const char *priority = NULL; const char * message; int pri; ip++; /* method */ ip = unwrap_string(bc, ip, &method, NULL); /* id */ ip = unwrap_string(bc, ip, &id, NULL); /*options*/ options=bc_makeArray(bc, &ip); /* priority */ pri=ntohl(bc[ip].value); ip++; switch (pri) { case B_LOW: priority="low"; break; case B_NORMAL: priority="normal"; break; case B_HIGH: priority="high"; break; case B_ANY: priority="any"; break; default: res=SIEVE_RUN_ERROR; } /* message */ ip = unwrap_string(bc, ip, &message, NULL); res = do_notify(notify_list, id, method, options, priority, message); break; } case B_DENOTIFY: { /* * i really have no idea what the count matchtype should do here. * the sanest thing would be to use 1. * however that would require passing on the match type to do_notify. * -jsmith2 */ comparator_t *comp = NULL; const char *pattern; regex_t *reg; const char *priority = NULL; void *comprock = NULL; int comparator; int pri; ip++; pri=ntohl(bc[ip].value); ip++; switch (pri) { case B_LOW: priority="low"; break; case B_NORMAL: priority="normal"; break; case B_HIGH: priority="high"; break; case B_ANY: priority="any"; break; default: res=SIEVE_RUN_ERROR; } if(res == SIEVE_RUN_ERROR) break; comparator =ntohl( bc[ip].value); ip++; if (comparator == B_ANY) { ip++;/* skip placeholder this has no comparator function */ comp=NULL; } else { int x= ntohl(bc[ip].value); ip++; comp=lookup_comp(B_ASCIICASEMAP,comparator, x, &comprock); } ip = unwrap_string(bc, ip, &pattern, NULL); if (comparator == B_REGEX) { char errmsg[1024]; /* Basically unused */ reg=bc_compile_regex(pattern, REG_EXTENDED | REG_NOSUB | REG_ICASE, errmsg, sizeof(errmsg)); if (!reg) { res = SIEVE_RUN_ERROR; } else { res = do_denotify(notify_list, comp, reg, comprock, priority); free(reg); } } else { res = do_denotify(notify_list, comp, pattern, comprock, priority); } break; } case B_VACATION_ORIG: case B_VACATION: { int respond; char *fromaddr = NULL; /* relative to message we send */ char *toaddr = NULL; /* relative to message we send */ const char *handle = NULL; const char *message = NULL; int seconds, mime; char buf[128]; char subject[1024]; int x; ip++; x = ntohl(bc[ip].len); respond = shouldRespond(m, i, x, bc, ip+2, &fromaddr, &toaddr); ip = ntohl(bc[ip+1].value) / 4; if (respond==SIEVE_OK) { ip = unwrap_string(bc, ip, &data, NULL); if (!data) { /* we have to generate a subject */ const char **s; strlcpy(buf, "subject", sizeof(buf)); if (i->getheader(m, buf, &s) != SIEVE_OK || s[0] == NULL) { strlcpy(subject, "Automated reply", sizeof(subject)); } else { /* s[0] contains the original subject */ const char *origsubj = s[0]; snprintf(subject, sizeof(subject), "Auto: %s", origsubj); } } else { /* user specified subject */ strlcpy(subject, data, sizeof(subject)); } ip = unwrap_string(bc, ip, &message, NULL); seconds = ntohl(bc[ip].value); if (op == B_VACATION_ORIG) { seconds *= DAY2SEC; } mime = ntohl(bc[ip+1].value); ip+=2; if (version >= 0x05) { ip = unwrap_string(bc, ip, &data, NULL); if (data) { /* user specified from address */ free(fromaddr); fromaddr = xstrdup(data); } ip = unwrap_string(bc, ip, &data, NULL); if (data) { /* user specified handle */ handle = data; } } res = do_vacation(actions, toaddr, fromaddr, xstrdup(subject), message, seconds, mime, handle); if (res == SIEVE_RUN_ERROR) *errmsg = "Vacation can not be used with Reject or Vacation"; } else if (respond == SIEVE_DONE) { /* skip subject and message */ ip = unwrap_string(bc, ip, &data, NULL); ip = unwrap_string(bc, ip, &data, NULL); ip+=2;/*skip days and mime flag*/ if (version >= 0x05) { /* skip from and handle */ ip = unwrap_string(bc, ip, &data, NULL); ip = unwrap_string(bc, ip, &data, NULL); } } else { res = SIEVE_RUN_ERROR; /* something is bad */ } break; } case B_NULL:/*15*/ ip++; break; case B_JUMP:/*16*/ ip= ntohl(bc[ip+1].jump); break; case B_INCLUDE:/*17*/ { int isglobal = (ntohl(bc[ip+1].value) & 63) == B_GLOBAL; int once = ntohl(bc[ip+1].value) & 64 ? 1 : 0; int isoptional = ntohl(bc[ip+1].value) & 128 ? 1 : 0; char fpath[4096]; ip = unwrap_string(bc, ip+2, &data, NULL); res = i->getinclude(sc, data, isglobal, fpath, sizeof(fpath)); if (res != SIEVE_OK) { if (isoptional == 0) *errmsg = "Include can not find script"; else res = SIEVE_OK; break; } res = sieve_script_load(fpath, &exe); if (res == SIEVE_SCRIPT_RELOADED) { if (once == 1) { res = SIEVE_OK; break; } } else if (res != SIEVE_OK) { /* SIEVE_FAIL */ if (isoptional == 0) *errmsg = "Include can not load script"; else res = SIEVE_OK; break; } res = sieve_eval_bc(exe, 1, i, sc, m, imapflags, actions, notify_list, errmsg); break; } case B_RETURN:/*18*/ if (is_incl) goto done; else res=1; break; default: if(errmsg) *errmsg = "Invalid sieve bytecode"; return SIEVE_FAIL; } if (res) /* we've either encountered an error or a stop */ break; } done: bc_cur->is_executing = 0; return res; }