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 23 /* 24 * Copyright 2001 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 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 /* 44 * mailx -- a modified version of a University of California at Berkeley 45 * mail program 46 * 47 * Handle name lists. 48 */ 49 50 #include "rcv.h" 51 #include <locale.h> 52 53 static struct name *nalloc(char str[]); 54 static int isfileaddr(char *name); 55 static int lengthof(struct name *name); 56 static struct name *gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype); 57 static char *norm(register char *user, register char *ubuf, int nbangs); 58 static struct name *put(struct name *list, struct name *node); 59 60 /* 61 * Allocate a single element of a name list, 62 * initialize its name field to the passed 63 * name and return it. 64 */ 65 66 static struct name * 67 nalloc(char str[]) 68 { 69 register struct name *np; 70 71 np = (struct name *) salloc(sizeof *np); 72 np->n_flink = NIL; 73 np->n_blink = NIL; 74 np->n_type = -1; 75 np->n_full = savestr(str); 76 np->n_name = skin(np->n_full); 77 return(np); 78 } 79 80 /* 81 * Find the tail of a list and return it. 82 */ 83 84 struct name * 85 tailof(struct name *name) 86 { 87 register struct name *np; 88 89 np = name; 90 if (np == NIL) 91 return(NIL); 92 while (np->n_flink != NIL) 93 np = np->n_flink; 94 return(np); 95 } 96 97 /* 98 * Extract a list of names from a line, 99 * and make a list of names from it. 100 * Return the list or NIL if none found. 101 */ 102 103 struct name * 104 extract(char line[], int arg_ntype) 105 { 106 short ntype = (short)arg_ntype; 107 register char *cp; 108 register struct name *top, *np, *t; 109 char nbuf[BUFSIZ], abuf[BUFSIZ]; 110 int comma; 111 112 if (line == NOSTR || strlen(line) == 0) 113 return(NIL); 114 comma = docomma(line); 115 top = NIL; 116 np = NIL; 117 cp = line; 118 while ((cp = yankword(cp, nbuf, sizeof (nbuf), comma)) != NOSTR) { 119 if (np != NIL && equal(nbuf, "at")) { 120 nstrcpy(abuf, sizeof (abuf), nbuf); 121 if ((cp = yankword(cp, nbuf, sizeof (nbuf), 122 comma)) == NOSTR) { 123 nstrcpy(nbuf, sizeof (nbuf), abuf); 124 goto normal; 125 } 126 snprintf(abuf, sizeof (abuf), "%s@%s", np->n_name, 127 nbuf); 128 np->n_name = savestr(abuf); 129 continue; 130 } 131 normal: 132 t = nalloc(nbuf); 133 t->n_type = ntype; 134 if (top == NIL) 135 top = t; 136 else 137 np->n_flink = t; 138 t->n_blink = np; 139 np = t; 140 } 141 return(top); 142 } 143 144 /* 145 * Turn a list of names into a string of the same names. 146 */ 147 148 char * 149 detract(register struct name *np, int ntype) 150 { 151 register int s; 152 register char *cp, *top; 153 register struct name *p; 154 155 if (np == NIL) 156 return(NOSTR); 157 s = 0; 158 for (p = np; p != NIL; p = p->n_flink) { 159 if ((ntype && (p->n_type & GMASK) != ntype) 160 || (p->n_type & GDEL)) 161 continue; 162 s += strlen(p->n_full) + 2; 163 } 164 if (s == 0) 165 return(NOSTR); 166 top = (char *)salloc((unsigned)(++s)); 167 cp = top; 168 for (p = np; p != NIL; p = p->n_flink) { 169 if ((ntype && (p->n_type & GMASK) != ntype) 170 || (p->n_type & GDEL)) 171 continue; 172 cp = copy(p->n_full, cp); 173 *cp++ = ','; 174 *cp++ = ' '; 175 } 176 *cp = 0; 177 return(top); 178 } 179 180 struct name * 181 outpre(struct name *to) 182 { 183 register struct name *np; 184 185 for (np = to; np; np = np->n_flink) 186 if (isfileaddr(np->n_name)) 187 np->n_type |= GDEL; 188 return to; 189 } 190 191 /* 192 * For each recipient in the passed name list with a / 193 * in the name, append the message to the end of the named file 194 * and remove him from the recipient list. 195 * 196 * Recipients whose name begins with | are piped through the given 197 * program and removed. 198 */ 199 200 int 201 outof(struct name *names, FILE *fo) 202 { 203 register int c; 204 register struct name *np; 205 time_t now; 206 char *date, *fname, *shell; 207 FILE *fout, *fin; 208 int ispipe; 209 int nout = 0; 210 int fd = 0; 211 #ifdef preSVr4 212 char line[BUFSIZ]; 213 #endif 214 215 for (np = names; np != NIL; np = np->n_flink) { 216 if (!isfileaddr(np->n_name) && np->n_name[0] != '|') 217 continue; 218 nout++; 219 ispipe = np->n_name[0] == '|'; 220 if (ispipe) 221 fname = np->n_name+1; 222 else 223 fname = safeexpand(np->n_name); 224 225 /* 226 * See if we have copied the complete message out yet. 227 * If not, do so. 228 */ 229 230 if (image < 0) { 231 fd = open(tempEdit, O_CREAT|O_EXCL|O_APPEND|O_WRONLY, 232 0600); 233 if ((fd < 0) && (errno == EEXIST)) { 234 if ((fd = open(tempEdit, O_APPEND|O_WRONLY, 235 0600)) < 0) { 236 perror(tempEdit); 237 senderr++; 238 goto cant; 239 } 240 } 241 if ((fout = fdopen(fd, "a")) == NULL) { 242 perror(tempEdit); 243 senderr++; 244 goto cant; 245 } 246 image = open(tempEdit, O_RDWR); 247 unlink(tempEdit); 248 if (image < 0) { 249 perror(tempEdit); 250 senderr++; 251 goto cant; 252 } else { 253 rewind(fo); 254 time(&now); 255 date = ctime(&now); 256 fprintf(fout, "From %s %s", myname, date); 257 while ((c = getc(fo)) != EOF) 258 putc(c, fout); 259 rewind(fo); 260 fflush(fout); 261 if (fferror(fout)) 262 perror(tempEdit); 263 fclose(fout); 264 } 265 } 266 267 /* 268 * Now either copy "image" to the desired file 269 * or give it as the standard input to the desired 270 * program as appropriate. 271 */ 272 273 if (ispipe) { 274 wait((int *)NULL); 275 switch (fork()) { 276 case 0: 277 sigchild(); 278 sigset(SIGHUP, SIG_IGN); 279 sigset(SIGINT, SIG_IGN); 280 sigset(SIGQUIT, SIG_IGN); 281 close(0); 282 dup(image); 283 close(image); 284 lseek(0, 0L, 0); 285 if ((shell = value("SHELL")) == NOSTR || *shell=='\0') 286 shell = SHELL; 287 (void) execlp(shell, shell, "-c", fname, (char *)0); 288 perror(shell); 289 exit(1); 290 break; 291 292 case (pid_t)-1: 293 perror("fork"); 294 senderr++; 295 goto cant; 296 } 297 } 298 else { 299 if ((fout = fopen(fname, "a")) == NULL) { 300 perror(fname); 301 senderr++; 302 goto cant; 303 } 304 fin = Fdopen(image, "r"); 305 if (fin == NULL) { 306 fprintf(stderr, 307 gettext("Can't reopen image\n")); 308 fclose(fout); 309 senderr++; 310 goto cant; 311 } 312 rewind(fin); 313 #ifdef preSVr4 314 putc(getc(fin), fout); 315 while (fgets(line, sizeof line, fin)) { 316 if (!strncmp(line, "From ", 5)) 317 putc('>', fout); 318 fputs(line, fout); 319 } 320 #else 321 while ((c = getc(fin)) != EOF) 322 putc(c, fout); 323 #endif 324 putc('\n', fout); 325 fflush(fout); 326 if (fferror(fout)) 327 senderr++, perror(fname); 328 fclose(fout); 329 fclose(fin); 330 } 331 cant: 332 /* 333 * In days of old we removed the entry from the 334 * the list; now for sake of header expansion 335 * we leave it in and mark it as deleted. 336 */ 337 338 #ifdef CRAZYWOW 339 { 340 register struct name *t, *x; 341 342 if (np == top) { 343 top = np->n_flink; 344 if (top != NIL) 345 top->n_blink = NIL; 346 np = top; 347 continue; 348 } 349 x = np->n_blink; 350 t = np->n_flink; 351 x->n_flink = t; 352 if (t != NIL) 353 t->n_blink = x; 354 np = t; 355 } 356 #endif 357 358 np->n_type |= GDEL; 359 } 360 if (image >= 0) { 361 close(image); 362 image = -1; 363 } 364 return(nout); 365 } 366 367 /* 368 * Determine if the passed address is a local "send to file" address. 369 * If any of the network metacharacters precedes any slashes, it can't 370 * be a filename. We cheat with .'s to allow path names like ./... 371 * If "fcc" has been unset, then short-circuit those tests, but not 372 * the +... test. 373 */ 374 static int 375 isfileaddr(char *name) 376 { 377 register char *cp; 378 char *fcc = value("fcc"); 379 380 if (any('@', name)) 381 return(0); 382 if (*name == '+') 383 return(1); 384 if (fcc == NOSTR) 385 return(0); 386 for (cp = name; *cp; cp++) { 387 if (*cp == '.') 388 continue; 389 if (any(*cp, metanet)) 390 return(0); 391 if (*cp == '/') 392 return(1); 393 } 394 return(0); 395 } 396 397 /* 398 * Map all of the aliased users in the invoker's mailrc 399 * file and insert them into the list. 400 * Changed after all these months of service to recursively 401 * expand names (2/14/80). 402 */ 403 404 struct name * 405 usermap(struct name *names) 406 { 407 register struct name *newnames, *np, *cp; 408 struct grouphead *gh; 409 register int metoo; 410 411 newnames = NIL; 412 np = names; 413 metoo = (value("metoo") != NOSTR); 414 while (np != NIL) { 415 if (np->n_name[0] == '\\') { 416 cp = np->n_flink; 417 newnames = put(newnames, np); 418 np = cp; 419 continue; 420 } 421 gh = findgroup(np->n_name); 422 cp = np->n_flink; 423 if (gh != NOGRP) 424 newnames = gexpand(newnames, gh, metoo, np->n_type); 425 else 426 newnames = put(newnames, np); 427 np = cp; 428 } 429 return(newnames); 430 } 431 432 /* 433 * Recursively expand a group name. We limit the expansion to some 434 * fixed level to keep things from going haywire. 435 * Direct recursion is not expanded for convenience. 436 */ 437 438 static struct name * 439 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype) 440 { 441 short ntype = (short)arg_ntype; 442 struct mgroup *gp; 443 struct grouphead *ngh; 444 struct name *np; 445 static int depth; 446 register char *cp; 447 448 if (depth > MAXEXP) { 449 printf(gettext("Expanding alias to depth larger than %d\n"), 450 MAXEXP); 451 return(nlist); 452 } 453 depth++; 454 for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) { 455 cp = gp->ge_name; 456 if (*cp == '\\') 457 goto quote; 458 if (strcmp(cp, gh->g_name) == 0) 459 goto quote; 460 if ((ngh = findgroup(cp)) != NOGRP) { 461 nlist = gexpand(nlist, ngh, metoo, ntype); 462 continue; 463 } 464 quote: 465 np = nalloc(cp); 466 np->n_type = ntype; 467 /* 468 * At this point should allow to expand 469 * to self if only person in group 470 */ 471 if (gp == gh->g_list && gp->ge_link == NOGE) 472 goto skip; 473 if (!metoo && samebody(myname, gp->ge_name, FALSE)) 474 np->n_type |= GDEL; 475 skip: 476 nlist = put(nlist, np); 477 } 478 depth--; 479 return(nlist); 480 } 481 482 /* 483 * Normalize a network name for comparison purposes. 484 */ 485 static char * 486 norm(register char *user, register char *ubuf, int nbangs) 487 { 488 register char *cp; 489 int inubuf = 0; 490 491 while (*user++ == '!'); 492 user--; 493 if (!strchr(user, '!')) { 494 snprintf(ubuf, BUFSIZ, "%s!%s", host, user); 495 user = ubuf; 496 inubuf++; 497 } 498 if (nbangs) { 499 cp = user + strlen(user); 500 while (nbangs--) 501 while (cp > user && *--cp != '!'); 502 user = (cp > user) ? ++cp : cp; 503 /* 504 * Now strip off all Internet-type 505 * hosts. 506 */ 507 if ((cp = strchr(user, '%')) == NOSTR) 508 cp = strchr(user, '@'); 509 if (cp != NOSTR) { 510 if (!inubuf) { 511 strncpy(ubuf, user, cp - user); 512 ubuf[cp - user] = '\0'; 513 user = ubuf; 514 } else 515 *cp = '\0'; 516 } 517 } 518 return user; 519 } 520 521 /* 522 * Implement allnet options. 523 */ 524 int 525 samebody(register char *user, register char *addr, int fuzzy) 526 { 527 char ubuf[BUFSIZ], abuf[BUFSIZ]; 528 char *allnet = value("allnet"); 529 int nbangs = allnet ? !strcmp(allnet, "uucp") ? 2 : 1 : 0; 530 531 if (fuzzy && value("fuzzymatch")) { 532 int i; 533 534 (void) strlcpy(ubuf, user, BUFSIZ); 535 for (i = 0; ubuf[i]; i++) 536 ubuf[i] = tolower(ubuf[i]); 537 (void) strlcpy(abuf, addr, BUFSIZ); 538 for (i = 0; abuf[i]; i++) 539 abuf[i] = tolower(abuf[i]); 540 return (strstr(abuf, ubuf) != NOSTR); 541 } 542 user = norm(user, ubuf, nbangs); 543 addr = norm(addr, abuf, nbangs); 544 return strcmp(user, addr) == 0; 545 } 546 547 /* 548 * Compute the length of the passed name list and 549 * return it. 550 */ 551 static int 552 lengthof(struct name *name) 553 { 554 register struct name *np; 555 register int c; 556 557 for (c = 0, np = name; np != NIL; c++, np = np->n_flink) 558 ; 559 return(c); 560 } 561 562 /* 563 * Concatenate the two passed name lists, return the result. 564 */ 565 566 struct name * 567 cat(struct name *n1, struct name *n2) 568 { 569 register struct name *tail; 570 571 if (n1 == NIL) 572 return(n2); 573 if (n2 == NIL) 574 return(n1); 575 tail = tailof(n1); 576 tail->n_flink = n2; 577 n2->n_blink = tail; 578 return(n1); 579 } 580 581 /* 582 * Unpack the name list onto a vector of strings. 583 * Return an error if the name list won't fit. 584 */ 585 586 char ** 587 unpack(struct name *np) 588 { 589 register char **ap, **top; 590 register struct name *n; 591 char hbuf[10]; 592 int t, extra, metoo, verbose; 593 594 n = np; 595 if ((t = lengthof(n)) == 0) 596 panic("No names to unpack"); 597 598 /* 599 * Compute the number of extra arguments we will need. 600 * We need at least 2 extra -- one for "mail" and one for 601 * the terminating 0 pointer. 602 * Additional spots may be needed to pass along -r and -f to 603 * the host mailer. 604 */ 605 606 extra = 2; 607 608 if (rflag != NOSTR) 609 extra += 2; 610 #ifdef SENDMAIL 611 extra++; 612 metoo = value("metoo") != NOSTR; 613 if (metoo) 614 extra++; 615 verbose = value("verbose") != NOSTR; 616 if (verbose) 617 extra++; 618 if (hflag) 619 extra += 2; 620 #endif /* SENDMAIL */ 621 top = (char **) salloc((t + extra) * sizeof (char *)); 622 ap = top; 623 *ap++ = "mail"; 624 if (rflag != NOSTR) { 625 *ap++ = "-r"; 626 *ap++ = rflag; 627 } 628 #ifdef SENDMAIL 629 *ap++ = "-i"; 630 if (metoo) 631 *ap++ = "-m"; 632 if (verbose) 633 *ap++ = "-v"; 634 if (hflag) { 635 *ap++ = "-h"; 636 snprintf(hbuf, sizeof (hbuf), "%d", hflag); 637 *ap++ = savestr(hbuf); 638 } 639 #endif /* SENDMAIL */ 640 while (n != NIL) { 641 if (n->n_type & GDEL) { 642 n = n->n_flink; 643 continue; 644 } 645 *ap++ = n->n_name; 646 n = n->n_flink; 647 } 648 *ap = NOSTR; 649 return(top); 650 } 651 652 /* 653 * See if the user named himself as a destination 654 * for outgoing mail. If so, set the global flag 655 * selfsent so that we avoid removing his mailbox. 656 */ 657 658 void 659 mechk(struct name *names) 660 { 661 register struct name *np; 662 663 for (np = names; np != NIL; np = np->n_flink) 664 if ((np->n_type & GDEL) == 0 && 665 samebody(np->n_name, myname, FALSE)) { 666 selfsent++; 667 return; 668 } 669 } 670 671 /* 672 * Remove all of the duplicates from the passed name list by 673 * insertion sorting them, then checking for dups. 674 * Return the head of the new list. 675 */ 676 677 struct name * 678 elide(struct name *names) 679 { 680 register struct name *np, *t, *newnames; 681 struct name *x; 682 683 if (names == NIL) 684 return(NIL); 685 newnames = names; 686 np = names; 687 np = np->n_flink; 688 if (np != NIL) 689 np->n_blink = NIL; 690 newnames->n_flink = NIL; 691 while (np != NIL) { 692 t = newnames; 693 while (strcmp(t->n_name, np->n_name) < 0) { 694 if (t->n_flink == NIL) 695 break; 696 t = t->n_flink; 697 } 698 699 /* 700 * If we ran out of t's, put the new entry after 701 * the current value of t. 702 */ 703 704 if (strcmp(t->n_name, np->n_name) < 0) { 705 t->n_flink = np; 706 np->n_blink = t; 707 t = np; 708 np = np->n_flink; 709 t->n_flink = NIL; 710 continue; 711 } 712 713 /* 714 * Otherwise, put the new entry in front of the 715 * current t. If at the front of the list, 716 * the new guy becomes the new head of the list. 717 */ 718 719 if (t == newnames) { 720 t = np; 721 np = np->n_flink; 722 t->n_flink = newnames; 723 newnames->n_blink = t; 724 t->n_blink = NIL; 725 newnames = t; 726 continue; 727 } 728 729 /* 730 * The normal case -- we are inserting into the 731 * middle of the list. 732 */ 733 734 x = np; 735 np = np->n_flink; 736 x->n_flink = t; 737 x->n_blink = t->n_blink; 738 t->n_blink->n_flink = x; 739 t->n_blink = x; 740 } 741 742 /* 743 * Now the list headed up by new is sorted. 744 * Go through it and remove duplicates. 745 * Remember the best "type" among all the 746 * duplicates of a name. 747 */ 748 749 np = newnames; 750 while (np != NIL) { 751 int type; 752 753 t = np; 754 type = np->n_type; 755 while (t->n_flink!=NIL && 756 strcmp(np->n_name, t->n_flink->n_name) == 0) { 757 t = t->n_flink; 758 /* "To" before "Cc" before "Bcc" */ 759 if (t->n_type < type) 760 type = t->n_type; 761 } 762 if (t == np || t == NIL) { 763 np = np->n_flink; 764 continue; 765 } 766 767 /* 768 * Now t points to the last entry with the same name 769 * as np. Make np point beyond t. 770 */ 771 772 np->n_flink = t->n_flink; 773 if (t->n_flink != NIL) 774 t->n_flink->n_blink = np; 775 np->n_type = type; 776 np = np->n_flink; 777 } 778 return(newnames); 779 } 780 781 /* 782 * Put another node onto a list of names and return 783 * the list. 784 */ 785 786 static struct name * 787 put(struct name *list, struct name *node) 788 { 789 node->n_flink = list; 790 node->n_blink = NIL; 791 if (list != NIL) 792 list->n_blink = node; 793 return(node); 794 } 795 796 797 /* 798 * Delete the given name from a namelist. 799 */ 800 struct name * 801 delname(register struct name *np, char name[]) 802 { 803 register struct name *p; 804 805 for (p = np; p != NIL; p = p->n_flink) 806 if (samebody(name, p->n_name, FALSE)) { 807 if (p->n_blink == NIL) { 808 if (p->n_flink != NIL) 809 p->n_flink->n_blink = NIL; 810 np = p->n_flink; 811 continue; 812 } 813 if (p->n_flink == NIL) { 814 if (p->n_blink != NIL) 815 p->n_blink->n_flink = NIL; 816 continue; 817 } 818 p->n_blink->n_flink = p->n_flink; 819 p->n_flink->n_blink = p->n_blink; 820 } 821 return(np); 822 } 823 824 /* 825 * Call the given routine on each element of the name 826 * list, replacing said value if need be. 827 */ 828 829 void 830 mapf(register struct name *np, char *from) 831 { 832 register struct name *p; 833 834 if (debug) fprintf(stderr, "mapf %lx, %s\n", (long)np, from); 835 for (p = np; p != NIL; p = p->n_flink) 836 if ((p->n_type & GDEL) == 0) { 837 p->n_name = netmap(p->n_name, from); 838 p->n_full = splice(p->n_name, p->n_full); 839 } 840 if (debug) fprintf(stderr, "mapf %s done\n", from); 841 }