13544Sjbeck /*
23544Sjbeck * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
33544Sjbeck * All rights reserved.
43544Sjbeck *
53544Sjbeck * By using this file, you agree to the terms and conditions set
63544Sjbeck * forth in the LICENSE file which can be found at the top level of
73544Sjbeck * the sendmail distribution.
83544Sjbeck *
9*11440SJohn.Beck@Sun.COM * $Id: example.c,v 8.4 2008/07/22 15:12:47 ca Exp $
103544Sjbeck */
113544Sjbeck
123544Sjbeck /*
133544Sjbeck ** A trivial example filter that logs all email to a file.
143544Sjbeck ** This milter also has some callbacks which it does not really use,
153544Sjbeck ** but they are defined to serve as an example.
163544Sjbeck */
173544Sjbeck
183544Sjbeck #include <sys/types.h>
193544Sjbeck #include <stdio.h>
203544Sjbeck #include <stdlib.h>
213544Sjbeck #include <string.h>
223544Sjbeck #include <sysexits.h>
233544Sjbeck #include <unistd.h>
243544Sjbeck
253544Sjbeck #include "libmilter/mfapi.h"
263544Sjbeck #include "libmilter/mfdef.h"
273544Sjbeck
283544Sjbeck #ifndef true
293544Sjbeck # define false 0
303544Sjbeck # define true 1
313544Sjbeck #endif /* ! true */
323544Sjbeck
333544Sjbeck struct mlfiPriv
343544Sjbeck {
353544Sjbeck char *mlfi_fname;
363544Sjbeck FILE *mlfi_fp;
373544Sjbeck };
383544Sjbeck
393544Sjbeck #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
403544Sjbeck
413544Sjbeck static unsigned long mta_caps = 0;
423544Sjbeck
433544Sjbeck sfsistat
mlfi_cleanup(ctx,ok)443544Sjbeck mlfi_cleanup(ctx, ok)
453544Sjbeck SMFICTX *ctx;
463544Sjbeck bool ok;
473544Sjbeck {
483544Sjbeck sfsistat rstat = SMFIS_CONTINUE;
493544Sjbeck struct mlfiPriv *priv = MLFIPRIV;
503544Sjbeck char *p;
513544Sjbeck char host[512];
523544Sjbeck char hbuf[1024];
533544Sjbeck
543544Sjbeck if (priv == NULL)
553544Sjbeck return rstat;
563544Sjbeck
573544Sjbeck /* close the archive file */
583544Sjbeck if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
593544Sjbeck {
603544Sjbeck /* failed; we have to wait until later */
613544Sjbeck rstat = SMFIS_TEMPFAIL;
623544Sjbeck (void) unlink(priv->mlfi_fname);
633544Sjbeck }
643544Sjbeck else if (ok)
653544Sjbeck {
663544Sjbeck /* add a header to the message announcing our presence */
673544Sjbeck if (gethostname(host, sizeof host) < 0)
683544Sjbeck snprintf(host, sizeof host, "localhost");
693544Sjbeck p = strrchr(priv->mlfi_fname, '/');
703544Sjbeck if (p == NULL)
713544Sjbeck p = priv->mlfi_fname;
723544Sjbeck else
733544Sjbeck p++;
743544Sjbeck snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
753544Sjbeck smfi_addheader(ctx, "X-Archived", hbuf);
763544Sjbeck }
773544Sjbeck else
783544Sjbeck {
793544Sjbeck /* message was aborted -- delete the archive file */
803544Sjbeck (void) unlink(priv->mlfi_fname);
813544Sjbeck }
823544Sjbeck
833544Sjbeck /* release private memory */
843544Sjbeck free(priv->mlfi_fname);
853544Sjbeck free(priv);
863544Sjbeck smfi_setpriv(ctx, NULL);
873544Sjbeck
883544Sjbeck /* return status */
893544Sjbeck return rstat;
903544Sjbeck }
913544Sjbeck
923544Sjbeck
933544Sjbeck sfsistat
mlfi_envfrom(ctx,envfrom)943544Sjbeck mlfi_envfrom(ctx, envfrom)
953544Sjbeck SMFICTX *ctx;
963544Sjbeck char **envfrom;
973544Sjbeck {
983544Sjbeck struct mlfiPriv *priv;
993544Sjbeck int fd = -1;
1003544Sjbeck
1013544Sjbeck /* allocate some private memory */
1023544Sjbeck priv = malloc(sizeof *priv);
1033544Sjbeck if (priv == NULL)
1043544Sjbeck {
1053544Sjbeck /* can't accept this message right now */
1063544Sjbeck return SMFIS_TEMPFAIL;
1073544Sjbeck }
1083544Sjbeck memset(priv, '\0', sizeof *priv);
1093544Sjbeck
1103544Sjbeck /* open a file to store this message */
1113544Sjbeck priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
1123544Sjbeck if (priv->mlfi_fname == NULL)
1133544Sjbeck {
1143544Sjbeck free(priv);
1153544Sjbeck return SMFIS_TEMPFAIL;
1163544Sjbeck }
1173544Sjbeck if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
1183544Sjbeck (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
1193544Sjbeck {
1203544Sjbeck if (fd >= 0)
1213544Sjbeck (void) close(fd);
1223544Sjbeck free(priv->mlfi_fname);
1233544Sjbeck free(priv);
1243544Sjbeck return SMFIS_TEMPFAIL;
1253544Sjbeck }
1263544Sjbeck
1273544Sjbeck /* save the private data */
1283544Sjbeck smfi_setpriv(ctx, priv);
1293544Sjbeck
1303544Sjbeck /* continue processing */
1313544Sjbeck return SMFIS_CONTINUE;
1323544Sjbeck }
1333544Sjbeck
1343544Sjbeck sfsistat
mlfi_header(ctx,headerf,headerv)1353544Sjbeck mlfi_header(ctx, headerf, headerv)
1363544Sjbeck SMFICTX *ctx;
1373544Sjbeck char *headerf;
1383544Sjbeck char *headerv;
1393544Sjbeck {
1403544Sjbeck /* write the header to the log file */
1413544Sjbeck fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
1423544Sjbeck
1433544Sjbeck /* continue processing */
1443544Sjbeck return ((mta_caps & SMFIP_NR_HDR) != 0)
1453544Sjbeck ? SMFIS_NOREPLY : SMFIS_CONTINUE;
1463544Sjbeck }
1473544Sjbeck
1483544Sjbeck sfsistat
mlfi_eoh(ctx)1493544Sjbeck mlfi_eoh(ctx)
1503544Sjbeck SMFICTX *ctx;
1513544Sjbeck {
1523544Sjbeck /* output the blank line between the header and the body */
1533544Sjbeck fprintf(MLFIPRIV->mlfi_fp, "\r\n");
1543544Sjbeck
1553544Sjbeck /* continue processing */
1563544Sjbeck return SMFIS_CONTINUE;
1573544Sjbeck }
1583544Sjbeck
1593544Sjbeck sfsistat
mlfi_body(ctx,bodyp,bodylen)1603544Sjbeck mlfi_body(ctx, bodyp, bodylen)
1613544Sjbeck SMFICTX *ctx;
1623544Sjbeck u_char *bodyp;
1633544Sjbeck size_t bodylen;
1643544Sjbeck {
1653544Sjbeck /* output body block to log file */
1663544Sjbeck if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
1673544Sjbeck {
1683544Sjbeck /* write failed */
1693544Sjbeck (void) mlfi_cleanup(ctx, false);
1703544Sjbeck return SMFIS_TEMPFAIL;
1713544Sjbeck }
1723544Sjbeck
1733544Sjbeck /* continue processing */
1743544Sjbeck return SMFIS_CONTINUE;
1753544Sjbeck }
1763544Sjbeck
1773544Sjbeck sfsistat
mlfi_eom(ctx)1783544Sjbeck mlfi_eom(ctx)
1793544Sjbeck SMFICTX *ctx;
1803544Sjbeck {
1813544Sjbeck return mlfi_cleanup(ctx, true);
1823544Sjbeck }
1833544Sjbeck
1843544Sjbeck sfsistat
mlfi_close(ctx)1853544Sjbeck mlfi_close(ctx)
1863544Sjbeck SMFICTX *ctx;
1873544Sjbeck {
1883544Sjbeck return SMFIS_ACCEPT;
1893544Sjbeck }
1903544Sjbeck
1913544Sjbeck sfsistat
mlfi_abort(ctx)1923544Sjbeck mlfi_abort(ctx)
1933544Sjbeck SMFICTX *ctx;
1943544Sjbeck {
1953544Sjbeck return mlfi_cleanup(ctx, false);
1963544Sjbeck }
1973544Sjbeck
1983544Sjbeck sfsistat
mlfi_unknown(ctx,cmd)1993544Sjbeck mlfi_unknown(ctx, cmd)
2003544Sjbeck SMFICTX *ctx;
2013544Sjbeck char *cmd;
2023544Sjbeck {
2033544Sjbeck return SMFIS_CONTINUE;
2043544Sjbeck }
2053544Sjbeck
2063544Sjbeck sfsistat
mlfi_data(ctx)2073544Sjbeck mlfi_data(ctx)
2083544Sjbeck SMFICTX *ctx;
2093544Sjbeck {
2103544Sjbeck return SMFIS_CONTINUE;
2113544Sjbeck }
2123544Sjbeck
2133544Sjbeck sfsistat
mlfi_negotiate(ctx,f0,f1,f2,f3,pf0,pf1,pf2,pf3)2143544Sjbeck mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
2153544Sjbeck SMFICTX *ctx;
2163544Sjbeck unsigned long f0;
2173544Sjbeck unsigned long f1;
2183544Sjbeck unsigned long f2;
2193544Sjbeck unsigned long f3;
2203544Sjbeck unsigned long *pf0;
2213544Sjbeck unsigned long *pf1;
2223544Sjbeck unsigned long *pf2;
2233544Sjbeck unsigned long *pf3;
2243544Sjbeck {
2253544Sjbeck /* milter actions: add headers */
2263544Sjbeck *pf0 = SMFIF_ADDHDRS;
2273544Sjbeck
2283544Sjbeck /* milter protocol steps: all but connect, HELO, RCPT */
2293544Sjbeck *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
2303544Sjbeck mta_caps = f1;
2313544Sjbeck if ((mta_caps & SMFIP_NR_HDR) != 0)
2323544Sjbeck *pf1 |= SMFIP_NR_HDR;
2333544Sjbeck *pf2 = 0;
2343544Sjbeck *pf3 = 0;
2353544Sjbeck return SMFIS_CONTINUE;
2363544Sjbeck }
2373544Sjbeck
2383544Sjbeck struct smfiDesc smfilter =
2393544Sjbeck {
2403544Sjbeck "SampleFilter", /* filter name */
2413544Sjbeck SMFI_VERSION, /* version code -- do not change */
2423544Sjbeck SMFIF_ADDHDRS, /* flags */
2433544Sjbeck NULL, /* connection info filter */
2443544Sjbeck NULL, /* SMTP HELO command filter */
2453544Sjbeck mlfi_envfrom, /* envelope sender filter */
2463544Sjbeck NULL, /* envelope recipient filter */
2473544Sjbeck mlfi_header, /* header filter */
2483544Sjbeck mlfi_eoh, /* end of header */
2493544Sjbeck mlfi_body, /* body block filter */
2503544Sjbeck mlfi_eom, /* end of message */
2513544Sjbeck mlfi_abort, /* message aborted */
2523544Sjbeck mlfi_close, /* connection cleanup */
2533544Sjbeck mlfi_unknown, /* unknown/unimplemented SMTP commands */
2543544Sjbeck mlfi_data, /* DATA command filter */
255*11440SJohn.Beck@Sun.COM mlfi_negotiate /* option negotiation at connection startup */
2563544Sjbeck };
2573544Sjbeck
2583544Sjbeck int
main(argc,argv)2593544Sjbeck main(argc, argv)
2603544Sjbeck int argc;
2613544Sjbeck char *argv[];
2623544Sjbeck {
2633544Sjbeck bool setconn;
2643544Sjbeck int c;
2653544Sjbeck
2663544Sjbeck setconn = false;
2673544Sjbeck
2683544Sjbeck /* Process command line options */
2693544Sjbeck while ((c = getopt(argc, argv, "p:")) != -1)
2703544Sjbeck {
2713544Sjbeck switch (c)
2723544Sjbeck {
2733544Sjbeck case 'p':
2743544Sjbeck if (optarg == NULL || *optarg == '\0')
2753544Sjbeck {
2763544Sjbeck (void) fprintf(stderr, "Illegal conn: %s\n",
2773544Sjbeck optarg);
2783544Sjbeck exit(EX_USAGE);
2793544Sjbeck }
2803544Sjbeck (void) smfi_setconn(optarg);
2813544Sjbeck setconn = true;
2823544Sjbeck break;
2833544Sjbeck
2843544Sjbeck }
2853544Sjbeck }
2863544Sjbeck if (!setconn)
2873544Sjbeck {
2883544Sjbeck fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
2893544Sjbeck exit(EX_USAGE);
2903544Sjbeck }
2913544Sjbeck if (smfi_register(smfilter) == MI_FAILURE)
2923544Sjbeck {
2933544Sjbeck fprintf(stderr, "smfi_register failed\n");
2943544Sjbeck exit(EX_UNAVAILABLE);
2953544Sjbeck }
2963544Sjbeck return smfi_main();
2973544Sjbeck }
2983544Sjbeck
299