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