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