${COURIER_HOME}/lib/modules
contains shared
libraries and programs which provide input and output modules to Courier. The
actual module itself can actually be installed and invoked anywhere else, the
SMTP module, in fact, is installed in bin. The shared library in lib,
however, may include functions which rewrite addresses for messages submitted
from the input module. All files for module named MODULE are stored in the
directory ${COURIER_HOME}/lib/modules/MODULE
.
modules.ctl
file${COURIER_HOME}/lib/modules/modules.ctl
contains a list
of all the modules that Courier loads. modules.ctl
is only used
if Courier is compiled with shared library support. If Courier is compiled
with static linkage only (by choice, or by necessity), all the module
libraries are statically linked into Courier, and modules.ctl
is
not used. Each line in modules.ctl is in the following form:
priority<SP>name<SP>filename, where
<SP>
designates the space character. The lines are sorted
in increasing priority order! "priority" is taken from the module's config
file, and filename is the filename of the shared library.
${COURIER_HOME}/lib/modules/name
directorymodules.ctl
file.
NAME=VALUE
notation, where NAME
is the name of the
configuration parameter, and VALUE
is its value.
submit
and
courierd
. The shared library provides code to rewrite addresses
to and from the canonical format for messages to or from this module. If
Courier is compiled with static linkage, this file does not exist - the
library is statically linked.
The actual name of the library may vary, and is specified in the
config
file.
If this shared library does not exist, an attempt is made to load
librewrite-old
. This allows an updated version of the shared
library to be installed in a live, running, system by renaming the current
one to librewrite-old
, then renaming the new one to
librewrite
.
Although submit will pick up new rewriting rules immediately,
courierd
must be SIGHUPed in order to reload the new shared
library.
NAME=name
- specifies the name of this module. Should be the
same as the directory name.
LIBRARY=filename
- specifies the name of the shared library
to load. Not used if Courier was compiled with static libraries.
VERSION=version
- version of the module interface. Not the
actual version of the module, but version of the interface between the
module, and Courier. The current version is version 0.
PRIORITY=n
- priority of the output module. Courier calls all
the modules' rewritedel
functions in the increasing priority
order until it finds one which accepts messages addressed to the recipient's
address.
PROG=pathname
- pathname to the output module program. Must
be a full path, unless the module itself is in the lib/MODULE
directory. If the PROG
parameter is missing, this module is an
input only module. If the attempt to execute PROG
fails, Courier
will attempt to execute PROG-old
, which allows an updated output
module to be inserted into a live system by renaming the current one
PROG-old
, then renaming the new output module as
PROG
.
MAXDELS=n
- maximum concurrent deliveries for this module. No
more than these many instances of PROG
will be started at any
given time.
MAXHOST=n
- maximum instances of PROG
that will
be started with the same HOST
parameter.
MAXRCPT=n
- maximum number of addresses that will be given to
any single instance of PROG
.
Please note that although these parameters are reread by
courierd
when it restarts, the individual output module may
impose its own restrictions on the valid limits for these parameters.
XXXX
is used to represent the name of the function in the
library for module XXXX
. For example, in the "local" module, the
rw_install
function is called local_rw_install
.
struct rw_list *XXXX_rw_install(const struct rw_install_info
*info);
The rw_install()
function is called after the shared library
is open. It receives a pointer to the following structure:
struct rw_install_info { int rw_verlo; int rw_verhi; const char *courier_home; } ;
rw_verlo/rw_verhi
- reserved for future expansion. Currently set
to 0. rw_install
function of modules compatible with this
Courier interface must check that rw_verlo
is set to 0, and
ignore rw_verhi
.
courier_home
- Courier's home directory, the shared library
can use this to find its files.
rw_search
functionstruct rw_list *rw_search(const char *);The
rw_search
function can be called by a library function in
order to return the rw_list (see below) structure of a function in another
library. rw_search
may NOT be called form
XXXX_rw_install
, because the library containing this function
may not've been installed yet. rw_search
may be called from the
XXXX_rw_init
function.
const char *XXXX_rw_init()
After all modules are installed, each module's rw_init()
function is called, which can complete any additional setup.
rw_init
should return a null pointer. Otherwise,
rw_init
should return the error message text. Courier will
refuse to start if rw_init
does not return a null pointer.
The library's rw_install
function must return a pointer to
the following structure. If rw_install
returns a NULL pointer,
Courier will refuse to start.
struct rw_list { int rw_version; void (*rewrite)(struct rw_info *info, void (*func)(struct rw_info *)); void (*rewrite_del)(struct rw_info *info, void (*func)(struct rw_info *), void (*delfunc)(struct rw_info *info, const struct rfc822token *host, const struct rfc822 *addr)); int (*filter_msg)(const char *, int, const char *, const char *, const char *, char *, unsigned); } ;
rw_version
- shared libraries compatible with this module
interface must set rw_version to zero.
rewrite
- this function is called to rewrite a single
address. The first argument points to the following structure:
struct rw_info {
int mode;
struct rfc822token *ptr;
void (*err_func)(int, const char *, struct rw_info *);
const struct rfc822token *sender;
const char *smodule;
void *udata;
} ;
mode
contains the following values, ORed into a bitmask:
RW_ENVSENDER
- rewrite envelope sender,
RW_ENVRECIPIENT
- rewrite envelope recipient,
RW_HEADER
- rewrite header. When calling rewrite()
,
one of these three bits will be set. Additional bits that may be set:
RW_OUTPUT
- rewrite
() should convert canonical
format to the transport format. If this bit is not set, rewrite should
convert from the transport format to the canonical format. In fact, the main
courierd
does not call rewrite with the RW_OUTPUT
bit set, because that function is performed by the dedicated output module,
which may handle rewriting on its own. Also, the RW_SUBMIT
can
be set together with RW_ENVSENDER
or
RW_ENVRECIPIENT
, indicating that this call is as a result of a
message being submitted for delivery (as opposed to address verification for
EXPN/VRFY
functionality).
It is possible that none of those bits are set, when invoked by another rewrite function.
ptr
is the address to rewrite, as rfc822 tokens.
udata
contains an arbitrary pointer, for usage by rewrite's
caller.
When mode has RW_ENVRECIPIENT
set, sender
points
to the envelope sender format in the canonical format (previous result of
RW_ENVSENDER
), otherwise this field is unused. If
sender
is NULL, this should be interpreted as an empty envelope
sender (or if rewrite
is being called in test mode.
smodule
is initialized when mode has the
RW_SUBMIT
bit set. It will point to the name of the module
argument to submit - the module sending the message.
err_func
is the function to call in case of an error.
rewrite
is expected to call either func
, (it's
second argument), or err_func
, before returning. If rewriting
succeeds, func
is called. If rewriting failed,
rewrite
must call the err_func
is function.
errcode
will be the RFC822-style error number,
errmsg
is the error message, which may be multiline (includes
newlines). The text in errmsg
is NOT prefixed by the error
number.
After calling func
, or err_func
,
rewrite
is expected to immediately terminate.
rewrite
may alter the 'ptr' link list in any form or fashion it
wants, except that it may NOT malloc or free any node, or a part thereof.
However, it can relink portions of the link list, or modify the link pointers
to include tokens created by rewrite
internally.
After func
or err_func
terminates,
rewrite
may deallocate everything it allocated, then terminate
itself.
This interface allows rewrite to execute very quickly, without allocating or deallocating any memory. If new RFC822 tokens are required, they can be allocated on the stack, and linked into the original list.
The rewrite_del
function is called to determine if the module
can accept delivery of a message to this address. The
rewrite_del
of all installed libraries are called until one of
them calls the delfunc
function. If rewrite_del
cannot accept delivery of a message, it should call func
. The
rewrite_del
function should call the delfunc
function to indicate that this module can accept mail addressed to the
address specified by info->ptr
. rewrite_del
receives the pointer to the rw_info structure, then the host
and
the address
information for the output module, as rfc822token
lists. If the mode field has the RW_SUBMIT
bit set, rewrite_del
can find the message's envelope sender address in canonical format) in
info->sender
.
Like rewrite
, rewrite_del
may make arbitrary
changes to info->ptr
, except that it may not deallocate
memory used for the existing list. rewrite_del
may modify the
link list, and allocate memory for new rfc822 tokens. After calling either
func
or delfunc
, rewrite_del
should
terminate immediately, and deallocate any allocated memory.
rewrite_del
must keep track of any allocated memory separate,
and cannot assume that info->ptr
hasn't changed.
When RW_SUBMIT
bit is set, rewrite_del
can be
used to perform additional recipient-specific code, which may be too
expensive to run every time courier goes through the queue. The
udata
field contains a pointer to caller-specific data. The
sender
field contains a pointer to the envelope sender, in
canonical format. Like rewrite
, rewrite_del
may
muck around with the rfc822token
list in ptr
.
rewrite_del
functions are called in order according to the
configured module priority. By setting a higher priority, it is possible to
have rewrite_del
rewrite the address so that it would be
accepted by another module's rewrite_del
, down the chain.
The last function, rw_filter_msg
, is called to run an
arbitrary spam filter which can be used to selectively reject messages.
rw_filter_msg
will be called after rewrite_del
accepted the message for delivery. The arguments are as follows:
rewrite_del
, as a text
string.rewrite_del
, as a text
string.rw_filter_msg
should return 0 if the message is acceptable, a
negative value to permanently reject the message to this recipient, and a
positive value for a temporary rejection.
PROG
PROG
is the output module that will be started by Courier when
it comes up. PROG can be a shell command, it is executed via "$SHELL
-c
". It will be started under userid mail, group mail, in
${COURIER_HOME}/lib/modules/NAME
. Courier will communicate with
PROG
on standard input and output.
If PROG
succesfully initializes, it should fork, and the
parent should exit with status of 0. Courier waits until PROG
exits. If it exits with a non-0 exit code, Courier will fail starting up. The
child process will then continue to read and write from standard output.
COURIER_HOME
, MAXDELS
, MAXHOST
, and
MAXRCPTS
will be placed in the environment, prior to executing
PROG
.
Tip: if the environment variables are not set, PROG
can
presume that it's being run in test mode, from the command line, and forego
the fork.
If PROG
terminates, Courier will consider it a fatal error
(Courier detects the input channel being closed).
If PROG
gets an end-of-file indication, it can assume that
Courier is being shut down, so it should promptly cease all activities,
without waiting for pending messages to be delivered.
To start delivery for a particular message, PROG
will receive
a newline-terminated command, specifying the message, and the recipients, and
the delivery ID. Once PROG
finishes delivering messages,
PROG
should write the results of the delivery into the message's
control file, then print the delivery ID on its standard output, terminated
by newline. If the module's config file specifies that the module can handle
multiple deliveries at the same time, PROG
may receive
additional deliveries before it finishes delivering the first message.
The command that prog receives is a newline-terminated line that looks like this:
msgnum<tab>sender<tab>delid<tab>host<tab>num1<tab>addr1<tab>num2<tab>addr2...
<tab> represents the ASCII tab character. This is basically a list of tab-separated fields. The first field is the message id number (the inode number).
sender is the message envelope sender, after it's rewritten to the module format, by the module shared library.
delid is the "delivery ID", which is a small integer, representing this
delivery. After PROG
finishes delivering the message, it should
print the message's delivery ID on standard output after saving the delivery
status of each recipient in the control file.
The host field specifies the host where the message should be delivered
to, as determined by the module's output rewrite rule. Following the host,
there will be one or more num/address pairs. address is the recipient's
address as determined by the output rewrite rule, and num is the recipient's
number in the message's list of recipients (this is used to save the delivery
status in the control file). Note that the address can be an empty string, so
there will be two consecutive tabs there.