xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 64961)
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.17.1.1 (Berkeley) 11/27/93";
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 **		TRUE -- if the resulting message should be a MIME format.
333 **		FALSE -- if MIME is not necessary.
334 **
335 **	Side Effects:
336 **		none.
337 */
338 
339 /* values for should_quote */
340 #define NO_QUOTE	0
341 #define SHOULD_QUOTE	1
342 #define SHOULD_MIME	2
343 
344 int
345 buildfname(gecos, login, buf)
346 	register unsigned char *gecos;
347 	const unsigned char *login;
348 	unsigned char *buf;
349 {
350 	register unsigned char *bp = buf;
351 	unsigned char *p;
352 	int should_quote = NO_QUOTE;
353 
354 	/* make sure specials, SPACE and CTLs are quoted within " " */
355 	for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
356 	{
357 		if (*p >= 0200)
358 		{
359 			should_quote = SHOULD_MIME;
360 			break;
361 		}
362 		switch (*p)
363 		{
364 		  case '(':
365 		  case ')':
366 		  case '<':
367 		  case '>':
368 		  case '@':
369 		  case ':':
370 		  case '\\':
371 		  case '"':
372 		  case '.':
373 		  case '[':
374 		  case ']':
375 			should_quote = SHOULD_QUOTE;
376 			break;
377 		}
378 	}
379 	if (should_quote == SHOULD_MIME)
380 	{
381 		strcpy (bp, "=?iso-8859-1?Q?");
382 		bp += 15;
383 		for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
384 		{
385 			if (*p == ' ')
386 				*bp++ = '_';
387                         else if (*p == '&')
388                         {
389 				(void) strcpy(bp, login);
390 				*bp = toupper(*bp);
391 				bp += strlen (bp);
392 			}
393 			else if (*p < 040 || *p >= 200 ||
394 				 strchr("_?()<>@:\\\".[]", *p) != NULL)
395 			{
396 				*bp++ = '=';
397 				*bp++ = "0123456789ABCDEF"[(*p >> 4) & 0xf];
398 				*bp++ = "0123456789ABCDEF"[*p & 0xf];
399 			}
400 			else
401 				*bp++ = *p;
402 		}
403 		strcpy (bp, "?= ");
404 		bp += 3;
405 	}
406 	else
407 	{
408 		if (should_quote)
409 			*bp++ = '"';
410 		for (p = gecos; *p && *p != ',' && *p != ';' && *p != '%'; p++)
411 		{
412 			if (*p == '&')
413 			{
414 				(void) strcpy(bp, login);
415 				*bp = toupper(*bp);
416 				while (*bp != '\0')
417 					bp++;
418 			}
419 			else
420 			{
421 				if (*p == '"')
422 					*bp++ = '\\';
423 				*bp++ = *p;
424 			}
425 		}
426 		if (bp[-1] == '\\')
427 			*bp++ = '\\';
428 		if (should_quote)
429 			*bp++ = '"';
430 	}
431 
432 	*bp = '\0';
433 	return should_quote == SHOULD_MIME;
434 }
435 /*
436 **  SAFEFILE -- return true if a file exists and is safe for a user.
437 **
438 **	Parameters:
439 **		fn -- filename to check.
440 **		uid -- user id to compare against.
441 **		gid -- group id to compare against.
442 **		uname -- user name to compare against (used for group
443 **			sets).
444 **		flags -- modifiers:
445 **			SF_MUSTOWN -- "uid" must own this file.
446 **			SF_NOSLINK -- file cannot be a symbolic link.
447 **		mode -- mode bits that must match.
448 **
449 **	Returns:
450 **		0 if fn exists, is owned by uid, and matches mode.
451 **		An errno otherwise.  The actual errno is cleared.
452 **
453 **	Side Effects:
454 **		none.
455 */
456 
457 #include <grp.h>
458 
459 #ifndef S_IXOTH
460 # define S_IXOTH	(S_IEXEC >> 6)
461 #endif
462 
463 #ifndef S_IXGRP
464 # define S_IXGRP	(S_IEXEC >> 3)
465 #endif
466 
467 #ifndef S_IXUSR
468 # define S_IXUSR	(S_IEXEC)
469 #endif
470 
471 int
472 safefile(fn, uid, gid, uname, flags, mode)
473 	char *fn;
474 	uid_t uid;
475 	gid_t gid;
476 	char *uname;
477 	int flags;
478 	int mode;
479 {
480 	register char *p;
481 	register struct group *gr = NULL;
482 	struct stat stbuf;
483 
484 	if (tTd(54, 4))
485 		printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
486 			fn, uid, gid, flags, mode);
487 	errno = 0;
488 
489 	for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
490 	{
491 		*p = '\0';
492 		if (stat(fn, &stbuf) < 0)
493 			break;
494 		if (stbuf.st_uid == uid && bitset(S_IXUSR, stbuf.st_mode))
495 			continue;
496 		if (stbuf.st_gid == gid && bitset(S_IXGRP, stbuf.st_mode))
497 			continue;
498 #ifndef NO_GROUP_SET
499 		if (uname != NULL &&
500 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
501 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
502 		{
503 			register char **gp;
504 
505 			for (gp = gr->gr_mem; *gp != NULL; gp++)
506 				if (strcmp(*gp, uname) == 0)
507 					break;
508 			if (*gp != NULL && bitset(S_IXGRP, stbuf.st_mode))
509 				continue;
510 		}
511 #endif
512 		if (!bitset(S_IXOTH, stbuf.st_mode))
513 			break;
514 	}
515 	if (p != NULL)
516 	{
517 		int ret = errno;
518 
519 		if (ret == 0)
520 			ret = EACCES;
521 		if (tTd(54, 4))
522 			printf("\t[dir %s] %s\n", fn, errstring(ret));
523 		*p = '/';
524 		return ret;
525 	}
526 
527 #ifdef HASLSTAT
528 	if ((bitset(SF_NOSLINK, flags) ? lstat(fn, &stbuf)
529 				       : stat(fn, &stbuf)) < 0)
530 #else
531 	if (stat(fn, &stbuf) < 0)
532 #endif
533 	{
534 		int ret = errno;
535 
536 		if (tTd(54, 4))
537 			printf("\t%s\n", errstring(ret));
538 
539 		errno = 0;
540 		return ret;
541 	}
542 
543 #ifdef S_ISLNK
544 	if (bitset(SF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
545 	{
546 		if (tTd(54, 4))
547 			printf("\t[mode %o]\tEPERM\n");
548 		return EPERM;
549 	}
550 #endif
551 
552 	if (uid == 0)
553 		mode >>= 6;
554 	else if (stbuf.st_uid != uid)
555 	{
556 		mode >>= 3;
557 		if (stbuf.st_gid == gid)
558 			;
559 #ifndef NO_GROUP_SET
560 		else if (uname != NULL &&
561 			 ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
562 			  (gr = getgrgid(stbuf.st_gid)) != NULL))
563 		{
564 			register char **gp;
565 
566 			for (gp = gr->gr_mem; *gp != NULL; gp++)
567 				if (strcmp(*gp, uname) == 0)
568 					break;
569 			if (*gp == NULL)
570 				mode >>= 3;
571 		}
572 #endif
573 		else
574 			mode >>= 3;
575 	}
576 	if (tTd(54, 4))
577 		printf("\t[uid %d, stat %o, mode %o] ",
578 			stbuf.st_uid, stbuf.st_mode, mode);
579 	if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
580 	     !bitset(SF_MUSTOWN, flags)) &&
581 	    (stbuf.st_mode & mode) == mode)
582 	{
583 		if (tTd(54, 4))
584 			printf("\tOK\n");
585 		return 0;
586 	}
587 	if (tTd(54, 4))
588 		printf("\tEACCES\n");
589 	return EACCES;
590 }
591 /*
592 **  FIXCRLF -- fix <CR><LF> in line.
593 **
594 **	Looks for the <CR><LF> combination and turns it into the
595 **	UNIX canonical <NL> character.  It only takes one line,
596 **	i.e., it is assumed that the first <NL> found is the end
597 **	of the line.
598 **
599 **	Parameters:
600 **		line -- the line to fix.
601 **		stripnl -- if true, strip the newline also.
602 **
603 **	Returns:
604 **		none.
605 **
606 **	Side Effects:
607 **		line is changed in place.
608 */
609 
610 fixcrlf(line, stripnl)
611 	char *line;
612 	bool stripnl;
613 {
614 	register char *p;
615 
616 	p = strchr(line, '\n');
617 	if (p == NULL)
618 		return;
619 	if (p > line && p[-1] == '\r')
620 		p--;
621 	if (!stripnl)
622 		*p++ = '\n';
623 	*p = '\0';
624 }
625 /*
626 **  DFOPEN -- determined file open
627 **
628 **	This routine has the semantics of fopen, except that it will
629 **	keep trying a few times to make this happen.  The idea is that
630 **	on very loaded systems, we may run out of resources (inodes,
631 **	whatever), so this tries to get around it.
632 */
633 
634 #ifndef O_ACCMODE
635 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
636 #endif
637 
638 struct omodes
639 {
640 	int	mask;
641 	int	mode;
642 	char	*farg;
643 } OpenModes[] =
644 {
645 	O_ACCMODE,		O_RDONLY,		"r",
646 	O_ACCMODE|O_APPEND,	O_WRONLY,		"w",
647 	O_ACCMODE|O_APPEND,	O_WRONLY|O_APPEND,	"a",
648 	O_TRUNC,		0,			"w+",
649 	O_APPEND,		O_APPEND,		"a+",
650 	0,			0,			"r+",
651 };
652 
653 FILE *
654 dfopen(filename, omode, cmode)
655 	char *filename;
656 	int omode;
657 	int cmode;
658 {
659 	register int tries;
660 	int fd;
661 	register struct omodes *om;
662 	struct stat st;
663 
664 	for (om = OpenModes; om->mask != 0; om++)
665 		if ((omode & om->mask) == om->mode)
666 			break;
667 
668 	for (tries = 0; tries < 10; tries++)
669 	{
670 		sleep((unsigned) (10 * tries));
671 		errno = 0;
672 		fd = open(filename, omode, cmode);
673 		if (fd >= 0)
674 			break;
675 		if (errno != ENFILE && errno != EINTR)
676 			break;
677 	}
678 	if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode))
679 	{
680 		int locktype;
681 
682 		/* lock the file to avoid accidental conflicts */
683 		if ((omode & O_ACCMODE) != O_RDONLY)
684 			locktype = LOCK_EX;
685 		else
686 			locktype = LOCK_SH;
687 		(void) lockfile(fd, filename, NULL, locktype);
688 		errno = 0;
689 	}
690 	if (fd < 0)
691 		return NULL;
692 	else
693 		return fdopen(fd, om->farg);
694 }
695 /*
696 **  PUTLINE -- put a line like fputs obeying SMTP conventions
697 **
698 **	This routine always guarantees outputing a newline (or CRLF,
699 **	as appropriate) at the end of the string.
700 **
701 **	Parameters:
702 **		l -- line to put.
703 **		fp -- file to put it onto.
704 **		m -- the mailer used to control output.
705 **
706 **	Returns:
707 **		none
708 **
709 **	Side Effects:
710 **		output of l to fp.
711 */
712 
713 putline(l, fp, m)
714 	register char *l;
715 	FILE *fp;
716 	MAILER *m;
717 {
718 	register char *p;
719 	register char svchar;
720 
721 	/* strip out 0200 bits -- these can look like TELNET protocol */
722 	if (bitnset(M_7BITS, m->m_flags))
723 	{
724 		for (p = l; (svchar = *p) != '\0'; ++p)
725 			if (bitset(0200, svchar))
726 				*p = svchar &~ 0200;
727 	}
728 
729 	do
730 	{
731 		/* find the end of the line */
732 		p = strchr(l, '\n');
733 		if (p == NULL)
734 			p = &l[strlen(l)];
735 
736 		if (TrafficLogFile != NULL)
737 			fprintf(TrafficLogFile, "%05d >>> ", getpid());
738 
739 		/* check for line overflow */
740 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
741 		{
742 			register char *q = &l[m->m_linelimit - 1];
743 
744 			svchar = *q;
745 			*q = '\0';
746 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
747 			{
748 				(void) putc('.', fp);
749 				if (TrafficLogFile != NULL)
750 					(void) putc('.', TrafficLogFile);
751 			}
752 			fputs(l, fp);
753 			(void) putc('!', fp);
754 			fputs(m->m_eol, fp);
755 			if (TrafficLogFile != NULL)
756 				fprintf(TrafficLogFile, "%s!\n%05d >>> ",
757 					l, getpid());
758 			*q = svchar;
759 			l = q;
760 		}
761 
762 		/* output last part */
763 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
764 		{
765 			(void) putc('.', fp);
766 			if (TrafficLogFile != NULL)
767 				(void) putc('.', TrafficLogFile);
768 		}
769 		if (TrafficLogFile != NULL)
770 			fprintf(TrafficLogFile, "%.*s\n", p - l, l);
771 		for ( ; l < p; ++l)
772 			(void) putc(*l, fp);
773 		fputs(m->m_eol, fp);
774 		if (*l == '\n')
775 			++l;
776 	} while (l[0] != '\0');
777 }
778 /*
779 **  XUNLINK -- unlink a file, doing logging as appropriate.
780 **
781 **	Parameters:
782 **		f -- name of file to unlink.
783 **
784 **	Returns:
785 **		none.
786 **
787 **	Side Effects:
788 **		f is unlinked.
789 */
790 
791 xunlink(f)
792 	char *f;
793 {
794 	register int i;
795 
796 # ifdef LOG
797 	if (LogLevel > 98)
798 		syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f);
799 # endif /* LOG */
800 
801 	i = unlink(f);
802 # ifdef LOG
803 	if (i < 0 && LogLevel > 97)
804 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
805 # endif /* LOG */
806 }
807 /*
808 **  XFCLOSE -- close a file, doing logging as appropriate.
809 **
810 **	Parameters:
811 **		fp -- file pointer for the file to close
812 **		a, b -- miscellaneous crud to print for debugging
813 **
814 **	Returns:
815 **		none.
816 **
817 **	Side Effects:
818 **		fp is closed.
819 */
820 
821 xfclose(fp, a, b)
822 	FILE *fp;
823 	char *a, *b;
824 {
825 	if (tTd(53, 99))
826 		printf("xfclose(%x) %s %s\n", fp, a, b);
827 #ifdef XDEBUG
828 	if (fileno(fp) == 1)
829 		syserr("xfclose(%s %s): fd = 1", a, b);
830 #endif
831 	if (fclose(fp) < 0 && tTd(53, 99))
832 		printf("xfclose FAILURE: %s\n", errstring(errno));
833 }
834 /*
835 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
836 **
837 **	Parameters:
838 **		buf -- place to put the input line.
839 **		siz -- size of buf.
840 **		fp -- file to read from.
841 **		timeout -- the timeout before error occurs.
842 **		during -- what we are trying to read (for error messages).
843 **
844 **	Returns:
845 **		NULL on error (including timeout).  This will also leave
846 **			buf containing a null string.
847 **		buf otherwise.
848 **
849 **	Side Effects:
850 **		none.
851 */
852 
853 static jmp_buf	CtxReadTimeout;
854 static int	readtimeout();
855 
856 char *
857 sfgets(buf, siz, fp, timeout, during)
858 	char *buf;
859 	int siz;
860 	FILE *fp;
861 	time_t timeout;
862 	char *during;
863 {
864 	register EVENT *ev = NULL;
865 	register char *p;
866 
867 	/* set the timeout */
868 	if (timeout != 0)
869 	{
870 		if (setjmp(CtxReadTimeout) != 0)
871 		{
872 # ifdef LOG
873 			syslog(LOG_NOTICE,
874 			    "timeout waiting for input from %s during %s\n",
875 			    CurHostName? CurHostName: "local", during);
876 # endif
877 			errno = 0;
878 			usrerr("451 timeout waiting for input during %s",
879 				during);
880 			buf[0] = '\0';
881 #ifdef XDEBUG
882 			checkfd012(during);
883 #endif
884 			return (NULL);
885 		}
886 		ev = setevent(timeout, readtimeout, 0);
887 	}
888 
889 	/* try to read */
890 	p = NULL;
891 	while (p == NULL && !feof(fp) && !ferror(fp))
892 	{
893 		errno = 0;
894 		p = fgets(buf, siz, fp);
895 		if (errno == EINTR)
896 			clearerr(fp);
897 	}
898 
899 	/* clear the event if it has not sprung */
900 	clrevent(ev);
901 
902 	/* clean up the books and exit */
903 	LineNumber++;
904 	if (p == NULL)
905 	{
906 		buf[0] = '\0';
907 		if (TrafficLogFile != NULL)
908 			fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid());
909 		return (NULL);
910 	}
911 	if (TrafficLogFile != NULL)
912 		fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf);
913 	if (SevenBit)
914 		for (p = buf; *p != '\0'; p++)
915 			*p &= ~0200;
916 	return (buf);
917 }
918 
919 static
920 readtimeout()
921 {
922 	longjmp(CtxReadTimeout, 1);
923 }
924 /*
925 **  FGETFOLDED -- like fgets, but know about folded lines.
926 **
927 **	Parameters:
928 **		buf -- place to put result.
929 **		n -- bytes available.
930 **		f -- file to read from.
931 **
932 **	Returns:
933 **		input line(s) on success, NULL on error or EOF.
934 **		This will normally be buf -- unless the line is too
935 **			long, when it will be xalloc()ed.
936 **
937 **	Side Effects:
938 **		buf gets lines from f, with continuation lines (lines
939 **		with leading white space) appended.  CRLF's are mapped
940 **		into single newlines.  Any trailing NL is stripped.
941 */
942 
943 char *
944 fgetfolded(buf, n, f)
945 	char *buf;
946 	register int n;
947 	FILE *f;
948 {
949 	register char *p = buf;
950 	char *bp = buf;
951 	register int i;
952 
953 	n--;
954 	while ((i = getc(f)) != EOF)
955 	{
956 		if (i == '\r')
957 		{
958 			i = getc(f);
959 			if (i != '\n')
960 			{
961 				if (i != EOF)
962 					(void) ungetc(i, f);
963 				i = '\r';
964 			}
965 		}
966 		if (--n <= 0)
967 		{
968 			/* allocate new space */
969 			char *nbp;
970 			int nn;
971 
972 			nn = (p - bp);
973 			if (nn < MEMCHUNKSIZE)
974 				nn *= 2;
975 			else
976 				nn += MEMCHUNKSIZE;
977 			nbp = xalloc(nn);
978 			bcopy(bp, nbp, p - bp);
979 			p = &nbp[p - bp];
980 			if (bp != buf)
981 				free(bp);
982 			bp = nbp;
983 			n = nn - (p - bp);
984 		}
985 		*p++ = i;
986 		if (i == '\n')
987 		{
988 			LineNumber++;
989 			i = getc(f);
990 			if (i != EOF)
991 				(void) ungetc(i, f);
992 			if (i != ' ' && i != '\t')
993 				break;
994 		}
995 	}
996 	if (p == bp)
997 		return (NULL);
998 	*--p = '\0';
999 	return (bp);
1000 }
1001 /*
1002 **  CURTIME -- return current time.
1003 **
1004 **	Parameters:
1005 **		none.
1006 **
1007 **	Returns:
1008 **		the current time.
1009 **
1010 **	Side Effects:
1011 **		none.
1012 */
1013 
1014 time_t
1015 curtime()
1016 {
1017 	auto time_t t;
1018 
1019 	(void) time(&t);
1020 	return (t);
1021 }
1022 /*
1023 **  ATOBOOL -- convert a string representation to boolean.
1024 **
1025 **	Defaults to "TRUE"
1026 **
1027 **	Parameters:
1028 **		s -- string to convert.  Takes "tTyY" as true,
1029 **			others as false.
1030 **
1031 **	Returns:
1032 **		A boolean representation of the string.
1033 **
1034 **	Side Effects:
1035 **		none.
1036 */
1037 
1038 bool
1039 atobool(s)
1040 	register char *s;
1041 {
1042 	if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL)
1043 		return (TRUE);
1044 	return (FALSE);
1045 }
1046 /*
1047 **  ATOOCT -- convert a string representation to octal.
1048 **
1049 **	Parameters:
1050 **		s -- string to convert.
1051 **
1052 **	Returns:
1053 **		An integer representing the string interpreted as an
1054 **		octal number.
1055 **
1056 **	Side Effects:
1057 **		none.
1058 */
1059 
1060 atooct(s)
1061 	register char *s;
1062 {
1063 	register int i = 0;
1064 
1065 	while (*s >= '0' && *s <= '7')
1066 		i = (i << 3) | (*s++ - '0');
1067 	return (i);
1068 }
1069 /*
1070 **  WAITFOR -- wait for a particular process id.
1071 **
1072 **	Parameters:
1073 **		pid -- process id to wait for.
1074 **
1075 **	Returns:
1076 **		status of pid.
1077 **		-1 if pid never shows up.
1078 **
1079 **	Side Effects:
1080 **		none.
1081 */
1082 
1083 int
1084 waitfor(pid)
1085 	int pid;
1086 {
1087 #ifdef WAITUNION
1088 	union wait st;
1089 #else
1090 	auto int st;
1091 #endif
1092 	int i;
1093 
1094 	do
1095 	{
1096 		errno = 0;
1097 		i = wait(&st);
1098 	} while ((i >= 0 || errno == EINTR) && i != pid);
1099 	if (i < 0)
1100 		return -1;
1101 #ifdef WAITUNION
1102 	return st.w_status;
1103 #else
1104 	return st;
1105 #endif
1106 }
1107 /*
1108 **  BITINTERSECT -- tell if two bitmaps intersect
1109 **
1110 **	Parameters:
1111 **		a, b -- the bitmaps in question
1112 **
1113 **	Returns:
1114 **		TRUE if they have a non-null intersection
1115 **		FALSE otherwise
1116 **
1117 **	Side Effects:
1118 **		none.
1119 */
1120 
1121 bool
1122 bitintersect(a, b)
1123 	BITMAP a;
1124 	BITMAP b;
1125 {
1126 	int i;
1127 
1128 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1129 		if ((a[i] & b[i]) != 0)
1130 			return (TRUE);
1131 	return (FALSE);
1132 }
1133 /*
1134 **  BITZEROP -- tell if a bitmap is all zero
1135 **
1136 **	Parameters:
1137 **		map -- the bit map to check
1138 **
1139 **	Returns:
1140 **		TRUE if map is all zero.
1141 **		FALSE if there are any bits set in map.
1142 **
1143 **	Side Effects:
1144 **		none.
1145 */
1146 
1147 bool
1148 bitzerop(map)
1149 	BITMAP map;
1150 {
1151 	int i;
1152 
1153 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
1154 		if (map[i] != 0)
1155 			return (FALSE);
1156 	return (TRUE);
1157 }
1158 /*
1159 **  STRCONTAINEDIN -- tell if one string is contained in another
1160 **
1161 **	Parameters:
1162 **		a -- possible substring.
1163 **		b -- possible superstring.
1164 **
1165 **	Returns:
1166 **		TRUE if a is contained in b.
1167 **		FALSE otherwise.
1168 */
1169 
1170 bool
1171 strcontainedin(a, b)
1172 	register char *a;
1173 	register char *b;
1174 {
1175 	int l;
1176 
1177 	l = strlen(a);
1178 	for (;;)
1179 	{
1180 		b = strchr(b, a[0]);
1181 		if (b == NULL)
1182 			return FALSE;
1183 		if (strncmp(a, b, l) == 0)
1184 			return TRUE;
1185 		b++;
1186 	}
1187 }
1188 /*
1189 **  CHECKFD012 -- check low numbered file descriptors
1190 **
1191 **	File descriptors 0, 1, and 2 should be open at all times.
1192 **	This routine verifies that, and fixes it if not true.
1193 **
1194 **	Parameters:
1195 **		where -- a tag printed if the assertion failed
1196 **
1197 **	Returns:
1198 **		none
1199 */
1200 
1201 checkfd012(where)
1202 	char *where;
1203 {
1204 #ifdef XDEBUG
1205 	register int i;
1206 	struct stat stbuf;
1207 
1208 	for (i = 0; i < 3; i++)
1209 	{
1210 		if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP)
1211 		{
1212 			/* oops.... */
1213 			int fd;
1214 
1215 			syserr("%s: fd %d not open", where, i);
1216 			fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666);
1217 			if (fd != i)
1218 			{
1219 				(void) dup2(fd, i);
1220 				(void) close(fd);
1221 			}
1222 		}
1223 	}
1224 #endif /* XDEBUG */
1225 }
1226 /*
1227 **  PRINTOPENFDS -- print the open file descriptors (for debugging)
1228 **
1229 **	Parameters:
1230 **		logit -- if set, send output to syslog; otherwise
1231 **			print for debugging.
1232 **
1233 **	Returns:
1234 **		none.
1235 */
1236 
1237 #include <netdb.h>
1238 #include <arpa/inet.h>
1239 
1240 printopenfds(logit)
1241 	bool logit;
1242 {
1243 	register int fd;
1244 	extern int DtableSize;
1245 
1246 	for (fd = 0; fd < DtableSize; fd++)
1247 		dumpfd(fd, FALSE, logit);
1248 }
1249 /*
1250 **  DUMPFD -- dump a file descriptor
1251 **
1252 **	Parameters:
1253 **		fd -- the file descriptor to dump.
1254 **		printclosed -- if set, print a notification even if
1255 **			it is closed; otherwise print nothing.
1256 **		logit -- if set, send output to syslog instead of stdout.
1257 */
1258 
1259 dumpfd(fd, printclosed, logit)
1260 	int fd;
1261 	bool printclosed;
1262 	bool logit;
1263 {
1264 	register struct hostent *hp;
1265 	register char *p;
1266 	struct sockaddr_in sin;
1267 	auto int slen;
1268 	struct stat st;
1269 	char buf[200];
1270 
1271 	p = buf;
1272 	sprintf(p, "%3d: ", fd);
1273 	p += strlen(p);
1274 
1275 	if (fstat(fd, &st) < 0)
1276 	{
1277 		if (printclosed || errno != EBADF)
1278 		{
1279 			sprintf(p, "CANNOT STAT (%s)", errstring(errno));
1280 			goto printit;
1281 		}
1282 		return;
1283 	}
1284 
1285 	slen = fcntl(fd, F_GETFL, NULL);
1286 	if (slen != -1)
1287 	{
1288 		sprintf(p, "fl=0x%x, ", slen);
1289 		p += strlen(p);
1290 	}
1291 
1292 	sprintf(p, "mode=%o: ", st.st_mode);
1293 	p += strlen(p);
1294 	switch (st.st_mode & S_IFMT)
1295 	{
1296 #ifdef S_IFSOCK
1297 	  case S_IFSOCK:
1298 		sprintf(p, "SOCK ");
1299 		p += strlen(p);
1300 		slen = sizeof sin;
1301 		if (getsockname(fd, (struct sockaddr *) &sin, &slen) < 0)
1302 			sprintf(p, "(badsock)");
1303 		else
1304 		{
1305 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
1306 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1307 						   : hp->h_name, ntohs(sin.sin_port));
1308 		}
1309 		p += strlen(p);
1310 		sprintf(p, "->");
1311 		p += strlen(p);
1312 		slen = sizeof sin;
1313 		if (getpeername(fd, (struct sockaddr *) &sin, &slen) < 0)
1314 			sprintf(p, "(badsock)");
1315 		else
1316 		{
1317 			hp = gethostbyaddr((char *) &sin.sin_addr, slen, AF_INET);
1318 			sprintf(p, "%s/%d", hp == NULL ? inet_ntoa(sin.sin_addr)
1319 						   : hp->h_name, ntohs(sin.sin_port));
1320 		}
1321 		break;
1322 #endif
1323 
1324 	  case S_IFCHR:
1325 		sprintf(p, "CHR: ");
1326 		p += strlen(p);
1327 		goto defprint;
1328 
1329 	  case S_IFBLK:
1330 		sprintf(p, "BLK: ");
1331 		p += strlen(p);
1332 		goto defprint;
1333 
1334 	  default:
1335 defprint:
1336 		sprintf(p, "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld",
1337 			major(st.st_dev), minor(st.st_dev), st.st_ino,
1338 			st.st_nlink, st.st_uid, st.st_gid, st.st_size);
1339 		break;
1340 	}
1341 
1342 printit:
1343 	if (logit)
1344 		syslog(LOG_INFO, "%s", buf);
1345 	else
1346 		printf("%s\n", buf);
1347 }
1348