xref: /csrg-svn/usr.sbin/sendmail/src/util.c (revision 8055)
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.30		09/05/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 		printf("\t%08x=", *av);
204 		xputs(*av++);
205 		putchar('\n');
206 	}
207 }
208 # endif DEBUG
209 /*
210 **  LOWER -- turn letter into lower case.
211 **
212 **	Parameters:
213 **		c -- character to turn into lower case.
214 **
215 **	Returns:
216 **		c, in lower case.
217 **
218 **	Side Effects:
219 **		none.
220 */
221 
222 char
223 lower(c)
224 	register char c;
225 {
226 	if (isascii(c) && isupper(c))
227 		c = c - 'A' + 'a';
228 	return (c);
229 }
230 /*
231 **  XPUTS -- put string doing control escapes.
232 **
233 **	Parameters:
234 **		s -- string to put.
235 **
236 **	Returns:
237 **		none.
238 **
239 **	Side Effects:
240 **		output to stdout
241 */
242 
243 # ifdef DEBUG
244 xputs(s)
245 	register char *s;
246 {
247 	register char c;
248 
249 	if (s == NULL)
250 	{
251 		printf("<null>");
252 		return;
253 	}
254 	while ((c = *s++) != '\0')
255 	{
256 		if (!isascii(c))
257 		{
258 			putchar('\\');
259 			c &= 0177;
260 		}
261 		if (iscntrl(c))
262 		{
263 			putchar('^');
264 			c |= 0100;
265 		}
266 		putchar(c);
267 	}
268 	(void) fflush(stdout);
269 }
270 # endif DEBUG
271 /*
272 **  MAKELOWER -- Translate a line into lower case
273 **
274 **	Parameters:
275 **		p -- the string to translate.  If NULL, return is
276 **			immediate.
277 **
278 **	Returns:
279 **		none.
280 **
281 **	Side Effects:
282 **		String pointed to by p is translated to lower case.
283 **
284 **	Called By:
285 **		parse
286 */
287 
288 makelower(p)
289 	register char *p;
290 {
291 	register char c;
292 
293 	if (p == NULL)
294 		return;
295 	for (; (c = *p) != '\0'; p++)
296 		if (isascii(c) && isupper(c))
297 			*p = c - 'A' + 'a';
298 }
299 /*
300 **  SAMEWORD -- return TRUE if the words are the same
301 **
302 **	Ignores case.
303 **
304 **	Parameters:
305 **		a, b -- the words to compare.
306 **
307 **	Returns:
308 **		TRUE if a & b match exactly (modulo case)
309 **		FALSE otherwise.
310 **
311 **	Side Effects:
312 **		none.
313 */
314 
315 bool
316 sameword(a, b)
317 	register char *a, *b;
318 {
319 	while (lower(*a) == lower(*b))
320 	{
321 		if (*a == '\0')
322 			return (TRUE);
323 		a++;
324 		b++;
325 	}
326 	return (FALSE);
327 }
328 /*
329 **  CLEAR -- clear a block of memory
330 **
331 **	Parameters:
332 **		p -- location to clear.
333 **		l -- number of bytes to clear.
334 **
335 **	Returns:
336 **		none.
337 **
338 **	Side Effects:
339 **		none.
340 */
341 
342 clear(p, l)
343 	register char *p;
344 	register int l;
345 {
346 	while (l-- > 0)
347 		*p++ = 0;
348 }
349 /*
350 **  BUILDFNAME -- build full name from gecos style entry.
351 **
352 **	This routine interprets the strange entry that would appear
353 **	in the GECOS field of the password file.
354 **
355 **	Parameters:
356 **		p -- name to build.
357 **		login -- the login name of this user (for &).
358 **		buf -- place to put the result.
359 **
360 **	Returns:
361 **		none.
362 **
363 **	Side Effects:
364 **		none.
365 */
366 
367 buildfname(p, login, buf)
368 	register char *p;
369 	char *login;
370 	char *buf;
371 {
372 	register char *bp = buf;
373 
374 	if (*p == '*')
375 		p++;
376 	while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
377 	{
378 		if (*p == '&')
379 		{
380 			(void) strcpy(bp, login);
381 			*bp = toupper(*bp);
382 			while (*bp != '\0')
383 				bp++;
384 			p++;
385 		}
386 		else
387 			*bp++ = *p++;
388 	}
389 	*bp = '\0';
390 }
391 /*
392 **  SAFEFILE -- return true if a file exists and is safe for a user.
393 **
394 **	Parameters:
395 **		fn -- filename to check.
396 **		uid -- uid to compare against.
397 **		mode -- mode bits that must match.
398 **
399 **	Returns:
400 **		TRUE if fn exists, is owned by uid, and matches mode.
401 **		FALSE otherwise.
402 **
403 **	Side Effects:
404 **		none.
405 */
406 
407 bool
408 safefile(fn, uid, mode)
409 	char *fn;
410 	int uid;
411 	int mode;
412 {
413 	struct stat stbuf;
414 
415 	if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
416 	    (stbuf.st_mode & mode) == mode)
417 		return (TRUE);
418 	return (FALSE);
419 }
420 /*
421 **  FIXCRLF -- fix <CR><LF> in line.
422 **
423 **	Looks for the <CR><LF> combination and turns it into the
424 **	UNIX canonical <NL> character.  It only takes one line,
425 **	i.e., it is assumed that the first <NL> found is the end
426 **	of the line.
427 **
428 **	Parameters:
429 **		line -- the line to fix.
430 **		stripnl -- if true, strip the newline also.
431 **
432 **	Returns:
433 **		none.
434 **
435 **	Side Effects:
436 **		line is changed in place.
437 */
438 
439 fixcrlf(line, stripnl)
440 	char *line;
441 	bool stripnl;
442 {
443 	register char *p;
444 
445 	p = index(line, '\n');
446 	if (p == NULL)
447 		return;
448 	if (p[-1] == '\r')
449 		p--;
450 	if (!stripnl)
451 		*p++ = '\n';
452 	*p = '\0';
453 }
454 /*
455 **  SYSLOG -- fake entry to fool lint
456 */
457 
458 # ifdef LOG
459 # ifdef lint
460 
461 /*VARARGS2*/
462 syslog(pri, fmt, args)
463 	int pri;
464 	char *fmt;
465 {
466 	pri = *fmt;
467 	args = pri;
468 	pri = args;
469 }
470 
471 # endif lint
472 # endif LOG
473 /*
474 **  DFOPEN -- determined file open
475 **
476 **	This routine has the semantics of fopen, except that it will
477 **	keep trying a few times to make this happen.  The idea is that
478 **	on very loaded systems, we may run out of resources (inodes,
479 **	whatever), so this tries to get around it.
480 */
481 
482 FILE *
483 dfopen(filename, mode)
484 	char *filename;
485 	char *mode;
486 {
487 	register int tries;
488 	register FILE *fp;
489 	extern int errno;
490 
491 	for (tries = 0; tries < 10; tries++)
492 	{
493 		sleep(10 * tries);
494 		errno = 0;
495 		fp = fopen(filename, mode);
496 		if (fp != NULL || errno != ENFILE)
497 			break;
498 	}
499 	return (fp);
500 }
501 /*
502 **  PUTLINE -- put a line like fputs obeying SMTP conventions
503 **
504 **	This routine always guarantees outputing a newline (or CRLF,
505 **	as appropriate) at the end of the string.
506 **
507 **	Parameters:
508 **		l -- line to put.
509 **		fp -- file to put it onto.
510 **		fullsmtp -- if set, obey strictest SMTP conventions.
511 **
512 **	Returns:
513 **		none
514 **
515 **	Side Effects:
516 **		output of l to fp.
517 */
518 
519 # define SMTPLINELIM	990	/* maximum line length */
520 
521 putline(l, fp, fullsmtp)
522 	register char *l;
523 	FILE *fp;
524 	bool fullsmtp;
525 {
526 	register char *p;
527 	char svchar;
528 
529 	do
530 	{
531 		/* find the end of the line */
532 		p = index(l, '\n');
533 		if (p == NULL)
534 			p = &l[strlen(l)];
535 
536 		/* check for line overflow */
537 		while (fullsmtp && (p - l) > SMTPLINELIM)
538 		{
539 			register char *q = &l[SMTPLINELIM - 1];
540 
541 			svchar = *q;
542 			*q = '\0';
543 			fputs(l, fp);
544 			fputs("!\r\n", fp);
545 			*q = svchar;
546 			l = q;
547 		}
548 
549 		/* output last part */
550 		svchar = *p;
551 		*p = '\0';
552 		fputs(l, fp);
553 		if (fullsmtp)
554 			fputc('\r', fp);
555 		fputc('\n', fp);
556 		*p = svchar;
557 		l = p;
558 		if (*l == '\n')
559 			l++;
560 	} while (l[0] != '\0');
561 }
562 /*
563 **  XUNLINK -- unlink a file, doing logging as appropriate.
564 **
565 **	Parameters:
566 **		f -- name of file to unlink.
567 **
568 **	Returns:
569 **		none.
570 **
571 **	Side Effects:
572 **		f is unlinked.
573 */
574 
575 xunlink(f)
576 	char *f;
577 {
578 	register int i;
579 
580 # ifdef LOG
581 	if (LogLevel > 20)
582 		syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
583 # endif LOG
584 
585 	i = unlink(f);
586 # ifdef LOG
587 	if (i < 0 && LogLevel > 21)
588 		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
589 # endif LOG
590 }
591 /*
592 **  SFGETS -- "safe" fgets -- times out.
593 **
594 **	Parameters:
595 **		buf -- place to put the input line.
596 **		siz -- size of buf.
597 **		fp -- file to read from.
598 **
599 **	Returns:
600 **		NULL on error (including timeout).
601 **		buf otherwise.
602 **
603 **	Side Effects:
604 **		none.
605 */
606 
607 static bool	TimeoutFlag;
608 
609 char *
610 sfgets(buf, siz, fp)
611 	char *buf;
612 	int siz;
613 	FILE *fp;
614 {
615 	register EVENT *ev = NULL;
616 	register char *p;
617 	extern readtimeout();
618 
619 	if (ReadTimeout != 0)
620 		ev = setevent(ReadTimeout, readtimeout, 0);
621 	TimeoutFlag = FALSE;
622 	do
623 	{
624 		errno = 0;
625 		p = fgets(buf, siz, fp);
626 	} while (!(p != NULL || TimeoutFlag || errno != EINTR));
627 	clrevent(ev);
628 	LineNumber++;
629 	return (p);
630 }
631 
632 static
633 readtimeout()
634 {
635 	TimeoutFlag = TRUE;
636 }
637 /*
638 **  FGETFOLDED -- like fgets, but know about folded lines.
639 **
640 **	Parameters:
641 **		buf -- place to put result.
642 **		n -- bytes available.
643 **		f -- file to read from.
644 **
645 **	Returns:
646 **		buf on success, NULL on error or EOF.
647 **
648 **	Side Effects:
649 **		buf gets lines from f, with continuation lines (lines
650 **		with leading white space) appended.  CRLF's are mapped
651 **		into single newlines.  Any trailing NL is stripped.
652 */
653 
654 char *
655 fgetfolded(buf, n, f)
656 	char *buf;
657 	register int n;
658 	FILE *f;
659 {
660 	register char *p = buf;
661 	register int i;
662 
663 	n--;
664 	while (sfgets(p, n, f) != NULL)
665 	{
666 		fixcrlf(p, TRUE);
667 		i = fgetc(f);
668 		if (i != EOF)
669 			ungetc(i, f);
670 		if (i != ' ' && i != '\t')
671 			return (buf);
672 		i = strlen(p);
673 		p += i;
674 		*p++ = '\n';
675 		n -= i + 1;
676 	}
677 	return (NULL);
678 }
679 /*
680 **  PINTVL -- produce printable version of a time interval
681 **
682 **	Parameters:
683 **		intvl -- the interval to be converted
684 **		brief -- if TRUE, print this in an extremely compact form
685 **			(basically used for logging).
686 **
687 **	Returns:
688 **		A pointer to a string version of intvl suitable for
689 **			printing or framing.
690 **
691 **	Side Effects:
692 **		none.
693 **
694 **	Warning:
695 **		The string returned is in a static buffer.
696 */
697 
698 # define PLURAL(n)	((n) == 1 ? "" : "s")
699 
700 char *
701 pintvl(intvl, brief)
702 	time_t intvl;
703 	bool brief;
704 {
705 	static char buf[MAXNAME];
706 	register char *p;
707 	int wk, dy, hr, mi, se;
708 
709 	if (intvl == 0 && !brief)
710 		return ("zero seconds");
711 
712 	/* decode the interval into weeks, days, hours, minutes, seconds */
713 	se = intvl % 60;
714 	intvl /= 60;
715 	mi = intvl % 60;
716 	intvl /= 60;
717 	hr = intvl % 24;
718 	intvl /= 24;
719 	if (brief)
720 		dy = intvl;
721 	else
722 	{
723 		dy = intvl % 7;
724 		intvl /= 7;
725 		wk = intvl;
726 	}
727 
728 	/* now turn it into a sexy form */
729 	p = buf;
730 	if (brief)
731 	{
732 		if (dy > 0)
733 		{
734 			(void) sprintf(p, "%d+", dy);
735 			p += strlen(p);
736 		}
737 		(void) sprintf(p, "%02d:%02d:%02d", hr, mi, se);
738 		return (buf);
739 	}
740 
741 	/* use the verbose form */
742 	if (wk > 0)
743 	{
744 		(void) sprintf(p, ", %d week%s", wk, PLURAL(wk));
745 		p += strlen(p);
746 	}
747 	if (dy > 0)
748 	{
749 		(void) sprintf(p, ", %d day%s", dy, PLURAL(dy));
750 		p += strlen(p);
751 	}
752 	if (hr > 0)
753 	{
754 		(void) sprintf(p, ", %d hour%s", hr, PLURAL(hr));
755 		p += strlen(p);
756 	}
757 	if (mi > 0)
758 	{
759 		(void) sprintf(p, ", %d minute%s", mi, PLURAL(mi));
760 		p += strlen(p);
761 	}
762 	if (se > 0)
763 	{
764 		(void) sprintf(p, ", %d second%s", se, PLURAL(se));
765 		p += strlen(p);
766 	}
767 
768 	return (buf + 2);
769 }
770 /*
771 **  CURTIME -- return current time.
772 **
773 **	Parameters:
774 **		none.
775 **
776 **	Returns:
777 **		the current time.
778 **
779 **	Side Effects:
780 **		none.
781 */
782 
783 time_t
784 curtime()
785 {
786 	auto time_t t;
787 
788 	(void) time(&t);
789 	return (t);
790 }
791