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