% $Id: pmilter.api.tex,v 1.32 2007/02/16 04:41:00 ca Exp $
%% this section should also be in README.
%% how can we do this?
%% put this into an include file and write a filter
%% that generates a format suitable for README?
%% or should we use \ifelse? that may cause problems with latex2html?
\section{Native Policy Milter API}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI}

Note: this API may evolve over time.

Naming conventions:
A {\it policy milter} (also called {\it pmilter})
is a program that uses the API provided by {\it libpmilter}.
The latter interacts with the SMTP servers via
an internal protocol, i.e., this protocol can be changed
without changing the visible API and should not directly be accessed
by a user application.

\subsection{Data Structures}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:DataStructures}

libpmilter itself uses three context structures all of which
must be treated by a milter as opaque.

\begin{enumerate}
\item
{\tt pmg\_ctx}: ``global'' libpmilter context
(only one per process).
\item
{\tt pmss\_ctx}: libpmilter context per SMTP server
that connects to this instance.
There can be multiple SMTP servers connecting to one libpmilter instance.
\item
{\tt pmse\_ctx}: libpmilter context per SMTP session.
\end{enumerate}

Any of the libpmilter functions takes one of these contexts as parameter;
e.g., all SMTP session oriented functions have a parameter of type
{\tt pmse\_ctx\_P}.

A milter can have its own contexts for each of these three environments,
see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:pmilterContexts}.

\subsection{Start and Stop}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:StartandStop}

The functions in this section return {\tt SM\_SUCCESS} (0) on success
and a negative value in case of an error.

First libpmilter must be initialized;
a pmilter must specify a variable
{\tt pmg\_ctx\_P pmg\_ctx;}
which is passed per reference to the initialization function:

{\tt sm\_ret\_T sm\_pmfi\_init(pmg\_ctx\_P *pmg\_ctx)}

The pmilter global context must be treated as opaque data structure,
it is passed to subsequent libpmilter function calls.

Next pmilter starts libpmilter by handing control over to the library;
the pmilter passes a description of its requirements and functionality:

{\tt sm\_ret\_T sm\_pmfi\_start(pmg\_ctx\_P pmg\_ctx, pmilter\_P pmilter)}

A milter can stop by calling:

{\tt sm\_ret\_T sm\_pmfi\_stop(pmg\_ctx\_P pmg\_ctx)}

There are various functions to set some options which can be called
after libpmilter is initialized but before it is started.
To set the path of the Unix domain socket over which the SMTP servers
(see Section \ref{ConfigurationforSMTPServer},
item \ref{ConfigurationforSMTPServer:pmilter})
and libpmilter communicate:

{\tt sm\_ret\_T sm\_pmfi\_setconn(pmg\_ctx\_P pmg\_ctx, const char *path)}

The backlog parameter of the {\tt listen(2)} function can be set:

{\tt sm\_ret\_T sm\_pmfi\_setbacklog(pmg\_ctx\_P pmg\_ctx, int backlog)}

The debug level of libpmilter might be set via
(this requires knowledge of the internals of the library
which can be acquired by looking at the source code):

{\tt sm\_ret\_T sm\_pmfi\_setdbg(pmg\_ctx\_P pmg\_ctx, int debuglevel)}

To set the communication timeout:

{\tt sm\_ret\_T sm\_pmfi\_settimeout(pmg\_ctx\_P pmg\_ctx, int timeout)}

\subsection{New SMTP Server}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:NewSMTPServer}

Whenever an SMTP server connects to a milter an option negotiation
is performed (similar to ESMTP itself).
% For details see Section \ref{impl:Milter:SMTPSMilterProtocol:OptionNegotiation}.
A pmilter can 
check whether server capabilities are acceptable and return
the options that it wants:

{\tt sm\_ret\_T pmfi\_negotiate(pmss\_ctx\_P pmss\_ctx, uint32\_t srv\_cap, uint32\_t srv\_fct, uint32\_t srv\_feat, uint32\_t srv\_misc, uint32\_t *pm\_cap, uint32\_t *pm\_fct, uint32\_t *pm\_feat, uint32\_t *pm\_misc)}

Currently only the capabilities field is used:
{\tt srv\_cap} is set by the SMTP server to a list (implemented as bit field)
of phases of the ESMTP dialogue that can be passed to a pmilter.
In turn the pmilter must set
{\tt *pm\_cap}
to includes those
phases of the ESMTP dialogue that it wants to receive.
For details, see {\tt include/sm/pmilter.h}.
For each of those phases a callback is invoked (see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SMTPSessionandTransaction})
which must be set by the pmilter in its description structure
{\tt struct pmilter\_S} (see {\tt include/sm/pmfapi.h}).

