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