1N/A/*
1N/A * Copyright (c) 2006 Sendmail, Inc. and its suppliers.
1N/A * All rights reserved.
1N/A *
1N/A * By using this file, you agree to the terms and conditions set
1N/A * forth in the LICENSE file which can be found at the top level of
1N/A * the sendmail distribution.
1N/A *
1N/A * $Id: example.c,v 8.4 2008/07/22 15:12:47 ca Exp $
1N/A */
1N/A
1N/A/*
1N/A** A trivial example filter that logs all email to a file.
1N/A** This milter also has some callbacks which it does not really use,
1N/A** but they are defined to serve as an example.
1N/A*/
1N/A
1N/A#include <sys/types.h>
1N/A#include <stdio.h>
1N/A#include <stdlib.h>
1N/A#include <string.h>
1N/A#include <sysexits.h>
1N/A#include <unistd.h>
1N/A
1N/A#include "libmilter/mfapi.h"
1N/A#include "libmilter/mfdef.h"
1N/A
1N/A#ifndef true
1N/A# define false 0
1N/A# define true 1
1N/A#endif /* ! true */
1N/A
1N/Astruct mlfiPriv
1N/A{
1N/A char *mlfi_fname;
1N/A FILE *mlfi_fp;
1N/A};
1N/A
1N/A#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx))
1N/A
1N/Astatic unsigned long mta_caps = 0;
1N/A
1N/Asfsistat
1N/Amlfi_cleanup(ctx, ok)
1N/A SMFICTX *ctx;
1N/A bool ok;
1N/A{
1N/A sfsistat rstat = SMFIS_CONTINUE;
1N/A struct mlfiPriv *priv = MLFIPRIV;
1N/A char *p;
1N/A char host[512];
1N/A char hbuf[1024];
1N/A
1N/A if (priv == NULL)
1N/A return rstat;
1N/A
1N/A /* close the archive file */
1N/A if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF)
1N/A {
1N/A /* failed; we have to wait until later */
1N/A rstat = SMFIS_TEMPFAIL;
1N/A (void) unlink(priv->mlfi_fname);
1N/A }
1N/A else if (ok)
1N/A {
1N/A /* add a header to the message announcing our presence */
1N/A if (gethostname(host, sizeof host) < 0)
1N/A snprintf(host, sizeof host, "localhost");
1N/A p = strrchr(priv->mlfi_fname, '/');
1N/A if (p == NULL)
1N/A p = priv->mlfi_fname;
1N/A else
1N/A p++;
1N/A snprintf(hbuf, sizeof hbuf, "%s@%s", p, host);
1N/A smfi_addheader(ctx, "X-Archived", hbuf);
1N/A }
1N/A else
1N/A {
1N/A /* message was aborted -- delete the archive file */
1N/A (void) unlink(priv->mlfi_fname);
1N/A }
1N/A
1N/A /* release private memory */
1N/A free(priv->mlfi_fname);
1N/A free(priv);
1N/A smfi_setpriv(ctx, NULL);
1N/A
1N/A /* return status */
1N/A return rstat;
1N/A}
1N/A
1N/A
1N/Asfsistat
1N/Amlfi_envfrom(ctx, envfrom)
1N/A SMFICTX *ctx;
1N/A char **envfrom;
1N/A{
1N/A struct mlfiPriv *priv;
1N/A int fd = -1;
1N/A
1N/A /* allocate some private memory */
1N/A priv = malloc(sizeof *priv);
1N/A if (priv == NULL)
1N/A {
1N/A /* can't accept this message right now */
1N/A return SMFIS_TEMPFAIL;
1N/A }
1N/A memset(priv, '\0', sizeof *priv);
1N/A
1N/A /* open a file to store this message */
1N/A priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX");
1N/A if (priv->mlfi_fname == NULL)
1N/A {
1N/A free(priv);
1N/A return SMFIS_TEMPFAIL;
1N/A }
1N/A if ((fd = mkstemp(priv->mlfi_fname)) < 0 ||
1N/A (priv->mlfi_fp = fdopen(fd, "w+")) == NULL)
1N/A {
1N/A if (fd >= 0)
1N/A (void) close(fd);
1N/A free(priv->mlfi_fname);
1N/A free(priv);
1N/A return SMFIS_TEMPFAIL;
1N/A }
1N/A
1N/A /* save the private data */
1N/A smfi_setpriv(ctx, priv);
1N/A
1N/A /* continue processing */
1N/A return SMFIS_CONTINUE;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_header(ctx, headerf, headerv)
1N/A SMFICTX *ctx;
1N/A char *headerf;
1N/A char *headerv;
1N/A{
1N/A /* write the header to the log file */
1N/A fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv);
1N/A
1N/A /* continue processing */
1N/A return ((mta_caps & SMFIP_NR_HDR) != 0)
1N/A ? SMFIS_NOREPLY : SMFIS_CONTINUE;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_eoh(ctx)
1N/A SMFICTX *ctx;
1N/A{
1N/A /* output the blank line between the header and the body */
1N/A fprintf(MLFIPRIV->mlfi_fp, "\r\n");
1N/A
1N/A /* continue processing */
1N/A return SMFIS_CONTINUE;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_body(ctx, bodyp, bodylen)
1N/A SMFICTX *ctx;
1N/A u_char *bodyp;
1N/A size_t bodylen;
1N/A{
1N/A /* output body block to log file */
1N/A if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0)
1N/A {
1N/A /* write failed */
1N/A (void) mlfi_cleanup(ctx, false);
1N/A return SMFIS_TEMPFAIL;
1N/A }
1N/A
1N/A /* continue processing */
1N/A return SMFIS_CONTINUE;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_eom(ctx)
1N/A SMFICTX *ctx;
1N/A{
1N/A return mlfi_cleanup(ctx, true);
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_close(ctx)
1N/A SMFICTX *ctx;
1N/A{
1N/A return SMFIS_ACCEPT;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_abort(ctx)
1N/A SMFICTX *ctx;
1N/A{
1N/A return mlfi_cleanup(ctx, false);
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_unknown(ctx, cmd)
1N/A SMFICTX *ctx;
1N/A char *cmd;
1N/A{
1N/A return SMFIS_CONTINUE;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_data(ctx)
1N/A SMFICTX *ctx;
1N/A{
1N/A return SMFIS_CONTINUE;
1N/A}
1N/A
1N/Asfsistat
1N/Amlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3)
1N/A SMFICTX *ctx;
1N/A unsigned long f0;
1N/A unsigned long f1;
1N/A unsigned long f2;
1N/A unsigned long f3;
1N/A unsigned long *pf0;
1N/A unsigned long *pf1;
1N/A unsigned long *pf2;
1N/A unsigned long *pf3;
1N/A{
1N/A /* milter actions: add headers */
1N/A *pf0 = SMFIF_ADDHDRS;
1N/A
1N/A /* milter protocol steps: all but connect, HELO, RCPT */
1N/A *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT;
1N/A mta_caps = f1;
1N/A if ((mta_caps & SMFIP_NR_HDR) != 0)
1N/A *pf1 |= SMFIP_NR_HDR;
1N/A *pf2 = 0;
1N/A *pf3 = 0;
1N/A return SMFIS_CONTINUE;
1N/A}
1N/A
1N/Astruct smfiDesc smfilter =
1N/A{
1N/A "SampleFilter", /* filter name */
1N/A SMFI_VERSION, /* version code -- do not change */
1N/A SMFIF_ADDHDRS, /* flags */
1N/A NULL, /* connection info filter */
1N/A NULL, /* SMTP HELO command filter */
1N/A mlfi_envfrom, /* envelope sender filter */
1N/A NULL, /* envelope recipient filter */
1N/A mlfi_header, /* header filter */
1N/A mlfi_eoh, /* end of header */
1N/A mlfi_body, /* body block filter */
1N/A mlfi_eom, /* end of message */
1N/A mlfi_abort, /* message aborted */
1N/A mlfi_close, /* connection cleanup */
1N/A mlfi_unknown, /* unknown/unimplemented SMTP commands */
1N/A mlfi_data, /* DATA command filter */
1N/A mlfi_negotiate /* option negotiation at connection startup */
1N/A};
1N/A
1N/Aint
1N/Amain(argc, argv)
1N/A int argc;
1N/A char *argv[];
1N/A{
1N/A bool setconn;
1N/A int c;
1N/A
1N/A setconn = false;
1N/A
1N/A /* Process command line options */
1N/A while ((c = getopt(argc, argv, "p:")) != -1)
1N/A {
1N/A switch (c)
1N/A {
1N/A case 'p':
1N/A if (optarg == NULL || *optarg == '\0')
1N/A {
1N/A (void) fprintf(stderr, "Illegal conn: %s\n",
1N/A optarg);
1N/A exit(EX_USAGE);
1N/A }
1N/A (void) smfi_setconn(optarg);
1N/A setconn = true;
1N/A break;
1N/A
1N/A }
1N/A }
1N/A if (!setconn)
1N/A {
1N/A fprintf(stderr, "%s: Missing required -p argument\n", argv[0]);
1N/A exit(EX_USAGE);
1N/A }
1N/A if (smfi_register(smfilter) == MI_FAILURE)
1N/A {
1N/A fprintf(stderr, "smfi_register failed\n");
1N/A exit(EX_UNAVAILABLE);
1N/A }
1N/A return smfi_main();
1N/A}
1N/A