1*058561cbSjbeck /* 2*058561cbSjbeck * Copyright (c) 2006 Sendmail, Inc. and its suppliers. 3*058561cbSjbeck * All rights reserved. 4*058561cbSjbeck * 5*058561cbSjbeck * By using this file, you agree to the terms and conditions set 6*058561cbSjbeck * forth in the LICENSE file which can be found at the top level of 7*058561cbSjbeck * the sendmail distribution. 8*058561cbSjbeck * 9*058561cbSjbeck * $Id: example.c,v 8.3 2006/12/20 21:22:34 ca Exp $ 10*058561cbSjbeck */ 11*058561cbSjbeck 12*058561cbSjbeck #pragma ident "%Z%%M% %I% %E% SMI" 13*058561cbSjbeck 14*058561cbSjbeck /* 15*058561cbSjbeck ** A trivial example filter that logs all email to a file. 16*058561cbSjbeck ** This milter also has some callbacks which it does not really use, 17*058561cbSjbeck ** but they are defined to serve as an example. 18*058561cbSjbeck */ 19*058561cbSjbeck 20*058561cbSjbeck #include <sys/types.h> 21*058561cbSjbeck #include <stdio.h> 22*058561cbSjbeck #include <stdlib.h> 23*058561cbSjbeck #include <string.h> 24*058561cbSjbeck #include <sysexits.h> 25*058561cbSjbeck #include <unistd.h> 26*058561cbSjbeck 27*058561cbSjbeck #include "libmilter/mfapi.h" 28*058561cbSjbeck #include "libmilter/mfdef.h" 29*058561cbSjbeck 30*058561cbSjbeck #ifndef true 31*058561cbSjbeck # define false 0 32*058561cbSjbeck # define true 1 33*058561cbSjbeck #endif /* ! true */ 34*058561cbSjbeck 35*058561cbSjbeck struct mlfiPriv 36*058561cbSjbeck { 37*058561cbSjbeck char *mlfi_fname; 38*058561cbSjbeck FILE *mlfi_fp; 39*058561cbSjbeck }; 40*058561cbSjbeck 41*058561cbSjbeck #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 42*058561cbSjbeck 43*058561cbSjbeck static unsigned long mta_caps = 0; 44*058561cbSjbeck 45*058561cbSjbeck sfsistat 46*058561cbSjbeck mlfi_cleanup(ctx, ok) 47*058561cbSjbeck SMFICTX *ctx; 48*058561cbSjbeck bool ok; 49*058561cbSjbeck { 50*058561cbSjbeck sfsistat rstat = SMFIS_CONTINUE; 51*058561cbSjbeck struct mlfiPriv *priv = MLFIPRIV; 52*058561cbSjbeck char *p; 53*058561cbSjbeck char host[512]; 54*058561cbSjbeck char hbuf[1024]; 55*058561cbSjbeck 56*058561cbSjbeck if (priv == NULL) 57*058561cbSjbeck return rstat; 58*058561cbSjbeck 59*058561cbSjbeck /* close the archive file */ 60*058561cbSjbeck if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) 61*058561cbSjbeck { 62*058561cbSjbeck /* failed; we have to wait until later */ 63*058561cbSjbeck rstat = SMFIS_TEMPFAIL; 64*058561cbSjbeck (void) unlink(priv->mlfi_fname); 65*058561cbSjbeck } 66*058561cbSjbeck else if (ok) 67*058561cbSjbeck { 68*058561cbSjbeck /* add a header to the message announcing our presence */ 69*058561cbSjbeck if (gethostname(host, sizeof host) < 0) 70*058561cbSjbeck snprintf(host, sizeof host, "localhost"); 71*058561cbSjbeck p = strrchr(priv->mlfi_fname, '/'); 72*058561cbSjbeck if (p == NULL) 73*058561cbSjbeck p = priv->mlfi_fname; 74*058561cbSjbeck else 75*058561cbSjbeck p++; 76*058561cbSjbeck snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); 77*058561cbSjbeck smfi_addheader(ctx, "X-Archived", hbuf); 78*058561cbSjbeck } 79*058561cbSjbeck else 80*058561cbSjbeck { 81*058561cbSjbeck /* message was aborted -- delete the archive file */ 82*058561cbSjbeck (void) unlink(priv->mlfi_fname); 83*058561cbSjbeck } 84*058561cbSjbeck 85*058561cbSjbeck /* release private memory */ 86*058561cbSjbeck free(priv->mlfi_fname); 87*058561cbSjbeck free(priv); 88*058561cbSjbeck smfi_setpriv(ctx, NULL); 89*058561cbSjbeck 90*058561cbSjbeck /* return status */ 91*058561cbSjbeck return rstat; 92*058561cbSjbeck } 93*058561cbSjbeck 94*058561cbSjbeck 95*058561cbSjbeck sfsistat 96*058561cbSjbeck mlfi_envfrom(ctx, envfrom) 97*058561cbSjbeck SMFICTX *ctx; 98*058561cbSjbeck char **envfrom; 99*058561cbSjbeck { 100*058561cbSjbeck struct mlfiPriv *priv; 101*058561cbSjbeck int fd = -1; 102*058561cbSjbeck 103*058561cbSjbeck /* allocate some private memory */ 104*058561cbSjbeck priv = malloc(sizeof *priv); 105*058561cbSjbeck if (priv == NULL) 106*058561cbSjbeck { 107*058561cbSjbeck /* can't accept this message right now */ 108*058561cbSjbeck return SMFIS_TEMPFAIL; 109*058561cbSjbeck } 110*058561cbSjbeck memset(priv, '\0', sizeof *priv); 111*058561cbSjbeck 112*058561cbSjbeck /* open a file to store this message */ 113*058561cbSjbeck priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX"); 114*058561cbSjbeck if (priv->mlfi_fname == NULL) 115*058561cbSjbeck { 116*058561cbSjbeck free(priv); 117*058561cbSjbeck return SMFIS_TEMPFAIL; 118*058561cbSjbeck } 119*058561cbSjbeck if ((fd = mkstemp(priv->mlfi_fname)) < 0 || 120*058561cbSjbeck (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) 121*058561cbSjbeck { 122*058561cbSjbeck if (fd >= 0) 123*058561cbSjbeck (void) close(fd); 124*058561cbSjbeck free(priv->mlfi_fname); 125*058561cbSjbeck free(priv); 126*058561cbSjbeck return SMFIS_TEMPFAIL; 127*058561cbSjbeck } 128*058561cbSjbeck 129*058561cbSjbeck /* save the private data */ 130*058561cbSjbeck smfi_setpriv(ctx, priv); 131*058561cbSjbeck 132*058561cbSjbeck /* continue processing */ 133*058561cbSjbeck return SMFIS_CONTINUE; 134*058561cbSjbeck } 135*058561cbSjbeck 136*058561cbSjbeck sfsistat 137*058561cbSjbeck mlfi_header(ctx, headerf, headerv) 138*058561cbSjbeck SMFICTX *ctx; 139*058561cbSjbeck char *headerf; 140*058561cbSjbeck char *headerv; 141*058561cbSjbeck { 142*058561cbSjbeck /* write the header to the log file */ 143*058561cbSjbeck fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv); 144*058561cbSjbeck 145*058561cbSjbeck /* continue processing */ 146*058561cbSjbeck return ((mta_caps & SMFIP_NR_HDR) != 0) 147*058561cbSjbeck ? SMFIS_NOREPLY : SMFIS_CONTINUE; 148*058561cbSjbeck } 149*058561cbSjbeck 150*058561cbSjbeck sfsistat 151*058561cbSjbeck mlfi_eoh(ctx) 152*058561cbSjbeck SMFICTX *ctx; 153*058561cbSjbeck { 154*058561cbSjbeck /* output the blank line between the header and the body */ 155*058561cbSjbeck fprintf(MLFIPRIV->mlfi_fp, "\r\n"); 156*058561cbSjbeck 157*058561cbSjbeck /* continue processing */ 158*058561cbSjbeck return SMFIS_CONTINUE; 159*058561cbSjbeck } 160*058561cbSjbeck 161*058561cbSjbeck sfsistat 162*058561cbSjbeck mlfi_body(ctx, bodyp, bodylen) 163*058561cbSjbeck SMFICTX *ctx; 164*058561cbSjbeck u_char *bodyp; 165*058561cbSjbeck size_t bodylen; 166*058561cbSjbeck { 167*058561cbSjbeck /* output body block to log file */ 168*058561cbSjbeck if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) 169*058561cbSjbeck { 170*058561cbSjbeck /* write failed */ 171*058561cbSjbeck (void) mlfi_cleanup(ctx, false); 172*058561cbSjbeck return SMFIS_TEMPFAIL; 173*058561cbSjbeck } 174*058561cbSjbeck 175*058561cbSjbeck /* continue processing */ 176*058561cbSjbeck return SMFIS_CONTINUE; 177*058561cbSjbeck } 178*058561cbSjbeck 179*058561cbSjbeck sfsistat 180*058561cbSjbeck mlfi_eom(ctx) 181*058561cbSjbeck SMFICTX *ctx; 182*058561cbSjbeck { 183*058561cbSjbeck return mlfi_cleanup(ctx, true); 184*058561cbSjbeck } 185*058561cbSjbeck 186*058561cbSjbeck sfsistat 187*058561cbSjbeck mlfi_close(ctx) 188*058561cbSjbeck SMFICTX *ctx; 189*058561cbSjbeck { 190*058561cbSjbeck return SMFIS_ACCEPT; 191*058561cbSjbeck } 192*058561cbSjbeck 193*058561cbSjbeck sfsistat 194*058561cbSjbeck mlfi_abort(ctx) 195*058561cbSjbeck SMFICTX *ctx; 196*058561cbSjbeck { 197*058561cbSjbeck return mlfi_cleanup(ctx, false); 198*058561cbSjbeck } 199*058561cbSjbeck 200*058561cbSjbeck sfsistat 201*058561cbSjbeck mlfi_unknown(ctx, cmd) 202*058561cbSjbeck SMFICTX *ctx; 203*058561cbSjbeck char *cmd; 204*058561cbSjbeck { 205*058561cbSjbeck return SMFIS_CONTINUE; 206*058561cbSjbeck } 207*058561cbSjbeck 208*058561cbSjbeck sfsistat 209*058561cbSjbeck mlfi_data(ctx) 210*058561cbSjbeck SMFICTX *ctx; 211*058561cbSjbeck { 212*058561cbSjbeck return SMFIS_CONTINUE; 213*058561cbSjbeck } 214*058561cbSjbeck 215*058561cbSjbeck sfsistat 216*058561cbSjbeck mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3) 217*058561cbSjbeck SMFICTX *ctx; 218*058561cbSjbeck unsigned long f0; 219*058561cbSjbeck unsigned long f1; 220*058561cbSjbeck unsigned long f2; 221*058561cbSjbeck unsigned long f3; 222*058561cbSjbeck unsigned long *pf0; 223*058561cbSjbeck unsigned long *pf1; 224*058561cbSjbeck unsigned long *pf2; 225*058561cbSjbeck unsigned long *pf3; 226*058561cbSjbeck { 227*058561cbSjbeck /* milter actions: add headers */ 228*058561cbSjbeck *pf0 = SMFIF_ADDHDRS; 229*058561cbSjbeck 230*058561cbSjbeck /* milter protocol steps: all but connect, HELO, RCPT */ 231*058561cbSjbeck *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT; 232*058561cbSjbeck mta_caps = f1; 233*058561cbSjbeck if ((mta_caps & SMFIP_NR_HDR) != 0) 234*058561cbSjbeck *pf1 |= SMFIP_NR_HDR; 235*058561cbSjbeck *pf2 = 0; 236*058561cbSjbeck *pf3 = 0; 237*058561cbSjbeck return SMFIS_CONTINUE; 238*058561cbSjbeck } 239*058561cbSjbeck 240*058561cbSjbeck struct smfiDesc smfilter = 241*058561cbSjbeck { 242*058561cbSjbeck "SampleFilter", /* filter name */ 243*058561cbSjbeck SMFI_VERSION, /* version code -- do not change */ 244*058561cbSjbeck SMFIF_ADDHDRS, /* flags */ 245*058561cbSjbeck NULL, /* connection info filter */ 246*058561cbSjbeck NULL, /* SMTP HELO command filter */ 247*058561cbSjbeck mlfi_envfrom, /* envelope sender filter */ 248*058561cbSjbeck NULL, /* envelope recipient filter */ 249*058561cbSjbeck mlfi_header, /* header filter */ 250*058561cbSjbeck mlfi_eoh, /* end of header */ 251*058561cbSjbeck mlfi_body, /* body block filter */ 252*058561cbSjbeck mlfi_eom, /* end of message */ 253*058561cbSjbeck mlfi_abort, /* message aborted */ 254*058561cbSjbeck mlfi_close, /* connection cleanup */ 255*058561cbSjbeck mlfi_unknown, /* unknown/unimplemented SMTP commands */ 256*058561cbSjbeck mlfi_data, /* DATA command filter */ 257*058561cbSjbeck mlfi_negotiate /* option negotation at connection startup */ 258*058561cbSjbeck }; 259*058561cbSjbeck 260*058561cbSjbeck int 261*058561cbSjbeck main(argc, argv) 262*058561cbSjbeck int argc; 263*058561cbSjbeck char *argv[]; 264*058561cbSjbeck { 265*058561cbSjbeck bool setconn; 266*058561cbSjbeck int c; 267*058561cbSjbeck 268*058561cbSjbeck setconn = false; 269*058561cbSjbeck 270*058561cbSjbeck /* Process command line options */ 271*058561cbSjbeck while ((c = getopt(argc, argv, "p:")) != -1) 272*058561cbSjbeck { 273*058561cbSjbeck switch (c) 274*058561cbSjbeck { 275*058561cbSjbeck case 'p': 276*058561cbSjbeck if (optarg == NULL || *optarg == '\0') 277*058561cbSjbeck { 278*058561cbSjbeck (void) fprintf(stderr, "Illegal conn: %s\n", 279*058561cbSjbeck optarg); 280*058561cbSjbeck exit(EX_USAGE); 281*058561cbSjbeck } 282*058561cbSjbeck (void) smfi_setconn(optarg); 283*058561cbSjbeck setconn = true; 284*058561cbSjbeck break; 285*058561cbSjbeck 286*058561cbSjbeck } 287*058561cbSjbeck } 288*058561cbSjbeck if (!setconn) 289*058561cbSjbeck { 290*058561cbSjbeck fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); 291*058561cbSjbeck exit(EX_USAGE); 292*058561cbSjbeck } 293*058561cbSjbeck if (smfi_register(smfilter) == MI_FAILURE) 294*058561cbSjbeck { 295*058561cbSjbeck fprintf(stderr, "smfi_register failed\n"); 296*058561cbSjbeck exit(EX_UNAVAILABLE); 297*058561cbSjbeck } 298*058561cbSjbeck return smfi_main(); 299*058561cbSjbeck } 300*058561cbSjbeck 301