home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume24 / newsgate / part02 / news2mail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-09  |  12.5 KB  |  518 lines

  1. /*
  2. **  NEWS2MAIL
  3. **  Read a news article on standard input, and send it to the mailing
  4. **  list as directed by the command-line arguments.  It does some
  5. **  parsing and converting of news headers into mail headers.
  6. **
  7. **  This program wants to lie to sendmail, so it should be setuid to
  8. **  one of the "trusted" users as listed in your sendmail.cf file.
  9. **
  10. */
  11. #include "gate.h"
  12. #include <sys/stat.h>
  13. #ifdef    RCSID
  14. static char RCS[] =
  15.     "$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/news2mail.c,v 1.10 91/02/12 14:50:11 rsalz Exp $";
  16. #endif    /* RCSID */
  17.  
  18.  
  19. /* Flags for special header lines. */
  20. typedef enum _HEADERTYPE {
  21.     HDR_NORM,
  22.     HDR_SUBJ,
  23.     HDR_CTRL,
  24.     HDR_REFS,
  25.     HDR_PATH,
  26.     HDR_FROM
  27. } HEADERTYPE;
  28.  
  29.  
  30. /* Header-cracking datatype. */
  31. typedef struct _HEADER {
  32.     char        *Tag;
  33.     int            Length;
  34.     HEADERTYPE        Flag;
  35.     char        Value[SM_SIZE];
  36. } HEADER;
  37.  
  38. STATIC int    Debugging;
  39. char        *Pname;
  40.  
  41. /* The headers we care about. */
  42. STATIC HEADER    Table[] = {
  43.     {    "Control",     7,     HDR_CTRL    },
  44.     {    "Date",         4,     HDR_NORM    },
  45.     {    "From",         4,     HDR_FROM    },
  46.     {    "Message-ID",    10,     HDR_NORM    },
  47.     {    "Organization",    12,     HDR_NORM    },
  48.     {    "Path",         4,     HDR_PATH    },
  49.     {    "References",    10,     HDR_REFS    },
  50.     {    "Reply-To",     8,     HDR_NORM    },
  51.     {    "Subject",     7,     HDR_SUBJ    },
  52. };
  53.  
  54.  
  55. /*
  56. **  Figure out the return address, by doing some optimization on the
  57. **  path.  If we find an Internet host or a UUCP neighbor, remove all
  58. **  hosts before that one from the path.
  59. */
  60. STATIC char *
  61. EditPath(path, Host)
  62.     register char    *path;
  63.     char        *Host;
  64. {
  65.     static char        **Hinet;
  66.     static char        **Huucp;
  67.     static char        **Lsys;
  68.     static char        buff[SM_SIZE];
  69.     register char    *p;
  70.     register char    *q;
  71.     register char    **V;
  72.     register int    ac;
  73.     register int    i;
  74.     register int    uucppal;
  75.     register int    inetpal;
  76.     FILE        *F;
  77.     FILE        *P;
  78.     struct stat        Sb1;
  79.     struct stat        Sb2;
  80.     char        **av;
  81.     char        *Inetsite;
  82.  
  83.     if ((ac = Split(path, &av, '!')) == 2) {
  84.     /* Simple case:  "site!user" */
  85.     Strcpy(buff, av[1]);
  86.     SplitFree(&av);
  87.     return buff;
  88.     }
  89.  
  90.     /* Initialize.  This is silly for now, but eventually we might want
  91.      * to be able to handle a batched feed. */
  92. #ifdef    UUNAME
  93.     if (Lsys == NULL) {
  94.     p = UUNAME;
  95.     /* If someone diddled L.sys, rebuild uuname output. */
  96.     if (stat(L_SYS, &Sb1) < 0 || stat(p, &Sb2) < 0
  97.       || Sb1.st_mtime >= Sb2.st_mtime)
  98.         if ((F = fopen(p, "w"))  == NULL)
  99.         Fprintf(stderr, "%s:  Can't create %s, %s.\n",
  100.             Pname, p, strerror(errno));
  101.         else {
  102.         if ((P = popen("exec uuname", "r")) == NULL)
  103.             Fprintf(stderr, "%s:  popen failed, %s.\n",
  104.                 Pname, strerror(errno));
  105.         else {
  106.             while (fgets(buff, sizeof buff, P))
  107.             Fputs(buff, F);
  108.             if (pclose(P))
  109.             Fprintf(stderr, "%s:  pclose failed, %s.\n",
  110.                 Pname, strerror(errno));
  111.         }
  112.         if (fclose(F) == EOF)
  113.             Fprintf(stderr, "%s:  Error closing %s, %s.\n",
  114.                 Pname, p, strerror(errno));
  115.         }
  116.  
  117.     /* Slurp up names of UUCP hosts we talk to. */
  118.     Lsys = ReadFile(p);
  119.  
  120. #ifdef    UUCP_INET
  121.     /* Slurp up the UUCP->Internet name mappings. */
  122.     for (Huucp = ReadFile(UUCP_INET), i = 1; Huucp[i]; i++)
  123.         ;
  124.     for (Hinet = NEW(char*, i), i = 0; p = Huucp[i]; i++) {
  125.         while (*p && !WHITE(*p))
  126.         p++;
  127.         if (*p)
  128.         for (*p++ = '\0'; *p && WHITE(*p); p++)
  129.             ;
  130.         Hinet[i] = p;
  131.     }
  132. #endif    /* UUCP_INET */
  133.     }
  134. #endif    /* UUNAME */
  135.  
  136.     /* Scan the path, noting if we find a UUCP or Internet neighbor. */
  137.     for (uucppal = 1, inetpal = 0, i = 0; i < ac; i++) {
  138.     for (V = Lsys; *V; V++)
  139.         if (EQ(av[i], *V))
  140.         uucppal = i;
  141.     for (V = Huucp; *V; V++)
  142.         if (EQ(av[i], *V)) {
  143.         inetpal = i;
  144.         Inetsite = Hinet[V - Huucp];
  145.         }
  146.     }
  147.  
  148.     if (inetpal < uucppal) {
  149.     /* No Internet site found, turn a!b!c into a!b!c@this-host */
  150.     for (p = buff + APPEND(buff, av[uucppal]); ++uucppal < ac; ) {
  151.         *p++ = '!';
  152.         p += APPEND(p, av[uucppal]);
  153.     }
  154.     *p++ = '@';
  155.     Strcpy(p, Host);
  156.     }
  157.     else if (inetpal == ac - 1)
  158.     /* Turn a!b!inet!user into user@inet.domain.name */
  159.     Sprintf(buff, "%s@%s", av[ac], Inetsite);
  160.     else {
  161.     /* Turn a!inet!b!user into b!user@inet.domain.name */
  162.     for (p = buff + APPEND(buff, av[++inetpal]); ++inetpal < ac; ) {
  163.         *p++ = '!';
  164.         p += APPEND(p, av[inetpal]);
  165.     }
  166.     *p++ = '@';
  167.     Strcpy(p, Inetsite);
  168.     }
  169.  
  170.     /* Convert all but the last "@" to "%" (fie on decwrl and psuecl!). */
  171.     if (p = IDX(buff, '@'))
  172.     for ( ; q = IDX(p + 1, '@'); p = q)
  173.         *p = '%';
  174.  
  175.     SplitFree(&av);
  176.     return buff;
  177. }
  178.  
  179.  
  180. /*
  181. **  Hack up the references, taking only the last three.
  182. */
  183. STATIC char *
  184. TrimReferences(refs)
  185.     char         *refs;
  186. {
  187.     static char        buff[SM_SIZE];
  188.     register char    *p;
  189.     register int    i;
  190.     register int    ac;
  191.     char        **av;
  192.  
  193.     if (ac = Split(refs, &av, '\0')) {
  194.     /* Tricky.  If there are five references, we want subscripts 2,3,4. */
  195.     i = ac < 3 ? 0 : ac - 3;
  196.     for (p = buff + APPEND(buff, av[i]); ++i < ac; ) {
  197.         *p++ = ',';
  198.         *p++ = ' ';
  199.         p += APPEND(p, av[i]);
  200.     }
  201.     SplitFree(&av);
  202.     }
  203.     else
  204.     buff[0] = '\0';
  205.     return buff;
  206. }
  207.  
  208.  
  209. #ifndef    HAVE_PUTENV
  210. /*
  211. **  A brute-forced implementation of putenv.  Wastes memory.  Consider
  212. **  it incentive to install the free BSD version...
  213. */
  214. int
  215. putenv(val)
  216.     char    *val;
  217. {
  218.     char    **new;
  219.     int        i;
  220.     int        length;
  221.     int        found;
  222.     char    *p;
  223.  
  224.     /* See if the value is already in the environment. */
  225.     found = -1;
  226.     if (p = IDX(val, '=')) {
  227.     for (length = ++p - val, i = 0; environ[i]; i++)
  228.         if (EQn(val, environ[i], length)) {
  229.         found = i;
  230.         break;
  231.         }
  232.     }
  233.  
  234.     /* Get the size, and space for the new environment. */
  235.     for (i = 0; environ[i]; i++)
  236.     ;
  237.     i += 2;
  238.     new = NEW(char*, i);
  239.     new[0] = val;
  240.  
  241.     /* Copy the old to the new. */
  242.     for (i = 0; environ[i]; i++)
  243.     if (i != found)
  244.         new[i + 1] = environ[i];
  245.     new[i + 1] = NULL;
  246.     environ = new;
  247.     return 0;
  248. }
  249. #endif    /* HAVE_PUTENV */
  250.  
  251.  
  252. /*
  253. **  Print a usage message and exit.
  254. */
  255. STATIC void
  256. Usage()
  257. {
  258.     Fprintf(stderr, "Usage:\n\t%s %s %s\n",
  259.     Pname,
  260.     "[-.] [-e var=val]",
  261.     "listname listaddr listadmin host [article]");
  262.     exit(EX_USAGE);
  263. }
  264.  
  265.  
  266. main(ac, av)
  267.     int            ac;
  268.     register char    *av[];
  269. {
  270.     static char        tmp[sizeof TEMPFILE];
  271.     register FILE    *F;
  272.     register HEADER    *hp;
  273.     register char    *p;
  274.     char        *sv[10];
  275.     char        buff[BUFSIZ];
  276.     char        SenderAddr[SM_SIZE];
  277.     char        ToAddr[SM_SIZE];
  278.     char        Host[SM_SIZE];
  279.     char        Fullname[SM_SIZE];
  280.     int            i;
  281.     int            HadEflag;
  282.     char        *Listname;
  283.     char        *Listaddr;
  284.     char        *Listadmin;
  285.     char        *Listhost;
  286.     char        *Article;
  287.  
  288.     /* Set defaults. */
  289.     Pname = (Pname = RDX(av[0], '/')) ? Pname + 1 : av[0];
  290.     HadEflag = FALSE;
  291.  
  292.     /* Parse JCL. */
  293.     while ((i = getopt(ac, av, "E:.")) != EOF)
  294.     switch (i) {
  295.     default:
  296.         Usage();
  297.         /* NOTREACHED */
  298.     case '.':
  299.         Debugging = TRUE;
  300.         break;
  301.     case 'E':
  302.         if (putenv(COPY(optarg))) {
  303.         Fprintf(stderr, "%s:  Can't add to environment, %s.\n",
  304.             Pname, strerror(errno));
  305.         exit(EX_TEMPFAIL);
  306.         }
  307.         HadEflag = TRUE;
  308.         break;
  309.     }
  310.     ac -= optind;
  311.     av += optind;
  312.     if (ac != 4 && ac != 5)
  313.     Usage();
  314.  
  315.     /* Parse the positional parameters. */
  316.     Listname = av[0];
  317.     Listaddr = av[1];
  318.     Listadmin = av[2];
  319.     Listhost = av[3];
  320.     Article = av[4];
  321.  
  322.     /* Arrange for logging. */
  323.     if (!Debugging && freopen(ERR_LOG, "a", stderr) == NULL)
  324.     /* Sigh; error in error handler.... */
  325.     (void)freopen("/dev/console", "w", stderr);
  326.  
  327.     if (Article && freopen(Article, "r", stdin) == NULL) {
  328.     Fprintf(stderr, "%s:  Can't open %s, %s.\n",
  329.         Pname, Article, strerror(errno));
  330.     exit(EX_NOINPUT);
  331.     }
  332.  
  333.     /* Who are we? */
  334.     if (gethostname(Host, sizeof Host) < 0) {
  335.     Fprintf(stderr, "%s:  Can't get hostname, %s.\n",
  336.         Pname, strerror(errno));
  337.     exit(EX_TEMPFAIL);
  338.     }
  339.  
  340.     /* Read headers, storing the ones we want. */
  341.     while (fgets(buff, sizeof buff, stdin)) {
  342.     if (p = IDX(buff, '\n'))
  343.         *p = '\0';
  344.     else
  345.         Fprintf(stderr, "%s:  Header line too long (%d bytes max)\n\t%s\n",
  346.         Pname, sizeof buff, buff);
  347.     if (p == buff)
  348.         /* Blank line means end of headers. */
  349.         break;
  350.  
  351.     for (hp = Table; hp < ENDOF(Table); hp++)
  352.         if (buff[hp->Length] == ':' && EQn(hp->Tag, buff, hp->Length)) {
  353.         /* Skip whitespace. */
  354.         for (p = &buff[hp->Length]; *p && WHITE(*p); p++)
  355.             ;
  356.         switch (hp->Flag) {
  357.         case HDR_SUBJ:
  358.             if (!EQn(p, "cmsg ", 5)) {
  359.             Strcpy(hp->Value, p);
  360.             break;
  361.             }
  362.             /* FALLTHROUGH */
  363.         case HDR_CTRL:
  364.             /* Eat rest of message. */
  365.             while (fgets(buff, sizeof buff, stdin))
  366.             ;
  367.             exit(EX_OK);
  368.             /* NOTREACHED */
  369.         case HDR_NORM:
  370.             Strcpy(hp->Value, p);
  371.             break;
  372.         case HDR_FROM:
  373.             /* Turn "joe@site.uucp (My Name)" into "(My Name)" */
  374.             if ((p = IDX(p, ' ')) && p[1] == '(')
  375.             Strcpy(Fullname, ++p);
  376.             else
  377.             Fullname[0] = '\0';
  378.             break;
  379.         case HDR_REFS:
  380.             Strcpy(hp->Value, TrimReferences(p));
  381.             break;
  382.         case HDR_PATH:
  383.             Strcpy(hp->Value, EditPath(p, Host));
  384.             break;
  385.         }
  386.     }
  387.     }
  388.  
  389.     /* Set up temp output. */
  390.     if ((F = fopen(mktemp(strcpy(tmp, TEMPFILE)), "w")) == NULL) {
  391.     Fprintf(stderr, "%s:  Can't create %s, %s.\n", tmp, strerror(errno));
  392.     exit(EX_CANTCREAT);
  393.     }
  394.  
  395.     if (IDX(Listadmin, '@'))
  396.     Strcpy(SenderAddr, Listadmin);
  397.     else
  398.     Sprintf(SenderAddr, "%s@%s", Listadmin, Listhost);
  399.     if (IDX(Listaddr, '@'))
  400.     Strcpy(ToAddr, Listaddr);
  401.     else
  402.     Sprintf(ToAddr, "%s@%s", Listaddr, Listhost);
  403.  
  404.     /* Print out a sanitized header for mail. */
  405.     if (IDX(Listname, '@'))
  406.     Strcpy(buff, Listname);
  407.     else
  408.     Sprintf(buff, "%s@%s", Listname, Listhost);
  409.     Fprintf(F, "Received: from USENET by %s with netnews\n", Host);
  410.     Fprintf(F, "\tfor %s (%s);\n", ToAddr, buff);
  411.     Fprintf(F, "\tcontact usenet@%s if you have questions.\n", Host);
  412.     Fprintf(F, "To: %s\n", buff);
  413.     for (hp = Table; hp < ENDOF(Table); hp++)
  414.     if (hp->Flag == HDR_PATH) {
  415.         Fprintf(F, "From: %s %s\n", hp->Value, Fullname);
  416.         Fprintf(F, "Sender: %s\n", SenderAddr);
  417.     }
  418.     else if (hp->Value[0])
  419.         Fprintf(F, "%s %s\n", hp->Tag, hp->Value);
  420.     Fprintf(F, "\n");
  421.  
  422.     /* Dump the body of the message. */
  423.     while (fgets(buff, sizeof buff, stdin))
  424.     Fputs(buff, F);
  425.     if (fclose(F) == EOF)
  426.     Fprintf(stderr, "%s:  Error closing %s, %s.\n",
  427.         Pname, tmp, strerror(errno));
  428.  
  429.     /* Set I/O correctly.  Stderr is going to the log, stdin should be the
  430.      * message we created.  Easiest thing is to unlink an open file. */
  431.     if (freopen(tmp, "r", stdin) == NULL) {
  432.     Fprintf(stderr, "%s:  Can't open %s for reading, %s.\n",
  433.         Pname, tmp, strerror(errno));
  434.     exit(EX_OSERR);
  435.     }
  436.     if (unlink(tmp) < 0)
  437.     Fprintf(stderr, "%s:  Can't unlink %s, %s.\n",
  438.         Pname, tmp, strerror(errno));
  439.  
  440.     /* Common code for all argument vectors. */
  441.     i = 0;
  442.  
  443. #ifdef    MAILSCRIPT
  444.     /* Build the MAILSCRIPT argument vector. */
  445.     sv[i++] = MAILSCRIPT;
  446.     /* Headers are inline. */
  447.     sv[i++] = "-ASIS";
  448.     /* Set the logical sender/from address. */
  449.     sv[i++] = "-From";
  450.     sv[i++] = SenderAddr;
  451.     /* Set the recipient. */
  452.     sv[i++] = "-recip";
  453.     sv[i++] = ToAddr;
  454. #endif    /* MAILSCRIPT */
  455.  
  456. #ifdef    SENDMAIL
  457.     /* Build of the SENDMAIL argument vector. */
  458.     sv[i++] = SENDMAIL;
  459.     /* Ignore periods as message terminator (same as -oi). */
  460.     sv[i++] = "-i";
  461.     /* Queued delivery. */
  462.     sv[i++] = "-odq";
  463.     /* Set the "From:" address. */
  464.     sv[i++] = "-f";
  465.     sv[i++] = SenderAddr;
  466.     /* Set the recipient. */
  467.     sv[i++] = ToAddr;
  468. #endif    /* SENDMAIL */
  469.  
  470. #ifdef    MMDF
  471.     /* Build of the MMDF argument vector. */
  472.     sv[i++] = MMDF;
  473.     /* Deliver to mailbox (m), deliver all mail now (ln), trust me (t), send
  474.      * no warnings (z), return to "Sender:" (s), get recipients from the
  475.      * "To:" address (xto*). */
  476.     sv[i++] = "-mlntzsxto*";
  477.     /* Set the From: address. */
  478.     sv[i++] = SenderAddr;
  479. #endif    /* MMDF */
  480.  
  481.     /* Null-terminate the vector. */
  482.     sv[i] = NULL;
  483.  
  484.     if (Debugging) {
  485.     for (i = 0; sv[i]; i++)
  486.         (void)printf(" |%s| ", sv[i]);
  487.     (void)printf("\n");
  488.     if (HadEflag) {
  489.         for (i = 0; sv[i]; i++)
  490.         (void)printf(" [%s] ", sv[i]);
  491.         (void)printf("\n");
  492.     }
  493.     while (fgets(buff, sizeof buff, stdin))
  494.         Fputs(buff, stdout);
  495.     exit(EX_OK);
  496.     }
  497.  
  498.     /* Try to setuid, if desired.  Could "factor out" the execv from the
  499.      * #ifdef, but I hate the way the resultant "dangling else" looks. */
  500. #ifdef    TRUSTED
  501.     if (setuid(TRUSTED) < 0)
  502.     Fprintf(stderr, "%s:  Can't setuid to %d, %s.\n",
  503.         Pname, TRUSTED, strerror(errno));
  504.     else
  505.     (void)execv(sv[0], sv);
  506. #else
  507.     (void)execv(sv[0], sv);
  508. #endif    /* TRUSTED */
  509.  
  510.     /* Something failed; dump the message and quit */
  511.     Fprintf(stderr, "%s:  Can't execv %s, %s.\n",
  512.     Pname, sv[0], strerror(errno));
  513.     while (fgets(buff, sizeof buff, stdin))
  514.     Fputs(buff, stdout);
  515.     exit(EX_OSERR);
  516.     /* NOTREACHED */
  517. }
  518.