smfi.c revision 95154
1/*
2 *  Copyright (c) 1999-2002 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 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: smfi.c,v 1.1.1.3 2002/04/10 03:05:00 gshapiro Exp $")
13#include <sm/varargs.h>
14#include "libmilter.h"
15
16/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
17#define MAXREPLYLEN	980	/* max. length of a reply string */
18#define MAXREPLIES	32	/* max. number of reply strings */
19
20/*
21**  SMFI_ADDHEADER -- send a new header to the MTA
22**
23**	Parameters:
24**		ctx -- Opaque context structure
25**		headerf -- Header field name
26**		headerv -- Header field value
27**
28**	Returns:
29**		MI_SUCCESS/MI_FAILURE
30*/
31
32int
33smfi_addheader(ctx, headerf, headerv)
34	SMFICTX *ctx;
35	char *headerf;
36	char *headerv;
37{
38	/* do we want to copy the stuff or have a special mi_wr_cmd call? */
39	size_t len, l1, l2;
40	int r;
41	char *buf;
42	struct timeval timeout;
43
44	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
45		return MI_FAILURE;
46	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
47		return MI_FAILURE;
48	timeout.tv_sec = ctx->ctx_timeout;
49	timeout.tv_usec = 0;
50	l1 = strlen(headerf);
51	l2 = strlen(headerv);
52	len = l1 + l2 + 2;
53	buf = malloc(len);
54	if (buf == NULL)
55		return MI_FAILURE;
56	(void) memcpy(buf, headerf, l1 + 1);
57	(void) memcpy(buf + l1 + 1, headerv, l2 + 1);
58	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDHEADER, buf, len);
59	free(buf);
60	return r;
61}
62
63/*
64**  SMFI_CHGHEADER -- send a changed header to the MTA
65**
66**	Parameters:
67**		ctx -- Opaque context structure
68**		headerf -- Header field name
69**		hdridx -- Header index value
70**		headerv -- Header field value
71**
72**	Returns:
73**		MI_SUCCESS/MI_FAILURE
74*/
75
76int
77smfi_chgheader(ctx, headerf, hdridx, headerv)
78	SMFICTX *ctx;
79	char *headerf;
80	mi_int32 hdridx;
81	char *headerv;
82{
83	/* do we want to copy the stuff or have a special mi_wr_cmd call? */
84	size_t len, l1, l2;
85	int r;
86	mi_int32 v;
87	char *buf;
88	struct timeval timeout;
89
90	if (headerf == NULL || *headerf == '\0')
91		return MI_FAILURE;
92	if (hdridx < 0)
93		return MI_FAILURE;
94	if (!mi_sendok(ctx, SMFIF_CHGHDRS))
95		return MI_FAILURE;
96	timeout.tv_sec = ctx->ctx_timeout;
97	timeout.tv_usec = 0;
98	if (headerv == NULL)
99		headerv = "";
100	l1 = strlen(headerf);
101	l2 = strlen(headerv);
102	len = l1 + l2 + 2 + MILTER_LEN_BYTES;
103	buf = malloc(len);
104	if (buf == NULL)
105		return MI_FAILURE;
106	v = htonl(hdridx);
107	(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
108	(void) memcpy(buf + MILTER_LEN_BYTES, headerf, l1 + 1);
109	(void) memcpy(buf + MILTER_LEN_BYTES + l1 + 1, headerv, l2 + 1);
110	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_CHGHEADER, buf, len);
111	free(buf);
112	return r;
113}
114
115/*
116**  SMFI_ADDRCPT -- send an additional recipient to the MTA
117**
118**	Parameters:
119**		ctx -- Opaque context structure
120**		rcpt -- recipient address
121**
122**	Returns:
123**		MI_SUCCESS/MI_FAILURE
124*/
125
126int
127smfi_addrcpt(ctx, rcpt)
128	SMFICTX *ctx;
129	char *rcpt;
130{
131	size_t len;
132	struct timeval timeout;
133
134	if (rcpt == NULL || *rcpt == '\0')
135		return MI_FAILURE;
136	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
137		return MI_FAILURE;
138	timeout.tv_sec = ctx->ctx_timeout;
139	timeout.tv_usec = 0;
140	len = strlen(rcpt) + 1;
141	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
142}
143
144/*
145**  SMFI_DELRCPT -- send a recipient to be removed to the MTA
146**
147**	Parameters:
148**		ctx -- Opaque context structure
149**		rcpt -- recipient address
150**
151**	Returns:
152**		MI_SUCCESS/MI_FAILURE
153*/
154
155int
156smfi_delrcpt(ctx, rcpt)
157	SMFICTX *ctx;
158	char *rcpt;
159{
160	size_t len;
161	struct timeval timeout;
162
163	if (rcpt == NULL || *rcpt == '\0')
164		return MI_FAILURE;
165	if (!mi_sendok(ctx, SMFIF_DELRCPT))
166		return MI_FAILURE;
167	timeout.tv_sec = ctx->ctx_timeout;
168	timeout.tv_usec = 0;
169	len = strlen(rcpt) + 1;
170	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
171}
172
173/*
174**  SMFI_REPLACEBODY -- send a body chunk to the MTA
175**
176**	Parameters:
177**		ctx -- Opaque context structure
178**		bodyp -- body chunk
179**		bodylen -- length of body chunk
180**
181**	Returns:
182**		MI_SUCCESS/MI_FAILURE
183*/
184
185int
186smfi_replacebody(ctx, bodyp, bodylen)
187	SMFICTX *ctx;
188	unsigned char *bodyp;
189	int bodylen;
190{
191	int len, off, r;
192	struct timeval timeout;
193
194	if (bodylen < 0 ||
195	    (bodyp == NULL && bodylen > 0))
196		return MI_FAILURE;
197	if (!mi_sendok(ctx, SMFIF_CHGBODY))
198		return MI_FAILURE;
199	timeout.tv_sec = ctx->ctx_timeout;
200	timeout.tv_usec = 0;
201
202	/* split body chunk if necessary */
203	off = 0;
204	while (bodylen > 0)
205	{
206		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
207						       bodylen;
208		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
209				(char *) (bodyp + off), len)) != MI_SUCCESS)
210			return r;
211		off += len;
212		bodylen -= len;
213	}
214	return MI_SUCCESS;
215}
216
217#if _FFR_QUARANTINE
218/*
219**  SMFI_QUARANTINE -- quarantine an envelope
220**
221**	Parameters:
222**		ctx -- Opaque context structure
223**		reason -- why?
224**
225**	Returns:
226**		MI_SUCCESS/MI_FAILURE
227*/
228
229int
230smfi_quarantine(ctx, reason)
231	SMFICTX *ctx;
232	char *reason;
233{
234	size_t len;
235	int r;
236	char *buf;
237	struct timeval timeout;
238
239	if (reason == NULL || *reason == '\0')
240		return MI_FAILURE;
241	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
242		return MI_FAILURE;
243	timeout.tv_sec = ctx->ctx_timeout;
244	timeout.tv_usec = 0;
245	len = strlen(reason) + 1;
246	buf = malloc(len);
247	if (buf == NULL)
248		return MI_FAILURE;
249	(void) memcpy(buf, reason, len);
250	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
251	free(buf);
252	return r;
253}
254#endif /* _FFR_QUARANTINE */
255
256/*
257**  MYISENHSC -- check whether a string contains an enhanced status code
258**
259**	Parameters:
260**		s -- string with possible enhanced status code.
261**		delim -- delim for enhanced status code.
262**
263**	Returns:
264**		0  -- no enhanced status code.
265**		>4 -- length of enhanced status code.
266**
267**	Side Effects:
268**		none.
269*/
270static int
271myisenhsc(s, delim)
272	const char *s;
273	int delim;
274{
275	int l, h;
276
277	if (s == NULL)
278		return 0;
279	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
280		return 0;
281	h = 0;
282	l = 2;
283	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
284		++h;
285	if (h == 0 || s[l + h] != '.')
286		return 0;
287	l += h + 1;
288	h = 0;
289	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
290		++h;
291	if (h == 0 || s[l + h] != delim)
292		return 0;
293	return l + h;
294}
295
296/*
297**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
298**
299**	Parameters:
300**		ctx -- Opaque context structure
301**		rcode -- The three-digit (RFC 821) SMTP reply code.
302**		xcode -- The extended (RFC 2034) reply code.
303**		message -- The text part of the SMTP reply.
304**
305**	Returns:
306**		MI_SUCCESS/MI_FAILURE
307*/
308
309int
310smfi_setreply(ctx, rcode, xcode, message)
311	SMFICTX *ctx;
312	char *rcode;
313	char *xcode;
314	char *message;
315{
316	size_t len;
317	char *buf;
318
319	if (rcode == NULL || ctx == NULL)
320		return MI_FAILURE;
321
322	/* ### <sp> \0 */
323	len = strlen(rcode) + 2;
324	if (len != 5)
325		return MI_FAILURE;
326	if ((rcode[0] != '4' && rcode[0] != '5') ||
327	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
328	    !isascii(rcode[2]) || !isdigit(rcode[2]))
329		return MI_FAILURE;
330	if (xcode != NULL)
331	{
332		if (!myisenhsc(xcode, '\0'))
333			return MI_FAILURE;
334		len += strlen(xcode) + 1;
335	}
336	if (message != NULL)
337	{
338		size_t ml;
339
340		/* XXX check also for unprintable chars? */
341		if (strpbrk(message, "\r\n") != NULL)
342			return MI_FAILURE;
343		ml = strlen(message);
344		if (ml > MAXREPLYLEN)
345			return MI_FAILURE;
346		len += ml + 1;
347	}
348	buf = malloc(len);
349	if (buf == NULL)
350		return MI_FAILURE;		/* oops */
351	(void) sm_strlcpy(buf, rcode, len);
352	(void) sm_strlcat(buf, " ", len);
353	if (xcode != NULL)
354		(void) sm_strlcat(buf, xcode, len);
355	if (message != NULL)
356	{
357		if (xcode != NULL)
358			(void) sm_strlcat(buf, " ", len);
359		(void) sm_strlcat(buf, message, len);
360	}
361	if (ctx->ctx_reply != NULL)
362		free(ctx->ctx_reply);
363	ctx->ctx_reply = buf;
364	return MI_SUCCESS;
365}
366
367#if _FFR_MULTILINE
368/*
369**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
370**
371**	Parameters:
372**		ctx -- Opaque context structure
373**		rcode -- The three-digit (RFC 821) SMTP reply code.
374**		xcode -- The extended (RFC 2034) reply code.
375**		txt, ... -- The text part of the SMTP reply,
376**			MUST be terminated with NULL.
377**
378**	Returns:
379**		MI_SUCCESS/MI_FAILURE
380*/
381
382int
383#if SM_VA_STD
384smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
385#else /* SM_VA_STD */
386smfi_setmlreply(ctx, rcode, xcode, va_alist)
387	SMFICTX *ctx;
388	const char *rcode;
389	const char *xcode;
390	va_dcl
391#endif /* SM_VA_STD */
392{
393	size_t len;
394	size_t rlen;
395	int args;
396	char *buf, *txt;
397	const char *xc;
398	char repl[16];
399	SM_VA_LOCAL_DECL
400
401	if (rcode == NULL || ctx == NULL)
402		return MI_FAILURE;
403
404	/* ### <sp> */
405	len = strlen(rcode) + 1;
406	if (len != 4)
407		return MI_FAILURE;
408	if ((rcode[0] != '4' && rcode[0] != '5') ||
409	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
410	    !isascii(rcode[2]) || !isdigit(rcode[2]))
411		return MI_FAILURE;
412	if (xcode != NULL)
413	{
414		if (!myisenhsc(xcode, '\0'))
415			return MI_FAILURE;
416		xc = xcode;
417	}
418	else
419	{
420		if (rcode[0] == '4')
421			xc = "4.0.0";
422		else
423			xc = "5.0.0";
424	}
425
426	/* add trailing space */
427	len += strlen(xc) + 1;
428	rlen = len;
429	args = 0;
430	SM_VA_START(ap, xcode);
431	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
432	{
433		size_t tl;
434
435		tl = strlen(txt);
436		if (tl > MAXREPLYLEN)
437			break;
438
439		/* this text, reply codes, \r\n */
440		len += tl + 2 + rlen;
441		if (++args > MAXREPLIES)
442			break;
443
444		/* XXX check also for unprintable chars? */
445		if (strpbrk(txt, "\r\n") != NULL)
446			break;
447	}
448	SM_VA_END(ap);
449	if (txt != NULL)
450		return MI_FAILURE;
451
452	/* trailing '\0' */
453	++len;
454	buf = malloc(len);
455	if (buf == NULL)
456		return MI_FAILURE;		/* oops */
457	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
458	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
459			   xc, " ");
460	SM_VA_START(ap, xcode);
461	txt = SM_VA_ARG(ap, char *);
462	if (txt != NULL)
463	{
464		(void) sm_strlcat2(buf, " ", txt, len);
465		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
466		{
467			if (--args <= 1)
468				repl[3] = ' ';
469			(void) sm_strlcat2(buf, "\r\n", repl, len);
470			(void) sm_strlcat(buf, txt, len);
471		}
472	}
473	if (ctx->ctx_reply != NULL)
474		free(ctx->ctx_reply);
475	ctx->ctx_reply = buf;
476	SM_VA_END(ap);
477	return MI_SUCCESS;
478}
479#endif /* _FFR_MULTILINE */
480
481/*
482**  SMFI_SETPRIV -- set private data
483**
484**	Parameters:
485**		ctx -- Opaque context structure
486**		privatedata -- pointer to private data
487**
488**	Returns:
489**		MI_SUCCESS/MI_FAILURE
490*/
491
492int
493smfi_setpriv(ctx, privatedata)
494	SMFICTX *ctx;
495	void *privatedata;
496{
497	if (ctx == NULL)
498		return MI_FAILURE;
499	ctx->ctx_privdata = privatedata;
500	return MI_SUCCESS;
501}
502
503/*
504**  SMFI_GETPRIV -- get private data
505**
506**	Parameters:
507**		ctx -- Opaque context structure
508**
509**	Returns:
510**		pointer to private data
511*/
512
513void *
514smfi_getpriv(ctx)
515	SMFICTX *ctx;
516{
517	if (ctx == NULL)
518		return NULL;
519	return ctx->ctx_privdata;
520}
521
522/*
523**  SMFI_GETSYMVAL -- get the value of a macro
524**
525**	See explanation in mfapi.h about layout of the structures.
526**
527**	Parameters:
528**		ctx -- Opaque context structure
529**		symname -- name of macro
530**
531**	Returns:
532**		value of macro (NULL in case of failure)
533*/
534
535char *
536smfi_getsymval(ctx, symname)
537	SMFICTX *ctx;
538	char *symname;
539{
540	int i;
541	char **s;
542	char one[2];
543	char braces[4];
544
545	if (ctx == NULL || symname == NULL || *symname == '\0')
546		return NULL;
547
548	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
549	{
550		one[0] = symname[1];
551		one[1] = '\0';
552	}
553	else
554		one[0] = '\0';
555	if (strlen(symname) == 1)
556	{
557		braces[0] = '{';
558		braces[1] = *symname;
559		braces[2] = '}';
560		braces[3] = '\0';
561	}
562	else
563		braces[0] = '\0';
564
565	/* search backwards through the macro array */
566	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
567	{
568		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
569		    ctx->ctx_mac_buf[i] == NULL)
570			continue;
571		while (s != NULL && *s != NULL)
572		{
573			if (strcmp(*s, symname) == 0)
574				return *++s;
575			if (one[0] != '\0' && strcmp(*s, one) == 0)
576				return *++s;
577			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
578				return *++s;
579			++s;	/* skip over macro value */
580			++s;	/* points to next macro name */
581		}
582	}
583	return NULL;
584}
585
586#if _FFR_SMFI_PROGRESS
587/*
588**  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
589**		     timeouts during long milter-side operations
590**
591**	Parameters:
592**		ctx -- Opaque context structure
593**
594**	Return value:
595**		MI_SUCCESS/MI_FAILURE
596*/
597
598int
599smfi_progress(ctx)
600	SMFICTX *ctx;
601{
602	struct timeval timeout;
603
604	if (ctx == NULL)
605		return MI_FAILURE;
606
607	timeout.tv_sec = ctx->ctx_timeout;
608	timeout.tv_usec = 0;
609
610	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
611}
612#endif /* _FFR_SMFI_PROGRESS */
613