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