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