This is mailfromd.info, produced by makeinfo version 6.8 from
mailfromd.texi.

Copyright (C) 2005-2025 Sergey Poznyakoff

   Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.  A
copy of the license is included in the section entitled "GNU Free
Documentation License".
INFO-DIR-SECTION Email
START-INFO-DIR-ENTRY
* Mailfromd: (mailfromd).          General-purpose mail-filtering software.
* mailfromd: (mailfromd) Invocation.  Mail Filtering and Real-time Modification daemon.
* calloutd: (mailfromd) calloutd.  A Stand-Alone Callout Daemon.
* mfdbtool: (mailfromd) mfdbtool.  Database Management Tool.
* mtasim: (mailfromd) mtasim.      MTA simulator.
* pmult: (mailfromd) pmult.        Pmilter multiplexer program.
END-INFO-DIR-ENTRY

    









     Dedico aquest treball a Lluis Llach, per obrir els nous horitzons.
    
    


File: mailfromd.info,  Node: Library,  Next: Using MFL Mode,  Prev: MFL,  Up: Top

5 The MFL Library Functions
***************************

This chapter describes library functions available in Mailfromd version
9.1.  For the simplicity of explanation, we use the word 'boolean' to
indicate variables of numeric type that are used as boolean values.  For
such variables, the term 'False' stands for the numeric 0, and 'True'
for any non-zero value.

* Menu:

* Macro access::
* Character translation::
* String transformation::
* String manipulation::
* String formatting::
* Character Type::
* I/O functions::
* Filtering functions::
* Email processing functions::
* Envelope modification functions::
* Header modification functions::
* Body Modification Functions::
* Message modification queue::
* Mail header functions::
* Mail body functions::
* EOM Functions::
* Current Message Functions::
* Mailbox functions::
* Message functions::
* Quarantine functions::
* SMTP Callout functions::
* Compatibility Callout functions::
* Internet address manipulation functions::
* DNS functions::
* Geolocation functions::
* Database functions::
* Control database::
* System functions::
* Passwd functions::
* Sieve Interface::
* Interfaces to Third-Party Programs::
* Rate limiting functions::
* Greylisting functions::
* Special test functions::
* Mail Sending Functions::
* Blacklisting Functions::
* SPF Functions::
* DKIM::
* Sockmaps::
* NLS Functions::
* Syslog Interface::
* Debugging Functions::
* Informative Functions::
* Mfmod Interface::


File: mailfromd.info,  Node: Macro access,  Next: Character translation,  Up: Library

5.1 Sendmail Macro Access Functions
===================================

 -- Built-in Function: string getmacro (string MACRO)
     Returns the value of Sendmail macro MACRO.  If MACRO is not
     defined, raises the 'e_macroundef' exception.

     Calling 'getmacro(NAME)' is completely equivalent to referencing
     '${NAME}', except that it allows to construct macro names
     programmatically, e.g.:

            if getmacro("auth_%var") = "foo"
              ...
            fi

 -- Built-in Function: boolean macro_defined (string NAME)
     Return true if Sendmail macro NAME is defined.

   Notice, that if your MTA supports macro name negotiation(1), you will
have to export macro names used by these two functions using '#pragma
miltermacros' construct.  Consider this example:

     func authcheck(string name)
     do
       string macname "auth_%name"
       if macro_defined(macname)
         if getmacro(macname)
           ...
         fi
       fi
     done

     #pragma miltermacros envfrom auth_authen

     prog envfrom
     do
       authcheck("authen")
     done

   In this case, the parser cannot deduce that the 'envfrom' handler
will attempt to reference the 'auth_authen' macro, therefore the
'#pragma miltermacros' is used to help it.

   ---------- Footnotes ----------

   (1) That is, if it supports Milter protocol 6 and upper.  Sendmail
8.14.0 and Postfix 2.6 and newer do.  MeTA1 (via 'pmult') does as well.
*Note MTA Configuration::, for more details.


File: mailfromd.info,  Node: Character translation,  Next: String transformation,  Prev: Macro access,  Up: Library

5.2 'tr', 'dc', and 'sq' functions
==================================

 -- Built-in Function: string tr (string SUBJ, string SET1, string SET2)
     Translates characters in string SUBJ and returns the resulting
     string.

     Translation rules are defined by two character sets: SET1 is a set
     of characters which, when encountered in SUBJ, must be replaced
     with the corresponding characters from SET2.  E.g.:

          tr('text', 'tx', 'ni') => 'nein'

     The source set SET1 can contain "character classes", sets of
     characters enclosed in square brackets.  A character class matches
     the input character if that character is listed in the class.  When
     a match occurs, the character is replaced with the corresponding
     character from SET2:

          tr('abacus', '[abc]', '_') => '____us'

     An exclamation sign at the beginning of a character class reverses
     its meaning, i.e.  the class matches any character _not_ listed in
     it:

          tr('abacus', '[!abc]', '_') => 'abac__'

     A character set can contain "ranges", specified as the first and
     last characters from the range separated by a dash.  A range 'X-Y'
     comprises all characters between X and Y inclusive.  For example,
     '[a-d]' is equivalent to '[abcd]'.  Character sets must be
     ascending, i.e.  '[a-d]' is correct, but '[d-a]' is not.  You may
     include '-' in its literal meaning by making it the first or last
     character between the brackets: '[0-9-]' matches any digit or dash.

     Similarly, to include a closing bracket, make it the first
     character in the class (after negation character, for excluding
     ranges), e.g.  '[][!]' matches the three characters '[', ']' and
     '!', whereas '[!][]' matches any character except '[' and ']'.

     "Named character classes" are special reserved names between '[:'
     and ':]' delimiters:

     '[:alnum:]'
          Matches any alphanumeric character.  Equivalent to
          '[:alpha:][:digit:]'.

     '[:alpha:]'
          Matches any alphabetic character.

     '[:blank:]'
          Matches horizontal space or tab.

     '[:cntrl:]'
          Matches a "control character", i.e.  a character with ASCII
          code less than 32.

     '[:digit:]'
          Matches a decimal digit (0 through 9).

     '[:graph:]'
          Matches any printable character except space (horizontal space
          and tab).

     '[:lower:]'
          Matches any lowercase letter.

     '[:print:]'
          Matches any printable character including space.

     '[:punct:]'
          Matches any printable character which is not a space or an
          alphanumeric character.

     '[:space:]'
          Matches 'white-space' characters: horizontal space (ASCII 32),
          form-feed (ASCII 12, or '\f'), newline (ASCII 10, or '\n'),
          carriage return (ASCII 13, or '\r'), horizontal tab (ASCII 9,
          or '\t'), and vertical tab (ASCII 11, or '\v').

     '[:upper:]'
          Matches any upper case letter.

     '[:xdigit:]'
          Matches any hexagesimal digit: '0' through '9', 'a' through
          'f' and 'A' through 'F'.

     Named classes can appear in character classes in SET1 anywhere a
     regular character is allowed.  Examples:

     '[][:alpha:]-]'
          Mathes alphabet letters (both cases), digits, closing bracket
          and dash.

     '[!][:alpha:]-]'
          A complement of the above: matches any character except the
          ones listed above.

     '[[:xdigit:][:blank:]]'
          Matches any hexagesimal digit or horizontal whitespace
          characters.

     The replacement set must not be empty.  Its length must be equal to
     or less than that of SET1 (character classes being counted as one
     character).  If SET1 contains more characters than SET2, the
     surplus ones will be translated to the last character from SET2:

          tr('lasted', 'alde', 'iL?') => 'List??'

     Both sets can contain character ranges, represented as 'C1-C2'.
     Whenever a range appears in SET1, a range must appear in the
     corresponding position of SET2:

          tr('gnu', 'a-z', 'A-Z') => 'GNU'

     Character ranges are not to be confused with ranges in character
     classes: they are similiar, but quite distinct.  Both match a
     single character, but while ranges translate to a corresponding
     character from the replacement range, ranges within character class
     translate to a single character:

          tr('gnu', '[a-z]', 'A') => 'AAA'

     Character ranges in SET1 must always be in ascending order (i.e.
     'a-z' is allowed, whereas 'z-a' is not).  Ranges in SET2 can be
     both ascending and descending, e.g.:

          tr('8029', '0-9', '9-0') => '1970'

     To translate a dash, place it as the first or last character in
     SET1:

          tr('in-place', '-ilp', ' Irg') => 'In grace'

     The 'tr' function will raise the 'e_inval' exception if SET2 is
     empty or SET1 contains a range without matching range in SET2.  It
     will raise the 'e_range' exception, if a descending range appears
     in SET1 or number of characters in a range from SET1 does not match
     that from the corresponding range in SET2.

 -- Built-in Function: string dc (string SUBJ, string SET1)
     Deletes from SUBJ characters that appear in SET1.  The syntax of
     SET1 is as described in 'tr', except that character ranges are
     treated as if appearing within character class (e.g.  'a-z' is the
     same as '[a-z]').

     For example, 'dc(subj, '0-9')' removes decimal digits from first
     argument.

 -- Built-in Function: string sq (string SUBJ, string SET1)
     Squeezes repeats, i.e.  replaces each sequence of a repeated
     character that is listed in SET1, with a single occurrence of that
     character.  The syntax of SET1 is as described in 'tr', except that
     character ranges are treated as if appearing within character class
     (e.g.  'a-z' is the same as '[a-z]').

     For example, 'sq(subj, '[[:space:]]')' replaces multiple
     occurrences of whitespace characters with a single character.


File: mailfromd.info,  Node: String transformation,  Next: String manipulation,  Prev: Character translation,  Up: Library

5.3 The 'sed' function
======================

The 'sed' function allows you to transform a string by replacing parts
of it that match a regular expression with another string.  This
function is somewhat similar to the 'sed' command line utility (hence
its name) and bears similarities to analogous functions in other
programming languages (e.g.  'sub' in 'awk' or the 's//' operator in
'perl').

 -- Built-in Function: string sed (string SUBJECT, EXPR, ...)
     The EXPR argument is an "s-expressions" of the the form:

          s/REGEXP/REPLACEMENT/[FLAGS]

     where REGEXP is a "regular expression", and REPLACEMENT is a
     replacement string for each part of the SUBJECT that matches
     REGEXP.  When 'sed' is invoked, it attempts to match SUBJECT
     against the REGEXP.  If the match succeeds, the portion of SUBJECT
     which was matched is replaced with REPLACEMENT.  Depending on the
     value of FLAGS (*note global replace::), this process may continue
     until the entire SUBJECT has been scanned.

     The resulting output serves as input for next argument, if such is
     supplied.  The process continues until all arguments have been
     applied.

     The function returns the output of the last s-expression.

   Both REGEXP and REPLACEMENT are described in detail in *note The "s"
Command: (sed)The "s" Command.

   Supported FLAGS are:

'g'
     Apply the replacement to _all_ matches to the REGEXP, not just the
     first.

'i'
     Use case-insensitive matching.  In the absence of this flag, the
     value set by the recent '#pragma regex icase' is used (*note icase:
     pragma regex.).

'x'
     REGEXP is an "extended regular expression" (*note Extended regular
     expressions: (sed)Extended regexps.).  In the absence of this flag,
     the value set by the recent '#pragma regex extended' (if any) is
     used (*note extended: pragma regex.).

'NUMBER'
     Only replace the NUMBERth match of the REGEXP.

     Note: the POSIX standard does not specify what should happen when
     you mix the 'g' and NUMBER modifiers.  'Mailfromd' follows the GNU
     'sed' implementation in this regard, so the interaction is defined
     to be: ignore matches before the NUMBERth, and then match and
     replace all matches from the NUMBERth on.

   Any delimiter can be used in lieue of '/', the only requirement being
that it be used consistently throughout the expression.  For example,
the following two expressions are equivalent:

     s/one/two/
     s,one,two,

   Changing delimiters is often useful when the REGEX contains slashes.
For instance, it is more convenient to write 's,/,-,' than 's/\//-/'.

   Here is an example of 'sed' usage:

       set email sed(input, 's/^<(.*)>$/\1/x')

It removes angle quotes from the value of the 'input' variable and
assigns the result to 'email'.

   To apply several s-expressions to the same input, you can either give
them as multiple arguments to the 'sed' function:

       set email sed(input, 's/^<(.*)>$/\1/x', 's/(.+@)(.+)/\1\L\2\E/x')

or give them in a single argument separated with semicolons:

       set email sed(input, 's/^<(.*)>$/\1/x;s/(.+@)(.+)/\1\L\2\E/x')

Both examples above remove optional angle quotes and convert the domain
name part to lower case.

   Regular expressions used in 'sed' arguments are controlled by the
'#pragma regex', as another expressions used throughout the MFL source
file.  To avoid using the 'x' modifier in the above example, one can
write:

       #pragma regex +extended
       set email sed(input, 's/^<(.*)>$/\1/', 's/(.+@)(.+)/\1\L\2\E/')

   *Note regex::, for details about that '#pragma'.

   So far all examples used constant s-expressions.  However, this is
not a requirement.  If necessary, the expression can be stored in a
variable or even constructed on the fly before passing it as argument to
'sed'.  For example, assume that you wish to remove the domain part from
the value, but only if that part matches one of predefined domains.  Let
a regular expression that matches these domains be stored in the
variable 'domain_rx'.  Then this can be done as follows:

       set email sed(input, "s/(.+)(@%domain_rx)/\1/")

   If the constructed regular expression uses variables whose value
should be matched exactly, such variables must be quoted before being
used as part of the regexp.  Mailfromd provides a convenience function
for this:

 -- Built-in Function: string qr (string STR[; string DELIM])
     Quote the string STR as a regular expression.  This function
     selects the characters to be escaped using the currently selected
     regular expression flavor (*note regex::).  At most two additional
     characters that must be escaped can be supplied in the DELIM
     optional parameter.  For example, to quote the variable 'x' for use
     in double-quoted s-expression:

            qr(x, '/"')


File: mailfromd.info,  Node: String manipulation,  Next: String formatting,  Prev: String transformation,  Up: Library

