example.c revision 302408
1230557Sjimharris/*
2230557Sjimharris *  Copyright (c) 2006 Proofpoint, Inc. and its suppliers.
3230557Sjimharris *	All rights reserved.
4230557Sjimharris *
5230557Sjimharris * By using this file, you agree to the terms and conditions set
6230557Sjimharris * forth in the LICENSE file which can be found at the top level of
7230557Sjimharris * the sendmail distribution.
8230557Sjimharris *
9230557Sjimharris * $Id: example.c,v 8.5 2013-11-22 20:51:36 ca Exp $
10230557Sjimharris */
11230557Sjimharris
12230557Sjimharris/*
13230557Sjimharris**  A trivial example filter that logs all email to a file.
14230557Sjimharris**  This milter also has some callbacks which it does not really use,
15230557Sjimharris**  but they are defined to serve as an example.
16230557Sjimharris*/
17230557Sjimharris
18230557Sjimharris#include <sys/types.h>
19230557Sjimharris#include <stdio.h>
20230557Sjimharris#include <stdlib.h>
21230557Sjimharris#include <string.h>
22230557Sjimharris#include <sysexits.h>
23230557Sjimharris#include <unistd.h>
24230557Sjimharris
25230557Sjimharris#include "libmilter/mfapi.h"
26230557Sjimharris#include "libmilter/mfdef.h"
27230557Sjimharris
28230557Sjimharris#ifndef true
29230557Sjimharris# define false	0
30230557Sjimharris# define true	1
31230557Sjimharris#endif /* ! true */
32230557Sjimharris
33230557Sjimharrisstruct mlfiPriv
34230557Sjimharris{
35230557Sjimharris	char	*mlfi_fname;
36230557Sjimharris	FILE	*mlfi_fp;
37230557Sjimharris};
38230557Sjimharris
39230557Sjimharris#define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
40230557Sjimharris
41230557Sjimharrisstatic unsigned long mta_caps = 0;
42230557Sjimharris
43230557Sjimharrissfsistat
44230557Sjimharrismlfi_cleanup(ctx, ok)
45230557Sjimharris	SMFICTX *ctx;
46230557Sjimharris	bool ok;
47230557Sjimharris{
48230557Sjimharris	sfsistat rstat = SMFIS_CONTINUE;
49230557Sjimharris	struct mlfiPriv *priv = MLFIPRIV;
50230557Sjimharris	char *p;
51230557Sjimharris	char host[512];
52230557Sjimharris	char hbuf[1024];
53230557Sjimharris
54230557Sjimharris	if (priv == NULL)
55230557Sjimharris		return rstat;
56230557Sjimharris
57230557Sjimharris	/* close the archive file */
58230557Sjimharris	if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
59230557Sjimharris	{
60230557Sjimharris		/* failed; we have to wait until later */
61230557Sjimharris		rstat = SMFIS_TEMPFAIL;
62230557Sjimharris		(void) unlink(priv->mlfi_fname);
63230557Sjimharris	}
64230557Sjimharris	else if (ok)
65230557Sjimharris	{
66230557Sjimharris		/* add a header to the message announcing our presence */
67230557Sjimharris		if (gethostname(host, sizeof host) < 0)
68230557Sjimharris			snprintf(host, sizeof host, "localhost");
69230557Sjimharris		p = strrchr(priv->mlfi_fname, '/');
70230557Sjimharris		if (p == NULL)
71230557Sjimharris			p = priv->mlfi_fname;
72230557Sjimharris		else
73230557Sjimharris			p++;
74230557Sjimharris		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
75230557Sjimharris		smfi_addheader(ctx, "X-Archived", hbuf);
76230557Sjimharris	}
77230557Sjimharris	else
78230557Sjimharris	{
79230557Sjimharris		/* message was aborted -- delete the archive file */
80230557Sjimharris		(void) unlink(priv->mlfi_fname);
81230557Sjimharris	}
82230557Sjimharris
83230557Sjimharris	/* release private memory */
84230557Sjimharris	free(priv->mlfi_fname);
85230557Sjimharris	free(priv);
86230557Sjimharris	smfi_setpriv(ctx, NULL);
87230557Sjimharris
88230557Sjimharris	/* return status */
89230557Sjimharris	return rstat;
90230557Sjimharris}
91230557Sjimharris
92230557Sjimharris
93230557Sjimharrissfsistat
94230557Sjimharrismlfi_envfrom(ctx, envfrom)
95230557Sjimharris	SMFICTX *ctx;
96230557Sjimharris	char **envfrom;
97230557Sjimharris{
98230557Sjimharris	struct mlfiPriv *priv;
99230557Sjimharris	int fd = -1;
100230557Sjimharris
101230557Sjimharris	/* allocate some private memory */
102230557Sjimharris	priv = malloc(sizeof *priv);
103230557Sjimharris	if (priv == NULL)
104230557Sjimharris	{
105230557Sjimharris		/* can't accept this message right now */
106230557Sjimharris		return SMFIS_TEMPFAIL;
107230557Sjimharris	}
108230557Sjimharris	memset(priv, '\0', sizeof *priv);
109230557Sjimharris
110230557Sjimharris	/* open a file to store this message */
111230557Sjimharris	priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
112230557Sjimharris	if (priv->mlfi_fname == NULL)
113230557Sjimharris	{
114230557Sjimharris		free(priv);
115230557Sjimharris		return SMFIS_TEMPFAIL;
116230557Sjimharris	}
117230557Sjimharris	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
118230557Sjimharris	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
119230557Sjimharris	{
120230557Sjimharris		if (fd >= 0)
121230557Sjimharris			(void) close(fd);
122230557Sjimharris		free(priv->mlfi_fname);
123230557Sjimharris		free(priv);
124230557Sjimharris		return SMFIS_TEMPFAIL;
125230557Sjimharris	}
126230557Sjimharris
127230557Sjimharris	/* save the private data */
128230557Sjimharris	smfi_setpriv(ctx, priv);
129230557Sjimharris
130230557Sjimharris	/* continue processing */
131230557Sjimharris	return SMFIS_CONTINUE;
132230557Sjimharris}
133230557Sjimharris
134230557Sjimharrissfsistat
135230557Sjimharrismlfi_header(ctx, headerf, headerv)
136230557Sjimharris	SMFICTX *ctx;
137230557Sjimharris	char *headerf;
138230557Sjimharris	char *headerv;
139230557Sjimharris{
140230557Sjimharris	/* write the header to the log file */
141230557Sjimharris	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
142230557Sjimharris
143230557Sjimharris	/* continue processing */
144230557Sjimharris	return ((mta_caps & SMFIP_NR_HDR) != 0)
145230557Sjimharris		? SMFIS_NOREPLY : SMFIS_CONTINUE;
146230557Sjimharris}
147230557Sjimharris
148230557Sjimharrissfsistat
149230557Sjimharrismlfi_eoh(ctx)
150230557Sjimharris	SMFICTX *ctx;
151230557Sjimharris{
152230557Sjimharris	/* output the blank line between the header and the body */
153230557Sjimharris	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
154230557Sjimharris
155230557Sjimharris	/* continue processing */
156230557Sjimharris	return SMFIS_CONTINUE;
157230557Sjimharris}
158230557Sjimharris
159230557Sjimharrissfsistat
160230557Sjimharrismlfi_body(ctx, bodyp, bodylen)
161230557Sjimharris	SMFICTX *ctx;
162230557Sjimharris	u_char *bodyp;
163230557Sjimharris	size_t bodylen;
164230557Sjimharris{
165230557Sjimharris	/* output body block to log file */
166230557Sjimharris	if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
167230557Sjimharris	{
168230557Sjimharris		/* write failed */
169230557Sjimharris		(void) mlfi_cleanup(ctx, false);
170230557Sjimharris		return SMFIS_TEMPFAIL;
171230557Sjimharris	}
172230557Sjimharris
173230557Sjimharris	/* continue processing */
174230557Sjimharris	return SMFIS_CONTINUE;
175230557Sjimharris}
176230557Sjimharris
177230557Sjimharrissfsistat
178230557Sjimharrismlfi_eom(ctx)
179230557Sjimharris	SMFICTX *ctx;
180230557Sjimharris{
181230557Sjimharris	return mlfi_cleanup(ctx, true);
182230557Sjimharris}
183230557Sjimharris
184230557Sjimharrissfsistat
185230557Sjimharrismlfi_close(ctx)
186230557Sjimharris	SMFICTX *ctx;
187230557Sjimharris{
188230557Sjimharris	return SMFIS_ACCEPT;
189230557Sjimharris}
190230557Sjimharris
191230557Sjimharrissfsistat
192230557Sjimharrismlfi_abort(ctx)
193230557Sjimharris	SMFICTX *ctx;
194230557Sjimharris{
195230557Sjimharris	return mlfi_cleanup(ctx, false);
196230557Sjimharris}
197230557Sjimharris
198230557Sjimharrissfsistat
199230557Sjimharrismlfi_unknown(ctx, cmd)
200230557Sjimharris	SMFICTX *ctx;
201230557Sjimharris	char *cmd;
202230557Sjimharris{
203230557Sjimharris	return SMFIS_CONTINUE;
204230557Sjimharris}
205230557Sjimharris
206230557Sjimharrissfsistat
207230557Sjimharrismlfi_data(ctx)
208230557Sjimharris	SMFICTX *ctx;
209230557Sjimharris{
210230557Sjimharris	return SMFIS_CONTINUE;
211230557Sjimharris}
212230557Sjimharris
213230557Sjimharrissfsistat
214230557Sjimharrismlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
215230557Sjimharris	SMFICTX *ctx;
216230557Sjimharris	unsigned long f0;
217230557Sjimharris	unsigned long f1;
218230557Sjimharris	unsigned long f2;
219230557Sjimharris	unsigned long f3;
220230557Sjimharris	unsigned long *pf0;
221230557Sjimharris	unsigned long *pf1;
222230557Sjimharris	unsigned long *pf2;
223230557Sjimharris	unsigned long *pf3;
224230557Sjimharris{
225230557Sjimharris	/* milter actions: add headers */
226230557Sjimharris	*pf0 = SMFIF_ADDHDRS;
227230557Sjimharris
228230557Sjimharris	/* milter protocol steps: all but connect, HELO, RCPT */
229230557Sjimharris	*pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
230230557Sjimharris	mta_caps = f1;
231230557Sjimharris	if ((mta_caps & SMFIP_NR_HDR) != 0)
232230557Sjimharris		*pf1 |= SMFIP_NR_HDR;
233230557Sjimharris	*pf2 = 0;
234230557Sjimharris	*pf3 = 0;
235230557Sjimharris	return SMFIS_CONTINUE;
236230557Sjimharris}
237230557Sjimharris
238230557Sjimharrisstruct smfiDesc smfilter =
239230557Sjimharris{
240230557Sjimharris	"SampleFilter",	/* filter name */
241230557Sjimharris	SMFI_VERSION,	/* version code -- do not change */
242230557Sjimharris	SMFIF_ADDHDRS,	/* flags */
243230557Sjimharris	NULL,		/* connection info filter */
244230557Sjimharris	NULL,		/* SMTP HELO command filter */
245230557Sjimharris	mlfi_envfrom,	/* envelope sender filter */
246230557Sjimharris	NULL,		/* envelope recipient filter */
247230557Sjimharris	mlfi_header,	/* header filter */
248230557Sjimharris	mlfi_eoh,	/* end of header */
249230557Sjimharris	mlfi_body,	/* body block filter */
250230557Sjimharris	mlfi_eom,	/* end of message */
251230557Sjimharris	mlfi_abort,	/* message aborted */
252230557Sjimharris	mlfi_close,	/* connection cleanup */
253230557Sjimharris	mlfi_unknown,	/* unknown/unimplemented SMTP commands */
254230557Sjimharris	mlfi_data,	/* DATA command filter */
255230557Sjimharris	mlfi_negotiate	/* option negotiation at connection startup */
256230557Sjimharris};
257230557Sjimharris
258230557Sjimharrisint
259230557Sjimharrismain(argc, argv)
260230557Sjimharris	int argc;
261230557Sjimharris	char *argv[];
262230557Sjimharris{
263230557Sjimharris	bool setconn;
264230557Sjimharris	int c;
265230557Sjimharris
266230557Sjimharris	setconn = false;
267230557Sjimharris
268230557Sjimharris	/* Process command line options */
269230557Sjimharris	while ((c = getopt(argc, argv, "p:")) != -1)
270230557Sjimharris	{
271		switch (c)
272		{
273		  case 'p':
274			if (optarg == NULL || *optarg == '\0')
275			{
276				(void) fprintf(stderr, "Illegal conn: %s\n",
277					       optarg);
278				exit(EX_USAGE);
279			}
280			(void) smfi_setconn(optarg);
281			setconn = true;
282			break;
283
284		}
285	}
286	if (!setconn)
287	{
288		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
289		exit(EX_USAGE);
290	}
291	if (smfi_register(smfilter) == MI_FAILURE)
292	{
293		fprintf(stderr, "smfi_register failed\n");
294		exit(EX_UNAVAILABLE);
295	}
296	return smfi_main();
297}
298
299