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