diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-07-02 14:35:10 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-07-02 14:48:27 +0300 |
commit | 14e0558e6051671d9ebc8083467e021dfd81780a (patch) | |
tree | 20510691091f71d6f8371aa1f88693e118b78fbb | |
parent | 643a00fef4328566c437f9f3db293ad764f00e58 (diff) | |
download | mailfromd-14e0558e6051671d9ebc8083467e021dfd81780a.tar.gz mailfromd-14e0558e6051671d9ebc8083467e021dfd81780a.tar.bz2 |
Remove header_delete_nth and header_replace_nth.
Both functions were unable to do their work properly, due to the
deficiences of the Milter API.
Rewrite header_rename, header_prefix_all and header_prefix_pattern.
* NEWS: Update.
* doc/functions.texi: Update discussion of header modification
functions.
* mflib/header_rename.mf4: Rewrite.
* src/builtin/header.bi (header_delete_nth)
(header_replace_nth): Remove.
* src/builtin/vars.bi (sendmail_header_count): Remove.
(get_sendmail_header_count,set_sendmail_header_count): Remove.
* src/engine.c (message_data): Remove members: hdrtrans, hdrcount.
All uses changed.
(md_hdrtrans_fill,md_hdrtrans): Remove.
* src/mailfromd.h (header_delete_nth,header_replace_nth): Remove.
All uses changed.
-rw-r--r-- | NEWS | 9 | ||||
-rw-r--r-- | doc/functions.texi | 74 | ||||
-rw-r--r-- | mflib/header_rename.mf4 | 128 | ||||
-rw-r--r-- | src/builtin/header.bi | 32 | ||||
-rw-r--r-- | src/builtin/vars.bi | 14 | ||||
-rw-r--r-- | src/engine.c | 129 | ||||
-rw-r--r-- | src/gram.y | 6 | ||||
-rw-r--r-- | src/mailfromd.h | 5 |
8 files changed, 152 insertions, 245 deletions
@@ -1,4 +1,4 @@ -Mailfromd NEWS -- history of user-visible changes. 2020-05-26 +Mailfromd NEWS -- history of user-visible changes. 2020-07-02 See the end of file for copying conditions. Please send Mailfromd bug reports to <bug-mailfromd@gnu.org.ua> @@ -15,6 +15,11 @@ signing the current message using DKIM. The typical use is dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem") done +* Functions header_delete_nth, header_replace_nth were removed + +There's no reliable way to address Nth header in the message using the +Milter API. + Version 8.7, 2019-01-03 @@ -28,7 +33,7 @@ overrides. This option is used by mtasim to avoid clobbering the existing callout sockets when starting new mailfromd instance. -* NS lookup NFL functions +* NS lookup MFL functions This release implements the following new MFL functions: diff --git a/doc/functions.texi b/doc/functions.texi index ccf5e39d..b9588998 100644 --- a/doc/functions.texi +++ b/doc/functions.texi @@ -785,23 +785,33 @@ actions, header functions allow to insert a new header into a particular place. @deftypefn {Built-in Function} void header_add (string @var{name}, @ - string @var{value} [, number @var{idx}]) -Adds a header @samp{@var{name}: @var{value}} to the message. If -@var{idx} is given, it specifies a 0-based index in the header list -where to insert this header. - -If @var{idx} is not supplied, the header is appended to the end of the -header list. + string @var{value}) +Adds a header @samp{@var{name}: @var{value}} to the message. In contrast to the @code{add} action, this function allows to construct the header name using arbitrary @acronym{MFL} expressions. @end deftypefn +@deftypefn {Built-in Function} void header_add (string @var{name}, @ + string @var{value}, number @var{idx}) +This syntax is preserved for backward compatibility. It is equivalent +to @code{header_insert}, which see. +@end deftypefn + @deftypefn {Built-in Function} void header_insert (string @var{name}, @ string @var{value}, number @var{idx}) -This function is equivalent to @code{header_add} with three arguments, -i.e. it inserts a header @samp{@var{name}: @samp{value}} at -@var{idx}th header position in the message. +This function inserts a header @samp{@var{name}: @samp{value}} at +@var{idx}th header position in the internal list of headers maintained +by the MTA. That list contains headers added to the message either by +the filter or by the MTA itself, but not the headers included in the +message itself. Some of the headers in this list are conditional, +e.g. the ones added by the @samp{H?@var{cond}?} directive in +@file{sendmail.cf}. MTA evaluates them after all header modifications +have been done and removes those of headers for which they yield false. +This means that the position at which the header added by +@code{header_insert} will appear in the final message will differ from +@var{idx}. + @end deftypefn @deftypefn {Built-in Function} void header_delete (string @var{name} @ @@ -838,15 +848,6 @@ instance to replace. @end enumerate @end deftypefn -@deftypefn {Built-in Function} void header_delete_nth (number @var{n}) -Deletes @var{n}th header. Headers are numbered from 1. -@end deftypefn - -@deftypefn {Built-in Function} void header_replace_nth (number @var{n}, @ - string @var{name}, string @var{value}) -Replaces @var{n}th header with @samp{@var{name}: @var{value}}. -@end deftypefn - @deftypefn {Library Function} void header_rename (string @var{name}, @ string @var{newname}[, number @var{idx}]) @flindex header_rename.mf @@ -856,6 +857,12 @@ Replaces @var{n}th header with @samp{@var{name}: @var{value}}. Renames the @var{idx}th instance of header @var{name} to @var{newname}. If @var{idx} is not given, assumes 1. +If the specified header or the @var{idx} instance of it is not present +in the current message, the function silently returns. All other +errors cause run-time exception. + +The position of the renamed header in the header list is not preserved. + The example below renames @samp{Subject} header to @samp{X-Old-Subject}: @smallexample @@ -874,22 +881,32 @@ done @*Defined in the module @file{header_rename.mf}. @*Available only in the @samp{eom} handler. -If @var{prefix} is given, rename all headers named @var{name} to -@samp{@var{prefix}-@var{name}}. Otherwise, remove all such headers. -@end deftypefn +Renames all headers named @var{name} by prefixing them with +@var{prefix}. If @var{prefix} is not supplied, removes all such +headers. + +All renamed headers will be placed in a continuous block in the header +list. The absolute position in the header list will change. Relative +ordering of renamed headers will be preserved. +@end deftypefn @deftypefn {Library Function} void header_prefix_pattern (string @var{pattern}, string @var{prefix}) @flindex header_rename.mf @*Defined in the module @file{header_rename.mf}. @*Available only in the @samp{eom} handler. -If @var{prefix} is given, rename all headers whose names match -@var{pattern} (in the sense of @code{fnmatch}, @pxref{Special -comparisons, fnmatches}) to @samp{@var{prefix}-@var{name}}. -Otherwise, remove them. +Renames all headers with names matching @var{pattern} (in the sense of +@code{fnmatch}, @pxref{Special comparisons, fnmatches}) by prefixing +them with @var{prefix}. + +All renamed headers will be placed in a continuous block in the header +list. The absolute position in the header list will change. Relative +ordering of renamed headers will be preserved. -For example, to prefix all headers whose names begin with -@samp{X-Spamd-} with an additional @samp{X-}: +If called with one argument, removes all headers matching @var{pattern}. + +For example, to prefix all headers beginning with @samp{X-Spamd-} with +an additional @samp{X-}: @smallexample require 'header_rename' @@ -901,7 +918,6 @@ done @end smallexample @end deftypefn - @node Body Modification Functions @section Body Modification Functions diff --git a/mflib/header_rename.mf4 b/mflib/header_rename.mf4 index 614c66d4..289e828f 100644 --- a/mflib/header_rename.mf4 +++ b/mflib/header_rename.mf4 @@ -15,6 +15,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ module 'header_rename'. +require status func header_rename(string name, string newname; number idx) do @@ -22,61 +23,128 @@ do set idx 1 fi number msg current_message() - number count message_header_count(msg) - string lc tolower(name) - loop for number i 1, - while i < count, - set i i + 1 + try do - if tolower(message_nth_header_name(msg, i)) == lc - set idx idx - 1 - if idx == 0 - header_replace_nth(i, newname, message_nth_header_value(msg, i)) - return - fi - fi + string value message_find_header(msg, name, idx) + header_delete(name, idx) + header_add(newname, value) + done + catch e_range + do + pass done done -/* If PREFIX is specified, rename all headers named NAME to - PREFIXNAME. Otherwise, remove all such headers. */ -func header_prefix_all(string pattern; string prefix) +/* + * Prefixes all headers with the given NAME with PREFIX. + * If PREFIX is not supplied, removes the matching headers. + */ +func header_prefix_all(string name; string prefix) do number msg current_message() - number count message_header_count(msg) - string lc tolower(pattern) - loop for number i count, + loop for number i message_header_count(msg, name), while i >= 1, set i i - 1 do - string name message_nth_header_name(msg, i) - if tolower(name) == lc - if defined(prefix) - header_replace_nth(i, prefix.name, message_nth_header_value(msg, i)) - else - header_delete_nth(i) - fi + if defined(prefix) + header_rename(name, prefix . name, i) + else + header_delete(name, i) fi done done -/* If PREFIX is specified, rename all headers matching PATTERN to - PREFIXNAME. Otherwise, remove them. */ +/* + * header_prefix_pattern(string pattern; string prefix) + * ---------------------------------------------------- + * Prefixes all headers that match PATTERN with PREFIX + * + * Implementing such functionality faces considerable difficulties because + * the milter API doesn't provide calls for iterating over all message + * headers. + * The basic algorithm is: + * 1. get the list of available messages from the captured message + * 2. for each header that matches pattern + * 2.1. save its value + * 2.2. delete that header + * 2.3. insert new header with the name constructed as concatenation + * or the prefix and original header name and value saved in 2.1 + * + * The 2.3 should ideally preserve locations of the renamed headers. + * However, that is impossible using the Milter API. It was consented then + * that it should preserve relative ordering of the renamed headers. + * + * The initial approach was to use header_add function. Unfortunately, it + * follows whimsical rules when selecting the position where to insert the + * header. Normally, headers are added to the end of the list. However, if + * a header with such name already exists, the new one is added before it. + * The flags assigned to the header (H_USER and H_TRACE, in Sendmail) alter + * that decision. Consequently, using header_add produces unjustifiably + * complex code. + * + * Then, there is header_insert. Formally, it inserts the header at the given + * index in the header list. However, the header list it operates on is not + * the list of headers as present in the message, but an opaque internal list + * maintained in MTA, whose contents is unknown to the filter and is updated + * after the filter finishes its job. The only more or less reliable way of + * using this call is with index 0, which is supposed to add the header at + * the beginning of that internal list. At least, this ensures that the + * relative ordering of the renamed headers is preserved. + * Obviously the renamed headers must be inserted in the reverse order. + * + * So, the modified algorithm is: + * 1. Build a temporary list of headers that match the pattern. Each + * element of the list contains the header name and its instance number. + * The list is sorted in reverse order: the last header in the message + * appears at the start of the list. + * Here, the difficulty is the lack of dictionary data type in MFL, so + * the list is built as colon-delimited list of header specifications. + * Each specification is HDR/N, where HDR is the header name and N is + * its instance number. + * 2. Iterate over that list using the `string_list_iterate' macro. For + * each element in the list + * 2.1. save the header value + * 2.2. delete that header + * 2.3. insert the renamed header in position 0. + */ +#pragma regex push +extended func header_prefix_pattern(string pattern; string prefix) do number msg current_message() number count message_header_count(msg) + string h_list ':' + loop for number i 1, while i <= count, set i i + 1 do string name message_nth_header_name(msg, i) if name fnmatches pattern - if defined(prefix) - header_replace_nth(i, prefix.name, message_nth_header_value(msg, i)) + if h_list matches ".*:%name/([[:digit:]]+):.*" + set h_list ":%name/" . (\1 + 1) . h_list else - header_delete_nth(i) + set h_list ":%name/1" . h_list fi fi done + + if h_list matches '^:(.+):$' + set h_list \1 + fi + + string_list_iterate(h_list, ":", h_spec, ` + if h_spec matches "^(.+)/([[:digit:]]+)$" + string value message_find_header(msg, \1, \2) + header_delete(\1, \2) + if defined(prefix) + header_add(prefix . \1, value, 0) + fi + fi') done + +#pragma regex pop + + + + +
\ No newline at end of file diff --git a/src/builtin/header.bi b/src/builtin/header.bi index a66d1885..053592c7 100644 --- a/src/builtin/header.bi +++ b/src/builtin/header.bi @@ -72,22 +72,6 @@ MF_DEFUN(header_delete, VOID, STRING name, OPTIONAL, NUMBER idx) } END -MF_CAPTURE -MF_DEFUN(header_delete_nth, VOID, NUMBER idx) -{ - struct mu_locus_range locus; - - env_get_locus(env, &locus); - - trace("%s%s:%u: %s %lu", - mailfromd_msgid(env_get_context(env)), - locus.beg.mu_file, locus.beg.mu_line, - msgmod_opcode_str(header_delete_nth), - idx); - env_msgmod(env, header_delete_nth, NULL, NULL, idx); -} -END - MF_DEFUN(header_replace, VOID, STRING name, STRING value, OPTIONAL, NUMBER idx) { struct mu_locus_range locus; @@ -103,19 +87,3 @@ MF_DEFUN(header_replace, VOID, STRING name, STRING value, OPTIONAL, NUMBER idx) } END -MF_CAPTURE -MF_DEFUN(header_replace_nth, VOID, NUMBER idx, STRING name, STRING value) -{ - struct mu_locus_range locus; - - env_get_locus(env, &locus); - - trace("%s%s:%u: %s \"%s: %s\" (%lu)", - mailfromd_msgid(env_get_context(env)), - locus.beg.mu_file, locus.beg.mu_line, - msgmod_opcode_str(header_replace_nth), - name, value, idx); - env_msgmod(env, header_replace_nth, name, value, idx); -} -END - diff --git a/src/builtin/vars.bi b/src/builtin/vars.bi index df34a65d..86454673 100644 --- a/src/builtin/vars.bi +++ b/src/builtin/vars.bi @@ -23,7 +23,6 @@ MF_VAR(milter_client_address, STRING, SYM_PRECIOUS); MF_VAR(milter_server_family, NUMBER, SYM_PRECIOUS); MF_VAR(milter_server_address, STRING, SYM_PRECIOUS); MF_VAR(milter_server_id, STRING, SYM_PRECIOUS); -MF_VAR(sendmail_header_count, NUMBER, SYM_PRECIOUS); /* Functions to access %rcpt_count */ unsigned long @@ -44,19 +43,6 @@ incr_rcpt_count(eval_environ_t env) MF_VAR_INC(rcpt_count); } -/* Same for sendmail_header_count */ -unsigned long -get_sendmail_header_count(eval_environ_t env) -{ - return MF_VAR_REF(sendmail_header_count, long); -} - -void -set_sendmail_header_count(eval_environ_t env, unsigned long v) -{ - MF_VAR_REF(sendmail_header_count, ulong, v); -} - /* define_milter_address name */ m4_define([<define_milter_address>],[< MF_DSEXP_SUPPRESS([<set_milter_$1_address>], diff --git a/src/engine.c b/src/engine.c index 877ba56d..9dc736a5 100644 --- a/src/engine.c +++ b/src/engine.c @@ -46,25 +46,11 @@ static void ctx_msgmod(void *data, struct msgmod_closure *cmd); /* Per-message data */ -/* Header translation table is used to translate absolute header number - into Sendmail notation (name, idx), where name is the header name and - idx is 1-based number of occurrence of the header with that name. - - This translation is needed for two functions provided by Mailfromd: - header_delete_nth and header_replace_nth. -*/ -struct hdrtrans { - const char *name; - size_t idx; -}; - struct message_data { eval_environ_t env; /* Evaluation environment */ mu_list_t mmq; /* Message Modification Queue */ char *helostr; /* Domain name obtained in HELO phase */ char msgid[64]; /* Message ID */ - struct hdrtrans *hdrtrans; /* Header translation table */ - size_t hdrcount; /* No. of entries in hdrtrans */ }; static struct message_data *test_message_data; @@ -76,8 +62,6 @@ test_message_data_init(eval_environ_t env) test_message_data->mmq = NULL; test_message_data->helostr = NULL; test_message_data->msgid[0] = 0; - test_message_data->hdrtrans = NULL; - test_message_data->hdrcount = 0; } static struct message_data * @@ -107,8 +91,6 @@ priv_get(SMFICTX *ctx) md->mmq = NULL; md->helostr = NULL; md->msgid[0] = 0; - md->hdrtrans = NULL; - md->hdrcount = 0; gacopyz_setpriv(ctx, md); env_init(md->env); if (gacopyz_server_sockname(ctx, &addr, &len) == 0) @@ -267,7 +249,6 @@ capture_eom(eval_environ_t env) } - /* Cleanup functions */ void filter_cleanup(SMFICTX *ctx) @@ -280,7 +261,6 @@ filter_cleanup(SMFICTX *ctx) free(md->helostr); destroy_environment(md->env); mu_list_destroy(&md->mmq); - free(md->hdrtrans); free(md); gacopyz_setpriv(ctx, NULL); } @@ -615,62 +595,10 @@ xlate_and_replace_body(SMFICTX *ctx, const char *value, size_t size) } static int -md_hdrtrans_fill(struct message_data *md) -{ - int rc; - mu_header_t hdr; - size_t count, i, j; - - if (env_get_header(md->env, &hdr)) - return 1; - rc = mu_header_get_field_count(hdr, &count); - if (rc) { - mu_diag_funcall(MU_DIAG_ERROR, - "mu_header_get_field_count", - NULL, rc); - return 1; - } - md->hdrtrans = mu_calloc(count, sizeof(md->hdrtrans[0])); - for (i = 1; i <= count; i++) { - const char *s; - rc = mu_header_sget_field_name(hdr, i, &s); - if (rc) { - mu_diag_funcall(MU_DIAG_ERROR, - "mu_header_sget_field_name", - NULL, rc); - return 1; - } - md->hdrtrans[i-1].name = s; - md->hdrtrans[i-1].idx = 1; - for (j = i - 1; j > 0; j--) - if (mu_c_strcasecmp(md->hdrtrans[j-1].name, s) == 0) { - md->hdrtrans[i-1].idx = - md->hdrtrans[j-1].idx + 1; - break; - } - } - md->hdrcount = count; - return 0; -} - -static int -md_hdrtrans(struct message_data *md, struct msgmod_closure *hdr) -{ - if (!md->hdrtrans && md_hdrtrans_fill(md)) - return -1; - if (hdr->idx == 0 || hdr->idx > md->hdrcount) - return 1; - hdr->name = (char*) md->hdrtrans[hdr->idx - 1].name; - hdr->idx = md->hdrtrans[hdr->idx - 1].idx; - return 0; -} - -static int run_msgmod(void *item, void *data) { - struct msgmod_closure *hdr = item, thdr; + struct msgmod_closure *hdr = item; SMFICTX *ctx = data; - struct message_data *md; mu_debug(MF_SOURCE_ENGINE, MU_DEBUG_TRACE6, ("%s %s: %s %u", @@ -691,25 +619,7 @@ run_msgmod(void *item, void *data) break; case header_insert: - /* Note: The get_sendmail_header_count() call returns the - number of headers defined by Sendmail (or other MTA) itself. - I have found no reliable method allowing to determine - that number automatically. In the absence of such method, - the user has to define the header count explicitly, by - setting the sendmail_header_count variable. For Sendmail, - a good estimate is given by the following command: - - grep -c ^H /etc/mail/sendmail.cf - - Notice, however, that some of the headers may be defined - conditionally, see the section 5.5. "H -- Define Header", - in the Sendmail(tm) Installation and Operation Guide, for - more information. - */ - md = priv_get(ctx); - gacopyz_insert_header(ctx, - hdr->idx + get_sendmail_header_count(md->env), - hdr->name, hdr->value); + gacopyz_insert_header(ctx, hdr->idx, hdr->name, hdr->value); break; case rcpt_add: @@ -737,41 +647,6 @@ run_msgmod(void *item, void *data) } break; - case header_replace_nth: - md = priv_get(ctx); - thdr = *hdr; - switch (md_hdrtrans(md, &thdr)) { - case -1: - return 1; - case 1: - return 0; - default: - break; - } - /* Remove old header */ - gacopyz_change_header(ctx, thdr.idx, thdr.name, NULL); - /* Insert the new one. For the meaning of - get_sendmail_header_count, see the comment to the - header_insert case above. - */ - gacopyz_insert_header(ctx, - hdr->idx + get_sendmail_header_count(md->env), - hdr->name, hdr->value); - break; - - case header_delete_nth: - thdr = *hdr; - switch (md_hdrtrans(priv_get(ctx), &thdr)) { - case -1: - return 1; - case 1: - return 0; - default: - break; - } - gacopyz_change_header(ctx, thdr.idx, thdr.name, NULL); - break; - case set_from: gacopyz_chgfrom(ctx, hdr->name, hdr->value); break; @@ -3303,12 +3303,6 @@ msgmod_opcode_str(enum msgmod_opcode opcode) case body_repl_fd: return "REPLACE BODY FROM FILE"; - case header_replace_nth: - return "REPLACE NTH HEADER"; - - case header_delete_nth: - return "DELETE NTH HEADER"; - case set_from: return "SET FROM"; diff --git a/src/mailfromd.h b/src/mailfromd.h index 59d0f839..520ea52a 100644 --- a/src/mailfromd.h +++ b/src/mailfromd.h @@ -218,8 +218,6 @@ enum msgmod_opcode { /* Message modification operation */ quarantine, /* Quarantine a message */ body_repl, /* Replace message body */ body_repl_fd, /* Replace message body from file */ - header_delete_nth, /* Delete Nth header */ - header_replace_nth, /* Replace Nth header */ set_from /* Change envelope sender */ }; @@ -757,9 +755,6 @@ unsigned long get_rcpt_count(eval_environ_t env); void clear_rcpt_count(eval_environ_t env); void incr_rcpt_count(eval_environ_t env); -unsigned long get_sendmail_header_count(eval_environ_t env); -void set_sendmail_header_count(eval_environ_t env, unsigned long v); - void set_last_poll_helo(eval_environ_t env, const char *text); void set_last_poll_greeting(eval_environ_t env, const char *text); void set_last_poll_host(eval_environ_t env, const char *host_addr); |