Skip to content
Snippets Groups Projects
ext-envelope.c 8.66 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 "array.h"

#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-code.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,
	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 
};

/* 
 * 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 const char * const _supported_envelope_parts[] = {
	/* Required */
	"from", "to", 
	
	/* Non-standard */
	"auth", 
	
	NULL  
};

static int _envelope_part_is_supported
(void *context ATTR_UNUSED, struct sieve_ast_argument *arg)
{
	if ( sieve_argument_is_string_literal(arg) ) {
		const char *epart = sieve_ast_strlist_strc(arg);

		const char * const *epsp = _supported_envelope_parts;
		while ( *epsp != NULL ) {
			if ( strcasecmp( *epsp, epart ) == 0 ) 
				return TRUE;

			epsp++;
		}
		
		return FALSE;
	} 
	
	return TRUE;
}

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;
				
	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, NULL, _envelope_part_is_supported) ) {		
		sieve_command_validate_error(validator, tst, 
			"specified envelope part '%s' is not supported by the envelope test", 
				sieve_ast_strlist_strc(epart));
		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);
}

/*
 * 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) &&
		sieve_opr_stringlist_dump(denv, address);
}

/*
 * Interpretation
 */

static int ext_envelope_get_fields
(const struct sieve_message_data *msgdata, const char *field, 
	const char *const **value_r) 
{
	const char *value;
	ARRAY_DEFINE(envelope_values, const char *);
	
 	t_array_init(&envelope_values, 2);
 	
	if ( strcasecmp(field, "from") == 0 )
		value = msgdata->return_path;
	else if ( strcasecmp(field, "to") == 0 )
		value = msgdata->to_address;	
	else if ( strcasecmp(field, "auth") == 0 ) /* Non-standard */ 
		value = msgdata->auth_user;
		
	if ( value != NULL )
		array_append(&envelope_values, &value, 1);
	
	(void)array_append_space(&envelope_values);
	*value_r = array_idx(&envelope_values, 0);

	return 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_octet_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 char *epart = str_c(envp_item);
		const char *const *fields;
			
		if ( ext_envelope_get_fields(renv->msgdata, epart, &fields) >= 0 ) {	
			/* From RFC 5228: 5.4.  Test envelope :
			 *   The null reverse-path is matched against as the empty string, 
			 *   regardless of the ADDRESS-PART argument specified.
			 */
			if ( strcmp(epart, "from") == 0 && 
				(fields[0] == NULL || **fields == '\0') ) {
				
				if ( (ret=sieve_match_value(mctx, "", 0)) < 0 ) {
					result = FALSE;
					break;
				}
			
				matched = ret > 0;
			} else {
				int i;
				for ( i = 0; !matched && fields[i] != NULL; i++ ) {
					if ( (ret=sieve_address_match(addrp, mctx, fields[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.