aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--doc/mailfromd.texi96
-rw-r--r--src/main.c392
-rw-r--r--tests/atlocal.in2
4 files changed, 435 insertions, 62 deletions
diff --git a/ChangeLog b/ChangeLog
index 2fe622e9..08f55f73 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,2 +2,9 @@
+ * src/main.c: Implement MU configuration statements.
+ * tests/atlocal.in (MFOPTS): Ignore site-wide and per-user
+ configuration files.
+ * doc/mailfromd.texi: Document sieve interface.
+
+2007-11-22 Sergey Poznyakoff <gray@gnu.org.ua>
+
* doc/mailfromd.texi: Update
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 731c4ae1..95c6741f 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -223,2 +223,3 @@ Built-in and Library Functions
* System functions::
+* Sieve Interface::
* Interfaces to Third-Party Programs::
@@ -987,3 +988,2 @@ the corresponding section below.
@cindex Upgrading from 4.1 to 4.2
-@UNREVISED{}
Upgrading to this version does not require any special efforts. You
@@ -5260,2 +5260,3 @@ in version @value{VERSION}.
* System functions::
+* Sieve Interface::
* Interfaces to Third-Party Programs::
@@ -6480,2 +6481,95 @@ the return status of the command otherwise.
+@node Sieve Interface
+@subsubsection Sieve Interface
+@cindex Sieve
+@UNREVISED{}
+ @samp{Sieve} is a powerful mail filtering language, defined in
+@acronym{RFC} 3028. @command{Mailfromd} supports an extended form
+of this language. For description of the language and available
+extensions, @xref{Sieve Language, Sieve Language, Sieve Language,
+mailutils, GNU Mailutils Manual}.
+
+@deftypefn {Built-in Function} boolean sieve (string @var{script} @
+ [, number @var{flags}])
+Compile the Sieve source file @var{script} and execute it over the
+collected message. This function can be used only in @code{eom}
+handler.
+
+@findex sieve.mfh
+Optional @var{flags} define additional debugging and verbosity
+settings. It is a bit-mask field, consisting of a bitwise @code{or}
+of one or more of the following flags, defined in @file{sieve.mfh}:
+
+@table @code
+@item MF_SIEVE_LOG
+Log every executed @samp{Sieve} action.
+
+@item MF_SIEVE_DEBUG_TRACE
+Trace execution of @samp{Sieve} tests.
+
+@item MF_SIEVE_DEBUG_INSTR
+Log every instruction, executed in the compiled @samp{Sieve} code.
+This produces huge amounts of output and is rarely useful, unless you
+suspect some bug in @samp{Sieve} implementation and wish to trace it.
+
+@item MF_SIEVE_DEBUG_MAILUTILS
+Log debugging information about the underlying Mailutils calls.
+
+@item MF_SIEVE_DEBUG_PROT
+Trace networking protocols.
+@end table
+
+For example, @code{MF_SIEVE_LOG|MF_SIEVE_DEBUG_TRACE} enables logging
+@samp{Sieve} actions and tests.
+
+The @code{sieve} function returns @code{true} if the message was
+accepted by the @var{script} program, and @code{false} otherwise.
+Here, the word @dfn{accepted} means that some form of @samp{KEEP}
+action (@pxref{Actions, keep, Actions, mailutils, GNU Mailutils
+Manual}) was executed over the message.
+@end deftypefn
+
+The following example discards each message not accepted by the
+@samp{Sieve} program @file{/etc/mail/filter.siv}:
+
+@smallexample
+#include_once <sieve.mfh>
+group eom
+do
+ if not sieve("/etc/mail/filter.siv", MF_SIEVE_LOG)
+ discard
+ fi
+done
+@end smallexample
+
+The example below illustrates how one can adjust logging flags
+depending on the current debugging level:
+
+@smallexample
+#include_once <sieve.mfh>
+prog eom
+do
+ number flags 0
+ number level debug_level("bi_sieve")
+ if %level >= 1
+ set flags %flags | MF_SIEVE_LOG
+ fi
+ if %level >= 2
+ set flags %flags | MF_SIEVE_DEBUG_TRACE
+ fi
+ if %level > 9
+ set flags %flags | MF_SIEVE_DEBUG_INSTR
+ fi
+ if %level > 19
+ set flags %flags | MF_SIEVE_DEBUG_MAILUTILS
+ fi
+ if %level > 20
+ set flags %flags | MF_SIEVE_DEBUG_PROT
+ fi
+
+ if not sieve("/etc/mail/filter.siv", %flags)
+ discard
+ fi
+done
+@end smallexample
diff --git a/src/main.c b/src/main.c
index 80c81226..3798ca3f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -39,2 +39,6 @@
# include <mailutils/argp.h>
+typedef struct {
+ char *file;
+ int line;
+} mu_cfg_locus_t;
#else
@@ -239,2 +243,17 @@ log_status(sfsistat status, SMFICTX *ctx)
+static void
+mf_error_on_locus(mu_cfg_locus_t *locus, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (locus) {
+ char *newfmt = NULL;
+ asprintf(&newfmt, "%s:%d: %s", locus->file, locus->line, fmt);
+ mu_verror(newfmt, ap);
+ } else
+ mu_verror(fmt, ap);
+ va_end(ap);
+}
+
@@ -252,4 +271,4 @@ compare_string(const void *item, const void *value)
DOMAIN_LIST */
-void
-read_domain_file(char *name)
+int
+read_domain_file(mu_cfg_locus_t *locus, char *name)
{
@@ -261,5 +280,5 @@ read_domain_file(char *name)
if (!fp) {
- mu_error(_("Cannot open file `%s': %s"),
+ mf_error_on_locus(locus, _("Cannot open file `%s': %s"),
name, mu_strerror(errno));
- return;
+ return 1;
}
@@ -289,2 +308,3 @@ read_domain_file(char *name)
fclose(fp);
+ return 0;
}
@@ -329,2 +349,120 @@ host_in_relayed_domain_p(char *client)
+/* ************************************************************************
+ Configuration directives and options.
+
+ This is rather complicated. Mailfromd can take its configurable values
+ from the following locations (in that order):
+
+ 1. From the mailutils configuration file suite, $sysconfdir/mailutils.rc
+ and/or any files included herein;
+ 2. From #pragma statements in the filter script file,
+ $sysconfdir/mailfromd.rc;
+ 3. From command line options;
+
+ Versions prior to 4.2.90 supported only [2] and [3]. Generally speaking,
+ [2] was needed because Mailutils versions prior to 1.2.90 lacked proper
+ configuration file support. With the advent of it, [2] became obsolete
+ and will probably be removed in some point in the future.
+
+ For the time being, however, I need to support all flavors of options,
+ because it is reasonable to assume that many of the installers still have
+ MU 1.2 or prior, with which [1] is not available.
+
+ Technically speaking, [1] is supported by mu_app_init, whenever compiled
+ with newer Mailutils, [2] is supported by the "option handling" code below,
+ and [3] is handled by argp machinery (called either from mu_app_init, for
+ MU >=1.2.90, or from mu_argp_parse, for older Mailutils versions).
+
+ There are some functions common to [1] and [2], these are defined in this
+ section.
+ ************************************************************************ */
+
+static int
+gid_comp(const void *item, const void *data)
+{
+ return (gid_t) item != (gid_t) data;
+}
+
+static int
+mf_option_group(mu_cfg_locus_t *locus, char *arg)
+{
+ struct group *group = getgrnam(arg);
+ if (group) {
+ if (!retain_groups) {
+ int rc = mu_list_create(&retain_groups);
+ if (rc) {
+ mf_error_on_locus(locus,
+ _("Cannot create list: %s"),
+ mu_strerror(rc));
+ return 1;
+ }
+ mu_list_set_comparator(retain_groups, gid_comp);
+ }
+ mu_list_append(retain_groups, (void*)group->gr_gid);
+ } else {
+ mf_error_on_locus(locus, _("Unknown group: %s"), arg);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+mf_option_mailfrom(mu_cfg_locus_t *locus, char *arg)
+{
+ int rc;
+ mu_address_t addr;
+
+ rc = mu_address_create(&addr, arg);
+ if (rc) {
+ mf_error_on_locus(locus, _("Cannot create address `%s': %s"),
+ arg, mu_strerror(rc));
+ return 1;
+ }
+ mu_address_destroy(&addr);
+ defer_initialize_variable("mailfrom_address", arg);
+ return 0;
+}
+
+static int
+mf_option_state_directory(mu_cfg_locus_t *locus, char *arg)
+{
+ struct stat st;
+ if (stat(arg, &st)) {
+ mf_error_on_locus(locus, _("Cannot stat file `%s': %s"),
+ arg,
+ mu_strerror(errno));
+ return 1;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ mf_error_on_locus(locus, _("`%s' is not a directory"), arg);
+ return 1;
+ }
+ if (arg[0] != '/') {
+ mf_error_on_locus(locus,
+ _("State directory `%s' is not an absolute file name"),
+ arg);
+ return 1;
+ }
+ mailfromd_state_dir = xstrdup(arg);
+ return 0;
+}
+
+static int
+mf_option_source_ip(mu_cfg_locus_t *locus, char *arg, unsigned long *pval)
+{
+ unsigned long address = inet_addr(arg);
+ if (address == INADDR_NONE) {
+ struct hostent *phe = gethostbyname(arg);
+ if (!phe) {
+ mf_error_on_locus(locus, _("Cannot resolve `%s'"),
+ arg);
+ return 1;
+ }
+ address = *(((unsigned long **) phe->h_addr_list)[0]);
+ }
+ *pval = address;
+ return 0;
+}
+
+
/* Option handling.
@@ -506,3 +644,3 @@ load_relay_file(void *item, void *data)
{
- read_domain_file(item);
+ read_domain_file(NULL, item);
return 0;
@@ -646,17 +784,9 @@ option_source(char *opt, void **pval, char *newval)
{
- unsigned long address = inet_addr (newval);
- if (address == INADDR_NONE) {
- struct hostent *phe = gethostbyname (newval);
- if (!phe) {
- mu_error(_("Cannot resolve `%s'"), newval);
- return 1;
- }
- address = *(((unsigned long **) phe->h_addr_list)[0]);
- }
+ unsigned long address;
- if (*pval == 0) {
- *pval = malloc (sizeof address);
- if (!*pval)
+ if (mf_option_source_ip(NULL, newval, &address))
return 1;
- }
+
+ if (*pval == 0)
+ *pval = xmalloc (sizeof address);
*(unsigned long*)*pval = address;
@@ -666,27 +796,5 @@ option_source(char *opt, void **pval, char *newval)
static int
-gid_comp(const void *item, const void *data)
-{
- return (gid_t) item != (gid_t) data;
-}
-
-static int
option_group(char *opt, void **pval, char *newval)
{
- struct group *group = getgrnam(newval);
- if (group) {
- if (!retain_groups) {
- int rc = mu_list_create(&retain_groups);
- if (rc) {
- mu_error(_("Cannot create list: %s"),
- mu_strerror(rc));
- return 1;
- }
- mu_list_set_comparator(retain_groups, gid_comp);
- }
- mu_list_append(retain_groups, (void*)group->gr_gid);
- } else {
- mu_error(_("Unknown group: %s"), newval);
- return 1;
- }
- return 0;
+ return mf_option_group(NULL, newval);
}
@@ -702,20 +810,3 @@ option_state_directory(char *opt, void **pval, char *newval)
{
- struct stat st;
- if (stat(newval, &st)) {
- mu_error(_("Cannot stat file `%s': %s"),
- newval,
- mu_strerror(errno));
- return 1;
- }
- if (!S_ISDIR(st.st_mode)) {
- mu_error(_("`%s' is not a directory"), newval);
- return 1;
- }
- if (newval[0] != '/') {
- mu_error(_("State directory `%s' is not an absolute file name"),
- newval);
- return 1;
- }
- mailfromd_state_dir = xstrdup(newval);
- return 0;
+ return mf_option_state_directory(NULL, newval);
}
@@ -1366,2 +1457,182 @@ static struct argp argp = {
+/* Mailutils-2.0 configuration */
+#if MAILUTILS_VERSION_NUMBER >= 1290
+int
+cb_timeout(time_t *pinterval, mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ const char *endp;
+ if (parse_time_interval(arg, pinterval, &endp)) {
+ mf_error_on_locus(locus,
+ _("unrecognized time format (near `%s')"),
+ endp);
+ return 1;
+ }
+ return 0;
+}
+
+int
+cb_milter_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ time_t interval;
+
+ if (cb_timeout(&interval, locus, data, arg))
+ return 1;
+ if (smfi_settimeout(interval) == MI_FAILURE) {
+ mf_error_on_locus(locus,
+ _("%s:%d: Invalid milter timeout: %lu"),
+ (unsigned long) interval);
+ exit(EX_USAGE);
+ }
+
+ return 0;
+}
+
+int
+cb_io_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ if (cb_timeout(&io_timeout, locus, data, arg))
+ return 1;
+ return 0;
+}
+
+int
+cb_connect_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ if (cb_timeout(&connect_timeout, locus, data, arg))
+ return 1;
+ return 0;
+}
+
+int
+cb_initial_response_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ if (cb_timeout(&response_timeout, locus, data, arg))
+ return 1;
+ return 0;
+}
+
+int
+cb_lock_retry_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ if (cb_timeout(&lock_retry_timeout_option, locus, data, arg))
+ return 1;
+ return 0;
+}
+
+int
+cb_set_variable(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ char *p, *value;
+ char *tmp = NULL;
+
+ for (p = arg; !(*p == ' ' || *p == '\t'); p++)
+ if (!*p) {
+ mf_error_on_locus(locus, _("missing value"));
+ return 1;
+ }
+
+ for (; *p == ' ' || *p == '\t'; p++)
+ if (!*p) {
+ mf_error_on_locus(locus, _("missing value"));
+ return 1;
+ }
+
+ if (*p == '"' || *p == '\'') {
+ size_t len = strlen(p) + 1;
+ tmp = xmalloc(len);
+ mu_argcv_unquote_copy(tmp, p, len);
+ value = tmp;
+ } else
+ value = p;
+
+ defer_initialize_variable(arg, value);
+ free(tmp);
+ return 0;
+}
+
+int
+cb_ehlo_domain(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ defer_initialize_variable("ehlo_domain", arg);
+ return 0;
+}
+
+int
+cb_mail_from_address(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ return mf_option_mailfrom(locus, arg);
+}
+
+int
+cb_debug(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ debug_parse_spec(arg);
+ return 0;
+}
+
+/* See also option_group. */
+int
+cb_group(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ return mf_option_group(locus, arg);
+}
+
+int
+cb_state_directory(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ return mf_option_state_directory(locus, arg);
+}
+
+int
+cb_relay_file(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ return read_domain_file(locus, arg);
+}
+
+int
+cb_source_ip(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ return mf_option_source_ip(locus, arg, &source_address);
+}
+
+int
+cb_include_path(mu_cfg_locus_t *locus, void *data, char *arg)
+{
+ char *p, *sp;
+
+ for (p = strtok_r(arg, ":", &sp); p; p = strtok_r(NULL, ":", &sp))
+ add_include_dir(p);
+ return 0;
+}
+
+/* Keep alphabetical ordering of statements */
+struct mu_cfg_param mf_cfg_param[] = {
+ { "connect-timeout", mu_cfg_callback, NULL, cb_connect_timeout },
+ { "debug", mu_cfg_callback, NULL, cb_debug },
+ { "ehlo-domain", mu_cfg_callback, NULL, cb_ehlo_domain },
+ { "group", mu_cfg_callback, NULL, cb_group },
+ { "include-path", mu_cfg_callback, NULL, cb_include_path },
+ { "initial-response-timeout", mu_cfg_callback, NULL,
+ cb_initial_response_timeout },
+ { "io-timeout", mu_cfg_callback, NULL, cb_io_timeout },
+ { "lock-retry-count", mu_cfg_size, &lock_retry_count_option },
+ { "lock-retry-timeout", mu_cfg_callback, NULL,
+ cb_lock_retry_timeout },
+ { "mail-from-address", mu_cfg_callback, NULL, cb_mail_from_address },
+ { "milter-timeout", mu_cfg_callback, NULL, cb_milter_timeout },
+ { "pidfile", mu_cfg_string, &pidfile },
+ { "relay-file", mu_cfg_callback, NULL, cb_relay_file },
+ { "setvar", mu_cfg_callback, NULL, cb_set_variable },
+ { "script-file", mu_cfg_string, &script_file },
+ { "source-info", mu_cfg_bool, &source_info_option },
+ /* FIXME: Could have used mu_cfg_ipv4 here, but... */
+ { "source-ip", mu_cfg_callback, NULL, cb_source_ip },
+ { "stack-trace", mu_cfg_bool, &stack_trace_option },
+ { "state-directory", mu_cfg_callback, NULL, cb_state_directory },
+ { "user", mu_cfg_string, &user },
+ { NULL }
+};
+#endif
+
+
/* Auxiliary functions */
@@ -1795,3 +2066,4 @@ main(int argc, char **argv)
#else
- rc = mu_app_init(&argp, capa, NULL, argc, argv, 0, &index, NULL);
+ rc = mu_app_init(&argp, capa, mf_cfg_param, argc, argv, 0, &index,
+ NULL);
#endif
diff --git a/tests/atlocal.in b/tests/atlocal.in
index b4679606..6e19f90a 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -24,3 +24,3 @@ trap "cleanup; test -r $XFAILFILE && cat $XFAILFILE; exit $?" 1 2 13 15
-MFOPTS="-I@abs_builddir@/etc -I@abs_top_srcdir@/tests/etc -I@abs_top_srcdir@/src -I@abs_top_srcdir@/mflib --no-preprocess"
+MFOPTS="-I@abs_builddir@/etc -I@abs_top_srcdir@/tests/etc -I@abs_top_srcdir@/src -I@abs_top_srcdir@/mflib --no-preprocess --no-site-rcfile --no-user-rcfile"
ETCDIR=@abs_top_srcdir@/tests/etc

Return to:

Send suggestions and report system problems to the System administrator.