xref: /freebsd/contrib/sendmail/src/util.c (revision f9e730bbb343d0a6aff3a9d4a7ea18dc1cf3ddc5)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)util.c	8.167 (Berkeley) 12/1/1998";
15 #endif /* not lint */
16 
17 # include "sendmail.h"
18 # include <sysexits.h>
19 /*
20 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
21 **
22 **	Runs through a string and strips off unquoted quote
23 **	characters and quote bits.  This is done in place.
24 **
25 **	Parameters:
26 **		s -- the string to strip.
27 **
28 **	Returns:
29 **		none.
30 **
31 **	Side Effects:
32 **		none.
33 **
34 **	Called By:
35 **		deliver
36 */
37 
38 void
39 stripquotes(s)
40 	char *s;
41 {
42 	register char *p;
43 	register char *q;
44 	register char c;
45 
46 	if (s == NULL)
47 		return;
48 
49 	p = q = s;
50 	do
51 	{
52 		c = *p++;
53 		if (c == '\\')
54 			c = *p++;
55 		else if (c == '"')
56 			continue;
57 		*q++ = c;
58 	} while (c != '\0');
59 }
60 /*
61 **  ADDQUOTES -- Adds quotes & quote bits to a string.
62 **
63 **	Runs through a string and adds characters and quote bits.
64 **
65 **	Parameters:
66 **		s -- the string to modify.
67 **
68 **	Returns:
69 **		pointer to quoted string.
70 **
71 **	Side Effects:
72 **		none.
73 **
74 */
75 
76 char *
77 addquotes(s)
78 	char *s;
79 {
80 	int len = 0;
81 	char c;
82 	char *p = s, *q, *r;
83 
84 	if (s == NULL)
85 		return NULL;
86 
87 	/* Find length of quoted string */
88 	while ((c = *p++) != '\0')
89 	{
90 		len++;
91 		if (c == '\\' || c == '"')
92 			len++;
93 	}
94 
95 	q = r = xalloc(len + 3);
96 	p = s;
97 
98 	/* add leading quote */
99 	*q++ = '"';
100 	while ((c = *p++) != '\0')
101 	{
102 		/* quote \ or " */
103 		if (c == '\\' || c == '"')
104 			*q++ = '\\';
105 		*q++ = c;
106 	}
107 	*q++ = '"';
108 	*q = '\0';
109 	return r;
110 }
111 /*
112 **  RFC822_STRING -- Checks string for proper RFC822 string quoting.
113 **
114 **	Runs through a string and verifies RFC822 special characters
115 **	are only found inside comments, quoted strings, or backslash
116 **	escaped.  Also verified balanced quotes and parenthesis.
117 **
118 **	Parameters:
119 **		s -- the string to modify.
120 **
121 **	Returns:
122 **		TRUE -- if the string is RFC822 compliant.
123 **		FALSE -- if the string is not RFC822 compliant.
124 **
125 **	Side Effects:
126 **		none.
127 **
128 */
129 
130 bool
131 rfc822_string(s)
132 	char *s;
133 {
134 	bool quoted = FALSE;
135 	int commentlev = 0;
136 	char *c = s;
137 
138 	if (s == NULL)
139 		return FALSE;
140 
141 	while (*c != '\0')
142 	{
143 		/* escaped character */
144 		if (*c == '\\')
145 		{
146 			c++;
147 			if (*c == '\0')
148 				return FALSE;
149 		}
150 		else if (commentlev == 0 && *c == '"')
151 			quoted = !quoted;
152 		else if (!quoted)
153 		{
154 			if (*c == ')')
155 			{
156 				/* unbalanced ')' */
157 				if (commentlev == 0)
158 					return FALSE;
159 				else
160 					commentlev--;
161 			}
162 			else if (*c == '(')
163 				commentlev++;
164 			else if (commentlev == 0 &&
165 				 strchr(MustQuoteChars, *c) != NULL)
166 				return FALSE;
167 		}
168 		c++;
169 	}
170 	/* unbalanced '"' or '(' */
171 	if (quoted || commentlev != 0)
172 		return FALSE;
173 	else
174 		return TRUE;
175 }
176 /*
177 **  SHORTEN_RFC822_STRING -- Truncate and rebalance an RFC822 string
178 **
179 **	Arbitratily shorten (in place) an RFC822 string and rebalance
180 **	comments and quotes.
181 **
182 **	Parameters:
183 **		string -- the string to shorten
184 **		length -- the maximum size, 0 if no maximum
185 **
186 **	Returns:
187 **		TRUE if string is changed, FALSE otherwise
188 **
189 **	Side Effects:
190 **		Changes string in place, possibly resulting
191 **		in a shorter string.
192 */
193 
194 bool
195 shorten_rfc822_string(string, length)
196 	char *string;
197 	size_t length;
198 {
199 	bool backslash = FALSE;
200 	bool modified = FALSE;
201 	bool quoted = FALSE;
202 	size_t slen;
203 	int parencount = 0;
204 	char *ptr = string;
205 
206 	/*
207 	**  If have to rebalance an already short enough string,
208 	**  need to do it within allocated space.
209 	*/
210 	slen = strlen(string);
211 	if (length == 0 || slen < length)
212 		length = slen;
213 
214 	while (*ptr != '\0')
215 	{
216 		if (backslash)
217 		{
218 			backslash = FALSE;
219 			goto increment;
220 		}
221 
222 		if (*ptr == '\\')
223 			backslash = TRUE;
224 		else if (*ptr == '(')
225 		{
226 			if (!quoted)
227 				parencount++;
228 		}
229 		else if (*ptr == ')')
230 		{
231 			if (--parencount < 0)
232 				parencount = 0;
233 		}
234 
235 		/* Inside a comment, quotes don't matter */
236 		if (parencount <= 0 && *ptr == '"')
237 			quoted = !quoted;
238 
239 increment:
240 		/* Check for sufficient space for next character */
241 		if (length - (ptr - string) <= ((backslash ? 1 : 0) +
242 						parencount +
243 						(quoted ? 1 : 0)))
244 		{
245 			/* Not enough, backtrack */
246 			if (*ptr == '\\')
247 				backslash = FALSE;
248 			else if (*ptr == '(' && !quoted)
249 				parencount--;
250 			else if (*ptr == '"' && parencount == 0)
251 				quoted = FALSE;
252 			break;
253 		}
254 		ptr++;
255 	}
256 
257 	/* Rebalance */
258 	while (parencount-- > 0)
259 	{
260 		if (*ptr != ')')
261 		{
262 			modified = TRUE;
263 			*ptr = ')';
264 		}
265 		ptr++;
266 	}
267 	if (quoted)
268 	{
269 		if (*ptr != '"')
270 		{
271 			modified = TRUE;
272 			*ptr = '"';
273 		}
274 		ptr++;
275 	}
276 	if (*ptr != '\0')
277 	{
278 		modified = TRUE;
279 		*ptr = '\0';
280 	}
281 	return modified;
282 }
283 /*
284 **  FIND_CHARACTER -- find an unquoted character in an RFC822 string
285 **
286 **	Find an unquoted, non-commented character in an RFC822
287 **	string and return a pointer to its location in the
288 **	string.
289 **
290 **	Parameters:
291 **		string -- the string to search
292 **		character -- the character to find
293 **
294 **	Returns:
295 **		pointer to the character, or
296 **		a pointer to the end of the line if character is not found
297 */
298 
299 char *
300 find_character(string, character)
301 	char *string;
302 	char character;
303 {
304 	bool backslash = FALSE;
305 	bool quoted = FALSE;
306 	int parencount = 0;
307 
308 	while (string != NULL && *string != '\0')
309 	{
310 		if (backslash)
311 		{
312 			backslash = FALSE;
313 			if (!quoted && character == '\\' && *string == '\\')
314 				break;
315 			string++;
316 			continue;
317 		}
318 		switch (*string)
319 		{
320 		  case '\\':
321 			backslash = TRUE;
322 			break;
323 
324 		  case '(':
325 			if (!quoted)
326 				parencount++;
327 			break;
328 
329 		  case ')':
330 			if (--parencount < 0)
331 				parencount = 0;
332 			break;
333 		}
334 
335 		/* Inside a comment, nothing matters */
336 		if (parencount > 0)
337 		{
338 			string++;
339 			continue;
340 		}
341 
342 		if (*string == '"')
343 			quoted = !quoted;
344 		else if (*string == character && !quoted)
345 			break;
346 		string++;
347 	}
348 
349 	/* Return pointer to the character */
350 	return string;
351 }
352 /*
353 **  XALLOC -- Allocate memory and bitch wildly on failure.
354 **
355 **	THIS IS A CLUDGE.  This should be made to give a proper
356 **	error -- but after all, what can we do?
357 **
358 **	Parameters:
359 **		sz -- size of area to allocate.
360 **
361 **	Returns:
362 **		pointer to data region.
363 **
364 **	Side Effects:
365 **		Memory is allocated.
366 */
367 
368 char *
369 xalloc(sz)
370 	register int sz;
371 {
372 	register char *p;
373 
374 	/* some systems can't handle size zero mallocs */
375 	if (sz <= 0)
376 		sz = 1;
377 
378 	p = malloc((unsigned) sz);
379 	if (p == NULL)
380 	{
381 		syserr("!Out of memory!!");
382 		/* exit(EX_UNAVAILABLE); */
383 	}
384 	return (p);
385 }
386 /*
387 **  COPYPLIST -- copy list of pointers.
388 **
389 **	This routine is the equivalent of newstr for lists of
390 **	pointers.
391 **
392 **	Parameters:
393 **		list -- list of pointers to copy.
394 **			Must be NULL terminated.
395 **		copycont -- if TRUE, copy the contents of the vector
396 **			(which must be a string) also.
397 **
398 **	Returns:
399 **		a copy of 'list'.
400 **
401 **	Side Effects:
402 **		none.
403 */
404 
405 char **
406 copyplist(list, copycont)
407 	char **list;
408 	bool copycont;
409 {
410 	register char **vp;
411 	register char **newvp;
412 
413 	for (vp = list; *vp != NULL; vp++)
414 		continue;
415 
416 	vp++;
417 
418 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
419 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
420 
421 	if (copycont)
422 	{
423 		for (vp = newvp; *vp != NULL; vp++)
424 			*vp = newstr(*vp);
425 	}
426 
427 	return (newvp);
428 }
429 /*
430 **  COPYQUEUE -- copy address queue.
431 **
432 **	This routine is the equivalent of newstr for address queues
433 **	addresses marked with QDONTSEND aren't copied
434 **
435 **	Parameters:
436 **		addr -- list of address structures to copy.
437 **
438 **	Returns:
439 **		a copy of 'addr'.
440 **
441 **	Side Effects:
442 **		none.
443 */
444 
445 ADDRESS *
446 copyqueue(addr)
447 	ADDRESS *addr;
448 {
449 	register ADDRESS *newaddr;
450 	ADDRESS *ret;
451 	register ADDRESS **tail = &ret;
452 
453 	while (addr != NULL)
454 	{
455 		if (!bitset(QDONTSEND, addr->q_flags))
456 		{
457 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
458 			STRUCTCOPY(*addr, *newaddr);
459 			*tail = newaddr;
460 			tail = &newaddr->q_next;
461 		}
462 		addr = addr->q_next;
463 	}
464 	*tail = NULL;
465 
466 	return ret;
467 }
468 /*
469 **  PRINTAV -- print argument vector.
470 **
471 **	Parameters:
472 **		av -- argument vector.
473 **
474 **	Returns:
475 **		none.
476 **
477 **	Side Effects:
478 **		prints av.
479 */
480 
481 void
482 printav(av)
483 	register char **av;
484 {
485 	while (*av != NULL)
486 	{
487 		if (tTd(0, 44))
488 			printf("\n\t%08lx=", (u_long) *av);
489 		else
490 			(void) putchar(' ');
491 		xputs(*av++);
492 	}
493 	(void) putchar('\n');
494 }
495 /*
496 **  LOWER -- turn letter into lower case.
497 **
498 **	Parameters:
499 **		c -- character to turn into lower case.
500 **
501 **	Returns:
502 **		c, in lower case.
503 **
504 **	Side Effects:
505 **		none.
506 */
507 
508 char
509 lower(c)
510 	register char c;
511 {
512 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
513 }
514 /*
515 **  XPUTS -- put string doing control escapes.
516 **
517 **	Parameters:
518 **		s -- string to put.
519 **
520 **	Returns:
521 **		none.
522 **
523 **	Side Effects:
524 **		output to stdout
525 */
526 
527 void
528 xputs(s)
529 	register const char *s;
530 {
531 	register int c;
532 	register struct metamac *mp;
533 	bool shiftout = FALSE;
534 	extern struct metamac MetaMacros[];
535 
536 	if (s == NULL)
537 	{
538 		printf("%s<null>%s", TermEscape.te_rv_on, TermEscape.te_rv_off);
539 		return;
540 	}
541 	while ((c = (*s++ & 0377)) != '\0')
542 	{
543 		if (shiftout)
544 		{
545 			printf("%s", TermEscape.te_rv_off);
546 			shiftout = FALSE;
547 		}
548 		if (!isascii(c))
549 		{
550 			if (c == MATCHREPL)
551 			{
552 				printf("%s$", TermEscape.te_rv_on);
553 				shiftout = TRUE;
554 				if (*s == '\0')
555 					continue;
556 				c = *s++ & 0377;
557 				goto printchar;
558 			}
559 			if (c == MACROEXPAND || c == MACRODEXPAND)
560 			{
561 				printf("%s$", TermEscape.te_rv_on);
562 				if (c == MACRODEXPAND)
563 					putchar('&');
564 				shiftout = TRUE;
565 				if (*s == '\0')
566 					continue;
567 				if (strchr("=~&?", *s) != NULL)
568 					putchar(*s++);
569 				if (bitset(0200, *s))
570 					printf("{%s}", macname(*s++ & 0377));
571 				else
572 					printf("%c", *s++);
573 				continue;
574 			}
575 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
576 			{
577 				if ((mp->metaval & 0377) == c)
578 				{
579 					printf("%s$%c",
580 						TermEscape.te_rv_on,
581 						mp->metaname);
582 					shiftout = TRUE;
583 					break;
584 				}
585 			}
586 			if (c == MATCHCLASS || c == MATCHNCLASS)
587 			{
588 				if (bitset(0200, *s))
589 					printf("{%s}", macname(*s++ & 0377));
590 				else if (*s != '\0')
591 					printf("%c", *s++);
592 			}
593 			if (mp->metaname != '\0')
594 				continue;
595 
596 			/* unrecognized meta character */
597 			printf("%sM-", TermEscape.te_rv_on);
598 			shiftout = TRUE;
599 			c &= 0177;
600 		}
601   printchar:
602 		if (isprint(c))
603 		{
604 			putchar(c);
605 			continue;
606 		}
607 
608 		/* wasn't a meta-macro -- find another way to print it */
609 		switch (c)
610 		{
611 		  case '\n':
612 			c = 'n';
613 			break;
614 
615 		  case '\r':
616 			c = 'r';
617 			break;
618 
619 		  case '\t':
620 			c = 't';
621 			break;
622 		}
623 		if (!shiftout)
624 		{
625 			printf("%s", TermEscape.te_rv_on);
626 			shiftout = TRUE;
627 		}
628 		if (isprint(c))
629 		{
630 			(void) putchar('\\');
631 			(void) putchar(c);
632 		}
633 		else
634 		{
635 			(void) putchar('^');
636 			(void) putchar(c ^ 0100);
637 		}
638 	}
639 	if (shiftout)
640 		printf("%s", TermEscape.te_rv_off);
641 	(void) fflush(stdout);
642 }
643 /*
644 **  MAKELOWER -- Translate a line into lower case
645 **
646 **	Parameters:
647 **		p -- the string to translate.  If NULL, return is
648 **			immediate.
649 **
650 **	Returns:
651 **		none.
652 **
653 **	Side Effects:
654 **		String pointed to by p is translated to lower case.
655 **
656 **	Called By:
657 **		parse
658 */
659 
660 void
661 makelower(p)
662 	register char *p;
663 {
664 	register char c;
665 
666 	if (p == NULL)
667 		return;
668 	for (; (c = *p) != '\0'; p++)
669 		if (isascii(c) && isupper(c))
670 			*p = tolower(c);
671 }
672 /*
673 **  BUILDFNAME -- build full name from gecos style entry.
674 **
675 **	This routine interprets the strange entry that would appear
676 **	in the GECOS field of the password file.
677 **
678 **	Parameters:
679 **		p -- name to build.
680 **		login -- the login name of this user (for &).
681 **		buf -- place to put the result.
682 **		buflen -- length of buf.
683 **
684 **	Returns:
685 **		none.
686 **
687 **	Side Effects:
688 **		none.
689 */
690 
691 void
692 buildfname(gecos, login, buf, buflen)
693 	register char *gecos;
694 	char *login;
695 	char *buf;
696 	int buflen;
697 {
698 	register char *p;
699 	register char *bp = buf;
700 
701 	if (*gecos == '*')
702 		gecos++;
703 
704 	/* copy gecos, interpolating & to be full name */
705 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
706 	{
707 		if (bp >= &buf[buflen - 1])
708 		{
709 			/* buffer overflow -- just use login name */
710 			snprintf(buf, buflen, "%s", login);
711 			return;
712 		}
713 		if (*p == '&')
714 		{
715 			/* interpolate full name */
716 			snprintf(bp, buflen - (bp - buf), "%s", login);
717 			*bp = toupper(*bp);
718 			bp += strlen(bp);
719 		}
720 		else
721 			*bp++ = *p;
722 	}
723 	*bp = '\0';
724 }
725 /*
726 **  FIXCRLF -- fix <CR><LF> in line.
727 **
728 **	Looks for the <CR><LF> combination and turns it into the
729 **	UNIX canonical <NL> character.  It only takes one line,
730 **	i.e., it is assumed that the first <NL> found is the end
731 **	of the line.
732 **
733 **	Parameters:
734 **		line -- the line to fix.
735 **		stripnl -- if true, strip the newline also.
736 **
737 **	Returns:
738 **		none.
739 **
740 **	Side Effects:
741 **		line is changed in place.
742 */
743 
744 void
745 fixcrlf(line, stripnl)
746 	char *line;
747 	bool stripnl;
748 {
749 	register char *p;
750 
751 	p = strchr(line, '\n');
752 	if (p == NULL)
753 		return;
754 	if (p > line && p[-1] == '\r')
755 		p--;
756 	if (!stripnl)
757 		*p++ = '\n';
758 	*p = '\0';
759 }
760 /*
761 **  PUTLINE -- put a line like fputs obeying SMTP conventions
762 **
763 **	This routine always guarantees outputing a newline (or CRLF,
764 **	as appropriate) at the end of the string.
765 **
766 **	Parameters:
767 **		l -- line to put.
768 **		mci -- the mailer connection information.
769 **
770 **	Returns:
771 **		none
772 **
773 **	Side Effects:
774 **		output of l to fp.
775 */
776 
777 void
778 putline(l, mci)
779 	register char *l;
780 	register MCI *mci;
781 {
782 	putxline(l, strlen(l), mci, PXLF_MAPFROM);
783 }
784 /*
785 **  PUTXLINE -- putline with flags bits.
786 **
787 **	This routine always guarantees outputing a newline (or CRLF,
788 **	as appropriate) at the end of the string.
789 **
790 **	Parameters:
791 **		l -- line to put.
792 **		len -- the length of the line.
793 **		mci -- the mailer connection information.
794 **		pxflags -- flag bits:
795 **		    PXLF_MAPFROM -- map From_ to >From_.
796 **		    PXLF_STRIP8BIT -- strip 8th bit.
797 **		    PXLF_HEADER -- map bare newline in header to newline space.
798 **
799 **	Returns:
800 **		none
801 **
802 **	Side Effects:
803 **		output of l to fp.
804 */
805 
806 void
807 putxline(l, len, mci, pxflags)
808 	register char *l;
809 	size_t len;
810 	register MCI *mci;
811 	int pxflags;
812 {
813 	register char *p, *end;
814 	int slop = 0;
815 	size_t eol_len = strlen(mci->mci_mailer->m_eol);
816 
817 	/* strip out 0200 bits -- these can look like TELNET protocol */
818 	if (bitset(MCIF_7BIT, mci->mci_flags) ||
819 	    bitset(PXLF_STRIP8BIT, pxflags))
820 	{
821 		register char svchar;
822 
823 		for (p = l; (svchar = *p) != '\0'; ++p)
824 			if (bitset(0200, svchar))
825 				*p = svchar &~ 0200;
826 	}
827 
828 	end = l + len;
829 	do
830 	{
831 		/* find the end of the line */
832 		p = memchr(l, '\n', end - l);
833 		if (p == NULL)
834 			p = end;
835 
836 		if (TrafficLogFile != NULL)
837 			fprintf(TrafficLogFile, "%05d >>> ", (int) getpid());
838 
839 		/* check for line overflow */
840 		while (mci->mci_mailer->m_linelimit > 0 &&
841 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
842 		{
843 			char *l_base = l;
844 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
845 
846 			if (l[0] == '.' && slop == 0 &&
847 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
848 			{
849 				(void) putc('.', mci->mci_out);
850 				if (!bitset(MCIF_INHEADER, mci->mci_flags))
851 					mci->mci_contentlen++;
852 				if (TrafficLogFile != NULL)
853 					(void) putc('.', TrafficLogFile);
854 			}
855 			else if (l[0] == 'F' && slop == 0 &&
856 				 bitset(PXLF_MAPFROM, pxflags) &&
857 				 strncmp(l, "From ", 5) == 0 &&
858 				 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
859 			{
860 				(void) putc('>', mci->mci_out);
861 				if (!bitset(MCIF_INHEADER, mci->mci_flags))
862 					mci->mci_contentlen++;
863 				if (TrafficLogFile != NULL)
864 					(void) putc('>', TrafficLogFile);
865 			}
866 			while (l < q)
867 			{
868 				(void) putc(*l++, mci->mci_out);
869 				if (!bitset(MCIF_INHEADER, mci->mci_flags))
870 					mci->mci_contentlen++;
871 			}
872 			(void) putc('!', mci->mci_out);
873 			if (!bitset(MCIF_INHEADER, mci->mci_flags))
874 				mci->mci_contentlen++;
875 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
876 			if (!bitset(MCIF_INHEADER, mci->mci_flags))
877 				mci->mci_contentlen += eol_len;
878 			(void) putc(' ', mci->mci_out);
879 			if (!bitset(MCIF_INHEADER, mci->mci_flags))
880 				mci->mci_contentlen++;
881 			if (TrafficLogFile != NULL)
882 			{
883 				for (l = l_base; l < q; l++)
884 					(void) putc(*l, TrafficLogFile);
885 				fprintf(TrafficLogFile, "!\n%05d >>>  ",
886 					(int) getpid());
887 			}
888 			slop = 1;
889 		}
890 
891 		/* output last part */
892 		if (l[0] == '.' && slop == 0 &&
893 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
894 		{
895 			(void) putc('.', mci->mci_out);
896 			if (!bitset(MCIF_INHEADER, mci->mci_flags))
897 				mci->mci_contentlen++;
898 			if (TrafficLogFile != NULL)
899 				(void) putc('.', TrafficLogFile);
900 		}
901 		else if (l[0] == 'F' && slop == 0 &&
902 			 bitset(PXLF_MAPFROM, pxflags) &&
903 			 strncmp(l, "From ", 5) == 0 &&
904 			 bitnset(M_ESCFROM, mci->mci_mailer->m_flags))
905 		{
906 			(void) putc('>', mci->mci_out);
907 			if (!bitset(MCIF_INHEADER, mci->mci_flags))
908 				mci->mci_contentlen++;
909 			if (TrafficLogFile != NULL)
910 				(void) putc('>', TrafficLogFile);
911 		}
912 		for ( ; l < p; ++l)
913 		{
914 			if (TrafficLogFile != NULL)
915 				(void) putc(*l, TrafficLogFile);
916 			(void) putc(*l, mci->mci_out);
917 			if (!bitset(MCIF_INHEADER, mci->mci_flags))
918 				mci->mci_contentlen++;
919 		}
920 		if (TrafficLogFile != NULL)
921 			(void) putc('\n', TrafficLogFile);
922 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
923 		if (!bitset(MCIF_INHEADER, mci->mci_flags))
924 			mci->mci_contentlen += eol_len;
925 		if (l < end && *l == '\n')
926 		{
927 			if (*++l != ' ' && *l != '\t' && *l != '\0' &&
928 			    bitset(PXLF_HEADER, pxflags))
929 			{
930 				(void) putc(' ', mci->mci_out);
931 				if (!bitset(MCIF_INHEADER, mci->mci_flags))
932 					mci->mci_contentlen++;
933 				if (TrafficLogFile != NULL)
934 					(void) putc(' ', TrafficLogFile);
935 			}
936 		}
937 	} while (l < end);
938 }
939 /*
940 **  XUNLINK -- unlink a file, doing logging as appropriate.
941 **
942 **	Parameters:
943 **		f -- name of file to unlink.
944 **
945 **	Returns:
946 **		none.
947 **
948 **	Side Effects:
949 **		f is unlinked.
950 */
951 
952 void
953 xunlink(f)
954 	char *f;
955 {
956 	register int i;
957 
958 	if (LogLevel > 98)
959 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
960 			"unlink %s",
961 			f);
962 
963 	i = unlink(f);
964 	if (i < 0 && LogLevel > 97)
965 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
966 			"%s: unlink-fail %d",
967 			f, errno);
968 }
969 /*
970 **  XFCLOSE -- close a file, doing logging as appropriate.
971 **
972 **	Parameters:
973 **		fp -- file pointer for the file to close
974 **		a, b -- miscellaneous crud to print for debugging
975 **
976 **	Returns:
977 **		none.
978 **
979 **	Side Effects:
980 **		fp is closed.
981 */
982 
983 void
984 xfclose(fp, a, b)
985 	FILE *fp;
986 	char *a, *b;
987 {
988 	if (tTd(53, 99))
989 		printf("xfclose(%lx) %s %s\n", (u_long) fp, a, b);
990 #if XDEBUG
991 	if (fileno(fp) == 1)
992 		syserr("xfclose(%s %s): fd = 1", a, b);
993 #endif
994 	if (fclose(fp) < 0 && tTd(53, 99))
995 		printf("xfclose FAILURE: %s\n", errstring(errno));
996 }
997 /*
998 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
999 **
1000 **	Parameters:
1001 **		buf -- place to put the input line.
1002 **		siz -- size of buf.
1003 **		fp -- file to read from.
1004 **		timeout -- the timeout before error occurs.
1005 **		during -- what we are trying to read (for error messages).
1006 **
1007 **	Returns:
1008 **		NULL on error (including timeout).  This will also leave
1009 **			buf containing a null string.
1010 **		buf otherwise.
1011 **
1012 **	Side Effects:
1013 **		none.
1014 */
1015 
1016 static jmp_buf	CtxReadTimeout;
1017 static void	readtimeout __P((time_t));
1018 
1019 char *
1020 sfgets(buf, siz, fp, timeout, during)
1021 	char *buf;
1022 	int siz;
1023 	FILE *fp;
1024 	time_t timeout;
1025 	char *during;
1026 {
1027 	register EVENT *ev = NULL;
1028 	register char *p;
1029 
1030 	if (fp == NULL)
1031 	{
1032 		buf[0] = '\0';
1033 		return NULL;
1034 	}
1035 
1036 	/* set the timeout */
1037 	if (timeout != 0)
1038 	{
1039 		if (setjmp(CtxReadTimeout) != 0)
1040 		{
1041 			if (LogLevel > 1)
1042 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
1043 				       "timeout waiting for input from %.100s during %s",
1044 				       CurHostName ? CurHostName : "local",
1045 				       during);
1046 			errno = 0;
1047 			buf[0] = '\0';
1048 #if XDEBUG
1049 			checkfd012(during);
1050 #endif
1051 			if (TrafficLogFile != NULL)
1052 				fprintf(TrafficLogFile, "%05d <<< [TIMEOUT]\n",
1053 					(int) getpid());
1054 			return (NULL);
1055 		}
1056 		ev = setevent(timeout, readtimeout, 0);
1057 	}
1058 
1059 	/* try to read */
1060 	p = NULL;
1061 	while (!feof(fp) && !ferror(fp))
1062 	{
1063 		errno = 0;
1064 		p = fgets(buf, siz, fp);
1065 		if (p != NULL || errno != EINTR)
1066 			break;
1067 		clearerr(fp);
1068 	}
1069 
1070 	/* clear the event if it has not sprung */
1071 	clrevent(ev);
1072 
1073 	/* clean up the books and exit */
1074 	LineNumber++;
1075 	if (p == NULL)
1076 	{
1077 		buf[0] = '\0';
1078 		if (TrafficLogFile != NULL)
1079 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
1080 		return (NULL);
1081 	}
1082 	if (TrafficLogFile != NULL)
1083 		fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
1084 	if (SevenBitInput)
1085 	{
1086 		for (p = buf; *p != '\0'; p++)
1087 			*p &= ~0200;
1088 	}
1089 	else if (!HasEightBits)
1090 	{
1091 		for (p = buf; *p != '\0'; p++)
1092 		{
1093 			if (bitset(0200, *p))
1094 			{
1095 				HasEightBits = TRUE;
1096 				break;
1097 			}
1098 		}
1099 	}
1100 	return (buf);
1101 }
1102 
1103 /* ARGSUSED */
1104 static void
1105 readtimeout(timeout)
1106 	time_t timeout;
1107 {
1108 	longjmp(CtxReadTimeout, 1);
1109 }
1110 /*
1111 **  FGETFOLDED -- like fgets, but know about folded lines.
1112 **
1113 **	Parameters:
1114 **		buf -- place to put result.
1115 **		n -- bytes available.
1116 **		f -- file to read from.
1117 **
1118 **	Returns:
1119 **		input line(s) on success, NULL on error or EOF.
1120 **		This will normally be buf -- unless the line is too
1121 **			long, when it will be xalloc()ed.
1122 **
1123 **	Side Effects:
1124 **		buf gets lines from f, with continuation lines (lines
1125 **		with leading white space) appended.  CRLF's are mapped
1126 **		into single newlines.  Any trailing NL is stripped.
1127 */
1128 
1129 char *
1130 fgetfolded(buf, n, f)
1131 	char *buf;
1132 	register int n;
1133 	FILE *f;
1134 {
1135 	register char *p = buf;
1136 	char *bp = buf;
1137 	register int i;
1138 
1139 	n--;
1140 	while ((i = getc(f)) != EOF)
1141 	{
1142 		if (i == '\r')
1143 		{
1144 			i = getc(f);
1145 			if (i != '\n')
1146 			{
1147 				if (i != EOF)
1148 					(void) ungetc(i, f);
1149 				i = '\r';
1150 			}
1151 		}
1152 		if (--n <= 0)
1153 		{
1154 			/* allocate new space */
1155 			char *nbp;
1156 			int nn;
1157 
1158 			nn = (p - bp);
1159 			if (nn < MEMCHUNKSIZE)
1160 				nn *= 2;
1161 			else
1162 				nn += MEMCHUNKSIZE;
1163 			nbp = xalloc(nn);
1164 			bcopy(bp, nbp, p - bp);
1165 			p = &nbp[p - bp];
1166 			if (bp != buf)
1167 				free(bp);
1168 			bp = nbp;
1169 			n = nn - (p - bp);
1170 		}
1171 		*p++ = i;
1172 		if (i == '\n')
1173 		{
1174 			LineNumber++;
1175 			i = getc(f);
1176 			if (i != EOF)
1177 				(void) ungetc(i, f);
1178 			if (i != ' ' && i != '\t')
1179 				break;
1180 		}
1181 	}
1182 	if (p == bp)
1183 		return (NULL);
1184 	if (p[-1] == '\n')
1185 		p--;
1186 	*p = '\0';
1187 	return (bp);
1188 }
1189 /*
1190 **  CURTIME -- return current time.
1191 **
1192 **	Parameters:
1193 **		none.
1194 **
1195 **	Returns:
1196 **		the current time.
1197 **
1198 **	Side Effects:
1199 **		none.
1200 */
1201 
1202 time_t
1203 curtime()
1204 {
1205 	auto time_t t;
1206 
1207 	(void) time(&t);
1208 	return (t);
1209 }
1210 /*
1211 **  ATOBOOL -- convert a string representation to boolean.
1212 **
1213 **	Defaults to "TRUE"
1214 **
1215 **	Parameters:
1216 **		s -- string to convert.  Takes "tTyY" as true,
1217 **			others as false.
1218 **
1219 **	Returns:
1220 **		A boolean representation of the string.
1221 **
1222 **	Side Effects:
1223 **		none.
1224 */
1225 
1226 bool
1227 atobool(s)
1228 	register char *s;
1229 {
1230 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1231 		return (TRUE);
1232 	return (FALSE);
1233 }
1234 /*
1235 **  ATOOCT -- convert a string representation to octal.
1236 **
1237 **	Parameters:
1238 **		s -- string to convert.
1239 **
1240 **	Returns:
1241 **		An integer representing the string interpreted as an
1242 **		octal number.
1243 **
1244 **	Side Effects:
1245 **		none.
1246 */
1247 
1248 int
1249 atooct(s)
1250 	register char *s;
1251 {
1252 	register int i = 0;
1253 
1254 	while (*s >= '0' && *s <= '7')
1255 		i = (i << 3) | (*s++ - '0');
1256 	return (i);
1257 }
1258 /*
1259 **  BITINTERSECT -- tell if two bitmaps intersect
1260 **
1261 **	Parameters:
1262 **		a, b -- the bitmaps in question
1263 **
1264 **	Returns:
1265 **		TRUE if they have a non-null intersection
1266 **		FALSE otherwise
1267 **
1268 **	Side Effects:
1269 **		none.
1270 */
1271 
1272 bool
1273 bitintersect(a, b)
1274 	BITMAP a;
1275 	BITMAP b;
1276 {
1277 	int i;
1278 
1279 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1280 		if ((a[i] & b[i]) != 0)
1281 			return (TRUE);
1282 	return (FALSE);
1283 }
1284 /*
1285 **  BITZEROP -- tell if a bitmap is all zero
1286 **
1287 **	Parameters:
1288 **		map -- the bit map to check
1289 **
1290 **	Returns:
1291 **		TRUE if map is all zero.
1292 **		FALSE if there are any bits set in map.
1293 **
1294 **	Side Effects:
1295 **		none.
1296 */
1297 
1298 bool
1299 bitzerop(map)
1300 	BITMAP map;
1301 {
1302 	int i;
1303 
1304 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1305 		if (map[i] != 0)
1306 			return (FALSE);
1307 	return (TRUE);
1308 }
1309 /*
1310 **  STRCONTAINEDIN -- tell if one string is contained in another
1311 **
1312 **	Parameters:
1313 **		a -- possible substring.
1314 **		b -- possible superstring.
1315 **
1316 **	Returns:
1317 **		TRUE if a is contained in b.
1318 **		FALSE otherwise.
1319 */
1320 
1321 bool
1322 strcontainedin(a, b)
1323 	register char *a;
1324 	register char *b;
1325 {
1326 	int la;
1327 	int lb;
1328 	int c;
1329 
1330 	la = strlen(a);
1331 	lb = strlen(b);
1332 	c = *a;
1333 	if (isascii(c) && isupper(c))
1334 		c = tolower(c);
1335 	for (; lb-- >= la; b++)
1336 	{
1337 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1338 			continue;
1339 		if (strncasecmp(a, b, la) == 0)
1340 			return TRUE;
1341 	}
1342 	return FALSE;
1343 }
1344 /*
1345 **  CHECKFD012 -- check low numbered file descriptors
1346 **
1347 **	File descriptors 0, 1, and 2 should be open at all times.
1348 **	This routine verifies that, and fixes it if not true.
1349 **
1350 **	Parameters:
1351 **		where -- a tag printed if the assertion failed
1352 **
1353 **	Returns:
1354 **		none
1355 */
1356 
1357 void
1358 checkfd012(where)
1359 	char *where;
1360 {
1361 #if XDEBUG
1362 	register int i;
1363 
1364 	for (i = 0; i < 3; i++)
1365 		fill_fd(i, where);
1366 #endif /* XDEBUG */
1367 }
1368 /*
1369 **  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1370 **
1371 **	Parameters:
1372 **		fd -- file descriptor to check.
1373 **		where -- tag to print on failure.
1374 **
1375 **	Returns:
1376 **		none.
1377 */
1378 
1379 void
1380 checkfdopen(fd, where)
1381 	int fd;
1382 	char *where;
1383 {
1384 #if XDEBUG
1385 	struct stat st;
1386 
1387 	if (fstat(fd, &st) < 0 && errno == EBADF)
1388 	{
1389 		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1390 		printopenfds(TRUE);
1391 	}
1392 #endif
1393 }
1394 /*
1395 **  CHECKFDS -- check for new or missing file descriptors
1396 **
1397 **	Parameters:
1398 **		where -- tag for printing.  If null, take a base line.
1399 **
1400 **	Returns:
1401 **		none
1402 **
1403 **	Side Effects:
1404 **		If where is set, shows changes since the last call.
1405 */
1406 
1407 void
1408 checkfds(where)
1409 	char *where;
1410 {
1411 	int maxfd;
1412 	register int fd;
1413 	bool printhdr = TRUE;
1414 	int save_errno = errno;
1415 	static BITMAP baseline;
1416 	extern int DtableSize;
1417 
1418 	if (DtableSize > 256)
1419 		maxfd = 256;
1420 	else
1421 		maxfd = DtableSize;
1422 	if (where == NULL)
1423 		clrbitmap(baseline);
1424 
1425 	for (fd = 0; fd < maxfd; fd++)
1426 	{
1427 		struct stat stbuf;
1428 
1429 		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1430 		{
1431 			if (!bitnset(fd, baseline))
1432 				continue;
1433 			clrbitn(fd, baseline);
1434 		}
1435 		else if (!bitnset(fd, baseline))
1436 			setbitn(fd, baseline);
1437 		else
1438 			continue;
1439 
1440 		/* file state has changed */
1441 		if (where == NULL)
1442 			continue;
1443 		if (printhdr)
1444 		{
1445 			sm_syslog(LOG_DEBUG, CurEnv->e_id,
1446 				"%s: changed fds:",
1447 				where);
1448 			printhdr = FALSE;
1449 		}
1450 		dumpfd(fd, TRUE, TRUE);
1451 	}
1452 	errno = save_errno;
1453 }
1454 /*
1455 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1456 **
1457 **	Parameters:
1458 **		logit -- if set, send output to syslog; otherwise
1459 **			print for debugging.
1460 **
1461 **	Returns:
1462 **		none.
1463 */
1464 
1465 #include <arpa/inet.h>
1466 
1467 void
1468 printopenfds(logit)
1469 	bool logit;
1470 {
1471 	register int fd;
1472 	extern int DtableSize;
1473 
1474 	for (fd = 0; fd < DtableSize; fd++)
1475 		dumpfd(fd, FALSE, logit);
1476 }
1477 /*
1478 **  DUMPFD -- dump a file descriptor
1479 **
1480 **	Parameters:
1481 **		fd -- the file descriptor to dump.
1482 **		printclosed -- if set, print a notification even if
1483 **			it is closed; otherwise print nothing.
1484 **		logit -- if set, send output to syslog instead of stdout.
1485 */
1486 
1487 void
1488 dumpfd(fd, printclosed, logit)
1489 	int fd;
1490 	bool printclosed;
1491 	bool logit;
1492 {
1493 	register char *p;
1494 	char *hp;
1495 #ifdef S_IFSOCK
1496 	SOCKADDR sa;
1497 #endif
1498 	auto SOCKADDR_LEN_T slen;
1499 	int i;
1500 #if STAT64 > 0
1501 	struct stat64 st;
1502 #else
1503 	struct stat st;
1504 #endif
1505 	char buf[200];
1506 
1507 	p = buf;
1508 	snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1509 	p += strlen(p);
1510 
1511 	if (
1512 #if STAT64 > 0
1513 	    fstat64(fd, &st)
1514 #else
1515 	    fstat(fd, &st)
1516 #endif
1517 	    < 0)
1518 	{
1519 		if (errno != EBADF)
1520 		{
1521 			snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
1522 				errstring(errno));
1523 			goto printit;
1524 		}
1525 		else if (printclosed)
1526 		{
1527 			snprintf(p, SPACELEFT(buf, p), "CLOSED");
1528 			goto printit;
1529 		}
1530 		return;
1531 	}
1532 
1533 	i = fcntl(fd, F_GETFL, NULL);
1534 	if (i != -1)
1535 	{
1536 		snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1537 		p += strlen(p);
1538 	}
1539 
1540 	snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode);
1541 	p += strlen(p);
1542 	switch (st.st_mode & S_IFMT)
1543 	{
1544 #ifdef S_IFSOCK
1545 	  case S_IFSOCK:
1546 		snprintf(p, SPACELEFT(buf, p), "SOCK ");
1547 		p += strlen(p);
1548 		slen = sizeof sa;
1549 		if (getsockname(fd, &sa.sa, &slen) < 0)
1550 			snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1551 		else
1552 		{
1553 			hp = hostnamebyanyaddr(&sa);
1554 			if (sa.sa.sa_family == AF_INET)
1555 				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1556 					hp, ntohs(sa.sin.sin_port));
1557 			else
1558 				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1559 		}
1560 		p += strlen(p);
1561 		snprintf(p, SPACELEFT(buf, p), "->");
1562 		p += strlen(p);
1563 		slen = sizeof sa;
1564 		if (getpeername(fd, &sa.sa, &slen) < 0)
1565 			snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1566 		else
1567 		{
1568 			hp = hostnamebyanyaddr(&sa);
1569 			if (sa.sa.sa_family == AF_INET)
1570 				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1571 					hp, ntohs(sa.sin.sin_port));
1572 			else
1573 				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1574 		}
1575 		break;
1576 #endif
1577 
1578 	  case S_IFCHR:
1579 		snprintf(p, SPACELEFT(buf, p), "CHR: ");
1580 		p += strlen(p);
1581 		goto defprint;
1582 
1583 	  case S_IFBLK:
1584 		snprintf(p, SPACELEFT(buf, p), "BLK: ");
1585 		p += strlen(p);
1586 		goto defprint;
1587 
1588 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1589 	  case S_IFIFO:
1590 		snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1591 		p += strlen(p);
1592 		goto defprint;
1593 #endif
1594 
1595 #ifdef S_IFDIR
1596 	  case S_IFDIR:
1597 		snprintf(p, SPACELEFT(buf, p), "DIR: ");
1598 		p += strlen(p);
1599 		goto defprint;
1600 #endif
1601 
1602 #ifdef S_IFLNK
1603 	  case S_IFLNK:
1604 		snprintf(p, SPACELEFT(buf, p), "LNK: ");
1605 		p += strlen(p);
1606 		goto defprint;
1607 #endif
1608 
1609 	  default:
1610 defprint:
1611 		if (sizeof st.st_ino > sizeof (long))
1612 			snprintf(p, SPACELEFT(buf, p),
1613 				 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
1614 				 major(st.st_dev), minor(st.st_dev),
1615 				 quad_to_string(st.st_ino),
1616 				 st.st_nlink, st.st_uid, st.st_gid);
1617 		else
1618 			snprintf(p, SPACELEFT(buf, p),
1619 				"dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
1620 				major(st.st_dev), minor(st.st_dev),
1621 				(unsigned long) st.st_ino,
1622 				st.st_nlink, st.st_uid, st.st_gid);
1623 		if (sizeof st.st_size > sizeof (long))
1624 			snprintf(p, SPACELEFT(buf, p), "size=%s",
1625 				 quad_to_string(st.st_size));
1626 		else
1627 			snprintf(p, SPACELEFT(buf, p), "size=%lu",
1628 				 (unsigned long) st.st_size);
1629 		break;
1630 	}
1631 
1632 printit:
1633 	if (logit)
1634 		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1635 			"%.800s", buf);
1636 	else
1637 		printf("%s\n", buf);
1638 }
1639 /*
1640 **  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1641 **
1642 **	Parameters:
1643 **		host -- the host to shorten (stripped in place).
1644 **
1645 **	Returns:
1646 **		none.
1647 */
1648 
1649 void
1650 shorten_hostname(host)
1651 	char host[];
1652 {
1653 	register char *p;
1654 	char *mydom;
1655 	int i;
1656 	bool canon = FALSE;
1657 
1658 	/* strip off final dot */
1659 	p = &host[strlen(host) - 1];
1660 	if (*p == '.')
1661 	{
1662 		*p = '\0';
1663 		canon = TRUE;
1664 	}
1665 
1666 	/* see if there is any domain at all -- if not, we are done */
1667 	p = strchr(host, '.');
1668 	if (p == NULL)
1669 		return;
1670 
1671 	/* yes, we have a domain -- see if it looks like us */
1672 	mydom = macvalue('m', CurEnv);
1673 	if (mydom == NULL)
1674 		mydom = "";
1675 	i = strlen(++p);
1676 	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1677 	    (mydom[i] == '.' || mydom[i] == '\0'))
1678 		*--p = '\0';
1679 }
1680 /*
1681 **  PROG_OPEN -- open a program for reading
1682 **
1683 **	Parameters:
1684 **		argv -- the argument list.
1685 **		pfd -- pointer to a place to store the file descriptor.
1686 **		e -- the current envelope.
1687 **
1688 **	Returns:
1689 **		pid of the process -- -1 if it failed.
1690 */
1691 
1692 int
1693 prog_open(argv, pfd, e)
1694 	char **argv;
1695 	int *pfd;
1696 	ENVELOPE *e;
1697 {
1698 	int pid;
1699 	int i;
1700 	int saveerrno;
1701 	int fdv[2];
1702 	char *p, *q;
1703 	char buf[MAXLINE + 1];
1704 	extern int DtableSize;
1705 
1706 	if (pipe(fdv) < 0)
1707 	{
1708 		syserr("%s: cannot create pipe for stdout", argv[0]);
1709 		return -1;
1710 	}
1711 	pid = fork();
1712 	if (pid < 0)
1713 	{
1714 		syserr("%s: cannot fork", argv[0]);
1715 		close(fdv[0]);
1716 		close(fdv[1]);
1717 		return -1;
1718 	}
1719 	if (pid > 0)
1720 	{
1721 		/* parent */
1722 		close(fdv[1]);
1723 		*pfd = fdv[0];
1724 		return pid;
1725 	}
1726 
1727 	/* child -- close stdin */
1728 	close(0);
1729 
1730 	/* stdout goes back to parent */
1731 	close(fdv[0]);
1732 	if (dup2(fdv[1], 1) < 0)
1733 	{
1734 		syserr("%s: cannot dup2 for stdout", argv[0]);
1735 		_exit(EX_OSERR);
1736 	}
1737 	close(fdv[1]);
1738 
1739 	/* stderr goes to transcript if available */
1740 	if (e->e_xfp != NULL)
1741 	{
1742 		if (dup2(fileno(e->e_xfp), 2) < 0)
1743 		{
1744 			syserr("%s: cannot dup2 for stderr", argv[0]);
1745 			_exit(EX_OSERR);
1746 		}
1747 	}
1748 
1749 	/* this process has no right to the queue file */
1750 	if (e->e_lockfp != NULL)
1751 		close(fileno(e->e_lockfp));
1752 
1753 	/* run as default user */
1754 	endpwent();
1755 	if (setgid(DefGid) < 0 && geteuid() == 0)
1756 		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
1757 	if (setuid(DefUid) < 0 && geteuid() == 0)
1758 		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
1759 
1760 	/* run in some directory */
1761 	if (ProgMailer != NULL)
1762 		p = ProgMailer->m_execdir;
1763 	else
1764 		p = NULL;
1765 	for (; p != NULL; p = q)
1766 	{
1767 		q = strchr(p, ':');
1768 		if (q != NULL)
1769 			*q = '\0';
1770 		expand(p, buf, sizeof buf, e);
1771 		if (q != NULL)
1772 			*q++ = ':';
1773 		if (buf[0] != '\0' && chdir(buf) >= 0)
1774 			break;
1775 	}
1776 	if (p == NULL)
1777 	{
1778 		/* backup directories */
1779 		if (chdir("/tmp") < 0)
1780 			(void) chdir("/");
1781 	}
1782 
1783 	/* arrange for all the files to be closed */
1784 	for (i = 3; i < DtableSize; i++)
1785 	{
1786 		register int j;
1787 
1788 		if ((j = fcntl(i, F_GETFD, 0)) != -1)
1789 			(void) fcntl(i, F_SETFD, j | 1);
1790 	}
1791 
1792 	/* now exec the process */
1793 	execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
1794 
1795 	/* woops!  failed */
1796 	saveerrno = errno;
1797 	syserr("%s: cannot exec", argv[0]);
1798 	if (transienterror(saveerrno))
1799 		_exit(EX_OSERR);
1800 	_exit(EX_CONFIG);
1801 	return -1;	/* avoid compiler warning on IRIX */
1802 }
1803 /*
1804 **  GET_COLUMN  -- look up a Column in a line buffer
1805 **
1806 **	Parameters:
1807 **		line -- the raw text line to search.
1808 **		col -- the column number to fetch.
1809 **		delim -- the delimiter between columns.  If null,
1810 **			use white space.
1811 **		buf -- the output buffer.
1812 **		buflen -- the length of buf.
1813 **
1814 **	Returns:
1815 **		buf if successful.
1816 **		NULL otherwise.
1817 */
1818 
1819 char *
1820 get_column(line, col, delim, buf, buflen)
1821 	char line[];
1822 	int col;
1823 	char delim;
1824 	char buf[];
1825 	int buflen;
1826 {
1827 	char *p;
1828 	char *begin, *end;
1829 	int i;
1830 	char delimbuf[4];
1831 
1832 	if (delim == '\0')
1833 		strcpy(delimbuf, "\n\t ");
1834 	else
1835 	{
1836 		delimbuf[0] = delim;
1837 		delimbuf[1] = '\0';
1838 	}
1839 
1840 	p = line;
1841 	if (*p == '\0')
1842 		return NULL;			/* line empty */
1843 	if (*p == delim && col == 0)
1844 		return NULL;			/* first column empty */
1845 
1846 	begin = line;
1847 
1848 	if (col == 0 && delim == '\0')
1849 	{
1850 		while (*begin != '\0' && isascii(*begin) && isspace(*begin))
1851 			begin++;
1852 	}
1853 
1854 	for (i = 0; i < col; i++)
1855 	{
1856 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
1857 			return NULL;		/* no such column */
1858 		begin++;
1859 		if (delim == '\0')
1860 		{
1861 			while (*begin != '\0' && isascii(*begin) && isspace(*begin))
1862 				begin++;
1863 		}
1864 	}
1865 
1866 	end = strpbrk(begin, delimbuf);
1867 	if (end == NULL)
1868 		i = strlen(begin);
1869 	else
1870 		i = end - begin;
1871 	if (i >= buflen)
1872 		i = buflen - 1;
1873 	strncpy(buf, begin, i);
1874 	buf[i] = '\0';
1875 	return buf;
1876 }
1877 /*
1878 **  CLEANSTRCPY -- copy string keeping out bogus characters
1879 **
1880 **	Parameters:
1881 **		t -- "to" string.
1882 **		f -- "from" string.
1883 **		l -- length of space available in "to" string.
1884 **
1885 **	Returns:
1886 **		none.
1887 */
1888 
1889 void
1890 cleanstrcpy(t, f, l)
1891 	register char *t;
1892 	register char *f;
1893 	int l;
1894 {
1895 	/* check for newlines and log if necessary */
1896 	(void) denlstring(f, TRUE, TRUE);
1897 
1898 	l--;
1899 	while (l > 0 && *f != '\0')
1900 	{
1901 		if (isascii(*f) &&
1902 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
1903 		{
1904 			l--;
1905 			*t++ = *f;
1906 		}
1907 		f++;
1908 	}
1909 	*t = '\0';
1910 }
1911 /*
1912 **  DENLSTRING -- convert newlines in a string to spaces
1913 **
1914 **	Parameters:
1915 **		s -- the input string
1916 **		strict -- if set, don't permit continuation lines.
1917 **		logattacks -- if set, log attempted attacks.
1918 **
1919 **	Returns:
1920 **		A pointer to a version of the string with newlines
1921 **		mapped to spaces.  This should be copied.
1922 */
1923 
1924 char *
1925 denlstring(s, strict, logattacks)
1926 	char *s;
1927 	bool strict;
1928 	bool logattacks;
1929 {
1930 	register char *p;
1931 	int l;
1932 	static char *bp = NULL;
1933 	static int bl = 0;
1934 
1935 	p = s;
1936 	while ((p = strchr(p, '\n')) != NULL)
1937 		if (strict || (*++p != ' ' && *p != '\t'))
1938 			break;
1939 	if (p == NULL)
1940 		return s;
1941 
1942 	l = strlen(s) + 1;
1943 	if (bl < l)
1944 	{
1945 		/* allocate more space */
1946 		if (bp != NULL)
1947 			free(bp);
1948 		bp = xalloc(l);
1949 		bl = l;
1950 	}
1951 	strcpy(bp, s);
1952 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
1953 		*p++ = ' ';
1954 
1955 	if (logattacks)
1956 	{
1957 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
1958 			"POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
1959 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
1960 			shortenstring(bp, MAXSHORTSTR));
1961 	}
1962 
1963 	return bp;
1964 }
1965 /*
1966 **  PATH_IS_DIR -- check to see if file exists and is a directory.
1967 **
1968 **	There are some additional checks for security violations in
1969 **	here.  This routine is intended to be used for the host status
1970 **	support.
1971 **
1972 **	Parameters:
1973 **		pathname -- pathname to check for directory-ness.
1974 **		createflag -- if set, create directory if needed.
1975 **
1976 **	Returns:
1977 **		TRUE -- if the indicated pathname is a directory
1978 **		FALSE -- otherwise
1979 */
1980 
1981 int
1982 path_is_dir(pathname, createflag)
1983 	char *pathname;
1984 	bool createflag;
1985 {
1986 	struct stat statbuf;
1987 
1988 #if HASLSTAT
1989 	if (lstat(pathname, &statbuf) < 0)
1990 #else
1991 	if (stat(pathname, &statbuf) < 0)
1992 #endif
1993 	{
1994 		if (errno != ENOENT || !createflag)
1995 			return FALSE;
1996 		if (mkdir(pathname, 0755) < 0)
1997 			return FALSE;
1998 		return TRUE;
1999 	}
2000 	if (!S_ISDIR(statbuf.st_mode))
2001 	{
2002 		errno = ENOTDIR;
2003 		return FALSE;
2004 	}
2005 
2006 	/* security: don't allow writable directories */
2007 	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2008 	{
2009 		errno = EACCES;
2010 		return FALSE;
2011 	}
2012 
2013 	return TRUE;
2014 }
2015 /*
2016 **  PROC_LIST_ADD -- add process id to list of our children
2017 **
2018 **	Parameters:
2019 **		pid -- pid to add to list.
2020 **
2021 **	Returns:
2022 **		none
2023 */
2024 
2025 struct procs
2026 {
2027 	pid_t	proc_pid;
2028 	char 	*proc_task;
2029 };
2030 
2031 static struct procs	*ProcListVec	= NULL;
2032 static int	ProcListSize	= 0;
2033 
2034 #define NO_PID		((pid_t) 0)
2035 #ifndef PROC_LIST_SEG
2036 # define PROC_LIST_SEG	32		/* number of pids to alloc at a time */
2037 #endif
2038 
2039 void
2040 proc_list_add(pid, task)
2041 	pid_t pid;
2042 	char *task;
2043 {
2044 	int i;
2045 
2046 	for (i = 0; i < ProcListSize; i++)
2047 	{
2048 		if (ProcListVec[i].proc_pid == NO_PID)
2049 			break;
2050 	}
2051 	if (i >= ProcListSize)
2052 	{
2053 		/* probe the existing vector to avoid growing infinitely */
2054 		proc_list_probe();
2055 
2056 		/* now scan again */
2057 		for (i = 0; i < ProcListSize; i++)
2058 		{
2059 			if (ProcListVec[i].proc_pid == NO_PID)
2060 				break;
2061 		}
2062 	}
2063 	if (i >= ProcListSize)
2064 	{
2065 		/* grow process list */
2066 		struct procs *npv;
2067 
2068 		npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG));
2069 		if (ProcListSize > 0)
2070 		{
2071 			bcopy(ProcListVec, npv, ProcListSize *
2072 						sizeof (struct procs));
2073 			free(ProcListVec);
2074 		}
2075 		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2076 		{
2077 			npv[i].proc_pid = NO_PID;
2078 			npv[i].proc_task = NULL;
2079 		}
2080 		i = ProcListSize;
2081 		ProcListSize += PROC_LIST_SEG;
2082 		ProcListVec = npv;
2083 	}
2084 	ProcListVec[i].proc_pid = pid;
2085 	ProcListVec[i].proc_task = newstr(task);
2086 
2087 	/* if process adding itself, it's not a child */
2088 	if (pid != getpid())
2089 		CurChildren++;
2090 }
2091 /*
2092 **  PROC_LIST_SET -- set pid task in process list
2093 **
2094 **	Parameters:
2095 **		pid -- pid to set
2096 **		task -- task of pid
2097 **
2098 **	Returns:
2099 **		none.
2100 */
2101 
2102 void
2103 proc_list_set(pid, task)
2104 	pid_t pid;
2105 	char *task;
2106 {
2107 	int i;
2108 
2109 	for (i = 0; i < ProcListSize; i++)
2110 	{
2111 		if (ProcListVec[i].proc_pid == pid)
2112 		{
2113 			if (ProcListVec[i].proc_task != NULL)
2114 				free(ProcListVec[i].proc_task);
2115 			ProcListVec[i].proc_task = newstr(task);
2116 			break;
2117 		}
2118 	}
2119 }
2120 /*
2121 **  PROC_LIST_DROP -- drop pid from process list
2122 **
2123 **	Parameters:
2124 **		pid -- pid to drop
2125 **
2126 **	Returns:
2127 **		none.
2128 */
2129 
2130 void
2131 proc_list_drop(pid)
2132 	pid_t pid;
2133 {
2134 	int i;
2135 
2136 	for (i = 0; i < ProcListSize; i++)
2137 	{
2138 		if (ProcListVec[i].proc_pid == pid)
2139 		{
2140 			ProcListVec[i].proc_pid = NO_PID;
2141 			if (ProcListVec[i].proc_task != NULL)
2142 			{
2143 				free(ProcListVec[i].proc_task);
2144 				ProcListVec[i].proc_task = NULL;
2145 			}
2146 			break;
2147 		}
2148 	}
2149 	if (CurChildren > 0)
2150 		CurChildren--;
2151 }
2152 /*
2153 **  PROC_LIST_CLEAR -- clear the process list
2154 **
2155 **	Parameters:
2156 **		none.
2157 **
2158 **	Returns:
2159 **		none.
2160 */
2161 
2162 void
2163 proc_list_clear()
2164 {
2165 	int i;
2166 
2167 	/* start from 1 since 0 is the daemon itself */
2168 	for (i = 1; i < ProcListSize; i++)
2169 	{
2170 		ProcListVec[i].proc_pid = NO_PID;
2171 		if (ProcListVec[i].proc_task != NULL)
2172 		{
2173 			free(ProcListVec[i].proc_task);
2174 			ProcListVec[i].proc_task = NULL;
2175 		}
2176 	}
2177 	CurChildren = 0;
2178 }
2179 /*
2180 **  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2181 **
2182 **	Parameters:
2183 **		none
2184 **
2185 **	Returns:
2186 **		none
2187 */
2188 
2189 void
2190 proc_list_probe()
2191 {
2192 	int i;
2193 
2194 	/* start from 1 since 0 is the daemon itself */
2195 	for (i = 1; i < ProcListSize; i++)
2196 	{
2197 		if (ProcListVec[i].proc_pid == NO_PID)
2198 			continue;
2199 		if (kill(ProcListVec[i].proc_pid, 0) < 0)
2200 		{
2201 			if (LogLevel > 3)
2202 				sm_syslog(LOG_DEBUG, CurEnv->e_id,
2203 					"proc_list_probe: lost pid %d",
2204 					(int) ProcListVec[i].proc_pid);
2205 			ProcListVec[i].proc_pid = NO_PID;
2206 			if (ProcListVec[i].proc_task != NULL)
2207 			{
2208 				free(ProcListVec[i].proc_task);
2209 				ProcListVec[i].proc_task = NULL;
2210 			}
2211 			CurChildren--;
2212 		}
2213 	}
2214 	if (CurChildren < 0)
2215 		CurChildren = 0;
2216 }
2217 /*
2218 **  PROC_LIST_DISPLAY -- display the process list
2219 **
2220 **	Parameters:
2221 **		out -- output file pointer
2222 **
2223 **	Returns:
2224 **		none.
2225 */
2226 
2227 void
2228 proc_list_display(out)
2229 	FILE *out;
2230 {
2231 	int i;
2232 
2233 	for (i = 0; i < ProcListSize; i++)
2234 	{
2235 		if (ProcListVec[i].proc_pid == NO_PID)
2236 			continue;
2237 
2238 		fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
2239 			ProcListVec[i].proc_task != NULL ?
2240 			ProcListVec[i].proc_task : "(unknown)",
2241 			(OpMode == MD_SMTP ||
2242 			 OpMode == MD_DAEMON ||
2243 			 OpMode == MD_ARPAFTP) ? "\r" : "");
2244 	}
2245 }
2246 /*
2247 **  SM_STRCASECMP -- 8-bit clean version of strcasecmp
2248 **
2249 **	Thank you, vendors, for making this all necessary.
2250 */
2251 
2252 /*
2253  * Copyright (c) 1987, 1993
2254  *	The Regents of the University of California.  All rights reserved.
2255  *
2256  * Redistribution and use in source and binary forms, with or without
2257  * modification, are permitted provided that the following conditions
2258  * are met:
2259  * 1. Redistributions of source code must retain the above copyright
2260  *    notice, this list of conditions and the following disclaimer.
2261  * 2. Redistributions in binary form must reproduce the above copyright
2262  *    notice, this list of conditions and the following disclaimer in the
2263  *    documentation and/or other materials provided with the distribution.
2264  * 3. All advertising materials mentioning features or use of this software
2265  *    must display the following acknowledgement:
2266  *	This product includes software developed by the University of
2267  *	California, Berkeley and its contributors.
2268  * 4. Neither the name of the University nor the names of its contributors
2269  *    may be used to endorse or promote products derived from this software
2270  *    without specific prior written permission.
2271  *
2272  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2273  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2274  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2275  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2276  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2277  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2278  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2279  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2280  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2281  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2282  * SUCH DAMAGE.
2283  */
2284 
2285 #if defined(LIBC_SCCS) && !defined(lint)
2286 static char sccsid[] = "@(#)strcasecmp.c	8.1 (Berkeley) 6/4/93";
2287 #endif /* LIBC_SCCS and not lint */
2288 
2289 /*
2290  * This array is designed for mapping upper and lower case letter
2291  * together for a case independent comparison.  The mappings are
2292  * based upon ascii character sequences.
2293  */
2294 static const u_char charmap[] = {
2295 	0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
2296 	0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
2297 	0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
2298 	0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
2299 	0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
2300 	0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
2301 	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
2302 	0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
2303 	0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2304 	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2305 	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2306 	0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
2307 	0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2308 	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2309 	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2310 	0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
2311 	0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
2312 	0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
2313 	0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
2314 	0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
2315 	0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
2316 	0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
2317 	0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
2318 	0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
2319 	0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
2320 	0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
2321 	0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
2322 	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
2323 	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
2324 	0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
2325 	0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
2326 	0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
2327 };
2328 
2329 int
2330 sm_strcasecmp(s1, s2)
2331 	const char *s1, *s2;
2332 {
2333 	register const u_char *cm = charmap,
2334 			*us1 = (const u_char *)s1,
2335 			*us2 = (const u_char *)s2;
2336 
2337 	while (cm[*us1] == cm[*us2++])
2338 		if (*us1++ == '\0')
2339 			return (0);
2340 	return (cm[*us1] - cm[*--us2]);
2341 }
2342 
2343 int
2344 sm_strncasecmp(s1, s2, n)
2345 	const char *s1, *s2;
2346 	register size_t n;
2347 {
2348 	if (n != 0) {
2349 		register const u_char *cm = charmap,
2350 				*us1 = (const u_char *)s1,
2351 				*us2 = (const u_char *)s2;
2352 
2353 		do {
2354 			if (cm[*us1] != cm[*us2++])
2355 				return (cm[*us1] - cm[*--us2]);
2356 			if (*us1++ == '\0')
2357 				break;
2358 		} while (--n != 0);
2359 	}
2360 	return (0);
2361 }
2362