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