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