1 /* 2 ** Sendmail 3 ** Copyright (c) 1983 Eric P. Allman 4 ** Berkeley, California 5 ** 6 ** Copyright (c) 1983 Regents of the University of California. 7 ** All rights reserved. The Berkeley software License Agreement 8 ** specifies the terms and conditions for redistribution. 9 */ 10 11 #ifndef lint 12 static char SccsId[] = "@(#)util.c 5.9 (Berkeley) 12/17/86"; 13 #endif not lint 14 15 # include <stdio.h> 16 # include <sys/types.h> 17 # include <sys/stat.h> 18 # include <sysexits.h> 19 # include <errno.h> 20 # include "sendmail.h" 21 22 /* 23 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 24 ** 25 ** Runs through a string and strips off unquoted quote 26 ** characters and quote bits. This is done in place. 27 ** 28 ** Parameters: 29 ** s -- the string to strip. 30 ** qf -- if set, remove actual `` " '' characters 31 ** as well as the quote bits. 32 ** 33 ** Returns: 34 ** none. 35 ** 36 ** Side Effects: 37 ** none. 38 ** 39 ** Called By: 40 ** deliver 41 */ 42 43 stripquotes(s, qf) 44 char *s; 45 bool qf; 46 { 47 register char *p; 48 register char *q; 49 register char c; 50 51 if (s == NULL) 52 return; 53 54 for (p = q = s; (c = *p++) != '\0'; ) 55 { 56 if (c != '"' || !qf) 57 *q++ = c & 0177; 58 } 59 *q = '\0'; 60 } 61 /* 62 ** QSTRLEN -- give me the string length assuming 0200 bits add a char 63 ** 64 ** Parameters: 65 ** s -- the string to measure. 66 ** 67 ** Reurns: 68 ** The length of s, including space for backslash escapes. 69 ** 70 ** Side Effects: 71 ** none. 72 */ 73 74 qstrlen(s) 75 register char *s; 76 { 77 register int l = 0; 78 register char c; 79 80 while ((c = *s++) != '\0') 81 { 82 if (bitset(0200, c)) 83 l++; 84 l++; 85 } 86 return (l); 87 } 88 /* 89 ** CAPITALIZE -- return a copy of a string, properly capitalized. 90 ** 91 ** Parameters: 92 ** s -- the string to capitalize. 93 ** 94 ** Returns: 95 ** a pointer to a properly capitalized string. 96 ** 97 ** Side Effects: 98 ** none. 99 */ 100 101 char * 102 capitalize(s) 103 register char *s; 104 { 105 static char buf[50]; 106 register char *p; 107 108 p = buf; 109 110 for (;;) 111 { 112 while (!isalpha(*s) && *s != '\0') 113 *p++ = *s++; 114 if (*s == '\0') 115 break; 116 *p++ = toupper(*s++); 117 while (isalpha(*s)) 118 *p++ = *s++; 119 } 120 121 *p = '\0'; 122 return (buf); 123 } 124 /* 125 ** XALLOC -- Allocate memory and bitch wildly on failure. 126 ** 127 ** THIS IS A CLUDGE. This should be made to give a proper 128 ** error -- but after all, what can we do? 129 ** 130 ** Parameters: 131 ** sz -- size of area to allocate. 132 ** 133 ** Returns: 134 ** pointer to data region. 135 ** 136 ** Side Effects: 137 ** Memory is allocated. 138 */ 139 140 char * 141 xalloc(sz) 142 register int sz; 143 { 144 register char *p; 145 extern char *malloc(); 146 147 p = malloc((unsigned) sz); 148 if (p == NULL) 149 { 150 syserr("Out of memory!!"); 151 abort(); 152 /* exit(EX_UNAVAILABLE); */ 153 } 154 return (p); 155 } 156 /* 157 ** COPYPLIST -- copy list of pointers. 158 ** 159 ** This routine is the equivalent of newstr for lists of 160 ** pointers. 161 ** 162 ** Parameters: 163 ** list -- list of pointers to copy. 164 ** Must be NULL terminated. 165 ** copycont -- if TRUE, copy the contents of the vector 166 ** (which must be a string) also. 167 ** 168 ** Returns: 169 ** a copy of 'list'. 170 ** 171 ** Side Effects: 172 ** none. 173 */ 174 175 char ** 176 copyplist(list, copycont) 177 char **list; 178 bool copycont; 179 { 180 register char **vp; 181 register char **newvp; 182 183 for (vp = list; *vp != NULL; vp++) 184 continue; 185 186 vp++; 187 188 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 189 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 190 191 if (copycont) 192 { 193 for (vp = newvp; *vp != NULL; vp++) 194 *vp = newstr(*vp); 195 } 196 197 return (newvp); 198 } 199 /* 200 ** PRINTAV -- print argument vector. 201 ** 202 ** Parameters: 203 ** av -- argument vector. 204 ** 205 ** Returns: 206 ** none. 207 ** 208 ** Side Effects: 209 ** prints av. 210 */ 211 212 printav(av) 213 register char **av; 214 { 215 while (*av != NULL) 216 { 217 if (tTd(0, 44)) 218 printf("\n\t%08x=", *av); 219 else 220 (void) putchar(' '); 221 xputs(*av++); 222 } 223 (void) putchar('\n'); 224 } 225 /* 226 ** LOWER -- turn letter into lower case. 227 ** 228 ** Parameters: 229 ** c -- character to turn into lower case. 230 ** 231 ** Returns: 232 ** c, in lower case. 233 ** 234 ** Side Effects: 235 ** none. 236 */ 237 238 char 239 lower(c) 240 register char c; 241 { 242 if (isascii(c) && isupper(c)) 243 c = c - 'A' + 'a'; 244 return (c); 245 } 246 /* 247 ** XPUTS -- put string doing control escapes. 248 ** 249 ** Parameters: 250 ** s -- string to put. 251 ** 252 ** Returns: 253 ** none. 254 ** 255 ** Side Effects: 256 ** output to stdout 257 */ 258 259 xputs(s) 260 register char *s; 261 { 262 register char c; 263 264 if (s == NULL) 265 { 266 printf("<null>"); 267 return; 268 } 269 (void) putchar('"'); 270 while ((c = *s++) != '\0') 271 { 272 if (!isascii(c)) 273 { 274 (void) putchar('\\'); 275 c &= 0177; 276 } 277 if (c < 040 || c >= 0177) 278 { 279 (void) putchar('^'); 280 c ^= 0100; 281 } 282 (void) putchar(c); 283 } 284 (void) putchar('"'); 285 (void) fflush(stdout); 286 } 287 /* 288 ** MAKELOWER -- Translate a line into lower case 289 ** 290 ** Parameters: 291 ** p -- the string to translate. If NULL, return is 292 ** immediate. 293 ** 294 ** Returns: 295 ** none. 296 ** 297 ** Side Effects: 298 ** String pointed to by p is translated to lower case. 299 ** 300 ** Called By: 301 ** parse 302 */ 303 304 makelower(p) 305 register char *p; 306 { 307 register char c; 308 309 if (p == NULL) 310 return; 311 for (; (c = *p) != '\0'; p++) 312 if (isascii(c) && isupper(c)) 313 *p = c - 'A' + 'a'; 314 } 315 /* 316 ** SAMEWORD -- return TRUE if the words are the same 317 ** 318 ** Ignores case. 319 ** 320 ** Parameters: 321 ** a, b -- the words to compare. 322 ** 323 ** Returns: 324 ** TRUE if a & b match exactly (modulo case) 325 ** FALSE otherwise. 326 ** 327 ** Side Effects: 328 ** none. 329 */ 330 331 bool 332 sameword(a, b) 333 register char *a, *b; 334 { 335 char ca, cb; 336 337 do 338 { 339 ca = *a++; 340 cb = *b++; 341 if (isascii(ca) && isupper(ca)) 342 ca = ca - 'A' + 'a'; 343 if (isascii(cb) && isupper(cb)) 344 cb = cb - 'A' + 'a'; 345 } while (ca != '\0' && ca == cb); 346 return (ca == cb); 347 } 348 /* 349 ** BUILDFNAME -- build full name from gecos style entry. 350 ** 351 ** This routine interprets the strange entry that would appear 352 ** in the GECOS field of the password file. 353 ** 354 ** Parameters: 355 ** p -- name to build. 356 ** login -- the login name of this user (for &). 357 ** buf -- place to put the result. 358 ** 359 ** Returns: 360 ** none. 361 ** 362 ** Side Effects: 363 ** none. 364 */ 365 366 buildfname(p, login, buf) 367 register char *p; 368 char *login; 369 char *buf; 370 { 371 register char *bp = buf; 372 373 if (*p == '*') 374 p++; 375 while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 376 { 377 if (*p == '&') 378 { 379 (void) strcpy(bp, login); 380 *bp = toupper(*bp); 381 while (*bp != '\0') 382 bp++; 383 p++; 384 } 385 else 386 *bp++ = *p++; 387 } 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[-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 # define SMTPLINELIM 990 /* maximum line length */ 503 504 putline(l, fp, m) 505 register char *l; 506 FILE *fp; 507 MAILER *m; 508 { 509 register char *p; 510 char svchar; 511 512 /* strip out 0200 bits -- these can look like TELNET protocol */ 513 if (bitnset(M_LIMITS, m->m_flags)) 514 { 515 p = l; 516 while ((*p++ &= ~0200) != 0) 517 continue; 518 } 519 520 do 521 { 522 /* find the end of the line */ 523 p = index(l, '\n'); 524 if (p == NULL) 525 p = &l[strlen(l)]; 526 527 /* check for line overflow */ 528 while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags)) 529 { 530 register char *q = &l[SMTPLINELIM - 1]; 531 532 svchar = *q; 533 *q = '\0'; 534 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 535 (void) putc('.', fp); 536 fputs(l, fp); 537 (void) putc('!', fp); 538 fputs(m->m_eol, fp); 539 *q = svchar; 540 l = q; 541 } 542 543 /* output last part */ 544 svchar = *p; 545 *p = '\0'; 546 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 547 (void) putc('.', fp); 548 fputs(l, fp); 549 fputs(m->m_eol, fp); 550 *p = svchar; 551 l = p; 552 if (*l == '\n') 553 l++; 554 } while (l[0] != '\0'); 555 } 556 /* 557 ** XUNLINK -- unlink a file, doing logging as appropriate. 558 ** 559 ** Parameters: 560 ** f -- name of file to unlink. 561 ** 562 ** Returns: 563 ** none. 564 ** 565 ** Side Effects: 566 ** f is unlinked. 567 */ 568 569 xunlink(f) 570 char *f; 571 { 572 register int i; 573 574 # ifdef LOG 575 if (LogLevel > 20) 576 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 577 # endif LOG 578 579 i = unlink(f); 580 # ifdef LOG 581 if (i < 0 && LogLevel > 21) 582 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 583 # endif LOG 584 } 585 /* 586 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 587 ** 588 ** Parameters: 589 ** buf -- place to put the input line. 590 ** siz -- size of buf. 591 ** fp -- file to read from. 592 ** 593 ** Returns: 594 ** NULL on error (including timeout). This will also leave 595 ** buf containing a null string. 596 ** buf otherwise. 597 ** 598 ** Side Effects: 599 ** none. 600 */ 601 602 static jmp_buf CtxReadTimeout; 603 604 #ifndef ETIMEDOUT 605 #define ETIMEDOUT EINTR 606 #endif 607 608 char * 609 sfgets(buf, siz, fp) 610 char *buf; 611 int siz; 612 FILE *fp; 613 { 614 register EVENT *ev = NULL; 615 register char *p; 616 extern readtimeout(); 617 618 /* set the timeout */ 619 if (ReadTimeout != 0) 620 { 621 if (setjmp(CtxReadTimeout) != 0) 622 { 623 errno = ETIMEDOUT; 624 syserr("net timeout"); 625 buf[0] = '\0'; 626 return (NULL); 627 } 628 ev = setevent((time_t) ReadTimeout, readtimeout, 0); 629 } 630 631 /* try to read */ 632 p = NULL; 633 while (p == NULL && !feof(fp) && !ferror(fp)) 634 { 635 errno = 0; 636 p = fgets(buf, siz, fp); 637 if (errno == EINTR) 638 clearerr(fp); 639 } 640 641 /* clear the event if it has not sprung */ 642 clrevent(ev); 643 644 /* clean up the books and exit */ 645 LineNumber++; 646 if (p == NULL) 647 { 648 buf[0] = '\0'; 649 return (NULL); 650 } 651 for (p = buf; *p != '\0'; p++) 652 *p &= ~0200; 653 return (buf); 654 } 655 656 static 657 readtimeout() 658 { 659 longjmp(CtxReadTimeout, 1); 660 } 661 /* 662 ** FGETFOLDED -- like fgets, but know about folded lines. 663 ** 664 ** Parameters: 665 ** buf -- place to put result. 666 ** n -- bytes available. 667 ** f -- file to read from. 668 ** 669 ** Returns: 670 ** buf on success, NULL on error or EOF. 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 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 *p++ = i; 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 { 710 *--p = '\0'; 711 return (buf); 712 } 713 } 714 } 715 return (NULL); 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' || index("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