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