smfi.c revision 132943
164562Sgshapiro/*
2132943Sgshapiro *  Copyright (c) 1999-2004 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
464562Sgshapiro *
564562Sgshapiro * By using this file, you agree to the terms and conditions set
664562Sgshapiro * forth in the LICENSE file which can be found at the top level of
764562Sgshapiro * the sendmail distribution.
864562Sgshapiro *
964562Sgshapiro */
1064562Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12132943SgshapiroSM_RCSID("@(#)$Id: smfi.c,v 8.72 2004/05/05 00:07:21 msk Exp $")
1390792Sgshapiro#include <sm/varargs.h>
1464562Sgshapiro#include "libmilter.h"
1564562Sgshapiro
16132943Sgshapirostatic int smfi_header __P((SMFICTX *, int, int, char *, char *));
17132943Sgshapiro
1890792Sgshapiro/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
1990792Sgshapiro#define MAXREPLYLEN	980	/* max. length of a reply string */
2090792Sgshapiro#define MAXREPLIES	32	/* max. number of reply strings */
2190792Sgshapiro
2264562Sgshapiro/*
23132943Sgshapiro**  SMFI_HEADER -- send a header to the MTA
2464562Sgshapiro**
2564562Sgshapiro**	Parameters:
2664562Sgshapiro**		ctx -- Opaque context structure
27132943Sgshapiro**		cmd -- Header modification command
28132943Sgshapiro**		hdridx -- Header index
2964562Sgshapiro**		headerf -- Header field name
3064562Sgshapiro**		headerv -- Header field value
3164562Sgshapiro**
32132943Sgshapiro**
3364562Sgshapiro**	Returns:
3464562Sgshapiro**		MI_SUCCESS/MI_FAILURE
3564562Sgshapiro*/
3664562Sgshapiro
37132943Sgshapirostatic int
38132943Sgshapirosmfi_header(ctx, cmd, hdridx, headerf, headerv)
3964562Sgshapiro	SMFICTX *ctx;
40132943Sgshapiro	int cmd;
41132943Sgshapiro	int hdridx;
4264562Sgshapiro	char *headerf;
4364562Sgshapiro	char *headerv;
4464562Sgshapiro{
45132943Sgshapiro	size_t len, l1, l2, offset;
4664562Sgshapiro	int r;
47132943Sgshapiro	mi_int32 v;
4864562Sgshapiro	char *buf;
4964562Sgshapiro	struct timeval timeout;
5064562Sgshapiro
5164562Sgshapiro	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
5264562Sgshapiro		return MI_FAILURE;
5364562Sgshapiro	timeout.tv_sec = ctx->ctx_timeout;
5464562Sgshapiro	timeout.tv_usec = 0;
55132943Sgshapiro	l1 = strlen(headerf) + 1;
56132943Sgshapiro	l2 = strlen(headerv) + 1;
57132943Sgshapiro	len = l1 + l2;
58132943Sgshapiro	if (hdridx >= 0)
59132943Sgshapiro		len += MILTER_LEN_BYTES;
6064562Sgshapiro	buf = malloc(len);
6164562Sgshapiro	if (buf == NULL)
6264562Sgshapiro		return MI_FAILURE;
63132943Sgshapiro	offset = 0;
64132943Sgshapiro	if (hdridx >= 0)
65132943Sgshapiro	{
66132943Sgshapiro		v = htonl(hdridx);
67132943Sgshapiro		(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
68132943Sgshapiro		offset += MILTER_LEN_BYTES;
69132943Sgshapiro	}
70132943Sgshapiro	(void) memcpy(buf + offset, headerf, l1);
71132943Sgshapiro	(void) memcpy(buf + offset + l1, headerv, l2);
72132943Sgshapiro	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
7364562Sgshapiro	free(buf);
7464562Sgshapiro	return r;
7564562Sgshapiro}
7664562Sgshapiro
7764562Sgshapiro/*
78132943Sgshapiro**  SMFI_ADDHEADER -- send a new header to the MTA
79132943Sgshapiro**
80132943Sgshapiro**	Parameters:
81132943Sgshapiro**		ctx -- Opaque context structure
82132943Sgshapiro**		headerf -- Header field name
83132943Sgshapiro**		headerv -- Header field value
84132943Sgshapiro**
85132943Sgshapiro**	Returns:
86132943Sgshapiro**		MI_SUCCESS/MI_FAILURE
87132943Sgshapiro*/
88132943Sgshapiro
89132943Sgshapiroint
90132943Sgshapirosmfi_addheader(ctx, headerf, headerv)
91132943Sgshapiro	SMFICTX *ctx;
92132943Sgshapiro	char *headerf;
93132943Sgshapiro	char *headerv;
94132943Sgshapiro{
95132943Sgshapiro	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
96132943Sgshapiro		return MI_FAILURE;
97132943Sgshapiro
98132943Sgshapiro	return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
99132943Sgshapiro}
100132943Sgshapiro
101132943Sgshapiro/*
102132943Sgshapiro**  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
103132943Sgshapiro**
104132943Sgshapiro**	Parameters:
105132943Sgshapiro**		ctx -- Opaque context structure
106132943Sgshapiro**  		hdridx -- index into header list where insertion should occur
107132943Sgshapiro**		headerf -- Header field name
108132943Sgshapiro**		headerv -- Header field value
109132943Sgshapiro**
110132943Sgshapiro**	Returns:
111132943Sgshapiro**		MI_SUCCESS/MI_FAILURE
112132943Sgshapiro*/
113132943Sgshapiro
114132943Sgshapiroint
115132943Sgshapirosmfi_insheader(ctx, hdridx, headerf, headerv)
116132943Sgshapiro	SMFICTX *ctx;
117132943Sgshapiro	int hdridx;
118132943Sgshapiro	char *headerf;
119132943Sgshapiro	char *headerv;
120132943Sgshapiro{
121132943Sgshapiro	if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
122132943Sgshapiro		return MI_FAILURE;
123132943Sgshapiro
124132943Sgshapiro	return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
125132943Sgshapiro}
126132943Sgshapiro
127132943Sgshapiro/*
12864562Sgshapiro**  SMFI_CHGHEADER -- send a changed header to the MTA
12964562Sgshapiro**
13064562Sgshapiro**	Parameters:
13164562Sgshapiro**		ctx -- Opaque context structure
13264562Sgshapiro**		headerf -- Header field name
13364562Sgshapiro**		hdridx -- Header index value
13464562Sgshapiro**		headerv -- Header field value
13564562Sgshapiro**
13664562Sgshapiro**	Returns:
13764562Sgshapiro**		MI_SUCCESS/MI_FAILURE
13864562Sgshapiro*/
13964562Sgshapiro
14064562Sgshapiroint
14164562Sgshapirosmfi_chgheader(ctx, headerf, hdridx, headerv)
14264562Sgshapiro	SMFICTX *ctx;
14364562Sgshapiro	char *headerf;
14464562Sgshapiro	mi_int32 hdridx;
14564562Sgshapiro	char *headerv;
14664562Sgshapiro{
147132943Sgshapiro	if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
14864562Sgshapiro		return MI_FAILURE;
14964562Sgshapiro	if (headerv == NULL)
15064562Sgshapiro		headerv = "";
151132943Sgshapiro
152132943Sgshapiro	return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
15364562Sgshapiro}
15494334Sgshapiro
15590792Sgshapiro/*
15664562Sgshapiro**  SMFI_ADDRCPT -- send an additional recipient to the MTA
15764562Sgshapiro**
15864562Sgshapiro**	Parameters:
15964562Sgshapiro**		ctx -- Opaque context structure
16064562Sgshapiro**		rcpt -- recipient address
16164562Sgshapiro**
16264562Sgshapiro**	Returns:
16364562Sgshapiro**		MI_SUCCESS/MI_FAILURE
16464562Sgshapiro*/
16564562Sgshapiro
16664562Sgshapiroint
16764562Sgshapirosmfi_addrcpt(ctx, rcpt)
16864562Sgshapiro	SMFICTX *ctx;
16964562Sgshapiro	char *rcpt;
17064562Sgshapiro{
17164562Sgshapiro	size_t len;
17264562Sgshapiro	struct timeval timeout;
17364562Sgshapiro
17464562Sgshapiro	if (rcpt == NULL || *rcpt == '\0')
17564562Sgshapiro		return MI_FAILURE;
17664562Sgshapiro	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
17764562Sgshapiro		return MI_FAILURE;
17864562Sgshapiro	timeout.tv_sec = ctx->ctx_timeout;
17964562Sgshapiro	timeout.tv_usec = 0;
18064562Sgshapiro	len = strlen(rcpt) + 1;
18164562Sgshapiro	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
18264562Sgshapiro}
18394334Sgshapiro
18490792Sgshapiro/*
18564562Sgshapiro**  SMFI_DELRCPT -- send a recipient to be removed to the MTA
18664562Sgshapiro**
18764562Sgshapiro**	Parameters:
18864562Sgshapiro**		ctx -- Opaque context structure
18964562Sgshapiro**		rcpt -- recipient address
19064562Sgshapiro**
19164562Sgshapiro**	Returns:
19264562Sgshapiro**		MI_SUCCESS/MI_FAILURE
19364562Sgshapiro*/
19464562Sgshapiro
19564562Sgshapiroint
19664562Sgshapirosmfi_delrcpt(ctx, rcpt)
19764562Sgshapiro	SMFICTX *ctx;
19864562Sgshapiro	char *rcpt;
19964562Sgshapiro{
20064562Sgshapiro	size_t len;
20164562Sgshapiro	struct timeval timeout;
20264562Sgshapiro
20364562Sgshapiro	if (rcpt == NULL || *rcpt == '\0')
20464562Sgshapiro		return MI_FAILURE;
20564562Sgshapiro	if (!mi_sendok(ctx, SMFIF_DELRCPT))
20664562Sgshapiro		return MI_FAILURE;
20764562Sgshapiro	timeout.tv_sec = ctx->ctx_timeout;
20864562Sgshapiro	timeout.tv_usec = 0;
20964562Sgshapiro	len = strlen(rcpt) + 1;
21064562Sgshapiro	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
21164562Sgshapiro}
21294334Sgshapiro
21390792Sgshapiro/*
21464562Sgshapiro**  SMFI_REPLACEBODY -- send a body chunk to the MTA
21564562Sgshapiro**
21664562Sgshapiro**	Parameters:
21764562Sgshapiro**		ctx -- Opaque context structure
21864562Sgshapiro**		bodyp -- body chunk
21964562Sgshapiro**		bodylen -- length of body chunk
22064562Sgshapiro**
22164562Sgshapiro**	Returns:
22264562Sgshapiro**		MI_SUCCESS/MI_FAILURE
22364562Sgshapiro*/
22464562Sgshapiro
22564562Sgshapiroint
22664562Sgshapirosmfi_replacebody(ctx, bodyp, bodylen)
22764562Sgshapiro	SMFICTX *ctx;
22890792Sgshapiro	unsigned char *bodyp;
22964562Sgshapiro	int bodylen;
23064562Sgshapiro{
23164562Sgshapiro	int len, off, r;
23264562Sgshapiro	struct timeval timeout;
23364562Sgshapiro
23490792Sgshapiro	if (bodylen < 0 ||
23590792Sgshapiro	    (bodyp == NULL && bodylen > 0))
23664562Sgshapiro		return MI_FAILURE;
23764562Sgshapiro	if (!mi_sendok(ctx, SMFIF_CHGBODY))
23864562Sgshapiro		return MI_FAILURE;
23964562Sgshapiro	timeout.tv_sec = ctx->ctx_timeout;
24064562Sgshapiro	timeout.tv_usec = 0;
24164562Sgshapiro
24264562Sgshapiro	/* split body chunk if necessary */
24364562Sgshapiro	off = 0;
24464562Sgshapiro	while (bodylen > 0)
24564562Sgshapiro	{
24664562Sgshapiro		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
24764562Sgshapiro						       bodylen;
24864562Sgshapiro		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
24964562Sgshapiro				(char *) (bodyp + off), len)) != MI_SUCCESS)
25064562Sgshapiro			return r;
25164562Sgshapiro		off += len;
25264562Sgshapiro		bodylen -= len;
25364562Sgshapiro	}
25464562Sgshapiro	return MI_SUCCESS;
25564562Sgshapiro}
25694334Sgshapiro
25790792Sgshapiro/*
25890792Sgshapiro**  SMFI_QUARANTINE -- quarantine an envelope
25990792Sgshapiro**
26090792Sgshapiro**	Parameters:
26190792Sgshapiro**		ctx -- Opaque context structure
26290792Sgshapiro**		reason -- why?
26390792Sgshapiro**
26490792Sgshapiro**	Returns:
26590792Sgshapiro**		MI_SUCCESS/MI_FAILURE
26690792Sgshapiro*/
26790792Sgshapiro
26890792Sgshapiroint
26990792Sgshapirosmfi_quarantine(ctx, reason)
27090792Sgshapiro	SMFICTX *ctx;
27190792Sgshapiro	char *reason;
27290792Sgshapiro{
27390792Sgshapiro	size_t len;
27490792Sgshapiro	int r;
27590792Sgshapiro	char *buf;
27690792Sgshapiro	struct timeval timeout;
27790792Sgshapiro
27890792Sgshapiro	if (reason == NULL || *reason == '\0')
27990792Sgshapiro		return MI_FAILURE;
28090792Sgshapiro	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
28190792Sgshapiro		return MI_FAILURE;
28290792Sgshapiro	timeout.tv_sec = ctx->ctx_timeout;
28390792Sgshapiro	timeout.tv_usec = 0;
28490792Sgshapiro	len = strlen(reason) + 1;
28590792Sgshapiro	buf = malloc(len);
28690792Sgshapiro	if (buf == NULL)
28790792Sgshapiro		return MI_FAILURE;
28890792Sgshapiro	(void) memcpy(buf, reason, len);
28990792Sgshapiro	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
29090792Sgshapiro	free(buf);
29190792Sgshapiro	return r;
29290792Sgshapiro}
29390792Sgshapiro
29490792Sgshapiro/*
29564562Sgshapiro**  MYISENHSC -- check whether a string contains an enhanced status code
29664562Sgshapiro**
29764562Sgshapiro**	Parameters:
29864562Sgshapiro**		s -- string with possible enhanced status code.
29964562Sgshapiro**		delim -- delim for enhanced status code.
30064562Sgshapiro**
30164562Sgshapiro**	Returns:
30264562Sgshapiro**		0  -- no enhanced status code.
30364562Sgshapiro**		>4 -- length of enhanced status code.
30464562Sgshapiro**
30564562Sgshapiro**	Side Effects:
30664562Sgshapiro**		none.
30764562Sgshapiro*/
30898121Sgshapiro
30964562Sgshapirostatic int
31064562Sgshapiromyisenhsc(s, delim)
31164562Sgshapiro	const char *s;
31264562Sgshapiro	int delim;
31364562Sgshapiro{
31464562Sgshapiro	int l, h;
31564562Sgshapiro
31664562Sgshapiro	if (s == NULL)
31764562Sgshapiro		return 0;
31864562Sgshapiro	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
31964562Sgshapiro		return 0;
32064562Sgshapiro	h = 0;
32164562Sgshapiro	l = 2;
32264562Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
32364562Sgshapiro		++h;
32464562Sgshapiro	if (h == 0 || s[l + h] != '.')
32564562Sgshapiro		return 0;
32664562Sgshapiro	l += h + 1;
32764562Sgshapiro	h = 0;
32864562Sgshapiro	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
32964562Sgshapiro		++h;
33064562Sgshapiro	if (h == 0 || s[l + h] != delim)
33164562Sgshapiro		return 0;
33264562Sgshapiro	return l + h;
33364562Sgshapiro}
33490792Sgshapiro
33590792Sgshapiro/*
33664562Sgshapiro**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
33764562Sgshapiro**
33864562Sgshapiro**	Parameters:
33964562Sgshapiro**		ctx -- Opaque context structure
34064562Sgshapiro**		rcode -- The three-digit (RFC 821) SMTP reply code.
34164562Sgshapiro**		xcode -- The extended (RFC 2034) reply code.
34264562Sgshapiro**		message -- The text part of the SMTP reply.
34364562Sgshapiro**
34464562Sgshapiro**	Returns:
34564562Sgshapiro**		MI_SUCCESS/MI_FAILURE
34664562Sgshapiro*/
34764562Sgshapiro
34864562Sgshapiroint
34964562Sgshapirosmfi_setreply(ctx, rcode, xcode, message)
35064562Sgshapiro	SMFICTX *ctx;
35164562Sgshapiro	char *rcode;
35264562Sgshapiro	char *xcode;
35364562Sgshapiro	char *message;
35464562Sgshapiro{
35590792Sgshapiro	size_t len;
35664562Sgshapiro	char *buf;
35764562Sgshapiro
35864562Sgshapiro	if (rcode == NULL || ctx == NULL)
35964562Sgshapiro		return MI_FAILURE;
36090792Sgshapiro
36190792Sgshapiro	/* ### <sp> \0 */
36290792Sgshapiro	len = strlen(rcode) + 2;
36390792Sgshapiro	if (len != 5)
36464562Sgshapiro		return MI_FAILURE;
36564562Sgshapiro	if ((rcode[0] != '4' && rcode[0] != '5') ||
36664562Sgshapiro	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
36764562Sgshapiro	    !isascii(rcode[2]) || !isdigit(rcode[2]))
36864562Sgshapiro		return MI_FAILURE;
36990792Sgshapiro	if (xcode != NULL)
37090792Sgshapiro	{
37190792Sgshapiro		if (!myisenhsc(xcode, '\0'))
37290792Sgshapiro			return MI_FAILURE;
37390792Sgshapiro		len += strlen(xcode) + 1;
37490792Sgshapiro	}
37590792Sgshapiro	if (message != NULL)
37690792Sgshapiro	{
37790792Sgshapiro		size_t ml;
37890792Sgshapiro
37990792Sgshapiro		/* XXX check also for unprintable chars? */
38090792Sgshapiro		if (strpbrk(message, "\r\n") != NULL)
38190792Sgshapiro			return MI_FAILURE;
38290792Sgshapiro		ml = strlen(message);
38390792Sgshapiro		if (ml > MAXREPLYLEN)
38490792Sgshapiro			return MI_FAILURE;
38590792Sgshapiro		len += ml + 1;
38690792Sgshapiro	}
38790792Sgshapiro	buf = malloc(len);
38890792Sgshapiro	if (buf == NULL)
38990792Sgshapiro		return MI_FAILURE;		/* oops */
39090792Sgshapiro	(void) sm_strlcpy(buf, rcode, len);
39190792Sgshapiro	(void) sm_strlcat(buf, " ", len);
39290792Sgshapiro	if (xcode != NULL)
39390792Sgshapiro		(void) sm_strlcat(buf, xcode, len);
39490792Sgshapiro	if (message != NULL)
39590792Sgshapiro	{
39690792Sgshapiro		if (xcode != NULL)
39790792Sgshapiro			(void) sm_strlcat(buf, " ", len);
39890792Sgshapiro		(void) sm_strlcat(buf, message, len);
39990792Sgshapiro	}
40090792Sgshapiro	if (ctx->ctx_reply != NULL)
40190792Sgshapiro		free(ctx->ctx_reply);
40290792Sgshapiro	ctx->ctx_reply = buf;
40390792Sgshapiro	return MI_SUCCESS;
40490792Sgshapiro}
40590792Sgshapiro
40690792Sgshapiro/*
40790792Sgshapiro**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
40890792Sgshapiro**
40990792Sgshapiro**	Parameters:
41090792Sgshapiro**		ctx -- Opaque context structure
41190792Sgshapiro**		rcode -- The three-digit (RFC 821) SMTP reply code.
41290792Sgshapiro**		xcode -- The extended (RFC 2034) reply code.
41390792Sgshapiro**		txt, ... -- The text part of the SMTP reply,
41490792Sgshapiro**			MUST be terminated with NULL.
41590792Sgshapiro**
41690792Sgshapiro**	Returns:
41790792Sgshapiro**		MI_SUCCESS/MI_FAILURE
41890792Sgshapiro*/
41990792Sgshapiro
42090792Sgshapiroint
42190792Sgshapiro#if SM_VA_STD
42290792Sgshapirosmfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
42390792Sgshapiro#else /* SM_VA_STD */
42490792Sgshapirosmfi_setmlreply(ctx, rcode, xcode, va_alist)
42590792Sgshapiro	SMFICTX *ctx;
42690792Sgshapiro	const char *rcode;
42790792Sgshapiro	const char *xcode;
42890792Sgshapiro	va_dcl
42990792Sgshapiro#endif /* SM_VA_STD */
43090792Sgshapiro{
43190792Sgshapiro	size_t len;
43290792Sgshapiro	size_t rlen;
43390792Sgshapiro	int args;
43490792Sgshapiro	char *buf, *txt;
43590792Sgshapiro	const char *xc;
43690792Sgshapiro	char repl[16];
43790792Sgshapiro	SM_VA_LOCAL_DECL
43890792Sgshapiro
43990792Sgshapiro	if (rcode == NULL || ctx == NULL)
44064562Sgshapiro		return MI_FAILURE;
44190792Sgshapiro
44290792Sgshapiro	/* ### <sp> */
44390792Sgshapiro	len = strlen(rcode) + 1;
44490792Sgshapiro	if (len != 4)
44590792Sgshapiro		return MI_FAILURE;
44690792Sgshapiro	if ((rcode[0] != '4' && rcode[0] != '5') ||
44790792Sgshapiro	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
44890792Sgshapiro	    !isascii(rcode[2]) || !isdigit(rcode[2]))
44990792Sgshapiro		return MI_FAILURE;
45090792Sgshapiro	if (xcode != NULL)
45190792Sgshapiro	{
45290792Sgshapiro		if (!myisenhsc(xcode, '\0'))
45390792Sgshapiro			return MI_FAILURE;
45490792Sgshapiro		xc = xcode;
45590792Sgshapiro	}
45690792Sgshapiro	else
45790792Sgshapiro	{
45890792Sgshapiro		if (rcode[0] == '4')
45990792Sgshapiro			xc = "4.0.0";
46090792Sgshapiro		else
46190792Sgshapiro			xc = "5.0.0";
46290792Sgshapiro	}
46390792Sgshapiro
46490792Sgshapiro	/* add trailing space */
46590792Sgshapiro	len += strlen(xc) + 1;
46690792Sgshapiro	rlen = len;
46790792Sgshapiro	args = 0;
46890792Sgshapiro	SM_VA_START(ap, xcode);
46990792Sgshapiro	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
47090792Sgshapiro	{
47190792Sgshapiro		size_t tl;
47290792Sgshapiro
47390792Sgshapiro		tl = strlen(txt);
47490792Sgshapiro		if (tl > MAXREPLYLEN)
47594334Sgshapiro			break;
47690792Sgshapiro
47790792Sgshapiro		/* this text, reply codes, \r\n */
47890792Sgshapiro		len += tl + 2 + rlen;
47990792Sgshapiro		if (++args > MAXREPLIES)
48094334Sgshapiro			break;
48190792Sgshapiro
48290792Sgshapiro		/* XXX check also for unprintable chars? */
48390792Sgshapiro		if (strpbrk(txt, "\r\n") != NULL)
48494334Sgshapiro			break;
48590792Sgshapiro	}
48690792Sgshapiro	SM_VA_END(ap);
48794334Sgshapiro	if (txt != NULL)
48894334Sgshapiro		return MI_FAILURE;
48990792Sgshapiro
49090792Sgshapiro	/* trailing '\0' */
49190792Sgshapiro	++len;
49264562Sgshapiro	buf = malloc(len);
49364562Sgshapiro	if (buf == NULL)
49464562Sgshapiro		return MI_FAILURE;		/* oops */
49590792Sgshapiro	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
49690792Sgshapiro	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
49790792Sgshapiro			   xc, " ");
49890792Sgshapiro	SM_VA_START(ap, xcode);
49990792Sgshapiro	txt = SM_VA_ARG(ap, char *);
50090792Sgshapiro	if (txt != NULL)
50190792Sgshapiro	{
50290792Sgshapiro		(void) sm_strlcat2(buf, " ", txt, len);
50390792Sgshapiro		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
50490792Sgshapiro		{
50590792Sgshapiro			if (--args <= 1)
50690792Sgshapiro				repl[3] = ' ';
50790792Sgshapiro			(void) sm_strlcat2(buf, "\r\n", repl, len);
50890792Sgshapiro			(void) sm_strlcat(buf, txt, len);
50990792Sgshapiro		}
51090792Sgshapiro	}
51164562Sgshapiro	if (ctx->ctx_reply != NULL)
51264562Sgshapiro		free(ctx->ctx_reply);
51364562Sgshapiro	ctx->ctx_reply = buf;
51490792Sgshapiro	SM_VA_END(ap);
51564562Sgshapiro	return MI_SUCCESS;
51664562Sgshapiro}
51790792Sgshapiro
51890792Sgshapiro/*
51964562Sgshapiro**  SMFI_SETPRIV -- set private data
52064562Sgshapiro**
52164562Sgshapiro**	Parameters:
52264562Sgshapiro**		ctx -- Opaque context structure
52364562Sgshapiro**		privatedata -- pointer to private data
52464562Sgshapiro**
52564562Sgshapiro**	Returns:
52664562Sgshapiro**		MI_SUCCESS/MI_FAILURE
52764562Sgshapiro*/
52864562Sgshapiro
52964562Sgshapiroint
53064562Sgshapirosmfi_setpriv(ctx, privatedata)
53164562Sgshapiro	SMFICTX *ctx;
53264562Sgshapiro	void *privatedata;
53364562Sgshapiro{
53464562Sgshapiro	if (ctx == NULL)
53564562Sgshapiro		return MI_FAILURE;
53664562Sgshapiro	ctx->ctx_privdata = privatedata;
53764562Sgshapiro	return MI_SUCCESS;
53864562Sgshapiro}
53994334Sgshapiro
54090792Sgshapiro/*
54164562Sgshapiro**  SMFI_GETPRIV -- get private data
54264562Sgshapiro**
54364562Sgshapiro**	Parameters:
54464562Sgshapiro**		ctx -- Opaque context structure
54564562Sgshapiro**
54664562Sgshapiro**	Returns:
54764562Sgshapiro**		pointer to private data
54864562Sgshapiro*/
54964562Sgshapiro
55064562Sgshapirovoid *
55164562Sgshapirosmfi_getpriv(ctx)
55264562Sgshapiro	SMFICTX *ctx;
55364562Sgshapiro{
55464562Sgshapiro	if (ctx == NULL)
55564562Sgshapiro		return NULL;
55664562Sgshapiro	return ctx->ctx_privdata;
55764562Sgshapiro}
55894334Sgshapiro
55990792Sgshapiro/*
56064562Sgshapiro**  SMFI_GETSYMVAL -- get the value of a macro
56164562Sgshapiro**
56264562Sgshapiro**	See explanation in mfapi.h about layout of the structures.
56364562Sgshapiro**
56464562Sgshapiro**	Parameters:
56564562Sgshapiro**		ctx -- Opaque context structure
56664562Sgshapiro**		symname -- name of macro
56764562Sgshapiro**
56864562Sgshapiro**	Returns:
56964562Sgshapiro**		value of macro (NULL in case of failure)
57064562Sgshapiro*/
57164562Sgshapiro
57264562Sgshapirochar *
57364562Sgshapirosmfi_getsymval(ctx, symname)
57464562Sgshapiro	SMFICTX *ctx;
57564562Sgshapiro	char *symname;
57664562Sgshapiro{
57764562Sgshapiro	int i;
57864562Sgshapiro	char **s;
57964562Sgshapiro	char one[2];
58064562Sgshapiro	char braces[4];
58164562Sgshapiro
58264562Sgshapiro	if (ctx == NULL || symname == NULL || *symname == '\0')
58364562Sgshapiro		return NULL;
58464562Sgshapiro
58564562Sgshapiro	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
58664562Sgshapiro	{
58764562Sgshapiro		one[0] = symname[1];
58864562Sgshapiro		one[1] = '\0';
58964562Sgshapiro	}
59064562Sgshapiro	else
59164562Sgshapiro		one[0] = '\0';
59264562Sgshapiro	if (strlen(symname) == 1)
59364562Sgshapiro	{
59464562Sgshapiro		braces[0] = '{';
59564562Sgshapiro		braces[1] = *symname;
59664562Sgshapiro		braces[2] = '}';
59764562Sgshapiro		braces[3] = '\0';
59864562Sgshapiro	}
59964562Sgshapiro	else
60064562Sgshapiro		braces[0] = '\0';
60164562Sgshapiro
60264562Sgshapiro	/* search backwards through the macro array */
60364562Sgshapiro	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
60464562Sgshapiro	{
60564562Sgshapiro		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
60664562Sgshapiro		    ctx->ctx_mac_buf[i] == NULL)
60764562Sgshapiro			continue;
60864562Sgshapiro		while (s != NULL && *s != NULL)
60964562Sgshapiro		{
61064562Sgshapiro			if (strcmp(*s, symname) == 0)
61164562Sgshapiro				return *++s;
61264562Sgshapiro			if (one[0] != '\0' && strcmp(*s, one) == 0)
61364562Sgshapiro				return *++s;
61464562Sgshapiro			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
61564562Sgshapiro				return *++s;
61664562Sgshapiro			++s;	/* skip over macro value */
61764562Sgshapiro			++s;	/* points to next macro name */
61864562Sgshapiro		}
61964562Sgshapiro	}
62064562Sgshapiro	return NULL;
62164562Sgshapiro}
62294334Sgshapiro
62394334Sgshapiro/*
62494334Sgshapiro**  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
62594334Sgshapiro**		     timeouts during long milter-side operations
62694334Sgshapiro**
62794334Sgshapiro**	Parameters:
62894334Sgshapiro**		ctx -- Opaque context structure
62994334Sgshapiro**
63094334Sgshapiro**	Return value:
63194334Sgshapiro**		MI_SUCCESS/MI_FAILURE
63294334Sgshapiro*/
63394334Sgshapiro
63494334Sgshapiroint
63594334Sgshapirosmfi_progress(ctx)
63694334Sgshapiro	SMFICTX *ctx;
63794334Sgshapiro{
63894334Sgshapiro	struct timeval timeout;
63994334Sgshapiro
64094334Sgshapiro	if (ctx == NULL)
64194334Sgshapiro		return MI_FAILURE;
64294334Sgshapiro
64394334Sgshapiro	timeout.tv_sec = ctx->ctx_timeout;
64494334Sgshapiro	timeout.tv_usec = 0;
64594334Sgshapiro
64694334Sgshapiro	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
64794334Sgshapiro}
648