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