@c Copyright (C) 2005--2025 Sergey Poznyakoff
@c Permission is granted to copy, distribute and/or modify this document
@c under the terms of the GNU Free Documentation License, Version 1.3 or
@c any later version published by the Free Software Foundation; with no
@c Invariant Sections, with the Front and Back-Cover texts at your option.

  This chapter describes library functions available in Mailfromd
version @value{VERSION}. For the simplicity of explanation, we use
the word @samp{boolean} to indicate variables of numeric type that are
used as boolean values. For such variables, the term @samp{False}
stands for the numeric 0, and @samp{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::
@end menu

@node Macro access
@section Sendmail Macro Access Functions

@deftypefn {Built-in Function} string getmacro (string @var{macro})
Returns the value of Sendmail macro @var{macro}.  If @var{macro} is
not defined, raises the @code{e_macroundef} exception.

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

@example
  if getmacro("auth_%var") = "foo"
    @dots{}
  fi
@end example
@end deftypefn

@deftypefn {Built-in Function} boolean macro_defined (string @var{name})
Return true if Sendmail macro @var{name} is defined.
@end deftypefn

  Notice, that if your @acronym{MTA} supports macro name
negotiation@footnote{That is, if it supports Milter protocol 6 and
upper.  Sendmail 8.14.0 and Postfix 2.6 and newer do.  MeTA1 (via
@command{pmult}) does as well.  @xref{MTA Configuration}, for more
details.}, you will have to export macro names used by these two
functions using @samp{#pragma miltermacros} construct.  Consider this
example:

@example
func authcheck(string name)
do
  string macname "auth_%name"
  if macro_defined(macname)
    if getmacro(macname)
      @dots{}
    fi
  fi
done

#pragma miltermacros envfrom auth_authen

prog envfrom
do
  authcheck("authen")
done
@end example

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

@node Character translation
@section @code{tr}, @code{dc}, and @code{sq} functions

@deftypefn {Built-in Function} string tr (string @var{subj}, string @var{set1}, @
   string @var{set2})
  Translates characters in string @var{subj} and returns the resulting
string.

  Translation rules are defined by two character sets: @var{set1} is a
set of characters which, when encountered in @var{subj}, must be replaced
with the corresponding characters from @var{set2}. E.g.:

@example
tr('text', 'tx', 'ni') @result{} 'nein'
@end example

  The source set @var{set1} can contain @dfn{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 @var{set2}:

@example
tr('abacus', '[abc]', '_') @result{} @samp{____us}
@end example

  An exclamation sign at the beginning of a character class reverses
its meaning, i.e. the class matches any character @emph{not} listed in
it:

@example
tr('abacus', '[!abc]', '_') @result{} @samp{abac__}
@end example

  A character set can contain @dfn{ranges}, specified as the first and
last characters from the range separated by a dash.  A range
@samp{@var{x}-@var{y}} comprises all characters between @var{x} and
@var{y} inclusive.  For example, @samp{[a-d]} is equivalent to
@samp{[abcd]}.  Character sets must be ascending, i.e. @samp{[a-d]} is
correct, but @samp{[d-a]} is not.  You may include @samp{-} in its
literal meaning by making it the first or last character between the
brackets: @samp{[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. @samp{[][!]} matches the three characters @samp{[}, @samp{]} and
@samp{!}, whereas @samp{[!][]} matches any character except @samp{[}
and @samp{]}.

  @dfn{Named character classes} are special reserved names between
@samp{[:} and @samp{:]} delimiters:

@table @code
@item [:alnum:]
Matches any alphanumeric character.  Equivalent to
@samp{[:alpha:][:digit:]}.

@item [:alpha:]
Matches any alphabetic character.

@item [:blank:]
Matches horizontal space or tab.

@item [:cntrl:]
Matches a @dfn{control character}, i.e. a character with ASCII code
less than 32.

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

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

@item [:lower:]
Matches any lowercase letter.

@item [:print:]
Matches any printable character including space.

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

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

@item [:upper:]
Matches any upper case letter.

@item [:xdigit:]
Matches any hexagesimal digit: @samp{0} through @samp{9}, @samp{a}
through @samp{f} and @samp{A} through @samp{F}.
@end table

  Named classes can appear in character classes in @var{set1} anywhere
a regular character is allowed.  Examples:

@table @samp
@item [][:alpha:]-]
Mathes alphabet letters (both cases), digits, closing bracket and
dash.

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

@item [[:xdigit:][:blank:]]
Matches any hexagesimal digit or horizontal whitespace characters.
@end table

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

@example
tr('lasted', 'alde', 'iL?') @result{} 'List??'
@end example

  Both sets can contain character ranges, represented as
@samp{@var{c1}-@var{c2}}.  Whenever a range appears in @var{set1}, a
range must appear in the corresponding position of @var{set2}:

@example
tr('gnu', 'a-z', 'A-Z') @result{} 'GNU'
@end example

  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:

@example
tr('gnu', '[a-z]', 'A') @result{} 'AAA'
@end example

  Character ranges in @var{set1} must always be in ascending order
(i.e. @samp{a-z} is allowed, whereas @samp{z-a} is not).  Ranges in
@var{set2} can be both ascending and descending, e.g.:

@example
tr('8029', '0-9', '9-0') @result{} '1970'
@end example

  To translate a dash, place it as the first or last character in
@var{set1}:

@example
tr('in-place', '-ilp', ' Irg') @result{} 'In grace'
@end example

 The @code{tr} function will raise the @code{e_inval} exception if
@var{set2} is empty or @var{set1} contains a range without matching
range in @var{set2}.  It will raise the @code{e_range} exception, if
a descending range appears in @var{set1} or number of characters in
a range from @var{set1} does not match that from the corresponding
range in @var{set2}.
@end deftypefn

@deftypefn {Built-in Function} string dc (string @var{subj}, string @var{set1})
Deletes from @var{subj} characters that appear in @var{set1}.  The
syntax of @var{set1} is as described in @code{tr}, except that
character ranges are treated as if appearing within character class
(e.g. @samp{a-z} is the same as @samp{[a-z]}).

For example, @code{dc(subj, '0-9')} removes decimal digits from first
argument.
@end deftypefn

@deftypefn {Built-in Function} string sq (string @var{subj}, string @var{set1})
Squeezes repeats, i.e. replaces each sequence of a repeated character
that is listed in @var{set1}, with a single occurrence of that character.
The syntax of @var{set1} is as described in @code{tr}, except that
character ranges are treated as if appearing within character class
(e.g. @samp{a-z} is the same as @samp{[a-z]}).

For example, @code{sq(subj, '[[:space:]]')} replaces multiple
occurrences of whitespace characters with a single character.
@end deftypefn

@node String transformation
@section The @code{sed} function

The @code{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 @command{sed} command line utility
(hence its name) and bears similarities to analogous functions in
other programming languages (e.g. @code{sub} in @command{awk} or the
@code{s//} operator in @command{perl}).

@deftypefn {Built-in Function} string sed (string @var{subject}, @var{expr}, @dots{})
@cindex s-expression
@anchor{s-expression}
The @var{expr} argument is an @dfn{s-expressions} of the the form:

@example
s/@var{regexp}/@var{replacement}/[@var{flags}]
@end example

@noindent
where @var{regexp} is a @dfn{regular expression}, and @var{replacement} is a
replacement string for each part of the @var{subject} that matches
@var{regexp}.  When @code{sed} is invoked, it attempts to match
@var{subject} against the @var{regexp}.  If the match succeeds, the
portion of @var{subject} which was matched is replaced with
@var{replacement}.  Depending on the value of @var{flags}
(@pxref{global replace}), this process may continue until the entire
@var{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.
@end deftypefn

Both @var{regexp} and @var{replacement} are described
in detail in @ref{The "s" Command, The "s" Command, The `s' Command,
sed, GNU sed}.

Supported @var{flags} are:

@table @samp
@anchor{global replace}
@cindex g, @option{transform} flag
@item g
Apply the replacement to @emph{all} matches to the @var{regexp}, not
just the first.

@cindex i, @option{transform} flag
@item i
Use case-insensitive matching.  In the absence of this flag, the value
set by the recent @code{#pragma regex icase} is used (@pxref{pragma
regex, icase}).

@cindex x, @option{transform} flag
@item x
@var{regexp} is an @dfn{extended regular expression} (@pxref{Extended
regexps, Extended regular expressions, Extended regular expressions,
sed, GNU sed}).  In the absence of this flag, the value set by the
recent @code{#pragma regex extended} (if any) is used (@pxref{pragma
regex, extended}).

@item @var{number}
Only replace the @var{number}th match of the @var{regexp}.

Note: the @acronym{POSIX} standard does not specify what should happen
when you mix the @samp{g} and @var{number} modifiers.  @command{Mailfromd}
follows the GNU @command{sed} implementation in this regard, so
the interaction is defined to be: ignore matches before the
@var{number}th, and then match and replace all matches from the
@var{number}th on.
@end table

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

@example
@group
s/one/two/
s,one,two,
@end group
@end example

  Changing delimiters is often useful when the @var{regex} contains
slashes.  For instance, it is more convenient to write @code{s,/,-,} than
@code{s/\//-/}.

  Here is an example of @code{sed} usage:

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

@noindent
It removes angle quotes from the value of the @samp{input} variable
and assigns the result to @samp{email}.

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

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

@noindent
or give them in a single argument separated with semicolons:

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

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

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

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

@xref{regex}, for details about that @code{#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 @code{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 @code{domain_rx}.  Then this can be done as
follows:

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

  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:

@deftypefn {Built-in Function} string qr (string @var{str}[; string @var{delim}])
Quote the string @var{str} as a regular expression.  This function
selects the characters to be escaped using the currently selected
regular expression flavor (@pxref{regex}).  At most two additional
characters that must be escaped can be supplied in the @var{delim}
optional parameter.  For example, to quote the variable @samp{x}
for use in double-quoted s-expression:

@example
  qr(x, '/"')
@end example
@end deftypefn

@node String manipulation
@section String Manipulation Functions
@deftypefn {Built-in Function} string escape (string @var{str}, @
   [string @var{chars}])
  Returns a copy of @var{str} with the characters from @var{chars}
escaped, i.e. prefixed with a backslash.  If @var{chars} is not
specified, @samp{\"} is assumed.

@example
escape('"a\tstr"ing') @result{} '\"a\\tstr\"ing'
escape('new "value"', '\" ') @result{} 'new\ \"value\"'
@end example
@end deftypefn

@deftypefn {Built-in Function} string unescape (string @var{str})
   Performs the reverse to @samp{escape}, i.e. removes any prefix
backslash characters.

@example
unescape('a \"quoted\" string') @result{} 'a "quoted" string'
@end example
@end deftypefn

@deftypefn {Built-in Function} string unescape (string @var{str}, @
   [string @var{chars}])
@end deftypefn

@deftypefn {Built-in Function} string domainpart (string @var{str})
  Returns the domain part of @var{str}, if it is a valid email address,
otherwise returns @var{str} itself.

@example
@group
domainpart("gray") @result{} "gray"
domainpart("gray@@gnu.org.ua") @result{} "gnu.org.ua"
@end group
@end example
@end deftypefn

@anchor{index-built-in}
@deftypefn {Built-in Function} number index (string @var{s}, string @var{t})
@deftypefnx {Built-in Function} number index (string @var{s}, string @var{t}, @
                                              number @var{start})
  Returns the index of the first occurrence of the string @var{t} in
the string @var{s}, or -1 if @var{t} is not present.

@example
index("string of rings", "ring") @result{} 2
@end example

  Optional argument @var{start}, if supplied, indicates the position
in string where to start searching.

@example
index("string of rings", "ring", 3) @result{} 10
@end example

  To find the last occurrence of a substring, use the function
@var{rindex} (@pxref{rindex}).
@end deftypefn

@anchor{interval}
@deftypefn {Built-in Function} number interval (string @var{str})
  Converts @var{str}, which should be a valid time interval
specification (@pxref{time interval specification}), to seconds.
@end deftypefn

@deftypefn {Built-in Function} number length (string @var{str})
  Returns the length of the string @var{str} in bytes.

@example
length("string") @result{} 6
@end example

@end deftypefn

@deftypefn {Built-in Function} string dequote (string @var{str})
  Removes @samp{<} and @samp{>} surrounding @var{str}.  If @var{str} is
not enclosed by angle brackets or these are unbalanced, the argument
is returned unchanged:

@example
dequote("<root@@gnu.org.ua>") @result{} "root@@gnu.org.ua"
dequote("root@@gnu.org.ua") @result{} "root@@gnu.org.ua"
dequote("there>") @result{} "there>"
@end example
@end deftypefn

@deftypefn {Built-in Function} string localpart (string @var{str})
  Returns the local part of @var{str} if it is a valid email address,
otherwise returns @var{str} unchanged.

@example
@group
localpart("gray") @result{} "gray"
localpart("gray@@gnu.org.ua") @result{} "gray"
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} string replstr (string @var{s}, number @var{n})
  Replicate a string, i.e. return a string, consisting of @var{s}
repeated @var{n} times:

@example
replstr("12", 3) @result{} "121212"
@end example
@end deftypefn

@deftypefn {Built-in Function} string revstr (string @var{s})
  Returns the string composed of the characters from @var{s} in
reversed order:

@example
revstr("foobar") @result{} "raboof"
@end example
@end deftypefn

@anchor{rindex}
@deftypefn {Built-in Function} number rindex (string @var{s}, string @var{t})
@deftypefnx {Built-in Function} number rindex (string @var{s}, string @var{t}, @
                                               number @var{start})

  Returns the index of the last occurrence of the string @var{t} in
the string @var{s}, or -1 if @var{t} is not present.

@example
rindex("string of rings", "ring") @result{} 10
@end example

  Optional argument @var{start}, if supplied, indicates the position
in string where to start searching.  E.g.:

@example
rindex("string of rings", "ring", 10) @result{} 2
@end example

  See also @ref{index-built-in, @code{index} built-in function,  String manipulation}.
@end deftypefn

@anchor{substr}
@deftypefn {Built-in Function} string substr (string @var{str}, @
 number @var{start})
@deftypefnx {Built-in Function} string substr (string @var{str}, @
 number @var{start}, number @var{length})

  Returns the at most @var{length}-character substring of @var{str}
starting at @var{start}.  If @var{length} is omitted, the rest of
@var{str} is used.

  If @var{length} is greater than the actual length of the string, the
@code{e_range} exception is signalled.

@example
substr("mailfrom", 4) @result{} "from"
substr("mailfrom", 4, 2) @result{} "fr"
@end example
@end deftypefn

@anchor{substring}
@deftypefn {Built-in Function} string substring (string @var{str}, @
 number @var{start}, number @var{end})
  Returns a substring of @var{str} between offsets @var{start} and
@var{end}, inclusive.  Negative @var{end} means offset from the end of
the string.  In other words, yo obtain a substring from @var{start} to the
end of the string, use @code{substring(@var{str}, @var{start}, -1)}:

@example
substring("mailfrom", 0, 3) @result{} "mail"
substring("mailfrom", 2, 5) @result{} "ilfr"
substring("mailfrom", 4, -1) @result{} "from"
substring("mailfrom", 4, length("mailfrom") - 1) @result{} "from"
substring("mailfrom", 4, -2) @result{} "fro"
@end example

This function signals @code{e_range} exception if either @var{start} or
@var{end} are outside the string length.
@end deftypefn

@anchor{tolower}
@deftypefn {Built-in Function} string tolower (string @var{str})

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

@example
tolower("MAIL") @result{} "mail"
@end example
@end deftypefn

@anchor{toupper}
@deftypefn {Built-in Function} string toupper (string @var{str})
  Returns a copy of the string @var{str}, with all the lower-case characters
translated to their corresponding upper-case counterparts.
Non-alphabetic characters are left unchanged.

@example
toupper("mail") @result{} "MAIL"
@end example
@end deftypefn

@deftypefn {Built-in Function} string ltrim (string @var{str}[, string @var{cset})
  Returns a copy of the input string @var{str} with any leading
characters present in @var{cset} removed.  If the latter is not given,
white space is removed (spaces, tabs, newlines, carriage returns, and
line feeds).

@example
ltrim("  a string") @result{} "a string"
ltrim("089", "0") @result{} "89"
@end example

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

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


@end deftypefn

@deftypefn {Built-in Function} string rtrim (string @var{str}[, string @var{cset})
  Returns a copy of the input string @var{str} with any trailing
characters present in @var{cset} removed.  If the latter is not given,
white space is removed (spaces, tabs, newlines, carriage returns, and
line feeds).
@end deftypefn

@deftypefn {Built-in Function} number vercmp (string @var{a}, string @var{b})
  Compares two strings as @command{mailfromd} version numbers.  The
result is negative if @var{b} precedes @var{a}, zero if they refer to
the same version, and positive if @var{b} follows @var{a}:

@example
vercmp("5.0", "5.1") @result{} 1
vercmp("4.4", "4.3") @result{} -1
vercmp("4.3.1", "4.3") @result{} -1
vercmp("8.0", "8.0") @result{} 0
@end example
@end deftypefn

@deftypefn {Library Function} string sa_format_score (number @var{code}, @
  number @var{prec})
Format @var{code} as a floating-point number with @var{prec} decimal
digits:

@example
sa_format_score(5000, 3) @result{} "5.000"
@end example

@flindex sa.mfl
This function is convenient for formatting SpamAssassin scores for use
in message headers and textual reports.  It is defined in module
@file{sa.mfl}.

@xref{sa, SpamAssassin}, for examples of its use.
@end deftypefn

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

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

@xref{sa, SpamAssassin}, for examples of its use.
@end deftypefn

@anchor{strip_domain_part}
@deftypefn {Library Function} string strip_domain_part (string @var{domain}, @
 number @var{n})

Returns at most @var{n} last components of the domain name @var{domain}.
If @var{n} is 0 the function returns @var{domain}.

@flindex strip_domain_part.mfl
 This function is defined in the module @file{strip_domain_part.mfl}
(@pxref{Modules}).

Examples:

@example
require strip_domain_part
strip_domain_part("puszcza.gnu.org.ua", 2) @result{} "org.ua"
strip_domain_part("puszcza.gnu.org.ua", 0) @result{} "puszcza.gnu.org.ua"
@end example
@end deftypefn

@deftypefn {Library Function} string verp_extract_user (string @var{email}, @
 string @var{domain})

If @var{email} is a valid @acronym{VERP}-style email address for
@var{domain}, that corresponds to a valid local user name
(@pxref{validuser}), this function returns the local user name,
corresponding to that email.  Otherwise, it returns empty string.

For example, assuming the local user @samp{gray} exists:

@example
verp_extract_user("gray=gnu.org.ua@@tuhs.org", 'gnu\..*')
  @result{} "gray"
@end example
@end deftypefn

@node String formatting
@section String formatting

@deftypefn {Built-in Function} string sprintf (string @var{format}, @dots{})
The function @code{sprintf} formats its argument according to
@var{format} (see below) and returns the resulting string.  It takes
varying number of parameters, the only mandatory one being
@var{format}.
@end deftypefn

@subheading Format string
The format string is a simplified version of the format argument to
@acronym{C} @code{printf}-family functions.

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

Notice, that in practice that means that you should use single quotes
with the @var{format} arguments, to protect conversion specifications from
being recognized as variable references (@pxref{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
@samp{*} and each conversion specifier asks for the next argument.  If
insufficiently many arguments are given, @code{sprintf} raises
@samp{e_range} exception.  One can also specify explicitly which
argument is taken, at each place where an argument is required, by
writing @samp{%@var{m}$}, instead of @samp{%} and @samp{*@var{m}$}
instead of @samp{*}, where the decimal integer @var{m} denotes the
position in the argument list of the desired argument, indexed
starting from 1.  Thus,

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

@subheading Flag characters
The character @samp{%} is followed by zero or more of the following
@dfn{flags}:

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

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

@item -
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  @samp{-}  overrides a @samp{0} if both are given.

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

@item +
A sign (@samp{+}  or @samp{-}) always be placed before a number
produced by a signed conversion.  By default a sign is used only for
negative numbers.  A @samp{+} overrides a space if both are used.
@end table

@subheading 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 @samp{*} or @samp{*@var{m}$} (for
some decimal integer @var{m}) to specify that the field width is given
in the next argument, or in the @var{m}-th argument, respectively,
which must be of numeric type.  A negative field width is taken as a
@samp{-} 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.

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

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

@table @asis
@item d
@itemx 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 @samp{1}.  When @samp{0} is printed with an explicit precision @samp{0}, the output is empty.

@item o
@itemx u
@itemx x
@itemx X
The numeric argument is converted to unsigned octal (@samp{o}),
unsigned decimal (@samp{u}), or unsigned hexadecimal (@samp{x} and
@samp{X}) notation.  The letters @samp{abcdef} are used for @samp{x}
conversions; the letters @samp{ABCDEF} are used for @samp{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
@samp{1}.  When @samp{0} is printed with an explicit precision 0, the
output is empty.

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

@item %
A @samp{%} is written.  No argument is converted.  The complete
conversion specification is @samp{%%}.
@end table

@node Character Type
@section Character Type

@cindex ctype_mismatch, global variable
These functions check whether all characters of @var{str} fall into a
certain character class according to the @samp{C} (@samp{POSIX})
locale@footnote{Support for other locales is planned for future
versions.}.  @samp{True} (1) is returned if they do, @samp{false} (0)
is returned otherwise.  In the latter case, the global variable
@code{ctype_mismatch} is set to the index of the first character that
is outside of the character class (characters are indexed from 0).

@deftypefn {Built-in Function} boolean isalnum (string @var{str})
Checks for alphanumeric characters:

@example
  isalnum("a123") @result{} 1
  isalnum("a.123") @result{} 0 (ctype_mismatch = 1)
@end example
@end deftypefn

@deftypefn {Built-in Function} boolean isalpha (string @var{str})
Checks for an alphabetic character:

@example
  isalnum("abc") @result{} 1
  isalnum("a123") @result{} 0
@end example
@end deftypefn

@deftypefn {Built-in Function} boolean isascii (string @var{str})
Checks whether all characters in @var{str} are 7-bit ones, that fit into
the @acronym{ASCII} character set.

@example
  isascii("abc") @result{} 1
  isascii("ab\0200") @result{} 0
@end example
@end deftypefn

@deftypefn {Built-in Function} boolean isblank (string @var{str})
Checks if @var{str} contains only blank characters; that is, spaces or
tabs.
@end deftypefn

@deftypefn {Built-in Function} boolean iscntrl (string @var{str})
Checks for control characters.
@end deftypefn

@deftypefn {Built-in Function} boolean isdigit (string @var{str})
Checks for digits (0 through 9).
@end deftypefn

@deftypefn {Built-in Function} boolean isgraph (string @var{str})
Checks for any printable characters except spaces.
@end deftypefn

@deftypefn {Built-in Function} boolean islower (string @var{str})
Checks for lower-case characters.
@end deftypefn

@deftypefn {Built-in Function} boolean isprint (string @var{str})
Checks for printable characters including space.
@end deftypefn

@deftypefn {Built-in Function} boolean ispunct (string @var{str})
Checks for any printable characters which are not a spaces or
alphanumeric characters.
@end deftypefn

@deftypefn {Built-in Function} boolean isspace (string @var{str})
Checks for white-space characters, i.e.: space, form-feed (@samp{\f}),
newline (@samp{\n}), carriage return (@samp{\r}), horizontal tab
(@samp{\t}), and vertical tab (@samp{\v}).
@end deftypefn

@deftypefn {Built-in Function} boolean isupper (string @var{str})
Checks for uppercase letters.
@end deftypefn

@deftypefn {Built-in Function} boolean isxdigit (string @var{str})
Checks for hexadecimal digits, i.e. one of @samp{0}, @samp{1},
@samp{2}, @samp{3}, @samp{4}, @samp{5}, @samp{6}, @samp{7}, @samp{8},
@samp{9}, @samp{a}, @samp{b}, @samp{c}, @samp{d}, @samp{e}, @samp{f},
@samp{A}, @samp{B}, @samp{C}, @samp{D}, @samp{E}, @samp{F}.
@end deftypefn

@node I/O functions
@section I/O functions

  @acronym{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 @code{open} which returns a @dfn{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
@code{close}.

  The number of available resource descriptors is limited.  The
default limit is @value{MAX_IOSTREAMS}.  You can tailor it to your needs
using the @code{max-streams} runtime configuration statement.
@xref{conf-runtime, max-streams}, for a detailed description.

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

@anchor{io_buffering}
@deftypevr {Built-in variable} number io_buffering
Sets the default buffering type.  Allowed values are (symbolic names
are defined in @file{status.mfl} module):

@table @code
@item 0
@itemx BUFFER_NONE
No buffering.  This is the default.

@item 1
@itemx BUFFER_FULL
Full buffering.  Size of the buffer is set by the
@code{io_buffer_size} global variable (see below).

@item 2
@itemx BUFFER_LINE
Line buffering.  When reading, it is pretty much the same as
@code{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 @code{io_buffer_size}
variable.  It will grow as needed during the I/O.
@end table
@end deftypevr

@anchor{io_buffer_size}
@deftypevr {Built-in variable} number io_buffer_size
Set the buffer size if @code{io_buffering} is set to
@code{BUFFER_FULL} or @code{BUFFER_LINE}.  By default, this variable
is set to the size of the system page.
@end deftypevr

@anchor{open}
@deftypefn {Built-in Function} number open (string @var{name})
  The @var{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 @acronym{I/O} operations.

  Buffering mode for the opened stream is defined by the
@code{io_buffering} and @code{io_buffer_size} global variables.  It
can be changed using the @code{setbuf} function (@pxref{setbuf}).

  First symbols of @var{name} determine the type of the resource to be
opened and the access mode:

@table @samp
@item >
  The rest of @var{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.

@item >>
  The rest of @var{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.

@item |
  Treat the rest of @var{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:

@table @asis
@item |2>null: @var{command}
Standard error is redirected to @file{/dev/null}.

@item |2>file:@var{name} @var{command}
Execute @var{command} with its standard error redirected to the file
@var{name}.  If the file exists, it will be truncated.

@item |2>>file:@var{name} @var{command}
Standard error of the @var{command} is appended to the file
@var{name}.  If file does not exist, it will be created.

The @samp{|2>null:} construct described above is a shortcut for

@example
|2>>file:/dev/null @var{command}
@end example

@item |2>syslog:@var{facility}[.@var{priority}] @var{command}
Standard error is redirected to the given syslog @var{facility} and,
optionally, @var{priority}.  If the latter is omitted, @samp{LOG_ERR}
is assumed.

Valid values for @var{facility} are: @samp{user}, @samp{daemon},
@samp{auth}, @samp{authpriv}, @samp{mail}, and @samp{local0}
through @samp{local7}.  Valid values for @var{priority} are:
@samp{emerg}, @samp{alert}, @samp{crit}, @samp{err}, @samp{warning},
@samp{notice}, @samp{info}, @samp{debug}.  Both @var{facility} and
@var{priority} may be given in upper, lower or
mixed cases.
@end table

Notice, that no whitespace characters are allowed between @samp{|} and
@samp{2>}.

@item |<
  Treat the rest of @var{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 @samp{|}).

@item |&
  Treat the rest of @var{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 @code{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
@samp{|}).  For example, the following redirects it to
syslog @samp{mail.debug}:

@example
|&2>syslog:mail.debug @var{command}
@end example

@item @@
  Treat the rest of @var{name} as the @acronym{URL} of a socket to
connect to.  Valid @acronym{URL} forms are described in @ref{milter
port specification}.
@end table

  If none of these prefixes is used, @var{name} is treated as a name
of an existing file and @code{open} will attempt to open this file for
reading.

  The @code{open} function will signal exception @code{e_failure} if it
is unable to open the resource or get the required access to it.
@end deftypefn

@deftypefn {Built-in Function} number spawn (string @var{cmd} [, @
   number @var{in}, number @var{out}, number @var{err}])
Runs the supplied command @var{cmd}.  The syntax of the @var{cmd} is
the same as for the @var{name} argument to @code{open} (see above),
which begins with @samp{|}, excepting that the @samp{|} sign is
optional.  That is:

@example
spawn("/bin/cat")
@end example

@noindent
has exactly the same effect as

@example
open("|/bin/cat")
@end example

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
@code{open} or @code{tempfile}.  The value @samp{-1} means no
redirection.

  Buffering mode for the opened stream is defined by the
@code{io_buffering} and @code{io_buffer_size} global variables.  It
can be changed using the @code{setbuf} function (@pxref{setbuf}).

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

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

@deftypefn {Built-in Function} void close (number @var{rd})
  The argument @var{rd} is a resource descriptor returned by a
previous call to @code{open}.  The function @code{close} closes the
resource and deallocates any memory associated with it.

  @code{close} will signal @code{e_range} exception if @var{rd} lies
outside of allowed range of resource descriptors.  @xref{conf-runtime,
max-streams}.
@end deftypefn

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

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

The @code{how} argument identifies which part of the connection to
shut down:

@table @asis
@kwindex SHUT_RD
@item SHUT_RD
Read connection.  All further receptions will be disallowed.

@kwindex SHUT_WR
@item SHUT_WR
Write connection.  All further transmissions will be disallowed.

@kwindex SHUT_RDWR
@item SHUT_RDWR
Shut down both read and write parts.
@end table
@end deftypefn

@deftypefn {Built-in Function} number tempfile ([string @var{tmpdir}])
Creates a nameless temporary file and returns its descriptor.
Optional @var{tmpdir} supplies the directory where to create the file,
instead of the default @file{/tmp}.
@end deftypefn

@deftypefn {Built-in Function} void rewind (number @var{rd})
Rewinds the stream identified by @var{rd} to its beginning.
@end deftypefn

@deftypefn {Built-in Function} number copy (number @var{dst}, number @var{src})
Copies all data from the stream @var{src} to @var{dst}.  Returns
number of bytes copied.
@end deftypefn

  The following functions provide basic read/write capabilities.

@deftypefn {Built-in Function} void write (number @var{rd}, string @var{str} @
          [, number @var{size}])
  Writes the string @var{str} to the resource descriptor @var{rd}.  If
the @var{size} argument is given, writes this number of bytes.

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

@deftypefn {Built-in Function} void write_body (number @var{rd}, pointer @var{bp} @
          , number @var{size})
Write the body segment of length @var{size} from pointer @var{bp} to
the stream @var{rd}.  This function can be used only in @code{prog
body} (@pxref{body handler}).  Its second and third arguments
correspond exactly to the parameters of the @code{body} handler, so
the following construct writes the message body to the resource
@code{fd}, which should have been open prior to invoking the
@code{body} handler:

@example
@group
prog body
do
  write_body(fd, $1, $2)
done
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} string read (number @var{rd}, number @var{n})
  Read and return at most @var{n} bytes from the resource descriptor @var{rd}.

If there are less than @var{n} bytes in the stream, the remaining
bytes will be returned.  Use @code{length()} to obtain the actual size
of the returned data.  If there are no bytes left, the @code{e_eof}
exception will be signalled.

  The function may signal the following exceptions:

@table @asis
@item e_range
@var{rd} lies outside of allowed range of resource descriptors.
@item e_eof
End of file encountered.
@item e_io
An @acronym{I/O} error occurred.
@end table
@end deftypefn

@deftypefn {Built-in Function} string getdelim (number @var{rd}, string @var{delim})
  Read and return the next string terminated by @var{delim} from the
resource descriptor @var{rd}.

  The terminating @var{delim} string will be removed from the return
value.

  When using this function, it is highly recommended to enable full
buffering for @var{fd}, either by setting @code{io_buffering} before
@code{open} (@pxref{io_buffering}) or by calling @code{setbuf}
after it (@pxref{setbuf}).  @xref{getline}, for an example.

  This function may signal the following exceptions:

@table @asis
@item e_range
@var{rd} lies outside of allowed range of resource descriptors.
@item e_eof
End of file encountered.
@item e_io
An @acronym{I/O} error occurred.
@end table

@end deftypefn

@anchor{getline}
@deftypefn {Built-in Function} string getline (number @var{rd})
  Read and return the next @dfn{line} from the resource
descriptor @var{rd}.  A line is any sequence of characters terminated
with the default @dfn{line delimiter}.  The default delimiter is
a property of @var{rd}, i.e. different descriptors can have different
line delimiters.  The default value is @samp{\n} (ASCII 10), and can
be changed using the @code{fd_set_delimiter} function (see below).

  When using this function, it is highly recommended to enable full
buffering for @var{fd}, either by setting @code{io_buffering} before
@code{open} (@pxref{io_buffering}) or by calling @code{setbuf}
after it (@pxref{setbuf}), e.g.:

@example
@group
  set fd open(input)
  setbuf(fd, BUFFER_FULL)
  set line getline(fd)
  ...
@end group
@end example

  This function may signal the following exceptions:

@table @asis
@item e_range
@var{rd} lies outside of allowed range of resource descriptors.
@item e_eof
End of file encountered.
@item e_io
An @acronym{I/O} error occurred.
@end table
@end deftypefn

@deftypefn {Built-in Function} void fd_set_delimiter (number @var{fd}, @
  string @var{delim})
Set new line delimiter for the descriptor @var{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:

@example
fd_set_delimiter(fd, "\r\n")
@end example
@end deftypefn

@deftypefn {Built-in Function} string fd_delimiter (number @var{fd})
Returns the line delimiter string for @var{fd}.
@end deftypefn

The following example shows how @command{mailfromd} @acronym{I/O} functions can
be used to automatically add @acronym{IP} addresses to an @acronym{RBL} zone:

@example
@group
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
@end group
@end example

@noindent
The function @code{reverse_ipstr} is defined in @ref{Internet address
manipulation functions}.

@anchor{setbuf}
@deftypefn {Built-in Function} void setbuf(number @var{fd}, @
  [number @var{type}, number @var{size}])
Changes the buffering mode of @var{fd} according to the remaining two
arguments.  The @var{type} specifies buffering type
(@pxref{io_buffering}), and @var{size} supplies the buffer size for
buffering types @code{BUFFER_FULL} and @code{BUFFER_LINE}.  If
@var{size} is omitted, it defaults to @code{io_buffer_size}
(@pxref{io_buffer_size}).  Omitted @var{type} defaults to
@code{io_buffering} (@pxref{io_buffering}).
@end deftypefn

@deftypefn {Built-in Function} number getbuftype(number @var{fd})
Returns the type of buffering currently in effect for the descriptor
@var{fd}.  @xref{io_buffering}, for a list of possible return values.

If this function returns @code{BUFFER_FULL} or @code{BUFFER_LINE}, you
can use @code{getbufsize} to get the associated buffer size.
@end deftypefn

@deftypefn {Built-in Function} number getbufsize(number @var{fd})
Returns the buffer size for the descriptor @var{fd}.
@end deftypefn

@node Filtering functions
@section Filtering functions

This section describes functions that transform data using
Mailutils @dfn{filter pipes}.  Filter pipe is a string defining data
flow between several @dfn{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 (@samp{|}).  For example, the @code{crlf} filter
inserts a carriage return character before each newline character.  A
filter doing that kind of transformation is defined as:

@example
"crlf"
@end example

Another filter, @code{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:

@example
"crlf | base64"
@end example

Some filters take one or more @dfn{arguments}.  These are specified as
a comma-delimited list in parentheses after the filter name.  For
example, the @code{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:

@example
"crlf | base64 | linelen(62)"
@end example

Many filters operate in two modes: @dfn{encode} and @dfn{decode}.  By
default all MFL functions apply filters in encode mode.  The desired
mode can be stated explicitly in the filter string by using
@code{encode()} and @code{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:

@example
"decode(base64 | crlf)"
@end example

@xref{Filters}, for a discussion of available filters and their arguments.

@deftypefn {Built-in Function} string filter_string (string @var{input}, string @var{filter_pipe})
Transforms the string @var{input} using filters in @var{filter_pipe}
and returns the result.  Example:

@example
set input "test\ninput\n"
filter_string(input, "crlf|base64") @result{} "dGVzdA0KaW5wdXQNCg=="
@end example
@end deftypefn

@deftypefn {Built-in Function} void filter_fd (number @var{src_fd}, number @var{dst_fd}, string @var{filter_pipe})
Given two I/O descriptors, reads data from @var{src_fd}, transforms it
using @var{filter_pipe} and writes the result to descriptor
@var{dst_fd}.

Both descriptors must be obtained using functions described in
@ref{I/O functions}.
@end deftypefn

@node Filters
@subsection Filters and Filter Pipes
@include filter_pipe.texi

@node Email processing functions
@section Email processing functions

@deftypefn {Built-in Function} number email_map (string @var{email})
  Parses @var{email} and returns a bitmap, consisting of zero or more of
the following flags:

@table @samp
@kwindex EMAIL_MULTIPLE
@item EMAIL_MULTIPLE
@var{email} has more than one email address.

@kwindex EMAIL_COMMENTS
@item EMAIL_COMMENTS
@var{email} has comment parts.

@kwindex EMAIL_PERSONAL
@item EMAIL_PERSONAL
@var{email} has personal part.

@kwindex EMAIL_LOCAL
@item EMAIL_LOCAL
@var{email} has local part.

@kwindex EMAIL_DOMAIN
@item EMAIL_DOMAIN
@var{email} has domain part.

@kwindex EMAIL_ROUTE
@item EMAIL_ROUTE
@var{email} has route part.
@end table

@flindex email.mfl
These constants are declared in the @file{email.mfl} module.  The
function @code{email_map} returns 0 if its argument is not a valid
email address.
@end deftypefn

@deftypefn {Library Function} boolean email_valid (string @var{email})
  Returns @samp{True} (1) if @var{email} is a valid email address,
consisting of local and domain parts only.  E.g.:

@example
email_valid("gray@@gnu.org") @result{} 1
email_valid("gray") @result{} 0
email_valid('"Sergey Poznyakoff <gray@@gnu.org>') @result{} 0
@end example

  This function is defined in @file{email.mfl} (@pxref{Modules}).
@end deftypefn

@node Envelope modification functions
@section Envelope Modification Functions

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

@deftypefn {Built-in Function} void set_from (string @var{email} @
                                              [, string @var{args}])
Sets envelope sender address to @var{email}, which must be a valid
email address.  Optional @var{args} supply arguments to ESMTP @samp{MAIL
FROM} command.
@end deftypefn

@deftypefn {Built-in Function} void rcpt_add (string @var{address})
Add the e-mail @var{address} to the envelope.
@end deftypefn

@deftypefn {Built-in Function} void rcpt_delete (string @var{address})
Remove @var{address} from the envelope.
@end deftypefn

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

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

@node Header modification functions
@section Header Modification Functions
@cindex header modification

  There are two ways to modify message headers in a @acronym{MFL}
script.  First is to use header actions, described in @ref{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.

@deftypefn {Built-in Function} void header_add (string @var{name}, @
  string @var{value})
Adds a header @samp{@var{name}: @var{value}} to the message.

In contrast to the @code{add} action, this function allows to construct
the header name using arbitrary @acronym{MFL} expressions.
@end deftypefn

@deftypefn {Built-in Function} void header_add (string @var{name}, @
  string @var{value}, number @var{idx})
This syntax is preserved for backward compatibility.  It is equivalent
to @code{header_insert}, which see.
@end deftypefn

@deftypefn {Built-in Function} void header_insert (string @var{name}, @
  string @var{value}, number @var{idx})
This function inserts a header @samp{@var{name}: @samp{value}} at
@var{idx}th 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 @samp{H?@var{cond}?} directive in
@file{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
@code{header_insert} will appear in the final message will differ from
@var{idx}.

@end deftypefn

@deftypefn {Built-in Function} void header_delete (string @var{name} @
  [, number @var{index}])
Delete header @var{name} from the envelope.  If @var{index} is given,
delete @var{index}th instance of the header @var{name}.

Notice the differences between this function and the @code{delete}
action:

@enumerate 1
@item It allows to construct the header name, whereas @code{delete}
requires it to be a literal string.

@item Optional @var{index} argument allows to select a particular header
instance to delete.
@end enumerate
@end deftypefn

@deftypefn {Built-in Function} void header_replace (string @var{name}, @
  string @var{value} [, number @var{index}])
Replace the value of the header @var{name} with @var{value}.  If
@var{index} is given, replace @var{index}th instance of header @var{name}.

Notice the differences between this function and the @code{replace}
action:

@enumerate 1
@item It allows to construct the header name, whereas @code{replace}
requires it to be a literal string.

@item Optional @var{index} argument allows to select a particular header
instance to replace.
@end enumerate
@end deftypefn

@deftypefn {Library Function} void header_rename (string @var{name}, @
  string @var{newname}[, number @var{idx}])
@flindex header_rename.mfl
@*Defined in the module @file{header_rename.mfl}.
@*Available only in the @samp{eom} handler.

Renames the @var{idx}th instance of header @var{name} to
@var{newname}.  If @var{idx} is not given, assumes 1.

If the specified header or the @var{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 @samp{Subject} header to @samp{X-Old-Subject}:

@example
require 'header_rename'

prog eom
do
  header_rename("Subject", "X-Old-Subject")
done
@end example
@end deftypefn

@deftypefn {Library Function} void header_prefix_all (string @var{name} @
  [, string @var{prefix}])
@flindex header_rename.mfl
@*Defined in the module @file{header_rename.mfl}.
@*Available only in the @samp{eom} handler.

Renames all headers named @var{name} by prefixing them with
@var{prefix}.  If @var{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.
@end deftypefn

@deftypefn {Library Function} void header_prefix_pattern (string @var{pattern}, string @var{prefix})
@flindex header_rename.mfl
@*Defined in the module @file{header_rename.mfl}.
@*Available only in the @samp{eom} handler.

Renames all headers with names matching @var{pattern} (in the sense of
@code{fnmatch}, @pxref{Special comparisons, fnmatches}) by prefixing
them with @var{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 @var{pattern}.

For example, to prefix all headers beginning with @samp{X-Spamd-} with
an additional @samp{X-}:

@example
require 'header_rename'

prog eom
do
  header_prefix_pattern("X-Spamd-*", "X-")
done
@end example
@end deftypefn

@node Body Modification Functions
@section Body Modification Functions

Body modification is an experimental feature of @acronym{MFL}.
The version @value{VERSION} provides only one function for that purpose.

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

Example:

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

No restrictions are imposed on the format of @var{text}.
@end deftypefn

@deftypefn {Built-in Function} void replbody_fd (number @var{fd})
Replaces the body of the message with the content of the stream
@var{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
@var{fd}.  Use @code{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 @file{/usr/bin/mailproc}, which is supposed to
read the body from its standard input and write the processed text to
its standard output:

@example
number fd   # @r{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 @code{mailproc}
  # command and read the new body from its standard output.
  rewind(fd)
  replbody_fd(spawn("</usr/bin/mailproc", fd))
done
@end example
@end deftypefn

@node Message modification queue
@section Message Modification Queue
@cindex 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 @dfn{message
modification queue}.  These changes are applied at the end of
processing, before @samp{eom} stage finishes
(@pxref{milter-control-flow}).

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

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

  Obviously, the intention was to add a @samp{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.:

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

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

@example
prog data
do
  if accepted
    continue
  fi
  ...
done
@end example

 To help you trace such problematic usages of @code{accept},
@command{mailfromd} emits the following warning:

@example
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
@end example

 If it is OK to lose all modifications, call @code{mmq_purge}, as
suggested in this message.

@deftypefn {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: @code{rcpt_add}, @code{rcpt_delete}, @code{header_add},
@code{header_insert}, @code{header_delete}, @code{header_replace},
@code{replbody}, @code{quarantine}.
@end deftypefn

@node Mail header functions
@section Mail Header Functions

@deftypefn {Built-in Function} string message_header_encode @
 (string @var{text}, [string @var{enc}, string @var{charset}])
 Encode @var{text} in accordance with @acronym{RFC} 2047.  Optional
arguments:

@table @var
@item enc
  Encoding to use.  Valid values are @samp{quoted-printable}, or
@samp{Q} (the default) and @samp{base64}, or @samp{B}.

@item charset
  Character set.  By default @samp{UTF-8}.
@end table

  If the function is unable to encode the string, it raises the
exception @code{e_failure}.

For example:

@example
@group
set string "Keld J@o{}rn Simonsen <keld@@dkuug.dk>"
message_header_encode(string, "ISO-8859-1")
  @result{} "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@@dkuug.dk>"
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} string message_header_decode @
 (string @var{text}, [string @var{charset}])
  @var{text} must be a header value encoded in accordance with @acronym{RFC}
2047.  The function returns the decoded string.  If the decoding fails,
it raises @code{e_failure} exception.  The optional argument
@var{charset} specifies the character set to use (default --
@samp{UTF-8}).

@example
@group
set string "=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@@dkuug.dk>"
message_header_decode(string)
 @result{} "Keld J@o{}rn Simonsen <keld@@dkuug.dk>"
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} string unfold (string @var{text})
  If @var{text} is a ``folded'' multi-line @acronym{RFC} 2822 header value,
unfold it.  If @var{text} is a single-line string, return its
unchanged copy.

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

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

Then, applying @code{unfold} to its value@footnote{For example:

@example
prog header
do
  echo unfold($2)
done
@end example
} will produce:

@example
Sent bugreports to <some-address@@some.net>
@end example
@end deftypefn

@node Mail body functions
@section Mail Body Functions

@deftypefn {Built-in Function} string body_string (pointer @var{text}, @
  number @var{count})
Converts first @var{count} bytes from the memory location pointed to by
@var{text} into a regular string.

This function is intended to convert the @code{$1} argument passed to
a @code{body} handler to a regular @acronym{MFL} string.  For more
information about its use, see @ref{body handler}.
@end deftypefn

@deftypefn {Built-in Function} bool body_has_nulls (pointer @var{text}, @
  number @var{count})
Returns @samp{True} if first @var{count} bytes of the string pointed
to by @var{text} contain ASCII NUL characters.

Example:

@example
prog body
do
  if body_has_nulls($1, $2)
    reject
  fi
done
@end example
@end deftypefn

@node EOM Functions
@section EOM Functions

The following function is available only in the @samp{eom} handler:

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

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

@node Current Message Functions
@section Current Message Functions

@anchor{current_message}
@deftypefn {Built-in Function} number current_message ()
This function can be used in @code{eom} handlers only.  It returns a
message descriptor referring to the current message.  @xref{Message
functions}, for a description of functions for accessing messages.
@end deftypefn

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

@deftypefn {Built-in Function} number current_header_count ([string @var{name}])

Return number of headers in the current message.  If @var{name}
is specified, return number of headers that have this name.

@example
  current_header_count() @result{} 6
  current_header_count("Subject") @result{} 1
@end example
@end deftypefn

@deftypefn {Built-in Function} string current_header_nth_name (number @var{n})

Return the name of the @var{n}th header.  The index @var{n} is 1-based.
@end deftypefn

@deftypefn {Built-in Function} string current_header_nth_value (number @var{n})

Return the value of the @var{n}th header.  The index @var{n} is
1-based.
@end deftypefn

@deftypefn {Built-in Function} string current_header (string name @
        [, number @var{n}])
Return the value of the named header, e.g.:

@example
  set s current_header("Subject")
@end example

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

@example
  set s current_header("Received", 2)
@end example

Header indices are 1-based.
@end deftypefn

All current_header function raise the @code{e_not_found} exception if the
requested header was not found.

@node Mailbox functions
@section Mailbox Functions
@cindex 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 @code{mailbox_open} function:

@deftypefn {Built-in Function} number mailbox_open (string @var{url} @
    [, string @var{mode}, string @var{perms}])
Open a mailbox identified by @var{url}.  Return a @dfn{mailbox
descriptor}: a unique numeric identifier that can subsequently be used
to access this mailbox.

The optional @var{mode} argument specifies the access mode for
the mailbox.  Its valid values are:

@multitable @columnfractions 0.2 0.7
@headitem Value @tab Meaning
@item r @tab Open mailbox for reading.  This is the default.
@item w @tab Open mailbox for writing.  If the mailbox does not exist,
it is created.
@item rw @tab Open mailbox for reading and writing.  If the mailbox
does not exist, it is created.
@item wr @tab Same as @samp{rw}.
@item w+ @tab Open mailbox for reading and writing.  If the mailbox
does not exist, it is created.
@item a @tab Open mailbox for appending messages to it.  If the
mailbox does not exist, an exception is signalled.
@item a+ @tab Open mailbox for appending messages to it.  If the
mailbox does not exist, it is created.
@end multitable

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

@example
[go](+|=)[wr]+
@end example

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

@example
g=rw,o+r
@end example
@end deftypefn

  The number of mailbox descriptors available for simultaneous opening
is @value{MAX_MBOXES}.  This value can be changed using the
@code{max-open-mailboxes} runtime configuration statement
(@pxref{conf-runtime, max-open-mailboxes}).

@deftypefn {Built-in Function} number mailbox_messages_count (number @var{nmbx})
Return the number of messages in mailbox.  The argument @var{nmbx} is
a valid mailbox descriptor as returned by a previous call to
@code{mailbox_open}.
@end deftypefn

@deftypefn {Built-in Function} number mailbox_get_message (number @var{mbx}, @
  number @var{n})
Retrieve @var{n}th message from the mailbox identified by descriptor
@var{mbx}.  On success, the function returns a @dfn{message
descriptor}, an integer number that can subsequently be used to access
that message (@pxref{Message functions}).  On error, an exception is
raised.

Messages in a mailbox are numbered starting from 1.
@end deftypefn

@deftypefn {Built-in Function} void mailbox_close (number @var{nmbx})
Close a mailbox previously opened by @code{mailbox_open}.
@end deftypefn

@deftypefn {Built-in Function} void mailbox_append_message (number @var{nmbx}, @
  number @var{nmsg})
Append message @var{nmsg} to mailbox @var{nmbx}.  The message
descriptor @var{nsmg} must be obtained from a previous call to
@code{mailbox_get_message} or @code{current_message}
(@pxref{current_message}).
@end deftypefn

@node Message functions
@section Message Functions
@cindex message functions
  The functions described below retrieve information from RFC822
messages.  The message to operate upon is identified by its
@dfn{descriptor}, an integer number returned by the previous call to
@code{mailbox_get_message} (@pxref{Mailbox functions,
mailbox_get_message}) or @code{current_message}
(@pxref{current_message}) function.  The maximum number of message
descriptors is limited by @value{MAX_MSGS}.  You can change this limit
using the @code{max-open-messages} runtime configuration statement
(@pxref{conf-runtime, max-open-messages}).

@deftypefn {Built-in Function} number message_size (number @var{nmsg})
Return the size of the message @var{nmsg}, in bytes.  @emph{Notice},
that if @var{nmsg} refers to current message
(@pxref{current_message}), the returned value is less than the size
seen by the @acronym{MTA}, because @command{mailfromd} recodes
@acronym{CR-LF} sequences to @acronym{LF}, i.e. removes carriage
returns (@acronym{ASCII} 13) occurring before line feeds
(@acronym{ASCII} 10.  To obtain actual message length as seen by the
@acronym{MTA}, add the number of lines in the message:

@example
  set actual_length message_size(nmsg) + message_lines(nmsg)
@end example

@end deftypefn

@deftypefn {Built-in Function} boolean message_body_is_empty (number @var{nmsg})
Returns @code{true} if the body of message @var{nmsg} has zero size or
contains only whitespace characters.  If the
@samp{Content-Transfer-Encoding} header is present, it is used to
decode body before processing.
@end deftypefn

@deftypefn {Built-in Function} void message_close (number @var{nmsg})
Close the message identified by descriptor @var{nmsg}.
@end deftypefn

@deftypefn {Built-in Function} number message_lines (number @var{nmsg})
Return total number of lines in message @var{nmsg}.  The following
relation holds true:

@example
message_lines(@var{x}) = message_body_lines(@var{x})
                         + message_header_lines(@var{x}) + 1
@end example
@end deftypefn

@deftypefn {Built-in Function} string message_read_line (number @var{nmsg})
Read and return next line from the message @var{nmsg}.  If
there are no more lines to read, raise the @code{eof} exception.

Use @code{message_rewind} to rewind the message stream and read its
contents again.
@end deftypefn

@deftypefn {Built-in Function} void message_rewind (number @var{nmsg})
Rewind the stream associated with message referred to by descriptor @var{nmsg}.
@end deftypefn

@deftypefn {Built-in Function} number message_from_stream (number @var{fd}; @
       string @var{filter_chain})
Converts contents of the stream identified by @var{fd} to a mail
message.  Returns identifier of the created message.

Optional @var{filter_chain} supplies the name of a @dfn{Mailutils
filter chain}, through which the data will be passed before converting.
See @uref{http://mailutils.org/wiki/Filter_chain}, for a description
of filter chains.
@end deftypefn

@deftypefn {Built-in Function} void message_to_stream (number @var{fd},@
                number @var{nmsg}; @
                string @var{filter_chain})
Copies message @var{nsmg} to stream descriptor @var{fd}.  The
descriptor must be obtained by a previous call to @code{open}.

Optional @var{filter_chain} supplies the name of a @dfn{Mailutils
filter chain}, through which the data will be passed before writing
them to @var{fd}.  See @uref{http://mailutils.org/wiki/Filter_chain},
for a description of filter chains.
@end deftypefn

@menu
* Header functions::
* Message body functions::
* MIME functions::
* Message digest functions::
@end menu

@node Header functions
@subsection Header functions

@deftypefn {Built-in Function} number message_header_size (number @var{nmsg})
Return the size, in bytes of the headers of message @var{nmsg}.  See the
note to the @code{message_size}, above.
@end deftypefn

@deftypefn {Built-in Function} number message_header_lines (number @var{nmsg})
Return number of lines occupied by headers in message @var{nmsg}.
@end deftypefn

@anchor{message_header_count}
@deftypefn {Built-in Function} number message_header_count (number @var{nmsg}, @
  [string @var{name}])
Return number of headers in message @var{nmsg}.

If @var{name} is supplied, count only headers with that name.
@end deftypefn

@deftypefn {Built-in Function} string message_find_header (number @var{nmsg}, @
  string @var{name} [, number @var{idx}])
Return value of header @var{name} from the message @var{nmsg}.  If the
message contains several headers with the same name, optional
parameter @var{idx} may be used to select one of them.  Headers are
numbered from @samp{1}.

If no matching header is not found, the @code{not_found} exception is
raised.  If another error occurs, the @code{failure} exception is
raised.

The returned string is a verbatim copy of the message contents (except
for eventual @acronym{CR-LF} -> @acronym{LF} translation, see above).
You might need to apply the @code{unfold} function to it (@pxref{Mail
header functions, unfold}).
@end deftypefn

@deftypefn {Built-in Function} string message_nth_header_name (number @var{nmsg}, number @var{n})
Returns the name of the @var{n}th header in message @var{nmsg}.  If
there is no such header, @code{e_range} exception is raised.
@end deftypefn

@deftypefn {Built-in Function} string message_nth_header_value (number @var{msg}, number @var{n})
Returns the value of the @var{n}th header in message @var{nmsg}.  If
there is no such header, @code{e_range} exception is raised.
@end deftypefn

@deftypefn {Built-in Function} boolean message_has_header (number @var{nmsg},@
  string @var{name} [, number @var{idx}])
Return @code{true} if message @var{nmsg} contains header with the
given @var{name}.  If there are several headers with the same name,
optional parameter @var{idx} may be used to select one of them.
@end deftypefn

@node Message body functions
@subsection Message body functions

@deftypefn {Built-in Function} number message_body_size (number @var{nmsg})
Return the size, in bytes, of the body of message @var{nmsg}.  See the
note to the @code{message_size}, above.
@end deftypefn

@deftypefn {Built-in Function} number message_body_lines (number @var{nmsg})
Return number of lines in the body of message referred to by
descriptor @var{nmsg}.
@end deftypefn

@deftypefn {Built-in Function} void message_body_rewind (number @var{nmsg})
Rewind the stream associated with the body of message referred to by
descriptor @var{nmsg}.

A call to @code{message_body_read_line} (see below) after calling this
function will return the first line from the message body.
@end deftypefn

@deftypefn {Built-in Function} string message_read_body_line (number @var{nmsg})
Read and return next line from the body of the message @var{nmsg}.  If
there are no more lines to read, raise the @code{eof} exception.

Use @code{message_body_rewind} (see above) to rewind the body stream
and read its contents again.
@end deftypefn

@deftypefn {Built-in Function} void message_body_to_stream (number @var{fd},@
                number @var{nmsg}; @
                string @var{filter_pipe})
Copies the body of the message @var{nsmg} to stream descriptor
@var{fd}.  The descriptor must be obtained by a previous call to
@code{open}.

Optional @var{filter_pipe} supplies a sequence of @dfn{Mailutils
filters}, through which the data will be passed before writing
them to @var{fd}.  @xref{Filtering functions}, for a discussion of
filter pipe syntax.

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

@xref{mimedecode}, for a detailed discussion of this feature.
@end deftypefn

@node MIME functions
@subsection MIME functions

@deftypefn {Built-in Function} boolean message_is_multipart (number @var{nmsg})
Return @code{true} if message @var{nmsg} is a multipart
(@acronym{MIME}) message.
@end deftypefn

@deftypefn {Built-in Function} number message_count_parts (number @var{nmsg})
Return number of parts in message @var{nmsg}, if it is a multipart
(@acronym{MIME}) message.  If it is not, return @samp{1}.

Use @code{message_is_multipart} to check whether the message is a
multipart one.
@end deftypefn

@deftypefn {Built-in Function} number message_get_part (number nmsg, @
  number @var{n})
Extract @var{n}th part from the multipart message @var{nmsg}.
Numeration of parts begins from @samp{1}.  Return message descriptor
referring to the extracted part.  Message parts are regarded as
messages, so any message functions can be applied to them.
@end deftypefn

@deftypefn {Built-in Function} string message_content_type (number @var{nmsg})
Returns content type for the message @var{nmsg}.  The returned string
is composed of content type and subtype, delimited by slash.

If @var{nmsg} is not a multipart message, the function returns
@samp{text/plain}.
@end deftypefn

@anchor{MIME decoding}
@cindex MIME, decoding
@cindex decode MIME
Several functions are provided for decoding multi-part messages.  Such
decoding is governed by @code{Content-Transfer-Encoding} and
@code{Content-Type} headers of the message.  The
@code{Content-Transfer-Encoding} header defines the method used to
encode the message.  The value of @code{Content-Type} header is used
to determine the character set the body is written in.

@anchor{mimedecode}
@kwindex mimedecode
Basic MIME decoding facilities are provided by the built-in function
@code{message_body_to_stream}, described in the previous subsection.
To instruct it to decode the content, pass it the @var{filter_chain}
argument beginning with the word @code{mimedecode}.  The usual
sequence is:

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

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

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

The @code{charset} filter takes also an optional second argument -- a
@dfn{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:

@table @samp
@item none
Stop further conversion and signal the @code{e_ilseq} exception.

@item copy-pass
Copy the offending character to the output verbatim.

@item copy-octal
Represent the offending character as a C octal sequence
(@samp{\@var{n}@var{n}@var{n}}, where @var{n} is an octal digit).
This is the default.
@end table

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

@example
@group
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
@end group
@end example

@flindex mime.mfl
The @file{mime.mfl} module provides additional functions for decoding
multi-part messages:

@deftypefn {Library Function} number message_body_decode (number @var{nmsg}; @
  string @var{charset}, string @var{fallback})
Decodes the body of the message (or message part) @var{nmsg},
optionally converting it to the given @var{charset}.  The
@var{fallback} argument specifies what to do if a byte sequence cannot
be converted to the specified character set.  @xref{iconv fallback},
for a detailed discussion.

The function returns a descriptor of the I/O stream that contains the
decoded material.  @xref{I/O functions} for a discussion of functions
available for reading from it.
@end deftypefn

@deftypefn {Library Function} number message_part_decode(number @var{nmsg}, number @var{part}; @
  string @var{charset}, string @var{fallback})
Decodes the body of the given part of a MIME message @var{nmsg}.  The
argument @var{part} is a 1-based index of the part in the message.
Optional arguments @var{charset} and @var{fallback} have the same
meaning as in @code{message_body_decode} (see above).

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

This function is equivalent to:

@example
message_body_decode(message_get_part(@var{nmsg}, @var{part}, @var{charset},
                    @var{fallback}))
@end example
@end deftypefn

@node Message digest functions
@subsection Message digest functions
@cindex message digest
@cindex digest, message

@dfn{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 @dfn{forward}
function in most @acronym{MUA}'s.

The usual way to handle a message digest in @acronym{MFL} is to
convert it first to a @acronym{MIME} message, and then to use
functions for accessing its parts (@pxref{MIME functions}).

@deftypefn {Built-in Function} number message_burst (number @var{nmsg} ; @
          number @var{flags})
Converts the message identified by the descriptor @var{nmsg} to a
multi-part message.  Returns a descriptor of the created message.

Optional argument @var{flags} controls the behavior of the bursting
agent.  It is a bitwise @sc{or} of error action and bursting flags.

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

@kwindex BURST_DECODE
@dfn{Bursting flags} control various aspects of the agent behavior.
Currently only one flag is defined, @samp{BURST_DECODE}, which
instructs the agent to decode any MIME parts (according to the
@samp{Content-Transfer-Encoding} header) it encounters while bursting
the message.
@end deftypefn

@cindex encapsulation boundaries, RFC 934
Parts of a message digest are separated by so-called
@dfn{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
@dfn{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.

@deftypevr {Built-in variable} number burst_eb_min_length
Minimal number of consecutive dashes an encapsulation boundary
must begin with.

The default is 2.
@end deftypevr

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

@example
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
@end example

@node Quarantine functions
@section Quarantine Functions

@deftypefn {Built-in Function} void quarantine (string @var{text})
Place the message to the quarantine queue, using @var{text} as
explanatory reason.
@end deftypefn

@node SMTP Callout functions
@section SMTP Callout Functions

@deftypefn {Library Function} number callout_open (string @var{url})
Opens connection to the callout server listening at @var{url}.
Returns the descriptor of the connection.
@end deftypefn

@deftypefn {Library Function} void callout_close (number @var{fd})
Closes the connection.  @var{fd} is the file descriptor returned by
the previous call to @code{callout_open}.
@end deftypefn

@deftypefn {Library Function} number callout_do (number @var{fd}, @
  string @var{email} [, string @var{rest}])
Instructs the callout server identified by @var{fd} (a file descriptor
returned by a previous call to @code{callout_open}) to verify the
validity of the @var{email}.  Optional @var{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 @samp{=} sign.
@xref{callout parameters}, for a discussion of available callout
parameters.

Possible return values:

@table @code
@item 0
Success.  The @var{email} is found to be valid.
@item e_not_found
@var{email} does not exist.
@item 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.
@item e_failure
Some error occurred.
@end table

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

Upon return, @code{callout_do} modifies the following variables:

@table @code
@item last_poll_host
Host name or IP address of the last polled SMTP server.

@item last_poll_greeting
Initial SMTP reply from the last polled host.

@item last_poll_helo
The reply to the @code{HELO} (@code{EHLO}) command, received from the
last polled host.

@item last_poll_sent
Last @acronym{SMTP} command sent to the polled host.  If nothing was
sent, @code{last_poll_sent} contains the string @samp{nothing}.

@item last_poll_recv
Last @acronym{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 @samp{nothing}.
@end table
@end deftypefn

The @dfn{default callout server} is defined by the @code{callout-url}
statement in the configuration file, or by the @code{callout}
statement in the @code{server milter} section (@pxref{configuring
default callout server}.   The following functions operate on that server.

@deftypefn {Built-in Function} string default_callout_server_url ()
Returns URL of the default callout server.
@end deftypefn

@deftypefn {Library Function} number callout (string @var{email})
Verifies the validity of the @var{email} using the default callout
server.
@end deftypefn

@node Compatibility Callout functions
@section Compatibility Callout Functions
  The following functions are wrappers over the callout functions
described in the previous section.  They are provided for backward
compatibility.

@flindex poll.mfl
  These functions  are defined in the module @file{poll.mfl}, which you
must require prior to using any of them.

@deftypefn {Library Function} boolean _pollhost @
  (string @var{ip}, string @var{email}, string @var{domain}, @
   string @var{mailfrom})
  Poll @acronym{SMTP} host @var{ip} for email address @var{email},
using @var{domain} as @code{EHLO} domain and @var{mailfrom} as
@code{MAIL FROM}.  Returns 0 or 1 depending on the result of the test.
In contrast to the @code{strictpoll} function, this function does not use
cache database and does not fall back to polling @acronym{MX} servers if the
main poll tempfails.  The function can throw one of the following
exceptions: @code{e_failure}, @code{e_temp_failure}.
@end deftypefn

@deftypefn {Library Function} boolean _pollmx @
  (string @var{ip}, string @var{email}, string @var{domain}, @
   string @var{mailfrom})
  Poll @acronym{MX}s of the @var{domain} for email address @var{email}, using
@var{domain} as @code{EHLO} domain and @var{mailfrom} as @code{MAIL
FROM} address.  Returns 0 or 1 depending on the result of the test.
In contrast to the @code{stdpoll} function, @code{_pollmx} does
not use cache database and does not fall back to polling the @var{ip}
if the poll fails.  The function can throw one of the following
exceptions: @code{e_failure}, @code{e_temp_failure}.
@end deftypefn

@deftypefn {Library Function} boolean stdpoll @
  (string @var{email}, string @var{domain}, string @var{mailfrom})

  Performs standard poll for @var{email}, using @var{domain} as
@code{EHLO} domain and @var{mailfrom} as @code{MAIL FROM} address.
Returns 0 or 1 depending on the result of the test.  Can raise one of
the following exceptions: @code{e_failure}, @code{e_temp_failure}.

  In @code{on} statement context, it is synonymous to @code{poll}
without explicit @var{host}.  @FIXME{more details and references.}
@end deftypefn

@deftypefn {Library Function} boolean strictpoll @
  (string @var{host}, string @var{email}, @
   string @var{domain}, string @var{mailfrom})

  Performs strict poll for @var{email} on host @var{host}.
See the description of @code{stdpoll} for the detailed information.

In @code{on} context, it is synonymous to @code{poll host @var{host}}.
@end deftypefn

@cindex multiple sender addresses, using with polling commands.
@cindex trying several sender addresses
The @var{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 @samp{<>} address.  For example:

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

See also @ref{conf-callout, mail-from-address}.

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

@float Table, poll-variables-table
@caption{Variables set by polling functions}
@multitable @columnfractions 0.30 0.70
@headitem Variable @tab Contains
@cindex last_poll_host, global variable, introduced
@item last_poll_host @tab Host name or @acronym{IP} address of the last polled
host.
@cindex last_poll_sent, global variable, introduced
@item last_poll_sent @tab Last @acronym{SMTP} command, sent to this
host.  If nothing was sent, it contains literal string @samp{nothing}.
@cindex last_poll_recv, global variable, introduced
@item last_poll_recv @tab Last @acronym{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 @samp{nothing}.
@cindex cache_used, global variable, introduced
@item cache_used @tab @code{1} if cached data were used instead of
polling, @code{0} otherwise.  This variable is set by @code{stdpoll}
and @code{strictpoll}.  If it equals @code{1}, none of the above
variables are modified.  @xref{cache_used example}, for an example.
@end multitable
@end float

@node Internet address manipulation functions
@section Internet address manipulation functions

  Following functions operate on @acronym{IP}v4 addresses in
numeric form.

@deftypefn {Built-in Function} number ntohl (number @var{n})
Converts the number @var{n}, from host to network byte order.
The argument @var{n} is treated as an unsigned 32-bit number.
@end deftypefn

@deftypefn {Built-in Function} number htonl (number @var{n})
Converts the number @var{n}, from network to host byte order.
The argument @var{n} is treated as an unsigned 32-bit number.
@end deftypefn

@deftypefn {Built-in Function} number ntohs (number @var{n})
The argument @var{n} is treated as an unsigned 16-bit number.
The function converts this number from network to host order.
@end deftypefn

@deftypefn {Built-in Function} number htons (number @var{n})
The argument @var{n} is treated as an unsigned 16-bit number.
The function converts this number from host to network order.
@end deftypefn

@deftypefn {Built-in Function} number inet_aton (string @var{s})
Converts the Internet host address @var{s} from the standard
numbers-and-dots notation into the equivalent integer in host
byte order.

@example
inet_aton("127.0.0.1") @result{} 2130706433
@end example

@emph{The numeric data type in @acronym{MFL} is signed, therefore
on machines with 32 bit integers, this conversion can result in a
negative number:}

@example
inet_aton("255.255.255.255") @result{} -1
@end example

@emph{However, this does not affect arithmetical operations on
@acronym{IP} addresses.}
@end deftypefn

@deftypefn {Built-in Function} string inet_ntoa (number @var{n})
Converts the Internet host address @var{n}, given in host byte order to
string in standard numbers-and-dots notation:

@example
inet_ntoa(2130706433) @result{} "127.0.0.1"
@end example
@end deftypefn

@deftypefn {Built-in Function} number len_to_netmask (number @var{n})
Convert number of masked bits @var{n} to @acronym{IP}v4 netmask:

@example
inet_ntoa(len_to_netmask(24)) @result{} 255.255.255.0
inet_ntoa(len_to_netmask(7)) @result{} 254.0.0.0
@end example

If @var{n} is greater than 32 the function raises @code{e_range}
exception.
@end deftypefn

@deftypefn {Built-in Function} number netmask_to_len (number @var{mask})
Convert @acronym{IP}v4 netmask @var{mask} into netmask length (number of bits
preserved by the mask):

@example
netmask_to_len(inet_aton("255.255.255.0")) @result{} 24
netmask_to_len(inet_aton("254.0.0.0")) @result{} 7
@end example
@end deftypefn

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

@deftypefn {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 @var{ip} is an IPv4 address, the return value is
obtained by reversing the order of octets in the input:

@example
reverse_ipstr("192.0.2.10") @result{} "10.2.0.192"
@end example

If @var{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.:

@example
reverse_ipstr("2001:db8:0:0:1::2")
@result{}
"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"
@end example
@end deftypefn

@deftypefn {Built-in Function} number is_ipstr(string @var{s})
Returns 1 if @var{s} is a string representation of an IP address
(IPv4 or IPv6) and 0 otherwise.
@end deftypefn

@deftypefn {Built-in Function} number is_ip4str(string @var{s})
Returns 1 if @var{s} is a string representation of an IPv4 address,
and 0 otherwise.
@end deftypefn

@deftypefn {Built-in Function} number is_ip6str(string @var{s})
Returns 1 if @var{s} is a string representation of an IPv6 address,
and 0 otherwise.
@end deftypefn

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

@anchor{match_cidr}
@deftypefn {Built-in Function} boolean match_cidr (string @var{ip}, @
   string @var{cidr})
  It returns @code{true} if the @acronym{IP} address @var{ip} pertains to the
@acronym{IP} range @var{cidr}.  The first argument, @var{ip}, is a string
representation of an @acronym{IP} address (IPv4 or IPv6).  The second
argument, @var{cidr}, is a string representation of a @acronym{IP}
range in @acronym{CIDR} notation, i.e. @samp{@var{addr}/@var{N}},
where @var{addr} is IP address and @var{N} specifies address @dfn{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 @acronym{IP} address of
the sending machine does not belong to the block @code{192.0.2.0/24}:

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

  The following example does the same for a IPv6 CIDR
@code{2001:DB8::/56}:

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

Notice, that in previous versions of @command{mailfromd} this function
was implemented as MFL function and required the use of
@file{match_cidr.mfl} module (@pxref{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):

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

Just remove the @code{require 'match_cidr'} from your code to make
this warning disappear.
@end deftypefn

@node DNS functions
@section DNS Functions

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

Other functions provide a simplified API.

@node dns_query
@subsection dns_query

@deftypefn {Built-in Function} number dns_query (number @var{type}, string @var{domain}; number @var{sort}, number @var{resolve})
This function looks up the domain name @var{name}.  The @var{type}
argument specifies type of the query to perform.  On success, the
function returns @dfn{DNS reply descriptor}, a non-negative integer
number identifying the reply.  It can then be passed to any of the
@samp{dns_reply_} functions discussed below in order to retrieve the
information from it.

If no matching records were found, the function returns @samp{-1}.

On error, it throws a corresponding exception.

@flindex dns.mfl
The @var{type} argument is one of the following constants (defined in
the module @samp{dns}):

@table @code
@kwindex DNS_TYPE_A
@item DNS_TYPE_A
Query the @samp{A} record.  The @var{domain} should be the hostname to
look up.

@kwindex DNS_TYPE_NS
@item DNS_TYPE_NS
Query the @samp{NS} records.

@kwindex DNS_TYPE_PTR
@item DNS_TYPE_PTR
Query the @samp{PTR} record.  The @var{domain} address should be the IP
address in dotted-quad form.

@kwindex DNS_TYPE_MX
@item DNS_TYPE_MX
Query the @samp{MX} records.

@kwindex DNS_TYPE_TXT
@item DNS_TYPE_TXT
Query the @samp{TXT} records.
@end table

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

@flindex status.mfl
Optional argument @var{resolve} is consulted if @var{type} is
@code{DNS_TYPE_MX} or @code{DNS_TYPE_NS}.  By default, queries for
these types return hostnames.  The @var{resolve} argument controls
whether to return IP addresses instead.  Its possible values (defined
in module @file{status.mfl} are:

@anchor{ip resolve modes}
@table @code
@kwindex RESOLVE_NONE
@item RESOLVE_NONE
Don't resolve hostnames to IP addresses.  This is the default.

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

@kwindex RESOLVE_IP4
@item RESOLVE_IP4
Resolve hostnames to IPv4 addresses (@samp{A} records).

@kwindex RESOLVE_IP6
@item RESOLVE_IP6
Resolve hostnames to IPv6 addresses (@samp{AAAA} records).
@end table

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

@example
  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
@end example
@end deftypefn

@deftypefn {Built-in Function} void dns_reply_release (number @var{rd})
Release the memory associated with the reply @var{rd}.
If @var{rd} is -1, the function does nothing.
@end deftypefn

@deftypefn {Built-in Function} number dns_reply_count (number @var{rd})
Return the number of records in the reply @var{rd}.  For convenience,
if @var{rd} is -1, the function returns 0.  If @var{rd} is negative
(excepting -1), a @samp{e_failure} exception is thrown.
@end deftypefn

@deftypefn {Built-in Function} string dns_reply_string (number @var{rd}, number @var{n})
Returns @var{n}th record from the DNS reply @var{rd}.
@end deftypefn

@deftypefn {Built-in Function} number dns_reply_ip (number @var{rd}, number @var{n})
Returns @var{n}th record from the DNS reply @var{rd}, if the reply
contains IPv4 addresses.
@end deftypefn

@node Simplified DNS functions
@subsection Simplified DNS functions

  These functions are implemented in two layers: @dfn{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.

@flindex dns.mfl
  The built-in layer is always available.  The library calls become
available after requesting the @file{dns} module (@pxref{Modules}):

@example
require dns
@end example

@deftypefn {Library Function} string dns_getaddr (string @var{domain})
  Returns a whitespace-separated list of @acronym{IP} addresses (@code{A}
records) for @var{domain}.
@end deftypefn

@deftypefn {Library Function} string dns_getname (string @var{ipstr})
  Returns a whitespace-separated list of domain names (@code{PTR}
records) for the @acronym{IP}v4 address @var{ipstr}.
@end deftypefn

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

Examples:

@example
getmx("mafra.cz") @result{} "smtp1.mafra.cz smtp2.mafra.cz relay.iol.cz"
getmx("idnes.cz") @result{} "smtp1.mafra.cz smtp2.mafra.cz relay.iol.cz"
getmx("gnu.org")  @result{} "mx10.gnu.org mx20.gnu.org"
getmx("org.pl") @result{} ""
@end example

@emph{Notes}:
@enumerate 1

@item Number of items returned by @code{getmx(@var{domain})} can
differ from that obtained from @code{getmx(@var{domain}, 1)}, e.g.:

@example
@group
getmx("aol.com")
  @result{} mailin-01.mx.aol.com mailin-02.mx.aol.com
            mailin-03.mx.aol.com mailin-04.mx.aol.com
getmx("aol.com", 1)
  @result{} 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
@end group
@end example

@item This function is a wrapper over @code{dns_query}.

If you intend to iterate over returned values, better use
@code{dns_query} directly, e.g. instead of doing

@example
string_list_iterate(getmx(domain), ` ', MX, `do_something(MX)')
@end example

@noindent
use

@example
@group
  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
@end group
@end example

@xref{dns_query}, for details about the @code{dns_query} function and
associated @code{dns_reply_*} calls.

@item This interface is semi-deprecated.

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

@anchor{primitive_hasmx}
@deftypefn {Built-in Function} boolean primitive_hasmx (string @var{domain})
  Returns @code{true} if the domain name given by its argument
has any @samp{MX} records.

  If the @acronym{DNS} query fails, this function throws @code{failure} or
@code{temp_failure}.
@end deftypefn

@anchor{hasmx}
@deftypefn {Library Function} boolean hasmx (string @var{domain})
  Returns @code{true} if the domain name given by its argument
has any @samp{MX} records.

  Otherwise, if @var{domain} has no @samp{MX}s or if the @acronym{DNS} query fails,
@code{hasmx} returns @code{false}.
@end deftypefn

@deftypefn {Built-in Function} string primitive_hostname (string @var{ip})
  The @var{ip} argument should be a string representing an @acronym{IP} address in
@dfn{dotted-quad} notation.  The function returns the canonical name of
the host with this @acronym{IP} address obtained from @acronym{DNS} lookup.  For example

@example
primitive_hostname ($@{client_addr@})
@end example

@noindent
returns the fully qualified domain name of the host represented by
Sendmail variable @samp{client_addr}.

  If there is no @samp{PTR} record for @var{ip}, @code{primitive_hostname}
raises the exception @code{e_not_found}.

  If @acronym{DNS} query fails, the function raises @code{failure} or
@code{temp_failure}, depending on the character of the failure.
@end deftypefn

@anchor{hostname function}
@deftypefn {Library Function} string hostname (string @var{ip})
  The @var{ip} argument should be a string representing an @acronym{IP} address in
@dfn{dotted-quad} notation.  The function returns the canonical name of
the host with this @acronym{IP} address obtained from @acronym{DNS} lookup.

  If there is no @samp{PTR} record for @var{ip}, or if the lookup fails,
the function returns @var{ip} unchanged.

  The previous @command{mailfromd} versions used the following
paradigm to check if an @acronym{IP} address resolves:

@example
  if hostname(ip) != ip
    ...
@end example

@end deftypefn

@anchor{primitive_ismx}
@deftypefn {Built-in Function} boolean primitive_ismx (string @var{domain}, @
 string @var{host})
  The @var{domain} argument is any valid domain name, the @var{host}
is a host name or @acronym{IP} address.

  The function returns @code{true} if @var{host} is one of the @samp{MX}
records for the @var{domain}.

  If @var{domain} has no @samp{MX} records, @code{primitive_ismx} raises
exception @code{e_not_found}.

  If @acronym{DNS} query fails, the function raises @code{failure} or
@code{temp_failure}, depending on the character of the failure.
@end deftypefn

@anchor{ismx}
@deftypefn {Library Function} boolean ismx (string @var{domain}, @
 string @var{host})
  The @var{domain} argument is any valid domain name, the @var{host}
is a host name or @acronym{IP} address.

  The function returns @code{true} if @var{host} is one of the @samp{MX}
records for the @var{domain}.  Otherwise it returns @code{false}.

  If @var{domain} has no @samp{MX} records, or if the @acronym{DNS} query fails, the
function returns @code{false}.
@end deftypefn

@deftypefn {Built-in Function} string primitive_resolve (string @var{host}, @
 [string @var{domain}, number @var{family}])
  Reverse of @code{primitive_hostname}.  The @code{primitive_resolve} function
returns the @acronym{IP} address for the host name specified by its @var{host}
argument.  If the SMTP session uses IPv4 protocol, @samp{A} record
is queried.  It it uses IPv6, @samp{AAAA} record is queried.  A
particular record type can be requested via optional @var{family},
which can have one of the following values (defined in
@file{status.mfl}):

@table @code
@kwindex RESOLVE_DFL
@item RESOLVE_DFL
Look for @samp{A} or @samp{AAAA}, depending on the connection type.
This is the default.

@kwindex RESOLVE_IP4
@item RESOLVE_IP4
Resolve to IPv4 addresses (@samp{A} records).

@kwindex RESOLVE_IP6
@item RESOLVE_IP6
Resolve to IPv6 addresses (@samp{AAAA} records).
@end table

  If @var{host} has no records of the requested type, the function raises the
exception @code{e_not_found}.

  If @acronym{DNS} lookup fails, the function raises @code{failure} or
@code{temp_failure}, depending on the character of the failure.

  Optional @var{domain} argument is deprecated.  If a non-empty string
is given as @var{domain}, the function works as follows:

@enumerate 1
@item If @var{domain} is @samp{in-addr.arpa} (case-insensitive)

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

Thus, the call

@example
primitive_resolve("192.0.2.1", "in-addr.arpa")
@end example

@noindent
is equivalent to

@example
primitive_hostname("192.0.2.1")
@end example

@item @var{domain} is @samp{ip6.arpa} (case-insensitive)

The @var{host} must be a string representation of an IPv6.  If it is
not, a @code{e_inval} exception is thrown.  The octets of this IPv6
address are reversed, a dot and @samp{domain} are appended to it, and
a @samp{PTR} record is queried for the resulting name.

Thus, the call

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

@noindent
is equivalent to

@example
primitive_hostname("2001:DB8::1")
@end example

@item @var{host} is a string representation of an IPv4 address

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

Thus,

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

@noindent
is equivalent to

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

@item @var{host} is a string representation of an IPv6 address

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

@item None of the above.
Same as @code{primitive_hostname(@samp{@var{host}.@var{domain}},'',@var{resolve})}.
@end enumerate
@end deftypefn

@deftypefn {Library Function} string resolve (string @var{host}, @
 [string @var{domain}, number @var{family}])
  Reverse of @code{hostname}.  The @code{resolve} function
returns @acronym{IP} address for the host name specified by @var{host}
argument.  If the host name cannot be resolved, or a @acronym{DNS} failure
occurs, the function returns @samp{"0"}.

  This function is entirely equivalent to @code{primitive_resolve}
(see above), except that it never raises exceptions.
@end deftypefn

@deftypefn {Built-in Function} string ptr_validate (string @var{ip})
Tests whether the DNS reverse-mapping for @var{ip} exists and
correctly points to a domain name within a particular domain.

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

This function can raise the following exceptions:

@table @code
@item e_not_found
Unable to resolve IP address or hostname.
@item e_failure
Fatal error while resolving.
@item e_temp_failure
Temporary error in DNS resolution.
@item e_too_many
Too many CNAME records (@pxref{CNAME chains}).
@end table
@end deftypefn

@deftypefn {Built-in Function} boolean primitive_hasns (string @var{domain})
Returns @samp{True} if the domain @var{domain} has at least one
@samp{NS} record. Throws exception if DNS lookup fails.
@end deftypefn

@deftypefn {Library Function} boolean hasns (string @var{domain})
Returns @samp{True} if the domain @var{domain} has at least one
@samp{NS} record. Returns @samp{False} if there are no @samp{NS}
records or if the DNS lookup fails.
@end deftypefn

@deftypefn {Library Function} string getns (string @var{domain} ; @
                                 boolean @var{resolve}, boolean @var{sort})
  Returns a whitespace-separated list of all the @samp{NS} records for
the domain @var{domain}. Optional parameters @var{resolve} and
@var{sort} control the formatting. If @var{resolve} is 0 (the default), the
resulting string will contain IP addresses of the NS servers. If
@var{resolve} is not 0, hostnames will be returned instead. If
@var{sort} is 1, the returned items will be sorted.

If the @acronym{DNS} query fails, @code{getns} raises an appropriate
exception.

@emph{Notes}:
@enumerate 1
@item This function is a wrapper over @code{dns_query}.

If you intend to iterate over returned values, better use
@code{dns_query} directly, e.g. instead of doing

@example
string_list_iterate(getns(domain), ` ', NS, `do_something(NS)')
@end example

@noindent
use

@example
@group
  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
@end group
@end example

@xref{dns_query}, for details about the @code{dns_query} function and
associated @code{dns_reply_*} calls.

@item This interface is semi-deprecated.

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

@node Geolocation functions
@section Geolocation functions
@cindex geolocation
@cindex GeoIP2
@flindex libmaxminddb
@kwindex WITH_GEOIP2
  The @dfn{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 @code{libmaxminddb} library is installed and
@command{mailfromd} is compiled with the @samp{GeoIP2} support.

  The @code{libmaxminddb} library is distributed by @samp{MaxMind} under
the terms of the @cite{Apache License} Version 2.0.  It is available
from @uref{https://dev.maxmind.com/geoip/geoip2/downloadable/#MaxMind_APIs}.

@deftypefn {Built-in Function} void geoip2_open (string @var{filename})
Opens the geolocation database file @var{filename}.  The database must
be in GeoIP2 format.

If the database cannot be opened, @code{geoip2_open} throws the
@code{e_failure} exception.

If this function is not called, geolocation functions described below
will try to open the database file @samp{/usr/share/GeoIP/GeoLite2-City.mmdb}.
@end deftypefn

@deftypefn {Built-in Function} string geoip2_dbname (void)
Returns the name of the geolocation database currently in use.
@end deftypefn

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:

@example
@group
@{
    "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@"urnberg",
            "fr":"Nuremberg"
        @}
    @},
    "subdivisions":[@{
        "geoname_id":2951839,
        "iso_code":"BY",
        "names":@{
            "en":"Bavaria",
            "de":"Bayern",
            "fr":"Bavi@`ere"
        @}
    @}
@}
@end group
@end example

Each particular data item in such structure is identified by its
@dfn{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 @code{city.names.en}.

@deftypefn {Built-in Function} string geoip2_get (string @var{ip}, string @var{path})
Looks up the IP address @var{ip} in the geolocation database.  If
found, returns data item identified by the search path @var{path}.

The function can throw the following exceptions:

@table @asis
@item e_not_found
The @var{ip} was not found in the database.

@item e_range
The @var{path} does not exist the returned map.

@item e_failure
General error occurred.  E.g. the database cannot be opened, @var{ip}
is not a valid IP address, etc.
@end table
@end deftypefn

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

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

@example
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
')
@end example

@node Database functions
@section Database Functions

The functions described below provide a user interface to
@acronym{DBM} databases.

Each @acronym{DBM} database is a separate disk file that keeps
@dfn{key/value pairs}.  The interface allows to retrieve the value
corresponding to a given key.  Both @samp{key} and @samp{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 @dfn{file mode} of the
database file.  The default file mode is @samp{640}
(i.e.@: @samp{rw-r----}, in symbolic notation).

These and other properties can be configured using the @code{dbprop} pragma:

@kwindex dbprop,
@cindex dbprop, pragma
@cindex #pragma dbprop
@example
#pragma dbprop @var{pattern} @var{prop} [@var{prop}]
@end example

The @var{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 @code{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 @var{prop} are:

@enumerate 1
@item
The word @samp{null}, meaning that the terminating null byte is
included in the key length.

Setting @samp{null} property is necessary, for databases created with
@command{makemap -N hash} command.

@item
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:

@example
@group
640
rw-r----
@end group
@end example

@item
@dfn{DBM scheme}, which specifies the type of the DBM (@pxref{Database
Formats}) to use for this database.  The scheme consists of DBM type
name followed by a colon and two slashes.  E.g. @samp{gdbm://} means
a GDBM database.  @xref{DBM scheme}, for a detailed discussion.
@end enumerate

For example, consider the following pragmas:

@example
#pragma dbprop /etc/mail/whitelist.db 640
@end example

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

Similarly, the following pragma:

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

@noindent
declares that all database files in directory @file{/etc/mail} are
Berkeley DB files, have privileges @samp{640}, and include null terminator
in the key length.  @emph{Notice}, the use of @command{m4} quoting
characters in the example below.  Without them, the sequence @samp{/*}
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
@var{null}).

@anchor{dbmap}
@deftypefn {Built-in Function} boolean dbmap (string @var{db}, @
    string @var{key}, [boolean @var{null}])
  Looks up @var{key} in the @acronym{DBM} file @var{db} and returns
@code{true} if it is found.

  See above for the meaning of @var{null}.

@xref{whitelisting}, for an example of using this function.
@end deftypefn

@deftypefn {Built-in Function} string dbget (string @var{db}, @
  string @var{key} [, string @var{default}, boolean @var{null}])
  Looks up @var{key} in the database @var{db} and returns the value
associated with it.  If the key is not found returns @var{default}, if
specified, or empty string otherwise.

  See above for the meaning of @var{null}.
@end deftypefn

@deftypefn {Built-in Function} void dbput (string @var{db}, @
 string @var{key}, string @var{value} [, @
 boolean @var{null}, number @var{mode} ])
  Inserts in the database a record with the given @var{key} and
@var{value}.  If a record with the given @var{key} already exists, its
value is replaced with the supplied one.

  See above for the meaning of @var{null}.  Optional @var{mode} allows
to explicitly specify the file mode for this database.  See also
@code{#pragma dbprop}, described above.
@end deftypefn

@deftypefn {Built-in Function} void dbinsert (string @var{db}, @
 string @var{key}, string @var{value} [, @
 boolean @var{replace}, boolean @var{null}, number @var{mode} ])
  This is an improved variant of @code{dbput}, which provides a
better control on the actions to take if the @var{key} already exists in the
database.  Namely, if @var{replace} is @samp{True}, the old value is
replaced with the new one.  Otherwise, the @samp{e_exists} exception
is thrown.
@end deftypefn

@deftypefn {Built-in Function} void dbdel (string @var{db}, @
 string @var{key} [, boolean @var{null}, number @var{mode}])
  Delete from the database the record with the given @var{key}.  If
there are no such record, return without signalling error.

  If the optional @var{null} argument is given and is not zero, the
terminating null character will be included in @var{key} length.

  Optional @var{mode} allows to explicitly specify the file mode for
this database.  See also @code{#pragma dbprop}, described above.
@end deftypefn

@flindex safedb.mfl
The functions above have also the corresponding exception-safe
interfaces, which return cleanly if the @samp{e_dbfailure} exception
occurs.  To use these interfaces, request the @file{safedb} module:

@example
require safedb
@end example

The exception-safe interfaces are:

@deftypefn {Library Function} number safedbmap (string @var{db}, @
  string @var{key} [, number @var{default}, boolean @var{null}])

  This is an exception-safe interface to @code{dbmap}.  If a
database error occurs while attempting to retrieve the record,
@code{safedbmap} returns @var{default} or @samp{0}, if it is
not defined.
@end deftypefn

@deftypefn {Library Function} string safedbget (string @var{db}, @
  string @var{key} [, string @var{default}, boolean @var{null}])

  This is an exception-safe interface to @code{dbget}.  If a
database error occurs while attempting to retrieve the record,
@code{safedbget} returns @var{default} or empty string, if it is
not defined.
@end deftypefn

@deftypefn {Library Function} void safedbput (string @var{db}, @
 string @var{key}, string @var{value} [, boolean @var{null}])

  This is an exception-safe interface to @code{dbput}.  If a
database error occurs while attempting to retrieve the record,
the function returns without raising exception.
@end deftypefn

@deftypefn {Library Function} void safedbdel (string @var{db}, @
 string @var{key} [, boolean @var{null}])

  This is an exception-safe interface to @code{dbdel}.  If a
database error occurs while attempting to delete the record,
the function returns without raising exception.
@end deftypefn

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

@anchor{dbm-seq}
The following functions provide a sequential access to the contents of
a @acronym{DBM} database:

@deftypefn {Built-in Function} number dbfirst (string @var{name})
Start sequential access to the database @var{name}.  The return value
is an opaque identifier, which is used by the remaining sequential
access functions.  This number is @samp{0} if the database is empty.
@end deftypefn

@deftypefn {Built-in Function} number dbnext (number @var{dn})
Select next record form the database.  The argument @var{dn} is the
access identifier, returned by a previous call to @code{dbfirst} or
@code{dbnext}.

Returns new access identifier.  This number is @samp{0} if all records
in the database have been visited.
@end deftypefn

 The usual approach for iterating over all records in a database
@var{dbname} is:

@example
@group
  loop for number dbn dbfirst(@var{dbname})
  do
    @dots{}
  done while dbnext(dbn)
@end group
@end example

  The following two functions can be used to access values of the
currently selected database record.  Their argument, @var{dn}, is the
access identifier, returned by a previous call to @code{dbfirst} or
@code{dbnext}.

@deftypefn {Built-in Function} string dbkey (number @var{dn})
Return the key from the selected database record.
@end deftypefn

@deftypefn {Built-in Function} string dbvalue (number @var{dn})
Return the value from the selected database record.
@end deftypefn

@deftypefn {Built-in Function} void dbbreak (number @var{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:

@example
@group
  loop for number dbn dbfirst(@var{dbname})
  do
    if some_condition
      dbbreak(dbn)
      break
    fi
  done while dbnext(dbn)
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} number db_expire_interval (string @var{fmt})
  The @var{fmt} argument is a database format identifier
(@pxref{Database Formats}).  If it is valid, the function returns the
expiration interval for that format.  @FIXME{How to obtain negative
expiration??} Otherwise, @code{db_expire_interval} raises the
@code{e_not_found} exception.
@end deftypefn

@deftypefn {Built-in Function} string db_name (string @var{fmtid})
  The @var{fmtid} argument is a database format identifier
(@pxref{Database Formats}).  The function returns the file name
for that format.  If @var{fmtid} does not match any known format,
@code{db_name} raises the @code{e_not_found} exception.
@end deftypefn

@cindex getting cache status
@cindex cache, getting status
@deftypefn {Built-in Function} number db_get_active (string @var{fmtid})
  Returns the flag indicating whether the cache database @var{fmtid}
is currently enabled.  If @var{fmtid} does not match any known format,
@code{db_name} raises the @code{e_not_found} exception.
@end deftypefn

@cindex disabling cache
@cindex cache, disabling
@deftypefn {Built-in Function} void db_set_active (string @var{fmtid}, @
                                         boolean @var{enable})
  Enables the cache database @var{fmtid} if @var{enable} is @samp{True},
or disables it otherwise.  For example, to disable @acronym{DNS}
caching, do:

@example
db_set_active("dns", 0)
@end example
@end deftypefn

@deftypefn {Built-in Function} boolean relayed (string @var{domain})
@anchor{relayed}
  Returns @code{true} if the string @var{domain} is found in one of
relayed domain files (@pxref{conf-base, relayed-domain-file}).  The
usual construct is:

@example
if relayed(hostname($@{client_addr@}))
  @dots{}
@end example

@noindent
which yields @code{true} if the @acronym{IP} address from @command{Sendmail} variable
@samp{client_addr} is relayed by the local machine.
@end deftypefn

@node Control database
@section Control Database
@flindex cdb.mfl
  @dfn{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 @file{cdb.mfl}.

@deftypevr {cdb variable} string cdb_name
Name of the database file to use as control database.  By default
it is @file{/etc/mail/mfctl.db}.
@end deftypevr

@deftypevr {cdb variable} number cdb_greylist_interval
Greylisting interval, for database records that prescribe greylisting
(see below).  Defaults to 900 seconds.
@end deftypevr

@deftypefn {Library Function} void cdb_check (string @var{prefix}, string @var{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 @var{prefix}:

@table @asis
@item ip
The @var{addr} argument must be an IP address.  The look up key is
@samp{ip:@var{addr}}.

@item email
The @var{addr} argument is an email address.  The key is
@samp{email:@var{cemail}}, where @var{cemail} is @var{addr} in
@dfn{canonical form}, obtained by converting its domain part to lower
case.

@item domain
The @var{addr} argument is an email address.  The key is formed as
@samp{domain:@var{dom}}, where @var{dom} is domain part of @var{addr}
converted to lower case.

@item subdomain
Initial key value is obtained as for @samp{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 @var{addr},
@var{x}, for which the key @samp{domain:@var{x}} exists in the database.

@item mx
If @var{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 @var{host} from that list the key
@samp{mx:@var{host}} is looked up.  First entry found is used.
@end table

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

@table @asis
@item CONTINUE
Continue to the next milter state.  @xref{continue}.

@item ACCEPT
Accept the mail.  @xref{accept}.

@item REJECT
Reject the mail.  @xref{reject}.

@item TEMPFAIL
Return a temporary failure.  @xref{tempfail}.

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

@item @var{code} @var{xcode} @var{text}
@itemx @var{code} @var{text}
@itemx @var{text}
Here, @var{code} is SMTP response code, @var{xcode} is extended
STMP response code, and @var{text} is an arbitrary text.  If
@var{code} is given, it must begin with @samp{4} or @samp{5}.  Its
first digit defines the action to be taken: temporary failure (if
@samp{4}) or reject (if @samp{5}).  If @var{xcode} is given, its first
digit must match that of @var{code}.

If only @var{text} is supplied, it is equivalent to

@example
reject(550, 5.1.0, @var{text})
@end example
@end table
@end deftypefn

@node System functions
@section System functions

@deftypefn {Built-in Function} boolean access (string @var{pathname}, @
  number @var{mode})
Checks whether the calling process can access the file @var{pathname}.
If @var{pathname} is a symbolic link, it is dereferenced.  The
function returns @samp{True} if the file can be accessed and
@samp{False} otherwise@footnote{@emph{Note}, that the return code is
inverted in respect to the system function @samp{access(2)}.}.

Symbolic values for @var{mode} are provided in module
@file{status}:

@table @asis
@kwindex F_OK
@item F_OK
Tests for the existence of the file.

@kwindex R_OK
@item R_OK
Tests whether the file exists and grants read permission.

@kwindex W_OK
@item W_OK
Tests whether the file exists and grants write permission.

@kwindex X_OK
@item X_OK
Tests whether the file exists and grants execute permission.
@end table
@end deftypefn

@deftypefn {Built-in Function} string getenv (string @var{name})
Searches the environment list for the variable @var{name} and returns
its value.  If the variable is not defined, the function raises the
exception @samp{e_not_found}.
@end deftypefn

@deftypefn {Built-in Function} string gethostname ([bool @var{fqn}])
Return the host name of this machine.

If the optional @var{fqn} is given and is @samp{true}, the function
will attempt to return fully-qualified host name, by attempting to
resolve it using @acronym{DNS}.
@end deftypefn

@deftypefn {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 @acronym{DNS}.

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

@flindex localdomain.mfl
@deftypefn {Library Function} string localdomain ()
Return the local domain name of this machine.

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

To use this function, require the @file{localdomain} module
(@pxref{Modules}), e.g.: @code{require localdomain}.
@end deftypefn

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

@anchor{strftime}
@deftypefn {Built-in Function} string strftime (string @var{fmt}, @
 number @var{timestamp})
@deftypefnx {Built-in Function} string strftime (string @var{fmt}, @
 number @var{timestamp}, boolean @var{gmt})
  Formats the time @var{timestamp} (seconds since the Epoch) according
to the format specification @var{format}.   Ordinary characters placed
in the format string are copied to the output without conversion.
Conversion specifiers are introduced by a @samp{%} character.
@xref{Time and Date Formats}, for a detailed description of the
conversion specifiers.  We recommend using single quotes
around @var{fmt} to prevent @samp{%} specifiers from being interpreted
as @code{Mailfromd} variables (@xref{Literals}, for a
discussion of quoted literals and variable interpretation within
them).

  The @var{timestamp} argument can be a return value of @code{time}
function (see above).

For example:
@example
@group
strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564)
 @result{} 2006-11-25 19:59:24 EET
strftime('%Y-%m-%d %H:%M:%S %Z', 1164477564, 1)
 @result{} 2006-11-25 17:59:24 GMT
@end group
@end example
@end deftypefn

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

@table @asis
@item %s
Name of this system.

@item %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 @acronym{DNS}.

@item %r
Kernel release.

@item %v
Kernel version.

@item %m
Name of the hardware type on which the system is running.
@end table

For example:

@example
  uname('%n runs %s, release %r on %m')
    @result{} "Trurl runs Linux, release 2.6.26 on i686"
@end example

Notice the use of single quotes.

@end deftypefn

@deftypefn {Built-in Function} void unlink (string @var{name})
Unlinks (deletes) the file @var{name}.  On error, throws the
@code{e_failure} exception.
@end deftypefn

@deftypefn {Built-in Function} number system (string @var{str})
  The function @code{system} executes a command specified in @var{str}
by calling @command{/bin/sh -c string}, and returns -1 on error or
the return status of the command otherwise.
@end deftypefn

@deftypefn {Built-in Function} void sleep (number @var{secs}[, @var{usec}])
Sleep for @var{secs} seconds.  If optional @var{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:

@example
  sleep(1,500000)
@end example

This function is intended mostly for debugging and experimental purposes.
@end deftypefn

@deftypefn {Built-in Function} number umask (number @var{mask})
Set the umask to @var{mask} & 0777.  Return the previous value of the
mask.
@end deftypefn

@node Passwd functions
@section System User Database

@deftypefn {Built-in Function} string getpwnam (string @var{name})
@deftypefnx {Built-in Function} string getpwuid (number @var{uid})
Look for the user @var{name} (@code{getpwnam}) or user ID @var{uid}
(@code{getpwuid}) in the system password database and return the
corresponding record, if found.  If not found, raise the
@samp{e_not_found} exception.

The returned record consists of six fields, separated by colon sign:

@example
uname:passwd:uid:gid:gecos:dir:shell
@end example

@multitable @columnfractions 0.3 0.6
@headitem Field @tab Meaning
@item uname     @tab user name
@item passwd    @tab user password
@item uid       @tab user ID
@item gid       @tab group ID
@item gecos     @tab real name
@item dir       @tab home directory
@item shell     @tab shell program
@end multitable

For example:

@example
getpwnam("gray")
@result{} "gray:x:1000:1000:Sergey Poznyakoff:/home/gray:/bin/bash"
@end example
@end deftypefn

Following two functions can be used to test for existence of a key in
the user database:

@deftypefn {Built-in Function} boolean mappwnam (string @var{name})
@deftypefnx {Built-in Function} boolean mappwuid (number @var{uid})
Return @samp{true} if @var{name} (or @var{uid}) is found in the system
user database.
@end deftypefn

@node Sieve Interface
@section Sieve Interface
@cindex Sieve

  @samp{Sieve} is a powerful mail filtering language, defined in
@acronym{RFC} 3028.  @command{Mailfromd} supports an extended form
of this language.  For a description of the language and available
extensions, see @ref{Sieve Language, Sieve Language, Sieve Language,
mailutils, GNU Mailutils Manual}.

@deftypefn {Built-in Function} boolean sieve (number @var{msg}, @
           string @var{script} @
           [, number @var{flags}, string @var{file}, number @var{line}])
Compile the Sieve program @var{script} and execute it over the
message identified by the descriptor @var{nmsg}.

@flindex sieve.mfl
Optional @var{flags} modify the behavior of the function.  It is a
bit-mask field, consisting of a bitwise @code{or} of one or more of
the following flags, defined in @file{sieve.mfl}:

@table @code
@kwindex MF_SIEVE_FILE
@item MF_SIEVE_FILE
The @var{script} argument specifies the name of a Sieve program file.
This is the default.

@kwindex MF_SIEVE_TEXT
@item MF_SIEVE_TEXT
The @var{script} argument is a string containing entire Sieve program.
Optional arguments @var{file} and @var{line} can be used to fix source
locations in Sieve diagnostic messages (see below).

@kwindex MF_SIEVE_LOG
@item MF_SIEVE_LOG
Log every executed @samp{Sieve} action.

@kwindex MF_SIEVE_DEBUG_TRACE
@item MF_SIEVE_DEBUG_TRACE
Trace execution of @samp{Sieve} tests.

@kwindex MF_SIEVE_DEBUG_INSTR
@item MF_SIEVE_DEBUG_INSTR
Log every instruction, executed in the compiled @samp{Sieve} code.
This produces huge amounts of output and is rarely useful, unless you
suspect some bug in @samp{Sieve} implementation and wish to trace it.
@end table

For example, @code{MF_SIEVE_LOG|MF_SIEVE_DEBUG_TRACE} enables logging
@samp{Sieve} actions and tests.

The @code{sieve} function returns @code{true} if the message was
accepted by the @var{script} program, and @code{false} otherwise.
Here, the word @dfn{accepted} means that some form of @samp{KEEP}
action (@pxref{Actions, keep, Actions, mailutils, GNU Mailutils
Manual}) was executed over the message.

While executing the Sieve script, Sieve environment (@cite{RFC 5183})
is initialized as follows:

@table @asis
@item domain
The domain name of the server Sieve is running on.
@item host
Host name of the server Sieve is running on.
@item location
The string @samp{MTA}.
@item name
The string @samp{GNU Mailutils}.
@item phase
The string @samp{pre}.
@item remote-host
Defined to the value of @samp{client_ptr} macro, if it was required.
@item remote-ip
Defined to the value of @samp{client_addr} macro, if it was required.
@item version
The version of GNU Mailutils.
@end table
@end deftypefn

The following example discards each message not accepted by the
@samp{Sieve} program @file{/etc/mail/filter.siv}:

@example
require 'sieve'

group eom
do
  if not sieve(current_message(), "/etc/mail/filter.siv", MF_SIEVE_LOG)
     discard
  fi
done
@end example

The Sieve program can be embedded in the MFL filter, as shown in the
example below:

@example
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
@end example

In such cases, any Sieve diagnostics (error messages, traces, etc.)
will be marked with the locations relative to the line where the call
to @code{sieve} appears.  For example, the above program produces the
following in the log:

@example
prog.mfl:7: FILEINTO; delivering into /tmp/sieved.mbox
@end example

Notice, that the line number correctly refers to the line where the
@code{fileinto} action appears in the source.  However, there are
cases where the reported line number is incorrect.  This happens,
for instance, if @var{script} is a string variable defined elsewhere.
To handle such cases, @code{sieve} accepts two optional parameters
which are used to compute the location in the Sieve program.  The
@var{file} parameter specifies the file name where the definition of
the program appears, and the @var{line} parameter gives the number of
line in that file where the program begins.  For example:

@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
@end example

The actual Sieve program begins two lines below the
@code{sieve_prog_line} constant definition, which is reflected in its
initialization.

@node Interfaces to Third-Party Programs
@section Interfaces to Third-Party Programs

  A set of functions is defined for interfacing with other filters via
@acronym{TCP}.  Currently implemented are interfaces with
@command{SpamAssassin} @command{spamd} daemon and with
@command{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 @code{true}.  Notice that in practice that means that such a
message @emph{should be rejected or deferred}.

  The address of the remote filter is supplied as the second argument
in the form of a standard @acronym{URL}:

@example
@var{proto}://@var{path}[:@var{port}]
@end example

@noindent
The @var{proto} part specifies the @dfn{connection protocol}.  It
should be @samp{tcp} for the @acronym{TCP} connection and @samp{file}
or @samp{socket} for the connection via @acronym{UNIX} socket.  In the latter
case the @var{proto} part can be omitted.  When using @acronym{TCP}
connection, the @var{path} part gives the remote host name or @acronym{IP}
address and the optional @var{port} specifies the port number or
service name to use.  For example:

@example
# @r{connect to @samp{remote.filter.net} on port 3314}:
tcp://remote.filter.net:3314

# @r{the same, using symbolic service name (must be defined in
# @file{/etc/services}):}
tcp://remote.filter.net:spamd

# @r{Connect via a local @acronym{UNIX} socket (equivalent forms):}
/var/run/filter.sock
file:///var/run/filter.sock
socket:///var/run/filter.sock
@end example

  The description of the interface functions follows.

@menu
* SpamAssassin::
* ClamAV::
@end menu

@node SpamAssassin
@subsection SpamAssassin
@anchor{sa}
@cindex SpamAssassin
@cindex spamd
@deftypefn {Built-in Function} boolean spamc (number @var{msg}, @
            string @var{url}, number @var{prec}, number @var{command})
  Send the message @var{msg}t to the SpamAssassin daemon (@code{spamd})
listening on the given @var{url}.  The @var{command} argument
identifies what kind of processing is needed for the message.  Allowed
values are:

@table @asis
@kwindex SA_SYMBOLS
@item 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 @code{sa_keywords} (see below).

@kwindex SA_REPORT
@item 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 @code{sa_keywords}.

@kwindex SA_LEARN_SPAM
@item SA_LEARN_SPAM
Learn the supplied message as spam.

@kwindex SA_LEARN_HAM
@item SA_LEARN_HAM
Learn the supplied message as ham.

@kwindex SA_FORGET
@item SA_FORGET
Forget any prior classification of the message.
@end table

The second argument, @var{prec}, gives the precision, in decimal
digits, to be used when converting SpamAssassin diagnostic data and
storing them into @command{mailfromd} variables.

@anchor{sa-floating-point-conversion}
The floating point SpamAssassin data are converted to the integer
@command{mailfromd} variables using the following relation:

@example
@var{var} = int(@var{sa-var} * 10**@var{prec})
@end example

@noindent
where @var{sa-var} stands for the SpamAssassin value and @var{var}
stands for the corresponding @command{mailfromd} one.  @code{int()}
means taking the integer part and @samp{**} denotes the exponentiation
operator.

  The function returns additional information via the following
variables:

@table @code
@cindex sa_score, global variable
@item sa_score
The spam score, converted to integer as described above.  To convert
it to a floating-point representation, use @code{sa_format_score}
function (@pxref{String manipulation, sa_format_score}).  See also the
example below.

@cindex sa_threshold, global variable
@item sa_threshold
The threshold, converted to integer form.

@cindex sa_keywords, global variable
@item sa_keywords
If @var{command} is @samp{SA_SYMBOLS}, this variable contains a
string of comma-separated SpamAssassin keywords identifying this
message, e.g.:

@example
ADVANCE_FEE_1,AWL,BAYES_99
@end example

If @var{command} is @samp{SA_REPORT}, the value of this variable is
a @dfn{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:

@enumerate
@item A preamble.
@item Content preview.

The words @samp{Content preview}, followed by a colon and an excerpt
of the message body.

@item Content analysis details.

It has the following form:

@example
Content analysis details: (@var{score} points, @var{max} required)
@end example

@noindent
where @var{score} and @var{max} are spam score and threshold in
floating point.

@item Score table.

The score table is formatted in three columns:

@table @asis
@item pts
The score, as a floating point number with one decimal digit.

@item rule name
SpamAssassin rule name that contributed this score.

@item description
Textual description of the rule
@end table

The score table can be extracted from @code{sa_keywords} using
@code{sa_format_report_header} function  (@pxref{String manipulation,
sa_format_report_header}), as illustrated in the example below.
@end enumerate

The value of this variable is undefined if @var{command} is
@samp{SA_LEARN_SPAM}, @samp{SA_LEARN_HAM} or @samp{SA_FORGET}.
@end table

The @code{spamc} function can signal the following exceptions:
@code{e_failure} if the connection fails, @code{e_url} if the supplied
@acronym{URL} is invalid and @code{e_range} if the supplied port number
is out of the range 1--65535.

An example of using this function:

@example
@group
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
@end group
@end example

  Here is a more advanced example:

@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
@end example
@end deftypefn

@deftypefn {Library Function} boolean sa (string @var{url}, @
            number @var{prec}; number @var{command})
Additional interface to the @code{spamc} function, provided for
backward compatibility.  It is equivalent to

@example
spamc(current_message(), @var{url}, @var{prec}, @var{command})
@end example

If @var{command} is not supplied, @samp{SA_SYMBOLS} is used.
@end deftypefn

@node ClamAV
@subsection ClamAV
@cindex ClamAV
@deftypefn {Built-in Function} boolean clamav (number @var{msg}, @
              string @var{url})
@cindex clamav_virus_name, global variable
  Pass the message @var{msg} to the ClamAV daemon at @var{url}.  Return
@code{true} if it detects a virus in it.  Return virus name in
@code{clamav_virus_name} global variable.

@cindex clamav_stream_max_length, global vaiable
@cindex @code{StreamMaxLength}, @file{clamd.conf} parameter.
@cindex clamd.conf
  The message is sent to @command{clamd} in chunks.  The size of each
chunk is defined by the variable @code{clamav_stream_max_length}.  Its
value should not be greater than that of the @code{StreamMaxLength}
parameter in @file{clamd.conf}.

  To stay on the safe side, @code{clamav_stream_max_length} defaults
to 4096 bytes.  To improve performance, adjust it to the actual value
of @code{StreamMaxLength}.

The @code{clamav} function can signal the following exceptions:

@table @code
@item 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
@code{clamav_stream_max_length} is greater than the
@code{StreamMaxLength} @code{clamd} configuration parameter.  In this
case, the @code{clamd} server will report:

@example
WARNING: INSTREAM: Size limit reached, (requested: @var{n}, max: @var{m})
@end example

@noindent
where @var{n} and @var{m} are decimal numbers.  Adjust your
@code{clamav_stream_max_length} value so that

@example
clamav_stream_max_length < m
@end example

@item e_url
The supplied @acronym{URL} is invalid.
@end table

  An example usage:

@example
@group
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
@end group
@end example
@end deftypefn

@node Rate limiting functions
@section Rate limiting functions

@deftypefn {Built-in Function} number rate (string @var{key}, @
   number @var{sample-interval}, @
   [number @var{min-samples}, number @var{threshold}])

  Returns the mail sending rate for @var{key} per
@var{sample-interval}.  Optional @var{min-samples}, if supplied,
specifies the minimal number of mails needed to obtain the
statistics.  The default is 2.  Optional @var{threshold}
controls rate database updates.  If the observed rate (per
@var{sample-interval} seconds) is higher than the @var{threshold}, the
hit counters for that key are not incremented and the database is not
updated.  Although the @var{threshold} argument is
optional@footnote{It is made optional in order to provide backward
compatibility with the releases of mailfromd prior to 5.0.93.}, its
use is strongly encouraged.  Normally, the value of @var{threshold} equals
the value compared with the return from @var{rate}, as in:

@example
@group
  if rate("$f-$client_addr", rate_interval, 4, maxrate) > maxrate
    tempfail 450 4.7.0 "Mail sending rate exceeded.  Try again later"
  fi
@end group
@end example

This function is a low-level interface.  Instead of using it directly,
we advise to use the @code{rateok} function, described below.
@end deftypefn

@deftypefn {Library Function} boolean rateok (string @var{key}, @
   number @var{sample-interval}, @
   number @var{threshold},
   [number @var{min-samples}])

@flindex rateok.mfl
To use this function, require the @file{rateok} module
(@pxref{Modules}), e.g.: @code{require rateok}.

The @code{rateok} function returns @samp{True} if the mail sending
rate for @var{key}, computed for the interval of @var{sample-interval}
seconds is less than the @var{threshold}.  Optional @var{min-samples}
parameter supplies the minimal number of mails needed to obtain the
statistics.  It defaults to 4.
@end deftypefn

@xref{Sending Rate}, for a detailed description of the @code{rateok} and
its use.  The @code{interval} function (@pxref{interval}) is often
used in the second argument to @code{rateok} or @code{rate}.

@deftypefn {Built-in Function} boolean tbf_rate (string @var{key}, @
   number @var{cost}, number @var{sample-interval}, number @var{burst-size})

   This function implements a classical token bucket filter algorithm.
Tokens are added to the bucket identified by the @var{key} at constant
rate of 1 token per @var{sample-interval} microseconds, to a maximum
of @var{burst-size} tokens.  If no bucket is found for the specified key, a
new bucket is created and initialized to contain @var{burst-size}
tokens.  If the bucket contains @var{cost} or more tokens, @var{cost}
tokens are removed from it and @code{tbf_rate} returns @samp{True}.
Otherwise, the function returns @samp{False}.

   For a detailed description of the Token Bucket Algorithm and its
use to limit mail rates, see @ref{TBF}.
@end deftypefn

@node Greylisting functions
@section Greylisting functions

@deftypefn {Built-in Function} boolean greylist @
  (string @var{key}, number @var{interval})

@cindex greylist_seconds_left, global variable
  Returns @samp{True} if the @var{key} is found in the greylist database
(controlled by @code{database greylist} configuration file statement,
@pxref{conf-database}).  The argument @var{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
@code{greylist_seconds_left}.  @xref{Greylisting}, for a detailed
explanation.

  The function @code{greylist} can signal @code{e_dbfailure} exception.
@end deftypefn

@deftypefn {Built-in Function} boolean is_greylisted (string @var{key}
Returns @samp{True} if the @var{key} is still greylisted.  If
@samp{true} is returned, the function also stores the number of
seconds left to the end of greylisting period in the global variable
@code{greylist_seconds_left}.

This function is available only if Con Tassios implementation of
greylisting is used.  @xref{greylisting types}, for a discussion of
available greylisting implementations.  @xref{greylist}, for a
way to switch to Con Tassios implementation.
@end deftypefn

@node Special test functions
@section Special Test Functions

@deftypefn {Library Function} boolean portprobe (string @var{host}, [number @var{port}])
@deftypefnx {Library Function} boolean listens (string @var{host}, [number @var{port}])
@flindex portprobe.mfl
  Returns @code{true} if the @acronym{IP} address or host name given by
@var{host} argument listens on the port number @var{port} (default
25).

  This function is defined in the module @file{portprobe}.
@end deftypefn

@anchor{validuser}
@deftypefn {Built-in Function} boolean validuser (string @var{name})
  Returns @code{true} if authenticity of the user @var{name} is
confirmed using mailutils authentication system.  @xref{Local Account
Verification}, for more details.
@end deftypefn

@anchor{valid_domain}
@deftypefn {Library Function} boolean valid_domain (string @var{domain})

Returns @code{true} if the domain name @var{domain} has a
corresponding A record or if it has any @samp{MX} records, i.e. if it
is possible to send mail to it.

@flindex valid_domain.mfl
To use this function, require the @file{valid_domain} module
(@pxref{Modules}):

@example
require valid_domain
@end example

@end deftypefn

@anchor{heloarg_test}
@deftypefn {Library Function} number heloarg_test (string @var{arg}, @
 string @var{remote_ip}, string @var{local_ip})

@flindex heloarg_test.mfl
  Verify if an argument of @samp{HELO} (@samp{EHLO}) command is
valid.  To use this function, require the @file{heloarg_test} module
(@pxref{Modules}).

Arguments:

@table @var
@item arg
@samp{HELO} (@samp{EHLO}) argument.  Typically, the value of @code{$s}
Sendmail macro;

@item remote_ip
@acronym{IP} address of the remote client.  Typically, the value of
@code{$client_addr} Sendmail macro;

@item local_ip
@acronym{IP} address of this @acronym{SMTP} server;
@end table

The function returns a number describing the result of the test, as
described in the following table.

@multitable @columnfractions 0.4 0.6
@headitem Code @tab Meaning
@item HELO_SUCCESS @tab @var{arg} successfully passes all tests.
@item HELO_MYIP @tab @var{arg} is our @acronym{IP} address.
@item HELO_IPNOMATCH @tab @var{arg} is an @acronym{IP}, but it does not match
the remote party @acronym{IP} address.
@item HELO_ARGNORESOLVE @tab @var{arg} is an @acronym{IP}, but it does not resolve.
@item HELO_ARGNOIP @tab @var{arg} is in square brackets, but it is not an @acronym{IP}
address.
@item HELO_ARGINVALID @tab @var{arg} is not an @acronym{IP} address and does not
resolve to one.
@item HELO_MYSERVERIP @tab @var{arg} resolves to our server @acronym{IP}.
@item HELO_IPMISMATCH @tab @var{arg} does not resolve to the remote
client @acronym{IP} address.
@end multitable

@end deftypefn

@node Mail Sending Functions
@section Mail Sending Functions

  The mail sending functions are new interfaces, introduced in
version 3.1.

  The underlying mechanism for sending mail, called @dfn{mailer}, is
specified by @option{--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 @acronym{URL}.

@cindex @acronym{URL}, mailer
@cindex mailer @acronym{URL}
@anchor{mailer url}
  @dfn{Mailer @acronym{URL}} begins with a protocol specification.
Two protocol specifications are currently supported: @samp{sendmail}
and @samp{smtp}.  The former means to use a
@command{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:

@table @option
@item -oi
Do not treat @samp{.} as message terminator.

@item -f @var{addr}
Use @var{addr} as the address of the sender.

@item -t
Get recipient addresses from the message.
@end table

These conditions are met by most existing @acronym{MTA} programs, such
as @command{exim} or @command{postfix} (to say nothing of
@command{sendmail} itself).

Following the protocol specification is the @dfn{mailer location}, which
is separated from it with a colon.  For the @samp{sendmail} protocol,
the mailer location sets the full file name of the Sendmail-compatible
@acronym{MTA} binary, for example:

@example
sendmail:/usr/sbin/sendmail
@end example

A special form of a sendmail @acronym{URL}, consisting of protocol
specification only (@samp{sendmail:}) is also allowed.  It means
``use the sendmail binary from the @code{_PATH_SENDMAIL}
macro in your @file{/usr/include/paths.h} file''.  This is the default
mailer.

The @samp{smtp} protocol means to use an @acronym{SMTP} server directly.
In this case the mailer location consists of two slashes,
followed by the @acronym{IP} address or host name of the @acronym{SMTP}
server, and, optionally, the port number.  If the port number is
present, it is separated from the rest of @acronym{URL} by a colon.
For example:

@example
@group
smtp://remote.server.net
smtp://remote.server.net:24
@end group
@end example

@deftypefn {Built-in Function} void send_mail (string @var{msg} @
 [, string @var{to}, string @var{from}, string @var{mailer}])

  Sends message @var{msg} to the email address @var{to}.  The value of
@var{msg} must be a valid @acronym{RFC} 2822 message, consisting of
headers and body.  Optional argument @var{to} can contain several email
addresses.  In this case the message will be sent to each recipient
specified in @var{to}.  If it is not specified, recipient addresses
will be obtained from the message headers.

Other optional arguments are:

@table @var
@item from
Sets the sender address.  By default @samp{<>} is used.

@item mailer
The @acronym{URL} of the mailer to use
@end table

Sample usage:

@example
@group
    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")
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} void send_text (string @var{text}, @
 string @var{headers} [, string @var{to}, @
 string @var{from}, string @var{mailer}])
  A more complex interface to mail sending functions.

Mandatory arguments:
@table @var
@item text
Text of the message to be sent.

@item headers
Headers for the message.

@end table

Optional arguments:
@table @var
@item to
Recipient email addresses.

@item from
Sender email address.

@item mailer
@acronym{URL} of the mailer to use.
@end table

  The above example can be rewritten using @code{send_text} as follows:

@example
@group
    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")
@end group
@end example
@end deftypefn

@deftypefn {Built-in Function} void send_message (number @var{msg} @
      [string @var{to}, string @var{from}, string @var{mailer}])
Send the message identified by descriptor @var{msg} (@pxref{Message
functions}).

Optional arguments are:

@table @var
@item to
Recipient email addresses.

@item from
Sender email address.

@item mailer
@acronym{URL} of the mailer to use.
@end table
@end deftypefn



@deftypefn {Built-in Function} void send_dsn (string @var{to}, @
 string @var{sender}, string @var{rcpt}, string @var{text} @
 [, string @var{headers}, string @var{from}, string @var{mailer}])
 This is an experimental interface which will change in the future
versions.  It sends a message disposition notification (@acronym{RFC}
2298, @acronym{RFC} 1894), of type @samp{deleted} to the email address
@var{to}.  Arguments are:

@table @var
@item to
Recipient email address.

@item sender
Original sender email address.

@item rcpt
Original recipient email address.

@item text
Notification text.
@end table

Optional arguments:
@table @var
@item headers
Message headers

@item from
Sender address.

@item mailer
@acronym{URL} of the mailer to use.
@end table
@end deftypefn

@deftypefn {Built-in Function} void create_dsn (string @var{sender}, @
 string @var{rcpt}, string @var{text} @
 [, string @var{headers}, string @var{from}])
Creates DSN message and returns its descriptor. Arguments are:

@table @var
@item sender
Original sender email address.

@item rcpt
Original recipient email address.

@item text
Notification text.

@item headers
Message headers

@item from
Sender address.
@end table
@end deftypefn

@node Blacklisting Functions
@section Blacklisting Functions

  The functions described in this subsection allow to check whether the
given @acronym{IP} address is listed in certain @dfn{black list} @acronym{DNS}
zone.

@anchor{match_dnsbl}
@deftypefn {Library Function} boolean match_dnsbl (string @var{address}, @
 string @var{zone}, string @var{range})

This function looks up @var{address} in the @acronym{DNS}
blacklist zone @var{zone} and checks if the return falls into the
given @var{range} of @acronym{IP} addresses.

It is intended as a replacement for the Sendmail macros @samp{dnsbl} and
@samp{enhdnsbl}.

@flindex match_dnsbl.mfl
To use @code{match_dnsbl}, require the @file{match_dnsbl} module
(@pxref{Modules}).

Arguments:

@table @var
@item address
@acronym{IP} address of the @acronym{SMTP} server to be tested.

@item zone
@acronym{FQDN} of the @acronym{DNS}bl zone to test against.

@item range
The range of @acronym{IP} addresses in @acronym{CIDR} notation or
the word @samp{ANY}, which stands for @samp{127.0.0.0/8}.
@end table

The function returns @code{true} if dns lookup for @var{address} in
the zone @var{dnsbl} yields an @acronym{IP} that falls within the range,
specified by @var{cidr}.  Otherwise, it returns @code{false}.

This function raises the following exceptions: @code{e_invip} if
@var{address} is invalid and @code{e_invcidr} if @var{cidr} is invalid.
@end deftypefn

@anchor{match_rhsbl}
@deftypefn {Library Function} boolean match_rhsbl (string @var{email}, @
 string @var{zone}, string @var{range})

This function checks if the @acronym{IP} address, corresponding to the domain
part of @var{email} is listed in the @acronym{RHS DNS} blacklist zone
@var{zone}, and if so, whether its record falls into the given range of
@acronym{IP} addresses @var{range}.

It is intended as a replacement for the Sendmail macro @samp{rhsbl}
by Derek J.@: Balling.

@flindex match_rhsbl.mfl
To use this function, require the @file{match_rhsbl} module (@pxref{Modules}).

Arguments:

@table @var
@item email
E-mail address, whose domain name should be tested (usually, it is
@code{$f})

@item zone
Domain name of the @acronym{RHS DNS} blacklist zone.

@item range
The range of @acronym{IP} addresses in @acronym{CIDR} notation.
@end table
@end deftypefn


@node SPF Functions
@section SPF Functions

@cindex @acronym{SPF}, defined
@cindex Sender Policy Framework, defined
  @dfn{Sender Policy Framework}, or @acronym{SPF} for short, is an
extension to @acronym{SMTP} protocol that allows to identify forged
identities supplied with the @code{MAIL FROM} and @code{HELO}
commands.  The framework is explained in detail in @acronym{RFC} 4408
(@uref{http://tools.ietf.org/html/rfc4408}) and on the
@uref{http://www.openspf.org/, SPF Project Site}.  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 @dfn{SPF record} -- a special
@acronym{DNS} resource record that contains a set of rules declaring
which hosts are, and are not, authorized to use a domain name for
@code{HELO} and @code{MAIL FROM} identities.  This resource record is
usually of type @code{TXT}.@footnote{Although @acronym{RFC} 4408
introduces a special @code{SPF} record type for this purpose, it is
not yet widely used.  As of version @value{VERSION}, @acronym{MFL}
does not support @code{SPF} @acronym{DNS} records.}

@cindex @acronym{SPF}, checking host record
@cindex checking @acronym{SPF} host records
@cindex @code{check_host} function, introduced
  The @acronym{MFL} script can verify if the identity matches the
published @acronym{SPF} record by calling @code{check_host} function
and analyzing its return code.  The function can be called either in
@code{helo} or in @code{envfrom} handler.  Its arguments are:

@table @var
@item ip
  The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.

@item domain
  The domain that provides the sought-after authorization information;
Normally it is the domain portion of the @code{MAIL FROM} or
@code{HELO} identity.

@item sender
  The @code{MAIL FROM} identity.

@item helo_domain
  The @code{HELO} identity.

@item my_domain
  The @acronym{SMTP} domain served by the local server.
@end table

  The function returns a numeric result code.  For convenience, all
possible return values are defined as macros in module @file{spf.mfl}.
The table below describes each value along with the recommended
actions for it:

@table @code
@cindex None, SPF result code
@item None
  A result of @code{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.

@cindex Neutral, SPF result code
@item Neutral
  The domain owner has explicitly stated that he cannot or does not
want to assert whether or not the @acronym{IP} address is authorized.  This
result must be treated exactly like @code{None}; the distinction
between them exists only for informational purposes

@cindex Pass, SPF result code
@item 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.

@cindex Fail, SPF result code
@item 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 @code{reject
550 5.7.1}, as recommended by @acronym{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:

@example
  reject 550 5.7.1 "SPF check failed:\n%spf_explanation"
@end example

@noindent
(for the description of @code{spf_explanation}, @pxref{spf_explanation})

@cindex SoftFail, SPF result code
@item 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 @code{Fail} and a @code{Neutral}.  It is not
recommended to reject the message based solely on this result.

@item TempError
  A transient error occurred while performing @acronym{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 @acronym{SMTP}
reply code of @samp{451} and DSN code @samp{4.4.3}, for example:

@example
  tempfail 451 4.4.3
           "Transient error while performing SPF verification"
@end example

@item 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 @code{TempError}
result.
@end table

  The following example illustrates the use of @acronym{SPF}
verification in @code{envfrom} handler:

@example
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
@end example

  The @acronym{SPF} support is implemented in @acronym{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.

@flindex spf.mfl
  The library layer is implemented in the module @file{spf.mfl}
(@pxref{Modules}).

  The rest of this node describes available @acronym{SPF} functions
and variables.

@deftypefn {Built-in Function} number spf_check_host (string @var{ip}, @
 string @var{domain}, string @var{sender}, string @var{helo_domain}, @
 string @var{my_domain})

  This function is the basic implementation of the @code{check_host}
function, defined in @acronym{RFC} 4408, chapter 4.  It fetches @acronym{SPF}
records, parses them, and evaluates them to determine whether a
particular host (@var{ip}) is or is not permitted to send mail from a
given email address (@var{sender}).  The function returns an @dfn{SPF
result code}.

  Arguments are:

@table @var
@item ip
  The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is @code{$client_addr}.

@item domain
  The domain that provides the sought-after authorization information;
Normally it is the domain portion of the @code{MAIL FROM} or
@code{HELO} identity.

@item sender
  The @code{MAIL FROM} identity.

@item helo_domain
  The @code{HELO} identity.

@item my_domain
  The @acronym{SMTP} domain served by the local server.
@end table

@anchor{spf-globals}
  Before returning the @code{spf_check_host} function stores
additional information in global variables:

@table @code
@item spf_explanation
@anchor{spf_explanation}
  If the result code is @code{Fail}, this variable contains the
explanation string as returned by the publishing domain, prefixed with
the value of the global variable @code{spf_explanation_prefix}.

  For example, if @code{spf_explanation_prefix} contains @samp{The
domain %@{o@} explains: }, and the publishing domain
@samp{example.com} returns the explanation string @samp{Please see
http://www.example.com/mailpolicy.html}, than the value of
@code{spf_explanation} will be:

@example
@group
The domain example.com explains:
Please see http://www.example.com/mailpolicy.html
@end group
@end example

(see @uref{http://tools.ietf.org/html/rfc4408, @acronym{RFC} 4408}, chapter 8,
for the description of @acronym{SPF} macro facility).

@item spf_mechanism
  Name of the @acronym{SPF} mechanism that decided about the result
code of the @acronym{SPF} record.   If one or more @samp{include} or
@samp{redirect} mechanisms were traversed before arriving at that
mechanism, their values are appended in the reverse order.
@end table

@end deftypefn

@deftypefn {Built-in Function} number spf_test_record (string @var{record}, @
 string @var{ip}, string @var{domain}, string @var{sender}, @
 string @var{helo_domain}, string @var{my_domain})

  Evaluate @acronym{SPF} record @var{record} as if it were published
by @var{domain}.  The rest of arguments are the same as for
@code{spf_check_host} above.

  This function is designed primarily for testing and debugging
purposes.  You would hardly need to use it.

  The @code{spf_test_record} function sets the same global variables
as @code{spf_check_host}.
@end deftypefn

@deftypefn {Library Function} number check_host (string @var{ip}, @
 string @var{domain}, string @var{sender}, string @var{helo})

  This function implements the @code{check_host} function, defined in
@acronym{RFC} 4408, chapter 4.  It fetches @acronym{SPF} records, parses them,
and evaluates them to determine whether a particular host (@var{ip})
is or is not permitted to send mail from a given email address
(@var{sender}).  The function returns an @dfn{SPF result code}.

  This function is a wrapper over the built-in @code{spf_check_host}.

  The arguments are:

@table @var
@item ip
  The @acronym{IP} address of the @acronym{SMTP} client that is emitting the mail.
Usually it is the same as the value of @code{$client_addr}.

@item domain
  The domain that provides the sought-after authorization information;
Normally it is the domain portion of the @code{MAIL FROM} or
@code{HELO} identity.

@item sender
  The @code{MAIL FROM} identity.

@item helo
  The @code{HELO} identity.
@end table
@end deftypefn

@deftypefn {Library Function} string spf_status_string (number @var{code})
  Converts numeric @acronym{SPF} result @var{code} to its string
representation.
@end deftypefn

@deftypevr {Built-in variable} string spf_explanation
  If @code{check_host} (or @code{spf_check_host} or
@code{spf_test_record}) returned @code{Fail}, this variable contains the
explanation string as returned by the publishing domain, prefixed with
the value of the global variable @code{spf_explanation_prefix}.

  For example, if @code{spf_explanation_prefix} contains @samp{The
domain %@{o@} explains: }, and the publishing domain
@samp{example.com} returns the explanation string @samp{Please see
http://www.example.com/mailpolicy.html}, than the value of
@code{spf_explanation} will be:

@example
@group
The domain example.com explains:
Please see http://www.example.com/mailpolicy.html
@end group
@end example

@end deftypevr

@deftypevr {Built-in variable} string spf_mechanism
  Set to the name of a @acronym{SPF} mechanism that decided about the
result code of the @acronym{SPF} record.
@end deftypevr

@deftypevr {Built-in variable} string spf_explanation_prefix
  The prefix to be appended to the explanation string before storing
it in the @code{spf_explanation} variable.  This string can contain
valid @acronym{SPF} macros (see
@uref{http://tools.ietf.org/html/rfc4408, @acronym{RFC} 4408}, chapter 8), for
example:

@example
set spf_explanation_prefix "%@{o@} explains: "
@end example

  The default value is @samp{""} (an empty string).
@end deftypevr

@node DKIM
@section DKIM
@cindex @acronym{DKIM}, defined
@cindex DomainKeys Identified Mail
  @acronym{DKIM} or @dfn{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 @acronym{DKIM}, please refer to RFC 6376
(@uref{http://tools.ietf.org/html/rfc6376}).

MFL provides functions for DKIM signing and verification.

@deftypefn {Built-in Function} number dkim_verify (number @var{msg})
Verifies the message @var{msg} (a message descriptor, obtained from
a call to @code{current_message}, @code{mailbox_get_message},
@code{message_from_stream} or a similar function).

Return value (constants defined in the @samp{status} module):

@defvr {dkim_verify status} DKIM_VERIFY_OK
The message contains one or more @samp{DKIM-Signature} headers and
one of them verified successfully.
@end defvr

@defvr {dkim_verify status} DKIM_VERIFY_PERMFAIL
The message contains one or more @samp{DKIM-Signature} headers, all
of which failed to verify.
@end defvr

@defvr {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.
@end defvr

The following two global variables are always set upon return from this
function: @code{dkim_explanation} and @code{dkim_explanation_code}.
These can be used to clarify the verification result to the end user.
The variable @code{dkim_signing_algorithm} is initialized with the
name of the algorithm used to sign the message.

Upon successful return, the variable @code{dkim_verified_signature} is
set to the value of the successfully verified DKIM signature.
@end deftypefn

@deftypevr {Built-in variable} string dkim_signing_algorithm
Name of the algorithm used to sign the message (either @samp{rsa-sha1}
or @samp{rsa-sha256}).  If the algorithm was not specified (e.g. the
signature is malformed), this variable is assigned an empty value.
@end deftypevr

@deftypevr {Built-in variable} string dkim_explanation
An explanatory message clarifying the verification result.
@end deftypevr

@deftypevr {Built-in variable} number dkim_explanation_code
A numeric code corresponding to the @samp{dkim_explanation} string.
Its possible values are defined in @samp{status.mfl}:

@defvr {DKIM explanation code} DKIM_EXPL_OK
@samp{DKIM verification passed}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_OK
@samp{DKIM verification passed}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_NO_SIG
@samp{No DKIM signature}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_INTERNAL_ERROR
@samp{internal error}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_SIG_SYNTAX
@samp{signature syntax error}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_SIG_MISS
@samp{signature is missing required tag}

According to the DKIM specification, required tags are: @code{a=},
@code{b=}, @code{bh=}, @code{d=}, @code{h=}, @code{s=}, @code{v=}.
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_DOMAIN_MISMATCH
@samp{domain mismatch}

The domain part of the @code{i=} tag does not match and is not a
subdomain of the domain listed in the @code{d=} tag.
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_VERSION
@samp{incompatible version}

Incompatible DKIM version listed in the @code{v=} tag.
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_ALGORITHM
@samp{unsupported signing algorithm}

Either the @code{a=} tag of the DKIM signature contains an unsupported
algorithm (currently supported algorithms are: @samp{rsa-sha1} and
@samp{rsa-sha256}) or this algorithm, while being supported by
@command{mailfromd}, is not listed in the @code{h=} tag of the public
DKIM key.
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_QUERY
@samp{unsupported query method}

The @code{q=} tag of the public DKIM key contains something other than
@samp{dns/txt}.
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_FROM
@samp{From field not signed}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_EXPIRED
@samp{signature expired}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_DNS_UNAVAIL
@samp{public key unavailable}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_DNS_NOTFOUND
@samp{public key not found}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_KEY_SYNTAX
@samp{key syntax error}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_KEY_REVOKED
@samp{key revoked}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_BODY
@samp{body hash did not verify}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_BASE64
@samp{can't decode b= tag}

Base64 decoding of the @code{b=} tag failed.
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_SIG
@samp{signature did not verify}
@end defvr

@defvr {DKIM explanation code} DKIM_EXPL_BAD_KEY_TYPE
@samp{unsupported public key type}

The @code{k=} tag of the public DKIM signature contains a value, other
than @samp{rsa}.
@end defvr
@end deftypevr

@deftypevr {Built-in variable} string dkim_verified_signature
Upon successful return from the @code{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.
@end deftypevr

An example of using the @samp{dkim_verify} function:

@example
@group
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
@end group
@end example

@cindex dkim, module
The @samp{dkim} module defines convenience functions for manipulating
with DKIM signatures:

@deftypefn {Library Function} dkim_signature_tag (string @var{sig}, string @var{tag})
Extracts the value of the tag @var{tag} from the DKIM signature
@var{sig}.  Signature must be normalized by performing the header
unwrapping and removing whitespace characters.

If the tag was not found, returns empty string, unless @var{tag} is
one of the tags listed in the table below.  If any of these tags are
absent, the following values are returned instead:

@multitable @columnfractions 0.3 0.6
@headitem Tag @tab Default value
@item c @tab @samp{simple/simple}
@item q @tab @samp{dns/txt}
@item i @tab @samp{@@} + the value of the @samp{d} tag.
@end multitable
@end deftypefn

@deftypefn {Library Function} string dkim_verified_signature_tag (string @var{tag})
Returns the value of tag @var{tag} from the
@samp{dkim_verified_signature} variable.
@end deftypefn

@deftypefn {Built-in Function} void dkim_sign (string @var{d}, @
 string @var{s}, string @var{keyfile}, [ string @var{ch}, @
 string @var{cb}, string @var{headers}, string @var{algo} ])

This function is available only in the @code{eom} handler.

Signs the current message.  @emph{Notice}, that no other modification
should be attempted on the message after calling this function.  Doing
so would make the signature invalid.

Mandatory arguments:

@table @var
@item 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 (@acronym{SDID}).

@item s
The selector name.  This value, along with @var{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

@example
  @var{s}._domainkey.@var{d}
@end example

@item 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).
@end table

Optional arguments:

@table @var
@item ch
Canonicalization algorithm for message headers.  Valid values are:
@samp{simple} and @samp{relaxed}.  @samp{simple} is the default.

@item cb
Canonicalization algorithm for message body.  Valid and default values
are the same as for @var{ch}.

@item 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 @samp{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 @var{headers} contained
@samp{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 @samp{Comments}
header field at the time of signing, putting @samp{Comments:Comments:}
in the @var{headers} parameter is sufficient to prevent any surplus
@samp{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 @var{header} list
contained @samp{Received:Received}) and the current message contained
three @samp{Received} headers:

@example
Received: A
Received: B
Received: C
@end example

@noindent
then these headers will be signed in the following order:

@example
Received: C
Received: B
@end example

The default value for this parameter, split over multiple lines for
readability, is as follows:

@itemize @bullet
@item "From:From:"
@item "Reply-To:Reply-To:"
@item "Subject:Subject:"
@item "Date:Date:"
@item "To:"
@item "Cc:"
@item "Resent-Date:"
@item "Resent-From:"
@item "Resent-To:"
@item "Resent-Cc:"
@item "In-Reply-To:"
@item "References:"
@item "List-Id:"
@item "List-Help:"
@item "List-Unsubscribe:"
@item "List-Subscribe:"
@item "List-Post:"
@item "List-Owner:"
@item "List-Archive"
@end itemize

@item algo
Signing algorithm: either @samp{rsa-sha256} or @samp{rsa-sha1}.
Default is @samp{rsa-sha256}.
@end table
@end deftypefn

An example of using this function:

@example
@group
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
@end group
@end example

@menu
* Setting up a DKIM record::
@end menu

@anchor{dkim_sign and sendmail}
@subheading 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 @command{mailfromd} is
used with @command{Sendmail}.  Before sending the message over SMTP,
@command{Sendmail} reformats the headers that contain a list of email
addresses, by applying to them a procedure called in its parlance
@dfn{commaization}.  The following headers are modified:
@code{Apparently-To}, @code{Bcc}, @code{Cc},
@code{Disposition-Notification-To}, @code{Errors-To}, @code{From},
@code{Reply-To}, @code{Resent-Bcc}, @code{Resent-Cc},
@code{Resent-From}, @code{Resent-Reply-To}, @code{Resent-Sender},
@code{Resent-To}, @code{Sender}, @code{To}.  Thus, if your
@code{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 @command{Sendmail} prefers, the DKIM signature would not verify on
the recipient side.  To prevent this from happening, @code{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:

@deftypevr {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 (@code{true}).  You can set it to 0
(@code{false}) if this behavior is not what you want (e.g. if you are
using @code{postfix} or some other MTA).
@end deftypevr

@anchor{MMQ and dkim_sign}
@subheading Note on interaction of dkim_sign with MMQ
The functions @code{header_add} and @code{header_insert}
(@pxref{Header modification functions}) as well as the @code{add}
action (@pxref{header manipulation}) cannot interact properly with
@code{dkim_sign} due to the shortcomings of the Milter API.  If any of
these was called, @code{dkim_sign} will throw the @code{e_badmmq}
exception with the following diagnostics:

@example
MMQ incompatible with dkim_sign: @var{op} on @var{h}, value @var{v}
@end example

@noindent
where @var{op} is the operation code (@samp{ADD HEADER} or
@samp{INSERT HEADER}), @var{h} is the header name and @var{v} is its
value.

The following example shows one graceful way of handling such
exception:

@example
@group
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
@end group
@end example

@xref{Message modification queue}, for a discussion of the message
modification queue.

@node Setting up a DKIM record
@subsection Setting up a DKIM record
@cindex DKIM, setting up
Follow these steps to set up your own DKIM record:

@enumerate 1
@item Generate a key pair:

Use the @command{openssl genrsa} command.  Run:

@example
openssl genrsa -out private.pem 2048
@end example

@noindent
The last argument is the size of the private key to generate in bits.

@item Extract the public key:

@example
openssl rsa -in private.pem -pubout -outform PEM -out public.pem
@end example

@item 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@footnote{@uref{https://tools.ietf.org/html/rfc4871}}.  The label
for this record is composed as follows:

@example
  @var{s}._domainkey.@var{d}
@end example

@noindent
where @var{d} is your domain name, and @var{s} is the selector you
chose to use.  You will use these two values as parameters to the
@code{dkim_sign} function in your @code{eom} handler.  E.g. if your
domain in @samp{example.com} and selector is @samp{s2048}, then the
DKIM TXT record label is @samp{s2048._domainkey.example.com}.

The public key file generated in step 2 will have the following
contents:

@example
-----BEGIN PUBLIC KEY-----
@var{base64}
-----END PUBLIC KEY-----
@end example

@noindent
where @var{base64} is the key itself in base64 encoding.  The minimal
DKIM TXT record will be:

@example
"v=DKIM1; p=@var{base64}"
@end example

The only mandatory @dfn{tag} is in fact @samp{p=}.  The use of
@samp{v=} is recommended.  More tags can be added as needed.  In
particular, while testing the DKIM support, it is advisable to add the
@samp{t=y} tag.
@end enumerate

@node Sockmaps
@section Sockmap Functions
@cindex socket map
  Socket map (@dfn{sockmap} for short) is a special type of
database used in Sendmail and MeTA1.  It uses a simple server/client protocol
over @acronym{INET} or @acronym{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 @dfn{map name} and
a @dfn{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 @dfn{status indicator} and @dfn{result},
separated by a single space.  The result part is optional.

For example, following is the query for key @samp{smith} in map @samp{aliases}:

@example
11:aliases news,
@end example

A possible reply is:

@example
18:OK root@@domain.net,
@end example

This reply means that the key @samp{news} was found in the map, and
the value corresponding to that key is @samp{root@@domain.net}.

The following reply means the key was not found:

@example
8:NOTFOUND,
@end example

For a detailed description of the sockmap protocol, see
@ref{Protocol,,, smap, Smap manual}.

@flindex sockmap.mfl
The @acronym{MFL} library provides two primitives for dealing with
sockmaps.  Both primitives become available after requiring the
@file{sockmap} module.

@deftypefn {Library Function} string sockmap_lookup (number @var{fd}, @
 string @var{map}, string @var{key})
This function look ups the @var{key} in the @var{map}.  The @var{fd}
refers to the sockmap to use.  It must be obtained as a result of a
previous call to @code{open} with the @acronym{URL} of the sockmap as
its first argument (@pxref{I/O functions, open}).  For example:

@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)
@end example
@end deftypefn

@deftypefn {Library Function} string sockmap_single_lookup (string @var{url}, @
 string @var{map}, string @var{key})
This function connects to the sockmap identified by the @var{url},
queries for @var{key} in @var{map} and closes the connection.  It is
useful when you need to perform only a single lookup on the sockmap.
@end deftypefn

@node NLS Functions
@section National Language Support Functions
@cindex @acronym{NLS}
@cindex National Language Support
  The @dfn{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.

@cindex internationalization
@cindex i18n
@cindex localization
@cindex l10n
  This section assumes the reader is familiar with the concepts of program
@dfn{internationalization} and @dfn{localization}.  If not, please
refer to @ref{Why, The Purpose of GNU @command{gettext}, The Purpose of GNU
@command{gettext}, gettext, GNU gettext manual}, before reading further.

@flindex nls.mfl
  In general, internationalization of any @acronym{MFL} script
follows the same rules as described in the @cite{GNU gettext manual}.
First of all, you select the program @dfn{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 @code{textdomain} function.  For the
purposes of this section, let's suppose the domain name is
@samp{myfilter}.  All @acronym{NLS} functions are provided in the
@file{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 @command{gettext} mechanism will look for file
@file{@var{dirname}/@var{locale}/LC_MESSAGES/@var{domainname}.mo},
where @var{dirname} is the message catalog hierarchy name,
@var{locale} is the locale name, and @var{domainname} is the name of
the message domain.  By default @var{dirname} is
@file{/usr/local/share/locale}, but you may change it using
@code{bindtextdomain} function.  The right place for this initial
@acronym{NLS} setup is in the @samp{begin} block (@pxref{begin/end}).
To summarize all the above, the usual @acronym{NLS} setup will look like:

@example
require nls

begin
do
  textdomain("myfilter")
  bindtextdomain("myfilter", "/usr/share/locale");
done
@end example

  For example, given the settings above, and supposing the environment
variable @env{LC_ALL} is set to @samp{pl}, translations will be looked
in file @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 @code{gettext} function, or @code{_}
(underscore) macro.  For example, the following statement will produce
translated textual description for @samp{450} response:

@example
tempfail 450 4.1.0 _("Try again later")
@end example

  Of course it assumes that the appropriate @file{myfile.mo} file
already exists.  If it does not, nothing bad happens: in this case the
macro @command{_}  (as well as @code{gettext} function) will simply
return its argument unchanged, so that the remote party will get the
textual message in English.

  The @samp{mo} files are binary files created from @samp{po} source
files using @command{msgfmt} utility, as described in @ref{Binaries,
Producing Binary MO Files, Producing Binary MO Files, gettext, GNU
gettext manual}.  In turn, the format of @samp{po} files is described
in @ref{PO Files, The Format of PO Files, The Format of PO Files,
gettext, GNU gettext manual}.

@deftypefn {Built-in Function} string bindtextdomain (string @var{domain}, @
  string @var{dirname})
This function sets the base directory of the hierarchy containing
message catalogs for a given message domain.

@var{domain} is a string identifying the textual domain.  If
it is not empty, the base directory for message catalogs belonging to
domain @var{domain} is set to @var{dirname}.  It is important that
@var{dirname} be an absolute pathname; otherwise it cannot be
guaranteed that the message catalogs will be found.

If @var{domain} is @samp{""}, @code{bindtextdomain} returns the
previously set base directory for domain @var{domain}.
@end deftypefn

  The rest of this section describes the @acronym{NLS} functions
supplied in the @file{nls} module.

@deftypefn {Built-in Function} string dgettext (string @var{domain}, @
  string @var{msgid})
@code{dgettext} attempts to translate the string @var{msgid} into the
currently active locale, according to the settings of the textual
domain @var{domain}.  If there is no translation available,
@code{dgettext} returns @var{msgid} unchanged.
@end deftypefn

@deftypefn {Built-in Function} string dngettext @
 (string @var{domain}, string @var{msgid}, string @var{msgid_plural}, @
  number @var{n})
The @code{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 @var{domain}.

@xref{Plural forms, Additional functions for plural forms,
Additional functions for plural forms, gettext, GNU gettext
utilities}, for a discussion of the plural form handling in
different languages.
@end deftypefn

@deftypefn {Library Function} string textdomain (string @var{domain})
The @code{textdomain} function sets the current message domain to
@var{domain}, if it is not empty.  In any case the function returns
the current message domain.  The current domain is @samp{mailfromd}
initially.  For example, the following sequence of @code{textdomain}
invocations will yield:

@example
textdomain("") @result{} "mailfromd"
textdomain("myfilter") @result{} "myfilter"
textdomain("") @result{} "myfilter"
@end example

@end deftypefn

@deftypefn {Library Function} string gettext (string @var{msgid})
@code{gettext} attempts to translate the string @var{msgid} into the
currently active locale, according to the settings of the current textual
domain (set using @code{textdomain} function).  If there is no
translation available, @code{gettext} returns @var{msgid} unchanged.
@end deftypefn

@deftypefn {Library Function} string ngettext @
 (string @var{msgid}, string @var{msgid_plural}, number @var{n})
The @code{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.

@xref{Plural forms, Additional functions for plural forms,
Additional functions for plural forms, gettext, GNU gettext
utilities}, for a discussion of the plural form handling in
different languages.
@end deftypefn

@node Syslog Interface
@section Syslog Interface

  The basic means for outputting diagnostic messages is the
@samp{echo} instruction  (@pxref{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
@samp{info}.

  If you want to send a message to another facility and/or priority,
use the @samp{syslog} function:

@deftypefn {Built-in Function} void syslog (number @var{priority}, string @var{text})
  Sends @var{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.
@end deftypefn

@findex syslog.mfl
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 @file{syslog} module:
@samp{LOG_KERN}, @samp{LOG_USER}, @samp{LOG_MAIL}, @samp{LOG_DAEMON},
@samp{LOG_AUTH}, @samp{LOG_SYSLOG}, @samp{LOG_LPR}, @samp{LOG_NEWS},
@samp{LOG_UUCP}, @samp{LOG_CRON}, @samp{LOG_AUTHPRIV}, @samp{LOG_FTP}
and @samp{LOG_LOCAL0} through @samp{LOG_LOCAL7}

The declared severity levels are: @samp{LOG_EMERG}, @samp{LOG_ALERT},
@samp{LOG_CRIT}, @samp{LOG_ERR}, @samp{LOG_WARNING},
@samp{LOG_NOTICE}, @samp{LOG_INFO} and @samp{LOG_DEBUG}.

@node Debugging Functions
@section Debugging Functions

  These functions are designed for debugging the MFL programs.

@deftypefn {Built-in Function} void debug (string @var{spec})
@kwindex mailutils_set_debug_level
Enable debugging.  The value of @var{spec} sets the debugging level.
@xref{debugging level specification}, for a description of its format.

For compatibility with previous versions, this function is also
available under the name @samp{mailutils_set_debug_level}.
@end deftypefn

@deftypefn {Built-in Function} number debug_level ([string @var{srcname}])
This function returns the debugging level currently in effect for the source
module @var{srcname}, or the global debugging level, if called without
arguments.

For example, if the program was started with
@option{--debug='all.trace5;@/engine.trace8'} option, then:

@example
debug_level() @result{} 127
debug_level("engine") @result{} 1023
debug_level("db") @result{} 0
@end example
@end deftypefn

@deftypefn {Built-in Function} boolean callout_transcript ([boolean @var{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 @option{--transcript}
command line option (@pxref{SMTP transcript}) or via the
@samp{transcript} configuration statement (@pxref{conf-server,
transcript}).

The optional @var{value}, supplies the new state for SMTP transcript.
Thus, calling @samp{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.:

@example
number xstate callout_transcript(1)
on poll $f do
  @dots{}
done
set xstate callout_transcript(0)
@end example

Note, that the use of this function (as well as the use of the
@option{--transcript} option) makes sense only if callouts are
performed by the @command{mailfromd} daemon itself.  It will not
work if a dedicated callout server is used for that purpose
(@pxref{calloutd}).
@end deftypefn

@anchor{debug_spec}
@deftypefn {Built-in Function} string debug_spec ([string @var{catnames}, @
  bool @var{showunset}])
Returns the current debugging level specification, as given by
@option{--debug} command line option or by the @code{debug} configuration
statement (@pxref{conf-debug}).

If the argument @var{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 @command{mailfromd} was started with
@option{--debug=all.trace5;@/spf.trace1;@/engine.trace8;@/db.trace0}, then:

@example
debug_spec() @result{} "all.trace5,engine.trace8"
debug_spec("all;engine") @result{} "all.trace5,engine.trace8"
debug_spec("engine;db") @result{} "db.trace0;engine.trace8"
debug_spec("prog") @result{} ""
@end example

@noindent
When called without arguments, @code{debug_spec} returns only
those categories which have been set, as shown in the first example
above.

  Optional @var{showunset} parameters controls whether to return unset
module specifications.  To print all debugging specifications, whether
set or not, use

@example
debug_spec("", 1)
@end example
@end deftypefn

  These three functions are intended to complement each other.  The
calls to @code{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:

@example
@group
    /* @r{Save debugging level for @file{dns.c} source} */
    set dlev debug_spec("dns", 1)
    /* @r{Set new debugging level} */
    debug("dns.trace8")
    .
    .
    .
    /* @r{Restore previous level} */
    debug(dlev)
@end group
@end example

@deftypefn {Built-in Function} void program_trace (string @var{module})
Enable tracing for a set of modules given in @var{module} argument.
@xref{--trace-program}, for a description of its format.
@end deftypefn

@deftypefn {Built-in Function} void cancel_program_trace (string @var{module})
Disable tracing for given modules.
@end deftypefn

  This pair of functions is also designed to be used together in
a bracket-like fashion.  They are useful for debugging
@command{mailfromd}, but are not advised to use otherwise, since
tracing slows down the execution considerably.

@deftypefn {Built-in Function} void stack_trace ()
Generate a stack trace in this point.  @xref{tracing runtime errors},
for the detailed description of stack traces.
@end deftypefn

@deftypefn {Built-in Function} string stack_trace_format (string @var{fmt})
Format each frame in stack trace to a string according to @var{fmt},
and return the concatenated strings.  The format string @var{fmt}
consists of regular characters, which are copied to the resulting
string verbatim, and @dfn{conversion specifiers}, prefixed with
percent sign, which expand to parts of stack trace.  The following
conversion specifiers are defined:

@table @code
@item %d
Frame number, counting from the innermost (last) frame, 0-based.
@item %f
Name of the function.
@item %n
Line number in the source file.
@item %s
Name of the source file.
@item %p
Value of the PC register.
@item %%
Single percent sign.
@end table

A @samp{%} followed by any other letter is reproduced verbatim.
@end deftypefn

@deftypefn {Built-in Function} string stack_trace_top (string @var{fmt})
Format the top-level stack frame according to @var{fmt}.  See above
for a discussion of format string.
@end deftypefn

The functions below are intended mainly for debugging MFL run-time
engine and for use in @command{mailfromd} testsuite.  You will hardly
need to use them in your programs.

@deftypefn {Built-in Function} void _expand_dataseg (number @var{n})
Expands the run-time data segment by at least @var{n} words.
@end deftypefn

@deftypefn {Built-in Function} number _reg (number @var{r})
@flindex _register
Returns the value of the register @var{r} at the moment of the call.
Symbolic names for run-time registers are provided in the module
@file{_register}:

@multitable @columnfractions 0.3 0.7
@headitem Name @tab Register
@item REG_PC   @tab Program counter
@item REG_TOS  @tab Top of stack
@item REG_TOH  @tab Top of heap
@item REG_BASE @tab Frame base
@item REG_REG  @tab General-purpose accumulator
@item REG_MATCHSTR @tab Last matched string pointer
@end multitable
@end deftypefn

@deftypefn {Built-in Function} number _stack_free ()
Returns number of words available for use in stack.  This is the same
as

@example
_reg(REG_TOS) - _reg(REG_TOH)
@end example
@end deftypefn

@deftypefn {Built-in Function} number _heap_reserve (number @var{n})
Use up next @var{n} words in the heap.  Return the address of the
first word.
@end deftypefn

@deftypefn {Built-in Function} void _wd ([number @var{n}])
Enters a time-consuming loop and waits there for @var{n} seconds (by
default -- indefinitely).  The intention is to facilitate attaching
to @command{mailfromd} with the debugger.  Before entering the loop,
a diagnostic message is printed on the @samp{crit} facility, informing
about the PID of the process and suggesting the command to be used to
attach to it, e.g.:

@example
mailfromd: process 21831 is waiting for debug
mailfromd: to attach: gdb -ex 'set variable mu_wd::_count_down=0'
 /usr/sbib/mailfromd 21831
@end example
@end deftypefn

@node Informative Functions
@section Informative Functions

These functions convert numeric identifiers of various MFL entities to
strings and vice-versa.

@deftypefn {Built-in Function} string milter_state_name (number @var{code})
Convert the numeric identifier of a @dfn{milter state} to textual form.
It is normally used to convert the @code{milter_state} variable
(@pxref{milter state}) to string, e.g.:

@example
milter_state_name(5) @result{} "connect"
@end example

If @var{code} does not refer to a valid milter state, the @code{e_inval}
exception is raised.
@end deftypefn

@deftypefn {Built-in Function} number milter_state_code (string @var{name})
Returns numeric code of the milter state @var{name}.  If @var{name}
does not refer to a valid milter state, returns 0
(@code{milter_state_none} from the @file{milter.mfl} module).
@end deftypefn

@deftypefn {Built-in Function} string milter_action_name (number @var{code})
Convert the numeric identifier of a reply action (@pxref{reply actions})
to textual name.

If @var{code} does not refer to a valid reply action, the @code{e_inval}
exception is raised.

@example
milter_state_code("connect") @result{} 5
@end example

This function is useful in action hooks.  @xref{action hook}, for details.
@end deftypefn

@deftypefn {Built-in Function} number milter_action_name (string @var{name})
Returns numeric code of the reply action identified by @var{name}.  If
@var{name} does not refer to a valid action, returns -1.
@end deftypefn

@node Mfmod Interface
@section Mfmod Interface Functions

The calls described in this section provide interface for invoking
functions defined in a @dfn{dynamically loaded library} and retrieving
their return values.  For a detailed description of this interface
and a discussion of its use, see @ref{mfmod}.

@anchor{dlopen}
@deftypefn {Built-in Function} number dlopen (@var{filename})
Loads the dynamically loaded library @var{filename} and returns a
numeric handle that can be used to call functions from that library.

Unless @var{filename} is an absolute pathname, it will be looked up in
@dfn{mfmod search path}, defined by the configuration variable
@code{runtime.mfmod-path} (@pxref{mfmod-path}).

Maximum number of dynamic libraries that can be loaded simultaneously
is limited by the configuration variable @code{runtime.max-mfmods}.

Once open, the library remains loaded until @command{mailfromd} exits.
There is no @code{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 @code{begin} section
(@pxref{begin/end}) and assign its return value to a global or static
variable, which will then be used by further @code{dl*} calls in this
module.
@end deftypefn

@deftypefn {Built-in Function} any dlcall (number @var{dlh}, @
  string @var{symbol}, string @var{types}, ...)
Calls a @dfn{mfmod function} @var{symbol} from the library identified
by handle @var{dlh} (a value returned by a previous call to
@code{dlopen}).  The @var{types} parameter defines types of the
remaining arguments.  It is a list of type letters, one for each argument:

@table @samp
@item s
String value.

@item n
@itemx d
Numeric value.

@item m
Message.
@end table

An example usage:

@example
set dlh dlopen("mfmod_test.so")
string x dlcall(dlh, "runtest", "sn", "input string", 3)
@end example

@noindent
This example calls the function @samp{runtest} from the
@file{mfmod_test.so} library with two arguments: a string @samp{input
string} and numeric value @samp{3} and assings the return value to the
variable @code{x}.

Type of the return value from @code{dlcall} is determined by the
value of @code{retval.type} upon return from the underlying library
function.  @xref{Loadable Library}, for a detailed description.

For more details about using this function, @ref{Interface Module}.
@end deftypefn
