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.4 (Berkeley) 01/21/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 (!isalpha(*s) && *s != '\0') 84 *p++ = *s++; 85 if (*s == '\0') 86 break; 87 *p++ = toupper(*s); 88 s++; 89 while (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 char 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++) != '\0') 241 { 242 if (!isascii(c)) 243 { 244 (void) putchar('\\'); 245 c &= 0177; 246 } 247 if (isprint(c)) 248 { 249 putchar(c); 250 continue; 251 } 252 if (c == MATCHREPL || c == '\001') 253 { 254 putchar('$'); 255 continue; 256 } 257 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 258 { 259 if (mp->metaval == c) 260 { 261 printf("$%c", mp->metaname); 262 c = '\0'; 263 break; 264 } 265 } 266 267 /* wasn't a meta-macro -- find another way to print it */ 268 switch (c) 269 { 270 case '\0': 271 continue; 272 273 case '\n': 274 c = 'n'; 275 break; 276 277 case '\r': 278 c = 'r'; 279 break; 280 281 case '\t': 282 c = 't'; 283 break; 284 285 default: 286 (void) putchar('^'); 287 (void) putchar(c ^ 0100); 288 continue; 289 } 290 } 291 (void) fflush(stdout); 292 } 293 /* 294 ** MAKELOWER -- Translate a line into lower case 295 ** 296 ** Parameters: 297 ** p -- the string to translate. If NULL, return is 298 ** immediate. 299 ** 300 ** Returns: 301 ** none. 302 ** 303 ** Side Effects: 304 ** String pointed to by p is translated to lower case. 305 ** 306 ** Called By: 307 ** parse 308 */ 309 310 makelower(p) 311 register char *p; 312 { 313 register char c; 314 315 if (p == NULL) 316 return; 317 for (; (c = *p) != '\0'; p++) 318 if (isascii(c) && isupper(c)) 319 *p = tolower(c); 320 } 321 /* 322 ** BUILDFNAME -- build full name from gecos style entry. 323 ** 324 ** This routine interprets the strange entry that would appear 325 ** in the GECOS field of the password file. 326 ** 327 ** Parameters: 328 ** p -- name to build. 329 ** login -- the login name of this user (for &). 330 ** buf -- place to put the result. 331 ** 332 ** Returns: 333 ** none. 334 ** 335 ** Side Effects: 336 ** none. 337 */ 338 339 buildfname(gecos, login, buf) 340 register char *gecos; 341 char *login; 342 char *buf; 343 { 344 register char *p; 345 register char *bp = buf; 346 int l; 347 348 if (*gecos == '*') 349 gecos++; 350 351 /* find length of final string */ 352 l = 0; 353 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 354 { 355 if (*p == '&') 356 l += strlen(login); 357 else 358 l++; 359 } 360 361 /* now fill in buf */ 362 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 363 { 364 if (*p == '&') 365 { 366 (void) strcpy(bp, login); 367 *bp = toupper(*bp); 368 while (*bp != '\0') 369 bp++; 370 } 371 else 372 *bp++ = *p; 373 } 374 *bp = '\0'; 375 } 376 /* 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 392 bool 393 safefile(fn, uid, mode) 394 char *fn; 395 uid_t uid; 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); 403 errno = 0; 404 return (FALSE); 405 } 406 /* 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 425 fixcrlf(line, stripnl) 426 char *line; 427 bool stripnl; 428 { 429 register char *p; 430 431 p = strchr(line, '\n'); 432 if (p == NULL) 433 return; 434 if (p > line && p[-1] == '\r') 435 p--; 436 if (!stripnl) 437 *p++ = '\n'; 438 *p = '\0'; 439 } 440 /* 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 449 FILE * 450 dfopen(filename, mode) 451 char *filename; 452 char *mode; 453 { 454 register int tries; 455 register FILE *fp; 456 457 for (tries = 0; tries < 10; tries++) 458 { 459 sleep((unsigned) (10 * tries)); 460 errno = 0; 461 fp = fopen(filename, mode); 462 if (fp != NULL) 463 break; 464 if (errno != ENFILE && errno != EINTR) 465 break; 466 } 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 } 481 return (fp); 482 } 483 /* 484 ** PUTLINE -- put a line like fputs obeying SMTP conventions 485 ** 486 ** This routine always guarantees outputing a newline (or CRLF, 487 ** as appropriate) at the end of the string. 488 ** 489 ** Parameters: 490 ** l -- line to put. 491 ** fp -- file to put it onto. 492 ** m -- the mailer used to control output. 493 ** 494 ** Returns: 495 ** none 496 ** 497 ** Side Effects: 498 ** output of l to fp. 499 */ 500 501 putline(l, fp, m) 502 register char *l; 503 FILE *fp; 504 MAILER *m; 505 { 506 register char *p; 507 register char svchar; 508 509 /* strip out 0200 bits -- these can look like TELNET protocol */ 510 if (bitnset(M_7BITS, m->m_flags)) 511 { 512 for (p = l; svchar = *p; ++p) 513 if (svchar & 0200) 514 *p = svchar &~ 0200; 515 } 516 517 do 518 { 519 /* find the end of the line */ 520 p = strchr(l, '\n'); 521 if (p == NULL) 522 p = &l[strlen(l)]; 523 524 /* check for line overflow */ 525 while (m->m_linelimit > 0 && (p - l) > m->m_linelimit) 526 { 527 register char *q = &l[m->m_linelimit - 1]; 528 529 svchar = *q; 530 *q = '\0'; 531 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 532 (void) putc('.', fp); 533 fputs(l, fp); 534 (void) putc('!', fp); 535 fputs(m->m_eol, fp); 536 *q = svchar; 537 l = q; 538 } 539 540 /* output last part */ 541 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 542 (void) putc('.', fp); 543 for ( ; l < p; ++l) 544 (void) putc(*l, fp); 545 fputs(m->m_eol, fp); 546 if (*l == '\n') 547 ++l; 548 } while (l[0] != '\0'); 549 } 550 /* 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 563 xunlink(f) 564 char *f; 565 { 566 register int i; 567 568 # ifdef LOG 569 if (LogLevel > 20) 570 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 571 # endif /* LOG */ 572 573 i = unlink(f); 574 # ifdef LOG 575 if (i < 0 && LogLevel > 21) 576 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 577 # endif /* LOG */ 578 } 579 /* 580 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 581 ** 582 ** Parameters: 583 ** buf -- place to put the input line. 584 ** siz -- size of buf. 585 ** fp -- file to read from. 586 ** timeout -- the timeout before error occurs. 587 ** 588 ** Returns: 589 ** NULL on error (including timeout). This will also leave 590 ** buf containing a null string. 591 ** buf otherwise. 592 ** 593 ** Side Effects: 594 ** none. 595 */ 596 597 static jmp_buf CtxReadTimeout; 598 599 char * 600 sfgets(buf, siz, fp, timeout) 601 char *buf; 602 int siz; 603 FILE *fp; 604 time_t timeout; 605 { 606 register EVENT *ev = NULL; 607 register char *p; 608 static int readtimeout(); 609 610 /* set the timeout */ 611 if (timeout != 0) 612 { 613 if (setjmp(CtxReadTimeout) != 0) 614 { 615 # ifdef LOG 616 syslog(LOG_NOTICE, 617 "timeout waiting for input from %s\n", 618 CurHostName? CurHostName: "local"); 619 # endif 620 errno = 0; 621 usrerr("451 timeout waiting for input"); 622 buf[0] = '\0'; 623 return (NULL); 624 } 625 ev = setevent(timeout, 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 ** input line(s) on success, NULL on error or EOF. 669 ** This will normally be buf -- unless the line is too 670 ** long, when it will be xalloc()ed. 671 ** 672 ** Side Effects: 673 ** buf gets lines from f, with continuation lines (lines 674 ** with leading white space) appended. CRLF's are mapped 675 ** into single newlines. Any trailing NL is stripped. 676 */ 677 678 char * 679 fgetfolded(buf, n, f) 680 char *buf; 681 register int n; 682 FILE *f; 683 { 684 register char *p = buf; 685 char *bp = buf; 686 register int i; 687 688 n--; 689 while ((i = getc(f)) != EOF) 690 { 691 if (i == '\r') 692 { 693 i = getc(f); 694 if (i != '\n') 695 { 696 if (i != EOF) 697 (void) ungetc(i, f); 698 i = '\r'; 699 } 700 } 701 if (--n <= 0) 702 { 703 /* allocate new space */ 704 char *nbp; 705 int nn; 706 707 nn = (p - bp); 708 if (nn < MEMCHUNKSIZE) 709 nn *= 2; 710 else 711 nn += MEMCHUNKSIZE; 712 nbp = xalloc(nn); 713 bcopy(bp, nbp, p - bp); 714 p = &nbp[p - bp]; 715 if (bp != buf) 716 free(bp); 717 bp = nbp; 718 n = nn - (p - bp); 719 } 720 *p++ = i; 721 if (i == '\n') 722 { 723 LineNumber++; 724 i = getc(f); 725 if (i != EOF) 726 (void) ungetc(i, f); 727 if (i != ' ' && i != '\t') 728 break; 729 } 730 } 731 if (p == bp) 732 return (NULL); 733 *--p = '\0'; 734 return (bp); 735 } 736 /* 737 ** CURTIME -- return current time. 738 ** 739 ** Parameters: 740 ** none. 741 ** 742 ** Returns: 743 ** the current time. 744 ** 745 ** Side Effects: 746 ** none. 747 */ 748 749 time_t 750 curtime() 751 { 752 auto time_t t; 753 754 (void) time(&t); 755 return (t); 756 } 757 /* 758 ** ATOBOOL -- convert a string representation to boolean. 759 ** 760 ** Defaults to "TRUE" 761 ** 762 ** Parameters: 763 ** s -- string to convert. Takes "tTyY" as true, 764 ** others as false. 765 ** 766 ** Returns: 767 ** A boolean representation of the string. 768 ** 769 ** Side Effects: 770 ** none. 771 */ 772 773 bool 774 atobool(s) 775 register char *s; 776 { 777 if (*s == '\0' || strchr("tTyY", *s) != NULL) 778 return (TRUE); 779 return (FALSE); 780 } 781 /* 782 ** ATOOCT -- convert a string representation to octal. 783 ** 784 ** Parameters: 785 ** s -- string to convert. 786 ** 787 ** Returns: 788 ** An integer representing the string interpreted as an 789 ** octal number. 790 ** 791 ** Side Effects: 792 ** none. 793 */ 794 795 atooct(s) 796 register char *s; 797 { 798 register int i = 0; 799 800 while (*s >= '0' && *s <= '7') 801 i = (i << 3) | (*s++ - '0'); 802 return (i); 803 } 804 /* 805 ** WAITFOR -- wait for a particular process id. 806 ** 807 ** Parameters: 808 ** pid -- process id to wait for. 809 ** 810 ** Returns: 811 ** status of pid. 812 ** -1 if pid never shows up. 813 ** 814 ** Side Effects: 815 ** none. 816 */ 817 818 waitfor(pid) 819 int pid; 820 { 821 auto int st; 822 int i; 823 824 do 825 { 826 errno = 0; 827 i = wait(&st); 828 } while ((i >= 0 || errno == EINTR) && i != pid); 829 if (i < 0) 830 st = -1; 831 return (st); 832 } 833 /* 834 ** BITINTERSECT -- tell if two bitmaps intersect 835 ** 836 ** Parameters: 837 ** a, b -- the bitmaps in question 838 ** 839 ** Returns: 840 ** TRUE if they have a non-null intersection 841 ** FALSE otherwise 842 ** 843 ** Side Effects: 844 ** none. 845 */ 846 847 bool 848 bitintersect(a, b) 849 BITMAP a; 850 BITMAP b; 851 { 852 int i; 853 854 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 855 if ((a[i] & b[i]) != 0) 856 return (TRUE); 857 return (FALSE); 858 } 859 /* 860 ** BITZEROP -- tell if a bitmap is all zero 861 ** 862 ** Parameters: 863 ** map -- the bit map to check 864 ** 865 ** Returns: 866 ** TRUE if map is all zero. 867 ** FALSE if there are any bits set in map. 868 ** 869 ** Side Effects: 870 ** none. 871 */ 872 873 bool 874 bitzerop(map) 875 BITMAP map; 876 { 877 int i; 878 879 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 880 if (map[i] != 0) 881 return (FALSE); 882 return (TRUE); 883 } 884