diff options
Diffstat (limited to 'pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c')
-rw-r--r-- | pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c new file mode 100644 index 0000000..dd21c88 --- /dev/null +++ b/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c @@ -0,0 +1,578 @@ +/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file + */ + +#include "lib.h" +#include "unichar.h" +#include "str-sanitize.h" + +#include "sieve-common.h" +#include "sieve-commands.h" +#include "sieve-code.h" +#include "sieve-binary.h" +#include "sieve-validator.h" +#include "sieve-generator.h" +#include "sieve-runtime.h" + +#include "ext-variables-common.h" +#include "ext-variables-limits.h" +#include "ext-variables-modifiers.h" + +#include <ctype.h> + +/* + * Core modifiers + */ + +extern const struct sieve_variables_modifier_def lower_modifier; +extern const struct sieve_variables_modifier_def upper_modifier; +extern const struct sieve_variables_modifier_def lowerfirst_modifier; +extern const struct sieve_variables_modifier_def upperfirst_modifier; +extern const struct sieve_variables_modifier_def quotewildcard_modifier; +extern const struct sieve_variables_modifier_def length_modifier; + +enum ext_variables_modifier_code { + EXT_VARIABLES_MODIFIER_LOWER, + EXT_VARIABLES_MODIFIER_UPPER, + EXT_VARIABLES_MODIFIER_LOWERFIRST, + EXT_VARIABLES_MODIFIER_UPPERFIRST, + EXT_VARIABLES_MODIFIER_QUOTEWILDCARD, + EXT_VARIABLES_MODIFIER_LENGTH +}; + +const struct sieve_variables_modifier_def *ext_variables_core_modifiers[] = { + &lower_modifier, + &upper_modifier, + &lowerfirst_modifier, + &upperfirst_modifier, + "ewildcard_modifier, + &length_modifier +}; + +const unsigned int ext_variables_core_modifiers_count = + N_ELEMENTS(ext_variables_core_modifiers); + +#define ext_variables_modifier_name(modf) \ + (modf)->object->def->name +#define ext_variables_modifiers_equal(modf1, modf2) \ + ( (modf1)->def == (modf2)->def ) +#define ext_variables_modifiers_equal_precedence(modf1, modf2) \ + ( (modf1)->def->precedence == (modf2)->def->precendence ) + +/* + * Modifier registry + */ + +void sieve_variables_modifier_register +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const struct sieve_extension *ext, + const struct sieve_variables_modifier_def *smodf_def) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + sieve_validator_object_registry_add(ctx->modifiers, ext, &smodf_def->obj_def); +} + +bool ext_variables_modifier_exists +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + const char *identifier) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + + return sieve_validator_object_registry_find(ctx->modifiers, identifier, NULL); +} + +const struct sieve_variables_modifier *ext_variables_modifier_create_instance +(const struct sieve_extension *var_ext, struct sieve_validator *valdtr, + struct sieve_command *cmd, const char *identifier) +{ + struct ext_variables_validator_context *ctx = + ext_variables_validator_context_get(var_ext, valdtr); + struct sieve_object object; + struct sieve_variables_modifier *modf; + pool_t pool; + + if ( !sieve_validator_object_registry_find + (ctx->modifiers, identifier, &object) ) + return NULL; + + pool = sieve_command_pool(cmd); + modf = p_new(pool, struct sieve_variables_modifier, 1); + modf->object = object; + modf->var_ext = var_ext; + modf->def = (const struct sieve_variables_modifier_def *) object.def; + + return modf; +} + +void ext_variables_register_core_modifiers +(const struct sieve_extension *ext, struct ext_variables_validator_context *ctx) +{ + unsigned int i; + + /* Register core modifiers*/ + for ( i = 0; i < ext_variables_core_modifiers_count; i++ ) { + sieve_validator_object_registry_add + (ctx->modifiers, ext, &(ext_variables_core_modifiers[i]->obj_def)); + } +} + +/* + * Core modifiers + */ + +/* Forward declarations */ + +static bool +mod_lower_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_upper_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_lowerfirst_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_upperfirst_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_length_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); +static bool +mod_quotewildcard_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result); + +/* Modifier objects */ + +const struct sieve_variables_modifier_def lower_modifier = { + SIEVE_OBJECT("lower", &modifier_operand, EXT_VARIABLES_MODIFIER_LOWER), + 40, + mod_lower_modify +}; + +const struct sieve_variables_modifier_def upper_modifier = { + SIEVE_OBJECT("upper", &modifier_operand, EXT_VARIABLES_MODIFIER_UPPER), + 40, + mod_upper_modify +}; + +const struct sieve_variables_modifier_def lowerfirst_modifier = { + SIEVE_OBJECT + ("lowerfirst", &modifier_operand, EXT_VARIABLES_MODIFIER_LOWERFIRST), + 30, + mod_lowerfirst_modify +}; + +const struct sieve_variables_modifier_def upperfirst_modifier = { + SIEVE_OBJECT + ("upperfirst", &modifier_operand, EXT_VARIABLES_MODIFIER_UPPERFIRST), + 30, + mod_upperfirst_modify +}; + +const struct sieve_variables_modifier_def quotewildcard_modifier = { + SIEVE_OBJECT + ("quotewildcard", &modifier_operand, EXT_VARIABLES_MODIFIER_QUOTEWILDCARD), + 20, + mod_quotewildcard_modify +}; + +const struct sieve_variables_modifier_def length_modifier = { + SIEVE_OBJECT("length", &modifier_operand, EXT_VARIABLES_MODIFIER_LENGTH), + 10, + mod_length_modify +}; + +/* Modifier implementations */ + +static bool +mod_upperfirst_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + content[0] = i_toupper(content[0]); + + return TRUE; +} + +static bool +mod_lowerfirst_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + content[0] = i_tolower(content[0]); + + return TRUE; +} + +static bool +mod_upper_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + (void)str_ucase(content); + + return TRUE; +} + +static bool +mod_lower_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + char *content; + + if ( str_len(in) == 0 ) { + *result = in; + return TRUE; + } + + *result = t_str_new(str_len(in)); + str_append_str(*result, in); + + content = str_c_modifiable(*result); + (void)str_lcase(content); + + return TRUE; +} + +static bool +mod_length_modify(const struct sieve_variables_modifier *modf ATTR_UNUSED, + string_t *in, string_t **result) +{ + *result = t_str_new(64); + str_printfa(*result, "%llu", (unsigned long long) + uni_utf8_strlen_n(str_data(in), str_len(in))); + return TRUE; +} + +static bool +mod_quotewildcard_modify(const struct sieve_variables_modifier *modf, + string_t *in, string_t **result) +{ + size_t max_var_size = + sieve_variables_get_max_variable_size(modf->var_ext); + const unsigned char *p, *poff, *pend; + size_t new_size; + + if ( str_len(in) == 0 ) { + /* empty string */ + *result = in; + return TRUE; + } + + /* allocate new string */ + new_size = str_len(in) + 16; + if (new_size > max_var_size) + new_size = max_var_size; + *result = t_str_new(new_size + 1); + + /* escape string */ + p = str_data(in); + pend = p + str_len(in); + poff = p; + while (p < pend) { + unsigned int n = uni_utf8_char_bytes((char)*p); + + if (n == 1 && (*p == '*' || *p == '?' || *p == '\\')) { + str_append_data(*result, poff, p - poff); + poff = p; + + if (str_len(*result) + 2 > max_var_size) + break; + + str_append_c(*result, '\\'); + } else if ((str_len(*result) + (p - poff) + n) > max_var_size) { + break; + } + if (p + n > pend) { + p = pend; + break; + } + p += n; + } + + str_append_data(*result, poff, p - poff); + + return TRUE; +} + +/* + * Modifier argument + */ + +/* [MODIFIER]: + * ":lower" / ":upper" / ":lowerfirst" / ":upperfirst" / + * ":quotewildcard" / ":length" + */ + +/* Forward declarations */ + +static bool tag_modifier_is_instance_of + (struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **context); + +/* Modifier tag object */ + +static const struct sieve_argument_def modifier_tag = { + .identifier = "MODIFIER", + .flags = SIEVE_ARGUMENT_FLAG_MULTIPLE, + .is_instance_of = tag_modifier_is_instance_of +}; + +/* Modifier tag implementation */ + +static bool tag_modifier_is_instance_of +(struct sieve_validator *valdtr, struct sieve_command *cmd, + const struct sieve_extension *ext, const char *identifier, void **data) +{ + const struct sieve_variables_modifier *modf; + + if ( data == NULL ) { + return ext_variables_modifier_exists(ext, valdtr, identifier); + } + + if ( (modf=ext_variables_modifier_create_instance + (ext, valdtr, cmd, identifier)) == NULL ) + return FALSE; + + *data = (void *) modf; + + return TRUE; +} + +/* Registration */ + +void sieve_variables_modifiers_link_tag +(struct sieve_validator *valdtr, const struct sieve_extension *var_ext, + struct sieve_command_registration *cmd_reg) +{ + sieve_validator_register_tag(valdtr, cmd_reg, var_ext, &modifier_tag, 0); +} + +/* Validation */ + +bool sieve_variables_modifiers_validate +(struct sieve_validator *valdtr, struct sieve_command *cmd, + ARRAY_TYPE(sieve_variables_modifier) *modifiers) +{ + struct sieve_ast_argument *arg; + + arg = sieve_command_first_argument(cmd); + while ( arg != NULL && arg != cmd->first_positional ) { + const struct sieve_variables_modifier *modfs; + const struct sieve_variables_modifier *modf; + unsigned int i, modf_count; + bool inserted; + + if ( !sieve_argument_is(arg, modifier_tag) ) { + arg = sieve_ast_argument_next(arg); + continue; + } + modf = (const struct sieve_variables_modifier *) + arg->argument->data; + + inserted = FALSE; + modfs = array_get(modifiers, &modf_count); + for ( i = 0; i < modf_count && !inserted; i++ ) { + + if ( modfs[i].def->precedence == modf->def->precedence ) { + sieve_argument_validate_error(valdtr, arg, + "modifiers :%s and :%s specified for the set command conflict " + "having equal precedence", + modfs[i].def->obj_def.identifier, modf->def->obj_def.identifier); + return FALSE; + } + + if ( modfs[i].def->precedence < modf->def->precedence ) { + array_insert(modifiers, i, modf, 1); + inserted = TRUE; + } + } + + if ( !inserted ) + array_append(modifiers, modf, 1); + + /* Added to modifier list; + self-destruct to prevent implicit code generation */ + arg = sieve_ast_arguments_detach(arg, 1); + } + return TRUE; +} + +bool sieve_variables_modifiers_generate +(const struct sieve_codegen_env *cgenv, + ARRAY_TYPE(sieve_variables_modifier) *modifiers) +{ + struct sieve_binary_block *sblock = cgenv->sblock; + const struct sieve_variables_modifier *modfs; + unsigned int i, modf_count; + + sieve_binary_emit_byte(sblock, array_count(modifiers)); + + modfs = array_get(modifiers, &modf_count); + for ( i = 0; i < modf_count; i++ ) { + ext_variables_opr_modifier_emit(sblock, + modfs[i].object.ext, modfs[i].def); + } + return TRUE; +} + +/* + * Modifier coding + */ + +const struct sieve_operand_class sieve_variables_modifier_operand_class = + { "modifier" }; + +static const struct sieve_extension_objects core_modifiers = + SIEVE_VARIABLES_DEFINE_MODIFIERS(ext_variables_core_modifiers); + +const struct sieve_operand_def modifier_operand = { + .name = "modifier", + .ext_def = &variables_extension, + .code = EXT_VARIABLES_OPERAND_MODIFIER, + .class = &sieve_variables_modifier_operand_class, + .interface = &core_modifiers +}; + +bool sieve_variables_modifiers_code_dump +(const struct sieve_dumptime_env *denv, sieve_size_t *address) +{ + unsigned int mdfs, i; + + /* Read the number of applied modifiers we need to read */ + if ( !sieve_binary_read_byte(denv->sblock, address, &mdfs) ) + return FALSE; + + /* Print all modifiers (sorted during code generation already) */ + for ( i = 0; i < mdfs; i++ ) { + if ( !ext_variables_opr_modifier_dump(denv, address) ) + return FALSE; + } + return TRUE; +} + +int sieve_variables_modifiers_code_read( + const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, sieve_size_t *address, + ARRAY_TYPE(sieve_variables_modifier) *modifiers) +{ + unsigned int lprec, mdfs, i; + + if ( !sieve_binary_read_byte(renv->sblock, address, &mdfs) ) { + sieve_runtime_trace_error(renv, "invalid modifier count"); + return SIEVE_EXEC_BIN_CORRUPT; + } + + t_array_init(modifiers, mdfs); + + lprec = (unsigned int)-1; + for ( i = 0; i < mdfs; i++ ) { + struct sieve_variables_modifier modf; + + if ( !ext_variables_opr_modifier_read(renv, var_ext, + address, &modf) ) + return SIEVE_EXEC_BIN_CORRUPT; + if ( modf.def != NULL ) { + if ( modf.def->precedence >= lprec ) { + sieve_runtime_trace_error(renv, + "unsorted modifier precedence"); + return SIEVE_EXEC_BIN_CORRUPT; + } + lprec = modf.def->precedence; + } + + array_append(modifiers, &modf, 1); + } + + return SIEVE_EXEC_OK; +} + +/* + * Modifier application + */ + +int sieve_variables_modifiers_apply +(const struct sieve_runtime_env *renv, + const struct sieve_extension *var_ext, + ARRAY_TYPE(sieve_variables_modifier) *modifiers, + string_t **value) +{ + const struct ext_variables_config *config = + ext_variables_get_config(var_ext); + const struct sieve_variables_modifier *modfs; + unsigned int i, modf_count; + + /* Hold value within limits */ + if ( str_len(*value) > config->max_variable_size ) { + /* assume variable originates from code, so copy it first */ + string_t *new_value = t_str_new(config->max_variable_size+3); + str_append_str(new_value, *value); + *value = new_value; + str_truncate_utf8(*value, config->max_variable_size); + } + + if ( !array_is_created(modifiers) ) + return SIEVE_EXEC_OK; + + modfs = array_get(modifiers, &modf_count); + if ( modf_count == 0 ) + return SIEVE_EXEC_OK; + + for ( i = 0; i < modf_count; i++ ) { + string_t *new_value; + const struct sieve_variables_modifier *modf = &modfs[i]; + + if ( modf->def != NULL && modf->def->modify != NULL ) { + if ( !modf->def->modify(modf, *value, &new_value) ) + return SIEVE_EXEC_FAILURE; + + *value = new_value; + if ( *value == NULL ) + return SIEVE_EXEC_FAILURE; + + sieve_runtime_trace_here + (renv, SIEVE_TRLVL_COMMANDS, + "modify :%s \"%s\" => \"%s\"", + sieve_variables_modifier_name(modf), + str_sanitize(str_c(*value), 256), + str_sanitize(str_c(new_value), 256)); + + /* Hold value within limits */ + if ( str_len(*value) > config->max_variable_size ) + str_truncate_utf8(*value, config->max_variable_size); + } + } + return SIEVE_EXEC_OK; +} |