/* 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 /* * 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); }