summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2007-11-22 12:23:06 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2007-11-22 12:23:06 (GMT)
commit080cc8f1c899f91f143694f3a3399a159239496b (patch) (side-by-side diff)
tree8cd38a0e3c0c376a614a60312a9c2e80bae2b845
parent861696aa40909d5c11ab4876b716d48fef73a49d (diff)
downloadmailfromd-080cc8f1c899f91f143694f3a3399a159239496b.tar.gz
mailfromd-080cc8f1c899f91f143694f3a3399a159239496b.tar.bz2
* 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. git-svn-id: file:///svnroot/mailfromd/trunk@1533 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (more/less context) (ignore whitespace changes)
-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 2fe622e..08f55f7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
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 731c4ae..95c6741 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -221,6 +221,7 @@ Built-in and Library Functions
* Database functions::
* I/O functions::
* System functions::
+* Sieve Interface::
* Interfaces to Third-Party Programs::
* Special test functions::
* Mail Sending Functions::
@@ -985,7 +986,6 @@ 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
@@ -5258,6 +5258,7 @@ in version @value{VERSION}.
* Database functions::
* I/O functions::
* System functions::
+* Sieve Interface::
* Interfaces to Third-Party Programs::
* Special test functions::
* Mail Sending Functions::
@@ -6477,8 +6478,101 @@ strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564, 1)
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
diff --git a/src/main.c b/src/main.c
index 80c8122..3798ca3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -37,6 +37,10 @@
#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
@@ -237,6 +241,21 @@ 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);
+}
+
/* Sendmail class file support */
@@ -250,8 +269,8 @@ 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];
@@ -259,9 +278,9 @@ read_domain_file(char *name)
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) {
@@ -287,6 +306,7 @@ read_domain_file(char *name)
}
fclose(fp);
+ return 0;
}
/* Return true if we relay domain NAME */
@@ -327,6 +347,124 @@ 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.
There are two classes of options: those that can be set using #pragma
@@ -504,7 +642,7 @@ 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;
}
@@ -644,51 +782,21 @@ 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
@@ -700,24 +808,7 @@ 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 {
@@ -1364,6 +1455,186 @@ 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 */
/* Switch to the given UID/GID */
@@ -1771,7 +2042,7 @@ main(int argc, char **argv)
#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];
@@ -1793,7 +2064,8 @@ main(int argc, char **argv)
#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 b467960..6e19f90 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -22,7 +22,7 @@ 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"

Return to:

Send suggestions and report system problems to the System administrator.