1 /* 2 * Copyright (c) 2006 Sendmail, 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.3 2006/12/20 21:22:34 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 33 struct mlfiPriv 34 { 35 char *mlfi_fname; 36 FILE *mlfi_fp; 37 }; 38 39 #define MLFIPRIV ((struct mlfiPriv *) smfi_getpriv(ctx)) 40 41 static unsigned long mta_caps = 0; 42 43 sfsistat 44 mlfi_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 93 sfsistat 94 mlfi_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 134 sfsistat 135 mlfi_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 148 sfsistat 149 mlfi_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 159 sfsistat 160 mlfi_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 177 sfsistat 178 mlfi_eom(ctx) 179 SMFICTX *ctx; 180 { 181 return mlfi_cleanup(ctx, true); 182 } 183 184 sfsistat 185 mlfi_close(ctx) 186 SMFICTX *ctx; 187 { 188 return SMFIS_ACCEPT; 189 } 190 191 sfsistat 192 mlfi_abort(ctx) 193 SMFICTX *ctx; 194 { 195 return mlfi_cleanup(ctx, false); 196 } 197 198 sfsistat 199 mlfi_unknown(ctx, cmd) 200 SMFICTX *ctx; 201 char *cmd; 202 { 203 return SMFIS_CONTINUE; 204 } 205 206 sfsistat 207 mlfi_data(ctx) 208 SMFICTX *ctx; 209 { 210 return SMFIS_CONTINUE; 211 } 212 213 sfsistat 214 mlfi_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 238 struct 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 negotation at connection startup */ 256 }; 257 258 int 259 main(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