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.1 (Berkeley) 12/21/92"; 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 c = *s; 241 if (c == MATCHREPL && isdigit(s[1]) && s[2] == '\0') 242 { 243 printf("$%c", s[1]); 244 return; 245 } 246 for (mp = MetaMacros; mp->metaname != NULL; mp++) 247 { 248 if (mp->metaval == c) 249 { 250 printf("$%c%s", mp->metaname, ++s); 251 return; 252 } 253 } 254 (void) putchar('"'); 255 while ((c = *s++) != '\0') 256 { 257 if (!isascii(c)) 258 { 259 (void) putchar('\\'); 260 c &= 0177; 261 } 262 if (c < 040 || c >= 0177) 263 { 264 switch (c) 265 { 266 case '\n': 267 c = 'n'; 268 break; 269 270 case '\r': 271 c = 'r'; 272 break; 273 274 case '\t': 275 c = 't'; 276 break; 277 278 case '\001': 279 (void) putchar('$'); 280 continue; 281 282 default: 283 (void) putchar('^'); 284 (void) putchar(c ^ 0100); 285 continue; 286 } 287 (void) putchar('\\'); 288 } 289 (void) putchar(c); 290 } 291 (void) putchar('"'); 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 > 20) 571 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 572 # endif /* LOG */ 573 574 i = unlink(f); 575 # ifdef LOG 576 if (i < 0 && LogLevel > 21) 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 ** 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) 601 char *buf; 602 int siz; 603 FILE *fp; 604 { 605 register EVENT *ev = NULL; 606 register char *p; 607 static int readtimeout(); 608 609 /* set the timeout */ 610 if (ReadTimeout != 0) 611 { 612 if (setjmp(CtxReadTimeout) != 0) 613 { 614 # ifdef LOG 615 syslog(LOG_NOTICE, 616 "timeout waiting for input from %s\n", 617 RealHostName? RealHostName: "local"); 618 # endif 619 errno = 0; 620 usrerr("451 timeout waiting for input"); 621 buf[0] = '\0'; 622 return (NULL); 623 } 624 ev = setevent((time_t) ReadTimeout, readtimeout, 0); 625 } 626 627 /* try to read */ 628 p = NULL; 629 while (p == NULL && !feof(fp) && !ferror(fp)) 630 { 631 errno = 0; 632 p = fgets(buf, siz, fp); 633 if (errno == EINTR) 634 clearerr(fp); 635 } 636 637 /* clear the event if it has not sprung */ 638 clrevent(ev); 639 640 /* clean up the books and exit */ 641 LineNumber++; 642 if (p == NULL) 643 { 644 buf[0] = '\0'; 645 return (NULL); 646 } 647 if (!EightBit) 648 for (p = buf; *p != '\0'; p++) 649 *p &= ~0200; 650 return (buf); 651 } 652 653 static 654 readtimeout() 655 { 656 longjmp(CtxReadTimeout, 1); 657 } 658 /* 659 ** FGETFOLDED -- like fgets, but know about folded lines. 660 ** 661 ** Parameters: 662 ** buf -- place to put result. 663 ** n -- bytes available. 664 ** f -- file to read from. 665 ** 666 ** Returns: 667 ** input line(s) on success, NULL on error or EOF. 668 ** This will normally be buf -- unless the line is too 669 ** long, when it will be xalloc()ed. 670 ** 671 ** Side Effects: 672 ** buf gets lines from f, with continuation lines (lines 673 ** with leading white space) appended. CRLF's are mapped 674 ** into single newlines. Any trailing NL is stripped. 675 */ 676 677 char * 678 fgetfolded(buf, n, f) 679 char *buf; 680 register int n; 681 FILE *f; 682 { 683 register char *p = buf; 684 char *bp = buf; 685 register int i; 686 687 n--; 688 while ((i = getc(f)) != EOF) 689 { 690 if (i == '\r') 691 { 692 i = getc(f); 693 if (i != '\n') 694 { 695 if (i != EOF) 696 (void) ungetc(i, f); 697 i = '\r'; 698 } 699 } 700 if (--n <= 0) 701 { 702 /* allocate new space */ 703 char *nbp; 704 int nn; 705 706 nn = (p - bp); 707 if (nn < MEMCHUNKSIZE) 708 nn *= 2; 709 else 710 nn += MEMCHUNKSIZE; 711 nbp = xalloc(nn); 712 bcopy(bp, nbp, p - bp); 713 p = &nbp[p - bp]; 714 if (bp != buf) 715 free(bp); 716 bp = nbp; 717 n = nn - (p - bp); 718 } 719 *p++ = i; 720 if (i == '\n') 721 { 722 LineNumber++; 723 i = getc(f); 724 if (i != EOF) 725 (void) ungetc(i, f); 726 if (i != ' ' && i != '\t') 727 break; 728 } 729 } 730 if (p == bp) 731 return (NULL); 732 *--p = '\0'; 733 return (bp); 734 } 735 /* 736 ** CURTIME -- return current time. 737 ** 738 ** Parameters: 739 ** none. 740 ** 741 ** Returns: 742 ** the current time. 743 ** 744 ** Side Effects: 745 ** none. 746 */ 747 748 time_t 749 curtime() 750 { 751 auto time_t t; 752 753 (void) time(&t); 754 return (t); 755 } 756 /* 757 ** ATOBOOL -- convert a string representation to boolean. 758 ** 759 ** Defaults to "TRUE" 760 ** 761 ** Parameters: 762 ** s -- string to convert. Takes "tTyY" as true, 763 ** others as false. 764 ** 765 ** Returns: 766 ** A boolean representation of the string. 767 ** 768 ** Side Effects: 769 ** none. 770 */ 771 772 bool 773 atobool(s) 774 register char *s; 775 { 776 if (*s == '\0' || strchr("tTyY", *s) != NULL) 777 return (TRUE); 778 return (FALSE); 779 } 780 /* 781 ** ATOOCT -- convert a string representation to octal. 782 ** 783 ** Parameters: 784 ** s -- string to convert. 785 ** 786 ** Returns: 787 ** An integer representing the string interpreted as an 788 ** octal number. 789 ** 790 ** Side Effects: 791 ** none. 792 */ 793 794 atooct(s) 795 register char *s; 796 { 797 register int i = 0; 798 799 while (*s >= '0' && *s <= '7') 800 i = (i << 3) | (*s++ - '0'); 801 return (i); 802 } 803 /* 804 ** WAITFOR -- wait for a particular process id. 805 ** 806 ** Parameters: 807 ** pid -- process id to wait for. 808 ** 809 ** Returns: 810 ** status of pid. 811 ** -1 if pid never shows up. 812 ** 813 ** Side Effects: 814 ** none. 815 */ 816 817 waitfor(pid) 818 int pid; 819 { 820 auto int st; 821 int i; 822 823 do 824 { 825 errno = 0; 826 i = wait(&st); 827 } while ((i >= 0 || errno == EINTR) && i != pid); 828 if (i < 0) 829 st = -1; 830 return (st); 831 } 832 /* 833 ** BITINTERSECT -- tell if two bitmaps intersect 834 ** 835 ** Parameters: 836 ** a, b -- the bitmaps in question 837 ** 838 ** Returns: 839 ** TRUE if they have a non-null intersection 840 ** FALSE otherwise 841 ** 842 ** Side Effects: 843 ** none. 844 */ 845 846 bool 847 bitintersect(a, b) 848 BITMAP a; 849 BITMAP b; 850 { 851 int i; 852 853 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 854 if ((a[i] & b[i]) != 0) 855 return (TRUE); 856 return (FALSE); 857 } 858 /* 859 ** BITZEROP -- tell if a bitmap is all zero 860 ** 861 ** Parameters: 862 ** map -- the bit map to check 863 ** 864 ** Returns: 865 ** TRUE if map is all zero. 866 ** FALSE if there are any bits set in map. 867 ** 868 ** Side Effects: 869 ** none. 870 */ 871 872 bool 873 bitzerop(map) 874 BITMAP map; 875 { 876 int i; 877 878 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 879 if (map[i] != 0) 880 return (FALSE); 881 return (TRUE); 882 } 883