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