xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision e58c8952)
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.39 (Berkeley) 04/14/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 || c == MACROEXPAND)
247 			{
248 				putchar('$');
249 				continue;
250 			}
251 			for (mp = MetaMacros; mp->metaname != '\0'; mp++)
252 			{
253 				if ((mp->metaval & 0377) == c)
254 				{
255 					printf("$%c", mp->metaname);
256 					break;
257 				}
258 			}
259 			if (mp->metaname != '\0')
260 				continue;
261 			(void) putchar('\\');
262 			c &= 0177;
263 		}
264 		if (isprint(c))
265 		{
266 			putchar(c);
267 			continue;
268 		}
269 
270 		/* wasn't a meta-macro -- find another way to print it */
271 		switch (c)
272 		{
273 		  case '\0':
274 			continue;
275 
276 		  case '\n':
277 			c = 'n';
278 			break;
279 
280 		  case '\r':
281 			c = 'r';
282 			break;
283 
284 		  case '\t':
285 			c = 't';
286 			break;
287 
288 		  default:
289 			(void) putchar('^');
290 			(void) putchar(c ^ 0100);
291 			continue;
292 		}
293 	}
294 	(void) fflush(stdout);
295 }
296 /*
297 **  MAKELOWER -- Translate a line into lower case
298 **
299 **	Parameters:
300 **		p -- the string to translate.  If NULL, return is
301 **			immediate.
302 **
303 **	Returns:
304 **		none.
305 **
306 **	Side Effects:
307 **		String pointed to by p is translated to lower case.
308 **
309 **	Called By:
310 **		parse
311 */
312 
313 makelower(p)
314 	register char *p;
315 {
316 	register char c;
317 
318 	if (p == NULL)
319 		return;
320 	for (; (c = *p) != '\0'; p++)
321 		if (isascii(c) && isupper(c))
322 			*p = tolower(c);
323 }
324 /*
325 **  BUILDFNAME -- build full name from gecos style entry.
326 **
327 **	This routine interprets the strange entry that would appear
328 **	in the GECOS field of the password file.
329 **
330 **	Parameters:
331 **		p -- name to build.
332 **		login -- the login name of this user (for &).
333 **		buf -- place to put the result.
334 **
335 **	Returns:
336 **		none.
337 **
338 **	Side Effects:
339 **		none.
340 */
341 
342 buildfname(gecos, login, buf)
343 	register char *gecos;
344 	char *login;
345 	char *buf;
346 {
347 	register char *p;
348 	register char *bp = buf;
349 	int l;
350 
351 	if (*gecos == '*')
352 		gecos++;
353 
354 	/* find length of final string */
355 	l = 0;
356 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
357 	{
358 		if (*p == '&')
359 			l += strlen(login);
360 		else
361 			l++;
362 	}
363 
364 	/* now fill in buf */
365 	for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++)
366 	{
367 		if (*p == '&')
368 		{
369 			(void) strcpy(bp, login);
370 			*bp = toupper(*bp);
371 			while (*bp != '\0')
372 				bp++;
373 		}
374 		else
375 			*bp++ = *p;
376 	}
377 	*bp = '\0';
378 }
379 /*
380 **  SAFEFILE -- return true if a file exists and is safe for a user.
381 **
382 **	Parameters:
383 **		fn -- filename to check.
384 **		uid -- user id to compare against.
385 **		gid -- group id to compare against.
386 **		uname -- user name to compare against (used for group
387 **			sets).
388 **		flags -- modifiers:
389 **			SFF_MUSTOWN -- "uid" must own this file.
390 **			SFF_NOSLINK -- file cannot be a symbolic link.
391 **		mode -- mode bits that must match.
392 **
393 **	Returns:
394 **		0 if fn exists, is owned by uid, and matches mode.
395 **		An errno otherwise.  The actual errno is cleared.
396 **
397 **	Side Effects:
398 **		none.
399 */
400 
401 #include <grp.h>
402 
403 #ifndef S_IXOTH
404 # define S_IXOTH	(S_IEXEC >> 6)
405 #endif
406 
407 #ifndef S_IXGRP
408 # define S_IXGRP	(S_IEXEC >> 3)
409 #endif
410 
411 #ifndef S_IXUSR
412 # define S_IXUSR	(S_IEXEC)
413 #endif
414 
415 int
416 safefile(fn, uid, gid, uname, flags, mode)
417 	char *fn;
418 	uid_t uid;
419 	gid_t gid;
420 	char *uname;
421 	int flags;
422 	int mode;
423 {
424 	register char *p;
425 	register struct group *gr = NULL;
426 	struct stat stbuf;
427 
428 	if (tTd(54, 4))
429 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
430 			fn, uid, gid, flags, mode);
431 	errno = 0;
432 
433 	for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
434 	{
435 		*p = '\0';
436 		if (stat(fn, &stbuf) < 0)
437 			break;
438 		if (uid == 0 && !bitset(SFF_ROOTOK, flags))
439 		{
440 			if (bitset(S_IXOTH, stbuf.st_mode))
441 				continue;
442 			break;
443 		}
444 		if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
445 			continue;
446 		if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
447 			continue;
448 #ifndef NO_GROUP_SET
449 		if (uname != NULL &&
450 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
451 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
452 		{
453 			register char **gp;
454 
455 			for (gp = gr->gr_mem; *gp != NULL; gp++)
456 				if (strcmp(*gp, uname) == 0)
457 					break;
458 			if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
459 				continue;
460 		}
461 #endif
462 		if (!bitset(S_IXOTH, stbuf.st_mode))
463 			break;
464 	}
465 	if (p != NULL)
466 	{
467 		int ret = errno;
468 
469 		if (ret == 0)
470 			ret = EACCES;
471 		if (tTd(54, 4))
472 			printf("\t[dir %s] %s\n", fn, errstring(ret));
473 		*p = '/';
474 		return ret;
475 	}
476 
477 #ifdef HASLSTAT
478 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, &stbuf)
479 					: stat(fn, &stbuf)) < 0)
480 #else
481 	if (stat(fn, &stbuf) < 0)
482 #endif
483 	{
484 		int ret = errno;
485 
486 		if (tTd(54, 4))
487 			printf("\t%s\n", errstring(ret));
488 
489 		errno = 0;
490 		return ret;
491 	}
492 
493 #ifdef S_ISLNK
494 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
495 	{
496 		if (tTd(54, 4))
497 			printf("\t[slink mode %o]\tEPERM\n", stbuf.st_mode);
498 		return EPERM;
499 	}
500 #endif
501 
502 	if (uid == 0 && !bitset(SFF_ROOTOK, flags))
503 		mode >>= 6;
504 	else if (stbuf.st_uid != uid)
505 	{
506 		mode >>= 3;
507 		if (stbuf.st_gid == gid)
508 			;
509 #ifndef NO_GROUP_SET
510 		else if (uname != NULL &&
511 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
512 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
513 		{
514 			register char **gp;
515 
516 			for (gp = gr->gr_mem; *gp != NULL; gp++)
517 				if (strcmp(*gp, uname) == 0)
518 					break;
519 			if (*gp == NULL)
520 				mode >>= 3;
521 		}
522 #endif
523 		else
524 			mode >>= 3;
525 	}
526 	if (tTd(54, 4))
527 		printf("\t[uid %d, stat %o, mode %o] ",
528 			stbuf.st_uid, stbuf.st_mode, mode);
529 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
530 	     !bitset(SFF_MUSTOWN, flags)) &&
531 	    (stbuf.st_mode & mode) == mode)
532 	{
533 		if (tTd(54, 4))
534 			printf("\tOK\n");
535 		return 0;
536 	}
537 	if (tTd(54, 4))
538 		printf("\tEACCES\n");
539 	return EACCES;
540 }
541 /*
542 **  FIXCRLF -- fix <CR><LF> in line.
543 **
544 **	Looks for the <CR><LF> combination and turns it into the
545 **	UNIX canonical <NL> character.  It only takes one line,
546 **	i.e., it is assumed that the first <NL> found is the end
547 **	of the line.
548 **
549 **	Parameters:
550 **		line -- the line to fix.
551 **		stripnl -- if true, strip the newline also.
552 **
553 **	Returns:
554 **		none.
555 **
556 **	Side Effects:
557 **		line is changed in place.
558 */
559 
560 fixcrlf(line, stripnl)
561 	char *line;
562 	bool stripnl;
563 {
564 	register char *p;
565 
566 	p = strchr(line, '\n');
567 	if (p == NULL)
568 		return;
569 	if (p > line && p[-1] == '\r')
570 		p--;
571 	if (!stripnl)
572 		*p++ = '\n';
573 	*p = '\0';
574 }
575 /*
576 **  DFOPEN -- determined file open
577 **
578 **	This routine has the semantics of fopen, except that it will
579 **	keep trying a few times to make this happen.  The idea is that
580 **	on very loaded systems, we may run out of resources (inodes,
581 **	whatever), so this tries to get around it.
582 */
583 
584 #ifndef O_ACCMODE
585 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
586 #endif
587 
588 struct omodes
589 {
590 	int	mask;
591 	int	mode;
592 	char	*farg;
593 } OpenModes[] =
594 {
595 	O_ACCMODE,		O_RDONLY,		"r",
596 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
597 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
598 	O_TRUNC,		0,			"w+",
599 	O_APPEND,		O_APPEND,		"a+",
600 	0,			0,			"r+",
601 };
602 
603 FILE *
604 dfopen(filename, omode, cmode)
605 	char *filename;
606 	int omode;
607 	int cmode;
608 {
609 	register int tries;
610 	int fd;
611 	register struct omodes *om;
612 	struct stat st;
613 
614 	for (om = OpenModes; om->mask != 0; om++)
615 		if ((omode & om->mask) == om->mode)
616 			break;
617 
618 	for (tries = 0; tries < 10; tries++)
619 	{
620 		sleep((unsigned) (10 * tries));
621 		errno = 0;
622 		fd = open(filename, omode, cmode);
623 		if (fd >= 0)
624 			break;
625 		switch (errno)
626 		{
627 		  case ENFILE:		/* system file table full */
628 		  case EINTR:		/* interrupted syscall */
629 #ifdef ETXTBSY
630 		  case ETXTBSY:		/* Apollo: net file locked */
631 #endif
632 			continue;
633 		}
634 		break;
635 	}
636 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
637 	{
638 		int locktype;
639 
640 		/* lock the file to avoid accidental conflicts */
641 		if ((omode & O_ACCMODE) != O_RDONLY)
642 			locktype = LOCK_EX;
643 		else
644 			locktype = LOCK_SH;
645 		(void) lockfile(fd, filename, NULL, locktype);
646 		errno = 0;
647 	}
648 	if (fd < 0)
649 		return NULL;
650 	else
651 		return fdopen(fd, om->farg);
652 }
653 /*
654 **  PUTLINE -- put a line like fputs obeying SMTP conventions
655 **
656 **	This routine always guarantees outputing a newline (or CRLF,
657 **	as appropriate) at the end of the string.
658 **
659 **	Parameters:
660 **		l -- line to put.
661 **		mci -- the mailer connection information.
662 **
663 **	Returns:
664 **		none
665 **
666 **	Side Effects:
667 **		output of l to fp.
668 */
669 
670 putline(l, mci)
671 	register char *l;
672 	register MCI *mci;
673 {
674 	register char *p;
675 	register char svchar;
676 	int slop = 0;
677 
678 	/* strip out 0200 bits -- these can look like TELNET protocol */
679 	if (bitset(MCIF_7BIT, mci->mci_flags))
680 	{
681 		for (p = l; (svchar = *p) != '\0'; ++p)
682 			if (bitset(0200, svchar))
683 				*p = svchar &~ 0200;
684 	}
685 
686 	do
687 	{
688 		/* find the end of the line */
689 		p = strchr(l, '\n');
690 		if (p == NULL)
691 			p = &l[strlen(l)];
692 
693 		if (TrafficLogFile != NULL)
694 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
695 
696 		/* check for line overflow */
697 		while (mci->mci_mailer->m_linelimit > 0 &&
698 		       (p - l + slop) > mci->mci_mailer->m_linelimit)
699 		{
700 			register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1];
701 
702 			svchar = *q;
703 			*q = '\0';
704 			if (l[0] == '.' && slop == 0 &&
705 			    bitnset(M_XDOT, mci->mci_mailer->m_flags))
706 			{
707 				(void) putc('.', mci->mci_out);
708 				if (TrafficLogFile != NULL)
709 					(void) putc('.', TrafficLogFile);
710 			}
711 			fputs(l, mci->mci_out);
712 			(void) putc('!', mci->mci_out);
713 			fputs(mci->mci_mailer->m_eol, mci->mci_out);
714 			(void) putc(' ', mci->mci_out);
715 			if (TrafficLogFile != NULL)
716 				fprintf(TrafficLogFile, "%s!\n%05d >>>  ",
717 					l, getpid());
718 			*q = svchar;
719 			l = q;
720 			slop = 1;
721 		}
722 
723 		/* output last part */
724 		if (l[0] == '.' && slop == 0 &&
725 		    bitnset(M_XDOT, mci->mci_mailer->m_flags))
726 		{
727 			(void) putc('.', mci->mci_out);
728 			if (TrafficLogFile != NULL)
729 				(void) putc('.', TrafficLogFile);
730 		}
731 		if (TrafficLogFile != NULL)
732 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
733 		for ( ; l < p; ++l)
734 			(void) putc(*l, mci->mci_out);
735 		fputs(mci->mci_mailer->m_eol, mci->mci_out);
736 		if (*l == '\n')
737 			++l;
738 	} while (l[0] != '\0');
739 }
740 /*
741 **  XUNLINK -- unlink a file, doing logging as appropriate.
742 **
743 **	Parameters:
744 **		f -- name of file to unlink.
745 **
746 **	Returns:
747 **		none.
748 **
749 **	Side Effects:
750 **		f is unlinked.
751 */
752 
753 xunlink(f)
754 	char *f;
755 {
756 	register int i;
757 
758 # ifdef LOG
759 	if (LogLevel > 98)
760 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
761 # endif /* LOG */
762 
763 	i = unlink(f);
764 # ifdef LOG
765 	if (i < 0 && LogLevel > 97)
766 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
767 # endif /* LOG */
768 }
769 /*
770 **  XFCLOSE -- close a file, doing logging as appropriate.
771 **
772 **	Parameters:
773 **		fp -- file pointer for the file to close
774 **		a, b -- miscellaneous crud to print for debugging
775 **
776 **	Returns:
777 **		none.
778 **
779 **	Side Effects:
780 **		fp is closed.
781 */
782 
783 xfclose(fp, a, b)
784 	FILE *fp;
785 	char *a, *b;
786 {
787 	if (tTd(53, 99))
788 		printf("xfclose(%x) %s %s\n", fp, a, b);
789 #ifdef XDEBUG
790 	if (fileno(fp) == 1)
791 		syserr("xfclose(%s %s): fd = 1", a, b);
792 #endif
793 	if (fclose(fp) < 0 && tTd(53, 99))
794 		printf("xfclose FAILURE: %s\n", errstring(errno));
795 }
796 /*
797 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
798 **
799 **	Parameters:
800 **		buf -- place to put the input line.
801 **		siz -- size of buf.
802 **		fp -- file to read from.
803 **		timeout -- the timeout before error occurs.
804 **		during -- what we are trying to read (for error messages).
805 **
806 **	Returns:
807 **		NULL on error (including timeout).  This will also leave
808 **			buf containing a null string.
809 **		buf otherwise.
810 **
811 **	Side Effects:
812 **		none.
813 */
814 
815 static jmp_buf	CtxReadTimeout;
816 static int	readtimeout();
817 static EVENT	*GlobalTimeout = NULL;
818 static bool	EnableTimeout = FALSE;
819 static int	ReadProgress;
820 
821 char *
822 sfgets(buf, siz, fp, timeout, during)
823 	char *buf;
824 	int siz;
825 	FILE *fp;
826 	time_t timeout;
827 	char *during;
828 {
829 	register EVENT *ev = NULL;
830 	register char *p;
831 
832 	if (fp == NULL)
833 	{
834 		buf[0] = '\0';
835 		return NULL;
836 	}
837 
838 	/* set the timeout */
839 	if (timeout != 0)
840 	{
841 		if (setjmp(CtxReadTimeout) != 0)
842 		{
843 # ifdef LOG
844 			syslog(LOG_NOTICE,
845 			    "timeout waiting for input from %s during %s\n",
846 			    CurHostName? CurHostName: "local", during);
847 # endif
848 			errno = 0;
849 			usrerr("451 timeout waiting for input during %s",
850 				during);
851 			buf[0] = '\0';
852 #ifdef XDEBUG
853 			checkfd012(during);
854 #endif
855 			return (NULL);
856 		}
857 		if (GlobalTimeout == NULL)
858 			ev = setevent(timeout, readtimeout, 0);
859 		else
860 			EnableTimeout = TRUE;
861 	}
862 
863 	/* try to read */
864 	p = NULL;
865 	while (!feof(fp) && !ferror(fp))
866 	{
867 		errno = 0;
868 		p = fgets(buf, siz, fp);
869 		if (p != NULL || errno != EINTR)
870 			break;
871 		clearerr(fp);
872 	}
873 
874 	/* clear the event if it has not sprung */
875 	if (GlobalTimeout == NULL)
876 		clrevent(ev);
877 	else
878 		EnableTimeout = FALSE;
879 
880 	/* clean up the books and exit */
881 	LineNumber++;
882 	if (p == NULL)
883 	{
884 		buf[0] = '\0';
885 		if (TrafficLogFile != NULL)
886 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
887 		return (NULL);
888 	}
889 	if (TrafficLogFile != NULL)
890 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
891 	if (SevenBit)
892 		for (p = buf; *p != '\0'; p++)
893 			*p &= ~0200;
894 	return (buf);
895 }
896 
897 void
898 sfgetset(timeout)
899 	time_t timeout;
900 {
901 	/* cancel pending timer */
902 	if (GlobalTimeout != NULL)
903 	{
904 		clrevent(GlobalTimeout);
905 		GlobalTimeout = NULL;
906 	}
907 
908 	/* schedule fresh one if so requested */
909 	if (timeout != 0)
910 	{
911 		ReadProgress = LineNumber;
912 		GlobalTimeout = setevent(timeout, readtimeout, timeout);
913 	}
914 }
915 
916 static
917 readtimeout(timeout)
918 	time_t timeout;
919 {
920 	/* terminate if ordinary timeout */
921 	if (GlobalTimeout == NULL)
922 		longjmp(CtxReadTimeout, 1);
923 
924 	/* terminate if no progress was made -- reset state */
925 	if (EnableTimeout && (LineNumber <= ReadProgress))
926 	{
927 		EnableTimeout = FALSE;
928 		GlobalTimeout = NULL;
929 		longjmp(CtxReadTimeout, 2);
930 	}
931 
932 	/* schedule a new timeout */
933 	GlobalTimeout = NULL;
934 	sfgetset(timeout);
935 }
936 /*
937 **  FGETFOLDED -- like fgets, but know about folded lines.
938 **
939 **	Parameters:
940 **		buf -- place to put result.
941 **		n -- bytes available.
942 **		f -- file to read from.
943 **
944 **	Returns:
945 **		input line(s) on success, NULL on error or EOF.
946 **		This will normally be buf -- unless the line is too
947 **			long, when it will be xalloc()ed.
948 **
949 **	Side Effects:
950 **		buf gets lines from f, with continuation lines (lines
951 **		with leading white space) appended.  CRLF's are mapped
952 **		into single newlines.  Any trailing NL is stripped.
953 */
954 
955 char *
956 fgetfolded(buf, n, f)
957 	char *buf;
958 	register int n;
959 	FILE *f;
960 {
961 	register char *p = buf;
962 	char *bp = buf;
963 	register int i;
964 
965 	n--;
966 	while ((i = getc(f)) != EOF)
967 	{
968 		if (i == '\r')
969 		{
970 			i = getc(f);
971 			if (i != '\n')
972 			{
973 				if (i != EOF)
974 					(void) ungetc(i, f);
975 				i = '\r';
976 			}
977 		}
978 		if (--n <= 0)
979 		{
980 			/* allocate new space */
981 			char *nbp;
982 			int nn;
983 
984 			nn = (p - bp);
985 			if (nn < MEMCHUNKSIZE)
986 				nn *= 2;
987 			else
988 				nn += MEMCHUNKSIZE;
989 			nbp = xalloc(nn);
990 			bcopy(bp, nbp, p - bp);
991 			p = &nbp[p - bp];
992 			if (bp != buf)
993 				free(bp);
994 			bp = nbp;
995 			n = nn - (p - bp);
996 		}
997 		*p++ = i;
998 		if (i == '\n')
999 		{
1000 			LineNumber++;
1001 			i = getc(f);
1002 			if (i != EOF)
1003 				(void) ungetc(i, f);
1004 			if (i != ' ' && i != '\t')
1005 				break;
1006 		}
1007 	}
1008 	if (p == bp)
1009 		return (NULL);
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 atooct(s)
1073 	register char *s;
1074 {
1075 	register int i = 0;
1076 
1077 	while (*s >= '0' && *s <= '7')
1078 		i = (i << 3) | (*s++ - '0');
1079 	return (i);
1080 }
1081 /*
1082 **  WAITFOR -- wait for a particular process id.
1083 **
1084 **	Parameters:
1085 **		pid -- process id to wait for.
1086 **
1087 **	Returns:
1088 **		status of pid.
1089 **		-1 if pid never shows up.
1090 **
1091 **	Side Effects:
1092 **		none.
1093 */
1094 
1095 int
1096 waitfor(pid)
1097 	int pid;
1098 {
1099 #ifdef WAITUNION
1100 	union wait st;
1101 #else
1102 	auto int st;
1103 #endif
1104 	int i;
1105 
1106 	do
1107 	{
1108 		errno = 0;
1109 		i = wait(&st);
1110 	} while ((i >= 0 || errno == EINTR) && i != pid);
1111 	if (i < 0)
1112 		return -1;
1113 #ifdef WAITUNION
1114 	return st.w_status;
1115 #else
1116 	return st;
1117 #endif
1118 }
1119 /*
1120 **  BITINTERSECT -- tell if two bitmaps intersect
1121 **
1122 **	Parameters:
1123 **		a, b -- the bitmaps in question
1124 **
1125 **	Returns:
1126 **		TRUE if they have a non-null intersection
1127 **		FALSE otherwise
1128 **
1129 **	Side Effects:
1130 **		none.
1131 */
1132 
1133 bool
1134 bitintersect(a, b)
1135 	BITMAP a;
1136 	BITMAP b;
1137 {
1138 	int i;
1139 
1140 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1141 		if ((a[i] & b[i]) != 0)
1142 			return (TRUE);
1143 	return (FALSE);
1144 }
1145 /*
1146 **  BITZEROP -- tell if a bitmap is all zero
1147 **
1148 **	Parameters:
1149 **		map -- the bit map to check
1150 **
1151 **	Returns:
1152 **		TRUE if map is all zero.
1153 **		FALSE if there are any bits set in map.
1154 **
1155 **	Side Effects:
1156 **		none.
1157 */
1158 
1159 bool
1160 bitzerop(map)
1161 	BITMAP map;
1162 {
1163 	int i;
1164 
1165 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1166 		if (map[i] != 0)
1167 			return (FALSE);
1168 	return (TRUE);
1169 }
1170 /*
1171 **  STRCONTAINEDIN -- tell if one string is contained in another
1172 **
1173 **	Parameters:
1174 **		a -- possible substring.
1175 **		b -- possible superstring.
1176 **
1177 **	Returns:
1178 **		TRUE if a is contained in b.
1179 **		FALSE otherwise.
1180 */
1181 
1182 bool
1183 strcontainedin(a, b)
1184 	register char *a;
1185 	register char *b;
1186 {
1187 	int la;
1188 	int lb;
1189 	int c;
1190 
1191 	la = strlen(a);
1192 	lb = strlen(b);
1193 	c = *a;
1194 	if (isascii(c) && isupper(c))
1195 		c = tolower(c);
1196 	for (; lb-- >= la; b++)
1197 	{
1198 		if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c)
1199 			continue;
1200 		if (strncasecmp(a, b, la) == 0)
1201 			return TRUE;
1202 	}
1203 	return FALSE;
1204 }
1205 /*
1206 **  CHECKFD012 -- check low numbered file descriptors
1207 **
1208 **	File descriptors 0, 1, and 2 should be open at all times.
1209 **	This routine verifies that, and fixes it if not true.
1210 **
1211 **	Parameters:
1212 **		where -- a tag printed if the assertion failed
1213 **
1214 **	Returns:
1215 **		none
1216 */
1217 
1218 checkfd012(where)
1219 	char *where;
1220 {
1221 #ifdef XDEBUG
1222 	register int i;
1223 	struct stat stbuf;
1224 
1225 	for (i = 0; i < 3; i++)
1226 	{
1227 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1228 		{
1229 			/* oops.... */
1230 			int fd;
1231 
1232 			syserr("%s: fd %d not open", where, i);
1233 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1234 			if (fd != i)
1235 			{
1236 				(void) dup2(fd, i);
1237 				(void) close(fd);
1238 			}
1239 		}
1240 	}
1241 #endif /* XDEBUG */
1242 }
1243 /*
1244 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1245 **
1246 **	Parameters:
1247 **		logit -- if set, send output to syslog; otherwise
1248 **			print for debugging.
1249 **
1250 **	Returns:
1251 **		none.
1252 */
1253 
1254 #include <netdb.h>
1255 #include <arpa/inet.h>
1256 
1257 printopenfds(logit)
1258 	bool logit;
1259 {
1260 	register int fd;
1261 	extern int DtableSize;
1262 
1263 	for (fd = 0; fd < DtableSize; fd++)
1264 		dumpfd(fd, FALSE, logit);
1265 }
1266 /*
1267 **  DUMPFD -- dump a file descriptor
1268 **
1269 **	Parameters:
1270 **		fd -- the file descriptor to dump.
1271 **		printclosed -- if set, print a notification even if
1272 **			it is closed; otherwise print nothing.
1273 **		logit -- if set, send output to syslog instead of stdout.
1274 */
1275 
1276 dumpfd(fd, printclosed, logit)
1277 	int fd;
1278 	bool printclosed;
1279 	bool logit;
1280 {
1281 	register struct hostent *hp;
1282 	register char *p;
1283 	char *fmtstr;
1284 	struct sockaddr_in sin;
1285 	auto int slen;
1286 	struct stat st;
1287 	char buf[200];
1288 
1289 	p = buf;
1290 	sprintf(p, "%3d: ", fd);
1291 	p += strlen(p);
1292 
1293 	if (fstat(fd, &st) < 0)
1294 	{
1295 		if (printclosed || errno != EBADF)
1296 		{
1297 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1298 			goto printit;
1299 		}
1300 		return;
1301 	}
1302 
1303 	slen = fcntl(fd, F_GETFL, NULL);
1304 	if (slen != -1)
1305 	{
1306 		sprintf(p, "fl=0x%x, ", slen);
1307 		p += strlen(p);
1308 	}
1309 
1310 	sprintf(p, "mode=%o: ", st.st_mode);
1311 	p += strlen(p);
1312 	switch (st.st_mode & S_IFMT)
1313 	{
1314 #ifdef S_IFSOCK
1315 	  case S_IFSOCK:
1316 		sprintf(p, "SOCK ");
1317 		p += strlen(p);
1318 		slen = sizeof sin;
1319 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
1320 			sprintf(p, "(badsock)");
1321 		else
1322 		{
1323 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
1324 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1325 						   : hp->h_name, ntohs(sin.sin_port));
1326 		}
1327 		p += strlen(p);
1328 		sprintf(p, "->");
1329 		p += strlen(p);
1330 		slen = sizeof sin;
1331 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
1332 			sprintf(p, "(badsock)");
1333 		else
1334 		{
1335 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
1336 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1337 						   : hp->h_name, ntohs(sin.sin_port));
1338 		}
1339 		break;
1340 #endif
1341 
1342 	  case S_IFCHR:
1343 		sprintf(p, "CHR: ");
1344 		p += strlen(p);
1345 		goto defprint;
1346 
1347 	  case S_IFBLK:
1348 		sprintf(p, "BLK: ");
1349 		p += strlen(p);
1350 		goto defprint;
1351 
1352 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK)
1353 	  case S_IFIFO:
1354 		sprintf(p, "FIFO: ");
1355 		p += strlen(p);
1356 		goto defprint;
1357 #endif
1358 
1359 #ifdef S_IFDIR
1360 	  case S_IFDIR:
1361 		sprintf(p, "DIR: ");
1362 		p += strlen(p);
1363 		goto defprint;
1364 #endif
1365 
1366 #ifdef S_IFLNK
1367 	  case S_IFLNK:
1368 		sprintf(p, "LNK: ");
1369 		p += strlen(p);
1370 		goto defprint;
1371 #endif
1372 
1373 	  default:
1374 defprint:
1375 		if (sizeof st.st_size > sizeof (long))
1376 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd";
1377 		else
1378 			fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld";
1379 		sprintf(p, fmtstr,
1380 			major(st.st_dev), minor(st.st_dev), st.st_ino,
1381 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1382 		break;
1383 	}
1384 
1385 printit:
1386 #ifdef LOG
1387 	if (logit)
1388 		syslog(LOG_DEBUG, "%s", buf);
1389 	else
1390 #endif
1391 		printf("%s\n", buf);
1392 }
1393 /*
1394 **  SHORTENSTRING -- return short version of a string
1395 **
1396 **	If the string is already short, just return it.  If it is too
1397 **	long, return the head and tail of the string.
1398 **
1399 **	Parameters:
1400 **		s -- the string to shorten.
1401 **		m -- the max length of the string.
1402 **
1403 **	Returns:
1404 **		Either s or a short version of s.
1405 */
1406 
1407 #ifndef MAXSHORTSTR
1408 # define MAXSHORTSTR	203
1409 #endif
1410 
1411 char *
1412 shortenstring(s, m)
1413 	register char *s;
1414 	int m;
1415 {
1416 	int l;
1417 	static char buf[MAXSHORTSTR + 1];
1418 
1419 	l = strlen(s);
1420 	if (l < m)
1421 		return s;
1422 	if (m > MAXSHORTSTR)
1423 		m = MAXSHORTSTR;
1424 	else if (m < 10)
1425 	{
1426 		if (m < 5)
1427 		{
1428 			strncpy(buf, s, m);
1429 			buf[m] = '\0';
1430 			return buf;
1431 		}
1432 		strncpy(buf, s, m - 3);
1433 		strcpy(buf + m - 3, "...");
1434 		return buf;
1435 	}
1436 	m = (m - 3) / 2;
1437 	strncpy(buf, s, m);
1438 	strcpy(buf + m, "...");
1439 	strcpy(buf + m + 3, s + l - m);
1440 	return buf;
1441 }
1442