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.4 2008/07/22 15:12:47 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
mlfi_cleanup(ctx,ok)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
mlfi_envfrom(ctx,envfrom)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
mlfi_header(ctx,headerf,headerv)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
mlfi_eoh(ctx)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
mlfi_body(ctx,bodyp,bodylen)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
mlfi_eom(ctx)178 mlfi_eom(ctx)
179 SMFICTX *ctx;
180 {
181 return mlfi_cleanup(ctx, true);
182 }
183
184 sfsistat
mlfi_close(ctx)185 mlfi_close(ctx)
186 SMFICTX *ctx;
187 {
188 return SMFIS_ACCEPT;
189 }
190
191 sfsistat
mlfi_abort(ctx)192 mlfi_abort(ctx)
193 SMFICTX *ctx;
194 {
195 return mlfi_cleanup(ctx, false);
196 }
197
198 sfsistat
mlfi_unknown(ctx,cmd)199 mlfi_unknown(ctx, cmd)
200 SMFICTX *ctx;
201 char *cmd;
202 {
203 return SMFIS_CONTINUE;
204 }
205
206 sfsistat
mlfi_data(ctx)207 mlfi_data(ctx)
208 SMFICTX *ctx;
209 {
210 return SMFIS_CONTINUE;
211 }
212
213 sfsistat
mlfi_negotiate(ctx,f0,f1,f2,f3,pf0,pf1,pf2,pf3)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 negotiation at connection startup */
256 };
257
258 int
main(argc,argv)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