summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libmilter
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/libmilter')
-rw-r--r--contrib/sendmail/libmilter/Makefile19
-rw-r--r--contrib/sendmail/libmilter/Makefile.m440
-rw-r--r--contrib/sendmail/libmilter/README234
-rw-r--r--contrib/sendmail/libmilter/comm.c357
-rw-r--r--contrib/sendmail/libmilter/docs/api.html322
-rw-r--r--contrib/sendmail/libmilter/docs/design.html147
-rw-r--r--contrib/sendmail/libmilter/docs/figure1.fig56
-rw-r--r--contrib/sendmail/libmilter/docs/figure1.jpgbin0 -> 21406 bytes
-rw-r--r--contrib/sendmail/libmilter/docs/figure1.ps173
-rw-r--r--contrib/sendmail/libmilter/docs/figure2.fig67
-rw-r--r--contrib/sendmail/libmilter/docs/figure2.jpgbin0 -> 47947 bytes
-rw-r--r--contrib/sendmail/libmilter/docs/figure2.ps242
-rw-r--r--contrib/sendmail/libmilter/docs/index.html92
-rw-r--r--contrib/sendmail/libmilter/docs/installation.html165
-rw-r--r--contrib/sendmail/libmilter/docs/other.html18
-rw-r--r--contrib/sendmail/libmilter/docs/overview.html217
-rw-r--r--contrib/sendmail/libmilter/docs/sample.html537
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_addheader.html130
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_addrcpt.html83
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_addrcpt_par.html88
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_chgfrom.html94
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_chgheader.html125
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_delrcpt.html82
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_getpriv.html62
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_getsymval.html105
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_insheader.html150
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_main.html51
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_opensocket.html84
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_progress.html68
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_quarantine.html73
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_register.html224
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_replacebody.html93
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setbacklog.html64
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setconn.html93
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setdbg.html67
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setmlreply.html145
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setpriv.html80
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setreply.html117
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_setsymlist.html107
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_settimeout.html66
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_stop.html74
-rw-r--r--contrib/sendmail/libmilter/docs/smfi_version.html92
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_abort.html83
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_body.html97
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_close.html81
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_connect.html121
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_data.html89
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_envfrom.html97
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_envrcpt.html97
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_eoh.html56
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_eom.html62
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_header.html111
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_helo.html64
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_negotiate.html277
-rw-r--r--contrib/sendmail/libmilter/docs/xxfi_unknown.html84
-rw-r--r--contrib/sendmail/libmilter/engine.c1915
-rw-r--r--contrib/sendmail/libmilter/example.c298
-rw-r--r--contrib/sendmail/libmilter/handler.c50
-rw-r--r--contrib/sendmail/libmilter/libmilter.h335
-rw-r--r--contrib/sendmail/libmilter/listener.c974
-rw-r--r--contrib/sendmail/libmilter/main.c247
-rw-r--r--contrib/sendmail/libmilter/monitor.c227
-rw-r--r--contrib/sendmail/libmilter/signal.c225
-rw-r--r--contrib/sendmail/libmilter/sm_gethost.c148
-rw-r--r--contrib/sendmail/libmilter/smfi.c889
-rw-r--r--contrib/sendmail/libmilter/worker.c775
66 files changed, 12505 insertions, 0 deletions
diff --git a/contrib/sendmail/libmilter/Makefile b/contrib/sendmail/libmilter/Makefile
new file mode 100644
index 0000000..b5bfba4
--- /dev/null
+++ b/contrib/sendmail/libmilter/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 8.2 2006/05/23 21:55:55 ca Exp $
+
+SHELL= /bin/sh
+BUILD= ./Build
+OPTIONS= $(CONFIG) $(FLAGS)
+
+all: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+check: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+clean: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+install: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) $@
+
+fresh: FRC
+ $(SHELL) $(BUILD) $(OPTIONS) -c
+
+FRC:
diff --git a/contrib/sendmail/libmilter/Makefile.m4 b/contrib/sendmail/libmilter/Makefile.m4
new file mode 100644
index 0000000..bc9bc66
--- /dev/null
+++ b/contrib/sendmail/libmilter/Makefile.m4
@@ -0,0 +1,40 @@
+dnl $Id: Makefile.m4,v 8.85 2009/11/24 21:59:33 ca Exp $
+include(confBUILDTOOLSDIR`/M4/switch.m4')
+
+dnl only required for compilation of EXTRAS
+define(`confREQUIRE_SM_OS_H', `true')
+define(`confMT', `true')
+
+# sendmail dir
+SMSRCDIR=ifdef(`confSMSRCDIR', `confSMSRCDIR', `${SRCDIR}/sendmail')
+PREPENDDEF(`confINCDIRS', `-I${SMSRCDIR} ')
+
+bldPRODUCT_START(`library', `libmilter')
+define(`bldINSTALLABLE', `true')
+define(`LIBMILTER_EXTRAS', `errstring.c strl.c')
+APPENDDEF(`confENVDEF', `-DNOT_SENDMAIL -Dsm_snprintf=snprintf')
+define(`bldSOURCES', `main.c engine.c listener.c worker.c handler.c comm.c smfi.c signal.c sm_gethost.c monitor.c LIBMILTER_EXTRAS ')
+define(`confBEFORE', `LIBMILTER_EXTRAS')
+bldPUSH_INSTALL_TARGET(`install-mfapi')
+bldPRODUCT_END
+
+PUSHDIVERT(3)
+errstring.c:
+ ${LN} ${LNOPTS} ${SRCDIR}/libsm/errstring.c .
+
+strl.c:
+ ${LN} ${LNOPTS} ${SRCDIR}/libsm/strl.c .
+POPDIVERT
+
+
+divert(bldTARGETS_SECTION)
+# Install the API header files
+MFAPI= ${SRCDIR}/inc`'lude/libmilter/mfapi.h
+MFDEF= ${SRCDIR}/inc`'lude/libmilter/mfdef.h
+install-mfapi: ${MFAPI}
+ if [ ! -d ${DESTDIR}${INCLUDEDIR}/libmilter ]; then mkdir -p ${DESTDIR}${INCLUDEDIR}/libmilter; else :; fi
+ ${INSTALL} -c -o ${INCOWN} -g ${INCGRP} -m ${INCMODE} ${MFAPI} ${DESTDIR}${INCLUDEDIR}/libmilter/mfapi.h
+ ${INSTALL} -c -o ${INCOWN} -g ${INCGRP} -m ${INCMODE} ${MFDEF} ${DESTDIR}${INCLUDEDIR}/libmilter/mfdef.h
+divert(0)
+
+bldFINISH
diff --git a/contrib/sendmail/libmilter/README b/contrib/sendmail/libmilter/README
new file mode 100644
index 0000000..e9c2cb1
--- /dev/null
+++ b/contrib/sendmail/libmilter/README
@@ -0,0 +1,234 @@
+This directory contains the source files for libmilter.
+
+The sendmail Mail Filter API (Milter) is designed to allow third-party
+programs access to mail messages as they are being processed in order to
+filter meta-information and content.
+
+This README file describes the steps needed to compile and run a filter,
+through reference to a sample filter which is attached at the end of this
+file. It is necessary to first build libmilter.a, which can be done by
+issuing the './Build' command in SRCDIR/libmilter .
+
+Starting with 8.13 sendmail is compiled by default with support for
+the milter API.
+
+Note: if you want to write a milter in Java, then see
+http://sendmail-jilter.sourceforge.net/
+
++----------------+
+| SECURITY HINTS |
++----------------+
+
+Note: we strongly recommend not to run any milter as root. Libmilter
+does not need root access to communicate with sendmail. It is a
+good security practice to run a program only with root privileges
+if really necessary. A milter should probably check first whether
+it runs as root and refuse to start in that case. libmilter will
+not unlink a socket when running as root.
+
++----------------------+
+| CONFIGURATION MACROS |
++----------------------+
+
+Libmilter uses a set of C preprocessor macros to specify platform specific
+features of the C compiler and standard C libraries.
+
+SM_CONF_POLL
+ Set to 1 if poll(2) should be used instead of select(2).
+
++-------------------+
+| BUILDING A FILTER |
++-------------------+
+
+The following command presumes that the sample code from the end of this
+README is saved to a file named 'sample.c' and built in the local platform-
+specific build subdirectory (SRCDIR/obj.*/libmilter).
+
+ cc -I../../include -o sample sample.c libmilter.a ../libsm/libsm.a -pthread
+
+It is recommended that you build your filters in a location outside of
+the sendmail source tree. Modify the compiler include references (-I)
+and the library locations accordingly. Also, some operating systems may
+require additional libraries. For example, SunOS 5.X requires '-lresolv
+-lsocket -lnsl'. Depending on your operating system you may need a library
+instead of the option -pthread, e.g., -lpthread.
+
+Filters must be thread-safe! Many operating systems now provide support for
+POSIX threads in the standard C libraries. The compiler flag to link with
+threading support differs according to the compiler and linker used. Check
+the Makefile in your appropriate obj.*/libmilter build subdirectory if you
+are unsure of the local flag used.
+
+Note that since filters use threads, it may be necessary to alter per
+process limits in your filter. For example, you might look at using
+setrlimit() to increase the number of open file descriptors if your filter
+is going to be busy.
+
+
++----------------------------------------+
+| SPECIFYING FILTERS IN SENDMAIL CONFIGS |
++----------------------------------------+
+
+Filters are specified with a key letter ``X'' (for ``eXternal'').
+
+For example:
+
+ Xfilter1, S=local:/var/run/f1.sock, F=R
+ Xfilter2, S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m
+ Xfilter3, S=inet:3333@localhost
+
+specifies three filters. Filters can be specified in your .mc file using
+the following:
+
+ INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R')
+ INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m')
+ INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost')
+
+The first attaches to a Unix-domain socket in the /var/run directory; the
+second uses an IPv6 socket on port 999 of localhost, and the third uses an
+IPv4 socket on port 3333 of localhost. The current flags (F=) are:
+
+ R Reject connection if filter unavailable
+ T Temporary fail connection if filter unavailable
+ 4 Shut down connection if filter unavailable
+ (with a 421 temporary error).
+
+If none of these is specified, the message is passed through sendmail
+in case of filter errors as if the failing filters were not present.
+
+Finally, you can override the default timeouts used by sendmail when
+talking to the filters using the T= equate. There are four fields inside
+of the T= equate:
+
+Letter Meaning
+ C Timeout for connecting to a filter (if 0, use system timeout)
+ S Timeout for sending information from the MTA to a filter
+ R Timeout for reading reply from the filter
+ E Overall timeout between sending end-of-message to filter
+ and waiting for the final acknowledgment
+
+Note the separator between each is a ';' as a ',' already separates equates
+and therefore can't separate timeouts. The default values (if not set in
+the config) are:
+
+T=C:5m;S:10s;R:10s;E:5m
+
+where 's' is seconds and 'm' is minutes.
+
+Which filters are invoked and their sequencing is handled by the
+InputMailFilters option. Note: if InputMailFilters is not defined no filters
+will be used.
+
+ O InputMailFilters=filter1, filter2, filter3
+
+This is is set automatically according to the order of the
+INPUT_MAIL_FILTER commands in your .mc file. Alternatively, you can
+reset its value by setting confINPUT_MAIL_FILTERS in your .mc file.
+This options causes the three filters to be called in the same order
+they were specified. It allows for possible future filtering on output
+(although this is not intended for this release).
+
+Also note that a filter can be defined without adding it to the input
+filter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your
+.mc file.
+
+To test sendmail with the sample filter, the following might be added (in
+the appropriate locations) to your .mc file:
+
+ INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
+
+
++------------------+
+| TESTING A FILTER |
++------------------+
+
+Once you have compiled a filter, modified your .mc file and restarted
+the sendmail process, you will want to test that the filter performs as
+intended.
+
+The sample filter takes one argument -p, which indicates the local port
+on which to create a listening socket for the filter. Maintaining
+consistency with the suggested options for sendmail.cf, this would be the
+UNIX domain socket located in /var/run/f1.sock.
+
+ % ./sample -p local:/var/run/f1.sock
+
+If the sample filter returns immediately to a command line, there was either
+an error with your command or a problem creating the specified socket.
+Further logging can be captured through the syslogd daemon. Using the
+'netstat -a' command can ensure that your filter process is listening on
+the appropriate local socket.
+
+Email messages must be injected via SMTP to be filtered. There are two
+simple means of doing this; either using the 'sendmail -bs' command, or
+by telnetting to port 25 of the machine configured for milter. Once
+connected via one of these options, the session can be continued through
+the use of standard SMTP commands.
+
+% sendmail -bs
+220 test.sendmail.com ESMTP Sendmail 8.14.0/8.14.0; Thu, 22 Jun 2006 13:05:23 -0500 (EST)
+HELO localhost
+250 test.sendmail.com Hello testy@localhost, pleased to meet you
+MAIL From:<testy>
+250 2.1.0 <testy>... Sender ok
+RCPT To:<root>
+250 2.1.5 <root>... Recipient ok
+DATA
+354 Enter mail, end with "." on a line by itself
+From: testy@test.sendmail.com
+To: root@test.sendmail.com
+Subject: testing sample filter
+
+Sample body
+.
+250 2.0.0 dB73Zxi25236 Message accepted for delivery
+QUIT
+221 2.0.0 test.sendmail.com closing connection
+
+In the above example, the lines beginning with numbers are output by the
+mail server, and those without are your input. If everything is working
+properly, you will find a file in /tmp by the name of msg.XXXXXXXX (where
+the Xs represent any combination of letters and numbers). This file should
+contain the message body and headers from the test email entered above.
+
+If the sample filter did not log your test email, there are a number of
+methods to narrow down the source of the problem. Check your system
+logs written by syslogd and see if there are any pertinent lines. You
+may need to reconfigure syslogd to capture all relevant data. Additionally,
+the logging level of sendmail can be raised with the LogLevel option.
+See the sendmail(8) manual page for more information.
+
+
++--------------+
+| REQUIREMENTS |
++--------------+
+
+libmilter requires pthread support in the operating system. Moreover, it
+requires that the library functions it uses are thread safe; which is true
+for the operating systems libmilter has been developed and tested on. On
+some operating systems this requires special compile time options (e.g.,
+not just -pthread). libmilter is currently known to work on (modulo problems
+in the pthread support of some specific versions):
+
+FreeBSD 3.x, 4.x
+SunOS 5.x (x >= 5)
+AIX 4.3.x
+HP UX 11.x
+Linux (recent versions/distributions)
+
+libmilter is currently not supported on:
+
+IRIX 6.x
+Ultrix
+
+Feedback about problems (and possible fixes) is welcome.
+
++--------------------------+
+| SOURCE FOR SAMPLE FILTER |
++--------------------------+
+
+Note that the filter example.c may not be thread safe on some operating
+systems. You should check your system man pages for the functions used
+below to verify the functions are thread safe.
+
+$Revision: 8.42 $, Last updated $Date: 2006/06/29 17:10:16 $
diff --git a/contrib/sendmail/libmilter/comm.c b/contrib/sendmail/libmilter/comm.c
new file mode 100644
index 0000000..e04681c
--- /dev/null
+++ b/contrib/sendmail/libmilter/comm.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 1999-2004, 2009 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: comm.c,v 8.70 2009/12/16 16:33:48 ca Exp $")
+
+#include "libmilter.h"
+#include <sm/errstring.h>
+#include <sys/uio.h>
+
+static ssize_t retry_writev __P((socket_t, struct iovec *, int, struct timeval *));
+static size_t Maxdatasize = MILTER_MAX_DATA_SIZE;
+
+/*
+** SMFI_SETMAXDATASIZE -- set limit for milter data read/write.
+**
+** Parameters:
+** sz -- new limit.
+**
+** Returns:
+** old limit
+*/
+
+size_t
+smfi_setmaxdatasize(sz)
+ size_t sz;
+{
+ size_t old;
+
+ old = Maxdatasize;
+ Maxdatasize = sz;
+ return old;
+}
+
+/*
+** MI_RD_CMD -- read a command
+**
+** Parameters:
+** sd -- socket descriptor
+** timeout -- maximum time to wait
+** cmd -- single character command read from sd
+** rlen -- pointer to length of result
+** name -- name of milter
+**
+** Returns:
+** buffer with rest of command
+** (malloc()ed here, should be free()d)
+** hack: encode error in cmd
+*/
+
+char *
+mi_rd_cmd(sd, timeout, cmd, rlen, name)
+ socket_t sd;
+ struct timeval *timeout;
+ char *cmd;
+ size_t *rlen;
+ char *name;
+{
+ ssize_t len;
+ mi_int32 expl;
+ ssize_t i;
+ FD_RD_VAR(rds, excs);
+ int ret;
+ int save_errno;
+ char *buf;
+ char data[MILTER_LEN_BYTES + 1];
+
+ *cmd = '\0';
+ *rlen = 0;
+
+ i = 0;
+ for (;;)
+ {
+ FD_RD_INIT(sd, rds, excs);
+ ret = FD_RD_READY(sd, rds, excs, timeout);
+ if (ret == 0)
+ break;
+ else if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (FD_IS_RD_EXC(sd, rds, excs))
+ {
+ *cmd = SMFIC_SELECT;
+ return NULL;
+ }
+
+ len = MI_SOCK_READ(sd, data + i, sizeof data - i);
+ if (MI_SOCK_READ_FAIL(len))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s, mi_rd_cmd: read returned %d: %s",
+ name, (int) len, sm_errstring(errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+ if (len == 0)
+ {
+ *cmd = SMFIC_EOF;
+ return NULL;
+ }
+ if (len >= (ssize_t) sizeof data - i)
+ break;
+ i += len;
+ }
+ if (ret == 0)
+ {
+ *cmd = SMFIC_TIMEOUT;
+ return NULL;
+ }
+ else if (ret < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: %s() returned %d: %s",
+ name, MI_POLLSELECT, ret, sm_errstring(errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+
+ *cmd = data[MILTER_LEN_BYTES];
+ data[MILTER_LEN_BYTES] = '\0';
+ (void) memcpy((void *) &expl, (void *) &(data[0]), MILTER_LEN_BYTES);
+ expl = ntohl(expl) - 1;
+ if (expl <= 0)
+ return NULL;
+ if (expl > Maxdatasize)
+ {
+ *cmd = SMFIC_TOOBIG;
+ return NULL;
+ }
+#if _FFR_ADD_NULL
+ buf = malloc(expl + 1);
+#else /* _FFR_ADD_NULL */
+ buf = malloc(expl);
+#endif /* _FFR_ADD_NULL */
+ if (buf == NULL)
+ {
+ *cmd = SMFIC_MALLOC;
+ return NULL;
+ }
+
+ i = 0;
+ for (;;)
+ {
+ FD_RD_INIT(sd, rds, excs);
+ ret = FD_RD_READY(sd, rds, excs, timeout);
+ if (ret == 0)
+ break;
+ else if (ret < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (FD_IS_RD_EXC(sd, rds, excs))
+ {
+ *cmd = SMFIC_SELECT;
+ free(buf);
+ return NULL;
+ }
+ len = MI_SOCK_READ(sd, buf + i, expl - i);
+ if (MI_SOCK_READ_FAIL(len))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: read returned %d: %s",
+ name, (int) len, sm_errstring(errno));
+ ret = -1;
+ break;
+ }
+ if (len == 0)
+ {
+ *cmd = SMFIC_EOF;
+ free(buf);
+ return NULL;
+ }
+ if (len > expl - i)
+ {
+ *cmd = SMFIC_RECVERR;
+ free(buf);
+ return NULL;
+ }
+ if (len >= expl - i)
+ {
+ *rlen = expl;
+#if _FFR_ADD_NULL
+ /* makes life simpler for common string routines */
+ buf[expl] = '\0';
+#endif /* _FFR_ADD_NULL */
+ return buf;
+ }
+ i += len;
+ }
+
+ save_errno = errno;
+ free(buf);
+
+ /* select returned 0 (timeout) or < 0 (error) */
+ if (ret == 0)
+ {
+ *cmd = SMFIC_TIMEOUT;
+ return NULL;
+ }
+ if (ret < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: mi_rd_cmd: %s() returned %d: %s",
+ name, MI_POLLSELECT, ret, sm_errstring(save_errno));
+ *cmd = SMFIC_RECVERR;
+ return NULL;
+ }
+ *cmd = SMFIC_UNKNERR;
+ return NULL;
+}
+
+/*
+** RETRY_WRITEV -- Keep calling the writev() system call
+** until all the data is written out or an error occurs.
+**
+** Parameters:
+** fd -- socket descriptor
+** iov -- io vector
+** iovcnt -- number of elements in io vector
+** must NOT exceed UIO_MAXIOV.
+** timeout -- maximum time to wait
+**
+** Returns:
+** success: number of bytes written
+** otherwise: MI_FAILURE
+*/
+
+static ssize_t
+retry_writev(fd, iov, iovcnt, timeout)
+ socket_t fd;
+ struct iovec *iov;
+ int iovcnt;
+ struct timeval *timeout;
+{
+ int i;
+ ssize_t n, written;
+ FD_WR_VAR(wrs);
+
+ written = 0;
+ for (;;)
+ {
+ while (iovcnt > 0 && iov[0].iov_len == 0)
+ {
+ iov++;
+ iovcnt--;
+ }
+ if (iovcnt <= 0)
+ return written;
+
+ /*
+ ** We don't care much about the timeout here,
+ ** it's very long anyway; correct solution would be
+ ** to take the time before the loop and reduce the
+ ** timeout after each invocation.
+ ** FD_SETSIZE is checked when socket is created.
+ */
+
+ FD_WR_INIT(fd, wrs);
+ i = FD_WR_READY(fd, wrs, timeout);
+ if (i == 0)
+ return MI_FAILURE;
+ if (i < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return MI_FAILURE;
+ }
+ n = writev(fd, iov, iovcnt);
+ if (n == -1)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return MI_FAILURE;
+ }
+
+ written += n;
+ for (i = 0; i < iovcnt; i++)
+ {
+ if (iov[i].iov_len > (unsigned int) n)
+ {
+ iov[i].iov_base = (char *)iov[i].iov_base + n;
+ iov[i].iov_len -= (unsigned int) n;
+ break;
+ }
+ n -= (int) iov[i].iov_len;
+ iov[i].iov_len = 0;
+ }
+ if (i == iovcnt)
+ return written;
+ }
+}
+
+/*
+** MI_WR_CMD -- write a cmd to sd
+**
+** Parameters:
+** sd -- socket descriptor
+** timeout -- maximum time to wait
+** cmd -- single character command to write
+** buf -- buffer with further data
+** len -- length of buffer (without cmd!)
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_wr_cmd(sd, timeout, cmd, buf, len)
+ socket_t sd;
+ struct timeval *timeout;
+ int cmd;
+ char *buf;
+ size_t len;
+{
+ size_t sl;
+ ssize_t l;
+ mi_int32 nl;
+ int iovcnt;
+ struct iovec iov[2];
+ char data[MILTER_LEN_BYTES + 1];
+
+ if (len > Maxdatasize || (len > 0 && buf == NULL))
+ return MI_FAILURE;
+
+ nl = htonl(len + 1); /* add 1 for the cmd char */
+ (void) memcpy(data, (void *) &nl, MILTER_LEN_BYTES);
+ data[MILTER_LEN_BYTES] = (char) cmd;
+ sl = MILTER_LEN_BYTES + 1;
+
+ /* set up the vector for the size / command */
+ iov[0].iov_base = (void *) data;
+ iov[0].iov_len = sl;
+ iovcnt = 1;
+ if (len >= 0 && buf != NULL)
+ {
+ iov[1].iov_base = (void *) buf;
+ iov[1].iov_len = len;
+ iovcnt = 2;
+ }
+
+ l = retry_writev(sd, iov, iovcnt, timeout);
+ if (l == MI_FAILURE)
+ return MI_FAILURE;
+ return MI_SUCCESS;
+}
diff --git a/contrib/sendmail/libmilter/docs/api.html b/contrib/sendmail/libmilter/docs/api.html
new file mode 100644
index 0000000..578e0ca
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/api.html
@@ -0,0 +1,322 @@
+<HTML>
+<HEAD><TITLE>Milter API</TITLE></HEAD>
+<BODY>
+<!--
+$Id: api.html,v 1.37 2009/05/19 00:40:52 ca Exp $
+-->
+<H1>Milter API</H1>
+
+<H2>Contents</H2>
+<UL>
+ <LI><A HREF="#LibraryControlFunctions">Library Control Functions</A>
+ <LI><A HREF="#DataAccessFunctions">Data Access Functions</A>
+ <LI><A HREF="#MessageModificationFunctions">Message Modification Functions</A>
+ <LI><A HREF="#Callbacks">Callbacks</A>
+ <LI><A HREF="#Miscellaneous">Miscellaneous</A>
+</UL>
+
+<H2><A NAME="LibraryControlFunctions">Library Control Functions</A></H2>
+
+Before handing control to libmilter (by calling
+<A HREF="smfi_main.html">smfi_main</A>), a filter may call the following
+functions to set libmilter parameters.
+In particular, the filter must call
+<A HREF="smfi_register.html">smfi_register</A> to register its callbacks.
+Each function will return either MI_SUCCESS or MI_FAILURE to
+indicate the status of the operation.
+
+<P>
+None of these functions communicate with the MTA. All alter the
+library's state, some of which is communicated to the MTA inside
+<A HREF="smfi_main.html">smfi_main</A>.
+
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR BGCOLOR="#dddddd"><TH>Function</TH><TH>Description</TH></TR>
+
+<TR><TD><A HREF="smfi_opensocket.html">smfi_opensocket</A></TD><TD>Try to create the interface socket.</TD></TR>
+
+<TR><TD><A HREF="smfi_register.html">smfi_register</A></TD><TD>Register a filter.</TD></TR>
+
+<TR><TD><A HREF="smfi_setconn.html">smfi_setconn</A></TD><TD>Specify socket to use.</TD></TR>
+
+<TR><TD><A HREF="smfi_settimeout.html">smfi_settimeout</A></TD><TD>Set timeout.</TD></TR>
+
+<TR><TD><A HREF="smfi_setbacklog.html">smfi_setbacklog</A></TD><TD>Define the incoming <CODE>listen(2)</CODE> queue size.</TD></TR>
+
+<TR><TD><A HREF="smfi_setdbg.html">smfi_setdbg</A></TD><TD>Set the milter library debugging (tracing) level.</TD></TR>
+
+<TR><TD><A HREF="smfi_stop.html">smfi_stop</A></TD><TD>Cause an orderly shutdown.</TD></TR>
+
+<TR><TD><A HREF="smfi_main.html">smfi_main</A></TD><TD>Hand control to libmilter.</TD></TR>
+
+</TABLE>
+
+<H2><A NAME="DataAccessFunctions">Data Access Functions</A></H2>
+
+The following functions may be called from within the filter-defined callbacks
+to access information about the current connection or message.
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR bgcolor="#dddddd"><TH>Function</TH><TH>Description</TH></TR>
+<TR><TD><A HREF="smfi_getsymval.html">smfi_getsymval</A></TD><TD>Return the value
+of a symbol.</TD></TR>
+
+<TR><TD><A HREF="smfi_getpriv.html">smfi_getpriv</A></TD><TD>Get the private data
+pointer.</TD></TR>
+
+<TR><TD><A HREF="smfi_setpriv.html">smfi_setpriv</A></TD><TD>Set the private data
+pointer.</TD></TR>
+
+<TR><TD><A HREF="smfi_setreply.html">smfi_setreply</A></TD><TD>Set the specific
+reply code to be used.</TD></TR>
+
+<TR><TD><A HREF="smfi_setmlreply.html">smfi_setmlreply</A></TD><TD>Set the
+specific multi-line reply to be used.</TD></TR>
+
+</TABLE>
+
+<H2><A NAME="MessageModificationFunctions">Message Modification Functions</A></H2>
+
+The following functions change a message's contents and attributes.
+<EM>They may only be called in <A HREF="xxfi_eom.html">xxfi_eom</A></EM>.
+All of these functions may invoke additional communication with the MTA.
+They will return either MI_SUCCESS or MI_FAILURE to indicate the status of
+the operation. Message data (senders, recipients, headers, body chunks)
+passed to these functions via parameters is copied and does not need to be
+preserved (i.e., allocated memory can be freed).
+
+<P>
+A filter must have set the appropriate flag (listed below) in the
+description passed to <A HREF="smfi_register.html">smfi_register</A>
+to call any message modification function. Failure to do so will
+cause the MTA to treat a call to the function as a failure of the
+filter, terminating its connection.
+
+<P>
+Note that the status returned indicates only whether or not the
+filter's message was successfully sent to the MTA, not whether or not
+the MTA performed the requested operation. For example,
+<A HREF="smfi_addheader.html">smfi_addheader</A>, when called with an
+illegal header name, will return MI_SUCCESS even though the MTA may
+later refuse to add the illegal header.
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR BGCOLOR="#dddddd"><TH>Function</TH><TH>Description</TH><TH>SMFIF_* flag</TR>
+<TR><TD><A HREF="smfi_addheader.html">smfi_addheader</A></TD><TD>Add a header to
+the message.</TD><TD>SMFIF_ADDHDRS</TD></TR>
+
+<TR><TD><A HREF="smfi_chgheader.html">smfi_chgheader</A></TD><TD>Change or delete a header.</TD><TD>SMFIF_CHGHDRS</TD></TR>
+
+<TR><TD><A HREF="smfi_insheader.html">smfi_insheader</A></TD><TD>Insert a
+header into the message.</TD><TD>SMFIF_ADDHDRS</TD></TR>
+
+<TR><TD><A HREF="smfi_chgfrom.html">smfi_chgfrom</A></TD><TD>Change the
+envelope sender address.</TD><TD>SMFIF_CHGFROM</TD></TR>
+
+<TR><TD><A HREF="smfi_addrcpt.html">smfi_addrcpt</A></TD><TD>Add a recipient to
+the envelope.</TD><TD>SMFIF_ADDRCPT</TD></TR>
+
+<TR><TD><A HREF="smfi_addrcpt_par.html">smfi_addrcpt_par</A></TD><TD>Add
+a recipient including ESMTP parameter to the envelope.
+</TD><TD>SMFIF_ADDRCPT_PAR</TD></TR>
+
+<TR><TD><A HREF="smfi_delrcpt.html">smfi_delrcpt</A></TD><TD>Delete a recipient
+from the envelope.</TD><TD>SMFIF_DELRCPT</TD></TR>
+
+<TR><TD><A HREF="smfi_replacebody.html">smfi_replacebody</A></TD><TD>Replace the
+body of the message.</TD><TD>SMFIF_CHGBODY</TD></TR>
+
+</TABLE>
+
+<H2>Other Message Handling Functions</H2>
+
+The following functions provide special case handling instructions for
+milter or the MTA, without altering the content or status of the message.
+<EM>They too may only be called in <A HREF="xxfi_eom.html">xxfi_eom</A></EM>.
+All of these functions may invoke additional communication with the MTA.
+They will return either MI_SUCCESS or MI_FAILURE to indicate the status of
+the operation.
+
+<P>
+Note that the status returned indicates only whether or not the
+filter's message was successfully sent to the MTA, not whether or not
+the MTA performed the requested operation.
+
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR BGCOLOR="#dddddd"><TH>Function</TH><TH>Description</TH></TR>
+<TR><TD><A HREF="smfi_progress.html">smfi_progress</A></TD><TD>Report operation in progress.</TD></TR>
+
+<TR><TD><A HREF="smfi_quarantine.html">smfi_quarantine</A></TD><TD>Quarantine a message.</TD></TR>
+
+</TABLE>
+
+<H2><A NAME="Callbacks">Callbacks</A></H2>
+
+The filter should implement one or more of the following callbacks,
+which are registered via <A HREF="smfi_register.html">smfi_register</A>:
+
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR BGCOLOR="#dddddd"><TH>Function</TH><TH>Description</TH></TR>
+
+<TR><TD><A HREF="xxfi_connect.html">xxfi_connect</A></TD><TD>connection info</TD></TR>
+
+<TR><TD><A HREF="xxfi_helo.html">xxfi_helo</A></TD><TD>SMTP HELO/EHLO command</TD></TR>
+
+<TR><TD><A HREF="xxfi_envfrom.html">xxfi_envfrom</A></TD><TD>envelope sender</TD></TR>
+
+<TR><TD><A HREF="xxfi_envrcpt.html">xxfi_envrcpt</A></TD><TD>envelope recipient</TD></TR>
+
+<TR><TD><A HREF="xxfi_data.html">xxfi_data</A></TD><TD>DATA command</TD></TR>
+
+<TR><TD><A HREF="xxfi_unknown.html">xxfi_unknown</A></TD><TD>Unknown SMTP command</TD></TR>
+
+<TR><TD><A HREF="xxfi_header.html">xxfi_header</A></TD><TD>header</TD></TR>
+
+<TR><TD><A HREF="xxfi_eoh.html">xxfi_eoh</A></TD><TD>end of header</TD></TR>
+
+<TR><TD><A HREF="xxfi_body.html">xxfi_body</A></TD><TD>body block</TD></TR>
+
+<TR><TD><A HREF="xxfi_eom.html">xxfi_eom</A></TD><TD>end of message</TD></TR>
+
+<TR><TD><A HREF="xxfi_abort.html">xxfi_abort</A></TD><TD>message aborted</TD></TR>
+
+<TR><TD><A HREF="xxfi_close.html">xxfi_close</A></TD><TD>connection cleanup</TD></TR>
+
+<TR><TD><A HREF="xxfi_negotiate.html">xxfi_negotiate</A></TD><TD>option negotiattion</TD></TR>
+
+</TABLE>
+
+<P>
+The above callbacks should all return one of the following return values,
+having the indicated meanings. Any return other than one of the below
+values constitutes an error, and will cause sendmail to terminate its
+connection to the offending filter.
+
+<P><A NAME="conn-spec">Milter</A> distinguishes between recipient-,
+message-, and connection-oriented routines. Recipient-oriented
+callbacks may affect the processing of a single message recipient;
+message-oriented callbacks, a single message; connection-oriented
+callbacks, an entire connection (during which multiple messages may be
+delivered to multiple sets of recipients).
+<A HREF="xxfi_envrcpt.html">xxfi_envrcpt</A> is recipient-oriented.
+<A HREF="xxfi_connect.html">xxfi_connect</A>,
+<A HREF="xxfi_helo.html">xxfi_helo</A> and
+<A HREF="xxfi_close.html">xxfi_close</A> are connection-oriented. All
+other callbacks are message-oriented.
+
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2>
+ <TR BGCOLOR="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR VALIGN="TOP">
+ <TD>SMFIS_CONTINUE</TD>
+ <TD>Continue processing the current connection, message, or recipient.
+ </TD>
+ </TR>
+ <TR VALIGN="TOP">
+ <TD>SMFIS_REJECT</TD>
+ <TD>For a connection-oriented routine, reject this connection; call <A HREF="xxfi_close.html">xxfi_close</A>.<BR>
+ For a message-oriented routine (except
+ <A HREF="xxfi_eom.html">xxfi_eom</A> or
+ <A HREF="xxfi_abort.html">xxfi_abort</A>), reject this message.<BR>
+ For a recipient-oriented routine, reject the current recipient (but continue processing the current message).
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_DISCARD</TD>
+ <TD>For a message- or recipient-oriented routine, accept this message, but silently discard it.<BR>
+ SMFIS_DISCARD should not be returned by a connection-oriented routine.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_ACCEPT</TD>
+ <TD>For a connection-oriented routine, accept this connection without further filter processing; call <A HREF="xxfi_close.html">xxfi_close</A>.<BR>
+ For a message- or recipient-oriented routine, accept this message without further filtering.<BR>
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_TEMPFAIL</TD>
+ <TD>Return a temporary failure, i.e., the corresponding SMTP command will return an appropriate 4xx status code.
+ For a message-oriented routine (except <A HREF="xxfi_envfrom.html">xxfi_envfrom</A>), fail for this message. <BR>
+ For a connection-oriented routine, fail for this connection; call <A HREF="xxfi_close.html">xxfi_close</A>. <BR>
+ For a recipient-oriented routine, only fail for the current recipient; continue message processing.
+ </TD>
+ </TR>
+
+ <TR valign="top">
+ <TD><A NAME="SMFIS_SKIP">SMFIS_SKIP</A></TD>
+ <TD>Skip further callbacks of the same type in this transaction.
+ Currently this return value is only allowed in
+ <A HREF="xxfi_body.html">xxfi_body()</A>.
+ It can be used if a milter has received sufficiently many
+ body chunks to make a decision, but still wants to invoke
+ message modification functions that are only allowed to be called from
+ <A HREF="xxfi_eom.html">xxfi_eom()</A>.
+ Note: the milter <EM>must</EM>
+ <A HREF="xxfi_negotiate.html">negotiate</A>
+ this behavior with the MTA, i.e., it must check whether
+ the protocol action
+ <A HREF="xxfi_negotiate.html#SMFIP_SKIP"><CODE>SMFIP_SKIP</CODE></A>
+ is available and if so, the milter must request it.
+ </TD>
+ </TR>
+
+ <TR valign="top">
+ <TD><A NAME="SMFIS_NOREPLY">SMFIS_NOREPLY</A></TD>
+ <TD>Do not send a reply back to the MTA.
+ The milter <EM>must</EM>
+ <A HREF="xxfi_negotiate.html">negotiate</A>
+ this behavior with the MTA, i.e., it must check whether
+ the appropriate protocol action
+ <A HREF="xxfi_negotiate.html#SMFIP_NR_"><CODE>SMFIP_NR_*</CODE></A>
+ is available and if so, the milter must request it.
+ If you set the
+ <A HREF="xxfi_negotiate.html#SMFIP_NR_"><CODE>SMFIP_NR_*</CODE></A>
+ protocol action for a callback, that callback <EM>must</EM>
+ always reply with
+ SMFIS_NOREPLY.
+ Using any other reply code is a violation of the API.
+ If in some cases your callback may return another value
+ (e.g., due to some resource shortages), then you
+ <EM>must not</EM> set
+ <A HREF="xxfi_negotiate.html#SMFIP_NR_"><CODE>SMFIP_NR_*</CODE></A>
+ and you must use
+ SMFIS_CONTINUE as the default return code.
+ (Alternatively you can try to delay reporting the problem to
+ a later callback for which
+ <A HREF="xxfi_negotiate.html#SMFIP_NR_"><CODE>SMFIP_NR_*</CODE></A>
+ is not set.)
+ </TD>
+ </TR>
+
+</TABLE>
+
+<H2><A NAME="Miscellaneous">Miscellaneous</A></H2>
+
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR BGCOLOR="#dddddd"><TH>Function</TH><TH>Description</TH></TR>
+
+<TR><TD><A HREF="smfi_version.html">smfi_version</A></TD><TD>libmilter (runtime) version info</TD></TR>
+
+<TR><TD><A HREF="smfi_setsymlist.html">smfi_setsymlist</A></TD><TD>
+Set the list of macros that the milter wants to receive from the MTA
+for a protocol stage.
+</TD></TR>
+
+</TABLE>
+
+<P>
+<TABLE BORDER="1" CELLSPACING=0 CELLPADDING=2><TR BGCOLOR="#dddddd"><TH>Constant</TH><TH>Description</TH></TR>
+
+<TR><TD><A HREF="smfi_version.html">SMFI_VERSION</A></TD><TD>libmilter (compile time) version info</TD></TR>
+
+</TABLE>
+
+
+<HR SIZE="1">
+<FONT SIZE="-1">
+Copyright (c) 2000, 2003, 2006, 2009 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/design.html b/contrib/sendmail/libmilter/docs/design.html
new file mode 100644
index 0000000..f2e5f11
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/design.html
@@ -0,0 +1,147 @@
+<HTML>
+<HEAD>
+<TITLE>Architecture</TITLE>
+</HEAD>
+<BODY>
+<!--
+$Id: design.html,v 1.12 2006/08/08 20:55:57 ca Exp $
+-->
+
+<H1>Architecture</H1>
+
+<H2>Contents</H2>
+
+<UL>
+ <LI>Design Goals
+ <LI>Implementing Filtering Policies
+ <LI>MTA - Filter Communication
+</UL>
+
+<H2>Goals</H2>
+
+The Sendmail Content Management API (Milter) provides an interface for
+third-party software to validate and modify messages as they pass
+through the mail transport system. Filters can process messages'
+connection (IP) information, envelope protocol elements, message
+headers, and/or message body contents, and modify a message's
+recipients, headers, and body. The MTA configuration file specifies
+which filters are to be applied, and in what order, allowing an
+administrator to combine multiple independently-developed filters.
+
+<P>
+We expect to see both vendor-supplied, configurable mail filtering
+applications and a multiplicity of script-like filters designed by and
+for MTA administrators. A certain degree of coding sophistication and
+domain knowledge on the part of the filter provider is assumed. This
+allows filters to exercise fine-grained control at the SMTP level.
+However, as will be seen in the example, many filtering applications
+can be written with relatively little protocol knowledge.
+
+<P>
+Given these expectations, the API is designed to achieve the following
+goals:
+
+<OL>
+ <LI>Safety/security.
+ Filter processes should not need to run as root
+ (of course, they can if required, but that is a local issue);
+ this will simplify coding
+ and limit the impact of security flaws in the filter program.
+<P>
+ <LI>Reliability.
+ Coding failures in a Milter process that cause that process
+ to hang or core-dump
+ should not stop mail delivery.
+ Faced with such a failure,
+ sendmail should use a default mechanism,
+ either behaving as if the filter were not present
+ or as if a required resource were unavailable.
+ The latter failure mode will generally have sendmail return
+ a 4xx SMTP code (although in later phases of the SMTP protocol
+ it may cause the mail to be queued for later processing).
+<P>
+ <LI>Simplicity.
+ The API should make implementation of a new filter
+ no more difficult than absolutely necessary.
+ Subgoals include:
+ <UL>
+ <LI>Encourage good thread practice
+ by defining thread-clean interfaces including local data hooks.
+ <LI>Provide all interfaces required
+ while avoiding unnecessary pedanticism.
+ </UL>
+<P>
+ <LI>Performance.
+ Simple filters should not seriously impact overall MTA performance.
+</OL>
+
+<H2>Implementing Filtering Policies</H2>
+
+Milter is designed to allow a server administrator to combine
+third-party filters to implement a desired mail filtering policy. For
+example, if a site wished to scan incoming mail for viruses on several
+platforms, eliminate unsolicited commercial email, and append a mandated
+footer to selected incoming messages, the administrator could configure
+the MTA to filter messages first through a server based anti-virus
+engine, then via a large-scale spam-catching service, and finally
+append the desired footer if the message still met requisite criteria.
+Any of these filters could be added or changed independently.
+
+<P>
+Thus the site administrator, not the filter writer, controls the
+overall mail filtering environment. In particular, he/she must decide
+which filters are run, in what order they are run, and how they
+communicate with the MTA. These parameters, as well as the
+actions to be taken if a filter becomes unavailable, are selectable
+during MTA configuration. <A href="installation.html">Further
+details</A> are available later in this document.
+
+<H2>MTA - Filter communication</H2>
+
+Filters run as separate processes, outside of the sendmail address
+space. The benefits of this are threefold:
+
+<OL>
+ <LI>The filter need not run with "root" permissions, thereby
+ avoiding a large family of potential security problems.</LI>
+
+ <LI>Failures in a particular filter will not affect the MTA or
+ other filters.</LI>
+
+ <LI>The filter can potentially have higher performance because of
+ the parallelism inherent in multiple processes.</LI>
+</OL>
+
+<P>
+Each filter may communicate with multiple MTAs at the same time over
+local or remote connections, using multiple threads of execution.
+<A HREF="#figure-1">Figure 1</A> illustrates a possible network of
+communication channels between a site's filters, its MTAs, and other
+MTAs on the network:
+</P>
+<DIV align="center">
+<A name="figure-1"><IMG src="figure1.jpg" ALT=""></A><BR>
+<B>Figure 1: A set of MTA's interacting with a set of filters.</B>
+</DIV>
+<P>
+The Milter library (libmilter) implements the communication protocol.
+It accepts connections from various MTAs, passes the relevant data to
+the filter through callbacks, then makes appropriate responses based
+on return codes. A filter may also send data to the MTA as a result
+of library calls. <A href="#figure-2">Figure 2</A> shows a single
+filter process processing messages from two MTAs:
+</P>
+<DIV align="center">
+<IMG src="figure2.jpg" ALT=""><BR>
+<B>Figure 2: A filter handling simultaneous requests from two MTA's.</B>
+</DIV>
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/figure1.fig b/contrib/sendmail/libmilter/docs/figure1.fig
new file mode 100644
index 0000000..e0cbdc8
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/figure1.fig
@@ -0,0 +1,56 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 975 225 5025 3375
+6 1500 1275 2700 2025
+2 2 0 2 1 7 50 0 -1 0.000 0 0 7 0 0 5
+ 1650 1425 2550 1425 2550 1875 1650 1875 1650 1425
+4 0 1 50 0 0 20 0.0000 4 195 645 1800 1725 MTA\001
+-6
+6 1950 2625 3150 3375
+2 2 0 2 4 7 50 0 -1 0.000 0 0 7 0 0 5
+ 2100 2775 3000 2775 3000 3225 2100 3225 2100 2775
+4 0 4 50 0 0 20 0.0000 4 195 645 2250 3075 MTA\001
+-6
+6 1050 225 2250 975
+2 2 0 2 14 7 50 0 -1 0.000 0 0 7 0 0 5
+ 1200 375 2100 375 2100 825 1200 825 1200 375
+4 0 14 50 0 0 20 0.0000 4 195 645 1350 675 MTA\001
+-6
+2 1 0 2 1 7 50 0 -1 0.000 0 0 7 0 0 2
+ 2550 1575 3750 2625
+2 1 0 2 1 7 50 0 -1 0.000 0 0 7 0 0 2
+ 2550 1575 3750 1575
+2 1 0 2 1 7 50 0 -1 0.000 0 0 7 0 0 2
+ 2550 1575 3750 825
+2 1 0 2 4 7 50 0 -1 0.000 0 0 7 0 0 2
+ 3000 2925 3750 2625
+2 1 0 2 14 7 50 0 -1 0.000 0 0 7 0 0 2
+ 2100 525 3750 825
+2 1 0 2 14 7 50 0 -1 0.000 0 0 7 0 0 2
+ 2100 525 3750 2625
+2 1 0 4 0 7 50 0 -1 0.000 0 0 7 0 0 2
+ 1050 3075 2100 3075
+2 1 0 4 0 7 50 0 -1 0.000 0 0 7 0 0 2
+ 1050 1725 1650 1725
+2 1 0 4 0 7 50 0 -1 0.000 0 0 7 0 0 2
+ 1050 675 1200 675
+2 2 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3750 2475 4950 2475 4950 2925 3750 2925 3750 2475
+2 2 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3750 1425 4950 1425 4950 1875 3750 1875 3750 1425
+2 2 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 3750 525 4950 525 4950 975 3750 975 3750 525
+4 0 0 50 0 1 20 0.0000 4 210 795 3900 2775 Filter 3\001
+4 0 0 50 0 1 20 0.0000 4 210 795 3900 1725 Filter 2\001
+4 0 0 50 0 1 20 0.0000 4 210 795 3900 825 Filter 1\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 300 525 1050 525 1050 3225 300 3225 300 525
+4 0 0 50 0 2 24 1.5708 4 255 1950 825 2850 INTERNET\001
diff --git a/contrib/sendmail/libmilter/docs/figure1.jpg b/contrib/sendmail/libmilter/docs/figure1.jpg
new file mode 100644
index 0000000..1a5f1de
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/figure1.jpg
Binary files differ
diff --git a/contrib/sendmail/libmilter/docs/figure1.ps b/contrib/sendmail/libmilter/docs/figure1.ps
new file mode 100644
index 0000000..ae31760
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/figure1.ps
@@ -0,0 +1,173 @@
+%!PS-Adobe-2.0
+%%Title: figure1.fig
+%%Creator: fig2dev Version 3.2.3 Patchlevel
+%%CreationDate: Tue Jun 6 14:00:04 2000
+%%For: sean@host232.Sendmail.COM (Sean O'rourke,5400)
+%%Orientation: Landscape
+%%Pages: 1
+%%BoundingBox: 0 0 612 792
+%%BeginSetup
+%%IncludeFeature: *PageSize Letter
+%%EndSetup
+%%Magnification: 1.0000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 792 moveto 0 0 lineto 612 0 lineto 612 792 lineto closepath clip newpath
+198.0 238.0 translate
+ 90 rotate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+%%Page: 1 1
+10 setmiterlimit
+ 0.06000 0.06000 sc
+%%Page: 1 1
+/Times-Bold ff 360.00 scf sf
+825 2850 m
+gs 1 -1 sc 90.0 rot (INTERNET) col0 sh gr
+/Times-Roman ff 300.00 scf sf
+1800 1725 m
+gs 1 -1 sc (MTA) col1 sh gr
+% Polyline
+15.000 slw
+n 2100 2775 m 3000 2775 l 3000 3225 l 2100 3225 l
+ cp gs col4 s gr
+/Times-Roman ff 300.00 scf sf
+2250 3075 m
+gs 1 -1 sc (MTA) col4 sh gr
+% Polyline
+n 1200 375 m 2100 375 l 2100 825 l 1200 825 l
+ cp gs col14 s gr
+/Times-Roman ff 300.00 scf sf
+1350 675 m
+gs 1 -1 sc (MTA) col14 sh gr
+% Polyline
+n 2550 1575 m
+ 3750 2625 l gs col1 s gr
+% Polyline
+n 2550 1575 m
+ 3750 1575 l gs col1 s gr
+% Polyline
+n 2550 1575 m
+ 3750 825 l gs col1 s gr
+% Polyline
+n 3000 2925 m
+ 3750 2625 l gs col4 s gr
+% Polyline
+n 2100 525 m
+ 3750 825 l gs col14 s gr
+% Polyline
+n 2100 525 m
+ 3750 2625 l gs col14 s gr
+% Polyline
+45.000 slw
+n 1050 3075 m
+ 2100 3075 l gs col0 s gr
+% Polyline
+n 1050 1725 m
+ 1650 1725 l gs col0 s gr
+% Polyline
+n 1050 675 m
+ 1200 675 l gs col0 s gr
+% Polyline
+15.000 slw
+n 3750 2475 m 4950 2475 l 4950 2925 l 3750 2925 l
+ cp gs col0 s gr
+% Polyline
+n 3750 1425 m 4950 1425 l 4950 1875 l 3750 1875 l
+ cp gs col0 s gr
+% Polyline
+n 3750 525 m 4950 525 l 4950 975 l 3750 975 l
+ cp gs col0 s gr
+/Times-Italic ff 300.00 scf sf
+3900 2775 m
+gs 1 -1 sc (Filter 3) col0 sh gr
+/Times-Italic ff 300.00 scf sf
+3900 1725 m
+gs 1 -1 sc (Filter 2) col0 sh gr
+/Times-Italic ff 300.00 scf sf
+3900 825 m
+gs 1 -1 sc (Filter 1) col0 sh gr
+% Polyline
+7.500 slw
+n 300 525 m 1050 525 l 1050 3225 l 300 3225 l
+ cp gs col0 s gr
+% Polyline
+15.000 slw
+n 1650 1425 m 2550 1425 l 2550 1875 l 1650 1875 l
+ cp gs col1 s gr
+$F2psEnd
+rs
+showpage
diff --git a/contrib/sendmail/libmilter/docs/figure2.fig b/contrib/sendmail/libmilter/docs/figure2.fig
new file mode 100644
index 0000000..c93bfe3
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/figure2.fig
@@ -0,0 +1,67 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+5 1 0 1 0 7 50 0 -1 0.000 0 0 1 0 2981.250 1200.000 2700 1050 3300 1200 2700 1350
+ 1 1 1.00 60.00 120.00
+6 4200 900 6450 1350
+2 2 0 1 1 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4200 900 6450 900 6450 1350 4200 1350 4200 900
+4 0 1 50 0 0 16 0.0000 4 195 2040 4350 1200 xxfi_header callback\001
+-6
+6 4200 2250 6450 2700
+2 2 0 1 4 7 50 0 -1 0.000 0 0 7 0 0 5
+ 4200 2250 6450 2250 6450 2700 4200 2700 4200 2250
+4 0 4 50 0 0 16 0.0000 4 195 2040 4350 2550 xxfi_header callback\001
+-6
+6 600 2100 1800 2850
+2 2 0 2 4 7 50 0 -1 0.000 0 0 7 0 0 5
+ 750 2250 1650 2250 1650 2700 750 2700 750 2250
+4 0 4 50 0 0 20 0.0000 4 195 645 900 2550 MTA\001
+-6
+6 600 750 1800 1500
+2 2 0 2 1 7 50 0 -1 0.000 0 0 7 0 0 5
+ 750 900 1650 900 1650 1350 750 1350 750 900
+4 0 1 50 0 0 20 0.0000 4 195 645 900 1200 MTA\001
+-6
+2 1 0 2 1 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 4200 1200 3600 1200
+2 1 0 2 1 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 3450 900 4050 900
+2 2 0 2 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2400 300 6600 300 6600 3300 2400 3300 2400 300
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+ 2550 750 3450 750 3450 2700 2550 2700 2550 750
+2 1 0 2 4 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 3450 2700 4050 2700
+2 1 0 2 4 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 4200 2400 3600 2400
+2 1 0 2 4 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 1650 2700 2250 2700
+2 1 0 2 4 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 2400 2400 1800 2400
+2 1 0 2 1 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 1650 900 2250 900
+2 1 0 2 1 7 50 0 -1 0.000 0 0 -1 1 0 2
+ 1 1 2.00 120.00 240.00
+ 2400 1200 1800 1200
+4 0 0 50 0 0 20 0.0000 4 195 630 3900 600 Filter\001
+4 0 4 50 0 0 12 0.0000 4 180 1620 3450 3000 callback (arguments)\001
+4 0 4 50 0 0 12 0.0000 4 180 1575 3900 1950 optional library calls,\001
+4 0 4 50 0 0 12 0.0000 4 135 855 3900 2175 return code\001
+4 0 4 50 0 0 12 0.0000 4 135 780 1500 2100 responses\001
+4 0 0 50 0 0 16 0.0000 4 165 645 2700 2085 Milter\001
+4 0 0 50 0 0 16 0.0000 4 225 675 2700 2400 library\001
+4 0 4 50 0 0 12 0.0000 4 135 510 1500 3000 header\001
diff --git a/contrib/sendmail/libmilter/docs/figure2.jpg b/contrib/sendmail/libmilter/docs/figure2.jpg
new file mode 100644
index 0000000..8b11485
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/figure2.jpg
Binary files differ
diff --git a/contrib/sendmail/libmilter/docs/figure2.ps b/contrib/sendmail/libmilter/docs/figure2.ps
new file mode 100644
index 0000000..861a193
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/figure2.ps
@@ -0,0 +1,242 @@
+%!PS-Adobe-2.0
+%%Title: figure2.fig
+%%Creator: fig2dev Version 3.2.3 Patchlevel
+%%CreationDate: Tue Jun 6 13:57:47 2000
+%%For: sean@host232.Sendmail.COM (Sean O'rourke,5400)
+%%Orientation: Landscape
+%%Pages: 1
+%%BoundingBox: 0 0 612 792
+%%BeginSetup
+%%IncludeFeature: *PageSize Letter
+%%EndSetup
+%%Magnification: 1.5000
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+newpath 0 792 moveto 0 0 lineto 612 0 lineto 612 792 lineto closepath clip newpath
+144.0 65.5 translate
+ 90 rotate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+
+$F2psBegin
+%%Page: 1 1
+10 setmiterlimit
+ 0.09000 0.09000 sc
+%%Page: 1 1
+/Times-Roman ff 180.00 scf sf
+1500 3000 m
+gs 1 -1 sc (header) col4 sh gr
+/Times-Roman ff 240.00 scf sf
+4350 1200 m
+gs 1 -1 sc (xxfi_header callback) col1 sh gr
+% Polyline
+7.500 slw
+n 4200 2250 m 6450 2250 l 6450 2700 l 4200 2700 l
+ cp gs col4 s gr
+/Times-Roman ff 240.00 scf sf
+4350 2550 m
+gs 1 -1 sc (xxfi_header callback) col4 sh gr
+% Polyline
+15.000 slw
+n 750 2250 m 1650 2250 l 1650 2700 l 750 2700 l
+ cp gs col4 s gr
+/Times-Roman ff 300.00 scf sf
+900 2550 m
+gs 1 -1 sc (MTA) col4 sh gr
+% Polyline
+n 750 900 m 1650 900 l 1650 1350 l 750 1350 l
+ cp gs col1 s gr
+/Times-Roman ff 300.00 scf sf
+900 1200 m
+gs 1 -1 sc (MTA) col1 sh gr
+% Arc
+7.500 slw
+gs clippath
+2713 1319 m 2667 1357 l 2761 1475 l 2710 1363 l 2808 1437 l cp
+eoclip
+n 2981.2 1200.0 318.8 -151.9 151.9 arc
+gs col0 s gr
+ gr
+
+% arrowhead
+n 2808 1437 m 2710 1363 l 2761 1475 l 2808 1437 l cp gs 0.00 setgray ef gr col0 s
+% Polyline
+15.000 slw
+gs clippath
+3585 1140 m 3585 1260 l 3872 1260 l 3632 1200 l 3872 1140 l cp
+eoclip
+n 4200 1200 m
+ 3600 1200 l gs col1 s gr gr
+
+% arrowhead
+n 3872 1140 m 3632 1200 l 3872 1260 l 3872 1140 l cp gs col1 1.00 shd ef gr col1 s
+% Polyline
+gs clippath
+4065 960 m 4065 840 l 3778 840 l 4018 900 l 3778 960 l cp
+eoclip
+n 3450 900 m
+ 4050 900 l gs col1 s gr gr
+
+% arrowhead
+n 3778 960 m 4018 900 l 3778 840 l 3778 960 l cp gs col1 1.00 shd ef gr col1 s
+% Polyline
+n 2400 300 m 6600 300 l 6600 3300 l 2400 3300 l
+ cp gs col0 s gr
+% Polyline
+7.500 slw
+n 2550 750 m 3450 750 l 3450 2700 l 2550 2700 l
+ cp gs col0 s gr
+% Polyline
+15.000 slw
+gs clippath
+4065 2760 m 4065 2640 l 3778 2640 l 4018 2700 l 3778 2760 l cp
+eoclip
+n 3450 2700 m
+ 4050 2700 l gs col4 s gr gr
+
+% arrowhead
+n 3778 2760 m 4018 2700 l 3778 2640 l 3778 2760 l cp gs col4 1.00 shd ef gr col4 s
+% Polyline
+gs clippath
+3585 2340 m 3585 2460 l 3872 2460 l 3632 2400 l 3872 2340 l cp
+eoclip
+n 4200 2400 m
+ 3600 2400 l gs col4 s gr gr
+
+% arrowhead
+n 3872 2340 m 3632 2400 l 3872 2460 l 3872 2340 l cp gs col4 1.00 shd ef gr col4 s
+% Polyline
+gs clippath
+2265 2760 m 2265 2640 l 1978 2640 l 2218 2700 l 1978 2760 l cp
+eoclip
+n 1650 2700 m
+ 2250 2700 l gs col4 s gr gr
+
+% arrowhead
+n 1978 2760 m 2218 2700 l 1978 2640 l 1978 2760 l cp gs col4 1.00 shd ef gr col4 s
+% Polyline
+gs clippath
+1785 2340 m 1785 2460 l 2072 2460 l 1832 2400 l 2072 2340 l cp
+eoclip
+n 2400 2400 m
+ 1800 2400 l gs col4 s gr gr
+
+% arrowhead
+n 2072 2340 m 1832 2400 l 2072 2460 l 2072 2340 l cp gs col4 1.00 shd ef gr col4 s
+% Polyline
+gs clippath
+2265 960 m 2265 840 l 1978 840 l 2218 900 l 1978 960 l cp
+eoclip
+n 1650 900 m
+ 2250 900 l gs col1 s gr gr
+
+% arrowhead
+n 1978 960 m 2218 900 l 1978 840 l 1978 960 l cp gs col1 1.00 shd ef gr col1 s
+% Polyline
+gs clippath
+1785 1140 m 1785 1260 l 2072 1260 l 1832 1200 l 2072 1140 l cp
+eoclip
+n 2400 1200 m
+ 1800 1200 l gs col1 s gr gr
+
+% arrowhead
+n 2072 1140 m 1832 1200 l 2072 1260 l 2072 1140 l cp gs col1 1.00 shd ef gr col1 s
+/Times-Roman ff 300.00 scf sf
+3900 600 m
+gs 1 -1 sc (Filter) col0 sh gr
+/Times-Roman ff 180.00 scf sf
+3450 3000 m
+gs 1 -1 sc (callback \(arguments\)) col4 sh gr
+/Times-Roman ff 180.00 scf sf
+3900 1950 m
+gs 1 -1 sc (optional library calls,) col4 sh gr
+/Times-Roman ff 180.00 scf sf
+3900 2175 m
+gs 1 -1 sc (return code) col4 sh gr
+/Times-Roman ff 180.00 scf sf
+1500 2100 m
+gs 1 -1 sc (responses) col4 sh gr
+/Times-Roman ff 240.00 scf sf
+2700 2085 m
+gs 1 -1 sc (Milter) col0 sh gr
+/Times-Roman ff 240.00 scf sf
+2700 2400 m
+gs 1 -1 sc (library) col0 sh gr
+% Polyline
+7.500 slw
+n 4200 900 m 6450 900 l 6450 1350 l 4200 1350 l
+ cp gs col1 s gr
+$F2psEnd
+rs
+showpage
diff --git a/contrib/sendmail/libmilter/docs/index.html b/contrib/sendmail/libmilter/docs/index.html
new file mode 100644
index 0000000..517d2f6
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/index.html
@@ -0,0 +1,92 @@
+<HTML>
+<HEAD>
+<TITLE>Filtering Mail with Sendmail</TITLE>
+</HEAD>
+<BODY>
+<!--
+$Id: index.html,v 1.13 2006/08/08 20:55:57 ca Exp $
+-->
+
+<H1>Filtering Mail with Sendmail</H1>
+
+<!--
+<P><B>Disclaimer</B>:
+This preliminary API description is provided for review only. This
+specification may change based on feedback from reviewers, and does
+not bind Sendmail to offer this functionality in any release.
+-->
+
+<H2>Introduction</H2>
+
+<P>
+Sendmail's Content Management API (milter) provides third-party
+programs to access mail messages as they are being processed by the
+Mail Transfer Agent (MTA), allowing them to examine and modify message
+content and meta-information. Filtering policies implemented by
+Milter-conformant filters may then be centrally configured and
+composed in an end-user's MTA configuration file.
+
+<P>
+Possible uses for filters include spam rejection, virus
+filtering, and content control. In general, Milter seeks to address
+site-wide filtering concerns in a scalable way. Individual users' mail
+filtering needs (e.g. sorting messages by subject) are left to
+client-level programs such as <A href="http://www.procmail.org">Procmail</A>.
+
+<P>
+This document is a technical introduction intended for those
+interested in developing Milter filters. It includes:
+<UL>
+<LI>A description of Milter's design goals.
+
+<LI>An explanation of Milter application architecture, including
+interactions between the support library and user code, and between
+filters and the MTA.
+
+<LI>A specification of the C application programming interface.
+<LI>An example of a simple Milter filter.
+</UL>
+
+<H2>Contents</H2>
+
+<UL>
+<LI><A href="design.html">Architecture</A>
+<UL>
+ <LI>Design Goals
+ <LI>Implementing Filtering Policies
+ <LI>MTA - Filter communication
+</UL>
+<LI><A href="overview.html">Technical Overview</A>
+<UL>
+ <LI>Initialization
+ <LI>Control flow
+ <LI>Multithreading
+ <LI>Resource Management
+ <LI>Signal Handling
+</UL>
+<LI><A href="api.html">API Documentation</A>
+<UL>
+ <LI>Library Control Functions
+ <LI>Data Access Functions
+ <LI>Message Modification Functions
+ <LI>Callbacks
+</UL>
+<LI><A href="installation.html">Installation and Configuration</A>
+<UL>
+ <LI>Compiling and Installing Your Filter
+ <LI>Configuring Sendmail
+</UL>
+<LI><A href="sample.html">A Sample Filter</A>
+<!-- <LI><A href="other.html">Other Sources of Information</A> -->
+</UL>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2001, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/installation.html b/contrib/sendmail/libmilter/docs/installation.html
new file mode 100644
index 0000000..07142e9
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/installation.html
@@ -0,0 +1,165 @@
+<HTML>
+<HEAD><TITLE>Installation and Configuration</TITLE>
+</HEAD>
+<BODY>
+<!--
+$Id: installation.html,v 1.23 2006/08/31 17:16:03 ca Exp $
+-->
+<H1>Installation</H1>
+<H2>Contents</H2>
+<UL>
+ <LI><A href="#compile">Compiling and Installing Your Filter</A>
+ <LI><A href="#config">Configuring Sendmail</A>
+</UL>
+
+<H2><A name="compile">Compiling and Installing Your Filter</A></H2>
+
+To compile a filter, modify the Makefile provided with the sample program, or:
+<UL>
+ <LI>Put the include and Sendmail directories in your include path
+ (e.g. -I/path/to/include -I/path/to/sendmail).
+
+ <LI>Make sure libmilter.a is in your library path, and link your
+ application with it (e.g. "-lmilter").
+
+ <LI>Compile with pthreads, either by using -pthread for gcc, or
+ linking with a pthreads support library (-lpthread).
+</UL>
+Your compile command line will look like
+<PRE>
+cc -I/path/to/include -I/path/to/sendmail -c myfile.c
+</PRE>
+and your linking command line will look something like
+<PRE>
+cc -o myfilter [object-files] -L[library-location] -lmilter -pthread
+</PRE>
+
+<H2><A name="config">Configuring Sendmail</A></H2>
+
+If you use a sendmail version older than 8.13 please see
+the instructions for your version.
+The default compilation options for sendmail enable support
+for milters since 8.13.
+
+<P>
+Next, you must add the desired filters to your sendmail configuration
+(.mc) file.
+Mail filters have three equates:
+The required <CODE>S=</CODE> equate specifies the socket where
+sendmail should look for the filter; the optional <CODE>F=</CODE> and
+<CODE>T=</CODE> equates specify flags and timeouts, respectively.
+All equates names, equate field names, and flag values are case sensitive.
+
+<P>
+The current flags (<CODE>F=</CODE>) are:
+<P>
+<TABLE cellspacing="1" cellpadding=4 border=1>
+<TR bgcolor="#dddddd" align=left valign=top>
+<TH>Flag</TH> <TH align="center">Meaning</TH>
+</TR>
+<TR align="left" valign=top>
+<TD>R</TD> <TD>Reject connection if filter unavailable</TD>
+</TR>
+<TR align="left" valign=top>
+<TD>T</TD> <TD>Temporary fail connection if filter unavailable</TD>
+</TR>
+</TABLE>
+
+If a filter is unavailable or unresponsive and no flags have been
+specified, the MTA will continue normal handling of the current
+connection.
+The MTA will try to contact the filter again on each new connection.
+
+<P>
+There are three fields inside of the <CODE>T=</CODE> equate: S, R, and E.
+Note the separator between each is a ";" (semicolon), as ","
+(comma) already separates equates.
+The value of each field is a decimal number followed by a single letter
+designating the units ("s" for seconds, "m" for minutes).
+The fields have the following meanings:
+<P>
+<TABLE cellspacing="1" cellpadding=4 border=1>
+<TR bgcolor="#dddddd" align=left valign=top>
+<TH>Flag</TH> <TH align="center">Meaning</TH>
+</TR>
+<TR align="left" valign=top>
+<TD>C</TD> <TD>Timeout for connecting to a filter. If set to 0, the
+ system's <CODE>connect(2)</CODE> timeout will be used.
+ Default: 5m</TD>
+</TR>
+<TR align="left" valign=top>
+<TD>S</TD> <TD>Timeout for sending information from the MTA to a
+ filter. Default: 10s</TD>
+</TR>
+<TR align="left" valign=top>
+<TD>R</TD> <TD>Timeout for reading reply from the filter. Default: 10s</TD>
+</TR>
+<TR align="left" valign=top>
+<TD>E</TD> <TD>Overall timeout between sending end-of-message to
+ filter and waiting for the final acknowledgment. Default: 5m</TD>
+</TR>
+</TABLE>
+
+<P>
+The following sendmail.mc example specifies three filters.
+The first two rendezvous on Unix-domain sockets in the /var/run directory;
+the third uses an IP socket on port 999.
+<PRE>
+ INPUT_MAIL_FILTER(`filter1', `S=unix:/var/run/f1.sock, F=R')
+ INPUT_MAIL_FILTER(`filter2', `S=unix:/var/run/f2.sock, F=T, T=S:1s;R:1s;E:5m')
+ INPUT_MAIL_FILTER(`filter3', `S=inet:999@localhost, T=C:2m')
+
+ define(`confINPUT_MAIL_FILTERS', `filter2,filter1,filter3')
+<HR width="30%">
+ m4 ../m4/cf.m4 myconfig.mc &gt; myconfig.cf
+</PRE>
+By default, the filters would be run in the order declared,
+i.e. "filter1, filter2, filter3"; however, since
+<CODE>confINPUT_MAIL_FILTERS</CODE> is defined, the filters will be
+run "filter2, filter1, filter3".
+Also note that a filter can be defined
+without adding it to the input filter list by using
+MAIL_FILTER() instead of INPUT_MAIL_FILTER().
+
+<P>
+The above macros will result in the following lines being added to
+your .cf file:
+<PRE>
+ Xfilter1, S=unix:/var/run/f1.sock, F=R
+ Xfilter2, S=unix:/var/run/f2.sock, F=T, T=S:1s;R:1s;E:5m
+ Xfilter3, S=inet:999@localhost, T=C:2m
+
+ O InputMailFilters=filter2,filter1,filter3
+</PRE>
+<P>
+Finally, the sendmail macros accessible via
+<A HREF="smfi_getsymval.html">smfi_getsymval</A> can be configured by
+defining the following m4 variables (or cf options):
+<TABLE cellspacing="1" cellpadding=4 border=1>
+<TR bgcolor="#dddddd" align=left valign=top>
+<TH align="center">In .mc file</TH> <TH align="center">In .cf file</TH>
+<TH align="center">Default Value</TH>
+</TR>
+<TR><TD>confMILTER_MACROS_CONNECT</TD><TD>Milter.macros.connect</TD>
+<TD><CODE>j, _, {daemon_name}, {if_name}, {if_addr}</CODE></TD></TR>
+<TR><TD>confMILTER_MACROS_HELO</TD><TD>Milter.macros.helo</TD>
+<TD><CODE>{tls_version}, {cipher}, {cipher_bits}, {cert_subject},
+{cert_issuer}</CODE></TD></TR>
+<TR><TD>confMILTER_MACROS_ENVFROM</TD><TD>Milter.macros.envfrom</TD>
+<TD><CODE>i, {auth_type}, {auth_authen}, {auth_ssf}, {auth_author},
+{mail_mailer}, {mail_host}, {mail_addr}</CODE></TD></TR>
+<TR><TD>confMILTER_MACROS_ENVRCPT</TD><TD>Milter.macros.envrcpt</TD>
+<TD><CODE>{rcpt_mailer}, {rcpt_host}, {rcpt_addr}</CODE></TD></TR>
+</TABLE>
+For information about available macros and their meanings, please
+consult the sendmail documentation.
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2003, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/other.html b/contrib/sendmail/libmilter/docs/other.html
new file mode 100644
index 0000000..c33b536
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/other.html
@@ -0,0 +1,18 @@
+<HTML>
+<HEAD><TITLE>Other Resources</TITLE>
+</HEAD>
+<BODY>
+<!--
+$Id: other.html,v 1.6 2006/08/08 20:55:57 ca Exp $
+-->
+FAQ? Mailing list? More sample filters?
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/overview.html b/contrib/sendmail/libmilter/docs/overview.html
new file mode 100644
index 0000000..5c6f21c
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/overview.html
@@ -0,0 +1,217 @@
+<HTML>
+<HEAD>
+<TITLE>Technical Overview</TITLE>
+</HEAD>
+<BODY>
+<!--
+$Id: overview.html,v 1.20 2009/11/13 18:15:05 ca Exp $
+-->
+
+<H1>Technical Overview</H1>
+
+<H2>Contents</H2>
+
+<UL>
+ <LI><A HREF="#Initialization">Initialization</A>
+ <LI><A HREF="#ControlFlow">Control Flow</A>
+ <LI><A HREF="#Multithreading">Multithreading</A>
+ <LI><A HREF="#ResourceManagement">Resource Management</A>
+ <LI><A HREF="#SignalHandling">Signal Handling</A>
+</UL>
+
+<H2><A NAME="Initialization">Initialization</A></H2>
+
+In addition to its own initialization,
+libmilter expects a filter to initialize several parameters
+before calling <A HREF="smfi_main.html">smfi_main</A>:
+<UL>
+ <LI>The callbacks the filter wishes to be called, and the types of
+ message modification it intends to perform (required, see
+ <A HREF="smfi_register.html">smfi_register</A>).
+
+ <LI>The socket address to be used when communicating with the MTA
+ (required, see <A HREF="smfi_setconn.html">smfi_setconn</A>).
+
+ <LI>The number of seconds to wait for MTA connections before
+ timing out (optional, see
+ <A HREF="smfi_settimeout.html">smfi_settimeout</A>).
+</UL>
+<P>
+If the filter fails to initialize libmilter,
+or if one or more of the parameters it has passed are invalid,
+a subsequent call to smfi_main will fail.
+
+<H2><A NAME="ControlFlow">Control Flow</A></H2>
+
+<P>
+The following pseudocode describes the filtering process from the
+perspective of a set of <CODE>N</CODE> MTA's,
+each corresponding to a connection.
+Callbacks are shown beside the processing stages in which they are invoked;
+if no callbacks are defined for a particular stage,
+that stage may be bypassed.
+Though it is not shown,
+processing may be aborted at any time during a message,
+in which case the
+<A HREF="xxfi_abort.html">xxfi_abort</A> callback is invoked and control
+returns to <CODE>MESSAGE</CODE>.
+<P>
+<PRE>
+For each of N connections
+{
+ For each filter
+ process connection (<A HREF="xxfi_connect.html">xxfi_connect</A>)
+ For each filter
+ process helo/ehlo (<A HREF="xxfi_helo.html">xxfi_helo</A>)
+MESSAGE:For each message in this connection (sequentially)
+ {
+ For each filter
+ process sender (<A HREF="xxfi_envfrom.html">xxfi_envfrom</A>)
+ For each recipient
+ {
+ For each filter
+ process recipient (<A HREF="xxfi_envrcpt.html">xxfi_envrcpt</A>)
+ }
+ For each filter
+ {
+ process DATA (<A HREF="xxfi_data.html">xxfi_data</A>)
+ For each header
+ process header (<A HREF="xxfi_header.html">xxfi_header</A>)
+ process end of headers (<A HREF="xxfi_eoh.html">xxfi_eoh</A>)
+ For each body block
+ process this body block (<A HREF="xxfi_body.html">xxfi_body</A>)
+ process end of message (<A HREF="xxfi_eom.html">xxfi_eom</A>)
+ }
+ }
+ For each filter
+ process end of connection (<A HREF="xxfi_close.html">xxfi_close</A>)
+}
+</PRE>
+
+<P>Note: Filters are contacted in order defined in config file.</P>
+
+<P>
+To write a filter, a vendor supplies callbacks to process relevant
+parts of a message transaction.
+The library then controls all sequencing, threading,
+and protocol exchange with the MTA.
+<A HREF="#figure-3">Figure 3</A> outlines control flow for a filter
+process, showing where different callbacks are invoked.
+</P>
+
+<DIV ALIGN="center"><A NAME="figure-3"></A>
+<TABLE border=1 cellspacing=0 cellpadding=2 width="70%">
+<TR bgcolor="#dddddd"><TH>SMTP Commands</TH><TH>Milter Callbacks</TH></TR>
+<TR><TD>(open SMTP connection)</TD><TD>xxfi_connect</TD></TR>
+<TR><TD>HELO ...</TD><TD>xxfi_helo</TD></TR>
+<TR><TD>MAIL From: ...</TD><TD>xxfi_envfrom</TD></TR>
+<TR><TD>RCPT To: ...</TD><TD>xxfi_envrcpt</TD></TR>
+<TR><TD>[more RCPTs]</TD><TD>[xxfi_envrcpt]</TD></TR>
+<TR><TD>DATA</TD><TD>xxfi_data</TD></TR>
+<TR><TD>Header: ...</TD><TD>xxfi_header</TD></TR>
+<TR><TD>[more headers]</TD><TD>[xxfi_header]</TD></TR>
+<TR><TD>&nbsp;</TD><TD>xxfi_eoh</TD></TR>
+<TR><TD>body... </TD><TD>xxfi_body</TD></TR>
+<TR><TD>[more body...]</TD><TD>[xxfi_body]</TD></TR>
+<TR><TD>.</TD><TD>xxfi_eom</TD></TR>
+<TR><TD>QUIT</TD><TD>xxfi_close</TD></TR>
+<TR><TD>(close SMTP connection)</TD><TD>&nbsp;</TD></TR>
+</TABLE>
+<B>Figure 3: Milter callbacks related to an SMTP transaction.</B>
+</DIV>
+
+<P>
+Note that although only a single message is shown above, multiple
+messages may be sent in a single connection.
+Note also that a message or connection may be aborted by
+either the remote host or the MTA
+at any point during the SMTP transaction.
+f this occurs during a message (between the MAIL command and the final "."),
+the filter's
+<A HREF="xxfi_abort.html">xxfi_abort</A> routine will be called.
+<A HREF="xxfi_close.html">xxfi_close</A> is called any time the
+connection closes.
+
+<H2><A NAME="Multithreading">Multithreading</A></H2>
+
+<P>
+A single filter process may handle any number of connections
+simultaneously.
+All filtering callbacks must therefore be reentrant,
+and use some appropriate external synchronization methods to access
+global data.
+Furthermore, since there is not a one-to-one correspondence
+between threads and connections
+(N connections mapped onto M threads, M &lt;= N),
+connection-specific data must be accessed
+through the handles provided by the Milter library.
+The programmer cannot rely on library-supplied thread-specific data blocks
+(e.g., <CODE>pthread_getspecific(3)</CODE>) to store connection-specific data.
+See the API documentation for
+<A HREF="smfi_setpriv.html">smfi_setpriv</A> and
+<A HREF="smfi_getpriv.html">smfi_getpriv</A> for details.
+
+<H2><A NAME="ResourceManagement">Resource Management</A></H2>
+
+Since filters are likely to be long-lived,
+and to handle many connections,
+proper deallocation of per-connection resources is important.
+The lifetime of a connection is bracketed by calls to the
+callbacks <A HREF="xxfi_connect.html">xxfi_connect</A> and
+<A HREF="xxfi_close.html">xxfi_close</A>.
+Therefore connection-specific
+resources (accessed via <A HREF="smfi_getpriv.html">smfi_getpriv</A>
+and <A HREF="smfi_setpriv.html">smfi_setpriv</A>) may be allocated in
+<A HREF="xxfi_connect.html">xxfi_connect</A>,
+and should be freed in
+<A HREF="xxfi_close.html">xxfi_close</A>.
+For further information see
+the <A HREF="api.html#conn-msg">discussion</A> of message- versus
+connection-oriented routines.
+In particular,
+note that there is only one connection-specific data pointer per connection.
+<P>
+
+Each message is bracketed by calls to
+<A HREF="xxfi_envfrom.html">xxfi_envfrom</A> and
+<A HREF="xxfi_eom.html">xxfi_eom</A> (or
+<A HREF="xxfi_abort.html">xxfi_abort</A>),
+implying that message-specific resources can be allocated
+and reclaimed in these routines.
+Since the messages in a connection are processed sequentially by each filter,
+there will be only one active message associated with a given
+connection and filter (and connection-private data block).
+These resources must still be accessed through
+<A HREF="smfi_getpriv.html">smfi_getpriv</A> and
+<A HREF="smfi_setpriv.html">smfi_setpriv</A>,
+and must be reclaimed in
+<A HREF="xxfi_abort.html">xxfi_abort</A>.
+
+<H2><A NAME="SignalHandling">Signal Handling</A></H2>
+
+libmilter takes care of signal handling,
+the filters are not influenced directly by signals.
+There are basically two types of signal handlers:
+
+<OL>
+<LI><TT>Stop</TT>: no new connections from the MTA will be accepted,
+but existing connections are allowed to continue.
+<LI><TT>Abort</TT>: all filters will be stopped as soon as the next
+communication with the MTA happens.
+</OL>
+
+Filters are not terminated asynchronously
+(except by signals that can't be caught).
+In the case of <TT>Abort</TT> the
+<A HREF="xxfi_abort.html">xxfi_abort</A> callback is invoked.
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2001, 2003, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/sample.html b/contrib/sendmail/libmilter/docs/sample.html
new file mode 100644
index 0000000..48e25c5
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/sample.html
@@ -0,0 +1,537 @@
+<HTML>
+<HEAD><TITLE>A Sample Filter</TITLE></HEAD>
+<BODY>
+<!--
+$Id: sample.html,v 1.22 2006/10/09 23:14:51 ca Exp $
+-->
+<H1>A Sample Filter</H1>
+
+The following sample logs each message to a separate temporary file,
+adds a recipient given with the -a flag,
+and rejects a disallowed recipient address given with the -r flag.
+It recognizes the following options:
+<P>
+<CENTER>
+<TABLE border="1" cellpadding=2 cellspacing=1>
+<TR><TD><CODE>-p port</CODE></TD><TD>The port through which the MTA will connect to the filter.</TD></TR>
+<TR><TD><CODE>-t sec</CODE></TD><TD>The timeout value.</TD></TR>
+<TR><TD><CODE>-r addr</CODE></TD><TD>A recipient to reject.</TD></TR>
+<TR><TD><CODE>-a addr</CODE></TD><TD>A recipient to add.</TD></TR>
+</TABLE>
+</CENTER>
+<HR>
+<PRE>
+#include &lt;sys/types.h&gt;
+#include &lt;sys/stat.h&gt;
+#include &lt;errno.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;stdlib.h&gt;
+#include &lt;string.h&gt;
+#include &lt;sysexits.h&gt;
+#include &lt;unistd.h&gt;
+
+#include "libmilter/mfapi.h"
+
+#ifndef bool
+# define bool int
+# define TRUE 1
+# define FALSE 0
+#endif /* ! bool */
+
+
+struct mlfiPriv
+{
+ char *mlfi_fname;
+ char *mlfi_connectfrom;
+ char *mlfi_helofrom;
+ FILE *mlfi_fp;
+};
+
+#define MLFIPRIV ((struct mlfiPriv *) <A href="smfi_getpriv.html">smfi_getpriv</A>(ctx))
+
+extern sfsistat mlfi_cleanup(SMFICTX *, bool);
+
+/* recipients to add and reject (set with -a and -r options) */
+char *add = NULL;
+char *reject = NULL;
+
+sfsistat
+<A href="xxfi_connect.html">mlfi_connect</A>(ctx, hostname, hostaddr)
+ SMFICTX *ctx;
+ char *hostname;
+ _SOCK_ADDR *hostaddr;
+{
+ struct mlfiPriv *priv;
+ char *ident;
+
+ /* allocate some private memory */
+ priv = malloc(sizeof *priv);
+ if (priv == NULL)
+ {
+ /* can't accept this message right now */
+ return SMFIS_TEMPFAIL;
+ }
+ memset(priv, '\0', sizeof *priv);
+
+ /* save the private data */
+ <A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, priv);
+
+ ident = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "_");
+ if (ident == NULL)
+ ident = "???";
+ if ((priv-&gt;mlfi_connectfrom = strdup(ident)) == NULL)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_helo.html">mlfi_helo</A>(ctx, helohost)
+ SMFICTX *ctx;
+ char *helohost;
+{
+ size_t len;
+ char *tls;
+ char *buf;
+ struct mlfiPriv *priv = MLFIPRIV;
+
+ tls = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{tls_version}");
+ if (tls == NULL)
+ tls = "No TLS";
+ if (helohost == NULL)
+ helohost = "???";
+ len = strlen(tls) + strlen(helohost) + 3;
+ if ((buf = (char*) malloc(len)) == NULL)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+ snprintf(buf, len, "%s, %s", helohost, tls);
+ if (priv-&gt;mlfi_helofrom != NULL)
+ free(priv-&gt;mlfi_helofrom);
+ priv-&gt;mlfi_helofrom = buf;
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_envfrom.html">mlfi_envfrom</A>(ctx, argv)
+ SMFICTX *ctx;
+ char **argv;
+{
+ int fd = -1;
+ int argc = 0;
+ struct mlfiPriv *priv = MLFIPRIV;
+ char *mailaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{mail_addr}");
+
+ /* open a file to store this message */
+ if ((priv-&gt;mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ if ((fd = mkstemp(priv-&gt;mlfi_fname)) == -1)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ if ((priv-&gt;mlfi_fp = fdopen(fd, "w+")) == NULL)
+ {
+ (void) close(fd);
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* count the arguments */
+ while (*argv++ != NULL)
+ ++argc;
+
+ /* log the connection information we stored earlier: */
+ if (fprintf(priv-&gt;mlfi_fp, "Connect from %s (%s)\n\n",
+ priv-&gt;mlfi_helofrom, priv-&gt;mlfi_connectfrom) == EOF)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+ /* log the sender */
+ if (fprintf(priv-&gt;mlfi_fp, "FROM %s (%d argument%s)\n",
+ mailaddr ? mailaddr : "???", argc,
+ (argc == 1) ? "" : "s") == EOF)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_envrcpt.html">mlfi_envrcpt</A>(ctx, argv)
+ SMFICTX *ctx;
+ char **argv;
+{
+ struct mlfiPriv *priv = MLFIPRIV;
+ char *rcptaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{rcpt_addr}");
+ int argc = 0;
+
+ /* count the arguments */
+ while (*argv++ != NULL)
+ ++argc;
+
+ /* log this recipient */
+ if (reject != NULL && rcptaddr != NULL &&
+ (strcasecmp(rcptaddr, reject) == 0))
+ {
+ if (fprintf(priv-&gt;mlfi_fp, "RCPT %s -- REJECTED\n",
+ rcptaddr) == EOF)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+ return SMFIS_REJECT;
+ }
+ if (fprintf(priv-&gt;mlfi_fp, "RCPT %s (%d argument%s)\n",
+ rcptaddr ? rcptaddr : "???", argc,
+ (argc == 1) ? "" : "s") == EOF)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_header.html">mlfi_header</A>(ctx, headerf, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ unsigned char *headerv;
+{
+ /* write the header to the log file */
+ if (fprintf(MLFIPRIV-&gt;mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_eoh.html">mlfi_eoh</A>(ctx)
+ SMFICTX *ctx;
+{
+ /* output the blank line between the header and the body */
+ if (fprintf(MLFIPRIV-&gt;mlfi_fp, "\n") == EOF)
+ {
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_body.html">mlfi_body</A>(ctx, bodyp, bodylen)
+ SMFICTX *ctx;
+ unsigned char *bodyp;
+ size_t bodylen;
+{
+ struct mlfiPriv *priv = MLFIPRIV;
+
+ /* output body block to log file */
+ if (fwrite(bodyp, bodylen, 1, priv-&gt;mlfi_fp) != 1)
+ {
+ /* write failed */
+ fprintf(stderr, "Couldn't write file %s: %s\n",
+ priv-&gt;mlfi_fname, strerror(errno));
+ (void) mlfi_cleanup(ctx, FALSE);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_eom.html">mlfi_eom</A>(ctx)
+ SMFICTX *ctx;
+{
+ bool ok = TRUE;
+
+ /* change recipients, if requested */
+ if (add != NULL)
+ ok = (<A href="smfi_addrcpt.html">smfi_addrcpt</A>(ctx, add) == MI_SUCCESS);
+ return mlfi_cleanup(ctx, ok);
+}
+
+sfsistat
+<A href="xxfi_abort.html">mlfi_abort</A>(ctx)
+ SMFICTX *ctx;
+{
+ return mlfi_cleanup(ctx, FALSE);
+}
+
+sfsistat
+mlfi_cleanup(ctx, ok)
+ SMFICTX *ctx;
+ bool ok;
+{
+ sfsistat rstat = SMFIS_CONTINUE;
+ struct mlfiPriv *priv = MLFIPRIV;
+ char *p;
+ char host[512];
+ char hbuf[1024];
+
+ if (priv == NULL)
+ return rstat;
+
+ /* close the archive file */
+ if (priv-&gt;mlfi_fp != NULL && fclose(priv-&gt;mlfi_fp) == EOF)
+ {
+ /* failed; we have to wait until later */
+ fprintf(stderr, "Couldn't close archive file %s: %s\n",
+ priv-&gt;mlfi_fname, strerror(errno));
+ rstat = SMFIS_TEMPFAIL;
+ (void) unlink(priv-&gt;mlfi_fname);
+ }
+ else if (ok)
+ {
+ /* add a header to the message announcing our presence */
+ if (gethostname(host, sizeof host) &lt; 0)
+ snprintf(host, sizeof host, "localhost");
+ p = strrchr(priv-&gt;mlfi_fname, '/');
+ if (p == NULL)
+ p = priv-&gt;mlfi_fname;
+ else
+ p++;
+ snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
+ if (<A href="smfi_addheader.html">smfi_addheader</A>(ctx, "X-Archived", hbuf) != MI_SUCCESS)
+ {
+ /* failed; we have to wait until later */
+ fprintf(stderr,
+ "Couldn't add header: X-Archived: %s\n",
+ hbuf);
+ ok = FALSE;
+ rstat = SMFIS_TEMPFAIL;
+ (void) unlink(priv-&gt;mlfi_fname);
+ }
+ }
+ else
+ {
+ /* message was aborted -- delete the archive file */
+ fprintf(stderr, "Message aborted. Removing %s\n",
+ priv-&gt;mlfi_fname);
+ rstat = SMFIS_TEMPFAIL;
+ (void) unlink(priv-&gt;mlfi_fname);
+ }
+
+ /* release private memory */
+ if (priv-&gt;mlfi_fname != NULL)
+ free(priv-&gt;mlfi_fname);
+
+ /* return status */
+ return rstat;
+}
+
+sfsistat
+<A href="xxfi_close.html">mlfi_close</A>(ctx)
+ SMFICTX *ctx;
+{
+ struct mlfiPriv *priv = MLFIPRIV;
+
+ if (priv == NULL)
+ return SMFIS_CONTINUE;
+ if (priv-&gt;mlfi_connectfrom != NULL)
+ free(priv-&gt;mlfi_connectfrom);
+ if (priv-&gt;mlfi_helofrom != NULL)
+ free(priv-&gt;mlfi_helofrom);
+ free(priv);
+ <A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, NULL);
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_unknown.html">mlfi_unknown</A>(ctx, cmd)
+ SMFICTX *ctx;
+ char *cmd;
+{
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_data.html">mlfi_data</A>(ctx)
+ SMFICTX *ctx;
+{
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+<A href="xxfi_negotiate.html">mlfi_negotiate</A>(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
+ SMFICTX *ctx;
+ unsigned long f0;
+ unsigned long f1;
+ unsigned long f2;
+ unsigned long f3;
+ unsigned long *pf0;
+ unsigned long *pf1;
+ unsigned long *pf2;
+ unsigned long *pf3;
+{
+ return SMFIS_ALL_OPTS;
+}
+
+struct smfiDesc smfilter =
+{
+ "SampleFilter", /* filter name */
+ SMFI_VERSION, /* version code -- do not change */
+ SMFIF_ADDHDRS|SMFIF_ADDRCPT,
+ /* flags */
+ <A href="xxfi_connect.html">mlfi_connect</A>, /* connection info filter */
+ <A href="xxfi_helo.html">mlfi_helo</A>, /* SMTP HELO command filter */
+ <A href="xxfi_envfrom.html">mlfi_envfrom</A>, /* envelope sender filter */
+ <A href="xxfi_envrcpt.html">mlfi_envrcpt</A>, /* envelope recipient filter */
+ <A href="xxfi_header.html">mlfi_header</A>, /* header filter */
+ <A href="xxfi_eoh.html">mlfi_eoh</A>, /* end of header */
+ <A href="xxfi_body.html">mlfi_body</A>, /* body block filter */
+ <A href="xxfi_eom.html">mlfi_eom</A>, /* end of message */
+ <A href="xxfi_abort.html">mlfi_abort</A>, /* message aborted */
+ <A href="xxfi_close.html">mlfi_close</A>, /* connection cleanup */
+ <A href="xxfi_unknown.html">mlfi_unknown</A>, /* unknown SMTP commands */
+ <A href="xxfi_data.html">mlfi_data</A>, /* DATA command */
+ <A href="xxfi_negotiate.html">mlfi_negotiate</A> /* Once, at the start of each SMTP connection */
+};
+
+static void
+usage(prog)
+ char *prog;
+{
+ fprintf(stderr,
+ "Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n",
+ prog);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ bool setconn = FALSE;
+ int c;
+ const char *args = "p:t:r:a:h";
+ extern char *optarg;
+
+ /* Process command line options */
+ while ((c = getopt(argc, argv, args)) != -1)
+ {
+ switch (c)
+ {
+ case 'p':
+ if (optarg == NULL || *optarg == '\0')
+ {
+ (void) fprintf(stderr, "Illegal conn: %s\n",
+ optarg);
+ exit(EX_USAGE);
+ }
+ if (<A href="smfi_setconn.html">smfi_setconn</A>(optarg) == MI_FAILURE)
+ {
+ (void) fprintf(stderr,
+ "smfi_setconn failed\n");
+ exit(EX_SOFTWARE);
+ }
+
+ /*
+ ** If we're using a local socket, make sure it
+ ** doesn't already exist. Don't ever run this
+ ** code as root!!
+ */
+
+ if (strncasecmp(optarg, "unix:", 5) == 0)
+ unlink(optarg + 5);
+ else if (strncasecmp(optarg, "local:", 6) == 0)
+ unlink(optarg + 6);
+ setconn = TRUE;
+ break;
+
+ case 't':
+ if (optarg == NULL || *optarg == '\0')
+ {
+ (void) fprintf(stderr, "Illegal timeout: %s\n",
+ optarg);
+ exit(EX_USAGE);
+ }
+ if (<A href="smfi_settimeout.html">smfi_settimeout</A>(atoi(optarg)) == MI_FAILURE)
+ {
+ (void) fprintf(stderr,
+ "smfi_settimeout failed\n");
+ exit(EX_SOFTWARE);
+ }
+ break;
+
+ case 'r':
+ if (optarg == NULL)
+ {
+ (void) fprintf(stderr,
+ "Illegal reject rcpt: %s\n",
+ optarg);
+ exit(EX_USAGE);
+ }
+ reject = optarg;
+ break;
+
+ case 'a':
+ if (optarg == NULL)
+ {
+ (void) fprintf(stderr,
+ "Illegal add rcpt: %s\n",
+ optarg);
+ exit(EX_USAGE);
+ }
+ add = optarg;
+ smfilter.xxfi_flags |= SMFIF_ADDRCPT;
+ break;
+
+ case 'h':
+ default:
+ usage(argv[0]);
+ exit(EX_USAGE);
+ }
+ }
+ if (!setconn)
+ {
+ fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
+ usage(argv[0]);
+ exit(EX_USAGE);
+ }
+ if (<A href="smfi_register.html">smfi_register</A>(smfilter) == MI_FAILURE)
+ {
+ fprintf(stderr, "smfi_register failed\n");
+ exit(EX_UNAVAILABLE);
+ }
+ return <A href="smfi_main.html">smfi_main</A>();
+}
+
+/* eof */
+
+</PRE>
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2004, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_addheader.html b/contrib/sendmail/libmilter/docs/smfi_addheader.html
new file mode 100644
index 0000000..460b4ca
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_addheader.html
@@ -0,0 +1,130 @@
+<HTML>
+<HEAD><TITLE>smfi_addheader</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_addheader.html,v 1.20 2009/05/18 23:51:23 ca Exp $
+-->
+<H1>smfi_addheader</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_addheader(
+ SMFICTX *ctx,
+ char *headerf,
+ char *headerv
+);
+</PRE>
+Add a header to the current message.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Adds a header to the current message.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>headerf</TD>
+ <TD>The header name, a non-NULL, null-terminated string.
+ </TD></TR>
+ <TR valign="top"><TD>headerv</TD>
+ <TD>The header value to be added, a non-NULL, null-terminated string.
+ This may be the empty string.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_addheader returns MI_FAILURE if:
+<UL><LI>headerf or headerv is NULL.
+ <LI>Adding headers in the current connection state is invalid.
+ <LI>Memory allocation fails.
+ <LI>A network error occurs.
+ <LI>SMFIF_ADDHDRS was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it returns MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+<UL><LI>smfi_addheader does not change a message's existing headers.
+To change a header's current value, use
+<A HREF="smfi_chgheader.html">smfi_chgheader</A>.
+ <LI>A filter which calls smfi_addheader must have set the SMFIF_ADDHDRS
+ flag in the smfiDesc_str passed to
+ <A href="smfi_register.html">smfi_register</A>.
+ <LI>For smfi_addheader, filter order is important.
+ <B>Later filters will see the header changes made by earlier ones.</B>
+ <LI>Neither the name nor the value of the header is checked for
+ standards compliance.
+ However, each line of the header must be under 2048 characters
+ and should be under 998 characters.
+ If longer headers are needed, make them multi-line.
+ To make a multi-line header,
+ insert a line feed (ASCII 0x0a, or <TT>\n</TT> in C)
+ followed by at least one whitespace character
+ such as a space (ASCII 0x20) or tab (ASCII 0x09, or <TT>\t</TT> in C).
+ The line feed should NOT be preceded by a carriage return (ASCII 0x0d);
+ the MTA will add this automatically.
+ <B>It is the filter writer's responsibility to ensure that no standards
+ are violated.</B>
+ <LI>The MTA adds a leading space to an added header value unless
+ the flag
+<A HREF="xxfi_negotiate.html#SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>
+ is set, in which case the milter
+ must include any desired leading spaces itself.
+</UL>
+</TD>
+</TR>
+
+<!----------- Example code ---------->
+<TR>
+<TH valign="top" align=left>EXAMPLE</TH>
+
+<TD>
+ <PRE>
+ int ret;
+ SMFICTX *ctx;
+
+ ...
+
+ ret = smfi_addheader(ctx, "Content-Type",
+ "multipart/mixed;\n\tboundary=\"foobar\"");
+ </PRE>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2003, 2006, 2009 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_addrcpt.html b/contrib/sendmail/libmilter/docs/smfi_addrcpt.html
new file mode 100644
index 0000000..cf997e5
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_addrcpt.html
@@ -0,0 +1,83 @@
+<HTML>
+<HEAD><TITLE>smfi_addrcpt</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_addrcpt.html,v 1.11 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_addrcpt</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_addrcpt(
+ SMFICTX *ctx,
+ char *rcpt
+);
+</PRE>
+Add a recipient for the current message.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Add a recipient to the message envelope.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>rcpt</TD>
+ <TD>The new recipient's address.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_addrcpt will fail and return MI_FAILURE if:
+<UL><LI>rcpt is NULL.
+ <LI>Adding recipients in the current connection state is invalid.
+ <LI>A network error occurs.
+ <LI>SMFIF_ADDRCPT was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it will return MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+A filter which calls smfi_addrcpt must have set the SMFIF_ADDRCPT flag
+in the smfiDesc_str passed to
+<A href="smfi_register.html">smfi_register</A>.
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_addrcpt_par.html b/contrib/sendmail/libmilter/docs/smfi_addrcpt_par.html
new file mode 100644
index 0000000..776b02c
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_addrcpt_par.html
@@ -0,0 +1,88 @@
+<HTML>
+<HEAD><TITLE>smfi_addrcpt_par</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_addrcpt_par.html,v 1.4 2007/03/19 16:38:02 ca Exp $
+-->
+<H1>smfi_addrcpt_par</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_addrcpt_par(
+ SMFICTX *ctx,
+ char *rcpt,
+ char *args
+);
+</PRE>
+Add a recipient for the current message including ESMTP arguments.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Add a recipient to the message envelope.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>rcpt</TD>
+ <TD>The new recipient's address.
+ </TD></TR>
+ <TR valign="top"><TD>args</TD>
+ <TD>The new recipient's ESMTP parameters.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_addrcpt will fail and return MI_FAILURE if:
+<UL><LI>rcpt is NULL.
+ <LI>Adding recipients in the current connection state is invalid.
+ <LI>A network error occurs.
+ <LI>SMFIF_ADDRCPT_PAR was not set when
+ <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it will return MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+A filter which calls smfi_addrcpt must have set the SMFIF_ADDRCPT_PAR flag
+in the smfiDesc_str passed to
+<A href="smfi_register.html">smfi_register</A>.
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_chgfrom.html b/contrib/sendmail/libmilter/docs/smfi_chgfrom.html
new file mode 100644
index 0000000..e8249e0
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_chgfrom.html
@@ -0,0 +1,94 @@
+<HTML>
+<HEAD><TITLE>smfi_chgfrom</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_chgfrom.html,v 1.3 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_chgfrom</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_chgfrom(
+ SMFICTX *ctx,
+ const char *mail,
+ char *args
+);
+</PRE>
+Change the envelope sender (MAIL From) of the current message.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Change the envelope sender (MAIL From) of the current message.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>mail</TD>
+ <TD>The new sender address.
+ </TD></TR>
+ <TR valign="top"><TD>args</TD>
+ <TD>ESMTP arguments.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_chgfrom will fail and return MI_FAILURE if:
+<UL><LI>mail is NULL.
+ <LI>Changing the sender in the current connection state is invalid.
+ <LI>A network error occurs.
+ <LI>SMFIF_CHGFROM was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it will return MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+A filter which calls smfi_chgfrom must have set the SMFIF_CHGFROM flag
+in the smfiDesc_str passed to
+<A href="smfi_register.html">smfi_register</A>.
+<BR>
+Even though all ESMTP arguments could be set via this call,
+it does not make sense to do so for many of them,
+e.g., SIZE and BODY.
+Setting those may cause problems, proper care must be taken.
+Moreover, there is no feedback from the MTA to the milter
+whether the call was successful.
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_chgheader.html b/contrib/sendmail/libmilter/docs/smfi_chgheader.html
new file mode 100644
index 0000000..517b5ba
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_chgheader.html
@@ -0,0 +1,125 @@
+<HTML>
+<HEAD><TITLE>smfi_chgheader</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_chgheader.html,v 1.18 2009/05/18 23:51:23 ca Exp $
+-->
+<H1>smfi_chgheader</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_chgheader(
+ SMFICTX *ctx,
+ char *headerf,
+ mi_int32 hdridx,
+ char *headerv
+);
+</PRE>
+Change or delete a message header.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Changes a header's value for the current message.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>headerf</TD>
+ <TD>The header name, a non-NULL, null-terminated string.
+ </TD></TR>
+ <TR valign="top"><TD>hdridx</TD>
+ <TD>Header index value (1-based). A hdridx value of 1 will modify the first occurrence of a header named headerf. If hdridx is greater than the number of times headerf appears, a new copy of headerf will be added.
+ </TD></TR>
+ <TR valign="top"><TD>headerv</TD>
+ <TD>The new value of the given header. headerv == NULL implies that the header should be deleted.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>
+smfi_chgheader will return MI_FAILURE if
+<UL><LI>headerf is NULL
+ <LI>Modifying headers in the current connection state is invalid.
+ <LI>Memory allocation fails.
+ <LI>A network error occurs.
+ <LI>SMFIF_CHGHDRS was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it returns MI_SUCCESS.
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+<UL><LI>While smfi_chgheader may be used to add new headers, it is more efficient and far safer to use <A href="smfi_addheader.html">smfi_addheader</A>.
+ <LI>A filter which calls smfi_chgheader must have set the SMFIF_CHGHDRS flag in the smfiDesc_str passed to <A href="smfi_register.html">smfi_register</A>.
+ <LI>For smfi_chgheader, filter order is important. <B>Later filters will see the header changes made by earlier ones.</B>
+ <LI>Neither the name nor the value of the header is checked for
+ standards compliance. However, each line of the header must be under
+ 2048 characters and should be under 998 characters. If longer headers
+ are needed, make them multi-line. To make a multi-line header, insert
+ a line feed (ASCII 0x0a, or <TT>\n</TT> in C) followed by at least
+ one whitespace character such as a space (ASCII 0x20) or tab (ASCII 0x09,
+ or <TT>\t</TT> in C). The line feed should NOT be preceded by a
+ carriage return (ASCII 0x0d); the MTA will add this automatically.
+ <B>It is the filter writer's responsibility to ensure that no standards
+ are violated.</B>
+ <LI>The MTA adds a leading space to a header value unless
+ the flag
+<A HREF="xxfi_negotiate.html#SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>
+ is set, in which case the milter
+ must include any desired leading spaces itself.
+</UL>
+</TD>
+</TR>
+
+<!----------- Example code ---------->
+<TR>
+<TH valign="top" align=left>EXAMPLE</TH>
+
+<TD>
+ <PRE>
+ int ret;
+ SMFICTX *ctx;
+
+ ...
+
+ ret = smfi_chgheader(ctx, "Content-Type", 1,
+ "multipart/mixed;\n\tboundary=\"foobar\"");
+ </PRE>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2003, 2009 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_delrcpt.html b/contrib/sendmail/libmilter/docs/smfi_delrcpt.html
new file mode 100644
index 0000000..c43dcd6
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_delrcpt.html
@@ -0,0 +1,82 @@
+<HTML>
+<HEAD><TITLE>smfi_delrcpt</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_delrcpt.html,v 1.11 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_delrcpt</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_delrcpt(
+ SMFICTX *ctx;
+ char *rcpt;
+);
+</PRE>
+Remove a recipient from the current message's envelope.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_delrcpt removes the named recipient from the current message's envelope.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>rcpt</TD>
+ <TD>The recipient address to be removed, a non-NULL, null-terminated string.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_delrcpt will fail and return MI_FAILURE if:
+<UL>
+ <LI>rcpt is NULL.
+ <LI>Deleting recipients in the current connection state is invalid.
+ <LI>A network error occurs.
+ <LI>SMFIF_DELRCPT was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it will return MI_SUCCESS
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+The addresses to be removed must match exactly. For example, an address and its expanded form do not match.
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_getpriv.html b/contrib/sendmail/libmilter/docs/smfi_getpriv.html
new file mode 100644
index 0000000..9584b9e
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_getpriv.html
@@ -0,0 +1,62 @@
+<HTML>
+<HEAD><TITLE>smfi_getpriv</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_getpriv.html,v 1.9 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_getpriv</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+void* smfi_getpriv(
+ SMFICTX *ctx
+);
+</PRE>
+Get the connection-specific data pointer for this connection.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_getpriv may be called in any of the xxfi_* callbacks.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>None.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_getpriv returns the private data pointer stored by a prior call to <A href="smfi_setpriv.html">smfi_setpriv</A>, or NULL if none has been set.</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_getsymval.html b/contrib/sendmail/libmilter/docs/smfi_getsymval.html
new file mode 100644
index 0000000..671dbfa
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_getsymval.html
@@ -0,0 +1,105 @@
+<HTML>
+<HEAD><TITLE>smfi_getsymval</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_getsymval.html,v 1.15 2007/03/19 16:49:11 ca Exp $
+-->
+<H1>smfi_getsymval</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+char* smfi_getsymval(
+ SMFICTX *ctx,
+ char *symname
+);
+</PRE>
+Get the value of a sendmail macro.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_getsymval may be called from within any of the xxfi_* callbacks. Which macros are defined will depend on when it is called.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>None.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>The opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>symname</TD>
+ <TD>The name of a sendmail macro.
+ Single letter macros can optionally be enclosed in braces ("{" and "}"),
+ longer macro names must be enclosed in braces, just as in a
+ <TT>sendmail.cf</TT> file.
+ <A href="#notes">See below</A> for default macros.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_getsymval returns the value of the given macro as a null-terminated string, or NULL if the macro is not defined.</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH><A name="notes">NOTES</A></TH>
+<TD>
+By default, the following macros are valid in the given contexts:
+
+<TABLE border="1" cellspacing=0>
+<TR bgcolor="#dddddd"><TH>Sent With</TH><TH>Macros</TH></TR>
+<TR><TD>xxfi_connect</TD> <TD>daemon_name, if_name, if_addr, j, _</TD></TR>
+<TR><TD>xxfi_helo</TD> <TD>tls_version, cipher, cipher_bits, cert_subject, cert_issuer</TD></TR>
+<TR><TD>xxfi_envfrom</TD> <TD>i, auth_type, auth_authen, auth_ssf, auth_author,
+ mail_mailer, mail_host, mail_addr</TD></TR>
+<TR><TD>xxfi_envrcpt</TD> <TD>rcpt_mailer, rcpt_host, rcpt_addr</TD></TR>
+
+<TR><TD>xxfi_data</TD> <TD>(none)</TD></TR>
+<TR><TD>xxfi_eoh</TD> <TD>(none)</TD></TR>
+<TR><TD>xxfi_eom</TD> <TD>msg_id</TD></TR>
+</TABLE>
+<P>
+All macros stay in effect from the point they are received
+until the end of the connection for the first two sets,
+the end of the message for the third (xxfi_envfrom) and last (xxfi_eom),
+and just for each recipient for xxfi_envrcpt.
+<P>
+The macro list can be changed using the confMILTER_MACROS_* options in
+sendmail.mc.
+The scopes of such macros will be determined by when they are set by sendmail.
+For descriptions of macros' values,
+please see the
+"Sendmail Installation and Operation Guide"
+provided with your sendmail distribution.
+
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2002-2003, 2007 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_insheader.html b/contrib/sendmail/libmilter/docs/smfi_insheader.html
new file mode 100644
index 0000000..5962e61
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_insheader.html
@@ -0,0 +1,150 @@
+<HTML>
+<HEAD><TITLE>smfi_insheader</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_insheader.html,v 1.10 2009/05/18 23:51:23 ca Exp $
+-->
+<H1>smfi_insheader</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_insheader(
+ SMFICTX *ctx,
+ int hdridx,
+ char *headerf,
+ char *headerv
+);
+</PRE>
+Prepend a header to the current message.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Prepends a header to the current message.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>hdridx</TD>
+ <TD>The location in the internal header list where this header should
+ be inserted; 0 makes it the topmost header, etc.
+ </TD></TR>
+ <TR valign="top"><TD>headerf</TD>
+ <TD>The header name, a non-NULL, null-terminated string.
+ </TD></TR>
+ <TR valign="top"><TD>headerv</TD>
+ <TD>The header value to be added, a non-NULL, null-terminated string. This may be the empty string.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_insheader returns MI_FAILURE if:
+<UL><LI>headerf or headerv is NULL.
+ <LI>Adding headers in the current connection state is invalid.
+ <LI>Memory allocation fails.
+ <LI>A network error occurs.
+ <LI>SMFIF_ADDHDRS was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it returns MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+<UL>
+ <LI>smfi_insheader does not change a message's existing headers.
+ To change a header's current value, use
+ <A HREF="smfi_chgheader.html">smfi_chgheader</A>.
+ <LI>A filter which calls smfi_insheader must have set the SMFIF_ADDHDRS
+ flag in the smfiDesc_str passed to
+ <A href="smfi_register.html">smfi_register</A>.
+ <LI>For smfi_insheader, filter order is important.
+ <B>Later filters will see the header changes made by earlier ones.</B>
+ <LI>A filter will receive <EM>only</EM> headers that have been sent
+ by the SMTP client and those header modifications by earlier filters.
+ It will <EM>not</EM> receive the headers that are inserted by sendmail
+ itself.
+ This makes the header insertion position highly dependent on
+ the headers that exist in the incoming message
+ and those that are configured to be added by sendmail.
+ For example, sendmail will always add a
+ <CODE>Received:</CODE> header to the beginning of the headers.
+ Setting <CODE>hdridx</CODE> to 0 will actually insert the header
+ before this <CODE>Received:</CODE> header.
+ However, later filters can be easily confused as they receive
+ the added header, but not the <CODE>Received:</CODE> header,
+ thus making it hard to insert a header at a fixed position.
+ <LI>If hdridx is a number larger than the number of headers in the message,
+ the header will simply be appended.
+ <LI>Neither the name nor the value of the header is checked for
+ standards compliance.
+ However, each line of the header must be under 2048 characters
+ and should be under 998 characters.
+ If longer headers are needed, make them multi-line.
+ To make a multi-line header,
+ insert a line feed (ASCII 0x0a, or <TT>\n</TT> in C)
+ followed by at least one whitespace character
+ such as a space (ASCII 0x20) or tab (ASCII 0x09, or <TT>\t</TT> in C).
+ The line feed should NOT be preceded by a carriage return (ASCII 0x0d);
+ the MTA will add this automatically.
+ <B>It is the filter writer's responsibility to ensure that no standards
+ are violated.</B>
+ <LI>The MTA adds a leading space to an inserted header value unless
+ the flag
+<A HREF="xxfi_negotiate.html#SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>
+ is set, in which case the milter
+ must include any desired leading spaces itself.
+</UL>
+</TD>
+</TR>
+
+<!----------- Example code ---------->
+<TR>
+<TH valign="top" align=left>EXAMPLE</TH>
+
+<TD>
+ <PRE>
+ int ret;
+ SMFICTX *ctx;
+
+ ...
+
+ ret = smfi_insheader(ctx, 0, "First", "See me?");
+ </PRE>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2004, 2006, 2009 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_main.html b/contrib/sendmail/libmilter/docs/smfi_main.html
new file mode 100644
index 0000000..a749386
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_main.html
@@ -0,0 +1,51 @@
+<HTML>
+<HEAD><TITLE>smfi_main</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_main.html,v 1.9 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_main</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_main(
+);
+</PRE>
+Hand control to libmilter event loop.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_main is called after a filter's initialization is complete.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_main hands control to the Milter event loop.</TD>
+</TR>
+</TABLE>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_main will return MI_FAILURE if it fails to establish a connection. This may occur for any of a variety of reasons (e.g. invalid address passed to <A href="smfi_setconn.html">smfi_setconn</A>). The reason for the failure will be logged. Otherwise, smfi_main will return MI_SUCCESS.</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_opensocket.html b/contrib/sendmail/libmilter/docs/smfi_opensocket.html
new file mode 100644
index 0000000..53ea62b
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_opensocket.html
@@ -0,0 +1,84 @@
+<HTML>
+<HEAD><TITLE>smfi_opensocket</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_opensocket.html,v 1.7 2008/01/31 17:29:33 ca Exp $
+-->
+<H1>smfi_opensocket</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_opensocket(
+ bool rmsocket
+);
+</PRE>
+Attempt to create the interface socket MTAs will use to connect to the
+filter.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from program mainline,
+after calling <TT>smfi_setconn()</TT> and <TT>smfi_register()</TT>,
+but before calling <TT>smfi_main()</TT>.
+</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_opensocket attempts to create the socket specified previously by
+a call to <TT>smfi_setconn()</TT> which will be the interface between MTAs
+and the filter.
+This allows the calling application to ensure that the
+socket can be created.
+If this is not called,
+<TT>smfi_main()</TT> will do so implicitly.
+</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>rmsocket</TD>
+ <TD>A flag indicating whether or not the library should try to
+ remove any existing UNIX domain socket before trying to create
+ a new one.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_opensocket will fail and return MI_FAILURE if:
+<UL>
+ <LI>The interface socket could not be created for any reason.
+ <LI><TT>rmsocket</TT> was <TT>true</TT>, and either the socket could
+ not be examined, or exists and could not be removed.
+ <LI><TT>smfi_setconn()</TT> or <TT>smfi_register()</TT>
+ have not been called.
+</UL>
+Otherwise, it will return MI_SUCCESS
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2003, 2008 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_progress.html b/contrib/sendmail/libmilter/docs/smfi_progress.html
new file mode 100644
index 0000000..801bdf3
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_progress.html
@@ -0,0 +1,68 @@
+<HTML>
+<HEAD><TITLE>smfi_progress</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_progress.html,v 1.5 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_progress</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_progress(
+ SMFICTX *ctx;
+);
+</PRE>
+Notify the MTA that an operation is still in progress.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_progress notifies the MTA that the filter is still working
+on a message, causing the MTA to re-start its timeouts.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_progress will fail and return MI_FAILURE if:
+<UL>
+ <LI>A network error occurs.
+</UL>
+Otherwise, it will return MI_SUCCESS
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_quarantine.html b/contrib/sendmail/libmilter/docs/smfi_quarantine.html
new file mode 100644
index 0000000..656a480
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_quarantine.html
@@ -0,0 +1,73 @@
+<HTML>
+<HEAD><TITLE>smfi_quarantine</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_quarantine.html,v 1.5 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_quarantine</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_quarantine(
+ SMFICTX *ctx;
+ char *reason;
+);
+</PRE>
+Quarantine the message using the given reason.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_quarantine quarantines the message using the given reason.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>reason</TD>
+ <TD>The quarantine reason, a non-NULL and non-empty null-terminated string.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_quarantine will fail and return MI_FAILURE if:
+<UL>
+ <LI>reason is NULL or empty.
+ <LI>A network error occurs.
+ <LI>SMFIF_QUARANTINE was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it will return MI_SUCCESS
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2002-2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_register.html b/contrib/sendmail/libmilter/docs/smfi_register.html
new file mode 100644
index 0000000..1a35918
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_register.html
@@ -0,0 +1,224 @@
+<HTML>
+<HEAD><TITLE>smfi_register</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_register.html,v 1.18 2006/12/20 18:37:11 ca Exp $
+-->
+<H1>smfi_register</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_register(
+ smfiDesc descr
+);
+</PRE>
+Register a set of filter callbacks.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=1>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_register must be called before smfi_main</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_register creates a filter using the information given in the
+smfiDesc argument.
+Multiple (successful) calls to smfi_register within a
+single process are not allowed,
+i.e., only one filter can be successfully registered.
+Note, however, that the library may not check whether this restriction
+is obeyed.
+</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>descr</TD>
+ <TD>
+A filter descriptor of type smfiDesc describing the filter's functions.
+<A NAME="smfiDesc">The structure</A> has the following members:
+<PRE>
+struct smfiDesc
+{
+ char *xxfi_name; /* filter name */
+ int xxfi_version; /* version code -- do not change */
+ unsigned long xxfi_flags; /* <A href="#flags">flags</A> */
+
+ /* connection info filter */
+ sfsistat (*<A href="xxfi_connect.html">xxfi_connect</A>)(SMFICTX *, char *, _SOCK_ADDR *);
+ /* SMTP HELO command filter */
+ sfsistat (*<A href="xxfi_helo.html">xxfi_helo</A>)(SMFICTX *, char *);
+ /* envelope sender filter */
+ sfsistat (*<A href="xxfi_envfrom.html">xxfi_envfrom</A>)(SMFICTX *, char **);
+ /* envelope recipient filter */
+ sfsistat (*<A href="xxfi_envrcpt.html">xxfi_envrcpt</A>)(SMFICTX *, char **);
+ /* header filter */
+ sfsistat (*<A href="xxfi_header.html">xxfi_header</A>)(SMFICTX *, char *, char *);
+ /* end of header */
+ sfsistat (*<A href="xxfi_eoh.html">xxfi_eoh</A>)(SMFICTX *);
+ /* body block */
+ sfsistat (*<A href="xxfi_body.html">xxfi_body</A>)(SMFICTX *, unsigned char *, size_t);
+ /* end of message */
+ sfsistat (*<A href="xxfi_eom.html">xxfi_eom</A>)(SMFICTX *);
+ /* message aborted */
+ sfsistat (*<A href="xxfi_abort.html">xxfi_abort</A>)(SMFICTX *);
+ /* connection cleanup */
+ sfsistat (*<A href="xxfi_close.html">xxfi_close</A>)(SMFICTX *);
+
+ /* any unrecognized or unimplemented command filter */
+ sfsistat (*xxfi_unknown)(SMFICTX *, const char *);
+
+ /* SMTP DATA command filter */
+ sfsistat (*xxfi_data)(SMFICTX *);
+
+ /* negotiation callback */
+ sfsistat (*<A HREF="xxfi_negotiate.html">xxfi_negotiate</A>)(SMFICTX *,
+ unsigned long, unsigned long, unsigned long, unsigned long,
+ unsigned long *, unsigned long *, unsigned long *, unsigned long *);
+};
+</PRE>
+
+A NULL value for any callback function indicates that the filter
+does not wish to process the given type of information,
+simply returning SMFIS_CONTINUE.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>
+smfi_register may return MI_FAILURE for any of the following reasons:
+<UL>
+<LI>memory allocation failed.
+<LI>incompatible version or illegal flags value.
+</UL>
+
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+
+<A NAME="flags">The xxfi_flags</A>
+field should contain the bitwise OR of zero or more of
+the following values, describing the actions the filter may take:
+<TABLE BORDER CELLPADDING="1" cellspacing=1>
+<TR valign="top" bgcolor="#dddddd"><TH align="left">Flag</TH><TH align="center">Description</TH></TR>
+ <TR align="left" valign=top>
+ <TD>
+ SMFIF_ADDHDRS
+ </TD>
+ <TD>
+ This filter may <A HREF="smfi_addheader.html">add headers</A>.
+ </TD>
+ </TR>
+ <TR align="left" valign=top>
+ <TD>
+ SMFIF_CHGHDRS
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_chgheader.html">change and/or delete headers</A>.
+ </TD>
+ </TR>
+ <TR align="left" valign=top>
+ <TD VALIGN="TOP">
+ SMFIF_CHGBODY
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_replacebody.html">replace the body</A> during filtering.
+ This may have significant performance impact
+ if other filters do body filtering after this filter.
+ </TD>
+ </TR>
+ <TR>
+ <TD VALIGN="TOP">
+ SMFIF_ADDRCPT
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_addrcpt.html">add recipients</A>
+ to the message.
+ </TD>
+ </TR>
+ <TR>
+ <TD VALIGN="TOP">
+ SMFIF_ADDRCPT_PAR
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_addrcpt_par.html">add recipients including ESMTP args</A>.
+ </TD>
+ </TR>
+ <TR>
+ <TD VALIGN="TOP">
+ SMFIF_DELRCPT
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_delrcpt.html">remove recipients</A> from the message.
+ </TD>
+ </TR>
+ <TR>
+ <TD VALIGN="TOP">
+ SMFIF_QUARANTINE
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_quarantine.html">quarantine</A> a message.
+ </TD>
+ </TR>
+
+ <TR>
+ <TD VALIGN="TOP">
+ SMFIF_CHGFROM
+ </TD>
+ <TD>
+ This filter may
+ <A HREF="smfi_chgfrom.html">change the envelope sender</A> (MAIL).
+ </TD>
+ </TR>
+
+ <TR>
+ <TD VALIGN="TOP">
+ SMFIF_SETSYMLIST
+ </TD>
+ <TD>
+ This filter can
+ <A HREF="smfi_setsymlist.html">send a set of symbols (macros)</A>
+ that it wants.
+ </TD>
+ </TR>
+
+</TABLE>
+
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2001, 2003, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_replacebody.html b/contrib/sendmail/libmilter/docs/smfi_replacebody.html
new file mode 100644
index 0000000..bc8d5ac
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_replacebody.html
@@ -0,0 +1,93 @@
+<HTML>
+<HEAD><TITLE>smfi_replacebody</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_replacebody.html,v 1.15 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_replacebody</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_replacebody(
+ SMFICTX *ctx,
+ unsigned char *bodyp,
+ int bodylen
+);
+</PRE>
+Replace message-body data.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called only from <A href="xxfi_eom.html">xxfi_eom</A>. smfi_replacebody may be called more than once.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_replacebody replaces the body of the current message. If called
+more than once, subsequent calls result in data being appended to the new
+body.
+</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>bodyp</TD>
+ <TD>A pointer to the start of the new body data, which does not have to be null-terminated. If bodyp is NULL, it is treated as having length == 0. Body data should be in CR/LF form.
+ </TD></TR>
+ <TR valign="top"><TD>bodylen</TD>
+ <TD>The number of data bytes pointed to by bodyp.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_replacebody fails and returns MI_FAILURE if:
+<UL>
+ <LI>bodyp == NULL and bodylen &gt; 0.
+ <LI>Changing the body in the current connection state is invalid.
+ <LI>A network error occurs.
+ <LI>SMFIF_CHGBODY was not set when <A href="smfi_register.html">smfi_register</A> was called.
+</UL>
+Otherwise, it will return MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+<UL>
+ <LI>Since the message body may be very large, setting SMFIF_CHGBODY may significantly affect filter performance.
+ <LI>If a filter sets SMFIF_CHGBODY but does not call smfi_replacebody, the original body remains unchanged.
+ <LI>For smfi_replacebody, filter order is important. <B>Later filters will see the new body contents created by earlier ones.</B>
+</UL>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2001, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setbacklog.html b/contrib/sendmail/libmilter/docs/smfi_setbacklog.html
new file mode 100644
index 0000000..8353cac
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setbacklog.html
@@ -0,0 +1,64 @@
+<HTML>
+<HEAD><TITLE>smfi_setbacklog</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setbacklog.html,v 1.6 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setbacklog</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setbacklog(
+ int obacklog
+);
+</PRE>
+Set the filter's <CODE>listen(2)</CODE> backlog value.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_setbacklog should only be called before <A href="smfi_main.html">smfi_main</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Sets the incoming socket backlog used by <CODE>listen(2)</CODE>.
+If smfi_setbacklog is not called, the operating system default is used.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>obacklog</TD>
+ <TD>The number of incoming connections to allow in the listen queue.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_setbacklog returns MI_FAILURE if obacklog is less than or equal
+to zero.</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2002-2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setconn.html b/contrib/sendmail/libmilter/docs/smfi_setconn.html
new file mode 100644
index 0000000..70a510e
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setconn.html
@@ -0,0 +1,93 @@
+<HTML>
+<HEAD><TITLE>smfi_setconn</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setconn.html,v 1.17 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setconn</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setconn(
+ char *oconn;
+);
+</PRE>
+Set the socket through which this filter should communicate with sendmail.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_setconn must be called once before <A href="smfi_main.html">smfi_main</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Sets the socket through which the filter communicates with sendmail.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>oconn</TD>
+ <TD>The address of the desired communication socket.
+ The address should be a NULL-terminated string in "proto:address"
+ format:
+ <UL>
+ <LI><CODE>{unix|local}:/path/to/file</CODE> -- A named pipe.
+ <LI><CODE>inet:port@{hostname|ip-address}</CODE> -- An IPV4 socket.
+ <LI><CODE>inet6:port@{hostname|ip-address}</CODE> -- An IPV6 socket.
+ </UL>
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_setconn will not fail on an invalid address.
+The failure will only be detected in <A href="smfi_main.html">smfi_main</A>.
+Nevertheless, smfi_setconn may fail for other reasons, e.g.,
+due to a lack of memory.
+</TD>
+</TR>
+
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+
+<TD>
+<UL>
+ <LI>If possible, filters should not run as root when communicating
+ over unix/local domain sockets.
+ <LI>Unix/local sockets should have their permissions set to
+ 0600 (read/write permission only for the socket's owner) or
+ 0660 (read/write permission for the socket's owner and group)
+ which is useful if the sendmail RunAsUser option is used.
+ The permissions for a unix/local domain socket are determined as
+ usual by <CODE>umask</CODE>, which should be set to 007 or 077.
+ Note some operating systems (e.g, Solaris) don't use the
+ permissions of the socket. On those systems, place the socket in a
+ protected directory.
+</UL>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setdbg.html b/contrib/sendmail/libmilter/docs/smfi_setdbg.html
new file mode 100644
index 0000000..e001d3f
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setdbg.html
@@ -0,0 +1,67 @@
+<HTML>
+<HEAD><TITLE>smfi_setdbg</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setdbg.html,v 1.3 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setdbg</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setdbg(
+ int level;
+);
+</PRE>
+Set the debugging (tracing) level for the milter library.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called from any any routine at any time.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>smfi_setdbg sets the milter library's internal debugging level
+to a new level so that code details may be traced.
+A level of zero turns off debugging. The greater
+(more positive) the level the more detailed the debugging. Six is
+the current, highest, useful value.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>level</TD>
+ <TD>The new debugging level
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_setdbg returns MI_SUCCESS by default.
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setmlreply.html b/contrib/sendmail/libmilter/docs/smfi_setmlreply.html
new file mode 100644
index 0000000..b01bacf
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setmlreply.html
@@ -0,0 +1,145 @@
+<HTML>
+<HEAD><TITLE>smfi_setmlreply</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setmlreply.html,v 1.4 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setmlreply</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setmlreply(
+ SMFICTX *ctx,
+ char *rcode,
+ char *xcode,
+ ...
+);
+</PRE>
+Set the default SMTP error reply code to a multi-line response. Only 4XX
+and 5XX replies are accepted.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_setmlreply may be called from any of the xxfi_ callbacks
+other than xxfi_connect.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Directly set the SMTP error reply code for this connection to the given
+lines after the xcode. The list of arguments must be NULL terminated.
+This code will be used on subsequent error replies resulting from actions
+taken by this filter.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>rcode</TD>
+ <TD>The three-digit (RFC 821/2821) SMTP reply code, as a
+ null-terminated string. rcode cannot be NULL, and must be a valid
+ 4XX or 5XX reply code.
+ </TD></TR>
+ <TR valign="top"><TD>xcode</TD>
+ <TD>The extended (RFC 1893/2034) reply code. If xcode is NULL, no
+ extended code is used. Otherwise, xcode must conform to RFC 1893/2034.
+ </TD></TR>
+ <TR valign="top"><TD>...</TD>
+ <TD>The remainder of the arguments are single lines of text, up to
+ 32 arguments, which will be used as the text part of the SMTP
+ reply. The list must be NULL terminated.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Example ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+<TD>
+For example, the code:<BR>
+<PRE>
+ ret = smfi_setmlreply(ctx, "550", "5.7.0",
+ "Spammer access rejected",
+ "Please see our policy at:",
+ "http://www.example.com/spampolicy.html",
+ NULL);
+</PRE>
+<BR>would give the SMTP response:<BR>
+<PRE>
+550-5.7.0 Spammer access rejected
+550-5.7.0 Please see our policy at:
+550 5.7.0 http://www.example.com/spampolicy.html
+</PRE>
+</TD>
+</TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_setmlreply will fail and return MI_FAILURE if:
+<UL>
+ <LI>The rcode or xcode argument is invalid.
+ <LI>A memory-allocation failure occurs.
+ <LI>If any text line contains a carraige return or line feed.
+ <LI>The length of any text line is more than MAXREPLYLEN (980).
+ <LI>More than 32 lines of text replies are given.
+</UL>
+Otherwise, it return MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+<UL>
+<LI>Values passed to smfi_setmlreply are not checked for standards compliance.
+<LI>The message parameter should contain only printable characters,
+other characters may lead to undefined behavior.
+For example, CR or LF will cause the call to fail,
+single '%' characters will cause the text to be ignored
+(if there really should be a '%' in the string,
+use '%%' just like for <TT>printf(3)</TT>).
+<LI>For details about reply codes and their meanings, please see RFC's
+<A href="http://www.rfc-editor.org/rfc/rfc821.txt">821</A>/
+<A href="http://www.rfc-editor.org/rfc/rfc2821.txt">2821</A>
+and
+<A href="http://www.rfc-editor.org/rfc/rfc1893.txt">1893</A>/
+<A href="http://www.rfc-editor.org/rfc/rfc2034.txt">2034</A>.
+<LI>If the reply code (rcode) given is a '4XX' code but SMFI_REJECT is used
+for the message, the custom reply is not used.
+<LI>Similarly, if the reply code (rcode) given is a '5XX' code but
+SMFI_TEMPFAIL is used for the message, the custom reply is not used.
+<BR>
+Note: in neither of the last two cases an error is returned to the milter,
+libmilter silently ignores the reply code.
+<LI>If the milter returns SMFI_TEMPFAIL and sets the reply code to '421',
+then the SMTP server will terminate the SMTP session with a 421 error code.
+</UL>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2002-2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setpriv.html b/contrib/sendmail/libmilter/docs/smfi_setpriv.html
new file mode 100644
index 0000000..1c287eb
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setpriv.html
@@ -0,0 +1,80 @@
+<HTML>
+<HEAD><TITLE>smfi_setpriv</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setpriv.html,v 1.11 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setpriv</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setpriv(
+ SMFICTX *ctx,
+ void *privatedata
+);
+</PRE>
+Set the private data pointer for this connection.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_setpriv may be called in any of the xxfi_* callbacks.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Sets the private data pointer for the context ctx.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>privatedata</TD>
+ <TD>Pointer to private data. This value will be returned by subsequent calls to <A href="smfi_getpriv.html">smfi_getpriv</A> using ctx.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_setpriv returns MI_FAILURE if ctx is an invalid context.
+Otherwise, it returns MI_SUCCESS.</TD>
+</TR>
+
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+
+<TD>There is only one private data pointer per connection; multiple
+calls to smfi_setpriv with different values will cause previous values
+to be lost.
+<P>
+Before a filter terminates it should release the private data
+and set the pointer to NULL.
+</TD>
+
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2001, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setreply.html b/contrib/sendmail/libmilter/docs/smfi_setreply.html
new file mode 100644
index 0000000..d857815
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setreply.html
@@ -0,0 +1,117 @@
+<HTML>
+<HEAD><TITLE>smfi_setreply</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setreply.html,v 1.17 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setreply</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setreply(
+ SMFICTX *ctx,
+ char *rcode,
+ char *xcode,
+ char *message
+);
+</PRE>
+Set the default SMTP error reply code. Only 4XX and 5XX replies are accepted.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_setreply may be called from any of the xxfi_ callbacks
+other than xxfi_connect.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Directly set the SMTP error reply code for this connection. This code
+will be used on subsequent error replies resulting from actions taken by
+this filter.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>rcode</TD>
+ <TD>The three-digit (RFC 821/2821) SMTP reply code, as a
+ null-terminated string. rcode cannot be NULL, and must be a valid
+ 4XX or 5XX reply code.
+ </TD></TR>
+ <TR valign="top"><TD>xcode</TD>
+ <TD>The extended (RFC 1893/2034) reply code. If xcode is NULL, no
+ extended code is used. Otherwise, xcode must conform to RFC 1893/2034.
+ </TD></TR>
+ <TR valign="top"><TD>message</TD>
+ <TD>The text part of the SMTP reply. If message is NULL, an empty message is used.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_setreply will fail and return MI_FAILURE if:
+<UL>
+ <LI>The rcode or xcode argument is invalid.
+ <LI>A memory-allocation failure occurs.
+</UL>
+Otherwise, it return MI_SUCCESS.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>
+<UL>
+<LI>Values passed to smfi_setreply are not checked for standards compliance.
+<LI>The message parameter should contain only printable characters,
+other characters may lead to undefined behavior.
+For example, CR or LF will cause the call to fail,
+single '%' characters will cause the text to be ignored
+(if there really should be a '%' in the string,
+use '%%' just like for <TT>printf(3)</TT>).
+<LI>For details about reply codes and their meanings, please see RFC's
+<A href="http://www.rfc-editor.org/rfc/rfc821.txt">821</A>/
+<A href="http://www.rfc-editor.org/rfc/rfc2821.txt">2821</A>
+and
+<A href="http://www.rfc-editor.org/rfc/rfc1893.txt">1893</A>/
+<A href="http://www.rfc-editor.org/rfc/rfc2034.txt">2034</A>.
+<LI>If the reply code (rcode) given is a '4XX' code but SMFI_REJECT is used
+for the message, the custom reply is not used.
+<LI>Similarly, if the reply code (rcode) given is a '5XX' code but
+SMFI_TEMPFAIL is used for the message, the custom reply is not used.
+<BR>
+Note: in neither of the last two cases an error is returned to the milter,
+libmilter silently ignores the reply code.
+<LI>If the milter returns SMFI_TEMPFAIL and sets the reply code to '421',
+then the SMTP server will terminate the SMTP session with a 421 error code.
+</UL>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2002-2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_setsymlist.html b/contrib/sendmail/libmilter/docs/smfi_setsymlist.html
new file mode 100644
index 0000000..7e8edff
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_setsymlist.html
@@ -0,0 +1,107 @@
+<HTML>
+<HEAD><TITLE>smfi_setsymlist</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_setsymlist.html,v 1.5 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_setsymlist</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_setsymlist(
+ SMFICTX *ctx,
+ int stage,
+ char *macros
+);
+</PRE>
+Set the list of macros that the milter wants to receive from the MTA
+for a protocol stage.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>This function must only be called during
+<A HREF="xxfi_negotiate.html">xxfi_negotiate()</A>.
+</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>This function can be used to override the list of macros that the
+milter wants to receive from the MTA.
+</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+
+ <TR><TD>ctx</TD>
+ <TD>the opaque context structure.
+ </TD></TR>
+
+ <TR><TD>stage</TD>
+ <TD>the protocol stage during which the macro list should be used.
+ See the file
+ <CODE>include/libmilter/mfapi.h</CODE> for legal values,
+ look for the C macros with the prefix
+ <CODE>SMFIM_</CODE>.
+ Available protocol stages are at least
+ the initial connection, HELO/EHLO, MAIL, RCPT, DATA,
+ end of header, and
+ the end of a message.
+ </TD></TR>
+
+ <TR><TD>macros</TD>
+ <TD>list of macros (separated by space).
+ Example: "{rcpt_mailer} {rcpt_host}"
+ </TD></TR>
+
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>MI_FAILURE is returned if
+<UL>
+<LI>there is not enough free memory to make a copy of the macro list,
+<LI><CODE>macros</CODE> is <CODE>NULL</CODE> or empty,
+<LI><CODE>stage</CODE> is not a valid protocol stage,
+<LI>the macro list for
+<CODE>stage</CODE> has been set before.
+</UL>
+Otherwise MI_SUCCESS is returned.
+</TD>
+</TR>
+
+<!----------- Notes ---------->
+<TR align="left" valign=top>
+<TH>NOTES</TH>
+<TD>There is an internal limit on the number of macros that can be
+set (currently 5),
+however, this limit is not enforced by libmilter, only by the MTA,
+but a possible violation of this restriction is not communicated back to
+the milter.</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_settimeout.html b/contrib/sendmail/libmilter/docs/smfi_settimeout.html
new file mode 100644
index 0000000..97d41cb
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_settimeout.html
@@ -0,0 +1,66 @@
+<HTML>
+<HEAD><TITLE>smfi_settimeout</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_settimeout.html,v 1.14 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_settimeout</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_settimeout(
+ int otimeout
+);
+</PRE>
+Set the filter's I/O timeout value.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>smfi_settimeout should only be called before <A href="smfi_main.html">smfi_main</A>.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>Sets the number of seconds libmilter will wait
+for an MTA communication (read or write) before timing out.
+If smfi_settimeout is not called, a default timeout of 7210 seconds is used.
+</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>otimeout</TD>
+ <TD>The number of seconds to wait before timing out (&gt; 0).
+ Zero means no wait, <B>not</B> "wait forever".
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_settimeout always returns MI_SUCCESS.</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2002-2003, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_stop.html b/contrib/sendmail/libmilter/docs/smfi_stop.html
new file mode 100644
index 0000000..87ecdb2
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_stop.html
@@ -0,0 +1,74 @@
+<HTML>
+<HEAD><TITLE>smfi_stop</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_stop.html,v 1.6 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>smfi_stop</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_stop(void);
+</PRE>
+Shutdown the milter.
+No connections will be accepted after this call.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>Called from any of the <A href="api.html#Callbacks">Callback</A> routines
+or any error-handling routines at any time.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH width="80">Effects</TH>
+<TD>The smfi_stop routine prevents that new connections
+will be accepted,
+however, it does not wait for existing connections (threads) to terminate.
+It will cause
+<A href="smfi_main.html">smfi_main</A> to return to the calling program,
+which may then exit or warm-restart.
+</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>void</TD>
+ <TD>Takes no arguement.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>RETURN VALUES</TH>
+
+<TD>smfi_stop always returns SMFI_CONTINUE. But note:
+<UL>
+ <LI>Another internal routine may already have asked the milter to abort.
+ <LI>Another routine may already have asked the milter to stop.
+ <LI>There is no way to cancel the stop process once it has begun.
+</UL>
+</TD>
+</TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2003, 2005 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/smfi_version.html b/contrib/sendmail/libmilter/docs/smfi_version.html
new file mode 100644
index 0000000..6dd451d
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/smfi_version.html
@@ -0,0 +1,92 @@
+<HTML>
+<HEAD><TITLE>smfi_version()</TITLE></HEAD>
+<BODY>
+<!--
+$Id: smfi_version.html,v 1.6 2008/05/02 23:06:26 ca Exp $
+-->
+<H1>smfi_version()</H1>
+
+<TABLE BORDER="0" CELLSPACING=4 CELLPADDING=4>
+<!---------- Synopsis ----------->
+<TR><TH VALIGN="TOP" ALIGN=LEFT WIDTH=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+int smfi_version(
+ unsigned int *pmajor,
+ unsigned int *pminor,
+ unsigned int *ppl
+);
+</PRE>
+Get the (runtime) version of libmilter.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH VALIGN="TOP" ALIGN=LEFT>DESCRIPTION</TH><TD>
+<TABLE BORDER="1" CELLSPACING=1 CELLPADDING=4>
+<TR ALIGN="LEFT" VALIGN=TOP>
+<TH WIDTH="80">Called When</TH>
+<TD>smfi_version may be called at any time.</TD>
+</TR>
+<TR ALIGN="LEFT" VALIGN=TOP>
+<TH WIDTH="80">Effects</TH>
+<TD>None.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH VALIGN="TOP" ALIGN=LEFT>ARGUMENTS</TH><TD>
+ <TABLE BORDER="1" CELLSPACING=0>
+ <TR BGCOLOR="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR VALIGN="TOP"><TD>pmajor</TD>
+ <TD>Pointer to an unsigned int variable to store major version number.
+ </TD></TR>
+ <TR VALIGN="TOP"><TD>pminor</TD>
+ <TD>Pointer to an unsigned int variable to store minor version number.
+ </TD></TR>
+ <TR VALIGN="TOP"><TD>ppl</TD>
+ <TD>Pointer to an unsigned int variable to store patch level number.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH VALIGN="TOP" ALIGN=LEFT>RETURN VALUES</TH>
+<TD>smfi_version returns MI_SUCCESS.</TD>
+</TR>
+
+</TABLE>
+
+Note: the compile time version of libmilter is available in the macro
+<CODE>SMFI_VERSION</CODE>.
+To extract the major and minor version as well as the current patch level
+from this macro, the macros
+<CODE>SM_LM_VRS_MAJOR(v)</CODE>,
+<CODE>SM_LM_VRS_MINOR(v)</CODE>, and
+<CODE>SM_LM_VRS_PLVL(v)</CODE>
+can be used, respectively.
+A milter can check the
+<CODE>SMFI_VERSION</CODE>
+macro to determine which functions to use
+(at compile time via C preprocessor statements).
+Using this macro and the
+<CODE>smfi_version()</CODE>
+function,
+a milter can determine at runtime whether it has been (dynamically)
+linked against the expected libmilter version.
+Such a function should only compare the major and minor version,
+not the patch level,
+i.e., the libmilter library will be compatible despite
+different patch levels.
+
+
+<HR SIZE="1">
+<FONT SIZE="-1">
+Copyright (c) 2006-2008 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_abort.html b/contrib/sendmail/libmilter/docs/xxfi_abort.html
new file mode 100644
index 0000000..0664dc1
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_abort.html
@@ -0,0 +1,83 @@
+<HTML>
+<HEAD><TITLE>xxfi_abort</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_abort.html,v 1.12 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>xxfi_abort</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_abort)(
+ SMFICTX *ctx
+);
+</PRE>
+Handle the current message's being aborted.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_abort may be called at any time during message processing (i.e. between some message-oriented routine and <A href="xxfi_eom.html">xxfi_eom</A>).</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>
+<UL>
+<LI>xxfi_abort must reclaim any resources allocated on a per-message
+basis, and must be tolerant of being called between any two
+message-oriented callbacks.
+
+<LI>Calls to xxfi_abort and <A href="xxfi_eom.html">xxfi_eom</A> are
+mutually exclusive.
+
+<LI>xxfi_abort is not responsible for reclaiming connection-specific
+data, since <A href="xxfi_close.html">xxfi_close</A> is always called
+when a connection is closed.
+
+<LI>Since the current message is already being aborted, the return
+value is currently ignored.
+
+<LI>xxfi_abort is only called if the message is aborted outside the
+filter's control <B>and</B> the filter has not completed its
+message-oriented processing. For example, if a filter has already
+returned SMFIS_ACCEPT, SMFIS_REJECT, or SMFIS_DISCARD from a
+message-oriented routine, xxfi_abort will not be called even if the
+message is later aborted outside its control.
+</UL>
+</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_body.html b/contrib/sendmail/libmilter/docs/xxfi_body.html
new file mode 100644
index 0000000..0a5f0f3
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_body.html
@@ -0,0 +1,97 @@
+<HTML>
+<HEAD><TITLE>xxfi_body</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_body.html,v 1.17 2007/03/26 20:12:46 ca Exp $
+-->
+<H1>xxfi_body</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_body)(
+ SMFICTX *ctx,
+ unsigned char *bodyp,
+ size_t len
+);
+</PRE>
+Handle a piece of a message's body.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_body is called zero or more times between xxfi_eoh and xxfi_eom.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>bodyp</TD>
+ <TD>Pointer to the start of this block of body data. bodyp is not valid outside this call to xxfi_body.
+ </TD></TR>
+ <TR valign="top"><TD>len</TD>
+ <TD>The amount of data pointed to by bodyp.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>
+<UL>
+<LI>bodyp points to a sequence of bytes.
+It is <EM>not</EM> a C string (a sequence of characters that is terminated by '\0').
+Therefore, do not use the usual C string functions like <CODE>strlen(3)</CODE>
+on this byte block.
+Moreover, the byte sequence may contain '\0' characters inside the block.
+Hence even if a trailing '\0' is added, C string functions may still fail
+to work as expected.
+<LI>Since message bodies can be very large, defining xxfi_body can
+significantly impact filter performance.
+<LI>End-of-lines are represented as received from SMTP (normally CR/LF).
+<LI>Later filters will see body changes made by earlier ones.
+<LI>Message bodies may be sent in multiple chunks, with one call to
+ xxfi_body per chunk.
+<LI>Return
+<A HREF="api.html#SMFIS_SKIP">SMFIS_SKIP</A>
+if a milter has received sufficiently many
+body chunks to make a decision,
+but still wants to invoke
+message modification functions that are only allowed to be called from
+<A HREF="xxfi_eom.html">xxfi_eom()</A>.
+Note: the milter <EM>must</EM>
+<A HREF="xxfi_negotiate.html">negotiate</A>
+this behavior with the MTA, i.e., it must check whether
+the protocol action
+<A HREF="xxfi_negotiate.html#SMFIP_SKIP"><CODE>SMFIP_SKIP</CODE></A>
+is available and if so, the milter must request it.
+</UL>
+</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2003, 2007 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_close.html b/contrib/sendmail/libmilter/docs/xxfi_close.html
new file mode 100644
index 0000000..2c2ae77
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_close.html
@@ -0,0 +1,81 @@
+<HTML>
+<HEAD><TITLE>xxfi_close</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_close.html,v 1.13 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>xxfi_close</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_close)(
+ SMFICTX *ctx
+);
+</PRE>
+The current connection is being closed.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_close is always called once at the end of each connection.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>
+<UL>
+<LI>xxfi_close may be called "out-of-order", i.e. before even the
+xxfi_connect is called.
+After a connection is established by the MTA to the filter,
+if the MTA decides this connection's traffic will be discarded
+(e.g. via an access_db result), no data will be passed to the
+filter from the MTA until the client closes down.
+At that time, xxfi_close is called.
+It can therefore be the only callback ever used for a given connection,
+and developers should anticipate this possibility when crafting their
+xxfi_close code.
+In particular, it is incorrect to assume the private context pointer
+will be something other than NULL in this callback.
+<LI>xxfi_close is called on close even if the previous mail
+transaction was aborted.
+<LI>xxfi_close is responsible for freeing any resources allocated on a
+per-connection basis.
+<LI>Since the connection is already closing, the return value is
+currently ignored.
+</UL>
+</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003, 2004 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_connect.html b/contrib/sendmail/libmilter/docs/xxfi_connect.html
new file mode 100644
index 0000000..87d5eeb
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_connect.html
@@ -0,0 +1,121 @@
+<HTML>
+<HEAD><TITLE>xxfi_connect</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_connect.html,v 1.19 2007/01/15 22:24:45 ca Exp $
+-->
+<H1>xxfi_connect</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_connect)(
+ SMFICTX *ctx,
+ char *hostname,
+ _SOCK_ADDR *hostaddr);
+</PRE>
+</TD></TR>
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR>
+<TH valign="top" align=left width=80>Called When</TH>
+<TD>Once, at the start of each SMTP connection.</TD>
+</TR>
+<TR>
+<TH valign="top" align=left width=80>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+<!--
+This callback function is invoked on each connection to the mail
+filter program.
+The callback is to be implemented by the Milter application developers.
+The name of the callback can be any valid function name.
+The function pointer is to be assigned to the
+smfiDesc.xxfi_connect and the pointer to the smfiDesc structure
+is passed to smfi_register().
+</TD></TR>
+-->
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR><TD>ctx</TD>
+ <TD>the opaque context structure.
+ </TD></TR>
+ <TR><TD>hostname</TD>
+ <TD>the host name of the message sender, as determined by a
+ reverse lookup on the host address.
+ If the reverse lookup fails
+ or if none of the IP addresses of the resolved host name
+ matches the original IP address,
+ hostname will contain the message sender's IP
+ address enclosed in square brackets (e.g. `[a.b.c.d]').
+ If the SMTP connection is made via stdin the value is
+ <CODE>localhost</CODE>.
+ </TD></TR>
+ <TR><TD>hostaddr</TD>
+ <TD>the host address,
+ as determined by a <CODE>getpeername(2)</CODE> call on the SMTP socket.
+ NULL if the type is not supported in the current version or if
+ the SMTP connection is made via stdin.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+<!----------- Return values ---------->
+<!--
+<TR>
+<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
+<TD><TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR valign="top">
+ <TD>SMFIS_ACCEPT</TD>
+ <TD>Accept all commands and messages from this client without any
+ further contact with the filter. </TD>
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_CONTINUE</TD>
+ <TD>Continue normal processing. </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_DISCARD</TD>
+ <TD>Undefined behaviour; do not use. </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_TEMPFAIL</TD>
+ <TD>Reject all commands and messages from this client with a
+ temporary failure reply code.
+ If also used in conjunction with <CODE>smfi_setreply()</CODE>
+ to set a reply whose SMTP code is 421,
+ the MTA will drop the connection immediately. </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_REJECT</TD>
+ <TD>Reject all commands and messages from this client with a
+ permanent failure reply code. </TD>
+ </TR>
+</TABLE>
+</TR>
+-->
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>If an earlier filter rejects the connection in its xxfi_connect()
+routine, this filter's xxfi_connect() will not be called.</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000-2001, 2003, 2007 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_data.html b/contrib/sendmail/libmilter/docs/xxfi_data.html
new file mode 100644
index 0000000..2633ee5
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_data.html
@@ -0,0 +1,89 @@
+<HTML>
+<HEAD><TITLE>xxfi_data</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_data.html,v 1.4 2007/01/25 01:00:20 ca Exp $
+-->
+<H1>xxfi_data</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_data)(
+ SMFICTX *ctx
+);
+</PRE>
+Handle the DATA command.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_data is called when the client uses the DATA command.
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
+<TD><TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR valign="top">
+ <TD>SMFIS_TEMPFAIL</TD>
+ <TD>Reject this message with a temporary error.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_REJECT</TD>
+ <TD>Reject this message.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_DISCARD</TD>
+ <TD>Accept and silently discard this message.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_ACCEPT</TD>
+ <TD>Accept this message.
+ </TD>
+ </TR>
+</TABLE>
+</TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>For more details on ESMTP responses, please see RFC
+<A href="http://www.rfc-editor.org/rfc/rfc1869.txt">1869</A>.</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_envfrom.html b/contrib/sendmail/libmilter/docs/xxfi_envfrom.html
new file mode 100644
index 0000000..6ae88cf
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_envfrom.html
@@ -0,0 +1,97 @@
+<HTML>
+<HEAD><TITLE>xxfi_envfrom</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_envfrom.html,v 1.14 2007/01/25 01:00:20 ca Exp $
+-->
+<H1>xxfi_envfrom</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_envfrom)(
+ SMFICTX *ctx,
+ char **argv
+);
+</PRE>
+Handle the MAIL (envelope sender) command.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_envfrom is called once at the beginning of each message
+(MAIL command),
+before xxfi_envrcpt.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>argv</TD>
+ <TD>Null-terminated SMTP command arguments;
+ argv[0] is guaranteed to be the sender address.
+ Later arguments are the ESMTP arguments.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
+<TD><TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR valign="top">
+ <TD>SMFIS_TEMPFAIL</TD>
+ <TD>Reject this sender and message with a temporary error; a new sender (and hence a new message) may subsequently be specified. <A href="xxfi_abort.html">xxfi_abort</A> is not called.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_REJECT</TD>
+ <TD>Reject this sender and message; a new sender/message may be specified. <A href="xxfi_abort.html">xxfi_abort</A> is not called.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_DISCARD</TD>
+ <TD>Accept and silently discard this message. <A href="xxfi_abort.html">xxfi_abort</A> is not called.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_ACCEPT</TD>
+ <TD>Accept this message. <A href="xxfi_abort.html">xxfi_abort</A> is not called.
+ </TD>
+ </TR>
+</TABLE>
+</TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>For more details on ESMTP responses, please see RFC
+<A href="http://www.rfc-editor.org/rfc/rfc1869.txt">1869</A>.</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_envrcpt.html b/contrib/sendmail/libmilter/docs/xxfi_envrcpt.html
new file mode 100644
index 0000000..9fb4ce8
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_envrcpt.html
@@ -0,0 +1,97 @@
+<HTML>
+<HEAD><TITLE>xxfi_envrcpt</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_envrcpt.html,v 1.15 2007/01/25 01:00:20 ca Exp $
+-->
+<H1>xxfi_envrcpt</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_envrcpt)(
+ SMFICTX *ctx,
+ char **argv
+);
+</PRE>
+Handle the envelope RCPT command.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_envrcpt is called once per recipient, hence one or more times per message, immediately after xxfi_envfrom.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>argv</TD>
+ <TD>Null-terminated SMTP command arguments;
+ argv[0] is guaranteed to be the recipient address.
+ Later arguments are the ESMTP arguments.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
+<TD><TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR valign="top">
+ <TD>SMFIS_TEMPFAIL</TD>
+ <TD>Temporarily fail for this particular recipient; further recipients
+ may still be sent. <A href="xxfi_abort.html">xxfi_abort</A> is not called.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_REJECT</TD>
+ <TD>Reject this particular recipient; further recipients may still be sent.
+ <A href="xxfi_abort.html">xxfi_abort</A> is not called.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_DISCARD</TD>
+ <TD>Accept and discard the message. <A href="xxfi_abort.html">xxfi_abort</A> will be called.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_ACCEPT</TD>
+ <TD>Accept recipient. <A href="xxfi_abort.html">xxfi_abort</A> will not be called.
+ </TD>
+ </TR>
+</TABLE>
+</TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>For more details on ESMTP responses, please see RFC
+<A href="http://www.rfc-editor.org/rfc/rfc1869.txt">1869</A>.</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_eoh.html b/contrib/sendmail/libmilter/docs/xxfi_eoh.html
new file mode 100644
index 0000000..2a74e7a
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_eoh.html
@@ -0,0 +1,56 @@
+<HTML>
+<HEAD><TITLE>xxfi_eoh</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_eoh.html,v 1.11 2006/12/21 18:30:35 ca Exp $
+-->
+<H1>xxfi_eoh</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_eoh)(
+ SMFICTX *ctx
+);
+</PRE>
+Handle the end of message headers.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_eoh is called once after all headers have been sent and processed.
+</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_eom.html b/contrib/sendmail/libmilter/docs/xxfi_eom.html
new file mode 100644
index 0000000..b5aee8b
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_eom.html
@@ -0,0 +1,62 @@
+<HTML>
+<HEAD><TITLE>xxfi_eom</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_eom.html,v 1.12 2006/12/21 18:30:36 ca Exp $
+-->
+<H1>xxfi_eom</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_eom)(
+ SMFICTX *ctx
+);
+</PRE>
+End of a message.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_eom is called once after all calls to <A href="xxfi_body.html">xxfi_body</A> for a given message.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>A filter is required to make all its modifications to the message headers, body, and envelope in xxfi_eom.
+Modifications are made via the smfi_* routines.
+</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_header.html b/contrib/sendmail/libmilter/docs/xxfi_header.html
new file mode 100644
index 0000000..8a5462f
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_header.html
@@ -0,0 +1,111 @@
+<HTML>
+<HEAD><TITLE>xxfi_header</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_header.html,v 1.17 2006/12/21 18:30:36 ca Exp $
+-->
+<H1>xxfi_header</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_header)(
+ SMFICTX *ctx,
+ char *headerf,
+ char *headerv
+);
+</PRE>
+Handle a message header.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_header is called once for each message header.</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>headerf</TD>
+ <TD> Header field name.
+ </TD></TR>
+ <TR valign="top"><TD>headerv</TD>
+ <TD>Header field value.
+ The content of the header may include folded white space,
+ i.e., multiple lines with following white space
+ where lines are separated by LF (not CR/LF).
+ The trailing line terminator (CR/LF) is removed.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>
+<UL>
+<LI>Starting with sendmail 8.14, spaces after the colon in a header
+field are preserved if requested using the flag
+<A HREF="xxfi_negotiate.html#SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>.
+That is, the header
+
+<PRE>
+From: sender &lt;f@example.com&gt;
+To: user &lt;t@example.com&gt;
+Subject:no
+</PRE>
+
+will be sent to a milter as
+
+<PRE>
+"From", " sender &lt;f@example.com&gt;"
+"To", " user &lt;t@example.com&gt;"
+"Subject", "no"
+</PRE>
+
+while previously
+(or without the flag
+<A HREF="xxfi_negotiate.html#SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>)
+it was:
+
+<PRE>
+"From", "sender &lt;f@example.com&gt;"
+"To", "user &lt;t@example.com&gt;"
+"Subject", "no"
+</PRE>
+
+
+<LI>Later filters will see header changes/additions made by earlier ones.
+<LI>For much more detail about header format, please see
+RFC <A href="http://www.rfc-editor.org/rfc/rfc822.html">822</A>
+and
+RFC <A href="http://www.rfc-editor.org/rfc/rfc2822.html">2822</A>
+</UL>
+</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003, 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_helo.html b/contrib/sendmail/libmilter/docs/xxfi_helo.html
new file mode 100644
index 0000000..613cb19
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_helo.html
@@ -0,0 +1,64 @@
+<HTML>
+<HEAD><TITLE>xxfi_helo</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_helo.html,v 1.12 2006/12/21 18:30:36 ca Exp $
+-->
+<H1>xxfi_helo</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_helo)(
+ SMFICTX *ctx,
+ char *helohost
+);
+</PRE>
+Handle the HELO/EHLO command.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH> <TD>xxfi_helo is called whenever the client
+sends a HELO/EHLO command.
+It may therefore be called several times or even not at all;
+some restrictions can be imposed by the MTA configuration.
+</TD>
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>helohost</TD>
+ <TD>Value passed to HELO/EHLO command, which should be
+ the domain name of the sending host (but is, in practice,
+ anything the sending host wants to send).
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2000, 2003, 2005 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_negotiate.html b/contrib/sendmail/libmilter/docs/xxfi_negotiate.html
new file mode 100644
index 0000000..0f69f70
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_negotiate.html
@@ -0,0 +1,277 @@
+<HTML>
+<HEAD><TITLE>xxfi_negotiate</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_negotiate.html,v 1.23 2006/12/20 18:57:08 ca Exp $
+-->
+<H1>xxfi_negotiate</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+#include &lt;libmilter/mfdef.h&gt;
+sfsistat (*xxfi_negotiate)(
+ SMFICTX *ctx,
+ unsigned long f0,
+ unsigned long f1,
+ unsigned long f2,
+ unsigned long f3,
+ unsigned long *pf0,
+ unsigned long *pf1,
+ unsigned long *pf2,
+ unsigned long *pf3);
+</PRE>
+</TD></TR>
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR>
+<TH valign="top" align=left width=80>Called When</TH>
+<TD>Once, at the start of each SMTP connection.</TD>
+</TR>
+<TR>
+<TH valign="top" align=left width=80>Default Behavior</TH>
+<TD>Return SMFIS_ALL_OPTS to change nothing.</TD>
+</TR>
+</TABLE>
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR><TD>ctx</TD>
+ <TD>the opaque context structure.
+ </TD></TR>
+ <TR><TD>f0</TD>
+ <TD>the actions offered by the MTA.
+ </TD></TR>
+ <TR><TD>f1</TD>
+ <TD>the protocol steps offered by the MTA.
+ <TR><TD>f2</TD>
+ <TD>for future extensions.
+ </TD></TR>
+ <TR><TD>f3</TD>
+ <TD>for future extensions.
+ </TD></TR>
+ <TR><TD>pf0</TD>
+ <TD>the actions requested by the milter.
+ </TD></TR>
+ <TR><TD>pf1</TD>
+ <TD>the protocol steps requested by the milter.
+ <TR><TD>pf2</TD>
+ <TD>for future extensions.
+ </TD></TR>
+ <TR><TD>pf3</TD>
+ <TD>for future extensions.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
+<TD><TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR valign="top">
+ <TD>SMFIS_ALL_OPTS</TD>
+ <TD>
+If a milter just wants to inspect the available protocol steps
+and actions, then it can return
+SMFIS_ALL_OPTS
+and the MTA will make all protocol steps and actions available
+to the milter.
+In this case, no values should be assigned to the output parameters
+<CODE>pf0</CODE> - <CODE>pf3</CODE>
+as they will be ignored.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_REJECT</TD>
+ <TD>Milter startup fails and it will not be contacted again
+(for the current connection).
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_CONTINUE</TD>
+ <TD>Continue processing.
+ In this case the milter <EM>must</EM> set all output parameters
+ <CODE>pf0</CODE> - <CODE>pf3</CODE>.
+ See below for an explanation how to set those output parameters.
+ </TD>
+ </TR>
+</TABLE>
+</TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>This function allows a milter to dynamically determine and
+request operations and actions during startup.
+In previous versions, the actions (f0) were fixed in the
+<A HREF="smfi_register.html#flags">flags</A> field of the
+<A HREF="smfi_register.html#smfiDesc">smfiDesc</A>
+structure
+and the protocol steps (f1) were implicitly derived by checking whether
+a callback was defined.
+Due to the extensions in the new milter version,
+such a static selection will not work if a milter requires
+new actions that are not available when talking to an older MTA.
+Hence in the negotiation callback a milter can determine
+which operations are available and dynamically select
+those which it needs and which are offered.
+If some operations are not available, the milter may either fall back
+to an older mode or abort the session and ask the user to upgrade.
+
+<!--
+<P>
+The protocol steps are defined in
+<CODE>include/libmilter/mfdef.h</CODE>:
+the macros start with
+<CODE>SMFIP_</CODE>.
+-->
+
+<P>
+Protocol steps
+(<CODE>f1</CODE>, <CODE>*pf1</CODE>):
+<UL>
+<LI><A NAME="SMFIP_RCPT_REJ"><CODE>SMFIP_RCPT_REJ</CODE></A>:
+By setting this bit, a milter can request that the
+MTA should also send <CODE>RCPT</CODE> commands that have been rejected
+because the user is unknown (or similar reasons), but not those
+which have been rejected because of syntax errors etc.
+If a milter requests this protocol step,
+then it should check the macro
+<CODE>{rcpt_mailer}</CODE>:
+if that is set to
+<CODE>error</CODE>,
+then the recipient will be rejected by the MTA.
+Usually the macros
+<CODE>{rcpt_host}</CODE> and <CODE>{rcpt_addr}</CODE>
+will contain an enhanced status code and an error text
+in that case, respectively.
+
+<LI><A NAME="SMFIP_SKIP"><CODE>SMFIP_SKIP</CODE></A>
+indicates that the MTA understand the
+<A HREF="api.html#SMFIS_SKIP">SMFIS_SKIP</A>
+return code.
+
+<LI><A NAME="SMFIP_NR_"><CODE>SMFIP_NR_*</CODE></A>
+indicates that the MTA understand the
+<A HREF="api.html#SMFIS_NOREPLY">SMFIS_NOREPLY</A>
+return code.
+There are flags for various protocol stages:
+
+<UL>
+
+<LI><A NAME="SMFIP_NR_CONN"><CODE>SMFIP_NR_CONN</CODE></A>:
+<A HREF="xxfi_connect.html">xxfi_connect()</A>
+
+<LI><A NAME="SMFIP_NR_HELO"><CODE>SMFIP_NR_HELO</CODE></A>:
+<A HREF="xxfi_helo.html">xxfi_helo()</A>
+
+<LI><A NAME="SMFIP_NR_MAIL"><CODE>SMFIP_NR_MAIL</CODE></A>:
+<A HREF="xxfi_envfrom.html">xxfi_envfrom()</A>
+
+<LI><A NAME="SMFIP_NR_RCPT"><CODE>SMFIP_NR_RCPT</CODE></A>:
+<A HREF="xxfi_envrcpt.html">xxfi_envrcpt()</A>
+
+<LI><A NAME="SMFIP_NR_DATA"><CODE>SMFIP_NR_DATA</CODE></A>:
+<A HREF="xxfi_data.html">xxfi_data()</A>
+
+<LI><A NAME="SMFIP_NR_UNKN"><CODE>SMFIP_NR_UNKN</CODE></A>:
+<A HREF="xxfi_unknown.html">xxfi_unknown()</A>
+
+<LI><A NAME="SMFIP_NR_EOH"><CODE>SMFIP_NR_EOH</CODE></A>:
+<A HREF="xxfi_eoh.html">xxfi_eoh()</A>
+
+<LI><A NAME="SMFIP_NR_BODY"><CODE>SMFIP_NR_BODY</CODE></A>:
+<A HREF="xxfi_body.html">xxfi_body()</A>
+
+<LI><A NAME="SMFIP_NR_HDR"><CODE>SMFIP_NR_HDR</CODE></A>:
+<A HREF="xxfi_header.html">xxfi_header()</A>
+
+</UL>
+
+<LI><A NAME="SMFIP_HDR_LEADSPC"><CODE>SMFIP_HDR_LEADSPC</CODE></A>
+indicates that the MTA can send header values with leading space intact.
+If this protocol step is requested, then the MTA will also not add a leading
+space to headers when they are added, inserted, or changed.
+
+<!--
+:'a,.s;^#define \(SMFIP_NO[A-Z]*\)[ ].*;<LI><A NAME="\1"><CODE>\1</CODE></A>:;
+-->
+<LI>The MTA can be instructed not to send information about
+various SMTP stages, these flags start with:
+<A NAME="SMFIP_NO"><CODE>SMFIP_NO*</CODE></A>.
+<UL>
+<LI><A NAME="SMFIP_NOCONNECT"><CODE>SMFIP_NOCONNECT</CODE></A>:
+<A HREF="xxfi_connect.html">xxfi_connect()</A>
+<LI><A NAME="SMFIP_NOHELO"><CODE>SMFIP_NOHELO</CODE></A>:
+<A HREF="xxfi_header.html">xxfi_header()</A>
+<LI><A NAME="SMFIP_NOMAIL"><CODE>SMFIP_NOMAIL</CODE></A>:
+<A HREF="xxfi_envfrom.html">xxfi_envfrom()</A>
+<LI><A NAME="SMFIP_NORCPT"><CODE>SMFIP_NORCPT</CODE></A>:
+<A HREF="xxfi_envrcpt.html">xxfi_envrcpt()</A>
+<LI><A NAME="SMFIP_NOBODY"><CODE>SMFIP_NOBODY</CODE></A>:
+<A HREF="xxfi_body.html">xxfi_body()</A>
+<LI><A NAME="SMFIP_NOHDRS"><CODE>SMFIP_NOHDRS</CODE></A>:
+<A HREF="xxfi_header.html">xxfi_header()</A>
+<LI><A NAME="SMFIP_NOEOH"><CODE>SMFIP_NOEOH</CODE></A>:
+<A HREF="xxfi_eoh.html">xxfi_eoh()</A>
+<LI><A NAME="SMFIP_NOUNKNOWN"><CODE>SMFIP_NOUNKNOWN</CODE></A>:
+<A HREF="xxfi_unknown.html">xxfi_unknown()</A>
+<LI><A NAME="SMFIP_NODATA"><CODE>SMFIP_NODATA</CODE></A>:
+<A HREF="xxfi_data.html">xxfi_data()</A>
+</UL>
+
+For each of these xxfi_* callbacks that a milter does not use
+the corresponding flag <EM>should</EM> be set in
+<CODE>*pf1</CODE>.
+
+</UL>
+
+<P>
+The available actions
+(<CODE>f0</CODE>, <CODE>*pf0</CODE>)
+are
+<!--
+defined in
+<CODE>include/libmilter/mfapi.h</CODE>:
+the macros start with
+<CODE>SMFIF_</CODE>;
+these are
+-->
+described
+<A HREF="smfi_register.html#flags">elsewhere (xxfi_flags)</A>.
+
+<P>
+If a milter returns SMFIS_CONTINUE, then it <EM>must</EM>
+set the desired actions and protocol steps
+via the (output) parameters
+<CODE>pf0</CODE>
+and
+<CODE>pf1</CODE>
+(which correspond to
+<CODE>f0</CODE>
+and
+<CODE>f1</CODE>, respectively).
+The (output) parameters
+<CODE>pf2</CODE> and
+<CODE>pf3</CODE>
+should be set to 0 for compatibility with future versions.
+
+</TD>
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/docs/xxfi_unknown.html b/contrib/sendmail/libmilter/docs/xxfi_unknown.html
new file mode 100644
index 0000000..0455dfd
--- /dev/null
+++ b/contrib/sendmail/libmilter/docs/xxfi_unknown.html
@@ -0,0 +1,84 @@
+<HTML>
+<HEAD><TITLE>xxfi_unknown</TITLE></HEAD>
+<BODY>
+<!--
+$Id: xxfi_unknown.html,v 1.4 2007/04/23 16:30:42 ca Exp $
+-->
+<H1>xxfi_unknown</H1>
+
+<TABLE border="0" cellspacing=4 cellpadding=4>
+<!---------- Synopsis ----------->
+<TR><TH valign="top" align=left width=100>SYNOPSIS</TH><TD>
+<PRE>
+#include &lt;libmilter/mfapi.h&gt;
+sfsistat (*xxfi_unknown)(
+ SMFICTX *ctx,
+ const char *arg
+);
+</PRE>
+Handle unknown and unimplemented SMTP commands.
+</TD></TR>
+
+<!----------- Description ---------->
+<TR><TH valign="top" align=left>DESCRIPTION</TH><TD>
+<TABLE border="1" cellspacing=1 cellpadding=4>
+<TR align="left" valign=top>
+<TH width="80">Called When</TH>
+<TD>xxfi_unknown is called when the client uses an SMTP command
+that is either unknown or not implemented by the MTA.
+</TR>
+<TR align="left" valign=top>
+<TH>Default Behavior</TH>
+<TD>Do nothing; return SMFIS_CONTINUE.</TD>
+</TR>
+</TABLE>
+
+<!----------- Arguments ---------->
+<TR><TH valign="top" align=left>ARGUMENTS</TH><TD>
+ <TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Argument</TH><TH>Description</TH></TR>
+ <TR valign="top"><TD>ctx</TD>
+ <TD>Opaque context structure.
+ </TD></TR>
+ <TR valign="top"><TD>arg</TD>
+ <TD>SMTP command including all arguments.
+ </TD></TR>
+ </TABLE>
+</TD></TR>
+
+<!----------- Return values ---------->
+<TR>
+<TH valign="top" align=left>SPECIAL RETURN VALUES</TH>
+<TD><TABLE border="1" cellspacing=0>
+ <TR bgcolor="#dddddd"><TH>Return value</TH><TH>Description</TH></TR>
+ <TR valign="top">
+ <TD>SMFIS_TEMPFAIL</TD>
+ <TD>Reject this command with a temporary error.
+ </TD>
+ </TR>
+ <TR valign="top">
+ <TD>SMFIS_REJECT</TD>
+ <TD>Reject this command.
+ </TD>
+ </TR>
+</TABLE>
+</TR>
+
+<!----------- Notes ---------->
+<TR>
+<TH valign="top" align=left>NOTES</TH>
+<TD>The SMTP command will always be rejected by the server,
+it is only possible to return a different error code.
+</TR>
+</TABLE>
+
+<HR size="1">
+<FONT size="-1">
+Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+All rights reserved.
+<BR>
+By using this file, you agree to the terms and conditions set
+forth in the LICENSE.
+</FONT>
+</BODY>
+</HTML>
diff --git a/contrib/sendmail/libmilter/engine.c b/contrib/sendmail/libmilter/engine.c
new file mode 100644
index 0000000..a2d3e1e
--- /dev/null
+++ b/contrib/sendmail/libmilter/engine.c
@@ -0,0 +1,1915 @@
+/*
+ * Copyright (c) 1999-2004, 2006-2008 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: engine.c,v 8.166 2009/11/06 00:57:07 ca Exp $")
+
+#include "libmilter.h"
+
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+
+/* generic argument for functions in the command table */
+struct arg_struct
+{
+ size_t a_len; /* length of buffer */
+ char *a_buf; /* argument string */
+ int a_idx; /* index for macro array */
+ SMFICTX_PTR a_ctx; /* context */
+};
+
+typedef struct arg_struct genarg;
+
+/* structure for commands received from MTA */
+struct cmdfct_t
+{
+ char cm_cmd; /* command */
+ int cm_argt; /* type of arguments expected */
+ int cm_next; /* next state */
+ int cm_todo; /* what to do next */
+ int cm_macros; /* index for macros */
+ int (*cm_fct) __P((genarg *)); /* function to execute */
+};
+
+typedef struct cmdfct_t cmdfct;
+
+/* possible values for cm_argt */
+#define CM_ARG0 0 /* no args */
+#define CM_ARG1 1 /* one arg (string) */
+#define CM_ARG2 2 /* two args (strings) */
+#define CM_ARGA 4 /* one string and _SOCK_ADDR */
+#define CM_ARGO 5 /* two integers */
+#define CM_ARGV 8 /* \0 separated list of args, NULL-terminated */
+#define CM_ARGN 9 /* \0 separated list of args (strings) */
+
+/* possible values for cm_todo */
+#define CT_CONT 0x0000 /* continue reading commands */
+#define CT_IGNO 0x0001 /* continue even when error */
+
+/* not needed right now, done via return code instead */
+#define CT_KEEP 0x0004 /* keep buffer (contains symbols) */
+#define CT_END 0x0008 /* last command of session, stop replying */
+
+/* index in macro array: macros only for these commands */
+#define CI_NONE (-1)
+#define CI_CONN 0
+#define CI_HELO 1
+#define CI_MAIL 2
+#define CI_RCPT 3
+#define CI_DATA 4
+#define CI_EOM 5
+#define CI_EOH 6
+#define CI_LAST CI_EOH
+#if CI_LAST < CI_DATA
+ERROR: do not compile with CI_LAST < CI_DATA
+#endif
+#if CI_LAST < CI_EOM
+ERROR: do not compile with CI_LAST < CI_EOM
+#endif
+#if CI_LAST < CI_EOH
+ERROR: do not compile with CI_LAST < CI_EOH
+#endif
+#if CI_LAST < CI_ENVRCPT
+ERROR: do not compile with CI_LAST < CI_ENVRCPT
+#endif
+#if CI_LAST < CI_ENVFROM
+ERROR: do not compile with CI_LAST < CI_ENVFROM
+#endif
+#if CI_LAST < CI_HELO
+ERROR: do not compile with CI_LAST < CI_HELO
+#endif
+#if CI_LAST < CI_CONNECT
+ERROR: do not compile with CI_LAST < CI_CONNECT
+#endif
+#if CI_LAST >= MAX_MACROS_ENTRIES
+ERROR: do not compile with CI_LAST >= MAX_MACROS_ENTRIES
+#endif
+
+/* function prototypes */
+static int st_abortfct __P((genarg *));
+static int st_macros __P((genarg *));
+static int st_optionneg __P((genarg *));
+static int st_bodychunk __P((genarg *));
+static int st_connectinfo __P((genarg *));
+static int st_bodyend __P((genarg *));
+static int st_helo __P((genarg *));
+static int st_header __P((genarg *));
+static int st_sender __P((genarg *));
+static int st_rcpt __P((genarg *));
+static int st_unknown __P((genarg *));
+static int st_data __P((genarg *));
+static int st_eoh __P((genarg *));
+static int st_quit __P((genarg *));
+static int sendreply __P((sfsistat, socket_t, struct timeval *, SMFICTX_PTR));
+static void fix_stm __P((SMFICTX_PTR));
+static bool trans_ok __P((int, int));
+static char **dec_argv __P((char *, size_t));
+static int dec_arg2 __P((char *, size_t, char **, char **));
+static void mi_clr_symlist __P((SMFICTX_PTR));
+
+#if _FFR_WORKERS_POOL
+static bool mi_rd_socket_ready __P((int));
+#endif /* _FFR_WORKERS_POOL */
+
+/* states */
+#define ST_NONE (-1)
+#define ST_INIT 0 /* initial state */
+#define ST_OPTS 1 /* option negotiation */
+#define ST_CONN 2 /* connection info */
+#define ST_HELO 3 /* helo */
+#define ST_MAIL 4 /* mail from */
+#define ST_RCPT 5 /* rcpt to */
+#define ST_DATA 6 /* data */
+#define ST_HDRS 7 /* headers */
+#define ST_EOHS 8 /* end of headers */
+#define ST_BODY 9 /* body */
+#define ST_ENDM 10 /* end of message */
+#define ST_QUIT 11 /* quit */
+#define ST_ABRT 12 /* abort */
+#define ST_UNKN 13 /* unknown SMTP command */
+#define ST_Q_NC 14 /* quit, new connection follows */
+#define ST_LAST ST_Q_NC /* last valid state */
+#define ST_SKIP 16 /* not a state but required for the state table */
+
+/* in a mail transaction? must be before eom according to spec. */
+#define ST_IN_MAIL(st) ((st) >= ST_MAIL && (st) < ST_ENDM)
+
+/*
+** set of next states
+** each state (ST_*) corresponds to bit in an int value (1 << state)
+** each state has a set of allowed transitions ('or' of bits of states)
+** so a state transition is valid if the mask of the next state
+** is set in the NX_* value
+** this function is coded in trans_ok(), see below.
+*/
+
+#define MI_MASK(x) (0x0001 << (x)) /* generate a bit "mask" for a state */
+#define NX_INIT (MI_MASK(ST_OPTS))
+#define NX_OPTS (MI_MASK(ST_CONN) | MI_MASK(ST_UNKN))
+#define NX_CONN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN))
+#define NX_HELO (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN))
+#define NX_MAIL (MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN))
+#define NX_RCPT (MI_MASK(ST_HDRS) | MI_MASK(ST_EOHS) | MI_MASK(ST_DATA) | \
+ MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | \
+ MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | MI_MASK(ST_UNKN))
+#define NX_DATA (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT))
+#define NX_HDRS (MI_MASK(ST_EOHS) | MI_MASK(ST_HDRS) | MI_MASK(ST_ABRT))
+#define NX_EOHS (MI_MASK(ST_BODY) | MI_MASK(ST_ENDM) | MI_MASK(ST_ABRT))
+#define NX_BODY (MI_MASK(ST_ENDM) | MI_MASK(ST_BODY) | MI_MASK(ST_ABRT))
+#define NX_ENDM (MI_MASK(ST_QUIT) | MI_MASK(ST_MAIL) | MI_MASK(ST_UNKN) | \
+ MI_MASK(ST_Q_NC))
+#define NX_QUIT 0
+#define NX_ABRT 0
+#define NX_UNKN (MI_MASK(ST_HELO) | MI_MASK(ST_MAIL) | \
+ MI_MASK(ST_RCPT) | MI_MASK(ST_ABRT) | \
+ MI_MASK(ST_DATA) | \
+ MI_MASK(ST_BODY) | MI_MASK(ST_UNKN) | \
+ MI_MASK(ST_ABRT) | MI_MASK(ST_QUIT) | MI_MASK(ST_Q_NC))
+#define NX_Q_NC (MI_MASK(ST_CONN) | MI_MASK(ST_UNKN))
+#define NX_SKIP MI_MASK(ST_SKIP)
+
+static int next_states[] =
+{
+ NX_INIT
+ , NX_OPTS
+ , NX_CONN
+ , NX_HELO
+ , NX_MAIL
+ , NX_RCPT
+ , NX_DATA
+ , NX_HDRS
+ , NX_EOHS
+ , NX_BODY
+ , NX_ENDM
+ , NX_QUIT
+ , NX_ABRT
+ , NX_UNKN
+ , NX_Q_NC
+};
+
+#define SIZE_NEXT_STATES (sizeof(next_states) / sizeof(next_states[0]))
+
+/* commands received by milter */
+static cmdfct cmds[] =
+{
+ {SMFIC_ABORT, CM_ARG0, ST_ABRT, CT_CONT, CI_NONE, st_abortfct }
+, {SMFIC_MACRO, CM_ARGV, ST_NONE, CT_KEEP, CI_NONE, st_macros }
+, {SMFIC_BODY, CM_ARG1, ST_BODY, CT_CONT, CI_NONE, st_bodychunk }
+, {SMFIC_CONNECT, CM_ARG2, ST_CONN, CT_CONT, CI_CONN, st_connectinfo }
+, {SMFIC_BODYEOB, CM_ARG1, ST_ENDM, CT_CONT, CI_EOM, st_bodyend }
+, {SMFIC_HELO, CM_ARG1, ST_HELO, CT_CONT, CI_HELO, st_helo }
+, {SMFIC_HEADER, CM_ARG2, ST_HDRS, CT_CONT, CI_NONE, st_header }
+, {SMFIC_MAIL, CM_ARGV, ST_MAIL, CT_CONT, CI_MAIL, st_sender }
+, {SMFIC_OPTNEG, CM_ARGO, ST_OPTS, CT_CONT, CI_NONE, st_optionneg }
+, {SMFIC_EOH, CM_ARG0, ST_EOHS, CT_CONT, CI_EOH, st_eoh }
+, {SMFIC_QUIT, CM_ARG0, ST_QUIT, CT_END, CI_NONE, st_quit }
+, {SMFIC_DATA, CM_ARG0, ST_DATA, CT_CONT, CI_DATA, st_data }
+, {SMFIC_RCPT, CM_ARGV, ST_RCPT, CT_IGNO, CI_RCPT, st_rcpt }
+, {SMFIC_UNKNOWN, CM_ARG1, ST_UNKN, CT_IGNO, CI_NONE, st_unknown }
+, {SMFIC_QUIT_NC, CM_ARG0, ST_Q_NC, CT_CONT, CI_NONE, st_quit }
+};
+
+/*
+** Additional (internal) reply codes;
+** must be coordinated wit libmilter/mfapi.h
+*/
+
+#define _SMFIS_KEEP 20
+#define _SMFIS_ABORT 21
+#define _SMFIS_OPTIONS 22
+#define _SMFIS_NOREPLY SMFIS_NOREPLY
+#define _SMFIS_FAIL (-1)
+#define _SMFIS_NONE (-2)
+
+/*
+** MI_ENGINE -- receive commands and process them
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_FAILURE/MI_SUCCESS
+*/
+
+int
+mi_engine(ctx)
+ SMFICTX_PTR ctx;
+{
+ size_t len;
+ int i;
+ socket_t sd;
+ int ret = MI_SUCCESS;
+ int ncmds = sizeof(cmds) / sizeof(cmdfct);
+ int curstate = ST_INIT;
+ int newstate;
+ bool call_abort;
+ sfsistat r;
+ char cmd;
+ char *buf = NULL;
+ genarg arg;
+ struct timeval timeout;
+ int (*f) __P((genarg *));
+ sfsistat (*fi_abort) __P((SMFICTX *));
+ sfsistat (*fi_close) __P((SMFICTX *));
+
+ arg.a_ctx = ctx;
+ sd = ctx->ctx_sd;
+ fi_abort = ctx->ctx_smfi->xxfi_abort;
+#if _FFR_WORKERS_POOL
+ curstate = ctx->ctx_state;
+ if (curstate == ST_INIT)
+ {
+ mi_clr_macros(ctx, 0);
+ fix_stm(ctx);
+ }
+#else /* _FFR_WORKERS_POOL */
+ mi_clr_macros(ctx, 0);
+ fix_stm(ctx);
+#endif /* _FFR_WORKERS_POOL */
+ r = _SMFIS_NONE;
+ do
+ {
+ /* call abort only if in a mail transaction */
+ call_abort = ST_IN_MAIL(curstate);
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ if (mi_stop() == MILTER_ABRT)
+ {
+ if (ctx->ctx_dbg > 3)
+ sm_dprintf("[%ld] milter_abort\n",
+ (long) ctx->ctx_id);
+ ret = MI_FAILURE;
+ break;
+ }
+
+ /*
+ ** Notice: buf is allocated by mi_rd_cmd() and it will
+ ** usually be free()d after it has been used in f().
+ ** However, if the function returns _SMFIS_KEEP then buf
+ ** contains macros and will not be free()d.
+ ** Hence r must be set to _SMFIS_NONE if a new buf is
+ ** allocated to avoid problem with housekeeping, esp.
+ ** if the code "break"s out of the loop.
+ */
+
+#if _FFR_WORKERS_POOL
+ /* Is the socket ready to be read ??? */
+ if (!mi_rd_socket_ready(sd))
+ {
+ ret = MI_CONTINUE;
+ break;
+ }
+#endif /* _FFR_WORKERS_POOL */
+
+ r = _SMFIS_NONE;
+ if ((buf = mi_rd_cmd(sd, &timeout, &cmd, &len,
+ ctx->ctx_smfi->xxfi_name)) == NULL &&
+ cmd < SMFIC_VALIDCMD)
+ {
+ if (ctx->ctx_dbg > 5)
+ sm_dprintf("[%ld] mi_engine: mi_rd_cmd error (%x)\n",
+ (long) ctx->ctx_id, (int) cmd);
+
+ /*
+ ** eof is currently treated as failure ->
+ ** abort() instead of close(), otherwise use:
+ ** if (cmd != SMFIC_EOF)
+ */
+
+ ret = MI_FAILURE;
+ break;
+ }
+ if (ctx->ctx_dbg > 4)
+ sm_dprintf("[%ld] got cmd '%c' len %d\n",
+ (long) ctx->ctx_id, cmd, (int) len);
+ for (i = 0; i < ncmds; i++)
+ {
+ if (cmd == cmds[i].cm_cmd)
+ break;
+ }
+ if (i >= ncmds)
+ {
+ /* unknown command */
+ if (ctx->ctx_dbg > 1)
+ sm_dprintf("[%ld] cmd '%c' unknown\n",
+ (long) ctx->ctx_id, cmd);
+ ret = MI_FAILURE;
+ break;
+ }
+ if ((f = cmds[i].cm_fct) == NULL)
+ {
+ /* stop for now */
+ if (ctx->ctx_dbg > 1)
+ sm_dprintf("[%ld] cmd '%c' not impl\n",
+ (long) ctx->ctx_id, cmd);
+ ret = MI_FAILURE;
+ break;
+ }
+
+ /* is new state ok? */
+ newstate = cmds[i].cm_next;
+ if (ctx->ctx_dbg > 5)
+ sm_dprintf("[%ld] cur %x new %x nextmask %x\n",
+ (long) ctx->ctx_id,
+ curstate, newstate, next_states[curstate]);
+
+ if (newstate != ST_NONE && !trans_ok(curstate, newstate))
+ {
+ if (ctx->ctx_dbg > 1)
+ sm_dprintf("[%ld] abort: cur %d (%x) new %d (%x) next %x\n",
+ (long) ctx->ctx_id,
+ curstate, MI_MASK(curstate),
+ newstate, MI_MASK(newstate),
+ next_states[curstate]);
+
+ /* call abort only if in a mail transaction */
+ if (fi_abort != NULL && call_abort)
+ (void) (*fi_abort)(ctx);
+
+ /*
+ ** try to reach the new state from HELO
+ ** if it can't be reached, ignore the command.
+ */
+
+ curstate = ST_HELO;
+ if (!trans_ok(curstate, newstate))
+ {
+ if (buf != NULL)
+ {
+ free(buf);
+ buf = NULL;
+ }
+ continue;
+ }
+ }
+ arg.a_len = len;
+ arg.a_buf = buf;
+ if (newstate != ST_NONE)
+ {
+ curstate = newstate;
+ ctx->ctx_state = curstate;
+ }
+ arg.a_idx = cmds[i].cm_macros;
+ call_abort = ST_IN_MAIL(curstate);
+
+ /* call function to deal with command */
+ MI_MONITOR_BEGIN(ctx, cmd);
+ r = (*f)(&arg);
+ MI_MONITOR_END(ctx, cmd);
+ if (r != _SMFIS_KEEP && buf != NULL)
+ {
+ free(buf);
+ buf = NULL;
+ }
+ if (sendreply(r, sd, &timeout, ctx) != MI_SUCCESS)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+
+ if (r == SMFIS_ACCEPT)
+ {
+ /* accept mail, no further actions taken */
+ curstate = ST_HELO;
+ }
+ else if (r == SMFIS_REJECT || r == SMFIS_DISCARD ||
+ r == SMFIS_TEMPFAIL)
+ {
+ /*
+ ** further actions depend on current state
+ ** if the IGNO bit is set: "ignore" the error,
+ ** i.e., stay in the current state
+ */
+ if (!bitset(CT_IGNO, cmds[i].cm_todo))
+ curstate = ST_HELO;
+ }
+ else if (r == _SMFIS_ABORT)
+ {
+ if (ctx->ctx_dbg > 5)
+ sm_dprintf("[%ld] function returned abort\n",
+ (long) ctx->ctx_id);
+ ret = MI_FAILURE;
+ break;
+ }
+ } while (!bitset(CT_END, cmds[i].cm_todo));
+
+ ctx->ctx_state = curstate;
+
+ if (ret == MI_FAILURE)
+ {
+ /* call abort only if in a mail transaction */
+ if (fi_abort != NULL && call_abort)
+ (void) (*fi_abort)(ctx);
+ }
+
+ /* has close been called? */
+ if (ctx->ctx_state != ST_QUIT
+#if _FFR_WORKERS_POOL
+ && ret != MI_CONTINUE
+#endif /* _FFR_WORKERS_POOL */
+ )
+ {
+ if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
+ (void) (*fi_close)(ctx);
+ }
+ if (r != _SMFIS_KEEP && buf != NULL)
+ free(buf);
+#if !_FFR_WORKERS_POOL
+ mi_clr_macros(ctx, 0);
+#endif /* _FFR_WORKERS_POOL */
+ return ret;
+}
+
+static size_t milter_addsymlist __P((SMFICTX_PTR, char *, char **));
+
+static size_t
+milter_addsymlist(ctx, buf, newbuf)
+ SMFICTX_PTR ctx;
+ char *buf;
+ char **newbuf;
+{
+ size_t len;
+ int i;
+ mi_int32 v;
+ char *buffer;
+
+ SM_ASSERT(ctx != NULL);
+ SM_ASSERT(buf != NULL);
+ SM_ASSERT(newbuf != NULL);
+ len = 0;
+ for (i = 0; i < MAX_MACROS_ENTRIES; i++)
+ {
+ if (ctx->ctx_mac_list[i] != NULL)
+ {
+ len += strlen(ctx->ctx_mac_list[i]) + 1 +
+ MILTER_LEN_BYTES;
+ }
+ }
+ if (len > 0)
+ {
+ size_t offset;
+
+ SM_ASSERT(len + MILTER_OPTLEN > len);
+ len += MILTER_OPTLEN;
+ buffer = malloc(len);
+ if (buffer != NULL)
+ {
+ (void) memcpy(buffer, buf, MILTER_OPTLEN);
+ offset = MILTER_OPTLEN;
+ for (i = 0; i < MAX_MACROS_ENTRIES; i++)
+ {
+ size_t l;
+
+ if (ctx->ctx_mac_list[i] == NULL)
+ continue;
+
+ SM_ASSERT(offset + MILTER_LEN_BYTES < len);
+ v = htonl(i);
+ (void) memcpy(buffer + offset, (void *) &v,
+ MILTER_LEN_BYTES);
+ offset += MILTER_LEN_BYTES;
+ l = strlen(ctx->ctx_mac_list[i]) + 1;
+ SM_ASSERT(offset + l <= len);
+ (void) memcpy(buffer + offset,
+ ctx->ctx_mac_list[i], l);
+ offset += l;
+ }
+ }
+ else
+ {
+ /* oops ... */
+ }
+ }
+ else
+ {
+ len = MILTER_OPTLEN;
+ buffer = buf;
+ }
+ *newbuf = buffer;
+ return len;
+}
+
+/*
+** GET_NR_BIT -- get "no reply" bit matching state
+**
+** Parameters:
+** state -- current protocol stage
+**
+** Returns:
+** 0: no matching bit
+** >0: the matching "no reply" bit
+*/
+
+static unsigned long get_nr_bit __P((int));
+
+static unsigned long
+get_nr_bit(state)
+ int state;
+{
+ unsigned long bit;
+
+ switch (state)
+ {
+ case ST_CONN:
+ bit = SMFIP_NR_CONN;
+ break;
+ case ST_HELO:
+ bit = SMFIP_NR_HELO;
+ break;
+ case ST_MAIL:
+ bit = SMFIP_NR_MAIL;
+ break;
+ case ST_RCPT:
+ bit = SMFIP_NR_RCPT;
+ break;
+ case ST_DATA:
+ bit = SMFIP_NR_DATA;
+ break;
+ case ST_UNKN:
+ bit = SMFIP_NR_UNKN;
+ break;
+ case ST_HDRS:
+ bit = SMFIP_NR_HDR;
+ break;
+ case ST_EOHS:
+ bit = SMFIP_NR_EOH;
+ break;
+ case ST_BODY:
+ bit = SMFIP_NR_BODY;
+ break;
+ default:
+ bit = 0;
+ break;
+ }
+ return bit;
+}
+
+/*
+** SENDREPLY -- send a reply to the MTA
+**
+** Parameters:
+** r -- reply code
+** sd -- socket descriptor
+** timeout_ptr -- (ptr to) timeout to use for sending
+** ctx -- context structure
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+sendreply(r, sd, timeout_ptr, ctx)
+ sfsistat r;
+ socket_t sd;
+ struct timeval *timeout_ptr;
+ SMFICTX_PTR ctx;
+{
+ int ret;
+ unsigned long bit;
+
+ ret = MI_SUCCESS;
+
+ bit = get_nr_bit(ctx->ctx_state);
+ if (bit != 0 && (ctx->ctx_pflags & bit) != 0 && r != SMFIS_NOREPLY)
+ {
+ if (r >= SMFIS_CONTINUE && r < _SMFIS_KEEP)
+ {
+ /* milter said it wouldn't reply, but it lied... */
+ smi_log(SMI_LOG_ERR,
+ "%s: milter claimed not to reply in state %d but did anyway %d\n",
+ ctx->ctx_smfi->xxfi_name,
+ ctx->ctx_state, r);
+
+ }
+
+ /*
+ ** Force specified behavior, otherwise libmilter
+ ** and MTA will fail to communicate properly.
+ */
+
+ switch (r)
+ {
+ case SMFIS_CONTINUE:
+ case SMFIS_TEMPFAIL:
+ case SMFIS_REJECT:
+ case SMFIS_DISCARD:
+ case SMFIS_ACCEPT:
+ case SMFIS_SKIP:
+ case _SMFIS_OPTIONS:
+ r = SMFIS_NOREPLY;
+ break;
+ }
+ }
+
+ switch (r)
+ {
+ case SMFIS_CONTINUE:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL, 0);
+ break;
+ case SMFIS_TEMPFAIL:
+ case SMFIS_REJECT:
+ if (ctx->ctx_reply != NULL &&
+ ((r == SMFIS_TEMPFAIL && *ctx->ctx_reply == '4') ||
+ (r == SMFIS_REJECT && *ctx->ctx_reply == '5')))
+ {
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_REPLYCODE,
+ ctx->ctx_reply,
+ strlen(ctx->ctx_reply) + 1);
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = NULL;
+ }
+ else
+ {
+ ret = mi_wr_cmd(sd, timeout_ptr, r == SMFIS_REJECT ?
+ SMFIR_REJECT : SMFIR_TEMPFAIL, NULL, 0);
+ }
+ break;
+ case SMFIS_DISCARD:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_DISCARD, NULL, 0);
+ break;
+ case SMFIS_ACCEPT:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_ACCEPT, NULL, 0);
+ break;
+ case SMFIS_SKIP:
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_SKIP, NULL, 0);
+ break;
+ case _SMFIS_OPTIONS:
+ {
+ mi_int32 v;
+ size_t len;
+ char *buffer;
+ char buf[MILTER_OPTLEN];
+
+ v = htonl(ctx->ctx_prot_vers2mta);
+ (void) memcpy(&(buf[0]), (void *) &v,
+ MILTER_LEN_BYTES);
+ v = htonl(ctx->ctx_aflags);
+ (void) memcpy(&(buf[MILTER_LEN_BYTES]), (void *) &v,
+ MILTER_LEN_BYTES);
+ v = htonl(ctx->ctx_pflags2mta);
+ (void) memcpy(&(buf[MILTER_LEN_BYTES * 2]),
+ (void *) &v, MILTER_LEN_BYTES);
+ len = milter_addsymlist(ctx, buf, &buffer);
+ if (buffer != NULL)
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIC_OPTNEG,
+ buffer, len);
+ else
+ ret = MI_FAILURE;
+ }
+ break;
+ case SMFIS_NOREPLY:
+ if (bit != 0 &&
+ (ctx->ctx_pflags & bit) != 0 &&
+ (ctx->ctx_mta_pflags & bit) == 0)
+ {
+ /*
+ ** milter doesn't want to send a reply,
+ ** but the MTA doesn't have that feature: fake it.
+ */
+
+ ret = mi_wr_cmd(sd, timeout_ptr, SMFIR_CONTINUE, NULL,
+ 0);
+ }
+ break;
+ default: /* don't send a reply */
+ break;
+ }
+ return ret;
+}
+
+/*
+** CLR_MACROS -- clear set of macros starting from a given index
+**
+** Parameters:
+** ctx -- context structure
+** m -- index from which to clear all macros
+**
+** Returns:
+** None.
+*/
+
+void
+mi_clr_macros(ctx, m)
+ SMFICTX_PTR ctx;
+ int m;
+{
+ int i;
+
+ for (i = m; i < MAX_MACROS_ENTRIES; i++)
+ {
+ if (ctx->ctx_mac_ptr[i] != NULL)
+ {
+ free(ctx->ctx_mac_ptr[i]);
+ ctx->ctx_mac_ptr[i] = NULL;
+ }
+ if (ctx->ctx_mac_buf[i] != NULL)
+ {
+ free(ctx->ctx_mac_buf[i]);
+ ctx->ctx_mac_buf[i] = NULL;
+ }
+ }
+}
+
+/*
+** MI_CLR_SYMLIST -- clear list of macros
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** None.
+*/
+
+static void
+mi_clr_symlist(ctx)
+ SMFICTX *ctx;
+{
+ int i;
+
+ SM_ASSERT(ctx != NULL);
+ for (i = SMFIM_FIRST; i <= SMFIM_LAST; i++)
+ {
+ if (ctx->ctx_mac_list[i] != NULL)
+ {
+ free(ctx->ctx_mac_list[i]);
+ ctx->ctx_mac_list[i] = NULL;
+ }
+ }
+}
+
+/*
+** MI_CLR_CTX -- clear context
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** None.
+*/
+
+void
+mi_clr_ctx(ctx)
+ SMFICTX *ctx;
+{
+ SM_ASSERT(ctx != NULL);
+ if (ValidSocket(ctx->ctx_sd))
+ {
+ (void) closesocket(ctx->ctx_sd);
+ ctx->ctx_sd = INVALID_SOCKET;
+ }
+ if (ctx->ctx_reply != NULL)
+ {
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = NULL;
+ }
+ if (ctx->ctx_privdata != NULL)
+ {
+ smi_log(SMI_LOG_WARN,
+ "%s: private data not NULL",
+ ctx->ctx_smfi->xxfi_name);
+ }
+ mi_clr_macros(ctx, 0);
+ mi_clr_symlist(ctx);
+ free(ctx);
+}
+
+/*
+** ST_OPTIONNEG -- negotiate options
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** abort/send options/continue
+*/
+
+static int
+st_optionneg(g)
+ genarg *g;
+{
+ mi_int32 i, v, fake_pflags, internal_pflags;
+ SMFICTX_PTR ctx;
+#if _FFR_MILTER_CHECK
+ bool testmode = false;
+#endif /* _FFR_MILTER_CHECK */
+ int (*fi_negotiate) __P((SMFICTX *,
+ unsigned long, unsigned long,
+ unsigned long, unsigned long,
+ unsigned long *, unsigned long *,
+ unsigned long *, unsigned long *));
+
+ if (g == NULL || g->a_ctx->ctx_smfi == NULL)
+ return SMFIS_CONTINUE;
+ ctx = g->a_ctx;
+ mi_clr_macros(ctx, g->a_idx + 1);
+ ctx->ctx_prot_vers = SMFI_PROT_VERSION;
+
+ /* check for minimum length */
+ if (g->a_len < MILTER_OPTLEN)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%ld]: len too short %d < %d",
+ ctx->ctx_smfi->xxfi_name,
+ (long) ctx->ctx_id, (int) g->a_len,
+ MILTER_OPTLEN);
+ return _SMFIS_ABORT;
+ }
+
+ /* protocol version */
+ (void) memcpy((void *) &i, (void *) &(g->a_buf[0]), MILTER_LEN_BYTES);
+ v = ntohl(i);
+
+#define SMFI_PROT_VERSION_MIN 2
+
+ /* check for minimum version */
+ if (v < SMFI_PROT_VERSION_MIN)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%ld]: protocol version too old %d < %d",
+ ctx->ctx_smfi->xxfi_name,
+ (long) ctx->ctx_id, v, SMFI_PROT_VERSION_MIN);
+ return _SMFIS_ABORT;
+ }
+ ctx->ctx_mta_prot_vers = v;
+ if (ctx->ctx_prot_vers < ctx->ctx_mta_prot_vers)
+ ctx->ctx_prot_vers2mta = ctx->ctx_prot_vers;
+ else
+ ctx->ctx_prot_vers2mta = ctx->ctx_mta_prot_vers;
+
+ (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES]),
+ MILTER_LEN_BYTES);
+ v = ntohl(i);
+
+ /* no flags? set to default value for V1 actions */
+ if (v == 0)
+ v = SMFI_V1_ACTS;
+ ctx->ctx_mta_aflags = v; /* MTA action flags */
+
+ internal_pflags = 0;
+ (void) memcpy((void *) &i, (void *) &(g->a_buf[MILTER_LEN_BYTES * 2]),
+ MILTER_LEN_BYTES);
+ v = ntohl(i);
+
+ /* no flags? set to default value for V1 protocol */
+ if (v == 0)
+ v = SMFI_V1_PROT;
+#if _FFR_MDS_NEGOTIATE
+ else if (ctx->ctx_smfi->xxfi_version >= SMFI_VERSION_MDS)
+ {
+ /*
+ ** Allow changing the size only if milter is compiled
+ ** against a version that supports this.
+ ** If a milter is dynamically linked against a newer
+ ** libmilter version, we don't want to "surprise"
+ ** it with a larger buffer as it may rely on it
+ ** even though it is not documented as a limit.
+ */
+
+ if (bitset(SMFIP_MDS_1M, v))
+ {
+ internal_pflags |= SMFIP_MDS_1M;
+ (void) smfi_setmaxdatasize(MILTER_MDS_1M);
+ }
+ else if (bitset(SMFIP_MDS_256K, v))
+ {
+ internal_pflags |= SMFIP_MDS_256K;
+ (void) smfi_setmaxdatasize(MILTER_MDS_256K);
+ }
+ }
+# if 0
+ /* don't log this for now... */
+ else if (ctx->ctx_smfi->xxfi_version < SMFI_VERSION_MDS &&
+ bitset(SMFIP_MDS_1M|SMFIP_MDS_256K, v))
+ {
+ smi_log(SMI_LOG_WARN,
+ "%s: st_optionneg[%ld]: milter version=%X, trying flags=%X",
+ ctx->ctx_smfi->xxfi_name,
+ (long) ctx->ctx_id, ctx->ctx_smfi->xxfi_version, v);
+ }
+# endif /* 0 */
+#endif /* _FFR_MDS_NEGOTIATE */
+
+ /*
+ ** MTA protocol flags.
+ ** We pass the internal flags to the milter as "read only",
+ ** i.e., a milter can read them so it knows which size
+ ** will be used, but any changes by a milter will be ignored
+ ** (see below, search for SMFI_INTERNAL).
+ */
+
+ ctx->ctx_mta_pflags = (v & ~SMFI_INTERNAL) | internal_pflags;
+
+ /*
+ ** Copy flags from milter struct into libmilter context;
+ ** this variable will be used later on to check whether
+ ** the MTA "actions" can fulfill the milter requirements,
+ ** but it may be overwritten by the negotiate callback.
+ */
+
+ ctx->ctx_aflags = ctx->ctx_smfi->xxfi_flags;
+ fake_pflags = SMFIP_NR_CONN
+ |SMFIP_NR_HELO
+ |SMFIP_NR_MAIL
+ |SMFIP_NR_RCPT
+ |SMFIP_NR_DATA
+ |SMFIP_NR_UNKN
+ |SMFIP_NR_HDR
+ |SMFIP_NR_EOH
+ |SMFIP_NR_BODY
+ ;
+
+ if (g->a_ctx->ctx_smfi != NULL &&
+ g->a_ctx->ctx_smfi->xxfi_version > 4 &&
+ (fi_negotiate = g->a_ctx->ctx_smfi->xxfi_negotiate) != NULL)
+ {
+ int r;
+ unsigned long m_aflags, m_pflags, m_f2, m_f3;
+
+ /*
+ ** let milter decide whether the features offered by the
+ ** MTA are "good enough".
+ ** Notes:
+ ** - libmilter can "fake" some features (e.g., SMFIP_NR_HDR)
+ ** - m_f2, m_f3 are for future extensions
+ */
+
+ m_f2 = m_f3 = 0;
+ m_aflags = ctx->ctx_mta_aflags;
+ m_pflags = ctx->ctx_pflags;
+ if ((SMFIP_SKIP & ctx->ctx_mta_pflags) != 0)
+ m_pflags |= SMFIP_SKIP;
+ r = fi_negotiate(g->a_ctx,
+ ctx->ctx_mta_aflags,
+ ctx->ctx_mta_pflags|fake_pflags,
+ 0, 0,
+ &m_aflags, &m_pflags, &m_f2, &m_f3);
+
+#if _FFR_MILTER_CHECK
+ testmode = bitset(SMFIP_TEST, m_pflags);
+ if (testmode)
+ m_pflags &= ~SMFIP_TEST;
+#endif /* _FFR_MILTER_CHECK */
+
+ /*
+ ** Types of protocol flags (pflags):
+ ** 1. do NOT send protocol step X
+ ** 2. MTA can do/understand something extra (SKIP,
+ ** send unknown RCPTs)
+ ** 3. MTA can deal with "no reply" for various protocol steps
+ ** Note: this mean that it isn't possible to simply set all
+ ** flags to get "everything":
+ ** setting a flag of type 1 turns off a step
+ ** (it should be the other way around:
+ ** a flag means a protocol step can be sent)
+ ** setting a flag of type 3 requires that milter
+ ** never sends a reply for the corresponding step.
+ ** Summary: the "negation" of protocol flags is causing
+ ** problems, but at least for type 3 there is no simple
+ ** solution.
+ **
+ ** What should "all options" mean?
+ ** send all protocol steps _except_ those for which there is
+ ** no callback (currently registered in ctx_pflags)
+ ** expect SKIP as return code? Yes
+ ** send unknown RCPTs? No,
+ ** must be explicitly requested?
+ ** "no reply" for some protocol steps? No,
+ ** must be explicitly requested.
+ */
+
+ if (SMFIS_ALL_OPTS == r)
+ {
+ ctx->ctx_aflags = ctx->ctx_mta_aflags;
+ ctx->ctx_pflags2mta = ctx->ctx_pflags;
+ if ((SMFIP_SKIP & ctx->ctx_mta_pflags) != 0)
+ ctx->ctx_pflags2mta |= SMFIP_SKIP;
+ }
+ else if (r != SMFIS_CONTINUE)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%ld]: xxfi_negotiate returned %d (protocol options=0x%lx, actions=0x%lx)",
+ ctx->ctx_smfi->xxfi_name,
+ (long) ctx->ctx_id, r, ctx->ctx_mta_pflags,
+ ctx->ctx_mta_aflags);
+ return _SMFIS_ABORT;
+ }
+ else
+ {
+ ctx->ctx_aflags = m_aflags;
+ ctx->ctx_pflags = m_pflags;
+ ctx->ctx_pflags2mta = m_pflags;
+ }
+
+ /* check whether some flags need to be "faked" */
+ i = ctx->ctx_pflags2mta;
+ if ((ctx->ctx_mta_pflags & i) != i)
+ {
+ unsigned int idx;
+ unsigned long b;
+
+ /*
+ ** If some behavior can be faked (set in fake_pflags),
+ ** but the MTA doesn't support it, then unset
+ ** that flag in the value that is sent to the MTA.
+ */
+
+ for (idx = 0; idx < 32; idx++)
+ {
+ b = 1 << idx;
+ if ((ctx->ctx_mta_pflags & b) != b &&
+ (fake_pflags & b) == b)
+ ctx->ctx_pflags2mta &= ~b;
+ }
+ }
+ }
+ else
+ {
+ /*
+ ** Set the protocol flags based on the values determined
+ ** in mi_listener() which checked the defined callbacks.
+ */
+
+ ctx->ctx_pflags2mta = ctx->ctx_pflags;
+ }
+
+ /* check whether actions and protocol requirements can be satisfied */
+ i = ctx->ctx_aflags;
+ if ((i & ctx->ctx_mta_aflags) != i)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%ld]: 0x%lx does not fulfill action requirements 0x%x",
+ ctx->ctx_smfi->xxfi_name,
+ (long) ctx->ctx_id, ctx->ctx_mta_aflags, i);
+ return _SMFIS_ABORT;
+ }
+
+ i = ctx->ctx_pflags2mta;
+ if ((ctx->ctx_mta_pflags & i) != i)
+ {
+ /*
+ ** Older MTAs do not support some protocol steps.
+ ** As this protocol is a bit "wierd" (it asks for steps
+ ** NOT to be taken/sent) we have to check whether we
+ ** should turn off those "negative" requests.
+ ** Currently these are only SMFIP_NODATA and SMFIP_NOUNKNOWN.
+ */
+
+ if (bitset(SMFIP_NODATA, ctx->ctx_pflags2mta) &&
+ !bitset(SMFIP_NODATA, ctx->ctx_mta_pflags))
+ ctx->ctx_pflags2mta &= ~SMFIP_NODATA;
+ if (bitset(SMFIP_NOUNKNOWN, ctx->ctx_pflags2mta) &&
+ !bitset(SMFIP_NOUNKNOWN, ctx->ctx_mta_pflags))
+ ctx->ctx_pflags2mta &= ~SMFIP_NOUNKNOWN;
+ i = ctx->ctx_pflags2mta;
+ }
+
+ if ((ctx->ctx_mta_pflags & i) != i)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: st_optionneg[%ld]: 0x%lx does not fulfill protocol requirements 0x%x",
+ ctx->ctx_smfi->xxfi_name,
+ (long) ctx->ctx_id, ctx->ctx_mta_pflags, i);
+ return _SMFIS_ABORT;
+ }
+ fix_stm(ctx);
+
+ if (ctx->ctx_dbg > 3)
+ sm_dprintf("[%ld] milter_negotiate:"
+ " mta_actions=0x%lx, mta_flags=0x%lx"
+ " actions=0x%lx, flags=0x%lx\n"
+ , (long) ctx->ctx_id
+ , ctx->ctx_mta_aflags, ctx->ctx_mta_pflags
+ , ctx->ctx_aflags, ctx->ctx_pflags);
+
+#if _FFR_MILTER_CHECK
+ if (ctx->ctx_dbg > 3)
+ sm_dprintf("[%ld] milter_negotiate:"
+ " testmode=%d, pflags2mta=%X, internal_pflags=%X\n"
+ , (long) ctx->ctx_id, testmode
+ , ctx->ctx_pflags2mta, internal_pflags);
+
+ /* in test mode: take flags without further modifications */
+ if (!testmode)
+ /* Warning: check statement below! */
+#endif /* _FFR_MILTER_CHECK */
+
+ /*
+ ** Remove the internal flags that might have been set by a milter
+ ** and set only those determined above.
+ */
+
+ ctx->ctx_pflags2mta = (ctx->ctx_pflags2mta & ~SMFI_INTERNAL)
+ | internal_pflags;
+ return _SMFIS_OPTIONS;
+}
+
+/*
+** ST_CONNECTINFO -- receive connection information
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_connectinfo(g)
+ genarg *g;
+{
+ size_t l;
+ size_t i;
+ char *s, family;
+ unsigned short port = 0;
+ _SOCK_ADDR sockaddr;
+ sfsistat (*fi_connect) __P((SMFICTX *, char *, _SOCK_ADDR *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ mi_clr_macros(g->a_ctx, g->a_idx + 1);
+ if (g->a_ctx->ctx_smfi == NULL ||
+ (fi_connect = g->a_ctx->ctx_smfi->xxfi_connect) == NULL)
+ return SMFIS_CONTINUE;
+
+ s = g->a_buf;
+ i = 0;
+ l = g->a_len;
+ while (s[i] != '\0' && i <= l)
+ ++i;
+ if (i + 1 >= l)
+ return _SMFIS_ABORT;
+
+ /* Move past trailing \0 in host string */
+ i++;
+ family = s[i++];
+ (void) memset(&sockaddr, '\0', sizeof sockaddr);
+ if (family != SMFIA_UNKNOWN)
+ {
+ if (i + sizeof port >= l)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%ld]: wrong len %d >= %d",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (long) g->a_ctx->ctx_id, (int) i, (int) l);
+ return _SMFIS_ABORT;
+ }
+ (void) memcpy((void *) &port, (void *) (s + i),
+ sizeof port);
+ i += sizeof port;
+
+ /* make sure string is terminated */
+ if (s[l - 1] != '\0')
+ return _SMFIS_ABORT;
+# if NETINET
+ if (family == SMFIA_INET)
+ {
+ if (inet_aton(s + i, (struct in_addr *) &sockaddr.sin.sin_addr)
+ != 1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%ld]: inet_aton failed",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (long) g->a_ctx->ctx_id);
+ return _SMFIS_ABORT;
+ }
+ sockaddr.sa.sa_family = AF_INET;
+ if (port > 0)
+ sockaddr.sin.sin_port = port;
+ }
+ else
+# endif /* NETINET */
+# if NETINET6
+ if (family == SMFIA_INET6)
+ {
+ if (mi_inet_pton(AF_INET6, s + i,
+ &sockaddr.sin6.sin6_addr) != 1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%ld]: mi_inet_pton failed",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (long) g->a_ctx->ctx_id);
+ return _SMFIS_ABORT;
+ }
+ sockaddr.sa.sa_family = AF_INET6;
+ if (port > 0)
+ sockaddr.sin6.sin6_port = port;
+ }
+ else
+# endif /* NETINET6 */
+# if NETUNIX
+ if (family == SMFIA_UNIX)
+ {
+ if (sm_strlcpy(sockaddr.sunix.sun_path, s + i,
+ sizeof sockaddr.sunix.sun_path) >=
+ sizeof sockaddr.sunix.sun_path)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%ld]: path too long",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (long) g->a_ctx->ctx_id);
+ return _SMFIS_ABORT;
+ }
+ sockaddr.sunix.sun_family = AF_UNIX;
+ }
+ else
+# endif /* NETUNIX */
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: connect[%ld]: unknown family %d",
+ g->a_ctx->ctx_smfi->xxfi_name,
+ (long) g->a_ctx->ctx_id, family);
+ return _SMFIS_ABORT;
+ }
+ }
+ return (*fi_connect)(g->a_ctx, g->a_buf,
+ family != SMFIA_UNKNOWN ? &sockaddr : NULL);
+}
+
+/*
+** ST_EOH -- end of headers
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_eoh(g)
+ genarg *g;
+{
+ sfsistat (*fi_eoh) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_eoh = g->a_ctx->ctx_smfi->xxfi_eoh) != NULL)
+ return (*fi_eoh)(g->a_ctx);
+ return SMFIS_CONTINUE;
+}
+
+/*
+** ST_DATA -- DATA command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_data(g)
+ genarg *g;
+{
+ sfsistat (*fi_data) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ g->a_ctx->ctx_smfi->xxfi_version > 3 &&
+ (fi_data = g->a_ctx->ctx_smfi->xxfi_data) != NULL)
+ return (*fi_data)(g->a_ctx);
+ return SMFIS_CONTINUE;
+}
+
+/*
+** ST_HELO -- helo/ehlo command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_helo(g)
+ genarg *g;
+{
+ sfsistat (*fi_helo) __P((SMFICTX *, char *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ mi_clr_macros(g->a_ctx, g->a_idx + 1);
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_helo = g->a_ctx->ctx_smfi->xxfi_helo) != NULL)
+ {
+ /* paranoia: check for terminating '\0' */
+ if (g->a_len == 0 || g->a_buf[g->a_len - 1] != '\0')
+ return MI_FAILURE;
+ return (*fi_helo)(g->a_ctx, g->a_buf);
+ }
+ return SMFIS_CONTINUE;
+}
+
+/*
+** ST_HEADER -- header line
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_header(g)
+ genarg *g;
+{
+ char *hf, *hv;
+ sfsistat (*fi_header) __P((SMFICTX *, char *, char *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi == NULL ||
+ (fi_header = g->a_ctx->ctx_smfi->xxfi_header) == NULL)
+ return SMFIS_CONTINUE;
+ if (dec_arg2(g->a_buf, g->a_len, &hf, &hv) == MI_SUCCESS)
+ return (*fi_header)(g->a_ctx, hf, hv);
+ else
+ return _SMFIS_ABORT;
+}
+
+#define ARGV_FCT(lf, rf, idx) \
+ char **argv; \
+ sfsistat (*lf) __P((SMFICTX *, char **)); \
+ int r; \
+ \
+ if (g == NULL) \
+ return _SMFIS_ABORT; \
+ mi_clr_macros(g->a_ctx, g->a_idx + 1); \
+ if (g->a_ctx->ctx_smfi == NULL || \
+ (lf = g->a_ctx->ctx_smfi->rf) == NULL) \
+ return SMFIS_CONTINUE; \
+ if ((argv = dec_argv(g->a_buf, g->a_len)) == NULL) \
+ return _SMFIS_ABORT; \
+ r = (*lf)(g->a_ctx, argv); \
+ free(argv); \
+ return r;
+
+/*
+** ST_SENDER -- MAIL FROM command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_sender(g)
+ genarg *g;
+{
+ ARGV_FCT(fi_envfrom, xxfi_envfrom, CI_MAIL)
+}
+
+/*
+** ST_RCPT -- RCPT TO command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_rcpt(g)
+ genarg *g;
+{
+ ARGV_FCT(fi_envrcpt, xxfi_envrcpt, CI_RCPT)
+}
+
+/*
+** ST_UNKNOWN -- unrecognized or unimplemented command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_unknown(g)
+ genarg *g;
+{
+ sfsistat (*fi_unknown) __P((SMFICTX *, const char *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ g->a_ctx->ctx_smfi->xxfi_version > 2 &&
+ (fi_unknown = g->a_ctx->ctx_smfi->xxfi_unknown) != NULL)
+ return (*fi_unknown)(g->a_ctx, (const char *) g->a_buf);
+ return SMFIS_CONTINUE;
+}
+
+/*
+** ST_MACROS -- deal with macros received from the MTA
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue/keep
+**
+** Side effects:
+** set pointer in macro array to current values.
+*/
+
+static int
+st_macros(g)
+ genarg *g;
+{
+ int i;
+ char **argv;
+
+ if (g == NULL || g->a_len < 1)
+ return _SMFIS_FAIL;
+ if ((argv = dec_argv(g->a_buf + 1, g->a_len - 1)) == NULL)
+ return _SMFIS_FAIL;
+ switch (g->a_buf[0])
+ {
+ case SMFIC_CONNECT:
+ i = CI_CONN;
+ break;
+ case SMFIC_HELO:
+ i = CI_HELO;
+ break;
+ case SMFIC_MAIL:
+ i = CI_MAIL;
+ break;
+ case SMFIC_RCPT:
+ i = CI_RCPT;
+ break;
+ case SMFIC_DATA:
+ i = CI_DATA;
+ break;
+ case SMFIC_BODYEOB:
+ i = CI_EOM;
+ break;
+ case SMFIC_EOH:
+ i = CI_EOH;
+ break;
+ default:
+ free(argv);
+ return _SMFIS_FAIL;
+ }
+ if (g->a_ctx->ctx_mac_ptr[i] != NULL)
+ free(g->a_ctx->ctx_mac_ptr[i]);
+ if (g->a_ctx->ctx_mac_buf[i] != NULL)
+ free(g->a_ctx->ctx_mac_buf[i]);
+ g->a_ctx->ctx_mac_ptr[i] = argv;
+ g->a_ctx->ctx_mac_buf[i] = g->a_buf;
+ return _SMFIS_KEEP;
+}
+
+/*
+** ST_QUIT -- quit command
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** noreply
+*/
+
+/* ARGSUSED */
+static int
+st_quit(g)
+ genarg *g;
+{
+ sfsistat (*fi_close) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_close = g->a_ctx->ctx_smfi->xxfi_close) != NULL)
+ (void) (*fi_close)(g->a_ctx);
+ mi_clr_macros(g->a_ctx, 0);
+ return _SMFIS_NOREPLY;
+}
+
+/*
+** ST_BODYCHUNK -- deal with a piece of the mail body
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+*/
+
+static int
+st_bodychunk(g)
+ genarg *g;
+{
+ sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g->a_ctx->ctx_smfi != NULL &&
+ (fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL)
+ return (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf,
+ g->a_len);
+ return SMFIS_CONTINUE;
+}
+
+/*
+** ST_BODYEND -- deal with the last piece of the mail body
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** continue or filter-specified value
+**
+** Side effects:
+** sends a reply for the body part (if non-empty).
+*/
+
+static int
+st_bodyend(g)
+ genarg *g;
+{
+ sfsistat r;
+ sfsistat (*fi_body) __P((SMFICTX *, unsigned char *, size_t));
+ sfsistat (*fi_eom) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ r = SMFIS_CONTINUE;
+ if (g->a_ctx->ctx_smfi != NULL)
+ {
+ if ((fi_body = g->a_ctx->ctx_smfi->xxfi_body) != NULL &&
+ g->a_len > 0)
+ {
+ socket_t sd;
+ struct timeval timeout;
+
+ timeout.tv_sec = g->a_ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ sd = g->a_ctx->ctx_sd;
+ r = (*fi_body)(g->a_ctx, (unsigned char *)g->a_buf,
+ g->a_len);
+ if (r != SMFIS_CONTINUE &&
+ sendreply(r, sd, &timeout, g->a_ctx) != MI_SUCCESS)
+ return _SMFIS_ABORT;
+ }
+ }
+ if (r == SMFIS_CONTINUE &&
+ (fi_eom = g->a_ctx->ctx_smfi->xxfi_eom) != NULL)
+ return (*fi_eom)(g->a_ctx);
+ return r;
+}
+
+/*
+** ST_ABORTFCT -- deal with aborts
+**
+** Parameters:
+** g -- generic argument structure
+**
+** Returns:
+** abort or filter-specified value
+*/
+
+static int
+st_abortfct(g)
+ genarg *g;
+{
+ sfsistat (*fi_abort) __P((SMFICTX *));
+
+ if (g == NULL)
+ return _SMFIS_ABORT;
+ if (g != NULL && g->a_ctx->ctx_smfi != NULL &&
+ (fi_abort = g->a_ctx->ctx_smfi->xxfi_abort) != NULL)
+ (void) (*fi_abort)(g->a_ctx);
+ return _SMFIS_NOREPLY;
+}
+
+/*
+** TRANS_OK -- is the state transition ok?
+**
+** Parameters:
+** old -- old state
+** new -- new state
+**
+** Returns:
+** state transition ok
+*/
+
+static bool
+trans_ok(old, new)
+ int old, new;
+{
+ int s, n;
+
+ s = old;
+ if (s >= SIZE_NEXT_STATES)
+ return false;
+ do
+ {
+ /* is this state transition allowed? */
+ if ((MI_MASK(new) & next_states[s]) != 0)
+ return true;
+
+ /*
+ ** no: try next state;
+ ** this works since the relevant states are ordered
+ ** strict sequentially
+ */
+
+ n = s + 1;
+ if (n >= SIZE_NEXT_STATES)
+ return false;
+
+ /*
+ ** can we actually "skip" this state?
+ ** see fix_stm() which sets this bit for those
+ ** states which the filter program is not interested in
+ */
+
+ if (bitset(NX_SKIP, next_states[n]))
+ s = n;
+ else
+ return false;
+ } while (s < SIZE_NEXT_STATES);
+ return false;
+}
+
+/*
+** FIX_STM -- add "skip" bits to the state transition table
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** None.
+**
+** Side effects:
+** may change state transition table.
+*/
+
+static void
+fix_stm(ctx)
+ SMFICTX_PTR ctx;
+{
+ unsigned long fl;
+
+ if (ctx == NULL || ctx->ctx_smfi == NULL)
+ return;
+ fl = ctx->ctx_pflags;
+ if (bitset(SMFIP_NOCONNECT, fl))
+ next_states[ST_CONN] |= NX_SKIP;
+ if (bitset(SMFIP_NOHELO, fl))
+ next_states[ST_HELO] |= NX_SKIP;
+ if (bitset(SMFIP_NOMAIL, fl))
+ next_states[ST_MAIL] |= NX_SKIP;
+ if (bitset(SMFIP_NORCPT, fl))
+ next_states[ST_RCPT] |= NX_SKIP;
+ if (bitset(SMFIP_NOHDRS, fl))
+ next_states[ST_HDRS] |= NX_SKIP;
+ if (bitset(SMFIP_NOEOH, fl))
+ next_states[ST_EOHS] |= NX_SKIP;
+ if (bitset(SMFIP_NOBODY, fl))
+ next_states[ST_BODY] |= NX_SKIP;
+ if (bitset(SMFIP_NODATA, fl))
+ next_states[ST_DATA] |= NX_SKIP;
+ if (bitset(SMFIP_NOUNKNOWN, fl))
+ next_states[ST_UNKN] |= NX_SKIP;
+}
+
+/*
+** DEC_ARGV -- split a buffer into a list of strings, NULL terminated
+**
+** Parameters:
+** buf -- buffer with several strings
+** len -- length of buffer
+**
+** Returns:
+** array of pointers to the individual strings
+*/
+
+static char **
+dec_argv(buf, len)
+ char *buf;
+ size_t len;
+{
+ char **s;
+ size_t i;
+ int elem, nelem;
+
+ nelem = 0;
+ for (i = 0; i < len; i++)
+ {
+ if (buf[i] == '\0')
+ ++nelem;
+ }
+ if (nelem == 0)
+ return NULL;
+
+ /* last entry is only for the name */
+ s = (char **)malloc((nelem + 1) * (sizeof *s));
+ if (s == NULL)
+ return NULL;
+ s[0] = buf;
+ for (i = 0, elem = 0; i < len && elem < nelem; i++)
+ {
+ if (buf[i] == '\0')
+ {
+ ++elem;
+ if (i + 1 >= len)
+ s[elem] = NULL;
+ else
+ s[elem] = &(buf[i + 1]);
+ }
+ }
+
+ /* overwrite last entry (already done above, just paranoia) */
+ s[elem] = NULL;
+ return s;
+}
+
+/*
+** DEC_ARG2 -- split a buffer into two strings
+**
+** Parameters:
+** buf -- buffer with two strings
+** len -- length of buffer
+** s1,s2 -- pointer to result strings
+**
+** Returns:
+** MI_FAILURE/MI_SUCCESS
+*/
+
+static int
+dec_arg2(buf, len, s1, s2)
+ char *buf;
+ size_t len;
+ char **s1;
+ char **s2;
+{
+ size_t i;
+
+ /* paranoia: check for terminating '\0' */
+ if (len == 0 || buf[len - 1] != '\0')
+ return MI_FAILURE;
+ *s1 = buf;
+ for (i = 1; i < len && buf[i] != '\0'; i++)
+ continue;
+ if (i >= len - 1)
+ return MI_FAILURE;
+ *s2 = buf + i + 1;
+ return MI_SUCCESS;
+}
+
+/*
+** SENDOK -- is it ok for the filter to send stuff to the MTA?
+**
+** Parameters:
+** ctx -- context structure
+** flag -- flag to check
+**
+** Returns:
+** sending allowed (in current state)
+*/
+
+bool
+mi_sendok(ctx, flag)
+ SMFICTX_PTR ctx;
+ int flag;
+{
+ if (ctx == NULL || ctx->ctx_smfi == NULL)
+ return false;
+
+ /* did the milter request this operation? */
+ if (flag != 0 && !bitset(flag, ctx->ctx_aflags))
+ return false;
+
+ /* are we in the correct state? It must be "End of Message". */
+ return ctx->ctx_state == ST_ENDM;
+}
+
+#if _FFR_WORKERS_POOL
+/*
+** MI_RD_SOCKET_READY - checks if the socket is ready for read(2)
+**
+** Parameters:
+** sd -- socket_t
+**
+** Returns:
+** true iff socket is ready for read(2)
+*/
+
+#define MI_RD_CMD_TO 1
+#define MI_RD_MAX_ERR 16
+
+static bool
+mi_rd_socket_ready (sd)
+ socket_t sd;
+{
+ int n;
+ int nerr = 0;
+#if SM_CONF_POLL
+ struct pollfd pfd;
+#else /* SM_CONF_POLL */
+ fd_set rd_set, exc_set;
+#endif /* SM_CONF_POLL */
+
+ do
+ {
+#if SM_CONF_POLL
+ pfd.fd = sd;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ n = poll(&pfd, 1, MI_RD_CMD_TO);
+#else /* SM_CONF_POLL */
+ struct timeval timeout;
+
+ FD_ZERO(&rd_set);
+ FD_ZERO(&exc_set);
+ FD_SET(sd, &rd_set);
+ FD_SET(sd, &exc_set);
+
+ timeout.tv_sec = MI_RD_CMD_TO / 1000;
+ timeout.tv_usec = 0;
+ n = select(sd + 1, &rd_set, NULL, &exc_set, &timeout);
+#endif /* SM_CONF_POLL */
+
+ if (n < 0)
+ {
+ if (errno == EINTR)
+ {
+ nerr++;
+ continue;
+ }
+ return true;
+ }
+
+ if (n == 0)
+ return false;
+ break;
+ } while (nerr < MI_RD_MAX_ERR);
+ if (nerr >= MI_RD_MAX_ERR)
+ return false;
+
+#if SM_CONF_POLL
+ return (pfd.revents != 0);
+#else /* SM_CONF_POLL */
+ return FD_ISSET(sd, &rd_set) || FD_ISSET(sd, &exc_set);
+#endif /* SM_CONF_POLL */
+}
+#endif /* _FFR_WORKERS_POOL */
diff --git a/contrib/sendmail/libmilter/example.c b/contrib/sendmail/libmilter/example.c
new file mode 100644
index 0000000..cef4b0f
--- /dev/null
+++ b/contrib/sendmail/libmilter/example.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * $Id: example.c,v 8.4 2008/07/22 15:12:47 ca Exp $
+ */
+
+/*
+** A trivial example filter that logs all email to a file.
+** This milter also has some callbacks which it does not really use,
+** but they are defined to serve as an example.
+*/
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "libmilter/mfapi.h"
+#include "libmilter/mfdef.h"
+
+#ifndef true
+# define false 0
+# define true 1
+#endif /* ! true */
+
+struct mlfiPriv
+{
+ char *mlfi_fname;
+ FILE *mlfi_fp;
+};
+
+#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
+
+static unsigned long mta_caps = 0;
+
+sfsistat
+mlfi_cleanup(ctx, ok)
+ SMFICTX *ctx;
+ bool ok;
+{
+ sfsistat rstat = SMFIS_CONTINUE;
+ struct mlfiPriv *priv = MLFIPRIV;
+ char *p;
+ char host[512];
+ char hbuf[1024];
+
+ if (priv == NULL)
+ return rstat;
+
+ /* close the archive file */
+ if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
+ {
+ /* failed; we have to wait until later */
+ rstat = SMFIS_TEMPFAIL;
+ (void) unlink(priv->mlfi_fname);
+ }
+ else if (ok)
+ {
+ /* add a header to the message announcing our presence */
+ if (gethostname(host, sizeof host) < 0)
+ snprintf(host, sizeof host, "localhost");
+ p = strrchr(priv->mlfi_fname, '/');
+ if (p == NULL)
+ p = priv->mlfi_fname;
+ else
+ p++;
+ snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
+ smfi_addheader(ctx, "X-Archived", hbuf);
+ }
+ else
+ {
+ /* message was aborted -- delete the archive file */
+ (void) unlink(priv->mlfi_fname);
+ }
+
+ /* release private memory */
+ free(priv->mlfi_fname);
+ free(priv);
+ smfi_setpriv(ctx, NULL);
+
+ /* return status */
+ return rstat;
+}
+
+
+sfsistat
+mlfi_envfrom(ctx, envfrom)
+ SMFICTX *ctx;
+ char **envfrom;
+{
+ struct mlfiPriv *priv;
+ int fd = -1;
+
+ /* allocate some private memory */
+ priv = malloc(sizeof *priv);
+ if (priv == NULL)
+ {
+ /* can't accept this message right now */
+ return SMFIS_TEMPFAIL;
+ }
+ memset(priv, '\0', sizeof *priv);
+
+ /* open a file to store this message */
+ priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
+ if (priv->mlfi_fname == NULL)
+ {
+ free(priv);
+ return SMFIS_TEMPFAIL;
+ }
+ if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
+ (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
+ {
+ if (fd >= 0)
+ (void) close(fd);
+ free(priv->mlfi_fname);
+ free(priv);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* save the private data */
+ smfi_setpriv(ctx, priv);
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_header(ctx, headerf, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ char *headerv;
+{
+ /* write the header to the log file */
+ fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
+
+ /* continue processing */
+ return ((mta_caps & SMFIP_NR_HDR) != 0)
+ ? SMFIS_NOREPLY : SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_eoh(ctx)
+ SMFICTX *ctx;
+{
+ /* output the blank line between the header and the body */
+ fprintf(MLFIPRIV->mlfi_fp, "\r\n");
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_body(ctx, bodyp, bodylen)
+ SMFICTX *ctx;
+ u_char *bodyp;
+ size_t bodylen;
+{
+ /* output body block to log file */
+ if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
+ {
+ /* write failed */
+ (void) mlfi_cleanup(ctx, false);
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* continue processing */
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_eom(ctx)
+ SMFICTX *ctx;
+{
+ return mlfi_cleanup(ctx, true);
+}
+
+sfsistat
+mlfi_close(ctx)
+ SMFICTX *ctx;
+{
+ return SMFIS_ACCEPT;
+}
+
+sfsistat
+mlfi_abort(ctx)
+ SMFICTX *ctx;
+{
+ return mlfi_cleanup(ctx, false);
+}
+
+sfsistat
+mlfi_unknown(ctx, cmd)
+ SMFICTX *ctx;
+ char *cmd;
+{
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_data(ctx)
+ SMFICTX *ctx;
+{
+ return SMFIS_CONTINUE;
+}
+
+sfsistat
+mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
+ SMFICTX *ctx;
+ unsigned long f0;
+ unsigned long f1;
+ unsigned long f2;
+ unsigned long f3;
+ unsigned long *pf0;
+ unsigned long *pf1;
+ unsigned long *pf2;
+ unsigned long *pf3;
+{
+ /* milter actions: add headers */
+ *pf0 = SMFIF_ADDHDRS;
+
+ /* milter protocol steps: all but connect, HELO, RCPT */
+ *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
+ mta_caps = f1;
+ if ((mta_caps & SMFIP_NR_HDR) != 0)
+ *pf1 |= SMFIP_NR_HDR;
+ *pf2 = 0;
+ *pf3 = 0;
+ return SMFIS_CONTINUE;
+}
+
+struct smfiDesc smfilter =
+{
+ "SampleFilter", /* filter name */
+ SMFI_VERSION, /* version code -- do not change */
+ SMFIF_ADDHDRS, /* flags */
+ NULL, /* connection info filter */
+ NULL, /* SMTP HELO command filter */
+ mlfi_envfrom, /* envelope sender filter */
+ NULL, /* envelope recipient filter */
+ mlfi_header, /* header filter */
+ mlfi_eoh, /* end of header */
+ mlfi_body, /* body block filter */
+ mlfi_eom, /* end of message */
+ mlfi_abort, /* message aborted */
+ mlfi_close, /* connection cleanup */
+ mlfi_unknown, /* unknown/unimplemented SMTP commands */
+ mlfi_data, /* DATA command filter */
+ mlfi_negotiate /* option negotiation at connection startup */
+};
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ bool setconn;
+ int c;
+
+ setconn = false;
+
+ /* Process command line options */
+ while ((c = getopt(argc, argv, "p:")) != -1)
+ {
+ switch (c)
+ {
+ case 'p':
+ if (optarg == NULL || *optarg == '\0')
+ {
+ (void) fprintf(stderr, "Illegal conn: %s\n",
+ optarg);
+ exit(EX_USAGE);
+ }
+ (void) smfi_setconn(optarg);
+ setconn = true;
+ break;
+
+ }
+ }
+ if (!setconn)
+ {
+ fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
+ exit(EX_USAGE);
+ }
+ if (smfi_register(smfilter) == MI_FAILURE)
+ {
+ fprintf(stderr, "smfi_register failed\n");
+ exit(EX_UNAVAILABLE);
+ }
+ return smfi_main();
+}
+
diff --git a/contrib/sendmail/libmilter/handler.c b/contrib/sendmail/libmilter/handler.c
new file mode 100644
index 0000000..2c34f1f
--- /dev/null
+++ b/contrib/sendmail/libmilter/handler.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1999-2003, 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: handler.c,v 8.39 2008/11/25 01:14:16 ca Exp $")
+
+#include "libmilter.h"
+
+#if !_FFR_WORKERS_POOL
+/*
+** HANDLE_SESSION -- Handle a connected session in its own context
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_handle_session(ctx)
+ SMFICTX_PTR ctx;
+{
+ int ret;
+
+ if (ctx == NULL)
+ return MI_FAILURE;
+ ctx->ctx_id = (sthread_t) sthread_get_id();
+
+ /*
+ ** Detach so resources are free when the thread returns.
+ ** If we ever "wait" for threads, this call must be removed.
+ */
+
+ if (pthread_detach(ctx->ctx_id) != 0)
+ ret = MI_FAILURE;
+ else
+ ret = mi_engine(ctx);
+ mi_clr_ctx(ctx);
+ ctx = NULL;
+ return ret;
+}
+#endif /* !_FFR_WORKERS_POOL */
diff --git a/contrib/sendmail/libmilter/libmilter.h b/contrib/sendmail/libmilter/libmilter.h
new file mode 100644
index 0000000..5824151
--- /dev/null
+++ b/contrib/sendmail/libmilter/libmilter.h
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1999-2003, 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ */
+
+/*
+** LIBMILTER.H -- include file for mail filter library functions
+*/
+
+#ifndef _LIBMILTER_H
+# define _LIBMILTER_H 1
+
+#include <sm/gen.h>
+
+#ifdef _DEFINE
+# define EXTERN
+# define INIT(x) = x
+SM_IDSTR(MilterlId, "@(#)$Id: libmilter.h,v 8.77 2008/11/25 18:28:18 ca Exp $")
+#else /* _DEFINE */
+# define EXTERN extern
+# define INIT(x)
+#endif /* _DEFINE */
+
+
+#include "sm/tailq.h"
+
+#define NOT_SENDMAIL 1
+#define _SOCK_ADDR union bigsockaddr
+#include "sendmail.h"
+
+#ifdef SM_ASSERT
+#undef SM_ASSERT
+#endif
+#ifndef SM_ASSERT
+#include <assert.h>
+#define SM_ASSERT(x) assert(x)
+#endif
+
+#include "libmilter/milter.h"
+
+#define MAX_MACROS_ENTRIES 7 /* max size of macro pointer array */
+
+typedef SM_TAILQ_HEAD(, smfi_str) smfi_hd_T;
+typedef struct smfi_str smfi_str_S;
+
+/*
+** Context for one milter session.
+**
+** Notes:
+** There is a 1-1 correlation between a sendmail SMTP server process,
+** an SMTP session, and an milter context. Due to the nature of SMTP
+** session handling in sendmail 8, this libmilter implementation deals
+** only with a single SMTP session per MTA - libmilter connection.
+**
+** There is no "global" context for libmilter, global variables are
+** just that (they are not "collected" in a context).
+**
+** Implementation hint:
+** macros are stored in mac_buf[] as sequence of:
+** macro_name \0 macro_value
+** (just as read from the MTA)
+** mac_ptr is a list of pointers into mac_buf to the beginning of each
+** entry, i.e., macro_name, macro_value, ...
+*/
+
+struct smfi_str
+{
+ sthread_t ctx_id; /* thread id */
+ socket_t ctx_sd; /* socket descriptor */
+ int ctx_dbg; /* debug level */
+ time_t ctx_timeout; /* timeout */
+ int ctx_state; /* state */
+ smfiDesc_ptr ctx_smfi; /* filter description */
+
+ int ctx_prot_vers; /* libmilter protocol version */
+ unsigned long ctx_aflags; /* milter action flags */
+
+ unsigned long ctx_pflags; /* milter protocol flags */
+
+ /*
+ ** milter protocol flags that are sent to the MTA;
+ ** this is the same as ctx_pflags except for those flags that
+ ** are not offered by the MTA but emulated in libmilter.
+ */
+
+ unsigned long ctx_pflags2mta;
+
+ /*
+ ** milter protocol version that is sent to the MTA;
+ ** this is the same as ctx_prot_vers unless the
+ ** MTA protocol version (ctx_mta_prot_vers) is smaller
+ ** but still "acceptable".
+ */
+
+ int ctx_prot_vers2mta;
+
+ char **ctx_mac_ptr[MAX_MACROS_ENTRIES];
+ char *ctx_mac_buf[MAX_MACROS_ENTRIES];
+ char *ctx_mac_list[MAX_MACROS_ENTRIES];
+ char *ctx_reply; /* reply code */
+ void *ctx_privdata; /* private data */
+
+ int ctx_mta_prot_vers; /* MTA protocol version */
+ unsigned long ctx_mta_pflags; /* MTA protocol flags */
+ unsigned long ctx_mta_aflags; /* MTA action flags */
+
+#if _FFR_THREAD_MONITOR
+ time_t ctx_start; /* start time of thread */
+ SM_TAILQ_ENTRY(smfi_str) ctx_mon_link;
+#endif /* _FFR_THREAD_MONITOR */
+
+#if _FFR_WORKERS_POOL
+ long ctx_sid; /* session identifier */
+ int ctx_wstate; /* state of the session (worker pool) */
+ int ctx_wait; /* elapsed time waiting for sm cmd */
+ SM_TAILQ_ENTRY(smfi_str) ctx_link;
+#endif /* _FFR_WORKERS_POOL */
+};
+
+# define ValidSocket(sd) ((sd) >= 0)
+# define INVALID_SOCKET (-1)
+# define closesocket close
+# define MI_SOCK_READ(s, b, l) read(s, b, l)
+# define MI_SOCK_READ_FAIL(x) ((x) < 0)
+# define MI_SOCK_WRITE(s, b, l) write(s, b, l)
+
+# define thread_create(ptid,wr,arg) pthread_create(ptid, NULL, wr, arg)
+# define sthread_get_id() pthread_self()
+
+typedef pthread_mutex_t smutex_t;
+# define smutex_init(mp) (pthread_mutex_init(mp, NULL) == 0)
+# define smutex_destroy(mp) (pthread_mutex_destroy(mp) == 0)
+# define smutex_lock(mp) (pthread_mutex_lock(mp) == 0)
+# define smutex_unlock(mp) (pthread_mutex_unlock(mp) == 0)
+# define smutex_trylock(mp) (pthread_mutex_trylock(mp) == 0)
+
+#if _FFR_WORKERS_POOL
+/* SM_CONF_POLL shall be defined with _FFR_WORKERS_POOL */
+# if !SM_CONF_POLL
+# define SM_CONF_POLL 1
+# endif /* SM_CONF_POLL */
+#endif /* _FFR_WORKERS_POOL */
+
+typedef pthread_cond_t scond_t;
+#define scond_init(cp) pthread_cond_init(cp, NULL)
+#define scond_destroy(cp) pthread_cond_destroy(cp)
+#define scond_wait(cp, mp) pthread_cond_wait(cp, mp)
+#define scond_signal(cp) pthread_cond_signal(cp)
+#define scond_broadcast(cp) pthread_cond_broadcast(cp)
+#define scond_timedwait(cp, mp, to) \
+ do \
+ { \
+ struct timespec timeout; \
+ struct timeval now; \
+ gettimeofday(&now, NULL); \
+ timeout.tv_sec = now.tv_sec + to; \
+ timeout.tv_nsec = now.tv_usec / 1000; \
+ r = pthread_cond_timedwait(cp,mp,&timeout); \
+ if (r != 0 && r != ETIMEDOUT) \
+ smi_log(SMI_LOG_ERR, \
+ "pthread_cond_timedwait error %d", r); \
+ } while (0)
+
+
+#if SM_CONF_POLL
+
+# include <poll.h>
+# define MI_POLLSELECT "poll"
+
+# define MI_POLL_RD_FLAGS (POLLIN | POLLPRI)
+# define MI_POLL_WR_FLAGS (POLLOUT)
+# define MI_MS(timeout) (((timeout)->tv_sec * 1000) + (timeout)->tv_usec)
+
+# define FD_RD_VAR(rds, excs) struct pollfd rds
+# define FD_WR_VAR(wrs) struct pollfd wrs
+
+# define FD_RD_INIT(sd, rds, excs) \
+ (rds).fd = (sd); \
+ (rds).events = MI_POLL_RD_FLAGS; \
+ (rds).revents = 0
+
+# define FD_WR_INIT(sd, wrs) \
+ (wrs).fd = (sd); \
+ (wrs).events = MI_POLL_WR_FLAGS; \
+ (wrs).revents = 0
+
+# define FD_IS_RD_EXC(sd, rds, excs) \
+ (((rds).revents & (POLLERR | POLLHUP | POLLNVAL)) != 0)
+
+# define FD_IS_WR_RDY(sd, wrs) \
+ (((wrs).revents & MI_POLL_WR_FLAGS) != 0)
+
+# define FD_IS_RD_RDY(sd, rds, excs) \
+ (((rds).revents & MI_POLL_RD_FLAGS) != 0)
+
+# define FD_WR_READY(sd, excs, timeout) \
+ poll(&(wrs), 1, MI_MS(timeout))
+
+# define FD_RD_READY(sd, rds, excs, timeout) \
+ poll(&(rds), 1, MI_MS(timeout))
+
+#else /* SM_CONF_POLL */
+
+# include <sm/fdset.h>
+# define MI_POLLSELECT "select"
+
+# define FD_RD_VAR(rds, excs) fd_set rds, excs
+# define FD_WR_VAR(wrs) fd_set wrs
+
+# define FD_RD_INIT(sd, rds, excs) \
+ FD_ZERO(&(rds)); \
+ FD_SET((unsigned int) (sd), &(rds)); \
+ FD_ZERO(&(excs)); \
+ FD_SET((unsigned int) (sd), &(excs))
+
+# define FD_WR_INIT(sd, wrs) \
+ FD_ZERO(&(wrs)); \
+ FD_SET((unsigned int) (sd), &(wrs))
+
+# define FD_IS_RD_EXC(sd, rds, excs) FD_ISSET(sd, &(excs))
+# define FD_IS_WR_RDY(sd, wrs) FD_ISSET((sd), &(wrs))
+# define FD_IS_RD_RDY(sd, rds, excs) FD_ISSET((sd), &(rds))
+
+# define FD_WR_READY(sd, wrs, timeout) \
+ select((sd) + 1, NULL, &(wrs), NULL, (timeout))
+# define FD_RD_READY(sd, rds, excs, timeout) \
+ select((sd) + 1, &(rds), NULL, &(excs), (timeout))
+
+#endif /* SM_CONF_POLL */
+
+#include <sys/time.h>
+
+/* some defaults */
+#define MI_TIMEOUT 7210 /* default timeout for read/write */
+#define MI_CHK_TIME 5 /* checking whether to terminate */
+
+#ifndef MI_SOMAXCONN
+# if SOMAXCONN > 20
+# define MI_SOMAXCONN SOMAXCONN
+# else /* SOMAXCONN */
+# define MI_SOMAXCONN 20
+# endif /* SOMAXCONN */
+#endif /* ! MI_SOMAXCONN */
+
+/* maximum number of repeated failures in mi_listener() */
+#define MAX_FAILS_M 16 /* malloc() */
+#define MAX_FAILS_T 16 /* thread creation */
+#define MAX_FAILS_A 16 /* accept() */
+#define MAX_FAILS_S 16 /* select() */
+
+/* internal "commands", i.e., error codes */
+#define SMFIC_TIMEOUT ((char) 1) /* timeout */
+#define SMFIC_SELECT ((char) 2) /* select error */
+#define SMFIC_MALLOC ((char) 3) /* malloc error */
+#define SMFIC_RECVERR ((char) 4) /* recv() error */
+#define SMFIC_EOF ((char) 5) /* eof */
+#define SMFIC_UNKNERR ((char) 6) /* unknown error */
+#define SMFIC_TOOBIG ((char) 7) /* body chunk too big */
+#define SMFIC_VALIDCMD ' ' /* first valid command */
+
+/* hack */
+#define smi_log syslog
+#define sm_dprintf (void) printf
+#define milter_ret int
+#define SMI_LOG_ERR LOG_ERR
+#define SMI_LOG_FATAL LOG_ERR
+#define SMI_LOG_WARN LOG_WARNING
+#define SMI_LOG_INFO LOG_INFO
+#define SMI_LOG_DEBUG LOG_DEBUG
+
+/* stop? */
+#define MILTER_CONT 0
+#define MILTER_STOP 1
+#define MILTER_ABRT 2
+
+/* functions */
+extern int mi_handle_session __P((SMFICTX_PTR));
+extern int mi_engine __P((SMFICTX_PTR));
+extern int mi_listener __P((char *, int, smfiDesc_ptr, time_t, int));
+extern void mi_clr_macros __P((SMFICTX_PTR, int));
+extern void mi_clr_ctx __P((SMFICTX_PTR));
+extern int mi_stop __P((void));
+extern int mi_control_startup __P((char *));
+extern void mi_stop_milters __P((int));
+extern void mi_clean_signals __P((void));
+extern struct hostent *mi_gethostbyname __P((char *, int));
+extern int mi_inet_pton __P((int, const char *, void *));
+extern void mi_closener __P((void));
+extern int mi_opensocket __P((char *, int, int, bool, smfiDesc_ptr));
+
+/* communication functions */
+extern char *mi_rd_cmd __P((socket_t, struct timeval *, char *, size_t *, char *));
+extern int mi_wr_cmd __P((socket_t, struct timeval *, int, char *, size_t));
+extern bool mi_sendok __P((SMFICTX_PTR, int));
+
+
+#if _FFR_THREAD_MONITOR
+extern bool Monitor;
+
+#define MI_MONITOR_INIT() mi_monitor_init()
+#define MI_MONITOR_BEGIN(ctx, cmd) \
+ do \
+ { \
+ if (Monitor) \
+ mi_monitor_work_begin(ctx, cmd);\
+ } while (0)
+
+#define MI_MONITOR_END(ctx, cmd) \
+ do \
+ { \
+ if (Monitor) \
+ mi_monitor_work_end(ctx, cmd); \
+ } while (0)
+
+int mi_monitor_init __P((void));
+int mi_monitor_work_begin __P((SMFICTX_PTR, int));
+int mi_monitor_work_end __P((SMFICTX_PTR, int));
+
+#else /* _FFR_THREAD_MONITOR */
+#define MI_MONITOR_INIT() MI_SUCCESS
+#define MI_MONITOR_BEGIN(ctx, cmd)
+#define MI_MONITOR_END(ctx, cmd)
+#endif /* _FFR_THREAD_MONITOR */
+
+#if _FFR_WORKERS_POOL
+extern int mi_pool_manager_init __P((void));
+extern int mi_pool_controller_init __P((void));
+extern int mi_start_session __P((SMFICTX_PTR));
+#endif /* _FFR_WORKERS_POOL */
+
+#endif /* ! _LIBMILTER_H */
diff --git a/contrib/sendmail/libmilter/listener.c b/contrib/sendmail/libmilter/listener.c
new file mode 100644
index 0000000..48c552f
--- /dev/null
+++ b/contrib/sendmail/libmilter/listener.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: listener.c,v 8.126 2009/12/16 16:40:23 ca Exp $")
+
+/*
+** listener.c -- threaded network listener
+*/
+
+#include "libmilter.h"
+#include <sm/errstring.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+# if NETINET || NETINET6
+# include <arpa/inet.h>
+# endif /* NETINET || NETINET6 */
+# if SM_CONF_POLL
+# undef SM_FD_OK_SELECT
+# define SM_FD_OK_SELECT(fd) true
+# endif /* SM_CONF_POLL */
+
+static smutex_t L_Mutex;
+static int L_family;
+static SOCKADDR_LEN_T L_socksize;
+static socket_t listenfd = INVALID_SOCKET;
+
+static socket_t mi_milteropen __P((char *, int, bool, char *));
+#if !_FFR_WORKERS_POOL
+static void *mi_thread_handle_wrapper __P((void *));
+#endif /* !_FFR_WORKERS_POOL */
+
+/*
+** MI_OPENSOCKET -- create the socket where this filter and the MTA will meet
+**
+** Parameters:
+** conn -- connection description
+** backlog -- listen backlog
+** dbg -- debug level
+** rmsocket -- if true, try to unlink() the socket first
+** (UNIX domain sockets only)
+** smfi -- filter structure to use
+**
+** Return value:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_opensocket(conn, backlog, dbg, rmsocket, smfi)
+ char *conn;
+ int backlog;
+ int dbg;
+ bool rmsocket;
+ smfiDesc_ptr smfi;
+{
+ if (smfi == NULL || conn == NULL)
+ return MI_FAILURE;
+
+ if (ValidSocket(listenfd))
+ return MI_SUCCESS;
+
+ if (dbg > 0)
+ {
+ smi_log(SMI_LOG_DEBUG,
+ "%s: Opening listen socket on conn %s",
+ smfi->xxfi_name, conn);
+ }
+ (void) smutex_init(&L_Mutex);
+ (void) smutex_lock(&L_Mutex);
+ listenfd = mi_milteropen(conn, backlog, rmsocket, smfi->xxfi_name);
+ if (!ValidSocket(listenfd))
+ {
+ smi_log(SMI_LOG_FATAL,
+ "%s: Unable to create listening socket on conn %s",
+ smfi->xxfi_name, conn);
+ (void) smutex_unlock(&L_Mutex);
+ return MI_FAILURE;
+ }
+ if (!SM_FD_OK_SELECT(listenfd))
+ {
+ smi_log(SMI_LOG_ERR, "%s: fd %d is larger than FD_SETSIZE %d",
+ smfi->xxfi_name, listenfd, FD_SETSIZE);
+ (void) smutex_unlock(&L_Mutex);
+ return MI_FAILURE;
+ }
+ (void) smutex_unlock(&L_Mutex);
+ return MI_SUCCESS;
+}
+
+/*
+** MI_MILTEROPEN -- setup socket to listen on
+**
+** Parameters:
+** conn -- connection description
+** backlog -- listen backlog
+** rmsocket -- if true, try to unlink() the socket first
+** (UNIX domain sockets only)
+** name -- name for logging
+**
+** Returns:
+** socket upon success, error code otherwise.
+**
+** Side effect:
+** sets sockpath if UNIX socket.
+*/
+
+#if NETUNIX
+static char *sockpath = NULL;
+#endif /* NETUNIX */
+
+static socket_t
+mi_milteropen(conn, backlog, rmsocket, name)
+ char *conn;
+ int backlog;
+ bool rmsocket;
+ char *name;
+{
+ socket_t sock;
+ int sockopt = 1;
+ int fdflags;
+ size_t len = 0;
+ char *p;
+ char *colon;
+ char *at;
+ SOCKADDR addr;
+
+ if (conn == NULL || conn[0] == '\0')
+ {
+ smi_log(SMI_LOG_ERR, "%s: empty or missing socket information",
+ name);
+ return INVALID_SOCKET;
+ }
+ (void) memset(&addr, '\0', sizeof addr);
+
+ /* protocol:filename or protocol:port@host */
+ p = conn;
+ colon = strchr(p, ':');
+ if (colon != NULL)
+ {
+ *colon = '\0';
+
+ if (*p == '\0')
+ {
+#if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ L_socksize = sizeof (struct sockaddr_un);
+#else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+ L_socksize = sizeof addr.sin;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+ L_socksize = sizeof addr.sin6;
+# else /* NETINET6 */
+ /* no protocols available */
+ smi_log(SMI_LOG_ERR,
+ "%s: no valid socket protocols available",
+ name);
+ return INVALID_SOCKET;
+# endif /* NETINET6 */
+# endif /* NETINET */
+#endif /* NETUNIX */
+ }
+#if NETUNIX
+ else if (strcasecmp(p, "unix") == 0 ||
+ strcasecmp(p, "local") == 0)
+ {
+ addr.sa.sa_family = AF_UNIX;
+ L_socksize = sizeof (struct sockaddr_un);
+ }
+#endif /* NETUNIX */
+#if NETINET
+ else if (strcasecmp(p, "inet") == 0)
+ {
+ addr.sa.sa_family = AF_INET;
+ L_socksize = sizeof addr.sin;
+ }
+#endif /* NETINET */
+#if NETINET6
+ else if (strcasecmp(p, "inet6") == 0)
+ {
+ addr.sa.sa_family = AF_INET6;
+ L_socksize = sizeof addr.sin6;
+ }
+#endif /* NETINET6 */
+ else
+ {
+ smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
+ name, p);
+ return INVALID_SOCKET;
+ }
+ *colon++ = ':';
+ }
+ else
+ {
+ colon = p;
+#if NETUNIX
+ /* default to AF_UNIX */
+ addr.sa.sa_family = AF_UNIX;
+ L_socksize = sizeof (struct sockaddr_un);
+#else /* NETUNIX */
+# if NETINET
+ /* default to AF_INET */
+ addr.sa.sa_family = AF_INET;
+ L_socksize = sizeof addr.sin;
+# else /* NETINET */
+# if NETINET6
+ /* default to AF_INET6 */
+ addr.sa.sa_family = AF_INET6;
+ L_socksize = sizeof addr.sin6;
+# else /* NETINET6 */
+ smi_log(SMI_LOG_ERR, "%s: unknown socket type %s",
+ name, p);
+ return INVALID_SOCKET;
+# endif /* NETINET6 */
+# endif /* NETINET */
+#endif /* NETUNIX */
+ }
+
+#if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX)
+ {
+# if 0
+ long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN;
+# endif /* 0 */
+
+ at = colon;
+ len = strlen(colon) + 1;
+ if (len >= sizeof addr.sunix.sun_path)
+ {
+ errno = EINVAL;
+ smi_log(SMI_LOG_ERR, "%s: UNIX socket name %s too long",
+ name, colon);
+ return INVALID_SOCKET;
+ }
+ (void) sm_strlcpy(addr.sunix.sun_path, colon,
+ sizeof addr.sunix.sun_path);
+# if 0
+ errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff,
+ S_IRUSR|S_IWUSR, NULL);
+
+ /* if not safe, don't create */
+ if (errno != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: UNIX socket name %s unsafe",
+ name, colon);
+ return INVALID_SOCKET;
+ }
+# endif /* 0 */
+ }
+#endif /* NETUNIX */
+
+#if NETINET || NETINET6
+ if (
+# if NETINET
+ addr.sa.sa_family == AF_INET
+# endif /* NETINET */
+# if NETINET && NETINET6
+ ||
+# endif /* NETINET && NETINET6 */
+# if NETINET6
+ addr.sa.sa_family == AF_INET6
+# endif /* NETINET6 */
+ )
+ {
+ unsigned short port;
+
+ /* Parse port@host */
+ at = strchr(colon, '@');
+ if (at == NULL)
+ {
+ switch (addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ addr.sin.sin_addr.s_addr = INADDR_ANY;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ addr.sin6.sin6_addr = in6addr_any;
+ break;
+# endif /* NETINET6 */
+ }
+ }
+ else
+ *at = '\0';
+
+ if (isascii(*colon) && isdigit(*colon))
+ port = htons((unsigned short) atoi(colon));
+ else
+ {
+# ifdef NO_GETSERVBYNAME
+ smi_log(SMI_LOG_ERR, "%s: invalid port number %s",
+ name, colon);
+ return INVALID_SOCKET;
+# else /* NO_GETSERVBYNAME */
+ register struct servent *sp;
+
+ sp = getservbyname(colon, "tcp");
+ if (sp == NULL)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: unknown port name %s",
+ name, colon);
+ return INVALID_SOCKET;
+ }
+ port = sp->s_port;
+# endif /* NO_GETSERVBYNAME */
+ }
+ if (at != NULL)
+ {
+ *at++ = '@';
+ if (*at == '[')
+ {
+ char *end;
+
+ end = strchr(at, ']');
+ if (end != NULL)
+ {
+ bool found = false;
+# if NETINET
+ unsigned long hid = INADDR_NONE;
+# endif /* NETINET */
+# if NETINET6
+ struct sockaddr_in6 hid6;
+# endif /* NETINET6 */
+
+ *end = '\0';
+# if NETINET
+ if (addr.sa.sa_family == AF_INET &&
+ (hid = inet_addr(&at[1])) != INADDR_NONE)
+ {
+ addr.sin.sin_addr.s_addr = hid;
+ addr.sin.sin_port = port;
+ found = true;
+ }
+# endif /* NETINET */
+# if NETINET6
+ (void) memset(&hid6, '\0', sizeof hid6);
+ if (addr.sa.sa_family == AF_INET6 &&
+ mi_inet_pton(AF_INET6, &at[1],
+ &hid6.sin6_addr) == 1)
+ {
+ addr.sin6.sin6_addr = hid6.sin6_addr;
+ addr.sin6.sin6_port = port;
+ found = true;
+ }
+# endif /* NETINET6 */
+ *end = ']';
+ if (!found)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Invalid numeric domain spec \"%s\"",
+ name, at);
+ return INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Invalid numeric domain spec \"%s\"",
+ name, at);
+ return INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ struct hostent *hp = NULL;
+
+ hp = mi_gethostbyname(at, addr.sa.sa_family);
+ if (hp == NULL)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unknown host name %s",
+ name, at);
+ return INVALID_SOCKET;
+ }
+ addr.sa.sa_family = hp->h_addrtype;
+ switch (hp->h_addrtype)
+ {
+# if NETINET
+ case AF_INET:
+ (void) memmove(&addr.sin.sin_addr,
+ hp->h_addr,
+ INADDRSZ);
+ addr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+
+# if NETINET6
+ case AF_INET6:
+ (void) memmove(&addr.sin6.sin6_addr,
+ hp->h_addr,
+ IN6ADDRSZ);
+ addr.sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+
+ default:
+ smi_log(SMI_LOG_ERR,
+ "%s: Unknown protocol for %s (%d)",
+ name, at, hp->h_addrtype);
+ return INVALID_SOCKET;
+ }
+# if NETINET6
+ freehostent(hp);
+# endif /* NETINET6 */
+ }
+ }
+ else
+ {
+ switch (addr.sa.sa_family)
+ {
+# if NETINET
+ case AF_INET:
+ addr.sin.sin_port = port;
+ break;
+# endif /* NETINET */
+# if NETINET6
+ case AF_INET6:
+ addr.sin6.sin6_port = port;
+ break;
+# endif /* NETINET6 */
+ }
+ }
+ }
+#endif /* NETINET || NETINET6 */
+
+ sock = socket(addr.sa.sa_family, SOCK_STREAM, 0);
+ if (!ValidSocket(sock))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to create new socket: %s",
+ name, sm_errstring(errno));
+ return INVALID_SOCKET;
+ }
+
+ if ((fdflags = fcntl(sock, F_GETFD, 0)) == -1 ||
+ fcntl(sock, F_SETFD, fdflags | FD_CLOEXEC) == -1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to set close-on-exec: %s", name,
+ sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ if (
+#if NETUNIX
+ addr.sa.sa_family != AF_UNIX &&
+#endif /* NETUNIX */
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
+ sizeof(sockopt)) == -1)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: set reuseaddr failed (%s)", name,
+ sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+#if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX && rmsocket)
+ {
+ struct stat s;
+
+ if (stat(colon, &s) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to stat() %s: %s",
+ name, colon, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ }
+ else if (!S_ISSOCK(s.st_mode))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: %s is not a UNIX domain socket",
+ name, colon);
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ else if (unlink(colon) != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to remove %s: %s",
+ name, colon, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ }
+#endif /* NETUNIX */
+
+ if (bind(sock, &addr.sa, L_socksize) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Unable to bind to port %s: %s",
+ name, conn, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+ if (listen(sock, backlog) < 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: listen call failed: %s", name,
+ sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+
+#if NETUNIX
+ if (addr.sa.sa_family == AF_UNIX && len > 0)
+ {
+ /*
+ ** Set global variable sockpath so the UNIX socket can be
+ ** unlink()ed at exit.
+ */
+
+ sockpath = (char *) malloc(len);
+ if (sockpath != NULL)
+ (void) sm_strlcpy(sockpath, colon, len);
+ else
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: can't malloc(%d) for sockpath: %s",
+ name, (int) len, sm_errstring(errno));
+ (void) closesocket(sock);
+ return INVALID_SOCKET;
+ }
+ }
+#endif /* NETUNIX */
+ L_family = addr.sa.sa_family;
+ return sock;
+}
+
+#if !_FFR_WORKERS_POOL
+/*
+** MI_THREAD_HANDLE_WRAPPER -- small wrapper to handle session
+**
+** Parameters:
+** arg -- argument to pass to mi_handle_session()
+**
+** Returns:
+** results from mi_handle_session()
+*/
+
+static void *
+mi_thread_handle_wrapper(arg)
+ void *arg;
+{
+ /*
+ ** Note: on some systems this generates a compiler warning:
+ ** cast to pointer from integer of different size
+ ** You can safely ignore this warning as the result of this function
+ ** is not used anywhere.
+ */
+
+ return (void *) mi_handle_session(arg);
+}
+#endif /* _FFR_WORKERS_POOL */
+
+/*
+** MI_CLOSENER -- close listen socket
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+mi_closener()
+{
+ (void) smutex_lock(&L_Mutex);
+ if (ValidSocket(listenfd))
+ {
+#if NETUNIX
+ bool removable;
+ struct stat sockinfo;
+ struct stat fileinfo;
+
+ removable = sockpath != NULL &&
+ geteuid() != 0 &&
+ fstat(listenfd, &sockinfo) == 0 &&
+ (S_ISFIFO(sockinfo.st_mode)
+# ifdef S_ISSOCK
+ || S_ISSOCK(sockinfo.st_mode)
+# endif /* S_ISSOCK */
+ );
+#endif /* NETUNIX */
+
+ (void) closesocket(listenfd);
+ listenfd = INVALID_SOCKET;
+
+#if NETUNIX
+ /* XXX sleep() some time before doing this? */
+ if (sockpath != NULL)
+ {
+ if (removable &&
+ stat(sockpath, &fileinfo) == 0 &&
+ ((fileinfo.st_dev == sockinfo.st_dev &&
+ fileinfo.st_ino == sockinfo.st_ino)
+# ifdef S_ISSOCK
+ || S_ISSOCK(fileinfo.st_mode)
+# endif /* S_ISSOCK */
+ )
+ &&
+ (S_ISFIFO(fileinfo.st_mode)
+# ifdef S_ISSOCK
+ || S_ISSOCK(fileinfo.st_mode)
+# endif /* S_ISSOCK */
+ ))
+ (void) unlink(sockpath);
+ free(sockpath);
+ sockpath = NULL;
+ }
+#endif /* NETUNIX */
+ }
+ (void) smutex_unlock(&L_Mutex);
+}
+
+/*
+** MI_LISTENER -- Generic listener harness
+**
+** Open up listen port
+** Wait for connections
+**
+** Parameters:
+** conn -- connection description
+** dbg -- debug level
+** smfi -- filter structure to use
+** timeout -- timeout for reads/writes
+** backlog -- listen queue backlog size
+**
+** Returns:
+** MI_SUCCESS -- Exited normally
+** (session finished or we were told to exit)
+** MI_FAILURE -- Network initialization failed.
+*/
+
+#if BROKEN_PTHREAD_SLEEP
+
+/*
+** Solaris 2.6, perhaps others, gets an internal threads library panic
+** when sleep() is used:
+**
+** thread_create() failed, returned 11 (EINVAL)
+** co_enable, thr_create() returned error = 24
+** libthread panic: co_enable failed (PID: 17793 LWP 1)
+** stacktrace:
+** ef526b10
+** ef52646c
+** ef534cbc
+** 156a4
+** 14644
+** 1413c
+** 135e0
+** 0
+*/
+
+# define MI_SLEEP(s) \
+{ \
+ int rs = 0; \
+ struct timeval st; \
+ \
+ st.tv_sec = (s); \
+ st.tv_usec = 0; \
+ if (st.tv_sec > 0) \
+ { \
+ for (;;) \
+ { \
+ rs = select(0, NULL, NULL, NULL, &st); \
+ if (rs < 0 && errno == EINTR) \
+ continue; \
+ if (rs != 0) \
+ { \
+ smi_log(SMI_LOG_ERR, \
+ "MI_SLEEP(): select() returned non-zero result %d, errno = %d", \
+ rs, errno); \
+ } \
+ break; \
+ } \
+ } \
+}
+#else /* BROKEN_PTHREAD_SLEEP */
+# define MI_SLEEP(s) sleep((s))
+#endif /* BROKEN_PTHREAD_SLEEP */
+
+int
+mi_listener(conn, dbg, smfi, timeout, backlog)
+ char *conn;
+ int dbg;
+ smfiDesc_ptr smfi;
+ time_t timeout;
+ int backlog;
+{
+ socket_t connfd = INVALID_SOCKET;
+#if _FFR_DUP_FD
+ socket_t dupfd = INVALID_SOCKET;
+#endif /* _FFR_DUP_FD */
+ int sockopt = 1;
+ int r, mistop;
+ int ret = MI_SUCCESS;
+ int mcnt = 0; /* error count for malloc() failures */
+ int tcnt = 0; /* error count for thread_create() failures */
+ int acnt = 0; /* error count for accept() failures */
+ int scnt = 0; /* error count for select() failures */
+ int save_errno = 0;
+#if !_FFR_WORKERS_POOL
+ sthread_t thread_id;
+#endif /* !_FFR_WORKERS_POOL */
+ _SOCK_ADDR cliaddr;
+ SOCKADDR_LEN_T clilen;
+ SMFICTX_PTR ctx;
+ FD_RD_VAR(rds, excs);
+ struct timeval chktime;
+
+ if (mi_opensocket(conn, backlog, dbg, false, smfi) == MI_FAILURE)
+ return MI_FAILURE;
+
+#if _FFR_WORKERS_POOL
+ if (mi_pool_controller_init() == MI_FAILURE)
+ return MI_FAILURE;
+#endif /* _FFR_WORKERS_POOL */
+
+ clilen = L_socksize;
+ while ((mistop = mi_stop()) == MILTER_CONT)
+ {
+ (void) smutex_lock(&L_Mutex);
+ if (!ValidSocket(listenfd))
+ {
+ ret = MI_FAILURE;
+ smi_log(SMI_LOG_ERR,
+ "%s: listenfd=%d corrupted, terminating, errno=%d",
+ smfi->xxfi_name, listenfd, errno);
+ (void) smutex_unlock(&L_Mutex);
+ break;
+ }
+
+ /* select on interface ports */
+ FD_RD_INIT(listenfd, rds, excs);
+ chktime.tv_sec = MI_CHK_TIME;
+ chktime.tv_usec = 0;
+ r = FD_RD_READY(listenfd, rds, excs, &chktime);
+ if (r == 0) /* timeout */
+ {
+ (void) smutex_unlock(&L_Mutex);
+ continue; /* just check mi_stop() */
+ }
+ if (r < 0)
+ {
+ save_errno = errno;
+ (void) smutex_unlock(&L_Mutex);
+ if (save_errno == EINTR)
+ continue;
+ scnt++;
+ smi_log(SMI_LOG_ERR,
+ "%s: %s() failed (%s), %s",
+ smfi->xxfi_name, MI_POLLSELECT,
+ sm_errstring(save_errno),
+ scnt >= MAX_FAILS_S ? "abort" : "try again");
+ MI_SLEEP(scnt);
+ if (scnt >= MAX_FAILS_S)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ if (!FD_IS_RD_RDY(listenfd, rds, excs))
+ {
+ /* some error: just stop for now... */
+ ret = MI_FAILURE;
+ (void) smutex_unlock(&L_Mutex);
+ smi_log(SMI_LOG_ERR,
+ "%s: %s() returned exception for socket, abort",
+ smfi->xxfi_name, MI_POLLSELECT);
+ break;
+ }
+ scnt = 0; /* reset error counter for select() */
+
+ (void) memset(&cliaddr, '\0', sizeof cliaddr);
+ connfd = accept(listenfd, (struct sockaddr *) &cliaddr,
+ &clilen);
+ save_errno = errno;
+ (void) smutex_unlock(&L_Mutex);
+
+ /*
+ ** If remote side closes before accept() finishes,
+ ** sockaddr might not be fully filled in.
+ */
+
+ if (ValidSocket(connfd) &&
+ (clilen == 0 ||
+# ifdef BSD4_4_SOCKADDR
+ cliaddr.sa.sa_len == 0 ||
+# endif /* BSD4_4_SOCKADDR */
+ cliaddr.sa.sa_family != L_family))
+ {
+ (void) closesocket(connfd);
+ connfd = INVALID_SOCKET;
+ save_errno = EINVAL;
+ }
+
+ /* check if acceptable for select() */
+ if (ValidSocket(connfd) && !SM_FD_OK_SELECT(connfd))
+ {
+ (void) closesocket(connfd);
+ connfd = INVALID_SOCKET;
+ save_errno = ERANGE;
+ }
+
+ if (!ValidSocket(connfd))
+ {
+ if (save_errno == EINTR
+#ifdef EAGAIN
+ || save_errno == EAGAIN
+#endif /* EAGAIN */
+#ifdef ECONNABORTED
+ || save_errno == ECONNABORTED
+#endif /* ECONNABORTED */
+#ifdef EMFILE
+ || save_errno == EMFILE
+#endif /* EMFILE */
+#ifdef ENFILE
+ || save_errno == ENFILE
+#endif /* ENFILE */
+#ifdef ENOBUFS
+ || save_errno == ENOBUFS
+#endif /* ENOBUFS */
+#ifdef ENOMEM
+ || save_errno == ENOMEM
+#endif /* ENOMEM */
+#ifdef ENOSR
+ || save_errno == ENOSR
+#endif /* ENOSR */
+#ifdef EWOULDBLOCK
+ || save_errno == EWOULDBLOCK
+#endif /* EWOULDBLOCK */
+ )
+ continue;
+ acnt++;
+ smi_log(SMI_LOG_ERR,
+ "%s: accept() returned invalid socket (%s), %s",
+ smfi->xxfi_name, sm_errstring(save_errno),
+ acnt >= MAX_FAILS_A ? "abort" : "try again");
+ MI_SLEEP(acnt);
+ if (acnt >= MAX_FAILS_A)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ acnt = 0; /* reset error counter for accept() */
+#if _FFR_DUP_FD
+ dupfd = fcntl(connfd, F_DUPFD, 256);
+ if (ValidSocket(dupfd) && SM_FD_OK_SELECT(dupfd))
+ {
+ close(connfd);
+ connfd = dupfd;
+ dupfd = INVALID_SOCKET;
+ }
+#endif /* _FFR_DUP_FD */
+
+ if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
+ (void *) &sockopt, sizeof sockopt) < 0)
+ {
+ smi_log(SMI_LOG_WARN,
+ "%s: set keepalive failed (%s)",
+ smfi->xxfi_name, sm_errstring(errno));
+ /* XXX: continue? */
+ }
+ if ((ctx = (SMFICTX_PTR) malloc(sizeof *ctx)) == NULL)
+ {
+ (void) closesocket(connfd);
+ mcnt++;
+ smi_log(SMI_LOG_ERR, "%s: malloc(ctx) failed (%s), %s",
+ smfi->xxfi_name, sm_errstring(save_errno),
+ mcnt >= MAX_FAILS_M ? "abort" : "try again");
+ MI_SLEEP(mcnt);
+ if (mcnt >= MAX_FAILS_M)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ mcnt = 0; /* reset error counter for malloc() */
+ (void) memset(ctx, '\0', sizeof *ctx);
+ ctx->ctx_sd = connfd;
+ ctx->ctx_dbg = dbg;
+ ctx->ctx_timeout = timeout;
+ ctx->ctx_smfi = smfi;
+ if (smfi->xxfi_connect == NULL)
+ ctx->ctx_pflags |= SMFIP_NOCONNECT;
+ if (smfi->xxfi_helo == NULL)
+ ctx->ctx_pflags |= SMFIP_NOHELO;
+ if (smfi->xxfi_envfrom == NULL)
+ ctx->ctx_pflags |= SMFIP_NOMAIL;
+ if (smfi->xxfi_envrcpt == NULL)
+ ctx->ctx_pflags |= SMFIP_NORCPT;
+ if (smfi->xxfi_header == NULL)
+ ctx->ctx_pflags |= SMFIP_NOHDRS;
+ if (smfi->xxfi_eoh == NULL)
+ ctx->ctx_pflags |= SMFIP_NOEOH;
+ if (smfi->xxfi_body == NULL)
+ ctx->ctx_pflags |= SMFIP_NOBODY;
+ if (smfi->xxfi_version <= 3 || smfi->xxfi_data == NULL)
+ ctx->ctx_pflags |= SMFIP_NODATA;
+ if (smfi->xxfi_version <= 2 || smfi->xxfi_unknown == NULL)
+ ctx->ctx_pflags |= SMFIP_NOUNKNOWN;
+
+#if _FFR_WORKERS_POOL
+# define LOG_CRT_FAIL "%s: mi_start_session() failed: %d, %s"
+ if ((r = mi_start_session(ctx)) != MI_SUCCESS)
+#else /* _FFR_WORKERS_POOL */
+# define LOG_CRT_FAIL "%s: thread_create() failed: %d, %s"
+ if ((r = thread_create(&thread_id,
+ mi_thread_handle_wrapper,
+ (void *) ctx)) != 0)
+#endif /* _FFR_WORKERS_POOL */
+ {
+ tcnt++;
+ smi_log(SMI_LOG_ERR,
+ LOG_CRT_FAIL,
+ smfi->xxfi_name, r,
+ tcnt >= MAX_FAILS_T ? "abort" : "try again");
+ MI_SLEEP(tcnt);
+ (void) closesocket(connfd);
+ free(ctx);
+ if (tcnt >= MAX_FAILS_T)
+ {
+ ret = MI_FAILURE;
+ break;
+ }
+ continue;
+ }
+ tcnt = 0;
+ }
+ if (ret != MI_SUCCESS)
+ mi_stop_milters(MILTER_ABRT);
+ else
+ {
+ if (mistop != MILTER_CONT)
+ smi_log(SMI_LOG_INFO, "%s: mi_stop=%d",
+ smfi->xxfi_name, mistop);
+ mi_closener();
+ }
+ (void) smutex_destroy(&L_Mutex);
+ return ret;
+}
diff --git a/contrib/sendmail/libmilter/main.c b/contrib/sendmail/libmilter/main.c
new file mode 100644
index 0000000..d6e7279
--- /dev/null
+++ b/contrib/sendmail/libmilter/main.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1999-2003, 2006, 2007 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: main.c,v 8.84 2008/09/02 05:37:06 ca Exp $")
+
+#define _DEFINE 1
+#include "libmilter.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+
+
+static smfiDesc_ptr smfi = NULL;
+
+/*
+** SMFI_REGISTER -- register a filter description
+**
+** Parameters:
+** smfilter -- description of filter to register
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_register(smfilter)
+ smfiDesc_str smfilter;
+{
+ size_t len;
+
+ if (smfi == NULL)
+ {
+ smfi = (smfiDesc_ptr) malloc(sizeof *smfi);
+ if (smfi == NULL)
+ return MI_FAILURE;
+ }
+ (void) memcpy(smfi, &smfilter, sizeof *smfi);
+ if (smfilter.xxfi_name == NULL)
+ smfilter.xxfi_name = "Unknown";
+
+ len = strlen(smfilter.xxfi_name) + 1;
+ smfi->xxfi_name = (char *) malloc(len);
+ if (smfi->xxfi_name == NULL)
+ return MI_FAILURE;
+ (void) sm_strlcpy(smfi->xxfi_name, smfilter.xxfi_name, len);
+
+ /* compare milter version with hard coded version */
+ if ((SM_LM_VRS_MAJOR(smfi->xxfi_version) != SM_LM_VRS_MAJOR(SMFI_VERSION) ||
+ SM_LM_VRS_MINOR(smfi->xxfi_version) != SM_LM_VRS_MINOR(SMFI_VERSION)) &&
+ smfi->xxfi_version != 2 &&
+ smfi->xxfi_version != 3 &&
+ smfi->xxfi_version != 4)
+ {
+ /* hard failure for now! */
+ smi_log(SMI_LOG_ERR,
+ "%s: smfi_register: version mismatch application: %d != milter: %d",
+ smfi->xxfi_name, smfi->xxfi_version,
+ (int) SMFI_VERSION);
+
+ /* XXX how about smfi? */
+ free(smfi->xxfi_name);
+ return MI_FAILURE;
+ }
+
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_STOP -- stop milter
+**
+** Parameters:
+** none.
+**
+** Returns:
+** success.
+*/
+
+int
+smfi_stop()
+{
+ mi_stop_milters(MILTER_STOP);
+ return MI_SUCCESS;
+}
+
+/*
+** Default values for some variables.
+** Most of these can be changed with the functions below.
+*/
+
+static int dbg = 0;
+static char *conn = NULL;
+static int timeout = MI_TIMEOUT;
+static int backlog = MI_SOMAXCONN;
+
+/*
+** SMFI_OPENSOCKET -- try the socket setup to make sure we'll be
+** able to start up
+**
+** Parameters:
+** rmsocket -- if true, instructs libmilter to attempt
+** to remove the socket before creating it;
+** only applies for "local:" or "unix:" sockets
+**
+** Return:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_opensocket(rmsocket)
+ bool rmsocket;
+{
+ if (smfi == NULL || conn == NULL)
+ return MI_FAILURE;
+
+ return mi_opensocket(conn, backlog, dbg, rmsocket, smfi);
+}
+
+/*
+** SMFI_SETDBG -- set debug level.
+**
+** Parameters:
+** odbg -- new debug level.
+**
+** Returns:
+** MI_SUCCESS
+*/
+
+int
+smfi_setdbg(odbg)
+ int odbg;
+{
+ dbg = odbg;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETTIMEOUT -- set timeout (for read/write).
+**
+** Parameters:
+** otimeout -- new timeout.
+**
+** Returns:
+** MI_SUCCESS
+*/
+
+int
+smfi_settimeout(otimeout)
+ int otimeout;
+{
+ timeout = otimeout;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETCONN -- set connection information (socket description)
+**
+** Parameters:
+** oconn -- new connection information.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setconn(oconn)
+ char *oconn;
+{
+ size_t l;
+
+ if (oconn == NULL || *oconn == '\0')
+ return MI_FAILURE;
+ l = strlen(oconn) + 1;
+ if ((conn = (char *) malloc(l)) == NULL)
+ return MI_FAILURE;
+ if (sm_strlcpy(conn, oconn, l) >= l)
+ return MI_FAILURE;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETBACKLOG -- set backlog
+**
+** Parameters:
+** obacklog -- new backlog.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setbacklog(obacklog)
+ int obacklog;
+{
+ if (obacklog <= 0)
+ return MI_FAILURE;
+ backlog = obacklog;
+ return MI_SUCCESS;
+}
+
+
+/*
+** SMFI_MAIN -- setup milter connnection and start listener.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_main()
+{
+ int r;
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ if (conn == NULL)
+ {
+ smi_log(SMI_LOG_FATAL, "%s: missing connection information",
+ smfi->xxfi_name);
+ return MI_FAILURE;
+ }
+
+ (void) atexit(mi_clean_signals);
+ if (mi_control_startup(smfi->xxfi_name) != MI_SUCCESS)
+ {
+ smi_log(SMI_LOG_FATAL,
+ "%s: Couldn't start signal thread",
+ smfi->xxfi_name);
+ return MI_FAILURE;
+ }
+ r = MI_MONITOR_INIT();
+
+ /* Startup the listener */
+ if (mi_listener(conn, dbg, smfi, timeout, backlog) != MI_SUCCESS)
+ r = MI_FAILURE;
+
+ return r;
+}
+
diff --git a/contrib/sendmail/libmilter/monitor.c b/contrib/sendmail/libmilter/monitor.c
new file mode 100644
index 0000000..366cf75
--- /dev/null
+++ b/contrib/sendmail/libmilter/monitor.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: monitor.c,v 8.7 2007/04/23 16:26:28 ca Exp $")
+#include "libmilter.h"
+
+#if _FFR_THREAD_MONITOR
+
+/*
+** Thread Monitoring
+** Todo: more error checking (return code from function calls)
+** add comments.
+*/
+
+bool Monitor = false; /* use monitoring? */
+static unsigned int Mon_exec_time = 0;
+
+/* mutex protects Mon_cur_ctx, Mon_ctx_head, and ctx_start */
+static smutex_t Mon_mutex;
+static scond_t Mon_cv;
+
+/*
+** Current ctx to monitor.
+** Invariant:
+** Mon_cur_ctx == NULL || Mon_cur_ctx is thread which was started the longest
+** time ago.
+**
+** Basically the entries in the list are ordered by time because new
+** entries are appended at the end. However, due to the concurrent
+** execution (multi-threaded) and no guaranteed order of wakeups
+** after a mutex_lock() attempt, the order might not be strict,
+** i.e., if the list contains e1 and e2 (in that order) then
+** the the start time of e2 can be (slightly) smaller than that of e1.
+** However, this slight inaccurracy should not matter for the proper
+** working of this algorithm.
+*/
+
+static SMFICTX_PTR Mon_cur_ctx = NULL;
+static smfi_hd_T Mon_ctx_head; /* head of the linked list of active contexts */
+
+/*
+** SMFI_SET_MAX_EXEC_TIME -- set maximum execution time for a thread
+**
+** Parameters:
+** tm -- maximum execution time for a thread
+**
+** Returns:
+** MI_SUCCESS
+*/
+
+int
+smfi_set_max_exec_time(tm)
+ unsigned int tm;
+{
+ Mon_exec_time = tm;
+ return MI_SUCCESS;
+}
+
+/*
+** MI_MONITOR_THREAD -- monitoring thread
+**
+** Parameters:
+** arg -- ignored (required by pthread_create())
+**
+** Returns:
+** NULL on termination.
+*/
+
+static void *
+mi_monitor_thread(arg)
+ void *arg;
+{
+ sthread_t tid;
+ int r;
+ time_t now, end;
+
+ SM_ASSERT(Monitor);
+ SM_ASSERT(Mon_exec_time > 0);
+ tid = (sthread_t) sthread_get_id();
+ if (pthread_detach(tid) != 0)
+ {
+ /* log an error */
+ return (void *)1;
+ }
+
+/*
+** NOTE: this is "flow through" code,
+** do NOT use do { } while ("break" is used here!)
+*/
+
+#define MON_CHK_STOP \
+ now = time(NULL); \
+ end = Mon_cur_ctx->ctx_start + Mon_exec_time; \
+ if (now > end) \
+ { \
+ smi_log(SMI_LOG_ERR, \
+ "WARNING: monitor timeout triggered, now=%ld, end=%ld, tid=%ld, state=0x%x",\
+ (long) now, (long) end, \
+ (long) Mon_cur_ctx->ctx_id, Mon_cur_ctx->ctx_state);\
+ mi_stop_milters(MILTER_STOP); \
+ break; \
+ }
+
+ (void) smutex_lock(&Mon_mutex);
+ while (mi_stop() == MILTER_CONT)
+ {
+ if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
+ {
+ struct timespec abstime;
+
+ MON_CHK_STOP;
+ abstime.tv_sec = end;
+ abstime.tv_nsec = 0;
+ r = pthread_cond_timedwait(&Mon_cv, &Mon_mutex,
+ &abstime);
+ }
+ else
+ r = pthread_cond_wait(&Mon_cv, &Mon_mutex);
+ if (mi_stop() != MILTER_CONT)
+ break;
+ if (Mon_cur_ctx != NULL && Mon_cur_ctx->ctx_start > 0)
+ {
+ MON_CHK_STOP;
+ }
+ }
+ (void) smutex_unlock(&Mon_mutex);
+
+ return NULL;
+}
+
+/*
+** MI_MONITOR_INIT -- initialize monitoring thread
+**
+** Parameters: none
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_monitor_init()
+{
+ int r;
+ sthread_t tid;
+
+ SM_ASSERT(!Monitor);
+ if (Mon_exec_time <= 0)
+ return MI_SUCCESS;
+ Monitor = true;
+ if (!smutex_init(&Mon_mutex))
+ return MI_FAILURE;
+ if (scond_init(&Mon_cv) != 0)
+ return MI_FAILURE;
+ SM_TAILQ_INIT(&Mon_ctx_head);
+
+ r = thread_create(&tid, mi_monitor_thread, (void *)NULL);
+ if (r != 0)
+ return r;
+ return MI_SUCCESS;
+}
+
+/*
+** MI_MONITOR_WORK_BEGIN -- record start of thread execution
+**
+** Parameters:
+** ctx -- session context
+** cmd -- milter command char
+**
+** Returns:
+** 0
+*/
+
+int
+mi_monitor_work_begin(ctx, cmd)
+ SMFICTX_PTR ctx;
+ int cmd;
+{
+ (void) smutex_lock(&Mon_mutex);
+ if (NULL == Mon_cur_ctx)
+ {
+ Mon_cur_ctx = ctx;
+ (void) scond_signal(&Mon_cv);
+ }
+ ctx->ctx_start = time(NULL);
+ SM_TAILQ_INSERT_TAIL(&Mon_ctx_head, ctx, ctx_mon_link);
+ (void) smutex_unlock(&Mon_mutex);
+ return 0;
+}
+
+/*
+** MI_MONITOR_WORK_END -- record end of thread execution
+**
+** Parameters:
+** ctx -- session context
+** cmd -- milter command char
+**
+** Returns:
+** 0
+*/
+
+int
+mi_monitor_work_end(ctx, cmd)
+ SMFICTX_PTR ctx;
+ int cmd;
+{
+ (void) smutex_lock(&Mon_mutex);
+ ctx->ctx_start = 0;
+ SM_TAILQ_REMOVE(&Mon_ctx_head, ctx, ctx_mon_link);
+ if (Mon_cur_ctx == ctx)
+ {
+ if (SM_TAILQ_EMPTY(&Mon_ctx_head))
+ Mon_cur_ctx = NULL;
+ else
+ Mon_cur_ctx = SM_TAILQ_FIRST(&Mon_ctx_head);
+ }
+ (void) smutex_unlock(&Mon_mutex);
+ return 0;
+}
+#endif /* _FFR_THREAD_MONITOR */
diff --git a/contrib/sendmail/libmilter/signal.c b/contrib/sendmail/libmilter/signal.c
new file mode 100644
index 0000000..ad68469
--- /dev/null
+++ b/contrib/sendmail/libmilter/signal.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1999-2004, 2006 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: signal.c,v 8.44 2006/03/03 03:42:04 ca Exp $")
+
+#include "libmilter.h"
+
+/*
+** thread to handle signals
+*/
+
+static smutex_t M_Mutex;
+
+static int MilterStop = MILTER_CONT;
+
+static void *mi_signal_thread __P((void *));
+static int mi_spawn_signal_thread __P((char *));
+
+/*
+** MI_STOP -- return value of MilterStop
+**
+** Parameters:
+** none.
+**
+** Returns:
+** value of MilterStop
+*/
+
+int
+mi_stop()
+{
+ return MilterStop;
+}
+/*
+** MI_STOP_MILTERS -- set value of MilterStop
+**
+** Parameters:
+** v -- new value for MilterStop.
+**
+** Returns:
+** none.
+*/
+
+void
+mi_stop_milters(v)
+ int v;
+{
+ (void) smutex_lock(&M_Mutex);
+ if (MilterStop < v)
+ MilterStop = v;
+
+ /* close listen socket */
+ mi_closener();
+ (void) smutex_unlock(&M_Mutex);
+}
+/*
+** MI_CLEAN_SIGNALS -- clean up signal handler thread
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+*/
+
+void
+mi_clean_signals()
+{
+ (void) smutex_destroy(&M_Mutex);
+}
+/*
+** MI_SIGNAL_THREAD -- thread to deal with signals
+**
+** Parameters:
+** name -- name of milter
+**
+** Returns:
+** NULL
+*/
+
+static void *
+mi_signal_thread(name)
+ void *name;
+{
+ int sig, errs, sigerr;
+ sigset_t set;
+
+ (void) sigemptyset(&set);
+ (void) sigaddset(&set, SIGHUP);
+ (void) sigaddset(&set, SIGTERM);
+
+ /* Handle Ctrl-C gracefully for debugging */
+ (void) sigaddset(&set, SIGINT);
+ errs = 0;
+
+ for (;;)
+ {
+ sigerr = sig = 0;
+#if defined(SOLARIS) || defined(__svr5__)
+ if ((sig = sigwait(&set)) < 0)
+#else /* defined(SOLARIS) || defined(__svr5__) */
+ if ((sigerr = sigwait(&set, &sig)) != 0)
+#endif /* defined(SOLARIS) || defined(__svr5__) */
+ {
+ /* some OS return -1 and set errno: copy it */
+ if (sigerr <= 0)
+ sigerr = errno;
+
+ /* this can happen on OSF/1 (at least) */
+ if (sigerr == EINTR)
+ continue;
+ smi_log(SMI_LOG_ERR,
+ "%s: sigwait returned error: %d",
+ (char *)name, sigerr);
+ if (++errs > MAX_FAILS_T)
+ {
+ mi_stop_milters(MILTER_ABRT);
+ return NULL;
+ }
+ continue;
+ }
+ errs = 0;
+
+ switch (sig)
+ {
+ case SIGHUP:
+ case SIGTERM:
+ mi_stop_milters(MILTER_STOP);
+ return NULL;
+ case SIGINT:
+ mi_stop_milters(MILTER_ABRT);
+ return NULL;
+ default:
+ smi_log(SMI_LOG_ERR,
+ "%s: sigwait returned unmasked signal: %d",
+ (char *)name, sig);
+ break;
+ }
+ }
+ /* NOTREACHED */
+}
+/*
+** MI_SPAWN_SIGNAL_THREAD -- spawn thread to handle signals
+**
+** Parameters:
+** name -- name of milter
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+mi_spawn_signal_thread(name)
+ char *name;
+{
+ sthread_t tid;
+ int r;
+ sigset_t set;
+
+ /* Mask HUP and KILL signals */
+ (void) sigemptyset(&set);
+ (void) sigaddset(&set, SIGHUP);
+ (void) sigaddset(&set, SIGTERM);
+ (void) sigaddset(&set, SIGINT);
+
+ if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't mask HUP and KILL signals", name);
+ return MI_FAILURE;
+ }
+ r = thread_create(&tid, mi_signal_thread, (void *)name);
+ if (r != 0)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't start signal thread: %d",
+ name, r);
+ return MI_FAILURE;
+ }
+ return MI_SUCCESS;
+}
+/*
+** MI_CONTROL_STARTUP -- startup for thread to handle signals
+**
+** Parameters:
+** name -- name of milter
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_control_startup(name)
+ char *name;
+{
+
+ if (!smutex_init(&M_Mutex))
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't initialize control pipe mutex", name);
+ return MI_FAILURE;
+ }
+
+ /*
+ ** spawn_signal_thread must happen before other threads are spawned
+ ** off so that it can mask the right signals and other threads
+ ** will inherit that mask.
+ */
+ if (mi_spawn_signal_thread(name) == MI_FAILURE)
+ {
+ smi_log(SMI_LOG_ERR,
+ "%s: Couldn't spawn signal thread", name);
+ (void) smutex_destroy(&M_Mutex);
+ return MI_FAILURE;
+ }
+ return MI_SUCCESS;
+}
diff --git a/contrib/sendmail/libmilter/sm_gethost.c b/contrib/sendmail/libmilter/sm_gethost.c
new file mode 100644
index 0000000..5706b89
--- /dev/null
+++ b/contrib/sendmail/libmilter/sm_gethost.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1999-2001, 2004 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: sm_gethost.c,v 8.27 2004/08/20 21:12:37 ca Exp $")
+
+#include <sendmail.h>
+#if NETINET || NETINET6
+# include <arpa/inet.h>
+#endif /* NETINET || NETINET6 */
+#include "libmilter.h"
+
+/*
+** MI_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
+**
+** Some operating systems have wierd problems with the gethostbyXXX
+** routines. For example, Solaris versions at least through 2.3
+** don't properly deliver a canonical h_name field. This tries to
+** work around these problems.
+**
+** Support IPv6 as well as IPv4.
+*/
+
+#if NETINET6 && NEEDSGETIPNODE
+
+static struct hostent *getipnodebyname __P((char *, int, int, int *));
+
+# ifndef AI_ADDRCONFIG
+# define AI_ADDRCONFIG 0 /* dummy */
+# endif /* ! AI_ADDRCONFIG */
+# ifndef AI_ALL
+# define AI_ALL 0 /* dummy */
+# endif /* ! AI_ALL */
+# ifndef AI_DEFAULT
+# define AI_DEFAULT 0 /* dummy */
+# endif /* ! AI_DEFAULT */
+
+static struct hostent *
+getipnodebyname(name, family, flags, err)
+ char *name;
+ int family;
+ int flags;
+ int *err;
+{
+ bool resv6 = true;
+ struct hostent *h;
+
+ if (family == AF_INET6)
+ {
+ /* From RFC2133, section 6.1 */
+ resv6 = bitset(RES_USE_INET6, _res.options);
+ _res.options |= RES_USE_INET6;
+ }
+ SM_SET_H_ERRNO(0);
+ h = gethostbyname(name);
+ if (family == AF_INET6 && !resv6)
+ _res.options &= ~RES_USE_INET6;
+ *err = h_errno;
+ return h;
+}
+
+void
+freehostent(h)
+ struct hostent *h;
+{
+ /*
+ ** Stub routine -- if they don't have getipnodeby*(),
+ ** they probably don't have the free routine either.
+ */
+
+ return;
+}
+#endif /* NEEDSGETIPNODE && NETINET6 */
+
+struct hostent *
+mi_gethostbyname(name, family)
+ char *name;
+ int family;
+{
+ struct hostent *h = NULL;
+#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
+# if SOLARIS == 20300 || SOLARIS == 203
+ static struct hostent hp;
+ static char buf[1000];
+ extern struct hostent *_switch_gethostbyname_r();
+
+ h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
+# else /* SOLARIS == 20300 || SOLARIS == 203 */
+ extern struct hostent *__switch_gethostbyname();
+
+ h = __switch_gethostbyname(name);
+# endif /* SOLARIS == 20300 || SOLARIS == 203 */
+#else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
+# if NETINET6
+ int flags = AI_DEFAULT|AI_ALL;
+ int err;
+# endif /* NETINET6 */
+
+# if NETINET6
+# if ADDRCONFIG_IS_BROKEN
+ flags &= ~AI_ADDRCONFIG;
+# endif /* ADDRCONFIG_IS_BROKEN */
+ h = getipnodebyname(name, family, flags, &err);
+ SM_SET_H_ERRNO(err);
+# else /* NETINET6 */
+ h = gethostbyname(name);
+# endif /* NETINET6 */
+
+#endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
+ return h;
+}
+
+#if NETINET6
+/*
+** MI_INET_PTON -- convert printed form to network address.
+**
+** Wrapper for inet_pton() which handles IPv6: labels.
+**
+** Parameters:
+** family -- address family
+** src -- string
+** dst -- destination address structure
+**
+** Returns:
+** 1 if the address was valid
+** 0 if the address wasn't parseable
+** -1 if error
+*/
+
+int
+mi_inet_pton(family, src, dst)
+ int family;
+ const char *src;
+ void *dst;
+{
+ if (family == AF_INET6 &&
+ strncasecmp(src, "IPv6:", 5) == 0)
+ src += 5;
+ return inet_pton(family, src, dst);
+}
+#endif /* NETINET6 */
diff --git a/contrib/sendmail/libmilter/smfi.c b/contrib/sendmail/libmilter/smfi.c
new file mode 100644
index 0000000..138623e
--- /dev/null
+++ b/contrib/sendmail/libmilter/smfi.c
@@ -0,0 +1,889 @@
+/*
+ * Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 ca Exp $")
+#include <sm/varargs.h>
+#include "libmilter.h"
+
+static int smfi_header __P((SMFICTX *, int, int, char *, char *));
+static int myisenhsc __P((const char *, int));
+
+/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
+#define MAXREPLYLEN 980 /* max. length of a reply string */
+#define MAXREPLIES 32 /* max. number of reply strings */
+
+/*
+** SMFI_HEADER -- send a header to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** cmd -- Header modification command
+** hdridx -- Header index
+** headerf -- Header field name
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+smfi_header(ctx, cmd, hdridx, headerf, headerv)
+ SMFICTX *ctx;
+ int cmd;
+ int hdridx;
+ char *headerf;
+ char *headerv;
+{
+ size_t len, l1, l2, offset;
+ int r;
+ mi_int32 v;
+ char *buf;
+ struct timeval timeout;
+
+ if (headerf == NULL || *headerf == '\0' || headerv == NULL)
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ l1 = strlen(headerf) + 1;
+ l2 = strlen(headerv) + 1;
+ len = l1 + l2;
+ if (hdridx >= 0)
+ len += MILTER_LEN_BYTES;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ offset = 0;
+ if (hdridx >= 0)
+ {
+ v = htonl(hdridx);
+ (void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
+ offset += MILTER_LEN_BYTES;
+ }
+ (void) memcpy(buf + offset, headerf, l1);
+ (void) memcpy(buf + offset + l1, headerv, l2);
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
+ free(buf);
+ return r;
+}
+
+/*
+** SMFI_ADDHEADER -- send a new header to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** headerf -- Header field name
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_addheader(ctx, headerf, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ char *headerv;
+{
+ if (!mi_sendok(ctx, SMFIF_ADDHDRS))
+ return MI_FAILURE;
+
+ return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
+}
+
+/*
+** SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
+**
+** Parameters:
+** ctx -- Opaque context structure
+** hdridx -- index into header list where insertion should occur
+** headerf -- Header field name
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_insheader(ctx, hdridx, headerf, headerv)
+ SMFICTX *ctx;
+ int hdridx;
+ char *headerf;
+ char *headerv;
+{
+ if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
+ return MI_FAILURE;
+
+ return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
+}
+
+/*
+** SMFI_CHGHEADER -- send a changed header to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** headerf -- Header field name
+** hdridx -- Header index value
+** headerv -- Header field value
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_chgheader(ctx, headerf, hdridx, headerv)
+ SMFICTX *ctx;
+ char *headerf;
+ mi_int32 hdridx;
+ char *headerv;
+{
+ if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
+ return MI_FAILURE;
+ if (headerv == NULL)
+ headerv = "";
+
+ return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
+}
+
+#if 0
+/*
+** BUF_CRT_SEND -- construct buffer to send from arguments
+**
+** Parameters:
+** ctx -- Opaque context structure
+** cmd -- command
+** arg0 -- first argument
+** argv -- list of arguments (NULL terminated)
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+buf_crt_send __P((SMFICTX *, int cmd, char *, char **));
+
+static int
+buf_crt_send(ctx, cmd, arg0, argv)
+ SMFICTX *ctx;
+ int cmd;
+ char *arg0;
+ char **argv;
+{
+ size_t len, l0, l1, offset;
+ int r;
+ char *buf, *arg, **argvl;
+ struct timeval timeout;
+
+ if (arg0 == NULL || *arg0 == '\0')
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ l0 = strlen(arg0) + 1;
+ len = l0;
+ argvl = argv;
+ while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
+ {
+ l1 = strlen(arg) + 1;
+ len += l1;
+ SM_ASSERT(len > l1);
+ }
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ (void) memcpy(buf, arg0, l0);
+ offset = l0;
+
+ argvl = argv;
+ while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
+ {
+ l1 = strlen(arg) + 1;
+ SM_ASSERT(offset < len);
+ SM_ASSERT(offset + l1 <= len);
+ (void) memcpy(buf + offset, arg, l1);
+ offset += l1;
+ SM_ASSERT(offset > l1);
+ }
+
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
+ free(buf);
+ return r;
+}
+#endif /* 0 */
+
+/*
+** SEND2 -- construct buffer to send from arguments
+**
+** Parameters:
+** ctx -- Opaque context structure
+** cmd -- command
+** arg0 -- first argument
+** argv -- list of arguments (NULL terminated)
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+send2 __P((SMFICTX *, int cmd, char *, char *));
+
+static int
+send2(ctx, cmd, arg0, arg1)
+ SMFICTX *ctx;
+ int cmd;
+ char *arg0;
+ char *arg1;
+{
+ size_t len, l0, l1, offset;
+ int r;
+ char *buf;
+ struct timeval timeout;
+
+ if (arg0 == NULL || *arg0 == '\0')
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ l0 = strlen(arg0) + 1;
+ len = l0;
+ if (arg1 != NULL)
+ {
+ l1 = strlen(arg1) + 1;
+ len += l1;
+ SM_ASSERT(len > l1);
+ }
+
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ (void) memcpy(buf, arg0, l0);
+ offset = l0;
+
+ if (arg1 != NULL)
+ {
+ l1 = strlen(arg1) + 1;
+ SM_ASSERT(offset < len);
+ SM_ASSERT(offset + l1 <= len);
+ (void) memcpy(buf + offset, arg1, l1);
+ offset += l1;
+ SM_ASSERT(offset > l1);
+ }
+
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
+ free(buf);
+ return r;
+}
+
+/*
+** SMFI_CHGFROM -- change enveloper sender ("from") address
+**
+** Parameters:
+** ctx -- Opaque context structure
+** from -- new envelope sender address ("MAIL From")
+** args -- ESMTP arguments
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_chgfrom(ctx, from, args)
+ SMFICTX *ctx;
+ char *from;
+ char *args;
+{
+ if (from == NULL || *from == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_CHGFROM))
+ return MI_FAILURE;
+ return send2(ctx, SMFIR_CHGFROM, from, args);
+}
+
+/*
+** SMFI_SETSYMLIST -- set list of macros that the MTA should send.
+**
+** Parameters:
+** ctx -- Opaque context structure
+** where -- SMTP stage
+** macros -- list of macros
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setsymlist(ctx, where, macros)
+ SMFICTX *ctx;
+ int where;
+ char *macros;
+{
+ SM_ASSERT(ctx != NULL);
+
+ if (macros == NULL || *macros == '\0')
+ return MI_FAILURE;
+ if (where < SMFIM_FIRST || where > SMFIM_LAST)
+ return MI_FAILURE;
+ if (where < 0 || where >= MAX_MACROS_ENTRIES)
+ return MI_FAILURE;
+
+ if (ctx->ctx_mac_list[where] != NULL)
+ return MI_FAILURE;
+
+ ctx->ctx_mac_list[where] = strdup(macros);
+ if (ctx->ctx_mac_list[where] == NULL)
+ return MI_FAILURE;
+
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcpt -- recipient address
+** args -- ESMTP arguments
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_addrcpt_par(ctx, rcpt, args)
+ SMFICTX *ctx;
+ char *rcpt;
+ char *args;
+{
+ if (rcpt == NULL || *rcpt == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
+ return MI_FAILURE;
+ return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
+}
+
+/*
+** SMFI_ADDRCPT -- send an additional recipient to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcpt -- recipient address
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_addrcpt(ctx, rcpt)
+ SMFICTX *ctx;
+ char *rcpt;
+{
+ size_t len;
+ struct timeval timeout;
+
+ if (rcpt == NULL || *rcpt == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_ADDRCPT))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ len = strlen(rcpt) + 1;
+ return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
+}
+
+/*
+** SMFI_DELRCPT -- send a recipient to be removed to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcpt -- recipient address
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_delrcpt(ctx, rcpt)
+ SMFICTX *ctx;
+ char *rcpt;
+{
+ size_t len;
+ struct timeval timeout;
+
+ if (rcpt == NULL || *rcpt == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_DELRCPT))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ len = strlen(rcpt) + 1;
+ return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
+}
+
+/*
+** SMFI_REPLACEBODY -- send a body chunk to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** bodyp -- body chunk
+** bodylen -- length of body chunk
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_replacebody(ctx, bodyp, bodylen)
+ SMFICTX *ctx;
+ unsigned char *bodyp;
+ int bodylen;
+{
+ int len, off, r;
+ struct timeval timeout;
+
+ if (bodylen < 0 ||
+ (bodyp == NULL && bodylen > 0))
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_CHGBODY))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+
+ /* split body chunk if necessary */
+ off = 0;
+ do
+ {
+ len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
+ bodylen;
+ if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
+ (char *) (bodyp + off), len)) != MI_SUCCESS)
+ return r;
+ off += len;
+ bodylen -= len;
+ } while (bodylen > 0);
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_QUARANTINE -- quarantine an envelope
+**
+** Parameters:
+** ctx -- Opaque context structure
+** reason -- why?
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_quarantine(ctx, reason)
+ SMFICTX *ctx;
+ char *reason;
+{
+ size_t len;
+ int r;
+ char *buf;
+ struct timeval timeout;
+
+ if (reason == NULL || *reason == '\0')
+ return MI_FAILURE;
+ if (!mi_sendok(ctx, SMFIF_QUARANTINE))
+ return MI_FAILURE;
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+ len = strlen(reason) + 1;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE;
+ (void) memcpy(buf, reason, len);
+ r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
+ free(buf);
+ return r;
+}
+
+/*
+** MYISENHSC -- check whether a string contains an enhanced status code
+**
+** Parameters:
+** s -- string with possible enhanced status code.
+** delim -- delim for enhanced status code.
+**
+** Returns:
+** 0 -- no enhanced status code.
+** >4 -- length of enhanced status code.
+**
+** Side Effects:
+** none.
+*/
+
+static int
+myisenhsc(s, delim)
+ const char *s;
+ int delim;
+{
+ int l, h;
+
+ if (s == NULL)
+ return 0;
+ if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
+ return 0;
+ h = 0;
+ l = 2;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ ++h;
+ if (h == 0 || s[l + h] != '.')
+ return 0;
+ l += h + 1;
+ h = 0;
+ while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
+ ++h;
+ if (h == 0 || s[l + h] != delim)
+ return 0;
+ return l + h;
+}
+
+/*
+** SMFI_SETREPLY -- set the reply code for the next reply to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcode -- The three-digit (RFC 821) SMTP reply code.
+** xcode -- The extended (RFC 2034) reply code.
+** message -- The text part of the SMTP reply.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setreply(ctx, rcode, xcode, message)
+ SMFICTX *ctx;
+ char *rcode;
+ char *xcode;
+ char *message;
+{
+ size_t len;
+ char *buf;
+
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+
+ /* ### <sp> \0 */
+ len = strlen(rcode) + 2;
+ if (len != 5)
+ return MI_FAILURE;
+ if ((rcode[0] != '4' && rcode[0] != '5') ||
+ !isascii(rcode[1]) || !isdigit(rcode[1]) ||
+ !isascii(rcode[2]) || !isdigit(rcode[2]))
+ return MI_FAILURE;
+ if (xcode != NULL)
+ {
+ if (!myisenhsc(xcode, '\0'))
+ return MI_FAILURE;
+ len += strlen(xcode) + 1;
+ }
+ if (message != NULL)
+ {
+ size_t ml;
+
+ /* XXX check also for unprintable chars? */
+ if (strpbrk(message, "\r\n") != NULL)
+ return MI_FAILURE;
+ ml = strlen(message);
+ if (ml > MAXREPLYLEN)
+ return MI_FAILURE;
+ len += ml + 1;
+ }
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE; /* oops */
+ (void) sm_strlcpy(buf, rcode, len);
+ (void) sm_strlcat(buf, " ", len);
+ if (xcode != NULL)
+ (void) sm_strlcat(buf, xcode, len);
+ if (message != NULL)
+ {
+ if (xcode != NULL)
+ (void) sm_strlcat(buf, " ", len);
+ (void) sm_strlcat(buf, message, len);
+ }
+ if (ctx->ctx_reply != NULL)
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = buf;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
+**
+** Parameters:
+** ctx -- Opaque context structure
+** rcode -- The three-digit (RFC 821) SMTP reply code.
+** xcode -- The extended (RFC 2034) reply code.
+** txt, ... -- The text part of the SMTP reply,
+** MUST be terminated with NULL.
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+#if SM_VA_STD
+smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
+#else /* SM_VA_STD */
+smfi_setmlreply(ctx, rcode, xcode, va_alist)
+ SMFICTX *ctx;
+ const char *rcode;
+ const char *xcode;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ size_t len;
+ size_t rlen;
+ int args;
+ char *buf, *txt;
+ const char *xc;
+ char repl[16];
+ SM_VA_LOCAL_DECL
+
+ if (rcode == NULL || ctx == NULL)
+ return MI_FAILURE;
+
+ /* ### <sp> */
+ len = strlen(rcode) + 1;
+ if (len != 4)
+ return MI_FAILURE;
+ if ((rcode[0] != '4' && rcode[0] != '5') ||
+ !isascii(rcode[1]) || !isdigit(rcode[1]) ||
+ !isascii(rcode[2]) || !isdigit(rcode[2]))
+ return MI_FAILURE;
+ if (xcode != NULL)
+ {
+ if (!myisenhsc(xcode, '\0'))
+ return MI_FAILURE;
+ xc = xcode;
+ }
+ else
+ {
+ if (rcode[0] == '4')
+ xc = "4.0.0";
+ else
+ xc = "5.0.0";
+ }
+
+ /* add trailing space */
+ len += strlen(xc) + 1;
+ rlen = len;
+ args = 0;
+ SM_VA_START(ap, xcode);
+ while ((txt = SM_VA_ARG(ap, char *)) != NULL)
+ {
+ size_t tl;
+
+ tl = strlen(txt);
+ if (tl > MAXREPLYLEN)
+ break;
+
+ /* this text, reply codes, \r\n */
+ len += tl + 2 + rlen;
+ if (++args > MAXREPLIES)
+ break;
+
+ /* XXX check also for unprintable chars? */
+ if (strpbrk(txt, "\r\n") != NULL)
+ break;
+ }
+ SM_VA_END(ap);
+ if (txt != NULL)
+ return MI_FAILURE;
+
+ /* trailing '\0' */
+ ++len;
+ buf = malloc(len);
+ if (buf == NULL)
+ return MI_FAILURE; /* oops */
+ (void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
+ (void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
+ xc, " ");
+ SM_VA_START(ap, xcode);
+ txt = SM_VA_ARG(ap, char *);
+ if (txt != NULL)
+ {
+ (void) sm_strlcat2(buf, " ", txt, len);
+ while ((txt = SM_VA_ARG(ap, char *)) != NULL)
+ {
+ if (--args <= 1)
+ repl[3] = ' ';
+ (void) sm_strlcat2(buf, "\r\n", repl, len);
+ (void) sm_strlcat(buf, txt, len);
+ }
+ }
+ if (ctx->ctx_reply != NULL)
+ free(ctx->ctx_reply);
+ ctx->ctx_reply = buf;
+ SM_VA_END(ap);
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_SETPRIV -- set private data
+**
+** Parameters:
+** ctx -- Opaque context structure
+** privatedata -- pointer to private data
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_setpriv(ctx, privatedata)
+ SMFICTX *ctx;
+ void *privatedata;
+{
+ if (ctx == NULL)
+ return MI_FAILURE;
+ ctx->ctx_privdata = privatedata;
+ return MI_SUCCESS;
+}
+
+/*
+** SMFI_GETPRIV -- get private data
+**
+** Parameters:
+** ctx -- Opaque context structure
+**
+** Returns:
+** pointer to private data
+*/
+
+void *
+smfi_getpriv(ctx)
+ SMFICTX *ctx;
+{
+ if (ctx == NULL)
+ return NULL;
+ return ctx->ctx_privdata;
+}
+
+/*
+** SMFI_GETSYMVAL -- get the value of a macro
+**
+** See explanation in mfapi.h about layout of the structures.
+**
+** Parameters:
+** ctx -- Opaque context structure
+** symname -- name of macro
+**
+** Returns:
+** value of macro (NULL in case of failure)
+*/
+
+char *
+smfi_getsymval(ctx, symname)
+ SMFICTX *ctx;
+ char *symname;
+{
+ int i;
+ char **s;
+ char one[2];
+ char braces[4];
+
+ if (ctx == NULL || symname == NULL || *symname == '\0')
+ return NULL;
+
+ if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
+ {
+ one[0] = symname[1];
+ one[1] = '\0';
+ }
+ else
+ one[0] = '\0';
+ if (strlen(symname) == 1)
+ {
+ braces[0] = '{';
+ braces[1] = *symname;
+ braces[2] = '}';
+ braces[3] = '\0';
+ }
+ else
+ braces[0] = '\0';
+
+ /* search backwards through the macro array */
+ for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
+ {
+ if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
+ ctx->ctx_mac_buf[i] == NULL)
+ continue;
+ while (s != NULL && *s != NULL)
+ {
+ if (strcmp(*s, symname) == 0)
+ return *++s;
+ if (one[0] != '\0' && strcmp(*s, one) == 0)
+ return *++s;
+ if (braces[0] != '\0' && strcmp(*s, braces) == 0)
+ return *++s;
+ ++s; /* skip over macro value */
+ ++s; /* points to next macro name */
+ }
+ }
+ return NULL;
+}
+
+/*
+** SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
+** timeouts during long milter-side operations
+**
+** Parameters:
+** ctx -- Opaque context structure
+**
+** Return value:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+smfi_progress(ctx)
+ SMFICTX *ctx;
+{
+ struct timeval timeout;
+
+ if (ctx == NULL)
+ return MI_FAILURE;
+
+ timeout.tv_sec = ctx->ctx_timeout;
+ timeout.tv_usec = 0;
+
+ return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
+}
+
+/*
+** SMFI_VERSION -- return (runtime) version of libmilter
+**
+** Parameters:
+** major -- (pointer to) major version
+** minor -- (pointer to) minor version
+** patchlevel -- (pointer to) patchlevel version
+**
+** Return value:
+** MI_SUCCESS
+*/
+
+int
+smfi_version(major, minor, patchlevel)
+ unsigned int *major;
+ unsigned int *minor;
+ unsigned int *patchlevel;
+{
+ if (major != NULL)
+ *major = SM_LM_VRS_MAJOR(SMFI_VERSION);
+ if (minor != NULL)
+ *minor = SM_LM_VRS_MINOR(SMFI_VERSION);
+ if (patchlevel != NULL)
+ *patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
+ return MI_SUCCESS;
+}
diff --git a/contrib/sendmail/libmilter/worker.c b/contrib/sendmail/libmilter/worker.c
new file mode 100644
index 0000000..28d404f
--- /dev/null
+++ b/contrib/sendmail/libmilter/worker.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2003-2004, 2007, 2009 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ * Contributed by Jose Marcio Martins da Cruz - Ecole des Mines de Paris
+ * Jose-Marcio.Martins@ensmp.fr
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: worker.c,v 8.17 2009/06/15 15:34:54 ca Exp $")
+
+#include "libmilter.h"
+
+#if _FFR_WORKERS_POOL
+
+typedef struct taskmgr_S taskmgr_T;
+
+#define TM_SIGNATURE 0x23021957
+
+struct taskmgr_S
+{
+ long tm_signature; /* has the controller been initialized */
+ sthread_t tm_tid; /* thread id of controller */
+ smfi_hd_T tm_ctx_head; /* head of the linked list of contexts */
+
+ int tm_nb_workers; /* number of workers in the pool */
+ int tm_nb_idle; /* number of workers waiting */
+
+ int tm_p[2]; /* poll control pipe */
+
+ smutex_t tm_w_mutex; /* linked list access mutex */
+ scond_t tm_w_cond; /* */
+};
+
+static taskmgr_T Tskmgr = {0};
+
+#define WRK_CTX_HEAD Tskmgr.tm_ctx_head
+
+#define RD_PIPE (Tskmgr.tm_p[0])
+#define WR_PIPE (Tskmgr.tm_p[1])
+
+#define PIPE_SEND_SIGNAL() \
+ do \
+ { \
+ char evt = 0x5a; \
+ int fd = WR_PIPE; \
+ if (write(fd, &evt, sizeof(evt)) != sizeof(evt)) \
+ smi_log(SMI_LOG_ERR, \
+ "Error writing to event pipe: %s", \
+ sm_errstring(errno)); \
+ } while (0)
+
+#ifndef USE_PIPE_WAKE_POLL
+# define USE_PIPE_WAKE_POLL 1
+#endif /* USE_PIPE_WAKE_POLL */
+
+/* poll check periodicity (default 10000 - 10 s) */
+#define POLL_TIMEOUT 10000
+
+/* worker conditional wait timeout (default 10 s) */
+#define COND_TIMEOUT 10
+
+/* functions */
+static int mi_close_session __P((SMFICTX_PTR));
+
+static void *mi_worker __P((void *));
+static void *mi_pool_controller __P((void *));
+
+static int mi_list_add_ctx __P((SMFICTX_PTR));
+static int mi_list_del_ctx __P((SMFICTX_PTR));
+
+/*
+** periodicity of cleaning up old sessions (timedout)
+** sessions list will be checked to find old inactive
+** sessions each DT_CHECK_OLD_SESSIONS sec
+*/
+
+#define DT_CHECK_OLD_SESSIONS 600
+
+#ifndef OLD_SESSION_TIMEOUT
+# define OLD_SESSION_TIMEOUT ctx->ctx_timeout
+#endif /* OLD_SESSION_TIMEOUT */
+
+/* session states - with respect to the pool of workers */
+#define WKST_INIT 0 /* initial state */
+#define WKST_READY_TO_RUN 1 /* command ready do be read */
+#define WKST_RUNNING 2 /* session running on a worker */
+#define WKST_READY_TO_WAIT 3 /* session just finished by a worker */
+#define WKST_WAITING 4 /* waiting for new command */
+#define WKST_CLOSING 5 /* session finished */
+
+#ifndef MIN_WORKERS
+# define MIN_WORKERS 2 /* minimum number of threads to keep around */
+#endif
+
+#define MIN_IDLE 1 /* minimum number of idle threads */
+
+
+/*
+** Macros for threads and mutex management
+*/
+
+#define TASKMGR_LOCK() \
+ do \
+ { \
+ if (!smutex_lock(&Tskmgr.tm_w_mutex)) \
+ smi_log(SMI_LOG_ERR, "TASKMGR_LOCK error"); \
+ } while (0)
+
+#define TASKMGR_UNLOCK() \
+ do \
+ { \
+ if (!smutex_unlock(&Tskmgr.tm_w_mutex)) \
+ smi_log(SMI_LOG_ERR, "TASKMGR_UNLOCK error"); \
+ } while (0)
+
+#define TASKMGR_COND_WAIT() \
+ scond_timedwait(&Tskmgr.tm_w_cond, &Tskmgr.tm_w_mutex, COND_TIMEOUT)
+
+#define TASKMGR_COND_SIGNAL() \
+ do \
+ { \
+ if (scond_signal(&Tskmgr.tm_w_cond) != 0) \
+ smi_log(SMI_LOG_ERR, "TASKMGR_COND_SIGNAL error"); \
+ } while (0)
+
+#define LAUNCH_WORKER(ctx) \
+ do \
+ { \
+ int r; \
+ sthread_t tid; \
+ \
+ if ((r = thread_create(&tid, mi_worker, ctx)) != 0) \
+ smi_log(SMI_LOG_ERR, "LAUNCH_WORKER error: %s",\
+ sm_errstring(r)); \
+ } while (0)
+
+#if POOL_DEBUG
+# define POOL_LEV_DPRINTF(lev, x) \
+ do { \
+ if ((lev) < ctx->ctx_dbg) \
+ sm_dprintf x; \
+ } while (0)
+#else /* POOL_DEBUG */
+# define POOL_LEV_DPRINTF(lev, x)
+#endif /* POOL_DEBUG */
+
+/*
+** MI_START_SESSION -- Start a session in the pool of workers
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_start_session(ctx)
+ SMFICTX_PTR ctx;
+{
+ static long id = 0;
+
+ SM_ASSERT(Tskmgr.tm_signature == TM_SIGNATURE);
+ SM_ASSERT(ctx != NULL);
+ POOL_LEV_DPRINTF(4, ("PIPE r=[%d] w=[%d]", RD_PIPE, WR_PIPE));
+ TASKMGR_LOCK();
+
+ if (mi_list_add_ctx(ctx) != MI_SUCCESS)
+ {
+ TASKMGR_UNLOCK();
+ return MI_FAILURE;
+ }
+
+ ctx->ctx_sid = id++;
+
+ /* if there is an idle worker, signal it, otherwise start new worker */
+ if (Tskmgr.tm_nb_idle > 0)
+ {
+ ctx->ctx_wstate = WKST_READY_TO_RUN;
+ TASKMGR_COND_SIGNAL();
+ }
+ else
+ {
+ ctx->ctx_wstate = WKST_RUNNING;
+ LAUNCH_WORKER(ctx);
+ }
+ TASKMGR_UNLOCK();
+ return MI_SUCCESS;
+}
+
+/*
+** MI_CLOSE_SESSION -- Close a session and clean up data structures
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+static int
+mi_close_session(ctx)
+ SMFICTX_PTR ctx;
+{
+ SM_ASSERT(ctx != NULL);
+
+ (void) mi_list_del_ctx(ctx);
+ mi_clr_ctx(ctx);
+
+ return MI_SUCCESS;
+}
+
+/*
+** MI_POOL_CONTROLER_INIT -- Launch the worker pool controller
+** Must be called before starting sessions.
+**
+** Parameters:
+** none
+**
+** Returns:
+** MI_SUCCESS/MI_FAILURE
+*/
+
+int
+mi_pool_controller_init()
+{
+ sthread_t tid;
+ int r, i;
+
+ if (Tskmgr.tm_signature == TM_SIGNATURE)
+ return MI_SUCCESS;
+
+ SM_TAILQ_INIT(&WRK_CTX_HEAD);
+ Tskmgr.tm_tid = (sthread_t) -1;
+ Tskmgr.tm_nb_workers = 0;
+ Tskmgr.tm_nb_idle = 0;
+
+ if (pipe(Tskmgr.tm_p) != 0)
+ {
+ smi_log(SMI_LOG_ERR, "can't create event pipe: %s",
+ sm_errstring(errno));
+ return MI_FAILURE;
+ }
+
+ (void) smutex_init(&Tskmgr.tm_w_mutex);
+ (void) scond_init(&Tskmgr.tm_w_cond);
+
+ /* Launch the pool controller */
+ if ((r = thread_create(&tid, mi_pool_controller, (void *) NULL)) != 0)
+ {
+ smi_log(SMI_LOG_ERR, "can't create controller thread: %s",
+ sm_errstring(r));
+ return MI_FAILURE;
+ }
+ Tskmgr.tm_tid = tid;
+ Tskmgr.tm_signature = TM_SIGNATURE;
+
+ /* Create the pool of workers */
+ for (i = 0; i < MIN_WORKERS; i++)
+ {
+ if ((r = thread_create(&tid, mi_worker, (void *) NULL)) != 0)
+ {
+ smi_log(SMI_LOG_ERR, "can't create workers crew: %s",
+ sm_errstring(r));
+ return MI_FAILURE;
+ }
+ }
+
+ return MI_SUCCESS;
+}
+
+/*
+** MI_POOL_CONTROLLER -- manage the pool of workers
+** This thread must be running when listener begins
+** starting sessions
+**
+** Parameters:
+** arg -- unused
+**
+** Returns:
+** NULL
+**
+** Control flow:
+** for (;;)
+** Look for timed out sessions
+** Select sessions to wait for sendmail command
+** Poll set of file descriptors
+** if timeout
+** continue
+** For each file descriptor ready
+** launch new thread if no worker available
+** else
+** signal waiting worker
+*/
+
+/* Poll structure array (pollfd) size step */
+#define PFD_STEP 256
+
+#define WAIT_FD(i) (pfd[i].fd)
+#define WAITFN "POLL"
+
+static void *
+mi_pool_controller(arg)
+ void *arg;
+{
+ struct pollfd *pfd = NULL;
+ int dim_pfd = 0;
+ bool rebuild_set = true;
+ int pcnt = 0; /* error count for poll() failures */
+ time_t lastcheck;
+
+ Tskmgr.tm_tid = sthread_get_id();
+ if (pthread_detach(Tskmgr.tm_tid) != 0)
+ {
+ smi_log(SMI_LOG_ERR, "Failed to detach pool controller thread");
+ return NULL;
+ }
+
+ pfd = (struct pollfd *) malloc(PFD_STEP * sizeof(struct pollfd));
+ if (pfd == NULL)
+ {
+ smi_log(SMI_LOG_ERR, "Failed to malloc pollfd array: %s",
+ sm_errstring(errno));
+ return NULL;
+ }
+ dim_pfd = PFD_STEP;
+
+ lastcheck = time(NULL);
+ for (;;)
+ {
+ SMFICTX_PTR ctx;
+ int nfd, rfd, i;
+ time_t now;
+
+ POOL_LEV_DPRINTF(4, ("Let's %s again...", WAITFN));
+
+ if (mi_stop() != MILTER_CONT)
+ break;
+
+ TASKMGR_LOCK();
+
+ now = time(NULL);
+
+ /* check for timed out sessions? */
+ if (lastcheck + DT_CHECK_OLD_SESSIONS < now)
+ {
+ ctx = SM_TAILQ_FIRST(&WRK_CTX_HEAD);
+ while (ctx != SM_TAILQ_END(&WRK_CTX_HEAD))
+ {
+ SMFICTX_PTR ctx_nxt;
+
+ ctx_nxt = SM_TAILQ_NEXT(ctx, ctx_link);
+ if (ctx->ctx_wstate == WKST_WAITING)
+ {
+ if (ctx->ctx_wait == 0)
+ ctx->ctx_wait = now;
+ else if (ctx->ctx_wait + OLD_SESSION_TIMEOUT
+ < now)
+ {
+ /* if session timed out, close it */
+ sfsistat (*fi_close) __P((SMFICTX *));
+
+ POOL_LEV_DPRINTF(4,
+ ("Closing old connection: sd=%d id=%d",
+ ctx->ctx_sd,
+ ctx->ctx_sid));
+
+ if ((fi_close = ctx->ctx_smfi->xxfi_close) != NULL)
+ (void) (*fi_close)(ctx);
+
+ mi_close_session(ctx);
+ }
+ }
+ ctx = ctx_nxt;
+ }
+ lastcheck = now;
+ }
+
+ if (rebuild_set)
+ {
+ /*
+ ** Initialize poll set.
+ ** Insert into the poll set the file descriptors of
+ ** all sessions waiting for a command from sendmail.
+ */
+
+ nfd = 0;
+
+ /* begin with worker pipe */
+ pfd[nfd].fd = RD_PIPE;
+ pfd[nfd].events = MI_POLL_RD_FLAGS;
+ pfd[nfd].revents = 0;
+ nfd++;
+
+ SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link)
+ {
+ /*
+ ** update ctx_wait - start of wait moment -
+ ** for timeout
+ */
+
+ if (ctx->ctx_wstate == WKST_READY_TO_WAIT)
+ ctx->ctx_wait = now;
+
+ /* add the session to the pollfd array? */
+ if ((ctx->ctx_wstate == WKST_READY_TO_WAIT) ||
+ (ctx->ctx_wstate == WKST_WAITING))
+ {
+ /*
+ ** Resize the pollfd array if it
+ ** isn't large enough.
+ */
+
+ if (nfd >= dim_pfd)
+ {
+ struct pollfd *tpfd;
+ size_t new;
+
+ new = (dim_pfd + PFD_STEP) *
+ sizeof(*tpfd);
+ tpfd = (struct pollfd *)
+ realloc(pfd, new);
+ if (tpfd != NULL)
+ {
+ pfd = tpfd;
+ dim_pfd += PFD_STEP;
+ }
+ else
+ {
+ smi_log(SMI_LOG_ERR,
+ "Failed to realloc pollfd array:%s",
+ sm_errstring(errno));
+ }
+ }
+
+ /* add the session to pollfd array */
+ if (nfd < dim_pfd)
+ {
+ ctx->ctx_wstate = WKST_WAITING;
+ pfd[nfd].fd = ctx->ctx_sd;
+ pfd[nfd].events = MI_POLL_RD_FLAGS;
+ pfd[nfd].revents = 0;
+ nfd++;
+ }
+ }
+ }
+ rebuild_set = false;
+ }
+
+ TASKMGR_UNLOCK();
+
+ /* Everything is ready, let's wait for an event */
+ rfd = poll(pfd, nfd, POLL_TIMEOUT);
+
+ POOL_LEV_DPRINTF(4, ("%s returned: at epoch %d value %d",
+ WAITFN, now, nfd));
+
+ /* timeout */
+ if (rfd == 0)
+ continue;
+
+ rebuild_set = true;
+
+ /* error */
+ if (rfd < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ pcnt++;
+ smi_log(SMI_LOG_ERR,
+ "%s() failed (%s), %s",
+ WAITFN, sm_errstring(errno),
+ pcnt >= MAX_FAILS_S ? "abort" : "try again");
+
+ if (pcnt >= MAX_FAILS_S)
+ goto err;
+ }
+ pcnt = 0;
+
+ /* something happened */
+ for (i = 0; i < nfd; i++)
+ {
+ if (pfd[i].revents == 0)
+ continue;
+
+ POOL_LEV_DPRINTF(4, ("%s event on pfd[%d/%d]=%d ",
+ WAITFN, i, nfd,
+ WAIT_FD(i)));
+
+ /* has a worker signaled an end of task ? */
+ if (WAIT_FD(i) == RD_PIPE)
+ {
+ char evt = 0;
+ int r = 0;
+
+ POOL_LEV_DPRINTF(4,
+ ("PIPE WILL READ evt = %08X %08X",
+ pfd[i].events, pfd[i].revents));
+
+ if ((pfd[i].revents & MI_POLL_RD_FLAGS) != 0)
+ {
+ r = read(RD_PIPE, &evt, sizeof(evt));
+ if (r == sizeof(evt))
+ {
+ /* Do nothing */
+ }
+ }
+
+ POOL_LEV_DPRINTF(4,
+ ("PIPE DONE READ i=[%d] fd=[%d] r=[%d] evt=[%d]",
+ i, RD_PIPE, r, evt));
+
+ if ((pfd[i].revents & ~MI_POLL_RD_FLAGS) != 0)
+ {
+ /* Exception handling */
+ }
+ continue;
+ }
+
+ /* no ! sendmail wants to send a command */
+ SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link)
+ {
+ if (ctx->ctx_wstate != WKST_WAITING)
+ continue;
+
+ POOL_LEV_DPRINTF(4,
+ ("Checking context sd=%d - fd=%d ",
+ ctx->ctx_sd , WAIT_FD(i)));
+
+ if (ctx->ctx_sd == pfd[i].fd)
+ {
+ TASKMGR_LOCK();
+
+ POOL_LEV_DPRINTF(4,
+ ("TASK: found %d for fd[%d]=%d",
+ ctx->ctx_sid, i, WAIT_FD(i)));
+
+ if (Tskmgr.tm_nb_idle > 0)
+ {
+ ctx->ctx_wstate = WKST_READY_TO_RUN;
+ TASKMGR_COND_SIGNAL();
+ }
+ else
+ {
+ ctx->ctx_wstate = WKST_RUNNING;
+ LAUNCH_WORKER(ctx);
+ }
+ TASKMGR_UNLOCK();
+ break;
+ }
+ }
+
+ POOL_LEV_DPRINTF(4,
+ ("TASK %s FOUND - Checking PIPE for fd[%d]",
+ ctx != NULL ? "" : "NOT", WAIT_FD(i)));
+ }
+ }
+
+ err:
+ if (pfd != NULL)
+ free(pfd);
+
+ Tskmgr.tm_signature = 0;
+ for (;;)
+ {
+ SMFICTX_PTR ctx;
+
+ ctx = SM_TAILQ_FIRST(&WRK_CTX_HEAD);
+ if (ctx == NULL)
+ break;
+ mi_close_session(ctx);
+ }
+
+ (void) smutex_destroy(&Tskmgr.tm_w_mutex);
+ (void) scond_destroy(&Tskmgr.tm_w_cond);
+
+ return NULL;
+}
+
+/*
+** Look for a task ready to run.
+** Value of ctx is NULL or a pointer to a task ready to run.
+*/
+
+#define GET_TASK_READY_TO_RUN() \
+ SM_TAILQ_FOREACH(ctx, &WRK_CTX_HEAD, ctx_link) \
+ { \
+ if (ctx->ctx_wstate == WKST_READY_TO_RUN) \
+ { \
+ ctx->ctx_wstate = WKST_RUNNING; \
+ break; \
+ } \
+ }
+
+/*
+** MI_WORKER -- worker thread
+** executes tasks distributed by the mi_pool_controller
+** or by mi_start_session
+**
+** Parameters:
+** arg -- pointer to context structure
+**
+** Returns:
+** NULL pointer
+*/
+
+static void *
+mi_worker(arg)
+ void *arg;
+{
+ SMFICTX_PTR ctx;
+ bool done;
+ sthread_t t_id;
+ int r;
+
+ ctx = (SMFICTX_PTR) arg;
+ done = false;
+ if (ctx != NULL)
+ ctx->ctx_wstate = WKST_RUNNING;
+
+ t_id = sthread_get_id();
+ if (pthread_detach(t_id) != 0)
+ {
+ smi_log(SMI_LOG_ERR, "Failed to detach worker thread");
+ if (ctx != NULL)
+ ctx->ctx_wstate = WKST_READY_TO_RUN;
+ return NULL;
+ }
+
+ TASKMGR_LOCK();
+ Tskmgr.tm_nb_workers++;
+ TASKMGR_UNLOCK();
+
+ while (!done)
+ {
+ if (mi_stop() != MILTER_CONT)
+ break;
+
+ /* let's handle next task... */
+ if (ctx != NULL)
+ {
+ int res;
+
+ POOL_LEV_DPRINTF(4,
+ ("worker %d: new task -> let's handle it",
+ t_id));
+ res = mi_engine(ctx);
+ POOL_LEV_DPRINTF(4,
+ ("worker %d: mi_engine returned %d", t_id, res));
+
+ TASKMGR_LOCK();
+ if (res != MI_CONTINUE)
+ {
+ ctx->ctx_wstate = WKST_CLOSING;
+
+ /*
+ ** Delete context from linked list of
+ ** sessions and close session.
+ */
+
+ mi_close_session(ctx);
+ }
+ else
+ {
+ ctx->ctx_wstate = WKST_READY_TO_WAIT;
+
+ POOL_LEV_DPRINTF(4,
+ ("writing to event pipe..."));
+
+ /*
+ ** Signal task controller to add new session
+ ** to poll set.
+ */
+
+ PIPE_SEND_SIGNAL();
+ }
+ TASKMGR_UNLOCK();
+ ctx = NULL;
+
+ }
+
+ /* check if there is any task waiting to be served */
+ TASKMGR_LOCK();
+
+ GET_TASK_READY_TO_RUN();
+
+ /* Got a task? */
+ if (ctx != NULL)
+ {
+ TASKMGR_UNLOCK();
+ continue;
+ }
+
+ /*
+ ** if not, let's check if there is enough idle workers
+ ** if yes: quit
+ */
+
+ if (Tskmgr.tm_nb_workers > MIN_WORKERS &&
+ Tskmgr.tm_nb_idle > MIN_IDLE)
+ done = true;
+
+ POOL_LEV_DPRINTF(4, ("worker %d: checking ... %d %d", t_id,
+ Tskmgr.tm_nb_workers, Tskmgr.tm_nb_idle + 1));
+
+ if (done)
+ {
+ POOL_LEV_DPRINTF(4, ("worker %d: quitting... ", t_id));
+ Tskmgr.tm_nb_workers--;
+ TASKMGR_UNLOCK();
+ continue;
+ }
+
+ /*
+ ** if no task ready to run, wait for another one
+ */
+
+ Tskmgr.tm_nb_idle++;
+ TASKMGR_COND_WAIT();
+ Tskmgr.tm_nb_idle--;
+
+ /* look for a task */
+ GET_TASK_READY_TO_RUN();
+
+ TASKMGR_UNLOCK();
+ }
+ return NULL;
+}
+
+/*
+** MI_LIST_ADD_CTX -- add new session to linked list
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_FAILURE/MI_SUCCESS
+*/
+
+static int
+mi_list_add_ctx(ctx)
+ SMFICTX_PTR ctx;
+{
+ SM_ASSERT(ctx != NULL);
+ SM_TAILQ_INSERT_TAIL(&WRK_CTX_HEAD, ctx, ctx_link);
+ return MI_SUCCESS;
+}
+
+/*
+** MI_LIST_DEL_CTX -- remove session from linked list when finished
+**
+** Parameters:
+** ctx -- context structure
+**
+** Returns:
+** MI_FAILURE/MI_SUCCESS
+*/
+
+static int
+mi_list_del_ctx(ctx)
+ SMFICTX_PTR ctx;
+{
+ SM_ASSERT(ctx != NULL);
+ if (SM_TAILQ_EMPTY(&WRK_CTX_HEAD))
+ return MI_FAILURE;
+
+ SM_TAILQ_REMOVE(&WRK_CTX_HEAD, ctx, ctx_link);
+ return MI_SUCCESS;
+}
+#endif /* _FFR_WORKERS_POOL */
OpenPOWER on IntegriCloud