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