GNU Mailutils Manual (split by section):   Section:   Chapter:FastBack: Programs   Up: Programs   FastForward: Libraries   Contents: Table of ContentsIndex: Function Index

3.12 mda

GNU local mail delivery agent reads a message from its standard input and delivers it to one or more local recipients listed in the command line. When we speak about local recipients, we mean that these are system users that are known to the system that runs mda. However, the mailboxes of these users can be local as well as remote ones. mda is able to deliver mail to any mailbox format, supported by GNU Mailutils. These formats, among others, include ‘smtp://’, ‘prog://’ and ‘sendmail://’ which are equivalent to forwarding a message over SMTP to a remote node.

Mda is also able to process incoming messages using Sieve, Scheme or Python scripts and, based on results of this processing, to take a decision on whether to actually deliver and where to deliver them. Due to its extensive scripting facilities, mda offers much more flexibility than other popular MDAs.

3.12.1 Using mda with Sendmail.

When used with Sendmail, mda must be invoked from the local mailer definition in the sendmail.cf file. The flags ‘lswS’ must be set for the mailer. These mean: the mailer is local, quote characters should be stripped off the address before invoking the mailer, the user must have a valid account on this machine and the userid should not be reset before calling the mailer. Additionally, the ‘fn’ flags may be specified to allow mda to generate the usual ‘From ’ envelope instead of the one supplied by sendmail.

If you wish to use mda with non-local authentication, such as SQL or LDAP, you also need to remove the ‘w’ flag, since in that case the user is not required to have a valid account on the machine that runs sendmail.

Here is an example of mailer definition in sendmail.cf

Mlocal, P=/usr/local/sbin/mda,
        F=lsDFMAw5:/|@qSPfhn9,
        S=EnvFromL/HdrFromL, R=EnvToL/HdrToL,
        T=DNS/RFC822/X-Unix,
        A=mail $u

To define local mailer in ‘mc’ source file, it will suffice to set:

