smfi.c revision 98121
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 8.64 2002/04/30 22:22:02 msk 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*/
270
271static int
272myisenhsc(s, delim)
273	const char *s;
274	int delim;
275{
276	int l, h;
277
278	if (s == NULL)
279		return 0;
280	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
281		return 0;
282	h = 0;
283	l = 2;
284	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
285		++h;
286	if (h == 0 || s[l + h] != '.')
287		return 0;
288	l += h + 1;
289	h = 0;
290	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
291		++h;
292	if (h == 0 || s[l + h] != delim)
293		return 0;
294	return l + h;
295}
296
297/*
298**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
299**
300**	Parameters:
301**		ctx -- Opaque context structure
302**		rcode -- The three-digit (RFC 821) SMTP reply code.
303**		xcode -- The extended (RFC 2034) reply code.
304**		message -- The text part of the SMTP reply.
305**
306**	Returns:
307**		MI_SUCCESS/MI_FAILURE
308*/
309
310int
311smfi_setreply(ctx, rcode, xcode, message)
312	SMFICTX *ctx;
313	char *rcode;
314	char *xcode;
315	char *message;
316{
317	size_t len;
318	char *buf;
319
320	if (rcode == NULL || ctx == NULL)
321		return MI_FAILURE;
322
323	/* ### <sp> \0 */
324	len = strlen(rcode) + 2;
325	if (len != 5)
326		return MI_FAILURE;
327	if ((rcode[0] != '4' && rcode[0] != '5') ||
328	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
329	    !isascii(rcode[2]) || !isdigit(rcode[2]))
330		return MI_FAILURE;
331	if (xcode != NULL)
332	{
333		if (!myisenhsc(xcode, '\0'))
334			return MI_FAILURE;
335		len += strlen(xcode) + 1;
336	}
337	if (message != NULL)
338	{
339		size_t ml;
340
341		/* XXX check also for unprintable chars? */
342		if (strpbrk(message, "\r\n") != NULL)
343			return MI_FAILURE;
344		ml = strlen(message);
345		if (ml > MAXREPLYLEN)
346			return MI_FAILURE;
347		len += ml + 1;
348	}
349	buf = malloc(len);
350	if (buf == NULL)
351		return MI_FAILURE;		/* oops */
352	(void) sm_strlcpy(buf, rcode, len);
353	(void) sm_strlcat(buf, " ", len);
354	if (xcode != NULL)
355		(void) sm_strlcat(buf, xcode, len);
356	if (message != NULL)
357	{
358		if (xcode != NULL)
359			(void) sm_strlcat(buf, " ", len);
360		(void) sm_strlcat(buf, message, len);
361	}
362	if (ctx->ctx_reply != NULL)
363		free(ctx->ctx_reply);
364	ctx->ctx_reply = buf;
365	return MI_SUCCESS;
366}
367
368#if _FFR_MULTILINE
369/*
370**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
371**
372**	Parameters:
373**		ctx -- Opaque context structure
374**		rcode -- The three-digit (RFC 821) SMTP reply code.
375**		xcode -- The extended (RFC 2034) reply code.
376**		txt, ... -- The text part of the SMTP reply,
377**			MUST be terminated with NULL.
378**
379**	Returns:
380**		MI_SUCCESS/MI_FAILURE
381*/
382
383int
384#if SM_VA_STD
385smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
386#else /* SM_VA_STD */
387smfi_setmlreply(ctx, rcode, xcode, va_alist)
388	SMFICTX *ctx;
389	const char *rcode;
390	const char *xcode;
391	va_dcl
392#endif /* SM_VA_STD */
393{
394	size_t len;
395	size_t rlen;
396	int args;
397	char *buf, *txt;
398	const char *xc;
399	char repl[16];
400	SM_VA_LOCAL_DECL
401
402	if (rcode == NULL || ctx == NULL)
403		return MI_FAILURE;
404
405	/* ### <sp> */
406	len = strlen(rcode) + 1;
407	if (len != 4)
408		return MI_FAILURE;
409	if ((rcode[0] != '4' && rcode[0] != '5') ||
410	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
411	    !isascii(rcode[2]) || !isdigit(rcode[2]))
412		return MI_FAILURE;
413	if (xcode != NULL)
414	{
415		if (!myisenhsc(xcode, '\0'))
416			return MI_FAILURE;
417		xc = xcode;
418	}
419	else
420	{
421		if (rcode[0] == '4')
422			xc = "4.0.0";
423		else
424			xc = "5.0.0";
425	}
426
427	/* add trailing space */
428	len += strlen(xc) + 1;
429	rlen = len;
430	args = 0;
431	SM_VA_START(ap, xcode);
432	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
433	{
434		size_t tl;
435
436		tl = strlen(txt);
437		if (tl > MAXREPLYLEN)
438			break;
439
440		/* this text, reply codes, \r\n */
441		len += tl + 2 + rlen;
442		if (++args > MAXREPLIES)
443			break;
444
445		/* XXX check also for unprintable chars? */
446		if (strpbrk(txt, "\r\n") != NULL)
447			break;
448	}
449	SM_VA_END(ap);
450	if (txt != NULL)
451		return MI_FAILURE;
452
453	/* trailing '\0' */
454	++len;
455	buf = malloc(len);
456	if (buf == NULL)
457		return MI_FAILURE;		/* oops */
458	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
459	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
460			   xc, " ");
461	SM_VA_START(ap, xcode);
462	txt = SM_VA_ARG(ap, char *);
463	if (txt != NULL)
464	{
465		(void) sm_strlcat2(buf, " ", txt, len);
466		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
467		{
468			if (--args <= 1)
469				repl[3] = ' ';
470			(void) sm_strlcat2(buf, "\r\n", repl, len);
471			(void) sm_strlcat(buf, txt, len);
472		}
473	}
474	if (ctx->ctx_reply != NULL)
475		free(ctx->ctx_reply);
476	ctx->ctx_reply = buf;
477	SM_VA_END(ap);
478	return MI_SUCCESS;
479}
480#endif /* _FFR_MULTILINE */
481
482/*
483**  SMFI_SETPRIV -- set private data
484**
485**	Parameters:
486**		ctx -- Opaque context structure
487**		privatedata -- pointer to private data
488**
489**	Returns:
490**		MI_SUCCESS/MI_FAILURE
491*/
492
493int
494smfi_setpriv(ctx, privatedata)
495	SMFICTX *ctx;
496	void *privatedata;
497{
498	if (ctx == NULL)
499		return MI_FAILURE;
500	ctx->ctx_privdata = privatedata;
501	return MI_SUCCESS;
502}
503
504/*
505**  SMFI_GETPRIV -- get private data
506**
507**	Parameters:
508**		ctx -- Opaque context structure
509**
510**	Returns:
511**		pointer to private data
512*/
513
514void *
515smfi_getpriv(ctx)
516	SMFICTX *ctx;
517{
518	if (ctx == NULL)
519		return NULL;
520	return ctx->ctx_privdata;
521}
522
523/*
524**  SMFI_GETSYMVAL -- get the value of a macro
525**
526**	See explanation in mfapi.h about layout of the structures.
527**
528**	Parameters:
529**		ctx -- Opaque context structure
530**		symname -- name of macro
531**
532**	Returns:
533**		value of macro (NULL in case of failure)
534*/
535
536char *
537smfi_getsymval(ctx, symname)
538	SMFICTX *ctx;
539	char *symname;
540{
541	int i;
542	char **s;
543	char one[2];
544	char braces[4];
545
546	if (ctx == NULL || symname == NULL || *symname == '\0')
547		return NULL;
548
549	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
550	{
551		one[0] = symname[1];
552		one[1] = '\0';
553	}
554	else
555		one[0] = '\0';
556	if (strlen(symname) == 1)
557	{
558		braces[0] = '{';
559		braces[1] = *symname;
560		braces[2] = '}';
561		braces[3] = '\0';
562	}
563	else
564		braces[0] = '\0';
565
566	/* search backwards through the macro array */
567	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
568	{
569		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
570		    ctx->ctx_mac_buf[i] == NULL)
571			continue;
572		while (s != NULL && *s != NULL)
573		{
574			if (strcmp(*s, symname) == 0)
575				return *++s;
576			if (one[0] != '\0' && strcmp(*s, one) == 0)
577				return *++s;
578			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
579				return *++s;
580			++s;	/* skip over macro value */
581			++s;	/* points to next macro name */
582		}
583	}
584	return NULL;
585}
586
587#if _FFR_SMFI_PROGRESS
588/*
589**  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
590**		     timeouts during long milter-side operations
591**
592**	Parameters:
593**		ctx -- Opaque context structure
594**
595**	Return value:
596**		MI_SUCCESS/MI_FAILURE
597*/
598
599int
600smfi_progress(ctx)
601	SMFICTX *ctx;
602{
603	struct timeval timeout;
604
605	if (ctx == NULL)
606		return MI_FAILURE;
607
608	timeout.tv_sec = ctx->ctx_timeout;
609	timeout.tv_usec = 0;
610
611	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
612}
613#endif /* _FFR_SMFI_PROGRESS */
614