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.34 11/03/82); 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 extern int errno; 522 523 for (tries = 0; tries < 10; tries++) 524 { 525 sleep(10 * tries); 526 errno = 0; 527 fp = fopen(filename, mode); 528 if (fp != NULL || errno != ENFILE) 529 break; 530 } 531 return (fp); 532 } 533 /* 534 ** PUTLINE -- put a line like fputs obeying SMTP conventions 535 ** 536 ** This routine always guarantees outputing a newline (or CRLF, 537 ** as appropriate) at the end of the string. 538 ** 539 ** Parameters: 540 ** l -- line to put. 541 ** fp -- file to put it onto. 542 ** fullsmtp -- if set, obey strictest SMTP conventions. 543 ** 544 ** Returns: 545 ** none 546 ** 547 ** Side Effects: 548 ** output of l to fp. 549 */ 550 551 # define SMTPLINELIM 990 /* maximum line length */ 552 553 putline(l, fp, fullsmtp) 554 register char *l; 555 FILE *fp; 556 bool fullsmtp; 557 { 558 register char *p; 559 char svchar; 560 561 do 562 { 563 /* find the end of the line */ 564 p = index(l, '\n'); 565 if (p == NULL) 566 p = &l[strlen(l)]; 567 568 /* check for line overflow */ 569 while (fullsmtp && (p - l) > SMTPLINELIM) 570 { 571 register char *q = &l[SMTPLINELIM - 1]; 572 573 svchar = *q; 574 *q = '\0'; 575 fputs(l, fp); 576 fputs("!\r\n", fp); 577 *q = svchar; 578 l = q; 579 } 580 581 /* output last part */ 582 svchar = *p; 583 *p = '\0'; 584 fputs(l, fp); 585 if (fullsmtp) 586 fputc('\r', fp); 587 fputc('\n', fp); 588 *p = svchar; 589 l = p; 590 if (*l == '\n') 591 l++; 592 } while (l[0] != '\0'); 593 } 594 /* 595 ** XUNLINK -- unlink a file, doing logging as appropriate. 596 ** 597 ** Parameters: 598 ** f -- name of file to unlink. 599 ** 600 ** Returns: 601 ** none. 602 ** 603 ** Side Effects: 604 ** f is unlinked. 605 */ 606 607 xunlink(f) 608 char *f; 609 { 610 register int i; 611 612 # ifdef LOG 613 if (LogLevel > 20) 614 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 615 # endif LOG 616 617 i = unlink(f); 618 # ifdef LOG 619 if (i < 0 && LogLevel > 21) 620 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 621 # endif LOG 622 } 623 /* 624 ** SFGETS -- "safe" fgets -- times out. 625 ** 626 ** Parameters: 627 ** buf -- place to put the input line. 628 ** siz -- size of buf. 629 ** fp -- file to read from. 630 ** 631 ** Returns: 632 ** NULL on error (including timeout). 633 ** buf otherwise. 634 ** 635 ** Side Effects: 636 ** none. 637 */ 638 639 static bool TimeoutFlag; 640 641 char * 642 sfgets(buf, siz, fp) 643 char *buf; 644 int siz; 645 FILE *fp; 646 { 647 register EVENT *ev = NULL; 648 register char *p; 649 extern readtimeout(); 650 651 if (ReadTimeout != 0) 652 ev = setevent(ReadTimeout, readtimeout, 0); 653 TimeoutFlag = FALSE; 654 do 655 { 656 errno = 0; 657 p = fgets(buf, siz, fp); 658 } while (!(p != NULL || TimeoutFlag || errno != EINTR)); 659 clrevent(ev); 660 LineNumber++; 661 return (p); 662 } 663 664 static 665 readtimeout() 666 { 667 TimeoutFlag = TRUE; 668 } 669 /* 670 ** FGETFOLDED -- like fgets, but know about folded lines. 671 ** 672 ** Parameters: 673 ** buf -- place to put result. 674 ** n -- bytes available. 675 ** f -- file to read from. 676 ** 677 ** Returns: 678 ** buf on success, NULL on error or EOF. 679 ** 680 ** Side Effects: 681 ** buf gets lines from f, with continuation lines (lines 682 ** with leading white space) appended. CRLF's are mapped 683 ** into single newlines. Any trailing NL is stripped. 684 */ 685 686 char * 687 fgetfolded(buf, n, f) 688 char *buf; 689 register int n; 690 FILE *f; 691 { 692 register char *p = buf; 693 register int i; 694 695 n--; 696 while (sfgets(p, n, f) != NULL) 697 { 698 fixcrlf(p, TRUE); 699 i = fgetc(f); 700 if (i != EOF) 701 ungetc(i, f); 702 if (i != ' ' && i != '\t') 703 return (buf); 704 i = strlen(p); 705 p += i; 706 *p++ = '\n'; 707 n -= i + 1; 708 } 709 return (NULL); 710 } 711 /* 712 ** PINTVL -- produce printable version of a time interval 713 ** 714 ** Parameters: 715 ** intvl -- the interval to be converted 716 ** brief -- if TRUE, print this in an extremely compact form 717 ** (basically used for logging). 718 ** 719 ** Returns: 720 ** A pointer to a string version of intvl suitable for 721 ** printing or framing. 722 ** 723 ** Side Effects: 724 ** none. 725 ** 726 ** Warning: 727 ** The string returned is in a static buffer. 728 */ 729 730 # define PLURAL(n) ((n) == 1 ? "" : "s") 731 732 char * 733 pintvl(intvl, brief) 734 time_t intvl; 735 bool brief; 736 { 737 static char buf[MAXNAME]; 738 register char *p; 739 int wk, dy, hr, mi, se; 740 741 if (intvl == 0 && !brief) 742 return ("zero seconds"); 743 744 /* decode the interval into weeks, days, hours, minutes, seconds */ 745 se = intvl % 60; 746 intvl /= 60; 747 mi = intvl % 60; 748 intvl /= 60; 749 hr = intvl % 24; 750 intvl /= 24; 751 if (brief) 752 dy = intvl; 753 else 754 { 755 dy = intvl % 7; 756 intvl /= 7; 757 wk = intvl; 758 } 759 760 /* now turn it into a sexy form */ 761 p = buf; 762 if (brief) 763 { 764 if (dy > 0) 765 { 766 (void) sprintf(p, "%d+", dy); 767 p += strlen(p); 768 } 769 (void) sprintf(p, "%02d:%02d:%02d", hr, mi, se); 770 return (buf); 771 } 772 773 /* use the verbose form */ 774 if (wk > 0) 775 { 776 (void) sprintf(p, ", %d week%s", wk, PLURAL(wk)); 777 p += strlen(p); 778 } 779 if (dy > 0) 780 { 781 (void) sprintf(p, ", %d day%s", dy, PLURAL(dy)); 782 p += strlen(p); 783 } 784 if (hr > 0) 785 { 786 (void) sprintf(p, ", %d hour%s", hr, PLURAL(hr)); 787 p += strlen(p); 788 } 789 if (mi > 0) 790 { 791 (void) sprintf(p, ", %d minute%s", mi, PLURAL(mi)); 792 p += strlen(p); 793 } 794 if (se > 0) 795 { 796 (void) sprintf(p, ", %d second%s", se, PLURAL(se)); 797 p += strlen(p); 798 } 799 800 return (buf + 2); 801 } 802 /* 803 ** CURTIME -- return current time. 804 ** 805 ** Parameters: 806 ** none. 807 ** 808 ** Returns: 809 ** the current time. 810 ** 811 ** Side Effects: 812 ** none. 813 */ 814 815 time_t 816 curtime() 817 { 818 auto time_t t; 819 820 (void) time(&t); 821 return (t); 822 } 823 /* 824 ** ATOBOOL -- convert a string representation to boolean. 825 ** 826 ** Defaults to "TRUE" 827 ** 828 ** Parameters: 829 ** s -- string to convert. Takes "tTyY" as true, 830 ** others as false. 831 ** 832 ** Returns: 833 ** A boolean representation of the string. 834 ** 835 ** Side Effects: 836 ** none. 837 */ 838 839 bool 840 atobool(s) 841 register char *s; 842 { 843 if (*s == '\0' || index("tTyY", *s) != NULL) 844 return (TRUE); 845 return (FALSE); 846 } 847 /* 848 ** ATOOCT -- convert a string representation to octal. 849 ** 850 ** Parameters: 851 ** s -- string to convert. 852 ** 853 ** Returns: 854 ** An integer representing the string interpreted as an 855 ** octal number. 856 ** 857 ** Side Effects: 858 ** none. 859 */ 860 861 atooct(s) 862 register char *s; 863 { 864 register int i = 0; 865 866 while (*s >= '0' && *s <= '7') 867 i = (i << 3) | (*s++ - '0'); 868 return (i); 869 } 870