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