smfi.c revision 173341
1124861Sharti/*
2122394Sharti *  Copyright (c) 1999-2007 Sendmail, Inc. and its suppliers.
3122394Sharti *	All rights reserved.
4122394Sharti *
5122394Sharti * By using this file, you agree to the terms and conditions set
6122394Sharti * forth in the LICENSE file which can be found at the top level of
7122394Sharti * the sendmail distribution.
8122394Sharti *
9122394Sharti */
10122394Sharti
11122394Sharti#include <sm/gen.h>
12122394ShartiSM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 ca Exp $")
13122394Sharti#include <sm/varargs.h>
14122394Sharti#include "libmilter.h"
15122394Sharti
16122394Shartistatic int smfi_header __P((SMFICTX *, int, int, char *, char *));
17122394Shartistatic int myisenhsc __P((const char *, int));
18124861Sharti
19124861Sharti/* for smfi_set{ml}reply, let's be generous. 256/16 should be sufficient */
20124861Sharti#define MAXREPLYLEN	980	/* max. length of a reply string */
21122394Sharti#define MAXREPLIES	32	/* max. number of reply strings */
22122394Sharti
23122394Sharti/*
24124861Sharti**  SMFI_HEADER -- send a header to the MTA
25124861Sharti**
26122394Sharti**	Parameters:
27122394Sharti**		ctx -- Opaque context structure
28122394Sharti**		cmd -- Header modification command
29122394Sharti**		hdridx -- Header index
30122394Sharti**		headerf -- Header field name
31122394Sharti**		headerv -- Header field value
32122394Sharti**
33122394Sharti**	Returns:
34122394Sharti**		MI_SUCCESS/MI_FAILURE
35122394Sharti*/
36122394Sharti
37122394Shartistatic int
38122394Shartismfi_header(ctx, cmd, hdridx, headerf, headerv)
39122394Sharti	SMFICTX *ctx;
40122394Sharti	int cmd;
41122394Sharti	int hdridx;
42122394Sharti	char *headerf;
43122394Sharti	char *headerv;
44122394Sharti{
45122394Sharti	size_t len, l1, l2, offset;
46122394Sharti	int r;
47122394Sharti	mi_int32 v;
48122394Sharti	char *buf;
49122394Sharti	struct timeval timeout;
50122394Sharti
51122394Sharti	if (headerf == NULL || *headerf == '\0' || headerv == NULL)
52122394Sharti		return MI_FAILURE;
53122394Sharti	timeout.tv_sec = ctx->ctx_timeout;
54122394Sharti	timeout.tv_usec = 0;
55122394Sharti	l1 = strlen(headerf) + 1;
56122394Sharti	l2 = strlen(headerv) + 1;
57122394Sharti	len = l1 + l2;
58122394Sharti	if (hdridx >= 0)
59122394Sharti		len += MILTER_LEN_BYTES;
60122394Sharti	buf = malloc(len);
61122394Sharti	if (buf == NULL)
62122394Sharti		return MI_FAILURE;
63122394Sharti	offset = 0;
64122394Sharti	if (hdridx >= 0)
65122394Sharti	{
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		l1 = strlen(arg1) + 1;
271		SM_ASSERT(offset < len);
272		SM_ASSERT(offset + l1 <= len);
273		(void) memcpy(buf + offset, arg1, l1);
274		offset += l1;
275		SM_ASSERT(offset > l1);
276	}
277
278	r = mi_wr_cmd(ctx->ctx_sd, &timeout, cmd, buf, len);
279	free(buf);
280	return r;
281}
282
283/*
284**  SMFI_CHGFROM -- change enveloper sender ("from") address
285**
286**	Parameters:
287**		ctx -- Opaque context structure
288**		from -- new envelope sender address ("MAIL From")
289**		args -- ESMTP arguments
290**
291**	Returns:
292**		MI_SUCCESS/MI_FAILURE
293*/
294
295int
296smfi_chgfrom(ctx, from, args)
297	SMFICTX *ctx;
298	char *from;
299	char *args;
300{
301	if (from == NULL || *from == '\0')
302		return MI_FAILURE;
303	if (!mi_sendok(ctx, SMFIF_CHGFROM))
304		return MI_FAILURE;
305	return send2(ctx, SMFIR_CHGFROM, from, args);
306}
307
308/*
309**  SMFI_SETSYMLIST -- set list of macros that the MTA should send.
310**
311**	Parameters:
312**		ctx -- Opaque context structure
313**		where -- SMTP stage
314**		macros -- list of macros
315**
316**	Returns:
317**		MI_SUCCESS/MI_FAILURE
318*/
319
320int
321smfi_setsymlist(ctx, where, macros)
322	SMFICTX *ctx;
323	int where;
324	char *macros;
325{
326	SM_ASSERT(ctx != NULL);
327
328	if (macros == NULL || *macros == '\0')
329		return MI_FAILURE;
330	if (where < SMFIM_FIRST || where > SMFIM_LAST)
331		return MI_FAILURE;
332	if (where < 0 || where >= MAX_MACROS_ENTRIES)
333		return MI_FAILURE;
334
335	if (ctx->ctx_mac_list[where] != NULL)
336		return MI_FAILURE;
337
338	ctx->ctx_mac_list[where] = strdup(macros);
339	if (ctx->ctx_mac_list[where] == NULL)
340		return MI_FAILURE;
341
342	return MI_SUCCESS;
343}
344
345/*
346**  SMFI_ADDRCPT_PAR -- send an additional recipient to the MTA
347**
348**	Parameters:
349**		ctx -- Opaque context structure
350**		rcpt -- recipient address
351**		args -- ESMTP arguments
352**
353**	Returns:
354**		MI_SUCCESS/MI_FAILURE
355*/
356
357int
358smfi_addrcpt_par(ctx, rcpt, args)
359	SMFICTX *ctx;
360	char *rcpt;
361	char *args;
362{
363	if (rcpt == NULL || *rcpt == '\0')
364		return MI_FAILURE;
365	if (!mi_sendok(ctx, SMFIF_ADDRCPT_PAR))
366		return MI_FAILURE;
367	return send2(ctx, SMFIR_ADDRCPT_PAR, rcpt, args);
368}
369
370/*
371**  SMFI_ADDRCPT -- send an additional recipient to the MTA
372**
373**	Parameters:
374**		ctx -- Opaque context structure
375**		rcpt -- recipient address
376**
377**	Returns:
378**		MI_SUCCESS/MI_FAILURE
379*/
380
381int
382smfi_addrcpt(ctx, rcpt)
383	SMFICTX *ctx;
384	char *rcpt;
385{
386	size_t len;
387	struct timeval timeout;
388
389	if (rcpt == NULL || *rcpt == '\0')
390		return MI_FAILURE;
391	if (!mi_sendok(ctx, SMFIF_ADDRCPT))
392		return MI_FAILURE;
393	timeout.tv_sec = ctx->ctx_timeout;
394	timeout.tv_usec = 0;
395	len = strlen(rcpt) + 1;
396	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_ADDRCPT, rcpt, len);
397}
398
399/*
400**  SMFI_DELRCPT -- send a recipient to be removed to the MTA
401**
402**	Parameters:
403**		ctx -- Opaque context structure
404**		rcpt -- recipient address
405**
406**	Returns:
407**		MI_SUCCESS/MI_FAILURE
408*/
409
410int
411smfi_delrcpt(ctx, rcpt)
412	SMFICTX *ctx;
413	char *rcpt;
414{
415	size_t len;
416	struct timeval timeout;
417
418	if (rcpt == NULL || *rcpt == '\0')
419		return MI_FAILURE;
420	if (!mi_sendok(ctx, SMFIF_DELRCPT))
421		return MI_FAILURE;
422	timeout.tv_sec = ctx->ctx_timeout;
423	timeout.tv_usec = 0;
424	len = strlen(rcpt) + 1;
425	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_DELRCPT, rcpt, len);
426}
427
428/*
429**  SMFI_REPLACEBODY -- send a body chunk to the MTA
430**
431**	Parameters:
432**		ctx -- Opaque context structure
433**		bodyp -- body chunk
434**		bodylen -- length of body chunk
435**
436**	Returns:
437**		MI_SUCCESS/MI_FAILURE
438*/
439
440int
441smfi_replacebody(ctx, bodyp, bodylen)
442	SMFICTX *ctx;
443	unsigned char *bodyp;
444	int bodylen;
445{
446	int len, off, r;
447	struct timeval timeout;
448
449	if (bodylen < 0 ||
450	    (bodyp == NULL && bodylen > 0))
451		return MI_FAILURE;
452	if (!mi_sendok(ctx, SMFIF_CHGBODY))
453		return MI_FAILURE;
454	timeout.tv_sec = ctx->ctx_timeout;
455	timeout.tv_usec = 0;
456
457	/* split body chunk if necessary */
458	off = 0;
459	do
460	{
461		len = (bodylen >= MILTER_CHUNK_SIZE) ? MILTER_CHUNK_SIZE :
462						       bodylen;
463		if ((r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_REPLBODY,
464				(char *) (bodyp + off), len)) != MI_SUCCESS)
465			return r;
466		off += len;
467		bodylen -= len;
468	} while (bodylen > 0);
469	return MI_SUCCESS;
470}
471
472/*
473**  SMFI_QUARANTINE -- quarantine an envelope
474**
475**	Parameters:
476**		ctx -- Opaque context structure
477**		reason -- why?
478**
479**	Returns:
480**		MI_SUCCESS/MI_FAILURE
481*/
482
483int
484smfi_quarantine(ctx, reason)
485	SMFICTX *ctx;
486	char *reason;
487{
488	size_t len;
489	int r;
490	char *buf;
491	struct timeval timeout;
492
493	if (reason == NULL || *reason == '\0')
494		return MI_FAILURE;
495	if (!mi_sendok(ctx, SMFIF_QUARANTINE))
496		return MI_FAILURE;
497	timeout.tv_sec = ctx->ctx_timeout;
498	timeout.tv_usec = 0;
499	len = strlen(reason) + 1;
500	buf = malloc(len);
501	if (buf == NULL)
502		return MI_FAILURE;
503	(void) memcpy(buf, reason, len);
504	r = mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_QUARANTINE, buf, len);
505	free(buf);
506	return r;
507}
508
509/*
510**  MYISENHSC -- check whether a string contains an enhanced status code
511**
512**	Parameters:
513**		s -- string with possible enhanced status code.
514**		delim -- delim for enhanced status code.
515**
516**	Returns:
517**		0  -- no enhanced status code.
518**		>4 -- length of enhanced status code.
519**
520**	Side Effects:
521**		none.
522*/
523
524static int
525myisenhsc(s, delim)
526	const char *s;
527	int delim;
528{
529	int l, h;
530
531	if (s == NULL)
532		return 0;
533	if (!((*s == '2' || *s == '4' || *s == '5') && s[1] == '.'))
534		return 0;
535	h = 0;
536	l = 2;
537	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
538		++h;
539	if (h == 0 || s[l + h] != '.')
540		return 0;
541	l += h + 1;
542	h = 0;
543	while (h < 3 && isascii(s[l + h]) && isdigit(s[l + h]))
544		++h;
545	if (h == 0 || s[l + h] != delim)
546		return 0;
547	return l + h;
548}
549
550/*
551**  SMFI_SETREPLY -- set the reply code for the next reply to the MTA
552**
553**	Parameters:
554**		ctx -- Opaque context structure
555**		rcode -- The three-digit (RFC 821) SMTP reply code.
556**		xcode -- The extended (RFC 2034) reply code.
557**		message -- The text part of the SMTP reply.
558**
559**	Returns:
560**		MI_SUCCESS/MI_FAILURE
561*/
562
563int
564smfi_setreply(ctx, rcode, xcode, message)
565	SMFICTX *ctx;
566	char *rcode;
567	char *xcode;
568	char *message;
569{
570	size_t len;
571	char *buf;
572
573	if (rcode == NULL || ctx == NULL)
574		return MI_FAILURE;
575
576	/* ### <sp> \0 */
577	len = strlen(rcode) + 2;
578	if (len != 5)
579		return MI_FAILURE;
580	if ((rcode[0] != '4' && rcode[0] != '5') ||
581	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
582	    !isascii(rcode[2]) || !isdigit(rcode[2]))
583		return MI_FAILURE;
584	if (xcode != NULL)
585	{
586		if (!myisenhsc(xcode, '\0'))
587			return MI_FAILURE;
588		len += strlen(xcode) + 1;
589	}
590	if (message != NULL)
591	{
592		size_t ml;
593
594		/* XXX check also for unprintable chars? */
595		if (strpbrk(message, "\r\n") != NULL)
596			return MI_FAILURE;
597		ml = strlen(message);
598		if (ml > MAXREPLYLEN)
599			return MI_FAILURE;
600		len += ml + 1;
601	}
602	buf = malloc(len);
603	if (buf == NULL)
604		return MI_FAILURE;		/* oops */
605	(void) sm_strlcpy(buf, rcode, len);
606	(void) sm_strlcat(buf, " ", len);
607	if (xcode != NULL)
608		(void) sm_strlcat(buf, xcode, len);
609	if (message != NULL)
610	{
611		if (xcode != NULL)
612			(void) sm_strlcat(buf, " ", len);
613		(void) sm_strlcat(buf, message, len);
614	}
615	if (ctx->ctx_reply != NULL)
616		free(ctx->ctx_reply);
617	ctx->ctx_reply = buf;
618	return MI_SUCCESS;
619}
620
621/*
622**  SMFI_SETMLREPLY -- set multiline reply code for the next reply to the MTA
623**
624**	Parameters:
625**		ctx -- Opaque context structure
626**		rcode -- The three-digit (RFC 821) SMTP reply code.
627**		xcode -- The extended (RFC 2034) reply code.
628**		txt, ... -- The text part of the SMTP reply,
629**			MUST be terminated with NULL.
630**
631**	Returns:
632**		MI_SUCCESS/MI_FAILURE
633*/
634
635int
636#if SM_VA_STD
637smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
638#else /* SM_VA_STD */
639smfi_setmlreply(ctx, rcode, xcode, va_alist)
640	SMFICTX *ctx;
641	const char *rcode;
642	const char *xcode;
643	va_dcl
644#endif /* SM_VA_STD */
645{
646	size_t len;
647	size_t rlen;
648	int args;
649	char *buf, *txt;
650	const char *xc;
651	char repl[16];
652	SM_VA_LOCAL_DECL
653
654	if (rcode == NULL || ctx == NULL)
655		return MI_FAILURE;
656
657	/* ### <sp> */
658	len = strlen(rcode) + 1;
659	if (len != 4)
660		return MI_FAILURE;
661	if ((rcode[0] != '4' && rcode[0] != '5') ||
662	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
663	    !isascii(rcode[2]) || !isdigit(rcode[2]))
664		return MI_FAILURE;
665	if (xcode != NULL)
666	{
667		if (!myisenhsc(xcode, '\0'))
668			return MI_FAILURE;
669		xc = xcode;
670	}
671	else
672	{
673		if (rcode[0] == '4')
674			xc = "4.0.0";
675		else
676			xc = "5.0.0";
677	}
678
679	/* add trailing space */
680	len += strlen(xc) + 1;
681	rlen = len;
682	args = 0;
683	SM_VA_START(ap, xcode);
684	while ((txt = SM_VA_ARG(ap, char *)) != NULL)
685	{
686		size_t tl;
687
688		tl = strlen(txt);
689		if (tl > MAXREPLYLEN)
690			break;
691
692		/* this text, reply codes, \r\n */
693		len += tl + 2 + rlen;
694		if (++args > MAXREPLIES)
695			break;
696
697		/* XXX check also for unprintable chars? */
698		if (strpbrk(txt, "\r\n") != NULL)
699			break;
700	}
701	SM_VA_END(ap);
702	if (txt != NULL)
703		return MI_FAILURE;
704
705	/* trailing '\0' */
706	++len;
707	buf = malloc(len);
708	if (buf == NULL)
709		return MI_FAILURE;		/* oops */
710	(void) sm_strlcpyn(buf, len, 3, rcode, args == 1 ? " " : "-", xc);
711	(void) sm_strlcpyn(repl, sizeof repl, 4, rcode, args == 1 ? " " : "-",
712			   xc, " ");
713	SM_VA_START(ap, xcode);
714	txt = SM_VA_ARG(ap, char *);
715	if (txt != NULL)
716	{
717		(void) sm_strlcat2(buf, " ", txt, len);
718		while ((txt = SM_VA_ARG(ap, char *)) != NULL)
719		{
720			if (--args <= 1)
721				repl[3] = ' ';
722			(void) sm_strlcat2(buf, "\r\n", repl, len);
723			(void) sm_strlcat(buf, txt, len);
724		}
725	}
726	if (ctx->ctx_reply != NULL)
727		free(ctx->ctx_reply);
728	ctx->ctx_reply = buf;
729	SM_VA_END(ap);
730	return MI_SUCCESS;
731}
732
733/*
734**  SMFI_SETPRIV -- set private data
735**
736**	Parameters:
737**		ctx -- Opaque context structure
738**		privatedata -- pointer to private data
739**
740**	Returns:
741**		MI_SUCCESS/MI_FAILURE
742*/
743
744int
745smfi_setpriv(ctx, privatedata)
746	SMFICTX *ctx;
747	void *privatedata;
748{
749	if (ctx == NULL)
750		return MI_FAILURE;
751	ctx->ctx_privdata = privatedata;
752	return MI_SUCCESS;
753}
754
755/*
756**  SMFI_GETPRIV -- get private data
757**
758**	Parameters:
759**		ctx -- Opaque context structure
760**
761**	Returns:
762**		pointer to private data
763*/
764
765void *
766smfi_getpriv(ctx)
767	SMFICTX *ctx;
768{
769	if (ctx == NULL)
770		return NULL;
771	return ctx->ctx_privdata;
772}
773
774/*
775**  SMFI_GETSYMVAL -- get the value of a macro
776**
777**	See explanation in mfapi.h about layout of the structures.
778**
779**	Parameters:
780**		ctx -- Opaque context structure
781**		symname -- name of macro
782**
783**	Returns:
784**		value of macro (NULL in case of failure)
785*/
786
787char *
788smfi_getsymval(ctx, symname)
789	SMFICTX *ctx;
790	char *symname;
791{
792	int i;
793	char **s;
794	char one[2];
795	char braces[4];
796
797	if (ctx == NULL || symname == NULL || *symname == '\0')
798		return NULL;
799
800	if (strlen(symname) == 3 && symname[0] == '{' && symname[2] == '}')
801	{
802		one[0] = symname[1];
803		one[1] = '\0';
804	}
805	else
806		one[0] = '\0';
807	if (strlen(symname) == 1)
808	{
809		braces[0] = '{';
810		braces[1] = *symname;
811		braces[2] = '}';
812		braces[3] = '\0';
813	}
814	else
815		braces[0] = '\0';
816
817	/* search backwards through the macro array */
818	for (i = MAX_MACROS_ENTRIES - 1 ; i >= 0; --i)
819	{
820		if ((s = ctx->ctx_mac_ptr[i]) == NULL ||
821		    ctx->ctx_mac_buf[i] == NULL)
822			continue;
823		while (s != NULL && *s != NULL)
824		{
825			if (strcmp(*s, symname) == 0)
826				return *++s;
827			if (one[0] != '\0' && strcmp(*s, one) == 0)
828				return *++s;
829			if (braces[0] != '\0' && strcmp(*s, braces) == 0)
830				return *++s;
831			++s;	/* skip over macro value */
832			++s;	/* points to next macro name */
833		}
834	}
835	return NULL;
836}
837
838/*
839**  SMFI_PROGRESS -- send "progress" message to the MTA to prevent premature
840**		     timeouts during long milter-side operations
841**
842**	Parameters:
843**		ctx -- Opaque context structure
844**
845**	Return value:
846**		MI_SUCCESS/MI_FAILURE
847*/
848
849int
850smfi_progress(ctx)
851	SMFICTX *ctx;
852{
853	struct timeval timeout;
854
855	if (ctx == NULL)
856		return MI_FAILURE;
857
858	timeout.tv_sec = ctx->ctx_timeout;
859	timeout.tv_usec = 0;
860
861	return mi_wr_cmd(ctx->ctx_sd, &timeout, SMFIR_PROGRESS, NULL, 0);
862}
863
864/*
865**  SMFI_VERSION -- return (runtime) version of libmilter
866**
867**	Parameters:
868**		major -- (pointer to) major version
869**		minor -- (pointer to) minor version
870**		patchlevel -- (pointer to) patchlevel version
871**
872**	Return value:
873**		MI_SUCCESS
874*/
875
876int
877smfi_version(major, minor, patchlevel)
878	unsigned int *major;
879	unsigned int *minor;
880	unsigned int *patchlevel;
881{
882	if (major != NULL)
883		*major = SM_LM_VRS_MAJOR(SMFI_VERSION);
884	if (minor != NULL)
885		*minor = SM_LM_VRS_MINOR(SMFI_VERSION);
886	if (patchlevel != NULL)
887		*patchlevel = SM_LM_VRS_PLVL(SMFI_VERSION);
888	return MI_SUCCESS;
889}
890