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