1058561cbSjbeck /* 2058561cbSjbeck * Copyright (c) 2006 Sendmail, Inc. and its suppliers. 3058561cbSjbeck * All rights reserved. 4058561cbSjbeck * 5058561cbSjbeck * By using this file, you agree to the terms and conditions set 6058561cbSjbeck * forth in the LICENSE file which can be found at the top level of 7058561cbSjbeck * the sendmail distribution. 8058561cbSjbeck * 9*e9af4bc0SJohn Beck * $Id: example.c,v 8.4 2008/07/22 15:12:47 ca Exp $ 10058561cbSjbeck */ 11058561cbSjbeck 12058561cbSjbeck /* 13058561cbSjbeck ** A trivial example filter that logs all email to a file. 14058561cbSjbeck ** This milter also has some callbacks which it does not really use, 15058561cbSjbeck ** but they are defined to serve as an example. 16058561cbSjbeck */ 17058561cbSjbeck 18058561cbSjbeck #include <sys/types.h> 19058561cbSjbeck #include <stdio.h> 20058561cbSjbeck #include <stdlib.h> 21058561cbSjbeck #include <string.h> 22058561cbSjbeck #include <sysexits.h> 23058561cbSjbeck #include <unistd.h> 24058561cbSjbeck 25058561cbSjbeck #include "libmilter/mfapi.h" 26058561cbSjbeck #include "libmilter/mfdef.h" 27058561cbSjbeck 28058561cbSjbeck #ifndef true 29058561cbSjbeck # define false 0 30058561cbSjbeck # define true 1 31058561cbSjbeck #endif /* ! true */ 32058561cbSjbeck 33058561cbSjbeck struct mlfiPriv 34058561cbSjbeck { 35058561cbSjbeck char *mlfi_fname; 36058561cbSjbeck FILE *mlfi_fp; 37058561cbSjbeck }; 38058561cbSjbeck 39058561cbSjbeck #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 40058561cbSjbeck 41058561cbSjbeck static unsigned long mta_caps = 0; 42058561cbSjbeck 43058561cbSjbeck sfsistat 44058561cbSjbeck mlfi_cleanup(ctx, ok) 45058561cbSjbeck SMFICTX *ctx; 46058561cbSjbeck bool ok; 47058561cbSjbeck { 48058561cbSjbeck sfsistat rstat = SMFIS_CONTINUE; 49058561cbSjbeck struct mlfiPriv *priv = MLFIPRIV; 50058561cbSjbeck char *p; 51058561cbSjbeck char host[512]; 52058561cbSjbeck char hbuf[1024]; 53058561cbSjbeck 54058561cbSjbeck if (priv == NULL) 55058561cbSjbeck return rstat; 56058561cbSjbeck 57058561cbSjbeck /* close the archive file */ 58058561cbSjbeck if (priv->mlfi_fp != NULL && fclose(priv->mlfi_fp) == EOF) 59058561cbSjbeck { 60058561cbSjbeck /* failed; we have to wait until later */ 61058561cbSjbeck rstat = SMFIS_TEMPFAIL; 62058561cbSjbeck (void) unlink(priv->mlfi_fname); 63058561cbSjbeck } 64058561cbSjbeck else if (ok) 65058561cbSjbeck { 66058561cbSjbeck /* add a header to the message announcing our presence */ 67058561cbSjbeck if (gethostname(host, sizeof host) < 0) 68058561cbSjbeck snprintf(host, sizeof host, "localhost"); 69058561cbSjbeck p = strrchr(priv->mlfi_fname, '/'); 70058561cbSjbeck if (p == NULL) 71058561cbSjbeck p = priv->mlfi_fname; 72058561cbSjbeck else 73058561cbSjbeck p++; 74058561cbSjbeck snprintf(hbuf, sizeof hbuf, "%s@%s", p, host); 75058561cbSjbeck smfi_addheader(ctx, "X-Archived", hbuf); 76058561cbSjbeck } 77058561cbSjbeck else 78058561cbSjbeck { 79058561cbSjbeck /* message was aborted -- delete the archive file */ 80058561cbSjbeck (void) unlink(priv->mlfi_fname); 81058561cbSjbeck } 82058561cbSjbeck 83058561cbSjbeck /* release private memory */ 84058561cbSjbeck free(priv->mlfi_fname); 85058561cbSjbeck free(priv); 86058561cbSjbeck smfi_setpriv(ctx, NULL); 87058561cbSjbeck 88058561cbSjbeck /* return status */ 89058561cbSjbeck return rstat; 90058561cbSjbeck } 91058561cbSjbeck 92058561cbSjbeck 93058561cbSjbeck sfsistat 94058561cbSjbeck mlfi_envfrom(ctx, envfrom) 95058561cbSjbeck SMFICTX *ctx; 96058561cbSjbeck char **envfrom; 97058561cbSjbeck { 98058561cbSjbeck struct mlfiPriv *priv; 99058561cbSjbeck int fd = -1; 100058561cbSjbeck 101058561cbSjbeck /* allocate some private memory */ 102058561cbSjbeck priv = malloc(sizeof *priv); 103058561cbSjbeck if (priv == NULL) 104058561cbSjbeck { 105058561cbSjbeck /* can't accept this message right now */ 106058561cbSjbeck return SMFIS_TEMPFAIL; 107058561cbSjbeck } 108058561cbSjbeck memset(priv, '\0', sizeof *priv); 109058561cbSjbeck 110058561cbSjbeck /* open a file to store this message */ 111058561cbSjbeck priv->mlfi_fname = strdup("/tmp/msg.XXXXXXXX"); 112058561cbSjbeck if (priv->mlfi_fname == NULL) 113058561cbSjbeck { 114058561cbSjbeck free(priv); 115058561cbSjbeck return SMFIS_TEMPFAIL; 116058561cbSjbeck } 117058561cbSjbeck if ((fd = mkstemp(priv->mlfi_fname)) < 0 || 118058561cbSjbeck (priv->mlfi_fp = fdopen(fd, "w+")) == NULL) 119058561cbSjbeck { 120058561cbSjbeck if (fd >= 0) 121058561cbSjbeck (void) close(fd); 122058561cbSjbeck free(priv->mlfi_fname); 123058561cbSjbeck free(priv); 124058561cbSjbeck return SMFIS_TEMPFAIL; 125058561cbSjbeck } 126058561cbSjbeck 127058561cbSjbeck /* save the private data */ 128058561cbSjbeck smfi_setpriv(ctx, priv); 129058561cbSjbeck 130058561cbSjbeck /* continue processing */ 131058561cbSjbeck return SMFIS_CONTINUE; 132058561cbSjbeck } 133058561cbSjbeck 134058561cbSjbeck sfsistat 135058561cbSjbeck mlfi_header(ctx, headerf, headerv) 136058561cbSjbeck SMFICTX *ctx; 137058561cbSjbeck char *headerf; 138058561cbSjbeck char *headerv; 139058561cbSjbeck { 140058561cbSjbeck /* write the header to the log file */ 141058561cbSjbeck fprintf(MLFIPRIV->mlfi_fp, "%s: %s\r\n", headerf, headerv); 142058561cbSjbeck 143058561cbSjbeck /* continue processing */ 144058561cbSjbeck return ((mta_caps & SMFIP_NR_HDR) != 0) 145058561cbSjbeck ? SMFIS_NOREPLY : SMFIS_CONTINUE; 146058561cbSjbeck } 147058561cbSjbeck 148058561cbSjbeck sfsistat 149058561cbSjbeck mlfi_eoh(ctx) 150058561cbSjbeck SMFICTX *ctx; 151058561cbSjbeck { 152058561cbSjbeck /* output the blank line between the header and the body */ 153058561cbSjbeck fprintf(MLFIPRIV->mlfi_fp, "\r\n"); 154058561cbSjbeck 155058561cbSjbeck /* continue processing */ 156058561cbSjbeck return SMFIS_CONTINUE; 157058561cbSjbeck } 158058561cbSjbeck 159058561cbSjbeck sfsistat 160058561cbSjbeck mlfi_body(ctx, bodyp, bodylen) 161058561cbSjbeck SMFICTX *ctx; 162058561cbSjbeck u_char *bodyp; 163058561cbSjbeck size_t bodylen; 164058561cbSjbeck { 165058561cbSjbeck /* output body block to log file */ 166058561cbSjbeck if (fwrite(bodyp, bodylen, 1, MLFIPRIV->mlfi_fp) <= 0) 167058561cbSjbeck { 168058561cbSjbeck /* write failed */ 169058561cbSjbeck (void) mlfi_cleanup(ctx, false); 170058561cbSjbeck return SMFIS_TEMPFAIL; 171058561cbSjbeck } 172058561cbSjbeck 173058561cbSjbeck /* continue processing */ 174058561cbSjbeck return SMFIS_CONTINUE; 175058561cbSjbeck } 176058561cbSjbeck 177058561cbSjbeck sfsistat 178058561cbSjbeck mlfi_eom(ctx) 179058561cbSjbeck SMFICTX *ctx; 180058561cbSjbeck { 181058561cbSjbeck return mlfi_cleanup(ctx, true); 182058561cbSjbeck } 183058561cbSjbeck 184058561cbSjbeck sfsistat 185058561cbSjbeck mlfi_close(ctx) 186058561cbSjbeck SMFICTX *ctx; 187058561cbSjbeck { 188058561cbSjbeck return SMFIS_ACCEPT; 189058561cbSjbeck } 190058561cbSjbeck 191058561cbSjbeck sfsistat 192058561cbSjbeck mlfi_abort(ctx) 193058561cbSjbeck SMFICTX *ctx; 194058561cbSjbeck { 195058561cbSjbeck return mlfi_cleanup(ctx, false); 196058561cbSjbeck } 197058561cbSjbeck 198058561cbSjbeck sfsistat 199058561cbSjbeck mlfi_unknown(ctx, cmd) 200058561cbSjbeck SMFICTX *ctx; 201058561cbSjbeck char *cmd; 202058561cbSjbeck { 203058561cbSjbeck return SMFIS_CONTINUE; 204058561cbSjbeck } 205058561cbSjbeck 206058561cbSjbeck sfsistat 207058561cbSjbeck mlfi_data(ctx) 208058561cbSjbeck SMFICTX *ctx; 209058561cbSjbeck { 210058561cbSjbeck return SMFIS_CONTINUE; 211058561cbSjbeck } 212058561cbSjbeck 213058561cbSjbeck sfsistat 214058561cbSjbeck mlfi_negotiate(ctx, f0, f1, f2, f3, pf0, pf1, pf2, pf3) 215058561cbSjbeck SMFICTX *ctx; 216058561cbSjbeck unsigned long f0; 217058561cbSjbeck unsigned long f1; 218058561cbSjbeck unsigned long f2; 219058561cbSjbeck unsigned long f3; 220058561cbSjbeck unsigned long *pf0; 221058561cbSjbeck unsigned long *pf1; 222058561cbSjbeck unsigned long *pf2; 223058561cbSjbeck unsigned long *pf3; 224058561cbSjbeck { 225058561cbSjbeck /* milter actions: add headers */ 226058561cbSjbeck *pf0 = SMFIF_ADDHDRS; 227058561cbSjbeck 228058561cbSjbeck /* milter protocol steps: all but connect, HELO, RCPT */ 229058561cbSjbeck *pf1 = SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NORCPT; 230058561cbSjbeck mta_caps = f1; 231058561cbSjbeck if ((mta_caps & SMFIP_NR_HDR) != 0) 232058561cbSjbeck *pf1 |= SMFIP_NR_HDR; 233058561cbSjbeck *pf2 = 0; 234058561cbSjbeck *pf3 = 0; 235058561cbSjbeck return SMFIS_CONTINUE; 236058561cbSjbeck } 237058561cbSjbeck 238058561cbSjbeck struct smfiDesc smfilter = 239058561cbSjbeck { 240058561cbSjbeck "SampleFilter", /* filter name */ 241058561cbSjbeck SMFI_VERSION, /* version code -- do not change */ 242058561cbSjbeck SMFIF_ADDHDRS, /* flags */ 243058561cbSjbeck NULL, /* connection info filter */ 244058561cbSjbeck NULL, /* SMTP HELO command filter */ 245058561cbSjbeck mlfi_envfrom, /* envelope sender filter */ 246058561cbSjbeck NULL, /* envelope recipient filter */ 247058561cbSjbeck mlfi_header, /* header filter */ 248058561cbSjbeck mlfi_eoh, /* end of header */ 249058561cbSjbeck mlfi_body, /* body block filter */ 250058561cbSjbeck mlfi_eom, /* end of message */ 251058561cbSjbeck mlfi_abort, /* message aborted */ 252058561cbSjbeck mlfi_close, /* connection cleanup */ 253058561cbSjbeck mlfi_unknown, /* unknown/unimplemented SMTP commands */ 254058561cbSjbeck mlfi_data, /* DATA command filter */ 255*e9af4bc0SJohn Beck mlfi_negotiate /* option negotiation at connection startup */ 256058561cbSjbeck }; 257058561cbSjbeck 258058561cbSjbeck int 259058561cbSjbeck main(argc, argv) 260058561cbSjbeck int argc; 261058561cbSjbeck char *argv[]; 262058561cbSjbeck { 263058561cbSjbeck bool setconn; 264058561cbSjbeck int c; 265058561cbSjbeck 266058561cbSjbeck setconn = false; 267058561cbSjbeck 268058561cbSjbeck /* Process command line options */ 269058561cbSjbeck while ((c = getopt(argc, argv, "p:")) != -1) 270058561cbSjbeck { 271058561cbSjbeck switch (c) 272058561cbSjbeck { 273058561cbSjbeck case 'p': 274058561cbSjbeck if (optarg == NULL || *optarg == '\0') 275058561cbSjbeck { 276058561cbSjbeck (void) fprintf(stderr, "Illegal conn: %s\n", 277058561cbSjbeck optarg); 278058561cbSjbeck exit(EX_USAGE); 279058561cbSjbeck } 280058561cbSjbeck (void) smfi_setconn(optarg); 281058561cbSjbeck setconn = true; 282058561cbSjbeck break; 283058561cbSjbeck 284058561cbSjbeck } 285058561cbSjbeck } 286058561cbSjbeck if (!setconn) 287058561cbSjbeck { 288058561cbSjbeck fprintf(stderr, "%s: Missing required -p argument\n", argv[0]); 289058561cbSjbeck exit(EX_USAGE); 290058561cbSjbeck } 291058561cbSjbeck if (smfi_register(smfilter) == MI_FAILURE) 292058561cbSjbeck { 293058561cbSjbeck fprintf(stderr, "smfi_register failed\n"); 294058561cbSjbeck exit(EX_UNAVAILABLE); 295058561cbSjbeck } 296058561cbSjbeck return smfi_main(); 297058561cbSjbeck } 298058561cbSjbeck 299