\subsection{SMTP Session and Transaction}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SMTPSessionandTransaction}

The protocol steps from ESMTP are forwarded to
the policy milter which can decide to accept or reject them.

\begin{itemize}
\item
New SMTP session:

{\tt sfsistat\_T pmfi\_connect(pmse\_ctx\_P pmse\_ctx, const char *hostname, sm\_sock\_addr\_T *hostaddr)}

{\tt hostname}: host name, as determined by a reverse lookup
on the host IP address;
{\tt hostaddr}: host address, as determined by a {\tt getpeername(2)}
call on the SMTP socket.


\item
SMTP {\tt HELO}/{\tt EHLO} command:

{\tt sfsistat\_T pmfi\_helo(pmse\_ctx\_P pmse\_ctx, const char *helohost, bool ehlo)}

{\tt helohost}: Value passed to {\tt HELO}/{\tt EHLO} command,
which should be the domain name of the sending host.
%(but is, in practice, anything the sending host wants to send).
{\tt ehlo}: {\tt true} iff {\tt EHLO} was used.

\item
{\tt MAIL} (envelope sender):

{\tt sfsistat\_T pmfi\_mail(pmse\_ctx\_P pmse\_ctx, const char *mail, char **argv)}

{\tt mail}: envelope mail address;
{\tt argv}: null-terminated {\tt MAIL} command arguments.

\item
{\tt RCPT} (envelope recipient):

{\tt sfsistat\_T pmfi\_rcpt(pmse\_ctx\_P pmse\_ctx, const char *rcpt, char **argv)}

{\tt rcpt}: envelope recipient address;
{\tt argv}: null-terminated {\tt RCPT} command arguments.

\item
{\tt DATA}:

{\tt sfsistat\_T pmfi\_data(pmse\_ctx\_P pmse\_ctx)}


\item
unknown/not implemented SMTP command:

{\tt sfsistat\_T pmfi\_unknown(pmse\_ctx\_P pmse\_ctx, const char *cmd)}

{\tt cmd}: SMTP command.
Note: this is not yet implemented.

\item
For each chunk of a message:

{\tt sfsistat\_T pmfi\_msg(pmse\_ctx\_P pmse\_ctx, unsigned char *msgp, size\_t msglen)}

{\tt msgp}: pointer to message data;
{\tt msglen}: length of message data.
There may be multiple message chunks passed to the filter.
End-of-lines are represented as received from SMTP
(normally Carriage-Return/Line-Feed; CRLF).
Notes:
\begin{itemize}
\item
the last message chunk contains the final dot of the SMTP transmission,
i.e., ``CRLF.CRLF''
\item
the message is not modified in any form,
i.e., dots at the begin of a line are duplicated (by the SMTP client)
as specified in section 4.5.2 of RFC 2821 \cite{bib:rfc2821}
which must be undone by the application if so desired.
\item
the message is {\it streamed} while being received.
That is, the mail is not first stored on disk and then sent to the filter,
but each part received from the client is sent directly to the filter
(at the same speed as received from the network which might be slow).
This may mean that the filter does not receive the entire message as
the transmission may get interrupted
or the SMTP server may decide to skip the rest of the message because
it exceeds the maximum size.
\item
the return code is ignored unless
{\tt SM\_SCAP\_PM\_MSG\_RC}
is set, see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities},
item
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities:SMSCAPPMMSGRC}.
\end{itemize}

\item
End of message (final dot of message has been received):

{\tt sfsistat\_T pmfi\_eom(pmse\_ctx\_P pmse\_ctx)}

\item
Message is aborted outside of the control of the filter,
for example, if the SMTP client issues an RSET command.

{\tt sm\_ret\_T pmfi\_abort(pmse\_ctx\_P pmse\_ctx)}

% both might be called anyway?
% If {\tt pmfi\_abort} is called, {\tt pmfi\_eom} will not be called
% and vice versa.

\item
{\tt QUIT} (end of an SMTP session):

{\tt sm\_ret\_T pmfi\_close(pmse\_ctx\_P pmse\_ctx)}

This is called when an SMTP session ends.

\end{itemize}

\subsection{Set and Get pmilter Contexts}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:pmilterContexts}

