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