xref: /titanic_53/usr/src/cmd/sendmail/libmilter/example.c (revision 058561cbaa119a6f2659bc27ef343e1b47266bb2)
1*058561cbSjbeck /*
2*058561cbSjbeck  *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3*058561cbSjbeck  *	All rights reserved.
4*058561cbSjbeck  *
5*058561cbSjbeck  * By using this file, you agree to the terms and conditions set
6*058561cbSjbeck  * forth in the LICENSE file which can be found at the top level of
7*058561cbSjbeck  * the sendmail distribution.
8*058561cbSjbeck  *
9*058561cbSjbeck  * $Id: example.c,v 8.3 2006/12/20 21:22:34 ca Exp $
10*058561cbSjbeck  */
11*058561cbSjbeck 
12*058561cbSjbeck #pragma ident	"%Z%%M%	%I%	%E% SMI"
13*058561cbSjbeck 
14*058561cbSjbeck /*
15*058561cbSjbeck **  A trivial example filter that logs all email to a file.
16*058561cbSjbeck **  This milter also has some callbacks which it does not really use,
17*058561cbSjbeck **  but they are defined to serve as an example.
18*058561cbSjbeck */
19*058561cbSjbeck 
20*058561cbSjbeck #include <sys/types.h>
21*058561cbSjbeck #include <stdio.h>
22*058561cbSjbeck #include <stdlib.h>
23*058561cbSjbeck #include <string.h>
24*058561cbSjbeck #include <sysexits.h>
25*058561cbSjbeck #include <unistd.h>
26*058561cbSjbeck 
27*058561cbSjbeck #include "libmilter/mfapi.h"
28*058561cbSjbeck #include "libmilter/mfdef.h"
29*058561cbSjbeck 
30*058561cbSjbeck #ifndef true
31*058561cbSjbeck # define false	0
32*058561cbSjbeck # define true	1
33*058561cbSjbeck #endif /* ! true */
34*058561cbSjbeck 
35*058561cbSjbeck struct mlfiPriv
36*058561cbSjbeck {
37*058561cbSjbeck 	char	*mlfi_fname;
38*058561cbSjbeck 	FILE	*mlfi_fp;
39*058561cbSjbeck };
40*058561cbSjbeck 
41*058561cbSjbeck #define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
42*058561cbSjbeck 
43*058561cbSjbeck static unsigned long mta_caps = 0;
44*058561cbSjbeck 
45*058561cbSjbeck sfsistat
46*058561cbSjbeck mlfi_cleanup(ctx, ok)
47*058561cbSjbeck 	SMFICTX *ctx;
48*058561cbSjbeck 	bool ok;
49*058561cbSjbeck {
50*058561cbSjbeck 	sfsistat rstat = SMFIS_CONTINUE;
51*058561cbSjbeck 	struct mlfiPriv *priv = MLFIPRIV;
52*058561cbSjbeck 	char *p;
53*058561cbSjbeck 	char host[512];
54*058561cbSjbeck 	char hbuf[1024];
55*058561cbSjbeck 
56*058561cbSjbeck 	if (priv == NULL)
57*058561cbSjbeck 		return rstat;
58*058561cbSjbeck 
59*058561cbSjbeck 	/* close the archive file */
60*058561cbSjbeck 	if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
61*058561cbSjbeck 	{
62*058561cbSjbeck 		/* failed; we have to wait until later */
63*058561cbSjbeck 		rstat = SMFIS_TEMPFAIL;
64*058561cbSjbeck 		(void) unlink(priv->mlfi_fname);
65*058561cbSjbeck 	}
66*058561cbSjbeck 	else if (ok)
67*058561cbSjbeck 	{
68*058561cbSjbeck 		/* add a header to the message announcing our presence */
69*058561cbSjbeck 		if (gethostname(host, sizeof host) < 0)
70*058561cbSjbeck 			snprintf(host, sizeof host, "localhost");
71*058561cbSjbeck 		p = strrchr(priv->mlfi_fname, '/');
72*058561cbSjbeck 		if (p == NULL)
73*058561cbSjbeck 			p = priv->mlfi_fname;
74*058561cbSjbeck 		else
75*058561cbSjbeck 			p++;
76*058561cbSjbeck 		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
77*058561cbSjbeck 		smfi_addheader(ctx, "X-Archived", hbuf);
78*058561cbSjbeck 	}
79*058561cbSjbeck 	else
80*058561cbSjbeck 	{
81*058561cbSjbeck 		/* message was aborted -- delete the archive file */
82*058561cbSjbeck 		(void) unlink(priv->mlfi_fname);
83*058561cbSjbeck 	}
84*058561cbSjbeck 
85*058561cbSjbeck 	/* release private memory */
86*058561cbSjbeck 	free(priv->mlfi_fname);
87*058561cbSjbeck 	free(priv);
88*058561cbSjbeck 	smfi_setpriv(ctx, NULL);
89*058561cbSjbeck 
90*058561cbSjbeck 	/* return status */
91*058561cbSjbeck 	return rstat;
92*058561cbSjbeck }
93*058561cbSjbeck 
94*058561cbSjbeck 
95*058561cbSjbeck sfsistat
96*058561cbSjbeck mlfi_envfrom(ctx, envfrom)
97*058561cbSjbeck 	SMFICTX *ctx;
98*058561cbSjbeck 	char **envfrom;
99*058561cbSjbeck {
100*058561cbSjbeck 	struct mlfiPriv *priv;
101*058561cbSjbeck 	int fd = -1;
102*058561cbSjbeck 
103*058561cbSjbeck 	/* allocate some private memory */
104*058561cbSjbeck 	priv = malloc(sizeof *priv);
105*058561cbSjbeck 	if (priv == NULL)
106*058561cbSjbeck 	{
107*058561cbSjbeck 		/* can't accept this message right now */
108*058561cbSjbeck 		return SMFIS_TEMPFAIL;
109*058561cbSjbeck 	}
110*058561cbSjbeck 	memset(priv, '\0', sizeof *priv);
111*058561cbSjbeck 
112*058561cbSjbeck 	/* open a file to store this message */
113*058561cbSjbeck 	priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
114*058561cbSjbeck 	if (priv->mlfi_fname == NULL)
115*058561cbSjbeck 	{
116*058561cbSjbeck 		free(priv);
117*058561cbSjbeck 		return SMFIS_TEMPFAIL;
118*058561cbSjbeck 	}
119*058561cbSjbeck 	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
120*058561cbSjbeck 	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
121*058561cbSjbeck 	{
122*058561cbSjbeck 		if (fd >= 0)
123*058561cbSjbeck 			(void) close(fd);
124*058561cbSjbeck 		free(priv->mlfi_fname);
125*058561cbSjbeck 		free(priv);
126*058561cbSjbeck 		return SMFIS_TEMPFAIL;
127*058561cbSjbeck 	}
128*058561cbSjbeck 
129*058561cbSjbeck 	/* save the private data */
130*058561cbSjbeck 	smfi_setpriv(ctx, priv);
131*058561cbSjbeck 
132*058561cbSjbeck 	/* continue processing */
133*058561cbSjbeck 	return SMFIS_CONTINUE;
134*058561cbSjbeck }
135*058561cbSjbeck 
136*058561cbSjbeck sfsistat
137*058561cbSjbeck mlfi_header(ctx, headerf, headerv)
138*058561cbSjbeck 	SMFICTX *ctx;
139*058561cbSjbeck 	char *headerf;
140*058561cbSjbeck 	char *headerv;
141*058561cbSjbeck {
142*058561cbSjbeck 	/* write the header to the log file */
143*058561cbSjbeck 	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
144*058561cbSjbeck 
145*058561cbSjbeck 	/* continue processing */
146*058561cbSjbeck 	return ((mta_caps & SMFIP_NR_HDR) != 0)
147*058561cbSjbeck 		? SMFIS_NOREPLY : SMFIS_CONTINUE;
148*058561cbSjbeck }
149*058561cbSjbeck 
150*058561cbSjbeck sfsistat
151*058561cbSjbeck mlfi_eoh(ctx)
152*058561cbSjbeck 	SMFICTX *ctx;
153*058561cbSjbeck {
154*058561cbSjbeck 	/* output the blank line between the header and the body */
155*058561cbSjbeck 	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
156*058561cbSjbeck 
157*058561cbSjbeck 	/* continue processing */
158*058561cbSjbeck 	return SMFIS_CONTINUE;
159*058561cbSjbeck }
160*058561cbSjbeck 
161*058561cbSjbeck sfsistat
162*058561cbSjbeck mlfi_body(ctx, bodyp, bodylen)
163*058561cbSjbeck 	SMFICTX *ctx;
164*058561cbSjbeck 	u_char *bodyp;
165*058561cbSjbeck 	size_t bodylen;
166*058561cbSjbeck {
167*058561cbSjbeck 	/* output body block to log file */
168*058561cbSjbeck 	if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
169*058561cbSjbeck 	{
170*058561cbSjbeck 		/* write failed */
171*058561cbSjbeck 		(void) mlfi_cleanup(ctx, false);
172*058561cbSjbeck 		return SMFIS_TEMPFAIL;
173*058561cbSjbeck 	}
174*058561cbSjbeck 
175*058561cbSjbeck 	/* continue processing */
176*058561cbSjbeck 	return SMFIS_CONTINUE;
177*058561cbSjbeck }
178*058561cbSjbeck 
179*058561cbSjbeck sfsistat
180*058561cbSjbeck mlfi_eom(ctx)
181*058561cbSjbeck 	SMFICTX *ctx;
182*058561cbSjbeck {
183*058561cbSjbeck 	return mlfi_cleanup(ctx, true);
184*058561cbSjbeck }
185*058561cbSjbeck 
186*058561cbSjbeck sfsistat
187*058561cbSjbeck mlfi_close(ctx)
188*058561cbSjbeck 	SMFICTX *ctx;
189*058561cbSjbeck {
190*058561cbSjbeck 	return SMFIS_ACCEPT;
191*058561cbSjbeck }
192*058561cbSjbeck 
193*058561cbSjbeck sfsistat
194*058561cbSjbeck mlfi_abort(ctx)
195*058561cbSjbeck 	SMFICTX *ctx;
196*058561cbSjbeck {
197*058561cbSjbeck 	return mlfi_cleanup(ctx, false);
198*058561cbSjbeck }
199*058561cbSjbeck 
200*058561cbSjbeck sfsistat
201*058561cbSjbeck mlfi_unknown(ctx, cmd)
202*058561cbSjbeck 	SMFICTX *ctx;
203*058561cbSjbeck 	char *cmd;
204*058561cbSjbeck {
205*058561cbSjbeck 	return SMFIS_CONTINUE;
206*058561cbSjbeck }
207*058561cbSjbeck 
208*058561cbSjbeck sfsistat
209*058561cbSjbeck mlfi_data(ctx)
210*058561cbSjbeck 	SMFICTX *ctx;
211*058561cbSjbeck {
212*058561cbSjbeck 	return SMFIS_CONTINUE;
213*058561cbSjbeck }
214*058561cbSjbeck 
215*058561cbSjbeck sfsistat
216*058561cbSjbeck mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
217*058561cbSjbeck 	SMFICTX *ctx;
218*058561cbSjbeck 	unsigned long f0;
219*058561cbSjbeck 	unsigned long f1;
220*058561cbSjbeck 	unsigned long f2;
221*058561cbSjbeck 	unsigned long f3;
222*058561cbSjbeck 	unsigned long *pf0;
223*058561cbSjbeck 	unsigned long *pf1;
224*058561cbSjbeck 	unsigned long *pf2;
225*058561cbSjbeck 	unsigned long *pf3;
226*058561cbSjbeck {
227*058561cbSjbeck 	/* milter actions: add headers */
228*058561cbSjbeck 	*pf0 = SMFIF_ADDHDRS;
229*058561cbSjbeck 
230*058561cbSjbeck 	/* milter protocol steps: all but connect, HELO, RCPT */
231*058561cbSjbeck 	*pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
232*058561cbSjbeck 	mta_caps = f1;
233*058561cbSjbeck 	if ((mta_caps & SMFIP_NR_HDR) != 0)
234*058561cbSjbeck 		*pf1 |= SMFIP_NR_HDR;
235*058561cbSjbeck 	*pf2 = 0;
236*058561cbSjbeck 	*pf3 = 0;
237*058561cbSjbeck 	return SMFIS_CONTINUE;
238*058561cbSjbeck }
239*058561cbSjbeck 
240*058561cbSjbeck struct smfiDesc smfilter =
241*058561cbSjbeck {
242*058561cbSjbeck 	"SampleFilter",	/* filter name */
243*058561cbSjbeck 	SMFI_VERSION,	/* version code -- do not change */
244*058561cbSjbeck 	SMFIF_ADDHDRS,	/* flags */
245*058561cbSjbeck 	NULL,		/* connection info filter */
246*058561cbSjbeck 	NULL,		/* SMTP HELO command filter */
247*058561cbSjbeck 	mlfi_envfrom,	/* envelope sender filter */
248*058561cbSjbeck 	NULL,		/* envelope recipient filter */
249*058561cbSjbeck 	mlfi_header,	/* header filter */
250*058561cbSjbeck 	mlfi_eoh,	/* end of header */
251*058561cbSjbeck 	mlfi_body,	/* body block filter */
252*058561cbSjbeck 	mlfi_eom,	/* end of message */
253*058561cbSjbeck 	mlfi_abort,	/* message aborted */
254*058561cbSjbeck 	mlfi_close,	/* connection cleanup */
255*058561cbSjbeck 	mlfi_unknown,	/* unknown/unimplemented SMTP commands */
256*058561cbSjbeck 	mlfi_data,	/* DATA command filter */
257*058561cbSjbeck 	mlfi_negotiate	/* option negotation at connection startup */
258*058561cbSjbeck };
259*058561cbSjbeck 
260*058561cbSjbeck int
261*058561cbSjbeck main(argc, argv)
262*058561cbSjbeck 	int argc;
263*058561cbSjbeck 	char *argv[];
264*058561cbSjbeck {
265*058561cbSjbeck 	bool setconn;
266*058561cbSjbeck 	int c;
267*058561cbSjbeck 
268*058561cbSjbeck 	setconn = false;
269*058561cbSjbeck 
270*058561cbSjbeck 	/* Process command line options */
271*058561cbSjbeck 	while ((c = getopt(argc, argv, "p:")) != -1)
272*058561cbSjbeck 	{
273*058561cbSjbeck 		switch (c)
274*058561cbSjbeck 		{
275*058561cbSjbeck 		  case 'p':
276*058561cbSjbeck 			if (optarg == NULL || *optarg == '\0')
277*058561cbSjbeck 			{
278*058561cbSjbeck 				(void) fprintf(stderr, "Illegal conn: %s\n",
279*058561cbSjbeck 					       optarg);
280*058561cbSjbeck 				exit(EX_USAGE);
281*058561cbSjbeck 			}
282*058561cbSjbeck 			(void) smfi_setconn(optarg);
283*058561cbSjbeck 			setconn = true;
284*058561cbSjbeck 			break;
285*058561cbSjbeck 
286*058561cbSjbeck 		}
287*058561cbSjbeck 	}
288*058561cbSjbeck 	if (!setconn)
289*058561cbSjbeck 	{
290*058561cbSjbeck 		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
291*058561cbSjbeck 		exit(EX_USAGE);
292*058561cbSjbeck 	}
293*058561cbSjbeck 	if (smfi_register(smfilter) == MI_FAILURE)
294*058561cbSjbeck 	{
295*058561cbSjbeck 		fprintf(stderr, "smfi_register failed\n");
296*058561cbSjbeck 		exit(EX_UNAVAILABLE);
297*058561cbSjbeck 	}
298*058561cbSjbeck 	return smfi_main();
299*058561cbSjbeck }
300*058561cbSjbeck 
301