README revision 110560
164562SgshapiroThis directory contains the source files for libmilter.
264562Sgshapiro
364562SgshapiroThe sendmail Mail Filter API (Milter) is designed to allow third-party
464562Sgshapiroprograms access to mail messages as they are being processed in order to
564562Sgshapirofilter meta-information and content.
664562Sgshapiro
764562SgshapiroThis README file describes the steps needed to compile and run a filter,
864562Sgshapirothrough reference to a sample filter which is attached at the end of this
964562Sgshapirofile.  It is necessary to first build libmilter.a, which can be done by
1064562Sgshapiroissuing the './Build' command in SRCDIR/libmilter .
1164562Sgshapiro
1290792SgshapiroNOTE: If you intend to use filters in sendmail, you must compile sendmail
1390792Sgshapirowith -DMILTER defined.  You can do this by adding the following to
1490792Sgshapiroyour devtools/Site/site.config.m4 file:
1564562Sgshapiro
1690792Sgshapiro	APPENDDEF(`conf_sendmail_ENVDEF', `-DMILTER')
1764562Sgshapiro
1890792Sgshapiro+----------------+
1990792Sgshapiro| SECURITY HINTS |
2090792Sgshapiro+----------------+
2164562Sgshapiro
2290792SgshapiroNote: we strongly recommend not to run any milter as root.  Libmilter
2390792Sgshapirodoes not need root access to communicate with sendmail.  It is a
2490792Sgshapirogood security practice to run a program only with root privileges
2590792Sgshapiroif really necessary.  A milter should probably check first whether
2690792Sgshapiroit runs as root and refuse to start in that case.  There is a
2790792Sgshapirocompile time option _FFR_MILTER_ROOT_UNSAFE which keeps libmilter
2890792Sgshapirofrom unlinking a socket when running as root.  It is recommended
2990792Sgshapiroto turn on this option:
3090792Sgshapiro
3190792Sgshapiro	APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER_ROOT_UNSAFE ')
3290792Sgshapiro
3390792Sgshapiro
3464562Sgshapiro+-------------------+
3564562Sgshapiro| BUILDING A FILTER |
3664562Sgshapiro+-------------------+
3764562Sgshapiro
3864562SgshapiroThe following command presumes that the sample code from the end of this
3964562SgshapiroREADME is saved to a file named 'sample.c' and built in the local platform-
4064562Sgshapirospecific build subdirectory (SRCDIR/obj.*/libmilter).
4164562Sgshapiro
42110560Sgshapiro	cc -I../../include -o sample sample.c libmilter.a ../libsm/libsm.a -pthread
4364562Sgshapiro
4464562SgshapiroIt is recommended that you build your filters in a location outside of
4564562Sgshapirothe sendmail source tree.  Modify the compiler include references (-I)
4664562Sgshapiroand the library locations accordingly.  Also, some operating systems may
4764562Sgshapirorequire additional libraries.  For example, SunOS 5.X requires '-lresolv
4890792Sgshapiro-lsocket -lnsl'.  Depending on your operating system you may need a library
4990792Sgshapiroinstead of the option -pthread, e.g., -lpthread.
5064562Sgshapiro
5164562SgshapiroFilters must be thread-safe!  Many operating systems now provide support for
5264562SgshapiroPOSIX threads in the standard C libraries.  The compiler flag to link with
5364562Sgshapirothreading support differs according to the compiler and linker used.  Check
5464562Sgshapirothe Makefile in your appropriate obj.*/libmilter build subdirectory if you
5564562Sgshapiroare unsure of the local flag used.
5664562Sgshapiro
5773188SgshapiroNote that since filters use threads, it may be necessary to alter per
5873188Sgshapiroprocess limits in your filter.  For example, you might look at using
5973188Sgshapirosetrlimit() to increase the number of open file descriptors if your filter
6073188Sgshapirois going to be busy.
6164562Sgshapiro
6273188Sgshapiro
6364562Sgshapiro+----------------------------------------+
6464562Sgshapiro| SPECIFYING FILTERS IN SENDMAIL CONFIGS |
6564562Sgshapiro+----------------------------------------+
6664562Sgshapiro
6764562SgshapiroFilters are specified with a key letter ``X'' (for ``eXternal'').
6864562Sgshapiro
6964562SgshapiroFor example:
7064562Sgshapiro
7164562Sgshapiro	Xfilter1, S=local:/var/run/f1.sock, F=R
7280785Sgshapiro	Xfilter2, S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m
7364562Sgshapiro	Xfilter3, S=inet:3333@localhost
7464562Sgshapiro
7564562Sgshapirospecifies three filters.  Filters can be specified in your .mc file using
7664562Sgshapirothe following:
7764562Sgshapiro
7864562Sgshapiro	INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R')
7980785Sgshapiro	INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m')
8064562Sgshapiro	INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost')
8164562Sgshapiro
8264562SgshapiroThe first attaches to a Unix-domain socket in the /var/run directory; the
8364562Sgshapirosecond uses an IPv6 socket on port 999 of localhost, and the third uses an
8464562SgshapiroIPv4 socket on port 3333 of localhost.  The current flags (F=) are:
8564562Sgshapiro
8664562Sgshapiro	R		Reject connection if filter unavailable
8764562Sgshapiro	T		Temporary fail connection if filter unavailable
8864562Sgshapiro
8966494SgshapiroIf neither F=R nor F=T is specified, the message is passed through sendmail
9090792Sgshapiroin case of filter errors as if the failing filters were not present.
9166494Sgshapiro
9264562SgshapiroFinally, you can override the default timeouts used by sendmail when
9380785Sgshapirotalking to the filters using the T= equate.  There are four fields inside
9464562Sgshapiroof the T= equate:
9564562Sgshapiro
9690792SgshapiroLetter		Meaning
9780785Sgshapiro  C		Timeout for connecting to a filter (if 0, use system timeout)
9880785Sgshapiro  S		Timeout for sending information from the MTA to a filter
9980785Sgshapiro  R		Timeout for reading reply from the filter
10080785Sgshapiro  E		Overall timeout between sending end-of-message to filter
10180785Sgshapiro		and waiting for the final acknowledgment
10264562Sgshapiro
10364562SgshapiroNote the separator between each is a ';' as a ',' already separates equates
10480785Sgshapiroand therefore can't separate timeouts.  The default values (if not set in
10580785Sgshapirothe config) are:
10664562Sgshapiro
10790792SgshapiroT=C:5m;S:10s;R:10s;E:5m
10864562Sgshapiro
10964562Sgshapirowhere 's' is seconds and 'm' is minutes.
11064562Sgshapiro
11166494SgshapiroWhich filters are invoked and their sequencing is handled by the 
11277349SgshapiroInputMailFilters option. Note: if InputMailFilters is not defined no filters
11377349Sgshapirowill be used.
11464562Sgshapiro
11566494Sgshapiro	O InputMailFilters=filter1, filter2, filter3
11666494Sgshapiro
11766494SgshapiroThis is is set automatically according to the order of the
11866494SgshapiroINPUT_MAIL_FILTER commands in your .mc file.  Alternatively, you can
11966494Sgshapiroreset its value by setting confINPUT_MAIL_FILTERS in your .mc file.
12066494SgshapiroThis options causes the three filters to be called in the same order
12166494Sgshapirothey were specified.  It allows for possible future filtering on output
12266494Sgshapiro(although this is not intended for this release).
12366494Sgshapiro
12464562SgshapiroAlso note that a filter can be defined without adding it to the input
12564562Sgshapirofilter list by using MAIL_FILTER() instead of INPUT_MAIL_FILTER() in your
12664562Sgshapiro.mc file.
12764562Sgshapiro
12864562SgshapiroTo test sendmail with the sample filter, the following might be added (in
12964562Sgshapirothe appropriate locations) to your .mc file:
13064562Sgshapiro
13164562Sgshapiro	INPUT_MAIL_FILTER(`sample', `S=local:/var/run/f1.sock')
13264562Sgshapiro
13364562Sgshapiro
13464562Sgshapiro+------------------+
13564562Sgshapiro| TESTING A FILTER |
13664562Sgshapiro+------------------+
13764562Sgshapiro
13864562SgshapiroOnce you have compiled a filter, modified your .mc file and restarted
13964562Sgshapirothe sendmail process, you will want to test that the filter performs as
14064562Sgshapirointended.
14164562Sgshapiro
14264562SgshapiroThe sample filter takes one argument -p, which indicates the local port
14364562Sgshapiroon which to create a listening socket for the filter.  Maintaining
14464562Sgshapiroconsistency with the suggested options for sendmail.cf, this would be the
14564562SgshapiroUNIX domain socket located in /var/run/f1.sock.
14664562Sgshapiro
14764562Sgshapiro	% ./sample -p local:/var/run/f1.sock
14864562Sgshapiro
14964562SgshapiroIf the sample filter returns immediately to a command line, there was either
15064562Sgshapiroan error with your command or a problem creating the specified socket.
15164562SgshapiroFurther logging can be captured through the syslogd daemon.  Using the
15264562Sgshapiro'netstat -a' command can ensure that your filter process is listening on
15364562Sgshapirothe appropriate local socket.
15464562Sgshapiro
15564562SgshapiroEmail messages must be injected via SMTP to be filtered.  There are two
15664562Sgshapirosimple means of doing this; either using the 'sendmail -bs' command, or
15764562Sgshapiroby telnetting to port 25 of the machine configured for milter.  Once
15864562Sgshapiroconnected via one of these options, the session can be continued through
15964562Sgshapirothe use of standard SMTP commands.
16064562Sgshapiro
16164562Sgshapiro% sendmail -bs
16266494Sgshapiro220 test.sendmail.com ESMTP Sendmail 8.11.0/8.11.0; Tue, 10 Nov 1970 13:05:23 -0500 (EST)
16364562SgshapiroHELO localhost
16464562Sgshapiro250 test.sendmail.com Hello testy@localhost, pleased to meet you
16564562SgshapiroMAIL From:<testy>
16664562Sgshapiro250 2.1.0 <testy>... Sender ok
16764562SgshapiroRCPT To:<root>
16864562Sgshapiro250 2.1.5 <root>... Recipient ok
16964562SgshapiroDATA
17064562Sgshapiro354 Enter mail, end with "." on a line by itself
17164562SgshapiroFrom: testy@test.sendmail.com
17264562SgshapiroTo: root@test.sendmail.com
17364562SgshapiroSubject: testing sample filter
17464562Sgshapiro
17564562SgshapiroSample body
17664562Sgshapiro.
17764562Sgshapiro250 2.0.0 dB73Zxi25236 Message accepted for delivery
17864562SgshapiroQUIT
17964562Sgshapiro221 2.0.0 test.sendmail.com closing connection
18064562Sgshapiro
18164562SgshapiroIn the above example, the lines beginning with numbers are output by the
18264562Sgshapiromail server, and those without are your input.  If everything is working
18364562Sgshapiroproperly, you will find a file in /tmp by the name of msg.XXXXXXXX (where
18464562Sgshapirothe Xs represent any combination of letters and numbers).  This file should
18564562Sgshapirocontain the message body and headers from the test email entered above.
18664562Sgshapiro
18764562SgshapiroIf the sample filter did not log your test email, there are a number of
18864562Sgshapiromethods to narrow down the source of the problem.  Check your system
18964562Sgshapirologs written by syslogd and see if there are any pertinent lines.  You
19064562Sgshapiromay need to reconfigure syslogd to capture all relevant data.  Additionally,
19164562Sgshapirothe logging level of sendmail can be raised with the LogLevel option.
19264562SgshapiroSee the sendmail(8) manual page for more information.
19364562Sgshapiro
19464562Sgshapiro
19590792Sgshapiro+--------------+
19690792Sgshapiro| REQUIREMENTS |
19790792Sgshapiro+--------------+
19890792Sgshapiro
19990792Sgshapirolibmilter requires pthread support in the operating system.  Moreover, it
20090792Sgshapirorequires that the library functions it uses are thread safe; which is true
20190792Sgshapirofor the operating systems libmilter has been developed and tested on.  On
20290792Sgshapirosome operating systems this requires special compile time options (e.g.,
20390792Sgshapironot just -pthread).  libmilter is currently known to work on (modulo problems
20490792Sgshapiroin the pthread support of some specific versions):
20590792Sgshapiro
20690792SgshapiroFreeBSD 3.x, 4.x
20790792SgshapiroSunOS 5.x (x >= 5)
20890792SgshapiroAIX 4.3.x
20990792SgshapiroHP UX 11.x
21090792SgshapiroLinux (recent versions/distributions)
21190792Sgshapiro
21290792Sgshapirolibmilter is currently not supported on:
21390792Sgshapiro
21490792SgshapiroIRIX 6.x
21590792SgshapiroUltrix
21690792Sgshapiro
21790792SgshapiroFeedback about problems (and possible fixes) is welcome.
21890792Sgshapiro
21964562Sgshapiro+--------------------------+
22064562Sgshapiro| SOURCE FOR SAMPLE FILTER |
22164562Sgshapiro+--------------------------+
22264562Sgshapiro
22371345SgshapiroNote that the filter below may not be thread safe on some operating
22471345Sgshapirosystems.  You should check your system man pages for the functions used
22571345Sgshapirobelow to verify the functions are thread safe.
22671345Sgshapiro
22764562Sgshapiro/* A trivial filter that logs all email to a file. */
22864562Sgshapiro
22964562Sgshapiro#include <sys/types.h>
23064562Sgshapiro#include <stdio.h>
23164562Sgshapiro#include <stdlib.h>
23264562Sgshapiro#include <string.h>
23364562Sgshapiro#include <sysexits.h>
23464562Sgshapiro#include <unistd.h>
23564562Sgshapiro
23664562Sgshapiro#include "libmilter/mfapi.h"
23764562Sgshapiro
23890792Sgshapiro#ifndef true
23964562Sgshapirotypedef int bool;
24090792Sgshapiro# define false	0
24190792Sgshapiro# define true	1
24290792Sgshapiro#endif /* ! true */
24364562Sgshapiro
24464562Sgshapirostruct mlfiPriv
24564562Sgshapiro{
24664562Sgshapiro	char	*mlfi_fname;
24764562Sgshapiro	FILE	*mlfi_fp;
24864562Sgshapiro};
24964562Sgshapiro
25064562Sgshapiro#define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
25164562Sgshapiro
25264562Sgshapiroextern sfsistat	 mlfi_cleanup(SMFICTX *, bool);
25364562Sgshapiro
25464562Sgshapirosfsistat
25564562Sgshapiromlfi_envfrom(ctx, envfrom)
25664562Sgshapiro	SMFICTX *ctx;
25764562Sgshapiro	char **envfrom;
25864562Sgshapiro{
25964562Sgshapiro	struct mlfiPriv *priv;
26077349Sgshapiro	int fd = -1;
26164562Sgshapiro
26264562Sgshapiro	/* allocate some private memory */
26364562Sgshapiro	priv = malloc(sizeof *priv);
26464562Sgshapiro	if (priv == NULL)
26564562Sgshapiro	{
26664562Sgshapiro		/* can't accept this message right now */
26764562Sgshapiro		return SMFIS_TEMPFAIL;
26864562Sgshapiro	}
26964562Sgshapiro	memset(priv, '\0', sizeof *priv);
27064562Sgshapiro
27164562Sgshapiro	/* open a file to store this message */
27264562Sgshapiro	priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
27364562Sgshapiro	if (priv->mlfi_fname == NULL)
27464562Sgshapiro	{
27564562Sgshapiro		free(priv);
27664562Sgshapiro		return SMFIS_TEMPFAIL;
27764562Sgshapiro	}
27864562Sgshapiro	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
27964562Sgshapiro	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
28064562Sgshapiro	{
28177349Sgshapiro		if (fd >= 0)
28277349Sgshapiro			(void) close(fd);
28364562Sgshapiro		free(priv->mlfi_fname);
28464562Sgshapiro		free(priv);
28564562Sgshapiro		return SMFIS_TEMPFAIL;
28664562Sgshapiro	}
28764562Sgshapiro
28864562Sgshapiro	/* save the private data */
28964562Sgshapiro	smfi_setpriv(ctx, priv);
29064562Sgshapiro
29164562Sgshapiro	/* continue processing */
29264562Sgshapiro	return SMFIS_CONTINUE;
29364562Sgshapiro}
29464562Sgshapiro
29564562Sgshapirosfsistat
29664562Sgshapiromlfi_header(ctx, headerf, headerv)
29764562Sgshapiro	SMFICTX *ctx;
29864562Sgshapiro	char *headerf;
29964562Sgshapiro	char *headerv;
30064562Sgshapiro{
30164562Sgshapiro	/* write the header to the log file */
30264562Sgshapiro	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
30364562Sgshapiro
30464562Sgshapiro	/* continue processing */
30564562Sgshapiro	return SMFIS_CONTINUE;
30664562Sgshapiro}
30764562Sgshapiro
30864562Sgshapirosfsistat
30964562Sgshapiromlfi_eoh(ctx)
31064562Sgshapiro	SMFICTX *ctx;
31164562Sgshapiro{
31264562Sgshapiro	/* output the blank line between the header and the body */
31364562Sgshapiro	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
31464562Sgshapiro
31564562Sgshapiro	/* continue processing */
31664562Sgshapiro	return SMFIS_CONTINUE;
31764562Sgshapiro}
31864562Sgshapiro
31964562Sgshapirosfsistat
32064562Sgshapiromlfi_body(ctx, bodyp, bodylen)
32164562Sgshapiro	SMFICTX *ctx;
32264562Sgshapiro	u_char *bodyp;
32364562Sgshapiro	size_t bodylen;
32464562Sgshapiro{
32564562Sgshapiro	/* output body block to log file */
32664562Sgshapiro	if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
32764562Sgshapiro	{
32864562Sgshapiro		/* write failed */
32990792Sgshapiro		(void) mlfi_cleanup(ctx, false);
33064562Sgshapiro		return SMFIS_TEMPFAIL;
33164562Sgshapiro	}
33264562Sgshapiro
33364562Sgshapiro	/* continue processing */
33464562Sgshapiro	return SMFIS_CONTINUE;
33564562Sgshapiro}
33664562Sgshapiro
33764562Sgshapirosfsistat
33864562Sgshapiromlfi_eom(ctx)
33964562Sgshapiro	SMFICTX *ctx;
34064562Sgshapiro{
34190792Sgshapiro	return mlfi_cleanup(ctx, true);
34264562Sgshapiro}
34364562Sgshapiro
34464562Sgshapirosfsistat
34564562Sgshapiromlfi_close(ctx)
34664562Sgshapiro	SMFICTX *ctx;
34764562Sgshapiro{
34864562Sgshapiro	return SMFIS_ACCEPT;
34964562Sgshapiro}
35064562Sgshapiro
35164562Sgshapirosfsistat
35264562Sgshapiromlfi_abort(ctx)
35364562Sgshapiro	SMFICTX *ctx;
35464562Sgshapiro{
35590792Sgshapiro	return mlfi_cleanup(ctx, false);
35664562Sgshapiro}
35764562Sgshapiro
35864562Sgshapirosfsistat
35964562Sgshapiromlfi_cleanup(ctx, ok)
36064562Sgshapiro	SMFICTX *ctx;
36164562Sgshapiro	bool ok;
36264562Sgshapiro{
36364562Sgshapiro	sfsistat rstat = SMFIS_CONTINUE;
36464562Sgshapiro	struct mlfiPriv *priv = MLFIPRIV;
36564562Sgshapiro	char *p;
36664562Sgshapiro	char host[512];
36764562Sgshapiro	char hbuf[1024];
36864562Sgshapiro
36964562Sgshapiro	if (priv == NULL)
37064562Sgshapiro		return rstat;
37164562Sgshapiro
37264562Sgshapiro	/* close the archive file */
37364562Sgshapiro	if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
37464562Sgshapiro	{
37564562Sgshapiro		/* failed; we have to wait until later */
37664562Sgshapiro		rstat = SMFIS_TEMPFAIL;
37764562Sgshapiro		(void) unlink(priv->mlfi_fname);
37864562Sgshapiro	}
37964562Sgshapiro	else if (ok)
38064562Sgshapiro	{
38164562Sgshapiro		/* add a header to the message announcing our presence */
38264562Sgshapiro		if (gethostname(host, sizeof host) < 0)
38390792Sgshapiro			snprintf(host, sizeof host, "localhost");
38464562Sgshapiro		p = strrchr(priv->mlfi_fname, '/');
38564562Sgshapiro		if (p == NULL)
38664562Sgshapiro			p = priv->mlfi_fname;
38764562Sgshapiro		else
38864562Sgshapiro			p++;
38964562Sgshapiro		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
39064562Sgshapiro		smfi_addheader(ctx, "X-Archived", hbuf);
39164562Sgshapiro	}
39264562Sgshapiro	else
39364562Sgshapiro	{
39464562Sgshapiro		/* message was aborted -- delete the archive file */
39564562Sgshapiro		(void) unlink(priv->mlfi_fname);
39664562Sgshapiro	}
39764562Sgshapiro
39864562Sgshapiro	/* release private memory */
39964562Sgshapiro	free(priv->mlfi_fname);
40064562Sgshapiro	free(priv);
40164562Sgshapiro	smfi_setpriv(ctx, NULL);
40264562Sgshapiro
40364562Sgshapiro	/* return status */
40464562Sgshapiro	return rstat;
40564562Sgshapiro}
40664562Sgshapiro
40764562Sgshapirostruct smfiDesc smfilter =
40864562Sgshapiro{
40964562Sgshapiro	"SampleFilter",	/* filter name */
41064562Sgshapiro	SMFI_VERSION,	/* version code -- do not change */
41164562Sgshapiro	SMFIF_ADDHDRS,	/* flags */
41264562Sgshapiro	NULL,		/* connection info filter */
41364562Sgshapiro	NULL,		/* SMTP HELO command filter */
41464562Sgshapiro	mlfi_envfrom,	/* envelope sender filter */
41564562Sgshapiro	NULL,		/* envelope recipient filter */
41664562Sgshapiro	mlfi_header,	/* header filter */
41764562Sgshapiro	mlfi_eoh,	/* end of header */
41864562Sgshapiro	mlfi_body,	/* body block filter */
41964562Sgshapiro	mlfi_eom,	/* end of message */
42064562Sgshapiro	mlfi_abort,	/* message aborted */
42164562Sgshapiro	mlfi_close	/* connection cleanup */
42264562Sgshapiro};
42364562Sgshapiro
42464562Sgshapiro
42564562Sgshapiroint
42664562Sgshapiromain(argc, argv)
42764562Sgshapiro	int argc;
42864562Sgshapiro	char *argv[];
42964562Sgshapiro{
43064562Sgshapiro	int c;
43164562Sgshapiro	const char *args = "p:";
43264562Sgshapiro
43364562Sgshapiro	/* Process command line options */
43464562Sgshapiro	while ((c = getopt(argc, argv, args)) != -1)
43564562Sgshapiro	{
43664562Sgshapiro		switch (c)
43764562Sgshapiro		{
43864562Sgshapiro		  case 'p':
43964562Sgshapiro			if (optarg == NULL || *optarg == '\0')
44064562Sgshapiro			{
44164562Sgshapiro				(void) fprintf(stderr, "Illegal conn: %s\n",
44264562Sgshapiro					       optarg);
44364562Sgshapiro				exit(EX_USAGE);
44464562Sgshapiro			}
44564562Sgshapiro			(void) smfi_setconn(optarg);
44664562Sgshapiro			break;
44764562Sgshapiro
44864562Sgshapiro		}
44964562Sgshapiro	}
45064562Sgshapiro	if (smfi_register(smfilter) == MI_FAILURE)
45164562Sgshapiro	{
45264562Sgshapiro		fprintf(stderr, "smfi_register failed\n");
45364562Sgshapiro		exit(EX_UNAVAILABLE);
45464562Sgshapiro	}
45564562Sgshapiro	return smfi_main();
45664562Sgshapiro}
45764562Sgshapiro
45864562Sgshapiro/* eof */
45964562Sgshapiro
460110560Sgshapiro$Revision: 8.35.2.1 $, Last updated $Date: 2002/10/21 14:31:57 $
461