2 <head><title>A Sample Filter</title></head>
4 <h1>A Sample Filter</h1>
6 The following sample logs each message to a separate temporary file,
7 adds a recipient given with the -a flag, and rejects a disallowed
8 recipient address given with the -r flag. It recognizes the following
12 <table border="1" cellpadding=2 cellspacing=1>
13 <tr><td><code>-p port</code></td><td>The port through which the MTA will connect to the filter.</td></tr>
14 <tr><td><code>-t sec</code></td><td>The timeout value.</td></tr>
15 <tr><td><code>-r addr</code></td><td>A recipient to reject.</td></tr>
16 <tr><td><code>-a addr</code></td><td>A recipient to add.</td></tr>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sysexits.h>
28 #include <unistd.h>
30 #include "libmilter/mfapi.h"
42 char *mlfi_connectfrom;
47 #define MLFIPRIV ((struct mlfiPriv *) <a href="smfi_getpriv.html">smfi_getpriv</a>(ctx))
49 extern sfsistat mlfi_cleanup(SMFICTX *, bool);
51 /* recipients to add and reject (set with -a and -r options) */
56 <a href="xxfi_connect.html">mlfi_connect</a>(ctx, hostname, hostaddr)
61 struct mlfiPriv *priv;
64 /* allocate some private memory */
65 priv = malloc(sizeof *priv);
68 /* can't accept this message right now */
69 return SMFIS_TEMPFAIL;
71 memset(priv, '\0', sizeof *priv);
73 /* save the private data */
74 <a href="smfi_setpriv.html">smfi_setpriv</a>(ctx, priv);
76 ident = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "_");
79 if ((priv->mlfi_connectfrom = strdup(ident)) == NULL)
81 (void) mlfi_cleanup(ctx, FALSE);
82 return SMFIS_TEMPFAIL;
85 /* continue processing */
86 return SMFIS_CONTINUE;
90 <a href="xxfi_helo.html">mlfi_helo</a>(ctx, helohost)
97 struct mlfiPriv *priv = MLFIPRIV;
99 tls = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{tls_version}");
102 if (helohost == NULL)
104 len = strlen(tls) + strlen(helohost) + 3;
105 if ((buf = (char*) malloc(len)) == NULL)
107 (void) mlfi_cleanup(ctx, FALSE);
108 return SMFIS_TEMPFAIL;
110 snprintf(buf, len, "%s, %s", helohost, tls);
111 if (priv->mlfi_helofrom != NULL)
112 free(priv->mlfi_helofrom);
113 priv->mlfi_helofrom = buf;
115 /* continue processing */
116 return SMFIS_CONTINUE;
120 <a href="xxfi_envfrom.html">mlfi_envfrom</a>(ctx, argv)
124 struct mlfiPriv *priv = MLFIPRIV;
125 char *mailaddr = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{mail_addr}");
128 /* open a file to store this message */
129 if ((priv->mlfi_fname = strdup("/tmp/msg.XXXXXX")) == NULL)
131 (void) mlfi_cleanup(ctx, FALSE);
132 return SMFIS_TEMPFAIL;
135 if (mkstemp(priv->mlfi_fname) == -1)
137 (void) mlfi_cleanup(ctx, FALSE);
138 return SMFIS_TEMPFAIL;
141 if ((priv->mlfi_fp = fopen(priv->mlfi_fname, "w+")) == NULL)
143 (void) mlfi_cleanup(ctx, FALSE);
144 return SMFIS_TEMPFAIL;
147 /* count the arguments */
148 while (*argv++ != NULL)
151 /* log the connection information we stored earlier: */
152 if (fprintf(priv->mlfi_fp, "Connect from %s (%s)\n\n",
153 priv->mlfi_helofrom, priv->mlfi_connectfrom) == EOF)
155 (void) mlfi_cleanup(ctx, FALSE);
156 return SMFIS_TEMPFAIL;
159 if (fprintf(priv->mlfi_fp, "FROM %s (%d argument%s)\n",
160 mailaddr ? mailaddr : "???", argc,
161 (argc == 1) ? "" : "s") == EOF)
163 (void) mlfi_cleanup(ctx, FALSE);
164 return SMFIS_TEMPFAIL;
167 /* continue processing */
168 return SMFIS_CONTINUE;
172 <a href="xxfi_envrcpt.html">mlfi_envrcpt</a>(ctx, argv)
176 struct mlfiPriv *priv = MLFIPRIV;
177 char *rcptaddr = <a href="smfi_getsymval.html">smfi_getsymval</a>(ctx, "{rcpt_addr}");
180 /* count the arguments */
181 while (*argv++ != NULL)
184 /* log this recipient */
185 if (reject != NULL && rcptaddr != NULL &&
186 (strcasecmp(rcptaddr, reject) == 0))
188 if (fprintf(priv->mlfi_fp, "RCPT %s -- REJECTED\n",
191 (void) mlfi_cleanup(ctx, FALSE);
192 return SMFIS_TEMPFAIL;
196 if (fprintf(priv->mlfi_fp, "RCPT %s (%d argument%s)\n",
197 rcptaddr ? rcptaddr : "???", argc,
198 (argc == 1) ? "" : "s") == EOF)
200 (void) mlfi_cleanup(ctx, FALSE);
201 return SMFIS_TEMPFAIL;
204 /* continue processing */
205 return SMFIS_CONTINUE;
209 <a href="xxfi_header.html">mlfi_header</a>(ctx, headerf, headerv)
212 unsigned char *headerv;
214 /* write the header to the log file */
215 if (fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv) == EOF)
217 (void) mlfi_cleanup(ctx, FALSE);
218 return SMFIS_TEMPFAIL;
221 /* continue processing */
222 return SMFIS_CONTINUE;
226 <a href="xxfi_eoh.html">mlfi_eoh</a>(ctx)
229 /* output the blank line between the header and the body */
230 if (fprintf(MLFIPRIV->mlfi_fp, "\n") == EOF)
232 (void) mlfi_cleanup(ctx, FALSE);
233 return SMFIS_TEMPFAIL;
236 /* continue processing */
237 return SMFIS_CONTINUE;
241 <a href="xxfi_body.html">mlfi_body</a>(ctx, bodyp, bodylen)
243 unsigned char *bodyp;
246 struct mlfiPriv *priv = MLFIPRIV;
248 /* output body block to log file */
249 if (fwrite(bodyp, bodylen, 1, priv->mlfi_fp) != 1)
252 fprintf(stderr, "Couldn't write file %s: %s\n",
253 priv->mlfi_fname, strerror(errno));
254 (void) mlfi_cleanup(ctx, FALSE);
255 return SMFIS_TEMPFAIL;
258 /* continue processing */
259 return SMFIS_CONTINUE;
263 <a href="xxfi_eom.html">mlfi_eom</a>(ctx)
268 /* change recipients, if requested */
270 ok = (<a href="smfi_addrcpt.html">smfi_addrcpt</a>(ctx, add) == MI_SUCCESS);
271 return mlfi_cleanup(ctx, ok);
275 <a href="xxfi_abort.html">mlfi_abort</a>(ctx)
278 return mlfi_cleanup(ctx, FALSE);
282 mlfi_cleanup(ctx, ok)
286 sfsistat rstat = SMFIS_CONTINUE;
287 struct mlfiPriv *priv = MLFIPRIV;
295 /* close the archive file */
296 if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
298 /* failed; we have to wait until later */
299 fprintf(stderr, "Couldn't close archive file %s: %s\n",
300 priv->mlfi_fname, strerror(errno));
301 rstat = SMFIS_TEMPFAIL;
302 (void) unlink(priv->mlfi_fname);
306 /* add a header to the message announcing our presence */
307 if (gethostname(host, sizeof host) < 0)
308 snprintf(host, sizeof host, "localhost");
309 p = strrchr(priv->mlfi_fname, '/');
311 p = priv->mlfi_fname;
314 snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
315 if (<a href="smfi_addheader.html">smfi_addheader</a>(ctx, "X-Archived", hbuf) != MI_SUCCESS)
317 /* failed; we have to wait until later */
319 "Couldn't add header: X-Archived: %s\n",
322 rstat = SMFIS_TEMPFAIL;
323 (void) unlink(priv->mlfi_fname);
328 /* message was aborted -- delete the archive file */
329 fprintf(stderr, "Message aborted. Removing %s\n",
330 priv->mlfi_fname);
331 rstat = SMFIS_TEMPFAIL;
332 (void) unlink(priv->mlfi_fname);
335 /* release private memory */
336 if (priv->mlfi_fname != NULL)
337 free(priv->mlfi_fname);
344 <a href="xxfi_close.html">mlfi_close</a>(ctx)
347 struct mlfiPriv *priv = MLFIPRIV;
350 return SMFIS_CONTINUE;
351 if (priv->mlfi_connectfrom != NULL)
352 free(priv->mlfi_connectfrom);
353 if (priv->mlfi_helofrom != NULL)
354 free(priv->mlfi_helofrom);
356 <a href="smfi_setpriv.html">smfi_setpriv</a>(ctx, NULL);
357 return SMFIS_CONTINUE;
360 struct smfiDesc smfilter =
362 "SampleFilter", /* filter name */
363 SMFI_VERSION, /* version code -- do not change */
364 SMFIF_ADDHDRS, /* flags */
365 <a href="xxfi_connect.html">mlfi_connect</a>, /* connection info filter */
366 <a href="xxfi_helo.html">mlfi_helo</a>, /* SMTP HELO command filter */
367 <a href="xxfi_envfrom.html">mlfi_envfrom</a>, /* envelope sender filter */
368 <a href="xxfi_envrcpt.html">mlfi_envrcpt</a>, /* envelope recipient filter */
369 <a href="xxfi_header.html">mlfi_header</a>, /* header filter */
370 <a href="xxfi_eoh.html">mlfi_eoh</a>, /* end of header */
371 <a href="xxfi_body.html">mlfi_body</a>, /* body block filter */
372 <a href="xxfi_eom.html">mlfi_eom</a>, /* end of message */
373 <a href="xxfi_abort.html">mlfi_abort</a>, /* message aborted */
374 <a href="xxfi_close.html">mlfi_close</a>, /* connection cleanup */
382 "Usage: %s [-p socket-addr] [-t timeout] [-r reject-addr] [-a add-addr]\n",
392 const char *args = "p:t:r:a:h";
395 /* Process command line options */
396 while ((c = getopt(argc, argv, args)) != -1)
401 if (optarg == NULL || *optarg == '\0')
403 (void) fprintf(stderr, "Illegal conn: %s\n",
407 if (<a href="smfi_setconn.html">smfi_setconn</a>(optarg) == MI_FAILURE)
409 (void) fprintf(stderr,
410 "smfi_setconn failed\n");
415 ** If we're using a local socket, make sure it
416 ** doesn't already exist. Don't ever run this
420 if (strncasecmp(optarg, "unix:", 5) == 0)
422 else if (strncasecmp(optarg, "local:", 6) == 0)
427 if (optarg == NULL || *optarg == '\0')
429 (void) fprintf(stderr, "Illegal timeout: %s\n",
433 if (<a href="smfi_settimeout.html">smfi_settimeout</a>(atoi(optarg)) == MI_FAILURE)
435 (void) fprintf(stderr,
436 "smfi_settimeout failed\n");
444 (void) fprintf(stderr,
445 "Illegal reject rcpt: %s\n",
455 (void) fprintf(stderr,
456 "Illegal add rcpt: %s\n",
461 smfilter.xxfi_flags |= SMFIF_ADDRCPT;
470 if (<a href="smfi_register.html">smfi_register</a>(smfilter) == MI_FAILURE)
472 fprintf(stderr, "smfi_register failed\n");
473 exit(EX_UNAVAILABLE);
475 return <a href="smfi_main.html">smfi_main</a>();
483 Copyright (c) 2000-2003 Sendmail, Inc. and its suppliers.
486 By using this file, you agree to the terms and conditions set
487 forth in the LICENSE.