aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-07-02 14:35:10 +0300
committerSergey Poznyakoff <gray@gnu.org>2020-07-02 14:48:27 +0300
commit14e0558e6051671d9ebc8083467e021dfd81780a (patch)
tree20510691091f71d6f8371aa1f88693e118b78fbb
parent643a00fef4328566c437f9f3db293ad764f00e58 (diff)
downloadmailfromd-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--NEWS9
-rw-r--r--doc/functions.texi74
-rw-r--r--mflib/header_rename.mf4128
-rw-r--r--src/builtin/header.bi32
-rw-r--r--src/builtin/vars.bi14
-rw-r--r--src/engine.c129
-rw-r--r--src/gram.y6
-rw-r--r--src/mailfromd.h5
8 files changed, 152 insertions, 245 deletions
diff --git a/NEWS b/NEWS
index f3a582d3..1ba2844d 100644
--- a/NEWS
+++ b/NEWS
@@ -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;
diff --git a/src/gram.y b/src/gram.y
index 5403484f..fbc894af 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -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);

Return to:

Send suggestions and report system problems to the System administrator.