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