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