xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision deff14a8)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	8.46 (Berkeley) 10/24/94";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <sysexits.h>
15 /*
16 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
17 **
18 **	Runs through a string and strips off unquoted quote
19 **	characters and quote bits.  This is done in place.
20 **
21 **	Parameters:
22 **		s -- the string to strip.
23 **
24 **	Returns:
25 **		none.
26 **
27 **	Side Effects:
28 **		none.
29 **
30 **	Called By:
31 **		deliver
32 */
33 
34 stripquotes(s)
35 	char *s;
36 {
37 	register char *p;
38 	register char *q;
39 	register char c;
40 
41 	if (s == NULL)
42 		return;
43 
44 	p = q = s;
45 	do
46 	{
47 		c = *p++;
48 		if (c == '\\')
49 			c = *p++;
50 		else if (c == '"')
51 			continue;
52 		*q++ = c;
53 	} while (c != '\0');
54 }
55 /*
56 **  XALLOC -- Allocate memory and bitch wildly on failure.
57 **
58 **	THIS IS A CLUDGE.  This should be made to give a proper
59 **	error -- but after all, what can we do?
60 **
61 **	Parameters:
62 **		sz -- size of area to allocate.
63 **
64 **	Returns:
65 **		pointer to data region.
66 **
67 **	Side Effects:
68 **		Memory is allocated.
69 */
70 
71 char *
72 xalloc(sz)
73 	register int sz;
74 {
75 	register char *p;
76 
77 	/* some systems can't handle size zero mallocs */
78 	if (sz <= 0)
79 		sz = 1;
80 
81 	p = malloc((unsigned) sz);
82 	if (p == NULL)
83 	{
84 		syserr("Out of memory!!");
85 		abort();
86 		/* exit(EX_UNAVAILABLE); */
87 	}
88 	return (p);
89 }
90 /*
91 **  COPYPLIST -- copy list of pointers.
92 **
93 **	This routine is the equivalent of newstr for lists of
94 **	pointers.
95 **
96 **	Parameters:
97 **		list -- list of pointers to copy.
98 **			Must be NULL terminated.
99 **		copycont -- if TRUE, copy the contents of the vector
100 **			(which must be a string) also.
101 **
102 **	Returns:
103 **		a copy of 'list'.
104 **
105 **	Side Effects:
106 **		none.
107 */
108 
109 char **
110 copyplist(list, copycont)
111 	char **list;
112 	bool copycont;
113 {
114 	register char **vp;
115 	register char **newvp;
116 
117 	for (vp = list; *vp != NULL; vp++)
118 		continue;
119 
120 	vp++;
121 
122 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
123 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
124 
125 	if (copycont)
126 	{
127 		for (vp = newvp; *vp != NULL; vp++)
128 			*vp = newstr(*vp);
129 	}
130 
131 	return (newvp);
132 }
133 /*
134 **  COPYQUEUE -- copy address queue.
135 **
136 **	This routine is the equivalent of newstr for address queues
137 **	addresses marked with QDONTSEND aren't copied
138 **
139 **	Parameters:
140 **		addr -- list of address structures to copy.
141 **
142 **	Returns:
143 **		a copy of 'addr'.
144 **
145 **	Side Effects:
146 **		none.
147 */
148 
149 ADDRESS *
150 copyqueue(addr)
151 	ADDRESS *addr;
152 {
153 	register ADDRESS *newaddr;
154 	ADDRESS *ret;
155 	register ADDRESS **tail = &ret;
156 
157 	while (addr != NULL)
158 	{
159 		if (!bitset(QDONTSEND, addr->q_flags))
160 		{
161 			newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS));
162 			STRUCTCOPY(*addr, *newaddr);
163 			*tail = newaddr;
164 			tail = &newaddr->q_next;
165 		}
166 		addr = addr->q_next;
167 	}
168 	*tail = NULL;
169 
170 	return ret;
171 }
172 /*
173 **  PRINTAV -- print argument vector.
174 **
175 **	Parameters:
176 **		av -- argument vector.
177 **
178 **	Returns:
179 **		none.
180 **
181 **	Side Effects:
182 **		prints av.
183 */
184 
185 printav(av)
186 	register char **av;
187 {
188 	while (*av != NULL)
189 	{
190 		if (tTd(0, 44))
191 			printf("\n\t%08x=", *av);
192 		else
193 			(void) putchar(' ');
194 		xputs(*av++);
195 	}
196 	(void) putchar('\n');
197 }
198 /*
199 **  LOWER -- turn letter into lower case.
200 **
201 **	Parameters:
202 **		c -- character to turn into lower case.
203 **
204 **	Returns:
205 **		c, in lower case.
206 **
207 **	Side Effects:
208 **		none.
209 */
210 
211 char
212 lower(c)
213 	register char c;
214 {
215 	return((isascii(c) && isupper(c)) ? tolower(c) : c);
216 }
217 /*
218 **  XPUTS -- put string doing control escapes.
219 **
220 **	Parameters:
221 **		s -- string to put.
222 **
223 **	Returns:
224 **		none.
225 **
226 **	Side Effects:
227 **		output to stdout
228 */
229 
230 xputs(s)
231 	register char *s;
232 {
233 	register int c;
234 	register struct metamac *mp;
235 	extern struct metamac MetaMacros[];
236 
237 	if (s == NULL)
238 	{
239 		printf("<null>");
240 		return;
241 	}
242 	while ((c = (*s++ & 0377)) != '\0')
243 	{
244 		if (!isascii(c))
245 		{
246 			if (c == MATCHREPL)
247 			{
248 				putchar('$');
249 				continue;
250 			}
251 			if (c == MACROEXPAND)
252 			{
253 				putchar('$');
254 				if (bitset(0200, *s))
255 					printf("{%s}", macname(*s++ & 0377));
256 				continue;
257 			}
258 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
259 			{
260 				if ((mp->metaval & 0377) == c)
261 				{
262 					printf("$%c", mp->metaname);
263 					break;
264 				}
265 			}
266 			if (mp->metaname != '\0')
267 				continue;
268 			(void) putchar('\\');
269 			c &= 0177;
270 		}
271 		if (isprint(c))
272 		{
273 			putchar(c);
274 			continue;
275 		}
276 
277 		/* wasn't a meta-macro -- find another way to print it */
278 		switch (c)
279 		{
280 		  case '\0':
281 			continue;
282 
283 		  case '\n':
284 			c = 'n';
285 			break;
286 
287 		  case '\r':
288 			c = 'r';
289 			break;
290 
291 		  case '\t':
292 			c = 't';
293 			break;
294 
295 		  default:
296 			(void) putchar('^');
297 			(void) putchar(c ^ 0100);
298 			continue;
299 		}
300 	}
301 	(void) fflush(stdout);
302 }
303 /*
304 **  MAKELOWER -- Translate a line into lower case
305 **
306 **	Parameters:
307 **		p -- the string to translate.  If NULL, return is
308 **			immediate.
309 **
310 **	Returns:
311 **		none.
312 **
313 **	Side Effects:
314 **		String pointed to by p is translated to lower case.
315 **
316 **	Called By:
317 **		parse
318 */
319 
320 makelower(p)
321 	register char *p;
322 {
323 	register char c;
324 
325 	if (p == NULL)
326 		return;
327 	for (; (c = *p) != '\0'; p++)
328 		if (isascii(c) && isupper(c))
329 			*p = tolower(c);
330 }
331 /*
332 **  BUILDFNAME -- build full name from gecos style entry.
333 **
334 **	This routine interprets the strange entry that would appear
335 **	in the GECOS field of the password file.
336 **
337 **	Parameters:
338 **		p -- name to build.
339 **		login -- the login name of this user (for &).
340 **		buf -- place to put the result.
341 **
342 **	Returns:
343 **		none.
344 **
345 **	Side Effects:
346 **		none.
347 */
348 
349 buildfname(gecos, login, buf)
350 	register char *gecos;
351 	char *login;
352 	char *buf;
353 {
354 	register char *p;
355 	register char *bp = buf;
356 	int l;
357 
358 	if (*gecos == '*')
359 		gecos++;
360 
361 	/* find length of final string */
362 	l = 0;
363 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
364 	{
365 		if (*p == '&')
366 			l += strlen(login);
367 		else
368 			l++;
369 	}
370 
371 	/* now fill in buf */
372 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
373 	{
374 		if (*p == '&')
375 		{
376 			(void) strcpy(bp, login);
377 			*bp = toupper(*bp);
378 			while (*bp != '\0')
379 				bp++;
380 		}
381 		else
382 			*bp++ = *p;
383 	}
384 	*bp = '\0';
385 }
386 /*
387 **  SAFEFILE -- return true if a file exists and is safe for a user.
388 **
389 **	Parameters:
390 **		fn -- filename to check.
391 **		uid -- user id to compare against.
392 **		gid -- group id to compare against.
393 **		uname -- user name to compare against (used for group
394 **			sets).
395 **		flags -- modifiers:
396 **			SFF_MUSTOWN -- "uid" must own this file.
397 **			SFF_NOSLINK -- file cannot be a symbolic link.
398 **		mode -- mode bits that must match.
399 **
400 **	Returns:
401 **		0 if fn exists, is owned by uid, and matches mode.
402 **		An errno otherwise.  The actual errno is cleared.
403 **
404 **	Side Effects:
405 **		none.
406 */
407 
408 #include <grp.h>
409 
410 #ifndef S_IXOTH
411 # define S_IXOTH	(S_IEXEC >> 6)
412 #endif
413 
414 #ifndef S_IXGRP
415 # define S_IXGRP	(S_IEXEC >> 3)
416 #endif
417 
418 #ifndef S_IXUSR
419 # define S_IXUSR	(S_IEXEC)
420 #endif
421 
422 int
423 safefile(fn, uid, gid, uname, flags, mode)
424 	char *fn;
425 	uid_t uid;
426 	gid_t gid;
427 	char *uname;
428 	int flags;
429 	int mode;
430 {
431 	register char *p;
432 	register struct group *gr = NULL;
433 	struct stat stbuf;
434 
435 	if (tTd(54, 4))
436 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
437 			fn, uid, gid, flags, mode);
438 	errno = 0;
439 
440 	for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
441 	{
442 		*p = '\0';
443 		if (stat(fn, &stbuf) < 0)
444 			break;
445 		if (uid == 0 && !bitset(SFF_ROOTOK, flags))
446 		{
447 			if (bitset(S_IXOTH, stbuf.st_mode))
448 				continue;
449 			break;
450 		}
451 		if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
452 			continue;
453 		if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
454 			continue;
455 #ifndef NO_GROUP_SET
456 		if (uname != NULL &&
457 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
458 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
459 		{
460 			register char **gp;
461 
462 			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
463 				if (strcmp(*gp, uname) == 0)
464 					break;
465 			if (gp != NULL && *gp != NULL &&
466 			    bitset(S_IXGRP, stbuf.st_mode))
467 				continue;
468 		}
469 #endif
470 		if (!bitset(S_IXOTH, stbuf.st_mode))
471 			break;
472 	}
473 	if (p != NULL)
474 	{
475 		int ret = errno;
476 
477 		if (ret == 0)
478 			ret = EACCES;
479 		if (tTd(54, 4))
480 			printf("\t[dir %s] %s\n", fn, errstring(ret));
481 		*p = '/';
482 		return ret;
483 	}
484 
485 #ifdef HASLSTAT
486 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf)
487 					: stat(fn, &stbuf)) < 0)
488 #else
489 	if (stat(fn, &stbuf) < 0)
490 #endif
491 	{
492 		int ret = errno;
493 
494 		if (tTd(54, 4))
495 			printf("\t%s\n", errstring(ret));
496 
497 		errno = 0;
498 		return ret;
499 	}
500 
501 #ifdef S_ISLNK
502 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
503 	{
504 		if (tTd(54, 4))
505 			printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode);
506 		return EPERM;
507 	}
508 #endif
509 
510 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
511 		mode >>= 6;
512 	else if (stbuf.st_uid != uid)
513 	{
514 		mode >>= 3;
515 		if (stbuf.st_gid == gid)
516 			;
517 #ifndef NO_GROUP_SET
518 		else if (uname != NULL &&
519 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
520 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
521 		{
522 			register char **gp;
523 
524 			for (gp = gr->gr_mem; *gp != NULL; gp++)
525 				if (strcmp(*gp, uname) == 0)
526 					break;
527 			if (*gp == NULL)
528 				mode >>= 3;
529 		}
530 #endif
531 		else
532 			mode >>= 3;
533 	}
534 	if (tTd(54, 4))
535 		printf("\t[uid %d, stat %o, mode %o] ",
536 			stbuf.st_uid, stbuf.st_mode, mode);
537 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
538 	     !bitset(SFF_MUSTOWN, flags)) &&
539 	    (stbuf.st_mode & mode) == mode)
540 	{
541 		if (tTd(54, 4))
542 			printf("\tOK\n");
543 		return 0;
544 	}
545 	if (tTd(54, 4))
546 		printf("\tEACCES\n");
547 	return EACCES;
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 fixcrlf(line, stripnl)
569 	char *line;
570 	bool stripnl;
571 {
572 	register char *p;
573 
574 	p = strchr(line, '\n');
575 	if (p == NULL)
576 		return;
577 	if (p > line && p[-1] == '\r')
578 		p--;
579 	if (!stripnl)
580 		*p++ = '\n';
581 	*p = '\0';
582 }
583 /*
584 **  DFOPEN -- determined file open
585 **
586 **	This routine has the semantics of fopen, except that it will
587 **	keep trying a few times to make this happen.  The idea is that
588 **	on very loaded systems, we may run out of resources (inodes,
589 **	whatever), so this tries to get around it.
590 */
591 
592 #ifndef O_ACCMODE
593 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
594 #endif
595 
596 struct omodes
597 {
598 	int	mask;
599 	int	mode;
600 	char	*farg;
601 } OpenModes[] =
602 {
603 	O_ACCMODE,		O_RDONLY,		"r",
604 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
605 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
606 	O_TRUNC,		0,			"w+",
607 	O_APPEND,		O_APPEND,		"a+",
608 	0,			0,			"r+",
609 };
610 
611 FILE *
612 dfopen(filename, omode, cmode)
613 	char *filename;
614 	int omode;
615 	int cmode;
616 {
617 	register int tries;
618 	int fd;
619 	register struct omodes *om;
620 	struct stat st;
621 
622 	for (om = OpenModes; om->mask != 0; om++)
623 		if ((omode & om->mask) == om->mode)
624 			break;
625 
626 	for (tries = 0; tries < 10; tries++)
627 	{
628 		sleep((unsigned) (10 * tries));
629 		errno = 0;
630 		fd = open(filename, omode, cmode);
631 		if (fd >= 0)
632 			break;
633 		switch (errno)
634 		{
635 		  case ENFILE:		/* system file table full */
636 		  case EINTR:		/* interrupted syscall */
637 #ifdef ETXTBSY
638 		  case ETXTBSY:		/* Apollo: net file locked */
639 #endif
640 			continue;
641 		}
642 		break;
643 	}
644 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
645 	{
646 		int locktype;
647 
648 		/* lock the file to avoid accidental conflicts */
649 		if ((omode & O_ACCMODE) != O_RDONLY)
650 			locktype = LOCK_EX;
651 		else
652 			locktype = LOCK_SH;
653 		(void) lockfile(fd, filename, NULL, locktype);
654 		errno = 0;
655 	}
656 	if (fd < 0)
657 		return NULL;
658 	else
659 		return fdopen(fd, om->farg);
660 }
661 /*
662 **  PUTLINE -- put a line like fputs obeying SMTP conventions
663 **
664 **	This routine always guarantees outputing a newline (or CRLF,
665 **	as appropriate) at the end of the string.
666 **
667 **	Parameters:
668 **		l -- line to put.
669 **		mci -- the mailer connection information.
670 **
671 **	Returns:
672 **		none
673 **
674 **	Side Effects:
675 **		output of l to fp.
676 */
677 
678 putline(l, mci)
679 	register char *l;
680 	register MCI *mci;
681 {
682 	register char *p;
683 	register char svchar;
684 	int slop = 0;
685 
686 	/* strip out 0200 bits -- these can look like TELNET protocol */
687 	if (bitset(MCIF_7BIT, mci->mci_flags))
688 	{
689 		for (p = l; (svchar = *p) != '\0'; ++p)
690 			if (bitset(0200, svchar))
691 				*p = svchar &~ 0200;
692 	}
693 
694 	do
695 	{
696 		/* find the end of the line */
697 		p = strchr(l, '\n');
698 		if (p == NULL)
699 			p = &l[strlen(l)];
700 
701 		if (TrafficLogFile != NULL)
702 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
703 
704 		/* check for line overflow */
705 		while (mci->mci_mailer->m_linelimit > 0 &&
706 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
707 		{
708 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
709 
710 			svchar = *q;
711 			*q = '\0';
712 			if (l[0] == '.' && slop == 0 &&
713 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
714 			{
715 				(void) putc('.', mci->mci_out);
716 				if (TrafficLogFile != NULL)
717 					(void) putc('.', TrafficLogFile);
718 			}
719 			fputs(l, mci->mci_out);
720 			(void) putc('!', mci->mci_out);
721 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
722 			(void) putc(' ', mci->mci_out);
723 			if (TrafficLogFile != NULL)
724 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
725 					l, getpid());
726 			*q = svchar;
727 			l = q;
728 			slop = 1;
729 		}
730 
731 		/* output last part */
732 		if (l[0] == '.' && slop == 0 &&
733 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
734 		{
735 			(void) putc('.', mci->mci_out);
736 			if (TrafficLogFile != NULL)
737 				(void) putc('.', TrafficLogFile);
738 		}
739 		if (TrafficLogFile != NULL)
740 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
741 		for ( ; l < p; ++l)
742 			(void) putc(*l, mci->mci_out);
743 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
744 		if (*l == '\n')
745 			++l;
746 	} while (l[0] != '\0');
747 }
748 /*
749 **  XUNLINK -- unlink a file, doing logging as appropriate.
750 **
751 **	Parameters:
752 **		f -- name of file to unlink.
753 **
754 **	Returns:
755 **		none.
756 **
757 **	Side Effects:
758 **		f is unlinked.
759 */
760 
761 xunlink(f)
762 	char *f;
763 {
764 	register int i;
765 
766 # ifdef LOG
767 	if (LogLevel > 98)
768 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
769 # endif /* LOG */
770 
771 	i = unlink(f);
772 # ifdef LOG
773 	if (i < 0 && LogLevel > 97)
774 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
775 # endif /* LOG */
776 }
777 /*
778 **  XFCLOSE -- close a file, doing logging as appropriate.
779 **
780 **	Parameters:
781 **		fp -- file pointer for the file to close
782 **		a, b -- miscellaneous crud to print for debugging
783 **
784 **	Returns:
785 **		none.
786 **
787 **	Side Effects:
788 **		fp is closed.
789 */
790 
791 xfclose(fp, a, b)
792 	FILE *fp;
793 	char *a, *b;
794 {
795 	if (tTd(53, 99))
796 		printf("xfclose(%x) %s %s\n", fp, a, b);
797 #ifdef XDEBUG
798 	if (fileno(fp) == 1)
799 		syserr("xfclose(%s %s): fd = 1", a, b);
800 #endif
801 	if (fclose(fp) < 0 && tTd(53, 99))
802 		printf("xfclose FAILURE: %s\n", errstring(errno));
803 }
804 /*
805 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
806 **
807 **	Parameters:
808 **		buf -- place to put the input line.
809 **		siz -- size of buf.
810 **		fp -- file to read from.
811 **		timeout -- the timeout before error occurs.
812 **		during -- what we are trying to read (for error messages).
813 **
814 **	Returns:
815 **		NULL on error (including timeout).  This will also leave
816 **			buf containing a null string.
817 **		buf otherwise.
818 **
819 **	Side Effects:
820 **		none.
821 */
822 
823 static jmp_buf	CtxReadTimeout;
824 static int	readtimeout();
825 
826 char *
827 sfgets(buf, siz, fp, timeout, during)
828 	char *buf;
829 	int siz;
830 	FILE *fp;
831 	time_t timeout;
832 	char *during;
833 {
834 	register EVENT *ev = NULL;
835 	register char *p;
836 
837 	if (fp == NULL)
838 	{
839 		buf[0] = '\0';
840 		return NULL;
841 	}
842 
843 	/* set the timeout */
844 	if (timeout != 0)
845 	{
846 		if (setjmp(CtxReadTimeout) != 0)
847 		{
848 # ifdef LOG
849 			syslog(LOG_NOTICE,
850 			    "timeout waiting for input from %s during %s\n",
851 			    CurHostName? CurHostName: "local", during);
852 # endif
853 			errno = 0;
854 			usrerr("451 timeout waiting for input during %s",
855 				during);
856 			buf[0] = '\0';
857 #ifdef XDEBUG
858 			checkfd012(during);
859 #endif
860 			return (NULL);
861 		}
862 		ev = setevent(timeout, readtimeout, 0);
863 	}
864 
865 	/* try to read */
866 	p = NULL;
867 	while (!feof(fp) && !ferror(fp))
868 	{
869 		errno = 0;
870 		p = fgets(buf, siz, fp);
871 		if (p != NULL || errno != EINTR)
872 			break;
873 		clearerr(fp);
874 	}
875 
876 	/* clear the event if it has not sprung */
877 	clrevent(ev);
878 
879 	/* clean up the books and exit */
880 	LineNumber++;
881 	if (p == NULL)
882 	{
883 		buf[0] = '\0';
884 		if (TrafficLogFile != NULL)
885 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
886 		return (NULL);
887 	}
888 	if (TrafficLogFile != NULL)
889 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
890 	if (SevenBitInput)
891 	{
892 		for (p = buf; *p != '\0'; p++)
893 			*p &= ~0200;
894 	}
895 	else if (!HasEightBits)
896 	{
897 		for (p = buf; *p != '\0'; p++)
898 		{
899 			if (bitset(0200, *p))
900 			{
901 				HasEightBits = TRUE;
902 				break;
903 			}
904 		}
905 	}
906 	return (buf);
907 }
908 
909 static
910 readtimeout(timeout)
911 	time_t timeout;
912 {
913 	longjmp(CtxReadTimeout, 1);
914 }
915 /*
916 **  FGETFOLDED -- like fgets, but know about folded lines.
917 **
918 **	Parameters:
919 **		buf -- place to put result.
920 **		n -- bytes available.
921 **		f -- file to read from.
922 **
923 **	Returns:
924 **		input line(s) on success, NULL on error or EOF.
925 **		This will normally be buf -- unless the line is too
926 **			long, when it will be xalloc()ed.
927 **
928 **	Side Effects:
929 **		buf gets lines from f, with continuation lines (lines
930 **		with leading white space) appended.  CRLF's are mapped
931 **		into single newlines.  Any trailing NL is stripped.
932 */
933 
934 char *
935 fgetfolded(buf, n, f)
936 	char *buf;
937 	register int n;
938 	FILE *f;
939 {
940 	register char *p = buf;
941 	char *bp = buf;
942 	register int i;
943 
944 	n--;
945 	while ((i = getc(f)) != EOF)
946 	{
947 		if (i == '\r')
948 		{
949 			i = getc(f);
950 			if (i != '\n')
951 			{
952 				if (i != EOF)
953 					(void) ungetc(i, f);
954 				i = '\r';
955 			}
956 		}
957 		if (--n <= 0)
958 		{
959 			/* allocate new space */
960 			char *nbp;
961 			int nn;
962 
963 			nn = (p - bp);
964 			if (nn < MEMCHUNKSIZE)
965 				nn *= 2;
966 			else
967 				nn += MEMCHUNKSIZE;
968 			nbp = xalloc(nn);
969 			bcopy(bp, nbp, p - bp);
970 			p = &nbp[p - bp];
971 			if (bp != buf)
972 				free(bp);
973 			bp = nbp;
974 			n = nn - (p - bp);
975 		}
976 		*p++ = i;
977 		if (i == '\n')
978 		{
979 			LineNumber++;
980 			i = getc(f);
981 			if (i != EOF)
982 				(void) ungetc(i, f);
983 			if (i != ' ' && i != '\t')
984 				break;
985 		}
986 	}
987 	if (p == bp)
988 		return (NULL);
989 	*--p = '\0';
990 	return (bp);
991 }
992 /*
993 **  CURTIME -- return current time.
994 **
995 **	Parameters:
996 **		none.
997 **
998 **	Returns:
999 **		the current time.
1000 **
1001 **	Side Effects:
1002 **		none.
1003 */
1004 
1005 time_t
1006 curtime()
1007 {
1008 	auto time_t t;
1009 
1010 	(void) time(&t);
1011 	return (t);
1012 }
1013 /*
1014 **  ATOBOOL -- convert a string representation to boolean.
1015 **
1016 **	Defaults to "TRUE"
1017 **
1018 **	Parameters:
1019 **		s -- string to convert.  Takes "tTyY" as true,
1020 **			others as false.
1021 **
1022 **	Returns:
1023 **		A boolean representation of the string.
1024 **
1025 **	Side Effects:
1026 **		none.
1027 */
1028 
1029 bool
1030 atobool(s)
1031 	register char *s;
1032 {
1033 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1034 		return (TRUE);
1035 	return (FALSE);
1036 }
1037 /*
1038 **  ATOOCT -- convert a string representation to octal.
1039 **
1040 **	Parameters:
1041 **		s -- string to convert.
1042 **
1043 **	Returns:
1044 **		An integer representing the string interpreted as an
1045 **		octal number.
1046 **
1047 **	Side Effects:
1048 **		none.
1049 */
1050 
1051 atooct(s)
1052 	register char *s;
1053 {
1054 	register int i = 0;
1055 
1056 	while (*s >= '0' && *s <= '7')
1057 		i = (i << 3) | (*s++ - '0');
1058 	return (i);
1059 }
1060 /*
1061 **  WAITFOR -- wait for a particular process id.
1062 **
1063 **	Parameters:
1064 **		pid -- process id to wait for.
1065 **
1066 **	Returns:
1067 **		status of pid.
1068 **		-1 if pid never shows up.
1069 **
1070 **	Side Effects:
1071 **		none.
1072 */
1073 
1074 int
1075 waitfor(pid)
1076 	int pid;
1077 {
1078 #ifdef WAITUNION
1079 	union wait st;
1080 #else
1081 	auto int st;
1082 #endif
1083 	int i;
1084 
1085 	do
1086 	{
1087 		errno = 0;
1088 		i = wait(&st);
1089 	} while ((i >= 0 || errno == EINTR) && i != pid);
1090 	if (i < 0)
1091 		return -1;
1092 #ifdef WAITUNION
1093 	return st.w_status;
1094 #else
1095 	return st;
1096 #endif
1097 }
1098 /*
1099 **  BITINTERSECT -- tell if two bitmaps intersect
1100 **
1101 **	Parameters:
1102 **		a, b -- the bitmaps in question
1103 **
1104 **	Returns:
1105 **		TRUE if they have a non-null intersection
1106 **		FALSE otherwise
1107 **
1108 **	Side Effects:
1109 **		none.
1110 */
1111 
1112 bool
1113 bitintersect(a, b)
1114 	BITMAP a;
1115 	BITMAP b;
1116 {
1117 	int i;
1118 
1119 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1120 		if ((a[i] & b[i]) != 0)
1121 			return (TRUE);
1122 	return (FALSE);
1123 }
1124 /*
1125 **  BITZEROP -- tell if a bitmap is all zero
1126 **
1127 **	Parameters:
1128 **		map -- the bit map to check
1129 **
1130 **	Returns:
1131 **		TRUE if map is all zero.
1132 **		FALSE if there are any bits set in map.
1133 **
1134 **	Side Effects:
1135 **		none.
1136 */
1137 
1138 bool
1139 bitzerop(map)
1140 	BITMAP map;
1141 {
1142 	int i;
1143 
1144 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1145 		if (map[i] != 0)
1146 			return (FALSE);
1147 	return (TRUE);
1148 }
1149 /*
1150 **  STRCONTAINEDIN -- tell if one string is contained in another
1151 **
1152 **	Parameters:
1153 **		a -- possible substring.
1154 **		b -- possible superstring.
1155 **
1156 **	Returns:
1157 **		TRUE if a is contained in b.
1158 **		FALSE otherwise.
1159 */
1160 
1161 bool
1162 strcontainedin(a, b)
1163 	register char *a;
1164 	register char *b;
1165 {
1166 	int la;
1167 	int lb;
1168 	int c;
1169 
1170 	la = strlen(a);
1171 	lb = strlen(b);
1172 	c = *a;
1173 	if (isascii(c) && isupper(c))
1174 		c = tolower(c);
1175 	for (; lb-- >= la; b++)
1176 	{
1177 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1178 			continue;
1179 		if (strncasecmp(a, b, la) == 0)
1180 			return TRUE;
1181 	}
1182 	return FALSE;
1183 }
1184 /*
1185 **  CHECKFD012 -- check low numbered file descriptors
1186 **
1187 **	File descriptors 0, 1, and 2 should be open at all times.
1188 **	This routine verifies that, and fixes it if not true.
1189 **
1190 **	Parameters:
1191 **		where -- a tag printed if the assertion failed
1192 **
1193 **	Returns:
1194 **		none
1195 */
1196 
1197 checkfd012(where)
1198 	char *where;
1199 {
1200 #ifdef XDEBUG
1201 	register int i;
1202 	struct stat stbuf;
1203 
1204 	for (i = 0; i < 3; i++)
1205 	{
1206 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1207 		{
1208 			/* oops.... */
1209 			int fd;
1210 
1211 			syserr("%s: fd %d not open", where, i);
1212 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1213 			if (fd != i)
1214 			{
1215 				(void) dup2(fd, i);
1216 				(void) close(fd);
1217 			}
1218 		}
1219 	}
1220 #endif /* XDEBUG */
1221 }
1222 /*
1223 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1224 **
1225 **	Parameters:
1226 **		logit -- if set, send output to syslog; otherwise
1227 **			print for debugging.
1228 **
1229 **	Returns:
1230 **		none.
1231 */
1232 
1233 #include <netdb.h>
1234 #include <arpa/inet.h>
1235 
1236 printopenfds(logit)
1237 	bool logit;
1238 {
1239 	register int fd;
1240 	extern int DtableSize;
1241 
1242 	for (fd = 0; fd < DtableSize; fd++)
1243 		dumpfd(fd, FALSE, logit);
1244 }
1245 /*
1246 **  DUMPFD -- dump a file descriptor
1247 **
1248 **	Parameters:
1249 **		fd -- the file descriptor to dump.
1250 **		printclosed -- if set, print a notification even if
1251 **			it is closed; otherwise print nothing.
1252 **		logit -- if set, send output to syslog instead of stdout.
1253 */
1254 
1255 dumpfd(fd, printclosed, logit)
1256 	int fd;
1257 	bool printclosed;
1258 	bool logit;
1259 {
1260 	register struct hostent *hp;
1261 	register char *p;
1262 	char *fmtstr;
1263 	struct sockaddr_in sin;
1264 	auto int slen;
1265 	struct stat st;
1266 	char buf[200];
1267 
1268 	p = buf;
1269 	sprintf(p, "%3d: ", fd);
1270 	p += strlen(p);
1271 
1272 	if (fstat(fd, &st) < 0)
1273 	{
1274 		if (printclosed || errno != EBADF)
1275 		{
1276 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1277 			goto printit;
1278 		}
1279 		return;
1280 	}
1281 
1282 	slen = fcntl(fd, F_GETFL, NULL);
1283 	if (slen != -1)
1284 	{
1285 		sprintf(p, "fl=0x%x, ", slen);
1286 		p += strlen(p);
1287 	}
1288 
1289 	sprintf(p, "mode=%o: ", st.st_mode);
1290 	p += strlen(p);
1291 	switch (st.st_mode & S_IFMT)
1292 	{
1293 #ifdef S_IFSOCK
1294 	  case S_IFSOCK:
1295 		sprintf(p, "SOCK ");
1296 		p += strlen(p);
1297 		slen = sizeof sin;
1298 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
1299 			sprintf(p, "(badsock)");
1300 		else
1301 		{
1302 			hp = gethostbyaddr((char *) &sin.sin_addr,
1303 					   INADDRSZ, AF_INET);
1304 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1305 						   : hp->h_name, ntohs(sin.sin_port));
1306 		}
1307 		p += strlen(p);
1308 		sprintf(p, "->");
1309 		p += strlen(p);
1310 		slen = sizeof sin;
1311 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
1312 			sprintf(p, "(badsock)");
1313 		else
1314 		{
1315 			hp = gethostbyaddr((char *) &sin.sin_addr,
1316 					   INADDRSZ, AF_INET);
1317 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1318 						   : hp->h_name, ntohs(sin.sin_port));
1319 		}
1320 		break;
1321 #endif
1322 
1323 	  case S_IFCHR:
1324 		sprintf(p, "CHR: ");
1325 		p += strlen(p);
1326 		goto defprint;
1327 
1328 	  case S_IFBLK:
1329 		sprintf(p, "BLK: ");
1330 		p += strlen(p);
1331 		goto defprint;
1332 
1333 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1334 	  case S_IFIFO:
1335 		sprintf(p, "FIFO: ");
1336 		p += strlen(p);
1337 		goto defprint;
1338 #endif
1339 
1340 #ifdef S_IFDIR
1341 	  case S_IFDIR:
1342 		sprintf(p, "DIR: ");
1343 		p += strlen(p);
1344 		goto defprint;
1345 #endif
1346 
1347 #ifdef S_IFLNK
1348 	  case S_IFLNK:
1349 		sprintf(p, "LNK: ");
1350 		p += strlen(p);
1351 		goto defprint;
1352 #endif
1353 
1354 	  default:
1355 defprint:
1356 		if (sizeof st.st_size > sizeof (long))
1357 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
1358 		else
1359 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
1360 		sprintf(p, fmtstr,
1361 			major(st.st_dev), minor(st.st_dev), st.st_ino,
1362 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1363 		break;
1364 	}
1365 
1366 printit:
1367 #ifdef LOG
1368 	if (logit)
1369 		syslog(LOG_DEBUG, "%s", buf);
1370 	else
1371 #endif
1372 		printf("%s\n", buf);
1373 }
1374 /*
1375 **  SHORTENSTRING -- return short version of a string
1376 **
1377 **	If the string is already short, just return it.  If it is too
1378 **	long, return the head and tail of the string.
1379 **
1380 **	Parameters:
1381 **		s -- the string to shorten.
1382 **		m -- the max length of the string.
1383 **
1384 **	Returns:
1385 **		Either s or a short version of s.
1386 */
1387 
1388 #ifndef MAXSHORTSTR
1389 # define MAXSHORTSTR	203
1390 #endif
1391 
1392 char *
1393 shortenstring(s, m)
1394 	register char *s;
1395 	int m;
1396 {
1397 	int l;
1398 	static char buf[MAXSHORTSTR + 1];
1399 
1400 	l = strlen(s);
1401 	if (l < m)
1402 		return s;
1403 	if (m > MAXSHORTSTR)
1404 		m = MAXSHORTSTR;
1405 	else if (m < 10)
1406 	{
1407 		if (m < 5)
1408 		{
1409 			strncpy(buf, s, m);
1410 			buf[m] = '\0';
1411 			return buf;
1412 		}
1413 		strncpy(buf, s, m - 3);
1414 		strcpy(buf + m - 3, "...");
1415 		return buf;
1416 	}
1417 	m = (m - 3) / 2;
1418 	strncpy(buf, s, m);
1419 	strcpy(buf + m, "...");
1420 	strcpy(buf + m + 3, s + l - m);
1421 	return buf;
1422 }
1423 /*
1424 **  GET_COLUMN  -- look up a Column in a line buffer
1425 **
1426 **	Parameters:
1427 **		line -- the raw text line to search.
1428 **		col -- the column number to fetch.
1429 **		delim -- the delimiter between columns.  If null,
1430 **			use white space.
1431 **		buf -- the output buffer.
1432 **
1433 **	Returns:
1434 **		buf if successful.
1435 **		NULL otherwise.
1436 */
1437 
1438 char *
1439 get_column(line, col, delim, buf)
1440 	char line[];
1441 	int col;
1442 	char delim;
1443 	char buf[];
1444 {
1445 	char *p;
1446 	char *begin, *end;
1447 	int i;
1448 	char delimbuf[3];
1449 
1450 	if (delim == '\0')
1451 		strcpy(delimbuf, "\t ");
1452 	else
1453 	{
1454 		delimbuf[0] = delim;
1455 		delimbuf[1] = '\0';
1456 	}
1457 
1458 	p = line;
1459 	if (*p == '\0')
1460 		return NULL;			/* line empty */
1461 	if (*p == delim && col == 0)
1462 		return NULL;			/* first column empty */
1463 
1464 	begin = line;
1465 
1466 	if (col == 0 && delim == '\0')
1467 	{
1468 		while (*begin && isspace(*begin))
1469 			begin++;
1470 	}
1471 
1472 	for (i = 0; i < col; i++)
1473 	{
1474 		if ((begin = strpbrk(begin, delimbuf)) == NULL)
1475 			return NULL;		/* no such column */
1476 		begin++;
1477 		if (delim == '\0')
1478 		{
1479 			while (*begin && isspace(*begin))
1480 				begin++;
1481 		}
1482 	}
1483 
1484 	end = strpbrk(begin, delimbuf);
1485 	if (end == NULL)
1486 	{
1487 		strcpy(buf, begin);
1488 	}
1489 	else
1490 	{
1491 		strncpy(buf, begin, end - begin);
1492 		buf[end - begin] = '\0';
1493 	}
1494 	return buf;
1495 }
1496