smfi.c revision 90792
1/*
2 *  Copyright (c) 1999-2001 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.57 2001/11/20 18:47:49 ca 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**  SMFI_ADDRCPT -- send an additional recipient to the MTA
116**
117**	Parameters:
118**		ctx -- Opaque context structure
119**		rcpt -- recipient address
120**
121**	Returns:
122**		MI_SUCCESS/MI_FAILURE
123*/
124
125int
126smfi_addrcpt(ctx, rcpt)
127	SMFICTX *ctx;
128	char *rcpt;
129{
130	size_t len;
131	struct timeval timeout;
132
133	if (rcpt == NULL || *rcpt == '\0')
134		return MI_FAILURE;
135	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
136		return MI_FAILURE;
137	timeout.tv_sec = ctx->ctx_timeout;
138	timeout.tv_usec = 0;
139	len = strlen(rcpt) + 1;
140	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
141}
142/*
143**  SMFI_DELRCPT -- send a recipient to be removed to the MTA
144**
145**	Parameters:
146**		ctx -- Opaque context structure
147**		rcpt -- recipient address
148**
149**	Returns:
150**		MI_SUCCESS/MI_FAILURE
151*/
152
153int
154smfi_delrcpt(ctx, rcpt)
155	SMFICTX *ctx;
156	char *rcpt;
157{
158	size_t len;
159	struct timeval timeout;
160
161	if (rcpt == NULL || *rcpt == '\0')
162		return MI_FAILURE;
163	if (!mi_sendok(ctx, SMFIF_DELRCPT))
164		return MI_FAILURE;
165	timeout.tv_sec = ctx->ctx_timeout;
166	timeout.tv_usec = 0;
167	len = strlen(rcpt) + 1;
168	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
169}
170/*
171**  SMFI_REPLACEBODY -- send a body chunk to the MTA
172**
173**	Parameters:
174**		ctx -- Opaque context structure
175**		bodyp -- body chunk
176**		bodylen -- length of body chunk
177**
178**	Returns:
179**		MI_SUCCESS/MI_FAILURE
180*/
181
182int
183smfi_replacebody(ctx, bodyp, bodylen)
184	SMFICTX *ctx;
185	unsigned char *bodyp;
186	int bodylen;
187{
188	int len, off, r;
189	struct timeval timeout;
190
191	if (bodylen < 0 ||
192	    (bodyp == NULL && bodylen > 0))
193		return MI_FAILURE;
194	if (!mi_sendok(ctx, SMFIF_CHGBODY))
195		return MI_FAILURE;
196	timeout.tv_sec = ctx->ctx_timeout;
197	timeout.tv_usec = 0;
198
199	/* split body chunk if necessary */
200	off = 0;
201	while (bodylen > 0)
202	{
203		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
204						       bodylen;
205		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
206				(char *) (bodyp + off), len)) != MI_SUCCESS)
207			return r;
208		off += len;
209		bodylen -= len;
210	}
211	return MI_SUCCESS;
212}
213#if _FFR_QUARANTINE
214/*
215**  SMFI_QUARANTINE -- quarantine an envelope
216**
217**	Parameters:
218**		ctx -- Opaque context structure
219**		reason -- why?
220**
221**	Returns:
222**		MI_SUCCESS/MI_FAILURE
223*/
224
225int
226smfi_quarantine(ctx, reason)
227	SMFICTX *ctx;
228	char *reason;
229{
230	size_t len;
231	int r;
232	char *buf;
233	struct timeval timeout;
234
235	if (reason == NULL || *reason == '\0')
236		return MI_FAILURE;
237	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
238		return MI_FAILURE;
239	timeout.tv_sec = ctx->ctx_timeout;
240	timeout.tv_usec = 0;
241	len = strlen(reason) + 1;
242	buf = malloc(len);
243	if (buf == NULL)
244		return MI_FAILURE;
245	(void) memcpy(buf, reason, len);
246	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
247	free(buf);
248	return r;
249}
250#endif /* _FFR_QUARANTINE */
251
252/*
253**  MYISENHSC -- check whether a string contains an enhanced status code
254**
255**	Parameters:
256**		s -- string with possible enhanced status code.
257**		delim -- delim for enhanced status code.
258**
259**	Returns:
260**		0  -- no enhanced status code.
261**		>4 -- length of enhanced status code.
262**
263**	Side Effects:
264**		none.
265*/
266static int
267myisenhsc(s, delim)
268	const char *s;
269	int delim;
270{
271	int l, h;
272
273	if (s == NULL)
274		return 0;
275	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
276		return 0;
277	h = 0;
278	l = 2;
279	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
280		++h;
281	if (h == 0 || s[l + h] != '.')
282		return 0;
283	l += h + 1;
284	h = 0;
285	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
286		++h;
287	if (h == 0 || s[l + h] != delim)
288		return 0;
289	return l + h;
290}
291
292/*
293**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
294**
295**	Parameters:
296**		ctx -- Opaque context structure
297**		rcode -- The three-digit (RFC 821) SMTP reply code.
298**		xcode -- The extended (RFC 2034) reply code.
299**		message -- The text part of the SMTP reply.
300**
301**	Returns:
302**		MI_SUCCESS/MI_FAILURE
303*/
304
305int
306smfi_setreply(ctx, rcode, xcode, message)
307	SMFICTX *ctx;
308	char *rcode;
309	char *xcode;
310	char *message;
311{
312	size_t len;
313	char *buf;
314
315	if (rcode == NULL || ctx == NULL)
316		return MI_FAILURE;
317
318	/* ### <sp> \0 */
319	len = strlen(rcode) + 2;
320	if (len != 5)
321		return MI_FAILURE;
322	if ((rcode[0] != '4' && rcode[0] != '5') ||
323	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
324	    !isascii(rcode[2]) || !isdigit(rcode[2]))
325		return MI_FAILURE;
326	if (xcode != NULL)
327	{
328		if (!myisenhsc(xcode, '\0'))
329			return MI_FAILURE;
330		len += strlen(xcode) + 1;
331	}
332	if (message != NULL)
333	{
334		size_t ml;
335
336		/* XXX check also for unprintable chars? */
337		if (strpbrk(message, "\r\n") != NULL)
338			return MI_FAILURE;
339		ml = strlen(message);
340		if (ml > MAXREPLYLEN)
341			return MI_FAILURE;
342		len += ml + 1;
343	}
344	buf = malloc(len);
345	if (buf == NULL)
346		return MI_FAILURE;		/* oops */
347	(void) sm_strlcpy(buf, rcode, len);
348	(void) sm_strlcat(buf, " ", len);
349	if (xcode != NULL)
350		(void) sm_strlcat(buf, xcode, len);
351	if (message != NULL)
352	{
353		if (xcode != NULL)
354			(void) sm_strlcat(buf, " ", len);
355		(void) sm_strlcat(buf, message, len);
356	}
357	if (ctx->ctx_reply != NULL)
358		free(ctx->ctx_reply);
359	ctx->ctx_reply = buf;
360	return MI_SUCCESS;
361}
362
363#if _FFR_MULTILINE
364/*
365**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
366**
367**	Parameters:
368**		ctx -- Opaque context structure
369**		rcode -- The three-digit (RFC 821) SMTP reply code.
370**		xcode -- The extended (RFC 2034) reply code.
371**		txt, ... -- The text part of the SMTP reply,
372**			MUST be terminated with NULL.
373**
374**	Returns:
375**		MI_SUCCESS/MI_FAILURE
376*/
377
378int
379#if SM_VA_STD
380smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
381#else /* SM_VA_STD */
382smfi_setmlreply(ctx, rcode, xcode, va_alist)
383	SMFICTX *ctx;
384	const char *rcode;
385	const char *xcode;
386	va_dcl
387#endif /* SM_VA_STD */
388{
389	size_t len;
390	size_t rlen;
391	int args;
392	char *buf, *txt;
393	const char *xc;
394	char repl[16];
395	SM_VA_LOCAL_DECL
396
397	if (rcode == NULL || ctx == NULL)
398		return MI_FAILURE;
399
400	/* ### <sp> */
401	len = strlen(rcode) + 1;
402	if (len != 4)
403		return MI_FAILURE;
404	if ((rcode[0] != '4' && rcode[0] != '5') ||
405	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
406	    !isascii(rcode[2]) || !isdigit(rcode[2]))
407		return MI_FAILURE;
408	if (xcode != NULL)
409	{
410		if (!myisenhsc(xcode, '\0'))
411			return MI_FAILURE;
412		xc = xcode;
413	}
414	else
415	{
416		if (rcode[0] == '4')
417			xc = "4.0.0";
418		else
419			xc = "5.0.0";
420	}
421
422	/* add trailing space */
423	len += strlen(xc) + 1;
424	rlen = len;
425	args = 0;
426	SM_VA_START(ap, xcode);
427	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
428	{
429		size_t tl;
430
431		tl = strlen(txt);
432		if (tl > MAXREPLYLEN)
433			return MI_FAILURE;
434
435		/* this text, reply codes, \r\n */
436		len += tl + 2 + rlen;
437		if (++args > MAXREPLIES)
438			return MI_FAILURE;
439
440		/* XXX check also for unprintable chars? */
441		if (strpbrk(txt, "\r\n") != NULL)
442			return MI_FAILURE;
443	}
444	SM_VA_END(ap);
445
446	/* trailing '\0' */
447	++len;
448	buf = malloc(len);
449	if (buf == NULL)
450		return MI_FAILURE;		/* oops */
451	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
452	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
453			   xc, " ");
454	SM_VA_START(ap, xcode);
455	txt = SM_VA_ARG(ap, char *);
456	if (txt != NULL)
457	{
458		(void) sm_strlcat2(buf, " ", txt, len);
459		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
460		{
461			if (--args <= 1)
462				repl[3] = ' ';
463			(void) sm_strlcat2(buf, "\r\n", repl, len);
464			(void) sm_strlcat(buf, txt, len);
465		}
466	}
467	if (ctx->ctx_reply != NULL)
468		free(ctx->ctx_reply);
469	ctx->ctx_reply = buf;
470	SM_VA_END(ap);
471	return MI_SUCCESS;
472}
473#endif /* _FFR_MULTILINE */
474
475/*
476**  SMFI_SETPRIV -- set private data
477**
478**	Parameters:
479**		ctx -- Opaque context structure
480**		privatedata -- pointer to private data
481**
482**	Returns:
483**		MI_SUCCESS/MI_FAILURE
484*/
485
486int
487smfi_setpriv(ctx, privatedata)
488	SMFICTX *ctx;
489	void *privatedata;
490{
491	if (ctx == NULL)
492		return MI_FAILURE;
493	ctx->ctx_privdata = privatedata;
494	return MI_SUCCESS;
495}
496/*
497**  SMFI_GETPRIV -- get private data
498**
499**	Parameters:
500**		ctx -- Opaque context structure
501**
502**	Returns:
503**		pointer to private data
504*/
505
506void *
507smfi_getpriv(ctx)
508	SMFICTX *ctx;
509{
510	if (ctx == NULL)
511		return NULL;
512	return ctx->ctx_privdata;
513}
514/*
515**  SMFI_GETSYMVAL -- get the value of a macro
516**
517**	See explanation in mfapi.h about layout of the structures.
518**
519**	Parameters:
520**		ctx -- Opaque context structure
521**		symname -- name of macro
522**
523**	Returns:
524**		value of macro (NULL in case of failure)
525*/
526
527char *
528smfi_getsymval(ctx, symname)
529	SMFICTX *ctx;
530	char *symname;
531{
532	int i;
533	char **s;
534	char one[2];
535	char braces[4];
536
537	if (ctx == NULL || symname == NULL || *symname == '\0')
538		return NULL;
539
540	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
541	{
542		one[0] = symname[1];
543		one[1] = '\0';
544	}
545	else
546		one[0] = '\0';
547	if (strlen(symname) == 1)
548	{
549		braces[0] = '{';
550		braces[1] = *symname;
551		braces[2] = '}';
552		braces[3] = '\0';
553	}
554	else
555		braces[0] = '\0';
556
557	/* search backwards through the macro array */
558	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
559	{
560		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
561		    ctx->ctx_mac_buf[i] == NULL)
562			continue;
563		while (s != NULL && *s != NULL)
564		{
565			if (strcmp(*s, symname) == 0)
566				return *++s;
567			if (one[0] != '\0' && strcmp(*s, one) == 0)
568				return *++s;
569			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
570				return *++s;
571			++s;	/* skip over macro value */
572			++s;	/* points to next macro name */
573		}
574	}
575	return NULL;
576}
577