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 5.29 (Berkeley) 07/19/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 /* 21 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 22 ** 23 ** Runs through a string and strips off unquoted quote 24 ** characters and quote bits. This is done in place. 25 ** 26 ** Parameters: 27 ** s -- the string to strip. 28 ** 29 ** Returns: 30 ** none. 31 ** 32 ** Side Effects: 33 ** none. 34 ** 35 ** Called By: 36 ** deliver 37 */ 38 39 stripquotes(s) 40 char *s; 41 { 42 register char *p; 43 register char *q; 44 register char c; 45 46 if (s == NULL) 47 return; 48 49 p = q = s; 50 do 51 { 52 c = *p++; 53 if (c == '\\') 54 c = *p++; 55 else if (c == '"') 56 continue; 57 *q++ = c; 58 } while (c != '\0'); 59 } 60 /* 61 ** CAPITALIZE -- return a copy of a string, properly capitalized. 62 ** 63 ** Parameters: 64 ** s -- the string to capitalize. 65 ** 66 ** Returns: 67 ** a pointer to a properly capitalized string. 68 ** 69 ** Side Effects: 70 ** none. 71 */ 72 73 char * 74 capitalize(s) 75 register char *s; 76 { 77 static char buf[50]; 78 register char *p; 79 80 p = buf; 81 82 for (;;) 83 { 84 while (!isalpha(*s) && *s != '\0') 85 *p++ = *s++; 86 if (*s == '\0') 87 break; 88 *p++ = toupper(*s); 89 s++; 90 while (isalpha(*s)) 91 *p++ = *s++; 92 } 93 94 *p = '\0'; 95 return (buf); 96 } 97 /* 98 ** XALLOC -- Allocate memory and bitch wildly on failure. 99 ** 100 ** THIS IS A CLUDGE. This should be made to give a proper 101 ** error -- but after all, what can we do? 102 ** 103 ** Parameters: 104 ** sz -- size of area to allocate. 105 ** 106 ** Returns: 107 ** pointer to data region. 108 ** 109 ** Side Effects: 110 ** Memory is allocated. 111 */ 112 113 char * 114 xalloc(sz) 115 register int sz; 116 { 117 register char *p; 118 extern char *malloc(); 119 120 p = malloc((unsigned) sz); 121 if (p == NULL) 122 { 123 syserr("Out of memory!!"); 124 abort(); 125 /* exit(EX_UNAVAILABLE); */ 126 } 127 return (p); 128 } 129 /* 130 ** COPYPLIST -- copy list of pointers. 131 ** 132 ** This routine is the equivalent of newstr for lists of 133 ** pointers. 134 ** 135 ** Parameters: 136 ** list -- list of pointers to copy. 137 ** Must be NULL terminated. 138 ** copycont -- if TRUE, copy the contents of the vector 139 ** (which must be a string) also. 140 ** 141 ** Returns: 142 ** a copy of 'list'. 143 ** 144 ** Side Effects: 145 ** none. 146 */ 147 148 char ** 149 copyplist(list, copycont) 150 char **list; 151 bool copycont; 152 { 153 register char **vp; 154 register char **newvp; 155 156 for (vp = list; *vp != NULL; vp++) 157 continue; 158 159 vp++; 160 161 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 162 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 163 164 if (copycont) 165 { 166 for (vp = newvp; *vp != NULL; vp++) 167 *vp = newstr(*vp); 168 } 169 170 return (newvp); 171 } 172 /* 173 ** PRINTAV -- print argument vector. 174 ** 175 ** Parameters: 176 ** av -- argument vector. 177 ** 178 ** Returns: 179 ** none. 180 ** 181 ** Side Effects: 182 ** prints av. 183 */ 184 185 printav(av) 186 register char **av; 187 { 188 while (*av != NULL) 189 { 190 if (tTd(0, 44)) 191 printf("\n\t%08x=", *av); 192 else 193 (void) putchar(' '); 194 xputs(*av++); 195 } 196 (void) putchar('\n'); 197 } 198 /* 199 ** LOWER -- turn letter into lower case. 200 ** 201 ** Parameters: 202 ** c -- character to turn into lower case. 203 ** 204 ** Returns: 205 ** c, in lower case. 206 ** 207 ** Side Effects: 208 ** none. 209 */ 210 211 char 212 lower(c) 213 register char c; 214 { 215 return(isascii(c) && isupper(c) ? tolower(c) : c); 216 } 217 /* 218 ** XPUTS -- put string doing control escapes. 219 ** 220 ** Parameters: 221 ** s -- string to put. 222 ** 223 ** Returns: 224 ** none. 225 ** 226 ** Side Effects: 227 ** output to stdout 228 */ 229 230 xputs(s) 231 register char *s; 232 { 233 register char c; 234 register struct metamac *mp; 235 extern struct metamac MetaMacros[]; 236 237 if (s == NULL) 238 { 239 printf("<null>"); 240 return; 241 } 242 c = *s; 243 if (c == MATCHREPL && isdigit(s[1]) && s[2] == '\0') 244 { 245 printf("$%c", s[1]); 246 return; 247 } 248 for (mp = MetaMacros; mp->metaname != NULL; mp++) 249 { 250 if (mp->metaval == c) 251 { 252 printf("$%c%s", mp->metaname, ++s); 253 return; 254 } 255 } 256 (void) putchar('"'); 257 while ((c = *s++) != '\0') 258 { 259 if (!isascii(c)) 260 { 261 (void) putchar('\\'); 262 c &= 0177; 263 } 264 if (c < 040 || c >= 0177) 265 { 266 switch (c) 267 { 268 case '\n': 269 c = 'n'; 270 break; 271 272 case '\r': 273 c = 'r'; 274 break; 275 276 case '\t': 277 c = 't'; 278 break; 279 280 case '\001': 281 (void) putchar('$'); 282 continue; 283 284 default: 285 (void) putchar('^'); 286 (void) putchar(c ^ 0100); 287 continue; 288 } 289 (void) putchar('\\'); 290 } 291 (void) putchar(c); 292 } 293 (void) putchar('"'); 294 (void) fflush(stdout); 295 } 296 /* 297 ** MAKELOWER -- Translate a line into lower case 298 ** 299 ** Parameters: 300 ** p -- the string to translate. If NULL, return is 301 ** immediate. 302 ** 303 ** Returns: 304 ** none. 305 ** 306 ** Side Effects: 307 ** String pointed to by p is translated to lower case. 308 ** 309 ** Called By: 310 ** parse 311 */ 312 313 makelower(p) 314 register char *p; 315 { 316 register char c; 317 318 if (p == NULL) 319 return; 320 for (; (c = *p) != '\0'; p++) 321 if (isascii(c) && isupper(c)) 322 *p = tolower(c); 323 } 324 /* 325 ** BUILDFNAME -- build full name from gecos style entry. 326 ** 327 ** This routine interprets the strange entry that would appear 328 ** in the GECOS field of the password file. 329 ** 330 ** Parameters: 331 ** p -- name to build. 332 ** login -- the login name of this user (for &). 333 ** buf -- place to put the result. 334 ** 335 ** Returns: 336 ** none. 337 ** 338 ** Side Effects: 339 ** none. 340 */ 341 342 buildfname(gecos, login, buf) 343 register char *gecos; 344 char *login; 345 char *buf; 346 { 347 register char *p; 348 register char *bp = buf; 349 int l; 350 bool quoteit; 351 352 if (*gecos == '*') 353 gecos++; 354 355 /* see if the full name needs to be quoted */ 356 l = 0; 357 quoteit = FALSE; 358 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 359 { 360 if (index("<>()'.", *p) != NULL) 361 quoteit = TRUE; 362 if (*p == '&') 363 l += strlen(login); 364 else 365 l++; 366 } 367 if (quoteit) 368 l += 2; 369 370 /* now fill in buf */ 371 if (quoteit) 372 *bp++ = '"'; 373 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 374 { 375 if (*p == '&') 376 { 377 (void) strcpy(bp, login); 378 *bp = toupper(*bp); 379 while (*bp != '\0') 380 bp++; 381 } 382 else 383 *bp++ = *p; 384 } 385 if (quoteit) 386 *bp++ = '"'; 387 *bp = '\0'; 388 } 389 /* 390 ** SAFEFILE -- return true if a file exists and is safe for a user. 391 ** 392 ** Parameters: 393 ** fn -- filename to check. 394 ** uid -- uid to compare against. 395 ** mode -- mode bits that must match. 396 ** 397 ** Returns: 398 ** TRUE if fn exists, is owned by uid, and matches mode. 399 ** FALSE otherwise. 400 ** 401 ** Side Effects: 402 ** none. 403 */ 404 405 bool 406 safefile(fn, uid, mode) 407 char *fn; 408 uid_t uid; 409 int mode; 410 { 411 struct stat stbuf; 412 413 if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 414 (stbuf.st_mode & mode) == mode) 415 return (TRUE); 416 errno = 0; 417 return (FALSE); 418 } 419 /* 420 ** FIXCRLF -- fix <CR><LF> in line. 421 ** 422 ** Looks for the <CR><LF> combination and turns it into the 423 ** UNIX canonical <NL> character. It only takes one line, 424 ** i.e., it is assumed that the first <NL> found is the end 425 ** of the line. 426 ** 427 ** Parameters: 428 ** line -- the line to fix. 429 ** stripnl -- if true, strip the newline also. 430 ** 431 ** Returns: 432 ** none. 433 ** 434 ** Side Effects: 435 ** line is changed in place. 436 */ 437 438 fixcrlf(line, stripnl) 439 char *line; 440 bool stripnl; 441 { 442 register char *p; 443 444 p = index(line, '\n'); 445 if (p == NULL) 446 return; 447 if (p > line && p[-1] == '\r') 448 p--; 449 if (!stripnl) 450 *p++ = '\n'; 451 *p = '\0'; 452 } 453 /* 454 ** DFOPEN -- determined file open 455 ** 456 ** This routine has the semantics of fopen, except that it will 457 ** keep trying a few times to make this happen. The idea is that 458 ** on very loaded systems, we may run out of resources (inodes, 459 ** whatever), so this tries to get around it. 460 */ 461 462 FILE * 463 dfopen(filename, mode) 464 char *filename; 465 char *mode; 466 { 467 register int tries; 468 register FILE *fp; 469 470 for (tries = 0; tries < 10; tries++) 471 { 472 sleep((unsigned) (10 * tries)); 473 errno = 0; 474 fp = fopen(filename, mode); 475 if (fp != NULL) 476 break; 477 if (errno != ENFILE && errno != EINTR) 478 break; 479 } 480 errno = 0; 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 = index(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 ** 587 ** Returns: 588 ** NULL on error (including timeout). This will also leave 589 ** buf containing a null string. 590 ** buf otherwise. 591 ** 592 ** Side Effects: 593 ** none. 594 */ 595 596 static jmp_buf CtxReadTimeout; 597 598 char * 599 sfgets(buf, siz, fp) 600 char *buf; 601 int siz; 602 FILE *fp; 603 { 604 register EVENT *ev = NULL; 605 register char *p; 606 static int readtimeout(); 607 608 /* set the timeout */ 609 if (ReadTimeout != 0) 610 { 611 if (setjmp(CtxReadTimeout) != 0) 612 { 613 # ifdef LOG 614 syslog(LOG_NOTICE, 615 "timeout waiting for input from %s\n", 616 RealHostName? RealHostName: "local"); 617 # endif 618 errno = 0; 619 usrerr("451 timeout waiting for input"); 620 buf[0] = '\0'; 621 return (NULL); 622 } 623 ev = setevent((time_t) ReadTimeout, readtimeout, 0); 624 } 625 626 /* try to read */ 627 p = NULL; 628 while (p == NULL && !feof(fp) && !ferror(fp)) 629 { 630 errno = 0; 631 p = fgets(buf, siz, fp); 632 if (errno == EINTR) 633 clearerr(fp); 634 } 635 636 /* clear the event if it has not sprung */ 637 clrevent(ev); 638 639 /* clean up the books and exit */ 640 LineNumber++; 641 if (p == NULL) 642 { 643 buf[0] = '\0'; 644 return (NULL); 645 } 646 if (!EightBit) 647 for (p = buf; *p != '\0'; p++) 648 *p &= ~0200; 649 return (buf); 650 } 651 652 static 653 readtimeout() 654 { 655 longjmp(CtxReadTimeout, 1); 656 } 657 /* 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 674 char * 675 fgetfolded(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--; 684 while ((i = getc(f)) != EOF) 685 { 686 if (i == '\r') 687 { 688 i = getc(f); 689 if (i != '\n') 690 { 691 if (i != EOF) 692 (void) ungetc(i, f); 693 i = '\r'; 694 } 695 } 696 if (--n > 0) 697 *p++ = i; 698 else if (n == 0) 699 nmessage(Arpa_Info, "warning: line truncated"); 700 if (i == '\n') 701 { 702 LineNumber++; 703 i = getc(f); 704 if (i != EOF) 705 (void) ungetc(i, f); 706 if (i != ' ' && i != '\t') 707 break; 708 } 709 } 710 if (p == buf) 711 return (NULL); 712 *--p = '\0'; 713 return (buf); 714 } 715 /* 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 728 time_t 729 curtime() 730 { 731 auto time_t t; 732 733 (void) time(&t); 734 return (t); 735 } 736 /* 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 752 bool 753 atobool(s) 754 register char *s; 755 { 756 if (*s == '\0' || index("tTyY", *s) != NULL) 757 return (TRUE); 758 return (FALSE); 759 } 760 /* 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 774 atooct(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 } 783 /* 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 797 waitfor(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 /* 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 826 bool 827 bitintersect(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 /* 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 852 bool 853 bitzerop(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 } 863