Skip to content
Snippets Groups Projects
ext-envelope.c 12.97 KiB
/* Copyright (c) 2002-2008 Dovecot Sieve authors, see the included COPYING file
 */

/* Extension envelope 
 * ------------------
 *
 * Authors: Stephan Bosch
 * Specification: RFC5228
 * Implementation: full
 * Status: experimental, largely untested
 *
 */

#include "lib.h"
#include "str-sanitize.h"
#include "array.h"

#include "sieve-common.h"
#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-code.h"
#include "sieve-address.h"
#include "sieve-comparators.h"
#include "sieve-match-types.h"
#include "sieve-address-parts.h"

#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-interpreter.h"
#include "sieve-dump.h"
#include "sieve-match.h"

/*
 * Forward declarations
 */

static const struct sieve_command envelope_test;
const struct sieve_operation envelope_operation;
const struct sieve_extension envelope_extension;

/* 
 * Extension 
 */

static int ext_my_id;

static bool ext_envelope_load(int ext_id);
static bool ext_envelope_validator_load(struct sieve_validator *validator);

const struct sieve_extension envelope_extension = { 
	"envelope", 
	&ext_my_id,
	ext_envelope_load,
	NULL,
	ext_envelope_validator_load, 
	NULL, NULL, NULL, NULL, NULL,
	SIEVE_EXT_DEFINE_OPERATION(envelope_operation), 
	SIEVE_EXT_DEFINE_NO_OPERANDS 
};

static bool ext_envelope_load(int ext_id) 
{
	ext_my_id = ext_id;
	return TRUE;
}

static bool ext_envelope_validator_load(struct sieve_validator *validator)
{
	/* Register new test */
	sieve_validator_register_command(validator, &envelope_test);

	return TRUE;
}

/* 
 * Envelope test 
 *
 * Syntax
 *   envelope [COMPARATOR] [ADDRESS-PART] [MATCH-TYPE]
 *     <envelope-part: string-list> <key-list: string-list>   
 */

static bool tst_envelope_registered
	(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg);
static bool tst_envelope_validate
	(struct sieve_validator *validator, struct sieve_command_context *tst);
static bool tst_envelope_generate
	(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx);

static const struct sieve_command envelope_test = { 
	"envelope", 
	SCT_TEST, 
	2, 0, FALSE, FALSE,
	tst_envelope_registered, 
	NULL,
	tst_envelope_validate, 
	tst_envelope_generate, 
	NULL 
};

/* 
 * Envelope operation 
 */

static bool ext_envelope_operation_dump
	(const struct sieve_operation *op, 
		const struct sieve_dumptime_env *denv, sieve_size_t *address);
static int ext_envelope_operation_execute
	(const struct sieve_operation *op,
		const struct sieve_runtime_env *renv, sieve_size_t *address);

const struct sieve_operation envelope_operation = { 
	"ENVELOPE",
	&envelope_extension,
	0,
	ext_envelope_operation_dump, 
	ext_envelope_operation_execute 
};

/*
 * Envelope parts
 *
 * FIXME: not available to extensions
 */

struct sieve_envelope_part {
	const char *identifier;

	const struct sieve_address *const *(*get_addresses)
		(const struct sieve_runtime_env *renv);
	const char * const *(*get_values)
		(const struct sieve_runtime_env *renv);
};

static const struct sieve_address *const *_from_part_get_addresses
	(const struct sieve_runtime_env *renv);
static const char *const *_from_part_get_values
	(const struct sieve_runtime_env *renv);
static const struct sieve_address *const *_to_part_get_addresses
	(const struct sieve_runtime_env *renv);
static const char *const *_to_part_get_values
	(const struct sieve_runtime_env *renv);
static const char *const *_auth_part_get_values
	(const struct sieve_runtime_env *renv);

static const struct sieve_envelope_part _from_part = {
	"from",
	_from_part_get_addresses,
	_from_part_get_values,
};

