/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file */ #include "lib.h" #include "mempool.h" #include "array.h" #include "rfc2822.h" #include "sieve-common.h" #include "sieve-error.h" #include "sieve-settings.h" #include "sieve-extensions.h" #include "ext-editheader-limits.h" #include "ext-editheader-common.h" /* * Extension configuration */ struct ext_editheader_header { const char *name; bool forbid_add:1; bool forbid_delete:1; }; struct ext_editheader_config { pool_t pool; ARRAY(struct ext_editheader_header) headers; size_t max_header_size; }; static struct ext_editheader_header * ext_editheader_config_header_find(struct ext_editheader_config *ext_config, const char *hname) { struct ext_editheader_header *headers; unsigned int count, i; headers = array_get_modifiable(&ext_config->headers, &count); for (i = 0; i < count; i++) { if (strcasecmp(hname, headers[i].name) == 0) return &headers[i]; } return NULL; } static void ext_editheader_config_headers(struct sieve_instance *svinst, struct ext_editheader_config *ext_config, const char *setting, bool forbid_add, bool forbid_delete) { const char *setval; setval = sieve_setting_get(svinst, setting); if (setval != NULL) { const char **headers = t_strsplit_spaces(setval, " \t"); while (*headers != NULL) { struct ext_editheader_header *header; if (!rfc2822_header_field_name_verify( *headers, strlen(*headers))) { e_warning(svinst->event, "editheader: " "setting %s contains invalid header field name " "`%s' (ignored)", setting, *headers); headers++; continue; } header = ext_editheader_config_header_find( ext_config, *headers); if (header == NULL) { header = array_append_space( &ext_config->headers); header->name = p_strdup(ext_config->pool, *headers); } if (forbid_add) header->forbid_add = TRUE; if (forbid_delete) header->forbid_delete = TRUE; headers++; } } } bool ext_editheader_load(const struct sieve_extension *ext, void **context) { struct ext_editheader_config *ext_config; struct sieve_instance *svinst = ext->svinst; size_t max_header_size; pool_t pool; if (*context != NULL) { ext_editheader_unload(ext); *context = NULL; } T_BEGIN { pool = pool_alloconly_create("editheader_config", 1024); ext_config = p_new(pool, struct ext_editheader_config, 1); ext_config->pool = pool; ext_config->max_header_size = EXT_EDITHEADER_DEFAULT_MAX_HEADER_SIZE; p_array_init(&ext_config->headers, pool, 16); ext_editheader_config_headers( svinst, ext_config, "sieve_editheader_protected", TRUE, TRUE); ext_editheader_config_headers( svinst, ext_config, "sieve_editheader_forbid_add", TRUE, FALSE); ext_editheader_config_headers( svinst, ext_config, "sieve_editheader_forbid_delete", FALSE, TRUE); if (sieve_setting_get_size_value( svinst, "sieve_editheader_max_header_size", &max_header_size)) { if (max_header_size < EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE) { e_warning(svinst->event, "editheader: " "value of sieve_editheader_max_header_size setting " "(=%zu) is less than the minimum (=%zu) " "(ignored)", max_header_size, (size_t)EXT_EDITHEADER_MINIMUM_MAX_HEADER_SIZE); } else { ext_config->max_header_size = max_header_size; } } } T_END; *context = (void *)ext_config; return TRUE; } void ext_editheader_unload(const struct sieve_extension *ext) { struct ext_editheader_config *ext_config = (struct ext_editheader_config *)ext->context; if (ext_config != NULL) pool_unref(&ext_config->pool); } /* * Protected headers */ bool ext_editheader_header_allow_add(const struct sieve_extension *ext, const char *hname) { struct ext_editheader_config *ext_config = (struct ext_editheader_config *)ext->context; const struct ext_editheader_header *header; if (strcasecmp(hname, "subject") == 0) return TRUE; if (strcasecmp(hname, "x-sieve-redirected-from") == 0) return FALSE; header = ext_editheader_config_header_find(ext_config, hname); if (header == NULL) return TRUE; return !header->forbid_add; } bool ext_editheader_header_allow_delete(const struct sieve_extension *ext, const char *hname) { struct ext_editheader_config *ext_config = (struct ext_editheader_config *)ext->context; const struct ext_editheader_header *header; if (strcasecmp(hname, "received") == 0 || strcasecmp(hname, "auto-submitted") == 0) return FALSE; if (strcasecmp(hname, "x-sieve-redirected-from") == 0) return FALSE; if (strcasecmp(hname, "subject") == 0) return TRUE; header = ext_editheader_config_header_find(ext_config, hname); if (header == NULL) return TRUE; return !header->forbid_delete; } /* * Limits */ bool ext_editheader_header_too_large(const struct sieve_extension *ext, size_t size) { struct ext_editheader_config *ext_config = (struct ext_editheader_config *)ext->context; return size > ext_config->max_header_size; }