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