static const struct sieve_envelope_part _to_part = {
	"to",
	_to_part_get_addresses,
	_to_part_get_values,
};	

static const struct sieve_envelope_part _auth_part = {
	"auth",
	NULL,
	_auth_part_get_values,
};	

static const struct sieve_envelope_part *_envelope_parts[] = {
	/* Required */
	&_from_part, &_to_part, 

	/* Non-standard */
	&_auth_part
};

static unsigned int _envelope_part_count = N_ELEMENTS(_envelope_parts);

static const struct sieve_envelope_part *_envelope_part_find
(const char *identifier)
{
	unsigned int i;

	for ( i = 0; i < _envelope_part_count; i++ ) {
		if ( strcasecmp( _envelope_parts[i]->identifier, identifier ) == 0 ) {
			return _envelope_parts[i];
        }
	}
	
	return NULL;
}


/* 
 * Command Registration 
 */

static bool tst_envelope_registered
(struct sieve_validator *validator, struct sieve_command_registration *cmd_reg) 
{
	/* The order of these is not significant */
	sieve_comparators_link_tag(validator, cmd_reg, SIEVE_AM_OPT_COMPARATOR);
	sieve_address_parts_link_tags(validator, cmd_reg, SIEVE_AM_OPT_ADDRESS_PART);
	sieve_match_types_link_tags(validator, cmd_reg, SIEVE_AM_OPT_MATCH_TYPE);
	
	return TRUE;
}

/* 
 * Validation 
 */
 
static int _envelope_part_is_supported
(void *context, struct sieve_ast_argument *arg)
{
	const struct sieve_envelope_part **not_address =
		(const struct sieve_envelope_part **) context;

	if ( sieve_argument_is_string_literal(arg) ) {
		const struct sieve_envelope_part *epart;

		if ( (epart=_envelope_part_find(sieve_ast_strlist_strc(arg))) != NULL ) {
			if ( epart->get_addresses == NULL ) {
				if ( *not_address == NULL )
					*not_address = epart;
			}
					
			return TRUE;
		}
		
		return FALSE;
	} 
	
	return TRUE; /* Can't check at compile time */
}

static bool tst_envelope_validate
(struct sieve_validator *validator, struct sieve_command_context *tst) 
{ 		
	struct sieve_ast_argument *arg = tst->first_positional;
	struct sieve_ast_argument *epart;
	const struct sieve_envelope_part *not_address = NULL;
				
	if ( !sieve_validate_positional_argument
		(validator, tst, arg, "envelope part", 1, SAAT_STRING_LIST) ) {
		return FALSE;
	}
	
	if ( !sieve_validator_argument_activate(validator, tst, arg, FALSE) )
		return FALSE;
		
	/* Check whether supplied envelope parts are supported
	 *   FIXME: verify dynamic envelope parts at runtime 
	 */
	epart = arg;
	if ( !sieve_ast_stringlist_map(&epart, (void *) &not_address, 
		_envelope_part_is_supported) ) {		
		
		sieve_argument_validate_error(validator, epart, 
			"specified envelope part '%s' is not supported by the envelope test", 
				str_sanitize(sieve_ast_strlist_strc(epart), 64));
		return FALSE;
	}

	if ( not_address != NULL ) {
		struct sieve_ast_argument *addrp_arg = 
			sieve_command_find_argument(tst, &address_part_tag);

		if ( addrp_arg != NULL ) {
			sieve_argument_validate_error(validator, addrp_arg,
				"address part ':%s' specified while non-address envelope part '%s' "
				"is tested with the envelope test",
                sieve_ast_argument_tag(addrp_arg), not_address->identifier);
	        return FALSE;
		}
	}
	
	arg = sieve_ast_argument_next(arg);
	
	if ( !sieve_validate_positional_argument
		(validator, tst, arg, "key list", 2, SAAT_STRING_LIST) ) {
		return FALSE;
	}
	
	if ( !sieve_validator_argument_activate(validator, tst, arg, FALSE) )
		return FALSE;

	/* Validate the key argument to a specified match type */
	return sieve_match_type_validate
		(validator, tst, arg, &is_match_type, &i_ascii_casemap_comparator);
}

