smfi.c revision 363466
1/*
2 *  Copyright (c) 1999-2007 Proofpoint, Inc. and its suppliers.
3 *	All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: smfi.c,v 8.84 2013-11-22 20:51:36 ca Exp $")
13#include <sm/varargs.h>
14#include "libmilter.h"
15
16static int smfi_header __P((SMFICTX *, int, int, char *, char *));
17static int myisenhsc __P((const char *, int));
18
19/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
20#define MAXREPLYLEN	980	/* max. length of a reply string */
21#define MAXREPLIES	32	/* max. number of reply strings */
22
23/*
24**  SMFI_HEADER -- send a header to the MTA
25**
26**	Parameters:
27**		ctx -- Opaque context structure
28**		cmd -- Header modification command
29**		hdridx -- Header index
30**		headerf -- Header field name
31**		headerv -- Header field value
32**
33**	Returns:
34**		MI_SUCCESS/MI_FAILURE
35*/
36
37static int
38smfi_header(ctx, cmd, hdridx, headerf, headerv)
39	SMFICTX *ctx;
40	int cmd;
41	int hdridx;
42	char *headerf;
43	char *headerv;
44{
45	size_t len, l1, l2, offset;
46	int r;
47	mi_int32 v;
48	char *buf;
49	struct timeval timeout;
50
51	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
52		return MI_FAILURE;
53	timeout.tv_sec = ctx->ctx_timeout;
54	timeout.tv_usec = 0;
55	l1 = strlen(headerf) + 1;
56	l2 = strlen(headerv) + 1;
57	len = l1 + l2;
58	if (hdridx >= 0)
59		len += MILTER_LEN_BYTES;
60	buf = malloc(len);
61	if (buf == NULL)
62		return MI_FAILURE;
63	offset = 0;
64	if (hdridx >= 0)
65	{
66		v = htonl(hdridx);
67		(void) memcpy(&(buf[0]), (void *) &v, MILTER_LEN_BYTES);
68		offset += MILTER_LEN_BYTES;
69	}
70	(void) memcpy(buf + offset, headerf, l1);
71	(void) memcpy(buf + offset + l1, headerv, l2);
72	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
73	free(buf);
74	return r;
75}
76
77/*
78**  SMFI_ADDHEADER -- send a new header to the MTA
79**
80**	Parameters:
81**		ctx -- Opaque context structure
82**		headerf -- Header field name
83**		headerv -- Header field value
84**
85**	Returns:
86**		MI_SUCCESS/MI_FAILURE
87*/
88
89int
90smfi_addheader(ctx, headerf, headerv)
91	SMFICTX *ctx;
92	char *headerf;
93	char *headerv;
94{
95	if (!mi_sendok(ctx, SMFIF_ADDHDRS))
96		return MI_FAILURE;
97
98	return smfi_header(ctx, SMFIR_ADDHEADER, -1, headerf, headerv);
99}
100
101/*
102**  SMFI_INSHEADER -- send a new header to the MTA (to be inserted)
103**
104**	Parameters:
105**		ctx -- Opaque context structure
106**		hdridx -- index into header list where insertion should occur
107**		headerf -- Header field name
108**		headerv -- Header field value
109**
110**	Returns:
111**		MI_SUCCESS/MI_FAILURE
112*/
113
114int
115smfi_insheader(ctx, hdridx, headerf, headerv)
116	SMFICTX *ctx;
117	int hdridx;
118	char *headerf;
119	char *headerv;
120{
121	if (!mi_sendok(ctx, SMFIF_ADDHDRS) || hdridx < 0)
122		return MI_FAILURE;
123
124	return smfi_header(ctx, SMFIR_INSHEADER, hdridx, headerf, headerv);
125}
126
127/*
128**  SMFI_CHGHEADER -- send a changed header to the MTA
129**
130**	Parameters:
131**		ctx -- Opaque context structure
132**		headerf -- Header field name
133**		hdridx -- Header index value
134**		headerv -- Header field value
135**
136**	Returns:
137**		MI_SUCCESS/MI_FAILURE
138*/
139
140int
141smfi_chgheader(ctx, headerf, hdridx, headerv)
142	SMFICTX *ctx;
143	char *headerf;
144	mi_int32 hdridx;
145	char *headerv;
146{
147	if (!mi_sendok(ctx, SMFIF_CHGHDRS) || hdridx < 0)
148		return MI_FAILURE;
149	if (headerv == NULL)
150		headerv = "";
151
152	return smfi_header(ctx, SMFIR_CHGHEADER, hdridx, headerf, headerv);
153}
154
155#if 0
156/*
157**  BUF_CRT_SEND -- construct buffer to send from arguments
158**
159**	Parameters:
160**		ctx -- Opaque context structure
161**		cmd -- command
162**		arg0 -- first argument
163**		argv -- list of arguments (NULL terminated)
164**
165**	Returns:
166**		MI_SUCCESS/MI_FAILURE
167*/
168
169static int
170buf_crt_send __P((SMFICTX *, int cmd, char *, char **));
171
172static int
173buf_crt_send(ctx, cmd, arg0, argv)
174	SMFICTX *ctx;
175	int cmd;
176	char *arg0;
177	char **argv;
178{
179	size_t len, l0, l1, offset;
180	int r;
181	char *buf, *arg, **argvl;
182	struct timeval timeout;
183
184	if (arg0 == NULL || *arg0 == '\0')
185		return MI_FAILURE;
186	timeout.tv_sec = ctx->ctx_timeout;
187	timeout.tv_usec = 0;
188	l0 = strlen(arg0) + 1;
189	len = l0;
190	argvl = argv;
191	while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
192	{
193		l1 = strlen(arg) + 1;
194		len += l1;
195		SM_ASSERT(len > l1);
196	}
197
198	buf = malloc(len);
199	if (buf == NULL)
200		return MI_FAILURE;
201	(void) memcpy(buf, arg0, l0);
202	offset = l0;
203
204	argvl = argv;
205	while (argvl != NULL && (arg = *argv) != NULL && *arg != '\0')
206	{
207		l1 = strlen(arg) + 1;
208		SM_ASSERT(offset < len);
209		SM_ASSERT(offset + l1 <= len);
210		(void) memcpy(buf + offset, arg, l1);
211		offset += l1;
212		SM_ASSERT(offset > l1);
213	}
214
215	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
216	free(buf);
217	return r;
218}
219#endif /* 0 */
220
221/*
222**  SEND2 -- construct buffer to send from arguments
223**
224**	Parameters:
225**		ctx -- Opaque context structure
226**		cmd -- command
227**		arg0 -- first argument
228**		argv -- list of arguments (NULL terminated)
229**
230**	Returns:
231**		MI_SUCCESS/MI_FAILURE
232*/
233
234static int
235send2 __P((SMFICTX *, int cmd, char *, char *));
236
237static int
238send2(ctx, cmd, arg0, arg1)
239	SMFICTX *ctx;
240	int cmd;
241	char *arg0;
242	char *arg1;
243{
244	size_t len, l0, l1, offset;
245	int r;
246	char *buf;
247	struct timeval timeout;
248
249	if (arg0 == NULL || *arg0 == '\0')
250		return MI_FAILURE;
251	timeout.tv_sec = ctx->ctx_timeout;
252	timeout.tv_usec = 0;
253	l0 = strlen(arg0) + 1;
254	len = l0;
255	if (arg1 != NULL)
256	{
257		l1 = strlen(arg1) + 1;
258		len += l1;
259		SM_ASSERT(len > l1);
260	}
261
262	buf = malloc(len);
263	if (buf == NULL)
264		return MI_FAILURE;
265	(void) memcpy(buf, arg0, l0);
266	offset = l0;
267
268	if (arg1 != NULL)
269	{
270		SM_ASSERT(offset < len);
271		SM_ASSERT(offset + l1 <= len);
272		(void) memcpy(buf + offset, arg1, l1);
273		offset += l1;
274		SM_ASSERT(offset > l1);
275	}
276
277	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
278	free(buf);
279	return r;
280}
281
282/*
283**  SMFI_CHGFROM -- change enveloper sender ("from") address
284**
285**	Parameters:
286**		ctx -- Opaque context structure
287**		from -- new envelope sender address ("MAIL From")
288**		args -- ESMTP arguments
289**
290**	Returns:
291**		MI_SUCCESS/MI_FAILURE
292*/
293
294int
295smfi_chgfrom(ctx, from, args)
296	SMFICTX *ctx;
297	char *from;
298	char *args;
299{
300	if (from == NULL || *from == '\0')
301		return MI_FAILURE;
302	if (!mi_sendok(ctx, SMFIF_CHGFROM))
303		return MI_FAILURE;
304	return send2(ctx, SMFIR_CHGFROM, from, args);
305}
306
307/*
308**  SMFI_SETSYMLIST -- set list of macros that the MTA should send.
309**
310**	Parameters:
311**		ctx -- Opaque context structure
312**		where -- SMTP stage
313**		macros -- list of macros
314**
315**	Returns:
316**		MI_SUCCESS/MI_FAILURE
317*/
318
319int
320smfi_setsymlist(ctx, where, macros)
321	SMFICTX *ctx;
322	int where;
323	char *macros;
324{
325	SM_ASSERT(ctx != NULL);
326
327	if (macros == NULL)
328		return MI_FAILURE;
329	if (where < SMFIM_FIRST || where > SMFIM_LAST)
330		return MI_FAILURE;
331	if (where < 0 || where >= MAX_MACROS_ENTRIES)
332		return MI_FAILURE;
333
334	if (ctx->ctx_mac_list[where] != NULL)
335		return MI_FAILURE;
336
337	ctx->ctx_mac_list[where] = strdup(macros);
338	if (ctx->ctx_mac_list[where] == NULL)
339		return MI_FAILURE;
340
341	return MI_SUCCESS;
342}
343
344/*
345**  SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
346**
347**	Parameters:
348**		ctx -- Opaque context structure
349**		rcpt -- recipient address
350**		args -- ESMTP arguments
351**
352**	Returns:
353**		MI_SUCCESS/MI_FAILURE
354*/
355
356int
357smfi_addrcpt_par(ctx, rcpt, args)
358	SMFICTX *ctx;
359	char *rcpt;
360	char *args;
361{
362	if (rcpt == NULL || *rcpt == '\0')
363		return MI_FAILURE;
364	if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
365		return MI_FAILURE;
366	return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
367}
368
369/*
370**  SMFI_ADDRCPT -- send an additional recipient to the MTA
371**
372**	Parameters:
373**		ctx -- Opaque context structure
374**		rcpt -- recipient address
375**
376**	Returns:
377**		MI_SUCCESS/MI_FAILURE
378*/
379
380int
381smfi_addrcpt(ctx, rcpt)
382	SMFICTX *ctx;
383	char *rcpt;
384{
385	size_t len;
386	struct timeval timeout;
387
388	if (rcpt == NULL || *rcpt == '\0')
389		return MI_FAILURE;
390	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
391		return MI_FAILURE;
392	timeout.tv_sec = ctx->ctx_timeout;
393	timeout.tv_usec = 0;
394	len = strlen(rcpt) + 1;
395	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
396}
397
398/*
399**  SMFI_DELRCPT -- send a recipient to be removed to the MTA
400**
401**	Parameters:
402**		ctx -- Opaque context structure
403**		rcpt -- recipient address
404**
405**	Returns:
406**		MI_SUCCESS/MI_FAILURE
407*/
408
409int
410smfi_delrcpt(ctx, rcpt)
411	SMFICTX *ctx;
412	char *rcpt;
413{
414	size_t len;
415	struct timeval timeout;
416
417	if (rcpt == NULL || *rcpt == '\0')
418		return MI_FAILURE;
419	if (!mi_sendok(ctx, SMFIF_DELRCPT))
420		return MI_FAILURE;
421	timeout.tv_sec = ctx->ctx_timeout;
422	timeout.tv_usec = 0;
423	len = strlen(rcpt) + 1;
424	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
425}
426
427/*
428**  SMFI_REPLACEBODY -- send a body chunk to the MTA
429**
430**	Parameters:
431**		ctx -- Opaque context structure
432**		bodyp -- body chunk
433**		bodylen -- length of body chunk
434**
435**	Returns:
436**		MI_SUCCESS/MI_FAILURE
437*/
438
439int
440smfi_replacebody(ctx, bodyp, bodylen)
441	SMFICTX *ctx;
442	unsigned char *bodyp;
443	int bodylen;
444{
445	int len, off, r;
446	struct timeval timeout;
447
448	if (bodylen < 0 ||
449	    (bodyp == NULL && bodylen > 0))
450		return MI_FAILURE;
451	if (!mi_sendok(ctx, SMFIF_CHGBODY))
452		return MI_FAILURE;
453	timeout.tv_sec = ctx->ctx_timeout;
454	timeout.tv_usec = 0;
455
456	/* split body chunk if necessary */
457	off = 0;
458	do
459	{
460		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
461						       bodylen;
462		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
463				(char *) (bodyp + off), len)) != MI_SUCCESS)
464			return r;
465		off += len;
466		bodylen -= len;
467	} while (bodylen > 0);
468	return MI_SUCCESS;
469}
470
471/*
472**  SMFI_QUARANTINE -- quarantine an envelope
473**
474**	Parameters:
475**		ctx -- Opaque context structure
476**		reason -- why?
477**
478**	Returns:
479**		MI_SUCCESS/MI_FAILURE
480*/
481
482int
483smfi_quarantine(ctx, reason)
484	SMFICTX *ctx;
485	char *reason;
486{
487	size_t len;
488	int r;
489	char *buf;
490	struct timeval timeout;
491
492	if (reason == NULL || *reason == '\0')
493		return MI_FAILURE;
494	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
495		return MI_FAILURE;
496	timeout.tv_sec = ctx->ctx_timeout;
497	timeout.tv_usec = 0;
498	len = strlen(reason) + 1;
499	buf = malloc(len);
500	if (buf == NULL)
501		return MI_FAILURE;
502	(void) memcpy(buf, reason, len);
503	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
504	free(buf);
505	return r;
506}
507
508/*
509**  MYISENHSC -- check whether a string contains an enhanced status code
510**
511**	Parameters:
512**		s -- string with possible enhanced status code.
513**		delim -- delim for enhanced status code.
514**
515**	Returns:
516**		0  -- no enhanced status code.
517**		>4 -- length of enhanced status code.
518**
519**	Side Effects:
520**		none.
521*/
522
523static int
524myisenhsc(s, delim)
525	const char *s;
526	int delim;
527{
528	int l, h;
529
530	if (s == NULL)
531		return 0;
532	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
533		return 0;
534	h = 0;
535	l = 2;
536	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
537		++h;
538	if (h == 0 || s[l + h] != '.')
539		return 0;
540	l += h + 1;
541	h = 0;
542	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
543		++h;
544	if (h == 0 || s[l + h] != delim)
545		return 0;
546	return l + h;
547}
548
549/*
550**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
551**
552**	Parameters:
553**		ctx -- Opaque context structure
554**		rcode -- The three-digit (RFC 821) SMTP reply code.
555**		xcode -- The extended (RFC 2034) reply code.
556**		message -- The text part of the SMTP reply.
557**
558**	Returns:
559**		MI_SUCCESS/MI_FAILURE
560*/
561
562int
563smfi_setreply(ctx, rcode, xcode, message)
564	SMFICTX *ctx;
565	char *rcode;
566	char *xcode;
567	char *message;
568{
569	size_t len;
570	char *buf;
571
572	if (rcode == NULL || ctx == NULL)
573		return MI_FAILURE;
574
575	/* ### <sp> \0 */
576	len = strlen(rcode) + 2;
577	if (len != 5)
578		return MI_FAILURE;
579	if ((rcode[0] != '4' && rcode[0] != '5') ||
580	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
581	    !isascii(rcode[2]) || !isdigit(rcode[2]))
582		return MI_FAILURE;
583	if (xcode != NULL)
584	{
585		if (!myisenhsc(xcode, '\0'))
586			return MI_FAILURE;
587		len += strlen(xcode) + 1;
588	}
589	if (message != NULL)
590	{
591		size_t ml;
592
593		/* XXX check also for unprintable chars? */
594		if (strpbrk(message, "\r\n") != NULL)
595			return MI_FAILURE;
596		ml = strlen(message);
597		if (ml > MAXREPLYLEN)
598			return MI_FAILURE;
599		len += ml + 1;
600	}
601	buf = malloc(len);
602	if (buf == NULL)
603		return MI_FAILURE;		/* oops */
604	(void) sm_strlcpy(buf, rcode, len);
605	(void) sm_strlcat(buf, " ", len);
606	if (xcode != NULL)
607		(void) sm_strlcat(buf, xcode, len);
608	if (message != NULL)
609	{
610		if (xcode != NULL)
611			(void) sm_strlcat(buf, " ", len);
612		(void) sm_strlcat(buf, message, len);
613	}
614	if (ctx->ctx_reply != NULL)
615		free(ctx->ctx_reply);
616	ctx->ctx_reply = buf;
617	return MI_SUCCESS;
618}
619
620/*
621**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
622**
623**	Parameters:
624**		ctx -- Opaque context structure
625**		rcode -- The three-digit (RFC 821) SMTP reply code.
626**		xcode -- The extended (RFC 2034) reply code.
627**		txt, ... -- The text part of the SMTP reply,
628**			MUST be terminated with NULL.
629**
630**	Returns:
631**		MI_SUCCESS/MI_FAILURE
632*/
633
634int
635#if SM_VA_STD
636smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
637#else /* SM_VA_STD */
638smfi_setmlreply(ctx, rcode, xcode, va_alist)
639	SMFICTX *ctx;
640	const char *rcode;
641	const char *xcode;
642	va_dcl
643#endif /* SM_VA_STD */
644{
645	size_t len;
646	size_t rlen;
647	int args;
648	char *buf, *txt;
649	const char *xc;
650	char repl[16];
651	SM_VA_LOCAL_DECL
652
653	if (rcode == NULL || ctx == NULL)
654		return MI_FAILURE;
655
656	/* ### <sp> */
657	len = strlen(rcode) + 1;
658	if (len != 4)
659		return MI_FAILURE;
660	if ((rcode[0] != '4' && rcode[0] != '5') ||
661	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
662	    !isascii(rcode[2]) || !isdigit(rcode[2]))
663		return MI_FAILURE;
664	if (xcode != NULL)
665	{
666		if (!myisenhsc(xcode, '\0'))
667			return MI_FAILURE;
668		xc = xcode;
669	}
670	else
671	{
672		if (rcode[0] == '4')
673			xc = "4.0.0";
674		else
675			xc = "5.0.0";
676	}
677
678	/* add trailing space */
679	len += strlen(xc) + 1;
680	rlen = len;
681	args = 0;
682	SM_VA_START(ap, xcode);
683	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
684	{
685		size_t tl;
686
687		tl = strlen(txt);
688		if (tl > MAXREPLYLEN)
689			break;
690
691		/* this text, reply codes, \r\n */
692		len += tl + 2 + rlen;
693		if (++args > MAXREPLIES)
694			break;
695
696		/* XXX check also for unprintable chars? */
697		if (strpbrk(txt, "\r\n") != NULL)
698			break;
699	}
700	SM_VA_END(ap);
701	if (txt != NULL)
702		return MI_FAILURE;
703
704	/* trailing '\0' */
705	++len;
706	buf = malloc(len);
707	if (buf == NULL)
708		return MI_FAILURE;		/* oops */
709	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
710	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
711			   xc, " ");
712	SM_VA_START(ap, xcode);
713	txt = SM_VA_ARG(ap, char *);
714	if (txt != NULL)
715	{
716		(void) sm_strlcat2(buf, " ", txt, len);
717		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
718		{
719			if (--args <= 1)
720				repl[3] = ' ';
721			(void) sm_strlcat2(buf, "\r\n", repl, len);
722			(void) sm_strlcat(buf, txt, len);
723		}
724	}
725	if (ctx->ctx_reply != NULL)
726		free(ctx->ctx_reply);
727	ctx->ctx_reply = buf;
728	SM_VA_END(ap);
729	return MI_SUCCESS;
730}
731
732/*
733**  SMFI_SETPRIV -- set private data
734**
735**	Parameters:
736**		ctx -- Opaque context structure
737**		privatedata -- pointer to private data
738**
739**	Returns:
740**		MI_SUCCESS/MI_FAILURE
741*/
742
743int
744smfi_setpriv(ctx, privatedata)
745	SMFICTX *ctx;
746	void *privatedata;
747{
748	if (ctx == NULL)
749		return MI_FAILURE;
750	ctx->ctx_privdata = privatedata;
751	return MI_SUCCESS;
752}
753
754/*
755**  SMFI_GETPRIV -- get private data
756**
757**	Parameters:
758**		ctx -- Opaque context structure
759**
760**	Returns:
761**		pointer to private data
762*/
763
764void *
765smfi_getpriv(ctx)
766	SMFICTX *ctx;
767{
768	if (ctx == NULL)
769		return NULL;
770	return ctx->ctx_privdata;
771}
772
773/*
774**  SMFI_GETSYMVAL -- get the value of a macro
775**
776**	See explanation in mfapi.h about layout of the structures.
777**
778**	Parameters:
779**		ctx -- Opaque context structure
780**		symname -- name of macro
781**
782**	Returns:
783**		value of macro (NULL in case of failure)
784*/
785
786char *
787smfi_getsymval(ctx, symname)
788	SMFICTX *ctx;
789	char *symname;
790{
791	int i;
792	char **s;
793	char one[2];
794	char braces[4];
795
796	if (ctx == NULL || symname == NULL || *symname == '\0')
797		return NULL;
798
799	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
800	{
801		one[0] = symname[1];
802		one[1] = '\0';
803	}
804	else
805		one[0] = '\0';
806	if (strlen(symname) == 1)
807	{
808		braces[0] = '{';
809		braces[1] = *symname;
810		braces[2] = '}';
811		braces[3] = '\0';
812	}
813	else
814		braces[0] = '\0';
815
816	/* search backwards through the macro array */
817	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
818	{
819		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
820		    ctx->ctx_mac_buf[i] == NULL)
821			continue;
822		while (s != NULL && *s != NULL)
823		{
824			if (strcmp(*s, symname) == 0)
825				return *++s;
826			if (one[0] != '\0' && strcmp(*s, one) == 0)
827				return *++s;
828			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
829				return *++s;
830			++s;	/* skip over macro value */
831			++s;	/* points to next macro name */
832		}
833	}
834	return NULL;
835}
836
837/*
838**  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
839**		     timeouts during long milter-side operations
840**
841**	Parameters:
842**		ctx -- Opaque context structure
843**
844**	Return value:
845**		MI_SUCCESS/MI_FAILURE
846*/
847
848int
849smfi_progress(ctx)
850	SMFICTX *ctx;
851{
852	struct timeval timeout;
853
854	if (ctx == NULL)
855		return MI_FAILURE;
856
857	timeout.tv_sec = ctx->ctx_timeout;
858	timeout.tv_usec = 0;
859
860	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
861}
862
863/*
864**  SMFI_VERSION -- return (runtime) version of libmilter
865**
866**	Parameters:
867**		major -- (pointer to) major version
868**		minor -- (pointer to) minor version
869**		patchlevel -- (pointer to) patchlevel version
870**
871**	Return value:
872**		MI_SUCCESS
873*/
874
875int
876smfi_version(major, minor, patchlevel)
877	unsigned int *major;
878	unsigned int *minor;
879	unsigned int *patchlevel;
880{
881	if (major != NULL)
882		*major = SM_LM_VRS_MAJOR(SMFI_VERSION);
883	if (minor != NULL)
884		*minor = SM_LM_VRS_MINOR(SMFI_VERSION);
885	if (patchlevel != NULL)
886		*patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
887	return MI_SUCCESS;
888}
889