1168515Sgshapiro/*
2261370Sgshapiro *  Copyright (c) 2006 Proofpoint, Inc. and its suppliers.
3168515Sgshapiro *	All rights reserved.
4168515Sgshapiro *
5168515Sgshapiro * By using this file, you agree to the terms and conditions set
6168515Sgshapiro * forth in the LICENSE file which can be found at the top level of
7168515Sgshapiro * the sendmail distribution.
8168515Sgshapiro *
9266711Sgshapiro * $Id: example.c,v 8.5 2013-11-22 20:51:36 ca Exp $
10168515Sgshapiro */
11168515Sgshapiro
12168515Sgshapiro/*
13168515Sgshapiro**  A trivial example filter that logs all email to a file.
14168515Sgshapiro**  This milter also has some callbacks which it does not really use,
15168515Sgshapiro**  but they are defined to serve as an example.
16168515Sgshapiro*/
17168515Sgshapiro
18168515Sgshapiro#include <sys/types.h>
19168515Sgshapiro#include <stdio.h>
20168515Sgshapiro#include <stdlib.h>
21168515Sgshapiro#include <string.h>
22168515Sgshapiro#include <sysexits.h>
23168515Sgshapiro#include <unistd.h>
24168515Sgshapiro
25168515Sgshapiro#include "libmilter/mfapi.h"
26168515Sgshapiro#include "libmilter/mfdef.h"
27168515Sgshapiro
28168515Sgshapiro#ifndef true
29168515Sgshapiro# define false	0
30168515Sgshapiro# define true	1
31168515Sgshapiro#endif /* ! true */
32168515Sgshapiro
33168515Sgshapirostruct mlfiPriv
34168515Sgshapiro{
35168515Sgshapiro	char	*mlfi_fname;
36168515Sgshapiro	FILE	*mlfi_fp;
37168515Sgshapiro};
38168515Sgshapiro
39168515Sgshapiro#define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
40168515Sgshapiro
41168515Sgshapirostatic unsigned long mta_caps = 0;
42168515Sgshapiro
43168515Sgshapirosfsistat
44168515Sgshapiromlfi_cleanup(ctx, ok)
45168515Sgshapiro	SMFICTX *ctx;
46168515Sgshapiro	bool ok;
47168515Sgshapiro{
48168515Sgshapiro	sfsistat rstat = SMFIS_CONTINUE;
49168515Sgshapiro	struct mlfiPriv *priv = MLFIPRIV;
50168515Sgshapiro	char *p;
51168515Sgshapiro	char host[512];
52168515Sgshapiro	char hbuf[1024];
53168515Sgshapiro
54168515Sgshapiro	if (priv == NULL)
55168515Sgshapiro		return rstat;
56168515Sgshapiro
57168515Sgshapiro	/* close the archive file */
58168515Sgshapiro	if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
59168515Sgshapiro	{
60168515Sgshapiro		/* failed; we have to wait until later */
61168515Sgshapiro		rstat = SMFIS_TEMPFAIL;
62168515Sgshapiro		(void) unlink(priv->mlfi_fname);
63168515Sgshapiro	}
64168515Sgshapiro	else if (ok)
65168515Sgshapiro	{
66168515Sgshapiro		/* add a header to the message announcing our presence */
67168515Sgshapiro		if (gethostname(host, sizeof host) < 0)
68168515Sgshapiro			snprintf(host, sizeof host, "localhost");
69168515Sgshapiro		p = strrchr(priv->mlfi_fname, '/');
70168515Sgshapiro		if (p == NULL)
71168515Sgshapiro			p = priv->mlfi_fname;
72168515Sgshapiro		else
73168515Sgshapiro			p++;
74168515Sgshapiro		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
75168515Sgshapiro		smfi_addheader(ctx, "X-Archived", hbuf);
76168515Sgshapiro	}
77168515Sgshapiro	else
78168515Sgshapiro	{
79168515Sgshapiro		/* message was aborted -- delete the archive file */
80168515Sgshapiro		(void) unlink(priv->mlfi_fname);
81168515Sgshapiro	}
82168515Sgshapiro
83168515Sgshapiro	/* release private memory */
84168515Sgshapiro	free(priv->mlfi_fname);
85168515Sgshapiro	free(priv);
86168515Sgshapiro	smfi_setpriv(ctx, NULL);
87168515Sgshapiro
88168515Sgshapiro	/* return status */
89168515Sgshapiro	return rstat;
90168515Sgshapiro}
91168515Sgshapiro
92168515Sgshapiro
93168515Sgshapirosfsistat
94168515Sgshapiromlfi_envfrom(ctx, envfrom)
95168515Sgshapiro	SMFICTX *ctx;
96168515Sgshapiro	char **envfrom;
97168515Sgshapiro{
98168515Sgshapiro	struct mlfiPriv *priv;
99168515Sgshapiro	int fd = -1;
100168515Sgshapiro
101168515Sgshapiro	/* allocate some private memory */
102168515Sgshapiro	priv = malloc(sizeof *priv);
103168515Sgshapiro	if (priv == NULL)
104168515Sgshapiro	{
105168515Sgshapiro		/* can't accept this message right now */
106168515Sgshapiro		return SMFIS_TEMPFAIL;
107168515Sgshapiro	}
108168515Sgshapiro	memset(priv, '\0', sizeof *priv);
109168515Sgshapiro
110168515Sgshapiro	/* open a file to store this message */
111168515Sgshapiro	priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
112168515Sgshapiro	if (priv->mlfi_fname == NULL)
113168515Sgshapiro	{
114168515Sgshapiro		free(priv);
115168515Sgshapiro		return SMFIS_TEMPFAIL;
116168515Sgshapiro	}
117168515Sgshapiro	if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
118168515Sgshapiro	    (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
119168515Sgshapiro	{
120168515Sgshapiro		if (fd >= 0)
121168515Sgshapiro			(void) close(fd);
122168515Sgshapiro		free(priv->mlfi_fname);
123168515Sgshapiro		free(priv);
124168515Sgshapiro		return SMFIS_TEMPFAIL;
125168515Sgshapiro	}
126168515Sgshapiro
127168515Sgshapiro	/* save the private data */
128168515Sgshapiro	smfi_setpriv(ctx, priv);
129168515Sgshapiro
130168515Sgshapiro	/* continue processing */
131168515Sgshapiro	return SMFIS_CONTINUE;
132168515Sgshapiro}
133168515Sgshapiro
134168515Sgshapirosfsistat
135168515Sgshapiromlfi_header(ctx, headerf, headerv)
136168515Sgshapiro	SMFICTX *ctx;
137168515Sgshapiro	char *headerf;
138168515Sgshapiro	char *headerv;
139168515Sgshapiro{
140168515Sgshapiro	/* write the header to the log file */
141168515Sgshapiro	fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
142168515Sgshapiro
143168515Sgshapiro	/* continue processing */
144168515Sgshapiro	return ((mta_caps & SMFIP_NR_HDR) != 0)
145168515Sgshapiro		? SMFIS_NOREPLY : SMFIS_CONTINUE;
146168515Sgshapiro}
147168515Sgshapiro
148168515Sgshapirosfsistat
149168515Sgshapiromlfi_eoh(ctx)
150168515Sgshapiro	SMFICTX *ctx;
151168515Sgshapiro{
152168515Sgshapiro	/* output the blank line between the header and the body */
153168515Sgshapiro	fprintf(MLFIPRIV->mlfi_fp, "\r\n");
154168515Sgshapiro
155168515Sgshapiro	/* continue processing */
156168515Sgshapiro	return SMFIS_CONTINUE;
157168515Sgshapiro}
158168515Sgshapiro
159168515Sgshapirosfsistat
160168515Sgshapiromlfi_body(ctx, bodyp, bodylen)
161168515Sgshapiro	SMFICTX *ctx;
162168515Sgshapiro	u_char *bodyp;
163168515Sgshapiro	size_t bodylen;
164168515Sgshapiro{
165168515Sgshapiro	/* output body block to log file */
166168515Sgshapiro	if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
167168515Sgshapiro	{
168168515Sgshapiro		/* write failed */
169168515Sgshapiro		(void) mlfi_cleanup(ctx, false);
170168515Sgshapiro		return SMFIS_TEMPFAIL;
171168515Sgshapiro	}
172168515Sgshapiro
173168515Sgshapiro	/* continue processing */
174168515Sgshapiro	return SMFIS_CONTINUE;
175168515Sgshapiro}
176168515Sgshapiro
177168515Sgshapirosfsistat
178168515Sgshapiromlfi_eom(ctx)
179168515Sgshapiro	SMFICTX *ctx;
180168515Sgshapiro{
181168515Sgshapiro	return mlfi_cleanup(ctx, true);
182168515Sgshapiro}
183168515Sgshapiro
184168515Sgshapirosfsistat
185168515Sgshapiromlfi_close(ctx)
186168515Sgshapiro	SMFICTX *ctx;
187168515Sgshapiro{
188168515Sgshapiro	return SMFIS_ACCEPT;
189168515Sgshapiro}
190168515Sgshapiro
191168515Sgshapirosfsistat
192168515Sgshapiromlfi_abort(ctx)
193168515Sgshapiro	SMFICTX *ctx;
194168515Sgshapiro{
195168515Sgshapiro	return mlfi_cleanup(ctx, false);
196168515Sgshapiro}
197168515Sgshapiro
198168515Sgshapirosfsistat
199168515Sgshapiromlfi_unknown(ctx, cmd)
200168515Sgshapiro	SMFICTX *ctx;
201168515Sgshapiro	char *cmd;
202168515Sgshapiro{
203168515Sgshapiro	return SMFIS_CONTINUE;
204168515Sgshapiro}
205168515Sgshapiro
206168515Sgshapirosfsistat
207168515Sgshapiromlfi_data(ctx)
208168515Sgshapiro	SMFICTX *ctx;
209168515Sgshapiro{
210168515Sgshapiro	return SMFIS_CONTINUE;
211168515Sgshapiro}
212168515Sgshapiro
213168515Sgshapirosfsistat
214168515Sgshapiromlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
215168515Sgshapiro	SMFICTX *ctx;
216168515Sgshapiro	unsigned long f0;
217168515Sgshapiro	unsigned long f1;
218168515Sgshapiro	unsigned long f2;
219168515Sgshapiro	unsigned long f3;
220168515Sgshapiro	unsigned long *pf0;
221168515Sgshapiro	unsigned long *pf1;
222168515Sgshapiro	unsigned long *pf2;
223168515Sgshapiro	unsigned long *pf3;
224168515Sgshapiro{
225168515Sgshapiro	/* milter actions: add headers */
226168515Sgshapiro	*pf0 = SMFIF_ADDHDRS;
227168515Sgshapiro
228168515Sgshapiro	/* milter protocol steps: all but connect, HELO, RCPT */
229168515Sgshapiro	*pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
230168515Sgshapiro	mta_caps = f1;
231168515Sgshapiro	if ((mta_caps & SMFIP_NR_HDR) != 0)
232168515Sgshapiro		*pf1 |= SMFIP_NR_HDR;
233168515Sgshapiro	*pf2 = 0;
234168515Sgshapiro	*pf3 = 0;
235168515Sgshapiro	return SMFIS_CONTINUE;
236168515Sgshapiro}
237168515Sgshapiro
238168515Sgshapirostruct smfiDesc smfilter =
239168515Sgshapiro{
240168515Sgshapiro	"SampleFilter",	/* filter name */
241168515Sgshapiro	SMFI_VERSION,	/* version code -- do not change */
242168515Sgshapiro	SMFIF_ADDHDRS,	/* flags */
243168515Sgshapiro	NULL,		/* connection info filter */
244168515Sgshapiro	NULL,		/* SMTP HELO command filter */
245168515Sgshapiro	mlfi_envfrom,	/* envelope sender filter */
246168515Sgshapiro	NULL,		/* envelope recipient filter */
247168515Sgshapiro	mlfi_header,	/* header filter */
248168515Sgshapiro	mlfi_eoh,	/* end of header */
249168515Sgshapiro	mlfi_body,	/* body block filter */
250168515Sgshapiro	mlfi_eom,	/* end of message */
251168515Sgshapiro	mlfi_abort,	/* message aborted */
252168515Sgshapiro	mlfi_close,	/* connection cleanup */
253168515Sgshapiro	mlfi_unknown,	/* unknown/unimplemented SMTP commands */
254168515Sgshapiro	mlfi_data,	/* DATA command filter */
255203004Sgshapiro	mlfi_negotiate	/* option negotiation at connection startup */
256168515Sgshapiro};
257168515Sgshapiro
258168515Sgshapiroint
259168515Sgshapiromain(argc, argv)
260168515Sgshapiro	int argc;
261168515Sgshapiro	char *argv[];
262168515Sgshapiro{
263168515Sgshapiro	bool setconn;
264168515Sgshapiro	int c;
265168515Sgshapiro
266168515Sgshapiro	setconn = false;
267168515Sgshapiro
268168515Sgshapiro	/* Process command line options */
269168515Sgshapiro	while ((c = getopt(argc, argv, "p:")) != -1)
270168515Sgshapiro	{
271168515Sgshapiro		switch (c)
272168515Sgshapiro		{
273168515Sgshapiro		  case 'p':
274168515Sgshapiro			if (optarg == NULL || *optarg == '\0')
275168515Sgshapiro			{
276168515Sgshapiro				(void) fprintf(stderr, "Illegal conn: %s\n",
277168515Sgshapiro					       optarg);
278168515Sgshapiro				exit(EX_USAGE);
279168515Sgshapiro			}
280168515Sgshapiro			(void) smfi_setconn(optarg);
281168515Sgshapiro			setconn = true;
282168515Sgshapiro			break;
283168515Sgshapiro
284168515Sgshapiro		}
285168515Sgshapiro	}
286168515Sgshapiro	if (!setconn)
287168515Sgshapiro	{
288168515Sgshapiro		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
289168515Sgshapiro		exit(EX_USAGE);
290168515Sgshapiro	}
291168515Sgshapiro	if (smfi_register(smfilter) == MI_FAILURE)
292168515Sgshapiro	{
293168515Sgshapiro		fprintf(stderr, "smfi_register failed\n");
294168515Sgshapiro		exit(EX_UNAVAILABLE);
295168515Sgshapiro	}
296168515Sgshapiro	return smfi_main();
297168515Sgshapiro}
298168515Sgshapiro
299