define(`LOCAL_MAILER_PATH', `/usr/local/sbin/mda')
define(`LOCAL_MAILER_ARGS', `mail $u')

3.12.2 Using mda with Exim.

Using mda with Exim is quite straightforward. The following example illustrates the definition of the appropriate transport and director in exim.conf:

# transport
mda_pipe:
  driver = pipe
  command = /usr/local/sbin/mda $local_part
  return_path_add
  delivery_date_add
  envelope_to_add
  
# director
mda:
  driver = localuser
  transport = mda_pipe

3.12.3 Using mda with MeTA1.

MeTA1 (http://meta1.org) communicates with the delivery agent using LMTP. Instead of using mda you will have to start the LMTP daemon lmtpd and configure MeTA1 to communicate with it. See MeTA1-lmtpd, for details.

3.12.4 Mailbox Quotas

Mailbox quota is a limit on the size of the mailbox. When a mailbox size reaches this limit, mda stops accepting messages for this recipient and returns an error condition to the sender. The error code is accompanied by the following error message:

user: mailbox quota exceeded for this recipient

Furthermore, if accepting the incoming message would make the mailbox size exceed the quota, such a message will be rejected as well. In this case, the error message is:

user: message would exceed maximum mailbox size for this recipient

In both cases, the default return code will be ‘service unavailable’ (corresponding to the SMTP return code ‘550’), unless the following statement is present in the maidag configuration file:

quota {
  exit-tempfail yes;
}

in which case a temporary error will be returned.

The mailbox quota can be retrieved from the following sources:

  1. Authentication method.
  2. DBM file.
  3. SQL database.

3.12.4.1 Keeping Quotas in DBM File

To use DBM quota database, GNU Mailutils must be compiled with one of the following command line options: --with-gdbm, --with-berkeley-db, --with-ndbm, --with-tokyocabinet, or --with-kyotocabinet. Examine the output of mda --show-config-options, if not sure.

The quota database should have the following structure:

Key

Key represents the user name. Special key ‘DEFAULT’ means default quota value, i.e. the one to be used if the user is not explicitly listed in the database.

Value

Mailbox quota for this user. If it is a number, it represents the maximum mailbox size in bytes. A number may optionally be followed by ‘kb’ or ‘mb’, meaning kilobytes and megabytes, respectively.

A special value ‘NONE’ means no mailbox size limit for this user.

Here is an example of a quota database in text form:

# Default quota value:
DEFAULT         5mb

# Following users have unlimited mailbox size
root            NONE
smith           NONE

# Rest of users
plog            26214400
karin           10mB

To use the DBM quota database, specify its absolute name using the database configuration statement in the quota section, e.g.:

quota {
  database /etc/mail/quota.db;
}  

3.12.4.2 Keeping Quotas in SQL Database

User quotas can be kept in an SQL table as well. Currently (as of mailutils version 3.14) it is assumed that this table can be accessed using the credentials set in ‘sql’ configuration statement (see SQL Statement).

For example, suppose you have the following quota table:

create table mailbox_quota (
  user_name varchar(32) binary not null,
  quota int,
  unique (user_name)
);

To retrieve user quota the following query can be used:

SELECT quota FROM mailbox_quota WHERE user_name='${user}'

To define this query use the sql-query statement:

quota {
  sql-query "SELECT quota "
            "FROM mailbox_quota "
            "WHERE user_name='${user}'";
}

There are no special provisions for specifying group quotas, similar to ‘DEFAULT’ in DBM databases. This is because group quotas can easily be implemented using SQL language. Mda always uses the first tuple from the set returned by mailbox quota query. So, you can add a special entry to the mailbox_quota table that would keep the group quota. In the discussion below we assume that the user_name column for this entry is lexicographically less than any other user name in the table. Let’s suppose the group quota name is ‘00DEFAULT’. Then the following query:

SELECT quota
FROM mailbox_quota
WHERE user_name IN ('${user}','00DEFAULT')
ORDER BY user_name DESC

will return two tuples if the user is found in mailbox_quota. Due to ORDER statement, the first tuple will contain quota for the user, which will be used by mda. On the other hand, if the requested user name is not present in the table, the above query will return a single tuple containing the group quota.

The following configuration statement instructs maidag to use this query for retrieving the user quota:

quota {
  sql-query "SELECT quota "
            "FROM mailbox_quota "
            "WHERE user_name IN ('${user}','00DEFAULT') "
            "ORDER BY user_name DESC";
}            

3.12.5 Scripting in mda

Mda can use global or per-user mail filters to decide whether to deliver the message, and where to deliver it. As of Mailutils version 3.14, such mail filters may be written in the following languages:

Mail filters to use are specified using ‘script’ configuration statement. The following meta-symbols can be used in its argument:

~
%h

Expands to the recipient home directory.

%u

Expands to the recipient user name.

By default, the filename extension decides which scripting language will be used. User can alter the choice using ‘language’ configuration statement. For example:

script {
  language python;
  pattern "~/.maidag-py-filter";
}  

3.12.5.1 Sieve MDA Filters

The file name of the Sieve filter to use is specified using ‘script’ configuration statement. For example, the following configuration statement:

script {
  pattern "~/.maidag.sv";
}  

instructs maidag to use file .maidag.sv in the recipient home directory as a Sieve filter.

Normal message delivery is attempted if execution of the Sieve code ended with keep action (either implicit or explicit).

Other Sieve actions are executed as described in Actions. For example, to deliver message to another mailbox, use the fileinto action.

Any modifications to headers or body of the message performed by the Sieve code will be visible in the delivered message.

3.12.5.2 Scheme MDA Filters

The file name of the Scheme mail filter is specified using ‘script’ configuration statement. For example, the following configuration statement:

script {
  pattern "~/.maidag.scm";
}  

instructs mda to use file .maidag.scm in the recipient home directory as a Scheme filter.

3.12.5.3 Python MDA Filters

The file name of the Python mail filter is specified using ‘script’ configuration statement. For example, the following configuration statement:

script {
  pattern "~/.maidag.py";
}  

instructs mda to use the file .maidag.py in the recipient home directory as a Python filter.

A simple example of a mail filter written in Python:

from mailutils import *
import maidag
import re

msg = message.Message (maidag.message)
hdr = msg.header

try:
    if 'List-Post' in hdr and 'Received' in hdr \
       and hdr['Received'].find ('fencepost.gnu.org') != -1:

        # check envelope's sender address
        m = re.search (r'([\w\-]+)-bounces\+([\w]+)=.*',
                       msg.envelope.get_sender ())
        if m:
            lbox = m.group (1)
            user = m.group (2)
            # open destination mailbox and append message
            dst = mailbox.MailboxDefault ('~/Mail/%s' % lbox)
            dst.open ('ac')
            dst.append_message (msg)
            dst.close ()
            # set deleted flag so maidag will not deliver msg elsewhere
            msg.attribute.set_deleted ()
except Exception:
    pass

3.12.6 Forwarding

A forward file is a special file in the user’s home directory that contains the email address of the mailbox where the user wants to forward his mail. Normally, forward files are processed by MTA. However, there are some MTA that lack this feature. One of them is MeTA1.

Mda provides a forwarding feature that is useful to compensate the lack of it. This feature is controlled by the forward section in the configuration file:

forward {
  # Process forward file.
  file name;
  # Configure safety checks for the forward file.
  file-checks (list);
}

The name of the forward file is given by the file statement in the forward section. A common usage is:

forward {
  file .forward;
}  

The forward file is always searched in the recipient home directory.

Before actually using the forward file, a number of safety checks are performed on it. If the file fails to pass one of these checks, no forwarding is performed and the message is delivered as usual. These checks are configured using the forward.file-checks statement:

forward {
  file .forward;
  file-checks (list);
}  

Its argument is a list of the following keywords:

groupwritablefile
file_iwgrp

The file must not be group writable.

worldwritablefile
file_iwoth

The file must not be world writable.

linkedfileinwritabledir
link

The file cannot be a symlink in a writable directory.

fileingroupwritabledir
dir_iwgrp

The file cannot reside in a group writable directory.

fileinworldwritabledir
dir_iwoth

The file cannot reside in a world writable directory.

all

All of the above checks.

The default is ‘file-checks all’.

Each of these keywords may be prefixed by ‘no’ to disable this particular check. For example:

forward {
  file-checks (nodir_iwoth, nodir_iwgrp);
  file .forward;
}  

3.12.7 MDA Configuration File Summary

The behavior of mda is affected by the following configuration statements:

StatementReference
debugSee debug statement.
mailboxSee mailbox statement.
lockingSee locking statement.
pamSee pam statement.
sqlSee sql statement.
virtdomainSee virtdomain statement.
radiusSee radius statement.
ldapSee ldap statement.
authSee auth statement.
mailerSee mailer statement.
MDA Config: stderr bool

If bool is true, log to standard error instead of syslog.

MDA Config: deliver { ... }

This section contains general delivery settings:

deliver {
  domain string;
  exit-multiple-delivery-success arg;
};
deliver: domain name

Default email domain.

deliver: exit-multiple-delivery-success arg;

In case of multiple delivery, exit with code 0 if at least one delivery succeeded.

MDA Config: forward { ... }

Controls the forwarding support:

forward {
  file name;
  file-checks (list);
}
forward: file name

Defines the name of the forward file. E.g.:

forward {
  file .forward;
}  

See Forwarding, for a detailed description.

forward: file-checks (list)

Configures safety checks to be performed on the forward file prior to using it. See Forwarding, for a detailed description.

MDA Config: quota { ... }

This section configures mail quota support. See Mailbox Quotas, for a detailed description.

quota {
  database name;
  sql-query query;
  exit-tempfail bool;
}
quota: database name

Sets the name of the quota database in DBM format. See DBM Quotas.

quota: sql-query query

If the quotas are kept in a SQL table, this statement defines the SQL query to retrieve the quota for a given user name. See SQL Quotas.

quota: exit-tempfail bool

By default, if a message cannot be delivered because the user has exceeded its mail quota, or its delivery would cause it to be exceeded, MDA exits with the ‘service unavailable’ status, which causes MTA to return the 550 code. If exit-tempfail is set to true, it will return a temporary error instead.

MDA Config: script { ... }

Controls scripting. See MDA Scripting.

script {
  language lang;
  pattern glob;
}
script: language lang

Defines the language that is used for scripting. Allowed values for lang are: ‘sieve’, ‘scheme’, or ‘python’. See scripting language.

script: pattern pat

Defines the pattern for the script file name. The ‘~’ at the begiining of the pattern will be replaced with the name of the home directory of the recipient user. The ‘%u’ in pattern will be replaced with the recipient user name, and ‘%h’ with the home directory for that user.

3.12.8 Mailing list implementation

This subsection explains how to implement mailing lists in mda using the ‘prog’ mailbox scheme.

Delivery to the ‘prog’ mailbox results in invoking the specified command with the given arguments and passing the message to its standard input. There are two ways to specify a ‘prog’ mailbox:

prog://program?args

Here, program is the absolute pathname of the program binary, and args are its arguments, separated by ‘&’ signs.

|program args

In this notation, args are command line arguments separated by white space.

In both cases, args do not include argv[0].

The ‘prog’ mailbox can be used to implement mailing lists.

For example, suppose that the mda configuration contains:

auth {
  authorization (sql, system);
  authentication (generic, system);
}

sql {
  interface mysql;
  db mail;
  getpwnam "SELECT user as name, mailbox, "
           "'x' as passwd, 500 as uid, 2 as gid, "
           "'/nonexistent' as dir, '/sbin/nologin' as shell "
           "FROM userdb "
           "WHERE user='${user}'";
}

Then, the following entries in the ‘userdb’ table implement the mailman@yourdomain mailing list:

mysql> select * from userdb;
+---------------------+---------------------------------------+
| user                | mailbox                               |
+---------------------+---------------------------------------+
| mailman             | |/usr/bin/mailman post mailman        |
| mailman-admin       | |/usr/bin/mailman admin mailman       |
| mailman-bounces     | |/usr/bin/mailman bounces mailman     |
| mailman-confirm     | |/usr/bin/mailman confirm mailman     |
| mailman-join        | |/usr/bin/mailman join mailman        |
| mailman-leave       | |/usr/bin/mailman leave mailman       |
| mailman-owner       | |/usr/bin/mailman owner mailman       |
| mailman-request     | |/usr/bin/mailman request mailman     |
| mailman-subscribe   | |/usr/bin/mailman subscribe mailman   |
| mailman-unsubscribe | |/usr/bin/mailman unsubscribe mailman |
+---------------------+---------------------------------------+

GNU Mailutils Manual (split by section):   Section:   Chapter:FastBack: Programs   Up: mda   FastForward: Libraries   Contents: Table of ContentsIndex: Function Index