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