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 3.44 04/17/83); 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((vp - list) * sizeof *vp); 177 bmove((char *) list, (char *) newvp, (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 while (lower(*a) == lower(*b)) 328 { 329 if (*a == '\0') 330 return (TRUE); 331 a++; 332 b++; 333 } 334 return (FALSE); 335 } 336 /* 337 ** CLEAR -- clear a block of memory 338 ** 339 ** Parameters: 340 ** p -- location to clear. 341 ** l -- number of bytes to clear. 342 ** 343 ** Returns: 344 ** none. 345 ** 346 ** Side Effects: 347 ** none. 348 */ 349 350 clear(p, l) 351 register char *p; 352 register int l; 353 { 354 while (l-- > 0) 355 *p++ = 0; 356 } 357 /* 358 ** BUILDFNAME -- build full name from gecos style entry. 359 ** 360 ** This routine interprets the strange entry that would appear 361 ** in the GECOS field of the password file. 362 ** 363 ** Parameters: 364 ** p -- name to build. 365 ** login -- the login name of this user (for &). 366 ** buf -- place to put the result. 367 ** 368 ** Returns: 369 ** none. 370 ** 371 ** Side Effects: 372 ** none. 373 */ 374 375 buildfname(p, login, buf) 376 register char *p; 377 char *login; 378 char *buf; 379 { 380 register char *bp = buf; 381 382 if (*p == '*') 383 p++; 384 while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 385 { 386 if (*p == '&') 387 { 388 (void) strcpy(bp, login); 389 *bp = toupper(*bp); 390 while (*bp != '\0') 391 bp++; 392 p++; 393 } 394 else 395 *bp++ = *p++; 396 } 397 *bp = '\0'; 398 } 399 /* 400 ** SAFEFILE -- return true if a file exists and is safe for a user. 401 ** 402 ** Parameters: 403 ** fn -- filename to check. 404 ** uid -- uid to compare against. 405 ** mode -- mode bits that must match. 406 ** 407 ** Returns: 408 ** TRUE if fn exists, is owned by uid, and matches mode. 409 ** FALSE otherwise. 410 ** 411 ** Side Effects: 412 ** none. 413 */ 414 415 bool 416 safefile(fn, uid, mode) 417 char *fn; 418 int uid; 419 int mode; 420 { 421 struct stat stbuf; 422 423 if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 424 (stbuf.st_mode & mode) == mode) 425 return (TRUE); 426 errno = 0; 427 return (FALSE); 428 } 429 /* 430 ** FIXCRLF -- fix <CR><LF> in line. 431 ** 432 ** Looks for the <CR><LF> combination and turns it into the 433 ** UNIX canonical <NL> character. It only takes one line, 434 ** i.e., it is assumed that the first <NL> found is the end 435 ** of the line. 436 ** 437 ** Parameters: 438 ** line -- the line to fix. 439 ** stripnl -- if true, strip the newline also. 440 ** 441 ** Returns: 442 ** none. 443 ** 444 ** Side Effects: 445 ** line is changed in place. 446 */ 447 448 fixcrlf(line, stripnl) 449 char *line; 450 bool stripnl; 451 { 452 register char *p; 453 454 p = index(line, '\n'); 455 if (p == NULL) 456 return; 457 if (p[-1] == '\r') 458 p--; 459 if (!stripnl) 460 *p++ = '\n'; 461 *p = '\0'; 462 } 463 /* 464 ** SYSLOG -- fake entry to fool lint 465 */ 466 467 # ifdef LOG 468 # ifdef lint 469 470 /*VARARGS2*/ 471 syslog(pri, fmt, args) 472 int pri; 473 char *fmt; 474 { 475 pri = *fmt; 476 args = pri; 477 pri = args; 478 } 479 480 # endif lint 481 # endif LOG 482 /* 483 ** DFOPEN -- determined file open 484 ** 485 ** This routine has the semantics of fopen, except that it will 486 ** keep trying a few times to make this happen. The idea is that 487 ** on very loaded systems, we may run out of resources (inodes, 488 ** whatever), so this tries to get around it. 489 */ 490 491 FILE * 492 dfopen(filename, mode) 493 char *filename; 494 char *mode; 495 { 496 register int tries; 497 register FILE *fp; 498 499 for (tries = 0; tries < 10; tries++) 500 { 501 sleep(10 * tries); 502 errno = 0; 503 fp = fopen(filename, mode); 504 if (fp != NULL) 505 break; 506 if (errno != ENFILE && errno != EINTR) 507 break; 508 } 509 errno = 0; 510 return (fp); 511 } 512 /* 513 ** PUTLINE -- put a line like fputs obeying SMTP conventions 514 ** 515 ** This routine always guarantees outputing a newline (or CRLF, 516 ** as appropriate) at the end of the string. 517 ** 518 ** Parameters: 519 ** l -- line to put. 520 ** fp -- file to put it onto. 521 ** m -- the mailer used to control output. 522 ** 523 ** Returns: 524 ** none 525 ** 526 ** Side Effects: 527 ** output of l to fp. 528 */ 529 530 # define SMTPLINELIM 990 /* maximum line length */ 531 532 putline(l, fp, m) 533 register char *l; 534 FILE *fp; 535 MAILER *m; 536 { 537 register char *p; 538 char svchar; 539 540 /* strip out 0200 bits -- these can look like TELNET protocol */ 541 if (bitnset(M_LIMITS, m->m_flags)) 542 { 543 p = l; 544 while ((*p++ &= ~0200) != 0) 545 continue; 546 } 547 548 do 549 { 550 /* find the end of the line */ 551 p = index(l, '\n'); 552 if (p == NULL) 553 p = &l[strlen(l)]; 554 555 /* check for line overflow */ 556 while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags)) 557 { 558 register char *q = &l[SMTPLINELIM - 1]; 559 560 svchar = *q; 561 *q = '\0'; 562 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 563 fputc('.', fp); 564 fputs(l, fp); 565 fputc('!', fp); 566 fputs(m->m_eol, fp); 567 *q = svchar; 568 l = q; 569 } 570 571 /* output last part */ 572 svchar = *p; 573 *p = '\0'; 574 if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 575 fputc('.', fp); 576 fputs(l, fp); 577 fputs(m->m_eol, fp); 578 *p = svchar; 579 l = p; 580 if (*l == '\n') 581 l++; 582 } while (l[0] != '\0'); 583 } 584 /* 585 ** XUNLINK -- unlink a file, doing logging as appropriate. 586 ** 587 ** Parameters: 588 ** f -- name of file to unlink. 589 ** 590 ** Returns: 591 ** none. 592 ** 593 ** Side Effects: 594 ** f is unlinked. 595 */ 596 597 xunlink(f) 598 char *f; 599 { 600 register int i; 601 602 # ifdef LOG 603 if (LogLevel > 20) 604 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 605 # endif LOG 606 607 i = unlink(f); 608 # ifdef LOG 609 if (i < 0 && LogLevel > 21) 610 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 611 # endif LOG 612 } 613 /* 614 ** SFGETS -- "safe" fgets -- times out. 615 ** 616 ** Parameters: 617 ** buf -- place to put the input line. 618 ** siz -- size of buf. 619 ** fp -- file to read from. 620 ** 621 ** Returns: 622 ** NULL on error (including timeout). 623 ** buf otherwise. 624 ** 625 ** Side Effects: 626 ** none. 627 */ 628 629 static bool TimeoutFlag; 630 631 char * 632 sfgets(buf, siz, fp) 633 char *buf; 634 int siz; 635 FILE *fp; 636 { 637 register EVENT *ev = NULL; 638 register char *p; 639 extern readtimeout(); 640 641 if (ReadTimeout != 0) 642 ev = setevent(ReadTimeout, readtimeout, 0); 643 TimeoutFlag = FALSE; 644 do 645 { 646 errno = 0; 647 p = fgets(buf, siz, fp); 648 } while (!(p != NULL || TimeoutFlag || errno != EINTR)); 649 clrevent(ev); 650 LineNumber++; 651 if (TimeoutFlag) 652 syserr("sfgets: timeout on read (mailer may be hung)"); 653 return (p); 654 } 655 656 static 657 readtimeout() 658 { 659 TimeoutFlag = TRUE; 660 } 661 /* 662 ** FGETFOLDED -- like fgets, but know about folded lines. 663 ** 664 ** Parameters: 665 ** buf -- place to put result. 666 ** n -- bytes available. 667 ** f -- file to read from. 668 ** 669 ** Returns: 670 ** buf on success, NULL on error or EOF. 671 ** 672 ** Side Effects: 673 ** buf gets lines from f, with continuation lines (lines 674 ** with leading white space) appended. CRLF's are mapped 675 ** into single newlines. Any trailing NL is stripped. 676 */ 677 678 char * 679 fgetfolded(buf, n, f) 680 char *buf; 681 register int n; 682 FILE *f; 683 { 684 register char *p = buf; 685 register int i; 686 687 n--; 688 while (fgets(p, n, f) != NULL) 689 { 690 LineNumber++; 691 fixcrlf(p, TRUE); 692 i = fgetc(f); 693 if (i != EOF) 694 ungetc(i, f); 695 if (i != ' ' && i != '\t') 696 return (buf); 697 i = strlen(p); 698 p += i; 699 *p++ = '\n'; 700 n -= i + 1; 701 } 702 return (NULL); 703 } 704 /* 705 ** CURTIME -- return current time. 706 ** 707 ** Parameters: 708 ** none. 709 ** 710 ** Returns: 711 ** the current time. 712 ** 713 ** Side Effects: 714 ** none. 715 */ 716 717 time_t 718 curtime() 719 { 720 auto time_t t; 721 722 (void) time(&t); 723 return (t); 724 } 725 /* 726 ** ATOBOOL -- convert a string representation to boolean. 727 ** 728 ** Defaults to "TRUE" 729 ** 730 ** Parameters: 731 ** s -- string to convert. Takes "tTyY" as true, 732 ** others as false. 733 ** 734 ** Returns: 735 ** A boolean representation of the string. 736 ** 737 ** Side Effects: 738 ** none. 739 */ 740 741 bool 742 atobool(s) 743 register char *s; 744 { 745 if (*s == '\0' || index("tTyY", *s) != NULL) 746 return (TRUE); 747 return (FALSE); 748 } 749 /* 750 ** ATOOCT -- convert a string representation to octal. 751 ** 752 ** Parameters: 753 ** s -- string to convert. 754 ** 755 ** Returns: 756 ** An integer representing the string interpreted as an 757 ** octal number. 758 ** 759 ** Side Effects: 760 ** none. 761 */ 762 763 atooct(s) 764 register char *s; 765 { 766 register int i = 0; 767 768 while (*s >= '0' && *s <= '7') 769 i = (i << 3) | (*s++ - '0'); 770 return (i); 771 } 772 /* 773 ** WAITFOR -- wait for a particular process id. 774 ** 775 ** Parameters: 776 ** pid -- process id to wait for. 777 ** 778 ** Returns: 779 ** status of pid. 780 ** -1 if pid never shows up. 781 ** 782 ** Side Effects: 783 ** none. 784 */ 785 786 waitfor(pid) 787 int pid; 788 { 789 auto int st; 790 int i; 791 792 do 793 { 794 errno = 0; 795 i = wait(&st); 796 } while ((i >= 0 || errno == EINTR) && i != pid); 797 if (i < 0) 798 st = -1; 799 return (st); 800 } 801 /* 802 ** CLOSEALL -- close all extraneous file descriptors 803 ** 804 ** Parameters: 805 ** none. 806 ** 807 ** Returns: 808 ** none. 809 ** 810 ** Side Effects: 811 ** Closes all file descriptors except zero, one, and two. 812 */ 813 814 closeall() 815 { 816 int i; 817 818 for (i = 3; i < 50; i++) 819 (void) close(i); 820 } 821 /* 822 ** BITINTERSECT -- tell if two bitmaps intersect 823 ** 824 ** Parameters: 825 ** a, b -- the bitmaps in question 826 ** 827 ** Returns: 828 ** TRUE if they have a non-null intersection 829 ** FALSE otherwise 830 ** 831 ** Side Effects: 832 ** none. 833 */ 834 835 bool 836 bitintersect(a, b) 837 BITMAP a; 838 BITMAP b; 839 { 840 int i; 841 842 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 843 if ((a[i] & b[i]) != 0) 844 return (TRUE); 845 return (FALSE); 846 } 847 /* 848 ** BITZEROP -- tell if a bitmap is all zero 849 ** 850 ** Parameters: 851 ** map -- the bit map to check 852 ** 853 ** Returns: 854 ** TRUE if map is all zero. 855 ** FALSE if there are any bits set in map. 856 ** 857 ** Side Effects: 858 ** none. 859 */ 860 861 bool 862 bitzerop(map) 863 BITMAP map; 864 { 865 int i; 866 867 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 868 if (map[i] != 0) 869 return (FALSE); 870 return (TRUE); 871 } 872