xref: /freebsd/contrib/sendmail/src/util.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
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.168 (Berkeley) 1/21/1999";
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 	int save_errno;
1030 
1031 	if (fp == NULL)
1032 	{
1033 		buf[0] = '\0';
1034 		return NULL;
1035 	}
1036 
1037 	/* set the timeout */
1038 	if (timeout != 0)
1039 	{
1040 		if (setjmp(CtxReadTimeout) != 0)
1041 		{
1042 			if (LogLevel > 1)
1043 				sm_syslog(LOG_NOTICE, CurEnv->e_id,
1044 				       "timeout waiting for input from %.100s during %s",
1045 				       CurHostName ? CurHostName : "local",
1046 				       during);
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 			errno = 0;
1055 			return (NULL);
1056 		}
1057 		ev = setevent(timeout, readtimeout, 0);
1058 	}
1059 
1060 	/* try to read */
1061 	p = NULL;
1062 	errno = 0;
1063 	while (!feof(fp) && !ferror(fp))
1064 	{
1065 		errno = 0;
1066 		p = fgets(buf, siz, fp);
1067 		if (p != NULL || errno != EINTR)
1068 			break;
1069 		clearerr(fp);
1070 	}
1071 	save_errno = errno;
1072 
1073 	/* clear the event if it has not sprung */
1074 	clrevent(ev);
1075 
1076 	/* clean up the books and exit */
1077 	LineNumber++;
1078 	if (p == NULL)
1079 	{
1080 		buf[0] = '\0';
1081 		if (TrafficLogFile != NULL)
1082 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", (int) getpid());
1083 		errno = save_errno;
1084 		return (NULL);
1085 	}
1086 	if (TrafficLogFile != NULL)
1087 		fprintf(TrafficLogFile, "%05d <<< %s", (int) getpid(), buf);
1088 	if (SevenBitInput)
1089 	{
1090 		for (p = buf; *p != '\0'; p++)
1091 			*p &= ~0200;
1092 	}
1093 	else if (!HasEightBits)
1094 	{
1095 		for (p = buf; *p != '\0'; p++)
1096 		{
1097 			if (bitset(0200, *p))
1098 			{
1099 				HasEightBits = TRUE;
1100 				break;
1101 			}
1102 		}
1103 	}
1104 	return (buf);
1105 }
1106 
1107 /* ARGSUSED */
1108 static void
1109 readtimeout(timeout)
1110 	time_t timeout;
1111 {
1112 	longjmp(CtxReadTimeout, 1);
1113 }
1114 /*
1115 **  FGETFOLDED -- like fgets, but know about folded lines.
1116 **
1117 **	Parameters:
1118 **		buf -- place to put result.
1119 **		n -- bytes available.
1120 **		f -- file to read from.
1121 **
1122 **	Returns:
1123 **		input line(s) on success, NULL on error or EOF.
1124 **		This will normally be buf -- unless the line is too
1125 **			long, when it will be xalloc()ed.
1126 **
1127 **	Side Effects:
1128 **		buf gets lines from f, with continuation lines (lines
1129 **		with leading white space) appended.  CRLF's are mapped
1130 **		into single newlines.  Any trailing NL is stripped.
1131 */
1132 
1133 char *
1134 fgetfolded(buf, n, f)
1135 	char *buf;
1136 	register int n;
1137 	FILE *f;
1138 {
1139 	register char *p = buf;
1140 	char *bp = buf;
1141 	register int i;
1142 
1143 	n--;
1144 	while ((i = getc(f)) != EOF)
1145 	{
1146 		if (i == '\r')
1147 		{
1148 			i = getc(f);
1149 			if (i != '\n')
1150 			{
1151 				if (i != EOF)
1152 					(void) ungetc(i, f);
1153 				i = '\r';
1154 			}
1155 		}
1156 		if (--n <= 0)
1157 		{
1158 			/* allocate new space */
1159 			char *nbp;
1160 			int nn;
1161 
1162 			nn = (p - bp);
1163 			if (nn < MEMCHUNKSIZE)
1164 				nn *= 2;
1165 			else
1166 				nn += MEMCHUNKSIZE;
1167 			nbp = xalloc(nn);
1168 			bcopy(bp, nbp, p - bp);
1169 			p = &nbp[p - bp];
1170 			if (bp != buf)
1171 				free(bp);
1172 			bp = nbp;
1173 			n = nn - (p - bp);
1174 		}
1175 		*p++ = i;
1176 		if (i == '\n')
1177 		{
1178 			LineNumber++;
1179 			i = getc(f);
1180 			if (i != EOF)
1181 				(void) ungetc(i, f);
1182 			if (i != ' ' && i != '\t')
1183 				break;
1184 		}
1185 	}
1186 	if (p == bp)
1187 		return (NULL);
1188 	if (p[-1] == '\n')
1189 		p--;
1190 	*p = '\0';
1191 	return (bp);
1192 }
1193 /*
1194 **  CURTIME -- return current time.
1195 **
1196 **	Parameters:
1197 **		none.
1198 **
1199 **	Returns:
1200 **		the current time.
1201 **
1202 **	Side Effects:
1203 **		none.
1204 */
1205 
1206 time_t
1207 curtime()
1208 {
1209 	auto time_t t;
1210 
1211 	(void) time(&t);
1212 	return (t);
1213 }
1214 /*
1215 **  ATOBOOL -- convert a string representation to boolean.
1216 **
1217 **	Defaults to "TRUE"
1218 **
1219 **	Parameters:
1220 **		s -- string to convert.  Takes "tTyY" as true,
1221 **			others as false.
1222 **
1223 **	Returns:
1224 **		A boolean representation of the string.
1225 **
1226 **	Side Effects:
1227 **		none.
1228 */
1229 
1230 bool
1231 atobool(s)
1232 	register char *s;
1233 {
1234 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1235 		return (TRUE);
1236 	return (FALSE);
1237 }
1238 /*
1239 **  ATOOCT -- convert a string representation to octal.
1240 **
1241 **	Parameters:
1242 **		s -- string to convert.
1243 **
1244 **	Returns:
1245 **		An integer representing the string interpreted as an
1246 **		octal number.
1247 **
1248 **	Side Effects:
1249 **		none.
1250 */
1251 
1252 int
1253 atooct(s)
1254 	register char *s;
1255 {
1256 	register int i = 0;
1257 
1258 	while (*s >= '0' && *s <= '7')
1259 		i = (i << 3) | (*s++ - '0');
1260 	return (i);
1261 }
1262 /*
1263 **  BITINTERSECT -- tell if two bitmaps intersect
1264 **
1265 **	Parameters:
1266 **		a, b -- the bitmaps in question
1267 **
1268 **	Returns:
1269 **		TRUE if they have a non-null intersection
1270 **		FALSE otherwise
1271 **
1272 **	Side Effects:
1273 **		none.
1274 */
1275 
1276 bool
1277 bitintersect(a, b)
1278 	BITMAP a;
1279 	BITMAP b;
1280 {
1281 	int i;
1282 
1283 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1284 		if ((a[i] & b[i]) != 0)
1285 			return (TRUE);
1286 	return (FALSE);
1287 }
1288 /*
1289 **  BITZEROP -- tell if a bitmap is all zero
1290 **
1291 **	Parameters:
1292 **		map -- the bit map to check
1293 **
1294 **	Returns:
1295 **		TRUE if map is all zero.
1296 **		FALSE if there are any bits set in map.
1297 **
1298 **	Side Effects:
1299 **		none.
1300 */
1301 
1302 bool
1303 bitzerop(map)
1304 	BITMAP map;
1305 {
1306 	int i;
1307 
1308 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1309 		if (map[i] != 0)
1310 			return (FALSE);
1311 	return (TRUE);
1312 }
1313 /*
1314 **  STRCONTAINEDIN -- tell if one string is contained in another
1315 **
1316 **	Parameters:
1317 **		a -- possible substring.
1318 **		b -- possible superstring.
1319 **
1320 **	Returns:
1321 **		TRUE if a is contained in b.
1322 **		FALSE otherwise.
1323 */
1324 
1325 bool
1326 strcontainedin(a, b)
1327 	register char *a;
1328 	register char *b;
1329 {
1330 	int la;
1331 	int lb;
1332 	int c;
1333 
1334 	la = strlen(a);
1335 	lb = strlen(b);
1336 	c = *a;
1337 	if (isascii(c) && isupper(c))
1338 		c = tolower(c);
1339 	for (; lb-- >= la; b++)
1340 	{
1341 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1342 			continue;
1343 		if (strncasecmp(a, b, la) == 0)
1344 			return TRUE;
1345 	}
1346 	return FALSE;
1347 }
1348 /*
1349 **  CHECKFD012 -- check low numbered file descriptors
1350 **
1351 **	File descriptors 0, 1, and 2 should be open at all times.
1352 **	This routine verifies that, and fixes it if not true.
1353 **
1354 **	Parameters:
1355 **		where -- a tag printed if the assertion failed
1356 **
1357 **	Returns:
1358 **		none
1359 */
1360 
1361 void
1362 checkfd012(where)
1363 	char *where;
1364 {
1365 #if XDEBUG
1366 	register int i;
1367 
1368 	for (i = 0; i < 3; i++)
1369 		fill_fd(i, where);
1370 #endif /* XDEBUG */
1371 }
1372 /*
1373 **  CHECKFDOPEN -- make sure file descriptor is open -- for extended debugging
1374 **
1375 **	Parameters:
1376 **		fd -- file descriptor to check.
1377 **		where -- tag to print on failure.
1378 **
1379 **	Returns:
1380 **		none.
1381 */
1382 
1383 void
1384 checkfdopen(fd, where)
1385 	int fd;
1386 	char *where;
1387 {
1388 #if XDEBUG
1389 	struct stat st;
1390 
1391 	if (fstat(fd, &st) < 0 && errno == EBADF)
1392 	{
1393 		syserr("checkfdopen(%d): %s not open as expected!", fd, where);
1394 		printopenfds(TRUE);
1395 	}
1396 #endif
1397 }
1398 /*
1399 **  CHECKFDS -- check for new or missing file descriptors
1400 **
1401 **	Parameters:
1402 **		where -- tag for printing.  If null, take a base line.
1403 **
1404 **	Returns:
1405 **		none
1406 **
1407 **	Side Effects:
1408 **		If where is set, shows changes since the last call.
1409 */
1410 
1411 void
1412 checkfds(where)
1413 	char *where;
1414 {
1415 	int maxfd;
1416 	register int fd;
1417 	bool printhdr = TRUE;
1418 	int save_errno = errno;
1419 	static BITMAP baseline;
1420 	extern int DtableSize;
1421 
1422 	if (DtableSize > 256)
1423 		maxfd = 256;
1424 	else
1425 		maxfd = DtableSize;
1426 	if (where == NULL)
1427 		clrbitmap(baseline);
1428 
1429 	for (fd = 0; fd < maxfd; fd++)
1430 	{
1431 		struct stat stbuf;
1432 
1433 		if (fstat(fd, &stbuf) < 0 && errno != EOPNOTSUPP)
1434 		{
1435 			if (!bitnset(fd, baseline))
1436 				continue;
1437 			clrbitn(fd, baseline);
1438 		}
1439 		else if (!bitnset(fd, baseline))
1440 			setbitn(fd, baseline);
1441 		else
1442 			continue;
1443 
1444 		/* file state has changed */
1445 		if (where == NULL)
1446 			continue;
1447 		if (printhdr)
1448 		{
1449 			sm_syslog(LOG_DEBUG, CurEnv->e_id,
1450 				"%s: changed fds:",
1451 				where);
1452 			printhdr = FALSE;
1453 		}
1454 		dumpfd(fd, TRUE, TRUE);
1455 	}
1456 	errno = save_errno;
1457 }
1458 /*
1459 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1460 **
1461 **	Parameters:
1462 **		logit -- if set, send output to syslog; otherwise
1463 **			print for debugging.
1464 **
1465 **	Returns:
1466 **		none.
1467 */
1468 
1469 #include <arpa/inet.h>
1470 
1471 void
1472 printopenfds(logit)
1473 	bool logit;
1474 {
1475 	register int fd;
1476 	extern int DtableSize;
1477 
1478 	for (fd = 0; fd < DtableSize; fd++)
1479 		dumpfd(fd, FALSE, logit);
1480 }
1481 /*
1482 **  DUMPFD -- dump a file descriptor
1483 **
1484 **	Parameters:
1485 **		fd -- the file descriptor to dump.
1486 **		printclosed -- if set, print a notification even if
1487 **			it is closed; otherwise print nothing.
1488 **		logit -- if set, send output to syslog instead of stdout.
1489 */
1490 
1491 void
1492 dumpfd(fd, printclosed, logit)
1493 	int fd;
1494 	bool printclosed;
1495 	bool logit;
1496 {
1497 	register char *p;
1498 	char *hp;
1499 #ifdef S_IFSOCK
1500 	SOCKADDR sa;
1501 #endif
1502 	auto SOCKADDR_LEN_T slen;
1503 	int i;
1504 #if STAT64 > 0
1505 	struct stat64 st;
1506 #else
1507 	struct stat st;
1508 #endif
1509 	char buf[200];
1510 
1511 	p = buf;
1512 	snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
1513 	p += strlen(p);
1514 
1515 	if (
1516 #if STAT64 > 0
1517 	    fstat64(fd, &st)
1518 #else
1519 	    fstat(fd, &st)
1520 #endif
1521 	    < 0)
1522 	{
1523 		if (errno != EBADF)
1524 		{
1525 			snprintf(p, SPACELEFT(buf, p), "CANNOT STAT (%s)",
1526 				errstring(errno));
1527 			goto printit;
1528 		}
1529 		else if (printclosed)
1530 		{
1531 			snprintf(p, SPACELEFT(buf, p), "CLOSED");
1532 			goto printit;
1533 		}
1534 		return;
1535 	}
1536 
1537 	i = fcntl(fd, F_GETFL, NULL);
1538 	if (i != -1)
1539 	{
1540 		snprintf(p, SPACELEFT(buf, p), "fl=0x%x, ", i);
1541 		p += strlen(p);
1542 	}
1543 
1544 	snprintf(p, SPACELEFT(buf, p), "mode=%o: ", st.st_mode);
1545 	p += strlen(p);
1546 	switch (st.st_mode & S_IFMT)
1547 	{
1548 #ifdef S_IFSOCK
1549 	  case S_IFSOCK:
1550 		snprintf(p, SPACELEFT(buf, p), "SOCK ");
1551 		p += strlen(p);
1552 		slen = sizeof sa;
1553 		if (getsockname(fd, &sa.sa, &slen) < 0)
1554 			snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1555 		else
1556 		{
1557 			hp = hostnamebyanyaddr(&sa);
1558 			if (sa.sa.sa_family == AF_INET)
1559 				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1560 					hp, ntohs(sa.sin.sin_port));
1561 			else
1562 				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1563 		}
1564 		p += strlen(p);
1565 		snprintf(p, SPACELEFT(buf, p), "->");
1566 		p += strlen(p);
1567 		slen = sizeof sa;
1568 		if (getpeername(fd, &sa.sa, &slen) < 0)
1569 			snprintf(p, SPACELEFT(buf, p), "(%s)", errstring(errno));
1570 		else
1571 		{
1572 			hp = hostnamebyanyaddr(&sa);
1573 			if (sa.sa.sa_family == AF_INET)
1574 				snprintf(p, SPACELEFT(buf, p), "%s/%d",
1575 					hp, ntohs(sa.sin.sin_port));
1576 			else
1577 				snprintf(p, SPACELEFT(buf, p), "%s", hp);
1578 		}
1579 		break;
1580 #endif
1581 
1582 	  case S_IFCHR:
1583 		snprintf(p, SPACELEFT(buf, p), "CHR: ");
1584 		p += strlen(p);
1585 		goto defprint;
1586 
1587 	  case S_IFBLK:
1588 		snprintf(p, SPACELEFT(buf, p), "BLK: ");
1589 		p += strlen(p);
1590 		goto defprint;
1591 
1592 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1593 	  case S_IFIFO:
1594 		snprintf(p, SPACELEFT(buf, p), "FIFO: ");
1595 		p += strlen(p);
1596 		goto defprint;
1597 #endif
1598 
1599 #ifdef S_IFDIR
1600 	  case S_IFDIR:
1601 		snprintf(p, SPACELEFT(buf, p), "DIR: ");
1602 		p += strlen(p);
1603 		goto defprint;
1604 #endif
1605 
1606 #ifdef S_IFLNK
1607 	  case S_IFLNK:
1608 		snprintf(p, SPACELEFT(buf, p), "LNK: ");
1609 		p += strlen(p);
1610 		goto defprint;
1611 #endif
1612 
1613 	  default:
1614 defprint:
1615 		if (sizeof st.st_ino > sizeof (long))
1616 			snprintf(p, SPACELEFT(buf, p),
1617 				 "dev=%d/%d, ino=%s, nlink=%d, u/gid=%d/%d, ",
1618 				 major(st.st_dev), minor(st.st_dev),
1619 				 quad_to_string(st.st_ino),
1620 				 st.st_nlink, st.st_uid, st.st_gid);
1621 		else
1622 			snprintf(p, SPACELEFT(buf, p),
1623 				"dev=%d/%d, ino=%lu, nlink=%d, u/gid=%d/%d, ",
1624 				major(st.st_dev), minor(st.st_dev),
1625 				(unsigned long) st.st_ino,
1626 				st.st_nlink, st.st_uid, st.st_gid);
1627 		if (sizeof st.st_size > sizeof (long))
1628 			snprintf(p, SPACELEFT(buf, p), "size=%s",
1629 				 quad_to_string(st.st_size));
1630 		else
1631 			snprintf(p, SPACELEFT(buf, p), "size=%lu",
1632 				 (unsigned long) st.st_size);
1633 		break;
1634 	}
1635 
1636 printit:
1637 	if (logit)
1638 		sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
1639 			"%.800s", buf);
1640 	else
1641 		printf("%s\n", buf);
1642 }
1643 /*
1644 **  SHORTEN_HOSTNAME -- strip local domain information off of hostname.
1645 **
1646 **	Parameters:
1647 **		host -- the host to shorten (stripped in place).
1648 **
1649 **	Returns:
1650 **		none.
1651 */
1652 
1653 void
1654 shorten_hostname(host)
1655 	char host[];
1656 {
1657 	register char *p;
1658 	char *mydom;
1659 	int i;
1660 	bool canon = FALSE;
1661 
1662 	/* strip off final dot */
1663 	p = &host[strlen(host) - 1];
1664 	if (*p == '.')
1665 	{
1666 		*p = '\0';
1667 		canon = TRUE;
1668 	}
1669 
1670 	/* see if there is any domain at all -- if not, we are done */
1671 	p = strchr(host, '.');
1672 	if (p == NULL)
1673 		return;
1674 
1675 	/* yes, we have a domain -- see if it looks like us */
1676 	mydom = macvalue('m', CurEnv);
1677 	if (mydom == NULL)
1678 		mydom = "";
1679 	i = strlen(++p);
1680 	if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 &&
1681 	    (mydom[i] == '.' || mydom[i] == '\0'))
1682 		*--p = '\0';
1683 }
1684 /*
1685 **  PROG_OPEN -- open a program for reading
1686 **
1687 **	Parameters:
1688 **		argv -- the argument list.
1689 **		pfd -- pointer to a place to store the file descriptor.
1690 **		e -- the current envelope.
1691 **
1692 **	Returns:
1693 **		pid of the process -- -1 if it failed.
1694 */
1695 
1696 int
1697 prog_open(argv, pfd, e)
1698 	char **argv;
1699 	int *pfd;
1700 	ENVELOPE *e;
1701 {
1702 	int pid;
1703 	int i;
1704 	int saveerrno;
1705 	int fdv[2];
1706 	char *p, *q;
1707 	char buf[MAXLINE + 1];
1708 	extern int DtableSize;
1709 
1710 	if (pipe(fdv) < 0)
1711 	{
1712 		syserr("%s: cannot create pipe for stdout", argv[0]);
1713 		return -1;
1714 	}
1715 	pid = fork();
1716 	if (pid < 0)
1717 	{
1718 		syserr("%s: cannot fork", argv[0]);
1719 		close(fdv[0]);
1720 		close(fdv[1]);
1721 		return -1;
1722 	}
1723 	if (pid > 0)
1724 	{
1725 		/* parent */
1726 		close(fdv[1]);
1727 		*pfd = fdv[0];
1728 		return pid;
1729 	}
1730 
1731 	/* child -- close stdin */
1732 	close(0);
1733 
1734 	/* stdout goes back to parent */
1735 	close(fdv[0]);
1736 	if (dup2(fdv[1], 1) < 0)
1737 	{
1738 		syserr("%s: cannot dup2 for stdout", argv[0]);
1739 		_exit(EX_OSERR);
1740 	}
1741 	close(fdv[1]);
1742 
1743 	/* stderr goes to transcript if available */
1744 	if (e->e_xfp != NULL)
1745 	{
1746 		if (dup2(fileno(e->e_xfp), 2) < 0)
1747 		{
1748 			syserr("%s: cannot dup2 for stderr", argv[0]);
1749 			_exit(EX_OSERR);
1750 		}
1751 	}
1752 
1753 	/* this process has no right to the queue file */
1754 	if (e->e_lockfp != NULL)
1755 		close(fileno(e->e_lockfp));
1756 
1757 	/* run as default user */
1758 	endpwent();
1759 	if (setgid(DefGid) < 0 && geteuid() == 0)
1760 		syserr("prog_open: setgid(%ld) failed", (long) DefGid);
1761 	if (setuid(DefUid) < 0 && geteuid() == 0)
1762 		syserr("prog_open: setuid(%ld) failed", (long) DefUid);
1763 
1764 	/* run in some directory */
1765 	if (ProgMailer != NULL)
1766 		p = ProgMailer->m_execdir;
1767 	else
1768 		p = NULL;
1769 	for (; p != NULL; p = q)
1770 	{
1771 		q = strchr(p, ':');
1772 		if (q != NULL)
1773 			*q = '\0';
1774 		expand(p, buf, sizeof buf, e);
1775 		if (q != NULL)
1776 			*q++ = ':';
1777 		if (buf[0] != '\0' && chdir(buf) >= 0)
1778 			break;
1779 	}
1780 	if (p == NULL)
1781 	{
1782 		/* backup directories */
1783 		if (chdir("/tmp") < 0)
1784 			(void) chdir("/");
1785 	}
1786 
1787 	/* arrange for all the files to be closed */
1788 	for (i = 3; i < DtableSize; i++)
1789 	{
1790 		register int j;
1791 
1792 		if ((j = fcntl(i, F_GETFD, 0)) != -1)
1793 			(void) fcntl(i, F_SETFD, j | 1);
1794 	}
1795 
1796 	/* now exec the process */
1797 	execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron);
1798 
1799 	/* woops!  failed */
1800 	saveerrno = errno;
1801 	syserr("%s: cannot exec", argv[0]);
1802 	if (transienterror(saveerrno))
1803 		_exit(EX_OSERR);
1804 	_exit(EX_CONFIG);
1805 	return -1;	/* avoid compiler warning on IRIX */
1806 }
1807 /*
1808 **  GET_COLUMN  -- look up a Column in a line buffer
1809 **
1810 **	Parameters:
1811 **		line -- the raw text line to search.
1812 **		col -- the column number to fetch.
1813 **		delim -- the delimiter between columns.  If null,
1814 **			use white space.
1815 **		buf -- the output buffer.
1816 **		buflen -- the length of buf.
1817 **
1818 **	Returns:
1819 **		buf if successful.
1820 **		NULL otherwise.
1821 */
1822 
1823 char *
1824 get_column(line, col, delim, buf, buflen)
1825 	char line[];
1826 	int col;
1827 	char delim;
1828 	char buf[];
1829 	int buflen;
1830 {
1831 	char *p;
1832 	char *begin, *end;
1833 	int i;
1834 	char delimbuf[4];
1835 
1836 	if (delim == '\0')
1837 		strcpy(delimbuf, "\n\t ");
1838 	else
1839 	{
1840 		delimbuf[0] = delim;
1841 		delimbuf[1] = '\0';
1842 	}
1843 
1844 	p = line;
1845 	if (*p == '\0')
1846 		return NULL;			/* line empty */
1847 	if (*p == delim && col == 0)
1848 		return NULL;			/* first column empty */
1849 
1850 	begin = line;
1851 
1852 	if (col == 0 && delim == '\0')
1853 	{
1854 		while (*begin != '\0' && isascii(*begin) && isspace(*begin))
1855 			begin++;
1856 	}
1857 
1858 	for (i = 0; i < col; i++)
1859 	{
1860 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
1861 			return NULL;		/* no such column */
1862 		begin++;
1863 		if (delim == '\0')
1864 		{
1865 			while (*begin != '\0' && isascii(*begin) && isspace(*begin))
1866 				begin++;
1867 		}
1868 	}
1869 
1870 	end = strpbrk(begin, delimbuf);
1871 	if (end == NULL)
1872 		i = strlen(begin);
1873 	else
1874 		i = end - begin;
1875 	if (i >= buflen)
1876 		i = buflen - 1;
1877 	strncpy(buf, begin, i);
1878 	buf[i] = '\0';
1879 	return buf;
1880 }
1881 /*
1882 **  CLEANSTRCPY -- copy string keeping out bogus characters
1883 **
1884 **	Parameters:
1885 **		t -- "to" string.
1886 **		f -- "from" string.
1887 **		l -- length of space available in "to" string.
1888 **
1889 **	Returns:
1890 **		none.
1891 */
1892 
1893 void
1894 cleanstrcpy(t, f, l)
1895 	register char *t;
1896 	register char *f;
1897 	int l;
1898 {
1899 	/* check for newlines and log if necessary */
1900 	(void) denlstring(f, TRUE, TRUE);
1901 
1902 	l--;
1903 	while (l > 0 && *f != '\0')
1904 	{
1905 		if (isascii(*f) &&
1906 		    (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL))
1907 		{
1908 			l--;
1909 			*t++ = *f;
1910 		}
1911 		f++;
1912 	}
1913 	*t = '\0';
1914 }
1915 /*
1916 **  DENLSTRING -- convert newlines in a string to spaces
1917 **
1918 **	Parameters:
1919 **		s -- the input string
1920 **		strict -- if set, don't permit continuation lines.
1921 **		logattacks -- if set, log attempted attacks.
1922 **
1923 **	Returns:
1924 **		A pointer to a version of the string with newlines
1925 **		mapped to spaces.  This should be copied.
1926 */
1927 
1928 char *
1929 denlstring(s, strict, logattacks)
1930 	char *s;
1931 	bool strict;
1932 	bool logattacks;
1933 {
1934 	register char *p;
1935 	int l;
1936 	static char *bp = NULL;
1937 	static int bl = 0;
1938 
1939 	p = s;
1940 	while ((p = strchr(p, '\n')) != NULL)
1941 		if (strict || (*++p != ' ' && *p != '\t'))
1942 			break;
1943 	if (p == NULL)
1944 		return s;
1945 
1946 	l = strlen(s) + 1;
1947 	if (bl < l)
1948 	{
1949 		/* allocate more space */
1950 		if (bp != NULL)
1951 			free(bp);
1952 		bp = xalloc(l);
1953 		bl = l;
1954 	}
1955 	strcpy(bp, s);
1956 	for (p = bp; (p = strchr(p, '\n')) != NULL; )
1957 		*p++ = ' ';
1958 
1959 	if (logattacks)
1960 	{
1961 		sm_syslog(LOG_NOTICE, CurEnv->e_id,
1962 			"POSSIBLE ATTACK from %.100s: newline in string \"%s\"",
1963 			RealHostName == NULL ? "[UNKNOWN]" : RealHostName,
1964 			shortenstring(bp, MAXSHORTSTR));
1965 	}
1966 
1967 	return bp;
1968 }
1969 /*
1970 **  PATH_IS_DIR -- check to see if file exists and is a directory.
1971 **
1972 **	There are some additional checks for security violations in
1973 **	here.  This routine is intended to be used for the host status
1974 **	support.
1975 **
1976 **	Parameters:
1977 **		pathname -- pathname to check for directory-ness.
1978 **		createflag -- if set, create directory if needed.
1979 **
1980 **	Returns:
1981 **		TRUE -- if the indicated pathname is a directory
1982 **		FALSE -- otherwise
1983 */
1984 
1985 int
1986 path_is_dir(pathname, createflag)
1987 	char *pathname;
1988 	bool createflag;
1989 {
1990 	struct stat statbuf;
1991 
1992 #if HASLSTAT
1993 	if (lstat(pathname, &statbuf) < 0)
1994 #else
1995 	if (stat(pathname, &statbuf) < 0)
1996 #endif
1997 	{
1998 		if (errno != ENOENT || !createflag)
1999 			return FALSE;
2000 		if (mkdir(pathname, 0755) < 0)
2001 			return FALSE;
2002 		return TRUE;
2003 	}
2004 	if (!S_ISDIR(statbuf.st_mode))
2005 	{
2006 		errno = ENOTDIR;
2007 		return FALSE;
2008 	}
2009 
2010 	/* security: don't allow writable directories */
2011 	if (bitset(S_IWGRP|S_IWOTH, statbuf.st_mode))
2012 	{
2013 		errno = EACCES;
2014 		return FALSE;
2015 	}
2016 
2017 	return TRUE;
2018 }
2019 /*
2020 **  PROC_LIST_ADD -- add process id to list of our children
2021 **
2022 **	Parameters:
2023 **		pid -- pid to add to list.
2024 **
2025 **	Returns:
2026 **		none
2027 */
2028 
2029 struct procs
2030 {
2031 	pid_t	proc_pid;
2032 	char 	*proc_task;
2033 };
2034 
2035 static struct procs	*ProcListVec	= NULL;
2036 static int	ProcListSize	= 0;
2037 
2038 #define NO_PID		((pid_t) 0)
2039 #ifndef PROC_LIST_SEG
2040 # define PROC_LIST_SEG	32		/* number of pids to alloc at a time */
2041 #endif
2042 
2043 void
2044 proc_list_add(pid, task)
2045 	pid_t pid;
2046 	char *task;
2047 {
2048 	int i;
2049 
2050 	for (i = 0; i < ProcListSize; i++)
2051 	{
2052 		if (ProcListVec[i].proc_pid == NO_PID)
2053 			break;
2054 	}
2055 	if (i >= ProcListSize)
2056 	{
2057 		/* probe the existing vector to avoid growing infinitely */
2058 		proc_list_probe();
2059 
2060 		/* now scan again */
2061 		for (i = 0; i < ProcListSize; i++)
2062 		{
2063 			if (ProcListVec[i].proc_pid == NO_PID)
2064 				break;
2065 		}
2066 	}
2067 	if (i >= ProcListSize)
2068 	{
2069 		/* grow process list */
2070 		struct procs *npv;
2071 
2072 		npv = (struct procs *) xalloc(sizeof (struct procs) * (ProcListSize + PROC_LIST_SEG));
2073 		if (ProcListSize > 0)
2074 		{
2075 			bcopy(ProcListVec, npv, ProcListSize *
2076 						sizeof (struct procs));
2077 			free(ProcListVec);
2078 		}
2079 		for (i = ProcListSize; i < ProcListSize + PROC_LIST_SEG; i++)
2080 		{
2081 			npv[i].proc_pid = NO_PID;
2082 			npv[i].proc_task = NULL;
2083 		}
2084 		i = ProcListSize;
2085 		ProcListSize += PROC_LIST_SEG;
2086 		ProcListVec = npv;
2087 	}
2088 	ProcListVec[i].proc_pid = pid;
2089 	ProcListVec[i].proc_task = newstr(task);
2090 
2091 	/* if process adding itself, it's not a child */
2092 	if (pid != getpid())
2093 		CurChildren++;
2094 }
2095 /*
2096 **  PROC_LIST_SET -- set pid task in process list
2097 **
2098 **	Parameters:
2099 **		pid -- pid to set
2100 **		task -- task of pid
2101 **
2102 **	Returns:
2103 **		none.
2104 */
2105 
2106 void
2107 proc_list_set(pid, task)
2108 	pid_t pid;
2109 	char *task;
2110 {
2111 	int i;
2112 
2113 	for (i = 0; i < ProcListSize; i++)
2114 	{
2115 		if (ProcListVec[i].proc_pid == pid)
2116 		{
2117 			if (ProcListVec[i].proc_task != NULL)
2118 				free(ProcListVec[i].proc_task);
2119 			ProcListVec[i].proc_task = newstr(task);
2120 			break;
2121 		}
2122 	}
2123 }
2124 /*
2125 **  PROC_LIST_DROP -- drop pid from process list
2126 **
2127 **	Parameters:
2128 **		pid -- pid to drop
2129 **
2130 **	Returns:
2131 **		none.
2132 */
2133 
2134 void
2135 proc_list_drop(pid)
2136 	pid_t pid;
2137 {
2138 	int i;
2139 
2140 	for (i = 0; i < ProcListSize; i++)
2141 	{
2142 		if (ProcListVec[i].proc_pid == pid)
2143 		{
2144 			ProcListVec[i].proc_pid = NO_PID;
2145 			if (ProcListVec[i].proc_task != NULL)
2146 			{
2147 				free(ProcListVec[i].proc_task);
2148 				ProcListVec[i].proc_task = NULL;
2149 			}
2150 			break;
2151 		}
2152 	}
2153 	if (CurChildren > 0)
2154 		CurChildren--;
2155 }
2156 /*
2157 **  PROC_LIST_CLEAR -- clear the process list
2158 **
2159 **	Parameters:
2160 **		none.
2161 **
2162 **	Returns:
2163 **		none.
2164 */
2165 
2166 void
2167 proc_list_clear()
2168 {
2169 	int i;
2170 
2171 	/* start from 1 since 0 is the daemon itself */
2172 	for (i = 1; i < ProcListSize; i++)
2173 	{
2174 		ProcListVec[i].proc_pid = NO_PID;
2175 		if (ProcListVec[i].proc_task != NULL)
2176 		{
2177 			free(ProcListVec[i].proc_task);
2178 			ProcListVec[i].proc_task = NULL;
2179 		}
2180 	}
2181 	CurChildren = 0;
2182 }
2183 /*
2184 **  PROC_LIST_PROBE -- probe processes in the list to see if they still exist
2185 **
2186 **	Parameters:
2187 **		none
2188 **
2189 **	Returns:
2190 **		none
2191 */
2192 
2193 void
2194 proc_list_probe()
2195 {
2196 	int i;
2197 
2198 	/* start from 1 since 0 is the daemon itself */
2199 	for (i = 1; i < ProcListSize; i++)
2200 	{
2201 		if (ProcListVec[i].proc_pid == NO_PID)
2202 			continue;
2203 		if (kill(ProcListVec[i].proc_pid, 0) < 0)
2204 		{
2205 			if (LogLevel > 3)
2206 				sm_syslog(LOG_DEBUG, CurEnv->e_id,
2207 					"proc_list_probe: lost pid %d",
2208 					(int) ProcListVec[i].proc_pid);
2209 			ProcListVec[i].proc_pid = NO_PID;
2210 			if (ProcListVec[i].proc_task != NULL)
2211 			{
2212 				free(ProcListVec[i].proc_task);
2213 				ProcListVec[i].proc_task = NULL;
2214 			}
2215 			CurChildren--;
2216 		}
2217 	}
2218 	if (CurChildren < 0)
2219 		CurChildren = 0;
2220 }
2221 /*
2222 **  PROC_LIST_DISPLAY -- display the process list
2223 **
2224 **	Parameters:
2225 **		out -- output file pointer
2226 **
2227 **	Returns:
2228 **		none.
2229 */
2230 
2231 void
2232 proc_list_display(out)
2233 	FILE *out;
2234 {
2235 	int i;
2236 
2237 	for (i = 0; i < ProcListSize; i++)
2238 	{
2239 		if (ProcListVec[i].proc_pid == NO_PID)
2240 			continue;
2241 
2242 		fprintf(out, "%d %s%s\n", (int) ProcListVec[i].proc_pid,
2243 			ProcListVec[i].proc_task != NULL ?
2244 			ProcListVec[i].proc_task : "(unknown)",
2245 			(OpMode == MD_SMTP ||
2246 			 OpMode == MD_DAEMON ||
2247 			 OpMode == MD_ARPAFTP) ? "\r" : "");
2248 	}
2249 }
2250 /*
2251 **  SM_STRCASECMP -- 8-bit clean version of strcasecmp
2252 **
2253 **	Thank you, vendors, for making this all necessary.
2254 */
2255 
2256 /*
2257  * Copyright (c) 1987, 1993
2258  *	The Regents of the University of California.  All rights reserved.
2259  *
2260  * Redistribution and use in source and binary forms, with or without
2261  * modification, are permitted provided that the following conditions
2262  * are met:
2263  * 1. Redistributions of source code must retain the above copyright
2264  *    notice, this list of conditions and the following disclaimer.
2265  * 2. Redistributions in binary form must reproduce the above copyright
2266  *    notice, this list of conditions and the following disclaimer in the
2267  *    documentation and/or other materials provided with the distribution.
2268  * 3. All advertising materials mentioning features or use of this software
2269  *    must display the following acknowledgement:
2270  *	This product includes software developed by the University of
2271  *	California, Berkeley and its contributors.
2272  * 4. Neither the name of the University nor the names of its contributors
2273  *    may be used to endorse or promote products derived from this software
2274  *    without specific prior written permission.
2275  *
2276  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2277  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2278  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2279  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2280  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2281  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2282  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2283  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2284  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2285  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2286  * SUCH DAMAGE.
2287  */
2288 
2289 #if defined(LIBC_SCCS) && !defined(lint)
2290 static char sccsid[] = "@(#)strcasecmp.c	8.1 (Berkeley) 6/4/93";
2291 #endif /* LIBC_SCCS and not lint */
2292 
2293 /*
2294  * This array is designed for mapping upper and lower case letter
2295  * together for a case independent comparison.  The mappings are
2296  * based upon ascii character sequences.
2297  */
2298 static const u_char charmap[] = {
2299 	0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
2300 	0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
2301 	0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
2302 	0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
2303 	0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
2304 	0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
2305 	0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
2306 	0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
2307 	0100, 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, 0133, 0134, 0135, 0136, 0137,
2311 	0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
2312 	0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
2313 	0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
2314 	0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
2315 	0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
2316 	0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
2317 	0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
2318 	0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
2319 	0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
2320 	0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
2321 	0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
2322 	0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
2323 	0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
2324 	0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
2325 	0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
2326 	0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
2327 	0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
2328 	0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
2329 	0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
2330 	0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377,
2331 };
2332 
2333 int
2334 sm_strcasecmp(s1, s2)
2335 	const char *s1, *s2;
2336 {
2337 	register const u_char *cm = charmap,
2338 			*us1 = (const u_char *)s1,
2339 			*us2 = (const u_char *)s2;
2340 
2341 	while (cm[*us1] == cm[*us2++])
2342 		if (*us1++ == '\0')
2343 			return (0);
2344 	return (cm[*us1] - cm[*--us2]);
2345 }
2346 
2347 int
2348 sm_strncasecmp(s1, s2, n)
2349 	const char *s1, *s2;
2350 	register size_t n;
2351 {
2352 	if (n != 0) {
2353 		register const u_char *cm = charmap,
2354 				*us1 = (const u_char *)s1,
2355 				*us2 = (const u_char *)s2;
2356 
2357 		do {
2358 			if (cm[*us1] != cm[*us2++])
2359 				return (cm[*us1] - cm[*--us2]);
2360 			if (*us1++ == '\0')
2361 				break;
2362 		} while (--n != 0);
2363 	}
2364 	return (0);
2365 }
2366