summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/plugins/variables/ext-variables-modifiers.c
diff options
context:
space:
mode:
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.c578
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,
+ &quotewildcard_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;
+}