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.4 2008/07/22 15:12:47 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 /* ! true */
32 
33 struct mlfiPriv
34 {
35 	char	*mlfi_fname;
36 	FILE	*mlfi_fp;
37 };
38 
39 #define MLFIPRIV	((struct mlfiPriv *) smfi_getpriv(ctx))
40 
41 static unsigned long mta_caps = 0;
42 
43 sfsistat
mlfi_cleanup(ctx,ok)44 mlfi_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 
93 sfsistat
mlfi_envfrom(ctx,envfrom)94 mlfi_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 
134 sfsistat
mlfi_header(ctx,headerf,headerv)135 mlfi_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 
148 sfsistat
mlfi_eoh(ctx)149 mlfi_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 
159 sfsistat
mlfi_body(ctx,bodyp,bodylen)160 mlfi_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 
177 sfsistat
mlfi_eom(ctx)178 mlfi_eom(ctx)
179 	SMFICTX *ctx;
180 {
181 	return mlfi_cleanup(ctx, true);
182 }
183 
184 sfsistat
mlfi_close(ctx)185 mlfi_close(ctx)
186 	SMFICTX *ctx;
187 {
188 	return SMFIS_ACCEPT;
189 }
190 
191 sfsistat
mlfi_abort(ctx)192 mlfi_abort(ctx)
193 	SMFICTX *ctx;
194 {
195 	return mlfi_cleanup(ctx, false);
196 }
197 
198 sfsistat
mlfi_unknown(ctx,cmd)199 mlfi_unknown(ctx, cmd)
200 	SMFICTX *ctx;
201 	char *cmd;
202 {
203 	return SMFIS_CONTINUE;
204 }
205 
206 sfsistat
mlfi_data(ctx)207 mlfi_data(ctx)
208 	SMFICTX *ctx;
209 {
210 	return SMFIS_CONTINUE;
211 }
212 
213 sfsistat
mlfi_negotiate(ctx,f0,f1,f2,f3,pf0,pf1,pf2,pf3)214 mlfi_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 
238 struct 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 
258 int
main(argc,argv)259 main(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