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