summaryrefslogtreecommitdiffabout
Unidiff
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
@@ -2,2 +2,9 @@
2 2
3 * src/main.c: Implement MU configuration statements.
4 * tests/atlocal.in (MFOPTS): Ignore site-wide and per-user
5 configuration files.
6 * doc/mailfromd.texi: Document sieve interface.
7
82007-11-22 Sergey Poznyakoff <gray@gnu.org.ua>
9
3 * doc/mailfromd.texi: Update 10 * doc/mailfromd.texi: Update
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 731c4ae..95c6741 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -223,2 +223,3 @@ Built-in and Library Functions
223* System functions:: 223* System functions::
224* Sieve Interface::
224* Interfaces to Third-Party Programs:: 225* Interfaces to Third-Party Programs::
@@ -987,3 +988,2 @@ the corresponding section below.
987@cindex Upgrading from 4.1 to 4.2 988@cindex Upgrading from 4.1 to 4.2
988@UNREVISED{}
989 Upgrading to this version does not require any special efforts. You 989 Upgrading to this version does not require any special efforts. You
@@ -5260,2 +5260,3 @@ in version @value{VERSION}.
5260* System functions:: 5260* System functions::
5261* Sieve Interface::
5261* Interfaces to Third-Party Programs:: 5262* Interfaces to Third-Party Programs::
@@ -6479,4 +6480,97 @@ the return status of the command otherwise.
6479@end deftypefn 6480@end deftypefn
6480
6481 6481
6482@node Sieve Interface
6483@subsubsection Sieve Interface
6484@cindex Sieve
6485@UNREVISED{}
6486 @samp{Sieve} is a powerful mail filtering language, defined in
6487@acronym{RFC} 3028. @command{Mailfromd} supports an extended form
6488of this language. For description of the language and available
6489extensions, @xref{Sieve Language, Sieve Language, Sieve Language,
6490mailutils, GNU Mailutils Manual}.
6491
6492@deftypefn {Built-in Function} boolean sieve (string @var{script} @
6493 [, number @var{flags}])
6494Compile the Sieve source file @var{script} and execute it over the
6495collected message. This function can be used only in @code{eom}
6496handler.
6497
6498@findex sieve.mfh
6499Optional @var{flags} define additional debugging and verbosity
6500settings. It is a bit-mask field, consisting of a bitwise @code{or}
6501of one or more of the following flags, defined in @file{sieve.mfh}:
6502
6503@table @code
6504@item MF_SIEVE_LOG
6505Log every executed @samp{Sieve} action.
6506
6507@item MF_SIEVE_DEBUG_TRACE
6508Trace execution of @samp{Sieve} tests.
6509
6510@item MF_SIEVE_DEBUG_INSTR
6511Log every instruction, executed in the compiled @samp{Sieve} code.
6512This produces huge amounts of output and is rarely useful, unless you
6513suspect some bug in @samp{Sieve} implementation and wish to trace it.
6514
6515@item MF_SIEVE_DEBUG_MAILUTILS
6516Log debugging information about the underlying Mailutils calls.
6517
6518@item MF_SIEVE_DEBUG_PROT
6519Trace networking protocols.
6520@end table
6521
6522For example, @code{MF_SIEVE_LOG|MF_SIEVE_DEBUG_TRACE} enables logging
6523@samp{Sieve} actions and tests.
6524
6525The @code{sieve} function returns @code{true} if the message was
6526accepted by the @var{script} program, and @code{false} otherwise.
6527Here, the word @dfn{accepted} means that some form of @samp{KEEP}
6528action (@pxref{Actions, keep, Actions, mailutils, GNU Mailutils
6529Manual}) was executed over the message.
6530@end deftypefn
6531
6532The following example discards each message not accepted by the
6533@samp{Sieve} program @file{/etc/mail/filter.siv}:
6534
6535@smallexample
6536#include_once <sieve.mfh>
6537group eom
6538do
6539 if not sieve("/etc/mail/filter.siv", MF_SIEVE_LOG)
6540 discard
6541 fi
6542done
6543@end smallexample
6544
6545The example below illustrates how one can adjust logging flags
6546depending on the current debugging level:
6547
6548@smallexample
6549#include_once <sieve.mfh>
6550prog eom
6551do
6552 number flags 0
6553 number level debug_level("bi_sieve")
6554 if %level >= 1
6555 set flags %flags | MF_SIEVE_LOG
6556 fi
6557 if %level >= 2
6558 set flags %flags | MF_SIEVE_DEBUG_TRACE
6559 fi
6560 if %level > 9
6561 set flags %flags | MF_SIEVE_DEBUG_INSTR
6562 fi
6563 if %level > 19
6564 set flags %flags | MF_SIEVE_DEBUG_MAILUTILS
6565 fi
6566 if %level > 20
6567 set flags %flags | MF_SIEVE_DEBUG_PROT
6568 fi
6569
6570 if not sieve("/etc/mail/filter.siv", %flags)
6571 discard
6572 fi
6573done
6574@end smallexample
6575
6482@node Interfaces to Third-Party Programs 6576@node 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
@@ -39,2 +39,6 @@
39# include <mailutils/argp.h> 39# include <mailutils/argp.h>
40typedef struct {
41 char *file;
42 int line;
43} mu_cfg_locus_t;
40#else 44#else
@@ -239,2 +243,17 @@ log_status(sfsistat status, SMFICTX *ctx)
239 243
244static void
245mf_error_on_locus(mu_cfg_locus_t *locus, const char *fmt, ...)
246{
247 va_list ap;
248
249 va_start(ap, fmt);
250 if (locus) {
251 char *newfmt = NULL;
252 asprintf(&newfmt, "%s:%d: %s", locus->file, locus->line, fmt);
253 mu_verror(newfmt, ap);
254 } else
255 mu_verror(fmt, ap);
256 va_end(ap);
257}
258
240 259
@@ -252,4 +271,4 @@ compare_string(const void *item, const void *value)
252 DOMAIN_LIST */ 271 DOMAIN_LIST */
253void 272int
254read_domain_file(char *name) 273read_domain_file(mu_cfg_locus_t *locus, char *name)
255{ 274{
@@ -261,5 +280,5 @@ read_domain_file(char *name)
261 if (!fp) { 280 if (!fp) {
262 mu_error(_("Cannot open file `%s': %s"), 281 mf_error_on_locus(locus, _("Cannot open file `%s': %s"),
263 name, mu_strerror(errno)); 282 name, mu_strerror(errno));
264 return; 283 return 1;
265 } 284 }
@@ -289,2 +308,3 @@ read_domain_file(char *name)
289 fclose(fp); 308 fclose(fp);
309 return 0;
290} 310}
@@ -329,2 +349,120 @@ host_in_relayed_domain_p(char *client)
329 349
350/* ************************************************************************
351 Configuration directives and options.
352
353 This is rather complicated. Mailfromd can take its configurable values
354 from the following locations (in that order):
355
356 1. From the mailutils configuration file suite, $sysconfdir/mailutils.rc
357 and/or any files included herein;
358 2. From #pragma statements in the filter script file,
359 $sysconfdir/mailfromd.rc;
360 3. From command line options;
361
362 Versions prior to 4.2.90 supported only [2] and [3]. Generally speaking,
363 [2] was needed because Mailutils versions prior to 1.2.90 lacked proper
364 configuration file support. With the advent of it, [2] became obsolete
365 and will probably be removed in some point in the future.
366
367 For the time being, however, I need to support all flavors of options,
368 because it is reasonable to assume that many of the installers still have
369 MU 1.2 or prior, with which [1] is not available.
370
371 Technically speaking, [1] is supported by mu_app_init, whenever compiled
372 with newer Mailutils, [2] is supported by the "option handling" code below,
373 and [3] is handled by argp machinery (called either from mu_app_init, for
374 MU >=1.2.90, or from mu_argp_parse, for older Mailutils versions).
375
376 There are some functions common to [1] and [2], these are defined in this
377 section.
378 ************************************************************************ */
379
380static int
381gid_comp(const void *item, const void *data)
382{
383 return (gid_t) item != (gid_t) data;
384}
385
386static int
387mf_option_group(mu_cfg_locus_t *locus, char *arg)
388{
389 struct group *group = getgrnam(arg);
390 if (group) {
391 if (!retain_groups) {
392 int rc = mu_list_create(&retain_groups);
393 if (rc) {
394 mf_error_on_locus(locus,
395 _("Cannot create list: %s"),
396 mu_strerror(rc));
397 return 1;
398 }
399 mu_list_set_comparator(retain_groups, gid_comp);
400 }
401 mu_list_append(retain_groups, (void*)group->gr_gid);
402 } else {
403 mf_error_on_locus(locus, _("Unknown group: %s"), arg);
404 return 1;
405 }
406 return 0;
407}
408
409static int
410mf_option_mailfrom(mu_cfg_locus_t *locus, char *arg)
411{
412 int rc;
413 mu_address_t addr;
414
415 rc = mu_address_create(&addr, arg);
416 if (rc) {
417 mf_error_on_locus(locus, _("Cannot create address `%s': %s"),
418 arg, mu_strerror(rc));
419 return 1;
420 }
421 mu_address_destroy(&addr);
422 defer_initialize_variable("mailfrom_address", arg);
423 return 0;
424}
425
426static int
427mf_option_state_directory(mu_cfg_locus_t *locus, char *arg)
428{
429 struct stat st;
430 if (stat(arg, &st)) {
431 mf_error_on_locus(locus, _("Cannot stat file `%s': %s"),
432 arg,
433 mu_strerror(errno));
434 return 1;
435 }
436 if (!S_ISDIR(st.st_mode)) {
437 mf_error_on_locus(locus, _("`%s' is not a directory"), arg);
438 return 1;
439 }
440 if (arg[0] != '/') {
441 mf_error_on_locus(locus,
442 _("State directory `%s' is not an absolute file name"),
443 arg);
444 return 1;
445 }
446 mailfromd_state_dir = xstrdup(arg);
447 return 0;
448}
449
450static int
451mf_option_source_ip(mu_cfg_locus_t *locus, char *arg, unsigned long *pval)
452{
453 unsigned long address = inet_addr(arg);
454 if (address == INADDR_NONE) {
455 struct hostent *phe = gethostbyname(arg);
456 if (!phe) {
457 mf_error_on_locus(locus, _("Cannot resolve `%s'"),
458 arg);
459 return 1;
460 }
461 address = *(((unsigned long **) phe->h_addr_list)[0]);
462 }
463 *pval = address;
464 return 0;
465}
466
467
330/* Option handling. 468/* Option handling.
@@ -506,3 +644,3 @@ load_relay_file(void *item, void *data)
506{ 644{
507 read_domain_file(item); 645 read_domain_file(NULL, item);
508 return 0; 646 return 0;
@@ -646,17 +784,9 @@ option_source(char *opt, void **pval, char *newval)
646{ 784{
647 unsigned long address = inet_addr (newval); 785 unsigned long address;
648 if (address == INADDR_NONE) { 786
649 struct hostent *phe = gethostbyname (newval); 787 if (mf_option_source_ip(NULL, newval, &address))
650 if (!phe) { 788 return 1;
651 mu_error(_("Cannot resolve `%s'"), newval);
652 return 1;
653 }
654 address = *(((unsigned long **) phe->h_addr_list)[0]);
655 }
656 789
657 if (*pval == 0) { 790 if (*pval == 0)
658 *pval = malloc (sizeof address); 791 *pval = xmalloc (sizeof address);
659 if (!*pval)
660 return 1;
661 }
662 *(unsigned long*)*pval = address; 792 *(unsigned long*)*pval = address;
@@ -666,27 +796,5 @@ option_source(char *opt, void **pval, char *newval)
666static int 796static int
667gid_comp(const void *item, const void *data)
668{
669 return (gid_t) item != (gid_t) data;
670}
671
672static int
673option_group(char *opt, void **pval, char *newval) 797option_group(char *opt, void **pval, char *newval)
674{ 798{
675 struct group *group = getgrnam(newval); 799 return mf_option_group(NULL, newval);
676 if (group) {
677 if (!retain_groups) {
678 int rc = mu_list_create(&retain_groups);
679 if (rc) {
680 mu_error(_("Cannot create list: %s"),
681 mu_strerror(rc));
682 return 1;
683 }
684 mu_list_set_comparator(retain_groups, gid_comp);
685 }
686 mu_list_append(retain_groups, (void*)group->gr_gid);
687 } else {
688 mu_error(_("Unknown group: %s"), newval);
689 return 1;
690 }
691 return 0;
692} 800}
@@ -702,20 +810,3 @@ option_state_directory(char *opt, void **pval, char *newval)
702{ 810{
703 struct stat st; 811 return mf_option_state_directory(NULL, newval);
704 if (stat(newval, &st)) {
705 mu_error(_("Cannot stat file `%s': %s"),
706 newval,
707 mu_strerror(errno));
708 return 1;
709 }
710 if (!S_ISDIR(st.st_mode)) {
711 mu_error(_("`%s' is not a directory"), newval);
712 return 1;
713 }
714 if (newval[0] != '/') {
715 mu_error(_("State directory `%s' is not an absolute file name"),
716 newval);
717 return 1;
718 }
719 mailfromd_state_dir = xstrdup(newval);
720 return 0;
721} 812}
@@ -1366,2 +1457,182 @@ static struct argp argp = {
1366 1457
1458/* Mailutils-2.0 configuration */
1459#if MAILUTILS_VERSION_NUMBER >= 1290
1460int
1461cb_timeout(time_t *pinterval, mu_cfg_locus_t *locus, void *data, char *arg)
1462{
1463 const char *endp;
1464 if (parse_time_interval(arg, pinterval, &endp)) {
1465 mf_error_on_locus(locus,
1466 _("unrecognized time format (near `%s')"),
1467 endp);
1468 return 1;
1469 }
1470 return 0;
1471}
1472
1473int
1474cb_milter_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
1475{
1476 time_t interval;
1477
1478 if (cb_timeout(&interval, locus, data, arg))
1479 return 1;
1480 if (smfi_settimeout(interval) == MI_FAILURE) {
1481 mf_error_on_locus(locus,
1482 _("%s:%d: Invalid milter timeout: %lu"),
1483 (unsigned long) interval);
1484 exit(EX_USAGE);
1485 }
1486
1487 return 0;
1488}
1489
1490int
1491cb_io_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
1492{
1493 if (cb_timeout(&io_timeout, locus, data, arg))
1494 return 1;
1495 return 0;
1496}
1497
1498int
1499cb_connect_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
1500{
1501 if (cb_timeout(&connect_timeout, locus, data, arg))
1502 return 1;
1503 return 0;
1504}
1505
1506int
1507cb_initial_response_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
1508{
1509 if (cb_timeout(&response_timeout, locus, data, arg))
1510 return 1;
1511 return 0;
1512}
1513
1514int
1515cb_lock_retry_timeout(mu_cfg_locus_t *locus, void *data, char *arg)
1516{
1517 if (cb_timeout(&lock_retry_timeout_option, locus, data, arg))
1518 return 1;
1519 return 0;
1520}
1521
1522int
1523cb_set_variable(mu_cfg_locus_t *locus, void *data, char *arg)
1524{
1525 char *p, *value;
1526 char *tmp = NULL;
1527
1528 for (p = arg; !(*p == ' ' || *p == '\t'); p++)
1529 if (!*p) {
1530 mf_error_on_locus(locus, _("missing value"));
1531 return 1;
1532 }
1533
1534 for (; *p == ' ' || *p == '\t'; p++)
1535 if (!*p) {
1536 mf_error_on_locus(locus, _("missing value"));
1537 return 1;
1538 }
1539
1540 if (*p == '"' || *p == '\'') {
1541 size_t len = strlen(p) + 1;
1542 tmp = xmalloc(len);
1543 mu_argcv_unquote_copy(tmp, p, len);
1544 value = tmp;
1545 } else
1546 value = p;
1547
1548 defer_initialize_variable(arg, value);
1549 free(tmp);
1550 return 0;
1551}
1552
1553int
1554cb_ehlo_domain(mu_cfg_locus_t *locus, void *data, char *arg)
1555{
1556 defer_initialize_variable("ehlo_domain", arg);
1557 return 0;
1558}
1559
1560int
1561cb_mail_from_address(mu_cfg_locus_t *locus, void *data, char *arg)
1562{
1563 return mf_option_mailfrom(locus, arg);
1564}
1565
1566int
1567cb_debug(mu_cfg_locus_t *locus, void *data, char *arg)
1568{
1569 debug_parse_spec(arg);
1570 return 0;
1571}
1572
1573/* See also option_group. */
1574int
1575cb_group(mu_cfg_locus_t *locus, void *data, char *arg)
1576{
1577 return mf_option_group(locus, arg);
1578}
1579
1580int
1581cb_state_directory(mu_cfg_locus_t *locus, void *data, char *arg)
1582{
1583 return mf_option_state_directory(locus, arg);
1584}
1585
1586int
1587cb_relay_file(mu_cfg_locus_t *locus, void *data, char *arg)
1588{
1589 return read_domain_file(locus, arg);
1590}
1591
1592int
1593cb_source_ip(mu_cfg_locus_t *locus, void *data, char *arg)
1594{
1595 return mf_option_source_ip(locus, arg, &source_address);
1596}
1597
1598int
1599cb_include_path(mu_cfg_locus_t *locus, void *data, char *arg)
1600{
1601 char *p, *sp;
1602
1603 for (p = strtok_r(arg, ":", &sp); p; p = strtok_r(NULL, ":", &sp))
1604 add_include_dir(p);
1605 return 0;
1606}
1607
1608/* Keep alphabetical ordering of statements */
1609struct mu_cfg_param mf_cfg_param[] = {
1610 { "connect-timeout", mu_cfg_callback, NULL, cb_connect_timeout },
1611 { "debug", mu_cfg_callback, NULL, cb_debug },
1612 { "ehlo-domain", mu_cfg_callback, NULL, cb_ehlo_domain },
1613 { "group", mu_cfg_callback, NULL, cb_group },
1614 { "include-path", mu_cfg_callback, NULL, cb_include_path },
1615 { "initial-response-timeout", mu_cfg_callback, NULL,
1616 cb_initial_response_timeout },
1617 { "io-timeout", mu_cfg_callback, NULL, cb_io_timeout },
1618 { "lock-retry-count", mu_cfg_size, &lock_retry_count_option },
1619 { "lock-retry-timeout", mu_cfg_callback, NULL,
1620 cb_lock_retry_timeout },
1621 { "mail-from-address", mu_cfg_callback, NULL, cb_mail_from_address },
1622 { "milter-timeout", mu_cfg_callback, NULL, cb_milter_timeout },
1623 { "pidfile", mu_cfg_string, &pidfile },
1624 { "relay-file", mu_cfg_callback, NULL, cb_relay_file },
1625 { "setvar", mu_cfg_callback, NULL, cb_set_variable },
1626 { "script-file", mu_cfg_string, &script_file },
1627 { "source-info", mu_cfg_bool, &source_info_option },
1628 /* FIXME: Could have used mu_cfg_ipv4 here, but... */
1629 { "source-ip", mu_cfg_callback, NULL, cb_source_ip },
1630 { "stack-trace", mu_cfg_bool, &stack_trace_option },
1631 { "state-directory", mu_cfg_callback, NULL, cb_state_directory },
1632 { "user", mu_cfg_string, &user },
1633 { NULL }
1634};
1635#endif
1636
1637
1367/* Auxiliary functions */ 1638/* Auxiliary functions */
@@ -1773,3 +2044,3 @@ main(int argc, char **argv)
1773 MU_AUTH_REGISTER_ALL_MODULES(); 2044 MU_AUTH_REGISTER_ALL_MODULES();
1774 mu_register_all_formats (); 2045 mu_register_all_formats();
1775 mu_register_all_mailer_formats(); 2046 mu_register_all_mailer_formats();
@@ -1795,3 +2066,4 @@ main(int argc, char **argv)
1795#else 2066#else
1796 rc = mu_app_init(&argp, capa, NULL, argc, argv, 0, &index, NULL); 2067 rc = mu_app_init(&argp, capa, mf_cfg_param, argc, argv, 0, &index,
2068 NULL);
1797#endif 2069#endif
diff --git a/tests/atlocal.in b/tests/atlocal.in
index b467960..6e19f90 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
24 24
25MFOPTS="-I@abs_builddir@/etc -I@abs_top_srcdir@/tests/etc -I@abs_top_srcdir@/src -I@abs_top_srcdir@/mflib --no-preprocess" 25MFOPTS="-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"
26ETCDIR=@abs_top_srcdir@/tests/etc 26ETCDIR=@abs_top_srcdir@/tests/etc

Return to:

Send suggestions and report system problems to the System administrator.