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