As explained in Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:DataStructures}
a milter can have a ``global'' context {\tt pmilter\_g\_ctx},
a context per SMTP server {\tt pmilter\_ss\_ctx},
and a context per SMTP session {\tt pmilter\_se\_ctx}.
The following functions are provided to set and get these contexts.

Set the ``global'' context {\tt pmilter\_g\_ctx}:

{\tt sm\_ret\_T sm\_pmfi\_set\_ctx\_g(pmg\_ctx\_P pmg\_ctx, void *pmilter\_g\_ctx)}.

This must be done after libpmilter has been initialized
but before control is transferred to it.

To retrieve the ``global'' context invoke:

{\tt void *sm\_pmfi\_get\_ctx\_g(pmg\_ctx\_P pmg\_ctx)}

Note: this requires the ``global'' libpmilter context which is
not usually passed to pmilter functions in callbacks.
See below how to access the ``global'' context {\tt pmilter\_g\_ctx}
from other places.

To set the pmilter context per SMTP server {\tt pmilter\_ss\_ctx} use:

{\tt sm\_ret\_T sm\_pmfi\_set\_ctx\_ss(pmss\_ctx\_P pmss\_ctx, void *pmilter\_ss\_ctx)};

to retrieve it call:

{\tt void *sm\_pmfi\_get\_ctx\_ss(pmss\_ctx\_P pmss\_ctx)}

The ``global'' pmilter context {\tt pmilter\_g\_ctx} can be retrieved
from the libpmilter context per SMTP server:

{\tt void *sm\_pmfi\_get\_ctx\_g\_ss(pmss\_ctx\_P pmss\_ctx)}

At the lowest level a context per SMTP session {\tt pmilter\_se\_ctx}
can be set via:

{\tt sm\_ret\_T sm\_pmfi\_set\_ctx\_se(pmse\_ctx\_P pmse\_ctx, void *pmilter\_se\_ctx)}

and retrieved by:

{\tt void *sm\_pmfi\_get\_ctx\_se(pmse\_ctx\_P pmse\_ctx)}.

Just as before there is a function to retrieve
the pmilter context per SMTP server {\tt pmilter\_ss\_ctx}
from the libpmilter context per SMTP session:

{\tt void *sm\_pmfi\_get\_ctx\_ss\_se(pmse\_ctx\_P pmse\_ctx)}

Note: if a pmilter uses these contexts, then it is useful
that each ``lower level'' context contains a link
to its ``higher level'' context.
That is,
each pmilter context per SMTP session {\tt pmilter\_se\_ctx}
should have a pointer to its
pmilter context per SMTP server {\tt pmilter\_ss\_ctx}
which in turn should have a pointer to the
``global'' pmilter context {\tt pmilter\_g\_ctx}.
This allows access from a function that is specific to a SMTP session
to each relevant context.

\subsection{Accessing MTA Symbols}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:AccessingMTASymbols}

A pmilter can set a list of symbols it wants to receive from the MTA
by calling

{\tt sm\_pmfi\_setmaclist(pmss\_ctx\_P pmss\_ctx, uint where, ...)}

during the option negotiation, i.e., in {\tt pmfi\_negotiate()}.
The parameter {\tt where} denotes the stage of the ESMTP dialogue
when the value of the symbol should be sent.
It must be one of

\begin{tabular}{ll}
{\tt PM\_SMST\_CONNECT}	& Session start	\\
{\tt PM\_SMST\_EHLO}		& EHLO or HELO command \\
{\tt PM\_SMST\_MAIL}		& MAIL command	\\
{\tt PM\_SMST\_RCPT}		& RCPT command	\\
{\tt PM\_SMST\_DATA}		& DATA command	\\
{\tt PM\_SMST\_DOT}		& Final dot of mail body	\\
\end{tabular}

A sequence of up to {\tt PM\_MAX\_MACROS} macros can be requested which
must end with {\tt PMM\_END}.
Valid values are:

