# A Sample Filter

The following sample logs each message to a separate temporary file, adds a recipient given with the -a flag, and rejects a disallowed recipient address given with the -r flag. It recognizes the following options:

`-p port` | The port through which the MTA will connect to the filter.
`-t sec` | The timeout value.
`-r addr` | A recipient to reject.
`-a addr` | A recipient to add.

    #include "mfapi.h"

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sysexits.h>
    #include <unistd.h>
    #ifndef bool
    #define bool char
    #define TRUE 1
    #define FALSE 0
    #endif

    extern int errno;

    struct mlfiPriv
    {
        char    *mlfi_fname;
        char    *mlfi_connectfrom;
        char    *mlfi_helofrom;
        FILE    *mlfi_fp;
    };

    #define MLFIPRIV        ((struct mlfiPriv *) smfi_getpriv(ctx))

    extern sfsistat  mlfi_cleanup(SMFICTX *, bool);
    /* recipients to add and reject (set with -a and -r options) */
    char *add, *reject;

    sfsistat
    mlfi_connect(ctx, hostname, hostaddr)
         SMFICTX *ctx;
         char *hostname;
         _SOCK_ADDR *hostaddr;
    {
        struct mlfiPriv *priv;
        char *ident;

        /* allocate some private memory */
        priv = malloc(sizeof *priv);
        if (priv == NULL)
        {
    	/* can't accept this message right now */
    	return SMFIS_TEMPFAIL;
        }
        memset(priv, '\0', sizeof *priv);

        /* save the private data */
        smfi_setpriv(ctx, priv);

        ident = smfi_getsymval(ctx, "_");
        if(!ident) ident = "???";
        if(!(priv->mlfi_connectfrom = strdup(ident))) {
    	return SMFIS_TEMPFAIL;
        }
        /* Continue processing. */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_helo(ctx, helohost)
         SMFICTX *ctx;
         char *helohost;
    {
        char *tls;
        char *buf;
        struct mlfiPriv *priv = MLFIPRIV;
        tls = smfi_getsymval(ctx, "{tls_version}");
        if(!tls) tls = "No TLS";
        if(!helohost) helohost = "???";
        if(!(buf = (char*)malloc(strlen(tls) + strlen(helohost) + 3))) {
    	return SMFIS_TEMPFAIL;
        }
        sprintf(buf, "%s, %s", helohost, tls);
        if(priv->mlfi_helofrom)
    	free(priv->mlfi_helofrom);
        priv->mlfi_helofrom = buf;
        /* Continue processing. */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_envfrom(ctx, argv)
         SMFICTX *ctx;
         char **argv;
    {
        struct mlfiPriv *priv = MLFIPRIV;
        char *mailaddr = smfi_getsymval(ctx, "{mail_addr}");
        int argc = 0;

        /* open a file to store this message */
        priv->mlfi_fname = strdup("/tmp/msg.XXXXXX");
        mkstemp(priv->mlfi_fname);
        if (priv->mlfi_fname == NULL)
    	return SMFIS_TEMPFAIL;
        if ((priv->mlfi_fp = fopen(priv->mlfi_fname, "w+")) == NULL)
        {
    	free(priv->mlfi_fname);
    	return SMFIS_TEMPFAIL;
        }

        /* count the arguments */
        while(*argv++) ++argc;
        /* log the connection information we stored earlier: */
        if(fprintf(priv->mlfi_fp, "Connect from %s (%s)\n\n",
    	       priv->mlfi_helofrom, priv->mlfi_connectfrom) == EOF) {
    	(void) mlfi_cleanup(ctx, FALSE);
    	return SMFIS_TEMPFAIL;
        }
        /* log the sender */
        if(fprintf(priv->mlfi_fp, "FROM %s (%d argument%s)\n",
    	       mailaddr?mailaddr:"???", argc,
    	       (argc == 1)?"":"s")
           == EOF) {
    	(void) mlfi_cleanup(ctx, FALSE);
    	return SMFIS_TEMPFAIL;
        }
        /* continue processing */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_envrcpt(ctx, argv)
         SMFICTX *ctx;
         char **argv;
    {
        struct mlfiPriv *priv = MLFIPRIV;
        char *rcptaddr = smfi_getsymval(ctx, "{rcpt_addr}");
        int argc = 0;
        /* count the arguments */
        while(*argv++) ++argc;
        /* log this recipient */
        if(reject && rcptaddr && (strcmp(rcptaddr, reject) == 0)) {
    	if(fprintf(priv->mlfi_fp, "RCPT %s -- REJECTED\n", rcptaddr)
    	   == EOF) {
    	    (void) mlfi_cleanup(ctx, FALSE);
    	    return SMFIS_TEMPFAIL;
    	}
    	return SMFIS_REJECT;
        }
        if(fprintf(priv->mlfi_fp, "RCPT %s (%d argument%s)\n",
    	       rcptaddr?rcptaddr:"???", argc,
    	       (argc == 1)?"":"s")
           == EOF) {
    	(void) mlfi_cleanup(ctx, FALSE);
    	return SMFIS_TEMPFAIL;
        }
        /* continue processing */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_header(ctx, headerf, headerv)
         SMFICTX *ctx;
         char *headerf;
         unsigned char *headerv;
    {
        /* write the header to the log file */
        fprintf(MLFIPRIV->mlfi_fp, "%s: %s\n", headerf, headerv);

        /* continue processing */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_eoh(ctx)
         SMFICTX *ctx;
    {
        /* output the blank line between the header and the body */
        fprintf(MLFIPRIV->mlfi_fp, "\n");

        /* continue processing */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_body(ctx, bodyp, bodylen)
         SMFICTX *ctx;
         unsigned char *bodyp;
         size_t bodylen;
    {
        /* output body block to log file */
        int nwritten;
        if ((nwritten = fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp)) != 1)
        {
    	/* write failed */
    	perror("error logging body");
    	(void) mlfi_cleanup(ctx, FALSE);
    	return SMFIS_TEMPFAIL;
        }

        /* continue processing */
        return SMFIS_CONTINUE;
    }

    sfsistat
    mlfi_eom(ctx)
         SMFICTX *ctx;
    {
        bool ok = TRUE;
        /* change recipients, if requested */
        if(add)
    	ok = ok && (smfi_addrcpt(ctx, add) == MI_SUCCESS);
        return mlfi_cleanup(ctx, ok);
    }

    sfsistat
    mlfi_abort(ctx)
         SMFICTX *ctx;
    {
        return mlfi_cleanup(ctx, FALSE);
    }

    sfsistat
    mlfi_cleanup(ctx, ok)
         SMFICTX *ctx;
         bool ok;
    {
        sfsistat rstat = SMFIS_CONTINUE;
        struct mlfiPriv *priv = MLFIPRIV;
        char *p;
        char host[512];
        char hbuf[1024];

        if (priv == NULL)
    	return rstat;

        /* close the archive file */
        if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
        {
    	/* failed; we have to wait until later */
    	fprintf(stderr, "Couldn't close archive file %s: %s\n",
    		priv->mlfi_fname, strerror(errno));
    	rstat = SMFIS_TEMPFAIL;
    	(void) unlink(priv->mlfi_fname);
        }
        else if (ok)
        {
    	/* add a header to the message announcing our presence */
    	if (gethostname(host, sizeof host) < 0)
    	    strncpy(host, "localhost", sizeof host);
    	p = strrchr(priv->mlfi_fname, '/');
    	if (p == NULL)
    	    p = priv->mlfi_fname;
    	else
    	    p++;
    	snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
    	smfi_addheader(ctx, "X-Archived", hbuf);
        }
        else
        {
    	/* message was aborted -- delete the archive file */
    	fprintf(stderr, "Message aborted.  Removing %s\n",
    		priv->mlfi_fname);
    	rstat = SMFIS_TEMPFAIL;
    	(void) unlink(priv->mlfi_fname);
        }

        /* release private memory */
        free(priv->mlfi_fname);

        /* return status */
        return rstat;
    }

    sfsistat
    mlfi_close(ctx)
         SMFICTX *ctx;
    {
        struct mlfiPriv *priv = MLFIPRIV;
        if(priv->mlfi_connectfrom)
    	free(priv->mlfi_connectfrom);
        if(priv->mlfi_helofrom)
    	free(priv->mlfi_helofrom);
        free(priv);
        smfi_setpriv(ctx, NULL);
        return SMFIS_CONTINUE;
    }

    struct smfiDesc smfilter =
    {
        "SampleFilter", /* filter name */
        SMFI_VERSION,   /* version code -- do not change */
        SMFIF_ADDHDRS,  /* flags */
        mlfi_connect,   /* connection info filter */
        mlfi_helo,      /* SMTP HELO command filter */
        mlfi_envfrom,   /* envelope sender filter */
        mlfi_envrcpt,   /* envelope recipient filter */
        mlfi_header,    /* header filter */
        mlfi_eoh,       /* end of header */
        mlfi_body,      /* body block filter */
        mlfi_eom,       /* end of message */
        mlfi_abort,     /* message aborted */
        mlfi_close,     /* connection cleanup */
    };

    static void
    usage()
    {
        fprintf(stderr,
    	    "Usage: sample [-p socket-addr] [-t timeout] [-r reject-addr] \n\
    \t[-a accept-addr]\n");
    }

    int
    main(argc, argv)
         int argc;
         char *argv[];
    {
        int retval;
        char c;
        const char *args = "p:t:r:a:h";
        extern char *optarg;

        /* Process command line options */
        while ((c = getopt(argc, argv, args)) != (char)EOF)
        {
    	switch (c)
    	{
    	case 'p':
    	    if (optarg == NULL || *optarg == '\0')
    	    {
    		(void) fprintf(stderr, "Illegal conn: %s\n",
    			       optarg);
    		exit(EX_USAGE);
    	    }
    	    if(smfi_setconn(optarg) == MI_FAILURE)
    	    {
    		(void) fputs("smfi_setconn failed", stderr);
    		exit(EX_SOFTWARE);
    	    }
    	    /*
    	    ** If we're using a local socket, make sure it doesn't
    	    ** already exist.
    	    */
    	    if(strncmp(optarg, "unix:", 5) == 0)
    		unlink(optarg + 5);
    	    else if(strncmp(optarg, "local:", 6) == 0)
    		unlink(optarg + 6);
    	    break;

    	case 't':
    	    if (optarg == NULL || *optarg == '\0')
    	    {
    		(void) fprintf(stderr, "Illegal timeout: %s\n",
    			       optarg);
    		exit(EX_USAGE);
    	    }
    	    if(smfi_settimeout(atoi(optarg)) == MI_FAILURE)
    	    {
    		(void) fputs("smfi_settimeout failed", stderr);
    		exit(EX_SOFTWARE);
    	    }
    	    break;

    	case 'r':
    	    if (optarg == NULL)
    	    {
    		(void) fprintf(stderr, "Illegal reject rcpt: %s\n",
    			       optarg);
    		exit(EX_USAGE);
    	    }
    	    reject = optarg;
    	    break;

    	case 'a':
    	    if (optarg == NULL)
    	    {
    		(void) fprintf(stderr, "Illegal add rcpt: %s\n",
    			       optarg);
    		exit(EX_USAGE);
    	    }
    	    add = optarg;
    	    smfilter.xxfi_flags |= SMFIF_ADDRCPT;
    	    break;
    	case 'h':
    	default:
    	    usage();
    	    exit(0);
    	}
        }
        if (smfi_register(smfilter) == MI_FAILURE)
        {
    	fprintf(stderr, "smfi_register failed\n");
    	exit(EX_UNAVAILABLE);
        }
        retval = smfi_main();
        return retval;
    }

    /* eof */

Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. All rights reserved.
By using this file, you agree to the terms and conditions set forth in the LICENSE.