sample.html revision 168515
1292920Sdim<HTML>
2292920Sdim<HEAD><TITLE>A Sample Filter</TITLE></HEAD>
3292920Sdim<BODY>
4292920Sdim<!--
5292920Sdim$Id: sample.html,v 1.22 2006/10/09 23:14:51 ca Exp $
6292920Sdim-->
7292920Sdim<H1>A Sample Filter</H1>
8292920Sdim
9292920SdimThe following sample logs each message to a separate temporary file,
10292920Sdimadds a recipient given with the -a flag,
11292920Sdimand rejects a disallowed recipient address given with the -r flag.
12292920SdimIt recognizes the following options:
13292920Sdim<P>
14292920Sdim<CENTER>
15292920Sdim<TABLE border="1" cellpadding=2 cellspacing=1>
16292920Sdim<TR><TD><CODE>-p port</CODE></TD><TD>The port through which the MTA will connect to the filter.</TD></TR>
17292920Sdim<TR><TD><CODE>-t sec</CODE></TD><TD>The timeout value.</TD></TR>
18292920Sdim<TR><TD><CODE>-r addr</CODE></TD><TD>A recipient to reject.</TD></TR>
19292920Sdim<TR><TD><CODE>-a addr</CODE></TD><TD>A recipient to add.</TD></TR>
20292920Sdim</TABLE>
21292920Sdim</CENTER>
22292920Sdim<HR>
23292920Sdim<PRE>
24292920Sdim#include &lt;sys/types.h&gt;
25292920Sdim#include &lt;sys/stat.h&gt;
26292920Sdim#include &lt;errno.h&gt;
27292920Sdim#include &lt;stdio.h&gt;
28292920Sdim#include &lt;stdlib.h&gt;
29292920Sdim#include &lt;string.h&gt;
30292920Sdim#include &lt;sysexits.h&gt;
31292920Sdim#include &lt;unistd.h&gt;
32292920Sdim
33292920Sdim#include "libmilter/mfapi.h"
34292920Sdim
35292920Sdim#ifndef bool
36292920Sdim# define bool	int
37292920Sdim# define TRUE	1
38292920Sdim# define FALSE	0
39292920Sdim#endif /* ! bool */
40292920Sdim
41292920Sdim
42292920Sdimstruct mlfiPriv
43292920Sdim{
44292920Sdim	char	*mlfi_fname;
45292920Sdim	char	*mlfi_connectfrom;
46292920Sdim	char	*mlfi_helofrom;
47292920Sdim	FILE	*mlfi_fp;
48292920Sdim};
49292920Sdim
50292920Sdim#define MLFIPRIV	((struct mlfiPriv *) <A href="smfi_getpriv.html">smfi_getpriv</A>(ctx))
51292920Sdim
52292920Sdimextern sfsistat		mlfi_cleanup(SMFICTX *, bool);
53292920Sdim
54292920Sdim/* recipients to add and reject (set with -a and -r options) */
55292920Sdimchar *add = NULL;
56292920Sdimchar *reject = NULL;
57292920Sdim
58292920Sdimsfsistat
59292920Sdim<A href="xxfi_connect.html">mlfi_connect</A>(ctx, hostname, hostaddr)
60292920Sdim	 SMFICTX *ctx;
61292920Sdim	 char *hostname;
62292920Sdim	 _SOCK_ADDR *hostaddr;
63292920Sdim{
64292920Sdim	struct mlfiPriv *priv;
65292920Sdim	char *ident;
66292920Sdim
67292920Sdim	/* allocate some private memory */
68292920Sdim	priv = malloc(sizeof *priv);
69292920Sdim	if (priv == NULL)
70292920Sdim	{
71292920Sdim		/* can't accept this message right now */
72292920Sdim		return SMFIS_TEMPFAIL;
73292920Sdim	}
74292920Sdim	memset(priv, '\0', sizeof *priv);
75292920Sdim
76292920Sdim	/* save the private data */
77292920Sdim	<A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, priv);
78292920Sdim
79292920Sdim	ident = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "_");
80292920Sdim	if (ident == NULL)
81292920Sdim		ident = "???";
82292920Sdim	if ((priv-&gt;mlfi_connectfrom = strdup(ident)) == NULL)
83292920Sdim	{
84292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
85292920Sdim		return SMFIS_TEMPFAIL;
86292920Sdim	}
87292920Sdim
88292920Sdim	/* continue processing */
89292920Sdim	return SMFIS_CONTINUE;
90292920Sdim}
91292920Sdim
92292920Sdimsfsistat
93292920Sdim<A href="xxfi_helo.html">mlfi_helo</A>(ctx, helohost)
94292920Sdim	 SMFICTX *ctx;
95292920Sdim	 char *helohost;
96292920Sdim{
97292920Sdim	size_t len;
98292920Sdim	char *tls;
99292920Sdim	char *buf;
100292920Sdim	struct mlfiPriv *priv = MLFIPRIV;
101292920Sdim
102292920Sdim	tls = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{tls_version}");
103292920Sdim	if (tls == NULL)
104292920Sdim		tls = "No TLS";
105292920Sdim	if (helohost == NULL)
106292920Sdim		helohost = "???";
107292920Sdim	len = strlen(tls) + strlen(helohost) + 3;
108292920Sdim	if ((buf = (char*) malloc(len)) == NULL)
109292920Sdim	{
110292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
111292920Sdim		return SMFIS_TEMPFAIL;
112292920Sdim	}
113292920Sdim	snprintf(buf, len, "%s, %s", helohost, tls);
114292920Sdim	if (priv-&gt;mlfi_helofrom != NULL)
115292920Sdim		free(priv-&gt;mlfi_helofrom);
116292920Sdim	priv-&gt;mlfi_helofrom = buf;
117292920Sdim
118292920Sdim	/* continue processing */
119292920Sdim	return SMFIS_CONTINUE;
120292920Sdim}
121292920Sdim
122292920Sdimsfsistat
123292920Sdim<A href="xxfi_envfrom.html">mlfi_envfrom</A>(ctx, argv)
124292920Sdim	 SMFICTX *ctx;
125292920Sdim	 char **argv;
126292920Sdim{
127292920Sdim	int fd = -1;
128292920Sdim	int argc = 0;
129292920Sdim	struct mlfiPriv *priv = MLFIPRIV;
130292920Sdim	char *mailaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{mail_addr}");
131292920Sdim
132292920Sdim	/* open a file to store this message */
133292920Sdim	if ((priv-&gt;mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
134292920Sdim	{
135292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
136292920Sdim		return SMFIS_TEMPFAIL;
137292920Sdim	}
138292920Sdim
139292920Sdim	if ((fd = mkstemp(priv-&gt;mlfi_fname)) == -1)
140292920Sdim	{
141292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
142292920Sdim		return SMFIS_TEMPFAIL;
143292920Sdim	}
144292920Sdim
145292920Sdim	if ((priv-&gt;mlfi_fp = fdopen(fd, "w+")) == NULL)
146292920Sdim	{
147292920Sdim		(void) close(fd);
148292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
149292920Sdim		return SMFIS_TEMPFAIL;
150292920Sdim	}
151292920Sdim
152292920Sdim	/* count the arguments */
153292920Sdim	while (*argv++ != NULL)
154292920Sdim		++argc;
155292920Sdim
156292920Sdim	/* log the connection information we stored earlier: */
157292920Sdim	if (fprintf(priv-&gt;mlfi_fp, "Connect from %s (%s)\n\n",
158292920Sdim		    priv-&gt;mlfi_helofrom, priv-&gt;mlfi_connectfrom) == EOF)
159292920Sdim	{
160292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
161292920Sdim		return SMFIS_TEMPFAIL;
162292920Sdim	}
163292920Sdim	/* log the sender */
164292920Sdim	if (fprintf(priv-&gt;mlfi_fp, "FROM %s (%d argument%s)\n",
165292920Sdim		    mailaddr ? mailaddr : "???", argc,
166292920Sdim		    (argc == 1) ? "" : "s") == EOF)
167292920Sdim	{
168292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
169292920Sdim		return SMFIS_TEMPFAIL;
170292920Sdim	}
171292920Sdim
172292920Sdim	/* continue processing */
173292920Sdim	return SMFIS_CONTINUE;
174292920Sdim}
175292920Sdim
176292920Sdimsfsistat
177292920Sdim<A href="xxfi_envrcpt.html">mlfi_envrcpt</A>(ctx, argv)
178292920Sdim	 SMFICTX *ctx;
179292920Sdim	 char **argv;
180292920Sdim{
181292920Sdim	struct mlfiPriv *priv = MLFIPRIV;
182292920Sdim	char *rcptaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{rcpt_addr}");
183292920Sdim	int argc = 0;
184292920Sdim
185292920Sdim	/* count the arguments */
186292920Sdim	while (*argv++ != NULL)
187292920Sdim		++argc;
188292920Sdim
189292920Sdim	/* log this recipient */
190292920Sdim	if (reject != NULL && rcptaddr != NULL &&
191292920Sdim	    (strcasecmp(rcptaddr, reject) == 0))
192292920Sdim	{
193292920Sdim		if (fprintf(priv-&gt;mlfi_fp, "RCPT %s -- REJECTED\n",
194292920Sdim			    rcptaddr) == EOF)
195292920Sdim		{
196292920Sdim			(void) mlfi_cleanup(ctx, FALSE);
197292920Sdim			return SMFIS_TEMPFAIL;
198292920Sdim		}
199292920Sdim		return SMFIS_REJECT;
200292920Sdim	}
201292920Sdim	if (fprintf(priv-&gt;mlfi_fp, "RCPT %s (%d argument%s)\n",
202292920Sdim		    rcptaddr ? rcptaddr : "???", argc,
203292920Sdim		    (argc == 1) ? "" : "s") == EOF)
204292920Sdim	{
205292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
206292920Sdim		return SMFIS_TEMPFAIL;
207292920Sdim	}
208292920Sdim
209292920Sdim	/* continue processing */
210292920Sdim	return SMFIS_CONTINUE;
211292920Sdim}
212292920Sdim
213292920Sdimsfsistat
214292920Sdim<A href="xxfi_header.html">mlfi_header</A>(ctx, headerf, headerv)
215292920Sdim	 SMFICTX *ctx;
216292920Sdim	 char *headerf;
217292920Sdim	 unsigned char *headerv;
218292920Sdim{
219292920Sdim	/* write the header to the log file */
220292920Sdim	if (fprintf(MLFIPRIV-&gt;mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
221292920Sdim	{
222292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
223292920Sdim		return SMFIS_TEMPFAIL;
224292920Sdim	}
225292920Sdim
226292920Sdim	/* continue processing */
227292920Sdim	return SMFIS_CONTINUE;
228292920Sdim}
229292920Sdim
230292920Sdimsfsistat
231292920Sdim<A href="xxfi_eoh.html">mlfi_eoh</A>(ctx)
232292920Sdim	 SMFICTX *ctx;
233292920Sdim{
234292920Sdim	/* output the blank line between the header and the body */
235292920Sdim	if (fprintf(MLFIPRIV-&gt;mlfi_fp, "\n") == EOF)
236292920Sdim	{
237292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
238292920Sdim		return SMFIS_TEMPFAIL;
239292920Sdim	}
240292920Sdim
241292920Sdim	/* continue processing */
242292920Sdim	return SMFIS_CONTINUE;
243292920Sdim}
244292920Sdim
245292920Sdimsfsistat
246292920Sdim<A href="xxfi_body.html">mlfi_body</A>(ctx, bodyp, bodylen)
247292920Sdim	 SMFICTX *ctx;
248292920Sdim	 unsigned char *bodyp;
249292920Sdim	 size_t bodylen;
250292920Sdim{
251292920Sdim        struct mlfiPriv *priv = MLFIPRIV;
252292920Sdim
253292920Sdim	/* output body block to log file */
254292920Sdim	if (fwrite(bodyp, bodylen, 1, priv-&gt;mlfi_fp) != 1)
255292920Sdim	{
256292920Sdim		/* write failed */
257292920Sdim		fprintf(stderr, "Couldn't write file %s: %s\n",
258292920Sdim			priv-&gt;mlfi_fname, strerror(errno));
259292920Sdim		(void) mlfi_cleanup(ctx, FALSE);
260292920Sdim		return SMFIS_TEMPFAIL;
261292920Sdim	}
262292920Sdim
263292920Sdim	/* continue processing */
264292920Sdim	return SMFIS_CONTINUE;
265292920Sdim}
266292920Sdim
267292920Sdimsfsistat
268292920Sdim<A href="xxfi_eom.html">mlfi_eom</A>(ctx)
269292920Sdim	 SMFICTX *ctx;
270292920Sdim{
271292920Sdim	bool ok = TRUE;
272292920Sdim
273292920Sdim	/* change recipients, if requested */
274292920Sdim	if (add != NULL)
275292920Sdim		ok = (<A href="smfi_addrcpt.html">smfi_addrcpt</A>(ctx, add) == MI_SUCCESS);
276292920Sdim	return mlfi_cleanup(ctx, ok);
277292920Sdim}
278292920Sdim
279292920Sdimsfsistat
280292920Sdim<A href="xxfi_abort.html">mlfi_abort</A>(ctx)
281292920Sdim	 SMFICTX *ctx;
282292920Sdim{
283292920Sdim	return mlfi_cleanup(ctx, FALSE);
284292920Sdim}
285292920Sdim
286292920Sdimsfsistat
287292920Sdimmlfi_cleanup(ctx, ok)
288292920Sdim	 SMFICTX *ctx;
289292920Sdim	 bool ok;
290292920Sdim{
291292920Sdim	sfsistat rstat = SMFIS_CONTINUE;
292292920Sdim	struct mlfiPriv *priv = MLFIPRIV;
293292920Sdim	char *p;
294292920Sdim	char host[512];
295292920Sdim	char hbuf[1024];
296292920Sdim
297292920Sdim	if (priv == NULL)
298292920Sdim		return rstat;
299292920Sdim
300292920Sdim	/* close the archive file */
301292920Sdim	if (priv-&gt;mlfi_fp != NULL && fclose(priv-&gt;mlfi_fp) == EOF)
302292920Sdim	{
303292920Sdim		/* failed; we have to wait until later */
304292920Sdim		fprintf(stderr, "Couldn't close archive file %s: %s\n",
305292920Sdim			priv-&gt;mlfi_fname, strerror(errno));
306292920Sdim		rstat = SMFIS_TEMPFAIL;
307292920Sdim		(void) unlink(priv-&gt;mlfi_fname);
308292920Sdim	}
309292920Sdim	else if (ok)
310292920Sdim	{
311292920Sdim		/* add a header to the message announcing our presence */
312292920Sdim		if (gethostname(host, sizeof host) &lt; 0)
313292920Sdim			snprintf(host, sizeof host, "localhost");
314292920Sdim		p = strrchr(priv-&gt;mlfi_fname, '/');
315292920Sdim		if (p == NULL)
316292920Sdim			p = priv-&gt;mlfi_fname;
317292920Sdim		else
318292920Sdim			p++;
319292920Sdim		snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
320292920Sdim		if (<A href="smfi_addheader.html">smfi_addheader</A>(ctx, "X-Archived", hbuf) != MI_SUCCESS)
321292920Sdim		{
322292920Sdim			/* failed; we have to wait until later */
323292920Sdim			fprintf(stderr,
324292920Sdim				"Couldn't add header: X-Archived: %s\n",
325292920Sdim				hbuf);
326292920Sdim			ok = FALSE;
327292920Sdim			rstat = SMFIS_TEMPFAIL;
328292920Sdim			(void) unlink(priv-&gt;mlfi_fname);
329292920Sdim		}
330292920Sdim	}
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
365sfsistat
366<A href="xxfi_unknown.html">mlfi_unknown</A>(ctx, cmd)
367	SMFICTX *ctx;
368	char *cmd;
369{
370	return SMFIS_CONTINUE;
371}
372
373sfsistat
374<A href="xxfi_data.html">mlfi_data</A>(ctx)
375	SMFICTX *ctx;
376{
377	return SMFIS_CONTINUE;
378}
379
380sfsistat
381<A href="xxfi_negotiate.html">mlfi_negotiate</A>(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
382	SMFICTX *ctx;
383	unsigned long f0;
384	unsigned long f1;
385	unsigned long f2;
386	unsigned long f3;
387	unsigned long *pf0;
388	unsigned long *pf1;
389	unsigned long *pf2;
390	unsigned long *pf3;
391{
392	return SMFIS_ALL_OPTS;
393}
394
395struct smfiDesc smfilter =
396{
397	"SampleFilter",	/* filter name */
398	SMFI_VERSION,	/* version code -- do not change */
399	SMFIF_ADDHDRS|SMFIF_ADDRCPT,
400			/* flags */
401	<A href="xxfi_connect.html">mlfi_connect</A>,	/* connection info filter */
402	<A href="xxfi_helo.html">mlfi_helo</A>,	/* SMTP HELO command filter */
403	<A href="xxfi_envfrom.html">mlfi_envfrom</A>,	/* envelope sender filter */
404	<A href="xxfi_envrcpt.html">mlfi_envrcpt</A>,	/* envelope recipient filter */
405	<A href="xxfi_header.html">mlfi_header</A>,	/* header filter */
406	<A href="xxfi_eoh.html">mlfi_eoh</A>,	/* end of header */
407	<A href="xxfi_body.html">mlfi_body</A>,	/* body block filter */
408	<A href="xxfi_eom.html">mlfi_eom</A>,	/* end of message */
409	<A href="xxfi_abort.html">mlfi_abort</A>,	/* message aborted */
410	<A href="xxfi_close.html">mlfi_close</A>,	/* connection cleanup */
411	<A href="xxfi_unknown.html">mlfi_unknown</A>,	/* unknown SMTP commands */
412	<A href="xxfi_data.html">mlfi_data</A>,	/* DATA command */
413	<A href="xxfi_negotiate.html">mlfi_negotiate</A>	/* Once, at the start of each SMTP connection */
414};
415
416static void
417usage(prog)
418	char *prog;
419{
420	fprintf(stderr,
421		"Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n",
422		prog);
423}
424
425int
426main(argc, argv)
427	 int argc;
428	 char **argv;
429{
430	bool setconn = FALSE;
431	int c;
432	const char *args = "p:t:r:a:h";
433	extern char *optarg;
434
435	/* Process command line options */
436	while ((c = getopt(argc, argv, args)) != -1)
437	{
438		switch (c)
439		{
440		  case 'p':
441			if (optarg == NULL || *optarg == '\0')
442			{
443				(void) fprintf(stderr, "Illegal conn: %s\n",
444					       optarg);
445				exit(EX_USAGE);
446			}
447			if (<A href="smfi_setconn.html">smfi_setconn</A>(optarg) == MI_FAILURE)
448			{
449				(void) fprintf(stderr,
450					       "smfi_setconn failed\n");
451				exit(EX_SOFTWARE);
452			}
453
454			/*
455			**  If we're using a local socket, make sure it
456			**  doesn't already exist.  Don't ever run this
457			**  code as root!!
458			*/
459
460			if (strncasecmp(optarg, "unix:", 5) == 0)
461				unlink(optarg + 5);
462			else if (strncasecmp(optarg, "local:", 6) == 0)
463				unlink(optarg + 6);
464			setconn = TRUE;
465			break;
466
467		  case 't':
468			if (optarg == NULL || *optarg == '\0')
469			{
470				(void) fprintf(stderr, "Illegal timeout: %s\n",
471					       optarg);
472				exit(EX_USAGE);
473			}
474			if (<A href="smfi_settimeout.html">smfi_settimeout</A>(atoi(optarg)) == MI_FAILURE)
475			{
476				(void) fprintf(stderr,
477					       "smfi_settimeout failed\n");
478				exit(EX_SOFTWARE);
479			}
480			break;
481
482		  case 'r':
483			if (optarg == NULL)
484			{
485				(void) fprintf(stderr,
486					       "Illegal reject rcpt: %s\n",
487					       optarg);
488				exit(EX_USAGE);
489			}
490			reject = optarg;
491			break;
492
493		  case 'a':
494			if (optarg == NULL)
495			{
496				(void) fprintf(stderr,
497					       "Illegal add rcpt: %s\n",
498					       optarg);
499				exit(EX_USAGE);
500			}
501			add = optarg;
502			smfilter.xxfi_flags |= SMFIF_ADDRCPT;
503			break;
504
505		  case 'h':
506		  default:
507			usage(argv[0]);
508			exit(EX_USAGE);
509		}
510	}
511	if (!setconn)
512	{
513		fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
514		usage(argv[0]);
515		exit(EX_USAGE);
516	}
517	if (<A href="smfi_register.html">smfi_register</A>(smfilter) == MI_FAILURE)
518	{
519		fprintf(stderr, "smfi_register failed\n");
520		exit(EX_UNAVAILABLE);
521	}
522	return <A href="smfi_main.html">smfi_main</A>();
523}
524
525/* eof */
526
527</PRE>
528<HR size="1">
529<FONT size="-1">
530Copyright (c) 2000-2004, 2006 Sendmail, Inc. and its suppliers.
531All rights reserved.
532<BR>
533By using this file, you agree to the terms and conditions set
534forth in the LICENSE.
535</FONT>
536</BODY>
537</HTML>
538