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