1168515Sgshapiro/* 2261370Sgshapiro * Copyright (c) 2006 Proofpoint, Inc. and its suppliers. 3168515Sgshapiro * All rights reserved. 4168515Sgshapiro * 5168515Sgshapiro * By using this file, you agree to the terms and conditions set 6168515Sgshapiro * forth in the LICENSE file which can be found at the top level of 7168515Sgshapiro * the sendmail distribution. 8168515Sgshapiro * 9266711Sgshapiro * $Id: example.c,v 8.5 2013-11-22 20:51:36 ca Exp $ 10168515Sgshapiro */ 11168515Sgshapiro 12168515Sgshapiro/* 13168515Sgshapiro** A trivial example filter that logs all email to a file. 14168515Sgshapiro** This milter also has some callbacks which it does not really use, 15168515Sgshapiro** but they are defined to serve as an example. 16168515Sgshapiro*/ 17168515Sgshapiro 18168515Sgshapiro#include <sys/types.h> 19168515Sgshapiro#include <stdio.h> 20168515Sgshapiro#include <stdlib.h> 21168515Sgshapiro#include <string.h> 22168515Sgshapiro#include <sysexits.h> 23168515Sgshapiro#include <unistd.h> 24168515Sgshapiro 25168515Sgshapiro#include "libmilter/mfapi.h" 26168515Sgshapiro#include "libmilter/mfdef.h" 27168515Sgshapiro 28168515Sgshapiro#ifndef true 29168515Sgshapiro# define false 0 30168515Sgshapiro# define true 1 31168515Sgshapiro#endif /* ! true */ 32168515Sgshapiro 33168515Sgshapirostruct mlfiPriv 34168515Sgshapiro{ 35168515Sgshapiro char *mlfi_fname; 36168515Sgshapiro FILE *mlfi_fp; 37168515Sgshapiro}; 38168515Sgshapiro 39168515Sgshapiro#define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 40168515Sgshapiro 41168515Sgshapirostatic unsigned long mta_caps = 0; 42168515Sgshapiro 43168515Sgshapirosfsistat 44168515Sgshapiromlfi_cleanup(ctx, ok) 45168515Sgshapiro SMFICTX *ctx; 46168515Sgshapiro bool ok; 47168515Sgshapiro{ 48168515Sgshapiro sfsistat rstat = SMFIS_CONTINUE; 49168515Sgshapiro struct mlfiPriv *priv = MLFIPRIV; 50168515Sgshapiro char *p; 51168515Sgshapiro char host[512]; 52168515Sgshapiro char hbuf[1024]; 53168515Sgshapiro 54168515Sgshapiro if (priv == NULL) 55168515Sgshapiro return rstat; 56168515Sgshapiro 57168515Sgshapiro /* close the archive file */ 58168515Sgshapiro if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) 59168515Sgshapiro { 60168515Sgshapiro /* failed; we have to wait until later */ 61168515Sgshapiro rstat = SMFIS_TEMPFAIL; 62168515Sgshapiro (void) unlink(priv->mlfi_fname); 63168515Sgshapiro } 64168515Sgshapiro else if (ok) 65168515Sgshapiro { 66168515Sgshapiro /* add a header to the message announcing our presence */ 67168515Sgshapiro if (gethostname(host, sizeof host) < 0) 68168515Sgshapiro snprintf(host, sizeof host, "localhost"); 69168515Sgshapiro p = strrchr(priv->mlfi_fname, '/'); 70168515Sgshapiro if (p == NULL) 71168515Sgshapiro p = priv->mlfi_fname; 72168515Sgshapiro else 73168515Sgshapiro p++; 74168515Sgshapiro snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); 75168515Sgshapiro smfi_addheader(ctx, "X-Archived", hbuf); 76168515Sgshapiro } 77168515Sgshapiro else 78168515Sgshapiro { 79168515Sgshapiro /* message was aborted -- delete the archive file */ 80168515Sgshapiro (void) unlink(priv->mlfi_fname); 81168515Sgshapiro } 82168515Sgshapiro 83168515Sgshapiro /* release private memory */ 84168515Sgshapiro free(priv->mlfi_fname); 85168515Sgshapiro free(priv); 86168515Sgshapiro smfi_setpriv(ctx, NULL); 87168515Sgshapiro 88168515Sgshapiro /* return status */ 89168515Sgshapiro return rstat; 90168515Sgshapiro} 91168515Sgshapiro 92168515Sgshapiro 93168515Sgshapirosfsistat 94168515Sgshapiromlfi_envfrom(ctx, envfrom) 95168515Sgshapiro SMFICTX *ctx; 96168515Sgshapiro char **envfrom; 97168515Sgshapiro{ 98168515Sgshapiro struct mlfiPriv *priv; 99168515Sgshapiro int fd = -1; 100168515Sgshapiro 101168515Sgshapiro /* allocate some private memory */ 102168515Sgshapiro priv = malloc(sizeof *priv); 103168515Sgshapiro if (priv == NULL) 104168515Sgshapiro { 105168515Sgshapiro /* can't accept this message right now */ 106168515Sgshapiro return SMFIS_TEMPFAIL; 107168515Sgshapiro } 108168515Sgshapiro memset(priv, '\0', sizeof *priv); 109168515Sgshapiro 110168515Sgshapiro /* open a file to store this message */ 111168515Sgshapiro priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX"); 112168515Sgshapiro if (priv->mlfi_fname == NULL) 113168515Sgshapiro { 114168515Sgshapiro free(priv); 115168515Sgshapiro return SMFIS_TEMPFAIL; 116168515Sgshapiro } 117168515Sgshapiro if ((fd = mkstemp(priv->mlfi_fname)) < 0 || 118168515Sgshapiro (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) 119168515Sgshapiro { 120168515Sgshapiro if (fd >= 0) 121168515Sgshapiro (void) close(fd); 122168515Sgshapiro free(priv->mlfi_fname); 123168515Sgshapiro free(priv); 124168515Sgshapiro return SMFIS_TEMPFAIL; 125168515Sgshapiro } 126168515Sgshapiro 127168515Sgshapiro /* save the private data */ 128168515Sgshapiro smfi_setpriv(ctx, priv); 129168515Sgshapiro 130168515Sgshapiro /* continue processing */ 131168515Sgshapiro return SMFIS_CONTINUE; 132168515Sgshapiro} 133168515Sgshapiro 134168515Sgshapirosfsistat 135168515Sgshapiromlfi_header(ctx, headerf, headerv) 136168515Sgshapiro SMFICTX *ctx; 137168515Sgshapiro char *headerf; 138168515Sgshapiro char *headerv; 139168515Sgshapiro{ 140168515Sgshapiro /* write the header to the log file */ 141168515Sgshapiro fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv); 142168515Sgshapiro 143168515Sgshapiro /* continue processing */ 144168515Sgshapiro return ((mta_caps & SMFIP_NR_HDR) != 0) 145168515Sgshapiro ? SMFIS_NOREPLY : SMFIS_CONTINUE; 146168515Sgshapiro} 147168515Sgshapiro 148168515Sgshapirosfsistat 149168515Sgshapiromlfi_eoh(ctx) 150168515Sgshapiro SMFICTX *ctx; 151168515Sgshapiro{ 152168515Sgshapiro /* output the blank line between the header and the body */ 153168515Sgshapiro fprintf(MLFIPRIV->mlfi_fp, "\r\n"); 154168515Sgshapiro 155168515Sgshapiro /* continue processing */ 156168515Sgshapiro return SMFIS_CONTINUE; 157168515Sgshapiro} 158168515Sgshapiro 159168515Sgshapirosfsistat 160168515Sgshapiromlfi_body(ctx, bodyp, bodylen) 161168515Sgshapiro SMFICTX *ctx; 162168515Sgshapiro u_char *bodyp; 163168515Sgshapiro size_t bodylen; 164168515Sgshapiro{ 165168515Sgshapiro /* output body block to log file */ 166168515Sgshapiro if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) 167168515Sgshapiro { 168168515Sgshapiro /* write failed */ 169168515Sgshapiro (void) mlfi_cleanup(ctx, false); 170168515Sgshapiro return SMFIS_TEMPFAIL; 171168515Sgshapiro } 172168515Sgshapiro 173168515Sgshapiro /* continue processing */ 174168515Sgshapiro return SMFIS_CONTINUE; 175168515Sgshapiro} 176168515Sgshapiro 177168515Sgshapirosfsistat 178168515Sgshapiromlfi_eom(ctx) 179168515Sgshapiro SMFICTX *ctx; 180168515Sgshapiro{ 181168515Sgshapiro return mlfi_cleanup(ctx, true); 182168515Sgshapiro} 183168515Sgshapiro 184168515Sgshapirosfsistat 185168515Sgshapiromlfi_close(ctx) 186168515Sgshapiro SMFICTX *ctx; 187168515Sgshapiro{ 188168515Sgshapiro return SMFIS_ACCEPT; 189168515Sgshapiro} 190168515Sgshapiro 191168515Sgshapirosfsistat 192168515Sgshapiromlfi_abort(ctx) 193168515Sgshapiro SMFICTX *ctx; 194168515Sgshapiro{ 195168515Sgshapiro return mlfi_cleanup(ctx, false); 196168515Sgshapiro} 197168515Sgshapiro 198168515Sgshapirosfsistat 199168515Sgshapiromlfi_unknown(ctx, cmd) 200168515Sgshapiro SMFICTX *ctx; 201168515Sgshapiro char *cmd; 202168515Sgshapiro{ 203168515Sgshapiro return SMFIS_CONTINUE; 204168515Sgshapiro} 205168515Sgshapiro 206168515Sgshapirosfsistat 207168515Sgshapiromlfi_data(ctx) 208168515Sgshapiro SMFICTX *ctx; 209168515Sgshapiro{ 210168515Sgshapiro return SMFIS_CONTINUE; 211168515Sgshapiro} 212168515Sgshapiro 213168515Sgshapirosfsistat 214168515Sgshapiromlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3) 215168515Sgshapiro SMFICTX *ctx; 216168515Sgshapiro unsigned long f0; 217168515Sgshapiro unsigned long f1; 218168515Sgshapiro unsigned long f2; 219168515Sgshapiro unsigned long f3; 220168515Sgshapiro unsigned long *pf0; 221168515Sgshapiro unsigned long *pf1; 222168515Sgshapiro unsigned long *pf2; 223168515Sgshapiro unsigned long *pf3; 224168515Sgshapiro{ 225168515Sgshapiro /* milter actions: add headers */ 226168515Sgshapiro *pf0 = SMFIF_ADDHDRS; 227168515Sgshapiro 228168515Sgshapiro /* milter protocol steps: all but connect, HELO, RCPT */ 229168515Sgshapiro *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT; 230168515Sgshapiro mta_caps = f1; 231168515Sgshapiro if ((mta_caps & SMFIP_NR_HDR) != 0) 232168515Sgshapiro *pf1 |= SMFIP_NR_HDR; 233168515Sgshapiro *pf2 = 0; 234168515Sgshapiro *pf3 = 0; 235168515Sgshapiro return SMFIS_CONTINUE; 236168515Sgshapiro} 237168515Sgshapiro 238168515Sgshapirostruct smfiDesc smfilter = 239168515Sgshapiro{ 240168515Sgshapiro "SampleFilter", /* filter name */ 241168515Sgshapiro SMFI_VERSION, /* version code -- do not change */ 242168515Sgshapiro SMFIF_ADDHDRS, /* flags */ 243168515Sgshapiro NULL, /* connection info filter */ 244168515Sgshapiro NULL, /* SMTP HELO command filter */ 245168515Sgshapiro mlfi_envfrom, /* envelope sender filter */ 246168515Sgshapiro NULL, /* envelope recipient filter */ 247168515Sgshapiro mlfi_header, /* header filter */ 248168515Sgshapiro mlfi_eoh, /* end of header */ 249168515Sgshapiro mlfi_body, /* body block filter */ 250168515Sgshapiro mlfi_eom, /* end of message */ 251168515Sgshapiro mlfi_abort, /* message aborted */ 252168515Sgshapiro mlfi_close, /* connection cleanup */ 253168515Sgshapiro mlfi_unknown, /* unknown/unimplemented SMTP commands */ 254168515Sgshapiro mlfi_data, /* DATA command filter */ 255203004Sgshapiro mlfi_negotiate /* option negotiation at connection startup */ 256168515Sgshapiro}; 257168515Sgshapiro 258168515Sgshapiroint 259168515Sgshapiromain(argc, argv) 260168515Sgshapiro int argc; 261168515Sgshapiro char *argv[]; 262168515Sgshapiro{ 263168515Sgshapiro bool setconn; 264168515Sgshapiro int c; 265168515Sgshapiro 266168515Sgshapiro setconn = false; 267168515Sgshapiro 268168515Sgshapiro /* Process command line options */ 269168515Sgshapiro while ((c = getopt(argc, argv, "p:")) != -1) 270168515Sgshapiro { 271168515Sgshapiro switch (c) 272168515Sgshapiro { 273168515Sgshapiro case 'p': 274168515Sgshapiro if (optarg == NULL || *optarg == '\0') 275168515Sgshapiro { 276168515Sgshapiro (void) fprintf(stderr, "Illegal conn: %s\n", 277168515Sgshapiro optarg); 278168515Sgshapiro exit(EX_USAGE); 279168515Sgshapiro } 280168515Sgshapiro (void) smfi_setconn(optarg); 281168515Sgshapiro setconn = true; 282168515Sgshapiro break; 283168515Sgshapiro 284168515Sgshapiro } 285168515Sgshapiro } 286168515Sgshapiro if (!setconn) 287168515Sgshapiro { 288168515Sgshapiro fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); 289168515Sgshapiro exit(EX_USAGE); 290168515Sgshapiro } 291168515Sgshapiro if (smfi_register(smfilter) == MI_FAILURE) 292168515Sgshapiro { 293168515Sgshapiro fprintf(stderr, "smfi_register failed\n"); 294168515Sgshapiro exit(EX_UNAVAILABLE); 295168515Sgshapiro } 296168515Sgshapiro return smfi_main(); 297168515Sgshapiro} 298168515Sgshapiro 299