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