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