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