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.38 01/01/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 ** crlf -- if set, output Carriage Return/Line Feed on lines 544 ** instead of newline. 545 ** fullsmtp -- if set, obey strictest SMTP conventions. 546 ** 547 ** Returns: 548 ** none 549 ** 550 ** Side Effects: 551 ** output of l to fp. 552 */ 553 554 # define SMTPLINELIM 990 /* maximum line length */ 555 556 putline(l, fp, crlf, fullsmtp) 557 register char *l; 558 FILE *fp; 559 bool crlf; 560 bool fullsmtp; 561 { 562 register char *p; 563 char svchar; 564 565 do 566 { 567 /* find the end of the line */ 568 p = index(l, '\n'); 569 if (p == NULL) 570 p = &l[strlen(l)]; 571 572 /* check for line overflow */ 573 while (fullsmtp && (p - l) > SMTPLINELIM) 574 { 575 register char *q = &l[SMTPLINELIM - 1]; 576 577 svchar = *q; 578 *q = '\0'; 579 fputs(l, fp); 580 fputc('!', fp); 581 if (crlf) 582 fputc('\r', fp); 583 fputc('\n', fp); 584 *q = svchar; 585 l = q; 586 } 587 588 /* output last part */ 589 svchar = *p; 590 *p = '\0'; 591 fputs(l, fp); 592 if (crlf) 593 fputc('\r', fp); 594 fputc('\n', fp); 595 *p = svchar; 596 l = p; 597 if (*l == '\n') 598 l++; 599 } while (l[0] != '\0'); 600 } 601 /* 602 ** XUNLINK -- unlink a file, doing logging as appropriate. 603 ** 604 ** Parameters: 605 ** f -- name of file to unlink. 606 ** 607 ** Returns: 608 ** none. 609 ** 610 ** Side Effects: 611 ** f is unlinked. 612 */ 613 614 xunlink(f) 615 char *f; 616 { 617 register int i; 618 619 # ifdef LOG 620 if (LogLevel > 20) 621 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 622 # endif LOG 623 624 i = unlink(f); 625 # ifdef LOG 626 if (i < 0 && LogLevel > 21) 627 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 628 # endif LOG 629 } 630 /* 631 ** SFGETS -- "safe" fgets -- times out. 632 ** 633 ** Parameters: 634 ** buf -- place to put the input line. 635 ** siz -- size of buf. 636 ** fp -- file to read from. 637 ** 638 ** Returns: 639 ** NULL on error (including timeout). 640 ** buf otherwise. 641 ** 642 ** Side Effects: 643 ** none. 644 */ 645 646 static bool TimeoutFlag; 647 648 char * 649 sfgets(buf, siz, fp) 650 char *buf; 651 int siz; 652 FILE *fp; 653 { 654 register EVENT *ev = NULL; 655 register char *p; 656 extern readtimeout(); 657 658 if (ReadTimeout != 0) 659 ev = setevent(ReadTimeout, readtimeout, 0); 660 TimeoutFlag = FALSE; 661 do 662 { 663 errno = 0; 664 p = fgets(buf, siz, fp); 665 } while (!(p != NULL || TimeoutFlag || errno != EINTR)); 666 clrevent(ev); 667 LineNumber++; 668 if (TimeoutFlag) 669 syserr("sfgets: timeout on read (mailer may be hung)"); 670 return (p); 671 } 672 673 static 674 readtimeout() 675 { 676 TimeoutFlag = TRUE; 677 } 678 /* 679 ** FGETFOLDED -- like fgets, but know about folded lines. 680 ** 681 ** Parameters: 682 ** buf -- place to put result. 683 ** n -- bytes available. 684 ** f -- file to read from. 685 ** 686 ** Returns: 687 ** buf on success, NULL on error or EOF. 688 ** 689 ** Side Effects: 690 ** buf gets lines from f, with continuation lines (lines 691 ** with leading white space) appended. CRLF's are mapped 692 ** into single newlines. Any trailing NL is stripped. 693 */ 694 695 char * 696 fgetfolded(buf, n, f) 697 char *buf; 698 register int n; 699 FILE *f; 700 { 701 register char *p = buf; 702 register int i; 703 704 n--; 705 while (fgets(p, n, f) != NULL) 706 { 707 LineNumber++; 708 fixcrlf(p, TRUE); 709 i = fgetc(f); 710 if (i != EOF) 711 ungetc(i, f); 712 if (i != ' ' && i != '\t') 713 return (buf); 714 i = strlen(p); 715 p += i; 716 *p++ = '\n'; 717 n -= i + 1; 718 } 719 return (NULL); 720 } 721 /* 722 ** CURTIME -- return current time. 723 ** 724 ** Parameters: 725 ** none. 726 ** 727 ** Returns: 728 ** the current time. 729 ** 730 ** Side Effects: 731 ** none. 732 */ 733 734 time_t 735 curtime() 736 { 737 auto time_t t; 738 739 (void) time(&t); 740 return (t); 741 } 742 /* 743 ** ATOBOOL -- convert a string representation to boolean. 744 ** 745 ** Defaults to "TRUE" 746 ** 747 ** Parameters: 748 ** s -- string to convert. Takes "tTyY" as true, 749 ** others as false. 750 ** 751 ** Returns: 752 ** A boolean representation of the string. 753 ** 754 ** Side Effects: 755 ** none. 756 */ 757 758 bool 759 atobool(s) 760 register char *s; 761 { 762 if (*s == '\0' || index("tTyY", *s) != NULL) 763 return (TRUE); 764 return (FALSE); 765 } 766 /* 767 ** ATOOCT -- convert a string representation to octal. 768 ** 769 ** Parameters: 770 ** s -- string to convert. 771 ** 772 ** Returns: 773 ** An integer representing the string interpreted as an 774 ** octal number. 775 ** 776 ** Side Effects: 777 ** none. 778 */ 779 780 atooct(s) 781 register char *s; 782 { 783 register int i = 0; 784 785 while (*s >= '0' && *s <= '7') 786 i = (i << 3) | (*s++ - '0'); 787 return (i); 788 } 789 /* 790 ** WAITFOR -- wait for a particular process id. 791 ** 792 ** Parameters: 793 ** pid -- process id to wait for. 794 ** 795 ** Returns: 796 ** status of pid. 797 ** -1 if pid never shows up. 798 ** 799 ** Side Effects: 800 ** none. 801 */ 802 803 waitfor(pid) 804 int pid; 805 { 806 auto int st; 807 int i; 808 809 do 810 { 811 errno = 0; 812 i = wait(&st); 813 } while ((i >= 0 || errno == EINTR) && i != pid); 814 if (i < 0) 815 st = -1; 816 return (st); 817 } 818 /* 819 ** CLOSEALL -- close all extraneous file descriptors 820 ** 821 ** Parameters: 822 ** none. 823 ** 824 ** Returns: 825 ** none. 826 ** 827 ** Side Effects: 828 ** Closes all file descriptors except zero, one, and two. 829 */ 830 831 closeall() 832 { 833 int i; 834 835 for (i = 3; i < 50; i++) 836 (void) close(i); 837 } 838