xref: /illumos-gate/usr/src/cmd/sendmail/libmilter/smfi.c (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
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 #include <sm/gen.h>
12 SM_RCSID("@(#)$Id: smfi.c,v 8.83 2007/04/23 16:44:39 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 		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 
295 int
296 smfi_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 
320 int
321 smfi_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 
357 int
358 smfi_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 
381 int
382 smfi_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 
410 int
411 smfi_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 
440 int
441 smfi_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 
483 int
484 smfi_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 
524 static int
525 myisenhsc(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 
563 int
564 smfi_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 
635 int
636 #if SM_VA_STD
637 smfi_setmlreply(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
638 #else /* SM_VA_STD */
639 smfi_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 
744 int
745 smfi_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 
765 void *
766 smfi_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 
787 char *
788 smfi_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 
849 int
850 smfi_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 
876 int
877 smfi_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