/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2012-2021 Free Software Foundation, Inc. GNU Mailutils is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Mailutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with GNU Mailutils. If not, see . */ /* This module implements the Editheader Extension (RFC 5293) */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include /* Syntax: addheader [:last] */ int sieve_addheader (mu_sieve_machine_t mach) { const char *field_name; const char *field_value; mu_message_t msg; mu_header_t hdr; int rc; mu_sieve_get_arg (mach, 0, SVT_STRING, &field_name); mu_sieve_get_arg (mach, 1, SVT_STRING, &field_value); mu_sieve_log_action (mach, "ADDHEADER", "%s: %s", field_name, field_value); if (mu_sieve_is_dry_run (mach)) return 0; msg = mu_sieve_get_message (mach); rc = mu_message_get_header (msg, &hdr); if (rc) { mu_sieve_error (mach, "%lu: %s: %s", (unsigned long) mu_sieve_get_message_num (mach), _("cannot get message header"), mu_strerror (rc)); mu_sieve_abort (mach); } rc = (mu_sieve_get_tag (mach, "last", SVT_VOID, NULL) ? mu_header_append : mu_header_prepend) (hdr, field_name, field_value); if (rc) { mu_sieve_error (mach, "%lu: %s: %s", (unsigned long) mu_sieve_get_message_num (mach), _("cannot append message header"), mu_strerror (rc)); mu_sieve_abort (mach); } return 0; } /* Syntax: deleteheader [:index [:last]] [COMPARATOR] [MATCH-TYPE] [] */ int sieve_deleteheader (mu_sieve_machine_t mach) { mu_sieve_value_t *val; const char *field_name; mu_message_t msg; mu_header_t hdr; int rc; mu_sieve_comparator_t comp; mu_iterator_t itr; size_t i, idx = 0; mu_sieve_get_arg (mach, 0, SVT_STRING, &field_name); val = mu_sieve_get_arg_optional (mach, 1); mu_sieve_log_action (mach, "DELETEHEADER", "%s%s", field_name, val ? " (values)" : "" ); if (mu_sieve_is_dry_run (mach)) return 0; msg = mu_sieve_get_message (mach); rc = mu_message_get_header (msg, &hdr); if (rc) { mu_sieve_error (mach, "%lu: %s: %s", (unsigned long) mu_sieve_get_message_num (mach), _("cannot get message header"), mu_strerror (rc)); mu_sieve_abort (mach); } rc = mu_header_get_iterator (hdr, &itr); if (rc) { mu_sieve_error (mach, "mu_header_get_iterator: %s", mu_strerror (rc)); mu_sieve_abort (mach); } if (mu_sieve_get_tag (mach, "last", SVT_VOID, NULL)) { int backwards = 1; mu_iterator_ctl (itr, mu_itrctl_set_direction, &backwards); } comp = mu_sieve_get_comparator (mach); mu_sieve_get_tag (mach, "index", SVT_NUMBER, &idx); for (i = 0, mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr)) { const char *fn, *fv; mu_iterator_current_kv (itr, (const void **)&fn, (void **)&fv); if (strcmp (field_name, fn)) continue; if (idx && ++i < idx) continue; if (val) { for (i = 0; i < val->v.list.count; i++) { mu_sieve_string_t *s = mu_sieve_string_raw (mach, &val->v.list, i); if (comp (mach, s, fv)) { mu_iterator_ctl (itr, mu_itrctl_delete, NULL); break; } } } else mu_iterator_ctl (itr, mu_itrctl_delete, NULL); if (idx) break; } mu_iterator_destroy (&itr); return 0; } /* addheader tagged arguments: */ static mu_sieve_tag_def_t addheader_tags[] = { { "last", SVT_VOID }, { NULL } }; static mu_sieve_tag_group_t addheader_tag_groups[] = { { addheader_tags, NULL }, { NULL } }; /* addheader required arguments: */ static mu_sieve_data_type addheader_args[] = { SVT_STRING, /* field name */ SVT_STRING, /* field value */ SVT_VOID }; #if 0 /* FIXME: The checker interface should be redone. Until then this function is commented out. Problems: 1. Checkers are called per group, there's no way to call them per tag. 2. See FIXMEs in the code. */ static int index_checker (const char *name, mu_list_t tags, mu_list_t args) { mu_iterator_t itr; mu_sieve_runtime_tag_t *match = NULL; int err; if (!tags || mu_list_get_iterator (tags, &itr)) return 0; err = 0; for (mu_iterator_first (itr); !err && !mu_iterator_is_done (itr); mu_iterator_next (itr)) { mu_sieve_runtime_tag_t *t; mu_iterator_current (itr, (void **)&t); if (strcmp (t->tag, "index") == 0) { if (match) { /* FIXME: 1. This function must be public. 2. locus should be included in t */ mu_sv_compile_error (&mu_sieve_locus, _("index specified twice in call to `%s'"), name); err = 1; break; } } } mu_iterator_destroy (&itr); if (err) return 1; if (match) { if (match->arg->v.number < 1) { // See FIXME above mu_sv_compile_error (&mu_sieve_locus, _("invalid index value: %s"), match->arg->v.string); return 1; } } return 0; } #endif static mu_sieve_tag_def_t match_part_tags[] = { { "is", SVT_VOID }, { "contains", SVT_VOID }, { "matches", SVT_VOID }, { "regex", SVT_VOID }, { "count", SVT_STRING }, { "value", SVT_STRING }, { "comparator", SVT_STRING }, { NULL } }; /* deleteheader tagged arguments: */ static mu_sieve_tag_def_t deleteheader_tags[] = { { "last", SVT_VOID }, { "index", SVT_NUMBER }, { NULL } }; static mu_sieve_tag_group_t deleteheader_tag_groups[] = { { deleteheader_tags, NULL }, { match_part_tags, mu_sieve_match_part_checker }, { NULL } }; /* deleteheader required arguments: */ static mu_sieve_data_type deleteheader_args[] = { SVT_STRING, /* field name or value pattern */ SVT_VOID }; int SIEVE_EXPORT (editheader, init) (mu_sieve_machine_t mach) { /* This dummy record is required by libmu_sieve */ mu_sieve_register_action (mach, "editheader", NULL, NULL, NULL, 1); mu_sieve_register_action (mach, "addheader", sieve_addheader, addheader_args, addheader_tag_groups, 1); mu_sieve_register_action_ext (mach, "deleteheader", sieve_deleteheader, deleteheader_args, deleteheader_args, deleteheader_tag_groups, 1); return 0; }