1 /* 2 * Copyright (c) 1983, 1995 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)util.c 8.66 (Berkeley) 05/13/95"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sysexits.h> 15 /* 16 ** STRIPQUOTES -- Strip quotes & quote bits from a string. 17 ** 18 ** Runs through a string and strips off unquoted quote 19 ** characters and quote bits. This is done in place. 20 ** 21 ** Parameters: 22 ** s -- the string to strip. 23 ** 24 ** Returns: 25 ** none. 26 ** 27 ** Side Effects: 28 ** none. 29 ** 30 ** Called By: 31 ** deliver 32 */ 33 34 stripquotes(s) 35 char *s; 36 { 37 register char *p; 38 register char *q; 39 register char c; 40 41 if (s == NULL) 42 return; 43 44 p = q = s; 45 do 46 { 47 c = *p++; 48 if (c == '\\') 49 c = *p++; 50 else if (c == '"') 51 continue; 52 *q++ = c; 53 } while (c != '\0'); 54 } 55 /* 56 ** XALLOC -- Allocate memory and bitch wildly on failure. 57 ** 58 ** THIS IS A CLUDGE. This should be made to give a proper 59 ** error -- but after all, what can we do? 60 ** 61 ** Parameters: 62 ** sz -- size of area to allocate. 63 ** 64 ** Returns: 65 ** pointer to data region. 66 ** 67 ** Side Effects: 68 ** Memory is allocated. 69 */ 70 71 char * 72 xalloc(sz) 73 register int sz; 74 { 75 register char *p; 76 77 /* some systems can't handle size zero mallocs */ 78 if (sz <= 0) 79 sz = 1; 80 81 p = malloc((unsigned) sz); 82 if (p == NULL) 83 { 84 syserr("Out of memory!!"); 85 abort(); 86 /* exit(EX_UNAVAILABLE); */ 87 } 88 return (p); 89 } 90 /* 91 ** COPYPLIST -- copy list of pointers. 92 ** 93 ** This routine is the equivalent of newstr for lists of 94 ** pointers. 95 ** 96 ** Parameters: 97 ** list -- list of pointers to copy. 98 ** Must be NULL terminated. 99 ** copycont -- if TRUE, copy the contents of the vector 100 ** (which must be a string) also. 101 ** 102 ** Returns: 103 ** a copy of 'list'. 104 ** 105 ** Side Effects: 106 ** none. 107 */ 108 109 char ** 110 copyplist(list, copycont) 111 char **list; 112 bool copycont; 113 { 114 register char **vp; 115 register char **newvp; 116 117 for (vp = list; *vp != NULL; vp++) 118 continue; 119 120 vp++; 121 122 newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 123 bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 124 125 if (copycont) 126 { 127 for (vp = newvp; *vp != NULL; vp++) 128 *vp = newstr(*vp); 129 } 130 131 return (newvp); 132 } 133 /* 134 ** COPYQUEUE -- copy address queue. 135 ** 136 ** This routine is the equivalent of newstr for address queues 137 ** addresses marked with QDONTSEND aren't copied 138 ** 139 ** Parameters: 140 ** addr -- list of address structures to copy. 141 ** 142 ** Returns: 143 ** a copy of 'addr'. 144 ** 145 ** Side Effects: 146 ** none. 147 */ 148 149 ADDRESS * 150 copyqueue(addr) 151 ADDRESS *addr; 152 { 153 register ADDRESS *newaddr; 154 ADDRESS *ret; 155 register ADDRESS **tail = &ret; 156 157 while (addr != NULL) 158 { 159 if (!bitset(QDONTSEND, addr->q_flags)) 160 { 161 newaddr = (ADDRESS *) xalloc(sizeof(ADDRESS)); 162 STRUCTCOPY(*addr, *newaddr); 163 *tail = newaddr; 164 tail = &newaddr->q_next; 165 } 166 addr = addr->q_next; 167 } 168 *tail = NULL; 169 170 return ret; 171 } 172 /* 173 ** PRINTAV -- print argument vector. 174 ** 175 ** Parameters: 176 ** av -- argument vector. 177 ** 178 ** Returns: 179 ** none. 180 ** 181 ** Side Effects: 182 ** prints av. 183 */ 184 185 printav(av) 186 register char **av; 187 { 188 while (*av != NULL) 189 { 190 if (tTd(0, 44)) 191 printf("\n\t%08x=", *av); 192 else 193 (void) putchar(' '); 194 xputs(*av++); 195 } 196 (void) putchar('\n'); 197 } 198 /* 199 ** LOWER -- turn letter into lower case. 200 ** 201 ** Parameters: 202 ** c -- character to turn into lower case. 203 ** 204 ** Returns: 205 ** c, in lower case. 206 ** 207 ** Side Effects: 208 ** none. 209 */ 210 211 char 212 lower(c) 213 register char c; 214 { 215 return((isascii(c) && isupper(c)) ? tolower(c) : c); 216 } 217 /* 218 ** XPUTS -- put string doing control escapes. 219 ** 220 ** Parameters: 221 ** s -- string to put. 222 ** 223 ** Returns: 224 ** none. 225 ** 226 ** Side Effects: 227 ** output to stdout 228 */ 229 230 xputs(s) 231 register char *s; 232 { 233 register int c; 234 register struct metamac *mp; 235 extern struct metamac MetaMacros[]; 236 237 if (s == NULL) 238 { 239 printf("<null>"); 240 return; 241 } 242 while ((c = (*s++ & 0377)) != '\0') 243 { 244 if (!isascii(c)) 245 { 246 if (c == MATCHREPL) 247 { 248 putchar('$'); 249 continue; 250 } 251 if (c == MACROEXPAND) 252 { 253 putchar('$'); 254 if (bitset(0200, *s)) 255 printf("{%s}", macname(*s++ & 0377)); 256 continue; 257 } 258 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 259 { 260 if ((mp->metaval & 0377) == c) 261 { 262 printf("$%c", mp->metaname); 263 break; 264 } 265 } 266 if (mp->metaname != '\0') 267 continue; 268 (void) putchar('\\'); 269 c &= 0177; 270 } 271 if (isprint(c)) 272 { 273 putchar(c); 274 continue; 275 } 276 277 /* wasn't a meta-macro -- find another way to print it */ 278 switch (c) 279 { 280 case '\n': 281 c = 'n'; 282 break; 283 284 case '\r': 285 c = 'r'; 286 break; 287 288 case '\t': 289 c = 't'; 290 break; 291 292 default: 293 (void) putchar('^'); 294 (void) putchar(c ^ 0100); 295 continue; 296 } 297 (void) putchar('\\'); 298 (void) putchar(c); 299 } 300 (void) fflush(stdout); 301 } 302 /* 303 ** MAKELOWER -- Translate a line into lower case 304 ** 305 ** Parameters: 306 ** p -- the string to translate. If NULL, return is 307 ** immediate. 308 ** 309 ** Returns: 310 ** none. 311 ** 312 ** Side Effects: 313 ** String pointed to by p is translated to lower case. 314 ** 315 ** Called By: 316 ** parse 317 */ 318 319 void 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 = tolower(c); 330 } 331 /* 332 ** BUILDFNAME -- build full name from gecos style entry. 333 ** 334 ** This routine interprets the strange entry that would appear 335 ** in the GECOS field of the password file. 336 ** 337 ** Parameters: 338 ** p -- name to build. 339 ** login -- the login name of this user (for &). 340 ** buf -- place to put the result. 341 ** 342 ** Returns: 343 ** none. 344 ** 345 ** Side Effects: 346 ** none. 347 */ 348 349 buildfname(gecos, login, buf) 350 register char *gecos; 351 char *login; 352 char *buf; 353 { 354 register char *p; 355 register char *bp = buf; 356 int l; 357 358 if (*gecos == '*') 359 gecos++; 360 361 /* find length of final string */ 362 l = 0; 363 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 364 { 365 if (*p == '&') 366 l += strlen(login); 367 else 368 l++; 369 } 370 371 /* now fill in buf */ 372 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 373 { 374 if (*p == '&') 375 { 376 (void) strcpy(bp, login); 377 *bp = toupper(*bp); 378 while (*bp != '\0') 379 bp++; 380 } 381 else 382 *bp++ = *p; 383 } 384 *bp = '\0'; 385 } 386 /* 387 ** SAFEFILE -- return true if a file exists and is safe for a user. 388 ** 389 ** Parameters: 390 ** fn -- filename to check. 391 ** uid -- user id to compare against. 392 ** gid -- group id to compare against. 393 ** uname -- user name to compare against (used for group 394 ** sets). 395 ** flags -- modifiers: 396 ** SFF_MUSTOWN -- "uid" must own this file. 397 ** SFF_NOSLINK -- file cannot be a symbolic link. 398 ** mode -- mode bits that must match. 399 ** st -- if set, points to a stat structure that will 400 ** get the stat info for the file. 401 ** 402 ** Returns: 403 ** 0 if fn exists, is owned by uid, and matches mode. 404 ** An errno otherwise. The actual errno is cleared. 405 ** 406 ** Side Effects: 407 ** none. 408 */ 409 410 #include <grp.h> 411 412 #ifndef S_IXOTH 413 # define S_IXOTH (S_IEXEC >> 6) 414 #endif 415 416 #ifndef S_IXGRP 417 # define S_IXGRP (S_IEXEC >> 3) 418 #endif 419 420 #ifndef S_IXUSR 421 # define S_IXUSR (S_IEXEC) 422 #endif 423 424 #define ST_MODE_NOFILE 0171147 /* unlikely to occur */ 425 426 int 427 safefile(fn, uid, gid, uname, flags, mode, st) 428 char *fn; 429 uid_t uid; 430 gid_t gid; 431 char *uname; 432 int flags; 433 int mode; 434 struct stat *st; 435 { 436 register char *p; 437 register struct group *gr = NULL; 438 struct stat stbuf; 439 440 if (tTd(54, 4)) 441 printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", 442 fn, uid, gid, flags, mode); 443 errno = 0; 444 if (st == NULL) 445 st = &stbuf; 446 447 if (!bitset(SFF_NOPATHCHECK, flags) || 448 (uid == 0 && !bitset(SFF_ROOTOK, flags))) 449 { 450 /* check the path to the file for acceptability */ 451 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') 452 { 453 *p = '\0'; 454 if (stat(fn, &stbuf) < 0) 455 break; 456 if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode)) 457 message("051 WARNING: writable directory %s", 458 fn); 459 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 460 { 461 if (bitset(S_IXOTH, stbuf.st_mode)) 462 continue; 463 break; 464 } 465 if (stbuf.st_uid == uid && 466 bitset(S_IXUSR, stbuf.st_mode)) 467 continue; 468 if (stbuf.st_gid == gid && 469 bitset(S_IXGRP, stbuf.st_mode)) 470 continue; 471 #ifndef NO_GROUP_SET 472 if (uname != NULL && 473 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 474 (gr = getgrgid(stbuf.st_gid)) != NULL)) 475 { 476 register char **gp; 477 478 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 479 if (strcmp(*gp, uname) == 0) 480 break; 481 if (gp != NULL && *gp != NULL && 482 bitset(S_IXGRP, stbuf.st_mode)) 483 continue; 484 } 485 #endif 486 if (!bitset(S_IXOTH, stbuf.st_mode)) 487 break; 488 } 489 if (p != NULL) 490 { 491 int ret = errno; 492 493 if (ret == 0) 494 ret = EACCES; 495 if (tTd(54, 4)) 496 printf("\t[dir %s] %s\n", fn, errstring(ret)); 497 *p = '/'; 498 return ret; 499 } 500 } 501 502 #ifdef HASLSTAT 503 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 504 : stat(fn, st)) < 0) 505 #else 506 if (stat(fn, st) < 0) 507 #endif 508 { 509 int ret = errno; 510 511 if (tTd(54, 4)) 512 printf("\t%s\n", errstring(ret)); 513 514 errno = 0; 515 if (!bitset(SFF_CREAT, flags)) 516 return ret; 517 518 /* check to see if legal to create the file */ 519 p = strrchr(fn, '/'); 520 if (p == NULL) 521 return ENOTDIR; 522 *p = '\0'; 523 if (stat(fn, &stbuf) >= 0) 524 { 525 int md = S_IWRITE|S_IEXEC; 526 if (stbuf.st_uid != uid) 527 md >>= 6; 528 if ((stbuf.st_mode & md) != md) 529 errno = EACCES; 530 } 531 ret = errno; 532 if (tTd(54, 4)) 533 printf("\t[final dir %s uid %d mode %o] %s\n", 534 fn, stbuf.st_uid, stbuf.st_mode, 535 errstring(ret)); 536 *p = '/'; 537 st->st_mode = ST_MODE_NOFILE; 538 return ret; 539 } 540 541 #ifdef S_ISLNK 542 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 543 { 544 if (tTd(54, 4)) 545 printf("\t[slink mode %o]\tEPERM\n", st->st_mode); 546 return EPERM; 547 } 548 #endif 549 if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 550 { 551 if (tTd(54, 4)) 552 printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode); 553 return EPERM; 554 } 555 if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode)) 556 { 557 if (tTd(29, 5)) 558 printf("failed (mode %o: x bits)\n", st->st_mode); 559 return EPERM; 560 } 561 562 if (bitset(SFF_SETUIDOK, flags)) 563 { 564 if (bitset(S_ISUID, st->st_mode) && 565 (st->st_uid != 0 || bitset(SFF_ROOTOK, flags))) 566 { 567 uid = st->st_uid; 568 uname = NULL; 569 } 570 if (bitset(S_ISGID, st->st_mode) && 571 (st->st_gid != 0 || bitset(SFF_ROOTOK, flags))) 572 gid = st->st_gid; 573 } 574 575 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 576 mode >>= 6; 577 else if (st->st_uid != uid) 578 { 579 mode >>= 3; 580 if (st->st_gid == gid) 581 ; 582 #ifndef NO_GROUP_SET 583 else if (uname != NULL && 584 ((gr != NULL && gr->gr_gid == st->st_gid) || 585 (gr = getgrgid(st->st_gid)) != NULL)) 586 { 587 register char **gp; 588 589 for (gp = gr->gr_mem; *gp != NULL; gp++) 590 if (strcmp(*gp, uname) == 0) 591 break; 592 if (*gp == NULL) 593 mode >>= 3; 594 } 595 #endif 596 else 597 mode >>= 3; 598 } 599 if (tTd(54, 4)) 600 printf("\t[uid %d, stat %o, mode %o] ", 601 st->st_uid, st->st_mode, mode); 602 if ((st->st_uid == uid || st->st_uid == 0 || 603 !bitset(SFF_MUSTOWN, flags)) && 604 (st->st_mode & mode) == mode) 605 { 606 if (tTd(54, 4)) 607 printf("\tOK\n"); 608 return 0; 609 } 610 if (tTd(54, 4)) 611 printf("\tEACCES\n"); 612 return EACCES; 613 } 614 /* 615 ** SAFEFOPEN -- do a file open with extra checking 616 ** 617 ** Parameters: 618 ** fn -- the file name to open. 619 ** omode -- the open-style mode flags. 620 ** cmode -- the create-style mode flags. 621 ** sff -- safefile flags. 622 ** 623 ** Returns: 624 ** Same as fopen. 625 */ 626 627 #ifndef O_ACCMODE 628 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 629 #endif 630 631 FILE * 632 safefopen(fn, omode, cmode, sff) 633 char *fn; 634 int omode; 635 int cmode; 636 int sff; 637 { 638 int rval; 639 FILE *fp; 640 int smode; 641 struct stat stb, sta; 642 extern char RealUserName[]; 643 644 if (bitset(O_CREAT, omode)) 645 sff |= SFF_CREAT; 646 smode = 0; 647 switch (omode & O_ACCMODE) 648 { 649 case O_RDONLY: 650 smode = S_IREAD; 651 break; 652 653 case O_WRONLY: 654 smode = S_IWRITE; 655 break; 656 657 case O_RDWR: 658 smode = S_IREAD|S_IWRITE; 659 break; 660 661 default: 662 smode = 0; 663 break; 664 } 665 if (bitset(SFF_OPENASROOT, sff)) 666 rval = safefile(fn, 0, 0, NULL, sff, smode, &stb); 667 else 668 rval = safefile(fn, RealUid, RealGid, RealUserName, 669 sff, smode, &stb); 670 if (rval != 0) 671 { 672 errno = rval; 673 return NULL; 674 } 675 if (stb.st_mode == ST_MODE_NOFILE) 676 omode |= O_EXCL; 677 678 fp = dfopen(fn, omode, cmode); 679 if (fp == NULL) 680 return NULL; 681 if (bitset(O_EXCL, omode)) 682 return fp; 683 if (fstat(fileno(fp), &sta) < 0 || 684 sta.st_nlink != stb.st_nlink || 685 sta.st_dev != stb.st_dev || 686 sta.st_ino != stb.st_ino || 687 sta.st_uid != stb.st_uid || 688 sta.st_gid != stb.st_gid) 689 { 690 syserr("554 cannot open: file %s changed after open", fn); 691 fclose(fp); 692 errno = EPERM; 693 return NULL; 694 } 695 return fp; 696 } 697 /* 698 ** FIXCRLF -- fix <CR><LF> in line. 699 ** 700 ** Looks for the <CR><LF> combination and turns it into the 701 ** UNIX canonical <NL> character. It only takes one line, 702 ** i.e., it is assumed that the first <NL> found is the end 703 ** of the line. 704 ** 705 ** Parameters: 706 ** line -- the line to fix. 707 ** stripnl -- if true, strip the newline also. 708 ** 709 ** Returns: 710 ** none. 711 ** 712 ** Side Effects: 713 ** line is changed in place. 714 */ 715 716 fixcrlf(line, stripnl) 717 char *line; 718 bool stripnl; 719 { 720 register char *p; 721 722 p = strchr(line, '\n'); 723 if (p == NULL) 724 return; 725 if (p > line && p[-1] == '\r') 726 p--; 727 if (!stripnl) 728 *p++ = '\n'; 729 *p = '\0'; 730 } 731 /* 732 ** DFOPEN -- determined file open 733 ** 734 ** This routine has the semantics of fopen, except that it will 735 ** keep trying a few times to make this happen. The idea is that 736 ** on very loaded systems, we may run out of resources (inodes, 737 ** whatever), so this tries to get around it. 738 */ 739 740 struct omodes 741 { 742 int mask; 743 int mode; 744 char *farg; 745 } OpenModes[] = 746 { 747 O_ACCMODE, O_RDONLY, "r", 748 O_ACCMODE|O_APPEND, O_WRONLY, "w", 749 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", 750 O_TRUNC, 0, "w+", 751 O_APPEND, O_APPEND, "a+", 752 0, 0, "r+", 753 }; 754 755 FILE * 756 dfopen(filename, omode, cmode) 757 char *filename; 758 int omode; 759 int cmode; 760 { 761 register int tries; 762 int fd; 763 register struct omodes *om; 764 struct stat st; 765 766 for (om = OpenModes; om->mask != 0; om++) 767 if ((omode & om->mask) == om->mode) 768 break; 769 770 for (tries = 0; tries < 10; tries++) 771 { 772 sleep((unsigned) (10 * tries)); 773 errno = 0; 774 fd = open(filename, omode, cmode); 775 if (fd >= 0) 776 break; 777 switch (errno) 778 { 779 case ENFILE: /* system file table full */ 780 case EINTR: /* interrupted syscall */ 781 #ifdef ETXTBSY 782 case ETXTBSY: /* Apollo: net file locked */ 783 #endif 784 continue; 785 } 786 break; 787 } 788 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) 789 { 790 int locktype; 791 792 /* lock the file to avoid accidental conflicts */ 793 if ((omode & O_ACCMODE) != O_RDONLY) 794 locktype = LOCK_EX; 795 else 796 locktype = LOCK_SH; 797 (void) lockfile(fd, filename, NULL, locktype); 798 errno = 0; 799 } 800 if (fd < 0) 801 return NULL; 802 else 803 return fdopen(fd, om->farg); 804 } 805 /* 806 ** PUTLINE -- put a line like fputs obeying SMTP conventions 807 ** 808 ** This routine always guarantees outputing a newline (or CRLF, 809 ** as appropriate) at the end of the string. 810 ** 811 ** Parameters: 812 ** l -- line to put. 813 ** mci -- the mailer connection information. 814 ** 815 ** Returns: 816 ** none 817 ** 818 ** Side Effects: 819 ** output of l to fp. 820 */ 821 822 putline(l, mci) 823 register char *l; 824 register MCI *mci; 825 { 826 extern void putxline(); 827 828 putxline(l, mci, PXLF_MAPFROM); 829 } 830 /* 831 ** PUTXLINE -- putline with flags bits. 832 ** 833 ** This routine always guarantees outputing a newline (or CRLF, 834 ** as appropriate) at the end of the string. 835 ** 836 ** Parameters: 837 ** l -- line to put. 838 ** mci -- the mailer connection information. 839 ** pxflags -- flag bits: 840 ** PXLF_MAPFROM -- map From_ to >From_. 841 ** PXLF_STRIP8BIT -- strip 8th bit. 842 ** 843 ** Returns: 844 ** none 845 ** 846 ** Side Effects: 847 ** output of l to fp. 848 */ 849 850 void 851 putxline(l, mci, pxflags) 852 register char *l; 853 register MCI *mci; 854 int pxflags; 855 { 856 register char *p; 857 register char svchar; 858 int slop = 0; 859 860 /* strip out 0200 bits -- these can look like TELNET protocol */ 861 if (bitset(MCIF_7BIT, mci->mci_flags) || 862 bitset(PXLF_STRIP8BIT, pxflags)) 863 { 864 for (p = l; (svchar = *p) != '\0'; ++p) 865 if (bitset(0200, svchar)) 866 *p = svchar &~ 0200; 867 } 868 869 do 870 { 871 /* find the end of the line */ 872 p = strchr(l, '\n'); 873 if (p == NULL) 874 p = &l[strlen(l)]; 875 876 if (TrafficLogFile != NULL) 877 fprintf(TrafficLogFile, "%05d >>> ", getpid()); 878 879 /* check for line overflow */ 880 while (mci->mci_mailer->m_linelimit > 0 && 881 (p - l + slop) > mci->mci_mailer->m_linelimit) 882 { 883 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 884 885 svchar = *q; 886 *q = '\0'; 887 if (l[0] == '.' && slop == 0 && 888 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 889 { 890 (void) putc('.', mci->mci_out); 891 if (TrafficLogFile != NULL) 892 (void) putc('.', TrafficLogFile); 893 } 894 else if (l[0] == 'F' && slop == 0 && 895 bitset(PXLF_MAPFROM, pxflags) && 896 strncmp(l, "From ", 5) == 0 && 897 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 898 { 899 (void) putc('>', mci->mci_out); 900 if (TrafficLogFile != NULL) 901 (void) putc('>', TrafficLogFile); 902 } 903 fputs(l, mci->mci_out); 904 (void) putc('!', mci->mci_out); 905 fputs(mci->mci_mailer->m_eol, mci->mci_out); 906 (void) putc(' ', mci->mci_out); 907 if (TrafficLogFile != NULL) 908 fprintf(TrafficLogFile, "%s!\n%05d >>> ", 909 l, getpid()); 910 *q = svchar; 911 l = q; 912 slop = 1; 913 } 914 915 /* output last part */ 916 if (l[0] == '.' && slop == 0 && 917 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 918 { 919 (void) putc('.', mci->mci_out); 920 if (TrafficLogFile != NULL) 921 (void) putc('.', TrafficLogFile); 922 } 923 if (TrafficLogFile != NULL) 924 fprintf(TrafficLogFile, "%.*s\n", p - l, l); 925 for ( ; l < p; ++l) 926 (void) putc(*l, mci->mci_out); 927 fputs(mci->mci_mailer->m_eol, mci->mci_out); 928 if (*l == '\n') 929 ++l; 930 } while (l[0] != '\0'); 931 } 932 /* 933 ** XUNLINK -- unlink a file, doing logging as appropriate. 934 ** 935 ** Parameters: 936 ** f -- name of file to unlink. 937 ** 938 ** Returns: 939 ** none. 940 ** 941 ** Side Effects: 942 ** f is unlinked. 943 */ 944 945 xunlink(f) 946 char *f; 947 { 948 register int i; 949 950 # ifdef LOG 951 if (LogLevel > 98) 952 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); 953 # endif /* LOG */ 954 955 i = unlink(f); 956 # ifdef LOG 957 if (i < 0 && LogLevel > 97) 958 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 959 # endif /* LOG */ 960 } 961 /* 962 ** XFCLOSE -- close a file, doing logging as appropriate. 963 ** 964 ** Parameters: 965 ** fp -- file pointer for the file to close 966 ** a, b -- miscellaneous crud to print for debugging 967 ** 968 ** Returns: 969 ** none. 970 ** 971 ** Side Effects: 972 ** fp is closed. 973 */ 974 975 xfclose(fp, a, b) 976 FILE *fp; 977 char *a, *b; 978 { 979 if (tTd(53, 99)) 980 printf("xfclose(%x) %s %s\n", fp, a, b); 981 #ifdef XDEBUG 982 if (fileno(fp) == 1) 983 syserr("xfclose(%s %s): fd = 1", a, b); 984 #endif 985 if (fclose(fp) < 0 && tTd(53, 99)) 986 printf("xfclose FAILURE: %s\n", errstring(errno)); 987 } 988 /* 989 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 990 ** 991 ** Parameters: 992 ** buf -- place to put the input line. 993 ** siz -- size of buf. 994 ** fp -- file to read from. 995 ** timeout -- the timeout before error occurs. 996 ** during -- what we are trying to read (for error messages). 997 ** 998 ** Returns: 999 ** NULL on error (including timeout). This will also leave 1000 ** buf containing a null string. 1001 ** buf otherwise. 1002 ** 1003 ** Side Effects: 1004 ** none. 1005 */ 1006 1007 static jmp_buf CtxReadTimeout; 1008 static void readtimeout(); 1009 1010 char * 1011 sfgets(buf, siz, fp, timeout, during) 1012 char *buf; 1013 int siz; 1014 FILE *fp; 1015 time_t timeout; 1016 char *during; 1017 { 1018 register EVENT *ev = NULL; 1019 register char *p; 1020 1021 if (fp == NULL) 1022 { 1023 buf[0] = '\0'; 1024 return NULL; 1025 } 1026 1027 /* set the timeout */ 1028 if (timeout != 0) 1029 { 1030 if (setjmp(CtxReadTimeout) != 0) 1031 { 1032 # ifdef LOG 1033 syslog(LOG_NOTICE, 1034 "timeout waiting for input from %s during %s\n", 1035 CurHostName? CurHostName: "local", during); 1036 # endif 1037 errno = 0; 1038 usrerr("451 timeout waiting for input during %s", 1039 during); 1040 buf[0] = '\0'; 1041 #ifdef XDEBUG 1042 checkfd012(during); 1043 #endif 1044 return (NULL); 1045 } 1046 ev = setevent(timeout, readtimeout, 0); 1047 } 1048 1049 /* try to read */ 1050 p = NULL; 1051 while (!feof(fp) && !ferror(fp)) 1052 { 1053 errno = 0; 1054 p = fgets(buf, siz, fp); 1055 if (p != NULL || errno != EINTR) 1056 break; 1057 clearerr(fp); 1058 } 1059 1060 /* clear the event if it has not sprung */ 1061 clrevent(ev); 1062 1063 /* clean up the books and exit */ 1064 LineNumber++; 1065 if (p == NULL) 1066 { 1067 buf[0] = '\0'; 1068 if (TrafficLogFile != NULL) 1069 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); 1070 return (NULL); 1071 } 1072 if (TrafficLogFile != NULL) 1073 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); 1074 if (SevenBitInput) 1075 { 1076 for (p = buf; *p != '\0'; p++) 1077 *p &= ~0200; 1078 } 1079 else if (!HasEightBits) 1080 { 1081 for (p = buf; *p != '\0'; p++) 1082 { 1083 if (bitset(0200, *p)) 1084 { 1085 HasEightBits = TRUE; 1086 break; 1087 } 1088 } 1089 } 1090 return (buf); 1091 } 1092 1093 static void 1094 readtimeout(timeout) 1095 time_t timeout; 1096 { 1097 longjmp(CtxReadTimeout, 1); 1098 } 1099 /* 1100 ** FGETFOLDED -- like fgets, but know about folded lines. 1101 ** 1102 ** Parameters: 1103 ** buf -- place to put result. 1104 ** n -- bytes available. 1105 ** f -- file to read from. 1106 ** 1107 ** Returns: 1108 ** input line(s) on success, NULL on error or EOF. 1109 ** This will normally be buf -- unless the line is too 1110 ** long, when it will be xalloc()ed. 1111 ** 1112 ** Side Effects: 1113 ** buf gets lines from f, with continuation lines (lines 1114 ** with leading white space) appended. CRLF's are mapped 1115 ** into single newlines. Any trailing NL is stripped. 1116 */ 1117 1118 char * 1119 fgetfolded(buf, n, f) 1120 char *buf; 1121 register int n; 1122 FILE *f; 1123 { 1124 register char *p = buf; 1125 char *bp = buf; 1126 register int i; 1127 1128 n--; 1129 while ((i = getc(f)) != EOF) 1130 { 1131 if (i == '\r') 1132 { 1133 i = getc(f); 1134 if (i != '\n') 1135 { 1136 if (i != EOF) 1137 (void) ungetc(i, f); 1138 i = '\r'; 1139 } 1140 } 1141 if (--n <= 0) 1142 { 1143 /* allocate new space */ 1144 char *nbp; 1145 int nn; 1146 1147 nn = (p - bp); 1148 if (nn < MEMCHUNKSIZE) 1149 nn *= 2; 1150 else 1151 nn += MEMCHUNKSIZE; 1152 nbp = xalloc(nn); 1153 bcopy(bp, nbp, p - bp); 1154 p = &nbp[p - bp]; 1155 if (bp != buf) 1156 free(bp); 1157 bp = nbp; 1158 n = nn - (p - bp); 1159 } 1160 *p++ = i; 1161 if (i == '\n') 1162 { 1163 LineNumber++; 1164 i = getc(f); 1165 if (i != EOF) 1166 (void) ungetc(i, f); 1167 if (i != ' ' && i != '\t') 1168 break; 1169 } 1170 } 1171 if (p == bp) 1172 return (NULL); 1173 *--p = '\0'; 1174 return (bp); 1175 } 1176 /* 1177 ** CURTIME -- return current time. 1178 ** 1179 ** Parameters: 1180 ** none. 1181 ** 1182 ** Returns: 1183 ** the current time. 1184 ** 1185 ** Side Effects: 1186 ** none. 1187 */ 1188 1189 time_t 1190 curtime() 1191 { 1192 auto time_t t; 1193 1194 (void) time(&t); 1195 return (t); 1196 } 1197 /* 1198 ** ATOBOOL -- convert a string representation to boolean. 1199 ** 1200 ** Defaults to "TRUE" 1201 ** 1202 ** Parameters: 1203 ** s -- string to convert. Takes "tTyY" as true, 1204 ** others as false. 1205 ** 1206 ** Returns: 1207 ** A boolean representation of the string. 1208 ** 1209 ** Side Effects: 1210 ** none. 1211 */ 1212 1213 bool 1214 atobool(s) 1215 register char *s; 1216 { 1217 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1218 return (TRUE); 1219 return (FALSE); 1220 } 1221 /* 1222 ** ATOOCT -- convert a string representation to octal. 1223 ** 1224 ** Parameters: 1225 ** s -- string to convert. 1226 ** 1227 ** Returns: 1228 ** An integer representing the string interpreted as an 1229 ** octal number. 1230 ** 1231 ** Side Effects: 1232 ** none. 1233 */ 1234 1235 atooct(s) 1236 register char *s; 1237 { 1238 register int i = 0; 1239 1240 while (*s >= '0' && *s <= '7') 1241 i = (i << 3) | (*s++ - '0'); 1242 return (i); 1243 } 1244 /* 1245 ** WAITFOR -- wait for a particular process id. 1246 ** 1247 ** Parameters: 1248 ** pid -- process id to wait for. 1249 ** 1250 ** Returns: 1251 ** status of pid. 1252 ** -1 if pid never shows up. 1253 ** 1254 ** Side Effects: 1255 ** none. 1256 */ 1257 1258 int 1259 waitfor(pid) 1260 int pid; 1261 { 1262 #ifdef WAITUNION 1263 union wait st; 1264 #else 1265 auto int st; 1266 #endif 1267 int i; 1268 1269 do 1270 { 1271 errno = 0; 1272 i = wait(&st); 1273 } while ((i >= 0 || errno == EINTR) && i != pid); 1274 if (i < 0) 1275 return -1; 1276 #ifdef WAITUNION 1277 return st.w_status; 1278 #else 1279 return st; 1280 #endif 1281 } 1282 /* 1283 ** BITINTERSECT -- tell if two bitmaps intersect 1284 ** 1285 ** Parameters: 1286 ** a, b -- the bitmaps in question 1287 ** 1288 ** Returns: 1289 ** TRUE if they have a non-null intersection 1290 ** FALSE otherwise 1291 ** 1292 ** Side Effects: 1293 ** none. 1294 */ 1295 1296 bool 1297 bitintersect(a, b) 1298 BITMAP a; 1299 BITMAP b; 1300 { 1301 int i; 1302 1303 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1304 if ((a[i] & b[i]) != 0) 1305 return (TRUE); 1306 return (FALSE); 1307 } 1308 /* 1309 ** BITZEROP -- tell if a bitmap is all zero 1310 ** 1311 ** Parameters: 1312 ** map -- the bit map to check 1313 ** 1314 ** Returns: 1315 ** TRUE if map is all zero. 1316 ** FALSE if there are any bits set in map. 1317 ** 1318 ** Side Effects: 1319 ** none. 1320 */ 1321 1322 bool 1323 bitzerop(map) 1324 BITMAP map; 1325 { 1326 int i; 1327 1328 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1329 if (map[i] != 0) 1330 return (FALSE); 1331 return (TRUE); 1332 } 1333 /* 1334 ** STRCONTAINEDIN -- tell if one string is contained in another 1335 ** 1336 ** Parameters: 1337 ** a -- possible substring. 1338 ** b -- possible superstring. 1339 ** 1340 ** Returns: 1341 ** TRUE if a is contained in b. 1342 ** FALSE otherwise. 1343 */ 1344 1345 bool 1346 strcontainedin(a, b) 1347 register char *a; 1348 register char *b; 1349 { 1350 int la; 1351 int lb; 1352 int c; 1353 1354 la = strlen(a); 1355 lb = strlen(b); 1356 c = *a; 1357 if (isascii(c) && isupper(c)) 1358 c = tolower(c); 1359 for (; lb-- >= la; b++) 1360 { 1361 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1362 continue; 1363 if (strncasecmp(a, b, la) == 0) 1364 return TRUE; 1365 } 1366 return FALSE; 1367 } 1368 /* 1369 ** CHECKFD012 -- check low numbered file descriptors 1370 ** 1371 ** File descriptors 0, 1, and 2 should be open at all times. 1372 ** This routine verifies that, and fixes it if not true. 1373 ** 1374 ** Parameters: 1375 ** where -- a tag printed if the assertion failed 1376 ** 1377 ** Returns: 1378 ** none 1379 */ 1380 1381 checkfd012(where) 1382 char *where; 1383 { 1384 #ifdef XDEBUG 1385 register int i; 1386 struct stat stbuf; 1387 1388 for (i = 0; i < 3; i++) 1389 { 1390 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP) 1391 { 1392 /* oops.... */ 1393 int fd; 1394 1395 syserr("%s: fd %d not open", where, i); 1396 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); 1397 if (fd != i) 1398 { 1399 (void) dup2(fd, i); 1400 (void) close(fd); 1401 } 1402 } 1403 } 1404 #endif /* XDEBUG */ 1405 } 1406 /* 1407 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1408 ** 1409 ** Parameters: 1410 ** logit -- if set, send output to syslog; otherwise 1411 ** print for debugging. 1412 ** 1413 ** Returns: 1414 ** none. 1415 */ 1416 1417 #include <arpa/inet.h> 1418 1419 printopenfds(logit) 1420 bool logit; 1421 { 1422 register int fd; 1423 extern int DtableSize; 1424 1425 for (fd = 0; fd < DtableSize; fd++) 1426 dumpfd(fd, FALSE, logit); 1427 } 1428 /* 1429 ** DUMPFD -- dump a file descriptor 1430 ** 1431 ** Parameters: 1432 ** fd -- the file descriptor to dump. 1433 ** printclosed -- if set, print a notification even if 1434 ** it is closed; otherwise print nothing. 1435 ** logit -- if set, send output to syslog instead of stdout. 1436 */ 1437 1438 dumpfd(fd, printclosed, logit) 1439 int fd; 1440 bool printclosed; 1441 bool logit; 1442 { 1443 register char *p; 1444 char *hp; 1445 char *fmtstr; 1446 SOCKADDR sa; 1447 auto int slen; 1448 struct stat st; 1449 char buf[200]; 1450 extern char *hostnamebyanyaddr(); 1451 1452 p = buf; 1453 sprintf(p, "%3d: ", fd); 1454 p += strlen(p); 1455 1456 if (fstat(fd, &st) < 0) 1457 { 1458 if (printclosed || errno != EBADF) 1459 { 1460 sprintf(p, "CANNOT STAT (%s)", errstring(errno)); 1461 goto printit; 1462 } 1463 return; 1464 } 1465 1466 slen = fcntl(fd, F_GETFL, NULL); 1467 if (slen != -1) 1468 { 1469 sprintf(p, "fl=0x%x, ", slen); 1470 p += strlen(p); 1471 } 1472 1473 sprintf(p, "mode=%o: ", st.st_mode); 1474 p += strlen(p); 1475 switch (st.st_mode & S_IFMT) 1476 { 1477 #ifdef S_IFSOCK 1478 case S_IFSOCK: 1479 sprintf(p, "SOCK "); 1480 p += strlen(p); 1481 slen = sizeof sa; 1482 if (getsockname(fd, &sa.sa, &slen) < 0) 1483 sprintf(p, "(%s)", errstring(errno)); 1484 else 1485 { 1486 hp = hostnamebyanyaddr(&sa); 1487 if (sa.sa.sa_family == AF_INET) 1488 sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port)); 1489 else 1490 sprintf(p, "%s", hp); 1491 } 1492 p += strlen(p); 1493 sprintf(p, "->"); 1494 p += strlen(p); 1495 slen = sizeof sa; 1496 if (getpeername(fd, &sa.sa, &slen) < 0) 1497 sprintf(p, "(%s)", errstring(errno)); 1498 else 1499 { 1500 hp = hostnamebyanyaddr(&sa); 1501 if (sa.sa.sa_family == AF_INET) 1502 sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port)); 1503 else 1504 sprintf(p, "%s", hp); 1505 } 1506 break; 1507 #endif 1508 1509 case S_IFCHR: 1510 sprintf(p, "CHR: "); 1511 p += strlen(p); 1512 goto defprint; 1513 1514 case S_IFBLK: 1515 sprintf(p, "BLK: "); 1516 p += strlen(p); 1517 goto defprint; 1518 1519 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1520 case S_IFIFO: 1521 sprintf(p, "FIFO: "); 1522 p += strlen(p); 1523 goto defprint; 1524 #endif 1525 1526 #ifdef S_IFDIR 1527 case S_IFDIR: 1528 sprintf(p, "DIR: "); 1529 p += strlen(p); 1530 goto defprint; 1531 #endif 1532 1533 #ifdef S_IFLNK 1534 case S_IFLNK: 1535 sprintf(p, "LNK: "); 1536 p += strlen(p); 1537 goto defprint; 1538 #endif 1539 1540 default: 1541 defprint: 1542 if (sizeof st.st_size > sizeof (long)) 1543 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; 1544 else 1545 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; 1546 sprintf(p, fmtstr, 1547 major(st.st_dev), minor(st.st_dev), st.st_ino, 1548 st.st_nlink, st.st_uid, st.st_gid, st.st_size); 1549 break; 1550 } 1551 1552 printit: 1553 #ifdef LOG 1554 if (logit) 1555 syslog(LOG_DEBUG, "%s", buf); 1556 else 1557 #endif 1558 printf("%s\n", buf); 1559 } 1560 /* 1561 ** SHORTENSTRING -- return short version of a string 1562 ** 1563 ** If the string is already short, just return it. If it is too 1564 ** long, return the head and tail of the string. 1565 ** 1566 ** Parameters: 1567 ** s -- the string to shorten. 1568 ** m -- the max length of the string. 1569 ** 1570 ** Returns: 1571 ** Either s or a short version of s. 1572 */ 1573 1574 #ifndef MAXSHORTSTR 1575 # define MAXSHORTSTR 203 1576 #endif 1577 1578 char * 1579 shortenstring(s, m) 1580 register char *s; 1581 int m; 1582 { 1583 int l; 1584 static char buf[MAXSHORTSTR + 1]; 1585 1586 l = strlen(s); 1587 if (l < m) 1588 return s; 1589 if (m > MAXSHORTSTR) 1590 m = MAXSHORTSTR; 1591 else if (m < 10) 1592 { 1593 if (m < 5) 1594 { 1595 strncpy(buf, s, m); 1596 buf[m] = '\0'; 1597 return buf; 1598 } 1599 strncpy(buf, s, m - 3); 1600 strcpy(buf + m - 3, "..."); 1601 return buf; 1602 } 1603 m = (m - 3) / 2; 1604 strncpy(buf, s, m); 1605 strcpy(buf + m, "..."); 1606 strcpy(buf + m + 3, s + l - m); 1607 return buf; 1608 } 1609 /* 1610 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1611 ** 1612 ** Parameters: 1613 ** host -- the host to shorten (stripped in place). 1614 ** 1615 ** Returns: 1616 ** none. 1617 */ 1618 1619 void 1620 shorten_hostname(host) 1621 char host[]; 1622 { 1623 register char *p; 1624 char *mydom; 1625 int i; 1626 1627 /* strip off final dot */ 1628 p = &host[strlen(host) - 1]; 1629 if (*p == '.') 1630 *p = '\0'; 1631 1632 /* see if there is any domain at all -- if not, we are done */ 1633 p = strchr(host, '.'); 1634 if (p == NULL) 1635 return; 1636 1637 /* yes, we have a domain -- see if it looks like us */ 1638 mydom = macvalue('m', CurEnv); 1639 if (mydom == NULL) 1640 mydom = ""; 1641 i = strlen(++p); 1642 if (strncasecmp(p, mydom, i) == 0 && 1643 (mydom[i] == '.' || mydom[i] == '\0')) 1644 *--p = '\0'; 1645 } 1646 /* 1647 ** GET_COLUMN -- look up a Column in a line buffer 1648 ** 1649 ** Parameters: 1650 ** line -- the raw text line to search. 1651 ** col -- the column number to fetch. 1652 ** delim -- the delimiter between columns. If null, 1653 ** use white space. 1654 ** buf -- the output buffer. 1655 ** 1656 ** Returns: 1657 ** buf if successful. 1658 ** NULL otherwise. 1659 */ 1660 1661 char * 1662 get_column(line, col, delim, buf) 1663 char line[]; 1664 int col; 1665 char delim; 1666 char buf[]; 1667 { 1668 char *p; 1669 char *begin, *end; 1670 int i; 1671 char delimbuf[3]; 1672 1673 if (delim == '\0') 1674 strcpy(delimbuf, "\t "); 1675 else 1676 { 1677 delimbuf[0] = delim; 1678 delimbuf[1] = '\0'; 1679 } 1680 1681 p = line; 1682 if (*p == '\0') 1683 return NULL; /* line empty */ 1684 if (*p == delim && col == 0) 1685 return NULL; /* first column empty */ 1686 1687 begin = line; 1688 1689 if (col == 0 && delim == '\0') 1690 { 1691 while (*begin && isspace(*begin)) 1692 begin++; 1693 } 1694 1695 for (i = 0; i < col; i++) 1696 { 1697 if ((begin = strpbrk(begin, delimbuf)) == NULL) 1698 return NULL; /* no such column */ 1699 begin++; 1700 if (delim == '\0') 1701 { 1702 while (*begin && isspace(*begin)) 1703 begin++; 1704 } 1705 } 1706 1707 end = strpbrk(begin, delimbuf); 1708 if (end == NULL) 1709 { 1710 strcpy(buf, begin); 1711 } 1712 else 1713 { 1714 strncpy(buf, begin, end - begin); 1715 buf[end - begin] = '\0'; 1716 } 1717 return buf; 1718 } 1719 /* 1720 ** CLEANSTRCPY -- copy string keeping out bogus characters 1721 ** 1722 ** Parameters: 1723 ** t -- "to" string. 1724 ** f -- "from" string. 1725 ** l -- length of space available in "to" string. 1726 ** 1727 ** Returns: 1728 ** none. 1729 */ 1730 1731 void 1732 cleanstrcpy(t, f, l) 1733 register char *t; 1734 register char *f; 1735 int l; 1736 { 1737 #ifdef LOG 1738 /* check for newlines and log if necessary */ 1739 (void) denlstring(f, TRUE, TRUE); 1740 #endif 1741 1742 l--; 1743 while (l > 0 && *f != '\0') 1744 { 1745 if (isascii(*f) && 1746 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 1747 { 1748 l--; 1749 *t++ = *f; 1750 } 1751 f++; 1752 } 1753 *t = '\0'; 1754 } 1755 /* 1756 ** DENLSTRING -- convert newlines in a string to spaces 1757 ** 1758 ** Parameters: 1759 ** s -- the input string 1760 ** strict -- if set, don't permit continuation lines. 1761 ** logattacks -- if set, log attempted attacks. 1762 ** 1763 ** Returns: 1764 ** A pointer to a version of the string with newlines 1765 ** mapped to spaces. This should be copied. 1766 */ 1767 1768 char * 1769 denlstring(s, strict, logattacks) 1770 char *s; 1771 bool strict; 1772 bool logattacks; 1773 { 1774 register char *p; 1775 int l; 1776 static char *bp = NULL; 1777 static int bl = 0; 1778 1779 p = s; 1780 while ((p = strchr(p, '\n')) != NULL) 1781 if (strict || (*++p != ' ' && *p != '\t')) 1782 break; 1783 if (p == NULL) 1784 return s; 1785 1786 l = strlen(s) + 1; 1787 if (bl < l) 1788 { 1789 /* allocate more space */ 1790 if (bp != NULL) 1791 free(bp); 1792 bp = xalloc(l); 1793 bl = l; 1794 } 1795 strcpy(bp, s); 1796 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 1797 *p++ = ' '; 1798 1799 /* 1800 #ifdef LOG 1801 if (logattacks) 1802 { 1803 syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"", 1804 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 1805 shortenstring(bp, 80)); 1806 } 1807 #endif 1808 */ 1809 1810 return bp; 1811 } 1812