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.39 01/06/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 exit(EX_UNAVAILABLE); 140 } 141 return (p); 142 } 143 /* 144 ** NEWSTR -- make copy of string. 145 ** 146 ** Space is allocated for it using xalloc. 147 ** 148 ** Parameters: 149 ** string to copy. 150 ** 151 ** Returns: 152 ** pointer to new string. 153 ** 154 ** Side Effects: 155 ** none. 156 */ 157 158 char * 159 newstr(s) 160 register char *s; 161 { 162 register char *p; 163 164 p = xalloc(strlen(s) + 1); 165 (void) strcpy(p, s); 166 return (p); 167 } 168 /* 169 ** COPYPLIST -- copy list of pointers. 170 ** 171 ** This routine is the equivalent of newstr for lists of 172 ** pointers. 173 ** 174 ** Parameters: 175 ** list -- list of pointers to copy. 176 ** Must be NULL terminated. 177 ** copycont -- if TRUE, copy the contents of the vector 178 ** (which must be a string) also. 179 ** 180 ** Returns: 181 ** a copy of 'list'. 182 ** 183 ** Side Effects: 184 ** none. 185 */ 186 187 char ** 188 copyplist(list, copycont) 189 char **list; 190 bool copycont; 191 { 192 register char **vp; 193 register char **newvp; 194 195 for (vp = list; *vp != NULL; vp++) 196 continue; 197 198 vp++; 199 200 newvp = (char **) xalloc((vp - list) * sizeof *vp); 201 bmove((char *) list, (char *) newvp, (vp - list) * sizeof *vp); 202 203 if (copycont) 204 { 205 for (vp = newvp; *vp != NULL; vp++) 206 *vp = newstr(*vp); 207 } 208 209 return (newvp); 210 } 211 /* 212 ** PRINTAV -- print argument vector. 213 ** 214 ** Parameters: 215 ** av -- argument vector. 216 ** 217 ** Returns: 218 ** none. 219 ** 220 ** Side Effects: 221 ** prints av. 222 */ 223 224 # ifdef DEBUG 225 printav(av) 226 register char **av; 227 { 228 while (*av != NULL) 229 { 230 if (tTd(0, 44)) 231 printf("\n\t%08x=", *av); 232 else 233 putchar(' '); 234 xputs(*av++); 235 } 236 putchar('\n'); 237 } 238 # endif DEBUG 239 /* 240 ** LOWER -- turn letter into lower case. 241 ** 242 ** Parameters: 243 ** c -- character to turn into lower case. 244 ** 245 ** Returns: 246 ** c, in lower case. 247 ** 248 ** Side Effects: 249 ** none. 250 */ 251 252 char 253 lower(c) 254 register char c; 255 { 256 if (isascii(c) && isupper(c)) 257 c = c - 'A' + 'a'; 258 return (c); 259 } 260 /* 261 ** XPUTS -- put string doing control escapes. 262 ** 263 ** Parameters: 264 ** s -- string to put. 265 ** 266 ** Returns: 267 ** none. 268 ** 269 ** Side Effects: 270 ** output to stdout 271 */ 272 273 # ifdef DEBUG 274 xputs(s) 275 register char *s; 276 { 277 register char c; 278 279 if (s == NULL) 280 { 281 printf("<null>"); 282 return; 283 } 284 putchar('"'); 285 while ((c = *s++) != '\0') 286 { 287 if (!isascii(c)) 288 { 289 putchar('\\'); 290 c &= 0177; 291 } 292 if (iscntrl(c)) 293 { 294 putchar('^'); 295 c |= 0100; 296 } 297 putchar(c); 298 } 299 putchar('"'); 300 (void) fflush(stdout); 301 } 302 # endif DEBUG 303 /* 304 ** MAKELOWER -- Translate a line into lower case 305 ** 306 ** Parameters: 307 ** p -- the string to translate. If NULL, return is 308 ** immediate. 309 ** 310 ** Returns: 311 ** none. 312 ** 313 ** Side Effects: 314 ** String pointed to by p is translated to lower case. 315 ** 316 ** Called By: 317 ** parse 318 */ 319 320 makelower(p) 321 register char *p; 322 { 323 register char c; 324 325 if (p == NULL) 326 return; 327 for (; (c = *p) != '\0'; p++) 328 if (isascii(c) && isupper(c)) 329 *p = c - 'A' + 'a'; 330 } 331 /* 332 ** SAMEWORD -- return TRUE if the words are the same 333 ** 334 ** Ignores case. 335 ** 336 ** Parameters: 337 ** a, b -- the words to compare. 338 ** 339 ** Returns: 340 ** TRUE if a & b match exactly (modulo case) 341 ** FALSE otherwise. 342 ** 343 ** Side Effects: 344 ** none. 345 */ 346 347 bool 348 sameword(a, b) 349 register char *a, *b; 350 { 351 while (lower(*a) == lower(*b)) 352 { 353 if (*a == '\0') 354 return (TRUE); 355 a++; 356 b++; 357 } 358 return (FALSE); 359 } 360 /* 361 ** CLEAR -- clear a block of memory 362 ** 363 ** Parameters: 364 ** p -- location to clear. 365 ** l -- number of bytes to clear. 366 ** 367 ** Returns: 368 ** none. 369 ** 370 ** Side Effects: 371 ** none. 372 */ 373 374 clear(p, l) 375 register char *p; 376 register int l; 377 { 378 while (l-- > 0) 379 *p++ = 0; 380 } 381 /* 382 ** BUILDFNAME -- build full name from gecos style entry. 383 ** 384 ** This routine interprets the strange entry that would appear 385 ** in the GECOS field of the password file. 386 ** 387 ** Parameters: 388 ** p -- name to build. 389 ** login -- the login name of this user (for &). 390 ** buf -- place to put the result. 391 ** 392 ** Returns: 393 ** none. 394 ** 395 ** Side Effects: 396 ** none. 397 */ 398 399 buildfname(p, login, buf) 400 register char *p; 401 char *login; 402 char *buf; 403 { 404 register char *bp = buf; 405 406 if (*p == '*') 407 p++; 408 while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 409 { 410 if (*p == '&') 411 { 412 (void) strcpy(bp, login); 413 *bp = toupper(*bp); 414 while (*bp != '\0') 415 bp++; 416 p++; 417 } 418 else 419 *bp++ = *p++; 420 } 421 *bp = '\0'; 422 } 423 /* 424 ** SAFEFILE -- return true if a file exists and is safe for a user. 425 ** 426 ** Parameters: 427 ** fn -- filename to check. 428 ** uid -- uid to compare against. 429 ** mode -- mode bits that must match. 430 ** 431 ** Returns: 432 ** TRUE if fn exists, is owned by uid, and matches mode. 433 ** FALSE otherwise. 434 ** 435 ** Side Effects: 436 ** none. 437 */ 438 439 bool 440 safefile(fn, uid, mode) 441 char *fn; 442 int uid; 443 int mode; 444 { 445 struct stat stbuf; 446 447 if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 448 (stbuf.st_mode & mode) == mode) 449 return (TRUE); 450 return (FALSE); 451 } 452 /* 453 ** FIXCRLF -- fix <CR><LF> in line. 454 ** 455 ** Looks for the <CR><LF> combination and turns it into the 456 ** UNIX canonical <NL> character. It only takes one line, 457 ** i.e., it is assumed that the first <NL> found is the end 458 ** of the line. 459 ** 460 ** Parameters: 461 ** line -- the line to fix. 462 ** stripnl -- if true, strip the newline also. 463 ** 464 ** Returns: 465 ** none. 466 ** 467 ** Side Effects: 468 ** line is changed in place. 469 */ 470 471 fixcrlf(line, stripnl) 472 char *line; 473 bool stripnl; 474 { 475 register char *p; 476 477 p = index(line, '\n'); 478 if (p == NULL) 479 return; 480 if (p[-1] == '\r') 481 p--; 482 if (!stripnl) 483 *p++ = '\n'; 484 *p = '\0'; 485 } 486 /* 487 ** SYSLOG -- fake entry to fool lint 488 */ 489 490 # ifdef LOG 491 # ifdef lint 492 493 /*VARARGS2*/ 494 syslog(pri, fmt, args) 495 int pri; 496 char *fmt; 497 { 498 pri = *fmt; 499 args = pri; 500 pri = args; 501 } 502 503 # endif lint 504 # endif LOG 505 /* 506 ** DFOPEN -- determined file open 507 ** 508 ** This routine has the semantics of fopen, except that it will 509 ** keep trying a few times to make this happen. The idea is that 510 ** on very loaded systems, we may run out of resources (inodes, 511 ** whatever), so this tries to get around it. 512 */ 513 514 FILE * 515 dfopen(filename, mode) 516 char *filename; 517 char *mode; 518 { 519 register int tries; 520 register FILE *fp; 521 522 for (tries = 0; tries < 10; tries++) 523 { 524 sleep(10 * tries); 525 errno = 0; 526 fp = fopen(filename, mode); 527 if (fp != NULL) 528 break; 529 if (errno != ENFILE && errno != EINTR) 530 break; 531 } 532 return (fp); 533 } 534 /* 535 ** PUTLINE -- put a line like fputs obeying SMTP conventions 536 ** 537 ** This routine always guarantees outputing a newline (or CRLF, 538 ** as appropriate) at the end of the string. 539 ** 540 ** Parameters: 541 ** l -- line to put. 542 ** fp -- file to put it onto. 543 ** m -- the mailer used to control output. 544 ** 545 ** Returns: 546 ** none 547 ** 548 ** Side Effects: 549 ** output of l to fp. 550 */ 551 552 # define SMTPLINELIM 990 /* maximum line length */ 553 554 putline(l, fp, m) 555 register char *l; 556 FILE *fp; 557 MAILER *m; 558 { 559 register char *p; 560 char svchar; 561 char *crlfstring = "\r\n"; 562 563 if (!bitset(M_CRLF, m->m_flags)) 564 crlfstring++; 565 566 do 567 { 568 /* find the end of the line */ 569 p = index(l, '\n'); 570 if (p == NULL) 571 p = &l[strlen(l)]; 572 573 /* check for line overflow */ 574 while (bitset(M_LIMITS, m->m_flags) && (p - l) > SMTPLINELIM) 575 { 576 register char *q = &l[SMTPLINELIM - 1]; 577 578 svchar = *q; 579 *q = '\0'; 580 if (l[0] == '.' && bitset(M_XDOT, m->m_flags)) 581 fputc('.', fp); 582 fputs(l, fp); 583 fputc('!', fp); 584 fputs(crlfstring, fp); 585 *q = svchar; 586 l = q; 587 } 588 589 /* output last part */ 590 svchar = *p; 591 *p = '\0'; 592 if (l[0] == '.' && bitset(M_XDOT, m->m_flags)) 593 fputc('.', fp); 594 fputs(l, fp); 595 fputs(crlfstring, fp); 596 *p = svchar; 597 l = p; 598 if (*l == '\n') 599 l++; 600 } while (l[0] != '\0'); 601 } 602 /* 603 ** XUNLINK -- unlink a file, doing logging as appropriate. 604 ** 605 ** Parameters: 606 ** f -- name of file to unlink. 607 ** 608 ** Returns: 609 ** none. 610 ** 611 ** Side Effects: 612 ** f is unlinked. 613 */ 614 615 xunlink(f) 616 char *f; 617 { 618 register int i; 619 620 # ifdef LOG 621 if (LogLevel > 20) 622 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 623 # endif LOG 624 625 i = unlink(f); 626 # ifdef LOG 627 if (i < 0 && LogLevel > 21) 628 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 629 # endif LOG 630 } 631 /* 632 ** SFGETS -- "safe" fgets -- times out. 633 ** 634 ** Parameters: 635 ** buf -- place to put the input line. 636 ** siz -- size of buf. 637 ** fp -- file to read from. 638 ** 639 ** Returns: 640 ** NULL on error (including timeout). 641 ** buf otherwise. 642 ** 643 ** Side Effects: 644 ** none. 645 */ 646 647 static bool TimeoutFlag; 648 649 char * 650 sfgets(buf, siz, fp) 651 char *buf; 652 int siz; 653 FILE *fp; 654 { 655 register EVENT *ev = NULL; 656 register char *p; 657 extern readtimeout(); 658 659 if (ReadTimeout != 0) 660 ev = setevent(ReadTimeout, readtimeout, 0); 661 TimeoutFlag = FALSE; 662 do 663 { 664 errno = 0; 665 p = fgets(buf, siz, fp); 666 } while (!(p != NULL || TimeoutFlag || errno != EINTR)); 667 clrevent(ev); 668 LineNumber++; 669 if (TimeoutFlag) 670 syserr("sfgets: timeout on read (mailer may be hung)"); 671 return (p); 672 } 673 674 static 675 readtimeout() 676 { 677 TimeoutFlag = TRUE; 678 } 679 /* 680 ** FGETFOLDED -- like fgets, but know about folded lines. 681 ** 682 ** Parameters: 683 ** buf -- place to put result. 684 ** n -- bytes available. 685 ** f -- file to read from. 686 ** 687 ** Returns: 688 ** buf on success, NULL on error or EOF. 689 ** 690 ** Side Effects: 691 ** buf gets lines from f, with continuation lines (lines 692 ** with leading white space) appended. CRLF's are mapped 693 ** into single newlines. Any trailing NL is stripped. 694 */ 695 696 char * 697 fgetfolded(buf, n, f) 698 char *buf; 699 register int n; 700 FILE *f; 701 { 702 register char *p = buf; 703 register int i; 704 705 n--; 706 while (fgets(p, n, f) != NULL) 707 { 708 LineNumber++; 709 fixcrlf(p, TRUE); 710 i = fgetc(f); 711 if (i != EOF) 712 ungetc(i, f); 713 if (i != ' ' && i != '\t') 714 return (buf); 715 i = strlen(p); 716 p += i; 717 *p++ = '\n'; 718 n -= i + 1; 719 } 720 return (NULL); 721 } 722 /* 723 ** CURTIME -- return current time. 724 ** 725 ** Parameters: 726 ** none. 727 ** 728 ** Returns: 729 ** the current time. 730 ** 731 ** Side Effects: 732 ** none. 733 */ 734 735 time_t 736 curtime() 737 { 738 auto time_t t; 739 740 (void) time(&t); 741 return (t); 742 } 743 /* 744 ** ATOBOOL -- convert a string representation to boolean. 745 ** 746 ** Defaults to "TRUE" 747 ** 748 ** Parameters: 749 ** s -- string to convert. Takes "tTyY" as true, 750 ** others as false. 751 ** 752 ** Returns: 753 ** A boolean representation of the string. 754 ** 755 ** Side Effects: 756 ** none. 757 */ 758 759 bool 760 atobool(s) 761 register char *s; 762 { 763 if (*s == '\0' || index("tTyY", *s) != NULL) 764 return (TRUE); 765 return (FALSE); 766 } 767 /* 768 ** ATOOCT -- convert a string representation to octal. 769 ** 770 ** Parameters: 771 ** s -- string to convert. 772 ** 773 ** Returns: 774 ** An integer representing the string interpreted as an 775 ** octal number. 776 ** 777 ** Side Effects: 778 ** none. 779 */ 780 781 atooct(s) 782 register char *s; 783 { 784 register int i = 0; 785 786 while (*s >= '0' && *s <= '7') 787 i = (i << 3) | (*s++ - '0'); 788 return (i); 789 } 790 /* 791 ** WAITFOR -- wait for a particular process id. 792 ** 793 ** Parameters: 794 ** pid -- process id to wait for. 795 ** 796 ** Returns: 797 ** status of pid. 798 ** -1 if pid never shows up. 799 ** 800 ** Side Effects: 801 ** none. 802 */ 803 804 waitfor(pid) 805 int pid; 806 { 807 auto int st; 808 int i; 809 810 do 811 { 812 errno = 0; 813 i = wait(&st); 814 } while ((i >= 0 || errno == EINTR) && i != pid); 815 if (i < 0) 816 st = -1; 817 return (st); 818 } 819 /* 820 ** CLOSEALL -- close all extraneous file descriptors 821 ** 822 ** Parameters: 823 ** none. 824 ** 825 ** Returns: 826 ** none. 827 ** 828 ** Side Effects: 829 ** Closes all file descriptors except zero, one, and two. 830 */ 831 832 closeall() 833 { 834 int i; 835 836 for (i = 3; i < 50; i++) 837 (void) close(i); 838 } 839