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