xref: /illumos-gate/usr/src/cmd/sendmail/libmilter/example.c (revision 2ca5b6595b95478e6568b0e77c6c83c8a870867a)
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