1058561cbSjbeck /*
2058561cbSjbeck  *  Copyright (c) 2006 Sendmail, Inc. and its suppliers.
3058561cbSjbeck  *	All rights reserved.
4058561cbSjbeck  *
5058561cbSjbeck  * By using this file, you agree to the terms and conditions set
6058561cbSjbeck  * forth in the LICENSE file which can be found at the top level of
7058561cbSjbeck  * the sendmail distribution.
8058561cbSjbeck  *
9*e9af4bc0SJohn Beck  * $Id: example.c,v 8.4 2008/07/22 15:12:47 ca Exp $
10058561cbSjbeck  */
11058561cbSjbeck 
12058561cbSjbeck /*
13058561cbSjbeck **  A trivial example filter that logs all email to a file.
14058561cbSjbeck **  This milter also has some callbacks which it does not really use,
15058561cbSjbeck **  but they are defined to serve as an example.
16058561cbSjbeck */
17058561cbSjbeck 
18058561cbSjbeck #include <sys/types.h>
19058561cbSjbeck #include <stdio.h>
20058561cbSjbeck #include <stdlib.h>
21058561cbSjbeck #include <string.h>
22058561cbSjbeck #include <sysexits.h>
23058561cbSjbeck #include <unistd.h>
24058561cbSjbeck 
25058561cbSjbeck #include "libmilter/mfapi.h"
26058561cbSjbeck #include "libmilter/mfdef.h"
27058561cbSjbeck 
28058561cbSjbeck #ifndef true
29058561cbSjbeck # define false	0
30058561cbSjbeck # define true	1
31058561cbSjbeck #endif /* ! true */
32058561cbSjbeck 
33058561cbSjbeck struct mlfiPriv
34058561cbSjbeck {
35058561cbSjbeck 	char	*mlfi_fname;
36058561cbSjbeck 	FILE	*mlfi_fp;
37058561cbSjbeck };
38058561cbSjbeck 
39058561cbSjbeck #define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
40058561cbSjbeck 
41058561cbSjbeck static unsigned long mta_caps = 0;
42058561cbSjbeck 
43058561cbSjbeck sfsistat
mlfi_cleanup(ctx,ok)44058561cbSjbeck mlfi_cleanup(ctx, ok)
45058561cbSjbeck 	SMFICTX *ctx;
46058561cbSjbeck 	bool ok;
47058561cbSjbeck {
48058561cbSjbeck 	sfsistat rstat = SMFIS_CONTINUE;
49058561cbSjbeck 	struct mlfiPriv *priv = MLFIPRIV;
50058561cbSjbeck 	char *p;
51058561cbSjbeck 	char host[512];
52058561cbSjbeck 	char hbuf[1024];
53058561cbSjbeck 
54058561cbSjbeck 	if (priv == NULL)
55058561cbSjbeck 		return rstat;
56058561cbSjbeck 
57058561cbSjbeck 	/* close the archive file */
58058561cbSjbeck 	if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
59058561cbSjbeck 	{
60058561cbSjbeck 		/* failed; we have to wait until later */
61058561cbSjbeck 		rstat = SMFIS_TEMPFAIL;
62058561cbSjbeck 		(void) unlink(priv->mlfi_fname);
63058561cbSjbeck 	}
64058561cbSjbeck 	else if (ok)
65058561cbSjbeck 	{
66058561cbSjbeck 		/* add a header to the message announcing our presence */
67058561cbSjbeck 		if (gethostname(host, sizeof host) < 0)
68058561cbSjbeck 			snprintf(host, sizeof host, "localhost");
69058561cbSjbeck 		p = strrchr(priv->mlfi_fname, '/');
70058561cbSjbeck 		if (p == NULL)
71058561cbSjbeck 			p = priv->mlfi_fname;
72058561cbSjbeck 		else
73058561cbSjbeck 			p++;
74058561cbSjbeck 		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
75058561cbSjbeck 		smfi_addheader(ctx, "X-Archived", hbuf);
76058561cbSjbeck 	}
77058561cbSjbeck 	else
78058561cbSjbeck 	{
79058561cbSjbeck 		/* message was aborted -- delete the archive file */
80058561cbSjbeck 		(void) unlink(priv->mlfi_fname);
81058561cbSjbeck 	}
82058561cbSjbeck 
83058561cbSjbeck 	/* release private memory */
84058561cbSjbeck 	free(priv->mlfi_fname);
85058561cbSjbeck 	free(priv);
86058561cbSjbeck 	smfi_setpriv(ctx, NULL);
87058561cbSjbeck 
88058561cbSjbeck 	/* return status */
89058561cbSjbeck 	return rstat;
90058561cbSjbeck }
91058561cbSjbeck 
92058561cbSjbeck 
93058561cbSjbeck sfsistat
mlfi_envfrom(ctx,envfrom)94058561cbSjbeck mlfi_envfrom(ctx, envfrom)
95058561cbSjbeck 	SMFICTX *ctx;
96058561cbSjbeck 	char **envfrom;
97058561cbSjbeck {
98058561cbSjbeck 	struct mlfiPriv *priv;
99058561cbSjbeck 	int fd = -1;
100058561cbSjbeck 
101058561cbSjbeck 	/* allocate some private memory */
102058561cbSjbeck 	priv = malloc(sizeof *priv);
103058561cbSjbeck 	if (priv == NULL)
104058561cbSjbeck 	{
105058561cbSjbeck 		/* can't accept this message right now */
106058561cbSjbeck 		return SMFIS_TEMPFAIL;
107058561cbSjbeck 	}
108058561cbSjbeck 	memset(priv, '\0', sizeof *priv);
109058561cbSjbeck 
110058561cbSjbeck 	/* open a file to store this message */
111058561cbSjbeck 	priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
112058561cbSjbeck 	if (priv->mlfi_fname == NULL)
113058561cbSjbeck 	{
114058561cbSjbeck 		free(priv);
115058561cbSjbeck 		return SMFIS_TEMPFAIL;
116058561cbSjbeck 	}
117058561cbSjbeck 	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
118058561cbSjbeck 	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
119058561cbSjbeck 	{
120058561cbSjbeck 		if (fd >= 0)
121058561cbSjbeck 			(void) close(fd);
122058561cbSjbeck 		free(priv->mlfi_fname);
123058561cbSjbeck 		free(priv);
124058561cbSjbeck 		return SMFIS_TEMPFAIL;
125058561cbSjbeck 	}
126058561cbSjbeck 
127058561cbSjbeck 	/* save the private data */
128058561cbSjbeck 	smfi_setpriv(ctx, priv);
129058561cbSjbeck 
130058561cbSjbeck 	/* continue processing */
131058561cbSjbeck 	return SMFIS_CONTINUE;
132058561cbSjbeck }
133058561cbSjbeck 
134058561cbSjbeck sfsistat
mlfi_header(ctx,headerf,headerv)135058561cbSjbeck mlfi_header(ctx, headerf, headerv)
136058561cbSjbeck 	SMFICTX *ctx;
137058561cbSjbeck 	char *headerf;
138058561cbSjbeck 	char *headerv;
139058561cbSjbeck {
140058561cbSjbeck 	/* write the header to the log file */
141058561cbSjbeck 	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
142058561cbSjbeck 
143058561cbSjbeck 	/* continue processing */
144058561cbSjbeck 	return ((mta_caps & SMFIP_NR_HDR) != 0)
145058561cbSjbeck 		? SMFIS_NOREPLY : SMFIS_CONTINUE;
146058561cbSjbeck }
147058561cbSjbeck 
148058561cbSjbeck sfsistat
mlfi_eoh(ctx)149058561cbSjbeck mlfi_eoh(ctx)
150058561cbSjbeck 	SMFICTX *ctx;
151058561cbSjbeck {
152058561cbSjbeck 	/* output the blank line between the header and the body */
153058561cbSjbeck 	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
154058561cbSjbeck 
155058561cbSjbeck 	/* continue processing */
156058561cbSjbeck 	return SMFIS_CONTINUE;
157058561cbSjbeck }
158058561cbSjbeck 
159058561cbSjbeck sfsistat
mlfi_body(ctx,bodyp,bodylen)160058561cbSjbeck mlfi_body(ctx, bodyp, bodylen)
161058561cbSjbeck 	SMFICTX *ctx;
162058561cbSjbeck 	u_char *bodyp;
163058561cbSjbeck 	size_t bodylen;
164058561cbSjbeck {
165058561cbSjbeck 	/* output body block to log file */
166058561cbSjbeck 	if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
167058561cbSjbeck 	{
168058561cbSjbeck 		/* write failed */
169058561cbSjbeck 		(void) mlfi_cleanup(ctx, false);
170058561cbSjbeck 		return SMFIS_TEMPFAIL;
171058561cbSjbeck 	}
172058561cbSjbeck 
173058561cbSjbeck 	/* continue processing */
174058561cbSjbeck 	return SMFIS_CONTINUE;
175058561cbSjbeck }
176058561cbSjbeck 
177058561cbSjbeck sfsistat
mlfi_eom(ctx)178058561cbSjbeck mlfi_eom(ctx)
179058561cbSjbeck 	SMFICTX *ctx;
180058561cbSjbeck {
181058561cbSjbeck 	return mlfi_cleanup(ctx, true);
182058561cbSjbeck }
183058561cbSjbeck 
184058561cbSjbeck sfsistat
mlfi_close(ctx)185058561cbSjbeck mlfi_close(ctx)
186058561cbSjbeck 	SMFICTX *ctx;
187058561cbSjbeck {
188058561cbSjbeck 	return SMFIS_ACCEPT;
189058561cbSjbeck }
190058561cbSjbeck 
191058561cbSjbeck sfsistat
mlfi_abort(ctx)192058561cbSjbeck mlfi_abort(ctx)
193058561cbSjbeck 	SMFICTX *ctx;
194058561cbSjbeck {
195058561cbSjbeck 	return mlfi_cleanup(ctx, false);
196058561cbSjbeck }
197058561cbSjbeck 
198058561cbSjbeck sfsistat
mlfi_unknown(ctx,cmd)199058561cbSjbeck mlfi_unknown(ctx, cmd)
200058561cbSjbeck 	SMFICTX *ctx;
201058561cbSjbeck 	char *cmd;
202058561cbSjbeck {
203058561cbSjbeck 	return SMFIS_CONTINUE;
204058561cbSjbeck }
205058561cbSjbeck 
206058561cbSjbeck sfsistat
mlfi_data(ctx)207058561cbSjbeck mlfi_data(ctx)
208058561cbSjbeck 	SMFICTX *ctx;
209058561cbSjbeck {
210058561cbSjbeck 	return SMFIS_CONTINUE;
211058561cbSjbeck }
212058561cbSjbeck 
213058561cbSjbeck sfsistat
mlfi_negotiate(ctx,f0,f1,f2,f3,pf0,pf1,pf2,pf3)214058561cbSjbeck mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
215058561cbSjbeck 	SMFICTX *ctx;
216058561cbSjbeck 	unsigned long f0;
217058561cbSjbeck 	unsigned long f1;
218058561cbSjbeck 	unsigned long f2;
219058561cbSjbeck 	unsigned long f3;
220058561cbSjbeck 	unsigned long *pf0;
221058561cbSjbeck 	unsigned long *pf1;
222058561cbSjbeck 	unsigned long *pf2;
223058561cbSjbeck 	unsigned long *pf3;
224058561cbSjbeck {
225058561cbSjbeck 	/* milter actions: add headers */
226058561cbSjbeck 	*pf0 = SMFIF_ADDHDRS;
227058561cbSjbeck 
228058561cbSjbeck 	/* milter protocol steps: all but connect, HELO, RCPT */
229058561cbSjbeck 	*pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
230058561cbSjbeck 	mta_caps = f1;
231058561cbSjbeck 	if ((mta_caps & SMFIP_NR_HDR) != 0)
232058561cbSjbeck 		*pf1 |= SMFIP_NR_HDR;
233058561cbSjbeck 	*pf2 = 0;
234058561cbSjbeck 	*pf3 = 0;
235058561cbSjbeck 	return SMFIS_CONTINUE;
236058561cbSjbeck }
237058561cbSjbeck 
238058561cbSjbeck struct smfiDesc smfilter =
239058561cbSjbeck {
240058561cbSjbeck 	"SampleFilter",	/* filter name */
241058561cbSjbeck 	SMFI_VERSION,	/* version code -- do not change */
242058561cbSjbeck 	SMFIF_ADDHDRS,	/* flags */
243058561cbSjbeck 	NULL,		/* connection info filter */
244058561cbSjbeck 	NULL,		/* SMTP HELO command filter */
245058561cbSjbeck 	mlfi_envfrom,	/* envelope sender filter */
246058561cbSjbeck 	NULL,		/* envelope recipient filter */
247058561cbSjbeck 	mlfi_header,	/* header filter */
248058561cbSjbeck 	mlfi_eoh,	/* end of header */
249058561cbSjbeck 	mlfi_body,	/* body block filter */
250058561cbSjbeck 	mlfi_eom,	/* end of message */
251058561cbSjbeck 	mlfi_abort,	/* message aborted */
252058561cbSjbeck 	mlfi_close,	/* connection cleanup */
253058561cbSjbeck 	mlfi_unknown,	/* unknown/unimplemented SMTP commands */
254058561cbSjbeck 	mlfi_data,	/* DATA command filter */
255*e9af4bc0SJohn Beck 	mlfi_negotiate	/* option negotiation at connection startup */
256058561cbSjbeck };
257058561cbSjbeck 
258058561cbSjbeck int
main(argc,argv)259058561cbSjbeck main(argc, argv)
260058561cbSjbeck 	int argc;
261058561cbSjbeck 	char *argv[];
262058561cbSjbeck {
263058561cbSjbeck 	bool setconn;
264058561cbSjbeck 	int c;
265058561cbSjbeck 
266058561cbSjbeck 	setconn = false;
267058561cbSjbeck 
268058561cbSjbeck 	/* Process command line options */
269058561cbSjbeck 	while ((c = getopt(argc, argv, "p:")) != -1)
270058561cbSjbeck 	{
271058561cbSjbeck 		switch (c)
272058561cbSjbeck 		{
273058561cbSjbeck 		  case 'p':
274058561cbSjbeck 			if (optarg == NULL || *optarg == '\0')
275058561cbSjbeck 			{
276058561cbSjbeck 				(void) fprintf(stderr, "Illegal conn: %s\n",
277058561cbSjbeck 					       optarg);
278058561cbSjbeck 				exit(EX_USAGE);
279058561cbSjbeck 			}
280058561cbSjbeck 			(void) smfi_setconn(optarg);
281058561cbSjbeck 			setconn = true;
282058561cbSjbeck 			break;
283058561cbSjbeck 
284058561cbSjbeck 		}
285058561cbSjbeck 	}
286058561cbSjbeck 	if (!setconn)
287058561cbSjbeck 	{
288058561cbSjbeck 		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
289058561cbSjbeck 		exit(EX_USAGE);
290058561cbSjbeck 	}
291058561cbSjbeck 	if (smfi_register(smfilter) == MI_FAILURE)
292058561cbSjbeck 	{
293058561cbSjbeck 		fprintf(stderr, "smfi_register failed\n");
294058561cbSjbeck 		exit(EX_UNAVAILABLE);
295058561cbSjbeck 	}
296058561cbSjbeck 	return smfi_main();
297058561cbSjbeck }
298058561cbSjbeck 
299