xref: /freebsd/contrib/sendmail/libmilter/smfi.c (revision 2cf0c51793da5a2fc03db8990fc2feb3f9aa119f)
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>
12 SM_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 
16 static int smfi_header __P((SMFICTX *, int, int, char *, char *));
17 static 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 
37 static int
38 smfi_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 
89 int
90 smfi_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 
114 int
115 smfi_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 
140 int
141 smfi_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 
169 static int
170 buf_crt_send __P((SMFICTX *, int cmd, char *, char **));
171 
172 static int
173 buf_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 
234 static int
235 send2 __P((SMFICTX *, int cmd, char *, char *));
236 
237 static int
238 send2(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 
294 int
295 smfi_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 
319 int
320 smfi_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 
356 int
357 smfi_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 
380 int
381 smfi_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 
409 int
410 smfi_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 
439 int
440 smfi_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 
482 int
483 smfi_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 
523 static int
524 myisenhsc(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 
562 int
563 smfi_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 
634 int
635 #if SM_VA_STD
636 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
637 #else /* SM_VA_STD */
638 smfi_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 
743 int
744 smfi_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 
764 void *
765 smfi_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 
786 char *
787 smfi_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 
848 int
849 smfi_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 
875 int
876 smfi_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