xref: /onnv-gate/usr/src/cmd/sendmail/libmilter/smfi.c (revision 4518:f3e8c15828bf)
10Sstevel@tonic-gate /*
2*4518Sjbeck  *  Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
30Sstevel@tonic-gate  *	All rights reserved.
40Sstevel@tonic-gate  *
50Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
60Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
70Sstevel@tonic-gate  * the sendmail distribution.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  */
100Sstevel@tonic-gate 
110Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
120Sstevel@tonic-gate 
130Sstevel@tonic-gate #include <sm/gen.h>
14*4518Sjbeck SM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 ca Exp $")
150Sstevel@tonic-gate #include <sm/varargs.h>
160Sstevel@tonic-gate #include "libmilter.h"
170Sstevel@tonic-gate 
180Sstevel@tonic-gate static int smfi_header __P((SMFICTX *, int, int, char *, char *));
190Sstevel@tonic-gate static int myisenhsc __P((const char *, int));
200Sstevel@tonic-gate 
210Sstevel@tonic-gate /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
220Sstevel@tonic-gate #define MAXREPLYLEN	980	/* max. length of a reply string */
230Sstevel@tonic-gate #define MAXREPLIES	32	/* max. number of reply strings */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate /*
260Sstevel@tonic-gate **  SMFI_HEADER -- send a header to the MTA
270Sstevel@tonic-gate **
280Sstevel@tonic-gate **	Parameters:
290Sstevel@tonic-gate **		ctx -- Opaque context structure
300Sstevel@tonic-gate **		cmd -- Header modification command
310Sstevel@tonic-gate **		hdridx -- Header index
320Sstevel@tonic-gate **		headerf -- Header field name
330Sstevel@tonic-gate **		headerv -- Header field value
340Sstevel@tonic-gate **
350Sstevel@tonic-gate **	Returns:
360Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
370Sstevel@tonic-gate */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate static int
smfi_header(ctx,cmd,hdridx,headerf,headerv)400Sstevel@tonic-gate smfi_header(ctx, cmd, hdridx, headerf, headerv)
410Sstevel@tonic-gate 	SMFICTX *ctx;
420Sstevel@tonic-gate 	int cmd;
430Sstevel@tonic-gate 	int hdridx;
440Sstevel@tonic-gate 	char *headerf;
450Sstevel@tonic-gate 	char *headerv;
460Sstevel@tonic-gate {
470Sstevel@tonic-gate 	size_t len, l1, l2, offset;
480Sstevel@tonic-gate 	int r;
490Sstevel@tonic-gate 	mi_int32 v;
500Sstevel@tonic-gate 	char *buf;
510Sstevel@tonic-gate 	struct timeval timeout;
520Sstevel@tonic-gate 
530Sstevel@tonic-gate 	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
540Sstevel@tonic-gate 		return MI_FAILURE;
550Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
560Sstevel@tonic-gate 	timeout.tv_usec = 0;
570Sstevel@tonic-gate 	l1 = strlen(headerf) + 1;
580Sstevel@tonic-gate 	l2 = strlen(headerv) + 1;
590Sstevel@tonic-gate 	len = l1 + l2;
600Sstevel@tonic-gate 	if (hdridx >= 0)
610Sstevel@tonic-gate 		len += MILTER_LEN_BYTES;
620Sstevel@tonic-gate 	buf = malloc(len);
630Sstevel@tonic-gate 	if (buf == NULL)
640Sstevel@tonic-gate 		return MI_FAILURE;
650Sstevel@tonic-gate 	offset = 0;
660Sstevel@tonic-gate 	if (hdridx >= 0)
670Sstevel@tonic-gate 	{
680Sstevel@tonic-gate 		v = htonl(hdridx);
690Sstevel@tonic-gate 		(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
700Sstevel@tonic-gate 		offset += MILTER_LEN_BYTES;
710Sstevel@tonic-gate 	}
720Sstevel@tonic-gate 	(void) memcpy(buf + offset, headerf, l1);
730Sstevel@tonic-gate 	(void) memcpy(buf + offset + l1, headerv, l2);
740Sstevel@tonic-gate 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
750Sstevel@tonic-gate 	free(buf);
760Sstevel@tonic-gate 	return r;
770Sstevel@tonic-gate }
780Sstevel@tonic-gate 
790Sstevel@tonic-gate /*
800Sstevel@tonic-gate **  SMFI_ADDHEADER -- send a new header to the MTA
810Sstevel@tonic-gate **
820Sstevel@tonic-gate **	Parameters:
830Sstevel@tonic-gate **		ctx -- Opaque context structure
840Sstevel@tonic-gate **		headerf -- Header field name
850Sstevel@tonic-gate **		headerv -- Header field value
860Sstevel@tonic-gate **
870Sstevel@tonic-gate **	Returns:
880Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
890Sstevel@tonic-gate */
900Sstevel@tonic-gate 
910Sstevel@tonic-gate int
smfi_addheader(ctx,headerf,headerv)920Sstevel@tonic-gate smfi_addheader(ctx, headerf, headerv)
930Sstevel@tonic-gate 	SMFICTX *ctx;
940Sstevel@tonic-gate 	char *headerf;
950Sstevel@tonic-gate 	char *headerv;
960Sstevel@tonic-gate {
970Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
980Sstevel@tonic-gate 		return MI_FAILURE;
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate 	return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
1010Sstevel@tonic-gate }
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /*
1040Sstevel@tonic-gate **  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
1050Sstevel@tonic-gate **
1060Sstevel@tonic-gate **	Parameters:
1070Sstevel@tonic-gate **		ctx -- Opaque context structure
1080Sstevel@tonic-gate **  		hdridx -- index into header list where insertion should occur
1090Sstevel@tonic-gate **		headerf -- Header field name
1100Sstevel@tonic-gate **		headerv -- Header field value
1110Sstevel@tonic-gate **
1120Sstevel@tonic-gate **	Returns:
1130Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
1140Sstevel@tonic-gate */
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate int
smfi_insheader(ctx,hdridx,headerf,headerv)1170Sstevel@tonic-gate smfi_insheader(ctx, hdridx, headerf, headerv)
1180Sstevel@tonic-gate 	SMFICTX *ctx;
1190Sstevel@tonic-gate 	int hdridx;
1200Sstevel@tonic-gate 	char *headerf;
1210Sstevel@tonic-gate 	char *headerv;
1220Sstevel@tonic-gate {
1230Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
1240Sstevel@tonic-gate 		return MI_FAILURE;
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 	return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate 
1290Sstevel@tonic-gate /*
1300Sstevel@tonic-gate **  SMFI_CHGHEADER -- send a changed header to the MTA
1310Sstevel@tonic-gate **
1320Sstevel@tonic-gate **	Parameters:
1330Sstevel@tonic-gate **		ctx -- Opaque context structure
1340Sstevel@tonic-gate **		headerf -- Header field name
1350Sstevel@tonic-gate **		hdridx -- Header index value
1360Sstevel@tonic-gate **		headerv -- Header field value
1370Sstevel@tonic-gate **
1380Sstevel@tonic-gate **	Returns:
1390Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
1400Sstevel@tonic-gate */
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate int
smfi_chgheader(ctx,headerf,hdridx,headerv)1430Sstevel@tonic-gate smfi_chgheader(ctx, headerf, hdridx, headerv)
1440Sstevel@tonic-gate 	SMFICTX *ctx;
1450Sstevel@tonic-gate 	char *headerf;
1460Sstevel@tonic-gate 	mi_int32 hdridx;
1470Sstevel@tonic-gate 	char *headerv;
1480Sstevel@tonic-gate {
1490Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
1500Sstevel@tonic-gate 		return MI_FAILURE;
1510Sstevel@tonic-gate 	if (headerv == NULL)
1520Sstevel@tonic-gate 		headerv = "";
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
1550Sstevel@tonic-gate }
1560Sstevel@tonic-gate 
1573544Sjbeck #if 0
1583544Sjbeck /*
1593544Sjbeck **  BUF_CRT_SEND -- construct buffer to send from arguments
1603544Sjbeck **
1613544Sjbeck **	Parameters:
1623544Sjbeck **		ctx -- Opaque context structure
1633544Sjbeck **		cmd -- command
1643544Sjbeck **		arg0 -- first argument
1653544Sjbeck **		argv -- list of arguments (NULL terminated)
1663544Sjbeck **
1673544Sjbeck **	Returns:
1683544Sjbeck **		MI_SUCCESS/MI_FAILURE
1693544Sjbeck */
1703544Sjbeck 
1713544Sjbeck static int
1723544Sjbeck buf_crt_send __P((SMFICTX *, int cmd, char *, char **));
1733544Sjbeck 
1743544Sjbeck static int
1753544Sjbeck buf_crt_send(ctx, cmd, arg0, argv)
1763544Sjbeck 	SMFICTX *ctx;
1773544Sjbeck 	int cmd;
1783544Sjbeck 	char *arg0;
1793544Sjbeck 	char **argv;
1803544Sjbeck {
1813544Sjbeck 	size_t len, l0, l1, offset;
1823544Sjbeck 	int r;
1833544Sjbeck 	char *buf, *arg, **argvl;
1843544Sjbeck 	struct timeval timeout;
1853544Sjbeck 
1863544Sjbeck 	if (arg0 == NULL || *arg0 == '\0')
1873544Sjbeck 		return MI_FAILURE;
1883544Sjbeck 	timeout.tv_sec = ctx->ctx_timeout;
1893544Sjbeck 	timeout.tv_usec = 0;
1903544Sjbeck 	l0 = strlen(arg0) + 1;
1913544Sjbeck 	len = l0;
1923544Sjbeck 	argvl = argv;
1933544Sjbeck 	while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
1943544Sjbeck 	{
1953544Sjbeck 		l1 = strlen(arg) + 1;
1963544Sjbeck 		len += l1;
1973544Sjbeck 		SM_ASSERT(len > l1);
1983544Sjbeck 	}
1993544Sjbeck 
2003544Sjbeck 	buf = malloc(len);
2013544Sjbeck 	if (buf == NULL)
2023544Sjbeck 		return MI_FAILURE;
2033544Sjbeck 	(void) memcpy(buf, arg0, l0);
2043544Sjbeck 	offset = l0;
2053544Sjbeck 
2063544Sjbeck 	argvl = argv;
2073544Sjbeck 	while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
2083544Sjbeck 	{
2093544Sjbeck 		l1 = strlen(arg) + 1;
2103544Sjbeck 		SM_ASSERT(offset < len);
2113544Sjbeck 		SM_ASSERT(offset + l1 <= len);
2123544Sjbeck 		(void) memcpy(buf + offset, arg, l1);
2133544Sjbeck 		offset += l1;
2143544Sjbeck 		SM_ASSERT(offset > l1);
2153544Sjbeck 	}
2163544Sjbeck 
2173544Sjbeck 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
2183544Sjbeck 	free(buf);
2193544Sjbeck 	return r;
2203544Sjbeck }
2213544Sjbeck #endif /* 0 */
2223544Sjbeck 
2233544Sjbeck /*
2243544Sjbeck **  SEND2 -- construct buffer to send from arguments
2253544Sjbeck **
2263544Sjbeck **	Parameters:
2273544Sjbeck **		ctx -- Opaque context structure
2283544Sjbeck **		cmd -- command
2293544Sjbeck **		arg0 -- first argument
2303544Sjbeck **		argv -- list of arguments (NULL terminated)
2313544Sjbeck **
2323544Sjbeck **	Returns:
2333544Sjbeck **		MI_SUCCESS/MI_FAILURE
2343544Sjbeck */
2353544Sjbeck 
2363544Sjbeck static int
2373544Sjbeck send2 __P((SMFICTX *, int cmd, char *, char *));
2383544Sjbeck 
2393544Sjbeck static int
send2(ctx,cmd,arg0,arg1)2403544Sjbeck send2(ctx, cmd, arg0, arg1)
2413544Sjbeck 	SMFICTX *ctx;
2423544Sjbeck 	int cmd;
2433544Sjbeck 	char *arg0;
2443544Sjbeck 	char *arg1;
2453544Sjbeck {
2463544Sjbeck 	size_t len, l0, l1, offset;
2473544Sjbeck 	int r;
2483544Sjbeck 	char *buf;
2493544Sjbeck 	struct timeval timeout;
2503544Sjbeck 
2513544Sjbeck 	if (arg0 == NULL || *arg0 == '\0')
2523544Sjbeck 		return MI_FAILURE;
2533544Sjbeck 	timeout.tv_sec = ctx->ctx_timeout;
2543544Sjbeck 	timeout.tv_usec = 0;
2553544Sjbeck 	l0 = strlen(arg0) + 1;
2563544Sjbeck 	len = l0;
2573544Sjbeck 	if (arg1 != NULL)
2583544Sjbeck 	{
2593544Sjbeck 		l1 = strlen(arg1) + 1;
2603544Sjbeck 		len += l1;
2613544Sjbeck 		SM_ASSERT(len > l1);
2623544Sjbeck 	}
2633544Sjbeck 
2643544Sjbeck 	buf = malloc(len);
2653544Sjbeck 	if (buf == NULL)
2663544Sjbeck 		return MI_FAILURE;
2673544Sjbeck 	(void) memcpy(buf, arg0, l0);
2683544Sjbeck 	offset = l0;
2693544Sjbeck 
2703544Sjbeck 	if (arg1 != NULL)
2713544Sjbeck 	{
2723544Sjbeck 		l1 = strlen(arg1) + 1;
2733544Sjbeck 		SM_ASSERT(offset < len);
2743544Sjbeck 		SM_ASSERT(offset + l1 <= len);
2753544Sjbeck 		(void) memcpy(buf + offset, arg1, l1);
2763544Sjbeck 		offset += l1;
2773544Sjbeck 		SM_ASSERT(offset > l1);
2783544Sjbeck 	}
2793544Sjbeck 
2803544Sjbeck 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
2813544Sjbeck 	free(buf);
2823544Sjbeck 	return r;
2833544Sjbeck }
2843544Sjbeck 
2853544Sjbeck /*
2863544Sjbeck **  SMFI_CHGFROM -- change enveloper sender ("from") address
2873544Sjbeck **
2883544Sjbeck **	Parameters:
2893544Sjbeck **		ctx -- Opaque context structure
2903544Sjbeck **		from -- new envelope sender address ("MAIL From")
2913544Sjbeck **		args -- ESMTP arguments
2923544Sjbeck **
2933544Sjbeck **	Returns:
2943544Sjbeck **		MI_SUCCESS/MI_FAILURE
2953544Sjbeck */
2963544Sjbeck 
2973544Sjbeck int
smfi_chgfrom(ctx,from,args)2983544Sjbeck smfi_chgfrom(ctx, from, args)
2993544Sjbeck 	SMFICTX *ctx;
3003544Sjbeck 	char *from;
3013544Sjbeck 	char *args;
3023544Sjbeck {
3033544Sjbeck 	if (from == NULL || *from == '\0')
3043544Sjbeck 		return MI_FAILURE;
3053544Sjbeck 	if (!mi_sendok(ctx, SMFIF_CHGFROM))
3063544Sjbeck 		return MI_FAILURE;
3073544Sjbeck 	return send2(ctx, SMFIR_CHGFROM, from, args);
3083544Sjbeck }
3093544Sjbeck 
3103544Sjbeck /*
3113544Sjbeck **  SMFI_SETSYMLIST -- set list of macros that the MTA should send.
3123544Sjbeck **
3133544Sjbeck **	Parameters:
3143544Sjbeck **		ctx -- Opaque context structure
3153544Sjbeck **		where -- SMTP stage
3163544Sjbeck **		macros -- list of macros
3173544Sjbeck **
3183544Sjbeck **	Returns:
3193544Sjbeck **		MI_SUCCESS/MI_FAILURE
3203544Sjbeck */
3213544Sjbeck 
3223544Sjbeck int
smfi_setsymlist(ctx,where,macros)3233544Sjbeck smfi_setsymlist(ctx, where, macros)
3243544Sjbeck 	SMFICTX *ctx;
3253544Sjbeck 	int where;
3263544Sjbeck 	char *macros;
3273544Sjbeck {
3283544Sjbeck 	SM_ASSERT(ctx != NULL);
3293544Sjbeck 
3303544Sjbeck 	if (macros == NULL || *macros == '\0')
3313544Sjbeck 		return MI_FAILURE;
3323544Sjbeck 	if (where < SMFIM_FIRST || where > SMFIM_LAST)
3333544Sjbeck 		return MI_FAILURE;
3343544Sjbeck 	if (where < 0 || where >= MAX_MACROS_ENTRIES)
3353544Sjbeck 		return MI_FAILURE;
3363544Sjbeck 
3373544Sjbeck 	if (ctx->ctx_mac_list[where] != NULL)
3383544Sjbeck 		return MI_FAILURE;
3393544Sjbeck 
3403544Sjbeck 	ctx->ctx_mac_list[where] = strdup(macros);
3413544Sjbeck 	if (ctx->ctx_mac_list[where] == NULL)
3423544Sjbeck 		return MI_FAILURE;
3433544Sjbeck 
3443544Sjbeck 	return MI_SUCCESS;
3453544Sjbeck }
3463544Sjbeck 
3473544Sjbeck /*
3483544Sjbeck **  SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
3493544Sjbeck **
3503544Sjbeck **	Parameters:
3513544Sjbeck **		ctx -- Opaque context structure
3523544Sjbeck **		rcpt -- recipient address
3533544Sjbeck **		args -- ESMTP arguments
3543544Sjbeck **
3553544Sjbeck **	Returns:
3563544Sjbeck **		MI_SUCCESS/MI_FAILURE
3573544Sjbeck */
3583544Sjbeck 
3593544Sjbeck int
smfi_addrcpt_par(ctx,rcpt,args)3603544Sjbeck smfi_addrcpt_par(ctx, rcpt, args)
3613544Sjbeck 	SMFICTX *ctx;
3623544Sjbeck 	char *rcpt;
3633544Sjbeck 	char *args;
3643544Sjbeck {
3653544Sjbeck 	if (rcpt == NULL || *rcpt == '\0')
3663544Sjbeck 		return MI_FAILURE;
3673544Sjbeck 	if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
3683544Sjbeck 		return MI_FAILURE;
3693544Sjbeck 	return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
3703544Sjbeck }
3713544Sjbeck 
3720Sstevel@tonic-gate /*
3730Sstevel@tonic-gate **  SMFI_ADDRCPT -- send an additional recipient to the MTA
3740Sstevel@tonic-gate **
3750Sstevel@tonic-gate **	Parameters:
3760Sstevel@tonic-gate **		ctx -- Opaque context structure
3770Sstevel@tonic-gate **		rcpt -- recipient address
3780Sstevel@tonic-gate **
3790Sstevel@tonic-gate **	Returns:
3800Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
3810Sstevel@tonic-gate */
3820Sstevel@tonic-gate 
3830Sstevel@tonic-gate int
smfi_addrcpt(ctx,rcpt)3840Sstevel@tonic-gate smfi_addrcpt(ctx, rcpt)
3850Sstevel@tonic-gate 	SMFICTX *ctx;
3860Sstevel@tonic-gate 	char *rcpt;
3870Sstevel@tonic-gate {
3880Sstevel@tonic-gate 	size_t len;
3890Sstevel@tonic-gate 	struct timeval timeout;
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	if (rcpt == NULL || *rcpt == '\0')
3920Sstevel@tonic-gate 		return MI_FAILURE;
3930Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
3940Sstevel@tonic-gate 		return MI_FAILURE;
3950Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
3960Sstevel@tonic-gate 	timeout.tv_usec = 0;
3970Sstevel@tonic-gate 	len = strlen(rcpt) + 1;
3980Sstevel@tonic-gate 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate /*
4020Sstevel@tonic-gate **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
4030Sstevel@tonic-gate **
4040Sstevel@tonic-gate **	Parameters:
4050Sstevel@tonic-gate **		ctx -- Opaque context structure
4060Sstevel@tonic-gate **		rcpt -- recipient address
4070Sstevel@tonic-gate **
4080Sstevel@tonic-gate **	Returns:
4090Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
4100Sstevel@tonic-gate */
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate int
smfi_delrcpt(ctx,rcpt)4130Sstevel@tonic-gate smfi_delrcpt(ctx, rcpt)
4140Sstevel@tonic-gate 	SMFICTX *ctx;
4150Sstevel@tonic-gate 	char *rcpt;
4160Sstevel@tonic-gate {
4170Sstevel@tonic-gate 	size_t len;
4180Sstevel@tonic-gate 	struct timeval timeout;
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate 	if (rcpt == NULL || *rcpt == '\0')
4210Sstevel@tonic-gate 		return MI_FAILURE;
4220Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_DELRCPT))
4230Sstevel@tonic-gate 		return MI_FAILURE;
4240Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
4250Sstevel@tonic-gate 	timeout.tv_usec = 0;
4260Sstevel@tonic-gate 	len = strlen(rcpt) + 1;
4270Sstevel@tonic-gate 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
4280Sstevel@tonic-gate }
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate /*
4310Sstevel@tonic-gate **  SMFI_REPLACEBODY -- send a body chunk to the MTA
4320Sstevel@tonic-gate **
4330Sstevel@tonic-gate **	Parameters:
4340Sstevel@tonic-gate **		ctx -- Opaque context structure
4350Sstevel@tonic-gate **		bodyp -- body chunk
4360Sstevel@tonic-gate **		bodylen -- length of body chunk
4370Sstevel@tonic-gate **
4380Sstevel@tonic-gate **	Returns:
4390Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
4400Sstevel@tonic-gate */
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate int
smfi_replacebody(ctx,bodyp,bodylen)4430Sstevel@tonic-gate smfi_replacebody(ctx, bodyp, bodylen)
4440Sstevel@tonic-gate 	SMFICTX *ctx;
4450Sstevel@tonic-gate 	unsigned char *bodyp;
4460Sstevel@tonic-gate 	int bodylen;
4470Sstevel@tonic-gate {
4480Sstevel@tonic-gate 	int len, off, r;
4490Sstevel@tonic-gate 	struct timeval timeout;
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	if (bodylen < 0 ||
4520Sstevel@tonic-gate 	    (bodyp == NULL && bodylen > 0))
4530Sstevel@tonic-gate 		return MI_FAILURE;
4540Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_CHGBODY))
4550Sstevel@tonic-gate 		return MI_FAILURE;
4560Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
4570Sstevel@tonic-gate 	timeout.tv_usec = 0;
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	/* split body chunk if necessary */
4600Sstevel@tonic-gate 	off = 0;
461616Sjbeck 	do
4620Sstevel@tonic-gate 	{
4630Sstevel@tonic-gate 		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
4640Sstevel@tonic-gate 						       bodylen;
4650Sstevel@tonic-gate 		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
4660Sstevel@tonic-gate 				(char *) (bodyp + off), len)) != MI_SUCCESS)
4670Sstevel@tonic-gate 			return r;
4680Sstevel@tonic-gate 		off += len;
4690Sstevel@tonic-gate 		bodylen -= len;
470616Sjbeck 	} while (bodylen > 0);
4710Sstevel@tonic-gate 	return MI_SUCCESS;
4720Sstevel@tonic-gate }
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate /*
4750Sstevel@tonic-gate **  SMFI_QUARANTINE -- quarantine an envelope
4760Sstevel@tonic-gate **
4770Sstevel@tonic-gate **	Parameters:
4780Sstevel@tonic-gate **		ctx -- Opaque context structure
4790Sstevel@tonic-gate **		reason -- why?
4800Sstevel@tonic-gate **
4810Sstevel@tonic-gate **	Returns:
4820Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
4830Sstevel@tonic-gate */
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate int
smfi_quarantine(ctx,reason)4860Sstevel@tonic-gate smfi_quarantine(ctx, reason)
4870Sstevel@tonic-gate 	SMFICTX *ctx;
4880Sstevel@tonic-gate 	char *reason;
4890Sstevel@tonic-gate {
4900Sstevel@tonic-gate 	size_t len;
4910Sstevel@tonic-gate 	int r;
4920Sstevel@tonic-gate 	char *buf;
4930Sstevel@tonic-gate 	struct timeval timeout;
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	if (reason == NULL || *reason == '\0')
4960Sstevel@tonic-gate 		return MI_FAILURE;
4970Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
4980Sstevel@tonic-gate 		return MI_FAILURE;
4990Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
5000Sstevel@tonic-gate 	timeout.tv_usec = 0;
5010Sstevel@tonic-gate 	len = strlen(reason) + 1;
5020Sstevel@tonic-gate 	buf = malloc(len);
5030Sstevel@tonic-gate 	if (buf == NULL)
5040Sstevel@tonic-gate 		return MI_FAILURE;
5050Sstevel@tonic-gate 	(void) memcpy(buf, reason, len);
5060Sstevel@tonic-gate 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
5070Sstevel@tonic-gate 	free(buf);
5080Sstevel@tonic-gate 	return r;
5090Sstevel@tonic-gate }
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate /*
5120Sstevel@tonic-gate **  MYISENHSC -- check whether a string contains an enhanced status code
5130Sstevel@tonic-gate **
5140Sstevel@tonic-gate **	Parameters:
5150Sstevel@tonic-gate **		s -- string with possible enhanced status code.
5160Sstevel@tonic-gate **		delim -- delim for enhanced status code.
5170Sstevel@tonic-gate **
5180Sstevel@tonic-gate **	Returns:
5190Sstevel@tonic-gate **		0  -- no enhanced status code.
5200Sstevel@tonic-gate **		>4 -- length of enhanced status code.
5210Sstevel@tonic-gate **
5220Sstevel@tonic-gate **	Side Effects:
5230Sstevel@tonic-gate **		none.
5240Sstevel@tonic-gate */
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate static int
myisenhsc(s,delim)5270Sstevel@tonic-gate myisenhsc(s, delim)
5280Sstevel@tonic-gate 	const char *s;
5290Sstevel@tonic-gate 	int delim;
5300Sstevel@tonic-gate {
5310Sstevel@tonic-gate 	int l, h;
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	if (s == NULL)
5340Sstevel@tonic-gate 		return 0;
5350Sstevel@tonic-gate 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
5360Sstevel@tonic-gate 		return 0;
5370Sstevel@tonic-gate 	h = 0;
5380Sstevel@tonic-gate 	l = 2;
5390Sstevel@tonic-gate 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
5400Sstevel@tonic-gate 		++h;
5410Sstevel@tonic-gate 	if (h == 0 || s[l + h] != '.')
5420Sstevel@tonic-gate 		return 0;
5430Sstevel@tonic-gate 	l += h + 1;
5440Sstevel@tonic-gate 	h = 0;
5450Sstevel@tonic-gate 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
5460Sstevel@tonic-gate 		++h;
5470Sstevel@tonic-gate 	if (h == 0 || s[l + h] != delim)
5480Sstevel@tonic-gate 		return 0;
5490Sstevel@tonic-gate 	return l + h;
5500Sstevel@tonic-gate }
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate /*
5530Sstevel@tonic-gate **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
5540Sstevel@tonic-gate **
5550Sstevel@tonic-gate **	Parameters:
5560Sstevel@tonic-gate **		ctx -- Opaque context structure
5570Sstevel@tonic-gate **		rcode -- The three-digit (RFC 821) SMTP reply code.
5580Sstevel@tonic-gate **		xcode -- The extended (RFC 2034) reply code.
5590Sstevel@tonic-gate **		message -- The text part of the SMTP reply.
5600Sstevel@tonic-gate **
5610Sstevel@tonic-gate **	Returns:
5620Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
5630Sstevel@tonic-gate */
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate int
smfi_setreply(ctx,rcode,xcode,message)5660Sstevel@tonic-gate smfi_setreply(ctx, rcode, xcode, message)
5670Sstevel@tonic-gate 	SMFICTX *ctx;
5680Sstevel@tonic-gate 	char *rcode;
5690Sstevel@tonic-gate 	char *xcode;
5700Sstevel@tonic-gate 	char *message;
5710Sstevel@tonic-gate {
5720Sstevel@tonic-gate 	size_t len;
5730Sstevel@tonic-gate 	char *buf;
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	if (rcode == NULL || ctx == NULL)
5760Sstevel@tonic-gate 		return MI_FAILURE;
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate 	/* ### <sp> \0 */
5790Sstevel@tonic-gate 	len = strlen(rcode) + 2;
5800Sstevel@tonic-gate 	if (len != 5)
5810Sstevel@tonic-gate 		return MI_FAILURE;
5820Sstevel@tonic-gate 	if ((rcode[0] != '4' && rcode[0] != '5') ||
5830Sstevel@tonic-gate 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
5840Sstevel@tonic-gate 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
5850Sstevel@tonic-gate 		return MI_FAILURE;
5860Sstevel@tonic-gate 	if (xcode != NULL)
5870Sstevel@tonic-gate 	{
5880Sstevel@tonic-gate 		if (!myisenhsc(xcode, '\0'))
5890Sstevel@tonic-gate 			return MI_FAILURE;
5900Sstevel@tonic-gate 		len += strlen(xcode) + 1;
5910Sstevel@tonic-gate 	}
5920Sstevel@tonic-gate 	if (message != NULL)
5930Sstevel@tonic-gate 	{
5940Sstevel@tonic-gate 		size_t ml;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 		/* XXX check also for unprintable chars? */
5970Sstevel@tonic-gate 		if (strpbrk(message, "\r\n") != NULL)
5980Sstevel@tonic-gate 			return MI_FAILURE;
5990Sstevel@tonic-gate 		ml = strlen(message);
6000Sstevel@tonic-gate 		if (ml > MAXREPLYLEN)
6010Sstevel@tonic-gate 			return MI_FAILURE;
6020Sstevel@tonic-gate 		len += ml + 1;
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 	buf = malloc(len);
6050Sstevel@tonic-gate 	if (buf == NULL)
6060Sstevel@tonic-gate 		return MI_FAILURE;		/* oops */
6070Sstevel@tonic-gate 	(void) sm_strlcpy(buf, rcode, len);
6080Sstevel@tonic-gate 	(void) sm_strlcat(buf, " ", len);
6090Sstevel@tonic-gate 	if (xcode != NULL)
6100Sstevel@tonic-gate 		(void) sm_strlcat(buf, xcode, len);
6110Sstevel@tonic-gate 	if (message != NULL)
6120Sstevel@tonic-gate 	{
6130Sstevel@tonic-gate 		if (xcode != NULL)
6140Sstevel@tonic-gate 			(void) sm_strlcat(buf, " ", len);
6150Sstevel@tonic-gate 		(void) sm_strlcat(buf, message, len);
6160Sstevel@tonic-gate 	}
6170Sstevel@tonic-gate 	if (ctx->ctx_reply != NULL)
6180Sstevel@tonic-gate 		free(ctx->ctx_reply);
6190Sstevel@tonic-gate 	ctx->ctx_reply = buf;
6200Sstevel@tonic-gate 	return MI_SUCCESS;
6210Sstevel@tonic-gate }
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate /*
6240Sstevel@tonic-gate **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
6250Sstevel@tonic-gate **
6260Sstevel@tonic-gate **	Parameters:
6270Sstevel@tonic-gate **		ctx -- Opaque context structure
6280Sstevel@tonic-gate **		rcode -- The three-digit (RFC 821) SMTP reply code.
6290Sstevel@tonic-gate **		xcode -- The extended (RFC 2034) reply code.
6300Sstevel@tonic-gate **		txt, ... -- The text part of the SMTP reply,
6310Sstevel@tonic-gate **			MUST be terminated with NULL.
6320Sstevel@tonic-gate **
6330Sstevel@tonic-gate **	Returns:
6340Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
6350Sstevel@tonic-gate */
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate int
6380Sstevel@tonic-gate #if SM_VA_STD
smfi_setmlreply(SMFICTX * ctx,const char * rcode,const char * xcode,...)6390Sstevel@tonic-gate smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
6400Sstevel@tonic-gate #else /* SM_VA_STD */
6410Sstevel@tonic-gate smfi_setmlreply(ctx, rcode, xcode, va_alist)
6420Sstevel@tonic-gate 	SMFICTX *ctx;
6430Sstevel@tonic-gate 	const char *rcode;
6440Sstevel@tonic-gate 	const char *xcode;
6450Sstevel@tonic-gate 	va_dcl
6460Sstevel@tonic-gate #endif /* SM_VA_STD */
6470Sstevel@tonic-gate {
6480Sstevel@tonic-gate 	size_t len;
6490Sstevel@tonic-gate 	size_t rlen;
6500Sstevel@tonic-gate 	int args;
6510Sstevel@tonic-gate 	char *buf, *txt;
6520Sstevel@tonic-gate 	const char *xc;
6530Sstevel@tonic-gate 	char repl[16];
6540Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	if (rcode == NULL || ctx == NULL)
6570Sstevel@tonic-gate 		return MI_FAILURE;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	/* ### <sp> */
6600Sstevel@tonic-gate 	len = strlen(rcode) + 1;
6610Sstevel@tonic-gate 	if (len != 4)
6620Sstevel@tonic-gate 		return MI_FAILURE;
6630Sstevel@tonic-gate 	if ((rcode[0] != '4' && rcode[0] != '5') ||
6640Sstevel@tonic-gate 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
6650Sstevel@tonic-gate 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
6660Sstevel@tonic-gate 		return MI_FAILURE;
6670Sstevel@tonic-gate 	if (xcode != NULL)
6680Sstevel@tonic-gate 	{
6690Sstevel@tonic-gate 		if (!myisenhsc(xcode, '\0'))
6700Sstevel@tonic-gate 			return MI_FAILURE;
6710Sstevel@tonic-gate 		xc = xcode;
6720Sstevel@tonic-gate 	}
6730Sstevel@tonic-gate 	else
6740Sstevel@tonic-gate 	{
6750Sstevel@tonic-gate 		if (rcode[0] == '4')
6760Sstevel@tonic-gate 			xc = "4.0.0";
6770Sstevel@tonic-gate 		else
6780Sstevel@tonic-gate 			xc = "5.0.0";
6790Sstevel@tonic-gate 	}
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	/* add trailing space */
6820Sstevel@tonic-gate 	len += strlen(xc) + 1;
6830Sstevel@tonic-gate 	rlen = len;
6840Sstevel@tonic-gate 	args = 0;
6850Sstevel@tonic-gate 	SM_VA_START(ap, xcode);
6860Sstevel@tonic-gate 	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
6870Sstevel@tonic-gate 	{
6880Sstevel@tonic-gate 		size_t tl;
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 		tl = strlen(txt);
6910Sstevel@tonic-gate 		if (tl > MAXREPLYLEN)
6920Sstevel@tonic-gate 			break;
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 		/* this text, reply codes, \r\n */
6950Sstevel@tonic-gate 		len += tl + 2 + rlen;
6960Sstevel@tonic-gate 		if (++args > MAXREPLIES)
6970Sstevel@tonic-gate 			break;
6980Sstevel@tonic-gate 
6990Sstevel@tonic-gate 		/* XXX check also for unprintable chars? */
7000Sstevel@tonic-gate 		if (strpbrk(txt, "\r\n") != NULL)
7010Sstevel@tonic-gate 			break;
7020Sstevel@tonic-gate 	}
7030Sstevel@tonic-gate 	SM_VA_END(ap);
7040Sstevel@tonic-gate 	if (txt != NULL)
7050Sstevel@tonic-gate 		return MI_FAILURE;
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate 	/* trailing '\0' */
7080Sstevel@tonic-gate 	++len;
7090Sstevel@tonic-gate 	buf = malloc(len);
7100Sstevel@tonic-gate 	if (buf == NULL)
7110Sstevel@tonic-gate 		return MI_FAILURE;		/* oops */
7120Sstevel@tonic-gate 	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
7130Sstevel@tonic-gate 	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
7140Sstevel@tonic-gate 			   xc, " ");
7150Sstevel@tonic-gate 	SM_VA_START(ap, xcode);
7160Sstevel@tonic-gate 	txt = SM_VA_ARG(ap, char *);
7170Sstevel@tonic-gate 	if (txt != NULL)
7180Sstevel@tonic-gate 	{
7190Sstevel@tonic-gate 		(void) sm_strlcat2(buf, " ", txt, len);
7200Sstevel@tonic-gate 		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
7210Sstevel@tonic-gate 		{
7220Sstevel@tonic-gate 			if (--args <= 1)
7230Sstevel@tonic-gate 				repl[3] = ' ';
7240Sstevel@tonic-gate 			(void) sm_strlcat2(buf, "\r\n", repl, len);
7250Sstevel@tonic-gate 			(void) sm_strlcat(buf, txt, len);
7260Sstevel@tonic-gate 		}
7270Sstevel@tonic-gate 	}
7280Sstevel@tonic-gate 	if (ctx->ctx_reply != NULL)
7290Sstevel@tonic-gate 		free(ctx->ctx_reply);
7300Sstevel@tonic-gate 	ctx->ctx_reply = buf;
7310Sstevel@tonic-gate 	SM_VA_END(ap);
7320Sstevel@tonic-gate 	return MI_SUCCESS;
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate 
7350Sstevel@tonic-gate /*
7360Sstevel@tonic-gate **  SMFI_SETPRIV -- set private data
7370Sstevel@tonic-gate **
7380Sstevel@tonic-gate **	Parameters:
7390Sstevel@tonic-gate **		ctx -- Opaque context structure
7400Sstevel@tonic-gate **		privatedata -- pointer to private data
7410Sstevel@tonic-gate **
7420Sstevel@tonic-gate **	Returns:
7430Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
7440Sstevel@tonic-gate */
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate int
smfi_setpriv(ctx,privatedata)7470Sstevel@tonic-gate smfi_setpriv(ctx, privatedata)
7480Sstevel@tonic-gate 	SMFICTX *ctx;
7490Sstevel@tonic-gate 	void *privatedata;
7500Sstevel@tonic-gate {
7510Sstevel@tonic-gate 	if (ctx == NULL)
7520Sstevel@tonic-gate 		return MI_FAILURE;
7530Sstevel@tonic-gate 	ctx->ctx_privdata = privatedata;
7540Sstevel@tonic-gate 	return MI_SUCCESS;
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate /*
7580Sstevel@tonic-gate **  SMFI_GETPRIV -- get private data
7590Sstevel@tonic-gate **
7600Sstevel@tonic-gate **	Parameters:
7610Sstevel@tonic-gate **		ctx -- Opaque context structure
7620Sstevel@tonic-gate **
7630Sstevel@tonic-gate **	Returns:
7640Sstevel@tonic-gate **		pointer to private data
7650Sstevel@tonic-gate */
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate void *
smfi_getpriv(ctx)7680Sstevel@tonic-gate smfi_getpriv(ctx)
7690Sstevel@tonic-gate 	SMFICTX *ctx;
7700Sstevel@tonic-gate {
7710Sstevel@tonic-gate 	if (ctx == NULL)
7720Sstevel@tonic-gate 		return NULL;
7730Sstevel@tonic-gate 	return ctx->ctx_privdata;
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate /*
7770Sstevel@tonic-gate **  SMFI_GETSYMVAL -- get the value of a macro
7780Sstevel@tonic-gate **
7790Sstevel@tonic-gate **	See explanation in mfapi.h about layout of the structures.
7800Sstevel@tonic-gate **
7810Sstevel@tonic-gate **	Parameters:
7820Sstevel@tonic-gate **		ctx -- Opaque context structure
7830Sstevel@tonic-gate **		symname -- name of macro
7840Sstevel@tonic-gate **
7850Sstevel@tonic-gate **	Returns:
7860Sstevel@tonic-gate **		value of macro (NULL in case of failure)
7870Sstevel@tonic-gate */
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate char *
smfi_getsymval(ctx,symname)7900Sstevel@tonic-gate smfi_getsymval(ctx, symname)
7910Sstevel@tonic-gate 	SMFICTX *ctx;
7920Sstevel@tonic-gate 	char *symname;
7930Sstevel@tonic-gate {
7940Sstevel@tonic-gate 	int i;
7950Sstevel@tonic-gate 	char **s;
7960Sstevel@tonic-gate 	char one[2];
7970Sstevel@tonic-gate 	char braces[4];
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	if (ctx == NULL || symname == NULL || *symname == '\0')
8000Sstevel@tonic-gate 		return NULL;
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
8030Sstevel@tonic-gate 	{
8040Sstevel@tonic-gate 		one[0] = symname[1];
8050Sstevel@tonic-gate 		one[1] = '\0';
8060Sstevel@tonic-gate 	}
8070Sstevel@tonic-gate 	else
8080Sstevel@tonic-gate 		one[0] = '\0';
8090Sstevel@tonic-gate 	if (strlen(symname) == 1)
8100Sstevel@tonic-gate 	{
8110Sstevel@tonic-gate 		braces[0] = '{';
8120Sstevel@tonic-gate 		braces[1] = *symname;
8130Sstevel@tonic-gate 		braces[2] = '}';
8140Sstevel@tonic-gate 		braces[3] = '\0';
8150Sstevel@tonic-gate 	}
8160Sstevel@tonic-gate 	else
8170Sstevel@tonic-gate 		braces[0] = '\0';
8180Sstevel@tonic-gate 
8190Sstevel@tonic-gate 	/* search backwards through the macro array */
8200Sstevel@tonic-gate 	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
8210Sstevel@tonic-gate 	{
8220Sstevel@tonic-gate 		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
8230Sstevel@tonic-gate 		    ctx->ctx_mac_buf[i] == NULL)
8240Sstevel@tonic-gate 			continue;
8250Sstevel@tonic-gate 		while (s != NULL && *s != NULL)
8260Sstevel@tonic-gate 		{
8270Sstevel@tonic-gate 			if (strcmp(*s, symname) == 0)
8280Sstevel@tonic-gate 				return *++s;
8290Sstevel@tonic-gate 			if (one[0] != '\0' && strcmp(*s, one) == 0)
8300Sstevel@tonic-gate 				return *++s;
8310Sstevel@tonic-gate 			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
8320Sstevel@tonic-gate 				return *++s;
8330Sstevel@tonic-gate 			++s;	/* skip over macro value */
8340Sstevel@tonic-gate 			++s;	/* points to next macro name */
8350Sstevel@tonic-gate 		}
8360Sstevel@tonic-gate 	}
8370Sstevel@tonic-gate 	return NULL;
8380Sstevel@tonic-gate }
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate /*
8410Sstevel@tonic-gate **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
8420Sstevel@tonic-gate **		     timeouts during long milter-side operations
8430Sstevel@tonic-gate **
8440Sstevel@tonic-gate **	Parameters:
8450Sstevel@tonic-gate **		ctx -- Opaque context structure
8460Sstevel@tonic-gate **
8470Sstevel@tonic-gate **	Return value:
8480Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
8490Sstevel@tonic-gate */
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate int
smfi_progress(ctx)8520Sstevel@tonic-gate smfi_progress(ctx)
8530Sstevel@tonic-gate 	SMFICTX *ctx;
8540Sstevel@tonic-gate {
8550Sstevel@tonic-gate 	struct timeval timeout;
8560Sstevel@tonic-gate 
8570Sstevel@tonic-gate 	if (ctx == NULL)
8580Sstevel@tonic-gate 		return MI_FAILURE;
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
8610Sstevel@tonic-gate 	timeout.tv_usec = 0;
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
8640Sstevel@tonic-gate }
8653544Sjbeck 
8663544Sjbeck /*
8673544Sjbeck **  SMFI_VERSION -- return (runtime) version of libmilter
8683544Sjbeck **
8693544Sjbeck **	Parameters:
8703544Sjbeck **		major -- (pointer to) major version
8713544Sjbeck **		minor -- (pointer to) minor version
8723544Sjbeck **		patchlevel -- (pointer to) patchlevel version
8733544Sjbeck **
8743544Sjbeck **	Return value:
8753544Sjbeck **		MI_SUCCESS
8763544Sjbeck */
8773544Sjbeck 
8783544Sjbeck int
smfi_version(major,minor,patchlevel)8793544Sjbeck smfi_version(major, minor, patchlevel)
8803544Sjbeck 	unsigned int *major;
8813544Sjbeck 	unsigned int *minor;
8823544Sjbeck 	unsigned int *patchlevel;
8833544Sjbeck {
8843544Sjbeck 	if (major != NULL)
8853544Sjbeck 		*major = SM_LM_VRS_MAJOR(SMFI_VERSION);
8863544Sjbeck 	if (minor != NULL)
8873544Sjbeck 		*minor = SM_LM_VRS_MINOR(SMFI_VERSION);
8883544Sjbeck 	if (patchlevel != NULL)
889*4518Sjbeck 		*patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
8903544Sjbeck 	return MI_SUCCESS;
8913544Sjbeck }
892