/*
 * Code generation
 */
 
static bool tst_envelope_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command_context *ctx) 
{
	(void)sieve_operation_emit_code(cgenv->sbin, &envelope_operation);

	/* Generate arguments */
	if ( !sieve_generate_arguments(cgenv, ctx, NULL) )
		return FALSE;

	return TRUE;
}

/* 
 * Code dump 
 */
 
static bool ext_envelope_operation_dump
(const struct sieve_operation *op ATTR_UNUSED, 
	const struct sieve_dumptime_env *denv, sieve_size_t *address)
{
	sieve_code_dumpf(denv, "ENVELOPE");
	sieve_code_descend(denv);

	/* Handle any optional arguments */
	if ( !sieve_addrmatch_default_dump_optionals(denv, address) )
		return FALSE;

	return
		sieve_opr_stringlist_dump(denv, address, "envelope part") &&
		sieve_opr_stringlist_dump(denv, address, "key list");
}

/*
 * Interpretation
 */

static const struct sieve_address *const *_from_part_get_addresses
(const struct sieve_runtime_env *renv)
{
	ARRAY_DEFINE(envelope_values, const struct sieve_address *);
	const struct sieve_address *address =
		sieve_address_parse_envelope_path(renv->msgdata->return_path);
	
	if ( address != NULL ) {
		t_array_init(&envelope_values, 2);

        array_append(&envelope_values, &address, 1);

	    (void)array_append_space(&envelope_values);
    	return array_idx(&envelope_values, 0);
	} 

	return NULL;
}

static const char *const *_from_part_get_values
(const struct sieve_runtime_env *renv)
{
	ARRAY_DEFINE(envelope_values, const char *);

	t_array_init(&envelope_values, 2);

	if ( renv->msgdata->return_path != NULL ) {
        array_append(&envelope_values, &renv->msgdata->return_path, 1);
	}

	(void)array_append_space(&envelope_values);

	return array_idx(&envelope_values, 0);
}

static const struct sieve_address *const *_to_part_get_addresses
(const struct sieve_runtime_env *renv)
{
	ARRAY_DEFINE(envelope_values, const struct sieve_address *);
	const struct sieve_address *address = 
		sieve_address_parse_envelope_path(renv->msgdata->to_address);	

	if ( address != NULL && address->local_part != NULL ) {
		t_array_init(&envelope_values, 2);

        array_append(&envelope_values, &address, 1);

	    (void)array_append_space(&envelope_values);
    	return array_idx(&envelope_values, 0);
	}

	return NULL;
}

static const char *const *_to_part_get_values
(const struct sieve_runtime_env *renv)
{
	ARRAY_DEFINE(envelope_values, const char *);

	t_array_init(&envelope_values, 2);

	if ( renv->msgdata->to_address != NULL ) {
        array_append(&envelope_values, &renv->msgdata->to_address, 1);
	}

	(void)array_append_space(&envelope_values);

	return array_idx(&envelope_values, 0);
}


static const char *const *_auth_part_get_values
(const struct sieve_runtime_env *renv)
{
	ARRAY_DEFINE(envelope_values, const char *);

	t_array_init(&envelope_values, 2);

	if ( renv->msgdata->auth_user != NULL )
        array_append(&envelope_values, &renv->msgdata->auth_user, 1);

	(void)array_append_space(&envelope_values);

	return array_idx(&envelope_values, 0);
}

