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
mlfi_cleanup(ctx,ok)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
mlfi_envfrom(ctx,envfrom)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
mlfi_header(ctx,headerf,headerv)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
mlfi_eoh(ctx)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
mlfi_body(ctx,bodyp,bodylen)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
mlfi_eom(ctx)178058561cbSjbeck mlfi_eom(ctx)
179058561cbSjbeck SMFICTX *ctx;
180058561cbSjbeck {
181058561cbSjbeck return mlfi_cleanup(ctx, true);
182058561cbSjbeck }
183058561cbSjbeck
184058561cbSjbeck sfsistat
mlfi_close(ctx)185058561cbSjbeck mlfi_close(ctx)
186058561cbSjbeck SMFICTX *ctx;
187058561cbSjbeck {
188058561cbSjbeck return SMFIS_ACCEPT;
189058561cbSjbeck }
190058561cbSjbeck
191058561cbSjbeck sfsistat
mlfi_abort(ctx)192058561cbSjbeck mlfi_abort(ctx)
193058561cbSjbeck SMFICTX *ctx;
194058561cbSjbeck {
195058561cbSjbeck return mlfi_cleanup(ctx, false);
196058561cbSjbeck }
197058561cbSjbeck
198058561cbSjbeck sfsistat
mlfi_unknown(ctx,cmd)199058561cbSjbeck mlfi_unknown(ctx, cmd)
200058561cbSjbeck SMFICTX *ctx;
201058561cbSjbeck char *cmd;
202058561cbSjbeck {
203058561cbSjbeck return SMFIS_CONTINUE;
204058561cbSjbeck }
205058561cbSjbeck
206058561cbSjbeck sfsistat
mlfi_data(ctx)207058561cbSjbeck mlfi_data(ctx)
208058561cbSjbeck SMFICTX *ctx;
209058561cbSjbeck {
210058561cbSjbeck return SMFIS_CONTINUE;
211058561cbSjbeck }
212058561cbSjbeck
213058561cbSjbeck sfsistat
mlfi_negotiate(ctx,f0,f1,f2,f3,pf0,pf1,pf2,pf3)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
main(argc,argv)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