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