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