xref: /onnv-gate/usr/src/cmd/sendmail/libmilter/example.c (revision 11440:802724e2906a)
13544Sjbeck /*
23544Sjbeck  *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
33544Sjbeck  *	All rights reserved.
43544Sjbeck  *
53544Sjbeck  * By using this file, you agree to the terms and conditions set
63544Sjbeck  * forth in the LICENSE file which can be found at the top level of
73544Sjbeck  * the sendmail distribution.
83544Sjbeck  *
9*11440SJohn.Beck@Sun.COM  * $Id: example.c,v 8.4 2008/07/22 15:12:47 ca Exp $
103544Sjbeck  */
113544Sjbeck 
123544Sjbeck /*
133544Sjbeck **  A trivial example filter that logs all email to a file.
143544Sjbeck **  This milter also has some callbacks which it does not really use,
153544Sjbeck **  but they are defined to serve as an example.
163544Sjbeck */
173544Sjbeck 
183544Sjbeck #include <sys/types.h>
193544Sjbeck #include <stdio.h>
203544Sjbeck #include <stdlib.h>
213544Sjbeck #include <string.h>
223544Sjbeck #include <sysexits.h>
233544Sjbeck #include <unistd.h>
243544Sjbeck 
253544Sjbeck #include "libmilter/mfapi.h"
263544Sjbeck #include "libmilter/mfdef.h"
273544Sjbeck 
283544Sjbeck #ifndef true
293544Sjbeck # define false	0
303544Sjbeck # define true	1
313544Sjbeck #endif /* ! true */
323544Sjbeck 
333544Sjbeck struct mlfiPriv
343544Sjbeck {
353544Sjbeck 	char	*mlfi_fname;
363544Sjbeck 	FILE	*mlfi_fp;
373544Sjbeck };
383544Sjbeck 
393544Sjbeck #define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
403544Sjbeck 
413544Sjbeck static unsigned long mta_caps = 0;
423544Sjbeck 
433544Sjbeck sfsistat
mlfi_cleanup(ctx,ok)443544Sjbeck mlfi_cleanup(ctx, ok)
453544Sjbeck 	SMFICTX *ctx;
463544Sjbeck 	bool ok;
473544Sjbeck {
483544Sjbeck 	sfsistat rstat = SMFIS_CONTINUE;
493544Sjbeck 	struct mlfiPriv *priv = MLFIPRIV;
503544Sjbeck 	char *p;
513544Sjbeck 	char host[512];
523544Sjbeck 	char hbuf[1024];
533544Sjbeck 
543544Sjbeck 	if (priv == NULL)
553544Sjbeck 		return rstat;
563544Sjbeck 
573544Sjbeck 	/* close the archive file */
583544Sjbeck 	if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
593544Sjbeck 	{
603544Sjbeck 		/* failed; we have to wait until later */
613544Sjbeck 		rstat = SMFIS_TEMPFAIL;
623544Sjbeck 		(void) unlink(priv->mlfi_fname);
633544Sjbeck 	}
643544Sjbeck 	else if (ok)
653544Sjbeck 	{
663544Sjbeck 		/* add a header to the message announcing our presence */
673544Sjbeck 		if (gethostname(host, sizeof host) < 0)
683544Sjbeck 			snprintf(host, sizeof host, "localhost");
693544Sjbeck 		p = strrchr(priv->mlfi_fname, '/');
703544Sjbeck 		if (p == NULL)
713544Sjbeck 			p = priv->mlfi_fname;
723544Sjbeck 		else
733544Sjbeck 			p++;
743544Sjbeck 		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
753544Sjbeck 		smfi_addheader(ctx, "X-Archived", hbuf);
763544Sjbeck 	}
773544Sjbeck 	else
783544Sjbeck 	{
793544Sjbeck 		/* message was aborted -- delete the archive file */
803544Sjbeck 		(void) unlink(priv->mlfi_fname);
813544Sjbeck 	}
823544Sjbeck 
833544Sjbeck 	/* release private memory */
843544Sjbeck 	free(priv->mlfi_fname);
853544Sjbeck 	free(priv);
863544Sjbeck 	smfi_setpriv(ctx, NULL);
873544Sjbeck 
883544Sjbeck 	/* return status */
893544Sjbeck 	return rstat;
903544Sjbeck }
913544Sjbeck 
923544Sjbeck 
933544Sjbeck sfsistat
mlfi_envfrom(ctx,envfrom)943544Sjbeck mlfi_envfrom(ctx, envfrom)
953544Sjbeck 	SMFICTX *ctx;
963544Sjbeck 	char **envfrom;
973544Sjbeck {
983544Sjbeck 	struct mlfiPriv *priv;
993544Sjbeck 	int fd = -1;
1003544Sjbeck 
1013544Sjbeck 	/* allocate some private memory */
1023544Sjbeck 	priv = malloc(sizeof *priv);
1033544Sjbeck 	if (priv == NULL)
1043544Sjbeck 	{
1053544Sjbeck 		/* can't accept this message right now */
1063544Sjbeck 		return SMFIS_TEMPFAIL;
1073544Sjbeck 	}
1083544Sjbeck 	memset(priv, '\0', sizeof *priv);
1093544Sjbeck 
1103544Sjbeck 	/* open a file to store this message */
1113544Sjbeck 	priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
1123544Sjbeck 	if (priv->mlfi_fname == NULL)
1133544Sjbeck 	{
1143544Sjbeck 		free(priv);
1153544Sjbeck 		return SMFIS_TEMPFAIL;
1163544Sjbeck 	}
1173544Sjbeck 	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
1183544Sjbeck 	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
1193544Sjbeck 	{
1203544Sjbeck 		if (fd >= 0)
1213544Sjbeck 			(void) close(fd);
1223544Sjbeck 		free(priv->mlfi_fname);
1233544Sjbeck 		free(priv);
1243544Sjbeck 		return SMFIS_TEMPFAIL;
1253544Sjbeck 	}
1263544Sjbeck 
1273544Sjbeck 	/* save the private data */
1283544Sjbeck 	smfi_setpriv(ctx, priv);
1293544Sjbeck 
1303544Sjbeck 	/* continue processing */
1313544Sjbeck 	return SMFIS_CONTINUE;
1323544Sjbeck }
1333544Sjbeck 
1343544Sjbeck sfsistat
mlfi_header(ctx,headerf,headerv)1353544Sjbeck mlfi_header(ctx, headerf, headerv)
1363544Sjbeck 	SMFICTX *ctx;
1373544Sjbeck 	char *headerf;
1383544Sjbeck 	char *headerv;
1393544Sjbeck {
1403544Sjbeck 	/* write the header to the log file */
1413544Sjbeck 	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
1423544Sjbeck 
1433544Sjbeck 	/* continue processing */
1443544Sjbeck 	return ((mta_caps & SMFIP_NR_HDR) != 0)
1453544Sjbeck 		? SMFIS_NOREPLY : SMFIS_CONTINUE;
1463544Sjbeck }
1473544Sjbeck 
1483544Sjbeck sfsistat
mlfi_eoh(ctx)1493544Sjbeck mlfi_eoh(ctx)
1503544Sjbeck 	SMFICTX *ctx;
1513544Sjbeck {
1523544Sjbeck 	/* output the blank line between the header and the body */
1533544Sjbeck 	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
1543544Sjbeck 
1553544Sjbeck 	/* continue processing */
1563544Sjbeck 	return SMFIS_CONTINUE;
1573544Sjbeck }
1583544Sjbeck 
1593544Sjbeck sfsistat
mlfi_body(ctx,bodyp,bodylen)1603544Sjbeck mlfi_body(ctx, bodyp, bodylen)
1613544Sjbeck 	SMFICTX *ctx;
1623544Sjbeck 	u_char *bodyp;
1633544Sjbeck 	size_t bodylen;
1643544Sjbeck {
1653544Sjbeck 	/* output body block to log file */
1663544Sjbeck 	if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
1673544Sjbeck 	{
1683544Sjbeck 		/* write failed */
1693544Sjbeck 		(void) mlfi_cleanup(ctx, false);
1703544Sjbeck 		return SMFIS_TEMPFAIL;
1713544Sjbeck 	}
1723544Sjbeck 
1733544Sjbeck 	/* continue processing */
1743544Sjbeck 	return SMFIS_CONTINUE;
1753544Sjbeck }
1763544Sjbeck 
1773544Sjbeck sfsistat
mlfi_eom(ctx)1783544Sjbeck mlfi_eom(ctx)
1793544Sjbeck 	SMFICTX *ctx;
1803544Sjbeck {
1813544Sjbeck 	return mlfi_cleanup(ctx, true);
1823544Sjbeck }
1833544Sjbeck 
1843544Sjbeck sfsistat
mlfi_close(ctx)1853544Sjbeck mlfi_close(ctx)
1863544Sjbeck 	SMFICTX *ctx;
1873544Sjbeck {
1883544Sjbeck 	return SMFIS_ACCEPT;
1893544Sjbeck }
1903544Sjbeck 
1913544Sjbeck sfsistat
mlfi_abort(ctx)1923544Sjbeck mlfi_abort(ctx)
1933544Sjbeck 	SMFICTX *ctx;
1943544Sjbeck {
1953544Sjbeck 	return mlfi_cleanup(ctx, false);
1963544Sjbeck }
1973544Sjbeck 
1983544Sjbeck sfsistat
mlfi_unknown(ctx,cmd)1993544Sjbeck mlfi_unknown(ctx, cmd)
2003544Sjbeck 	SMFICTX *ctx;
2013544Sjbeck 	char *cmd;
2023544Sjbeck {
2033544Sjbeck 	return SMFIS_CONTINUE;
2043544Sjbeck }
2053544Sjbeck 
2063544Sjbeck sfsistat
mlfi_data(ctx)2073544Sjbeck mlfi_data(ctx)
2083544Sjbeck 	SMFICTX *ctx;
2093544Sjbeck {
2103544Sjbeck 	return SMFIS_CONTINUE;
2113544Sjbeck }
2123544Sjbeck 
2133544Sjbeck sfsistat
mlfi_negotiate(ctx,f0,f1,f2,f3,pf0,pf1,pf2,pf3)2143544Sjbeck mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
2153544Sjbeck 	SMFICTX *ctx;
2163544Sjbeck 	unsigned long f0;
2173544Sjbeck 	unsigned long f1;
2183544Sjbeck 	unsigned long f2;
2193544Sjbeck 	unsigned long f3;
2203544Sjbeck 	unsigned long *pf0;
2213544Sjbeck 	unsigned long *pf1;
2223544Sjbeck 	unsigned long *pf2;
2233544Sjbeck 	unsigned long *pf3;
2243544Sjbeck {
2253544Sjbeck 	/* milter actions: add headers */
2263544Sjbeck 	*pf0 = SMFIF_ADDHDRS;
2273544Sjbeck 
2283544Sjbeck 	/* milter protocol steps: all but connect, HELO, RCPT */
2293544Sjbeck 	*pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
2303544Sjbeck 	mta_caps = f1;
2313544Sjbeck 	if ((mta_caps & SMFIP_NR_HDR) != 0)
2323544Sjbeck 		*pf1 |= SMFIP_NR_HDR;
2333544Sjbeck 	*pf2 = 0;
2343544Sjbeck 	*pf3 = 0;
2353544Sjbeck 	return SMFIS_CONTINUE;
2363544Sjbeck }
2373544Sjbeck 
2383544Sjbeck struct smfiDesc smfilter =
2393544Sjbeck {
2403544Sjbeck 	"SampleFilter",	/* filter name */
2413544Sjbeck 	SMFI_VERSION,	/* version code -- do not change */
2423544Sjbeck 	SMFIF_ADDHDRS,	/* flags */
2433544Sjbeck 	NULL,		/* connection info filter */
2443544Sjbeck 	NULL,		/* SMTP HELO command filter */
2453544Sjbeck 	mlfi_envfrom,	/* envelope sender filter */
2463544Sjbeck 	NULL,		/* envelope recipient filter */
2473544Sjbeck 	mlfi_header,	/* header filter */
2483544Sjbeck 	mlfi_eoh,	/* end of header */
2493544Sjbeck 	mlfi_body,	/* body block filter */
2503544Sjbeck 	mlfi_eom,	/* end of message */
2513544Sjbeck 	mlfi_abort,	/* message aborted */
2523544Sjbeck 	mlfi_close,	/* connection cleanup */
2533544Sjbeck 	mlfi_unknown,	/* unknown/unimplemented SMTP commands */
2543544Sjbeck 	mlfi_data,	/* DATA command filter */
255*11440SJohn.Beck@Sun.COM 	mlfi_negotiate	/* option negotiation at connection startup */
2563544Sjbeck };
2573544Sjbeck 
2583544Sjbeck int
main(argc,argv)2593544Sjbeck main(argc, argv)
2603544Sjbeck 	int argc;
2613544Sjbeck 	char *argv[];
2623544Sjbeck {
2633544Sjbeck 	bool setconn;
2643544Sjbeck 	int c;
2653544Sjbeck 
2663544Sjbeck 	setconn = false;
2673544Sjbeck 
2683544Sjbeck 	/* Process command line options */
2693544Sjbeck 	while ((c = getopt(argc, argv, "p:")) != -1)
2703544Sjbeck 	{
2713544Sjbeck 		switch (c)
2723544Sjbeck 		{
2733544Sjbeck 		  case 'p':
2743544Sjbeck 			if (optarg == NULL || *optarg == '\0')
2753544Sjbeck 			{
2763544Sjbeck 				(void) fprintf(stderr, "Illegal conn: %s\n",
2773544Sjbeck 					       optarg);
2783544Sjbeck 				exit(EX_USAGE);
2793544Sjbeck 			}
2803544Sjbeck 			(void) smfi_setconn(optarg);
2813544Sjbeck 			setconn = true;
2823544Sjbeck 			break;
2833544Sjbeck 
2843544Sjbeck 		}
2853544Sjbeck 	}
2863544Sjbeck 	if (!setconn)
2873544Sjbeck 	{
2883544Sjbeck 		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
2893544Sjbeck 		exit(EX_USAGE);
2903544Sjbeck 	}
2913544Sjbeck 	if (smfi_register(smfilter) == MI_FAILURE)
2923544Sjbeck 	{
2933544Sjbeck 		fprintf(stderr, "smfi_register failed\n");
2943544Sjbeck 		exit(EX_UNAVAILABLE);
2953544Sjbeck 	}
2963544Sjbeck 	return smfi_main();
2973544Sjbeck }
2983544Sjbeck 
299