aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--doc/mailfromd.texi98
-rw-r--r--src/main.c398
-rw-r--r--tests/atlocal.in2
4 files changed, 439 insertions, 66 deletions
diff --git a/ChangeLog b/ChangeLog
index 2fe622e9..08f55f73 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,13 @@
2007-11-22 Sergey Poznyakoff <gray@gnu.org.ua>
+ * 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
2007-11-21 Sergey Poznyakoff <gray@gnu.org.ua>
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 731c4ae1..95c6741f 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -220,8 +220,9 @@ Built-in and Library Functions
* DNS functions::
* Database functions::
* I/O functions::
* System functions::
+* Sieve Interface::
* Interfaces to Third-Party Programs::
* Special test functions::
* Mail Sending Functions::
* NLS Functions::
@@ -984,9 +985,8 @@ the corresponding section below.
@node 410-420
@section Upgrading from 4.1 to 4.2
@cindex Upgrading from 4.1 to 4.2
-@UNREVISED{}
Upgrading to this version does not require any special efforts. You
can use your configuration files and filter scripts without any
changes. The only difference worth noticing is that starting from this
version @command{mailfromd} is always compiled with asynchronous
@@ -5257,8 +5257,9 @@ in version @value{VERSION}.
* DNS functions::
* Database functions::
* I/O functions::
* System functions::
+* Sieve Interface::
* Interfaces to Third-Party Programs::
* Special test functions::
* Mail Sending Functions::
* NLS Functions::
@@ -6476,10 +6477,103 @@ strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564, 1)
The function @code{system} executes a command specified in @var{str}
by calling @command{/bin/sh -c string}, and returns -1 on error or
the return status of the command otherwise.
@end deftypefn
-
+@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
+
@node Interfaces to Third-Party Programs
@subsubsection Interfaces to Third-Party Programs
A set of functions is defined for interfacing with other filters via
diff --git a/src/main.c b/src/main.c
index 80c81226..3798ca3f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -36,8 +36,12 @@
#include <mailutils/mailutils.h>
#if MAILUTILS_VERSION_NUMBER < 1290
# include <mailutils/argp.h>
+typedef struct {
+ char *file;
+ int line;
+} mu_cfg_locus_t;
#else
# include <mailutils/libargp.h>
#endif
#include "mailfromd.h"
@@ -236,8 +240,23 @@ log_status(sfsistat status, SMFICTX *ctx)
mailfromd_msgid(ctx), status);
}
}
+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);
+}
+
/* Sendmail class file support */
static mu_list_t domain_list;
@@ -249,20 +268,20 @@ compare_string(const void *item, const void *value)
}
/* Read domains from sendmail-style domain file NAME and store them in
DOMAIN_LIST */
-void
-read_domain_file(char *name)
+int
+read_domain_file(mu_cfg_locus_t *locus, char *name)
{
FILE *fp;
char buf[256];
char *p;
fp = fopen(name, "r");
if (!fp) {
- mu_error(_("Cannot open file `%s': %s"),
- name, mu_strerror(errno));
- return;
+ mf_error_on_locus(locus, _("Cannot open file `%s': %s"),
+ name, mu_strerror(errno));
+ return 1;
}
if (!domain_list) {
mu_list_create(&domain_list);
@@ -286,8 +305,9 @@ read_domain_file(char *name)
mu_list_append(domain_list, strdup(p));
}
fclose(fp);
+ return 0;
}
/* Return true if we relay domain NAME */
int
@@ -326,8 +346,126 @@ host_in_relayed_domain_p(char *client)
return rc;
}
+/* ************************************************************************
+ 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.
There are two classes of options: those that can be set using #pragma
directive in the configuration file, and those that can be used only in
@@ -503,9 +641,9 @@ set_response_timeout(void *value)
static int
load_relay_file(void *item, void *data)
{
- read_domain_file(item);
+ read_domain_file(NULL, item);
return 0;
}
static void
@@ -643,53 +781,23 @@ option_relay(char *opt, void **pval, char *newval)
static int
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 (mf_option_source_ip(NULL, newval, &address))
+ return 1;
- if (*pval == 0) {
- *pval = malloc (sizeof address);
- if (!*pval)
- return 1;
- }
+ if (*pval == 0)
+ *pval = xmalloc (sizeof address);
*(unsigned long*)*pval = address;
return 0;
}
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);
}
void
set_state_directory(void *value)
@@ -699,26 +807,9 @@ set_state_directory(void *value)
int
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);
}
struct option_cache {
char *name;
@@ -1363,8 +1454,188 @@ static struct argp argp = {
NULL
};
+/* 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 */
/* Switch to the given UID/GID */
int
@@ -1770,9 +2041,9 @@ main(int argc, char **argv)
textdomain (PACKAGE);
#endif
MU_AUTH_REGISTER_ALL_MODULES();
- mu_register_all_formats ();
+ mu_register_all_formats();
mu_register_all_mailer_formats();
if (!program_invocation_short_name)
program_invocation_short_name = argv[0];
argp_program_version_hook = version;
@@ -1792,9 +2063,10 @@ main(int argc, char **argv)
mu_argp_init(program_version, "<" PACKAGE_BUGREPORT ">");
#if MAILUTILS_VERSION_NUMBER < 1290
rc = mu_argp_parse(&argp, &argc, &argv, 0, capa, &index, NULL);
#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
if (rc)
exit (EX_CONFIG);
diff --git a/tests/atlocal.in b/tests/atlocal.in
index b4679606..6e19f90a 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -21,9 +21,9 @@ cleanup() {
}
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
PORT="unix:$STATEDIR/socket"
MTAOPTS="-X$PORT --stdio"

Return to:

Send suggestions and report system problems to the System administrator.