17c478bd9Sstevel@tonic-gate /*
2*24472db6Sjbeck  *  Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
67c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
77c478bd9Sstevel@tonic-gate  * the sendmail distribution.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  */
107c478bd9Sstevel@tonic-gate 
117c478bd9Sstevel@tonic-gate #include <sm/gen.h>
12*24472db6Sjbeck SM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 ca Exp $")
137c478bd9Sstevel@tonic-gate #include <sm/varargs.h>
147c478bd9Sstevel@tonic-gate #include "libmilter.h"
157c478bd9Sstevel@tonic-gate 
167c478bd9Sstevel@tonic-gate static int smfi_header __P((SMFICTX *, int, int, char *, char *));
177c478bd9Sstevel@tonic-gate static int myisenhsc __P((const char *, int));
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate /* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
207c478bd9Sstevel@tonic-gate #define MAXREPLYLEN	980	/* max. length of a reply string */
217c478bd9Sstevel@tonic-gate #define MAXREPLIES	32	/* max. number of reply strings */
227c478bd9Sstevel@tonic-gate 
237c478bd9Sstevel@tonic-gate /*
247c478bd9Sstevel@tonic-gate **  SMFI_HEADER -- send a header to the MTA
257c478bd9Sstevel@tonic-gate **
267c478bd9Sstevel@tonic-gate **	Parameters:
277c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
287c478bd9Sstevel@tonic-gate **		cmd -- Header modification command
297c478bd9Sstevel@tonic-gate **		hdridx -- Header index
307c478bd9Sstevel@tonic-gate **		headerf -- Header field name
317c478bd9Sstevel@tonic-gate **		headerv -- Header field value
327c478bd9Sstevel@tonic-gate **
337c478bd9Sstevel@tonic-gate **	Returns:
347c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
357c478bd9Sstevel@tonic-gate */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate static int
smfi_header(ctx,cmd,hdridx,headerf,headerv)387c478bd9Sstevel@tonic-gate smfi_header(ctx, cmd, hdridx, headerf, headerv)
397c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
407c478bd9Sstevel@tonic-gate 	int cmd;
417c478bd9Sstevel@tonic-gate 	int hdridx;
427c478bd9Sstevel@tonic-gate 	char *headerf;
437c478bd9Sstevel@tonic-gate 	char *headerv;
447c478bd9Sstevel@tonic-gate {
457c478bd9Sstevel@tonic-gate 	size_t len, l1, l2, offset;
467c478bd9Sstevel@tonic-gate 	int r;
477c478bd9Sstevel@tonic-gate 	mi_int32 v;
487c478bd9Sstevel@tonic-gate 	char *buf;
497c478bd9Sstevel@tonic-gate 	struct timeval timeout;
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
527c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
537c478bd9Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
547c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
557c478bd9Sstevel@tonic-gate 	l1 = strlen(headerf) + 1;
567c478bd9Sstevel@tonic-gate 	l2 = strlen(headerv) + 1;
577c478bd9Sstevel@tonic-gate 	len = l1 + l2;
587c478bd9Sstevel@tonic-gate 	if (hdridx >= 0)
597c478bd9Sstevel@tonic-gate 		len += MILTER_LEN_BYTES;
607c478bd9Sstevel@tonic-gate 	buf = malloc(len);
617c478bd9Sstevel@tonic-gate 	if (buf == NULL)
627c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
637c478bd9Sstevel@tonic-gate 	offset = 0;
647c478bd9Sstevel@tonic-gate 	if (hdridx >= 0)
657c478bd9Sstevel@tonic-gate 	{
667c478bd9Sstevel@tonic-gate 		v = htonl(hdridx);
677c478bd9Sstevel@tonic-gate 		(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
687c478bd9Sstevel@tonic-gate 		offset += MILTER_LEN_BYTES;
697c478bd9Sstevel@tonic-gate 	}
707c478bd9Sstevel@tonic-gate 	(void) memcpy(buf + offset, headerf, l1);
717c478bd9Sstevel@tonic-gate 	(void) memcpy(buf + offset + l1, headerv, l2);
727c478bd9Sstevel@tonic-gate 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
737c478bd9Sstevel@tonic-gate 	free(buf);
747c478bd9Sstevel@tonic-gate 	return r;
757c478bd9Sstevel@tonic-gate }
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /*
787c478bd9Sstevel@tonic-gate **  SMFI_ADDHEADER -- send a new header to the MTA
797c478bd9Sstevel@tonic-gate **
807c478bd9Sstevel@tonic-gate **	Parameters:
817c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
827c478bd9Sstevel@tonic-gate **		headerf -- Header field name
837c478bd9Sstevel@tonic-gate **		headerv -- Header field value
847c478bd9Sstevel@tonic-gate **
857c478bd9Sstevel@tonic-gate **	Returns:
867c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
877c478bd9Sstevel@tonic-gate */
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate int
smfi_addheader(ctx,headerf,headerv)907c478bd9Sstevel@tonic-gate smfi_addheader(ctx, headerf, headerv)
917c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
927c478bd9Sstevel@tonic-gate 	char *headerf;
937c478bd9Sstevel@tonic-gate 	char *headerv;
947c478bd9Sstevel@tonic-gate {
957c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
967c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
997c478bd9Sstevel@tonic-gate }
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate **  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
1037c478bd9Sstevel@tonic-gate **
1047c478bd9Sstevel@tonic-gate **	Parameters:
1057c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
1067c478bd9Sstevel@tonic-gate **  		hdridx -- index into header list where insertion should occur
1077c478bd9Sstevel@tonic-gate **		headerf -- Header field name
1087c478bd9Sstevel@tonic-gate **		headerv -- Header field value
1097c478bd9Sstevel@tonic-gate **
1107c478bd9Sstevel@tonic-gate **	Returns:
1117c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
1127c478bd9Sstevel@tonic-gate */
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate int
smfi_insheader(ctx,hdridx,headerf,headerv)1157c478bd9Sstevel@tonic-gate smfi_insheader(ctx, hdridx, headerf, headerv)
1167c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
1177c478bd9Sstevel@tonic-gate 	int hdridx;
1187c478bd9Sstevel@tonic-gate 	char *headerf;
1197c478bd9Sstevel@tonic-gate 	char *headerv;
1207c478bd9Sstevel@tonic-gate {
1217c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
1227c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
1257c478bd9Sstevel@tonic-gate }
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate /*
1287c478bd9Sstevel@tonic-gate **  SMFI_CHGHEADER -- send a changed header to the MTA
1297c478bd9Sstevel@tonic-gate **
1307c478bd9Sstevel@tonic-gate **	Parameters:
1317c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
1327c478bd9Sstevel@tonic-gate **		headerf -- Header field name
1337c478bd9Sstevel@tonic-gate **		hdridx -- Header index value
1347c478bd9Sstevel@tonic-gate **		headerv -- Header field value
1357c478bd9Sstevel@tonic-gate **
1367c478bd9Sstevel@tonic-gate **	Returns:
1377c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
1387c478bd9Sstevel@tonic-gate */
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate int
smfi_chgheader(ctx,headerf,hdridx,headerv)1417c478bd9Sstevel@tonic-gate smfi_chgheader(ctx, headerf, hdridx, headerv)
1427c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
1437c478bd9Sstevel@tonic-gate 	char *headerf;
1447c478bd9Sstevel@tonic-gate 	mi_int32 hdridx;
1457c478bd9Sstevel@tonic-gate 	char *headerv;
1467c478bd9Sstevel@tonic-gate {
1477c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
1487c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
1497c478bd9Sstevel@tonic-gate 	if (headerv == NULL)
1507c478bd9Sstevel@tonic-gate 		headerv = "";
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
1537c478bd9Sstevel@tonic-gate }
1547c478bd9Sstevel@tonic-gate 
155058561cbSjbeck #if 0
156058561cbSjbeck /*
157058561cbSjbeck **  BUF_CRT_SEND -- construct buffer to send from arguments
158058561cbSjbeck **
159058561cbSjbeck **	Parameters:
160058561cbSjbeck **		ctx -- Opaque context structure
161058561cbSjbeck **		cmd -- command
162058561cbSjbeck **		arg0 -- first argument
163058561cbSjbeck **		argv -- list of arguments (NULL terminated)
164058561cbSjbeck **
165058561cbSjbeck **	Returns:
166058561cbSjbeck **		MI_SUCCESS/MI_FAILURE
167058561cbSjbeck */
168058561cbSjbeck 
169058561cbSjbeck static int
170058561cbSjbeck buf_crt_send __P((SMFICTX *, int cmd, char *, char **));
171058561cbSjbeck 
172058561cbSjbeck static int
173058561cbSjbeck buf_crt_send(ctx, cmd, arg0, argv)
174058561cbSjbeck 	SMFICTX *ctx;
175058561cbSjbeck 	int cmd;
176058561cbSjbeck 	char *arg0;
177058561cbSjbeck 	char **argv;
178058561cbSjbeck {
179058561cbSjbeck 	size_t len, l0, l1, offset;
180058561cbSjbeck 	int r;
181058561cbSjbeck 	char *buf, *arg, **argvl;
182058561cbSjbeck 	struct timeval timeout;
183058561cbSjbeck 
184058561cbSjbeck 	if (arg0 == NULL || *arg0 == '\0')
185058561cbSjbeck 		return MI_FAILURE;
186058561cbSjbeck 	timeout.tv_sec = ctx->ctx_timeout;
187058561cbSjbeck 	timeout.tv_usec = 0;
188058561cbSjbeck 	l0 = strlen(arg0) + 1;
189058561cbSjbeck 	len = l0;
190058561cbSjbeck 	argvl = argv;
191058561cbSjbeck 	while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
192058561cbSjbeck 	{
193058561cbSjbeck 		l1 = strlen(arg) + 1;
194058561cbSjbeck 		len += l1;
195058561cbSjbeck 		SM_ASSERT(len > l1);
196058561cbSjbeck 	}
197058561cbSjbeck 
198058561cbSjbeck 	buf = malloc(len);
199058561cbSjbeck 	if (buf == NULL)
200058561cbSjbeck 		return MI_FAILURE;
201058561cbSjbeck 	(void) memcpy(buf, arg0, l0);
202058561cbSjbeck 	offset = l0;
203058561cbSjbeck 
204058561cbSjbeck 	argvl = argv;
205058561cbSjbeck 	while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
206058561cbSjbeck 	{
207058561cbSjbeck 		l1 = strlen(arg) + 1;
208058561cbSjbeck 		SM_ASSERT(offset < len);
209058561cbSjbeck 		SM_ASSERT(offset + l1 <= len);
210058561cbSjbeck 		(void) memcpy(buf + offset, arg, l1);
211058561cbSjbeck 		offset += l1;
212058561cbSjbeck 		SM_ASSERT(offset > l1);
213058561cbSjbeck 	}
214058561cbSjbeck 
215058561cbSjbeck 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
216058561cbSjbeck 	free(buf);
217058561cbSjbeck 	return r;
218058561cbSjbeck }
219058561cbSjbeck #endif /* 0 */
220058561cbSjbeck 
221058561cbSjbeck /*
222058561cbSjbeck **  SEND2 -- construct buffer to send from arguments
223058561cbSjbeck **
224058561cbSjbeck **	Parameters:
225058561cbSjbeck **		ctx -- Opaque context structure
226058561cbSjbeck **		cmd -- command
227058561cbSjbeck **		arg0 -- first argument
228058561cbSjbeck **		argv -- list of arguments (NULL terminated)
229058561cbSjbeck **
230058561cbSjbeck **	Returns:
231058561cbSjbeck **		MI_SUCCESS/MI_FAILURE
232058561cbSjbeck */
233058561cbSjbeck 
234058561cbSjbeck static int
235058561cbSjbeck send2 __P((SMFICTX *, int cmd, char *, char *));
236058561cbSjbeck 
237058561cbSjbeck static int
send2(ctx,cmd,arg0,arg1)238058561cbSjbeck send2(ctx, cmd, arg0, arg1)
239058561cbSjbeck 	SMFICTX *ctx;
240058561cbSjbeck 	int cmd;
241058561cbSjbeck 	char *arg0;
242058561cbSjbeck 	char *arg1;
243058561cbSjbeck {
244058561cbSjbeck 	size_t len, l0, l1, offset;
245058561cbSjbeck 	int r;
246058561cbSjbeck 	char *buf;
247058561cbSjbeck 	struct timeval timeout;
248058561cbSjbeck 
249058561cbSjbeck 	if (arg0 == NULL || *arg0 == '\0')
250058561cbSjbeck 		return MI_FAILURE;
251058561cbSjbeck 	timeout.tv_sec = ctx->ctx_timeout;
252058561cbSjbeck 	timeout.tv_usec = 0;
253058561cbSjbeck 	l0 = strlen(arg0) + 1;
254058561cbSjbeck 	len = l0;
255058561cbSjbeck 	if (arg1 != NULL)
256058561cbSjbeck 	{
257058561cbSjbeck 		l1 = strlen(arg1) + 1;
258058561cbSjbeck 		len += l1;
259058561cbSjbeck 		SM_ASSERT(len > l1);
260058561cbSjbeck 	}
261058561cbSjbeck 
262058561cbSjbeck 	buf = malloc(len);
263058561cbSjbeck 	if (buf == NULL)
264058561cbSjbeck 		return MI_FAILURE;
265058561cbSjbeck 	(void) memcpy(buf, arg0, l0);
266058561cbSjbeck 	offset = l0;
267058561cbSjbeck 
268058561cbSjbeck 	if (arg1 != NULL)
269058561cbSjbeck 	{
270058561cbSjbeck 		l1 = strlen(arg1) + 1;
271058561cbSjbeck 		SM_ASSERT(offset < len);
272058561cbSjbeck 		SM_ASSERT(offset + l1 <= len);
273058561cbSjbeck 		(void) memcpy(buf + offset, arg1, l1);
274058561cbSjbeck 		offset += l1;
275058561cbSjbeck 		SM_ASSERT(offset > l1);
276058561cbSjbeck 	}
277058561cbSjbeck 
278058561cbSjbeck 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
279058561cbSjbeck 	free(buf);
280058561cbSjbeck 	return r;
281058561cbSjbeck }
282058561cbSjbeck 
283058561cbSjbeck /*
284058561cbSjbeck **  SMFI_CHGFROM -- change enveloper sender ("from") address
285058561cbSjbeck **
286058561cbSjbeck **	Parameters:
287058561cbSjbeck **		ctx -- Opaque context structure
288058561cbSjbeck **		from -- new envelope sender address ("MAIL From")
289058561cbSjbeck **		args -- ESMTP arguments
290058561cbSjbeck **
291058561cbSjbeck **	Returns:
292058561cbSjbeck **		MI_SUCCESS/MI_FAILURE
293058561cbSjbeck */
294058561cbSjbeck 
295058561cbSjbeck int
smfi_chgfrom(ctx,from,args)296058561cbSjbeck smfi_chgfrom(ctx, from, args)
297058561cbSjbeck 	SMFICTX *ctx;
298058561cbSjbeck 	char *from;
299058561cbSjbeck 	char *args;
300058561cbSjbeck {
301058561cbSjbeck 	if (from == NULL || *from == '\0')
302058561cbSjbeck 		return MI_FAILURE;
303058561cbSjbeck 	if (!mi_sendok(ctx, SMFIF_CHGFROM))
304058561cbSjbeck 		return MI_FAILURE;
305058561cbSjbeck 	return send2(ctx, SMFIR_CHGFROM, from, args);
306058561cbSjbeck }
307058561cbSjbeck 
308058561cbSjbeck /*
309058561cbSjbeck **  SMFI_SETSYMLIST -- set list of macros that the MTA should send.
310058561cbSjbeck **
311058561cbSjbeck **	Parameters:
312058561cbSjbeck **		ctx -- Opaque context structure
313058561cbSjbeck **		where -- SMTP stage
314058561cbSjbeck **		macros -- list of macros
315058561cbSjbeck **
316058561cbSjbeck **	Returns:
317058561cbSjbeck **		MI_SUCCESS/MI_FAILURE
318058561cbSjbeck */
319058561cbSjbeck 
320058561cbSjbeck int
smfi_setsymlist(ctx,where,macros)321058561cbSjbeck smfi_setsymlist(ctx, where, macros)
322058561cbSjbeck 	SMFICTX *ctx;
323058561cbSjbeck 	int where;
324058561cbSjbeck 	char *macros;
325058561cbSjbeck {
326058561cbSjbeck 	SM_ASSERT(ctx != NULL);
327058561cbSjbeck 
328058561cbSjbeck 	if (macros == NULL || *macros == '\0')
329058561cbSjbeck 		return MI_FAILURE;
330058561cbSjbeck 	if (where < SMFIM_FIRST || where > SMFIM_LAST)
331058561cbSjbeck 		return MI_FAILURE;
332058561cbSjbeck 	if (where < 0 || where >= MAX_MACROS_ENTRIES)
333058561cbSjbeck 		return MI_FAILURE;
334058561cbSjbeck 
335058561cbSjbeck 	if (ctx->ctx_mac_list[where] != NULL)
336058561cbSjbeck 		return MI_FAILURE;
337058561cbSjbeck 
338058561cbSjbeck 	ctx->ctx_mac_list[where] = strdup(macros);
339058561cbSjbeck 	if (ctx->ctx_mac_list[where] == NULL)
340058561cbSjbeck 		return MI_FAILURE;
341058561cbSjbeck 
342058561cbSjbeck 	return MI_SUCCESS;
343058561cbSjbeck }
344058561cbSjbeck 
345058561cbSjbeck /*
346058561cbSjbeck **  SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
347058561cbSjbeck **
348058561cbSjbeck **	Parameters:
349058561cbSjbeck **		ctx -- Opaque context structure
350058561cbSjbeck **		rcpt -- recipient address
351058561cbSjbeck **		args -- ESMTP arguments
352058561cbSjbeck **
353058561cbSjbeck **	Returns:
354058561cbSjbeck **		MI_SUCCESS/MI_FAILURE
355058561cbSjbeck */
356058561cbSjbeck 
357058561cbSjbeck int
smfi_addrcpt_par(ctx,rcpt,args)358058561cbSjbeck smfi_addrcpt_par(ctx, rcpt, args)
359058561cbSjbeck 	SMFICTX *ctx;
360058561cbSjbeck 	char *rcpt;
361058561cbSjbeck 	char *args;
362058561cbSjbeck {
363058561cbSjbeck 	if (rcpt == NULL || *rcpt == '\0')
364058561cbSjbeck 		return MI_FAILURE;
365058561cbSjbeck 	if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
366058561cbSjbeck 		return MI_FAILURE;
367058561cbSjbeck 	return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
368058561cbSjbeck }
369058561cbSjbeck 
3707c478bd9Sstevel@tonic-gate /*
3717c478bd9Sstevel@tonic-gate **  SMFI_ADDRCPT -- send an additional recipient to the MTA
3727c478bd9Sstevel@tonic-gate **
3737c478bd9Sstevel@tonic-gate **	Parameters:
3747c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
3757c478bd9Sstevel@tonic-gate **		rcpt -- recipient address
3767c478bd9Sstevel@tonic-gate **
3777c478bd9Sstevel@tonic-gate **	Returns:
3787c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
3797c478bd9Sstevel@tonic-gate */
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate int
smfi_addrcpt(ctx,rcpt)3827c478bd9Sstevel@tonic-gate smfi_addrcpt(ctx, rcpt)
3837c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
3847c478bd9Sstevel@tonic-gate 	char *rcpt;
3857c478bd9Sstevel@tonic-gate {
3867c478bd9Sstevel@tonic-gate 	size_t len;
3877c478bd9Sstevel@tonic-gate 	struct timeval timeout;
3887c478bd9Sstevel@tonic-gate 
3897c478bd9Sstevel@tonic-gate 	if (rcpt == NULL || *rcpt == '\0')
3907c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
3917c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
3927c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
3937c478bd9Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
3947c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
3957c478bd9Sstevel@tonic-gate 	len = strlen(rcpt) + 1;
3967c478bd9Sstevel@tonic-gate 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate /*
4007c478bd9Sstevel@tonic-gate **  SMFI_DELRCPT -- send a recipient to be removed to the MTA
4017c478bd9Sstevel@tonic-gate **
4027c478bd9Sstevel@tonic-gate **	Parameters:
4037c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
4047c478bd9Sstevel@tonic-gate **		rcpt -- recipient address
4057c478bd9Sstevel@tonic-gate **
4067c478bd9Sstevel@tonic-gate **	Returns:
4077c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
4087c478bd9Sstevel@tonic-gate */
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate int
smfi_delrcpt(ctx,rcpt)4117c478bd9Sstevel@tonic-gate smfi_delrcpt(ctx, rcpt)
4127c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
4137c478bd9Sstevel@tonic-gate 	char *rcpt;
4147c478bd9Sstevel@tonic-gate {
4157c478bd9Sstevel@tonic-gate 	size_t len;
4167c478bd9Sstevel@tonic-gate 	struct timeval timeout;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	if (rcpt == NULL || *rcpt == '\0')
4197c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
4207c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_DELRCPT))
4217c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
4227c478bd9Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
4237c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
4247c478bd9Sstevel@tonic-gate 	len = strlen(rcpt) + 1;
4257c478bd9Sstevel@tonic-gate 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
4267c478bd9Sstevel@tonic-gate }
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate /*
4297c478bd9Sstevel@tonic-gate **  SMFI_REPLACEBODY -- send a body chunk to the MTA
4307c478bd9Sstevel@tonic-gate **
4317c478bd9Sstevel@tonic-gate **	Parameters:
4327c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
4337c478bd9Sstevel@tonic-gate **		bodyp -- body chunk
4347c478bd9Sstevel@tonic-gate **		bodylen -- length of body chunk
4357c478bd9Sstevel@tonic-gate **
4367c478bd9Sstevel@tonic-gate **	Returns:
4377c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
4387c478bd9Sstevel@tonic-gate */
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate int
smfi_replacebody(ctx,bodyp,bodylen)4417c478bd9Sstevel@tonic-gate smfi_replacebody(ctx, bodyp, bodylen)
4427c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
4437c478bd9Sstevel@tonic-gate 	unsigned char *bodyp;
4447c478bd9Sstevel@tonic-gate 	int bodylen;
4457c478bd9Sstevel@tonic-gate {
4467c478bd9Sstevel@tonic-gate 	int len, off, r;
4477c478bd9Sstevel@tonic-gate 	struct timeval timeout;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	if (bodylen < 0 ||
4507c478bd9Sstevel@tonic-gate 	    (bodyp == NULL && bodylen > 0))
4517c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
4527c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_CHGBODY))
4537c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
4547c478bd9Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
4557c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	/* split body chunk if necessary */
4587c478bd9Sstevel@tonic-gate 	off = 0;
45949218d4fSjbeck 	do
4607c478bd9Sstevel@tonic-gate 	{
4617c478bd9Sstevel@tonic-gate 		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
4627c478bd9Sstevel@tonic-gate 						       bodylen;
4637c478bd9Sstevel@tonic-gate 		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
4647c478bd9Sstevel@tonic-gate 				(char *) (bodyp + off), len)) != MI_SUCCESS)
4657c478bd9Sstevel@tonic-gate 			return r;
4667c478bd9Sstevel@tonic-gate 		off += len;
4677c478bd9Sstevel@tonic-gate 		bodylen -= len;
46849218d4fSjbeck 	} while (bodylen > 0);
4697c478bd9Sstevel@tonic-gate 	return MI_SUCCESS;
4707c478bd9Sstevel@tonic-gate }
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate /*
4737c478bd9Sstevel@tonic-gate **  SMFI_QUARANTINE -- quarantine an envelope
4747c478bd9Sstevel@tonic-gate **
4757c478bd9Sstevel@tonic-gate **	Parameters:
4767c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
4777c478bd9Sstevel@tonic-gate **		reason -- why?
4787c478bd9Sstevel@tonic-gate **
4797c478bd9Sstevel@tonic-gate **	Returns:
4807c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
4817c478bd9Sstevel@tonic-gate */
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate int
smfi_quarantine(ctx,reason)4847c478bd9Sstevel@tonic-gate smfi_quarantine(ctx, reason)
4857c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
4867c478bd9Sstevel@tonic-gate 	char *reason;
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	size_t len;
4897c478bd9Sstevel@tonic-gate 	int r;
4907c478bd9Sstevel@tonic-gate 	char *buf;
4917c478bd9Sstevel@tonic-gate 	struct timeval timeout;
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	if (reason == NULL || *reason == '\0')
4947c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
4957c478bd9Sstevel@tonic-gate 	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
4967c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
4977c478bd9Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
4987c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
4997c478bd9Sstevel@tonic-gate 	len = strlen(reason) + 1;
5007c478bd9Sstevel@tonic-gate 	buf = malloc(len);
5017c478bd9Sstevel@tonic-gate 	if (buf == NULL)
5027c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
5037c478bd9Sstevel@tonic-gate 	(void) memcpy(buf, reason, len);
5047c478bd9Sstevel@tonic-gate 	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
5057c478bd9Sstevel@tonic-gate 	free(buf);
5067c478bd9Sstevel@tonic-gate 	return r;
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate /*
5107c478bd9Sstevel@tonic-gate **  MYISENHSC -- check whether a string contains an enhanced status code
5117c478bd9Sstevel@tonic-gate **
5127c478bd9Sstevel@tonic-gate **	Parameters:
5137c478bd9Sstevel@tonic-gate **		s -- string with possible enhanced status code.
5147c478bd9Sstevel@tonic-gate **		delim -- delim for enhanced status code.
5157c478bd9Sstevel@tonic-gate **
5167c478bd9Sstevel@tonic-gate **	Returns:
5177c478bd9Sstevel@tonic-gate **		0  -- no enhanced status code.
5187c478bd9Sstevel@tonic-gate **		>4 -- length of enhanced status code.
5197c478bd9Sstevel@tonic-gate **
5207c478bd9Sstevel@tonic-gate **	Side Effects:
5217c478bd9Sstevel@tonic-gate **		none.
5227c478bd9Sstevel@tonic-gate */
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate static int
myisenhsc(s,delim)5257c478bd9Sstevel@tonic-gate myisenhsc(s, delim)
5267c478bd9Sstevel@tonic-gate 	const char *s;
5277c478bd9Sstevel@tonic-gate 	int delim;
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate 	int l, h;
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	if (s == NULL)
5327c478bd9Sstevel@tonic-gate 		return 0;
5337c478bd9Sstevel@tonic-gate 	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
5347c478bd9Sstevel@tonic-gate 		return 0;
5357c478bd9Sstevel@tonic-gate 	h = 0;
5367c478bd9Sstevel@tonic-gate 	l = 2;
5377c478bd9Sstevel@tonic-gate 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
5387c478bd9Sstevel@tonic-gate 		++h;
5397c478bd9Sstevel@tonic-gate 	if (h == 0 || s[l + h] != '.')
5407c478bd9Sstevel@tonic-gate 		return 0;
5417c478bd9Sstevel@tonic-gate 	l += h + 1;
5427c478bd9Sstevel@tonic-gate 	h = 0;
5437c478bd9Sstevel@tonic-gate 	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
5447c478bd9Sstevel@tonic-gate 		++h;
5457c478bd9Sstevel@tonic-gate 	if (h == 0 || s[l + h] != delim)
5467c478bd9Sstevel@tonic-gate 		return 0;
5477c478bd9Sstevel@tonic-gate 	return l + h;
5487c478bd9Sstevel@tonic-gate }
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate /*
5517c478bd9Sstevel@tonic-gate **  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
5527c478bd9Sstevel@tonic-gate **
5537c478bd9Sstevel@tonic-gate **	Parameters:
5547c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
5557c478bd9Sstevel@tonic-gate **		rcode -- The three-digit (RFC 821) SMTP reply code.
5567c478bd9Sstevel@tonic-gate **		xcode -- The extended (RFC 2034) reply code.
5577c478bd9Sstevel@tonic-gate **		message -- The text part of the SMTP reply.
5587c478bd9Sstevel@tonic-gate **
5597c478bd9Sstevel@tonic-gate **	Returns:
5607c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
5617c478bd9Sstevel@tonic-gate */
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate int
smfi_setreply(ctx,rcode,xcode,message)5647c478bd9Sstevel@tonic-gate smfi_setreply(ctx, rcode, xcode, message)
5657c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
5667c478bd9Sstevel@tonic-gate 	char *rcode;
5677c478bd9Sstevel@tonic-gate 	char *xcode;
5687c478bd9Sstevel@tonic-gate 	char *message;
5697c478bd9Sstevel@tonic-gate {
5707c478bd9Sstevel@tonic-gate 	size_t len;
5717c478bd9Sstevel@tonic-gate 	char *buf;
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	if (rcode == NULL || ctx == NULL)
5747c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
5757c478bd9Sstevel@tonic-gate 
5767c478bd9Sstevel@tonic-gate 	/* ### <sp> \0 */
5777c478bd9Sstevel@tonic-gate 	len = strlen(rcode) + 2;
5787c478bd9Sstevel@tonic-gate 	if (len != 5)
5797c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
5807c478bd9Sstevel@tonic-gate 	if ((rcode[0] != '4' && rcode[0] != '5') ||
5817c478bd9Sstevel@tonic-gate 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
5827c478bd9Sstevel@tonic-gate 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
5837c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
5847c478bd9Sstevel@tonic-gate 	if (xcode != NULL)
5857c478bd9Sstevel@tonic-gate 	{
5867c478bd9Sstevel@tonic-gate 		if (!myisenhsc(xcode, '\0'))
5877c478bd9Sstevel@tonic-gate 			return MI_FAILURE;
5887c478bd9Sstevel@tonic-gate 		len += strlen(xcode) + 1;
5897c478bd9Sstevel@tonic-gate 	}
5907c478bd9Sstevel@tonic-gate 	if (message != NULL)
5917c478bd9Sstevel@tonic-gate 	{
5927c478bd9Sstevel@tonic-gate 		size_t ml;
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 		/* XXX check also for unprintable chars? */
5957c478bd9Sstevel@tonic-gate 		if (strpbrk(message, "\r\n") != NULL)
5967c478bd9Sstevel@tonic-gate 			return MI_FAILURE;
5977c478bd9Sstevel@tonic-gate 		ml = strlen(message);
5987c478bd9Sstevel@tonic-gate 		if (ml > MAXREPLYLEN)
5997c478bd9Sstevel@tonic-gate 			return MI_FAILURE;
6007c478bd9Sstevel@tonic-gate 		len += ml + 1;
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate 	buf = malloc(len);
6037c478bd9Sstevel@tonic-gate 	if (buf == NULL)
6047c478bd9Sstevel@tonic-gate 		return MI_FAILURE;		/* oops */
6057c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(buf, rcode, len);
6067c478bd9Sstevel@tonic-gate 	(void) sm_strlcat(buf, " ", len);
6077c478bd9Sstevel@tonic-gate 	if (xcode != NULL)
6087c478bd9Sstevel@tonic-gate 		(void) sm_strlcat(buf, xcode, len);
6097c478bd9Sstevel@tonic-gate 	if (message != NULL)
6107c478bd9Sstevel@tonic-gate 	{
6117c478bd9Sstevel@tonic-gate 		if (xcode != NULL)
6127c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(buf, " ", len);
6137c478bd9Sstevel@tonic-gate 		(void) sm_strlcat(buf, message, len);
6147c478bd9Sstevel@tonic-gate 	}
6157c478bd9Sstevel@tonic-gate 	if (ctx->ctx_reply != NULL)
6167c478bd9Sstevel@tonic-gate 		free(ctx->ctx_reply);
6177c478bd9Sstevel@tonic-gate 	ctx->ctx_reply = buf;
6187c478bd9Sstevel@tonic-gate 	return MI_SUCCESS;
6197c478bd9Sstevel@tonic-gate }
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate /*
6227c478bd9Sstevel@tonic-gate **  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
6237c478bd9Sstevel@tonic-gate **
6247c478bd9Sstevel@tonic-gate **	Parameters:
6257c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
6267c478bd9Sstevel@tonic-gate **		rcode -- The three-digit (RFC 821) SMTP reply code.
6277c478bd9Sstevel@tonic-gate **		xcode -- The extended (RFC 2034) reply code.
6287c478bd9Sstevel@tonic-gate **		txt, ... -- The text part of the SMTP reply,
6297c478bd9Sstevel@tonic-gate **			MUST be terminated with NULL.
6307c478bd9Sstevel@tonic-gate **
6317c478bd9Sstevel@tonic-gate **	Returns:
6327c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
6337c478bd9Sstevel@tonic-gate */
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate int
6367c478bd9Sstevel@tonic-gate #if SM_VA_STD
smfi_setmlreply(SMFICTX * ctx,const char * rcode,const char * xcode,...)6377c478bd9Sstevel@tonic-gate smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
6387c478bd9Sstevel@tonic-gate #else /* SM_VA_STD */
6397c478bd9Sstevel@tonic-gate smfi_setmlreply(ctx, rcode, xcode, va_alist)
6407c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
6417c478bd9Sstevel@tonic-gate 	const char *rcode;
6427c478bd9Sstevel@tonic-gate 	const char *xcode;
6437c478bd9Sstevel@tonic-gate 	va_dcl
6447c478bd9Sstevel@tonic-gate #endif /* SM_VA_STD */
6457c478bd9Sstevel@tonic-gate {
6467c478bd9Sstevel@tonic-gate 	size_t len;
6477c478bd9Sstevel@tonic-gate 	size_t rlen;
6487c478bd9Sstevel@tonic-gate 	int args;
6497c478bd9Sstevel@tonic-gate 	char *buf, *txt;
6507c478bd9Sstevel@tonic-gate 	const char *xc;
6517c478bd9Sstevel@tonic-gate 	char repl[16];
6527c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	if (rcode == NULL || ctx == NULL)
6557c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	/* ### <sp> */
6587c478bd9Sstevel@tonic-gate 	len = strlen(rcode) + 1;
6597c478bd9Sstevel@tonic-gate 	if (len != 4)
6607c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
6617c478bd9Sstevel@tonic-gate 	if ((rcode[0] != '4' && rcode[0] != '5') ||
6627c478bd9Sstevel@tonic-gate 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
6637c478bd9Sstevel@tonic-gate 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
6647c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
6657c478bd9Sstevel@tonic-gate 	if (xcode != NULL)
6667c478bd9Sstevel@tonic-gate 	{
6677c478bd9Sstevel@tonic-gate 		if (!myisenhsc(xcode, '\0'))
6687c478bd9Sstevel@tonic-gate 			return MI_FAILURE;
6697c478bd9Sstevel@tonic-gate 		xc = xcode;
6707c478bd9Sstevel@tonic-gate 	}
6717c478bd9Sstevel@tonic-gate 	else
6727c478bd9Sstevel@tonic-gate 	{
6737c478bd9Sstevel@tonic-gate 		if (rcode[0] == '4')
6747c478bd9Sstevel@tonic-gate 			xc = "4.0.0";
6757c478bd9Sstevel@tonic-gate 		else
6767c478bd9Sstevel@tonic-gate 			xc = "5.0.0";
6777c478bd9Sstevel@tonic-gate 	}
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	/* add trailing space */
6807c478bd9Sstevel@tonic-gate 	len += strlen(xc) + 1;
6817c478bd9Sstevel@tonic-gate 	rlen = len;
6827c478bd9Sstevel@tonic-gate 	args = 0;
6837c478bd9Sstevel@tonic-gate 	SM_VA_START(ap, xcode);
6847c478bd9Sstevel@tonic-gate 	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
6857c478bd9Sstevel@tonic-gate 	{
6867c478bd9Sstevel@tonic-gate 		size_t tl;
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 		tl = strlen(txt);
6897c478bd9Sstevel@tonic-gate 		if (tl > MAXREPLYLEN)
6907c478bd9Sstevel@tonic-gate 			break;
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate 		/* this text, reply codes, \r\n */
6937c478bd9Sstevel@tonic-gate 		len += tl + 2 + rlen;
6947c478bd9Sstevel@tonic-gate 		if (++args > MAXREPLIES)
6957c478bd9Sstevel@tonic-gate 			break;
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 		/* XXX check also for unprintable chars? */
6987c478bd9Sstevel@tonic-gate 		if (strpbrk(txt, "\r\n") != NULL)
6997c478bd9Sstevel@tonic-gate 			break;
7007c478bd9Sstevel@tonic-gate 	}
7017c478bd9Sstevel@tonic-gate 	SM_VA_END(ap);
7027c478bd9Sstevel@tonic-gate 	if (txt != NULL)
7037c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 	/* trailing '\0' */
7067c478bd9Sstevel@tonic-gate 	++len;
7077c478bd9Sstevel@tonic-gate 	buf = malloc(len);
7087c478bd9Sstevel@tonic-gate 	if (buf == NULL)
7097c478bd9Sstevel@tonic-gate 		return MI_FAILURE;		/* oops */
7107c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
7117c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
7127c478bd9Sstevel@tonic-gate 			   xc, " ");
7137c478bd9Sstevel@tonic-gate 	SM_VA_START(ap, xcode);
7147c478bd9Sstevel@tonic-gate 	txt = SM_VA_ARG(ap, char *);
7157c478bd9Sstevel@tonic-gate 	if (txt != NULL)
7167c478bd9Sstevel@tonic-gate 	{
7177c478bd9Sstevel@tonic-gate 		(void) sm_strlcat2(buf, " ", txt, len);
7187c478bd9Sstevel@tonic-gate 		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
7197c478bd9Sstevel@tonic-gate 		{
7207c478bd9Sstevel@tonic-gate 			if (--args <= 1)
7217c478bd9Sstevel@tonic-gate 				repl[3] = ' ';
7227c478bd9Sstevel@tonic-gate 			(void) sm_strlcat2(buf, "\r\n", repl, len);
7237c478bd9Sstevel@tonic-gate 			(void) sm_strlcat(buf, txt, len);
7247c478bd9Sstevel@tonic-gate 		}
7257c478bd9Sstevel@tonic-gate 	}
7267c478bd9Sstevel@tonic-gate 	if (ctx->ctx_reply != NULL)
7277c478bd9Sstevel@tonic-gate 		free(ctx->ctx_reply);
7287c478bd9Sstevel@tonic-gate 	ctx->ctx_reply = buf;
7297c478bd9Sstevel@tonic-gate 	SM_VA_END(ap);
7307c478bd9Sstevel@tonic-gate 	return MI_SUCCESS;
7317c478bd9Sstevel@tonic-gate }
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate /*
7347c478bd9Sstevel@tonic-gate **  SMFI_SETPRIV -- set private data
7357c478bd9Sstevel@tonic-gate **
7367c478bd9Sstevel@tonic-gate **	Parameters:
7377c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
7387c478bd9Sstevel@tonic-gate **		privatedata -- pointer to private data
7397c478bd9Sstevel@tonic-gate **
7407c478bd9Sstevel@tonic-gate **	Returns:
7417c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
7427c478bd9Sstevel@tonic-gate */
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate int
smfi_setpriv(ctx,privatedata)7457c478bd9Sstevel@tonic-gate smfi_setpriv(ctx, privatedata)
7467c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
7477c478bd9Sstevel@tonic-gate 	void *privatedata;
7487c478bd9Sstevel@tonic-gate {
7497c478bd9Sstevel@tonic-gate 	if (ctx == NULL)
7507c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
7517c478bd9Sstevel@tonic-gate 	ctx->ctx_privdata = privatedata;
7527c478bd9Sstevel@tonic-gate 	return MI_SUCCESS;
7537c478bd9Sstevel@tonic-gate }
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate /*
7567c478bd9Sstevel@tonic-gate **  SMFI_GETPRIV -- get private data
7577c478bd9Sstevel@tonic-gate **
7587c478bd9Sstevel@tonic-gate **	Parameters:
7597c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
7607c478bd9Sstevel@tonic-gate **
7617c478bd9Sstevel@tonic-gate **	Returns:
7627c478bd9Sstevel@tonic-gate **		pointer to private data
7637c478bd9Sstevel@tonic-gate */
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate void *
smfi_getpriv(ctx)7667c478bd9Sstevel@tonic-gate smfi_getpriv(ctx)
7677c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
7687c478bd9Sstevel@tonic-gate {
7697c478bd9Sstevel@tonic-gate 	if (ctx == NULL)
7707c478bd9Sstevel@tonic-gate 		return NULL;
7717c478bd9Sstevel@tonic-gate 	return ctx->ctx_privdata;
7727c478bd9Sstevel@tonic-gate }
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate /*
7757c478bd9Sstevel@tonic-gate **  SMFI_GETSYMVAL -- get the value of a macro
7767c478bd9Sstevel@tonic-gate **
7777c478bd9Sstevel@tonic-gate **	See explanation in mfapi.h about layout of the structures.
7787c478bd9Sstevel@tonic-gate **
7797c478bd9Sstevel@tonic-gate **	Parameters:
7807c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
7817c478bd9Sstevel@tonic-gate **		symname -- name of macro
7827c478bd9Sstevel@tonic-gate **
7837c478bd9Sstevel@tonic-gate **	Returns:
7847c478bd9Sstevel@tonic-gate **		value of macro (NULL in case of failure)
7857c478bd9Sstevel@tonic-gate */
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate char *
smfi_getsymval(ctx,symname)7887c478bd9Sstevel@tonic-gate smfi_getsymval(ctx, symname)
7897c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
7907c478bd9Sstevel@tonic-gate 	char *symname;
7917c478bd9Sstevel@tonic-gate {
7927c478bd9Sstevel@tonic-gate 	int i;
7937c478bd9Sstevel@tonic-gate 	char **s;
7947c478bd9Sstevel@tonic-gate 	char one[2];
7957c478bd9Sstevel@tonic-gate 	char braces[4];
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	if (ctx == NULL || symname == NULL || *symname == '\0')
7987c478bd9Sstevel@tonic-gate 		return NULL;
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
8017c478bd9Sstevel@tonic-gate 	{
8027c478bd9Sstevel@tonic-gate 		one[0] = symname[1];
8037c478bd9Sstevel@tonic-gate 		one[1] = '\0';
8047c478bd9Sstevel@tonic-gate 	}
8057c478bd9Sstevel@tonic-gate 	else
8067c478bd9Sstevel@tonic-gate 		one[0] = '\0';
8077c478bd9Sstevel@tonic-gate 	if (strlen(symname) == 1)
8087c478bd9Sstevel@tonic-gate 	{
8097c478bd9Sstevel@tonic-gate 		braces[0] = '{';
8107c478bd9Sstevel@tonic-gate 		braces[1] = *symname;
8117c478bd9Sstevel@tonic-gate 		braces[2] = '}';
8127c478bd9Sstevel@tonic-gate 		braces[3] = '\0';
8137c478bd9Sstevel@tonic-gate 	}
8147c478bd9Sstevel@tonic-gate 	else
8157c478bd9Sstevel@tonic-gate 		braces[0] = '\0';
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	/* search backwards through the macro array */
8187c478bd9Sstevel@tonic-gate 	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
8197c478bd9Sstevel@tonic-gate 	{
8207c478bd9Sstevel@tonic-gate 		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
8217c478bd9Sstevel@tonic-gate 		    ctx->ctx_mac_buf[i] == NULL)
8227c478bd9Sstevel@tonic-gate 			continue;
8237c478bd9Sstevel@tonic-gate 		while (s != NULL && *s != NULL)
8247c478bd9Sstevel@tonic-gate 		{
8257c478bd9Sstevel@tonic-gate 			if (strcmp(*s, symname) == 0)
8267c478bd9Sstevel@tonic-gate 				return *++s;
8277c478bd9Sstevel@tonic-gate 			if (one[0] != '\0' && strcmp(*s, one) == 0)
8287c478bd9Sstevel@tonic-gate 				return *++s;
8297c478bd9Sstevel@tonic-gate 			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
8307c478bd9Sstevel@tonic-gate 				return *++s;
8317c478bd9Sstevel@tonic-gate 			++s;	/* skip over macro value */
8327c478bd9Sstevel@tonic-gate 			++s;	/* points to next macro name */
8337c478bd9Sstevel@tonic-gate 		}
8347c478bd9Sstevel@tonic-gate 	}
8357c478bd9Sstevel@tonic-gate 	return NULL;
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate **  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
8407c478bd9Sstevel@tonic-gate **		     timeouts during long milter-side operations
8417c478bd9Sstevel@tonic-gate **
8427c478bd9Sstevel@tonic-gate **	Parameters:
8437c478bd9Sstevel@tonic-gate **		ctx -- Opaque context structure
8447c478bd9Sstevel@tonic-gate **
8457c478bd9Sstevel@tonic-gate **	Return value:
8467c478bd9Sstevel@tonic-gate **		MI_SUCCESS/MI_FAILURE
8477c478bd9Sstevel@tonic-gate */
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate int
smfi_progress(ctx)8507c478bd9Sstevel@tonic-gate smfi_progress(ctx)
8517c478bd9Sstevel@tonic-gate 	SMFICTX *ctx;
8527c478bd9Sstevel@tonic-gate {
8537c478bd9Sstevel@tonic-gate 	struct timeval timeout;
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	if (ctx == NULL)
8567c478bd9Sstevel@tonic-gate 		return MI_FAILURE;
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	timeout.tv_sec = ctx->ctx_timeout;
8597c478bd9Sstevel@tonic-gate 	timeout.tv_usec = 0;
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
8627c478bd9Sstevel@tonic-gate }
863058561cbSjbeck 
864058561cbSjbeck /*
865058561cbSjbeck **  SMFI_VERSION -- return (runtime) version of libmilter
866058561cbSjbeck **
867058561cbSjbeck **	Parameters:
868058561cbSjbeck **		major -- (pointer to) major version
869058561cbSjbeck **		minor -- (pointer to) minor version
870058561cbSjbeck **		patchlevel -- (pointer to) patchlevel version
871058561cbSjbeck **
872058561cbSjbeck **	Return value:
873058561cbSjbeck **		MI_SUCCESS
874058561cbSjbeck */
875058561cbSjbeck 
876058561cbSjbeck int
smfi_version(major,minor,patchlevel)877058561cbSjbeck smfi_version(major, minor, patchlevel)
878058561cbSjbeck 	unsigned int *major;
879058561cbSjbeck 	unsigned int *minor;
880058561cbSjbeck 	unsigned int *patchlevel;
881058561cbSjbeck {
882058561cbSjbeck 	if (major != NULL)
883058561cbSjbeck 		*major = SM_LM_VRS_MAJOR(SMFI_VERSION);
884058561cbSjbeck 	if (minor != NULL)
885058561cbSjbeck 		*minor = SM_LM_VRS_MINOR(SMFI_VERSION);
886058561cbSjbeck 	if (patchlevel != NULL)
887*24472db6Sjbeck 		*patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
888058561cbSjbeck 	return MI_SUCCESS;
889058561cbSjbeck }
890