1<html>
2<head><title>A Sample Filter</title></head>
3<body>
4<!--
5$Id: sample.html,v 1.18 2004/02/27 00:49:28 msk Exp $
6-->
7<h1>A Sample Filter</h1>
8
9The following sample logs each message to a separate temporary file,
10adds a recipient given with the -a flag, and rejects a disallowed
11recipient address given with the -r flag.  It recognizes the following
12options:
13<p>
14<center>
15<table border="1" cellpadding=2 cellspacing=1>
16<tr><td><code>-p port</code></td><td>The port through which the MTA will connect to the filter.</td></tr>
17<tr><td><code>-t sec</code></td><td>The timeout value.</td></tr>
18<tr><td><code>-r addr</code></td><td>A recipient to reject.</td></tr>
19<tr><td><code>-a addr</code></td><td>A recipient to add.</td></tr>
20</table>
21</center>
22<hr>
23<pre>
24#include &lt;sys/types.h&gt;
25#include &lt;sys/stat.h&gt;
26#include &lt;errno.h&gt;
27#include &lt;stdio.h&gt;
28#include &lt;stdlib.h&gt;
29#include &lt;string.h&gt;
30#include &lt;sysexits.h&gt;
31#include &lt;unistd.h&gt;
32
33#include "libmilter/mfapi.h"
34
35#ifndef bool
36# define bool	int
37# define TRUE	1
38# define FALSE	0
39#endif /* ! bool */
40
41
42struct mlfiPriv
43{
44	char	*mlfi_fname;
45	char	*mlfi_connectfrom;
46	char	*mlfi_helofrom;
47	FILE	*mlfi_fp;
48};
49
50#define MLFIPRIV	((struct mlfiPriv *) <a href="smfi_getpriv.html">smfi_getpriv</a>(ctx))
51
52extern sfsistat		mlfi_cleanup(SMFICTX *, bool);
53
54/* recipients to add and reject (set with -a and -r options) */
55char *add = NULL;
56char *reject = NULL;
57
58sfsistat
59<a href="xxfi_connect.html">mlfi_connect</a>(ctx, hostname, hostaddr)
60	 SMFICTX *ctx;
61	 char *hostname;
62	 _SOCK_ADDR *hostaddr;
63{
64	struct mlfiPriv *priv;
65	char *ident;
66
67	/* allocate some private memory */
68	priv = malloc(sizeof *priv);
69	if (priv == NULL)
70	{
71		/* can't accept this message right now */
72		return SMFIS_TEMPFAIL;
73	}
74	memset(priv, '\0', sizeof *priv);
75
76	/* save the private data */
77	<a href="smfi_setpriv.html">smfi_setpriv</a>(ctx, priv);
78
79	ident = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "_");
80	if (ident == NULL)
81		ident = "???";
82	if ((priv-&gt;mlfi_connectfrom = strdup(ident)) == NULL)
83	{
84		(void) mlfi_cleanup(ctx, FALSE);
85		return SMFIS_TEMPFAIL;
86	}
87
88	/* continue processing */
89	return SMFIS_CONTINUE;
90}
91
92sfsistat
93<a href="xxfi_helo.html">mlfi_helo</a>(ctx, helohost)
94	 SMFICTX *ctx;
95	 char *helohost;
96{
97	size_t len;
98	char *tls;
99	char *buf;
100	struct mlfiPriv *priv = MLFIPRIV;
101
102	tls = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{tls_version}");
103	if (tls == NULL)
104		tls = "No TLS";
105	if (helohost == NULL)
106		helohost = "???";
107	len = strlen(tls) + strlen(helohost) + 3;
108	if ((buf = (char*) malloc(len)) == NULL)
109	{
110		(void) mlfi_cleanup(ctx, FALSE);
111		return SMFIS_TEMPFAIL;
112	}
113	snprintf(buf, len, "%s, %s", helohost, tls);
114	if (priv-&gt;mlfi_helofrom != NULL)
115		free(priv-&gt;mlfi_helofrom);
116	priv-&gt;mlfi_helofrom = buf;
117
118	/* continue processing */
119	return SMFIS_CONTINUE;
120}
121
122sfsistat
123<a href="xxfi_envfrom.html">mlfi_envfrom</a>(ctx, argv)
124	 SMFICTX *ctx;
125	 char **argv;
126{
127	int fd = -1;
128	int argc = 0;
129	struct mlfiPriv *priv = MLFIPRIV;
130	char *mailaddr = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{mail_addr}");
131
132	/* open a file to store this message */
133	if ((priv-&gt;mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
134	{
135		(void) mlfi_cleanup(ctx, FALSE);
136		return SMFIS_TEMPFAIL;
137	}
138
139	if ((fd = mkstemp(priv-&gt;mlfi_fname)) == -1)
140	{
141		(void) mlfi_cleanup(ctx, FALSE);
142		return SMFIS_TEMPFAIL;
143	}
144
145	if ((priv-&gt;mlfi_fp = fdopen(fd, "w+")) == NULL)
146	{
147		(void) close(fd);
148		(void) mlfi_cleanup(ctx, FALSE);
149		return SMFIS_TEMPFAIL;
150	}
151
152	/* count the arguments */
153	while (*argv++ != NULL)
154		++argc;
155
156	/* log the connection information we stored earlier: */
157	if (fprintf(priv-&gt;mlfi_fp, "Connect from %s (%s)\n\n",
158		    priv-&gt;mlfi_helofrom, priv-&gt;mlfi_connectfrom) == EOF)
159	{
160		(void) mlfi_cleanup(ctx, FALSE);
161		return SMFIS_TEMPFAIL;
162	}
163	/* log the sender */
164	if (fprintf(priv-&gt;mlfi_fp, "FROM %s (%d argument%s)\n",
165		    mailaddr ? mailaddr : "???", argc,
166		    (argc == 1) ? "" : "s") == EOF)
167	{
168		(void) mlfi_cleanup(ctx, FALSE);
169		return SMFIS_TEMPFAIL;
170	}
171
172	/* continue processing */
173	return SMFIS_CONTINUE;
174}
175
176sfsistat
177<a href="xxfi_envrcpt.html">mlfi_envrcpt</a>(ctx, argv)
178	 SMFICTX *ctx;
179	 char **argv;
180{
181	struct mlfiPriv *priv = MLFIPRIV;
182	char *rcptaddr = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{rcpt_addr}");
183	int argc = 0;
184
185	/* count the arguments */
186	while (*argv++ != NULL)
187		++argc;
188
189	/* log this recipient */
190	if (reject != NULL && rcptaddr != NULL &&
191	    (strcasecmp(rcptaddr, reject) == 0))
192	{
193		if (fprintf(priv-&gt;mlfi_fp, "RCPT %s -- REJECTED\n",
194			    rcptaddr) == EOF)
195		{
196			(void) mlfi_cleanup(ctx, FALSE);
197			return SMFIS_TEMPFAIL;
198		}
199		return SMFIS_REJECT;
200	}
201	if (fprintf(priv-&gt;mlfi_fp, "RCPT %s (%d argument%s)\n",
202		    rcptaddr ? rcptaddr : "???", argc,
203		    (argc == 1) ? "" : "s") == EOF)
204	{
205		(void) mlfi_cleanup(ctx, FALSE);
206		return SMFIS_TEMPFAIL;
207	}
208
209	/* continue processing */
210	return SMFIS_CONTINUE;
211}
212
213sfsistat
214<a href="xxfi_header.html">mlfi_header</a>(ctx, headerf, headerv)
215	 SMFICTX *ctx;
216	 char *headerf;
217	 unsigned char *headerv;
218{
219	/* write the header to the log file */
220	if (fprintf(MLFIPRIV-&gt;mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
221	{
222		(void) mlfi_cleanup(ctx, FALSE);
223		return SMFIS_TEMPFAIL;
224	}
225
226	/* continue processing */
227	return SMFIS_CONTINUE;
228}
229
230sfsistat
231<a href="xxfi_eoh.html">mlfi_eoh</a>(ctx)
232	 SMFICTX *ctx;
233{
234	/* output the blank line between the header and the body */
235	if (fprintf(MLFIPRIV-&gt;mlfi_fp, "\n") == EOF)
236	{
237		(void) mlfi_cleanup(ctx, FALSE);
238		return SMFIS_TEMPFAIL;
239	}
240
241	/* continue processing */
242	return SMFIS_CONTINUE;
243}
244
245sfsistat
246<a href="xxfi_body.html">mlfi_body</a>(ctx, bodyp, bodylen)
247	 SMFICTX *ctx;
248	 unsigned char *bodyp;
249	 size_t bodylen;
250{
251        struct mlfiPriv *priv = MLFIPRIV;
252
253	/* output body block to log file */
254	if (fwrite(bodyp, bodylen, 1, priv-&gt;mlfi_fp) != 1)
255	{
256		/* write failed */
257		fprintf(stderr, "Couldn't write file %s: %s\n",
258			priv-&gt;mlfi_fname, strerror(errno));
259		(void) mlfi_cleanup(ctx, FALSE);
260		return SMFIS_TEMPFAIL;
261	}
262
263	/* continue processing */
264	return SMFIS_CONTINUE;
265}
266
267sfsistat
268<a href="xxfi_eom.html">mlfi_eom</a>(ctx)
269	 SMFICTX *ctx;
270{
271	bool ok = TRUE;
272
273	/* change recipients, if requested */
274	if (add != NULL)
275		ok = (<a href="smfi_addrcpt.html">smfi_addrcpt</a>(ctx, add) == MI_SUCCESS);
276	return mlfi_cleanup(ctx, ok);
277}
278
279sfsistat
280<a href="xxfi_abort.html">mlfi_abort</a>(ctx)
281	 SMFICTX *ctx;
282{
283	return mlfi_cleanup(ctx, FALSE);
284}
285
286sfsistat
287mlfi_cleanup(ctx, ok)
288	 SMFICTX *ctx;
289	 bool ok;
290{
291	sfsistat rstat = SMFIS_CONTINUE;
292	struct mlfiPriv *priv = MLFIPRIV;
293	char *p;
294	char host[512];
295	char hbuf[1024];
296
297	if (priv == NULL)
298		return rstat;
299
300	/* close the archive file */
301	if (priv-&gt;mlfi_fp != NULL && fclose(priv-&gt;mlfi_fp) == EOF)
302	{
303		/* failed; we have to wait until later */
304		fprintf(stderr, "Couldn't close archive file %s: %s\n",
305			priv-&gt;mlfi_fname, strerror(errno));
306		rstat = SMFIS_TEMPFAIL;
307		(void) unlink(priv-&gt;mlfi_fname);
308	}
309	else if (ok)
310	{
311		/* add a header to the message announcing our presence */
312		if (gethostname(host, sizeof host) &lt; 0)
313			snprintf(host, sizeof host, "localhost");
314		p = strrchr(priv-&gt;mlfi_fname, '/');
315		if (p == NULL)
316			p = priv-&gt;mlfi_fname;
317		else
318			p++;
319		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
320		if (<a href="smfi_addheader.html">smfi_addheader</a>(ctx, "X-Archived", hbuf) != MI_SUCCESS)
321		{
322			/* failed; we have to wait until later */
323			fprintf(stderr,
324				"Couldn't add header: X-Archived: %s\n",
325				hbuf);
326			ok = FALSE;
327			rstat = SMFIS_TEMPFAIL;
328			(void) unlink(priv-&gt;mlfi_fname);
329		}
330	}
331	else
332	{
333		/* message was aborted -- delete the archive file */
334		fprintf(stderr, "Message aborted.  Removing %s\n",
335			priv-&gt;mlfi_fname);
336		rstat = SMFIS_TEMPFAIL;
337		(void) unlink(priv-&gt;mlfi_fname);
338	}
339
340	/* release private memory */
341	if (priv-&gt;mlfi_fname != NULL)
342		free(priv-&gt;mlfi_fname);
343
344	/* return status */
345	return rstat;
346}
347
348sfsistat
349<a href="xxfi_close.html">mlfi_close</a>(ctx)
350	 SMFICTX *ctx;
351{
352	struct mlfiPriv *priv = MLFIPRIV;
353
354	if (priv == NULL)
355		return SMFIS_CONTINUE;
356	if (priv-&gt;mlfi_connectfrom != NULL)
357		free(priv-&gt;mlfi_connectfrom);
358	if (priv-&gt;mlfi_helofrom != NULL)
359		free(priv-&gt;mlfi_helofrom);
360	free(priv);
361	<a href="smfi_setpriv.html">smfi_setpriv</a>(ctx, NULL);
362	return SMFIS_CONTINUE;
363}
364
365struct smfiDesc smfilter =
366{
367	"SampleFilter",	/* filter name */
368	SMFI_VERSION,	/* version code -- do not change */
369	SMFIF_ADDHDRS|SMFIF_ADDRCPT,
370			/* flags */
371	<a href="xxfi_connect.html">mlfi_connect</a>,	/* connection info filter */
372	<a href="xxfi_helo.html">mlfi_helo</a>,	/* SMTP HELO command filter */
373	<a href="xxfi_envfrom.html">mlfi_envfrom</a>,	/* envelope sender filter */
374	<a href="xxfi_envrcpt.html">mlfi_envrcpt</a>,	/* envelope recipient filter */
375	<a href="xxfi_header.html">mlfi_header</a>,	/* header filter */
376	<a href="xxfi_eoh.html">mlfi_eoh</a>,	/* end of header */
377	<a href="xxfi_body.html">mlfi_body</a>,	/* body block filter */
378	<a href="xxfi_eom.html">mlfi_eom</a>,	/* end of message */
379	<a href="xxfi_abort.html">mlfi_abort</a>,	/* message aborted */
380	<a href="xxfi_close.html">mlfi_close</a>,	/* connection cleanup */
381};
382
383static void
384usage(prog)
385	char *prog;
386{
387	fprintf(stderr,
388		"Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n",
389		prog);
390}
391
392int
393main(argc, argv)
394	 int argc;
395	 char **argv;
396{
397	bool setconn = FALSE;
398	int c;
399	const char *args = "p:t:r:a:h";
400	extern char *optarg;
401
402	/* Process command line options */
403	while ((c = getopt(argc, argv, args)) != -1)
404	{
405		switch (c)
406		{
407		  case 'p':
408			if (optarg == NULL || *optarg == '\0')
409			{
410				(void) fprintf(stderr, "Illegal conn: %s\n",
411					       optarg);
412				exit(EX_USAGE);
413			}
414			if (<a href="smfi_setconn.html">smfi_setconn</a>(optarg) == MI_FAILURE)
415			{
416				(void) fprintf(stderr,
417					       "smfi_setconn failed\n");
418				exit(EX_SOFTWARE);
419			}
420
421			/*
422			**  If we're using a local socket, make sure it
423			**  doesn't already exist.  Don't ever run this
424			**  code as root!!
425			*/
426
427			if (strncasecmp(optarg, "unix:", 5) == 0)
428				unlink(optarg + 5);
429			else if (strncasecmp(optarg, "local:", 6) == 0)
430				unlink(optarg + 6);
431			setconn = TRUE;
432			break;
433
434		  case 't':
435			if (optarg == NULL || *optarg == '\0')
436			{
437				(void) fprintf(stderr, "Illegal timeout: %s\n",
438					       optarg);
439				exit(EX_USAGE);
440			}
441			if (<a href="smfi_settimeout.html">smfi_settimeout</a>(atoi(optarg)) == MI_FAILURE)
442			{
443				(void) fprintf(stderr,
444					       "smfi_settimeout failed\n");
445				exit(EX_SOFTWARE);
446			}
447			break;
448
449		  case 'r':
450			if (optarg == NULL)
451			{
452				(void) fprintf(stderr,
453					       "Illegal reject rcpt: %s\n",
454					       optarg);
455				exit(EX_USAGE);
456			}
457			reject = optarg;
458			break;
459
460		  case 'a':
461			if (optarg == NULL)
462			{
463				(void) fprintf(stderr,
464					       "Illegal add rcpt: %s\n",
465					       optarg);
466				exit(EX_USAGE);
467			}
468			add = optarg;
469			smfilter.xxfi_flags |= SMFIF_ADDRCPT;
470			break;
471
472		  case 'h':
473		  default:
474			usage(argv[0]);
475			exit(EX_USAGE);
476		}
477	}
478	if (!setconn)
479	{
480		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
481		usage(argv[0]);
482		exit(EX_USAGE);
483	}
484	if (<a href="smfi_register.html">smfi_register</a>(smfilter) == MI_FAILURE)
485	{
486		fprintf(stderr, "smfi_register failed\n");
487		exit(EX_UNAVAILABLE);
488	}
489	return <a href="smfi_main.html">smfi_main</a>();
490}
491
492/* eof */
493
494</pre>
495<hr size="1">
496<font size="-1">
497Copyright (c) 2000-2004 Sendmail, Inc. and its suppliers.
498All rights reserved.
499<br>
500By using this file, you agree to the terms and conditions set
501forth in the LICENSE.
502</font>
503</body>
504</html>
505