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