1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 * 12 * Sendmail 13 * Copyright (c) 1983 Eric P. Allman 14 * Berkeley, California 15 */ 16 17 #ifndef lint 18 static char sccsid[] = "@(#)util.c 5.11 (Berkeley) 03/13/88"; 19 #endif /* not lint */ 20 21 # include <stdio.h> 22 # include <sys/types.h> 23 # include <sys/stat.h> 24 # include <sysexits.h> 25 # include <errno.h> 26 # include "sendmail.h" 27 28 /* 29 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 30 ** 31 ** Runs through a string and strips off unquoted quote 32 ** characters and quote bits. This is done in place. 33 ** 34 ** Parameters: 35 ** s -- the string to strip. 36 ** qf -- if set, remove actual `` " '' characters 37 ** as well as the quote bits. 38 ** 39 ** Returns: 40 ** none. 41 ** 42 ** Side Effects: 43 ** none. 44 ** 45 ** Called By: 46 ** deliver 47 */ 48 49 stripquotes(s, qf) 50 char *s; 51 bool qf; 52 { 53 register char *p; 54 register char *q; 55 register char c; 56 57 if (s == NULL) 58 return; 59 60 for (p = q = s; (c = *p++) != '\0'; ) 61 { 62 if (c != '"' || !qf) 63 *q++ = c & 0177; 64 } 65 *q = '\0'; 66 } 67 /* 68 ** QSTRLEN -- give me the string length assuming 0200 bits add a char 69 ** 70 ** Parameters: 71 ** s -- the string to measure. 72 ** 73 ** Reurns: 74 ** The length of s, including space for backslash escapes. 75 ** 76 ** Side Effects: 77 ** none. 78 */ 79 80 qstrlen(s) 81 register char *s; 82 { 83 register int l = 0; 84 register char c; 85 86 while ((c = *s++) != '\0') 87 { 88 if (bitset(0200, c)) 89 l++; 90 l++; 91 } 92 return (l); 93 } 94 /* 95 ** CAPITALIZE -- return a copy of a string, properly capitalized. 96 ** 97 ** Parameters: 98 ** s -- the string to capitalize. 99 ** 100 ** Returns: 101 ** a pointer to a properly capitalized string. 102 ** 103 ** Side Effects: 104 ** none. 105 */ 106 107 char * 108 capitalize(s) 109 register char *s; 110 { 111 static char buf[50]; 112 register char *p; 113 114 p = buf; 115 116 for (;;) 117 { 118 while (!isalpha(*s) && *s != '\0') 119 *p++ = *s++; 120 if (*s == '\0') 121 break; 122 *p++ = toupper(*s++); 123 while (isalpha(*s)) 124 *p++ = *s++; 125 } 126 127 *p = '\0'; 128 return (buf); 129 } 130 /* 131 ** XALLOC -- Allocate memory and bitch wildly on failure. 132 ** 133 ** THIS IS A CLUDGE. This should be made to give a proper 134 ** error -- but after all, what can we do? 135 ** 136 ** Parameters: 137 ** sz -- size of area to allocate. 138 ** 139 ** Returns: 140 ** pointer to data region. 141 ** 142 ** Side Effects: 143 ** Memory is allocated. 144 */ 145 146 char * 147 xalloc(sz) 148 register int sz; 149 { 150 register char *p; 151 extern char *malloc(); 152 153 p = malloc((unsigned) sz); 154 if (p == NULL) 155 { 156 syserr("Out of memory!!"); 157 abort(); 158 /* exit(EX_UNAVAILABLE); */ 159 } 160 return (p); 161 } 162 /* 163 ** COPYPLIST -- copy list of pointers. 164 ** 165 ** This routine is the equivalent of newstr for lists of 166 ** pointers. 167 ** 168 ** Parameters: 169 ** list -- list of pointers to copy. 170 ** Must be NULL terminated. 171 ** copycont -- if TRUE, copy the contents of the vector 172 ** (which must be a string) also. 173 ** 174 ** Returns: 175 ** a copy of 'list'. 176 ** 177 ** Side Effects: 178 ** none. 179 */ 180 181 char ** 182 copyplist(list, copycont) 183 char **list; 184 bool copycont; 185 { 186 register char **vp; 187 register char **newvp; 188 189 for (vp = list; *vp != NULL; vp++) 190 continue; 191 192 vp++; 193 194 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 195 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 196 197 if (copycont) 198 { 199 for (vp = newvp; *vp != NULL; vp++) 200 *vp = newstr(*vp); 201 } 202 203 return (newvp); 204 } 205 /* 206 ** PRINTAV -- print argument vector. 207 ** 208 ** Parameters: 209 ** av -- argument vector. 210 ** 211 ** Returns: 212 ** none. 213 ** 214 ** Side Effects: 215 ** prints av. 216 */ 217 218 printav(av) 219 register char **av; 220 { 221 while (*av != NULL) 222 { 223 if (tTd(0, 44)) 224 printf("\n\t%08x=", *av); 225 else 226 (void) putchar(' '); 227 xputs(*av++); 228 } 229 (void) putchar('\n'); 230 } 231 /* 232 ** LOWER -- turn letter into lower case. 233 ** 234 ** Parameters: 235 ** c -- character to turn into lower case. 236 ** 237 ** Returns: 238 ** c, in lower case. 239 ** 240 ** Side Effects: 241 ** none. 242 */ 243 244 char 245 lower(c) 246 register char c; 247 { 248 return(isascii(c) && isupper(c) ? tolower(c) : c); 249 } 250 /* 251 ** XPUTS -- put string doing control escapes. 252 ** 253 ** Parameters: 254 ** s -- string to put. 255 ** 256 ** Returns: 257 ** none. 258 ** 259 ** Side Effects: 260 ** output to stdout 261 */ 262 263 xputs(s) 264 register char *s; 265 { 266 register char c; 267 268 if (s == NULL) 269 { 270 printf("<null>"); 271 return; 272 } 273 (void) putchar('"'); 274 while ((c = *s++) != '\0') 275 { 276 if (!isascii(c)) 277 { 278 (void) putchar('\\'); 279 c &= 0177; 280 } 281 if (c < 040 || c >= 0177) 282 { 283 (void) putchar('^'); 284 c ^= 0100; 285 } 286 (void) putchar(c); 287 } 288 (void) putchar('"'); 289 (void) fflush(stdout); 290 } 291 /* 292 ** MAKELOWER -- Translate a line into lower case 293 ** 294 ** Parameters: 295 ** p -- the string to translate. If NULL, return is 296 ** immediate. 297 ** 298 ** Returns: 299 ** none. 300 ** 301 ** Side Effects: 302 ** String pointed to by p is translated to lower case. 303 ** 304 ** Called By: 305 ** parse 306 */ 307 308 makelower(p) 309 register char *p; 310 { 311 register char c; 312 313 if (p == NULL) 314 return; 315 for (; (c = *p) != '\0'; p++) 316 if (isascii(c) && isupper(c)) 317 *p = tolower(c); 318 } 319 /* 320 ** BUILDFNAME -- build full name from gecos style entry. 321 ** 322 ** This routine interprets the strange entry that would appear 323 ** in the GECOS field of the password file. 324 ** 325 ** Parameters: 326 ** p -- name to build. 327 ** login -- the login name of this user (for &). 328 ** buf -- place to put the result. 329 ** 330 ** Returns: 331 ** none. 332 ** 333 ** Side Effects: 334 ** none. 335 */ 336 337 buildfname(p, login, buf) 338 register char *p; 339 char *login; 340 char *buf; 341 { 342 register char *bp = buf; 343 344 if (*p == '*') 345 p++; 346 while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 347 { 348 if (*p == '&') 349 { 350 (void) strcpy(bp, login); 351 *bp = toupper(*bp); 352 while (*bp != '\0') 353 bp++; 354 p++; 355 } 356 else 357 *bp++ = *p++; 358 } 359 *bp = '\0'; 360 } 361 /* 362 ** SAFEFILE -- return true if a file exists and is safe for a user. 363 ** 364 ** Parameters: 365 ** fn -- filename to check. 366 ** uid -- uid to compare against. 367 ** mode -- mode bits that must match. 368 ** 369 ** Returns: 370 ** TRUE if fn exists, is owned by uid, and matches mode. 371 ** FALSE otherwise. 372 ** 373 ** Side Effects: 374 ** none. 375 */ 376 377 bool 378 safefile(fn, uid, mode) 379 char *fn; 380 int uid; 381 int mode; 382 { 383 struct stat stbuf; 384 385 if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 386 (stbuf.st_mode & mode) == mode) 387 return (TRUE); 388 errno = 0; 389 return (FALSE); 390 } 391 /* 392 ** FIXCRLF -- fix <CR><LF> in line. 393 ** 394 ** Looks for the <CR><LF> combination and turns it into the 395 ** UNIX canonical <NL> character. It only takes one line, 396 ** i.e., it is assumed that the first <NL> found is the end 397 ** of the line. 398 ** 399 ** Parameters: 400 ** line -- the line to fix. 401 ** stripnl -- if true, strip the newline also. 402 ** 403 ** Returns: 404 ** none. 405 ** 406 ** Side Effects: 407 ** line is changed in place. 408 */ 409 410 fixcrlf(line, stripnl) 411 char *line; 412 bool stripnl; 413 { 414 register char *p; 415 416 p = index(line, '\n'); 417 if (p == NULL) 418 return; 419 if (p[-1] == '\r') 420 p--; 421 if (!stripnl) 422 *p++ = '\n'; 423 *p = '\0'; 424 } 425 /* 426 ** DFOPEN -- determined file open 427 ** 428 ** This routine has the semantics of fopen, except that it will 429 ** keep trying a few times to make this happen. The idea is that 430 ** on very loaded systems, we may run out of resources (inodes, 431 ** whatever), so this tries to get around it. 432 */ 433 434 FILE * 435 dfopen(filename, mode) 436 char *filename; 437 char *mode; 438 { 439 register int tries; 440 register FILE *fp; 441 442 for (tries = 0; tries < 10; tries++) 443 { 444 sleep((unsigned) (10 * tries)); 445 errno = 0; 446 fp = fopen(filename, mode); 447 if (fp != NULL) 448 break; 449 if (errno != ENFILE && errno != EINTR) 450 break; 451 } 452 errno = 0; 453 return (fp); 454 } 455 /* 456 ** PUTLINE -- put a line like fputs obeying SMTP conventions 457 ** 458 ** This routine always guarantees outputing a newline (or CRLF, 459 ** as appropriate) at the end of the string. 460 ** 461 ** Parameters: 462 ** l -- line to put. 463 ** fp -- file to put it onto. 464 ** m -- the mailer used to control output. 465 ** 466 ** Returns: 467 ** none 468 ** 469 ** Side Effects: 470 ** output of l to fp. 471 */ 472 473 # define SMTPLINELIM 990 /* maximum line length */ 474 475 putline(l, fp, m) 476 register char *l; 477 FILE *fp; 478 MAILER *m; 479 { 480 register char *p; 481 char svchar; 482 483 /* strip out 0200 bits -- these can look like TELNET protocol */ 484 if (bitnset(M_LIMITS, m->m_flags)) 485 { 486 p = l; 487 while ((*p++ &= ~0200) != 0) 488 continue; 489 } 490 491 do 492 { 493 /* find the end of the line */ 494 p = index(l, '\n'); 495 if (p == NULL) 496 p = &l[strlen(l)]; 497 498 /* check for line overflow */ 499 while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags)) 500 { 501 register char *q = &l[SMTPLINELIM - 1]; 502 503 svchar = *q; 504 *q = '\0'; 505 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 506 (void) putc('.', fp); 507 fputs(l, fp); 508 (void) putc('!', fp); 509 fputs(m->m_eol, fp); 510 *q = svchar; 511 l = q; 512 } 513 514 /* output last part */ 515 svchar = *p; 516 *p = '\0'; 517 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 518 (void) putc('.', fp); 519 fputs(l, fp); 520 fputs(m->m_eol, fp); 521 *p = svchar; 522 l = p; 523 if (*l == '\n') 524 l++; 525 } while (l[0] != '\0'); 526 } 527 /* 528 ** XUNLINK -- unlink a file, doing logging as appropriate. 529 ** 530 ** Parameters: 531 ** f -- name of file to unlink. 532 ** 533 ** Returns: 534 ** none. 535 ** 536 ** Side Effects: 537 ** f is unlinked. 538 */ 539 540 xunlink(f) 541 char *f; 542 { 543 register int i; 544 545 # ifdef LOG 546 if (LogLevel > 20) 547 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 548 # endif LOG 549 550 i = unlink(f); 551 # ifdef LOG 552 if (i < 0 && LogLevel > 21) 553 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 554 # endif LOG 555 } 556 /* 557 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 558 ** 559 ** Parameters: 560 ** buf -- place to put the input line. 561 ** siz -- size of buf. 562 ** fp -- file to read from. 563 ** 564 ** Returns: 565 ** NULL on error (including timeout). This will also leave 566 ** buf containing a null string. 567 ** buf otherwise. 568 ** 569 ** Side Effects: 570 ** none. 571 */ 572 573 static jmp_buf CtxReadTimeout; 574 575 #ifndef ETIMEDOUT 576 #define ETIMEDOUT EINTR 577 #endif 578 579 char * 580 sfgets(buf, siz, fp) 581 char *buf; 582 int siz; 583 FILE *fp; 584 { 585 register EVENT *ev = NULL; 586 register char *p; 587 extern readtimeout(); 588 589 /* set the timeout */ 590 if (ReadTimeout != 0) 591 { 592 if (setjmp(CtxReadTimeout) != 0) 593 { 594 errno = ETIMEDOUT; 595 syserr("net timeout"); 596 buf[0] = '\0'; 597 return (NULL); 598 } 599 ev = setevent((time_t) ReadTimeout, readtimeout, 0); 600 } 601 602 /* try to read */ 603 p = NULL; 604 while (p == NULL && !feof(fp) && !ferror(fp)) 605 { 606 errno = 0; 607 p = fgets(buf, siz, fp); 608 if (errno == EINTR) 609 clearerr(fp); 610 } 611 612 /* clear the event if it has not sprung */ 613 clrevent(ev); 614 615 /* clean up the books and exit */ 616 LineNumber++; 617 if (p == NULL) 618 { 619 buf[0] = '\0'; 620 return (NULL); 621 } 622 for (p = buf; *p != '\0'; p++) 623 *p &= ~0200; 624 return (buf); 625 } 626 627 static 628 readtimeout() 629 { 630 longjmp(CtxReadTimeout, 1); 631 } 632 /* 633 ** FGETFOLDED -- like fgets, but know about folded lines. 634 ** 635 ** Parameters: 636 ** buf -- place to put result. 637 ** n -- bytes available. 638 ** f -- file to read from. 639 ** 640 ** Returns: 641 ** buf on success, NULL on error or EOF. 642 ** 643 ** Side Effects: 644 ** buf gets lines from f, with continuation lines (lines 645 ** with leading white space) appended. CRLF's are mapped 646 ** into single newlines. Any trailing NL is stripped. 647 */ 648 649 char * 650 fgetfolded(buf, n, f) 651 char *buf; 652 register int n; 653 FILE *f; 654 { 655 register char *p = buf; 656 register int i; 657 658 n--; 659 while ((i = getc(f)) != EOF) 660 { 661 if (i == '\r') 662 { 663 i = getc(f); 664 if (i != '\n') 665 { 666 if (i != EOF) 667 (void) ungetc(i, f); 668 i = '\r'; 669 } 670 } 671 if (--n > 0) 672 *p++ = i; 673 if (i == '\n') 674 { 675 LineNumber++; 676 i = getc(f); 677 if (i != EOF) 678 (void) ungetc(i, f); 679 if (i != ' ' && i != '\t') 680 { 681 *--p = '\0'; 682 return (buf); 683 } 684 } 685 } 686 return (NULL); 687 } 688 /* 689 ** CURTIME -- return current time. 690 ** 691 ** Parameters: 692 ** none. 693 ** 694 ** Returns: 695 ** the current time. 696 ** 697 ** Side Effects: 698 ** none. 699 */ 700 701 time_t 702 curtime() 703 { 704 auto time_t t; 705 706 (void) time(&t); 707 return (t); 708 } 709 /* 710 ** ATOBOOL -- convert a string representation to boolean. 711 ** 712 ** Defaults to "TRUE" 713 ** 714 ** Parameters: 715 ** s -- string to convert. Takes "tTyY" as true, 716 ** others as false. 717 ** 718 ** Returns: 719 ** A boolean representation of the string. 720 ** 721 ** Side Effects: 722 ** none. 723 */ 724 725 bool 726 atobool(s) 727 register char *s; 728 { 729 if (*s == '\0' || index("tTyY", *s) != NULL) 730 return (TRUE); 731 return (FALSE); 732 } 733 /* 734 ** ATOOCT -- convert a string representation to octal. 735 ** 736 ** Parameters: 737 ** s -- string to convert. 738 ** 739 ** Returns: 740 ** An integer representing the string interpreted as an 741 ** octal number. 742 ** 743 ** Side Effects: 744 ** none. 745 */ 746 747 atooct(s) 748 register char *s; 749 { 750 register int i = 0; 751 752 while (*s >= '0' && *s <= '7') 753 i = (i << 3) | (*s++ - '0'); 754 return (i); 755 } 756 /* 757 ** WAITFOR -- wait for a particular process id. 758 ** 759 ** Parameters: 760 ** pid -- process id to wait for. 761 ** 762 ** Returns: 763 ** status of pid. 764 ** -1 if pid never shows up. 765 ** 766 ** Side Effects: 767 ** none. 768 */ 769 770 waitfor(pid) 771 int pid; 772 { 773 auto int st; 774 int i; 775 776 do 777 { 778 errno = 0; 779 i = wait(&st); 780 } while ((i >= 0 || errno == EINTR) && i != pid); 781 if (i < 0) 782 st = -1; 783 return (st); 784 } 785 /* 786 ** BITINTERSECT -- tell if two bitmaps intersect 787 ** 788 ** Parameters: 789 ** a, b -- the bitmaps in question 790 ** 791 ** Returns: 792 ** TRUE if they have a non-null intersection 793 ** FALSE otherwise 794 ** 795 ** Side Effects: 796 ** none. 797 */ 798 799 bool 800 bitintersect(a, b) 801 BITMAP a; 802 BITMAP b; 803 { 804 int i; 805 806 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 807 if ((a[i] & b[i]) != 0) 808 return (TRUE); 809 return (FALSE); 810 } 811 /* 812 ** BITZEROP -- tell if a bitmap is all zero 813 ** 814 ** Parameters: 815 ** map -- the bit map to check 816 ** 817 ** Returns: 818 ** TRUE if map is all zero. 819 ** FALSE if there are any bits set in map. 820 ** 821 ** Side Effects: 822 ** none. 823 */ 824 825 bool 826 bitzerop(map) 827 BITMAP map; 828 { 829 int i; 830 831 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 832 if (map[i] != 0) 833 return (FALSE); 834 return (TRUE); 835 } 836