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.14 (Berkeley) 12/05/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 > line && 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 char * 576 sfgets(buf, siz, fp) 577 char *buf; 578 int siz; 579 FILE *fp; 580 { 581 register EVENT *ev = NULL; 582 register char *p; 583 extern readtimeout(); 584 585 /* set the timeout */ 586 if (ReadTimeout != 0) 587 { 588 if (setjmp(CtxReadTimeout) != 0) 589 { 590 # ifdef LOG 591 syslog(LOG_NOTICE, 592 "timeout waiting for input from %s\n", 593 RealHostName); 594 # endif 595 errno = 0; 596 usrerr("timeout waiting for input"); 597 buf[0] = '\0'; 598 return (NULL); 599 } 600 ev = setevent((time_t) ReadTimeout, readtimeout, 0); 601 } 602 603 /* try to read */ 604 p = NULL; 605 while (p == NULL && !feof(fp) && !ferror(fp)) 606 { 607 errno = 0; 608 p = fgets(buf, siz, fp); 609 if (errno == EINTR) 610 clearerr(fp); 611 } 612 613 /* clear the event if it has not sprung */ 614 clrevent(ev); 615 616 /* clean up the books and exit */ 617 LineNumber++; 618 if (p == NULL) 619 { 620 buf[0] = '\0'; 621 return (NULL); 622 } 623 for (p = buf; *p != '\0'; p++) 624 *p &= ~0200; 625 return (buf); 626 } 627 628 static 629 readtimeout() 630 { 631 longjmp(CtxReadTimeout, 1); 632 } 633 /* 634 ** FGETFOLDED -- like fgets, but know about folded lines. 635 ** 636 ** Parameters: 637 ** buf -- place to put result. 638 ** n -- bytes available. 639 ** f -- file to read from. 640 ** 641 ** Returns: 642 ** buf on success, NULL on error or EOF. 643 ** 644 ** Side Effects: 645 ** buf gets lines from f, with continuation lines (lines 646 ** with leading white space) appended. CRLF's are mapped 647 ** into single newlines. Any trailing NL is stripped. 648 */ 649 650 char * 651 fgetfolded(buf, n, f) 652 char *buf; 653 register int n; 654 FILE *f; 655 { 656 register char *p = buf; 657 register int i; 658 659 n--; 660 while ((i = getc(f)) != EOF) 661 { 662 if (i == '\r') 663 { 664 i = getc(f); 665 if (i != '\n') 666 { 667 if (i != EOF) 668 (void) ungetc(i, f); 669 i = '\r'; 670 } 671 } 672 if (--n > 0) 673 *p++ = i; 674 if (i == '\n') 675 { 676 LineNumber++; 677 i = getc(f); 678 if (i != EOF) 679 (void) ungetc(i, f); 680 if (i != ' ' && i != '\t') 681 { 682 *--p = '\0'; 683 return (buf); 684 } 685 } 686 } 687 return (NULL); 688 } 689 /* 690 ** CURTIME -- return current time. 691 ** 692 ** Parameters: 693 ** none. 694 ** 695 ** Returns: 696 ** the current time. 697 ** 698 ** Side Effects: 699 ** none. 700 */ 701 702 time_t 703 curtime() 704 { 705 auto time_t t; 706 707 (void) time(&t); 708 return (t); 709 } 710 /* 711 ** ATOBOOL -- convert a string representation to boolean. 712 ** 713 ** Defaults to "TRUE" 714 ** 715 ** Parameters: 716 ** s -- string to convert. Takes "tTyY" as true, 717 ** others as false. 718 ** 719 ** Returns: 720 ** A boolean representation of the string. 721 ** 722 ** Side Effects: 723 ** none. 724 */ 725 726 bool 727 atobool(s) 728 register char *s; 729 { 730 if (*s == '\0' || index("tTyY", *s) != NULL) 731 return (TRUE); 732 return (FALSE); 733 } 734 /* 735 ** ATOOCT -- convert a string representation to octal. 736 ** 737 ** Parameters: 738 ** s -- string to convert. 739 ** 740 ** Returns: 741 ** An integer representing the string interpreted as an 742 ** octal number. 743 ** 744 ** Side Effects: 745 ** none. 746 */ 747 748 atooct(s) 749 register char *s; 750 { 751 register int i = 0; 752 753 while (*s >= '0' && *s <= '7') 754 i = (i << 3) | (*s++ - '0'); 755 return (i); 756 } 757 /* 758 ** WAITFOR -- wait for a particular process id. 759 ** 760 ** Parameters: 761 ** pid -- process id to wait for. 762 ** 763 ** Returns: 764 ** status of pid. 765 ** -1 if pid never shows up. 766 ** 767 ** Side Effects: 768 ** none. 769 */ 770 771 waitfor(pid) 772 int pid; 773 { 774 auto int st; 775 int i; 776 777 do 778 { 779 errno = 0; 780 i = wait(&st); 781 } while ((i >= 0 || errno == EINTR) && i != pid); 782 if (i < 0) 783 st = -1; 784 return (st); 785 } 786 /* 787 ** BITINTERSECT -- tell if two bitmaps intersect 788 ** 789 ** Parameters: 790 ** a, b -- the bitmaps in question 791 ** 792 ** Returns: 793 ** TRUE if they have a non-null intersection 794 ** FALSE otherwise 795 ** 796 ** Side Effects: 797 ** none. 798 */ 799 800 bool 801 bitintersect(a, b) 802 BITMAP a; 803 BITMAP b; 804 { 805 int i; 806 807 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 808 if ((a[i] & b[i]) != 0) 809 return (TRUE); 810 return (FALSE); 811 } 812 /* 813 ** BITZEROP -- tell if a bitmap is all zero 814 ** 815 ** Parameters: 816 ** map -- the bit map to check 817 ** 818 ** Returns: 819 ** TRUE if map is all zero. 820 ** FALSE if there are any bits set in map. 821 ** 822 ** Side Effects: 823 ** none. 824 */ 825 826 bool 827 bitzerop(map) 828 BITMAP map; 829 { 830 int i; 831 832 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 833 if (map[i] != 0) 834 return (FALSE); 835 return (TRUE); 836 } 837