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