static int ext_envelope_operation_execute
(const struct sieve_operation *op ATTR_UNUSED,
	const struct sieve_runtime_env *renv, sieve_size_t *address)
{
	bool result = TRUE;
	const struct sieve_comparator *cmp = &i_ascii_casemap_comparator;
	const struct sieve_match_type *mtch = &is_match_type;
	const struct sieve_address_part *addrp = &all_address_part;
	struct sieve_match_context *mctx;
	struct sieve_coded_stringlist *envp_list;
	struct sieve_coded_stringlist *key_list;
	string_t *envp_item;
	bool matched;
	int ret;

	/*
	 * Read operands
	 */
	
	sieve_runtime_trace(renv, "ENVELOPE test");

	if ( (ret=sieve_addrmatch_default_get_optionals
		(renv, address, &addrp, &mtch, &cmp)) <= 0 )
		return ret; 

	/* Read envelope-part */
	if ( (envp_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
		sieve_runtime_trace_error(renv, "invalid envelope-part operand");
		return SIEVE_EXEC_BIN_CORRUPT;
	}

	/* Read key-list */
	if ( (key_list=sieve_opr_stringlist_read(renv, address)) == NULL ) {
		sieve_runtime_trace_error(renv, "invalid key-list operand");
		return SIEVE_EXEC_BIN_CORRUPT;
	}
	
	/* Initialize match */
	mctx = sieve_match_begin(renv->interp, mtch, cmp, NULL, key_list);
	
	/* Iterate through all requested headers to match */
	envp_item = NULL;
	matched = FALSE;
	while ( result && !matched && 
		(result=sieve_coded_stringlist_next_item(envp_list, &envp_item)) 
		&& envp_item != NULL ) {
		const struct sieve_envelope_part *epart;
			
		if ( (epart=_envelope_part_find(str_c(envp_item))) != NULL ) {
			const struct sieve_address * const *addresses = NULL;
			int i;

			if ( epart->get_addresses != NULL ) {
				/* Field contains addresses */
				addresses = epart->get_addresses(renv);

				if ( addresses != NULL ) {
					for ( i = 0; !matched && addresses[i] != NULL; i++ ) {
						if ( addresses[i]->local_part == NULL ) {
							/* Null path <> */
							ret = sieve_match_value(mctx, "", 0);
						} else {
							const char *part = addrp->extract_from(addresses[i]);
							if ( part != NULL ) 
								ret = sieve_match_value(mctx, part, strlen(part));
						}

						if ( ret < 0 ) {
							result = FALSE;
							break;
						}

       	        		matched = ret > 0;
					}
				}
			} 
			if ( epart->get_values != NULL && addresses == NULL && 
				addrp == &all_address_part ) {
				/* Field contains something else */
				const char *const *values = epart->get_values(renv);

				if ( values == NULL ) continue;
	
				for ( i = 0; !matched && values[i] != NULL; i++ ) {				

					if ( (ret=sieve_match_value
						(mctx, values[i], strlen(values[i]))) < 0 ) {
	                    result = FALSE;
    	                break;
        	        }
			
					matched = ret > 0;				
				}
			}
		}
	}
	
	/* Finish match */
	if ( (ret=sieve_match_end(mctx)) < 0 ) 
		result = FALSE;
	else
		matched = ( ret > 0 || matched );

	if ( result ) {
		/* Set test result for subsequent conditional jump */
		sieve_interpreter_set_test_result(renv->interp, matched);
		return SIEVE_EXEC_OK;
	}
	
	sieve_runtime_trace_error(renv, "invalid string-list item");	
	return SIEVE_EXEC_BIN_CORRUPT;
}

Consent

On this website, we use the web analytics service Matomo to analyze and review the use of our website. Through the collected statistics, we can improve our offerings and make them more appealing for you. Here, you can decide whether to allow us to process your data and set corresponding cookies for these purposes, in addition to technically necessary cookies. Further information on data protection—especially regarding "cookies" and "Matomo"—can be found in our privacy policy. You can withdraw your consent at any time.