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