\begin{enumerate}
\item
{\tt PMM\_SRVHOSTNAME}
hostname of SMTP server.
\item
{\tt PMM\_SEID}	
session id.
\item
{\tt PMM\_CLIENT\_RESOLVE}
result of client lookups.
\item
{\tt PMM\_MAIL\_TAID}	
transaction id.
\item
{\tt PMM\_DOT\_MSGID}	
Message-Id.
\item
{\tt PMM\_TLS\_VERSION}	
TLS/SSL version used.
\item
{\tt PMM\_TLS\_CIPHER\_SUITE}
cipher suite used.
\item
{\tt PMM\_TLS\_CIPHER\_BITS}
effective key length of the symmetric encryption algorithm.
\item
{\tt PMM\_TLS\_ALG\_BITS}
maximum key length of the symmetric encryption algorithm.
This may be less than the effective key length
for {\it export controlled} algorithms.
\item
{\tt PMM\_TLS\_VRFY}	
the result of the verification of the presented cert.
\item
{\tt PMM\_TLS\_CERT\_SUBJECT}
the DN (distinguished name) of the presented certificate.
\item
{\tt PMM\_TLS\_CERT\_ISSUER}
the DN (distinguished name) of the CA (certifcate authority)
that signed the presented certificate (the cert issuer).
\item
{\tt PMM\_TLS\_CN\_SUBJECT}
the CN (common name) of the presented certificate.
\item
{\tt PMM\_TLS\_CN\_ISSUER}
the CN (common name) of the CA that signed the presented certificate.
\end{enumerate}

Notes:
\begin{itemize}
\item
{\tt PMM\_MAIL\_TAID} cannot be requested before {\tt PM\_SMST\_MAIL}
and
{\tt PMM\_DOT\_MSGID} can only be requested at stage
{\tt PM\_SMST\_DOT}.
\item
All macros beginning with {\tt PMM\_TLS} are only valid
after a {\tt STARTTLS} command.
\end{itemize}

To retrieve the value of a symbol the function

{\tt sm\_pmfi\_getmac(pmse\_ctx\_P pmse\_ctx, uint32\_t macro, char **pvalue)}

can be used in the various callback functions of the ESMTP dialogue.
If the macro was not in the request list, an error will be returned.
If the macro has not yet been received, {\tt *pvalue} will be {\tt NULL}.
Otherwise {\tt *pvalue} will point to the value of the macro.
Note: the string to which {\tt *pvalue} points must {\em not} be changed.

% BEGIN X-1
\subsection{Sender Modification}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SenderModification}

The sender address ({\tt MAIL}) can be replaced:

{\tt sm\_ret\_T sm\_pmfi\_mail\_rplc(pmse\_ctx\_P pmse\_ctx, const char *mail\_pa, char **argv)}

This function must only be called during {\tt pmfi\_eom()}.
The address {\tt mail\_pa} must be in RFC 2821 format.
The argument {\tt argv} can be used to specify SMTP parameters for
the sender address, however,
this is currently not implemented,
hence it must be set to {\tt NULL} for now.

\subsection{Recipient Modifications}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:RecipientModifications}

Recipients can be added:

{\tt sm\_ret\_T sm\_pmfi\_rcpt\_add(pmse\_ctx\_P pmse\_ctx, const char *rcpt\_pa, char **argv)}

or deleted:

{\tt sm\_ret\_T sm\_pmfi\_rcpt\_del(pmse\_ctx\_P pmse\_ctx, const char *rcpt\_pa, rcpt\_idx\_T rcpt\_idx)}

These functions must only be called during {\tt pmfi\_eom()}.
The addresses {\tt rcpt\_pa} must be in RFC 2821 format.
The argument {\tt argv} can be used to specify SMTP parameters for
the recipient address, however,
this is currently not implemented,
hence it must be set to {\tt NULL} for now.
As the MTA does not remove identical recipient addresses,
the address itself is not sufficient to identify one {\tt RCPT},
but its index must be specified too.
This index can be retrieved during a {\tt RCPT} command
({\tt pmfi\_rcpt()})
using

{\tt sm\_ret\_T sm\_pmfi\_getrcpt\_idx(pmse\_ctx\_P pmse\_ctx, rcpt\_idx\_T *prcpt\_idx)}

Note: it is invalid to remove all recipients of a transaction.
To discard a transaction, return {\tt SMTP\_R\_DISCARD}
as a result of one of the transaction oriented callbacks, e.g.,
{\tt pmfi\_eom()}.

\subsection{Header Modifications}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:HeaderModifications}

To request modifications of the header of a mail being sent, the function

{\tt sm\_pmfi\_hdr\_mod(pmse\_ctx\_P pmse\_ctx, uint type, uint pos, const unsigned char *header)}

can be used.
This function must only be called during {\tt pmfi\_eom()}.
The argument {\tt type} specifies which kind of modification is requested,
legitimate values are defined in {\tt include/sm/hdrmoddef.h};
these are:
{\tt SM\_HDRMOD\_T\_PREPEND},
{\tt SM\_HDRMOD\_T\_INSERT},
{\tt SM\_HDRMOD\_T\_REPLACE},
{\tt SM\_HDRMOD\_T\_REMOVE}, and
{\tt SM\_HDRMOD\_T\_APPEND}.

