diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-code.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/sieve-code.c | 1169 |
1 files changed, 1169 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-code.c b/pigeonhole/src/lib-sieve/sieve-code.c new file mode 100644 index 0000000..82bd7f1 --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-code.c @@ -0,0 +1,1169 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "str.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-limits.h" +#include "sieve-extensions.h" +#include "sieve-stringlist.h" +#include "sieve-actions.h" +#include "sieve-binary.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" + +#include "sieve-code.h" + +#include <stdio.h> + +/* + * Code stringlist + */ + +/* Forward declarations */ + +static int sieve_code_stringlist_next_item + (struct sieve_stringlist *_strlist, string_t **str_r); +static void sieve_code_stringlist_reset + (struct sieve_stringlist *_strlist); +static int sieve_code_stringlist_get_length + (struct sieve_stringlist *_strlist); + +/* Coded stringlist object */ + +struct sieve_code_stringlist { + struct sieve_stringlist strlist; + + sieve_size_t start_address; + sieve_size_t end_address; + sieve_size_t current_offset; + int length; + int index; +}; + +static struct sieve_stringlist *sieve_code_stringlist_create +(const struct sieve_runtime_env *renv, + sieve_size_t start_address, unsigned int length, sieve_size_t end) +{ + struct sieve_code_stringlist *strlist; + + if ( end > sieve_binary_block_get_size(renv->sblock) ) + return NULL; + + strlist = t_new(struct sieve_code_stringlist, 1); + strlist->strlist.runenv = renv; + strlist->strlist.exec_status = SIEVE_EXEC_OK; + strlist->strlist.next_item = sieve_code_stringlist_next_item; + strlist->strlist.reset = sieve_code_stringlist_reset; + strlist->strlist.get_length = sieve_code_stringlist_get_length; + strlist->start_address = start_address; + strlist->current_offset = start_address; + strlist->end_address = end; + strlist->length = length; + strlist->index = 0; + + return &strlist->strlist; +} + +/* Stringlist implementation */ + +static int sieve_code_stringlist_next_item +(struct sieve_stringlist *_strlist, string_t **str_r) +{ + struct sieve_code_stringlist *strlist = + (struct sieve_code_stringlist *) _strlist; + sieve_size_t address; + *str_r = NULL; + int ret; + + /* Check for end of list */ + if ( strlist->index >= strlist->length ) + return 0; + + /* Read next item */ + address = strlist->current_offset; + if ( (ret=sieve_opr_string_read(_strlist->runenv, &address, NULL, str_r)) + == SIEVE_EXEC_OK ) { + strlist->index++; + strlist->current_offset = address; + return 1; + } + + _strlist->exec_status = ret; + return -1; +} + +static void sieve_code_stringlist_reset +(struct sieve_stringlist *_strlist) +{ + struct sieve_code_stringlist *strlist = + (struct sieve_code_stringlist *) _strlist; + + strlist->current_offset = strlist->start_address; + strlist->index = 0; +} + +static int sieve_code_stringlist_get_length +(struct sieve_stringlist *_strlist) +{ + struct sieve_code_stringlist *strlist = + (struct sieve_code_stringlist *) _strlist; + + return strlist->length; +} + +static bool sieve_code_stringlist_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + unsigned int length, sieve_size_t end, const char *field_name) +{ + unsigned int i; + + if ( end > sieve_binary_block_get_size(denv->sblock) ) + return FALSE; + + if ( field_name != NULL ) + sieve_code_dumpf(denv, "%s: STRLIST [%u] (end: %08llx)", + field_name, length, (unsigned long long) end); + else + sieve_code_dumpf(denv, "STRLIST [%u] (end: %08llx)", + length, (unsigned long long) end); + + sieve_code_descend(denv); + + for ( i = 0; i < length; i++ ) { + bool success = TRUE; + + T_BEGIN { + success = sieve_opr_string_dump(denv, address, NULL); + } T_END; + + if ( !success || *address > end ) + return FALSE; + } + + if ( *address != end ) return FALSE; + + sieve_code_ascend(denv); + + return TRUE; +} + +/* + * Core operands + */ + +extern const struct sieve_operand_def comparator_operand; +extern const struct sieve_operand_def match_type_operand; +extern const struct sieve_operand_def address_part_operand; + +const struct sieve_operand_def *sieve_operands[] = { + &omitted_operand, /* SIEVE_OPERAND_OPTIONAL */ + &number_operand, + &string_operand, + &stringlist_operand, + &comparator_operand, + &match_type_operand, + &address_part_operand, + &catenated_string_operand +}; + +const unsigned int sieve_operand_count = + N_ELEMENTS(sieve_operands); + +/* + * Operand functions + */ + +sieve_size_t sieve_operand_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_operand_def *opr_def) +{ + sieve_size_t address; + + if ( ext != NULL ) { + address = sieve_binary_emit_extension + (sblock, ext, sieve_operand_count); + + sieve_binary_emit_extension_object + (sblock, &opr_def->ext_def->operands, opr_def->code); + + return address; + } + + return sieve_binary_emit_byte(sblock, opr_def->code); +} + +bool sieve_operand_read +(struct sieve_binary_block *sblock, sieve_size_t *address, + const char *field_name, struct sieve_operand *operand) +{ + unsigned int code = sieve_operand_count; + + operand->address = *address; + operand->field_name = field_name; + operand->ext = NULL; + operand->def = NULL; + + if ( !sieve_binary_read_extension(sblock, address, &code, &operand->ext) ) + return FALSE; + + if ( operand->ext == NULL ) { + if ( code < sieve_operand_count ) + operand->def = sieve_operands[code]; + + return ( operand->def != NULL ); + } + + if ( operand->ext->def == NULL ) + return FALSE; + + operand->def = (const struct sieve_operand_def *) + sieve_binary_read_extension_object(sblock, address, + &operand->ext->def->operands); + + return ( operand->def != NULL ); +} + +/* + * Optional operand + */ + +int sieve_opr_optional_next +(struct sieve_binary_block *sblock, sieve_size_t *address, signed int *opt_code) +{ + /* Start of optional operand block */ + if ( *opt_code == 0 ) { + sieve_size_t tmp_addr = *address; + unsigned int op; + + if ( !sieve_binary_read_byte(sblock, &tmp_addr, &op) || + op != SIEVE_OPERAND_OPTIONAL ) + return 0; + + *address = tmp_addr; + } + + /* Read optional operand code */ + if ( !sieve_binary_read_code(sblock, address, opt_code) ) + return -1; + + /* Return 0 at end of list */ + return ( *opt_code != 0 ? 1 : 0 ); +} + +/* + * Operand definitions + */ + +/* Omitted */ + +const struct sieve_operand_class omitted_class = + { "OMITTED" }; + +const struct sieve_operand_def omitted_operand = { + .name = "@OMITTED", + .code = SIEVE_OPERAND_OPTIONAL, + .class = &omitted_class +}; + +/* Number */ + +static bool opr_number_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_number_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, sieve_number_t *number_r); + +const struct sieve_opr_number_interface number_interface = { + opr_number_dump, + opr_number_read +}; + +const struct sieve_operand_class number_class = + { "number" }; + +const struct sieve_operand_def number_operand = { + .name = "@number", + .code = SIEVE_OPERAND_NUMBER, + .class = &number_class, + .interface = &number_interface +}; + +/* String */ + +static bool opr_string_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_string_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r); + +const struct sieve_opr_string_interface string_interface ={ + opr_string_dump, + opr_string_read +}; + +const struct sieve_operand_class string_class = + { "string" }; + +const struct sieve_operand_def string_operand = { + .name = "@string", + .code = SIEVE_OPERAND_STRING, + .class = &string_class, + .interface = &string_interface +}; + +/* String List */ + +static bool opr_stringlist_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address); +static int opr_stringlist_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, struct sieve_stringlist **strlist_r); + +const struct sieve_opr_stringlist_interface stringlist_interface = { + opr_stringlist_dump, + opr_stringlist_read +}; + +const struct sieve_operand_class stringlist_class = + { "string-list" }; + +const struct sieve_operand_def stringlist_operand = { + .name = "@string-list", + .code = SIEVE_OPERAND_STRING_LIST, + .class = &stringlist_class, + .interface = &stringlist_interface +}; + +/* Catenated String */ + +static bool opr_catenated_string_dump + (const struct sieve_dumptime_env *denv, const struct sieve_operand *operand, + sieve_size_t *address); +static int opr_catenated_string_read + (const struct sieve_runtime_env *renv, const struct sieve_operand *operand, + sieve_size_t *address, string_t **str); + +const struct sieve_opr_string_interface catenated_string_interface = { + opr_catenated_string_dump, + opr_catenated_string_read +}; + +const struct sieve_operand_def catenated_string_operand = { + .name = "@catenated-string", + .code = SIEVE_OPERAND_CATENATED_STRING, + .class = &string_class, + .interface = &catenated_string_interface +}; + +/* + * Operand implementations + */ + +/* Omitted */ + +void sieve_opr_omitted_emit(struct sieve_binary_block *sblock) +{ + (void) sieve_operand_emit(sblock, NULL, &omitted_operand); +} + +/* Number */ + +void sieve_opr_number_emit +(struct sieve_binary_block *sblock, sieve_number_t number) +{ + (void) sieve_operand_emit(sblock, NULL, &number_operand); + (void) sieve_binary_emit_integer(sblock, number); +} + +bool sieve_opr_number_dump_data +(const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name) +{ + const struct sieve_opr_number_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_number(oprnd) ) + return FALSE; + + intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) + return FALSE; + + return intf->dump(denv, oprnd, address); +} + +bool sieve_opr_number_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) + return FALSE; + + return sieve_opr_number_dump_data(denv, &operand, address, field_name); +} + +int sieve_opr_number_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, sieve_number_t *number_r) +{ + const struct sieve_opr_number_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_number(oprnd) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "expected number operand but found %s", sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; + } + + intf = (const struct sieve_opr_number_interface *) oprnd->def->interface; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "number operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + return intf->read(renv, oprnd, address, number_r); +} + +int sieve_opr_number_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, sieve_number_t *number_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0) + return ret; + + return sieve_opr_number_read_data + (renv, &operand, address, field_name, number_r); +} + +static bool opr_number_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + sieve_number_t number = 0; + + if (sieve_binary_read_integer(denv->sblock, address, &number) ) { + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: NUM %llu", oprnd->field_name, + (unsigned long long) number); + else + sieve_code_dumpf(denv, "NUM %llu", (unsigned long long) number); + + return TRUE; + } + + return FALSE; +} + +static int opr_number_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, sieve_number_t *number_r) +{ + if ( !sieve_binary_read_integer(renv->sblock, address, number_r) ) { + sieve_runtime_trace_operand_error(renv, oprnd, "invalid number operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* String */ + +void sieve_opr_string_emit(struct sieve_binary_block *sblock, string_t *str) +{ + (void) sieve_operand_emit(sblock, NULL, &string_operand); + (void) sieve_binary_emit_string(sblock, str); +} + +bool sieve_opr_string_dump_data +(const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name) +{ + const struct sieve_opr_string_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_string(oprnd) ) { + sieve_code_dumpf(denv, "ERROR: INVALID STRING OPERAND %s", + sieve_operand_name(oprnd)); + return FALSE; + } + + intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) { + sieve_code_dumpf(denv, "ERROR: DUMP STRING OPERAND"); + return FALSE; + } + + return intf->dump(denv, oprnd, address); +} + +bool sieve_opr_string_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + return sieve_opr_string_dump_data(denv, &operand, address, field_name); +} + +bool sieve_opr_string_dump_ex +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name, const char *omitted_value) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + sieve_code_dumpf(denv, "ERROR: INVALID OPERAND"); + return FALSE; + } + + if ( omitted_value != NULL && sieve_operand_is_omitted(&operand) ) { + if ( *omitted_value != '\0' ) + sieve_code_dumpf(denv, "%s: %s", field_name, omitted_value); + return TRUE; + } + + return sieve_opr_string_dump_data(denv, &operand, address, field_name); +} + +int sieve_opr_string_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, string_t **str_r) +{ + const struct sieve_opr_string_interface *intf; + + oprnd->field_name = field_name; + + if ( !sieve_operand_is_string(oprnd) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "expected string operand but found %s", sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; + } + + intf = (const struct sieve_opr_string_interface *) oprnd->def->interface; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "string operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + return intf->read(renv, oprnd, address, str_r); +} + +int sieve_opr_string_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, string_t **str_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r); +} + +int sieve_opr_string_read_ex +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, bool optional, string_t **str_r, bool *literal_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + if ( optional && sieve_operand_is_omitted(&operand) ) { + *str_r = NULL; + return 1; + } + + if ( literal_r != NULL ) + *literal_r = sieve_operand_is_string_literal(&operand); + + return sieve_opr_string_read_data(renv, &operand, address, field_name, str_r); +} + +static void _dump_string +(const struct sieve_dumptime_env *denv, string_t *str, + const char *field_name) +{ + if ( str_len(str) > 80 ) { + if ( field_name != NULL ) + sieve_code_dumpf(denv, "%s: STR[%ld] \"%s", + field_name, (long) str_len(str), str_sanitize(str_c(str), 80)); + else + sieve_code_dumpf(denv, "STR[%ld] \"%s", + (long) str_len(str), str_sanitize(str_c(str), 80)); + } else { + if ( field_name != NULL ) + sieve_code_dumpf(denv, "%s: STR[%ld] \"%s\"", + field_name, (long) str_len(str), str_sanitize(str_c(str), 80)); + else + sieve_code_dumpf(denv, "STR[%ld] \"%s\"", + (long) str_len(str), str_sanitize(str_c(str), 80)); + } +} + +bool opr_string_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + string_t *str; + + if ( sieve_binary_read_string(denv->sblock, address, &str) ) { + _dump_string(denv, str, oprnd->field_name); + + return TRUE; + } + + return FALSE; +} + +static int opr_string_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str_r) +{ + if ( !sieve_binary_read_string(renv->sblock, address, str_r) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "invalid string operand"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + return SIEVE_EXEC_OK; +} + +/* String list */ + +void sieve_opr_stringlist_emit_start +(struct sieve_binary_block *sblock, unsigned int listlen, void **context) +{ + sieve_size_t *end_offset = t_new(sieve_size_t, 1); + + /* Emit byte identifying the type of operand */ + (void) sieve_operand_emit(sblock, NULL, &stringlist_operand); + + /* Give the interpreter an easy way to skip over this string list */ + *end_offset = sieve_binary_emit_offset(sblock, 0); + *context = (void *) end_offset; + + /* Emit the length of the list */ + (void) sieve_binary_emit_unsigned(sblock, listlen); +} + +void sieve_opr_stringlist_emit_item +(struct sieve_binary_block *sblock, void *context ATTR_UNUSED, string_t *item) +{ + (void) sieve_opr_string_emit(sblock, item); +} + +void sieve_opr_stringlist_emit_end +(struct sieve_binary_block *sblock, void *context) +{ + sieve_size_t *end_offset = (sieve_size_t *) context; + + (void) sieve_binary_resolve_offset(sblock, *end_offset); +} + +bool sieve_opr_stringlist_dump_data +(const struct sieve_dumptime_env *denv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name) +{ + if ( oprnd == NULL || oprnd->def == NULL ) + return FALSE; + + oprnd->field_name = field_name; + + if ( oprnd->def->class == &stringlist_class ) { + const struct sieve_opr_stringlist_interface *intf = + (const struct sieve_opr_stringlist_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) + return FALSE; + + return intf->dump(denv, oprnd, address); + } else if ( oprnd->def->class == &string_class ) { + const struct sieve_opr_string_interface *intf = + (const struct sieve_opr_string_interface *) oprnd->def->interface; + + if ( intf->dump == NULL ) + return FALSE; + + return intf->dump(denv, oprnd, address); + } + + return FALSE; +} + +bool sieve_opr_stringlist_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + return FALSE; + } + + return sieve_opr_stringlist_dump_data(denv, &operand, address, field_name); +} + +bool sieve_opr_stringlist_dump_ex +(const struct sieve_dumptime_env *denv, sieve_size_t *address, + const char *field_name, const char *omitted_value) +{ + struct sieve_operand operand; + + sieve_code_mark(denv); + + if ( !sieve_operand_read(denv->sblock, address, field_name, &operand) ) { + return FALSE; + } + + if ( omitted_value != NULL && sieve_operand_is_omitted(&operand) ) { + if ( *omitted_value != '\0' ) + sieve_code_dumpf(denv, "%s: %s", field_name, omitted_value); + return TRUE; + } + + return sieve_opr_stringlist_dump_data(denv, &operand, address, field_name); +} + +int sieve_opr_stringlist_read_data +(const struct sieve_runtime_env *renv, struct sieve_operand *oprnd, + sieve_size_t *address, const char *field_name, + struct sieve_stringlist **strlist_r) +{ + if ( oprnd == NULL || oprnd->def == NULL ) + return SIEVE_EXEC_FAILURE; + + oprnd->field_name = field_name; + + if ( oprnd->def->class == &stringlist_class ) { + const struct sieve_opr_stringlist_interface *intf = + (const struct sieve_opr_stringlist_interface *) oprnd->def->interface; + int ret; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + if ( (ret=intf->read(renv, oprnd, address, strlist_r)) <= 0 ) + return ret; + + return SIEVE_EXEC_OK; + } else if ( oprnd->def->class == &string_class ) { + /* Special case, accept single string as string list as well. */ + const struct sieve_opr_string_interface *intf = + (const struct sieve_opr_string_interface *) oprnd->def->interface; + int ret; + + if ( intf->read == NULL ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist string operand not implemented"); + return SIEVE_EXEC_FAILURE; + } + + if ( strlist_r == NULL ) { + if ( (ret=intf->read(renv, oprnd, address, NULL)) <= 0 ) + return ret; + } else { + string_t *stritem; + if ( (ret=intf->read(renv, oprnd, address, &stritem)) <= 0 ) + return ret; + + *strlist_r = sieve_single_stringlist_create + (renv, stritem, FALSE); + } + return SIEVE_EXEC_OK; + } + + sieve_runtime_trace_operand_error(renv, oprnd, + "expected stringlist or string operand but found %s", + sieve_operand_name(oprnd)); + return SIEVE_EXEC_BIN_CORRUPT; +} + +int sieve_opr_stringlist_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, struct sieve_stringlist **strlist_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + return sieve_opr_stringlist_read_data + (renv, &operand, address, field_name, strlist_r); +} + +int sieve_opr_stringlist_read_ex +(const struct sieve_runtime_env *renv, sieve_size_t *address, + const char *field_name, bool optional, struct sieve_stringlist **strlist_r) +{ + struct sieve_operand operand; + int ret; + + if ( (ret=sieve_operand_runtime_read(renv, address, field_name, &operand)) + <= 0 ) + return ret; + + if ( optional && sieve_operand_is_omitted(&operand) ) { + *strlist_r = NULL; + return 1; + } + + return sieve_opr_stringlist_read_data + (renv, &operand, address, field_name, strlist_r); +} + +static bool opr_stringlist_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + sieve_size_t pc = *address; + sieve_size_t end; + unsigned int length = 0; + sieve_offset_t end_offset; + + if ( !sieve_binary_read_offset(denv->sblock, address, &end_offset) ) + return FALSE; + + end = pc + end_offset; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &length) ) + return FALSE; + + return sieve_code_stringlist_dump + (denv, address, length, end, oprnd->field_name); +} + +static int opr_stringlist_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, struct sieve_stringlist **strlist_r) +{ + sieve_size_t pc = *address; + sieve_size_t end; + unsigned int length = 0; + sieve_offset_t end_offset; + + if ( !sieve_binary_read_offset(renv->sblock, address, &end_offset) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist corrupt: invalid end offset"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + end = pc + end_offset; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &length) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "stringlist corrupt: invalid length data"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + if ( strlist_r != NULL ) + *strlist_r = sieve_code_stringlist_create + (renv, *address, (unsigned int) length, end); + + /* Skip over the string list for now */ + *address = end; + + return SIEVE_EXEC_OK; +} + +/* Catenated String */ + +void sieve_opr_catenated_string_emit +(struct sieve_binary_block *sblock, unsigned int elements) +{ + (void) sieve_operand_emit(sblock, NULL, &catenated_string_operand); + (void) sieve_binary_emit_unsigned(sblock, elements); +} + +static bool opr_catenated_string_dump +(const struct sieve_dumptime_env *denv, const struct sieve_operand *oprnd, + sieve_size_t *address) +{ + unsigned int elements = 0; + unsigned int i; + + if ( !sieve_binary_read_unsigned(denv->sblock, address, &elements) ) + return FALSE; + + if ( oprnd->field_name != NULL ) + sieve_code_dumpf(denv, "%s: CAT-STR [%ld]:", + oprnd->field_name, (long) elements); + else + sieve_code_dumpf(denv, "CAT-STR [%ld]:", (long) elements); + + sieve_code_descend(denv); + for ( i = 0; i < (unsigned int) elements; i++ ) { + if ( !sieve_opr_string_dump(denv, address, NULL) ) + return FALSE; + } + sieve_code_ascend(denv); + + return TRUE; +} + +static int opr_catenated_string_read +(const struct sieve_runtime_env *renv, const struct sieve_operand *oprnd, + sieve_size_t *address, string_t **str) +{ + unsigned int elements = 0; + unsigned int i; + int ret; + + if ( !sieve_binary_read_unsigned(renv->sblock, address, &elements) ) { + sieve_runtime_trace_operand_error(renv, oprnd, + "catenated string corrupt: invalid element count data"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + /* Parameter str can be NULL if we are requested to only skip and not + * actually read the argument. + */ + if ( str == NULL ) { + for ( i = 0; i < (unsigned int) elements; i++ ) { + if ( (ret=sieve_opr_string_read(renv, address, NULL, NULL)) <= 0 ) + return ret; + } + } else { + string_t *strelm; + string_t **elm = &strelm; + + *str = t_str_new(128); + for ( i = 0; i < (unsigned int) elements; i++ ) { + + if ( (ret=sieve_opr_string_read(renv, address, NULL, elm)) <= 0 ) + return ret; + + if ( elm != NULL ) { + str_append_str(*str, strelm); + + if ( str_len(*str) > SIEVE_MAX_STRING_LEN ) { + str_truncate(*str, SIEVE_MAX_STRING_LEN); + elm = NULL; + } + } + } + } + + return SIEVE_EXEC_OK; +} + +/* + * Core operations + */ + +/* Forward declarations */ + +static bool opc_jmp_dump + (const struct sieve_dumptime_env *denv, sieve_size_t *address); + +static int opc_jmp_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); +static int opc_jmptrue_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); +static int opc_jmpfalse_execute + (const struct sieve_runtime_env *renv, sieve_size_t *address); + +/* Operation objects defined in this file */ + +const struct sieve_operation_def sieve_jmp_operation = { + .mnemonic = "JMP", + .code = SIEVE_OPERATION_JMP, + .dump = opc_jmp_dump, + .execute = opc_jmp_execute +}; + +const struct sieve_operation_def sieve_jmptrue_operation = { + .mnemonic = "JMPTRUE", + .code = SIEVE_OPERATION_JMPTRUE, + .dump = opc_jmp_dump, + .execute = opc_jmptrue_execute +}; + +const struct sieve_operation_def sieve_jmpfalse_operation = { + .mnemonic = "JMPFALSE", + .code = SIEVE_OPERATION_JMPFALSE, + .dump = opc_jmp_dump, + .execute = opc_jmpfalse_execute +}; + +/* Operation objects defined in other files */ + +extern const struct sieve_operation_def cmd_stop_operation; +extern const struct sieve_operation_def cmd_keep_operation; +extern const struct sieve_operation_def cmd_discard_operation; +extern const struct sieve_operation_def cmd_redirect_operation; + +extern const struct sieve_operation_def tst_address_operation; +extern const struct sieve_operation_def tst_header_operation; +extern const struct sieve_operation_def tst_exists_operation; +extern const struct sieve_operation_def tst_size_over_operation; +extern const struct sieve_operation_def tst_size_under_operation; + +const struct sieve_operation_def *sieve_operations[] = { + NULL, + + &sieve_jmp_operation, + &sieve_jmptrue_operation, + &sieve_jmpfalse_operation, + + &cmd_stop_operation, + &cmd_keep_operation, + &cmd_discard_operation, + &cmd_redirect_operation, + + &tst_address_operation, + &tst_header_operation, + &tst_exists_operation, + &tst_size_over_operation, + &tst_size_under_operation +}; + +const unsigned int sieve_operation_count = + N_ELEMENTS(sieve_operations); + +/* + * Operation functions + */ + +sieve_size_t sieve_operation_emit +(struct sieve_binary_block *sblock, const struct sieve_extension *ext, + const struct sieve_operation_def *op_def) +{ + sieve_size_t address; + + if ( ext != NULL ) { + i_assert( op_def->ext_def != NULL ); + address = sieve_binary_emit_extension + (sblock, ext, sieve_operation_count); + + sieve_binary_emit_extension_object + (sblock, &op_def->ext_def->operations, op_def->code); + return address; + } + + i_assert( op_def->ext_def == NULL ); + return sieve_binary_emit_byte(sblock, op_def->code); +} + +bool sieve_operation_read +(struct sieve_binary_block *sblock, sieve_size_t *address, + struct sieve_operation *oprtn) +{ + unsigned int code = sieve_operation_count; + + oprtn->address = *address; + oprtn->def = NULL; + oprtn->ext = NULL; + + if ( !sieve_binary_read_extension(sblock, address, &code, &oprtn->ext) ) + return FALSE; + + if ( oprtn->ext == NULL ) { + if ( code < sieve_operation_count ) { + oprtn->def = sieve_operations[code]; + } + + return ( oprtn->def != NULL ); + } + + oprtn->def = (const struct sieve_operation_def *) + sieve_binary_read_extension_object(sblock, address, + &oprtn->ext->def->operations); + + return ( oprtn->def != NULL ); +} + +/* + * Jump operations + */ + +/* Code dump */ + +static bool opc_jmp_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + const struct sieve_operation *oprtn = denv->oprtn; + unsigned int pc = *address; + sieve_offset_t offset; + + if ( sieve_binary_read_offset(denv->sblock, address, &offset) ) + sieve_code_dumpf(denv, "%s %d [%08x]", + sieve_operation_mnemonic(oprtn), offset, pc + offset); + else + return FALSE; + + return TRUE; +} + +/* Code execution */ + +static int opc_jmp_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + return sieve_interpreter_program_jump(renv->interp, TRUE, FALSE); +} + +static int opc_jmptrue_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + bool result = sieve_interpreter_get_test_result(renv->interp); + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if result is true"); + sieve_runtime_trace_descend(renv); + + return sieve_interpreter_program_jump(renv->interp, result, FALSE); +} + +static int opc_jmpfalse_execute +(const struct sieve_runtime_env *renv, sieve_size_t *address ATTR_UNUSED) +{ + bool result = sieve_interpreter_get_test_result(renv->interp); + + sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS, "jump if result is false"); + sieve_runtime_trace_descend(renv); + + return sieve_interpreter_program_jump(renv->interp, !result, FALSE); +} |