5.4 String Manipulation Functions
=================================

 -- Built-in Function: string escape (string STR, [string CHARS])
     Returns a copy of STR with the characters from CHARS escaped, i.e.
     prefixed with a backslash.  If CHARS is not specified, '\"' is
     assumed.

          escape('"a\tstr"ing') => '\"a\\tstr\"ing'
          escape('new "value"', '\" ') => 'new\ \"value\"'

 -- Built-in Function: string unescape (string STR)
     Performs the reverse to 'escape', i.e.  removes any prefix
     backslash characters.

          unescape('a \"quoted\" string') => 'a "quoted" string'

 -- Built-in Function: string unescape (string STR, [string CHARS])

 -- Built-in Function: string domainpart (string STR)
     Returns the domain part of STR, if it is a valid email address,
     otherwise returns STR itself.

          domainpart("gray") => "gray"
          domainpart("gray@gnu.org.ua") => "gnu.org.ua"

 -- Built-in Function: number index (string S, string T)
 -- Built-in Function: number index (string S, string T, number START)
     Returns the index of the first occurrence of the string T in the
     string S, or -1 if T is not present.

          index("string of rings", "ring") => 2

     Optional argument START, if supplied, indicates the position in
     string where to start searching.

          index("string of rings", "ring", 3) => 10

     To find the last occurrence of a substring, use the function RINDEX
     (*note rindex::).

 -- Built-in Function: number interval (string STR)
     Converts STR, which should be a valid time interval specification
     (*note time interval specification::), to seconds.

 -- Built-in Function: number length (string STR)
     Returns the length of the string STR in bytes.

          length("string") => 6

 -- Built-in Function: string dequote (string STR)
     Removes '<' and '>' surrounding STR.  If STR is not enclosed by
     angle brackets or these are unbalanced, the argument is returned
     unchanged:

          dequote("<root@gnu.org.ua>") => "root@gnu.org.ua"
          dequote("root@gnu.org.ua") => "root@gnu.org.ua"
          dequote("there>") => "there>"

 -- Built-in Function: string localpart (string STR)
     Returns the local part of STR if it is a valid email address,
     otherwise returns STR unchanged.

          localpart("gray") => "gray"
          localpart("gray@gnu.org.ua") => "gray"

 -- Built-in Function: string replstr (string S, number N)
     Replicate a string, i.e.  return a string, consisting of S repeated
     N times:

          replstr("12", 3) => "121212"

 -- Built-in Function: string revstr (string S)
     Returns the string composed of the characters from S in reversed
     order:

          revstr("foobar") => "raboof"

 -- Built-in Function: number rindex (string S, string T)
 -- Built-in Function: number rindex (string S, string T, number START)

     Returns the index of the last occurrence of the string T in the
     string S, or -1 if T is not present.

          rindex("string of rings", "ring") => 10

     Optional argument START, if supplied, indicates the position in
     string where to start searching.  E.g.:

          rindex("string of rings", "ring", 10) => 2

     See also *note 'index' built-in function: index-built-in.

 -- Built-in Function: string substr (string STR, number START)
 -- Built-in Function: string substr (string STR, number START, number
          LENGTH)

     Returns the at most LENGTH-character substring of STR starting at
     START.  If LENGTH is omitted, the rest of STR is used.

     If LENGTH is greater than the actual length of the string, the
     'e_range' exception is signalled.

          substr("mailfrom", 4) => "from"
          substr("mailfrom", 4, 2) => "fr"

 -- Built-in Function: string substring (string STR, number START,
          number END)
     Returns a substring of STR between offsets START and END,
     inclusive.  Negative END means offset from the end of the string.
     In other words, yo obtain a substring from START to the end of the
     string, use 'substring(STR, START, -1)':

          substring("mailfrom", 0, 3) => "mail"
          substring("mailfrom", 2, 5) => "ilfr"
          substring("mailfrom", 4, -1) => "from"
          substring("mailfrom", 4, length("mailfrom") - 1) => "from"
          substring("mailfrom", 4, -2) => "fro"

     This function signals 'e_range' exception if either START or END
     are outside the string length.

 -- Built-in Function: string tolower (string STR)

     Returns a copy of the string STR, with all the upper-case
     characters translated to their corresponding lower-case
     counterparts.  Non-alphabetic characters are left unchanged.

          tolower("MAIL") => "mail"

 -- Built-in Function: string toupper (string STR)
     Returns a copy of the string STR, with all the lower-case
     characters translated to their corresponding upper-case
     counterparts.  Non-alphabetic characters are left unchanged.

          toupper("mail") => "MAIL"

 -- Built-in Function: string ltrim (string STR[, string CSET)
     Returns a copy of the input string STR with any leading characters
     present in CSET removed.  If the latter is not given, white space
     is removed (spaces, tabs, newlines, carriage returns, and line
     feeds).

          ltrim("  a string") => "a string"
          ltrim("089", "0") => "89"

     Note the last example.  It shows how 'ltrim' can be used to convert
     decimal numbers in string representation that begins with '0'.
     Normally such strings will be treated as representing octal
     numbers.  If they are indeed decimal, use 'ltrim' to strip off the
     leading zeros, e.g.:

          set dayofyear ltrim(strftime('%j', time()), "0")

 -- Built-in Function: string rtrim (string STR[, string CSET)
     Returns a copy of the input string STR with any trailing characters
     present in CSET removed.  If the latter is not given, white space
     is removed (spaces, tabs, newlines, carriage returns, and line
     feeds).

 -- Built-in Function: number vercmp (string A, string B)
     Compares two strings as 'mailfromd' version numbers.  The result is
     negative if B precedes A, zero if they refer to the same version,
     and positive if B follows A:

          vercmp("5.0", "5.1") => 1
          vercmp("4.4", "4.3") => -1
          vercmp("4.3.1", "4.3") => -1
          vercmp("8.0", "8.0") => 0

 -- Library Function: string sa_format_score (number CODE, number PREC)
     Format CODE as a floating-point number with PREC decimal digits:

          sa_format_score(5000, 3) => "5.000"

     This function is convenient for formatting SpamAssassin scores for
     use in message headers and textual reports.  It is defined in
     module 'sa.mfl'.

     *Note SpamAssassin: sa, for examples of its use.

 -- Library Function: string sa_format_report_header (string TEXT)
     Format a SpamAssassin report text in order to include it in a RFC
     822 header.  This function selects the score listing from TEXT, and
     prefixes each line with '* '.  Its result looks like:

          *  0.2 NO_REAL_NAME           From: does not include a real name
          *  0.1 HTML_MESSAGE           BODY: HTML included in message

     *Note SpamAssassin: sa, for examples of its use.

 -- Library Function: string strip_domain_part (string DOMAIN, number N)

     Returns at most N last components of the domain name DOMAIN.  If N
     is 0 the function returns DOMAIN.

     This function is defined in the module 'strip_domain_part.mfl'
     (*note Modules::).

     Examples:

          require strip_domain_part
          strip_domain_part("puszcza.gnu.org.ua", 2) => "org.ua"
          strip_domain_part("puszcza.gnu.org.ua", 0) => "puszcza.gnu.org.ua"

 -- Library Function: string verp_extract_user (string EMAIL, string
          DOMAIN)

     If EMAIL is a valid VERP-style email address for DOMAIN, that
     corresponds to a valid local user name (*note validuser::), this
     function returns the local user name, corresponding to that email.
     Otherwise, it returns empty string.

     For example, assuming the local user 'gray' exists:

          verp_extract_user("gray=gnu.org.ua@tuhs.org", 'gnu\..*')
            => "gray"


File: mailfromd.info,  Node: String formatting,  Next: Character Type,  Prev: String manipulation,  Up: Library

5.5 String formatting
=====================

 -- Built-in Function: string sprintf (string FORMAT, ...)
     The function 'sprintf' formats its argument according to FORMAT
     (see below) and returns the resulting string.  It takes varying
     number of parameters, the only mandatory one being FORMAT.

Format string
-------------

The format string is a simplified version of the format argument to C
'printf'-family functions.

   The format string is composed of zero or more "directives": ordinary
characters (not '%'), which are copied unchanged to the output stream;
and "conversion specifications", each of which results in fetching zero
or more subsequent arguments.  Each conversion specification is
introduced by the character '%', and ends with a conversion specifier.
In between there may be (in this order) zero or more "flags", an
optional "minimum field width", and an optional "precision".

   Notice, that in practice that means that you should use single quotes
with the FORMAT arguments, to protect conversion specifications from
being recognized as variable references (*note singe-vs-double::).

   No type conversion is done on arguments, so it is important that the
supplied arguments match their corresponding conversion specifiers.  By
default, the arguments are used in the order given, where each '*' and
each conversion specifier asks for the next argument.  If insufficiently
many arguments are given, 'sprintf' raises 'e_range' exception.  One can
also specify explicitly which argument is taken, at each place where an
argument is required, by writing '%M$', instead of '%' and '*M$' instead
of '*', where the decimal integer M denotes the position in the argument
list of the desired argument, indexed starting from 1.  Thus,

         sprintf('%*d', width, num);
and
         sprintf('%2$*1$d', width, num);
are equivalent.  The second style allows repeated references to the same
argument.

Flag characters
---------------

The character '%' is followed by zero or more of the following "flags":

'#'
     The value should be converted to an "alternate form".  For 'o'
     conversions, the first character of the output string is made zero
     (by prefixing a '0' if it was not zero already).  For 'x' and 'X'
     conversions, a non-zero result has the string '0x' (or '0X' for 'X'
     conversions) prepended to it.  Other conversions are not affected
     by this flag.

'0'
     The value should be zero padded.  For 'd', 'i', 'o', 'u', 'x', and
     'X' conversions, the converted value is padded on the left with
     zeros rather than blanks.  If the '0' and '-' flags both appear,
     the '0' flag is ignored.  If a precision is given, the '0' flag is
     ignored.  Other conversions are not affected by this flag.

'-'
     The converted value is to be left adjusted on the field boundary.
     (The default is right justification.)  The converted value is
     padded on the right with blanks, rather than on the left with
     blanks or zeros.  A '-' overrides a '0' if both are given.

'' ' (a space)'
     A blank should be left before a positive number (or empty string)
     produced by a signed conversion.

'+'
     A sign ('+' or '-') always be placed before a number produced by a
     signed conversion.  By default a sign is used only for negative
     numbers.  A '+' overrides a space if both are used.

Field width
-----------

An optional decimal digit string (with nonzero first digit) specifying a
minimum field width.  If the converted value has fewer characters than
the field width, it will be padded with spaces on the left (or right, if
the left-adjustment flag has been given).  Instead of a decimal digit
string one may write '*' or '*M$' (for some decimal integer M) to
specify that the field width is given in the next argument, or in the
M-th argument, respectively, which must be of numeric type.  A negative
field width is taken as a '-' flag followed by a positive field width.
In no case does a non-existent or small field width cause truncation of
a field; if the result of a conversion is wider than the field width,
the field is expanded to contain the conversion result.

Precision
---------

An optional precision, in the form of a period ('.') followed by an
optional decimal digit string.  Instead of a decimal digit string one
may write '*' or '*M$' (for some decimal integer M) to specify that the
precision is given in the next argument, or in the M-th argument,
respectively, which must be of numeric type.  If the precision is given
as just '.', or the precision is negative, the precision is taken to be
zero.  This gives the minimum number of digits to appear for 'd', 'i',
'o', 'u', 'x', and 'X' conversions, or the maximum number of characters
to be printed from a string for the 's' conversion.

Conversion specifier
--------------------

A character that specifies the type of conversion to be applied.  The
conversion specifiers and their meanings are:

d
i
     The numeric argument is converted to signed decimal notation.  The
     precision, if any, gives the minimum number of digits that must
     appear; if the converted value requires fewer digits, it is padded
     on the left with zeros.  The default precision is '1'.  When '0' is
     printed with an explicit precision '0', the output is empty.

o
u
x
X
     The numeric argument is converted to unsigned octal ('o'), unsigned
     decimal ('u'), or unsigned hexadecimal ('x' and 'X') notation.  The
     letters 'abcdef' are used for 'x' conversions; the letters 'ABCDEF'
     are used for 'X' conversions.  The precision, if any, gives the
     minimum number of digits that must appear; if the converted value
     requires fewer digits, it is padded on the left with zeros.  The
     default precision is '1'.  When '0' is printed with an explicit
     precision 0, the output is empty.

s
     The string argument is written to the output.  If a precision is
     specified, no more than the number specified of characters are
     written.

%
     A '%' is written.  No argument is converted.  The complete
     conversion specification is '%%'.


File: mailfromd.info,  Node: Character Type,  Next: I/O functions,  Prev: String formatting,  Up: Library

5.6 Character Type
==================

These functions check whether all characters of STR fall into a certain
character class according to the 'C' ('POSIX') locale(1).  'True' (1) is
returned if they do, 'false' (0) is returned otherwise.  In the latter
case, the global variable 'ctype_mismatch' is set to the index of the
first character that is outside of the character class (characters are
indexed from 0).

 -- Built-in Function: boolean isalnum (string STR)
     Checks for alphanumeric characters:

            isalnum("a123") => 1
            isalnum("a.123") => 0 (ctype_mismatch = 1)

 -- Built-in Function: boolean isalpha (string STR)
     Checks for an alphabetic character:

            isalnum("abc") => 1
            isalnum("a123") => 0

 -- Built-in Function: boolean isascii (string STR)
     Checks whether all characters in STR are 7-bit ones, that fit into
     the ASCII character set.

            isascii("abc") => 1
            isascii("ab\0200") => 0

 -- Built-in Function: boolean isblank (string STR)
     Checks if STR contains only blank characters; that is, spaces or
     tabs.

 -- Built-in Function: boolean iscntrl (string STR)
     Checks for control characters.

 -- Built-in Function: boolean isdigit (string STR)
     Checks for digits (0 through 9).

 -- Built-in Function: boolean isgraph (string STR)
     Checks for any printable characters except spaces.

 -- Built-in Function: boolean islower (string STR)
     Checks for lower-case characters.

 -- Built-in Function: boolean isprint (string STR)
     Checks for printable characters including space.

 -- Built-in Function: boolean ispunct (string STR)
     Checks for any printable characters which are not a spaces or
     alphanumeric characters.

 -- Built-in Function: boolean isspace (string STR)
     Checks for white-space characters, i.e.: space, form-feed ('\f'),
     newline ('\n'), carriage return ('\r'), horizontal tab ('\t'), and
     vertical tab ('\v').

 -- Built-in Function: boolean isupper (string STR)
     Checks for uppercase letters.

 -- Built-in Function: boolean isxdigit (string STR)
     Checks for hexadecimal digits, i.e.  one of '0', '1', '2', '3',
     '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'A',
     'B', 'C', 'D', 'E', 'F'.

   ---------- Footnotes ----------

   (1) Support for other locales is planned for future versions.


File: mailfromd.info,  Node: I/O functions,  Next: Filtering functions,  Prev: Character Type,  Up: Library

5.7 I/O functions
=================

MFL provides a set of functions for writing to disk files, pipes or
sockets and reading from them.  The idea behind them is the same as in
most other programming languages: first you open the resource with a
call to 'open' which returns a "descriptor" i.e.  an integer number
uniquely identifying the resource.  Then you can write or read from it
using this descriptor.  Finally, when the resource is no longer needed,
you can close it with a call to 'close'.

   The number of available resource descriptors is limited.  The default
limit is 1024.  You can tailor it to your needs using the 'max-streams'
runtime configuration statement.  *Note max-streams: conf-runtime, for a
detailed description.

   By default, all I/O operations are unbuffered.  This can be changed
by setting the following global variables:

 -- Built-in variable: number io_buffering
     Sets the default buffering type.  Allowed values are (symbolic
     names are defined in 'status.mfl' module):

     '0'
     'BUFFER_NONE'
          No buffering.  This is the default.

     '1'
     'BUFFER_FULL'
          Full buffering.  Size of the buffer is set by the
          'io_buffer_size' global variable (see below).

     '2'
     'BUFFER_LINE'
          Line buffering.  When reading, it is pretty much the same as
          'BUFFER_FULL'.  When writing, the data are accumulated in
          buffer and actually sent to the underlying transport stream
          when the newline character is seen.

          The initial size of the buffer is set by the 'io_buffer_size'
          variable.  It will grow as needed during the I/O.

 -- Built-in variable: number io_buffer_size
     Set the buffer size if 'io_buffering' is set to 'BUFFER_FULL' or
     'BUFFER_LINE'.  By default, this variable is set to the size of the
     system page.

 -- Built-in Function: number open (string NAME)
     The NAME argument specifies the name of a resource to open and the
     access rights you need to have on it.  The function returns a
     descriptor of the opened stream, which can subsequently be used as
     an argument to other I/O operations.

     Buffering mode for the opened stream is defined by the
     'io_buffering' and 'io_buffer_size' global variables.  It can be
     changed using the 'setbuf' function (*note setbuf::).

     First symbols of NAME determine the type of the resource to be
     opened and the access mode:

     '>'
          The rest of NAME is a name of a file.  Open the file for
          read-write access.  If the file exists, truncate it to zero
          length, otherwise create the file.

     '>>'
          The rest of NAME is a name of a file.  Open the file for
          appending (writing at end of file).  The file is created if it
          does not exist.

     '|'
          Treat the rest of NAME as the command name and its arguments.
          Run this command and open its standard input for writing.  The
          standard error is closed before launching the program.  This
          can be altered by using the following versions of this
          construct:

          |2>null: COMMAND
               Standard error is redirected to '/dev/null'.

          |2>file:NAME COMMAND
               Execute COMMAND with its standard error redirected to the
               file NAME.  If the file exists, it will be truncated.

          |2>>file:NAME COMMAND
               Standard error of the COMMAND is appended to the file
               NAME.  If file does not exist, it will be created.

               The '|2>null:' construct described above is a shortcut
               for

                    |2>>file:/dev/null COMMAND

          |2>syslog:FACILITY[.PRIORITY] COMMAND
               Standard error is redirected to the given syslog FACILITY
               and, optionally, PRIORITY.  If the latter is omitted,
               'LOG_ERR' is assumed.

               Valid values for FACILITY are: 'user', 'daemon', 'auth',
               'authpriv', 'mail', and 'local0' through 'local7'.  Valid
               values for PRIORITY are: 'emerg', 'alert', 'crit', 'err',
               'warning', 'notice', 'info', 'debug'.  Both FACILITY and
               PRIORITY may be given in upper, lower or mixed cases.

          Notice, that no whitespace characters are allowed between '|'
          and '2>'.

     '|<'
          Treat the rest of NAME as the command name and its arguments.
          Run this command with its stdin closed and stdout open for
          reading.

          The standard error is treated as described above (see '|').

     '|&'
          Treat the rest of NAME as the command name and its arguments.
          Run this command and set up for two-way communication with it,
          i.e writes to the descriptor returned by 'open' will send data
          to the program's standard input, reads from the descriptor
          will get data from the program's standard output.

          The standard error is treated as described above (see '|').
          For example, the following redirects it to syslog
          'mail.debug':

               |&2>syslog:mail.debug COMMAND

     '@'
          Treat the rest of NAME as the URL of a socket to connect to.
          Valid URL forms are described in *note milter port
          specification::.

     If none of these prefixes is used, NAME is treated as a name of an
     existing file and 'open' will attempt to open this file for
     reading.

     The 'open' function will signal exception 'e_failure' if it is
     unable to open the resource or get the required access to it.

 -- Built-in Function: number spawn (string CMD [, number IN, number
          OUT, number ERR])
     Runs the supplied command CMD.  The syntax of the CMD is the same
     as for the NAME argument to 'open' (see above), which begins with
     '|', excepting that the '|' sign is optional.  That is:

          spawn("/bin/cat")

     has exactly the same effect as

          open("|/bin/cat")

     Optional arguments specify file stream descriptors to be used for
     the program standard input, output and error streams,
     correspondingly.  If supplied, these should be the values returned
     by a previous call to 'open' or 'tempfile'.  The value '-1' means
     no redirection.

     Buffering mode for the opened stream is defined by the
     'io_buffering' and 'io_buffer_size' global variables.  It can be
     changed using the 'setbuf' function (*note setbuf::).

     The example below starts the 'awk' program with a simple expression
     as its argument and redirects the content of the file '/etc/passwd'
     to its standard input.  The returned stream descriptor is bound to
     the command's standard output (see the description of '|<' prefix
     above).  The standard error is closed:

          number fd spawn("<awk -F: '{print $1}'", open("/etc/passwd"))

 -- Built-in Function: void close (number RD)
     The argument RD is a resource descriptor returned by a previous
     call to 'open'.  The function 'close' closes the resource and
     deallocates any memory associated with it.

     'close' will signal 'e_range' exception if RD lies outside of
     allowed range of resource descriptors.  *Note max-streams:
     conf-runtime.

   Notice that you are not required to close resources opened by 'open'.
Any unclosed resource will be closed automatically upon the termination
of the filtering program.

 -- Built-in Function: void shutdown (number RD, number HOW)
     This function causes all or part of a full-duplex connection to be
     closed.  The RD must be either a socket descriptor (returned by
     'open(@...)') or a two-way pipe socket descriptor (returned by
     'open(|&...)'), otherwise the call to 'shutdown' is completely
     equivalent to 'close'.

     The 'how' argument identifies which part of the connection to shut
     down:

     SHUT_RD
          Read connection.  All further receptions will be disallowed.

     SHUT_WR
          Write connection.  All further transmissions will be
          disallowed.

     SHUT_RDWR
          Shut down both read and write parts.

 -- Built-in Function: number tempfile ([string TMPDIR])
     Creates a nameless temporary file and returns its descriptor.
     Optional TMPDIR supplies the directory where to create the file,
     instead of the default '/tmp'.

 -- Built-in Function: void rewind (number RD)
     Rewinds the stream identified by RD to its beginning.

 -- Built-in Function: number copy (number DST, number SRC)
     Copies all data from the stream SRC to DST.  Returns number of
     bytes copied.

   The following functions provide basic read/write capabilities.

 -- Built-in Function: void write (number RD, string STR [, number
          SIZE])
     Writes the string STR to the resource descriptor RD.  If the SIZE
     argument is given, writes this number of bytes.

     This function always attempts to write the requested amount of
     data.  It will signal 'e_range' exception if RD lies outside of
     allowed range of resource descriptors, 'e_io' exception if an I/O
     error occurs, and 'e_eof' exception if it wrote 0 bytes (e.g.
     because the underlying device is full).

 -- Built-in Function: void write_body (number RD, pointer BP , number
          SIZE)
     Write the body segment of length SIZE from pointer BP to the stream
     RD.  This function can be used only in 'prog body' (*note body
     handler::).  Its second and third arguments correspond exactly to
     the parameters of the 'body' handler, so the following construct
     writes the message body to the resource 'fd', which should have
     been open prior to invoking the 'body' handler:

          prog body
          do
            write_body(fd, $1, $2)
          done

 -- Built-in Function: string read (number RD, number N)
     Read and return at most N bytes from the resource descriptor RD.

     If there are less than N bytes in the stream, the remaining bytes
     will be returned.  Use 'length()' to obtain the actual size of the
     returned data.  If there are no bytes left, the 'e_eof' exception
     will be signalled.

     The function may signal the following exceptions:

     e_range
          RD lies outside of allowed range of resource descriptors.
     e_eof
          End of file encountered.
     e_io
          An I/O error occurred.

 -- Built-in Function: string getdelim (number RD, string DELIM)
     Read and return the next string terminated by DELIM from the
     resource descriptor RD.

     The terminating DELIM string will be removed from the return value.

     When using this function, it is highly recommended to enable full
     buffering for FD, either by setting 'io_buffering' before 'open'
     (*note io_buffering::) or by calling 'setbuf' after it (*note
     setbuf::).  *Note getline::, for an example.

     This function may signal the following exceptions:

     e_range
          RD lies outside of allowed range of resource descriptors.
     e_eof
          End of file encountered.
     e_io
          An I/O error occurred.

 -- Built-in Function: string getline (number RD)
     Read and return the next "line" from the resource descriptor RD.  A
     line is any sequence of characters terminated with the default
     "line delimiter".  The default delimiter is a property of RD, i.e.
     different descriptors can have different line delimiters.  The
     default value is '\n' (ASCII 10), and can be changed using the
     'fd_set_delimiter' function (see below).

     When using this function, it is highly recommended to enable full
     buffering for FD, either by setting 'io_buffering' before 'open'
     (*note io_buffering::) or by calling 'setbuf' after it (*note
     setbuf::), e.g.:

            set fd open(input)
            setbuf(fd, BUFFER_FULL)
            set line getline(fd)
            ...

     This function may signal the following exceptions:

     e_range
          RD lies outside of allowed range of resource descriptors.
     e_eof
          End of file encountered.
     e_io
          An I/O error occurred.

 -- Built-in Function: void fd_set_delimiter (number FD, string DELIM)
     Set new line delimiter for the descriptor FD, which must be in
     opened state.

     Default delimiter is a newline character (ASCII 10).  The following
     example shows how to change it to CRLF sequence:

          fd_set_delimiter(fd, "\r\n")

 -- Built-in Function: string fd_delimiter (number FD)
     Returns the line delimiter string for FD.

   The following example shows how 'mailfromd' I/O functions can be used
to automatically add IP addresses to an RBL zone:

     set nsupdate_cmd
       "/usr/bin/nsupdate -k /etc/bind/Kmail.+157+14657.private"

     func block_address(string addr)
     do
       number fd
       string domain

       set fd open "|%nsupdate_cmd"

       set domain reverse_ipstr(addr) . ".rbl.myzone.come"
       write(fd, "prereq nxrrset %domain A\n"
                  "update add %domain 86400 A %addr\n\n"
     done

The function 'reverse_ipstr' is defined in *note Internet address
manipulation functions::.

 -- Built-in Function: void setbuf(number FD, [number TYPE, number
          SIZE])
     Changes the buffering mode of FD according to the remaining two
     arguments.  The TYPE specifies buffering type (*note
     io_buffering::), and SIZE supplies the buffer size for buffering
     types 'BUFFER_FULL' and 'BUFFER_LINE'.  If SIZE is omitted, it
     defaults to 'io_buffer_size' (*note io_buffer_size::).  Omitted
     TYPE defaults to 'io_buffering' (*note io_buffering::).

 -- Built-in Function: number getbuftype(number FD)
     Returns the type of buffering currently in effect for the
     descriptor FD.  *Note io_buffering::, for a list of possible return
     values.

     If this function returns 'BUFFER_FULL' or 'BUFFER_LINE', you can
     use 'getbufsize' to get the associated buffer size.

 -- Built-in Function: number getbufsize(number FD)
     Returns the buffer size for the descriptor FD.


File: mailfromd.info,  Node: Filtering functions,  Next: Email processing functions,  Prev: I/O functions,  Up: Library

5.8 Filtering functions
=======================

This section describes functions that transform data using Mailutils
"filter pipes".  Filter pipe is a string defining data flow between
several "filters".  Each filter takes input, transforms it according to
certain rules and produces the transformed data on its output.  As in
shell, multiple filters are connected using pipe characters ('|').  For
example, the 'crlf' filter inserts a carriage return character before
each newline character.  A filter doing that kind of transformation is
defined as:

     "crlf"

   Another filter, 'base64', converts its input to a BASE64 encoded
string.  To transform each newline into carriage return + newline pair
and encode the resulting stream in BASE64, one would write:

     "crlf | base64"

   Some filters take one or more "arguments".  These are specified as a
comma-delimited list in parentheses after the filter name.  For example,
the 'linelen' filter limits the length of each output line to the given
number of octets.  The following filter pipe will limit the length of
base64 lines in the filter above to 62 octets:

     "crlf | base64 | linelen(62)"

   Many filters operate in two modes: "encode" and "decode".  By default
all MFL functions apply filters in encode mode.  The desired mode can be
stated explicitly in the filter string by using 'encode()' and
'decode()' functions.  They take a filter pipe line as their argument.
For example, the following will decode the stream produced by the
example filter above:

     "decode(base64 | crlf)"

   *Note Filters::, for a discussion of available filters and their
arguments.

 -- Built-in Function: string filter_string (string INPUT, string
          FILTER_PIPE)
     Transforms the string INPUT using filters in FILTER_PIPE and
     returns the result.  Example:

          set input "test\ninput\n"
          filter_string(input, "crlf|base64") => "dGVzdA0KaW5wdXQNCg=="

 -- Built-in Function: void filter_fd (number SRC_FD, number DST_FD,
          string FILTER_PIPE)
     Given two I/O descriptors, reads data from SRC_FD, transforms it
     using FILTER_PIPE and writes the result to descriptor DST_FD.

     Both descriptors must be obtained using functions described in
     *note I/O functions::.

* Menu:

* Filters::


File: mailfromd.info,  Node: Filters,  Up: Filtering functions

5.8.1 Filters and Filter Pipes
------------------------------

A "filter pipe" is a string consisting of filter invocations delimited
by pipe characters ('|').  Each invocation is a filter name optionally
followed by a comma-separated list of parameters.  Most filters can
operate in two modes: "encode" and "decode".  Unless specified
otherwise, filters are invoked in encode mode.  To change the mode, the
'encode' and 'decode' meta-filters are provided.  Argments to these
filters are filter pipes that will be executed in the corresponding
mode.

   The following Mailutils filters are available:

 -- Filter: 7bit
     In encode mode, converts its input into 7-bit ASCII, by clearing
     the 8th bit on each processed byte.

     In decode mode, it operates exactly as the 8bit filter, i.e.
     copies its input to the output verbatim.

     The filter takes no arguments.

 -- Filter: 8bit
 -- Filter: binary
     Copies its input to output verbatim.

 -- Filter: base64
 -- Filter: B
     Encodes or decodes the input using the 'base64' encoding.

     The only difference between 'BASE64' and 'B' is that, in encode
     mode, the former limits each ouput line length to 76 octets,
     whereas the latter produces a contiguous stream of base64 data.

     In decode mode, both filters operate exactly the same way.

 -- Filter: charset (CSET)
 -- Filter: charset (CSET, FALLBACK)
     A convenience interface to the 'iconv' filter, available for use
     only in the 'message_body_to_stream' function.  It decodes the part
     of a MIME message from its original character set, which is
     determined from the value of the 'Content-Type' header, to the
     destination character set CSET.  Optional FALLBACK parameter
     specifies the representation fallback to be used for octets that
     cannot be converted between the charater sets.  Its use is
     described in *Note iconv::.

     This filter is normally takes its input from the 'mimedecode'
     filter, as in:

          message_body_to_stream(fd, msg, 'mimedecode|charset(utf-8)')

     *Note mimedecode::, for a detailed discussion.

 -- Filter: crlf
 -- Filter: rfc822
     Converts line separators from LF (ASCII 10) to CRLF (ASCII 13 10)
     and vice-versa.

     In decode mode, translates each CRLF to LF. Takes no arguments.

     In encode mode, translates each LF to CRLF. If an optional argument
     '-n' is given, produces a "normalized" output, by preserving each
     input CRLF sequence untouched (otherwise such sequences will be are
     translated to CR CR LF).

 -- Filter: crlfdot
     In encode mode, replaces each LF ('\n' or ASCII 10) character with
     CRLF ('\r\n', ASCII 13 10), and "byte-stuffs" the output by
     producing an additional '.' in front of any '.' appearing at the
     beginning of a line in input.  Upon end of input, it outputs
     additional '.\r\n', if the last output character was '\n', or
     '\r\n.\r\n' otherwise.

     If supplied the '-n' argument, it preserves each CRLF input
     sequence untranslated (see the 'CRLF' above).

     In decode mode, the reverse is performed: each CRLF is replaced
     with a single LF byte, and additional dots are removed from
     beginning of lines.  A single dot on a line by itself marks the end
     of the stream and causes the filter to return EOF.

 -- Filter: dot
     In encode mode, "byte-stuffs" the input by outputting an additional
     dot ('.') in front of any dot appearing at the beginning of a line.
     Upon encountering end of input, it outputs additional '.\n'.

     In decode mode, the reverse is performed: additional dots are
     removed from beginning of lines.  A single dot on a line by itself
     (i.e.  the sequence '\n.\n') marks the end of the stream and causes
     the filter to return EOF.

     This filter doesn't take arguments.

 -- Filter: from
     Performs a traditional UNIX processing of lines starting with a
     'From' followed by a space character.

     In encode mode, each 'From ' at the beginning of a line is replaced
     by '>From '.

     In decode mode, the reverse operation is performed: initial
     greater-then sign ('>') is removed from any line starting with
     '>From '.

     The filter takes no arguments.

 -- Filter: fromrd
     MBOXRD-compatible processing of envelope lines.

     In encode mode, each 'From ' optionally preceded by any number of
     contiguous '>' characters and appearing at the beginning of a line
     is prefixed by another '>' character on output.

     In decode mode, the reverse operation is performed: initial
     greater-then sign ('>') is removed from any line starting with one
     or more '>' characters followed by 'From '.

 -- Filter: header
     This filter treats its input as a RFC-2822 email message.  It
     extracts its header part (i.e.  everything up to the first empty
     line) and copies it to the output.  The body of the message is
     ignored.

     The filter operates only in decode mode and takes no arguments.

 -- Filter: iconv (SRC, DST [, FALLBACK])
     Converts input from character set SRC to DST.  The filter works the
     same way in both decode and encode modes.

     It takes two mandatory arguments: the names of the input (SRC) and
     output (DST) charset.  Optional third argument specifies what to do
     when an illegal character sequence is encountered in the input
     stream.  Its possible values are:

     'none'
          Raise a 'e_ilseq' exception.

     'copy-pass'
          Copy the offending octet to the output verbatim and continue
          conversion from the next octet.

     'copy-octal'
          Print the offending octet to the output using the C octal
          conversion and continue conversion from the next octet.

     The default is 'copy-octal'.

     The following example creates a 'iconv' filter for converting from
     'iso-8859-2' to 'utf-8', raising the 'e_ilseq' exception on the
     first conversion error:

          iconv(iso-8859-2, utf-8, none)

 -- Filter: inline-comment
 -- Filter: inline-comment (STR, [options])
     In decode mode, the filter removes from the input all lines
     beginning with a given "inline comment sequence" STR.  The default
     comment sequence is ';' (a semicolon).

     The following options modify the default behavior:

     '-i, STR'
          Emit line number information after each contiguous sequence of
          removed lines.  The argument STR supplies an "information
          starter" - a sequence of characters which is output before the
          actual line number.

     '-r'
          Remove empty lines, i.e.  the lines that contain only
          whitespace characters.

     '-s'
          Squeeze whitespace.  Each sequence of two or more whitespace
          characters encountered on input is replaced by a single space
          character on output.

     '-S'
          A "whitespace-must-follow" mode.  A comment sequence is
          recognized only if followed by a whitespace character.  The
          character itself is retained on output.

     In encode mode the 'inline-comment' filter adds a comment-starter
     sequence at the beginning of each line.  The default
     comment-starter is ';' and can be changed by specifying the desired
     comment starter as the first argument.

     The only option supported in this mode is '-S', which enables the
     whitespace-must-follow mode, in which a single space character
     (ASCII 20) is output after each comment sequence.

 -- Filter: linecon
 -- Filter: linecon (-i, STR)
     Implements a familiar UNIX line-continuation facility.  The filter
     removes from itsinput stream any newline character immediately
     preceded by a backslash.  This filter operates only in decode mode.

     If given the arguments ('-i', STR), enables the "line number
     information facility".  This facility emits current input line
     number (prefixed with STR) after each contiguous sequence of one or
     more removed newline characters.  It is useful for implementing
     parsers which are normally supposed to identify eventual erroneous
     lines with their input line numbers.

 -- Filter: linelen (N)
     Limits the length of each output line to a certain number of
     octets.  It operates in encode mode only and requires a single
     parameter: the desired output length in octets.  This filter makes
     no attempt to analyze the lexical structure of the input: the
     newline caracters are inserted when the length of the output line
     reaches a predefined maximum.  Any newline characters present in
     the input are taken into account when computing the input line
     length.

 -- Filter: mimedecode
     This is a domain-specific filter available for use only with the
     'message_body_to_stream' function.  It decodes the part of a MIME
     message from whatever encoding that was used to store it in the
     message to a stream of bytes.  *Note mimedecode::.

 -- Filter: quoted-printable
 -- Filter: Q
     Encodes or decodes the input using the "quoted-printable" encoding.

 -- Filter: XML
     In encode mode, the 'xml' filter converts input stream (which must
     contain valid UTF-8 characters) into a form suitable for inclusion
     into a XML or HTML document, i.e.  it replaces '<', '>', and '&'
     with '&lt;', '&gt;', and '&amp;', correspondingly, and replaces
     invalid characters with their numeric character reference
     representation.

     In decode mode, a reverse operation is performed.

     The filter does not take arguments.


File: mailfromd.info,  Node: Email processing functions,  Next: Envelope modification functions,  Prev: Filtering functions,  Up: Library

5.9 Email processing functions
==============================

 -- Built-in Function: number email_map (string EMAIL)
     Parses EMAIL and returns a bitmap, consisting of zero or more of
     the following flags:

     'EMAIL_MULTIPLE'
          EMAIL has more than one email address.

     'EMAIL_COMMENTS'
          EMAIL has comment parts.

     'EMAIL_PERSONAL'
          EMAIL has personal part.

     'EMAIL_LOCAL'
          EMAIL has local part.

     'EMAIL_DOMAIN'
          EMAIL has domain part.

     'EMAIL_ROUTE'
          EMAIL has route part.

     These constants are declared in the 'email.mfl' module.  The
     function 'email_map' returns 0 if its argument is not a valid email
     address.

 -- Library Function: boolean email_valid (string EMAIL)
     Returns 'True' (1) if EMAIL is a valid email address, consisting of
     local and domain parts only.  E.g.:

          email_valid("gray@gnu.org") => 1
          email_valid("gray") => 0
          email_valid('"Sergey Poznyakoff <gray@gnu.org>') => 0

     This function is defined in 'email.mfl' (*note Modules::).


File: mailfromd.info,  Node: Envelope modification functions,  Next: Header modification functions,  Prev: Email processing functions,  Up: Library

5.10 Envelope Modification Functions
====================================

Envelope modification functions set sender and add or delete recipient
addresses from the message envelope.  This allows MFL scripts to
redirect messages to another addresses.

 -- Built-in Function: void set_from (string EMAIL [, string ARGS])
     Sets envelope sender address to EMAIL, which must be a valid email
     address.  Optional ARGS supply arguments to ESMTP 'MAIL FROM'
     command.

 -- Built-in Function: void rcpt_add (string ADDRESS)
     Add the e-mail ADDRESS to the envelope.

 -- Built-in Function: void rcpt_delete (string ADDRESS)
     Remove ADDRESS from the envelope.

   The following example code uses these functions to implement a simple
alias-like capability:

     prog envrcpt
     do
        string alias dbget(aliasdb, $1, "NULL", 1)
        if alias != "NULL"
          rcpt_delete($1)
          rcpt_add(alias)
        fi
     done


File: mailfromd.info,  Node: Header modification functions,  Next: Body Modification Functions,  Prev: Envelope modification functions,  Up: Library

5.11 Header Modification Functions
==================================

There are two ways to modify message headers in a MFL script.  First is
to use header actions, described in *note Actions::, and the second way
is to use message modification functions.  Compared with the actions,
the functions offer a series of advantages.  For example, using
functions you can construct the name of the header to operate upon (e.g.
by concatenating several arguments), something which is impossible when
using actions.  Moreover, apart from three basic operations (add, modify
and remove), as supported by header actions, header functions allow to
insert a new header into a particular place.

 -- Built-in Function: void header_add (string NAME, string VALUE)
     Adds a header 'NAME: VALUE' to the message.

     In contrast to the 'add' action, this function allows to construct
     the header name using arbitrary MFL expressions.

 -- Built-in Function: void header_add (string NAME, string VALUE,
          number IDX)
     This syntax is preserved for backward compatibility.  It is
     equivalent to 'header_insert', which see.

 -- Built-in Function: void header_insert (string NAME, string VALUE,
          number IDX)
     This function inserts a header 'NAME: 'value'' at IDXth header
     position in the internal list of headers maintained by the MTA.
     That list contains headers added to the message either by the
     filter or by the MTA itself, but not the headers included in the
     message itself.  Some of the headers in this list are conditional,
     e.g.  the ones added by the 'H?COND?' directive in 'sendmail.cf'.
     MTA evaluates them after all header modifications have been done
     and removes those of headers for which they yield false.  This
     means that the position at which the header added by
     'header_insert' will appear in the final message will differ from
     IDX.

 -- Built-in Function: void header_delete (string NAME [, number INDEX])
     Delete header NAME from the envelope.  If INDEX is given, delete
     INDEXth instance of the header NAME.

     Notice the differences between this function and the 'delete'
     action:

       1. It allows to construct the header name, whereas 'delete'
          requires it to be a literal string.

       2. Optional INDEX argument allows to select a particular header
          instance to delete.

 -- Built-in Function: void header_replace (string NAME, string VALUE [,
          number INDEX])
     Replace the value of the header NAME with VALUE.  If INDEX is
     given, replace INDEXth instance of header NAME.

     Notice the differences between this function and the 'replace'
     action:

       1. It allows to construct the header name, whereas 'replace'
          requires it to be a literal string.

       2. Optional INDEX argument allows to select a particular header
          instance to replace.

 -- Library Function: void header_rename (string NAME, string NEWNAME[,
          number IDX])

     Defined in the module 'header_rename.mfl'.
     Available only in the 'eom' handler.

     Renames the IDXth instance of header NAME to NEWNAME.  If IDX is
     not given, assumes 1.

     If the specified header or the IDX instance of it is not present in
     the current message, the function silently returns.  All other
     errors cause run-time exception.

     The position of the renamed header in the header list is not
     preserved.

     The example below renames 'Subject' header to 'X-Old-Subject':

          require 'header_rename'

          prog eom
          do
            header_rename("Subject", "X-Old-Subject")
          done

 -- Library Function: void header_prefix_all (string NAME [, string
          PREFIX])

     Defined in the module 'header_rename.mfl'.
     Available only in the 'eom' handler.

     Renames all headers named NAME by prefixing them with PREFIX.  If
     PREFIX is not supplied, removes all such headers.

     All renamed headers will be placed in a continuous block in the
     header list.  The absolute position in the header list will change.
     Relative ordering of renamed headers will be preserved.

 -- Library Function: void header_prefix_pattern (string PATTERN, string
          PREFIX)

     Defined in the module 'header_rename.mfl'.
     Available only in the 'eom' handler.

     Renames all headers with names matching PATTERN (in the sense of
     'fnmatch', *note fnmatches: Special comparisons.) by prefixing them
     with PREFIX.

     All renamed headers will be placed in a continuous block in the
     header list.  The absolute position in the header list will change.
     Relative ordering of renamed headers will be preserved.

     If called with one argument, removes all headers matching PATTERN.

     For example, to prefix all headers beginning with 'X-Spamd-' with
     an additional 'X-':

          require 'header_rename'

          prog eom
          do
            header_prefix_pattern("X-Spamd-*", "X-")
          done


File: mailfromd.info,  Node: Body Modification Functions,  Next: Message modification queue,  Prev: Header modification functions,  Up: Library

5.12 Body Modification Functions
================================

Body modification is an experimental feature of MFL.  The version 9.1
provides only one function for that purpose.

 -- Built-in Function: void replbody (string TEXT)
     Replace the body of the message with TEXT.  Notice, that TEXT must
     not contain RFC 822 headers.  See the previous section if you want
     to manipulate message headers.

     Example:

            replbody("Body of this message has been removed by the mail filter.")

     No restrictions are imposed on the format of TEXT.

 -- Built-in Function: void replbody_fd (number FD)
     Replaces the body of the message with the content of the stream FD.
     Use this function if the body is very big, or if it is returned by
     an external program.

     Notice that this function starts reading from the current position
     in FD.  Use 'rewind' if you wish to read from the beginning of the
     stream.

     The example below shows how to preprocess the body of the message
     using external program '/usr/bin/mailproc', which is supposed to
     read the body from its standard input and write the processed text
     to its standard output:

          number fd   # Temporary file descriptor

          prog data
          do
            # Open the temporary file
            set fd tempfile()
          done

          prog body
          do
            # Write the body to it.
            write_body(fd, $1, $2)
          done

          prog eom
          do
            # Use the resulting stream as the stdin to the mailproc
            # command and read the new body from its standard output.
            rewind(fd)
            replbody_fd(spawn("</usr/bin/mailproc", fd))
          done


File: mailfromd.info,  Node: Message modification queue,  Next: Mail header functions,  Prev: Body Modification Functions,  Up: Library

5.13 Message Modification Queue
===============================

Message modification functions described in the previous subsections do
not take effect immediately, in the moment they are called.  Instead
they store the requested changes in the internal "message modification
queue".  These changes are applied at the end of processing, before
'eom' stage finishes (*note Figure 3.1: milter-control-flow.).

   One important consequence of this way of operation is that calling
any MTA action (*note Actions::), causes all prior modifications to the
message to be ignored.  That is because after receiving the action
command, MTA will not call filter for that message any more.  In
particular, the 'eom' handler will not be called, and the message
modification queue will not be flushed.  While it is logical for such
actions as 'reject' or 'tempfail', it may be quite confusing for
'accept'.  Consider, for example, the following code:

     prog envfrom
     do
       if $1 == ""
         header_add("X-Filter", "foo")
         accept
       fi
     done

   Obviously, the intention was to add a 'X-Filter' header and accept
the message if it was sent from the null address.  What happens in
reality, however, is a bit different: the message is accepted, but no
header is added to it.  If you need to accept the message and retain any
modifications you have done to it, you need to use an auxiliary
variable, e.g.:

     number accepted 0
     prog envfrom
     do
       if $1 == ""
         header_add("X-Filter", "foo")
         set accepted 1
       fi
     done

   Then, test this variable for non-zero value at the beginning of each
subsequent handler, e.g.:

     prog data
     do
       if accepted
         continue
       fi
       ...
     done

   To help you trace such problematic usages of 'accept', 'mailfromd'
emits the following warning:

     RUNTIME WARNING near /etc/mailfromd.mfl:36: `accept' causes previous
     message modification commands to be ignored; call mmq_purge() prior
     to `accept', to suppress this warning

   If it is OK to lose all modifications, call 'mmq_purge', as suggested
in this message.

 -- Built-in Function: void mmq_purge ()
     Remove all modification requests from the queue.  This function
     undoes the effect of any of the following functions, if they had
     been called previously: 'rcpt_add', 'rcpt_delete', 'header_add',
     'header_insert', 'header_delete', 'header_replace', 'replbody',
     'quarantine'.


File: mailfromd.info,  Node: Mail header functions,  Next: Mail body functions,  Prev: Message modification queue,  Up: Library

5.14 Mail Header Functions
==========================

 -- Built-in Function: string message_header_encode (string TEXT,
          [string ENC, string CHARSET])
     Encode TEXT in accordance with RFC 2047.  Optional arguments:

     ENC
          Encoding to use.  Valid values are 'quoted-printable', or 'Q'
          (the default) and 'base64', or 'B'.

     CHARSET
          Character set.  By default 'UTF-8'.

     If the function is unable to encode the string, it raises the
     exception 'e_failure'.

     For example:

          set string "Keld Jørn Simonsen <keld@dkuug.dk>"
          message_header_encode(string, "ISO-8859-1")
            => "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>"

 -- Built-in Function: string message_header_decode (string TEXT,
          [string CHARSET])
     TEXT must be a header value encoded in accordance with RFC 2047.
     The function returns the decoded string.  If the decoding fails, it
     raises 'e_failure' exception.  The optional argument CHARSET
     specifies the character set to use (default - 'UTF-8').

          set string "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>"
          message_header_decode(string)
           => "Keld Jørn Simonsen <keld@dkuug.dk>"

 -- Built-in Function: string unfold (string TEXT)
     If TEXT is a "folded" multi-line RFC 2822 header value, unfold it.
     If TEXT is a single-line string, return its unchanged copy.

     For example, suppose that the message being processed contained the
     following header:

          List-Id: Sent bugreports to
            <some-address@some.net>

     Then, applying 'unfold' to its value(1) will produce:

          Sent bugreports to <some-address@some.net>

   ---------- Footnotes ----------

   (1) For example:

     prog header
     do
       echo unfold($2)
     done


File: mailfromd.info,  Node: Mail body functions,  Next: EOM Functions,  Prev: Mail header functions,  Up: Library

5.15 Mail Body Functions
========================

 -- Built-in Function: string body_string (pointer TEXT, number COUNT)
     Converts first COUNT bytes from the memory location pointed to by
     TEXT into a regular string.

     This function is intended to convert the '$1' argument passed to a
     'body' handler to a regular MFL string.  For more information about
     its use, see *note body handler::.

 -- Built-in Function: bool body_has_nulls (pointer TEXT, number COUNT)
     Returns 'True' if first COUNT bytes of the string pointed to by
     TEXT contain ASCII NUL characters.

     Example:

          prog body
          do
            if body_has_nulls($1, $2)
              reject
            fi
          done


File: mailfromd.info,  Node: EOM Functions,  Next: Current Message Functions,  Prev: Mail body functions,  Up: Library

5.16 EOM Functions
==================

The following function is available only in the 'eom' handler:

 -- Built-in Function: void progress ()
     Notify the MTA that the filter is still processing the message.
     This causes MTA to restart its timeouts and allows additional
     amount of time for execution of 'eom'.

     Use this function if your 'eom' handler needs additional time for
     processing the message (e.g.  for scanning a very big MIME
     message).  You may call it several times, if the need be, although
     such usage is not recommended.


File: mailfromd.info,  Node: Current Message Functions,  Next: Mailbox functions,  Prev: EOM Functions,  Up: Library

5.17 Current Message Functions
==============================

 -- Built-in Function: number current_message ()
     This function can be used in 'eom' handlers only.  It returns a
     message descriptor referring to the current message.  *Note Message
     functions::, for a description of functions for accessing messages.

   The functions below access the headers from the current message.
They are available in the following handlers: 'eoh', 'body', 'eom'.

 -- Built-in Function: number current_header_count ([string NAME])

     Return number of headers in the current message.  If NAME is
     specified, return number of headers that have this name.

            current_header_count() => 6
            current_header_count("Subject") => 1

 -- Built-in Function: string current_header_nth_name (number N)

     Return the name of the Nth header.  The index N is 1-based.

 -- Built-in Function: string current_header_nth_value (number N)

     Return the value of the Nth header.  The index N is 1-based.

 -- Built-in Function: string current_header (string name [, number N])
     Return the value of the named header, e.g.:

            set s current_header("Subject")

     Optional second argument specifies the header instance, if there
     are more than 1 header of the same name, e.g.:

            set s current_header("Received", 2)

     Header indices are 1-based.

   All current_header function raise the 'e_not_found' exception if the
requested header was not found.


File: mailfromd.info,  Node: Mailbox functions,  Next: Message functions,  Prev: Current Message Functions,  Up: Library

5.18 Mailbox Functions
======================

A set of functions is provided for accessing mailboxes and messages
within them.  In this subsection we describe the functions for accessing
mailboxes.

   A mailbox is opened using 'mailbox_open' function:

 -- Built-in Function: number mailbox_open (string URL [, string MODE,
          string PERMS])
     Open a mailbox identified by URL.  Return a "mailbox descriptor": a
     unique numeric identifier that can subsequently be used to access
     this mailbox.

     The optional MODE argument specifies the access mode for the
     mailbox.  Its valid values are:

     Value          Meaning
     ------------------------------------------------------------------
     r              Open mailbox for reading.  This is the default.
     w              Open mailbox for writing.  If the mailbox does
                    not exist, it is created.
     rw             Open mailbox for reading and writing.  If the
                    mailbox does not exist, it is created.
     wr             Same as 'rw'.
     w+             Open mailbox for reading and writing.  If the
                    mailbox does not exist, it is created.
     a              Open mailbox for appending messages to it.  If
                    the mailbox does not exist, an exception is
                    signalled.
     a+             Open mailbox for appending messages to it.  If
                    the mailbox does not exist, it is created.

     The optional PERMS argument specifies the permissions to use in
     case a new file (or files) is created.  It is a comma-separated
     list of:

          [go](+|=)[wr]+

     The initial letter controls which users' access is to be set: users
     in the file's group ('g') or other users not in the file's group
     ('o').  The following character controls whether the permissions
     are added to the default ones ('+') or applied instead of them
     ('=').  The remaining letters specify the permissions: 'r' for read
     access and 'w' for write access.  For example:

          g=rw,o+r

   The number of mailbox descriptors available for simultaneous opening
is 64.  This value can be changed using the 'max-open-mailboxes' runtime
configuration statement (*note max-open-mailboxes: conf-runtime.).

 -- Built-in Function: number mailbox_messages_count (number NMBX)
     Return the number of messages in mailbox.  The argument NMBX is a
     valid mailbox descriptor as returned by a previous call to
     'mailbox_open'.

 -- Built-in Function: number mailbox_get_message (number MBX, number N)
     Retrieve Nth message from the mailbox identified by descriptor MBX.
     On success, the function returns a "message descriptor", an integer
     number that can subsequently be used to access that message (*note
     Message functions::).  On error, an exception is raised.

     Messages in a mailbox are numbered starting from 1.

 -- Built-in Function: void mailbox_close (number NMBX)
     Close a mailbox previously opened by 'mailbox_open'.

 -- Built-in Function: void mailbox_append_message (number NMBX, number
          NMSG)
     Append message NMSG to mailbox NMBX.  The message descriptor NSMG
     must be obtained from a previous call to 'mailbox_get_message' or
     'current_message' (*note current_message::).


File: mailfromd.info,  Node: Message functions,  Next: Quarantine functions,  Prev: Mailbox functions,  Up: Library

5.19 Message Functions
======================

The functions described below retrieve information from RFC822 messages.
The message to operate upon is identified by its "descriptor", an
integer number returned by the previous call to 'mailbox_get_message'
(*note mailbox_get_message: Mailbox functions.) or 'current_message'
(*note current_message::) function.  The maximum number of message
descriptors is limited by 1024.  You can change this limit using the
'max-open-messages' runtime configuration statement (*note
max-open-messages: conf-runtime.).

 -- Built-in Function: number message_size (number NMSG)
     Return the size of the message NMSG, in bytes.  _Notice_, that if
     NMSG refers to current message (*note current_message::), the
     returned value is less than the size seen by the MTA, because
     'mailfromd' recodes CR-LF sequences to LF, i.e.  removes carriage
     returns (ASCII 13) occurring before line feeds (ASCII 10.  To
     obtain actual message length as seen by the MTA, add the number of
     lines in the message:

            set actual_length message_size(nmsg) + message_lines(nmsg)

 -- Built-in Function: boolean message_body_is_empty (number NMSG)
     Returns 'true' if the body of message NMSG has zero size or
     contains only whitespace characters.  If the
     'Content-Transfer-Encoding' header is present, it is used to decode
     body before processing.

 -- Built-in Function: void message_close (number NMSG)
     Close the message identified by descriptor NMSG.

 -- Built-in Function: number message_lines (number NMSG)
     Return total number of lines in message NMSG.  The following
     relation holds true:

          message_lines(X) = message_body_lines(X)
                                   + message_header_lines(X) + 1

 -- Built-in Function: string message_read_line (number NMSG)
     Read and return next line from the message NMSG.  If there are no
     more lines to read, raise the 'eof' exception.

     Use 'message_rewind' to rewind the message stream and read its
     contents again.

 -- Built-in Function: void message_rewind (number NMSG)
     Rewind the stream associated with message referred to by descriptor
     NMSG.

 -- Built-in Function: number message_from_stream (number FD; string
          FILTER_CHAIN)
     Converts contents of the stream identified by FD to a mail message.
     Returns identifier of the created message.

     Optional FILTER_CHAIN supplies the name of a "Mailutils filter
     chain", through which the data will be passed before converting.
     See <http://mailutils.org/wiki/Filter_chain>, for a description of
     filter chains.

 -- Built-in Function: void message_to_stream (number FD, number NMSG;
          string FILTER_CHAIN)
     Copies message NSMG to stream descriptor FD.  The descriptor must
     be obtained by a previous call to 'open'.

     Optional FILTER_CHAIN supplies the name of a "Mailutils filter
     chain", through which the data will be passed before writing them
     to FD.  See <http://mailutils.org/wiki/Filter_chain>, for a
     description of filter chains.

* Menu:

* Header functions::
* Message body functions::
* MIME functions::
* Message digest functions::


File: mailfromd.info,  Node: Header functions,  Next: Message body functions,  Up: Message functions

5.19.1 Header functions
-----------------------

 -- Built-in Function: number message_header_size (number NMSG)
     Return the size, in bytes of the headers of message NMSG.  See the
     note to the 'message_size', above.

 -- Built-in Function: number message_header_lines (number NMSG)
     Return number of lines occupied by headers in message NMSG.

 -- Built-in Function: number message_header_count (number NMSG, [string
          NAME])
     Return number of headers in message NMSG.

     If NAME is supplied, count only headers with that name.

 -- Built-in Function: string message_find_header (number NMSG, string
          NAME [, number IDX])
     Return value of header NAME from the message NMSG.  If the message
     contains several headers with the same name, optional parameter IDX
     may be used to select one of them.  Headers are numbered from '1'.

     If no matching header is not found, the 'not_found' exception is
     raised.  If another error occurs, the 'failure' exception is
     raised.

     The returned string is a verbatim copy of the message contents
     (except for eventual CR-LF -> LF translation, see above).  You
     might need to apply the 'unfold' function to it (*note unfold: Mail
     header functions.).

 -- Built-in Function: string message_nth_header_name (number NMSG,
          number N)
     Returns the name of the Nth header in message NMSG.  If there is no
     such header, 'e_range' exception is raised.

 -- Built-in Function: string message_nth_header_value (number MSG,
          number N)
     Returns the value of the Nth header in message NMSG.  If there is
     no such header, 'e_range' exception is raised.

 -- Built-in Function: boolean message_has_header (number NMSG, string
          NAME [, number IDX])
     Return 'true' if message NMSG contains header with the given NAME.
     If there are several headers with the same name, optional parameter
     IDX may be used to select one of them.


File: mailfromd.info,  Node: Message body functions,  Next: MIME functions,  Prev: Header functions,  Up: Message functions

5.19.2 Message body functions
-----------------------------

 -- Built-in Function: number message_body_size (number NMSG)
     Return the size, in bytes, of the body of message NMSG.  See the
     note to the 'message_size', above.

 -- Built-in Function: number message_body_lines (number NMSG)
     Return number of lines in the body of message referred to by
     descriptor NMSG.

 -- Built-in Function: void message_body_rewind (number NMSG)
     Rewind the stream associated with the body of message referred to
     by descriptor NMSG.

     A call to 'message_body_read_line' (see below) after calling this
     function will return the first line from the message body.

 -- Built-in Function: string message_read_body_line (number NMSG)
     Read and return next line from the body of the message NMSG.  If
     there are no more lines to read, raise the 'eof' exception.

     Use 'message_body_rewind' (see above) to rewind the body stream and
     read its contents again.

 -- Built-in Function: void message_body_to_stream (number FD, number
          NMSG; string FILTER_PIPE)
     Copies the body of the message NSMG to stream descriptor FD.  The
     descriptor must be obtained by a previous call to 'open'.

     Optional FILTER_PIPE supplies a sequence of "Mailutils filters",
     through which the data will be passed before writing them to FD.
     *Note Filtering functions::, for a discussion of filter pipe
     syntax.

     In addition to filters described in *Note Filters::, two special
     filters are provided for use with this function: 'mimedecode' and
     'charset'.  The 'mimedecode' filter instructs the function to
     decode the message body by reverting the encoding specified by its
     'Content-Transfer-Encoding' header.  It is normally used as the
     very first filter in chain.  The 'charset' filter recodes the
     message body from it original character set to the character set
     specified as its argument.

     *Note mimedecode::, for a detailed discussion of this feature.


File: mailfromd.info,  Node: MIME functions,  Next: Message digest functions,  Prev: Message body functions,  Up: Message functions

5.19.3 MIME functions
---------------------

 -- Built-in Function: boolean message_is_multipart (number NMSG)
     Return 'true' if message NMSG is a multipart (MIME) message.

 -- Built-in Function: number message_count_parts (number NMSG)
     Return number of parts in message NMSG, if it is a multipart (MIME)
     message.  If it is not, return '1'.

     Use 'message_is_multipart' to check whether the message is a
     multipart one.

 -- Built-in Function: number message_get_part (number nmsg, number N)
     Extract Nth part from the multipart message NMSG.  Numeration of
     parts begins from '1'.  Return message descriptor referring to the
     extracted part.  Message parts are regarded as messages, so any
     message functions can be applied to them.

 -- Built-in Function: string message_content_type (number NMSG)
     Returns content type for the message NMSG.  The returned string is
     composed of content type and subtype, delimited by slash.

     If NMSG is not a multipart message, the function returns
     'text/plain'.

   Several functions are provided for decoding multi-part messages.
Such decoding is governed by 'Content-Transfer-Encoding' and
'Content-Type' headers of the message.  The 'Content-Transfer-Encoding'
header defines the method used to encode the message.  The value of
'Content-Type' header is used to determine the character set the body is
written in.

   Basic MIME decoding facilities are provided by the built-in function
'message_body_to_stream', described in the previous subsection.  To
instruct it to decode the content, pass it the FILTER_CHAIN argument
beginning with the word 'mimedecode'.  The usual sequence is:

     set fd open("> outfile")
     message_body_to_stream(fd, msg, "mimedecode")

   To ensure that the produced stream is represented in a specific
character set, use the 'charset' special filter.  Its argument is the
name of the character set to recode the text to:

     set fd open("> outfile")
     message_body_to_stream(fd, msg, "mimedecode|charset(utf-8)")

   The 'charset' filter takes also an optional second argument - a
"fallback" method, specifying what to do when an octet sequence is
encountered that cannot be represented in the requested character set.
Possible values for this argument are:

'none'
     Stop further conversion and signal the 'e_ilseq' exception.

'copy-pass'
     Copy the offending character to the output verbatim.

'copy-octal'
     Represent the offending character as a C octal sequence ('\NNN',
     where N is an octal digit).  This is the default.

   To decode a particular part of the message, first extract it using
the 'message_get_part' function.  Recall that message parts are messages
as well, and as such can be passed to 'message_body_to_stream'.  For
example, the following code fragment extracts all top-level parts of a
multi-part message to files named 'part.N':

     if message_is_multipart(msg)
       set n message_count_parts(msg)
       loop for set i 1, while i <= n, set i i + 1
       do
         set fd open("> part.%i")
         message_body_to_stream(fd, message_get_part(msg, i), "mimedecode")
         close(fd)
       done
     fi

   The 'mime.mfl' module provides additional functions for decoding
multi-part messages:

 -- Library Function: number message_body_decode (number NMSG; string
          CHARSET, string FALLBACK)
     Decodes the body of the message (or message part) NMSG, optionally
     converting it to the given CHARSET.  The FALLBACK argument
     specifies what to do if a byte sequence cannot be converted to the
     specified character set.  *Note iconv fallback::, for a detailed
     discussion.

     The function returns a descriptor of the I/O stream that contains
     the decoded material.  *Note I/O functions:: for a discussion of
     functions available for reading from it.

 -- Library Function: number message_part_decode(number NMSG, number
          PART; string CHARSET, string FALLBACK)
     Decodes the body of the given part of a MIME message NMSG.  The
     argument PART is a 1-based index of the part in the message.
     Optional arguments CHARSET and FALLBACK have the same meaning as in
     'message_body_decode' (see above).

     Returns a descriptor of the I/O stream that contains the decoded
     material.

     This function is equivalent to:

          message_body_decode(message_get_part(NMSG, PART, CHARSET,
                              FALLBACK))


File: mailfromd.info,  Node: Message digest functions,  Prev: MIME functions,  Up: Message functions

5.19.4 Message digest functions
-------------------------------

"Message digests" are specially formatted messages that contain certain
number of mail messages, encapsulated using the method described in RFC
934.  Such digests are often used in mailing lists to reduce the
frequency of sending mails.  Messages of this format are also produced
by the "forward" function in most MUA's.

   The usual way to handle a message digest in MFL is to convert it
first to a MIME message, and then to use functions for accessing its
parts (*note MIME functions::).

 -- Built-in Function: number message_burst (number NMSG ; number FLAGS)
     Converts the message identified by the descriptor NMSG to a
     multi-part message.  Returns a descriptor of the created message.

     Optional argument FLAGS controls the behavior of the bursting
     agent.  It is a bitwise OR of error action and bursting flags.

     "Error action" defines what to do if a part of the digest is not in
     RFC822 message format.  If it is 'BURST_ERR_FAIL' (the default),
     the function will raise the 'e_format' exception.  If ONERR is
     'BURST_ERR_IGNORE', the improperly formatted part will be ignored.
     Finally, the value 'BURST_ERR_BODY' instructs 'message_burst' to
     create a replacement part with empty headers and the text of the
     offending part as its body.

     "Bursting flags" control various aspects of the agent behavior.
     Currently only one flag is defined, 'BURST_DECODE', which instructs
     the agent to decode any MIME parts (according to the
     'Content-Transfer-Encoding' header) it encounters while bursting
     the message.

   Parts of a message digest are separated by so-called "encapsulation
boundaries", which are in essence lines beginning with at least one dash
followed by a non-whitespace character.  A dash followed by a whitespace
serves as a "byte-stuffing" character, a sort of escape for lines which
begin with a dash themselves.  Unfortunately, there are mail agents
which do not follow byte-stuffing rules and pass lines beginning with
dashes unmodified into resulting digests.  To help handle such cases a
global variable is provided which controls how much dashes should the
line begin with for it to be recognized as an encapsulation boundary.

 -- Built-in variable: number burst_eb_min_length
     Minimal number of consecutive dashes an encapsulation boundary must
     begin with.

     The default is 2.

   The following example shows a function which saves all parts of a
digest message to separate disk files.  The argument ORIG is a message
descriptor.  The resulting files are named by concatenating the string
supplied by the STEM argument and the ordinal number (1-based) of the
message part.

     func burst_digest(number orig, string stem)
     do
       number msg message_burst(orig)
       number nparts message_count_parts(msg)

       loop for number i 1,
            while i <= nparts,
            set i i + 1
       do
         number part message_get_part(msg, i)
         number out open(sprintf('>%s%02d', stem, i))
         message_to_stream(out, part)
       done
       message_close(msg)
     done


File: mailfromd.info,  Node: Quarantine functions,  Next: SMTP Callout functions,  Prev: Message functions,  Up: Library

5.20 Quarantine Functions
=========================

 -- Built-in Function: void quarantine (string TEXT)
     Place the message to the quarantine queue, using TEXT as
     explanatory reason.


File: mailfromd.info,  Node: SMTP Callout functions,  Next: Compatibility Callout functions,  Prev: Quarantine functions,  Up: Library

5.21 SMTP Callout Functions
===========================

 -- Library Function: number callout_open (string URL)
     Opens connection to the callout server listening at URL.  Returns
     the descriptor of the connection.

 -- Library Function: void callout_close (number FD)
     Closes the connection.  FD is the file descriptor returned by the
     previous call to 'callout_open'.

 -- Library Function: number callout_do (number FD, string EMAIL [,
          string REST])
     Instructs the callout server identified by FD (a file descriptor
     returned by a previous call to 'callout_open') to verify the
     validity of the EMAIL.  Optional REST argument supplies additional
     parameters for the server.  It is formatted as a sequence of
     parameter settings delimited by whitespaces.  Each setting is a
     parameter name and value separated by a '=' sign.  *Note callout
     parameters::, for a discussion of available callout parameters.

     Possible return values:

     '0'
          Success.  The EMAIL is found to be valid.
     'e_not_found'
          EMAIL does not exist.
     'e_temp_failure'
          The email validity cannot be determined right now, e.g.
          because remote SMTP server returned temporary failure.  The
          caller should retry verification later.
     'e_failure'
          Some error occurred.

     The function will throw the 'e_callout_proto' exception if the
     remote host doesn't speak the correct callout protocol.

     Upon return, 'callout_do' modifies the following variables:

     'last_poll_host'
          Host name or IP address of the last polled SMTP server.

     'last_poll_greeting'
          Initial SMTP reply from the last polled host.

     'last_poll_helo'
          The reply to the 'HELO' ('EHLO') command, received from the
          last polled host.

     'last_poll_sent'
          Last SMTP command sent to the polled host.  If nothing was
          sent, 'last_poll_sent' contains the string 'nothing'.

     'last_poll_recv'
          Last SMTP reply received from the remote host.  In case of
          multi-line replies, only the first line is stored.  If nothing
          was received the variable contains the string 'nothing'.

   The "default callout server" is defined by the 'callout-url'
statement in the configuration file, or by the 'callout' statement in
the 'server milter' section (*note configuring default callout server::.
The following functions operate on that server.

 -- Built-in Function: string default_callout_server_url ()
     Returns URL of the default callout server.

 -- Library Function: number callout (string EMAIL)
     Verifies the validity of the EMAIL using the default callout
     server.


File: mailfromd.info,  Node: Compatibility Callout functions,  Next: Internet address manipulation functions,  Prev: SMTP Callout functions,  Up: Library

5.22 Compatibility Callout Functions
====================================

The following functions are wrappers over the callout functions
described in the previous section.  They are provided for backward
compatibility.

   These functions are defined in the module 'poll.mfl', which you must
require prior to using any of them.

 -- Library Function: boolean _pollhost (string IP, string EMAIL, string
          DOMAIN, string MAILFROM)
     Poll SMTP host IP for email address EMAIL, using DOMAIN as 'EHLO'
     domain and MAILFROM as 'MAIL FROM'.  Returns 0 or 1 depending on
     the result of the test.  In contrast to the 'strictpoll' function,
     this function does not use cache database and does not fall back to
     polling MX servers if the main poll tempfails.  The function can
     throw one of the following exceptions: 'e_failure',
     'e_temp_failure'.

 -- Library Function: boolean _pollmx (string IP, string EMAIL, string
          DOMAIN, string MAILFROM)
     Poll MXs of the DOMAIN for email address EMAIL, using DOMAIN as
     'EHLO' domain and MAILFROM as 'MAIL FROM' address.  Returns 0 or 1
     depending on the result of the test.  In contrast to the 'stdpoll'
     function, '_pollmx' does not use cache database and does not fall
     back to polling the IP if the poll fails.  The function can throw
     one of the following exceptions: 'e_failure', 'e_temp_failure'.

 -- Library Function: boolean stdpoll (string EMAIL, string DOMAIN,
          string MAILFROM)

     Performs standard poll for EMAIL, using DOMAIN as 'EHLO' domain and
     MAILFROM as 'MAIL FROM' address.  Returns 0 or 1 depending on the
     result of the test.  Can raise one of the following exceptions:
     'e_failure', 'e_temp_failure'.

     In 'on' statement context, it is synonymous to 'poll' without
     explicit HOST.

 -- Library Function: boolean strictpoll (string HOST, string EMAIL,
          string DOMAIN, string MAILFROM)

     Performs strict poll for EMAIL on host HOST.  See the description
     of 'stdpoll' for the detailed information.

     In 'on' context, it is synonymous to 'poll host HOST'.

   The MAILFROM argument can be a comma-separated list of email
addresses, which can be useful for servers that are unusually picky
about sender addresses.  It is advised, however, that this list always
contain the '<>' address.  For example:

     _pollhost($client_addr, $f, "domain", "postmaster@my.net,<>")

   See also *note mail-from-address: conf-callout.

   Before returning, all described functions set the following built-in
variables:


Variable               Contains
--------------------------------------------------------------------------
last_poll_host         Host name or IP address of the last polled host.
last_poll_sent         Last SMTP command, sent to this host.  If
                       nothing was sent, it contains literal string
                       'nothing'.
last_poll_recv         Last SMTP reply received from this host.  In
                       case of multi-line replies, only the first line
                       is stored.  If nothing was received the variable
                       contains the string 'nothing'.
cache_used             '1' if cached data were used instead of polling,
                       '0' otherwise.  This variable is set by
                       'stdpoll' and 'strictpoll'.  If it equals '1',
                       none of the above variables are modified.
                       *Note cache_used example::, for an example.

Table 5.1: Variables set by polling functions


File: mailfromd.info,  Node: Internet address manipulation functions,  Next: DNS functions,  Prev: Compatibility Callout functions,  Up: Library

5.23 Internet address manipulation functions
============================================

Following functions operate on IPv4 addresses in numeric form.

 -- Built-in Function: number ntohl (number N)
     Converts the number N, from host to network byte order.  The
     argument N is treated as an unsigned 32-bit number.

 -- Built-in Function: number htonl (number N)
     Converts the number N, from network to host byte order.  The
     argument N is treated as an unsigned 32-bit number.

 -- Built-in Function: number ntohs (number N)
     The argument N is treated as an unsigned 16-bit number.  The
     function converts this number from network to host order.

 -- Built-in Function: number htons (number N)
     The argument N is treated as an unsigned 16-bit number.  The
     function converts this number from host to network order.

 -- Built-in Function: number inet_aton (string S)
     Converts the Internet host address S from the standard
     numbers-and-dots notation into the equivalent integer in host byte
     order.

          inet_aton("127.0.0.1") => 2130706433

     _The numeric data type in MFL is signed, therefore on machines with
     32 bit integers, this conversion can result in a negative number:_

          inet_aton("255.255.255.255") => -1

     _However, this does not affect arithmetical operations on IP
     addresses._

 -- Built-in Function: string inet_ntoa (number N)
     Converts the Internet host address N, given in host byte order to
     string in standard numbers-and-dots notation:

          inet_ntoa(2130706433) => "127.0.0.1"

 -- Built-in Function: number len_to_netmask (number N)
     Convert number of masked bits N to IPv4 netmask:

          inet_ntoa(len_to_netmask(24)) => 255.255.255.0
          inet_ntoa(len_to_netmask(7)) => 254.0.0.0

     If N is greater than 32 the function raises 'e_range' exception.

 -- Built-in Function: number netmask_to_len (number MASK)
     Convert IPv4 netmask MASK into netmask length (number of bits
     preserved by the mask):

          netmask_to_len(inet_aton("255.255.255.0")) => 24
          netmask_to_len(inet_aton("254.0.0.0")) => 7

   The following functions operate on string representation of IPv4 and
IPv6 addresses.

 -- Built-in Function: string reverse_ipstr(string ip)
     Converts the IP address ip to reverse format suitable for use in
     DNS labels.  That is, if IP is an IPv4 address, the return value is
     obtained by reversing the order of octets in the input:

          reverse_ipstr("192.0.2.10") => "10.2.0.192"

     If IP is an IPv6 address, the return string is formed as a sequence
     of nibbles separated by dots, encoded in reverse order, i.e.  the
     low-order nibble is encoded first, followed by the next low-order
     nibble and so on.  Each nibble is represented by a hexadecimal
     digit appending each byte from the IP represented in hex from the
     last to first, delimited by dots, e.g.:

          reverse_ipstr("2001:db8:0:0:1::2")
          =>
          "2.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2"

 -- Built-in Function: number is_ipstr(string S)
     Returns 1 if S is a string representation of an IP address (IPv4 or
     IPv6) and 0 otherwise.

 -- Built-in Function: number is_ip4str(string S)
     Returns 1 if S is a string representation of an IPv4 address, and 0
     otherwise.

 -- Built-in Function: number is_ip6str(string S)
     Returns 1 if S is a string representation of an IPv6 address, and 0
     otherwise.

   The following functions operate on IP addresses or CIDRs represented
as strings:

 -- Built-in Function: boolean match_cidr (string IP, string CIDR)
     It returns 'true' if the IP address IP pertains to the IP range
     CIDR.  The first argument, IP, is a string representation of an IP
     address (IPv4 or IPv6).  The second argument, CIDR, is a string
     representation of a IP range in CIDR notation, i.e.  'ADDR/N',
     where ADDR is IP address and N specifies address "prefix length" -
     the number of meaningful initial bits, counting from the left side
     of the address.

     The following example will reject the mail if the IP address of the
     sending machine does not belong to the block '192.0.2.0/24':

          if not match_cidr(${client_addr}, "192.0.2.0/24")
            reject
          fi

     The following example does the same for a IPv6 CIDR
     '2001:DB8::/56':

          if not match_cidr(${client_addr}, "2001:DB8::/56")
            reject
          fi

     Notice, that in previous versions of 'mailfromd' this function was
     implemented as MFL function and required the use of
     'match_cidr.mfl' module (*note 8170-9000::.  This is no longer the
     case.  The module itself is retained in the distribution for
     compatibility reasons.  If your code uses it, you will see the
     following warning during compilation phase (split in two lines for
     typesetting reasons):

          mailfromd: match_cidr.mfl:18.1-78: warning: This module is
          deprecated. The match_cidr function is now built-in.

     Just remove the 'require 'match_cidr'' from your code to make this
     warning disappear.


File: mailfromd.info,  Node: DNS functions,  Next: Geolocation functions,  Prev: Internet address manipulation functions,  Up: Library

5.24 DNS Functions
==================

MFL offers two sets of functions for querying the Domain Name System.
The 'dns_query' function and associated 'dns_reply_' functions provide a
generalized DNS API.

   Other functions provide a simplified API.

* Menu:

* dns_query::
* Simplified DNS functions::


File: mailfromd.info,  Node: dns_query,  Next: Simplified DNS functions,  Up: DNS functions

5.24.1 dns_query
----------------

 -- Built-in Function: number dns_query (number TYPE, string DOMAIN;
          number SORT, number RESOLVE)
     This function looks up the domain name NAME.  The TYPE argument
     specifies type of the query to perform.  On success, the function
     returns "DNS reply descriptor", a non-negative integer number
     identifying the reply.  It can then be passed to any of the
     'dns_reply_' functions discussed below in order to retrieve the
     information from it.

     If no matching records were found, the function returns '-1'.

     On error, it throws a corresponding exception.

     The TYPE argument is one of the following constants (defined in the
     module 'dns'):

     'DNS_TYPE_A'
          Query the 'A' record.  The DOMAIN should be the hostname to
          look up.

     'DNS_TYPE_NS'
          Query the 'NS' records.

     'DNS_TYPE_PTR'
          Query the 'PTR' record.  The DOMAIN address should be the IP
          address in dotted-quad form.

     'DNS_TYPE_MX'
          Query the 'MX' records.

     'DNS_TYPE_TXT'
          Query the 'TXT' records.

     If the query returns multiple RR sets, the optional argument SORT
     controls whether they should be returned in the same order as
     obtained from the DNS (0, the default), or should be sorted (1).

     Optional argument RESOLVE is consulted if TYPE is 'DNS_TYPE_MX' or
     'DNS_TYPE_NS'.  By default, queries for these types return
     hostnames.  The RESOLVE argument controls whether to return IP
     addresses instead.  Its possible values (defined in module
     'status.mfl' are:

     'RESOLVE_NONE'
          Don't resolve hostnames to IP addresses.  This is the default.

     'RESOLVE_DFL'
          Resolve hostnames to IP addresses according to the address
          family of the SMTP session.  That is, use 'A' records if the
          client connected using the INET family (i.e.  connected to the
          IPv4 address), and use 'AAAA' records if the client connected
          to the IPv6 address.

     'RESOLVE_IP4'
          Resolve hostnames to IPv4 addresses ('A' records).

     'RESOLVE_IP6'
          Resolve hostnames to IPv6 addresses ('AAAA' records).

     To extract actual data from the 'dns_query' return value, use the
     functions 'dns_reply_count' and 'dns_reply_string'.  The usual
     processing sequence is:

            require dns

            # Send the query and save the reply descriptor
            set n dns_query(DNS_TYPE_NS, domain_name)

            if n >= 0
              # If non-empty set is returned, iterate over each value in it:
              loop for set i 0,
                   while i < dns_reply_count(n),
                   set i i + 1
              do
                # Get the actual data:
                echo dns_reply_string(n, i)
              done
              # Release the memory associated with the reply.
              dns_reply_release(n)
            fi

 -- Built-in Function: void dns_reply_release (number RD)
     Release the memory associated with the reply RD.  If RD is -1, the
     function does nothing.

 -- Built-in Function: number dns_reply_count (number RD)
     Return the number of records in the reply RD.  For convenience, if
     RD is -1, the function returns 0.  If RD is negative (excepting
     -1), a 'e_failure' exception is thrown.

 -- Built-in Function: string dns_reply_string (number RD, number N)
     Returns Nth record from the DNS reply RD.

 -- Built-in Function: number dns_reply_ip (number RD, number N)
     Returns Nth record from the DNS reply RD, if the reply contains
     IPv4 addresses.


File: mailfromd.info,  Node: Simplified DNS functions,  Prev: dns_query,  Up: DNS functions

5.24.2 Simplified DNS functions
-------------------------------

These functions are implemented in two layers: "primitive" built-in
functions which raise exceptions if the lookup fails, and library calls
that are warranted to always return meaningful value without throwing
exceptions.

   The built-in layer is always available.  The library calls become
available after requesting the 'dns' module (*note Modules::):

     require dns

 -- Library Function: string dns_getaddr (string DOMAIN)
     Returns a whitespace-separated list of IP addresses ('A' records)
     for DOMAIN.

 -- Library Function: string dns_getname (string IPSTR)
     Returns a whitespace-separated list of domain names ('PTR' records)
     for the IPv4 address IPSTR.

 -- Library Function: string getmx (string DOMAIN [, boolean IP])
     Returns a whitespace-separated list of 'MX' names (if IP is not
     given or if it is '0') or 'MX' IP addresses (if 'IP!=0')) for
     DOMAIN.  Within the returned string, items are sorted in order of
     increasing 'MX' priority.  If DOMAIN has no 'MX' records, an empty
     string is returned.  If the DNS query fails, 'getmx' raises an
     appropriate exception.

     Examples:

          getmx("mafra.cz") => "smtp1.mafra.cz smtp2.mafra.cz relay.iol.cz"
          getmx("idnes.cz") => "smtp1.mafra.cz smtp2.mafra.cz relay.iol.cz"
          getmx("gnu.org")  => "mx10.gnu.org mx20.gnu.org"
          getmx("org.pl") => ""

     _Notes_:

       1. Number of items returned by 'getmx(DOMAIN)' can differ from
          that obtained from 'getmx(DOMAIN, 1)', e.g.:

               getmx("aol.com")
                 => mailin-01.mx.aol.com mailin-02.mx.aol.com
                           mailin-03.mx.aol.com mailin-04.mx.aol.com
               getmx("aol.com", 1)
                 => 64.12.137.89 64.12.137.168 64.12.137.184
                           64.12.137.249 64.12.138.57 64.12.138.88
                           64.12.138.120 64.12.138.185 205.188.155.89
                           205.188.156.185 205.188.156.249 205.188.157.25
                           205.188.157.217 205.188.158.121 205.188.159.57
                           205.188.159.217

       2. This function is a wrapper over 'dns_query'.

          If you intend to iterate over returned values, better use
          'dns_query' directly, e.g.  instead of doing

               string_list_iterate(getmx(domain), ` ', MX, `do_something(MX)')

          use

                 set n dns_query(DNS_TYPE_MX, domain)
                 if n >= 0
                   loop for set i 0,
                        while i < dns_reply_count(n),
                        set i i + 1
                   do
                     do_something(dns_reply_string(n, i))
                   done
                   dns_reply_release(n)
                 fi

          *Note dns_query::, for details about the 'dns_query' function
          and associated 'dns_reply_*' calls.

       3. This interface is semi-deprecated.

          It will most probably be removed in future releases, when
          array data types are implemented.

 -- Built-in Function: boolean primitive_hasmx (string DOMAIN)
     Returns 'true' if the domain name given by its argument has any
     'MX' records.

     If the DNS query fails, this function throws 'failure' or
     'temp_failure'.

 -- Library Function: boolean hasmx (string DOMAIN)
     Returns 'true' if the domain name given by its argument has any
     'MX' records.

     Otherwise, if DOMAIN has no 'MX's or if the DNS query fails,
     'hasmx' returns 'false'.

 -- Built-in Function: string primitive_hostname (string IP)
     The IP argument should be a string representing an IP address in
     "dotted-quad" notation.  The function returns the canonical name of
     the host with this IP address obtained from DNS lookup.  For
     example

          primitive_hostname (${client_addr})

     returns the fully qualified domain name of the host represented by
     Sendmail variable 'client_addr'.

     If there is no 'PTR' record for IP, 'primitive_hostname' raises the
     exception 'e_not_found'.

     If DNS query fails, the function raises 'failure' or
     'temp_failure', depending on the character of the failure.

 -- Library Function: string hostname (string IP)
     The IP argument should be a string representing an IP address in
     "dotted-quad" notation.  The function returns the canonical name of
     the host with this IP address obtained from DNS lookup.

     If there is no 'PTR' record for IP, or if the lookup fails, the
     function returns IP unchanged.

     The previous 'mailfromd' versions used the following paradigm to
     check if an IP address resolves:

            if hostname(ip) != ip
              ...

 -- Built-in Function: boolean primitive_ismx (string DOMAIN, string
          HOST)
     The DOMAIN argument is any valid domain name, the HOST is a host
     name or IP address.

     The function returns 'true' if HOST is one of the 'MX' records for
     the DOMAIN.

     If DOMAIN has no 'MX' records, 'primitive_ismx' raises exception
     'e_not_found'.

     If DNS query fails, the function raises 'failure' or
     'temp_failure', depending on the character of the failure.

 -- Library Function: boolean ismx (string DOMAIN, string HOST)
     The DOMAIN argument is any valid domain name, the HOST is a host
     name or IP address.

     The function returns 'true' if HOST is one of the 'MX' records for
     the DOMAIN.  Otherwise it returns 'false'.

     If DOMAIN has no 'MX' records, or if the DNS query fails, the
     function returns 'false'.

 -- Built-in Function: string primitive_resolve (string HOST, [string
          DOMAIN, number FAMILY])
     Reverse of 'primitive_hostname'.  The 'primitive_resolve' function
     returns the IP address for the host name specified by its HOST
     argument.  If the SMTP session uses IPv4 protocol, 'A' record is
     queried.  It it uses IPv6, 'AAAA' record is queried.  A particular
     record type can be requested via optional FAMILY, which can have
     one of the following values (defined in 'status.mfl'):

     'RESOLVE_DFL'
          Look for 'A' or 'AAAA', depending on the connection type.
          This is the default.

     'RESOLVE_IP4'
          Resolve to IPv4 addresses ('A' records).

     'RESOLVE_IP6'
          Resolve to IPv6 addresses ('AAAA' records).

     If HOST has no records of the requested type, the function raises
     the exception 'e_not_found'.

     If DNS lookup fails, the function raises 'failure' or
     'temp_failure', depending on the character of the failure.

     Optional DOMAIN argument is deprecated.  If a non-empty string is
     given as DOMAIN, the function works as follows:

       1. If DOMAIN is 'in-addr.arpa' (case-insensitive)

          The HOST must be a string representation of an IPv4 address in
          dotted-quad form.  If it is not, a 'e_inval' exception is
          thrown.  The octets in the IPv4 are reversed, a dot and
          'domain' are appended to it, and a 'PTR' record is queried for
          the resulting name.

          Thus, the call

               primitive_resolve("192.0.2.1", "in-addr.arpa")

          is equivalent to

               primitive_hostname("192.0.2.1")

       2. DOMAIN is 'ip6.arpa' (case-insensitive)

          The HOST must be a string representation of an IPv6.  If it is
          not, a 'e_inval' exception is thrown.  The octets of this IPv6
          address are reversed, a dot and 'domain' are appended to it,
          and a 'PTR' record is queried for the resulting name.

          Thus, the call

               primitive_resolve("2001:DB8::1", "ip6.arpa")

          is equivalent to

               primitive_hostname("2001:DB8::1")

       3. HOST is a string representation of an IPv4 address

          The address is reversed as in (1), then a dot and 'domain' are
          appended to it.  Finally, the DNS is queried for an 'A' record
          of the resulting name.

          Thus,

               primitive_resolve("192.0.2.1", "rev.example.com")

          is equivalent to

               primitive_resolve("1.2.0.192.rev.example.com")

       4. HOST is a string representation of an IPv6 address

          The address is reversed as in (2), then a dot and 'domain' are
          appended to it.  Finally, the DNS is queried for an 'AAAA'
          record of the resulting name.

       5. None of the above.  Same as
          'primitive_hostname('HOST.DOMAIN','',RESOLVE)'.

 -- Library Function: string resolve (string HOST, [string DOMAIN,
          number FAMILY])
     Reverse of 'hostname'.  The 'resolve' function returns IP address
     for the host name specified by HOST argument.  If the host name
     cannot be resolved, or a DNS failure occurs, the function returns
     '"0"'.

     This function is entirely equivalent to 'primitive_resolve' (see
     above), except that it never raises exceptions.

 -- Built-in Function: string ptr_validate (string IP)
     Tests whether the DNS reverse-mapping for IP exists and correctly
     points to a domain name within a particular domain.

     First, it obtains all 'PTR' records for IP.  Then, for each record
     returned, a look up for 'A' (or 'AAAA', if IP is an IPv6 address)
     records is performed and IP addresses of each record are compared
     against IP.  The function returns true if a matching 'A' (or
     'AAAA') record is found.

     This function can raise the following exceptions:

     'e_not_found'
          Unable to resolve IP address or hostname.
     'e_failure'
          Fatal error while resolving.
     'e_temp_failure'
          Temporary error in DNS resolution.
     'e_too_many'
          Too many CNAME records (*note CNAME chains::).

 -- Built-in Function: boolean primitive_hasns (string DOMAIN)
     Returns 'True' if the domain DOMAIN has at least one 'NS' record.
     Throws exception if DNS lookup fails.

 -- Library Function: boolean hasns (string DOMAIN)
     Returns 'True' if the domain DOMAIN has at least one 'NS' record.
     Returns 'False' if there are no 'NS' records or if the DNS lookup
     fails.

 -- Library Function: string getns (string DOMAIN ; boolean RESOLVE,
          boolean SORT)
     Returns a whitespace-separated list of all the 'NS' records for the
     domain DOMAIN.  Optional parameters RESOLVE and SORT control the
     formatting.  If RESOLVE is 0 (the default), the resulting string
     will contain IP addresses of the NS servers.  If RESOLVE is not 0,
     hostnames will be returned instead.  If SORT is 1, the returned
     items will be sorted.

     If the DNS query fails, 'getns' raises an appropriate exception.

     _Notes_:
       1. This function is a wrapper over 'dns_query'.

          If you intend to iterate over returned values, better use
          'dns_query' directly, e.g.  instead of doing

               string_list_iterate(getns(domain), ` ', NS, `do_something(NS)')

          use

                 set n dns_query(DNS_TYPE_NS, domain)
                 if n >= 0
                   loop for set i 0,
                        while i < dns_reply_count(n),
                        set i i + 1
                   do
                     do_something(dns_reply_string(n, i))
                   done
                   dns_reply_release(n)
                 fi

          *Note dns_query::, for details about the 'dns_query' function
          and associated 'dns_reply_*' calls.

       2. This interface is semi-deprecated.

          It will most probably be removed in future releases, when
          array data types are implemented.


File: mailfromd.info,  Node: Geolocation functions,  Next: Database functions,  Prev: DNS functions,  Up: Library

5.25 Geolocation functions
==========================

The "geolocation functions" allow you to identify the country where the
given IP address or host name is located.  These functions are available
only if the 'libmaxminddb' library is installed and 'mailfromd' is
compiled with the 'GeoIP2' support.

   The 'libmaxminddb' library is distributed by 'MaxMind' under the
terms of the 'Apache License' Version 2.0.  It is available from
<https://dev.maxmind.com/geoip/geoip2/downloadable/#MaxMind_APIs>.

 -- Built-in Function: void geoip2_open (string FILENAME)
     Opens the geolocation database file FILENAME.  The database must be
     in GeoIP2 format.

     If the database cannot be opened, 'geoip2_open' throws the
     'e_failure' exception.

     If this function is not called, geolocation functions described
     below will try to open the database file
     '/usr/share/GeoIP/GeoLite2-City.mmdb'.

 -- Built-in Function: string geoip2_dbname (void)
     Returns the name of the geolocation database currently in use.

   The geolocation database for each IP address, which serves as a look
up key, stores a set of items describing this IP. This set is organized
as a map of key-value pairs.  Each key is a string value.  A value can
be a scalar, another map or array of values.  Using JSON notation, the
result of a look up in the database might look as:

     {
         "country":{
             "geoname_id":2921044,
             "iso_code":"DE",
             "names":{
                 "en": "Germany",
                 "de": "Deutschland",
                 "fr":"Allemagne"
             },
         },
         "continent":{
             "code":"EU",
             "geoname_id":6255148,
             "names":{
                 "en":"Europe",
                 "de":"Europa",
                 "fr":"Europe"
             }
         },
         "location":{
             "accuracy_radius":200,
             "latitude":49.4478,
             "longitude":11.0683,
             "time_zone":"Europe/Berlin"
         },
         "city":{
             "geoname_id":2861650,
             "names":{
                 "en":"Nuremberg",
                 "de":"Nürnberg",
                 "fr":"Nuremberg"
             }
         },
         "subdivisions":[{
             "geoname_id":2951839,
             "iso_code":"BY",
             "names":{
                 "en":"Bavaria",
                 "de":"Bayern",
                 "fr":"Bavière"
             }
         }
     }

   Each particular data item in such structure is identified by its
"search path", which is a dot-delimited list of key names leading to
that value.  For example, using the above map, the name of the city in
English can be retrieved using the key 'city.names.en'.

 -- Built-in Function: string geoip2_get (string IP, string PATH)
     Looks up the IP address IP in the geolocation database.  If found,
     returns data item identified by the search path PATH.

     The function can throw the following exceptions:

     e_not_found
          The IP was not found in the database.

     e_range
          The PATH does not exist the returned map.

     e_failure
          General error occurred.  E.g.  the database cannot be opened,
          IP is not a valid IP address, etc.

 -- Built-in Function: string geoip2_get_json (string IP [; number
          INDENT)
     Looks up the IP in the database and returns entire data set
     associated with it, formatted as a JSON object.  If the optional
     parameter INDENT is supplied and is greater than zero, it gives the
     indentation for each nesting level in the JSON object.

   Applications may test whether the GeoIP2 support is present and
enable the corresponding code blocks conditionally, by testing if the
'WITH_GEOIP2' m4 macro is defined.  For example, the following code adds
to the message the 'X-Originator-Country' header, containing the 2
letter code of the country where the client machine is located.  If
'mailfromd' is compiled without the 'GeoIP2' support, it does nothing:

     m4_ifdef(`WITH_GEOIP2',`
       try
       do
         header_add("X-Originator-Country", geoip2_get($client_addr,
                                                       'country.iso_code'))
       done
       catch e_not_found or e_range
       do
         pass
       done
     ')


File: mailfromd.info,  Node: Database functions,  Next: Control database,  Prev: Geolocation functions,  Up: Library

5.26 Database Functions
=======================

The functions described below provide a user interface to DBM databases.

   Each DBM database is a separate disk file that keeps "key/value
pairs".  The interface allows to retrieve the value corresponding to a
given key.  Both 'key' and 'value' are null-terminated character
strings.  To lookup a key, it is important to know whether its length
includes the terminating null byte.  By default, it is assumed that it
does not.

   Another important database property is the "file mode" of the
database file.  The default file mode is '640' (i.e. 'rw-r----', in
symbolic notation).

   These and other properties can be configured using the 'dbprop'
pragma:

     #pragma dbprop PATTERN PROP [PROP]

   The PATTERN is the database name or shell-style globbing pattern.
Properties defined by that pragma apply to each database whose name
matches this pattern.  If several 'dbprop' pragmas match the database
name, the one that matches exactly is preferred.

   The rest of arguments define properties for that database.  The valid
values for PROP are:

  1. The word 'null', meaning that the terminating null byte is included
     in the key length.

     Setting 'null' property is necessary, for databases created with
     'makemap -N hash' command.

  2. File mode for the disk file.  It can be either an octal number, or
     a symbolic mode specification in ls-like format.  E.g., the
     following two formats are equivalent:

          640
          rw-r----

  3. "DBM scheme", which specifies the type of the DBM (*note Database
     Formats::) to use for this database.  The scheme consists of DBM
     type name followed by a colon and two slashes.  E.g.  'gdbm://'
     means a GDBM database.  *Note DBM scheme::, for a detailed
     discussion.

   For example, consider the following pragmas:

     #pragma dbprop /etc/mail/whitelist.db 640

   It tells that the database file 'whitelist.db' has privileges '640'
and do not include null in the key length.

   Similarly, the following pragma:

     #pragma dbprop `/etc/mail/*.db' null 600 bdb://

declares that all database files in directory '/etc/mail' are Berkeley
DB files, have privileges '640', and include null terminator in the key
length.  _Notice_, the use of 'm4' quoting characters in the example
below.  Without them, the sequence '/*' would have been taken as the
beginning of a comment.

   Additionally, for compatibility with previous versions (up to 5.0),
the terminating null property can be requested via an optional argument
to the database functions (in description below, marked as NULL).

 -- Built-in Function: boolean dbmap (string DB, string KEY, [boolean
          NULL])
     Looks up KEY in the DBM file DB and returns 'true' if it is found.

     See above for the meaning of NULL.

     *Note whitelisting::, for an example of using this function.

 -- Built-in Function: string dbget (string DB, string KEY [, string
          DEFAULT, boolean NULL])
     Looks up KEY in the database DB and returns the value associated
     with it.  If the key is not found returns DEFAULT, if specified, or
     empty string otherwise.

     See above for the meaning of NULL.

 -- Built-in Function: void dbput (string DB, string KEY, string VALUE
          [, boolean NULL, number MODE ])
     Inserts in the database a record with the given KEY and VALUE.  If
     a record with the given KEY already exists, its value is replaced
     with the supplied one.

     See above for the meaning of NULL.  Optional MODE allows to
     explicitly specify the file mode for this database.  See also
     '#pragma dbprop', described above.

 -- Built-in Function: void dbinsert (string DB, string KEY, string
          VALUE [, boolean REPLACE, boolean NULL, number MODE ])
     This is an improved variant of 'dbput', which provides a better
     control on the actions to take if the KEY already exists in the
     database.  Namely, if REPLACE is 'True', the old value is replaced
     with the new one.  Otherwise, the 'e_exists' exception is thrown.

 -- Built-in Function: void dbdel (string DB, string KEY [, boolean
          NULL, number MODE])
     Delete from the database the record with the given KEY.  If there
     are no such record, return without signalling error.

     If the optional NULL argument is given and is not zero, the
     terminating null character will be included in KEY length.

     Optional MODE allows to explicitly specify the file mode for this
     database.  See also '#pragma dbprop', described above.

   The functions above have also the corresponding exception-safe
interfaces, which return cleanly if the 'e_dbfailure' exception occurs.
To use these interfaces, request the 'safedb' module:

     require safedb

   The exception-safe interfaces are:

 -- Library Function: number safedbmap (string DB, string KEY [, number
          DEFAULT, boolean NULL])

     This is an exception-safe interface to 'dbmap'.  If a database
     error occurs while attempting to retrieve the record, 'safedbmap'
     returns DEFAULT or '0', if it is not defined.

 -- Library Function: string safedbget (string DB, string KEY [, string
          DEFAULT, boolean NULL])

     This is an exception-safe interface to 'dbget'.  If a database
     error occurs while attempting to retrieve the record, 'safedbget'
     returns DEFAULT or empty string, if it is not defined.

 -- Library Function: void safedbput (string DB, string KEY, string
          VALUE [, boolean NULL])

     This is an exception-safe interface to 'dbput'.  If a database
     error occurs while attempting to retrieve the record, the function
     returns without raising exception.

 -- Library Function: void safedbdel (string DB, string KEY [, boolean
          NULL])

     This is an exception-safe interface to 'dbdel'.  If a database
     error occurs while attempting to delete the record, the function
     returns without raising exception.

   The verbosity of 'safedb' interfaces in case of database error is
controlled by the value of 'safedb_verbose' variable.  If it is '0',
these functions return silently.  This is the default behavior.
Otherwise, if 'safedb_verbose' is not '0', these functions log the
detailed diagnostics about the database error and return.

   The following functions provide a sequential access to the contents
of a DBM database:

 -- Built-in Function: number dbfirst (string NAME)
     Start sequential access to the database NAME.  The return value is
     an opaque identifier, which is used by the remaining sequential
     access functions.  This number is '0' if the database is empty.

 -- Built-in Function: number dbnext (number DN)
     Select next record form the database.  The argument DN is the
     access identifier, returned by a previous call to 'dbfirst' or
     'dbnext'.

     Returns new access identifier.  This number is '0' if all records
     in the database have been visited.

   The usual approach for iterating over all records in a database
DBNAME is:

       loop for number dbn dbfirst(DBNAME)
       do
         ...
       done while dbnext(dbn)

   The following two functions can be used to access values of the
currently selected database record.  Their argument, DN, is the access
identifier, returned by a previous call to 'dbfirst' or 'dbnext'.

 -- Built-in Function: string dbkey (number DN)
     Return the key from the selected database record.

 -- Built-in Function: string dbvalue (number DN)
     Return the value from the selected database record.

 -- Built-in Function: void dbbreak (number DN)
     Stop sequential access to the database and deallocate all
     associated resources.  Use this function if you need to break from
     the sequential access loop, as in the example below:

            loop for number dbn dbfirst(DBNAME)
            do
              if some_condition
                dbbreak(dbn)
                break
              fi
            done while dbnext(dbn)

 -- Built-in Function: number db_expire_interval (string FMT)
     The FMT argument is a database format identifier (*note Database
     Formats::).  If it is valid, the function returns the expiration
     interval for that format.  Otherwise, 'db_expire_interval' raises
     the 'e_not_found' exception.

 -- Built-in Function: string db_name (string FMTID)
     The FMTID argument is a database format identifier (*note Database
     Formats::).  The function returns the file name for that format.
     If FMTID does not match any known format, 'db_name' raises the
     'e_not_found' exception.

 -- Built-in Function: number db_get_active (string FMTID)
     Returns the flag indicating whether the cache database FMTID is
     currently enabled.  If FMTID does not match any known format,
     'db_name' raises the 'e_not_found' exception.

 -- Built-in Function: void db_set_active (string FMTID, boolean ENABLE)
     Enables the cache database FMTID if ENABLE is 'True', or disables
     it otherwise.  For example, to disable DNS caching, do:

          db_set_active("dns", 0)

 -- Built-in Function: boolean relayed (string DOMAIN)
     Returns 'true' if the string DOMAIN is found in one of relayed
     domain files (*note relayed-domain-file: conf-base.).  The usual
     construct is:

          if relayed(hostname(${client_addr}))
            ...

     which yields 'true' if the IP address from 'Sendmail' variable
     'client_addr' is relayed by the local machine.


File: mailfromd.info,  Node: Control database,  Next: System functions,  Prev: Database functions,  Up: Library

5.27 Control Database
=====================

"Control database" is a DBM file whose records define actions to be
taken for mails coming from particular IP or email addresses.  Functions
and variables for using the control database are defined in module
'cdb.mfl'.

 -- cdb variable: string cdb_name
     Name of the database file to use as control database.  By default
     it is '/etc/mail/mfctl.db'.

 -- cdb variable: number cdb_greylist_interval
     Greylisting interval, for database records that prescribe
     greylisting (see below).  Defaults to 900 seconds.

 -- Library Function: void cdb_check (string PREFIX, string ADDR)
     Perform a look up in the database.  If the value is found, take the
     action it indicates.

     The key to use for the look up depends on the value of PREFIX:

     ip
          The ADDR argument must be an IP address.  The look up key is
          'ip:ADDR'.

     email
          The ADDR argument is an email address.  The key is
          'email:CEMAIL', where CEMAIL is ADDR in "canonical form",
          obtained by converting its domain part to lower case.

     domain
          The ADDR argument is an email address.  The key is formed as
          'domain:DOM', where DOM is domain part of ADDR converted to
          lower case.

     subdomain
          Initial key value is obtained as for 'domain'.  If the key is
          found, the requested action is performed.  Otherwise, the
          shortest hostname prefix (sequence of characters up to and
          including the first dot) is stripped off the domain and the
          process is retried.

          Thus, the action is determined by the longest subdomain of
          ADDR, X, for which the key 'domain:X' exists in the database.

     mx
          If ADDR is an email address, its domain part is selected,
          otherwise it is used as is.  The list of MX servers for this
          domain is obtained.  For each HOST from that list the key
          'mx:HOST' is looked up.  First entry found is used.

     The function 'cdb_check' returns if the key was not found in the
     database or if the value found was 'OK' (case-insensitive) or
     empty.  Otherwise, the looked up value determines the action (*note
     reply actions::), as described in the following table.  The action
     is returned and execution of the filter program stops.

     CONTINUE
          Continue to the next milter state.  *Note continue::.

     ACCEPT
          Accept the mail.  *Note accept::.

     REJECT
          Reject the mail.  *Note reject::.

     TEMPFAIL
          Return a temporary failure.  *Note tempfail::.

     GREYLIST
          Greylist the mail using the interval defined by the
          'cdb_greylist_interval' variable.  If called in 'envrcpt'
          handler, the action is taken immediately.  Otherwise, if
          called in 'connect', 'helo' or 'envfrom', the action is
          delayed until 'envrcpt' is invoked.  Otherwise, if called in
          any other handler an error is reported.

     CODE XCODE TEXT
     CODE TEXT
     TEXT
          Here, CODE is SMTP response code, XCODE is extended STMP
          response code, and TEXT is an arbitrary text.  If CODE is
          given, it must begin with '4' or '5'.  Its first digit defines
          the action to be taken: temporary failure (if '4') or reject
          (if '5').  If XCODE is given, its first digit must match that
          of CODE.

          If only TEXT is supplied, it is equivalent to

               reject(550, 5.1.0, TEXT)


File: mailfromd.info,  Node: System functions,  Next: Passwd functions,  Prev: Control database,  Up: Library

5.28 System functions
=====================

 -- Built-in Function: boolean access (string PATHNAME, number MODE)
     Checks whether the calling process can access the file PATHNAME.
     If PATHNAME is a symbolic link, it is dereferenced.  The function
     returns 'True' if the file can be accessed and 'False'
     otherwise(1).

     Symbolic values for MODE are provided in module 'status':

     F_OK
          Tests for the existence of the file.

     R_OK
          Tests whether the file exists and grants read permission.

     W_OK
          Tests whether the file exists and grants write permission.

     X_OK
          Tests whether the file exists and grants execute permission.

 -- Built-in Function: string getenv (string NAME)
     Searches the environment list for the variable NAME and returns its
     value.  If the variable is not defined, the function raises the
     exception 'e_not_found'.

 -- Built-in Function: string gethostname ([bool FQN])
     Return the host name of this machine.

     If the optional FQN is given and is 'true', the function will
     attempt to return fully-qualified host name, by attempting to
     resolve it using DNS.

 -- Built-in Function: string getdomainname ()
     Return the domain name of this machine.  Note, that it does not
     necessarily coincide with the actual machine name in DNS.

     Depending on the underlying 'libc' implementation, this call may
     return empty string or the string '(none)'.  Do not rely on it to
     get the real domain name of the box 'mailfromd' runs on, use
     'localdomain' (see below) instead.

 -- Library Function: string localdomain ()
     Return the local domain name of this machine.

     This function first uses 'getdomainname' to make a first guess.  If
     it does not return a meaningful value, 'localdomain' calls
     'gethostname(1)' to determine the fully qualified host name of the
     machine, and returns its domain part.

     To use this function, require the 'localdomain' module (*note
     Modules::), e.g.: 'require localdomain'.

 -- Built-in Function: number time ()
     Return the time since the Epoch (00:00:00 UTC, January 1, 1970),
     measured in seconds.

 -- Built-in Function: string strftime (string FMT, number TIMESTAMP)
 -- Built-in Function: string strftime (string FMT, number TIMESTAMP,
          boolean GMT)
     Formats the time TIMESTAMP (seconds since the Epoch) according to
     the format specification FORMAT.  Ordinary characters placed in the
     format string are copied to the output without conversion.
     Conversion specifiers are introduced by a '%' character.  *Note
     Time and Date Formats::, for a detailed description of the
     conversion specifiers.  We recommend using single quotes around FMT
     to prevent '%' specifiers from being interpreted as 'Mailfromd'
     variables (*Note Literals::, for a discussion of quoted literals
     and variable interpretation within them).

     The TIMESTAMP argument can be a return value of 'time' function
     (see above).

     For example:
          strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564)
           => 2006-11-25 19:59:24 EET
          strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564, 1)
           => 2006-11-25 17:59:24 GMT

 -- Built-in Function: string uname (string FORMAT)
     This function returns system information formatted according to the
     format specification FORMAT.  Ordinary characters placed in the
     format string are copied to the output without conversion.
     Conversion specifiers are introduced by a '%' character.  The
     following conversions are defined:

     %s
          Name of this system.

     %n
          Name of this node within the communications network to which
          this node is attached.  Note, that it does not necessarily
          coincide with the actual machine name in DNS.

     %r
          Kernel release.

     %v
          Kernel version.

     %m
          Name of the hardware type on which the system is running.

     For example:

            uname('%n runs %s, release %r on %m')
              => "Trurl runs Linux, release 2.6.26 on i686"

     Notice the use of single quotes.

 -- Built-in Function: void unlink (string NAME)
     Unlinks (deletes) the file NAME.  On error, throws the 'e_failure'
     exception.

 -- Built-in Function: number system (string STR)
     The function 'system' executes a command specified in STR by
     calling '/bin/sh -c string', and returns -1 on error or the return
     status of the command otherwise.

 -- Built-in Function: void sleep (number SECS[, USEC])
     Sleep for SECS seconds.  If optional USEC argument is given, it
     specifies additional number of microseconds to wait for.  For
     example, to suspend execution of the filter for 1.5 seconds:

            sleep(1,500000)

     This function is intended mostly for debugging and experimental
     purposes.

 -- Built-in Function: number umask (number MASK)
     Set the umask to MASK & 0777.  Return the previous value of the
     mask.

   ---------- Footnotes ----------

   (1) _Note_, that the return code is inverted in respect to the system
function 'access(2)'.


File: mailfromd.info,  Node: Passwd functions,  Next: Sieve Interface,  Prev: System functions,  Up: Library

5.29 System User Database
=========================

 -- Built-in Function: string getpwnam (string NAME)
 -- Built-in Function: string getpwuid (number UID)
     Look for the user NAME ('getpwnam') or user ID UID ('getpwuid') in
     the system password database and return the corresponding record,
     if found.  If not found, raise the 'e_not_found' exception.

     The returned record consists of six fields, separated by colon
     sign:

          uname:passwd:uid:gid:gecos:dir:shell

     Field                  Meaning
     -------------------------------------------------------------------
     uname                  user name
     passwd                 user password
     uid                    user ID
     gid                    group ID
     gecos                  real name
     dir                    home directory
     shell                  shell program

     For example:

          getpwnam("gray")
          => "gray:x:1000:1000:Sergey Poznyakoff:/home/gray:/bin/bash"

   Following two functions can be used to test for existence of a key in
the user database:

 -- Built-in Function: boolean mappwnam (string NAME)
 -- Built-in Function: boolean mappwuid (number UID)
     Return 'true' if NAME (or UID) is found in the system user
     database.


File: mailfromd.info,  Node: Sieve Interface,  Next: Interfaces to Third-Party Programs,  Prev: Passwd functions,  Up: Library

5.30 Sieve Interface
====================

'Sieve' is a powerful mail filtering language, defined in RFC 3028.
'Mailfromd' supports an extended form of this language.  For a
description of the language and available extensions, see *note Sieve
Language: (mailutils)Sieve Language.

 -- Built-in Function: boolean sieve (number MSG, string SCRIPT [,
          number FLAGS, string FILE, number LINE])
     Compile the Sieve program SCRIPT and execute it over the message
     identified by the descriptor NMSG.

     Optional FLAGS modify the behavior of the function.  It is a
     bit-mask field, consisting of a bitwise 'or' of one or more of the
     following flags, defined in 'sieve.mfl':

     'MF_SIEVE_FILE'
          The SCRIPT argument specifies the name of a Sieve program
          file.  This is the default.

     'MF_SIEVE_TEXT'
          The SCRIPT argument is a string containing entire Sieve
          program.  Optional arguments FILE and LINE can be used to fix
          source locations in Sieve diagnostic messages (see below).

     'MF_SIEVE_LOG'
          Log every executed 'Sieve' action.

     'MF_SIEVE_DEBUG_TRACE'
          Trace execution of 'Sieve' tests.

     'MF_SIEVE_DEBUG_INSTR'
          Log every instruction, executed in the compiled 'Sieve' code.
          This produces huge amounts of output and is rarely useful,
          unless you suspect some bug in 'Sieve' implementation and wish
          to trace it.

     For example, 'MF_SIEVE_LOG|MF_SIEVE_DEBUG_TRACE' enables logging
     'Sieve' actions and tests.

     The 'sieve' function returns 'true' if the message was accepted by
     the SCRIPT program, and 'false' otherwise.  Here, the word
     "accepted" means that some form of 'KEEP' action (*note keep:
     (mailutils)Actions.) was executed over the message.

     While executing the Sieve script, Sieve environment ('RFC 5183') is
     initialized as follows:

     domain
          The domain name of the server Sieve is running on.
     host
          Host name of the server Sieve is running on.
     location
          The string 'MTA'.
     name
          The string 'GNU Mailutils'.
     phase
          The string 'pre'.
     remote-host
          Defined to the value of 'client_ptr' macro, if it was
          required.
     remote-ip
          Defined to the value of 'client_addr' macro, if it was
          required.
     version
          The version of GNU Mailutils.

   The following example discards each message not accepted by the
'Sieve' program '/etc/mail/filter.siv':

     require 'sieve'

     group eom
     do
       if not sieve(current_message(), "/etc/mail/filter.siv", MF_SIEVE_LOG)
          discard
       fi
     done

   The Sieve program can be embedded in the MFL filter, as shown in the
example below:

     require 'sieve'

     prog eom
     do
       if not sieve(current_message(),
                    "require \"fileinto\";\n"
                    "fileinto \"/tmp/sieved.mbox\";",
                    MF_SIEVE_TEXT | MF_SIEVE_LOG)
          discard
       fi
     done

   In such cases, any Sieve diagnostics (error messages, traces, etc.)
will be marked with the locations relative to the line where the call to
'sieve' appears.  For example, the above program produces the following
in the log:

     prog.mfl:7: FILEINTO; delivering into /tmp/sieved.mbox

   Notice, that the line number correctly refers to the line where the
'fileinto' action appears in the source.  However, there are cases where
the reported line number is incorrect.  This happens, for instance, if
SCRIPT is a string variable defined elsewhere.  To handle such cases,
'sieve' accepts two optional parameters which are used to compute the
location in the Sieve program.  The FILE parameter specifies the file
name where the definition of the program appears, and the LINE parameter
gives the number of line in that file where the program begins.  For
example:

     require 'sieve'

     const sieve_prog_line __line__ + 2
     string sieve_prog <<EOT
     require "fileinto";
     fileinto "/tmp/sieved.mbox";
     EOT

     prog eom
     do
       if not sieve(current_message(),
                    sieve_prog, MF_SIEVE_TEXT | MF_SIEVE_LOG,
                    __file__, sieve_prog_line)
          discard
       fi
     done

   The actual Sieve program begins two lines below the 'sieve_prog_line'
constant definition, which is reflected in its initialization.


File: mailfromd.info,  Node: Interfaces to Third-Party Programs,  Next: Rate limiting functions,  Prev: Sieve Interface,  Up: Library

5.31 Interfaces to Third-Party Programs
=======================================

A set of functions is defined for interfacing with other filters via
TCP.  Currently implemented are interfaces with 'SpamAssassin' 'spamd'
daemon and with 'ClamAV' anti-virus.

   Both interfaces work much the same way: the remote filter is
connected and the message is passed to it.  If the remote filter
confirms that the message matches its requirements, the function returns
'true'.  Notice that in practice that means that such a message _should
be rejected or deferred_.

   The address of the remote filter is supplied as the second argument
in the form of a standard URL:

     PROTO://PATH[:PORT]

The PROTO part specifies the "connection protocol".  It should be 'tcp'
for the TCP connection and 'file' or 'socket' for the connection via
UNIX socket.  In the latter case the PROTO part can be omitted.  When
using TCP connection, the PATH part gives the remote host name or IP
address and the optional PORT specifies the port number or service name
to use.  For example:

     # connect to 'remote.filter.net' on port 3314:
     tcp://remote.filter.net:3314

     # the same, using symbolic service name (must be defined in
     # '/etc/services'):
     tcp://remote.filter.net:spamd

     # Connect via a local UNIX socket (equivalent forms):
     /var/run/filter.sock
     file:///var/run/filter.sock
     socket:///var/run/filter.sock

   The description of the interface functions follows.

* Menu:

* SpamAssassin::
* ClamAV::


File: mailfromd.info,  Node: SpamAssassin,  Next: ClamAV,  Up: Interfaces to Third-Party Programs

5.31.1 SpamAssassin
-------------------

 -- Built-in Function: boolean spamc (number MSG, string URL, number
          PREC, number COMMAND)
     Send the message MSGt to the SpamAssassin daemon ('spamd')
     listening on the given URL.  The COMMAND argument identifies what
     kind of processing is needed for the message.  Allowed values are:

     SA_SYMBOLS
          Process the message and return 1 or 0 depending on whether it
          is diagnosed as spam or not.  Store SpamAssassin keywords in
          the global variable 'sa_keywords' (see below).

     SA_REPORT
          Process the message and return 1 or 0 depending on whether it
          is diagnosed as spam or not.  Store entire SpamAssassin report
          in the global variable 'sa_keywords'.

     SA_LEARN_SPAM
          Learn the supplied message as spam.

     SA_LEARN_HAM
          Learn the supplied message as ham.

     SA_FORGET
          Forget any prior classification of the message.

     The second argument, PREC, gives the precision, in decimal digits,
     to be used when converting SpamAssassin diagnostic data and storing
     them into 'mailfromd' variables.

     The floating point SpamAssassin data are converted to the integer
     'mailfromd' variables using the following relation:

          VAR = int(SA-VAR * 10**PREC)

     where SA-VAR stands for the SpamAssassin value and VAR stands for
     the corresponding 'mailfromd' one.  'int()' means taking the
     integer part and '**' denotes the exponentiation operator.

     The function returns additional information via the following
     variables:

     'sa_score'
          The spam score, converted to integer as described above.  To
          convert it to a floating-point representation, use
          'sa_format_score' function (*note sa_format_score: String
          manipulation.).  See also the example below.

     'sa_threshold'
          The threshold, converted to integer form.

     'sa_keywords'
          If COMMAND is 'SA_SYMBOLS', this variable contains a string of
          comma-separated SpamAssassin keywords identifying this
          message, e.g.:

               ADVANCE_FEE_1,AWL,BAYES_99

          If COMMAND is 'SA_REPORT', the value of this variable is a
          "spam report" message.  It is a multi-line textual message,
          containing detailed description of spam scores in a tabular
          form.  It consists of the following parts:

            1. A preamble.
            2. Content preview.

               The words 'Content preview', followed by a colon and an
               excerpt of the message body.

            3. Content analysis details.

               It has the following form:

                    Content analysis details: (SCORE points, MAX required)

               where SCORE and MAX are spam score and threshold in
               floating point.

            4. Score table.

               The score table is formatted in three columns:

               pts
                    The score, as a floating point number with one
                    decimal digit.

               rule name
                    SpamAssassin rule name that contributed this score.

               description
                    Textual description of the rule

               The score table can be extracted from 'sa_keywords' using
               'sa_format_report_header' function (*note
               sa_format_report_header: String manipulation.), as
               illustrated in the example below.

          The value of this variable is undefined if COMMAND is
          'SA_LEARN_SPAM', 'SA_LEARN_HAM' or 'SA_FORGET'.

     The 'spamc' function can signal the following exceptions:
     'e_failure' if the connection fails, 'e_url' if the supplied URL is
     invalid and 'e_range' if the supplied port number is out of the
     range 1-65535.

     An example of using this function:

          prog eom
          do
            if spamc(current_message(), "tcp://192.168.10.1:3333", 3,
                     SA_SYMBOLS)
              reject 550 5.7.0
                   "Spam detected, score %sa_score with threshold %sa_threshold"
            fi
          done

     Here is a more advanced example:

          prog eom
          do
            set prec 3
            if spamc(current_message(),
                     "tcp://192.168.10.1:3333", prec, SA_REPORT)
              add "X-Spamd-Status" "SPAM"
            else
              add "X-Spamd-Status" "OK"
            fi
            add "X-Spamd-Score" sa_format_score(sa_score, prec)
            add "X-Spamd-Threshold" sa_format_score(sa_threshold, prec)
            add "X-Spamd-Keywords" sa_format_report_header(sa_keywords)
          done

 -- Library Function: boolean sa (string URL, number PREC; number
          COMMAND)
     Additional interface to the 'spamc' function, provided for backward
     compatibility.  It is equivalent to

          spamc(current_message(), URL, PREC, COMMAND)

     If COMMAND is not supplied, 'SA_SYMBOLS' is used.


File: mailfromd.info,  Node: ClamAV,  Prev: SpamAssassin,  Up: Interfaces to Third-Party Programs

5.31.2 ClamAV
-------------

 -- Built-in Function: boolean clamav (number MSG, string URL)
     Pass the message MSG to the ClamAV daemon at URL.  Return 'true' if
     it detects a virus in it.  Return virus name in 'clamav_virus_name'
     global variable.

     The message is sent to 'clamd' in chunks.  The size of each chunk
     is defined by the variable 'clamav_stream_max_length'.  Its value
     should not be greater than that of the 'StreamMaxLength' parameter
     in 'clamd.conf'.

     To stay on the safe side, 'clamav_stream_max_length' defaults to
     4096 bytes.  To improve performance, adjust it to the actual value
     of 'StreamMaxLength'.

     The 'clamav' function can signal the following exceptions:

     'e_failure'
          Unable to connect to the server, or failed to send the message
          to it.  In particular, this exception will be raised if the
          value of 'clamav_stream_max_length' is greater than the
          'StreamMaxLength' 'clamd' configuration parameter.  In this
          case, the 'clamd' server will report:

               WARNING: INSTREAM: Size limit reached, (requested: N, max: M)

          where N and M are decimal numbers.  Adjust your
          'clamav_stream_max_length' value so that

               clamav_stream_max_length < m

     'e_url'
          The supplied URL is invalid.

     An example usage:

          prog eom
          do
            if clamav(current_message(), "tcp://192.168.10.1:6300")
              reject 550 5.7.0 "Infected with %clamav_virus_name"
            fi
          done


File: mailfromd.info,  Node: Rate limiting functions,  Next: Greylisting functions,  Prev: Interfaces to Third-Party Programs,  Up: Library

5.32 Rate limiting functions
============================

 -- Built-in Function: number rate (string KEY, number SAMPLE-INTERVAL,
          [number MIN-SAMPLES, number THRESHOLD])

     Returns the mail sending rate for KEY per SAMPLE-INTERVAL.
     Optional MIN-SAMPLES, if supplied, specifies the minimal number of
     mails needed to obtain the statistics.  The default is 2.  Optional
     THRESHOLD controls rate database updates.  If the observed rate
     (per SAMPLE-INTERVAL seconds) is higher than the THRESHOLD, the hit
     counters for that key are not incremented and the database is not
     updated.  Although the THRESHOLD argument is optional(1), its use
     is strongly encouraged.  Normally, the value of THRESHOLD equals
     the value compared with the return from RATE, as in:

            if rate("$f-$client_addr", rate_interval, 4, maxrate) > maxrate
              tempfail 450 4.7.0 "Mail sending rate exceeded.  Try again later"
            fi

     This function is a low-level interface.  Instead of using it
     directly, we advise to use the 'rateok' function, described below.

 -- Library Function: boolean rateok (string KEY, number
          SAMPLE-INTERVAL, number THRESHOLD,
     [number MIN-SAMPLES])

     To use this function, require the 'rateok' module (*note
     Modules::), e.g.: 'require rateok'.

     The 'rateok' function returns 'True' if the mail sending rate for
     KEY, computed for the interval of SAMPLE-INTERVAL seconds is less
     than the THRESHOLD.  Optional MIN-SAMPLES parameter supplies the
     minimal number of mails needed to obtain the statistics.  It
     defaults to 4.

   *Note Sending Rate::, for a detailed description of the 'rateok' and
its use.  The 'interval' function (*note interval::) is often used in
the second argument to 'rateok' or 'rate'.

 -- Built-in Function: boolean tbf_rate (string KEY, number COST, number
          SAMPLE-INTERVAL, number BURST-SIZE)

     This function implements a classical token bucket filter algorithm.
     Tokens are added to the bucket identified by the KEY at constant
     rate of 1 token per SAMPLE-INTERVAL microseconds, to a maximum of
     BURST-SIZE tokens.  If no bucket is found for the specified key, a
     new bucket is created and initialized to contain BURST-SIZE tokens.
     If the bucket contains COST or more tokens, COST tokens are removed
     from it and 'tbf_rate' returns 'True'.  Otherwise, the function
     returns 'False'.

     For a detailed description of the Token Bucket Algorithm and its
     use to limit mail rates, see *note TBF::.

   ---------- Footnotes ----------

   (1) It is made optional in order to provide backward compatibility
with the releases of mailfromd prior to 5.0.93.


File: mailfromd.info,  Node: Greylisting functions,  Next: Special test functions,  Prev: Rate limiting functions,  Up: Library

5.33 Greylisting functions
==========================

 -- Built-in Function: boolean greylist (string KEY, number INTERVAL)

     Returns 'True' if the KEY is found in the greylist database
     (controlled by 'database greylist' configuration file statement,
     *note conf-database::).  The argument INTERVAL gives the
     greylisting interval in seconds.  The function stores the number of
     seconds left to the end of greylisting period in the global
     variable 'greylist_seconds_left'.  *Note Greylisting::, for a
     detailed explanation.

     The function 'greylist' can signal 'e_dbfailure' exception.

 -- Built-in Function: boolean is_greylisted (string KEY
     Returns 'True' if the KEY is still greylisted.  If 'true' is
     returned, the function also stores the number of seconds left to
     the end of greylisting period in the global variable
     'greylist_seconds_left'.

     This function is available only if Con Tassios implementation of
     greylisting is used.  *Note greylisting types::, for a discussion
     of available greylisting implementations.  *Note greylist::, for a
     way to switch to Con Tassios implementation.


File: mailfromd.info,  Node: Special test functions,  Next: Mail Sending Functions,  Prev: Greylisting functions,  Up: Library

5.34 Special Test Functions
===========================

 -- Library Function: boolean portprobe (string HOST, [number PORT])
 -- Library Function: boolean listens (string HOST, [number PORT])
     Returns 'true' if the IP address or host name given by HOST
     argument listens on the port number PORT (default 25).

     This function is defined in the module 'portprobe'.

 -- Built-in Function: boolean validuser (string NAME)
     Returns 'true' if authenticity of the user NAME is confirmed using
     mailutils authentication system.  *Note Local Account
     Verification::, for more details.

 -- Library Function: boolean valid_domain (string DOMAIN)

     Returns 'true' if the domain name DOMAIN has a corresponding A
     record or if it has any 'MX' records, i.e.  if it is possible to
     send mail to it.

     To use this function, require the 'valid_domain' module (*note
     Modules::):

          require valid_domain

 -- Library Function: number heloarg_test (string ARG, string REMOTE_IP,
          string LOCAL_IP)

     Verify if an argument of 'HELO' ('EHLO') command is valid.  To use
     this function, require the 'heloarg_test' module (*note Modules::).

     Arguments:

     ARG
          'HELO' ('EHLO') argument.  Typically, the value of '$s'
          Sendmail macro;

     REMOTE_IP
          IP address of the remote client.  Typically, the value of
          '$client_addr' Sendmail macro;

     LOCAL_IP
          IP address of this SMTP server;

     The function returns a number describing the result of the test, as
     described in the following table.

     Code                          Meaning
     --------------------------------------------------------------------------
     HELO_SUCCESS                  ARG successfully passes all tests.
     HELO_MYIP                     ARG is our IP address.
     HELO_IPNOMATCH                ARG is an IP, but it does not match the
                                   remote party IP address.
     HELO_ARGNORESOLVE             ARG is an IP, but it does not resolve.
     HELO_ARGNOIP                  ARG is in square brackets, but it is not
                                   an IP address.
     HELO_ARGINVALID               ARG is not an IP address and does not
                                   resolve to one.
     HELO_MYSERVERIP               ARG resolves to our server IP.
     HELO_IPMISMATCH               ARG does not resolve to the remote client
                                   IP address.


File: mailfromd.info,  Node: Mail Sending Functions,  Next: Blacklisting Functions,  Prev: Special test functions,  Up: Library

5.35 Mail Sending Functions
===========================

The mail sending functions are new interfaces, introduced in version
3.1.

   The underlying mechanism for sending mail, called "mailer", is
specified by '--mailer' command line option.  This global setting can be
overridden using the last optional argument to a particular function.
In any case, the mailer is specified in the form of a URL.

   "Mailer URL" begins with a protocol specification.  Two protocol
specifications are currently supported: 'sendmail' and 'smtp'.  The
former means to use a 'sendmail'-compatible program to send mails.  Such
a program must be able to read mail from its standard input and must
support the following options:

'-oi'
     Do not treat '.' as message terminator.

'-f ADDR'
     Use ADDR as the address of the sender.

'-t'
     Get recipient addresses from the message.

   These conditions are met by most existing MTA programs, such as
'exim' or 'postfix' (to say nothing of 'sendmail' itself).

   Following the protocol specification is the "mailer location", which
is separated from it with a colon.  For the 'sendmail' protocol, the
mailer location sets the full file name of the Sendmail-compatible MTA
binary, for example:

     sendmail:/usr/sbin/sendmail

   A special form of a sendmail URL, consisting of protocol
specification only ('sendmail:') is also allowed.  It means "use the
sendmail binary from the '_PATH_SENDMAIL' macro in your
'/usr/include/paths.h' file".  This is the default mailer.

   The 'smtp' protocol means to use an SMTP server directly.  In this
case the mailer location consists of two slashes, followed by the IP
address or host name of the SMTP server, and, optionally, the port
number.  If the port number is present, it is separated from the rest of
URL by a colon.  For example:

     smtp://remote.server.net
     smtp://remote.server.net:24

 -- Built-in Function: void send_mail (string MSG [, string TO, string
          FROM, string MAILER])

     Sends message MSG to the email address TO.  The value of MSG must
     be a valid RFC 2822 message, consisting of headers and body.
     Optional argument TO can contain several email addresses.  In this
     case the message will be sent to each recipient specified in TO.
     If it is not specified, recipient addresses will be obtained from
     the message headers.

     Other optional arguments are:

     FROM
          Sets the sender address.  By default '<>' is used.

     MAILER
          The URL of the mailer to use

     Sample usage:

              set message <<- EOT
                    Subject: Test message
                    To: Postmaster <postmaster@gnu.org.ua>
                    From: Mailfromd <devnull@gnu.org.ua>
                    X-Agent: %__package__ (%__version__)

                    Dear postmaster,

                    This is to notify you that our /etc/mailfromd.mfl
                    needs a revision.
                    --
                    Mailfromd filter administrator
              EOT
              send_mail(message, "postmaster@gnu.org.ua")

 -- Built-in Function: void send_text (string TEXT, string HEADERS [,
          string TO, string FROM, string MAILER])
     A more complex interface to mail sending functions.

     Mandatory arguments:
     TEXT
          Text of the message to be sent.

     HEADERS
          Headers for the message.

     Optional arguments:
     TO
          Recipient email addresses.

     FROM
          Sender email address.

     MAILER
          URL of the mailer to use.

     The above example can be rewritten using 'send_text' as follows:

              set headers << -EOT
                    Subject: Test message
                    To: Postmaster <postmaster@gnu.org.ua>
                    From: Mailfromd <devnull@gnu.org.ua>
                    X-Agent: %__package__ (%__version__)
              EOT
              set text <<- EOT
                    Dear postmaster,

                    This is to notify you that our /etc/mailfromd.mfl
                    needs a revision.
                    --
                    Mailfromd filter administrator
              EOT
              send_text(text, headers, "postmaster@gnu.org.ua")

 -- Built-in Function: void send_message (number MSG [string TO, string
          FROM, string MAILER])
     Send the message identified by descriptor MSG (*note Message
     functions::).

     Optional arguments are:

     TO
          Recipient email addresses.

     FROM
          Sender email address.

     MAILER
          URL of the mailer to use.

 -- Built-in Function: void send_dsn (string TO, string SENDER, string
          RCPT, string TEXT [, string HEADERS, string FROM, string
          MAILER])
     This is an experimental interface which will change in the future
     versions.  It sends a message disposition notification (RFC 2298,
     RFC 1894), of type 'deleted' to the email address TO.  Arguments
     are:

     TO
          Recipient email address.

     SENDER
          Original sender email address.

     RCPT
          Original recipient email address.

     TEXT
          Notification text.

     Optional arguments:
     HEADERS
          Message headers

     FROM
          Sender address.

     MAILER
          URL of the mailer to use.

 -- Built-in Function: void create_dsn (string SENDER, string RCPT,
          string TEXT [, string HEADERS, string FROM])
     Creates DSN message and returns its descriptor.  Arguments are:

     SENDER
          Original sender email address.

     RCPT
          Original recipient email address.

     TEXT
          Notification text.

     HEADERS
          Message headers

     FROM
          Sender address.


File: mailfromd.info,  Node: Blacklisting Functions,  Next: SPF Functions,  Prev: Mail Sending Functions,  Up: Library

5.36 Blacklisting Functions
===========================

The functions described in this subsection allow to check whether the
given IP address is listed in certain "black list" DNS zone.

 -- Library Function: boolean match_dnsbl (string ADDRESS, string ZONE,
          string RANGE)

     This function looks up ADDRESS in the DNS blacklist zone ZONE and
     checks if the return falls into the given RANGE of IP addresses.

     It is intended as a replacement for the Sendmail macros 'dnsbl' and
     'enhdnsbl'.

     To use 'match_dnsbl', require the 'match_dnsbl' module (*note
     Modules::).

     Arguments:

     ADDRESS
          IP address of the SMTP server to be tested.

     ZONE
          FQDN of the DNSbl zone to test against.

     RANGE
          The range of IP addresses in CIDR notation or the word 'ANY',
          which stands for '127.0.0.0/8'.

     The function returns 'true' if dns lookup for ADDRESS in the zone
     DNSBL yields an IP that falls within the range, specified by CIDR.
     Otherwise, it returns 'false'.

     This function raises the following exceptions: 'e_invip' if ADDRESS
     is invalid and 'e_invcidr' if CIDR is invalid.

 -- Library Function: boolean match_rhsbl (string EMAIL, string ZONE,
          string RANGE)

     This function checks if the IP address, corresponding to the domain
     part of EMAIL is listed in the RHS DNS blacklist zone ZONE, and if
     so, whether its record falls into the given range of IP addresses
     RANGE.

     It is intended as a replacement for the Sendmail macro 'rhsbl' by
     Derek J. Balling.

     To use this function, require the 'match_rhsbl' module (*note
     Modules::).

     Arguments:

     EMAIL
          E-mail address, whose domain name should be tested (usually,
          it is '$f')

     ZONE
          Domain name of the RHS DNS blacklist zone.

     RANGE
          The range of IP addresses in CIDR notation.


File: mailfromd.info,  Node: SPF Functions,  Next: DKIM,  Prev: Blacklisting Functions,  Up: Library

5.37 SPF Functions
==================

"Sender Policy Framework", or SPF for short, is an extension to SMTP
protocol that allows to identify forged identities supplied with the
'MAIL FROM' and 'HELO' commands.  The framework is explained in detail
in RFC 4408 (<http://tools.ietf.org/html/rfc4408>) and on the SPF
Project Site (http://www.openspf.org/).  The following description is a
short introduction only, and the users are encouraged to refer to the
original specification for the detailed description of the framework.

   The domain holder publishes an "SPF record" - a special DNS resource
record that contains a set of rules declaring which hosts are, and are
not, authorized to use a domain name for 'HELO' and 'MAIL FROM'
identities.  This resource record is usually of type 'TXT'.(1)

   The MFL script can verify if the identity matches the published SPF
record by calling 'check_host' function and analyzing its return code.
The function can be called either in 'helo' or in 'envfrom' handler.
Its arguments are:

IP
     The IP address of the SMTP client that is emitting the mail.
     Usually it is '$client_addr'.

DOMAIN
     The domain that provides the sought-after authorization
     information; Normally it is the domain portion of the 'MAIL FROM'
     or 'HELO' identity.

SENDER
     The 'MAIL FROM' identity.

HELO_DOMAIN
     The 'HELO' identity.

MY_DOMAIN
     The SMTP domain served by the local server.

   The function returns a numeric result code.  For convenience, all
possible return values are defined as macros in module 'spf.mfl'.  The
table below describes each value along with the recommended actions for
it:

'None'
     A result of 'None' means that no records were published by the
     domain or that no checkable sender domain could be determined from
     the given identity.  The checking software cannot ascertain whether
     or not the client host is authorized.  Such a message can be
     subject to further checks that will decide about its fate.

'Neutral'
     The domain owner has explicitly stated that he cannot or does not
     want to assert whether or not the IP address is authorized.  This
     result must be treated exactly like 'None'; the distinction between
     them exists only for informational purposes

'Pass'
     The client is authorized to send mail with the given identity.  The
     message can be subject to further policy checks with confidence in
     the legitimate use of the identity or it can be accepted in the
     absence of such checks.

'Fail'
     The client is not authorized to use the domain in the given
     identity.  The proper action in this case can be to mark the
     message with a header explicitly stating it is spam, or to reject
     it outright.

     If you choose to reject such mails, we suggest to use 'reject 550
     5.7.1', as recommended by RFC 4408.  The reject can return either a
     default explanation string, or the one supplied by the domain that
     published the SPF records, as in the example below:

            reject 550 5.7.1 "SPF check failed:\n%spf_explanation"

     (for the description of 'spf_explanation', *note spf_explanation::)

'SoftFail'
     The domain believes the host is not authorized but is not willing
     to make that strong of a statement.  This result code should be
     treated as somewhere in between a 'Fail' and a 'Neutral'.  It is
     not recommended to reject the message based solely on this result.

'TempError'
     A transient error occurred while performing SPF check.  The proper
     action in this case is to accept or temporarily reject the message.
     If you choose the latter, we suggest to use SMTP reply code of
     '451' and DSN code '4.4.3', for example:

            tempfail 451 4.4.3
                     "Transient error while performing SPF verification"

'PermError'
     This result means that the domain's published records could not be
     correctly interpreted.  This signals an error condition that
     requires manual intervention to be resolved, as opposed to the
     'TempError' result.

   The following example illustrates the use of SPF verification in
'envfrom' handler:

     require 'status'
     require 'spf'

     prog envfrom
     do
       switch check_host($client_addr, domainpart($f), $f, $s)
       do
       case Fail:
         string text ""
         if spf_explanation != ""
           set text "%text\n%spf_explanation"
         fi
         reject 550 5.7.1 "SPF MAIL FROM check failed: %text"

       case Pass:
         accept

       case TempError:
         tempfail 451 4.4.3
                  "Transient error while performing SPF verification"

       default:
         on poll $f do
         when success:
           accept
         when not_found or failure:
           reject 550 5.1.0 "Sender validity not confirmed"
         when temp_failure:
           tempfail 450 4.7.0 "Temporary failure during sender verification"
         done
       done
     done

   The SPF support is implemented in MFL in two layers: a built-in layer
that provides basic support, and a library layer that provides a
convenience wrapper over the library function.

   The library layer is implemented in the module 'spf.mfl' (*note
Modules::).

   The rest of this node describes available SPF functions and
variables.

 -- Built-in Function: number spf_check_host (string IP, string DOMAIN,
          string SENDER, string HELO_DOMAIN, string MY_DOMAIN)

     This function is the basic implementation of the 'check_host'
     function, defined in RFC 4408, chapter 4.  It fetches SPF records,
     parses them, and evaluates them to determine whether a particular
     host (IP) is or is not permitted to send mail from a given email
     address (SENDER).  The function returns an "SPF result code".

     Arguments are:

     IP
          The IP address of the SMTP client that is emitting the mail.
          Usually it is '$client_addr'.

     DOMAIN
          The domain that provides the sought-after authorization
          information; Normally it is the domain portion of the 'MAIL
          FROM' or 'HELO' identity.

     SENDER
          The 'MAIL FROM' identity.

     HELO_DOMAIN
          The 'HELO' identity.

     MY_DOMAIN
          The SMTP domain served by the local server.

     Before returning the 'spf_check_host' function stores additional
     information in global variables:

     'spf_explanation'
          If the result code is 'Fail', this variable contains the
          explanation string as returned by the publishing domain,
          prefixed with the value of the global variable
          'spf_explanation_prefix'.

          For example, if 'spf_explanation_prefix' contains 'The domain
          %{o} explains: ', and the publishing domain 'example.com'
          returns the explanation string 'Please see
          http://www.example.com/mailpolicy.html', than the value of
          'spf_explanation' will be:

               The domain example.com explains:
               Please see http://www.example.com/mailpolicy.html

          (see RFC 4408 (http://tools.ietf.org/html/rfc4408), chapter 8,
          for the description of SPF macro facility).

     'spf_mechanism'
          Name of the SPF mechanism that decided about the result code
          of the SPF record.  If one or more 'include' or 'redirect'
          mechanisms were traversed before arriving at that mechanism,
          their values are appended in the reverse order.

 -- Built-in Function: number spf_test_record (string RECORD, string IP,
          string DOMAIN, string SENDER, string HELO_DOMAIN, string
          MY_DOMAIN)

     Evaluate SPF record RECORD as if it were published by DOMAIN.  The
     rest of arguments are the same as for 'spf_check_host' above.

     This function is designed primarily for testing and debugging
     purposes.  You would hardly need to use it.

     The 'spf_test_record' function sets the same global variables as
     'spf_check_host'.

 -- Library Function: number check_host (string IP, string DOMAIN,
          string SENDER, string HELO)

     This function implements the 'check_host' function, defined in RFC
     4408, chapter 4.  It fetches SPF records, parses them, and
     evaluates them to determine whether a particular host (IP) is or is
     not permitted to send mail from a given email address (SENDER).
     The function returns an "SPF result code".

     This function is a wrapper over the built-in 'spf_check_host'.

     The arguments are:

     IP
          The IP address of the SMTP client that is emitting the mail.
          Usually it is the same as the value of '$client_addr'.

     DOMAIN
          The domain that provides the sought-after authorization
          information; Normally it is the domain portion of the 'MAIL
          FROM' or 'HELO' identity.

     SENDER
          The 'MAIL FROM' identity.

     HELO
          The 'HELO' identity.

 -- Library Function: string spf_status_string (number CODE)
     Converts numeric SPF result CODE to its string representation.

 -- Built-in variable: string spf_explanation
     If 'check_host' (or 'spf_check_host' or 'spf_test_record') returned
     'Fail', this variable contains the explanation string as returned
     by the publishing domain, prefixed with the value of the global
     variable 'spf_explanation_prefix'.

     For example, if 'spf_explanation_prefix' contains 'The domain %{o}
     explains: ', and the publishing domain 'example.com' returns the
     explanation string 'Please see
     http://www.example.com/mailpolicy.html', than the value of
     'spf_explanation' will be:

          The domain example.com explains:
          Please see http://www.example.com/mailpolicy.html

 -- Built-in variable: string spf_mechanism
     Set to the name of a SPF mechanism that decided about the result
     code of the SPF record.

 -- Built-in variable: string spf_explanation_prefix
     The prefix to be appended to the explanation string before storing
     it in the 'spf_explanation' variable.  This string can contain
     valid SPF macros (see RFC 4408
     (http://tools.ietf.org/html/rfc4408), chapter 8), for example:

          set spf_explanation_prefix "%{o} explains: "

     The default value is '""' (an empty string).

   ---------- Footnotes ----------

   (1) Although RFC 4408 introduces a special 'SPF' record type for this
purpose, it is not yet widely used.  As of version 9.1, MFL does not
support 'SPF' DNS records.


File: mailfromd.info,  Node: DKIM,  Next: Sockmaps,  Prev: SPF Functions,  Up: Library

5.38 DKIM
=========

DKIM or "DomainKeys Identified Mail" is an email authentication method
that allows recipients to verify if an email was authorized by the owner
of the domain that email claims to originate from.  It does so by adding
a digital signature which is verified using a public key published as a
DNS TXT record.  For technical details about DKIM, please refer to RFC
6376 (<http://tools.ietf.org/html/rfc6376>).

   MFL provides functions for DKIM signing and verification.

 -- Built-in Function: number dkim_verify (number MSG)
     Verifies the message MSG (a message descriptor, obtained from a
     call to 'current_message', 'mailbox_get_message',
     'message_from_stream' or a similar function).

     Return value (constants defined in the 'status' module):

      -- dkim_verify status: DKIM_VERIFY_OK
          The message contains one or more 'DKIM-Signature' headers and
          one of them verified successfully.

      -- dkim_verify status: DKIM_VERIFY_PERMFAIL
          The message contains one or more 'DKIM-Signature' headers, all
          of which failed to verify.

      -- dkim_verify status: DKIM_VERIFY_TEMPFAIL
          The message was not signed using DKIM, or the DNS query to
          obtain the public key failed, or an internal software error
          occurred during verification.

     The following two global variables are always set upon return from
     this function: 'dkim_explanation' and 'dkim_explanation_code'.
     These can be used to clarify the verification result to the end
     user.  The variable 'dkim_signing_algorithm' is initialized with
     the name of the algorithm used to sign the message.

     Upon successful return, the variable 'dkim_verified_signature' is
     set to the value of the successfully verified DKIM signature.

 -- Built-in variable: string dkim_signing_algorithm
     Name of the algorithm used to sign the message (either 'rsa-sha1'
     or 'rsa-sha256').  If the algorithm was not specified (e.g.  the
     signature is malformed), this variable is assigned an empty value.

 -- Built-in variable: string dkim_explanation
     An explanatory message clarifying the verification result.

 -- Built-in variable: number dkim_explanation_code
     A numeric code corresponding to the 'dkim_explanation' string.  Its
     possible values are defined in 'status.mfl':

      -- DKIM explanation code: DKIM_EXPL_OK
          'DKIM verification passed'

      -- DKIM explanation code: DKIM_EXPL_OK
          'DKIM verification passed'

      -- DKIM explanation code: DKIM_EXPL_NO_SIG
          'No DKIM signature'

      -- DKIM explanation code: DKIM_EXPL_INTERNAL_ERROR
          'internal error'

      -- DKIM explanation code: DKIM_EXPL_SIG_SYNTAX
          'signature syntax error'

      -- DKIM explanation code: DKIM_EXPL_SIG_MISS
          'signature is missing required tag'

          According to the DKIM specification, required tags are: 'a=',
          'b=', 'bh=', 'd=', 'h=', 's=', 'v='.

      -- DKIM explanation code: DKIM_EXPL_DOMAIN_MISMATCH
          'domain mismatch'

          The domain part of the 'i=' tag does not match and is not a
          subdomain of the domain listed in the 'd=' tag.

      -- DKIM explanation code: DKIM_EXPL_BAD_VERSION
          'incompatible version'

          Incompatible DKIM version listed in the 'v=' tag.

      -- DKIM explanation code: DKIM_EXPL_BAD_ALGORITHM
          'unsupported signing algorithm'

          Either the 'a=' tag of the DKIM signature contains an
          unsupported algorithm (currently supported algorithms are:
          'rsa-sha1' and 'rsa-sha256') or this algorithm, while being
          supported by 'mailfromd', is not listed in the 'h=' tag of the
          public DKIM key.

      -- DKIM explanation code: DKIM_EXPL_BAD_QUERY
          'unsupported query method'

          The 'q=' tag of the public DKIM key contains something other
          than 'dns/txt'.

      -- DKIM explanation code: DKIM_EXPL_FROM
          'From field not signed'

      -- DKIM explanation code: DKIM_EXPL_EXPIRED
          'signature expired'

      -- DKIM explanation code: DKIM_EXPL_DNS_UNAVAIL
          'public key unavailable'

      -- DKIM explanation code: DKIM_EXPL_DNS_NOTFOUND
          'public key not found'

      -- DKIM explanation code: DKIM_EXPL_KEY_SYNTAX
          'key syntax error'

      -- DKIM explanation code: DKIM_EXPL_KEY_REVOKED
          'key revoked'

      -- DKIM explanation code: DKIM_EXPL_BAD_BODY
          'body hash did not verify'

      -- DKIM explanation code: DKIM_EXPL_BAD_BASE64
          'can't decode b= tag'

          Base64 decoding of the 'b=' tag failed.

      -- DKIM explanation code: DKIM_EXPL_BAD_SIG
          'signature did not verify'

      -- DKIM explanation code: DKIM_EXPL_BAD_KEY_TYPE
          'unsupported public key type'

          The 'k=' tag of the public DKIM signature contains a value,
          other than 'rsa'.

 -- Built-in variable: string dkim_verified_signature
     Upon successful return from the 'dkim_verify' function, this
     variable holds the value of the successfully verified DKIM header.
     This value is unfolded and all whitespace is removed from it.

   An example of using the 'dkim_verify' function:

     require status
     require dkim

     prog eom
     do
       string result
       switch dkim_verify(current_message())
       do
          case DKIM_VERIFY_OK:
              set result "pass; verified for " .
                         dkim_verified_signature_tag('i')
          case DKIM_VERIFY_PERMFAIL:
              set result "fail (%dkim_explanation)"
          case DKIM_VERIFY_TEMPFAIL:
              set result "neutral"
       done
       header_add("X-Verification-Result", "dkim=%result")
     done

   The 'dkim' module defines convenience functions for manipulating with
DKIM signatures:

 -- Library Function: dkim_signature_tag (string SIG, string TAG)
     Extracts the value of the tag TAG from the DKIM signature SIG.
     Signature must be normalized by performing the header unwrapping
     and removing whitespace characters.

     If the tag was not found, returns empty string, unless TAG is one
     of the tags listed in the table below.  If any of these tags are
     absent, the following values are returned instead:

     Tag                    Default value
     -------------------------------------------------------------------
     c                      'simple/simple'
     q                      'dns/txt'
     i                      '@' + the value of the 'd' tag.

 -- Library Function: string dkim_verified_signature_tag (string TAG)
     Returns the value of tag TAG from the 'dkim_verified_signature'
     variable.

 -- Built-in Function: void dkim_sign (string D, string S, string
          KEYFILE, [ string CH, string CB, string HEADERS, string ALGO
          ])

     This function is available only in the 'eom' handler.

     Signs the current message.  _Notice_, that no other modification
     should be attempted on the message after calling this function.
     Doing so would make the signature invalid.

     Mandatory arguments:

     D
          Name of the domain claiming responsibility for an introduction
          of a message into the mail stream.  It is also known as the
          signing domain identifier (SDID).

     S
          The selector name.  This value, along with D identifies the
          location of the DKIM public key necessary for verifying the
          message.  The public key is stored in the DNS TXT record for

                 S._domainkey.D

     KEYFILE
          Name of the disk file that keeps the private key for signing
          the message.  The file must be in PKCS#1 or PKCS#8 format (PEM
          formatted).

     Optional arguments:

     CH
          Canonicalization algorithm for message headers.  Valid values
          are: 'simple' and 'relaxed'.  'simple' is the default.

     CB
          Canonicalization algorithm for message body.  Valid and
          default values are the same as for CH.

     HEADERS
          A colon-separated list of header field names that identify the
          header fields that must be signed.  Optional whitespace is
          allowed at either side of each colon separator.  Header names
          are case-insensitive.  This list must contain at least the
          'From' header.

          It may contain names of headers that are not present in the
          message being signed.  This provides a way to explicitly
          assert the absence of a header field.  For example, if HEADERS
          contained 'X-Mailer' and that header is not present in the
          message being signed, but is added by a third party later, the
          signature verification will fail.

          Similarly, listing a header field name once more than the
          actual number of its occurrences in a message allows you to
          prevent any further additions.  For example, if there is a
          single 'Comments' header field at the time of signing, putting
          'Comments:Comments:' in the HEADERS parameter is sufficient to
          prevent any surplus 'Comments' headers from being added later
          on.

          Multiple instances of the same header name are allowed.  They
          mean that multiple occurrences of the corresponding header
          field will be included in the header hash.  When such multiple
          header occurrences are referenced, they will be presented to
          the hashing algorithm in the reverse order.  E.g.  if the
          HEADER list contained 'Received:Received') and the current
          message contained three 'Received' headers:

               Received: A
               Received: B
               Received: C

          then these headers will be signed in the following order:

               Received: C
               Received: B

          The default value for this parameter, split over multiple
          lines for readability, is as follows:

             * "From:From:"
             * "Reply-To:Reply-To:"
             * "Subject:Subject:"
             * "Date:Date:"
             * "To:"
             * "Cc:"
             * "Resent-Date:"
             * "Resent-From:"
             * "Resent-To:"
             * "Resent-Cc:"
             * "In-Reply-To:"
             * "References:"
             * "List-Id:"
             * "List-Help:"
             * "List-Unsubscribe:"
             * "List-Subscribe:"
             * "List-Post:"
             * "List-Owner:"
             * "List-Archive"

     ALGO
          Signing algorithm: either 'rsa-sha256' or 'rsa-sha1'.  Default
          is 'rsa-sha256'.

   An example of using this function:

     precious string domain "example.org"
     precious string selector "s2048"
     prog eom
     do
       dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem",
                 "relaxed", "relaxed", "from:to:subject")
     done

* Menu:

* Setting up a DKIM record::

Note on interaction of dkim_sign with Sendmail
----------------------------------------------

When sending a signed message, it is critical that no other
modifications be applied to the message after it has been signed.
Unfortunately, it is not always the case when 'mailfromd' is used with
'Sendmail'.  Before sending the message over SMTP, 'Sendmail' reformats
the headers that contain a list of email addresses, by applying to them
a procedure called in its parlance "commaization".  The following
headers are modified: 'Apparently-To', 'Bcc', 'Cc',
'Disposition-Notification-To', 'Errors-To', 'From', 'Reply-To',
'Resent-Bcc', 'Resent-Cc', 'Resent-From', 'Resent-Reply-To',
'Resent-Sender', 'Resent-To', 'Sender', 'To'.  Thus, if your 'dkim_sign'
includes any of these in the signature (which is the default) and some
of them happen to be formatted other way than the one 'Sendmail'
prefers, the DKIM signature would not verify on the recipient side.  To
prevent this from happening, 'dkim_sign' mimics the Sendmail behavior
and reformats those headers before signing the message.  This should
ensure that the message signed and the message actually sent are the
same.  This default behavior is controlled by the following global
variable:

 -- Built-in variable: number dkim_sendmail_commaize
     "Commaize" the address headers (see the list above) of the message
     the same way Sendmail does, and then sign the resulting message.

     The default value is 1 ('true').  You can set it to 0 ('false') if
     this behavior is not what you want (e.g.  if you are using
     'postfix' or some other MTA).

Note on interaction of dkim_sign with MMQ
-----------------------------------------

The functions 'header_add' and 'header_insert' (*note Header
modification functions::) as well as the 'add' action (*note header
manipulation::) cannot interact properly with 'dkim_sign' due to the
shortcomings of the Milter API. If any of these was called, 'dkim_sign'
will throw the 'e_badmmq' exception with the following diagnostics:

     MMQ incompatible with dkim_sign: OP on H, value V

where OP is the operation code ('ADD HEADER' or 'INSERT HEADER'), H is
the header name and V is its value.

   The following example shows one graceful way of handling such
exception:

     prog eom
     do
         try
         do
             dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem")
         done
         catch e_badmmq
         do
             # Purge the message modification queue
             mmq_purge()
             # and retry
             dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem")
         done
     done

   *Note Message modification queue::, for a discussion of the message
modification queue.


File: mailfromd.info,  Node: Setting up a DKIM record,  Up: DKIM

5.38.1 Setting up a DKIM record
-------------------------------

Follow these steps to set up your own DKIM record:

  1. Generate a key pair:

     Use the 'openssl genrsa' command.  Run:

          openssl genrsa -out private.pem 2048

     The last argument is the size of the private key to generate in
     bits.

  2. Extract the public key:

          openssl rsa -in private.pem -pubout -outform PEM -out public.pem

  3. Set up a DKIM record in your domain:

     A DKIM record is a TXT type DNS record that holds the public key
     part for verifying messages.  Its format is defined in RFC 4871(1).
     The label for this record is composed as follows:

            S._domainkey.D

     where D is your domain name, and S is the selector you chose to
     use.  You will use these two values as parameters to the
     'dkim_sign' function in your 'eom' handler.  E.g.  if your domain
     in 'example.com' and selector is 's2048', then the DKIM TXT record
     label is 's2048._domainkey.example.com'.

     The public key file generated in step 2 will have the following
     contents:

          -----BEGIN PUBLIC KEY-----
          BASE64
          -----END PUBLIC KEY-----

     where BASE64 is the key itself in base64 encoding.  The minimal
     DKIM TXT record will be:

          "v=DKIM1; p=BASE64"

     The only mandatory "tag" is in fact 'p='.  The use of 'v=' is
     recommended.  More tags can be added as needed.  In particular,
     while testing the DKIM support, it is advisable to add the 't=y'
     tag.

   ---------- Footnotes ----------

   (1) <https://tools.ietf.org/html/rfc4871>


File: mailfromd.info,  Node: Sockmaps,  Next: NLS Functions,  Prev: DKIM,  Up: Library

5.39 Sockmap Functions
======================

Socket map ("sockmap" for short) is a special type of database used in
Sendmail and MeTA1.  It uses a simple server/client protocol over INET
or UNIX stream sockets.  The server listens on a socket for queries.
The client connects to the server and sends it a query, consisting of a
"map name" and a "key" separated by a single space.  Both map name and
key are sequences of non-whitespace characters.  The map name serves to
identify the type of the query.  The server replies with a response
consisting of a "status indicator" and "result", separated by a single
space.  The result part is optional.

   For example, following is the query for key 'smith' in map 'aliases':

     11:aliases news,

   A possible reply is:

     18:OK root@domain.net,

   This reply means that the key 'news' was found in the map, and the
value corresponding to that key is 'root@domain.net'.

   The following reply means the key was not found:

     8:NOTFOUND,

   For a detailed description of the sockmap protocol, see *note
(smap)Protocol::.

   The MFL library provides two primitives for dealing with sockmaps.
Both primitives become available after requiring the 'sockmap' module.

 -- Library Function: string sockmap_lookup (number FD, string MAP,
          string KEY)
     This function look ups the KEY in the MAP.  The FD refers to the
     sockmap to use.  It must be obtained as a result of a previous call
     to 'open' with the URL of the sockmap as its first argument (*note
     open: I/O functions.).  For example:

            number fd open("@ unix:///var/spool/meta1/smap/socket")
            string ret sockmap_query(fd, "aliases", $rcpt_to)
            if ret matches "OK (.+)"
              set alias \1
            fi
            close(fd)

 -- Library Function: string sockmap_single_lookup (string URL, string
          MAP, string KEY)
     This function connects to the sockmap identified by the URL,
     queries for KEY in MAP and closes the connection.  It is useful
     when you need to perform only a single lookup on the sockmap.


File: mailfromd.info,  Node: NLS Functions,  Next: Syslog Interface,  Prev: Sockmaps,  Up: Library

5.40 National Language Support Functions
========================================

The "National Language Support" functions allow you to write your
scripts in such a way, that any textual messages they display are
automatically translated to your native language, or, more precisely, to
the language required by your current locale.

   This section assumes the reader is familiar with the concepts of
program "internationalization" and "localization".  If not, please refer
to *note The Purpose of GNU 'gettext': (gettext)Why, before reading
further.

   In general, internationalization of any MFL script follows the same
rules as described in the 'GNU gettext manual'.  First of all, you
select the program "message domain", i.e.  the identifier of a set of
translatable messages your script contain.  This identifier is then used
to select appropriate translation.  The message domain is set using
'textdomain' function.  For the purposes of this section, let's suppose
the domain name is 'myfilter'.  All NLS functions are provided in the
'nls' module, which you need to require prior to using any of them.

   To find translations of textual message to the current locale, the
underlying 'gettext' mechanism will look for file
'DIRNAME/LOCALE/LC_MESSAGES/DOMAINNAME.mo', where DIRNAME is the message
catalog hierarchy name, LOCALE is the locale name, and DOMAINNAME is the
name of the message domain.  By default DIRNAME is
'/usr/local/share/locale', but you may change it using 'bindtextdomain'
function.  The right place for this initial NLS setup is in the 'begin'
block (*note begin/end::).  To summarize all the above, the usual NLS
setup will look like:

     require nls

     begin
     do
       textdomain("myfilter")
       bindtextdomain("myfilter", "/usr/share/locale");
     done

   For example, given the settings above, and supposing the environment
variable 'LC_ALL' is set to 'pl', translations will be looked in file
'/usr/share/locale/pl/LC_MESSAGES/myfilter.mo'.

   Once this preparatory work is done, you can request each message to
be translated by using 'gettext' function, or '_' (underscore) macro.
For example, the following statement will produce translated textual
description for '450' response:

     tempfail 450 4.1.0 _("Try again later")

   Of course it assumes that the appropriate 'myfile.mo' file already
exists.  If it does not, nothing bad happens: in this case the macro '_'
(as well as 'gettext' function) will simply return its argument
unchanged, so that the remote party will get the textual message in
English.

   The 'mo' files are binary files created from 'po' source files using
'msgfmt' utility, as described in *note Producing Binary MO Files:
(gettext)Binaries.  In turn, the format of 'po' files is described in
*note The Format of PO Files: (gettext)PO Files.

 -- Built-in Function: string bindtextdomain (string DOMAIN, string
          DIRNAME)
     This function sets the base directory of the hierarchy containing
     message catalogs for a given message domain.

     DOMAIN is a string identifying the textual domain.  If it is not
     empty, the base directory for message catalogs belonging to domain
     DOMAIN is set to DIRNAME.  It is important that DIRNAME be an
     absolute pathname; otherwise it cannot be guaranteed that the
     message catalogs will be found.

     If DOMAIN is '""', 'bindtextdomain' returns the previously set base
     directory for domain DOMAIN.

   The rest of this section describes the NLS functions supplied in the
'nls' module.

 -- Built-in Function: string dgettext (string DOMAIN, string MSGID)
     'dgettext' attempts to translate the string MSGID into the
     currently active locale, according to the settings of the textual
     domain DOMAIN.  If there is no translation available, 'dgettext'
     returns MSGID unchanged.

 -- Built-in Function: string dngettext (string DOMAIN, string MSGID,
          string MSGID_PLURAL, number N)
     The 'dngettext' functions attempts to translate a text string into
     the language specified by the current locale, by looking up the
     appropriate singular or plural form of the translation in a message
     catalog, set for the textual domain DOMAIN.

     *Note Additional functions for plural forms: (gettext)Plural forms,
     for a discussion of the plural form handling in different
     languages.

 -- Library Function: string textdomain (string DOMAIN)
     The 'textdomain' function sets the current message domain to
     DOMAIN, if it is not empty.  In any case the function returns the
     current message domain.  The current domain is 'mailfromd'
     initially.  For example, the following sequence of 'textdomain'
     invocations will yield:

          textdomain("") => "mailfromd"
          textdomain("myfilter") => "myfilter"
          textdomain("") => "myfilter"

 -- Library Function: string gettext (string MSGID)
     'gettext' attempts to translate the string MSGID into the currently
     active locale, according to the settings of the current textual
     domain (set using 'textdomain' function).  If there is no
     translation available, 'gettext' returns MSGID unchanged.

 -- Library Function: string ngettext (string MSGID, string
          MSGID_PLURAL, number N)
     The 'ngettext' functions attempts to translate a text string into
     the language specified by the current locale, by looking up the
     appropriate singular or plural form of the translation in a message
     catalog, set for the current textual domain.

     *Note Additional functions for plural forms: (gettext)Plural forms,
     for a discussion of the plural form handling in different
     languages.


File: mailfromd.info,  Node: Syslog Interface,  Next: Debugging Functions,  Prev: NLS Functions,  Up: Library

5.41 Syslog Interface
=====================

The basic means for outputting diagnostic messages is the 'echo'
instruction (*note Echo::), which sends its arguments to the currently
established logging channel.  In daemon mode, the latter is normally
connected to syslog, so any echoed messages are sent there with the
facility selected in mailfromd configuration and priority 'info'.

   If you want to send a message to another facility and/or priority,
use the 'syslog' function:

 -- Built-in Function: void syslog (number PRIORITY, string TEXT)
     Sends TEXT to syslog.  The priority argument is formed by ORing the
     facility and the level values (explained below).  The facility
     level is optional.  If not supplied, the currently selected logging
     facility is used.

   The facility specifies what type of program is logging the message,
and the level indicates its relative severity.  The following symbolic
facility values are declared in the 'syslog' module: 'LOG_KERN',
'LOG_USER', 'LOG_MAIL', 'LOG_DAEMON', 'LOG_AUTH', 'LOG_SYSLOG',
'LOG_LPR', 'LOG_NEWS', 'LOG_UUCP', 'LOG_CRON', 'LOG_AUTHPRIV', 'LOG_FTP'
and 'LOG_LOCAL0' through 'LOG_LOCAL7'

   The declared severity levels are: 'LOG_EMERG', 'LOG_ALERT',
'LOG_CRIT', 'LOG_ERR', 'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO' and
'LOG_DEBUG'.


File: mailfromd.info,  Node: Debugging Functions,  Next: Informative Functions,  Prev: Syslog Interface,  Up: Library

5.42 Debugging Functions
========================

These functions are designed for debugging the MFL programs.

 -- Built-in Function: void debug (string SPEC)
     Enable debugging.  The value of SPEC sets the debugging level.
     *Note debugging level specification::, for a description of its
     format.

     For compatibility with previous versions, this function is also
     available under the name 'mailutils_set_debug_level'.

 -- Built-in Function: number debug_level ([string SRCNAME])
     This function returns the debugging level currently in effect for
     the source module SRCNAME, or the global debugging level, if called
     without arguments.

     For example, if the program was started with
     '--debug='all.trace5;engine.trace8'' option, then:

          debug_level() => 127
          debug_level("engine") => 1023
          debug_level("db") => 0

 -- Built-in Function: boolean callout_transcript ([boolean VALUE])
     Returns the current state of the callout SMTP transcript.  The
     result is 1 if the transcript is enabled and 0 otherwise.  The
     transcript is normally enabled either by the use of the
     '--transcript' command line option (*note SMTP transcript::) or via
     the 'transcript' configuration statement (*note transcript:
     conf-server.).

     The optional VALUE, supplies the new state for SMTP transcript.
     Thus, calling 'callout_transcript(0)' disables the transcript.

     This function can be used in bracket-like fashion to enable
     transcript for a certain part of MFL program, e.g.:

          number xstate callout_transcript(1)
          on poll $f do
            ...
          done
          set xstate callout_transcript(0)

     Note, that the use of this function (as well as the use of the
     '--transcript' option) makes sense only if callouts are performed
     by the 'mailfromd' daemon itself.  It will not work if a dedicated
     callout server is used for that purpose (*note calloutd::).

 -- Built-in Function: string debug_spec ([string CATNAMES, bool
          SHOWUNSET])
     Returns the current debugging level specification, as given by
     '--debug' command line option or by the 'debug' configuration
     statement (*note conf-debug::).

     If the argument SRCNAMES is specified, it is treated as a
     semicolon-separated list of categories for which the debugging
     specification is to be returned.

     For example, if 'mailfromd' was started with
     '--debug=all.trace5;spf.trace1;engine.trace8;db.trace0', then:

          debug_spec() => "all.trace5,engine.trace8"
          debug_spec("all;engine") => "all.trace5,engine.trace8"
          debug_spec("engine;db") => "db.trace0;engine.trace8"
          debug_spec("prog") => ""

     When called without arguments, 'debug_spec' returns only those
     categories which have been set, as shown in the first example
     above.

     Optional SHOWUNSET parameters controls whether to return unset
     module specifications.  To print all debugging specifications,
     whether set or not, use

          debug_spec("", 1)

   These three functions are intended to complement each other.  The
calls to 'debug' can be placed around some piece of code you wish to
debug, to enable specific debugging information for this code fragment
only.  For example:

         /* Save debugging level for 'dns.c' source */
         set dlev debug_spec("dns", 1)
         /* Set new debugging level */
         debug("dns.trace8")
         .
         .
         .
         /* Restore previous level */
         debug(dlev)

 -- Built-in Function: void program_trace (string MODULE)
     Enable tracing for a set of modules given in MODULE argument.
     *Note --trace-program::, for a description of its format.

 -- Built-in Function: void cancel_program_trace (string MODULE)
     Disable tracing for given modules.

   This pair of functions is also designed to be used together in a
bracket-like fashion.  They are useful for debugging 'mailfromd', but
are not advised to use otherwise, since tracing slows down the execution
considerably.

 -- Built-in Function: void stack_trace ()
     Generate a stack trace in this point.  *Note tracing runtime
     errors::, for the detailed description of stack traces.

 -- Built-in Function: string stack_trace_format (string FMT)
     Format each frame in stack trace to a string according to FMT, and
     return the concatenated strings.  The format string FMT consists of
     regular characters, which are copied to the resulting string
     verbatim, and "conversion specifiers", prefixed with percent sign,
     which expand to parts of stack trace.  The following conversion
     specifiers are defined:

     '%d'
          Frame number, counting from the innermost (last) frame,
          0-based.
     '%f'
          Name of the function.
     '%n'
          Line number in the source file.
     '%s'
          Name of the source file.
     '%p'
          Value of the PC register.
     '%%'
          Single percent sign.

     A '%' followed by any other letter is reproduced verbatim.

 -- Built-in Function: string stack_trace_top (string FMT)
     Format the top-level stack frame according to FMT.  See above for a
     discussion of format string.

   The functions below are intended mainly for debugging MFL run-time
engine and for use in 'mailfromd' testsuite.  You will hardly need to
use them in your programs.

 -- Built-in Function: void _expand_dataseg (number N)
     Expands the run-time data segment by at least N words.

 -- Built-in Function: number _reg (number R)
     Returns the value of the register R at the moment of the call.
     Symbolic names for run-time registers are provided in the module
     '_register':

     Name                   Register
     --------------------------------------------------------------------------
     REG_PC                 Program counter
     REG_TOS                Top of stack
     REG_TOH                Top of heap
     REG_BASE               Frame base
     REG_REG                General-purpose accumulator
     REG_MATCHSTR           Last matched string pointer

 -- Built-in Function: number _stack_free ()
     Returns number of words available for use in stack.  This is the
     same as

          _reg(REG_TOS) - _reg(REG_TOH)

 -- Built-in Function: number _heap_reserve (number N)
     Use up next N words in the heap.  Return the address of the first
     word.

 -- Built-in Function: void _wd ([number N])
     Enters a time-consuming loop and waits there for N seconds (by
     default - indefinitely).  The intention is to facilitate attaching
     to 'mailfromd' with the debugger.  Before entering the loop, a
     diagnostic message is printed on the 'crit' facility, informing
     about the PID of the process and suggesting the command to be used
     to attach to it, e.g.:

          mailfromd: process 21831 is waiting for debug
          mailfromd: to attach: gdb -ex 'set variable mu_wd::_count_down=0'
           /usr/sbib/mailfromd 21831


File: mailfromd.info,  Node: Informative Functions,  Next: Mfmod Interface,  Prev: Debugging Functions,  Up: Library

5.43 Informative Functions
==========================

These functions convert numeric identifiers of various MFL entities to
strings and vice-versa.

 -- Built-in Function: string milter_state_name (number CODE)
     Convert the numeric identifier of a "milter state" to textual form.
     It is normally used to convert the 'milter_state' variable (*note
     milter state::) to string, e.g.:

          milter_state_name(5) => "connect"

     If CODE does not refer to a valid milter state, the 'e_inval'
     exception is raised.

 -- Built-in Function: number milter_state_code (string NAME)
     Returns numeric code of the milter state NAME.  If NAME does not
     refer to a valid milter state, returns 0 ('milter_state_none' from
     the 'milter.mfl' module).

 -- Built-in Function: string milter_action_name (number CODE)
     Convert the numeric identifier of a reply action (*note reply
     actions::) to textual name.

     If CODE does not refer to a valid reply action, the 'e_inval'
     exception is raised.

          milter_state_code("connect") => 5

     This function is useful in action hooks.  *Note action hook::, for
     details.

 -- Built-in Function: number milter_action_name (string NAME)
     Returns numeric code of the reply action identified by NAME.  If
     NAME does not refer to a valid action, returns -1.


File: mailfromd.info,  Node: Mfmod Interface,  Prev: Informative Functions,  Up: Library

5.44 Mfmod Interface Functions
==============================

The calls described in this section provide interface for invoking
functions defined in a "dynamically loaded library" and retrieving their
return values.  For a detailed description of this interface and a
discussion of its use, see *note mfmod::.

 -- Built-in Function: number dlopen (FILENAME)
     Loads the dynamically loaded library FILENAME and returns a numeric
     handle that can be used to call functions from that library.

     Unless FILENAME is an absolute pathname, it will be looked up in
     "mfmod search path", defined by the configuration variable
     'runtime.mfmod-path' (*note mfmod-path::).

     Maximum number of dynamic libraries that can be loaded
     simultaneously is limited by the configuration variable
     'runtime.max-mfmods'.

     Once open, the library remains loaded until 'mailfromd' exits.
     There is no 'dlclose' function, since it is not deemed necessary
     (at the time of this writing, at least).  Therefore, the common
     practice is to call this function in a 'begin' section (*note
     begin/end::) and assign its return value to a global or static
     variable, which will then be used by further 'dl*' calls in this
     module.

 -- Built-in Function: any dlcall (number DLH, string SYMBOL, string
          TYPES, ...)
     Calls a "mfmod function" SYMBOL from the library identified by
     handle DLH (a value returned by a previous call to 'dlopen').  The
     TYPES parameter defines types of the remaining arguments.  It is a
     list of type letters, one for each argument:

     's'
          String value.

     'n'
     'd'
          Numeric value.

     'm'
          Message.

     An example usage:

          set dlh dlopen("mfmod_test.so")
          string x dlcall(dlh, "runtest", "sn", "input string", 3)

     This example calls the function 'runtest' from the 'mfmod_test.so'
     library with two arguments: a string 'input string' and numeric
     value '3' and assings the return value to the variable 'x'.

     Type of the return value from 'dlcall' is determined by the value
     of 'retval.type' upon return from the underlying library function.
     *Note Loadable Library::, for a detailed description.

     For more details about using this function, *note Interface
     Module::.


File: mailfromd.info,  Node: Using MFL Mode,  Next: Mailfromd Configuration,  Prev: Library,  Up: Top

6 Using the GNU Emacs MFL Mode
******************************

MFL sources are usual ASCII files and you may edit them with any editor
you like.  However, the best choice for this job (as well as for many
others) is, without doubt, GNU Emacs.  To ease the work of editing
script files, the 'mailfromd' package provides a special Emacs mode,
called "MFL mode".

   The elisp source file providing this mode, 'mfl-mode.el', is
installed automatically, provided that GNU Emacs is present on your
machine.  To enable the mode, add the following lines to your Emacs
setup file (either system-wide 'site-start.el', or your personal one,
'~/.emacs'):

     (autoload 'mfl-mode "mfl-mode")
     (setq auto-mode-alist (append auto-mode-alist
                            '(("\\.mfl$" . mfl-mode))))

   The first directive loads the MFL mode, and the second one tells
Emacs to apply it to any file whose name ends in '.mfl' suffix.

   MFL mode provides automatic indentation and syntax highlighting for
MFL sources.  The default indentation setup is the same as the one used
throughout this book:

   * Handler and function definitions start at column 1;
   * A block statement, i.e. 'do', 'done', 'if', 'else', 'elif' and
     'fi', occupies a line by itself, with the only exception that 'do'
     after an 'on' statement is located on the same line with it;
   * A 'do' statement that follows function or handler definition is
     placed in column 1.
   * Each subsequent level of nesting is indented two columns to the
     right (*note mfl-basic-offset::).
   * A closing statement ('done', 'else', 'elif', 'fi') is placed at the
     same column as the corresponding opening statement;
   * Branch statements ('case' and 'when') are placed in the same column
     as their controlling keyword ('switch' and 'on', correspondingly
     (*note mfl-case-line-offset::).
   * Loop substatements (*note Loops::) are offset 5 columns to the
     right from the controlling 'loop' keyword.  (*note
     mfl-loop-statement-offset::).  Continuation statements within loop
     header are offset 5 columns from the indentation of their
     controlling keyword, either 'for' or 'while' (*note
     mfl-loop-continuation-offset::).

   The mode provides two special commands that help navigate through the
complex filter scripts:

'C-M-a'
     Move to the beginning of current function or handler definition.

'C-M-e'
     Move to the end of current function or handler definition.

   Here, "current function or handler" means the one within which your
cursor currently stays.

   You can use 'C-M-e' repeatedly to walk through all function and
handler definitions in your script files.  Similarly, repeatedly
pressing 'C-M-a' will visit all the definitions in the opposite
direction (from the last up to the very first one).

   Another special command, 'C-c C-c', allows to verify the syntax of
your script file.  This command runs 'mailfromd' in syntax check mode
(*note Testing Filter Scripts::) and displays its output in a secondary
window, which allows to navigate through eventual diagnostic messages
and to jump to source locations described by them.

   All MFL mode settings are customizable.  To change any of them, press
'M-x customize' and visit 'Environment/Unix/Mfl' customization group.
This group offers two subgroups: 'Mfl Lint group' and 'Mfl Indentation
group'.

   'Mfl Lint group' controls invocation of mailfromd by 'C-c C-c'.  This
group contains two variables:

 -- MFL-mode setting: mfl-mailfromd-command
     The 'mailfromd' to be invoked.  By default, it is 'mailfromd'.  You
     will have to change it, if 'mailfromd' cannot be found using 'PATH'
     environment variable, or if you wish to pass it some special
     options.  However, do not include '--lint' or '-I' options in this
     variable.  The '--lint' option is given automatically, and include
     paths are controlled by 'mfl-include-path' variable (see below).

 -- MFL-mode setting: mfl-include-path
     A list of directories to be appended to 'mailfromd' include search
     path (*note include search path::).  By default it is empty.

   'Mfl Indentation group' controls automatic indentation of MFL
scripts.  This group contains the following settings:

 -- MFL-mode setting: mfl-basic-offset
     This variable sets the basic indentation increment.  It is set to
     2, by default, which corresponds to the following indentation
     style:

          prog envfrom
          do
            if $f = ""
              accept
            else
              ...
            fi
          done

 -- MFL-mode setting: mfl-case-line-offset
     Indentation offset for 'case' and 'when' lines, relative to the
     column of their controlling keyword.  The default is 0, i.e.:

          switch x
          do
          case 0:
            ...
          default:
            ...
          done

 -- MFL-mode setting: mfl-returns-offset
     Indentation offset of 'returns' and 'alias' statements, relative to
     the controlling 'func' keyword.  The default value is 2, which
     corresponds to:

          func foo()
            alias bar
            returns string

 -- MFL-mode setting: mfl-comment-offset
     Indentation increment for multi-line comments.  The default value
     is 1, which makes:

             /* first comment line
                second comment line */

 -- MFL-mode setting: mfl-loop-statement-offset
     Indentation increment for parts of a 'loop' statement.  The default
     value is 5, which corresponds to the following style:

          loop for STMT,
               while COND,
               INCR
          do

 -- MFL-mode setting: mfl-loop-continuation-offset
     If any of the 'loop' parts occupies several lines, the indentation
     of continuation lines relative to the first line is controlled by
     'mfl-loop-continuation-offset', which defaults to 5:

          loop for set n 0
                    set z 1,
               while n != 10
                    or z != 2,
               set n n + 1


File: mailfromd.info,  Node: Mailfromd Configuration,  Next: Invocation,  Prev: Using MFL Mode,  Up: Top

7 Configuring 'mailfromd'
*************************

Upon startup, 'mailfromd' checks if the file '/etc/mailfromd.conf'
exists.(1)  If it does, the program attempts to retrieve its
configuration settings from that file.

   The 'mailfromd.conf' file must be written in the "GNU mailutils
configuration format", as described in *note Configuration File Syntax:
(mailutils)conf-syntax.  This format can be summarized as follows:

Comments
     Inline comments begin with '//' or '#' and end at the end of the
     line.  Multiline comments are delimited by '/*' and '*/'.
     Multiline comments cannot be nested, but can contain inline comment
     markers.
Empty lines and whitespace
     Empty lines are ignored.  Whitespace characters (i.e.  horizontal,
     vertical space, and newline) are ignored, except as they serve to
     separate tokens.
Statements
     A statement consists of a keyword and a value, separated by
     whitespace.  Statements terminate with a semicolon.  E.g.

          pidfile /var/run/mailfromd.pid;
Block statements
     A block statement consists of a keyword and a list of statements
     enclosed in '{' and '}' characters.  Optional "label" can appear
     between the keyword and opening curly brace.  E.g.:

          logging {
            syslog on;
            facility mail;
          }

     Block statement is not required to terminate with a semicolon,
     although it is allowed to.
File Inclusion
     The 'include' statement causes inclusion of the file listed as its
     value:

          include /usr/share/mailfromd/config.inc;

   The 'mailfromd.conf' file is used by all programs that form the
'mailfromd' package, i.e.  'mailfromd', 'calloutd', 'mfdbtool', and
'pmult'.  Since the sets of statements understood by each of them
differ, special syntactic means are provided to separate
program-specific configurations from each other.

   First of all, if the argument to 'include' is a directory, then the
program will search that directory for a file with the same name as the
base name of the program itself.  If found, this file will be loaded
after finishing parsing the 'mailfromd.conf' file.  Otherwise, this
statement is ignored.

   Secondly, the special block statement 'program TAG' is processed only
if TAG matches the base name of the program being run.  Again, it is
processed after the main 'mailfromd.conf' file.

   Thus, if you need to provide configuration for the 'calloutd'
component, there are two ways of doing so.  First, you can place it to a
file named 'calloutd' placed in a separate directory (say,
'/etc/mailfromd.d'), and use the name of that directory in a 'include'
statement in the main configuration file:

     include /etc/mailfromd.d;

   Secondly, you can use the 'program' statement as follows:

     program calloutd {
       ...
     }

* Menu:

* conf-types::        Special Configuration Data Types
* conf-base::         Base Mailfromd Configuration
* conf-preprocessor:: Preprocessor Configuration
* conf-resolver::     DNS Resolver Configuration
* conf-server::       Server Configuration
* conf-milter::       Milter Connection Configuration
* conf-debug::        Logging and Debugging configuration
* conf-timeout::      Timeout Configuration
* conf-callout::      Call-out Configuration
* conf-priv::         Privilege Configuration
* conf-database::     Database Configuration
* conf-runtime::      Runtime Constants
* conf-mailutils::    Standard Mailutils Statements

   ---------- Footnotes ----------

   (1) The exact location is determined at compile time: the '/etc'
directory is the system configuration directory set when building
'mailfromd' (*note Building::).


File: mailfromd.info,  Node: conf-types,  Next: conf-base,  Up: Mailfromd Configuration

7.1 Special Configuration Data Types
====================================

In addition to the usual data types (*note (mailutils)Statements::),
'mailfromd' configuration introduces the following two special ones:

time interval specification
     The "time interval specification" is a string that defines an
     interval, much the same way we do this in English: it consists of
     one or more pairs 'number'-'time unit'.  For example, the following
     are valid interval specifications:

          1 hour
          2 hours 35 seconds
          1 year 7 months 2 weeks 2 days 11 hours 12 seconds

     The pairs can occur in any order, however unusual it may sound to a
     human ear, e.g. '2 days 1 year'.  If the 'time unit' is omitted,
     seconds are supposed.

Connection URL
     unix://FILE
     unix:FILE
     local://FILE
     local:FILE
          A named pipe (socket).

     inet://ADDRESS:PORT
     inet:PORT@ADDRESS
          An IPv4 connection to host ADDRESS at port PORT.  Port must be
          specified either as a decimal number or as a string
          representing the port name in '/etc/services'.

     inet6:PORT@ADDRESS
          An IPv6 connection to host ADDRESS at port PORT.  This port
          type is not yet supported.


File: mailfromd.info,  Node: conf-base,  Next: conf-preprocessor,  Prev: conf-types,  Up: Mailfromd Configuration

7.2 Base Mailfromd Configuration
================================

 -- Mailfromd Conf: script-file file
     Read filter script from FILE.  By default it is
     'SYSCONFDIR/mailfromd.mfl'.

 -- Mailfromd Conf: setvar name value
     Initialize MFL variable NAME to VALUE.

 -- Mailfromd Conf: include-path path
     Add directories to the list of directories to be searched for
     include files.  *Note include search path::.

     Argument is a list of directory names separated by colons.  These
     names are added to the search path before the default search path
     directories, but after any directories added by the previous
     'include-path' statements, if any.

     *Note --include-path::

 -- Mailfromd Conf: module-path path
     Add directories to the module search path *Note module search
     path::.

     Argument is a list of directory names separated by colons.  These
     names are added to the search path before the default search path
     directories, but after any directories added by the previous
     'module-path' statements, if any.

     *Note --module-path::

 -- Mailfromd Conf: state-directory dir
     Set program state directory.  *Note statedir::.

 -- Mailfromd Conf: relayed-domain-file file
     Append domain names from the named FILE to the list of relayed
     domains.  This list can be inspected from MFL script using the
     'relayed' function (*note relayed::).

     The FILE argument is either a single file name or a list of file
     names, e.g.:

          relayed-domain-file /etc/mail/sendmail.cw;
          relayed-domain-file (/etc/mail/sendmail.cw, /etc/mail/relay-domains);

 -- Mailfromd Conf: source-ip ipaddr
     Set source IP address for outgoing TCP connections.

 -- Mailfromd Conf: pidfile file
     Set the name of the file to store PID value in.  The file must be
     writable for the user or group 'mailfromd' runs as (*note
     conf-priv::).


File: mailfromd.info,  Node: conf-preprocessor,  Next: conf-resolver,  Prev: conf-base,  Up: Mailfromd Configuration

7.3 Preprocessor Configuration
==============================

MFL preprocessor is configured using the 'preprocessor' statement
(default values shown):

     preprocessor {
       # Enable preprocessor
       enable yes;
       # Preprocessor command line stub.
       command "m4 -s";
       # Pass current include path to the preprocessor via -I options.
       pass-includes false;
       # Pass to the preprocessor the feature definitions via -D options
       # as well as any -D/-U options from the command line
       pass-defines true;
       # Name of the preprocessor setup file.  Unless absolute, it is
       # looked up in the include path.
       setup-file "pp-setup";
     }

   Its substatements are:

 -- preprocessor: enable BOOL
     Enable or disable preprocessor.  If disabled in configuration file,
     preprocessor can be forcefully enabled in the command line by using
     the '--preprocessor' option.

 -- preprocessor: command STRING
     The stub for the preprocessor command line.  Before use, it will be
     eventually augmented by '-I' and '-D' options, depending on the
     'pass-includes' and 'pass-defines' settings in this section.  This
     is described in detail in *note Configuring Preprocessor::.

 -- preprocessor: pass-includes BOOL
     If 'true' the directories from include search path (*note include
     search path:: will be passed to the preprocessor via the '-I'
     options.

 -- preprocessor: pass-defines BOOL
     If 'true', the -D options defining optional features will be passed
     to the preprocessor (e.g.  '-DWITH_DKIM'), as well as any '-D' and
     '-U' options from the 'mailfromd' command line.

 -- preprocessor: setup-file STRING
     Name of the preprocessor setup file (*note Preprocessor::).  Unless
     STRING begins with a slash, the file with this name is looked up in
     the current include search path (*note include search path::).  If
     found, its absolute name is passed to the preprocessor as its first
     argument.

     If STRING begins with a slash, it is passed to the preprocessor as
     is.


File: mailfromd.info,  Node: conf-resolver,  Next: conf-server,  Prev: conf-preprocessor,  Up: Mailfromd Configuration

7.4 DNS Resolver Configuration
==============================

DNS resolver settings are configured using the 'resolver' compound
statement:

     resolver {
       config FILENAME;
       max-cname-chain NUM;
     }

 -- resolver: config FILENAME
     Name of the resolver configuration file to use, instead of the
     default '/etc/resolv.conf'.

 -- resolver: max-cname-chain NUM
     Maximum allowed length of a DNS "CNAME chain" that will be
     followed.  A "CNAME chain" is a sequence of 'CNAME' records
     pointing to another 'CNAME's.  Although CNAME chains are not
     considered a good practice, many sites still use them.  By default
     the 'mailfromd' resolver allows at most one CNAME record pointing
     to a CNAME (this corresponds to 'max-cname-chain 2').  If you need
     to follow longer chains, raise this value.  Note however, that
     using values greater than 5 is not a good idea, anyway.


File: mailfromd.info,  Node: conf-server,  Next: conf-milter,  Prev: conf-resolver,  Up: Mailfromd Configuration

7.5 Server Configuration
========================

A single 'mailfromd' daemon can run several "servers".  These are
configured in the following statement:

     server TYPE {
       id NAME;
       listen URL;
       backlog NUM;
       max-instances NUM;
       single-process BOOL;
       reuseaddr BOOL;
       option LIST;
       default BOOL;
       callout URL;
       acl { ... }
     }

 -- Mailfromd Conf: server TYPE
     Define a server.  The TYPE is either 'milter' or 'callout'.  *Note
     SMTP Timeouts::, for a description of various types of servers.

     The substatements in the 'server' block provide parameters for
     configuring this server.

 -- server: id name
     Assign an identifier to this server.  This identifier is used as a
     suffix to syslog tag (*note syslog tag::) in messages related to
     this server.  For example, if a server block had the following
     statement in it:

          id main;

     then all messages related to this server will be marked with tag
     'mailfromd#main'.

     The part before the '#' is set using the 'tag' statement in
     'logging' block (*note Mailutils Configuration File:
     (mailutils)Logging Statement.).

     This identifier can be inspected from the MFL code using the
     'milter_server_id' variable (*note milter_server_id::).

 -- server: listen url
     Listen for connections on the given URL.  *Note milter port
     specification::, for a description of allowed URL formats.

     Example:

          listen inet://10.10.10.1:3331;

     The MFL code can access its address using the
     'milter_server_address' and 'milter_server_family' variables.

 -- server: backlog NUM
     Configures the size of the queue of pending connections.  Default
     value is 8.

 -- server: max-instances number
     Sets the maximum number of instances allowed for this server.

 -- server: single-process bool
     When set to 'yes', this server will run in "single-process" mode,
     i.e.  it will not fork sub-processes to serve requests.  This
     option is meant exclusively to assist in debugging 'mailfromd'.
     Don't use it for anything else but for debugging!

 -- server: reuseaddr bool
     When set to 'yes', 'mailfromd' will attempt to reuse existing
     socket addresses.  This is the default behavior.

   If the server TYPE is 'callout', the following statement is also
allowed:

 -- server: option LIST
     Configures server options.  As of version 9.1 only one option is
     defined:

     default
          Mark this server as the default one.  This means it will be
          used by every milter server that doesn't define the
          'callout-url' statement.

 -- server: default bool
     When set to 'yes', this server is marked as a default callout
     server for all milter servers declared in the configuration.  This
     is equivalent to 'option default'.

   if the server TYPE is 'milter', you can use the following statement
to query a remote callout server:

 -- server: callout url
     Use a callout server at URL (*note milter port specification::).

   You can also set a "global callout server", which will be used by all
milter servers that do not set the 'callout' statement:

 -- Mailfromd Conf: callout-url url
     Set global callout server.  *Note milter port specification::, for
     allowed URL formats.


File: mailfromd.info,  Node: conf-milter,  Next: conf-debug,  Prev: conf-server,  Up: Mailfromd Configuration

7.6 Milter Connection Configuration
===================================

 -- Mailfromd Conf: milter-timeout time
     Sets the timeout value for connection between the filter and the
     MTA.  Default value is 7210 seconds.  You normally do not need to
     change this value.

 -- Mailfromd Conf: acl
     This block statement configures "access control list" for incoming
     Milter connections.  *Note Mailutils Configuration File:
     (mailutils)ACL Statement, for a description of its syntax.  E.g.:

          acl {
            allow from 10.10.10.0/24;
            deny from any;
          }


File: mailfromd.info,  Node: conf-debug,  Next: conf-timeout,  Prev: conf-milter,  Up: Mailfromd Configuration

7.7 Logging and Debugging configuration
=======================================

 -- Mailfromd Conf: logger mech
     Set default logger mechanism.  Allowed values for MECH are:

     stderr
          Log everything to the standard error.

     syslog
          Log to syslog.

     syslog:async
          Log to syslog using the asynchronous syslog implementation.

     *Note Logging and Debugging::, for a detailed discussion.  See also
     *note Using non-blocking syslog: syslog-async, for information on
     how to set default syslog implementation at compile time.

 -- Mailfromd Conf: debug spec
     Set 'mailfromd' debug verbosity level.  The SPEC must be a valid
     debugging level specification (*note debugging level
     specification::).

 -- Mailfromd Conf: stack-trace bool
     Enables stack trace dumps on runtime errors.  This feature is
     useful for locating the source of an error, especially in complex
     scripts.  *Note tracing runtime errors::, for a detailed
     description.

 -- Mailfromd Conf: trace-actions bool
     Enable action tracing.  If BOOL is 'true', 'mailfromd' will log all
     executed actions.  *Note Logging and Debugging::, for a detailed
     description of action tracing.

 -- Mailfromd Conf: trace-program modlist
     Enable program instruction tracing for modules in MODLIST, a
     comma-separated list of source code modules, e.g.:

          trace-program (bi_io,bi_db);

     This statement enables tracing for functions from modules 'bi_io.c'
     and 'bi_db.c' (notice, that you need not give file suffixes).

     This tracing is useful for debugging 'mailfromd', but is not
     advised to use otherwise, since it is very time-costly.

 -- Mailfromd Conf: transcript bool
     Enable transcripts of call-out SMTP sessions.  *Note SMTP
     transcript::, for a detailed description of SMTP transcripts.


File: mailfromd.info,  Node: conf-timeout,  Next: conf-callout,  Prev: conf-debug,  Up: Mailfromd Configuration

7.8 Timeout Configuration
=========================

The SMTP timeouts used in callout sessions are configured via
'smtp-timeout' statement:

Syntax
------

     smtp-timeout TYPE {
       connection INTERVAL;
       initial-response INTERVAL;
       helo INTERVAL;
       mail INTERVAL;
       rcpt INTERVAL;
       rset INTERVAL;
       quit INTERVAL;
     }

 -- Mailfromd Conf: smtp-timeout TYPE
     Declare SMTP timeouts of the given TYPE, which may be 'soft' or
     'hard'.

     Callout SMTP sessions initiated by polling functions, are
     controlled by two sets of timeouts: 'soft' and 'hard'.  "Soft
     timeouts" are used by the mailfromd milter servers.  "Hard
     timeouts" are used by callout servers (*note callout server::).
     When a soft timeout is exceeded, the calling procedure is delivered
     an 'e_temp_failure' exception and the session is scheduled for
     processing by a callout server.  The latter re-runs the session
     using hard timeouts.  If a hard timeout is exceeded, the address is
     marked as 'not_found' and is stored in the cache database with that
     status.

     Normally, soft timeouts are set to shorter values, suitable for use
     in MFL scripts without causing excessive delays.  Hard timeouts are
     set to large values, as requested by RFC 2822 and guarantee
     obtaining a definite answer (see below for the default values).

Statements
----------

The TIME argument for all 'smtp-timeout' sub-statements is expressed in
time interval units, as described in *note time interval
specification::.

 -- smtp-timeout: connection time
     Sets initial connection timeout for callout tests.  If the
     connection is not established within this time, the corresponding
     callout function returns temporary failure.

 -- smtp-timeout: initial-response time
     Sets the time to wait for the initial SMTP response.

 -- smtp-timeout: helo time
     Timeout for a response to 'HELO' (or 'EHLO') command.

 -- smtp-timeout: mail time
     Timeout for a response to 'MAIL' command.

 -- smtp-timeout: rcpt time
     Timeout for a response to 'RCPT' command.

 -- smtp-timeout: rset time
     Timeout for a response to 'RSET' command.

 -- smtp-timeout: quit time
     Timeout for a response to 'QUIT' command.

Default Values
--------------

The default timeout settings are:


Timeout                              Soft               Hard
---------------------------------------------------------------------------
connection                           10s                5m
initial-response                     30s                5m
helo                                 I/O                5m
mail                                 I/O                10m
rcpt                                 I/O                5m
rset                                 I/O                5m
quit                                 I/O                2m

Table 7.1: Default SMTP timeouts

 -- Mailfromd Conf: io-timeout time
     Sets a general SMTP I/O operation timeout.  This timeout is used as
     the default for entries marked with 'I/O' in the above table.  The
     default is 3 seconds.


File: mailfromd.info,  Node: conf-callout,  Next: conf-priv,  Prev: conf-timeout,  Up: Mailfromd Configuration

7.9 Call-out Configuration
==========================

 -- Mailfromd Conf: ehlo-domain string
     Sets default domain used in 'EHLO' (or 'HELO') SMTP command when
     probing the remote host.  This value can be overridden by 'from'
     parameter to 'poll' command (*note poll::).

     This statement assigns the value STRING to the 'ehlo_domain'
     variable (*note ehlo_domain::), and is therefore equivalent to

          setvar ehlo_domain STRING;

 -- Mailfromd Conf: mail-from-address string
     Sets default email addresses used in 'MAIL FROM:' SMTP command when
     probing the remote host.  This value can be overridden by 'as'
     parameter to 'poll' command (*note poll::).

     This statement assigns the value STRING to the 'mailfrom_address'
     variable (*note mailfrom_address::), and is therefore equivalent to

          setvar mailfrom_address STRING;

 -- Mailfromd Conf: enable-vrfy bool
     Enables the use of SMTP VRFY statement prior to normal callout
     sequence.  If VRFY is supported by the remote server, 'mailfromd'
     relies on its reply and does not perform normal callout.

     The use of this statement is not recommended, because many existing
     VRFY implementations always return affirmative result, no matter is
     the requested email handled by the server or not.

     The default is 'enable-vrfy no', i.e. VRFY is disabled.

 -- Mailfromd Conf: smtp-starttls string
     Configures whether to issue the 'STARTTLS' command if the mail
     server offers such capability.  Allowed values are:

      -- smtp-starttls value: never
          Never use 'STARTTLS'.

      -- smtp-starttls value: always
          Always use 'STARTTLS' if supported by the server.

      -- smtp-starttls value: ondemand
          Use 'STARTTLS' only if 'MAIL FROM:' command failed with the
          code 530 (Authorization required).

     Default is 'ondemand'.

     Notice, that the 'smtp-starttls' feature depends on whether GnuTLS
     support is available in 'libmailutils'.  You can check whether it
     is available by inspecting the output of 'mailfromd
     --show-defaults' (*note Examining Defaults::): if so, the 'optional
     features' line will contain the word 'STARTTLS'.

 -- Mailfromd Conf: tls { ... }
     Configures TLS settings for the callout.  This is a compound
     statement.  The two most important statements in this compound are:

      -- Mailfromd TLS: ssl-priorities STRING
          Configures the TLS session's handshake algorithms and options
          in a compact, easy-to-use format.  *Note (GnuTLS)Priority
          strings::, for a detailed description of the priority string
          format.

          Default value is 'NORMAL:%COMPAT'.  You may need to adjust it
          in order to work with older or misconfigured servers, e.g.:

               tls {
                 ssl-priorities "LEGACY:%COMPAT";
               }

      -- Mailfromd TLS: handshake-timeout N
          Sets the timeout for TLS handshake to N seconds.

     The remaining three statements are not of much importance for
     callout.  They are described here for completeness sake:

      -- Mailfromd TLS: ssl-ca-file file
          Specifies the pathname of the certificate authority file.

      -- Mailfromd TLS: ssl-certificate-file file
          Specifies the pathname of the certificate file.

      -- Mailfromd TLS: ssl-key-file file
          Specifies the pathname of the certificate key file.


File: mailfromd.info,  Node: conf-priv,  Next: conf-database,  Prev: conf-callout,  Up: Mailfromd Configuration

7.10 Privilege Configuration
============================

 -- Mailfromd Conf: user name
     Switch to this user's privileges after startup.  *Note Starting and
     Stopping::, for a discussion of the privileges 'mailfromd' runs
     under and the options that affect them.  See also 'group' below.

 -- Mailfromd Conf: group name
     Retain the supplementary group NAME when switching to user
     privileges.  By default 'mailfromd' clears the list of
     supplementary groups when switching to user privileges, but this
     statement allows to retain the given group.  It can be specified
     multiple times to retain several groups.  This option may be
     necessary to maintain proper access rights for various files.
     *Note Starting and Stopping::.


File: mailfromd.info,  Node: conf-database,  Next: conf-runtime,  Prev: conf-priv,  Up: Mailfromd Configuration

7.11 Database Configuration
===========================

Syntax
------

     database DBNAME {
       file NAME;
       enable BOOL;
       expire-interval INTERVAL;
       positive-expire-interval INTERVAL;
       negative-expire-interval INTERVAL;
     }

 -- Mailfromd Conf: database dbname
     The 'database' statement controls run-time parameters of the DBM
     database identified by DBNAME.  Allowed values for the latter are:

     'cache'
          Callout cache database.

     'rate'
          Sending rate database.  *Note Sending Rate::.

     'tbf'
          Token-bucket filter database.  *Note TBF::.

     'greylist'
          Greylisting database.  *Note Greylisting::.

Statements
----------

 -- database: file name
     Set the database file name.  The file name can be prefixed with the
     "DBM scheme", which indicates the desired DBM implementation to
     use.  The scheme consists of DBM type name followed by a colon and
     two slashes.  To obtain the list of available DBM types, run
     'mailfromd --show-defaults' (*note Examining Defaults::) and
     examine the 'supported database types:' line, e.g.:

          $ mailfromd --show-defaults | grep '^supported database types:'
          supported database types:gdbm, bdb

     If no DBM scheme is supplied, the default DBM implementation will
     be used.  The default implementation is set up on compile time and
     can be inspected using the following command:

          mailfromd --show-defaults | grep '^default database type:'

     The part that follows DBM scheme can be either absolute or relative
     file name.  Absolute file name (begining with '/') is used
     verbatim.  Relative file name (i.e.  the name that begins with any
     character except '/') is modified by prepending it with the
     mailfromd local state directory (*note statedir::).

     To illustrate the above, suppose that the local state directory is
     '/var/mailfromd' and default DBM implementation is 'gdbm'.  Then:

     Value of 'file'        Resulting filename     DBM type
     -------------------------------------------------------------
     'tbf.db'               /var/mailfromd/tbf.db  GDBM
     'bdb://tbf.db'         /var/mailfromd/tbf.db  Berkeley DB
     '/run/tbf.db'          /run/tbf.db            GDBM
     'bdb:///run/tbf.db'    /run/tbf.db            Berkeley DB

 -- database: enable bool
     Enable or disable this database.

 -- database: expire-interval time
     Set the expiration interval for this database DBNAME.  *Note time
     interval specification::, for a description of TIME format.

 -- database: positive-expire-interval time
     This statement is valid only for 'cache' database.  It sets the
     expiration interval for positive ('success') cache entries.

 -- database: negative-expire-interval time
     This statement is valid only for 'cache' database, where it sets
     expiration interval for negative ('not_found') cache entries.

Additional Statements
---------------------

 -- Mailfromd Conf: database-type TYPE
     Set default database type.  TYPE is one of the database types
     supported by mailutils (i.e., for Mailutils 3.0: 'gdbm', 'ndbm',
     'bdb', 'kc', and 'tc').  Run

          mailfromd --show-defaults | grep 'supported databases:'

     to get a list of type names supported by your build of 'mailfromd'
     (*note Examining Defaults::).

 -- Mailfromd Conf: database-mode MODE
     Defines file mode for newly created database files.  MODE must be a
     valid file mode in octal.


File: mailfromd.info,  Node: conf-runtime,  Next: conf-mailutils,  Prev: conf-database,  Up: Mailfromd Configuration

7.12 Runtime Constants Configuration
====================================

 -- Mailfromd Conf: runtime { statements }
     The statements in the 'runtime' section configure various values
     used by MFL builtin functions.

 -- runtime: max-streams number
     Sets the maximum number of stream descriptors that can be opened
     simultaneously.  Default is 1024.  *Note I/O functions::.

 -- runtime: max-open-mailboxes number
     Sets the maximum number of available mailbox descriptors.  This
     value is used by MFL mailbox functions (*note Mailbox functions::).

 -- runtime: max-open-messages number
     Sets the maximum number of messages that can be opened
     simultaneously using the 'mailbox_get_message' function.  *Note
     Message functions::, for details.

 -- runtime: max-mfmods number
     Maximum number of dynamically loaded modules ("mfmod"s) that can be
     used simultaneously.  *Note mfmod::.

 -- runtime: mfmod-path string
     Search path for dynamically loaded modules - a colon-delimited list
     of directories where to look for libraries loaded using the
     'dlopen' function (*note dlopen::).  Default 'mfmod-path' value is
     'PREFIX/mailfromd/lib', where PREFIX is the installation prefix
     directory.  *Note mfmod::, for details.


File: mailfromd.info,  Node: conf-mailutils,  Prev: conf-runtime,  Up: Mailfromd Configuration

7.13 Standard Mailutils Statements
==================================

The following standard Mailutils statements are understood:

Statement              Reference
-------------------------------------------------------------------
auth                   *Note Mailutils Configuration File: (mailutils)auth statement.
debug                  *Note Mailutils Configuration File: (mailutils)debug statement.
include                *Note Mailutils Configuration File: (mailutils)include.
logging                *Note Mailutils Configuration File: (mailutils)logging statement.
mailer                 *Note Mailutils Configuration File: (mailutils)mailer statement.
locking                *Note Mailutils Configuration File: (mailutils)locking statement.


File: mailfromd.info,  Node: Invocation,  Next: MTA Configuration,  Prev: Mailfromd Configuration,  Up: Top

8 'Mailfromd' Command Line Syntax
*********************************

The 'mailfromd' binary is started from the command line using the
following syntax (brackets indicate optional parts):

     $ mailfromd [OPTIONS] [ASGN] [SCRIPT]

The meaning of each invocation part is described in the table below:

OPTIONS
     The command line options (*note options::).

ASGN
     Sendmail macro assignments.  These are currently meaningful only
     with the '--test' option (*note test mode::), but this may change
     in the future.  Each assignment has the form:

          VAR=VALUE

     where VAR is the name of a Sendmail macro and VALUE is the value to
     be assigned to it.

SCRIPT
     The file name of the filter script, if other than the default one.

* Menu:

* options::                     Command Line Options.
* Starting and Stopping::       How to Start and Shut Down the Daemon.


File: mailfromd.info,  Node: options,  Next: Starting and Stopping,  Up: Invocation

8.1 Command Line Options.
=========================

* Menu:

* Operation Modifiers::
* General Settings::
* Preprocessor Options::
* Timeout Control::
* Logging and Debugging Options::
* Informational Options::


File: mailfromd.info,  Node: Operation Modifiers,  Next: General Settings,  Up: options

8.1.1 Operation Modifiers
-------------------------

'--daemon'
     Run in daemon mode (default).

'--run[=START]'
     Load the script named in the command line and execute the function
     named START, or 'main', if START is not given.

     The full invocation syntax for this mode is:

          mailfromd [OPTIONS] --run[=START \
                    [MACRO=VALUE] [FILE ARGS...]

     Its parts are:

     OPTIONS
          Any mailfromd options needed to tune its behavior.

     START
          Start symbol.  Defaults to 'main'.

     MACRO=VALUE
          Any number of Sendmail macro definitions.  MACRO is the macro
          name and VALUE is the value to initialize it to.

     FILE
          Script file name.  If omitted, default script file will be
          used (*note default script file::).

     ARGS...
          Command line arguments that will be pased to the START MFL
          function as parameters.

     The START function must be defined as:

          func START (...) returns number

     *Note Run Mode::, for a detailed discussion of this feature.

'-t[STATE]'
'--test[=STATE]'
     Run in test mode.  *Note Testing Filter Scripts::.  Default STATE
     is 'envfrom'.  This option implies '--stderr' (*note --stderr::).


File: mailfromd.info,  Node: General Settings,  Next: Preprocessor Options,  Prev: Operation Modifiers,  Up: options

8.1.2 General Settings
----------------------

'--callout-socket=STRING'
     Set socket for the default callout server.  This is mainly useful
     together with the '--mtasim' option.

'--echo'
'--echo=FILE'
     This option controls where the output of the 'echo' statement
     (*note Echo::) goes when 'mailfromd' is run in test mode, i.e.
     using the '--test' or '--run' options (*note Testing Filter
     Scripts::).

     When used without argument it directs the 'echo' output to the
     standard output stream.  If an argument is supplied (second form
     above), the output goes to the named file.  The file will be
     created if it doesn't exist.  Notice, that the use of '=' is
     compulsory when specifying an argument.  A single dash as a
     filename means standard output, so that '--echo=-' and '--echo' are
     equivalent.

'--foreground'
     Stay in foreground.  When given this option, 'mailfromd' will not
     disconnect itself from the controlling terminal and will run in the
     foreground.

'-g NAME'
'--group=NAME'

     Retain the group NAME when switching to user privileges.  *Note
     Starting and Stopping::.

     This option complements the 'group' configuration statement (*note
     group: conf-priv.).

'--include-path=DIR'
'-I DIR'
     Add the directory DIR to the list of directories to be searched for
     include files (*note include search path::).  This setting affects
     the '#include' statement (*note include::) and is used to locate
     preprocessor setup file (*note Preprocessor::).  If enabled in the
     configuration (*note pass-includes: conf-preprocessor.),
     directories from the include search path will also be added to the
     preprocessor command line as additional '-I' options.

'--module-path=DIR'
'-P DIR'
     Add the directory DIR to the list of directories searched for MFL
     module files (*note module search path::).  This setting affects
     the 'require' and 'import' MFL statements.  *Note Modules::.

'--mailer=URL'
'-M URL'
     Set the URL of the mailer to use.  *Note Mail Sending Functions::.

'--mtasim'
     This option is reserved for use by 'mtasim' (*note mtasim::).

'-O[LEVEL]'
'--optimize[=LEVEL]'
     Set optimization level for code generator.  Two levels are
     implemented: '0', meaning no optimization, and '1', meaning full
     optimization.

'-p STRING'
'--port=STRING'
'--milter-socket=STRING'
     Set communication socket.  Overrides the 'listen' configuration
     statement, which you are advised to use instead (*note listen:
     conf-server.).

'--pidfile=FILE'
     Set pidfile name.  Overrides the 'pidfile' configuration statement,
     which you are advised to use instead (*note pidfile: conf-base.).

'--relayed-domain-file=FILE'
     Read relayed domains from FILE.  Overrides the
     'relayed-domain-file' configuration statement (*note
     relayed-domain-file: conf-base.), which you are advised to use
     instead.  *Note Avoiding Verification Loops::, and the description
     of 'relayed' function (*note relayed::) for more information.

'--resolv-conf-file=FILE'
     Read resolver settings from FILE, instead of the default
     '/etc/resolv.conf'.

'--state-directory=DIR'
     Set new program state directory.  *Note Local state directory:
     statedir, for the description of this directory and its purposes.
     This option overrides the settings of 'state-directory'
     configuration statement, described in *note state-directory:
     conf-base.

'-S IP'
'--source-ip=IP'
     Set source address for TCP connections.  Overrides the 'source-ip'
     configuration statement, which you are advised to use instead
     (*note source-ip: conf-base.).

'-u NAME'
'--user NAME'
     Switch to this user privileges after startup.  Overrides the 'user'
     configuration file statement, which you are advised to use instead
     (*note user: conf-priv.).  Default user is 'mail'.

'-v VAR=VALUE'
'--variable VAR=VALUE'
     Assign VALUE to the global variable VAR.  The variable must be
     declared in your startup script.  *Note overriding initial
     values::, for a detailed discussion of this option.


File: mailfromd.info,  Node: Preprocessor Options,  Next: Timeout Control,  Prev: General Settings,  Up: options

8.1.3 Preprocessor Options
--------------------------

Following command line options control the preprocessor feature.  *Note
Preprocessor::, for a detailed discussion of these.

'--no-preprocessor'
     Do not run the preprocessor.

'--preprocessor[=COMMAND]'
     If COMMAND is supplied, use it as the external preprocessor instead
     of the default.  This overrides the 'preprocessor.command' setting
     (*note conf-preprocessor::).

     If used without arguments, forces the use of the configured
     preprocessor.  This form is intended as a way to manually enable
     the use of the preprocessor if disabled in the configuration.

'-D NAME[=VALUE]'
'--define=NAME[=VALUE]'
     Define a preprocessor symbol NAME to have a value VALUE.  This
     option is ignored if the 'preprocessor.pass-defines' configuration
     setting is 'false'.

'-U NAME'
'--undefine=NAME'
     Undefine the preprocessor symbol NAME.  This option is ignored if
     the 'preprocessor.pass-defines' configuration setting is 'false'.

'-E'
     Stop after the preprocessing stage; do not run the compiler proper.
     The output is in the form of preprocessed source code, which is
     sent to the standard output.


File: mailfromd.info,  Node: Timeout Control,  Next: Logging and Debugging Options,  Prev: Preprocessor Options,  Up: options

8.1.4 Timeout Control
---------------------

*Note time interval specification::, for information on INTERVAL format.

'--milter-timeout=INTERVAL'
     Set MTA connection timeout.  Overrides 'milter-timeout' statement
     in the 'mailfromd' configuration file, which you are advised to use
     instead (*note milter-timeout: conf-milter.).


File: mailfromd.info,  Node: Logging and Debugging Options,  Next: Informational Options,  Prev: Timeout Control,  Up: options

8.1.5 Logging and Debugging Options
-----------------------------------

'--location-column'
'--no-location-column'
     Mention column number in error messages.  *Note location-column:
     Testing Filter Scripts.  Use '--no-location-column' to disable

'-d STRING'
'--debug=STRING'
     Set debugging level.  *Note Logging and Debugging::.

'--dump-code'
     Parse and compile the script file and dump the disassembled listing
     of the produced code to the terminal.  *Note Logging and
     Debugging::.

'--dump-grammar-trace'
     Enable debugging the script file parser.  While parsing the file,
     the detailed dump of the parser states and tokens seen will be
     output.

'--dump-lex-trace'
     Enable debugging the lexical analyzer.  While parsing the script
     file, the detailed dump of the lexer states and matched rules will
     be output.

'--dump-macros'
     Show Sendmail macros used in the script file.  The macro names are
     displayed as comma-separated lists, grouped by handler names.
     *Note Sendmail::, for a detailed description of this option and its
     usage.

'--dump-tree'
     Parse and compile the script file and dump the parse tree in a
     printable form to the terminal.

'--dump-xref'
     Print a cross-reference of variables used in the filter script.
     *Note Testing Filter Scripts::.

'-E'
     Stop after the preprocessing stage; do not run the compiler proper.
     The output is in the form of preprocessed source code, which is
     sent to the standard output.  *Note Preprocessor::.

'--lint'
     Check script file syntax and exit.  If the file is OK, return 0 to
     the shell, otherwise print appropriate messages to stderr and exit
     with code 78 ('configuration error').

'--single-process'
     Do not fork sub-processes to serve requests.  This option is meant
     to assist in debugging 'mailfromd'.  Don't use it for anything else
     but for debugging, as it terribly degrades performance!

'--stack-trace'
'--no-stack-trace'
     Add MFL stack trace information to runtime error output.  Overrides
     'stack-trace' configuration statement.  Use the '--no-stack-trace'
     to disable trace information.

     *Note tracing runtime errors::, for more information on this
     feature.

'--gacopyz-log=LEVEL'
     Set desired logging level for 'gacopyz' library (*note Gacopyz::).
     There are five logging levels.  The following table lists them in
     order of decreasing priority:

     fatal
          Log fatal errors.

     err
          Log error messages.

     warn
          Log warning messages.

     info
          Log informational messages.  In particular, this enables
          printing messages on each subprocess startup and termination,
          which look like that:

               Apr 28 09:00:11 host mailfromd[9411]: connect
                   from 192.168.10.1:50398
               Apr 28 09:00:11 host mailfromd[9411]: finishing
                   connection

          This level can be useful for debugging your scripts.

     debug
          Log debugging information.

     proto
          Log Milter protocol interactions.  This level prints huge
          amounts of information, in particular it displays dumps of
          each Milter packet sent and received.

     Although it is possible to set these levels independently of each
     other, it is seldom practical.  Therefore, the option
     '--gacopyz-log=LEVEL' enables all logging levels from LEVEL up.
     For example, '--gacopyz-log=warn' enables log levels 'warn', 'err'
     and 'fatal'.  It is the default.  If you need to trace each
     subprocess startup and shutdown, set '--gacopyz-log=info'.  Setting
     the logging level to 'proto' can be needed only for 'Gacopyz'
     developers, to debug the protocol.

     *Note Testing Filter Scripts::.

'--logger=MECH'
     Set logger mechanism (MECH is one of 'stderr', 'syslog',
     'syslog:async').  *Note Logging and Debugging::.

'--log-facility=FACILITY'
     Output logs to syslog FACILITY.

'--log-tag=STRING'
     Tag syslog entries with the given STRING, instead of the program
     name.

'--source-info'
'--no-source-info'
     Include C source information in debugging messages.  This is
     similar to setting 'line-info yes' in the 'debug' configuration
     block (*note line-info: (mailutils)debug statement.).

     The '--no-source-info' can be used to cancel the effect of the
     'line-info yes' configuration statement.

     You do not need this option, unless you are developing or debugging
     'mailfromd'.

'--syntax-check'
     Synonym for '--lint'.

'--trace'
'--no-trace'
     Enable or disable action tracing.  If '--trace' is given,
     'mailfromd' will log all executed actions.  *Note Logging and
     Debugging::.

'--trace-program[=STRING]'
     Enable program instruction tracing.  With this option 'mailfromd'
     will log execution of every instruction in the compiled filter
     program.  The optional arguments allows to specify a
     comma-separated list of source code modules for which the tracing
     is to be enabled, for example '--trace-program=bi_io,bi_db' enables
     tracing for functions from modules 'bi_io.c' and 'bi_db.c' (notice,
     that you need not give file suffixes in STRING).

     This option is useful for debugging 'mailfromd', but is not advised
     to use otherwise, since it is very time-costly.

'-X'
'--transcript'
'--no-transcript'
     Enable or disable transcript of the SMTP sessions to the log
     channel.  *Note Logging and Debugging::.

'--syslog'
     Selects default syslog mechanism for diagnostic output.

'--stderr'
     Directs all logging to standard output.  Similar to
     '--logger=stderr'.

'--xref'
     Same as '--dump-xref'.  *Note Logging and Debugging::.


File: mailfromd.info,  Node: Informational Options,  Prev: Logging and Debugging Options,  Up: options

8.1.6 Informational Options
---------------------------

'-?'
'--help'
     Give a short help summary.

'--usage'
     Give a short usage message.

'-V'
'--version'
     Print program version.

'--show-defaults'
     Show compilation defaults.  *Note Examining Defaults::.


File: mailfromd.info,  Node: Starting and Stopping,  Prev: options,  Up: Invocation

8.2 Starting and Stopping
=========================

Right after startup, when 'mailfromd' has done the operations that
require root privileges, it switches to the privileges of the user it is
configured to run as (*note default user privileges::) or the one given
in its configuration file (*note user: conf-priv.).  During this process
it will drop all supplementary groups and switch to the principal group
of that user.

   Such limited privileges of the daemon can cause difficulties if your
filter script needs to access some files (e.g. 'Sendmail' databases)
that are not accessible to that user and group.  For example, the
following fragment using 'dbmap' function:

     if dbmap("/etc/mail/aliases.db", $f, 1)
       ...
     fi

will normally fail, because '/etc/mail/aliases.db' is readable only to
the root and members of the group 'smmsp'.

   In such situations you need to instruct 'mailfromd' to retain the
privileges of one or several supplementary groups when switching to the
user privileges.  This is done using 'group' statement in the
'mailfromd' configuration file (*note group: conf-priv.).  In example
above, you need to use the following settings:

     group smmsp;

(The same effect can be achieved with '--group' command line option:
'mailfromd --group=smmsp').

   To stop a running instance of 'mailfromd' use one of the following
signals: 'SIGQUIT', 'SIGTERM', 'SIGINT'.  All three signals have the
same effect: the program cancels handling any pending requests,
deinitializes the communication socket (if it is a UNIX socket, the
program unlinks it) and exits.

   To restart the running 'mailfromd' instance, send it 'SIGHUP'.  For
restart to be possible, two conditions must be met: 'mailfromd' must be
invoked with the full file name, and the configuration file name must be
full as well.  If either of them is not met, 'mailfromd' displays a
similar warning message:

     warning: script file is given without full file name
     warning: restart (SIGHUP) will not work

or:

     warning: mailfromd started without full file name
     warning: restart (SIGHUP) will not work

   The reaction of 'mailfromd' on 'SIGHUP' in this case is the same as
on the three signals described previously, i.e.  cleanup and exit
immediately.

   The PID of the master instance of 'mailfromd' is kept on the
"pidfile", which is named 'mailfromd.pid' and is located in the program
"state directory".  Assuming the default location of the latter, the
following command will stop the running instance of the daemon:

     kill -TERM `head -n1 /usr/local/var/mailfromd/mailfromd.pid`

   The default pidfile location is shown in the output of 'mailfromd
--show-defaults' (*note Examining Defaults::), and can be changed at run
time using 'pidfile' statement (*note pidfile: conf-base.).

   To facilitate the use of 'mailfromd', it is shipped with a shell
script that can be used to launch it on system startup and shut it down
when the system goes down.  The script, called 'rc.mailfromd', is
located in the directory '/etc' of the distribution.  It takes a single
argument, specifying the action that should be taken:

start
     Start the program.

stop
     Shut down the program

reload
     Reload the program, by sending it 'SIGHUP' signal.

restart
     Shut down the program and start it again.

status
     Display program status.  It displays the PID of the master process
     and its command line, for example:

          $ /etc/rc.d/rc.mailfromd status
          mailformd appears to be running at 26030
          26030 /usr/local/sbin/mailfromd --group smmsp

     If the second line is not displayed, this most probably mean that
     there is a 'stale' pidfile, i.e.  the one left though the program
     is not running.

     An empty 'rc.mailfromd status' output means that 'mailfromd' is not
     running.

configtest [FILE]
     Check the script file syntax, report any errors found and exit.  If
     FILE is given it is checked instead of the default one.

macros [-c] [FILE]
     Parse the script file (or FILE, if it is given, extract the names
     of Sendmail macros it uses and generate corresponding export
     statements usable in the Sendmail configuration file.  By default,
     'mc' statements are generated.  If '-c' ('--cf') is given, the
     statements for 'sendmail.cf' are output.  See the next chapter for
     the detailed description of this mode.

   You can pass any additional arguments to 'mailfromd' by editing
'ARGS' variable near line 22.

   The script is not installed by default.  You will have to copy it to
the directory where your system start-up scripts reside and ensure it is
called during the system startup and shut down.  The exact instructions
on how to do so depend on the operating system you use and are beyond
the scope of this manual.


File: mailfromd.info,  Node: MTA Configuration,  Next: calloutd,  Prev: Invocation,  Up: Top

9 Using 'mailfromd' with Various MTAs
*************************************

The following sections describe how to configure various Milter-capable
MTAs to work with 'mailfromd'.

* Menu:

* Sendmail::
* MeTA1::
* Postfix::


File: mailfromd.info,  Node: Sendmail,  Next: MeTA1,  Up: MTA Configuration

9.1 Using 'mailfromd' with Sendmail.
====================================

This chapter assumes you are familiar with Sendmail configuration in
general and with Milter configuration directives in particular.  It
concentrates only on issues, specific for 'mailfromd'.

   To prepare 'Sendmail' to communicate with 'mailfromd' you need first
to set up the "milter port".  This is done with 'INPUT_MAIL_FILTER'
statement in your 'Sendmail' file:

     INPUT_MAIL_FILTER(`mailfrom', `S=unix:/usr/local/var/mailfromd/mailfrom')

   Make sure that the value of 'S' matches the value of 'listen'
statement in your 'mailfromd.conf' file (*note listen: conf-milter.).
Notice, however, that they may not be literally the same, because
'listen' allows to specify socket address in various formats, whereas
Sendmail's 'S' accepts only milter format.

   If you prefer to fiddle directly with 'sendmail.cf' file, use this
statement instead:

     Xmailfrom, S=unix:/usr/local/var/mailfromd/mailfrom

   If you are using Sendmail version 8.14.0 or newer, you may skip to
the end of this section.  These versions implement newer Milter protocol
that enables 'mailfromd' to negotiate with the MTA the macros it needs
for each state.

   Older versions of Sendmail do not offer this feature.  For Sendmail
versions prior to 8.14.0, you need to manually configure 'Sendmail' to
export macros you need in your 'mailfromd.mfl' file.  The simplest way
to do so is using 'rc.mailfromd' script, introduced in the previous
chapter.  Run it with 'macros' command line argument and copy its output
to your 'sendmail.mc' configuration file:

     $ rc.mailfromd macros

   If you prefer to work with 'sendmail.cf' directly, use '-c' ('--cf')
command line option:

     $ rc.mailfromd macros -c

   Finally, if you use other mailfromd script file than that already
installed (for example, you are preparing a new configuration while the
old one is still being used in production environment), give its name in
the command line:

     $ rc.mailfromd macros newscript.mfl
     # or:
     $ rc.mailfromd macros -c newscript.mfl

   If you use this method, you can skip the rest of this chapter.
However, if you are a daring sort of person and prefer to do everything
manually, follow the instructions below.

   First of all you need to build a list of macros used by handlers in
your 'mailfromd.mfl' file.  You can obtain it running 'mailfromd
--dump-macros'.  This will display all macros used in your handlers,
grouped by handler name, for example:

     envfrom i, f, {client_addr}
     envrcpt f, {client_addr}, {rcpt_addr}

   Now, modify 'confMILTER_MACROS_HANDLER' macros in your 'mc' file.
Here, HANDLER means the uppercase name of the 'mailfromd' handler you
want to export macros to, i.e.  the first word on each line of the above
'mailfromd --dump-macros' output.  _Notice,_ that in addition to these
macros, you should also export the macro 'i' for the very first handler
('rc.mailfromd macros' takes care of it automatically, but you preferred
to do everything yourself...)  It is necessary in order for 'mailfromd'
to include 'Message-ID' in its log messages (*note Message-ID::).

   For example, given the above macros listing, which corresponds to our
sample configuration (*note Filter Script Example::), the 'sendmail.mc'
snippet will contain:

     define(`confMILTER_MACROS_ENVFROM',dnl
     confMILTER_MACROS_ENVFROM `, i, f, {client_addr}')
     define(`confMILTER_MACROS_ENVRCPT',dnl
     confMILTER_MACROS_ENVRCPT `, f, {client_addr}, {rcpt_addr}')

   Special attention should be paid to 's' macro ('HELO' domain name).
In 'Sendmail' versions up to 8.13.7 (at least) it is available only to
'helo' handler.  If you wish to make it available elsewhere you will
need to use the method described in *note HELO Domain::

   Now, if you are a _really_ daring person and prefer to do everything
manually _and_ to hack your 'sendmail.cf' file directly, you certainly
don't need any advices.  Nonetheless, here's how the two statements
above _could_ look in this case:

     O Milter.macros.envfrom=i, {auth_type}, {auth_authen}, \
        {auth_ssf}, {auth_author}, {mail_mailer}, {mail_host}, \
        {mail_addr} ,{mail_addr}, {client_addr}, f
     O Milter.macros.envrcpt={rcpt_mailer}, {rcpt_host}, \
        {rcpt_addr} ,i, f, {client_addr}


File: mailfromd.info,  Node: MeTA1,  Next: Postfix,  Prev: Sendmail,  Up: MTA Configuration

9.2 Using 'mailfromd' with MeTA1.
=================================

MeTA1 (<http://www.meta1.org>) is an MTA of next generation which is
designed to provide the following main features:

   * Security
   * Reliability
   * Efficiency
   * Configurability
   * Extendibility

   Instead of using Sendmail-compatible Milter protocol, it implements a
new protocol, called "policy milter", therefore an additional program is
required to communicate with 'mailfromd'.  This program is a
"Pmilter-Milter multiplexer" 'pmult', which is part of the 'Mailfromd'
distribution.  *Note pmult::, for a detailed description of its
configuration.

   The configuration of 'Meta1--Mailfromf' interaction can be subdivided
into three tasks.

  1. Configure 'mailfromd'

     This was already covered in previous chapters.  No special
     'MeTA1'-dependent configuration is needed.

  2. Configure 'pmult' to communicate with 'mailfromd'

     This is described in detail in *note pmult::.

  3. Set up MeTA1 to communicate with 'pmult'

     The MeTA1 configuration file is located in '/etc/meta1/meta1.conf'.
     Configure the 'smtps' component, by adding the following section:

          policy_milter {
              socket {
                  type = TYPE;
                  address = ADDR;
                  [path = PATH;]
                  [port = PORT-NO;]
              };
              [timeout = INTERVAL;]
              [flags = { FLAG };]
          };

     Statements in square brackets are optional.  The meaning of each
     instruction is as follows:

     'type = TYPE'
          Set the type of the socket to communicate with 'pmult'.
          Allowed values for TYPE are:

          inet
               Use INET socket.  The socket address and port number are
               set using the 'address' and 'port' statements (see
               below).

          unix
               Use UNIX socket.  The socket path is given by the 'path'
               statement (see below).

          Notice, that depending on the 'type' setting you have to set
          up either 'address'/'port' or 'path', but not both.

     'address = ADDR'
          Configure the socket address for 'type = inet'.  ADDR is the
          IP address on which 'pmult' is listening (*note listen
          statement: pmult-conf.).

     'port = PORT-NO'
          Port number 'pmult' is listening on (*note listen statement:
          pmult-conf.).

     'path = SOCKET-FILE'
          Full pathname of the socket file, if 'type = unix'.

     'timeout = INTERVAL'
          Sets the maximum amount of time to wait for a reply from
          'pmult'.

          The behavior of 'smtps' in case of time out depends on the
          'flags' settings:

     'flags = { FLAG }'

          FLAG is one of the following:

          abort
               If 'pmult' does not respond, abort the current SMTP
               session with a '421' error.

          accept_but_reconnect
               If 'pmult' does not respond, continue the current session
               but try to reconnect for the next session.

   For example, if the 'pmult' configuration has:

     listen inet://127.0.0.1:3333;

then the corresponding part in '/etc/meta1/meta1.conf' will be

     smtps {
         policy_milter {
             socket {
                 type = inet;
                 address = 127.0.0.1;
                 port = 3333;
             };
             ...
         };
         ...
     };

   Similarly, if the 'pmult' configuration has:

     listen unix:///var/spool/meta1/pmult/socket;

then the '/etc/meta1/meta1.conf' should have:

     smtps {
         policy_milter {
             socket {
                 type = unix;
                 path = /var/spool/meta1/pmult/socket;
             };
             ...
         };
         ...
     };


File: mailfromd.info,  Node: Postfix,  Prev: MeTA1,  Up: MTA Configuration

9.3 Using 'mailfromd' with Postfix
==================================

To configure 'postfix' to work with your filter, you need to inform it
about the socket your filter is listening on.  The 'smtpd_milters' (or
'non_smtpd_milters') statement in '/etc/postfix/main.cf' serves this
purpose.  If the filter is to handle mail that arrives via SMTP, use
'smtpd_milters'.  If it is to handle mail submitted locally to the
queue, use 'non_smtpd_milters'.  In both cases, the value is a
whitespace-separated list of socket addresses.  Note, that Postfix
syntax for socket addresses differs from that used by Sendmail and
mailfromd.  The differences are summarized in the following table:


Sendmail                 Mailfromd                Postfix
---------------------------------------------------------------------------
inet:PORT@HOST           inet://HOST:PORT         inet:HOST:PORT
unix:FILE                unix://FILE              unix:FILE

Table 9.1: Socket addresses in various formats

   For example, if your 'mailfromd' listens on 'inet://127.0.0.1:4111',
add the following to '/etc/postfix/main.cf':

     smtpd_milters = inet:127.0.0.1:4111

   'Mailfromd' uses Milter protocol version 6.  Postfix, starting from
version 2.6 uses the same version.  Older versions of Postfix use Milter
protocol 2 by default.  Normally, it should not be a problem, as
'mailfromd' tries to detect what version the server is speaking.  If,
however, it fails to select the proper version, you will have to
instruct Postfix what version to use.  To do so, add the following
statement to '/etc/postfix/main.cf':

     milter_protocol = 6

   The way Postfix handles macros differs from that of Sendmail.
Postfix emulates a limited subset of Sendmail macros, and not all of
them are are available when you would expect them to.  In particular,
the 'i' macro is not available before the 'DATA' stage, which brings two
consequences.  First, 'mailfromd' log messages will not include message
ID until the 'DATA' stage is reached.  Secondly, you cannot use 'i' in
handlers 'connect', 'helo', 'envfrom' and 'envrcpt',

   If you wish to tailor Postfix defaults to export the actual macros
used by your filter, run 'mailfromd --dump-macros' and filter its output
through the 'postfix-macros.sed' filter, which is installed to the
'PREFIX/share/mailfromd' directory, e.g.:

     $ mailfromd --dump-macros | \
       sed -f /usr/share/mailfromd/postfix-macros.sed
     milter_helo_macros = {s}
     milter_mail_macros = {client_addr} {s} {f}
     milter_rcpt_macros = {rcpt_addr} {f} {client_addr}
     milter_end_of_data_macros = {i}

   Cut and paste its output to your '/etc/postfix/main.cf'.

   For more details regarding Postfix interaction with Milter and
available Postfix configuration options, see Postfix before-queue Milter
support (http://www.postfix.org/MILTER_README.html).


File: mailfromd.info,  Node: calloutd,  Next: mfdbtool,  Prev: MTA Configuration,  Up: Top

10 'calloutd'
*************

The callout verification is usually performed by a special instance of
'mailfromd' (*note callout server::).  However, it is also possible to
set up a dedicated callout server on a separate machine.  You can choose
to do so, for instance, in order to reduce the load on the server
running 'mailfromd'.

   This stand-alone callout facility is provided by the 'calloutd'
daemon.

* Menu:

* config-calloutd::     Calloutd Configuration.
* invocation-calloutd:: Calloutd Command-Line Options.
* protocol-calloutd::   The Callout Protocol.


File: mailfromd.info,  Node: config-calloutd,  Next: invocation-calloutd,  Up: calloutd

10.1 Calloutd Configuration
===========================

Main configuration file '/etc/mailfromd.conf' is used (*note Mailfromd
Configuration::).  The configuration statements are basically the same
as for 'mailfromd'.

   The address to listen on is defined in the 'server' statement.
Basically, it is the only statement the configuration file is required
to have.  The minimal configuration can look like:

     program calloutd {
       server {
         listen inet://198.51.100.1:3535;
       }
     }

   To instruct the 'mailfromd' daemon to use this server, the following
statement should be added to the '/etc/mailfromd.conf' file:

     program mailfromd {
       callout-url inet://198.51.100.1:3535;
     }

   The 'server' statement differs a little from the similar statement
for 'mailfromd'.  This and another 'calloutd'-specific statements are
described in detail in the subsections that follow.  The rest of
statements is shared with 'mailfromd'.  The following table lists all
supported configuration statements along with cross-references to the
correspondent descriptions:

Statement              Reference
-------------------------------------------------------------------
'acl'                  *Note Mailutils Configuration File: (mailutils)acl statement.
'auth'                 *Note Mailutils Configuration File: (mailutils)auth statement.
'database'             *Note conf-database::.
'database-mode'        *Note database-mode: conf-database.
'database-type'        *Note database-type: conf-database.
'debug' (section)      *Note Mailutils Configuration File: (mailutils)debug statement.
'debug'                *Note conf-calloutd-log::.
'ehlo-domain'          *Note ehlo-domain: conf-callout.
'enable-vrfy'          *Note enable-vrfy: conf-callout.
'group'                *Note group: conf-priv.
'include'              *Note Mailutils Configuration File: (mailutils)include.
'io-timeout'           *Note io-timeout: conf-timeout.
'locking'              *Note Mailutils Configuration File: (mailutils)locking statement.
'lock-retry-count'     *Note lock-retry-count: conf-database.
'lock-retry-timeout'   *Note lock-retry-timeout: conf-database.
'logger'               *Note conf-calloutd-log::.
'logging'              *Note Mailutils Configuration File: (mailutils)logging statement.
'mailer'               *Note Mailutils Configuration File: (mailutils)mailer statement.
'mail-from-address'    *Note mail-from-address: conf-callout.
'pidfile'              *Note pidfile: conf-calloutd-setup.
'server'               *Note conf-calloutd-server::.
'source-ip'            *Note source-ip: conf-calloutd-setup.
'smtp-timeout'         *Note conf-timeout::.
'state-directory'      *Note state-directory: conf-calloutd-setup.
'transcript'           *Note conf-calloutd-log::,
'user'                 *Note user: conf-priv.

* Menu:

* conf-calloutd-setup:: 'calloutd' General Setup.
* conf-calloutd-server:: The 'server' Statement.
* conf-calloutd-log:: 'calloutd' Logging.


File: mailfromd.info,  Node: conf-calloutd-setup,  Next: conf-calloutd-server,  Up: config-calloutd

10.1.1 'calloutd' General Setup
-------------------------------

 -- Calloutd Conf: source-ip IP
     Sets source IP address for TCP connections.

 -- Calloutd Conf: pidfile filename
     Defines the name of the file to store PID value in.

 -- Calloutd Conf: state-directory dir
     Sets the name of the program state directory.  *Note statedir::.


File: mailfromd.info,  Node: conf-calloutd-server,  Next: conf-calloutd-log,  Prev: conf-calloutd-setup,  Up: config-calloutd

10.1.2 The 'server' statement
-----------------------------

The 'server' statement configures how 'calloutd' will communicate with
the client 'mailfromd' server.

     server {
       id NAME;
       listen URL;
       backlog NUM;
       max-instances NUM;
       single-process BOOL;
       reuseaddr BOOL;
       default BOOL;
       callout URL;
       acl { ... }
     }

 -- Calloutd Conf: server
     Define a server.  Optional label may follow the 'server' keyword.
     The label is ignored.

     The substatements in the 'server' block provide parameters for
     configuring this server.

 -- server: id name
     Assign an identifier to this server.  This identifier is used as a
     suffix to syslog tag (*note syslog tag::) in messages related to
     this server.  For example, if a server block had the following
     statement in it:

          id main;

     then all messages related to this server will be marked with tag
     'calloutd#main'.

     The part before the '#' is set using the 'tag' statement in
     'logging' block (*note Mailutils Configuration File:
     (mailutils)Logging Statement.).

 -- server: listen url
     Listen for connections on the given URL.  *Note milter port
     specification::, for a description of allowed URL formats.

     Example:

          listen inet://10.10.10.1:3331;

 -- server: backlog NUM
     Configures the size of the queue of pending connections.  Default
     value is 8.

 -- server: max-instances number
     Sets the maximum number of instances allowed for this server.

 -- server: single-process bool
     When set to 'yes', this server will run in "single-process" mode,
     i.e.  it will not fork sub-processes to serve requests.  This
     option is meant exclusively to assist in debugging 'calloutd'.
     Don't use it for anything else but for debugging!

 -- server: reuseaddr bool
     When set to 'yes', 'calloutd' will attempt to reuse existing socket
     addresses.  This is the default behavior.

 -- server: acl STATEMENTS
     Defines access control list for this server.  *Note Mailutils
     Configuration File: (mailutils)ACL Statement, for a detailed
     discussion.

     If the global ACL is defined as well, an incoming connection is
     checked against both lists: first the per-server ACL, then the
     global one.  The connection will be permitted only if it passes
     both checks.


File: mailfromd.info,  Node: conf-calloutd-log,  Prev: conf-calloutd-server,  Up: config-calloutd

10.1.3 'calloutd' logging
-------------------------

 -- Calloutd Conf: logger mech
     Set default logger mechanism.  Allowed values for MECH are:

     stderr
          Log everything to the standard error.

     syslog
          Log to syslog.

     syslog:async
          Log to syslog using the asynchronous syslog implementation.

     *Note Logging and Debugging::, for a detailed discussion.  See also
     *note Using non-blocking syslog: syslog-async, for information on
     how to set default syslog implementation at compile time.

 -- Calloutd Conf: debug spec
     Set 'mailfromd' debug verbosity level.  The SPEC must be a valid
     debugging level specification (*note debugging level
     specification::).

 -- Calloutd Conf: transcript BOOL
     If the boolean value BOOL is 'true', enables the transcript of
     call-out SMTP sessions.


File: mailfromd.info,  Node: invocation-calloutd,  Next: protocol-calloutd,  Prev: config-calloutd,  Up: calloutd

10.2 Calloutd Command-Line Options
==================================

The 'calloutd' invocation syntax is:

       calloutd [OPTION...]

   The following options are available:

Server configuration modifiers
------------------------------

'--foreground'
     Stay in foreground.  When given this option, 'calloutd' will not
     disconnect itself from the controlling terminal and will run in the
     foreground.

'-g NAME'
'--group=NAME'

     Retain the group NAME when switching to user privileges.  *Note
     Starting and Stopping::.

'--pidfile=FILE'
     Set pidfile name.  Overrides the 'pidfile' configuration statement,
     which you are advised to use instead (*note pidfile: conf-base.).

'--resolv-conf-file=FILE'
     Read resolver settings from FILE, instead of the default
     '/etc/resolv.conf'.

'-S IP'
'--source-ip=IP'
     Set source address for TCP connections.  Overrides the 'source-ip'
     configuration statement, which you are advised to use instead
     (*note source-ip: conf-base.).

'--single-process'
     Do not fork sub-processes to serve requests.  This option is meant
     to assist in debugging 'calloutd'.  Don't use it for anything else
     but for debugging, as it terribly degrades performance!

'--state-directory=DIR'
     Set new program state directory.  *Note Local state directory:
     statedir, for the description of this directory and its purposes.

'-u NAME'
'--user NAME'
     Switch to this user's privileges after startup.  Overrides the
     'user' configuration file statement, which you are advised to use
     instead (*note user: conf-priv.).  Default user is 'mail'.

Logging and debugging options
-----------------------------

'-d STRING'
'--debug=STRING'
     Set debugging level.  *Note Logging and Debugging::.

'--log-facility=FACILITY'
     Output logs to syslog FACILITY.

'--log-tag=STRING'
     Tag syslog entries with the given STRING, instead of the program
     name.

'--logger=MECH'
     Set logger mechanism (MECH is one of 'stderr', 'syslog',
     'syslog:async').  *Note Logging and Debugging::.

'--syslog'
     Selects default syslog mechanism for diagnostic output.

'--stderr'
     Directs all logging to standard output.  Similar to
     '--logger=stderr'.

'-S IP'
'--source-ip=IP'
     Set source address for TCP connections.  Overrides the 'source-ip'
     configuration statement, which you are advised to use instead
     (*note source-ip: conf-base.).

'--debug-level=LEVEL'
     Set Mailutils debugging level.  See
     <http://mailutils.org/wiki/Debug_level>, for a detailed discussion
     of LEVEL argument.

'--source-info'
'--no-source-info'
     Include C source information in debugging messages.  This is
     similar to setting 'line-info yes' in the 'debug' configuration
     block (*note line-info: (mailutils)debug statement.).

     The '--no-source-info' can be used to cancel the effect of the
     'line-info yes' configuration statement.

     You do not need this option, unless you are developing or debugging
     'calloutd'.

'-X'
'--transcript'
'--no-transcript'
     Enable or disable transcript of the SMTP sessions to the log
     channel.  *Note Logging and Debugging::.

Configuration file control
--------------------------

'--config-file=FILE'
     Load this configuration file.

'--config-lint'
     Check syntax of configuration files and exit.  Exit code is 0 if
     the file or files are OK, and 78 otherwise.

'--config-verbose'
     Verbosely log parsing of the configuration files.

'--no-site-config'
'--no-config'
     Don't load site-wide configuration file.

'--set=PARAM=VALUE'
     Set configuration parameter

Informational options
---------------------

'--config-help'
     Show configuration file summary.

'--show-config-options'
     Show compilation options.

'-?'
'--help'
     Give a short help list.

'--usage'
     Give a short usage message.

'-V'
'--version'
     Print program version

