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