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