The argument {\tt header} must be a complete header line including the
proper line ending (CRLF).
The argument {\tt pos} specifies the position for the types
{\tt SM\_HDRMOD\_T\_INSERT},
{\tt SM\_HDRMOD\_T\_REPLACE}, and
{\tt SM\_HDRMOD\_T\_REMOVE}.
The first header of the original mail has position one;
zero is the {\tt Received:} header added by the SMTP server.

\subsection{Message Replacement}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:MessageReplacement}

If a pmilter wants to replace the entire message, the function
{\tt pmfi\_eom()}
must return the value
{\tt SMTP\_R\_RPLCMSG}.
This will cause the invocation of the callback

{\tt sfsistat\_T pmfi\_msg\_rplc(pmse\_ctx\_P pmse\_ctx, const unsigned char **pmsgchunk, size\_t *pmsglen)}

which then must set a pointer to a message chunk and its length.
Multiple chunks can be sent by returning
{\tt SMTP\_R\_CONT}
as result of the callback.
For the last chunk,
{\tt SMTP\_R\_OK}
should be returned.
The size of each chunk ({\tt pmsglen}) must be less than
{\tt PMILTER\_CHUNK\_SIZE}
as defined in
{\tt include/sm/pmfapi.h}.

libpmilter will thereafter invoke the callback

{\tt sfsistat\_T pmfi\_msg\_rplc\_stat(pmse\_ctx\_P pmse\_ctx, sm\_ret\_T status)}

to give the pmilter a chance to clean up after the transaction,
and to let it know whether the message replacement was successful.

Notes:
\begin{itemize}
\item
as the entire message is replaced and by default only the data that
is received from the SMTP client is sent to a pmilter,
the {\tt Received:} header field that smtps generates is lost.
To avoid this, a pmilter can request to receive that header field
by setting
{\tt SM\_SCAP\_PM\_SND\_RCVD}
and sending it as first chunk of the message replacement.
\item
the message must be in SMTP format, i.e., lines must end in CRLF
and the final chunk should have the usual SMTP end of message indication:
CRLF dot CRLF
({\tt \textbackslash r\textbackslash n.\textbackslash r\textbackslash n}),
however, it can also just end in CRLF.
\item
if
{\tt pmfi\_msg\_rplc()}
encounters an error, e.g.,
due to an API violation or due to a communication error with smtps,
then it will invoke
{\tt pmfi\_msg\_rplc\_stat()}
directly without waiting for the entire message
even if it consists of more chunks.
\end{itemize}

% END X-1


\subsection{Further Capabilities}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities}

In addition to selecting which SMTP commands to send to pmilter
(see Section
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:NewSMTPServer}),
there are some more capabilities available:

\begin{enumerate}
\item
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities:SMSCAPPMRCPTST}
{\tt SM\_SCAP\_PM\_RCPT\_ST}
causes the MTA to send {\tt RCPT} information
even if the command has been rejected,
e.g., because the recipient is unknown, the recipient has been rejected
due to access map checks, or relaying has been denied.
Note: {\tt RCPT} commands that are rejected for other reasons, e.g.,
because the address is syntactically invalid, or some limit
(maximum number of recipients) is exceeded,
will not be sent to pmilter.

The function

{\tt sm\_ret\_T sm\_pmfi\_getstatus(pmse\_ctx\_P pmse\_ctx, sfsistat\_T *pstatus)}

should be used in that case to access the current SMTP reply code
for the command.
This functionality is useful for a pmilter that wants
to keep track of all recipients, not just those which are accepted,
e.g., to deal with dictionary attacks.

\item
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:FurtherCapabilities:SMSCAPPMMSGRC}
{\tt SM\_SCAP\_PM\_MSG\_RC}
allows a pmilter to return a reply code
as specified in
\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:ReturnValues}
from {\tt pmfi\_msg()}.
This is useful if a pmilter can make a decision about the mail
without having to read the entire message.
If this capability is turned on,
{\tt pmfi\_msg()}
must return
{\tt SMTP\_R\_CONT}
for each message chunk by default to receive subsequent parts.
Otherwise {\tt pmfi\_eom()} will not be called but the return code from
{\tt pmfi\_msg()}
will be used at the end of the message (in response to the final dot).
\end{enumerate}

