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

  1. /*
  2. **  MAIL2NEWS
  3. **  Gateway mail messages into netnews.  Usage:
  4. **    mail2news [inews flags] -o Organization
  5. **  In order to do this, there are a number of interesting transformations
  6. **  that need to be made on the headers...
  7. **
  8. **  This program is descended from:  @(#)recnews.c 2.10 4/16/85.
  9. */
  10. #include "gate.h"
  11. #include <signal.h>
  12. #include <sys/file.h>
  13. #ifdef    RCSID
  14. static char RCS[] =
  15.     "$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/mail2news.c,v 1.13 91/03/13 15:57:32 rsalz Exp $";
  16. #endif    /* RCSID */
  17.  
  18.  
  19. #define    WAIT_CORED(s)        (s & 0200)
  20. #define    WAIT_EXITSIG(s)        (s & 0177)
  21. #define    WAIT_EXITCODE(s)    ((s >> 8) & 0377)
  22.  
  23. /* For those who don't have this in <sys/file.h>. */
  24. #ifndef    R_OK
  25. #define    R_OK            4
  26. #endif    /* R_OK */
  27.  
  28. /* Stuff for pipe(2). */
  29. #define STDIN            0
  30. #define    PIPE_READER        0
  31. #define    PIPE_WRITER        1
  32.  
  33.  
  34. /* Global variables. */
  35. STATIC int    Debugging;
  36. STATIC int    ChildPid = -1;
  37. char        *Pname;
  38.  
  39.  
  40.  
  41. /*
  42. **  Go down to the absolute minimum.
  43. */
  44. STATIC void
  45. TrimEnvironment()
  46. {
  47.     environ = NULL;
  48. }
  49.  
  50.  
  51. /*
  52. **  Quickie hack to see of a mail message is a "please drop me" request.
  53. **  Reads the message on the input file, and returns NULL if it should
  54. **  be ignored, or a FILE handle if we should process it.
  55. **
  56. **  The original stand-alone program written was Russ Nelson,
  57. **  <nelson@clutx.clarkson.edu>.  I hacked on it, and made it into a
  58. **  subroutine.  Perhaps a better way to test is to make the test less
  59. **  conservative, and see what "real" articles get caught, and make
  60. **  adjustments then?  Comments solicited.
  61. */
  62. STATIC FILE *
  63. IsSubRequest(F)
  64.     register FILE    *F;
  65. {
  66.     register FILE    *Out;
  67.     register char    *p;
  68.     register int    c;
  69.     register int    drop_or_add;
  70.     register int    from_or_to;
  71.     register int    mail_word;
  72.     register int    count;
  73.     char        word[SM_SIZE];
  74.     char        buff[SM_SIZE];
  75.  
  76.     /* Create temp file; if we can't let the message through. */
  77.     if ((Out = fopen(mktemp(strcpy(buff, TEMPFILE)), "w")) == NULL)
  78.     return F;
  79.  
  80.     /* Clear counts. */
  81.     drop_or_add = 0;
  82.     from_or_to = 0;
  83.     mail_word = 0;
  84.     count = 0;
  85.  
  86.     /* Read input a word at a time. */
  87.     for (p = word; (c = getc(F)) != EOF; ) {
  88.     (void)putc(c, Out);
  89.     if (!isalpha(c)) {
  90.         *p = '\0';
  91.         if (p > word)
  92.         count++;
  93.         p = word;
  94.  
  95.         if (EQ(word, "remove") || EQ(word, "drop") || EQ(word, "off")
  96.          || EQ(word, "subscribe") || EQ(word, "get") || EQ(word, "add"))
  97.         drop_or_add++;
  98.         else if (EQ(word, "from") || EQ(word, "to"))
  99.         from_or_to++;
  100.         else if (EQ(word, "mail") || EQ(word, "mailing")
  101.           || EQ(word, "list") || EQ(word, "dl"))
  102.         mail_word++;
  103.     }
  104.     else if (p < &word[sizeof word - 1])
  105.         *p++ = isupper(c) ? tolower(c) : c;
  106.     }
  107.  
  108.     (void)fclose(F);
  109.     (void)fclose(Out);
  110.  
  111.     /* Use fancy-shmancy AI techniques to determine what the message is. */
  112.     c = count < 25 && drop_or_add && from_or_to && mail_word;
  113.     F = c ? NULL : fopen(buff, "r");
  114.  
  115.     (void)unlink(buff);
  116.     return F;
  117. }
  118.  
  119.  
  120. /*
  121. **  Modify the Newsgroups: as directed by the command string.
  122. */
  123. STATIC void
  124. DoCommand(hp, command, group)
  125.     register HBUF        *hp;
  126.     char            *command;
  127.     char            *group;
  128. {
  129.     register char        *p;
  130.     register int        i;
  131.     register int        n;
  132.     register int        nng;
  133.     char            **tokens;
  134.     char            **ng;
  135.     char            buff[BUFSIZ];
  136.  
  137.     if ((n = Split(command, &tokens, '\0')) == 0) {
  138.     SplitFree(&tokens);
  139.     return;
  140.     }
  141.  
  142.     nng = Split(hp->nbuf, &ng, NGDELIM);
  143.     p = hp->nbuf;
  144.     switch (tokens[0][0]) {
  145.     case 'a':                /* Add        */
  146.     if (n > 1)
  147.         for (p += strlen(p), i = 1; i < n; i++) {
  148.         *p++ = NGDELIM;
  149.         p += APPEND(p, tokens[i]);
  150.         }
  151.     break;
  152.     case 'd':                /* Delete    */
  153.     for (i = 0; i < nng; i++)
  154.         if (!EQ(ng[i], group)) {
  155.         if (p > hp->nbuf)
  156.             *p++ = NGDELIM;
  157.         p += APPEND(p, ng[i]);
  158.         }
  159.     if (p == hp->nbuf)
  160.         Strcpy(hp->nbuf, "junk");
  161.     break;
  162.     case 'k':                /* Kill        */
  163.     Fprintf(stderr, "%s:  Your posting to %s was killed by %s.\n",
  164.         Pname, hp->nbuf, n > 1 ? tokens[1] : group);
  165.     exit(EX_NOPERM);
  166.     /* NOTREACHED */
  167.     case 'm':                /* Move        */
  168.     if (n > 1)
  169.         if (nng == 1)
  170.         Strcpy(hp->nbuf, tokens[1]);
  171.         else
  172.         for (i = 0; i < nng; i++) {
  173.             if (p > hp->nbuf)
  174.             *p++ = NGDELIM;
  175.             p += APPEND(p, EQ(ng[i], group) ? tokens[1] : ng[i]);
  176.         }
  177.     break;
  178.     case 'q':                /* Quiet kill    */
  179.     if (Debugging) {
  180.         (void)printf("Quiet kill (ignored for debugging).\n");
  181.         break;
  182.     }
  183.     /* Eat the message up, and pretend we delivered it. */
  184.     while (fgets(buff, sizeof buff, stdin))
  185.         ;
  186.     exit(EX_OK);
  187.     /* NOTREACHED */
  188.     }
  189.  
  190.     SplitFree(&tokens);
  191.     SplitFree(&ng);
  192. }
  193.  
  194.  
  195. /*
  196. **  Split a line that looks like XpatternXcommandX into the pattern and
  197. **  the command.  Initialize the RE matcher with the pattern, and return
  198. **  the command.
  199. */
  200. STATIC char *
  201. ParsePattern(p, lineno)
  202.     register char    *p;
  203.     int            lineno;
  204. {
  205.     register char    *cp;
  206.     register char    *command;
  207.     register char    delim;
  208.     char        *RE;
  209.  
  210.     /* Ignore comments and blank lines. */
  211.     if (*p == '#' || *p == '\0')
  212.     return NULL;
  213.  
  214.     for (delim = *p++, RE = cp = p, command = NULL; *cp; *p++ = *cp++)
  215.     if (*cp == '\\' && cp[1] == delim)
  216.         cp++;
  217.     else if (*cp == delim) {
  218.         /* Found delimiter; mark command, terminate RE. */
  219.         command = ++cp;
  220.         *p = '\0';
  221.         break;
  222.     }
  223.  
  224.     if (command == NULL || *command == '\0')
  225.     Fprintf(stderr, "%s:  Incomplete regular expression, line %d.\n",
  226.         Pname, lineno);
  227.     else if (cp = re_comp(RE))
  228.     Fprintf(stderr, "%s:  Bad regular expression, line %d: %s.\n",
  229.         Pname, lineno, cp);
  230.     else
  231.     return command;
  232.  
  233. #ifdef    lint
  234.     /* My, my, aren't we anal. */
  235.     (void)re_subs("", "");
  236.     re_modw("");
  237. #endif    /* lint */
  238.  
  239.     return NULL;
  240. }
  241.  
  242.  
  243. /*
  244. **  Convert string to lower case, return a new copy.
  245. */
  246. STATIC char *
  247. MakeLowerCopy(s)
  248.     register char    *s;
  249. {
  250.     register char    *p;
  251.  
  252.     for (p = s = COPY(s); *p; p++)
  253.     if (isupper(*p))
  254.         *p = tolower(*p);
  255.     return s;
  256. }
  257.  
  258.  
  259. /*
  260. **  Change newsgroups if the Subject:, Keywords:, or Summary: match a
  261. **  pattern found in the newsgroup remap file.
  262. */
  263. STATIC void
  264. Editnewsgroups(hp)
  265.     register HBUF        *hp;
  266. {
  267.     register char        *p;
  268.     register int        n;
  269.     register int        i;
  270.     register int        j;
  271.     register int        t;
  272.     char            **groups;
  273.     char            **mapline;
  274.     char            *hdrline[4];
  275.     char            buff[LG_SIZE];
  276.  
  277.     /* Copy some headers, but if nothing's there, give up. */
  278.     i = 0;
  279.     if (hp->title)
  280.     hdrline[i++] = MakeLowerCopy(hp->title);
  281.     if (hp->keywords)
  282.     hdrline[i++] = MakeLowerCopy(hp->keywords);
  283.     if (hp->summary)
  284.     hdrline[i++] = MakeLowerCopy(hp->summary);
  285.     if (i == 0)
  286.     return;
  287.     hdrline[i] = NULL;
  288.  
  289.     /* For all the newsgroups, see if there's a mapping file. */
  290.     for (n = Split(hp->nbuf, &groups, NGDELIM), i = 0; i < n; i++) {
  291.     if (groups[i] == NULL || groups[i][0] == '\0')
  292.         continue;
  293.  
  294.     /* Gate the name of the mapping file. */
  295. #ifdef    IN_ONEPLACE
  296.     Strcpy(buff, IN_ONEPLACE);
  297. #endif    /* IN_ONEPLACE */
  298. #ifdef    IN_SPOOLDIR
  299.     {
  300.         register char    *q;
  301.  
  302.         for (p = buff + APPEND(buff, IN_SPOOLDIR), q = groups[i]; *q; q++)
  303.         *p++ = *q == '.' ? '/' : *q;
  304.         Strcpy(p, "/recnews.cmd");
  305.     }
  306. #endif    /* IN_SPOOLDIR */
  307. #ifdef    IN_CMDDIR
  308.     Sprintf(buff, "%s/%s", IN_CMDDIR, groups[i]);
  309. #endif    /* IN_CMDDIR */
  310.  
  311.     if (access(buff, R_OK) >= 0 && (mapline = ReadFile(buff))) {
  312.         /* For all lines in the file, if there's a command and the
  313.          * pattern matches, execute the command. */
  314.         for (j = 0; mapline[j]; j++)
  315.         if (p = ParsePattern(mapline[j], j))
  316.             for (t = 0; hdrline[t]; t++)
  317.             if (re_exec(hdrline[t]) == 1) {
  318.                 DoCommand(hp, p, groups[i]);
  319.                 break;
  320.             }
  321.         FreeFile(mapline);
  322.     }
  323.     }
  324.  
  325.     /* Free dynamic space. */
  326.     for (i = 0; hdrline[i]; i++)
  327.     free(hdrline[i]);
  328.     SplitFree(&groups);
  329. }
  330.  
  331.  
  332. /*
  333. **  Signal-catcher and child-reapers.
  334. */
  335.  
  336.  
  337. /*
  338. **  Exit such that sendmail will again later.
  339. */
  340. STATIC CATCHER
  341. Sig_tempfail()
  342. {
  343.     exit(EX_TEMPFAIL);
  344. }
  345.  
  346.  
  347. /*
  348. **  Reap the inews child properly, and exit with his exit code, so that
  349. **  ultimate success or failure rests with inews.
  350. */
  351. STATIC CATCHER
  352. childgone()
  353. {
  354.     register int    pid;
  355.     int            W;
  356.  
  357.     if ((pid = wait(&W)) != ChildPid || pid == -1)
  358.     exit(EX_OSERR);
  359.     
  360.     /* Was it a good death? */
  361.     if (WAIT_EXITSIG(W)) {
  362.     Fprintf(stderr, "%s:  Child %d killed by signal %d.\n",
  363.         Pname, pid, WAIT_EXITSIG(W));
  364.     if (WAIT_CORED(W))
  365.         Fprintf(stderr, "%s:  Child %d dumped core.\n", Pname, pid);
  366.     exit(EX_SOFTWARE);
  367.     }
  368.  
  369. #ifdef    MMDF
  370.     /* We need a way to tell temporary errors from permanent ones.  Inews
  371.      * will reject messages because of too much quoting, for example,
  372.      * so the message will sit in the queue forever.  Until then we'll
  373.      * have to lose messages on any error. */
  374.     exit(0);
  375. #else
  376.     exit(WAIT_EXITCODE(W));
  377. #endif    /* MMDF */
  378. }
  379.  
  380.  
  381.  
  382. /*
  383. **  Convert the characters following dots to upper case, if they're
  384. **  lower case.  Two dots in a row will leave one dot in their place.
  385. **  Modifies the argument.
  386. */
  387. STATIC char *
  388. HackPeriods(string)
  389.     char        *string;
  390. {
  391.     register char    *s;
  392.     register char    *p;
  393.  
  394.     if (string) {
  395.     for (p = s = string; *p; *s++ = *p++)
  396.         if (*p == '.') {
  397.         if (*++p == '\0') {
  398.             *s++ = '.';
  399.             break;
  400.         }
  401.         if (islower(*p))
  402.             *p = toupper(*p);
  403.         }
  404.     *s = '\0';
  405.     }
  406.     return string;
  407. }
  408.  
  409.  
  410.  
  411. main(ac, av)
  412.     register int    ac;
  413.     register char    *av[];
  414. {
  415.     register char    **vec;
  416.     register char    *p;
  417.     register FILE    *F;
  418.     register FILE    *Infile;
  419.     HBUF        H;
  420.     char        **iv;
  421.     char        buff[BUFSIZ];
  422.     int            fd[2];
  423.     int            FlushSubRequests;
  424.     int            SubjectRequired;
  425.     int            GotEquals;
  426.  
  427.     Pname = ((Pname = RDX(av[0], '/')) ? Pname + 1 : av[0]);
  428.     Infile = stdin;
  429.  
  430.     /* Remove any trace of who we are. */
  431.     TrimEnvironment();
  432.  
  433.     /* So that cores will actually drop... */
  434.     if (chdir("/tmp") < 0) {
  435.     Fprintf(stderr, "%s:  Can't chdir(/tmp), %s.\n", Pname,
  436.         strerror(errno));
  437.     exit(EX_TEMPFAIL);
  438.     }
  439.  
  440.     /* If someone wants to shut down the system, tell sendmail to
  441.      * try again later. */
  442.     Signal(SIGTERM, Sig_tempfail);
  443.  
  444. #ifdef    SENDMAIL
  445.     /* First read should fetch us the UNIX From_ line.  Not done in MMDF. */
  446.     if (fgets(buff, sizeof buff, Infile) == NULL)
  447.     exit(EX_NOINPUT);
  448.  
  449.     if (!EQn(buff, "From ", 5)) {
  450.     Fprintf(stderr, "%s:  Input didn't start with UNIX From line:\n",
  451.         Pname);
  452.     Fprintf(stderr,"\t%s.\n", buff);
  453.     exit(EX_DATAERR);
  454.     }
  455. #endif    /* SENDMAIL */
  456.  
  457.     /* Read the mail header. */
  458.     rfc822read(&H, Infile, buff, sizeof buff);
  459.  
  460.     /* Process the argument list, copying anything that we don't recognize
  461.      * over to the inews argument list and changing things as we see fit. */
  462.     FlushSubRequests = FALSE;
  463.     GotEquals = FALSE;
  464.     SubjectRequired = TRUE;
  465.     iv = NEW(char*, ac+ 2);
  466.     iv[0] = INEWS;
  467.     iv[1] = "-h";
  468.     for (vec = iv + 2; p = *++av; )
  469.     if (p[0] != '-')
  470.         *vec++ = p;
  471.      else
  472.         switch(p[1]) {
  473.         case 'x':
  474.         SubjectRequired = FALSE;
  475.         /* FALLTHROUGH */
  476.         default:
  477.         *vec++ = p;
  478.         break;
  479.         case '=':
  480.         if (!GotEquals)
  481.             iv++;
  482.         iv[0] = p[2] ? &p[2] : *++av;
  483.         GotEquals++;
  484.         break;
  485.         case '.':
  486.         Debugging++;
  487.         break;
  488.         case 'n':
  489.         /* Newsgroup this messages goes to. */
  490.         Strcpy(H.nbuf, p[2] ? &p[2] : *++av);
  491.         break;
  492.         case 'o':
  493.         /* Default organization. */
  494.         if (H.organization[0] == '\0')
  495.             Strcpy(H.organization, HackPeriods(p[2] ? &p[2] : *++av));
  496.         else if (p[2] == '\0')
  497.             av++;
  498.         SubjectRequired = FALSE;
  499.         break;
  500.         case 'F':
  501.         FlushSubRequests = TRUE;
  502.         break;
  503.         }
  504.     *vec++ = NULL;
  505.  
  506.     /* Bash on the mail header. */
  507.     if (p = HackHeader(&H, SubjectRequired)) {
  508.     Fprintf(stderr, "%s:  Rejected by netnews because:\n", Pname);
  509.     Fprintf(stderr, "\t%s.\n", p);
  510.     if (H.nbuf[0])
  511.         Fprintf(stderr, "\tIt was going into the newsgroup%s %s.\n",
  512.         IDX(H.nbuf, NGDELIM) ? "s" : "", H.nbuf);
  513.     exit(EX_DATAERR);
  514.     }
  515.     Editnewsgroups(&H);
  516.  
  517.     if (Debugging) {
  518.     for (vec = iv; *vec; vec++)
  519.         (void)printf(" |%s| ", *vec);
  520.     (void)printf("\n");
  521.     if (!rfc822write(&H, stdout))
  522.         Fprintf(stderr, "%s:  Can't write header, %s.\n",
  523.         Pname, strerror(errno));
  524.     while (fgets(buff, sizeof buff, Infile))
  525.         Fputs(buff, stdout);
  526.     exit(EX_OK);
  527.     }
  528.  
  529.     if (FlushSubRequests && (Infile = IsSubRequest(Infile)) == NULL) {
  530.     Fprintf(stderr, "%s:  Rejected by netnews becase:\n", Pname);
  531.     Fprintf(stderr, "\tIt seems like a subscription request.\n");
  532.     exit(EX_DATAERR);
  533.     }
  534.  
  535.     /* Get ready to spawn an inews. */
  536.     if (pipe(fd) < 0) {
  537.     Fprintf(stderr, "%s:  Can't pipe, %s.\n", Pname, strerror(errno));
  538.     exit(EX_TEMPFAIL);
  539.     }
  540.     Fflush(stderr);
  541.     Fflush(stdout);
  542. #ifdef    SIGCHLD
  543.     Signal(SIGCHLD, childgone);
  544. #endif    /* SIGCHLD */
  545. #ifdef    SIGCLD
  546.     Signal(SIGCLD, childgone);
  547. #endif    /* SIGCLD */
  548.  
  549.     if ((ChildPid = fork()) < 0) {
  550.     Fprintf(stderr,"%s:  Can't fork, %s.\n", Pname, strerror(errno));
  551.     exit(EX_TEMPFAIL);
  552.     }
  553.     if (ChildPid == 0) {
  554.     /* Redirect I/O; it's unlikely the test below will fail. */
  555.     if (fd[PIPE_READER] != STDIN) {
  556.         Close(STDIN);
  557.         if (dup(fd[PIPE_READER]) != STDIN)
  558.         Fprintf(stderr, "%s:  Can't redirect input, %s.\n",
  559.             Pname, strerror(errno));
  560.     }
  561.     Close(fd[PIPE_READER]);
  562.     Close(fd[PIPE_WRITER]);
  563.     (void)execv(iv[0], iv);
  564.     Fprintf(stderr, "%s:  Can't exec %s, %s.\n",
  565.         Pname, iv[0], strerror(errno));
  566.     exit(EX_OSERR);
  567.     }
  568.  
  569.     /* Set things up after the fork. */
  570.     Close(fd[PIPE_READER]);
  571.     Signal(SIGPIPE, childgone);
  572.     if ((F = fdopen(fd[PIPE_WRITER], "w")) == NULL)
  573.     exit(EX_OSERR);
  574.  
  575.     /* Stuff the header. */
  576.     if (!rfc822write(&H, F)) {
  577.     Fprintf(stderr, "%s:  Can't write header, %s.\n",
  578.         Pname, strerror(errno));
  579.     exit(EX_IOERR);
  580.     }
  581.  
  582.     /* Write the rest of the message. */
  583.     while (fgets(buff, sizeof buff, Infile)) {
  584.     Fputs(buff, F);
  585.     if (ferror(F))
  586.         break;
  587.     }
  588.  
  589.     /* Close down the pipe. */
  590.     Fflush(F);
  591.     if (ferror(F)) {
  592.     Fprintf(stderr, "%s:  Error flushing pipe to news, %s.\n",
  593.         Pname, strerror(errno));
  594.     exit(EX_IOERR);
  595.     }
  596.     if (fclose(F) == EOF)
  597.     Fprintf(stderr, "%s:  Error closing pipe to news, %s.\n",
  598.         Pname, strerror(errno));
  599.  
  600.     /* Wait for inews, and exit as it does. */
  601.     childgone();
  602.     /* NOTREACHED */
  603. }
  604.