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.30 09/05/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 printf("\t%08x=", *av); 204 xputs(*av++); 205 putchar('\n'); 206 } 207 } 208 # endif DEBUG 209 /* 210 ** LOWER -- turn letter into lower case. 211 ** 212 ** Parameters: 213 ** c -- character to turn into lower case. 214 ** 215 ** Returns: 216 ** c, in lower case. 217 ** 218 ** Side Effects: 219 ** none. 220 */ 221 222 char 223 lower(c) 224 register char c; 225 { 226 if (isascii(c) && isupper(c)) 227 c = c - 'A' + 'a'; 228 return (c); 229 } 230 /* 231 ** XPUTS -- put string doing control escapes. 232 ** 233 ** Parameters: 234 ** s -- string to put. 235 ** 236 ** Returns: 237 ** none. 238 ** 239 ** Side Effects: 240 ** output to stdout 241 */ 242 243 # ifdef DEBUG 244 xputs(s) 245 register char *s; 246 { 247 register char c; 248 249 if (s == NULL) 250 { 251 printf("<null>"); 252 return; 253 } 254 while ((c = *s++) != '\0') 255 { 256 if (!isascii(c)) 257 { 258 putchar('\\'); 259 c &= 0177; 260 } 261 if (iscntrl(c)) 262 { 263 putchar('^'); 264 c |= 0100; 265 } 266 putchar(c); 267 } 268 (void) fflush(stdout); 269 } 270 # endif DEBUG 271 /* 272 ** MAKELOWER -- Translate a line into lower case 273 ** 274 ** Parameters: 275 ** p -- the string to translate. If NULL, return is 276 ** immediate. 277 ** 278 ** Returns: 279 ** none. 280 ** 281 ** Side Effects: 282 ** String pointed to by p is translated to lower case. 283 ** 284 ** Called By: 285 ** parse 286 */ 287 288 makelower(p) 289 register char *p; 290 { 291 register char c; 292 293 if (p == NULL) 294 return; 295 for (; (c = *p) != '\0'; p++) 296 if (isascii(c) && isupper(c)) 297 *p = c - 'A' + 'a'; 298 } 299 /* 300 ** SAMEWORD -- return TRUE if the words are the same 301 ** 302 ** Ignores case. 303 ** 304 ** Parameters: 305 ** a, b -- the words to compare. 306 ** 307 ** Returns: 308 ** TRUE if a & b match exactly (modulo case) 309 ** FALSE otherwise. 310 ** 311 ** Side Effects: 312 ** none. 313 */ 314 315 bool 316 sameword(a, b) 317 register char *a, *b; 318 { 319 while (lower(*a) == lower(*b)) 320 { 321 if (*a == '\0') 322 return (TRUE); 323 a++; 324 b++; 325 } 326 return (FALSE); 327 } 328 /* 329 ** CLEAR -- clear a block of memory 330 ** 331 ** Parameters: 332 ** p -- location to clear. 333 ** l -- number of bytes to clear. 334 ** 335 ** Returns: 336 ** none. 337 ** 338 ** Side Effects: 339 ** none. 340 */ 341 342 clear(p, l) 343 register char *p; 344 register int l; 345 { 346 while (l-- > 0) 347 *p++ = 0; 348 } 349 /* 350 ** BUILDFNAME -- build full name from gecos style entry. 351 ** 352 ** This routine interprets the strange entry that would appear 353 ** in the GECOS field of the password file. 354 ** 355 ** Parameters: 356 ** p -- name to build. 357 ** login -- the login name of this user (for &). 358 ** buf -- place to put the result. 359 ** 360 ** Returns: 361 ** none. 362 ** 363 ** Side Effects: 364 ** none. 365 */ 366 367 buildfname(p, login, buf) 368 register char *p; 369 char *login; 370 char *buf; 371 { 372 register char *bp = buf; 373 374 if (*p == '*') 375 p++; 376 while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 377 { 378 if (*p == '&') 379 { 380 (void) strcpy(bp, login); 381 *bp = toupper(*bp); 382 while (*bp != '\0') 383 bp++; 384 p++; 385 } 386 else 387 *bp++ = *p++; 388 } 389 *bp = '\0'; 390 } 391 /* 392 ** SAFEFILE -- return true if a file exists and is safe for a user. 393 ** 394 ** Parameters: 395 ** fn -- filename to check. 396 ** uid -- uid to compare against. 397 ** mode -- mode bits that must match. 398 ** 399 ** Returns: 400 ** TRUE if fn exists, is owned by uid, and matches mode. 401 ** FALSE otherwise. 402 ** 403 ** Side Effects: 404 ** none. 405 */ 406 407 bool 408 safefile(fn, uid, mode) 409 char *fn; 410 int uid; 411 int mode; 412 { 413 struct stat stbuf; 414 415 if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 416 (stbuf.st_mode & mode) == mode) 417 return (TRUE); 418 return (FALSE); 419 } 420 /* 421 ** FIXCRLF -- fix <CR><LF> in line. 422 ** 423 ** Looks for the <CR><LF> combination and turns it into the 424 ** UNIX canonical <NL> character. It only takes one line, 425 ** i.e., it is assumed that the first <NL> found is the end 426 ** of the line. 427 ** 428 ** Parameters: 429 ** line -- the line to fix. 430 ** stripnl -- if true, strip the newline also. 431 ** 432 ** Returns: 433 ** none. 434 ** 435 ** Side Effects: 436 ** line is changed in place. 437 */ 438 439 fixcrlf(line, stripnl) 440 char *line; 441 bool stripnl; 442 { 443 register char *p; 444 445 p = index(line, '\n'); 446 if (p == NULL) 447 return; 448 if (p[-1] == '\r') 449 p--; 450 if (!stripnl) 451 *p++ = '\n'; 452 *p = '\0'; 453 } 454 /* 455 ** SYSLOG -- fake entry to fool lint 456 */ 457 458 # ifdef LOG 459 # ifdef lint 460 461 /*VARARGS2*/ 462 syslog(pri, fmt, args) 463 int pri; 464 char *fmt; 465 { 466 pri = *fmt; 467 args = pri; 468 pri = args; 469 } 470 471 # endif lint 472 # endif LOG 473 /* 474 ** DFOPEN -- determined file open 475 ** 476 ** This routine has the semantics of fopen, except that it will 477 ** keep trying a few times to make this happen. The idea is that 478 ** on very loaded systems, we may run out of resources (inodes, 479 ** whatever), so this tries to get around it. 480 */ 481 482 FILE * 483 dfopen(filename, mode) 484 char *filename; 485 char *mode; 486 { 487 register int tries; 488 register FILE *fp; 489 extern int errno; 490 491 for (tries = 0; tries < 10; tries++) 492 { 493 sleep(10 * tries); 494 errno = 0; 495 fp = fopen(filename, mode); 496 if (fp != NULL || errno != ENFILE) 497 break; 498 } 499 return (fp); 500 } 501 /* 502 ** PUTLINE -- put a line like fputs obeying SMTP conventions 503 ** 504 ** This routine always guarantees outputing a newline (or CRLF, 505 ** as appropriate) at the end of the string. 506 ** 507 ** Parameters: 508 ** l -- line to put. 509 ** fp -- file to put it onto. 510 ** fullsmtp -- if set, obey strictest SMTP conventions. 511 ** 512 ** Returns: 513 ** none 514 ** 515 ** Side Effects: 516 ** output of l to fp. 517 */ 518 519 # define SMTPLINELIM 990 /* maximum line length */ 520 521 putline(l, fp, fullsmtp) 522 register char *l; 523 FILE *fp; 524 bool fullsmtp; 525 { 526 register char *p; 527 char svchar; 528 529 do 530 { 531 /* find the end of the line */ 532 p = index(l, '\n'); 533 if (p == NULL) 534 p = &l[strlen(l)]; 535 536 /* check for line overflow */ 537 while (fullsmtp && (p - l) > SMTPLINELIM) 538 { 539 register char *q = &l[SMTPLINELIM - 1]; 540 541 svchar = *q; 542 *q = '\0'; 543 fputs(l, fp); 544 fputs("!\r\n", fp); 545 *q = svchar; 546 l = q; 547 } 548 549 /* output last part */ 550 svchar = *p; 551 *p = '\0'; 552 fputs(l, fp); 553 if (fullsmtp) 554 fputc('\r', fp); 555 fputc('\n', fp); 556 *p = svchar; 557 l = p; 558 if (*l == '\n') 559 l++; 560 } while (l[0] != '\0'); 561 } 562 /* 563 ** XUNLINK -- unlink a file, doing logging as appropriate. 564 ** 565 ** Parameters: 566 ** f -- name of file to unlink. 567 ** 568 ** Returns: 569 ** none. 570 ** 571 ** Side Effects: 572 ** f is unlinked. 573 */ 574 575 xunlink(f) 576 char *f; 577 { 578 register int i; 579 580 # ifdef LOG 581 if (LogLevel > 20) 582 syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 583 # endif LOG 584 585 i = unlink(f); 586 # ifdef LOG 587 if (i < 0 && LogLevel > 21) 588 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 589 # endif LOG 590 } 591 /* 592 ** SFGETS -- "safe" fgets -- times out. 593 ** 594 ** Parameters: 595 ** buf -- place to put the input line. 596 ** siz -- size of buf. 597 ** fp -- file to read from. 598 ** 599 ** Returns: 600 ** NULL on error (including timeout). 601 ** buf otherwise. 602 ** 603 ** Side Effects: 604 ** none. 605 */ 606 607 static bool TimeoutFlag; 608 609 char * 610 sfgets(buf, siz, fp) 611 char *buf; 612 int siz; 613 FILE *fp; 614 { 615 register EVENT *ev = NULL; 616 register char *p; 617 extern readtimeout(); 618 619 if (ReadTimeout != 0) 620 ev = setevent(ReadTimeout, readtimeout, 0); 621 TimeoutFlag = FALSE; 622 do 623 { 624 errno = 0; 625 p = fgets(buf, siz, fp); 626 } while (!(p != NULL || TimeoutFlag || errno != EINTR)); 627 clrevent(ev); 628 LineNumber++; 629 return (p); 630 } 631 632 static 633 readtimeout() 634 { 635 TimeoutFlag = TRUE; 636 } 637 /* 638 ** FGETFOLDED -- like fgets, but know about folded lines. 639 ** 640 ** Parameters: 641 ** buf -- place to put result. 642 ** n -- bytes available. 643 ** f -- file to read from. 644 ** 645 ** Returns: 646 ** buf on success, NULL on error or EOF. 647 ** 648 ** Side Effects: 649 ** buf gets lines from f, with continuation lines (lines 650 ** with leading white space) appended. CRLF's are mapped 651 ** into single newlines. Any trailing NL is stripped. 652 */ 653 654 char * 655 fgetfolded(buf, n, f) 656 char *buf; 657 register int n; 658 FILE *f; 659 { 660 register char *p = buf; 661 register int i; 662 663 n--; 664 while (sfgets(p, n, f) != NULL) 665 { 666 fixcrlf(p, TRUE); 667 i = fgetc(f); 668 if (i != EOF) 669 ungetc(i, f); 670 if (i != ' ' && i != '\t') 671 return (buf); 672 i = strlen(p); 673 p += i; 674 *p++ = '\n'; 675 n -= i + 1; 676 } 677 return (NULL); 678 } 679 /* 680 ** PINTVL -- produce printable version of a time interval 681 ** 682 ** Parameters: 683 ** intvl -- the interval to be converted 684 ** brief -- if TRUE, print this in an extremely compact form 685 ** (basically used for logging). 686 ** 687 ** Returns: 688 ** A pointer to a string version of intvl suitable for 689 ** printing or framing. 690 ** 691 ** Side Effects: 692 ** none. 693 ** 694 ** Warning: 695 ** The string returned is in a static buffer. 696 */ 697 698 # define PLURAL(n) ((n) == 1 ? "" : "s") 699 700 char * 701 pintvl(intvl, brief) 702 time_t intvl; 703 bool brief; 704 { 705 static char buf[MAXNAME]; 706 register char *p; 707 int wk, dy, hr, mi, se; 708 709 if (intvl == 0 && !brief) 710 return ("zero seconds"); 711 712 /* decode the interval into weeks, days, hours, minutes, seconds */ 713 se = intvl % 60; 714 intvl /= 60; 715 mi = intvl % 60; 716 intvl /= 60; 717 hr = intvl % 24; 718 intvl /= 24; 719 if (brief) 720 dy = intvl; 721 else 722 { 723 dy = intvl % 7; 724 intvl /= 7; 725 wk = intvl; 726 } 727 728 /* now turn it into a sexy form */ 729 p = buf; 730 if (brief) 731 { 732 if (dy > 0) 733 { 734 (void) sprintf(p, "%d+", dy); 735 p += strlen(p); 736 } 737 (void) sprintf(p, "%02d:%02d:%02d", hr, mi, se); 738 return (buf); 739 } 740 741 /* use the verbose form */ 742 if (wk > 0) 743 { 744 (void) sprintf(p, ", %d week%s", wk, PLURAL(wk)); 745 p += strlen(p); 746 } 747 if (dy > 0) 748 { 749 (void) sprintf(p, ", %d day%s", dy, PLURAL(dy)); 750 p += strlen(p); 751 } 752 if (hr > 0) 753 { 754 (void) sprintf(p, ", %d hour%s", hr, PLURAL(hr)); 755 p += strlen(p); 756 } 757 if (mi > 0) 758 { 759 (void) sprintf(p, ", %d minute%s", mi, PLURAL(mi)); 760 p += strlen(p); 761 } 762 if (se > 0) 763 { 764 (void) sprintf(p, ", %d second%s", se, PLURAL(se)); 765 p += strlen(p); 766 } 767 768 return (buf + 2); 769 } 770 /* 771 ** CURTIME -- return current time. 772 ** 773 ** Parameters: 774 ** none. 775 ** 776 ** Returns: 777 ** the current time. 778 ** 779 ** Side Effects: 780 ** none. 781 */ 782 783 time_t 784 curtime() 785 { 786 auto time_t t; 787 788 (void) time(&t); 789 return (t); 790 } 791