2 <HEAD><TITLE>A Sample Filter</TITLE></HEAD>
5 $Id: sample.html,v 1.22 2006/10/09 23:14:51 ca Exp $
7 <H1>A Sample Filter</H1>
9 The following sample logs each message to a separate temporary file,
10 adds a recipient given with the -a flag,
11 and rejects a disallowed recipient address given with the -r flag.
12 It recognizes the following options:
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>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sysexits.h>
31 #include <unistd.h>
33 #include "libmilter/mfapi.h"
45 char *mlfi_connectfrom;
50 #define MLFIPRIV ((struct mlfiPriv *) <A href="smfi_getpriv.html">smfi_getpriv</A>(ctx))
52 extern sfsistat mlfi_cleanup(SMFICTX *, bool);
54 /* recipients to add and reject (set with -a and -r options) */
59 <A href="xxfi_connect.html">mlfi_connect</A>(ctx, hostname, hostaddr)
64 struct mlfiPriv *priv;
67 /* allocate some private memory */
68 priv = malloc(sizeof *priv);
71 /* can't accept this message right now */
72 return SMFIS_TEMPFAIL;
74 memset(priv, '\0', sizeof *priv);
76 /* save the private data */
77 <A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, priv);
79 ident = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "_");
82 if ((priv->mlfi_connectfrom = strdup(ident)) == NULL)
84 (void) mlfi_cleanup(ctx, FALSE);
85 return SMFIS_TEMPFAIL;
88 /* continue processing */
89 return SMFIS_CONTINUE;
93 <A href="xxfi_helo.html">mlfi_helo</A>(ctx, helohost)
100 struct mlfiPriv *priv = MLFIPRIV;
102 tls = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{tls_version}");
105 if (helohost == NULL)
107 len = strlen(tls) + strlen(helohost) + 3;
108 if ((buf = (char*) malloc(len)) == NULL)
110 (void) mlfi_cleanup(ctx, FALSE);
111 return SMFIS_TEMPFAIL;
113 snprintf(buf, len, "%s, %s", helohost, tls);
114 if (priv->mlfi_helofrom != NULL)
115 free(priv->mlfi_helofrom);
116 priv->mlfi_helofrom = buf;
118 /* continue processing */
119 return SMFIS_CONTINUE;
123 <A href="xxfi_envfrom.html">mlfi_envfrom</A>(ctx, argv)
129 struct mlfiPriv *priv = MLFIPRIV;
130 char *mailaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{mail_addr}");
132 /* open a file to store this message */
133 if ((priv->mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
135 (void) mlfi_cleanup(ctx, FALSE);
136 return SMFIS_TEMPFAIL;
139 if ((fd = mkstemp(priv->mlfi_fname)) == -1)
141 (void) mlfi_cleanup(ctx, FALSE);
142 return SMFIS_TEMPFAIL;
145 if ((priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
148 (void) mlfi_cleanup(ctx, FALSE);
149 return SMFIS_TEMPFAIL;
152 /* count the arguments */
153 while (*argv++ != NULL)
156 /* log the connection information we stored earlier: */
157 if (fprintf(priv->mlfi_fp, "Connect from %s (%s)\n\n",
158 priv->mlfi_helofrom, priv->mlfi_connectfrom) == EOF)
160 (void) mlfi_cleanup(ctx, FALSE);
161 return SMFIS_TEMPFAIL;
164 if (fprintf(priv->mlfi_fp, "FROM %s (%d argument%s)\n",
165 mailaddr ? mailaddr : "???", argc,
166 (argc == 1) ? "" : "s") == EOF)
168 (void) mlfi_cleanup(ctx, FALSE);
169 return SMFIS_TEMPFAIL;
172 /* continue processing */
173 return SMFIS_CONTINUE;
177 <A href="xxfi_envrcpt.html">mlfi_envrcpt</A>(ctx, argv)
181 struct mlfiPriv *priv = MLFIPRIV;
182 char *rcptaddr = <A href="smfi_getsymval.html">smfi_getsymval</A>(ctx, "{rcpt_addr}");
185 /* count the arguments */
186 while (*argv++ != NULL)
189 /* log this recipient */
190 if (reject != NULL && rcptaddr != NULL &&
191 (strcasecmp(rcptaddr, reject) == 0))
193 if (fprintf(priv->mlfi_fp, "RCPT %s -- REJECTED\n",
196 (void) mlfi_cleanup(ctx, FALSE);
197 return SMFIS_TEMPFAIL;
201 if (fprintf(priv->mlfi_fp, "RCPT %s (%d argument%s)\n",
202 rcptaddr ? rcptaddr : "???", argc,
203 (argc == 1) ? "" : "s") == EOF)
205 (void) mlfi_cleanup(ctx, FALSE);
206 return SMFIS_TEMPFAIL;
209 /* continue processing */
210 return SMFIS_CONTINUE;
214 <A href="xxfi_header.html">mlfi_header</A>(ctx, headerf, headerv)
217 unsigned char *headerv;
219 /* write the header to the log file */
220 if (fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
222 (void) mlfi_cleanup(ctx, FALSE);
223 return SMFIS_TEMPFAIL;
226 /* continue processing */
227 return SMFIS_CONTINUE;
231 <A href="xxfi_eoh.html">mlfi_eoh</A>(ctx)
234 /* output the blank line between the header and the body */
235 if (fprintf(MLFIPRIV->mlfi_fp, "\n") == EOF)
237 (void) mlfi_cleanup(ctx, FALSE);
238 return SMFIS_TEMPFAIL;
241 /* continue processing */
242 return SMFIS_CONTINUE;
246 <A href="xxfi_body.html">mlfi_body</A>(ctx, bodyp, bodylen)
248 unsigned char *bodyp;
251 struct mlfiPriv *priv = MLFIPRIV;
253 /* output body block to log file */
254 if (fwrite(bodyp, bodylen, 1, priv->mlfi_fp) != 1)
257 fprintf(stderr, "Couldn't write file %s: %s\n",
258 priv->mlfi_fname, strerror(errno));
259 (void) mlfi_cleanup(ctx, FALSE);
260 return SMFIS_TEMPFAIL;
263 /* continue processing */
264 return SMFIS_CONTINUE;
268 <A href="xxfi_eom.html">mlfi_eom</A>(ctx)
273 /* change recipients, if requested */
275 ok = (<A href="smfi_addrcpt.html">smfi_addrcpt</A>(ctx, add) == MI_SUCCESS);
276 return mlfi_cleanup(ctx, ok);
280 <A href="xxfi_abort.html">mlfi_abort</A>(ctx)
283 return mlfi_cleanup(ctx, FALSE);
287 mlfi_cleanup(ctx, ok)
291 sfsistat rstat = SMFIS_CONTINUE;
292 struct mlfiPriv *priv = MLFIPRIV;
300 /* close the archive file */
301 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
303 /* failed; we have to wait until later */
304 fprintf(stderr, "Couldn't close archive file %s: %s\n",
305 priv->mlfi_fname, strerror(errno));
306 rstat = SMFIS_TEMPFAIL;
307 (void) unlink(priv->mlfi_fname);
311 /* add a header to the message announcing our presence */
312 if (gethostname(host, sizeof host) < 0)
313 snprintf(host, sizeof host, "localhost");
314 p = strrchr(priv->mlfi_fname, '/');
316 p = priv->mlfi_fname;
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)
322 /* failed; we have to wait until later */
324 "Couldn't add header: X-Archived: %s\n",
327 rstat = SMFIS_TEMPFAIL;
328 (void) unlink(priv->mlfi_fname);
333 /* message was aborted -- delete the archive file */
334 fprintf(stderr, "Message aborted. Removing %s\n",
335 priv->mlfi_fname);
336 rstat = SMFIS_TEMPFAIL;
337 (void) unlink(priv->mlfi_fname);
340 /* release private memory */
341 if (priv->mlfi_fname != NULL)
342 free(priv->mlfi_fname);
349 <A href="xxfi_close.html">mlfi_close</A>(ctx)
352 struct mlfiPriv *priv = MLFIPRIV;
355 return SMFIS_CONTINUE;
356 if (priv->mlfi_connectfrom != NULL)
357 free(priv->mlfi_connectfrom);
358 if (priv->mlfi_helofrom != NULL)
359 free(priv->mlfi_helofrom);
361 <A href="smfi_setpriv.html">smfi_setpriv</A>(ctx, NULL);
362 return SMFIS_CONTINUE;
366 <A href="xxfi_unknown.html">mlfi_unknown</A>(ctx, cmd)
370 return SMFIS_CONTINUE;
374 <A href="xxfi_data.html">mlfi_data</A>(ctx)
377 return SMFIS_CONTINUE;
381 <A href="xxfi_negotiate.html">mlfi_negotiate</A>(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
392 return SMFIS_ALL_OPTS;
395 struct smfiDesc smfilter =
397 "SampleFilter", /* filter name */
398 SMFI_VERSION, /* version code -- do not change */
399 SMFIF_ADDHDRS|SMFIF_ADDRCPT,
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 */
421 "Usage: %s -p socket-addr [-t timeout] [-r reject-addr] [-a add-addr]\n",
430 bool setconn = FALSE;
432 const char *args = "p:t:r:a:h";
435 /* Process command line options */
436 while ((c = getopt(argc, argv, args)) != -1)
441 if (optarg == NULL || *optarg == '\0')
443 (void) fprintf(stderr, "Illegal conn: %s\n",
447 if (<A href="smfi_setconn.html">smfi_setconn</A>(optarg) == MI_FAILURE)
449 (void) fprintf(stderr,
450 "smfi_setconn failed\n");
455 ** If we're using a local socket, make sure it
456 ** doesn't already exist. Don't ever run this
460 if (strncasecmp(optarg, "unix:", 5) == 0)
462 else if (strncasecmp(optarg, "local:", 6) == 0)
468 if (optarg == NULL || *optarg == '\0')
470 (void) fprintf(stderr, "Illegal timeout: %s\n",
474 if (<A href="smfi_settimeout.html">smfi_settimeout</A>(atoi(optarg)) == MI_FAILURE)
476 (void) fprintf(stderr,
477 "smfi_settimeout failed\n");
485 (void) fprintf(stderr,
486 "Illegal reject rcpt: %s\n",
496 (void) fprintf(stderr,
497 "Illegal add rcpt: %s\n",
502 smfilter.xxfi_flags |= SMFIF_ADDRCPT;
513 fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
517 if (<A href="smfi_register.html">smfi_register</A>(smfilter) == MI_FAILURE)
519 fprintf(stderr, "smfi_register failed\n");
520 exit(EX_UNAVAILABLE);
522 return <A href="smfi_main.html">smfi_main</A>();
530 Copyright (c) 2000-2004, 2006 Sendmail, Inc. and its suppliers.
533 By using this file, you agree to the terms and conditions set
534 forth in the LICENSE.