\subsection{Miscellaneous Functions}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:MiscellaneousFunctions}

To set a reply text in an SMTP session or transaction oriented callback
in addition to the reply code use:

{\tt sm\_ret\_T sm\_pmfi\_setreply(pmse\_ctx\_P pmse\_ctx, const char *reply)}

Note: the reply string {\em must} contain the full SMTP reply, i.e., it
must be of the form

{\tt XYZ D.S.N text\textbackslash r\textbackslash n}

where
{\tt XYZ} is a valid SMTP reply code (see RFC 2821 \cite{bib:rfc2821})
which {\em must} match the return code of the function from which
{\tt sm\_pmfi\_setreply()} is called,
{\tt D.S.N} is an enhanced status code as defined in RFC 3463
\cite{bib:rfc3463}
and the rest is an explanation of the status including CRLF
({\tt \textbackslash r\textbackslash n}).


To set reply codes for commands that need multiple reply values
the function:

{\tt sm\_ret\_T sm\_pmfi\_setreplies(pmse\_ctx\_P pmse\_ctx, uint nreplies,
int *rcodes, const char **rtexts)}

must be used.
Currently this function makes only sense if
{\tt PRDR}
is available in the SMTP server and actually used by the client.
A pmilter can determine the latter by parsing the arguments of the
{\tt MAIL}
command (see {\tt pmfi\_mail()}.
Note: currently the argument {\tt rtexts} is ignored,
only the array of reply codes ({\tt rcodes}) is used.
The size of this array must be {\tt nreplies}
which must match the number of valid {\tt RCPT}s for this transaction.
The reply codes in that array must be in the same order
in which the {\tt RCPT}s have been received.
% For details see ...

Return version number of libpmilter:

{\tt sm\_ret\_T sm\_pmfi\_version(pmg\_ctx\_P pmg\_ctx, uint32\_t *major, uint32\_t *minor, uint32\_t *patchlevel)}

This can be used to compare the version number of the library against
which pmilter is linked
with the version number against which pmilter is compiled.
The major version numbers must match otherwise the program will not run.

Signal handler callback:

{\tt sm\_ret\_T pmfi\_signal(pmg\_ctx\_P pmg\_ctx, int sig)}

This function will be called when a USR1 or USR2 signal is received;
it is not called within a signal handler, i.e., the code does not have
to be signal-safe.
Note: this is not yet implemented.

\subsection{Return Values}
\label{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:ReturnValues}

SMTP Session and transaction oriented functions
% (\ref{func:Milter:ExternalInterfaces:NativePolicyMilterAPI:SMTPSessionandTransaction})
use 
{\tt sfsistat\_T}
as return type.
Allowed values for this type are (as defined in
{\tt include/sm/smreplycodes.h}):
\begin{itemize}
\item
{\tt SMTP\_R\_OK}: accept command.
\item
{\tt SMTP\_R\_ACCEPT}: accept entire transaction or session
depending on where this value is returned.
Note: this is just a shortcut for
{\tt SMTP\_R\_SET\_QUICK(SMTP\_R\_OK)}.
%not yet implemented
%\item
%{\tt SMTP\_R\_RELAY}: allow relaying for this transaction.
\item
{\tt SMTP\_R\_DISCARD}: discard effect of command.
\item
{\tt SMTP\_R\_CONT}: continue other checks.
%\item
%{\tt SMTP\_R\_DUNNO}: continue other checks.
%\item
%{\tt SMTP\_R\_SKIP}: continue other checks.
\item
{\tt SMTP\_R\_SSD}: shut down SMTP session.
\item
{\tt SMTP\_R\_TEMP}: reject command with a temporary error.
\item
{\tt SMTP\_R\_SYNTAX}: syntax error.
%\item
%{\tt SMTP\_R\_REJECT}: reject command.
\item
{\tt SMTP\_R\_PERM}: reject command with a permanent error.
\item
other valid SMTP reply codes
\cite{bib:rfc2821}.
\end{itemize}

Additionally return values can be modified by using
{\tt SMTP\_R\_SET\_QUICK(returnvalue)}.
See Section \ref{ConfigurationMapsforSMAR:access:quick}
for the effects of this.

%% what to do for: accept command and don't bother me anymore?
%% SMTP_R_SET_QUICK(SMTPR_OK)??

For functions that use {\tt sm\_ret\_T} as return type
a successful call returns {\tt SM\_SUCCESS} (0)
and a negative value in case of an error.
