1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 1985-2002 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 /* 32 * University Copyright- Copyright (c) 1982, 1986, 1988 33 * The Regents of the University of California 34 * All Rights Reserved 35 * 36 * University Acknowledgment- Portions of this document are derived from 37 * software developed by the University of California, Berkeley, and its 38 * contributors. 39 */ 40 41 #include "rcv.h" 42 #include <locale.h> 43 44 /* 45 * mailx -- a modified version of a University of California at Berkeley 46 * mail program 47 * 48 * Auxiliary functions. 49 */ 50 51 static char *phrase(char *name, int token, int comma); 52 static char *ripoff(register char *buf); 53 54 /* 55 * Return a pointer to a dynamic copy of the argument. 56 */ 57 58 char * 59 savestr(char *str) 60 { 61 register char *cp, *cp2, *top; 62 63 for (cp = str; *cp; cp++) 64 ; 65 top = (char *)salloc((unsigned)(cp-str + 1)); 66 if (top == NOSTR) 67 return(NOSTR); 68 for (cp = str, cp2 = top; *cp; cp++) 69 *cp2++ = *cp; 70 *cp2 = 0; 71 return(top); 72 } 73 74 /* 75 * Announce a fatal error and die. 76 */ 77 78 void 79 panic(char *str) 80 { 81 fprintf(stderr, gettext("mailx: Panic - %s\n"), str); 82 exit(1); 83 /* NOTREACHED */ 84 } 85 86 /* 87 * Touch the named message by setting its MTOUCH flag. 88 * Touched messages have the effect of not being sent 89 * back to the system mailbox on exit. 90 */ 91 92 void 93 touch(int mesg) 94 { 95 register struct message *mp; 96 97 if (mesg < 1 || mesg > msgCount) 98 return; 99 mp = &message[mesg-1]; 100 mp->m_flag |= MTOUCH; 101 if ((mp->m_flag & MREAD) == 0) 102 mp->m_flag |= MREAD|MSTATUS; 103 } 104 105 /* 106 * Test to see if the passed file name is a directory. 107 * Return true if it is. 108 */ 109 110 int 111 isdir(char name[]) 112 { 113 struct stat sbuf; 114 115 if (stat(name, &sbuf) < 0) 116 return(0); 117 return((sbuf.st_mode & S_IFMT) == S_IFDIR); 118 } 119 120 /* 121 * Count the number of arguments in the given string raw list. 122 */ 123 124 int 125 argcount(char **argv) 126 { 127 register char **ap; 128 129 for (ap = argv; *ap != NOSTR; ap++) 130 ; 131 return(ap-argv); 132 } 133 134 /* 135 * Return the desired header line from the passed message 136 * pointer (or NOSTR if the desired header field is not available). 137 * Read all the header lines and concatenate multiple instances of 138 * the requested header. 139 */ 140 141 char * 142 hfield(char field[], struct message *mp, char *(*add)(char *, char *)) 143 { 144 register FILE *ibuf; 145 char linebuf[LINESIZE]; 146 register long lc; 147 char *r = NOSTR; 148 149 ibuf = setinput(mp); 150 if ((lc = mp->m_lines) <= 0) 151 return(NOSTR); 152 if (readline(ibuf, linebuf) < 0) 153 return(NOSTR); 154 lc--; 155 while ((lc = gethfield(ibuf, linebuf, lc)) >= 0) 156 if (ishfield(linebuf, field)) 157 r = (*add)(r, hcontents(linebuf)); 158 return r; 159 } 160 161 /* 162 * Return the next header field found in the given message. 163 * Return > 0 if something found, <= 0 elsewise. 164 * Must deal with \ continuations & other such fraud. 165 */ 166 167 int 168 gethfield( 169 register FILE *f, 170 char linebuf[], 171 register long rem) 172 { 173 char line2[LINESIZE]; 174 register char *cp, *cp2; 175 register int c; 176 177 for (;;) { 178 if (rem <= 0) 179 return(-1); 180 if (readline(f, linebuf) < 0) 181 return(-1); 182 rem--; 183 if (strlen(linebuf) == 0) 184 return(-1); 185 if (isspace(linebuf[0])) 186 continue; 187 if (!headerp(linebuf)) 188 return(-1); 189 190 /* 191 * I guess we got a headline. 192 * Handle wraparounding 193 */ 194 195 for (;;) { 196 if (rem <= 0) 197 break; 198 c = getc(f); 199 ungetc(c, f); 200 if (!isspace(c) || c == '\n') 201 break; 202 if (readline(f, line2) < 0) 203 break; 204 rem--; 205 cp2 = line2; 206 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 207 ; 208 if (strlen(linebuf) + strlen(cp2) >= 209 (unsigned)LINESIZE-2) 210 break; 211 cp = &linebuf[strlen(linebuf)]; 212 while (cp > linebuf && 213 (isspace(cp[-1]) || cp[-1] == '\\')) 214 cp--; 215 *cp++ = ' '; 216 for (cp2 = line2; *cp2 != 0 && isspace(*cp2); cp2++) 217 ; 218 nstrcpy(cp, LINESIZE - (cp - linebuf), cp2); 219 } 220 if ((c = strlen(linebuf)) > 0) { 221 cp = &linebuf[c-1]; 222 while (cp > linebuf && isspace(*cp)) 223 cp--; 224 *++cp = 0; 225 } 226 return(rem); 227 } 228 /* NOTREACHED */ 229 } 230 231 /* 232 * Check whether the passed line is a header line of 233 * the desired breed. 234 */ 235 236 int 237 ishfield(char linebuf[], char field[]) 238 { 239 register char *cp; 240 241 if ((cp = strchr(linebuf, ':')) == NOSTR) 242 return(0); 243 if (cp == linebuf) 244 return(0); 245 *cp = 0; 246 if (icequal(linebuf, field)) { 247 *cp = ':'; 248 return(1); 249 } 250 *cp = ':'; 251 return(0); 252 } 253 254 /* 255 * Extract the non label information from the given header field 256 * and return it. 257 */ 258 259 char * 260 hcontents(char hfield[]) 261 { 262 register char *cp; 263 264 if ((cp = strchr(hfield, ':')) == NOSTR) 265 return(NOSTR); 266 cp++; 267 while (*cp && isspace(*cp)) 268 cp++; 269 return(cp); 270 } 271 272 /* 273 * Compare two strings, ignoring case. 274 */ 275 276 int 277 icequal(register char *s1, register char *s2) 278 { 279 280 while (toupper(*s1++) == toupper(*s2)) 281 if (*s2++ == 0) 282 return(1); 283 return(0); 284 } 285 286 /* 287 * Copy a string, lowercasing it as we go. Here dstsize is the size of 288 * the destination buffer dst. 289 */ 290 void 291 istrcpy(char *dst, int dstsize, char *src) 292 { 293 register char *cp, *cp2; 294 295 cp2 = dst; 296 cp = src; 297 298 while (--dstsize > 0 && *cp != '\0') 299 *cp2++ = tolower(*cp++); 300 *cp2 = '\0'; 301 } 302 303 /* 304 * The following code deals with input stacking to do source 305 * commands. All but the current file pointer are saved on 306 * the stack. 307 */ 308 309 static int ssp = -1; /* Top of file stack */ 310 static struct sstack { 311 FILE *s_file; /* File we were in. */ 312 int s_cond; /* Saved state of conditionals */ 313 int s_loading; /* Loading .mailrc, etc. */ 314 } *sstack; 315 316 /* 317 * Pushdown current input file and switch to a new one. 318 * Set the global flag "sourcing" so that others will realize 319 * that they are no longer reading from a tty (in all probability). 320 */ 321 322 int 323 source(char name[]) 324 { 325 register FILE *fi; 326 register char *cp; 327 328 if ((cp = expand(name)) == NOSTR) 329 return(1); 330 if ((fi = fopen(cp, "r")) == NULL) { 331 printf(gettext("Unable to open %s\n"), cp); 332 return(1); 333 } 334 335 if (!maxfiles) { 336 if ((maxfiles = (int)ulimit(4, 0)) < 0) 337 #ifndef _NFILE 338 # define _NFILE 20 339 #endif 340 maxfiles = _NFILE; 341 sstack = (struct sstack *)calloc(maxfiles, sizeof(struct sstack)); 342 if (sstack == NULL) { 343 printf(gettext( 344 "Couldn't allocate memory for sourcing stack\n")); 345 fclose(fi); 346 return(1); 347 } 348 } 349 350 sstack[++ssp].s_file = input; 351 sstack[ssp].s_cond = cond; 352 sstack[ssp].s_loading = loading; 353 loading = 0; 354 cond = CANY; 355 input = fi; 356 sourcing++; 357 return(0); 358 } 359 360 /* 361 * Pop the current input back to the previous level. 362 * Update the "sourcing" flag as appropriate. 363 */ 364 365 int 366 unstack(void) 367 { 368 if (ssp < 0) { 369 printf(gettext("\"Source\" stack over-pop.\n")); 370 sourcing = 0; 371 return(1); 372 } 373 fclose(input); 374 if (cond != CANY) 375 printf(gettext("Unmatched \"if\"\n")); 376 cond = sstack[ssp].s_cond; 377 loading = sstack[ssp].s_loading; 378 input = sstack[ssp--].s_file; 379 if (ssp < 0) 380 sourcing = loading; 381 return(0); 382 } 383 384 /* 385 * Touch the indicated file. 386 * This is nifty for the shell. 387 * If we have the utime() system call, this is better served 388 * by using that, since it will work for empty files. 389 * On non-utime systems, we must sleep a second, then read. 390 */ 391 392 void 393 alter(char name[]) 394 { 395 int rc = utime(name, utimep); 396 extern int errno; 397 398 if (rc != 0) { 399 fprintf(stderr, gettext("Cannot utime %s in aux:alter\n"), 400 name); 401 fprintf(stderr, gettext("Errno: %d\n"), errno); 402 } 403 } 404 405 /* 406 * Examine the passed line buffer and 407 * return true if it is all blanks and tabs. 408 */ 409 410 int 411 blankline(const char linebuf[]) 412 { 413 register const char *cp; 414 415 for (cp = linebuf; *cp; cp++) 416 if (!any(*cp, " \t")) 417 return(0); 418 return(1); 419 } 420 421 /* 422 * Skin an arpa net address according to the RFC 822 interpretation 423 * of "host-phrase." 424 */ 425 static char * 426 phrase(char *name, int token, int comma) 427 { 428 register char c; 429 register char *cp, *cp2; 430 char *bufend, *nbufp; 431 int gotlt, lastsp, didq; 432 char nbuf[LINESIZE]; 433 int nesting; 434 435 if (name == NOSTR) 436 return(NOSTR); 437 if (strlen(name) >= (unsigned)LINESIZE) 438 nbufp = (char *)salloc(strlen(name)); 439 else 440 nbufp = nbuf; 441 gotlt = 0; 442 lastsp = 0; 443 bufend = nbufp; 444 for (cp = name, cp2 = bufend; (c = *cp++) != 0;) { 445 switch (c) { 446 case '(': 447 /* 448 Start of a comment, ignore it. 449 */ 450 nesting = 1; 451 while ((c = *cp) != 0) { 452 cp++; 453 switch(c) { 454 case '\\': 455 if (*cp == 0) goto outcm; 456 cp++; 457 break; 458 case '(': 459 nesting++; 460 break; 461 case ')': 462 --nesting; 463 break; 464 } 465 if (nesting <= 0) break; 466 } 467 outcm: 468 lastsp = 0; 469 break; 470 case '"': 471 /* 472 Start a quoted string. 473 Copy it in its entirety. 474 */ 475 didq = 0; 476 while ((c = *cp) != 0) { 477 cp++; 478 switch (c) { 479 case '\\': 480 if ((c = *cp) == 0) goto outqs; 481 cp++; 482 break; 483 case '"': 484 goto outqs; 485 } 486 if (gotlt == 0 || gotlt == '<') { 487 if (lastsp) { 488 lastsp = 0; 489 *cp2++ = ' '; 490 } 491 if (!didq) { 492 *cp2++ = '"'; 493 didq++; 494 } 495 *cp2++ = c; 496 } 497 } 498 outqs: 499 if (didq) 500 *cp2++ = '"'; 501 lastsp = 0; 502 break; 503 504 case ' ': 505 case '\t': 506 case '\n': 507 if (token && (!comma || c == '\n')) { 508 done: 509 cp[-1] = 0; 510 return cp; 511 } 512 lastsp = 1; 513 break; 514 515 case ',': 516 *cp2++ = c; 517 if (gotlt != '<') { 518 if (token) 519 goto done; 520 bufend = cp2; 521 gotlt = 0; 522 } 523 break; 524 525 case '<': 526 cp2 = bufend; 527 gotlt = c; 528 lastsp = 0; 529 break; 530 531 case '>': 532 if (gotlt == '<') { 533 gotlt = c; 534 break; 535 } 536 537 /* FALLTHROUGH . . . */ 538 539 default: 540 if (gotlt == 0 || gotlt == '<') { 541 if (lastsp) { 542 lastsp = 0; 543 *cp2++ = ' '; 544 } 545 *cp2++ = c; 546 } 547 break; 548 } 549 } 550 *cp2 = 0; 551 return (token ? --cp : equal(name, nbufp) ? name : 552 nbufp == nbuf ? savestr(nbuf) : nbufp); 553 } 554 555 char * 556 skin(char *name) 557 { 558 return phrase(name, 0, 0); 559 } 560 561 /* 562 * Here sz is the buffer size of word. 563 */ 564 char * 565 yankword(char *name, char *word, int sz, int comma) 566 { 567 char *cp; 568 569 if (name == 0) 570 return 0; 571 while (isspace(*name)) 572 name++; 573 if (*name == 0) 574 return 0; 575 cp = phrase(name, 1, comma); 576 nstrcpy(word, sz, name); 577 return cp; 578 } 579 580 int 581 docomma(char *s) 582 { 583 return s && strpbrk(s, "(<,"); 584 } 585 586 /* 587 * Fetch the sender's name from the passed message. 588 */ 589 590 char * 591 nameof(register struct message *mp) 592 { 593 char namebuf[LINESIZE]; 594 char linebuf[LINESIZE]; 595 register char *cp, *cp2; 596 register FILE *ibuf; 597 int first = 1, wint = 0; 598 char *tmp; 599 600 if (value("from") && (cp = hfield("from", mp, addto)) != NOSTR) 601 return ripoff(cp); 602 ibuf = setinput(mp); 603 copy("", namebuf); 604 if (readline(ibuf, linebuf) <= 0) 605 return(savestr(namebuf)); 606 newname: 607 for (cp = linebuf; *cp != ' '; cp++) 608 ; 609 while (any(*cp, " \t")) 610 cp++; 611 for (cp2 = &namebuf[strlen(namebuf)]; *cp && !any(*cp, " \t") && 612 cp2-namebuf < LINESIZE-1; *cp2++ = *cp++) 613 ; 614 *cp2 = '\0'; 615 for (;;) { 616 if (readline(ibuf, linebuf) <= 0) 617 break; 618 if (substr(linebuf,"forwarded by ") != -1) 619 continue; 620 if (linebuf[0] == 'F') 621 cp = linebuf; 622 else if (linebuf[0] == '>') 623 cp = linebuf + 1; 624 else 625 break; 626 if (strncmp(cp, "From ", 5) != 0) 627 break; 628 if ((wint = substr(cp, "remote from ")) != -1) { 629 cp += wint + 12; 630 if (first) { 631 copy(cp, namebuf); 632 first = 0; 633 } else { 634 tmp = strrchr(namebuf, '!') + 1; 635 nstrcpy(tmp, 636 sizeof (namebuf) - (tmp - namebuf), 637 cp); 638 } 639 nstrcat(namebuf, sizeof (namebuf), "!"); 640 goto newname; 641 } else 642 break; 643 } 644 for (cp = namebuf; *cp == '!'; cp++); 645 while (ishost(host, cp)) 646 cp = strchr(cp, '!') + 1; 647 if (value("mustbang") && !strchr(cp, '!')) { 648 snprintf(linebuf, sizeof (linebuf), "%s!%s", 649 host, cp); 650 cp = linebuf; 651 } 652 if (cp2 = hfield("from", mp, addto)) 653 return(splice(cp, cp2)); 654 else 655 return(savestr(cp)); 656 } 657 658 /* 659 * Splice an address into a commented recipient header. 660 */ 661 char * 662 splice(char *addr, char *hdr) 663 { 664 char buf[LINESIZE]; 665 char *cp, *cp2; 666 667 if (cp = strchr(hdr, '<')) { 668 cp2 = strchr(cp, '>'); 669 if (cp2 == NULL) { 670 nstrcpy(buf, sizeof (buf), addr); 671 } else { 672 snprintf(buf, sizeof (buf), "%.*s%s%s", 673 cp - hdr + 1, hdr, addr, cp2); 674 } 675 } else if (cp = strchr(hdr, '(')) { 676 snprintf(buf, sizeof (buf), "%s %s", 677 addr, cp); 678 } else 679 nstrcpy(buf, sizeof (buf), addr); 680 return savestr(ripoff(buf)); 681 } 682 683 static char * 684 ripoff(register char *buf) 685 { 686 register char *cp; 687 688 cp = buf + strlen(buf); 689 while (--cp >= buf && isspace(*cp)); 690 if (cp >= buf && *cp == ',') 691 cp--; 692 *++cp = 0; 693 return buf; 694 } 695 696 /* 697 * Are any of the characters in the two strings the same? 698 */ 699 700 int 701 anyof(register char *s1, register char *s2) 702 { 703 register int c; 704 705 while ((c = *s1++) != 0) 706 if (any(c, s2)) 707 return(1); 708 return(0); 709 } 710 711 /* 712 * See if the given header field is supposed to be ignored. 713 * Fields of the form "Content-*" can't be ignored when saving. 714 */ 715 int 716 isign(char *field, int saving) 717 { 718 char realfld[BUFSIZ]; 719 720 /* 721 * Lower-case the string, so that "Status" and "status" 722 * will hash to the same place. 723 */ 724 istrcpy(realfld, sizeof (realfld), field); 725 726 if (saving && strncmp(realfld, "content-", 8) == 0) 727 return (0); 728 729 if (nretained > 0) 730 return (!member(realfld, retain)); 731 else 732 return (member(realfld, ignore)); 733 } 734 735 int 736 member(register char *realfield, register struct ignore **table) 737 { 738 register struct ignore *igp; 739 740 for (igp = table[hash(realfield)]; igp != 0; igp = igp->i_link) 741 if (equal(igp->i_field, realfield)) 742 return (1); 743 744 return (0); 745 } 746 747 /* 748 * This routine looks for string2 in string1. 749 * If found, it returns the position string2 is found at, 750 * otherwise it returns a -1. 751 */ 752 int 753 substr(char *string1, char *string2) 754 { 755 int i, j, len1, len2; 756 757 len1 = strlen(string1); 758 len2 = strlen(string2); 759 for (i = 0; i < len1 - len2 + 1; i++) { 760 for (j = 0; j < len2 && string1[i+j] == string2[j]; j++) 761 ; 762 if (j == len2) 763 return(i); 764 } 765 return(-1); 766 } 767 768 /* 769 * Copies src to the dstsize buffer at dst. The copy will never 770 * overflow the destination buffer and the buffer will always be null 771 * terminated. 772 */ 773 char * 774 nstrcpy(char *dst, int dstsize, char *src) 775 { 776 char *cp, *cp2; 777 778 cp2 = dst; 779 cp = src; 780 781 while (--dstsize > 0 && *cp != '\0') 782 *cp2++ = *cp++; 783 *cp2 = '\0'; 784 return(dst); 785 } 786 787 /* 788 * Appends src to the dstsize buffer at dst. The append will never 789 * overflow the destination buffer and the buffer will always be null 790 * terminated. 791 */ 792 char * 793 nstrcat(char *dst, int dstsize, char *src) 794 { 795 char *cp, *cp2; 796 797 cp2 = dst; 798 cp = src; 799 800 while (*cp2 != '\0') { 801 cp2++; 802 dstsize--; 803 } 804 while (--dstsize > 0 && *cp != '\0') 805 *cp2++ = *cp++; 806 *cp2 = '\0'; 807 return(dst); 808 } 809