diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-match.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/sieve-match.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-match.c b/pigeonhole/src/lib-sieve/sieve-match.c new file mode 100644 index 0000000..37d37fe --- /dev/null +++ b/pigeonhole/src/lib-sieve/sieve-match.c @@ -0,0 +1,293 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "mempool.h" +#include "hash.h" +#include "array.h" +#include "str-sanitize.h" + +#include "sieve-extensions.h" +#include "sieve-commands.h" +#include "sieve-stringlist.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-interpreter.h" +#include "sieve-dump.h" +#include "sieve-comparators.h" +#include "sieve-match-types.h" +#include "sieve-runtime-trace.h" + +#include "sieve-match.h" + +/* + * Matching implementation + */ + +struct sieve_match_context *sieve_match_begin +(const struct sieve_runtime_env *renv, + const struct sieve_match_type *mcht, + const struct sieve_comparator *cmp) +{ + struct sieve_match_context *mctx; + pool_t pool; + + /* Reject unimplemented match-type */ + if ( mcht->def == NULL || (mcht->def->match == NULL && + mcht->def->match_keys == NULL && mcht->def->match_key == NULL) ) + return NULL; + + /* Create match context */ + pool = pool_alloconly_create("sieve_match_context", 1024); + mctx = p_new(pool, struct sieve_match_context, 1); + mctx->pool = pool; + mctx->runenv = renv; + mctx->match_type = mcht; + mctx->comparator = cmp; + mctx->exec_status = SIEVE_EXEC_OK; + mctx->trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING); + + /* Trace */ + if ( mctx->trace ) { + sieve_runtime_trace_descend(renv); + sieve_runtime_trace(renv, 0, + "starting `:%s' match with `%s' comparator:", + sieve_match_type_name(mcht), sieve_comparator_name(cmp)); + } + + /* Initialize match type */ + if ( mcht->def != NULL && mcht->def->match_init != NULL ) { + mcht->def->match_init(mctx); + } + + return mctx; +} + +int sieve_match_value +(struct sieve_match_context *mctx, const char *value, size_t value_size, + struct sieve_stringlist *key_list) +{ + const struct sieve_match_type *mcht = mctx->match_type; + const struct sieve_runtime_env *renv = mctx->runenv; + int match, ret; + + if ( mctx->trace ) { + sieve_runtime_trace(renv, 0, + "matching value `%s'", str_sanitize(value, 80)); + } + + /* Match to key values */ + + sieve_stringlist_reset(key_list); + + if ( mctx->trace ) + sieve_stringlist_set_trace(key_list, TRUE); + + sieve_runtime_trace_descend(renv); + + if ( mcht->def->match_keys != NULL ) { + /* Call match-type's own key match handler */ + match = mcht->def->match_keys(mctx, value, value_size, key_list); + } else { + string_t *key_item = NULL; + + /* Default key match loop */ + match = 0; + while ( match == 0 && + (ret=sieve_stringlist_next_item(key_list, &key_item)) > 0 ) { + T_BEGIN { + match = mcht->def->match_key + (mctx, value, value_size, str_c(key_item), str_len(key_item)); + + if ( mctx->trace ) { + sieve_runtime_trace(renv, 0, + "with key `%s' => %d", str_sanitize(str_c(key_item), 80), + match); + } + } T_END; + } + + if ( ret < 0 ) { + mctx->exec_status = key_list->exec_status; + match = -1; + } + } + + sieve_runtime_trace_ascend(renv); + + if ( mctx->match_status < 0 || match < 0 ) + mctx->match_status = -1; + else + mctx->match_status = + ( mctx->match_status > match ? mctx->match_status : match ); + return match; +} + +int sieve_match_end(struct sieve_match_context **mctx, int *exec_status) +{ + const struct sieve_match_type *mcht = (*mctx)->match_type; + const struct sieve_runtime_env *renv = (*mctx)->runenv; + int match = (*mctx)->match_status; + + if ( mcht->def != NULL && mcht->def->match_deinit != NULL ) + mcht->def->match_deinit(*mctx); + + if ( exec_status != NULL ) + *exec_status = (*mctx)->exec_status; + + pool_unref(&(*mctx)->pool); + + sieve_runtime_trace(renv, SIEVE_TRLVL_MATCHING, + "finishing match with result: %s", + ( match > 0 ? "matched" : ( match < 0 ? "error" : "not matched" ) )); + sieve_runtime_trace_ascend(renv); + + return match; +} + +int sieve_match +(const struct sieve_runtime_env *renv, + const struct sieve_match_type *mcht, + const struct sieve_comparator *cmp, + struct sieve_stringlist *value_list, + struct sieve_stringlist *key_list, + int *exec_status) +{ + struct sieve_match_context *mctx; + string_t *value_item = NULL; + int match, ret; + + if ( (mctx=sieve_match_begin(renv, mcht, cmp)) == NULL ) + return 0; + + /* Match value to keys */ + + sieve_stringlist_reset(value_list); + + if ( mctx->trace ) + sieve_stringlist_set_trace(value_list, TRUE); + + if ( mcht->def->match != NULL ) { + /* Call match-type's match handler */ + match = mctx->match_status = + mcht->def->match(mctx, value_list, key_list); + + } else { + /* Default value match loop */ + + match = 0; + while ( match == 0 && + (ret=sieve_stringlist_next_item(value_list, &value_item)) > 0 ) { + + match = sieve_match_value + (mctx, str_c(value_item), str_len(value_item), key_list); + } + + if ( ret < 0 ) { + mctx->exec_status = value_list->exec_status; + match = -1; + } + } + + (void)sieve_match_end(&mctx, exec_status); + return match; +} + +/* + * Reading match operands + */ + +int sieve_match_opr_optional_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address, int *opt_code) +{ + int _opt_code = 0; + bool final = FALSE, opok = TRUE; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + while ( opok ) { + int opt; + + if ( (opt=sieve_opr_optional_dump(denv, address, opt_code)) <= 0 ) + return opt; + + switch ( *opt_code ) { + case SIEVE_MATCH_OPT_COMPARATOR: + opok = sieve_opr_comparator_dump(denv, address); + break; + case SIEVE_MATCH_OPT_MATCH_TYPE: + opok = sieve_opr_match_type_dump(denv, address); + break; + default: + return ( final ? -1 : 1 ); + } + } + + return -1; +} + +int sieve_match_opr_optional_read +(const struct sieve_runtime_env *renv, sieve_size_t *address, int *opt_code, + int *exec_status, struct sieve_comparator *cmp, struct sieve_match_type *mcht) +{ + int _opt_code = 0; + bool final = FALSE; + int status = SIEVE_EXEC_OK; + + if ( opt_code == NULL ) { + opt_code = &_opt_code; + final = TRUE; + } + + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_OK; + + while ( status == SIEVE_EXEC_OK ) { + int opt; + + if ( (opt=sieve_opr_optional_read(renv, address, opt_code)) <= 0 ){ + if ( opt < 0 && exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return opt; + } + + switch ( *opt_code ) { + case SIEVE_MATCH_OPT_COMPARATOR: + if (cmp == NULL) { + sieve_runtime_trace_error(renv, "unexpected comparator operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_comparator_read(renv, address, cmp); + break; + case SIEVE_MATCH_OPT_MATCH_TYPE: + if (mcht == NULL) { + sieve_runtime_trace_error(renv, "unexpected match-type operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + status = sieve_opr_match_type_read(renv, address, mcht); + break; + default: + if ( final ) { + sieve_runtime_trace_error(renv, "invalid optional operand"); + if ( exec_status != NULL ) + *exec_status = SIEVE_EXEC_BIN_CORRUPT; + return -1; + } + return 1; + } + } + + if ( exec_status != NULL ) + *exec_status = status; + return -1; +} + |