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.27 (Berkeley) 07/12/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 while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 374 { 375 if (*p == '&') 376 { 377 (void) strcpy(bp, login); 378 *bp = toupper(*bp); 379 while (*bp != '\0') 380 bp++; 381 p++; 382 } 383 else 384 *bp++ = *p++; 385 } 386 if (quoteit) 387 *bp++ = '"'; 388 *bp = '\0'; 389 } 390 /* 391 ** SAFEFILE -- return true if a file exists and is safe for a user. 392 ** 393 ** Parameters: 394 ** fn -- filename to check. 395 ** uid -- uid to compare against. 396 ** mode -- mode bits that must match. 397 ** 398 ** Returns: 399 ** TRUE if fn exists, is owned by uid, and matches mode. 400 ** FALSE otherwise. 401 ** 402 ** Side Effects: 403 ** none. 404 */ 405 406 bool 407 safefile(fn, uid, mode) 408 char *fn; 409 int uid; 410 int mode; 411 { 412 struct stat stbuf; 413 414 if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 415 (stbuf.st_mode & mode) == mode) 416 return (TRUE); 417 errno = 0; 418 return (FALSE); 419 } 420 /* 421 ** FIXCRLF -- fix <CR><LF> in line. 422 ** 423 ** Looks for the <CR><LF> combination and turns it into the 424 ** UNIX canonical <NL> character. It only takes one line, 425 ** i.e., it is assumed that the first <NL> found is the end 426 ** of the line. 427 ** 428 ** Parameters: 429 ** line -- the line to fix. 430 ** stripnl -- if true, strip the newline also. 431 ** 432 ** Returns: 433 ** none. 434 ** 435 ** Side Effects: 436 ** line is changed in place. 437 */ 438 439 fixcrlf(line, stripnl) 440 char *line; 441 bool stripnl; 442 { 443 register char *p; 444 445 p = index(line, '\n'); 446 if (p == NULL) 447 return; 448 if (p > line && p[-1] == '\r') 449 p--; 450 if (!stripnl) 451 *p++ = '\n'; 452 *p = '\0'; 453 } 454 /* 455 ** DFOPEN -- determined file open 456 ** 457 ** This routine has the semantics of fopen, except that it will 458 ** keep trying a few times to make this happen. The idea is that 459 ** on very loaded systems, we may run out of resources (inodes, 460 ** whatever), so this tries to get around it. 461 */ 462 463 FILE * 464 dfopen(filename, mode) 465 char *filename; 466 char *mode; 467 { 468 register int tries; 469 register FILE *fp; 470 471 for (tries = 0; tries < 10; tries++) 472 { 473 sleep((unsigned) (10 * tries)); 474 errno = 0; 475 fp = fopen(filename, mode); 476 if (fp != NULL) 477 break; 478 if (errno != ENFILE && errno != EINTR) 479 break; 480 } 481 errno = 0; 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 = index(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 ** buf on success, NULL on error or EOF. 668 ** 669 ** Side Effects: 670 ** buf gets lines from f, with continuation lines (lines 671 ** with leading white space) appended. CRLF's are mapped 672 ** into single newlines. Any trailing NL is stripped. 673 */ 674 675 char * 676 fgetfolded(buf, n, f) 677 char *buf; 678 register int n; 679 FILE *f; 680 { 681 register char *p = buf; 682 register int i; 683 684 n--; 685 while ((i = getc(f)) != EOF) 686 { 687 if (i == '\r') 688 { 689 i = getc(f); 690 if (i != '\n') 691 { 692 if (i != EOF) 693 (void) ungetc(i, f); 694 i = '\r'; 695 } 696 } 697 if (--n > 0) 698 *p++ = i; 699 else if (n == 0) 700 nmessage(Arpa_Info, "warning: line truncated"); 701 if (i == '\n') 702 { 703 LineNumber++; 704 i = getc(f); 705 if (i != EOF) 706 (void) ungetc(i, f); 707 if (i != ' ' && i != '\t') 708 break; 709 } 710 } 711 if (p == buf) 712 return (NULL); 713 *--p = '\0'; 714 return (buf); 715 } 716 /* 717 ** CURTIME -- return current time. 718 ** 719 ** Parameters: 720 ** none. 721 ** 722 ** Returns: 723 ** the current time. 724 ** 725 ** Side Effects: 726 ** none. 727 */ 728 729 time_t 730 curtime() 731 { 732 auto time_t t; 733 734 (void) time(&t); 735 return (t); 736 } 737 /* 738 ** ATOBOOL -- convert a string representation to boolean. 739 ** 740 ** Defaults to "TRUE" 741 ** 742 ** Parameters: 743 ** s -- string to convert. Takes "tTyY" as true, 744 ** others as false. 745 ** 746 ** Returns: 747 ** A boolean representation of the string. 748 ** 749 ** Side Effects: 750 ** none. 751 */ 752 753 bool 754 atobool(s) 755 register char *s; 756 { 757 if (*s == '\0' || index("tTyY", *s) != NULL) 758 return (TRUE); 759 return (FALSE); 760 } 761 /* 762 ** ATOOCT -- convert a string representation to octal. 763 ** 764 ** Parameters: 765 ** s -- string to convert. 766 ** 767 ** Returns: 768 ** An integer representing the string interpreted as an 769 ** octal number. 770 ** 771 ** Side Effects: 772 ** none. 773 */ 774 775 atooct(s) 776 register char *s; 777 { 778 register int i = 0; 779 780 while (*s >= '0' && *s <= '7') 781 i = (i << 3) | (*s++ - '0'); 782 return (i); 783 } 784 /* 785 ** WAITFOR -- wait for a particular process id. 786 ** 787 ** Parameters: 788 ** pid -- process id to wait for. 789 ** 790 ** Returns: 791 ** status of pid. 792 ** -1 if pid never shows up. 793 ** 794 ** Side Effects: 795 ** none. 796 */ 797 798 waitfor(pid) 799 int pid; 800 { 801 auto int st; 802 int i; 803 804 do 805 { 806 errno = 0; 807 i = wait(&st); 808 } while ((i >= 0 || errno == EINTR) && i != pid); 809 if (i < 0) 810 st = -1; 811 return (st); 812 } 813 /* 814 ** BITINTERSECT -- tell if two bitmaps intersect 815 ** 816 ** Parameters: 817 ** a, b -- the bitmaps in question 818 ** 819 ** Returns: 820 ** TRUE if they have a non-null intersection 821 ** FALSE otherwise 822 ** 823 ** Side Effects: 824 ** none. 825 */ 826 827 bool 828 bitintersect(a, b) 829 BITMAP a; 830 BITMAP b; 831 { 832 int i; 833 834 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 835 if ((a[i] & b[i]) != 0) 836 return (TRUE); 837 return (FALSE); 838 } 839 /* 840 ** BITZEROP -- tell if a bitmap is all zero 841 ** 842 ** Parameters: 843 ** map -- the bit map to check 844 ** 845 ** Returns: 846 ** TRUE if map is all zero. 847 ** FALSE if there are any bits set in map. 848 ** 849 ** Side Effects: 850 ** none. 851 */ 852 853 bool 854 bitzerop(map) 855 BITMAP map; 856 { 857 int i; 858 859 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 860 if (map[i] != 0) 861 return (FALSE); 862 return (TRUE); 863 } 864