Print this page
5166 sendmail package should be replaceable
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
Reviewed by: Toomas Soome <tsoome@me.com>


  21  */
  22 
  23 /*
  24  * Copyright 2001 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  29 /*        All Rights Reserved   */
  30 
  31 /*
  32  * University Copyright- Copyright (c) 1982, 1986, 1988
  33  * The Regents of the University of California
  34  * All Rights Reserved
  35  *
  36  * University Acknowledgment- Portions of this document are derived from
  37  * software developed by the University of California, Berkeley, and its
  38  * contributors.
  39  */
  40 
  41 #pragma ident   "%Z%%M% %I%     %E% SMI"
  42 
  43 /*
  44  * mailx -- a modified version of a University of California at Berkeley
  45  *      mail program
  46  *
  47  * Handle name lists.
  48  */
  49 
  50 #include "rcv.h"
  51 #include <locale.h>
  52 
  53 static struct name      *nalloc(char str[]);
  54 static int              isfileaddr(char *name);
  55 static int              lengthof(struct name *name);
  56 static struct name      *gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype);
  57 static char             *norm(register char *user, register char *ubuf, int nbangs);


  58 static struct name      *put(struct name *list, struct name *node);
  59 
  60 /*
  61  * Allocate a single element of a name list,
  62  * initialize its name field to the passed
  63  * name and return it.
  64  */
  65 
  66 static struct name *
  67 nalloc(char str[])
  68 {
  69         register struct name *np;
  70 
  71         np = (struct name *) salloc(sizeof *np);
  72         np->n_flink = NIL;
  73         np->n_blink = NIL;
  74         np->n_type = -1;
  75         np->n_full = savestr(str);
  76         np->n_name = skin(np->n_full);
  77         return(np);
  78 }
  79 
  80 /*
  81  * Find the tail of a list and return it.
  82  */
  83 
  84 struct name *
  85 tailof(struct name *name)
  86 {
  87         register struct name *np;
  88 
  89         np = name;
  90         if (np == NIL)
  91                 return(NIL);
  92         while (np->n_flink != NIL)
  93                 np = np->n_flink;
  94         return(np);
  95 }
  96 
  97 /*
  98  * Extract a list of names from a line,
  99  * and make a list of names from it.
 100  * Return the list or NIL if none found.
 101  */
 102 
 103 struct name *
 104 extract(char line[], int arg_ntype)
 105 {
 106         short ntype = (short)arg_ntype;
 107         register char *cp;
 108         register struct name *top, *np, *t;
 109         char nbuf[BUFSIZ], abuf[BUFSIZ];
 110         int comma;
 111 
 112         if (line == NOSTR || strlen(line) == 0)
 113                 return(NIL);
 114         comma = docomma(line);
 115         top = NIL;
 116         np = NIL;
 117         cp = line;
 118         while ((cp = yankword(cp, nbuf, sizeof (nbuf), comma)) != NOSTR) {
 119                 if (np != NIL && equal(nbuf, "at")) {
 120                         nstrcpy(abuf, sizeof (abuf), nbuf);
 121                         if ((cp = yankword(cp, nbuf, sizeof (nbuf),
 122                                 comma)) == NOSTR) {
 123                                 nstrcpy(nbuf, sizeof (nbuf), abuf);
 124                                 goto normal;
 125                         }
 126                         snprintf(abuf, sizeof (abuf), "%s@%s", np->n_name,
 127                                 nbuf); 
 128                         np->n_name = savestr(abuf);
 129                         continue;
 130                 }
 131 normal:
 132                 t = nalloc(nbuf);
 133                 t->n_type = ntype;
 134                 if (top == NIL)
 135                         top = t;
 136                 else
 137                         np->n_flink = t;
 138                 t->n_blink = np;
 139                 np = t;
 140         }
 141         return(top);
 142 }
 143 
 144 /*
 145  * Turn a list of names into a string of the same names.
 146  */
 147 
 148 char *
 149 detract(register struct name *np, int ntype)
 150 {
 151         register int s;
 152         register char *cp, *top;
 153         register struct name *p;
 154 
 155         if (np == NIL)
 156                 return(NOSTR);
 157         s = 0;
 158         for (p = np; p != NIL; p = p->n_flink) {
 159                 if ((ntype && (p->n_type & GMASK) != ntype)
 160                  || (p->n_type & GDEL))
 161                         continue;
 162                 s += strlen(p->n_full) + 2;
 163         }
 164         if (s == 0)
 165                 return(NOSTR);
 166         top = (char *)salloc((unsigned)(++s));
 167         cp = top;
 168         for (p = np; p != NIL; p = p->n_flink) {
 169                 if ((ntype && (p->n_type & GMASK) != ntype)
 170                  || (p->n_type & GDEL))
 171                         continue;
 172                 cp = copy(p->n_full, cp);
 173                 *cp++ = ',';
 174                 *cp++ = ' ';
 175         }
 176         *cp = 0;
 177         return(top);
 178 }
 179 
 180 struct name *
 181 outpre(struct name *to)
 182 {
 183         register struct name *np;
 184 
 185         for (np = to; np; np = np->n_flink)
 186                 if (isfileaddr(np->n_name))
 187                         np->n_type |= GDEL;
 188         return to;
 189 }
 190 
 191 /*
 192  * For each recipient in the passed name list with a /
 193  * in the name, append the message to the end of the named file
 194  * and remove him from the recipient list.
 195  *
 196  * Recipients whose name begins with | are piped through the given
 197  * program and removed.
 198  */
 199 
 200 int
 201 outof(struct name *names, FILE *fo)
 202 {
 203         register int c;
 204         register struct name *np;
 205         time_t now;
 206         char *date, *fname, *shell;
 207         FILE *fout, *fin;
 208         int ispipe;


 265                 }
 266 
 267                 /*
 268                  * Now either copy "image" to the desired file
 269                  * or give it as the standard input to the desired
 270                  * program as appropriate.
 271                  */
 272 
 273                 if (ispipe) {
 274                         wait((int *)NULL);
 275                         switch (fork()) {
 276                         case 0:
 277                                 sigchild();
 278                                 sigset(SIGHUP, SIG_IGN);
 279                                 sigset(SIGINT, SIG_IGN);
 280                                 sigset(SIGQUIT, SIG_IGN);
 281                                 close(0);
 282                                 dup(image);
 283                                 close(image);
 284                                 lseek(0, 0L, 0);
 285                                 if ((shell = value("SHELL")) == NOSTR || *shell=='\0')

 286                                         shell = SHELL;
 287                                 (void) execlp(shell, shell, "-c", fname, (char *)0);

 288                                 perror(shell);
 289                                 exit(1);
 290                                 break;
 291 
 292                         case (pid_t)-1:
 293                                 perror("fork");
 294                                 senderr++;
 295                                 goto cant;
 296                         }
 297                 }
 298                 else {
 299                         if ((fout = fopen(fname, "a")) == NULL) {
 300                                 perror(fname);
 301                                 senderr++;
 302                                 goto cant;
 303                         }
 304                         fin = Fdopen(image, "r");
 305                         if (fin == NULL) {
 306                                 fprintf(stderr,
 307                                     gettext("Can't reopen image\n"));
 308                                 fclose(fout);
 309                                 senderr++;
 310                                 goto cant;
 311                         }
 312                         rewind(fin);
 313 #ifdef preSVr4
 314                         putc(getc(fin), fout);
 315                         while (fgets(line, sizeof line, fin)) {
 316                                 if (!strncmp(line, "From ", 5))
 317                                         putc('>', fout);
 318                                 fputs(line, fout);
 319                         }
 320 #else
 321                         while ((c = getc(fin)) != EOF)
 322                                 putc(c, fout);
 323 #endif
 324                         putc('\n', fout);
 325                         fflush(fout);
 326                         if (fferror(fout))
 327                                 senderr++, perror(fname);
 328                         fclose(fout);
 329                         fclose(fin);
 330                 }
 331 cant:
 332                 /*
 333                  * In days of old we removed the entry from the
 334                  * the list; now for sake of header expansion
 335                  * we leave it in and mark it as deleted.
 336                  */


 344                         if (top != NIL)
 345                                 top->n_blink = NIL;
 346                         np = top;
 347                         continue;
 348                 }
 349                 x = np->n_blink;
 350                 t = np->n_flink;
 351                 x->n_flink = t;
 352                 if (t != NIL)
 353                         t->n_blink = x;
 354                 np = t;
 355                 }
 356 #endif
 357 
 358                 np->n_type |= GDEL;
 359         }
 360         if (image >= 0) {
 361                 close(image);
 362                 image = -1;
 363         }
 364         return(nout);
 365 }
 366 
 367 /*
 368  * Determine if the passed address is a local "send to file" address.
 369  * If any of the network metacharacters precedes any slashes, it can't
 370  * be a filename.  We cheat with .'s to allow path names like ./...
 371  * If "fcc" has been unset, then short-circuit those tests, but not
 372  * the +... test.
 373  */
 374 static int 
 375 isfileaddr(char *name)
 376 {
 377         register char *cp;
 378         char *fcc = value("fcc");
 379 
 380         if (any('@', name))
 381                 return(0);
 382         if (*name == '+')
 383                 return(1);
 384         if (fcc == NOSTR)
 385                 return(0);
 386         for (cp = name; *cp; cp++) {
 387                 if (*cp == '.')
 388                         continue;
 389                 if (any(*cp, metanet))
 390                         return(0);
 391                 if (*cp == '/')
 392                         return(1);
 393         }
 394         return(0);
 395 }
 396 
 397 /*
 398  * Map all of the aliased users in the invoker's mailrc
 399  * file and insert them into the list.
 400  * Changed after all these months of service to recursively
 401  * expand names (2/14/80).
 402  */
 403 
 404 struct name *
 405 usermap(struct name *names)
 406 {
 407         register struct name *newnames, *np, *cp;
 408         struct grouphead *gh;
 409         register int metoo;
 410 
 411         newnames = NIL;
 412         np = names;
 413         metoo = (value("metoo") != NOSTR);
 414         while (np != NIL) {
 415                 if (np->n_name[0] == '\\') {
 416                         cp = np->n_flink;
 417                         newnames = put(newnames, np);
 418                         np = cp;
 419                         continue;
 420                 }
 421                 gh = findgroup(np->n_name);
 422                 cp = np->n_flink;
 423                 if (gh != NOGRP)
 424                         newnames = gexpand(newnames, gh, metoo, np->n_type);
 425                 else
 426                         newnames = put(newnames, np);
 427                 np = cp;
 428         }
 429         return(newnames);
 430 }
 431 
 432 /*
 433  * Recursively expand a group name.  We limit the expansion to some
 434  * fixed level to keep things from going haywire.
 435  * Direct recursion is not expanded for convenience.
 436  */
 437 
 438 static struct name *
 439 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype)
 440 {
 441         short ntype = (short)arg_ntype;
 442         struct mgroup *gp;
 443         struct grouphead *ngh;
 444         struct name *np;
 445         static int depth;
 446         register char *cp;
 447 
 448         if (depth > MAXEXP) {
 449                 printf(gettext("Expanding alias to depth larger than %d\n"),
 450                     MAXEXP);
 451                 return(nlist);
 452         }
 453         depth++;
 454         for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
 455                 cp = gp->ge_name;
 456                 if (*cp == '\\')
 457                         goto quote;
 458                 if (strcmp(cp, gh->g_name) == 0)
 459                         goto quote;
 460                 if ((ngh = findgroup(cp)) != NOGRP) {
 461                         nlist = gexpand(nlist, ngh, metoo, ntype);
 462                         continue;
 463                 }
 464 quote:
 465                 np = nalloc(cp);
 466                 np->n_type = ntype;
 467                 /*
 468                  * At this point should allow to expand
 469                  * to self if only person in group
 470                  */
 471                 if (gp == gh->g_list && gp->ge_link == NOGE)
 472                         goto skip;
 473                 if (!metoo && samebody(myname, gp->ge_name, FALSE))
 474                         np->n_type |= GDEL;
 475 skip:
 476                 nlist = put(nlist, np);
 477         }
 478         depth--;
 479         return(nlist);
 480 }
 481 
 482 /*
 483  * Normalize a network name for comparison purposes.
 484  */
 485 static char *
 486 norm(register char *user, register char *ubuf, int nbangs)
 487 {
 488         register char *cp;
 489         int inubuf = 0;
 490 
 491         while (*user++ == '!');

 492         user--;
 493         if (!strchr(user, '!')) {
 494                 snprintf(ubuf, BUFSIZ, "%s!%s", host, user);
 495                 user = ubuf;
 496                 inubuf++;
 497         }
 498         if (nbangs) {
 499                 cp = user + strlen(user);
 500                 while (nbangs--)
 501                         while (cp > user && *--cp != '!');

 502                 user = (cp > user) ? ++cp : cp;
 503                 /*
 504                  * Now strip off all Internet-type
 505                  * hosts.
 506                  */
 507                 if ((cp = strchr(user, '%')) == NOSTR)
 508                         cp = strchr(user, '@');
 509                 if (cp != NOSTR) {
 510                         if (!inubuf) {
 511                                 strncpy(ubuf, user, cp - user);
 512                                 ubuf[cp - user] = '\0';
 513                                 user = ubuf;
 514                         } else
 515                                 *cp = '\0';
 516                 }
 517         }
 518         return user;
 519 }
 520 
 521 /*
 522  * Implement allnet options.
 523  */
 524 int 
 525 samebody(register char *user, register char *addr, int fuzzy)
 526 {
 527         char ubuf[BUFSIZ], abuf[BUFSIZ];
 528         char *allnet = value("allnet");
 529         int nbangs = allnet ? !strcmp(allnet, "uucp") ? 2 : 1 : 0;
 530 
 531         if (fuzzy && value("fuzzymatch")) {
 532                 int i;
 533 
 534                 (void) strlcpy(ubuf, user, BUFSIZ);
 535                 for (i = 0; ubuf[i]; i++)
 536                         ubuf[i] = tolower(ubuf[i]);
 537                 (void) strlcpy(abuf, addr, BUFSIZ);
 538                 for (i = 0; abuf[i]; i++)
 539                         abuf[i] = tolower(abuf[i]);
 540                 return (strstr(abuf, ubuf) != NOSTR);
 541         }
 542         user = norm(user, ubuf, nbangs);
 543         addr = norm(addr, abuf, nbangs);
 544         return strcmp(user, addr) == 0;
 545 }
 546 
 547 /*
 548  * Compute the length of the passed name list and
 549  * return it.
 550  */
 551 static int 
 552 lengthof(struct name *name)
 553 {
 554         register struct name *np;
 555         register int c;
 556 
 557         for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
 558                 ;
 559         return(c);
 560 }
 561 
 562 /*
 563  * Concatenate the two passed name lists, return the result.
 564  */
 565 
 566 struct name *
 567 cat(struct name *n1, struct name *n2)
 568 {
 569         register struct name *tail;
 570 
 571         if (n1 == NIL)
 572                 return(n2);
 573         if (n2 == NIL)
 574                 return(n1);
 575         tail = tailof(n1);
 576         tail->n_flink = n2;
 577         n2->n_blink = tail;
 578         return(n1);
 579 }
 580 
 581 /*
 582  * Unpack the name list onto a vector of strings.
 583  * Return an error if the name list won't fit.
 584  */
 585 
 586 char **
 587 unpack(struct name *np)
 588 {
 589         register char **ap, **top;
 590         register struct name *n;
 591         char hbuf[10];
 592         int t, extra, metoo, verbose;
 593 
 594         n = np;
 595         if ((t = lengthof(n)) == 0)
 596                 panic("No names to unpack");
 597 
 598         /*
 599          * Compute the number of extra arguments we will need.
 600          * We need at least 2 extra -- one for "mail" and one for
 601          * the terminating 0 pointer.
 602          * Additional spots may be needed to pass along -r and -f to 
 603          * the host mailer.
 604          */
 605 
 606         extra = 2;
 607 
 608         if (rflag != NOSTR)
 609                 extra += 2;
 610 #ifdef SENDMAIL
 611         extra++;
 612         metoo = value("metoo") != NOSTR;
 613         if (metoo)
 614                 extra++;
 615         verbose = value("verbose") != NOSTR;
 616         if (verbose)
 617                 extra++;
 618         if (hflag)
 619                 extra += 2;
 620 #endif /* SENDMAIL */
 621         top = (char **) salloc((t + extra) * sizeof (char *));
 622         ap = top;
 623         *ap++ = "mail";
 624         if (rflag != NOSTR) {
 625                 *ap++ = "-r";
 626                 *ap++ = rflag;
 627         }
 628 #ifdef SENDMAIL
 629         *ap++ = "-i";
 630         if (metoo)
 631                 *ap++ = "-m";
 632         if (verbose)
 633                 *ap++ = "-v";
 634         if (hflag) {
 635                 *ap++ = "-h";
 636                 snprintf(hbuf, sizeof (hbuf), "%d", hflag);
 637                 *ap++ = savestr(hbuf);
 638         }
 639 #endif /* SENDMAIL */
 640         while (n != NIL) {
 641                 if (n->n_type & GDEL) {
 642                         n = n->n_flink;
 643                         continue;
 644                 }
 645                 *ap++ = n->n_name;
 646                 n = n->n_flink;
 647         }
 648         *ap = NOSTR;
 649         return(top);
 650 }
 651 
 652 /*
 653  * See if the user named himself as a destination
 654  * for outgoing mail.  If so, set the global flag
 655  * selfsent so that we avoid removing his mailbox.
 656  */
 657 
 658 void 
 659 mechk(struct name *names)
 660 {
 661         register struct name *np;
 662 
 663         for (np = names; np != NIL; np = np->n_flink)
 664                 if ((np->n_type & GDEL) == 0 &&
 665                     samebody(np->n_name, myname, FALSE)) {
 666                         selfsent++;
 667                         return;
 668                 }
 669 }
 670 
 671 /*
 672  * Remove all of the duplicates from the passed name list by
 673  * insertion sorting them, then checking for dups.
 674  * Return the head of the new list.
 675  */
 676 
 677 struct name *
 678 elide(struct name *names)
 679 {
 680         register struct name *np, *t, *newnames;
 681         struct name *x;
 682 
 683         if (names == NIL)
 684                 return(NIL);
 685         newnames = names;
 686         np = names;
 687         np = np->n_flink;
 688         if (np != NIL)
 689                 np->n_blink = NIL;
 690         newnames->n_flink = NIL;
 691         while (np != NIL) {
 692                 t = newnames;
 693                 while (strcmp(t->n_name, np->n_name) < 0) {
 694                         if (t->n_flink == NIL)
 695                                 break;
 696                         t = t->n_flink;
 697                 }
 698 
 699                 /*
 700                  * If we ran out of t's, put the new entry after
 701                  * the current value of t.
 702                  */
 703 
 704                 if (strcmp(t->n_name, np->n_name) < 0) {


 735                 np = np->n_flink;
 736                 x->n_flink = t;
 737                 x->n_blink = t->n_blink;
 738                 t->n_blink->n_flink = x;
 739                 t->n_blink = x;
 740         }
 741 
 742         /*
 743          * Now the list headed up by new is sorted.
 744          * Go through it and remove duplicates.
 745          * Remember the best "type" among all the
 746          * duplicates of a name.
 747          */
 748 
 749         np = newnames;
 750         while (np != NIL) {
 751                 int type;
 752 
 753                 t = np;
 754                 type = np->n_type;
 755                 while (t->n_flink!=NIL &&
 756                     strcmp(np->n_name, t->n_flink->n_name) == 0) {
 757                         t = t->n_flink;
 758                         /* "To" before "Cc" before "Bcc" */
 759                         if (t->n_type < type)
 760                                 type = t->n_type;
 761                 }
 762                 if (t == np || t == NIL) {
 763                         np = np->n_flink;
 764                         continue;
 765                 }
 766 
 767                 /*
 768                  * Now t points to the last entry with the same name
 769                  * as np.  Make np point beyond t.
 770                  */
 771 
 772                 np->n_flink = t->n_flink;
 773                 if (t->n_flink != NIL)
 774                         t->n_flink->n_blink = np;
 775                 np->n_type = type;
 776                 np = np->n_flink;
 777         }
 778         return(newnames);
 779 }
 780 
 781 /*
 782  * Put another node onto a list of names and return
 783  * the list.
 784  */
 785 
 786 static struct name *
 787 put(struct name *list, struct name *node)
 788 {
 789         node->n_flink = list;
 790         node->n_blink = NIL;
 791         if (list != NIL)
 792                 list->n_blink = node;
 793         return(node);
 794 }
 795 
 796 
 797 /*
 798  * Delete the given name from a namelist.
 799  */
 800 struct name *
 801 delname(register struct name *np, char name[])
 802 {
 803         register struct name *p;
 804 
 805         for (p = np; p != NIL; p = p->n_flink)
 806                 if (samebody(name, p->n_name, FALSE)) {
 807                         if (p->n_blink == NIL) {
 808                                 if (p->n_flink != NIL)
 809                                         p->n_flink->n_blink = NIL;
 810                                 np = p->n_flink;
 811                                 continue;
 812                         }
 813                         if (p->n_flink == NIL) {
 814                                 if (p->n_blink != NIL)
 815                                         p->n_blink->n_flink = NIL;
 816                                 continue;
 817                         }
 818                         p->n_blink->n_flink = p->n_flink;
 819                         p->n_flink->n_blink = p->n_blink;
 820                 }
 821         return(np);
 822 }
 823 
 824 /*
 825  * Call the given routine on each element of the name
 826  * list, replacing said value if need be.
 827  */
 828 
 829 void 
 830 mapf(register struct name *np, char *from)
 831 {
 832         register struct name *p;
 833 
 834         if (debug) fprintf(stderr, "mapf %lx, %s\n", (long)np, from);
 835         for (p = np; p != NIL; p = p->n_flink)
 836                 if ((p->n_type & GDEL) == 0) {
 837                         p->n_name = netmap(p->n_name, from);
 838                         p->n_full = splice(p->n_name, p->n_full);
 839                 }
 840         if (debug) fprintf(stderr, "mapf %s done\n", from);
 841 }


  21  */
  22 
  23 /*
  24  * Copyright 2001 Sun Microsystems, Inc.  All rights reserved.
  25  * Use is subject to license terms.
  26  */
  27 
  28 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  29 /*        All Rights Reserved   */
  30 
  31 /*
  32  * University Copyright- Copyright (c) 1982, 1986, 1988
  33  * The Regents of the University of California
  34  * All Rights Reserved
  35  *
  36  * University Acknowledgment- Portions of this document are derived from
  37  * software developed by the University of California, Berkeley, and its
  38  * contributors.
  39  */
  40 


  41 /*
  42  * mailx -- a modified version of a University of California at Berkeley
  43  *      mail program
  44  *
  45  * Handle name lists.
  46  */
  47 
  48 #include "rcv.h"
  49 #include <locale.h>
  50 
  51 static struct name      *nalloc(char str[]);
  52 static int              isfileaddr(char *name);
  53 static int              lengthof(struct name *name);
  54 static struct name      *gexpand(struct name *nlist, struct grouphead *gh,
  55     int metoo, int arg_ntype);
  56 static char             *norm(register char *user, register char *ubuf,
  57     int nbangs);
  58 static struct name      *put(struct name *list, struct name *node);
  59 
  60 /*
  61  * Allocate a single element of a name list,
  62  * initialize its name field to the passed
  63  * name and return it.
  64  */
  65 
  66 static struct name *
  67 nalloc(char str[])
  68 {
  69         register struct name *np;
  70 
  71         np = (struct name *)salloc(sizeof (*np));
  72         np->n_flink = NIL;
  73         np->n_blink = NIL;
  74         np->n_type = -1;
  75         np->n_full = savestr(str);
  76         np->n_name = skin(np->n_full);
  77         return (np);
  78 }
  79 
  80 /*
  81  * Find the tail of a list and return it.
  82  */
  83 
  84 struct name *
  85 tailof(struct name *name)
  86 {
  87         register struct name *np;
  88 
  89         np = name;
  90         if (np == NIL)
  91                 return (NIL);
  92         while (np->n_flink != NIL)
  93                 np = np->n_flink;
  94         return (np);
  95 }
  96 
  97 /*
  98  * Extract a list of names from a line,
  99  * and make a list of names from it.
 100  * Return the list or NIL if none found.
 101  */
 102 
 103 struct name *
 104 extract(char line[], int arg_ntype)
 105 {
 106         short ntype = (short)arg_ntype;
 107         register char *cp;
 108         register struct name *top, *np, *t;
 109         char nbuf[BUFSIZ], abuf[BUFSIZ];
 110         int comma;
 111 
 112         if (line == NOSTR || strlen(line) == 0)
 113                 return (NIL);
 114         comma = docomma(line);
 115         top = NIL;
 116         np = NIL;
 117         cp = line;
 118         while ((cp = yankword(cp, nbuf, sizeof (nbuf), comma)) != NOSTR) {
 119                 if (np != NIL && equal(nbuf, "at")) {
 120                         nstrcpy(abuf, sizeof (abuf), nbuf);
 121                         if ((cp = yankword(cp, nbuf, sizeof (nbuf),
 122                             comma)) == NOSTR) {
 123                                 nstrcpy(nbuf, sizeof (nbuf), abuf);
 124                                 goto normal;
 125                         }
 126                         snprintf(abuf, sizeof (abuf), "%s@%s", np->n_name,
 127                             nbuf);
 128                         np->n_name = savestr(abuf);
 129                         continue;
 130                 }
 131 normal:
 132                 t = nalloc(nbuf);
 133                 t->n_type = ntype;
 134                 if (top == NIL)
 135                         top = t;
 136                 else
 137                         np->n_flink = t;
 138                 t->n_blink = np;
 139                 np = t;
 140         }
 141         return (top);
 142 }
 143 
 144 /*
 145  * Turn a list of names into a string of the same names.
 146  */
 147 
 148 char *
 149 detract(register struct name *np, int ntype)
 150 {
 151         register int s;
 152         register char *cp, *top;
 153         register struct name *p;
 154 
 155         if (np == NIL)
 156                 return (NOSTR);
 157         s = 0;
 158         for (p = np; p != NIL; p = p->n_flink) {
 159                 if ((ntype && (p->n_type & GMASK) != ntype) ||
 160                     (p->n_type & GDEL))
 161                         continue;
 162                 s += strlen(p->n_full) + 2;
 163         }
 164         if (s == 0)
 165                 return (NOSTR);
 166         top = (char *)salloc((unsigned)(++s));
 167         cp = top;
 168         for (p = np; p != NIL; p = p->n_flink) {
 169                 if ((ntype && (p->n_type & GMASK) != ntype) ||
 170                     (p->n_type & GDEL))
 171                         continue;
 172                 cp = copy(p->n_full, cp);
 173                 *cp++ = ',';
 174                 *cp++ = ' ';
 175         }
 176         *cp = 0;
 177         return (top);
 178 }
 179 
 180 struct name *
 181 outpre(struct name *to)
 182 {
 183         register struct name *np;
 184 
 185         for (np = to; np; np = np->n_flink)
 186                 if (isfileaddr(np->n_name))
 187                         np->n_type |= GDEL;
 188         return (to);
 189 }
 190 
 191 /*
 192  * For each recipient in the passed name list with a /
 193  * in the name, append the message to the end of the named file
 194  * and remove him from the recipient list.
 195  *
 196  * Recipients whose name begins with | are piped through the given
 197  * program and removed.
 198  */
 199 
 200 int
 201 outof(struct name *names, FILE *fo)
 202 {
 203         register int c;
 204         register struct name *np;
 205         time_t now;
 206         char *date, *fname, *shell;
 207         FILE *fout, *fin;
 208         int ispipe;


 265                 }
 266 
 267                 /*
 268                  * Now either copy "image" to the desired file
 269                  * or give it as the standard input to the desired
 270                  * program as appropriate.
 271                  */
 272 
 273                 if (ispipe) {
 274                         wait((int *)NULL);
 275                         switch (fork()) {
 276                         case 0:
 277                                 sigchild();
 278                                 sigset(SIGHUP, SIG_IGN);
 279                                 sigset(SIGINT, SIG_IGN);
 280                                 sigset(SIGQUIT, SIG_IGN);
 281                                 close(0);
 282                                 dup(image);
 283                                 close(image);
 284                                 lseek(0, 0L, 0);
 285                                 if ((shell = value("SHELL")) == NOSTR ||
 286                                     *shell == '\0')
 287                                         shell = SHELL;
 288                                 (void) execlp(shell, shell, "-c", fname,
 289                                     (char *)0);
 290                                 perror(shell);
 291                                 exit(1);
 292                                 break;
 293 
 294                         case (pid_t)-1:
 295                                 perror("fork");
 296                                 senderr++;
 297                                 goto cant;
 298                         }
 299                 } else {

 300                         if ((fout = fopen(fname, "a")) == NULL) {
 301                                 perror(fname);
 302                                 senderr++;
 303                                 goto cant;
 304                         }
 305                         fin = Fdopen(image, "r");
 306                         if (fin == NULL) {
 307                                 fprintf(stderr,
 308                                     gettext("Can't reopen image\n"));
 309                                 fclose(fout);
 310                                 senderr++;
 311                                 goto cant;
 312                         }
 313                         rewind(fin);
 314 #ifdef preSVr4
 315                         putc(getc(fin), fout);
 316                         while (fgets(line, sizeof (line), fin)) {
 317                                 if (strncmp(line, "From ", 5) == 0)
 318                                         putc('>', fout);
 319                                 fputs(line, fout);
 320                         }
 321 #else
 322                         while ((c = getc(fin)) != EOF)
 323                                 putc(c, fout);
 324 #endif
 325                         putc('\n', fout);
 326                         fflush(fout);
 327                         if (fferror(fout))
 328                                 senderr++, perror(fname);
 329                         fclose(fout);
 330                         fclose(fin);
 331                 }
 332 cant:
 333                 /*
 334                  * In days of old we removed the entry from the
 335                  * the list; now for sake of header expansion
 336                  * we leave it in and mark it as deleted.
 337                  */


 345                         if (top != NIL)
 346                                 top->n_blink = NIL;
 347                         np = top;
 348                         continue;
 349                 }
 350                 x = np->n_blink;
 351                 t = np->n_flink;
 352                 x->n_flink = t;
 353                 if (t != NIL)
 354                         t->n_blink = x;
 355                 np = t;
 356                 }
 357 #endif
 358 
 359                 np->n_type |= GDEL;
 360         }
 361         if (image >= 0) {
 362                 close(image);
 363                 image = -1;
 364         }
 365         return (nout);
 366 }
 367 
 368 /*
 369  * Determine if the passed address is a local "send to file" address.
 370  * If any of the network metacharacters precedes any slashes, it can't
 371  * be a filename.  We cheat with .'s to allow path names like ./...
 372  * If "fcc" has been unset, then short-circuit those tests, but not
 373  * the +... test.
 374  */
 375 static int
 376 isfileaddr(char *name)
 377 {
 378         register char *cp;
 379         char *fcc = value("fcc");
 380 
 381         if (any('@', name))
 382                 return (0);
 383         if (*name == '+')
 384                 return (1);
 385         if (fcc == NOSTR)
 386                 return (0);
 387         for (cp = name; *cp; cp++) {
 388                 if (*cp == '.')
 389                         continue;
 390                 if (any(*cp, metanet))
 391                         return (0);
 392                 if (*cp == '/')
 393                         return (1);
 394         }
 395         return (0);
 396 }
 397 
 398 /*
 399  * Map all of the aliased users in the invoker's mailrc
 400  * file and insert them into the list.
 401  * Changed after all these months of service to recursively
 402  * expand names (2/14/80).
 403  */
 404 
 405 struct name *
 406 usermap(struct name *names)
 407 {
 408         register struct name *newnames, *np, *cp;
 409         struct grouphead *gh;
 410         register int metoo;
 411 
 412         newnames = NIL;
 413         np = names;
 414         metoo = (value("metoo") != NOSTR);
 415         while (np != NIL) {
 416                 if (np->n_name[0] == '\\') {
 417                         cp = np->n_flink;
 418                         newnames = put(newnames, np);
 419                         np = cp;
 420                         continue;
 421                 }
 422                 gh = findgroup(np->n_name);
 423                 cp = np->n_flink;
 424                 if (gh != NOGRP)
 425                         newnames = gexpand(newnames, gh, metoo, np->n_type);
 426                 else
 427                         newnames = put(newnames, np);
 428                 np = cp;
 429         }
 430         return (newnames);
 431 }
 432 
 433 /*
 434  * Recursively expand a group name.  We limit the expansion to some
 435  * fixed level to keep things from going haywire.
 436  * Direct recursion is not expanded for convenience.
 437  */
 438 
 439 static struct name *
 440 gexpand(struct name *nlist, struct grouphead *gh, int metoo, int arg_ntype)
 441 {
 442         short ntype = (short)arg_ntype;
 443         struct mgroup *gp;
 444         struct grouphead *ngh;
 445         struct name *np;
 446         static int depth;
 447         register char *cp;
 448 
 449         if (depth > MAXEXP) {
 450                 printf(gettext("Expanding alias to depth larger than %d\n"),
 451                     MAXEXP);
 452                 return (nlist);
 453         }
 454         depth++;
 455         for (gp = gh->g_list; gp != NOGE; gp = gp->ge_link) {
 456                 cp = gp->ge_name;
 457                 if (*cp == '\\')
 458                         goto quote;
 459                 if (strcmp(cp, gh->g_name) == 0)
 460                         goto quote;
 461                 if ((ngh = findgroup(cp)) != NOGRP) {
 462                         nlist = gexpand(nlist, ngh, metoo, ntype);
 463                         continue;
 464                 }
 465 quote:
 466                 np = nalloc(cp);
 467                 np->n_type = ntype;
 468                 /*
 469                  * At this point should allow to expand
 470                  * to self if only person in group
 471                  */
 472                 if (gp == gh->g_list && gp->ge_link == NOGE)
 473                         goto skip;
 474                 if (!metoo && samebody(myname, gp->ge_name, FALSE))
 475                         np->n_type |= GDEL;
 476 skip:
 477                 nlist = put(nlist, np);
 478         }
 479         depth--;
 480         return (nlist);
 481 }
 482 
 483 /*
 484  * Normalize a network name for comparison purposes.
 485  */
 486 static char *
 487 norm(register char *user, register char *ubuf, int nbangs)
 488 {
 489         register char *cp;
 490         int inubuf = 0;
 491 
 492         while (*user++ == '!')
 493                 ;
 494         user--;
 495         if (!strchr(user, '!')) {
 496                 snprintf(ubuf, BUFSIZ, "%s!%s", host, user);
 497                 user = ubuf;
 498                 inubuf++;
 499         }
 500         if (nbangs) {
 501                 cp = user + strlen(user);
 502                 while (nbangs--)
 503                         while (cp > user && *--cp != '!')
 504                                 ;
 505                 user = (cp > user) ? ++cp : cp;
 506                 /*
 507                  * Now strip off all Internet-type
 508                  * hosts.
 509                  */
 510                 if ((cp = strchr(user, '%')) == NOSTR)
 511                         cp = strchr(user, '@');
 512                 if (cp != NOSTR) {
 513                         if (!inubuf) {
 514                                 strncpy(ubuf, user, cp - user);
 515                                 ubuf[cp - user] = '\0';
 516                                 user = ubuf;
 517                         } else
 518                                 *cp = '\0';
 519                 }
 520         }
 521         return (user);
 522 }
 523 
 524 /*
 525  * Implement allnet options.
 526  */
 527 int
 528 samebody(register char *user, register char *addr, int fuzzy)
 529 {
 530         char ubuf[BUFSIZ], abuf[BUFSIZ];
 531         char *allnet = value("allnet");
 532         int nbangs = allnet ? (strcmp(allnet, "uucp") == 0) ? 2 : 1 : 0;
 533 
 534         if (fuzzy && value("fuzzymatch")) {
 535                 int i;
 536 
 537                 (void) strlcpy(ubuf, user, BUFSIZ);
 538                 for (i = 0; ubuf[i]; i++)
 539                         ubuf[i] = tolower(ubuf[i]);
 540                 (void) strlcpy(abuf, addr, BUFSIZ);
 541                 for (i = 0; abuf[i]; i++)
 542                         abuf[i] = tolower(abuf[i]);
 543                 return (strstr(abuf, ubuf) != NOSTR);
 544         }
 545         user = norm(user, ubuf, nbangs);
 546         addr = norm(addr, abuf, nbangs);
 547         return (strcmp(user, addr) == 0);
 548 }
 549 
 550 /*
 551  * Compute the length of the passed name list and
 552  * return it.
 553  */
 554 static int
 555 lengthof(struct name *name)
 556 {
 557         register struct name *np;
 558         register int c;
 559 
 560         for (c = 0, np = name; np != NIL; c++, np = np->n_flink)
 561                 ;
 562         return (c);
 563 }
 564 
 565 /*
 566  * Concatenate the two passed name lists, return the result.
 567  */
 568 
 569 struct name *
 570 cat(struct name *n1, struct name *n2)
 571 {
 572         register struct name *tail;
 573 
 574         if (n1 == NIL)
 575                 return (n2);
 576         if (n2 == NIL)
 577                 return (n1);
 578         tail = tailof(n1);
 579         tail->n_flink = n2;
 580         n2->n_blink = tail;
 581         return (n1);
 582 }
 583 
 584 /*
 585  * Unpack the name list onto a vector of strings.
 586  * Return an error if the name list won't fit.
 587  */
 588 
 589 char **
 590 unpack(struct name *np)
 591 {
 592         register char **ap, **top;
 593         register struct name *n;
 594         char hbuf[10];
 595         int t, extra, metoo, verbose;
 596 
 597         n = np;
 598         if ((t = lengthof(n)) == 0)
 599                 panic("No names to unpack");
 600 
 601         /*
 602          * Compute the number of extra arguments we will need.
 603          * We need at least 2 extra -- one for "mail" and one for
 604          * the terminating 0 pointer.
 605          * Additional spots may be needed to pass along -r and -f to
 606          * the host mailer.
 607          */
 608 
 609         extra = 2;
 610 
 611         if (rflag != NOSTR)
 612                 extra += 2;

 613         extra++;
 614         metoo = value("metoo") != NOSTR;
 615         if (metoo)
 616                 extra++;
 617         verbose = value("verbose") != NOSTR;
 618         if (verbose)
 619                 extra++;
 620         if (hflag)
 621                 extra += 2;
 622         top = (char **)salloc((t + extra) * sizeof (char *));

 623         ap = top;
 624         *ap++ = "sendmail";
 625         if (rflag != NOSTR) {
 626                 *ap++ = "-r";
 627                 *ap++ = rflag;
 628         }

 629         *ap++ = "-i";
 630         if (metoo)
 631                 *ap++ = "-m";
 632         if (verbose)
 633                 *ap++ = "-v";
 634         if (hflag) {
 635                 *ap++ = "-h";
 636                 snprintf(hbuf, sizeof (hbuf), "%d", hflag);
 637                 *ap++ = savestr(hbuf);
 638         }

 639         while (n != NIL) {
 640                 if (n->n_type & GDEL) {
 641                         n = n->n_flink;
 642                         continue;
 643                 }
 644                 *ap++ = n->n_name;
 645                 n = n->n_flink;
 646         }
 647         *ap = NOSTR;
 648         return (top);
 649 }
 650 
 651 /*
 652  * See if the user named himself as a destination
 653  * for outgoing mail.  If so, set the global flag
 654  * selfsent so that we avoid removing his mailbox.
 655  */
 656 
 657 void
 658 mechk(struct name *names)
 659 {
 660         register struct name *np;
 661 
 662         for (np = names; np != NIL; np = np->n_flink)
 663                 if ((np->n_type & GDEL) == 0 &&
 664                     samebody(np->n_name, myname, FALSE)) {
 665                         selfsent++;
 666                         return;
 667                 }
 668 }
 669 
 670 /*
 671  * Remove all of the duplicates from the passed name list by
 672  * insertion sorting them, then checking for dups.
 673  * Return the head of the new list.
 674  */
 675 
 676 struct name *
 677 elide(struct name *names)
 678 {
 679         register struct name *np, *t, *newnames;
 680         struct name *x;
 681 
 682         if (names == NIL)
 683                 return (NIL);
 684         newnames = names;
 685         np = names;
 686         np = np->n_flink;
 687         if (np != NIL)
 688                 np->n_blink = NIL;
 689         newnames->n_flink = NIL;
 690         while (np != NIL) {
 691                 t = newnames;
 692                 while (strcmp(t->n_name, np->n_name) < 0) {
 693                         if (t->n_flink == NIL)
 694                                 break;
 695                         t = t->n_flink;
 696                 }
 697 
 698                 /*
 699                  * If we ran out of t's, put the new entry after
 700                  * the current value of t.
 701                  */
 702 
 703                 if (strcmp(t->n_name, np->n_name) < 0) {


 734                 np = np->n_flink;
 735                 x->n_flink = t;
 736                 x->n_blink = t->n_blink;
 737                 t->n_blink->n_flink = x;
 738                 t->n_blink = x;
 739         }
 740 
 741         /*
 742          * Now the list headed up by new is sorted.
 743          * Go through it and remove duplicates.
 744          * Remember the best "type" among all the
 745          * duplicates of a name.
 746          */
 747 
 748         np = newnames;
 749         while (np != NIL) {
 750                 int type;
 751 
 752                 t = np;
 753                 type = np->n_type;
 754                 while (t->n_flink != NIL &&
 755                     strcmp(np->n_name, t->n_flink->n_name) == 0) {
 756                         t = t->n_flink;
 757                         /* "To" before "Cc" before "Bcc" */
 758                         if (t->n_type < type)
 759                                 type = t->n_type;
 760                 }
 761                 if (t == np || t == NIL) {
 762                         np = np->n_flink;
 763                         continue;
 764                 }
 765 
 766                 /*
 767                  * Now t points to the last entry with the same name
 768                  * as np.  Make np point beyond t.
 769                  */
 770 
 771                 np->n_flink = t->n_flink;
 772                 if (t->n_flink != NIL)
 773                         t->n_flink->n_blink = np;
 774                 np->n_type = type;
 775                 np = np->n_flink;
 776         }
 777         return (newnames);
 778 }
 779 
 780 /*
 781  * Put another node onto a list of names and return
 782  * the list.
 783  */
 784 
 785 static struct name *
 786 put(struct name *list, struct name *node)
 787 {
 788         node->n_flink = list;
 789         node->n_blink = NIL;
 790         if (list != NIL)
 791                 list->n_blink = node;
 792         return (node);
 793 }
 794 
 795 
 796 /*
 797  * Delete the given name from a namelist.
 798  */
 799 struct name *
 800 delname(register struct name *np, char name[])
 801 {
 802         register struct name *p;
 803 
 804         for (p = np; p != NIL; p = p->n_flink)
 805                 if (samebody(name, p->n_name, FALSE)) {
 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 void
 829 mapf(register struct name *np, char *from)
 830 {
 831         register struct name *p;
 832 
 833         if (debug) fprintf(stderr, "mapf %lx, %s\n", (long)np, from);
 834         for (p = np; p != NIL; p = p->n_flink)
 835                 if ((p->n_type & GDEL) == 0) {
 836                         p->n_name = netmap(p->n_name, from);
 837                         p->n_full = splice(p->n_name, p->n_full);
 838                 }
 839         if (debug) fprintf(stderr, "mapf %s done\n", from);
 840 }