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