xref: /original-bsd/usr.sbin/sendmail/src/util.c (revision a5a45b47)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)util.c	5.25 (Berkeley) 02/24/92";
11 #endif /* not lint */
12 
13 # include <stdio.h>
14 # include <sys/types.h>
15 # include <sys/stat.h>
16 # include <sysexits.h>
17 # include <errno.h>
18 # include "sendmail.h"
19 
20 /*
21 **  STRIPQUOTES -- Strip quotes & quote bits from a string.
22 **
23 **	Runs through a string and strips off unquoted quote
24 **	characters and quote bits.  This is done in place.
25 **
26 **	Parameters:
27 **		s -- the string to strip.
28 **		qf -- if set, remove actual `` " '' characters
29 **			as well as the quote bits.
30 **
31 **	Returns:
32 **		none.
33 **
34 **	Side Effects:
35 **		none.
36 **
37 **	Called By:
38 **		deliver
39 */
40 
41 stripquotes(s, qf)
42 	char *s;
43 	bool qf;
44 {
45 	register char *p;
46 	register char *q;
47 	register char c;
48 
49 	if (s == NULL)
50 		return;
51 
52 	for (p = q = s; (c = *p++) != '\0'; )
53 	{
54 		if (c != '"' || !qf)
55 			*q++ = c & 0177;
56 	}
57 	*q = '\0';
58 }
59 /*
60 **  QSTRLEN -- give me the string length assuming 0200 bits add a char
61 **
62 **	Parameters:
63 **		s -- the string to measure.
64 **
65 **	Reurns:
66 **		The length of s, including space for backslash escapes.
67 **
68 **	Side Effects:
69 **		none.
70 */
71 
72 qstrlen(s)
73 	register char *s;
74 {
75 	register int l = 0;
76 	register char c;
77 
78 	while ((c = *s++) != '\0')
79 	{
80 		if (bitset(0200, c))
81 			l++;
82 		l++;
83 	}
84 	return (l);
85 }
86 /*
87 **  CAPITALIZE -- return a copy of a string, properly capitalized.
88 **
89 **	Parameters:
90 **		s -- the string to capitalize.
91 **
92 **	Returns:
93 **		a pointer to a properly capitalized string.
94 **
95 **	Side Effects:
96 **		none.
97 */
98 
99 char *
100 capitalize(s)
101 	register char *s;
102 {
103 	static char buf[50];
104 	register char *p;
105 
106 	p = buf;
107 
108 	for (;;)
109 	{
110 		while (!isalpha(*s) && *s != '\0')
111 			*p++ = *s++;
112 		if (*s == '\0')
113 			break;
114 		*p++ = toupper(*s);
115 		s++;
116 		while (isalpha(*s))
117 			*p++ = *s++;
118 	}
119 
120 	*p = '\0';
121 	return (buf);
122 }
123 /*
124 **  XALLOC -- Allocate memory and bitch wildly on failure.
125 **
126 **	THIS IS A CLUDGE.  This should be made to give a proper
127 **	error -- but after all, what can we do?
128 **
129 **	Parameters:
130 **		sz -- size of area to allocate.
131 **
132 **	Returns:
133 **		pointer to data region.
134 **
135 **	Side Effects:
136 **		Memory is allocated.
137 */
138 
139 char *
140 xalloc(sz)
141 	register int sz;
142 {
143 	register char *p;
144 	extern char *malloc();
145 
146 	p = malloc((unsigned) sz);
147 	if (p == NULL)
148 	{
149 		syserr("Out of memory!!");
150 		abort();
151 		/* exit(EX_UNAVAILABLE); */
152 	}
153 	return (p);
154 }
155 /*
156 **  COPYPLIST -- copy list of pointers.
157 **
158 **	This routine is the equivalent of newstr for lists of
159 **	pointers.
160 **
161 **	Parameters:
162 **		list -- list of pointers to copy.
163 **			Must be NULL terminated.
164 **		copycont -- if TRUE, copy the contents of the vector
165 **			(which must be a string) also.
166 **
167 **	Returns:
168 **		a copy of 'list'.
169 **
170 **	Side Effects:
171 **		none.
172 */
173 
174 char **
175 copyplist(list, copycont)
176 	char **list;
177 	bool copycont;
178 {
179 	register char **vp;
180 	register char **newvp;
181 
182 	for (vp = list; *vp != NULL; vp++)
183 		continue;
184 
185 	vp++;
186 
187 	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
188 	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
189 
190 	if (copycont)
191 	{
192 		for (vp = newvp; *vp != NULL; vp++)
193 			*vp = newstr(*vp);
194 	}
195 
196 	return (newvp);
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 
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
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 
256 xputs(s)
257 	register char *s;
258 {
259 	register char c;
260 	register struct metamac *mp;
261 	extern struct metamac MetaMacros[];
262 
263 	if (s == NULL)
264 	{
265 		printf("<null>");
266 		return;
267 	}
268 	c = *s;
269 	if (c == MATCHREPL && isdigit(s[1]) && s[2] == '\0')
270 	{
271 		printf("$%c", s[1]);
272 		return;
273 	}
274 	for (mp = MetaMacros; mp->metaname != NULL; mp++)
275 	{
276 		if (mp->metaval == c)
277 		{
278 			printf("$%c%s", mp->metaname, ++s);
279 			return;
280 		}
281 	}
282 	(void) putchar('"');
283 	while ((c = *s++) != '\0')
284 	{
285 		if (!isascii(c))
286 		{
287 			(void) putchar('\\');
288 			c &= 0177;
289 		}
290 		if (c < 040 || c >= 0177)
291 		{
292 			switch (c)
293 			{
294 			  case '\n':
295 				c = 'n';
296 				break;
297 
298 			  case '\r':
299 				c = 'r';
300 				break;
301 
302 			  case '\t':
303 				c = 't';
304 				break;
305 
306 			  case '\001':
307 				(void) putchar('$');
308 				continue;
309 
310 			  default:
311 				(void) putchar('^');
312 				(void) putchar(c ^ 0100);
313 				continue;
314 			}
315 			(void) putchar('\\');
316 		}
317 		(void) putchar(c);
318 	}
319 	(void) putchar('"');
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 
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 
368 buildfname(p, login, buf)
369 	register char *p;
370 	char *login;
371 	char *buf;
372 {
373 	register char *bp = buf;
374 
375 	if (*p == '*')
376 		p++;
377 	while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
378 	{
379 		if (*p == '&')
380 		{
381 			(void) strcpy(bp, login);
382 			*bp = toupper(*bp);
383 			while (*bp != '\0')
384 				bp++;
385 			p++;
386 		}
387 		else
388 			*bp++ = *p++;
389 	}
390 	*bp = '\0';
391 }
392 /*
393 **  SAFEFILE -- return true if a file exists and is safe for a user.
394 **
395 **	Parameters:
396 **		fn -- filename to check.
397 **		uid -- uid to compare against.
398 **		mode -- mode bits that must match.
399 **
400 **	Returns:
401 **		TRUE if fn exists, is owned by uid, and matches mode.
402 **		FALSE otherwise.
403 **
404 **	Side Effects:
405 **		none.
406 */
407 
408 bool
409 safefile(fn, uid, mode)
410 	char *fn;
411 	int uid;
412 	int mode;
413 {
414 	struct stat stbuf;
415 
416 	if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
417 	    (stbuf.st_mode & mode) == mode)
418 		return (TRUE);
419 	errno = 0;
420 	return (FALSE);
421 }
422 /*
423 **  FIXCRLF -- fix <CR><LF> in line.
424 **
425 **	Looks for the <CR><LF> combination and turns it into the
426 **	UNIX canonical <NL> character.  It only takes one line,
427 **	i.e., it is assumed that the first <NL> found is the end
428 **	of the line.
429 **
430 **	Parameters:
431 **		line -- the line to fix.
432 **		stripnl -- if true, strip the newline also.
433 **
434 **	Returns:
435 **		none.
436 **
437 **	Side Effects:
438 **		line is changed in place.
439 */
440 
441 fixcrlf(line, stripnl)
442 	char *line;
443 	bool stripnl;
444 {
445 	register char *p;
446 
447 	p = index(line, '\n');
448 	if (p == NULL)
449 		return;
450 	if (p > line && p[-1] == '\r')
451 		p--;
452 	if (!stripnl)
453 		*p++ = '\n';
454 	*p = '\0';
455 }
456 /*
457 **  DFOPEN -- determined file open
458 **
459 **	This routine has the semantics of fopen, except that it will
460 **	keep trying a few times to make this happen.  The idea is that
461 **	on very loaded systems, we may run out of resources (inodes,
462 **	whatever), so this tries to get around it.
463 */
464 
465 FILE *
466 dfopen(filename, mode)
467 	char *filename;
468 	char *mode;
469 {
470 	register int tries;
471 	register FILE *fp;
472 
473 	for (tries = 0; tries < 10; tries++)
474 	{
475 		sleep((unsigned) (10 * tries));
476 		errno = 0;
477 		fp = fopen(filename, mode);
478 		if (fp != NULL)
479 			break;
480 		if (errno != ENFILE && errno != EINTR)
481 			break;
482 	}
483 	errno = 0;
484 	return (fp);
485 }
486 /*
487 **  PUTLINE -- put a line like fputs obeying SMTP conventions
488 **
489 **	This routine always guarantees outputing a newline (or CRLF,
490 **	as appropriate) at the end of the string.
491 **
492 **	Parameters:
493 **		l -- line to put.
494 **		fp -- file to put it onto.
495 **		m -- the mailer used to control output.
496 **
497 **	Returns:
498 **		none
499 **
500 **	Side Effects:
501 **		output of l to fp.
502 */
503 
504 putline(l, fp, m)
505 	register char *l;
506 	FILE *fp;
507 	MAILER *m;
508 {
509 	register char *p;
510 	register char svchar;
511 
512 	/* strip out 0200 bits -- these can look like TELNET protocol */
513 	if (bitnset(M_7BITS, m->m_flags))
514 	{
515 		for (p = l; svchar = *p; ++p)
516 			if (svchar & 0200)
517 				*p = svchar &~ 0200;
518 	}
519 
520 	do
521 	{
522 		/* find the end of the line */
523 		p = index(l, '\n');
524 		if (p == NULL)
525 			p = &l[strlen(l)];
526 
527 		/* check for line overflow */
528 		while (m->m_linelimit > 0 && (p - l) > m->m_linelimit)
529 		{
530 			register char *q = &l[m->m_linelimit - 1];
531 
532 			svchar = *q;
533 			*q = '\0';
534 			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
535 				(void) putc('.', fp);
536 			fputs(l, fp);
537 			(void) putc('!', fp);
538 			fputs(m->m_eol, fp);
539 			*q = svchar;
540 			l = q;
541 		}
542 
543 		/* output last part */
544 		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
545 			(void) putc('.', fp);
546 		for ( ; l < p; ++l)
547 			(void) putc(*l, fp);
548 		fputs(m->m_eol, fp);
549 		if (*l == '\n')
550 			++l;
551 	} while (l[0] != '\0');
552 }
553 /*
554 **  XUNLINK -- unlink a file, doing logging as appropriate.
555 **
556 **	Parameters:
557 **		f -- name of file to unlink.
558 **
559 **	Returns:
560 **		none.
561 **
562 **	Side Effects:
563 **		f is unlinked.
564 */
565 
566 xunlink(f)
567 	char *f;
568 {
569 	register int i;
570 
571 # ifdef LOG
572 	if (LogLevel > 20)
573 		syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
574 # endif LOG
575 
576 	i = unlink(f);
577 # ifdef LOG
578 	if (i < 0 && LogLevel > 21)
579 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
580 # endif LOG
581 }
582 /*
583 **  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
584 **
585 **	Parameters:
586 **		buf -- place to put the input line.
587 **		siz -- size of buf.
588 **		fp -- file to read from.
589 **
590 **	Returns:
591 **		NULL on error (including timeout).  This will also leave
592 **			buf containing a null string.
593 **		buf otherwise.
594 **
595 **	Side Effects:
596 **		none.
597 */
598 
599 static jmp_buf	CtxReadTimeout;
600 
601 char *
602 sfgets(buf, siz, fp)
603 	char *buf;
604 	int siz;
605 	FILE *fp;
606 {
607 	register EVENT *ev = NULL;
608 	register char *p;
609 	static int readtimeout();
610 
611 	/* set the timeout */
612 	if (ReadTimeout != 0)
613 	{
614 		if (setjmp(CtxReadTimeout) != 0)
615 		{
616 # ifdef LOG
617 			syslog(LOG_NOTICE,
618 			    "timeout waiting for input from %s\n",
619 			    RealHostName? RealHostName: "local");
620 # endif
621 			errno = 0;
622 			usrerr("451 timeout waiting for input");
623 			buf[0] = '\0';
624 			return (NULL);
625 		}
626 		ev = setevent((time_t) ReadTimeout, readtimeout, 0);
627 	}
628 
629 	/* try to read */
630 	p = NULL;
631 	while (p == NULL && !feof(fp) && !ferror(fp))
632 	{
633 		errno = 0;
634 		p = fgets(buf, siz, fp);
635 		if (errno == EINTR)
636 			clearerr(fp);
637 	}
638 
639 	/* clear the event if it has not sprung */
640 	clrevent(ev);
641 
642 	/* clean up the books and exit */
643 	LineNumber++;
644 	if (p == NULL)
645 	{
646 		buf[0] = '\0';
647 		return (NULL);
648 	}
649 	if (!EightBit)
650 		for (p = buf; *p != '\0'; p++)
651 			*p &= ~0200;
652 	return (buf);
653 }
654 
655 static
656 readtimeout()
657 {
658 	longjmp(CtxReadTimeout, 1);
659 }
660 /*
661 **  FGETFOLDED -- like fgets, but know about folded lines.
662 **
663 **	Parameters:
664 **		buf -- place to put result.
665 **		n -- bytes available.
666 **		f -- file to read from.
667 **
668 **	Returns:
669 **		buf on success, NULL on error or EOF.
670 **
671 **	Side Effects:
672 **		buf gets lines from f, with continuation lines (lines
673 **		with leading white space) appended.  CRLF's are mapped
674 **		into single newlines.  Any trailing NL is stripped.
675 */
676 
677 char *
678 fgetfolded(buf, n, f)
679 	char *buf;
680 	register int n;
681 	FILE *f;
682 {
683 	register char *p = buf;
684 	register int i;
685 
686 	n--;
687 	while ((i = getc(f)) != EOF)
688 	{
689 		if (i == '\r')
690 		{
691 			i = getc(f);
692 			if (i != '\n')
693 			{
694 				if (i != EOF)
695 					(void) ungetc(i, f);
696 				i = '\r';
697 			}
698 		}
699 		if (--n > 0)
700 			*p++ = i;
701 		else if (n == 0)
702 			nmessage(Arpa_Info, "warning: line truncated");
703 		if (i == '\n')
704 		{
705 			LineNumber++;
706 			i = getc(f);
707 			if (i != EOF)
708 				(void) ungetc(i, f);
709 			if (i != ' ' && i != '\t')
710 				break;
711 		}
712 	}
713 	if (p == buf)
714 		return (NULL);
715 	*--p = '\0';
716 	if (!EightBit)
717 	{
718 		/* headers always have to be 7-bit */
719 		for (p = buf; (i = *p) != '\0'; *p++)
720 			if (bitset(0200, i))
721 				*p = i & ~0200;
722 	}
723 	return (buf);
724 }
725 /*
726 **  CURTIME -- return current time.
727 **
728 **	Parameters:
729 **		none.
730 **
731 **	Returns:
732 **		the current time.
733 **
734 **	Side Effects:
735 **		none.
736 */
737 
738 time_t
739 curtime()
740 {
741 	auto time_t t;
742 
743 	(void) time(&t);
744 	return (t);
745 }
746 /*
747 **  ATOBOOL -- convert a string representation to boolean.
748 **
749 **	Defaults to "TRUE"
750 **
751 **	Parameters:
752 **		s -- string to convert.  Takes "tTyY" as true,
753 **			others as false.
754 **
755 **	Returns:
756 **		A boolean representation of the string.
757 **
758 **	Side Effects:
759 **		none.
760 */
761 
762 bool
763 atobool(s)
764 	register char *s;
765 {
766 	if (*s == '\0' || index("tTyY", *s) != NULL)
767 		return (TRUE);
768 	return (FALSE);
769 }
770 /*
771 **  ATOOCT -- convert a string representation to octal.
772 **
773 **	Parameters:
774 **		s -- string to convert.
775 **
776 **	Returns:
777 **		An integer representing the string interpreted as an
778 **		octal number.
779 **
780 **	Side Effects:
781 **		none.
782 */
783 
784 atooct(s)
785 	register char *s;
786 {
787 	register int i = 0;
788 
789 	while (*s >= '0' && *s <= '7')
790 		i = (i << 3) | (*s++ - '0');
791 	return (i);
792 }
793 /*
794 **  WAITFOR -- wait for a particular process id.
795 **
796 **	Parameters:
797 **		pid -- process id to wait for.
798 **
799 **	Returns:
800 **		status of pid.
801 **		-1 if pid never shows up.
802 **
803 **	Side Effects:
804 **		none.
805 */
806 
807 waitfor(pid)
808 	int pid;
809 {
810 	auto int st;
811 	int i;
812 
813 	do
814 	{
815 		errno = 0;
816 		i = wait(&st);
817 	} while ((i >= 0 || errno == EINTR) && i != pid);
818 	if (i < 0)
819 		st = -1;
820 	return (st);
821 }
822 /*
823 **  BITINTERSECT -- tell if two bitmaps intersect
824 **
825 **	Parameters:
826 **		a, b -- the bitmaps in question
827 **
828 **	Returns:
829 **		TRUE if they have a non-null intersection
830 **		FALSE otherwise
831 **
832 **	Side Effects:
833 **		none.
834 */
835 
836 bool
837 bitintersect(a, b)
838 	BITMAP a;
839 	BITMAP b;
840 {
841 	int i;
842 
843 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
844 		if ((a[i] & b[i]) != 0)
845 			return (TRUE);
846 	return (FALSE);
847 }
848 /*
849 **  BITZEROP -- tell if a bitmap is all zero
850 **
851 **	Parameters:
852 **		map -- the bit map to check
853 **
854 **	Returns:
855 **		TRUE if map is all zero.
856 **		FALSE if there are any bits set in map.
857 **
858 **	Side Effects:
859 **		none.
860 */
861 
862 bool
863 bitzerop(map)
864 	BITMAP map;
865 {
866 	int i;
867 
868 	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
869 		if (map[i] != 0)
870 			return (FALSE);
871 	return (TRUE);
872 }
873