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