home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume1 / vnews / part5 < prev    next >
Internet Message Format  |  1986-11-30  |  60KB

  1. From: decvax!vax135!hou3c!ka
  2. Date: Mon, 21 Jan 85 22:38:18 est
  3. Newsgroups: mod.sources
  4. Subject: Vnews part 5
  5.  
  6. # Welcome to vnews release 2.11-B 1/17/85.
  7. # This is part 5 out of 7.
  8. # Feed me into sh (NOT csh).
  9.  
  10. if test ! -d postnews
  11. then    mkdir postnews
  12. fi
  13.  
  14. cat > postnews/postnews.c <<\!E!O!F!
  15. /*
  16.  * Postnews: post a news message to Usenet.  This C version replaces a shell
  17.  * script, and does more intelligent prompting and filtering than possible
  18.  * in a shell script.
  19.  */
  20.  
  21. #ifndef lint
  22. static char    *SccsId = "@(#)postnews.c    1.16    9/18/84";
  23. #endif !lint
  24.  
  25. /* #include "params.h" */
  26. #include "config.h"
  27. #include "defs.h"
  28. #include "libextern.h"
  29. #include <stdio.h>
  30. #include <signal.h>
  31. #include <sys/types.h>
  32. #include <grp.h>
  33. #include <pwd.h>
  34. #include <sys/stat.h>
  35. #include <ctype.h>
  36. #include <time.h>
  37.  
  38. char tempfname[50];        /* file name used for making article */
  39. char original[BUFLEN];        /* file name of original, used in followup */
  40. char homedir[BUFLEN];        /* HOME environment setting */
  41. char ccname[BUFLEN];        /* file name for article copy */
  42.  
  43. /* article header information */
  44. char subject[BUFLEN];
  45. char distribution[BUFLEN];
  46. char references[BUFLEN];
  47. char newsgroups[BUFLEN];
  48. char moderator[BUFLEN];
  49.  
  50. char *Progname = "postnews";        /* for xerror */
  51.  
  52. time_t fmodtime;
  53. int ismod = 0;
  54. char buf[BUFLEN];
  55.  
  56. struct distr {
  57.     char abbr[24];
  58.     char descr[128];
  59. } distr[16];
  60.  
  61. extern    struct    passwd *getpwnam(), *getpwuid(), *getpwent();
  62. FILE *ckfopen();
  63.  
  64. main(argc, argv)
  65. char *argv[];
  66. {
  67.     init();
  68.  
  69.     if (argc == 2) {
  70.         if (strncmp(SPOOL, argv[1], strlen(SPOOL)))
  71.             xerror("Can only followup to articles in %s", SPOOL);
  72.         followup(argv[1]);
  73.         strcpy(original, argv[1]);
  74.     } else
  75.     if (askyes("Is this message in response to some other message? ","no")) {
  76.         char ng[BUFLEN], num[BUFLEN];
  77.         long i, j;
  78.         register char *c;
  79.         int fd;
  80.         char canpost;
  81.  
  82.         getpr("In what newsgroup was the article posted? ",ng);
  83.         if (!valid_ng(ng, &i, &j, &canpost))
  84.             if (canpost != 'n' )
  85.                 byebye("There is no such newsgroup.");
  86.             else
  87.                 byebye("You are not allowed to post to that group.");
  88.  
  89.         for(;;) {
  90.             getpr("\nWhat was the article number? (type ? for help) ", num);
  91.             if (num[0] == 0)
  92.                 continue;
  93.             else if (num[0] == '/') {
  94.                 artlist(ng, num + 1) ;
  95.                 continue ;
  96.             }
  97.             else if (num[0] == '?') {
  98.                 printf("Valid article numbers are from %ld to %ld\n", j, i);
  99.                 printf("Type /string to get a list of articles containing string\nin the author or title fields.\n");
  100.                 continue;
  101.             }
  102.             sprintf(original, "%s/%s", SPOOL, ng);
  103.             for (c=original+strlen(SPOOL)+1; *c ;++c)
  104.                 if (*c == '.')
  105.                     *c = '/';
  106.             strcat(original, "/");
  107.             strcat(original, num);
  108.  
  109.             if ((fd=open(original,0)) >= 0) {
  110.                 close(fd);
  111.                 printf("\narticle %s\n", original);
  112.                 if (article_line(original, "From: ", buf))
  113.                     printf("%s\n", buf);
  114.                 if (article_line(original, "Subject: ", buf))
  115.                     printf("%s\n", buf);
  116.                 if (askyes("Is this the one you want? ", ""))
  117.                     break;
  118.             } else
  119.                 printf("I can't find that article.\n");
  120.         }
  121.  
  122.         followup(original);
  123.     } else {
  124.         do {
  125.             getpr("Subject: ", subject);
  126.         } while (*subject == '\0');
  127.  
  128.         while (!get_newsgroup())
  129.             ;
  130.         get_distribution();
  131.     }
  132.  
  133.     if (pre_checks())
  134.         exit(1);
  135.     edit_article();
  136.     post_checks();
  137.  
  138.     post_article();
  139. }
  140.  
  141. /*
  142.  * Find out the topic of interest.
  143.  */
  144. get_newsgroup()
  145. {
  146.     int n;
  147.     long i;
  148.     char canpost;
  149.  
  150.     printf("Newsgroups (enter one at a time, end with a blank line):\n");
  151.     printf("For a list of newsgroups, type ?\n");
  152.     n = 0;
  153.     newsgroups[0] = '\0';
  154.  
  155.     for(;;) {
  156.         getpr("> ", buf);
  157.         if (buf[0] == '\0')
  158.             if (n == 0)
  159.                 return FALSE;
  160.             else
  161.                 return TRUE;
  162.         if (buf[0] == '?'){
  163.             /* too lazy to do it myself.... */
  164.             printf("These are the currently active groups:\n");
  165.             sprintf(buf,"exec cat %s/newsgroups", LIB);
  166.             system(buf);
  167.             continue;
  168.         }
  169.         if (valid_ng(buf, &i, &i, &canpost)) {
  170.             if (n++ != 0)
  171.                 strcat(newsgroups, ",");
  172.             strcat(newsgroups, buf);
  173.         } else {
  174.             if (canpost == 'n')
  175.                 printf("You are not allowed to post to %s\n",
  176.                     buf);
  177.             else
  178.                 printf("%s is not a valid newsgroup.\n", buf);
  179.         }
  180.     }
  181. }
  182.  
  183. /*
  184.  * Find out how widely the author wants the message distributed.
  185.  */
  186. get_distribution()
  187. {
  188.     register int i;
  189.     register char *r;
  190.     char def[BUFLEN];
  191.     char *index();
  192.  
  193.     strcpy(def, newsgroups);
  194.     r = index(def, '.');
  195.     if (r) {
  196.         *r = '\0';
  197.         if (strcmp(def, "net") == 0)
  198.             strcpy(def, "world");
  199.     } else
  200.         strcpy(def, "local");
  201.  
  202.     if (strcmp(def,"to") == 0) {
  203.         distribution[0] = '\0';
  204.         return;        /* He's probably testing something */
  205.     }
  206.     if (ngmatch("net.test", newsgroups))
  207.         strcpy(def, "local");
  208.     for(;;) {
  209.         sprintf(buf, "Distribution (default='%s', '?' for help) : ", def);
  210.         getpr(buf, distribution);
  211.         if (distribution[0] == '\0')
  212.             strcpy(distribution, def);
  213.  
  214.         /* Did the user ask for help? */
  215.         if (distribution[0] == '?') {
  216.             printf("How widely should your article be distributed?\n");
  217.             for (i=0; distr[i].abbr[0]; i++)
  218.                 printf("%s\t%s\n", distr[i].abbr, distr[i].descr);
  219.             continue;
  220.         }
  221.  
  222.         /* Check that it's a proper distribution */
  223.         for (i=0; distr[i].abbr[0]; i++) {
  224.             if (strncmp(distr[i].abbr, distribution, sizeof(distr[0].abbr)) == 0) {
  225.                 /* Found a match. Do any special rewriting. */
  226.                 if (strcmp(distribution, "world") == 0)
  227.                     strcpy(distribution, "net");
  228.                 return;
  229.             }
  230.         }
  231.  
  232.         printf("Type ? for help.\n");
  233.     }
  234. }
  235.  
  236. /*
  237.  * Do sanity checks before the author types in the message.
  238.  */
  239. pre_checks()
  240. {
  241.     check_mod();
  242.     if (recording(newsgroups))
  243.         return 1;
  244.     return 0;
  245. }
  246.  
  247. /*
  248.  * Check to see if the newsgroup is moderated.
  249.  */
  250. check_mod()
  251. {
  252.     register FILE *fd;
  253.     char ng[64], mod[BUFLEN];
  254.  
  255.     sprintf(buf, "%s/%s", LIB, "moderators");
  256.     fd = ckfopen(buf, "r");
  257.  
  258.     while (!feof(fd)) {
  259.         if (fgets(buf, sizeof buf, fd) == NULL) {
  260.             fclose(fd);
  261.             return;
  262.         }
  263.         twosplit(buf, ng, mod);
  264.         if (ngmatch(newsgroups, ng)) {
  265.             strcpy(moderator, mod);
  266.             ismod = 1;
  267.             return;
  268.         }
  269.     }
  270. }
  271.  
  272. /*
  273.  * Set up the temp file with headers.
  274.  */
  275. edit_article()
  276. {
  277.     FILE *tf, *of;
  278.     char *editor;
  279.     char *endflag = "";
  280.     char *p;
  281.     char *getenv();
  282.     struct stat stbuf;
  283.  
  284.     editor = getenv("EDITOR");
  285.     strcpy(tempfname, "/tmp/postXXXXXX");
  286.     mktemp(tempfname);
  287.  
  288.     /* insert a header */
  289.     tf = ckfopen(tempfname, "w");
  290.     fprintf(tf, "Subject: %s\n", subject);
  291.     fprintf(tf, "Newsgroups: %s\n", newsgroups);
  292.     if (distribution[0] != '\0')
  293.         fprintf(tf, "Distribution: %s\n", distribution);
  294.     if (ismod)
  295.         fprintf(tf, "To: %s\n", moderator);
  296.  
  297.     if (references[0] != '\0') {
  298.         fprintf(tf, "References: %s\n\n", references);
  299.  
  300.         of = ckfopen(original, "r");
  301.         while (fgets(buf, BUFSIZ, of) != NULL)
  302.             if (buf[0] == '\n')    /* skip headers */
  303.                 break;
  304.         while (fgets(buf, BUFSIZ, of) != NULL)
  305.             fprintf(tf, "> %s", buf);
  306.         fclose(of);
  307.     }
  308.  
  309.     fprintf(tf, "\n*** REPLACE THIS LINE WITH YOUR MESSAGE ***\n");
  310.     fflush(tf);
  311.     fstat(fileno(tf), &stbuf);
  312.     fmodtime = stbuf.st_mtime;
  313.     fclose(tf);
  314.  
  315.     /* edit the file */
  316.     if (editor == NULL)
  317.         editor = DFTEDITOR;
  318.  
  319.     p = editor + strlen(editor) - 2;
  320.     if (strcmp(p, "vi") == 0 && references[0] == '\0')
  321.         endflag = "+";
  322.  
  323.     sprintf(buf, "exec %s %s %s", editor, endflag, tempfname);
  324.  
  325.     system(buf);
  326. }
  327.  
  328. /*
  329.  * Do sanity checks after the author has typed in the message.
  330.  */
  331. post_checks()
  332. {
  333.     char group[BUFLEN];
  334.     char s[BUFLEN + 24];
  335.     char *c;
  336.     struct stat stbuf;
  337.  
  338.     if (stat(tempfname, &stbuf) < 0) {
  339.         printf("File deleted - no message posted.\n");
  340.         unlink(tempfname);
  341.         exit(1);
  342.     }
  343.  
  344.     if (stbuf.st_mtime == fmodtime || stbuf.st_size < 5) {
  345.         printf("File not modified - no message posted.\n");
  346.         unlink(tempfname);
  347.         exit(1);
  348.     }
  349.  
  350.     /* Sanity checks for certain newsgroups */
  351.     if (ngmatch(newsgroups, "all.wanted") && ngmatch(distribution,"net,na,usa,att,btl")) {
  352.         printf("Is your message something that might go in your local\n");
  353.         printf("newspaper, for example a used car ad, or an apartment\n");
  354.         printf("for rent? ");
  355.         if (askyes("","")) {
  356.             printf("It's pointless to distribute your article widely, since\n");
  357.             printf("people more than a hundred miles away won't be interested.\n");
  358.             printf("Please use a more restricted distribution.\n");
  359.             get_distribution();
  360.             modify_article(tempfname,"Distribution: ",distribution,"replace");
  361.         }
  362.     }
  363.  
  364.     if (ngmatch(newsgroups, "all.jokes")) {
  365.         if (askyes("Could this be offensive to anyone? ","")) {
  366.             getpr("Whom might it offend? ", group);
  367.             sprintf(s," - offensive to %s (ROT13)",group);
  368.             modify_article(tempfname, "Subject: ", s, "append");
  369.             encode(tempfname);
  370.         }
  371.     }
  372.  
  373.     if (ngmatch(newsgroups, "net.general")) {
  374.         c = newsgroups;
  375.         while (*c != ',' && *c)
  376.             ++c;
  377.         if (*c == ',') {
  378.             printf("Everybody reads net.general, so it doesn't make sense to\n");
  379.             printf("post to newsgroups in addition to net.general.    If your\n");
  380.             printf("article belongs in one of these other newsgroups, then you\n");
  381.             printf("should not post to net.general.    If it is important enough\n");
  382.             printf("for net.general, then you shouldn't post it in other places\n");
  383.             printf("as well.    Please reenter the newsgroups.\n");
  384.             get_newsgroup();
  385.             modify_article(tempfname,"Newsgroups: ",newsgroups,"replace");
  386.         }
  387.         else {
  388.             printf("net.general is for important announcements.\n");
  389.             printf("It is not for items for which you couldn't think\n");
  390.             printf("of a better place - those belong in net.misc.\n");
  391.             if (!askyes("Are you sure your message belongs in net.general? ","")) {
  392.                 get_newsgroup();
  393.                 modify_article(tempfname, "Newsgroups: ", newsgroups, "replace");
  394.             }
  395.         }
  396.     }
  397. }
  398.  
  399. /*
  400.  * Save a copy of the article in the users NEWSARCHIVE directory.
  401.  */
  402. save_article()
  403. {
  404.     FILE *in, *out;
  405.     int c;
  406.     time_t timenow, time();
  407.     char *today, *ctime();
  408.  
  409.  
  410.     in = ckfopen(tempfname, "r");
  411.     out = ckfopen(ccname, "a");
  412.     timenow = time((time_t)0);
  413.     today = ctime(&timenow);
  414.     fprintf(out,"From postnews %s",today);
  415.     while ((c=getc(in)) != EOF)
  416.         putc(c, out);
  417.     putc('\n', out);
  418.     fclose(in);
  419.     fclose(out);
  420. }
  421.  
  422. /*
  423.  * Post the article to the net.
  424.  */
  425. post_article()
  426. {
  427.     extern char MAILPARSER[];
  428.     int status;
  429.  
  430.     printf("%s article...\n", ismod ? "Mailing" : "Posting" );
  431.     if (ismod)
  432.         sprintf(buf, "exec %s -t < %s", MAILPARSER, tempfname);
  433.     else
  434.         sprintf(buf, "exec %s -wi < %s", POSTNM, tempfname);
  435.     status = system(buf);
  436.  
  437.     if (status) {
  438.         printf("Article not %s - exit status %d\n", ismod ? "mailed" : "posted", status);
  439.         savedead();
  440.     } else {
  441.         printf("Article %s successfully.\n", ismod ? "mailed" : "posted" );
  442.         if (ccname[0]) {
  443.             if (ismod)
  444.                 save_article();
  445.             printf("A copy has been saved in %s\n", ccname);
  446.         }
  447.     }
  448.     unlink(tempfname);
  449. }
  450.  
  451. /*
  452.  * Save an article that couldn't be posted.
  453.  */
  454. savedead() {
  455.     FILE *in, *out;
  456.     char deadfile[256];
  457.     int c;
  458.  
  459.     in = ckfopen(tempfname, "r");
  460.     sprintf(deadfile, "%s/dead.article", homedir);
  461.     out = ckfopen(deadfile, "a");
  462.     while ((c = getc(in)) != EOF)
  463.         putc(c, out);
  464.     putc('\n', out);
  465.     fclose(in);
  466.     fclose(out);
  467.     printf("Article saved in %s\n", deadfile);
  468. }
  469.  
  470. /*
  471.  * Initialization.
  472.  */
  473. init()
  474. {
  475.     FILE *fd;
  476.     register char *p;
  477.     int i;
  478.     char *getenv();
  479.  
  480.     references[0] = '\0';
  481.     distribution[0] = '\0';
  482.  
  483.     p = getenv("HOME");
  484.     if (p == NULL) {
  485.         p = getenv("LOGDIR");
  486.         if (p == NULL) {
  487.             struct passwd *pw;
  488.             pw = getpwuid(getuid());
  489.             if (pw == NULL) {
  490.                 fprintf(stderr,"You're not in /etc/passwd\n");
  491.                 exit(1);
  492.             }
  493.             p = pw->pw_dir;
  494.         }
  495.     }
  496.     strcpy(homedir, p);
  497.  
  498.  
  499.     p = getenv("NEWSARCHIVE");
  500.     if (p != NULL)
  501.         strcpy(ccname, p);
  502. /*
  503.     else
  504.         sprintf(ccname, "%s/author_copy", homedir);
  505. */
  506.  
  507.     pathinit();
  508.     sprintf(buf, "%s/%s", LIB, "distributions");
  509.     fd = ckfopen(buf, "r");
  510.     for (i=0; !feof(fd); i++) {
  511.         if (fgets(buf, sizeof buf, fd) == NULL)
  512.             break;
  513.         twosplit(buf, distr[i].abbr,distr[i].descr);
  514.     }
  515.     fclose(fd);
  516. }
  517.  
  518. /*
  519.  * Split a line of the form
  520.  *        text whitespace text
  521.  * into two strings.    Also trim off any trailing newline.
  522.  * This is destructive on src.
  523.  */
  524. twosplit(src, dest1, dest2)
  525. char *src, *dest1, *dest2;
  526. {
  527.     register char *p;
  528.  
  529.     nstrip(src);
  530.     for (p=src; isalnum(*p) || ispunct(*p); p++)
  531.         ;
  532.     *p++ = 0;
  533.     for ( ; isspace(*p); p++)
  534.         ;
  535.     strcpy(dest1, src);
  536.     strcpy(dest2, p);
  537. }
  538.  
  539. /*
  540.  * Get a yes or no answer to a question.    A default may be used.
  541.  */
  542. askyes(msg, def)
  543. char *msg, *def;
  544. {
  545.  
  546.     printf("%s", msg);
  547.     buf[0] = 0;
  548.     gets(buf);
  549.     switch(buf[0]) {
  550.     case 'y':
  551.     case 'Y':
  552.         return TRUE;
  553.     case 'n':
  554.     case 'N':
  555.         return FALSE;
  556.     case '\0':
  557.         switch(*def) {
  558.         case 'y':
  559.         case 'Y':
  560.             return TRUE;
  561.         case 'n':
  562.         case 'N':
  563.             return FALSE;
  564.         }
  565.     default:
  566.         printf("Please answer yes or no.\n");
  567.         return askyes(msg, def);
  568.     }
  569. }
  570.  
  571. /*
  572.  * Get a character string into buf, using prompt msg.
  573.  */
  574. getpr(msg, bfr)
  575. char *msg, *bfr;
  576. {
  577.     static int numeof = 0;
  578.     printf("%s", msg);
  579.     gets(bfr);
  580.     nstrip(bfr);
  581.     if (feof(stdin)) {
  582.         if (numeof++ > 3) {
  583.             fprintf(stderr,"Too many EOFs\n");
  584.             exit(1);
  585.         }
  586.         clearerr(stdin);
  587.     }
  588. }
  589.  
  590. byebye(mesg)
  591. char *mesg;
  592. {
  593.     printf("%s\n", mesg);
  594.     exit(1);
  595. }
  596.  
  597. /*
  598.  * make a modification to the header of an article
  599.  *
  600.  *     fname -- name of article
  601.  *     field -- header field to modify
  602.  *     line    -- modification line
  603.  *     how     -- 'a' or 'A' to append line to existing header line
  604.  *            anything else to replace existing line
  605.  *
  606.  * example:
  607.  *     modify_article("/tmp/article" , "Subject: " , "new subject" , "replace");
  608.  *
  609.  *
  610.  */
  611. modify_article(fname, field, line, how)
  612. char *fname, *field, *line, *how;
  613. {
  614.     FILE *fpart, *fptmp;
  615.     char *temp2fname = "/tmp/postXXXXXX";
  616.     int i;
  617.  
  618.     mktemp(temp2fname);
  619.  
  620.     fptmp = ckfopen(temp2fname, "w");
  621.     fpart = ckfopen(fname, "r");
  622.  
  623.     i = strlen(field);
  624.     while (fgets(buf, BUFLEN, fpart) != NULL) {
  625.         if (strncmp(field, buf, i) == 0) {
  626.             nstrip(buf);
  627.             if (*how=='a' || *how=='A')
  628.                 /* append to current field */
  629.                 sprintf(buf, "%s%s\n", buf, line);
  630.             else
  631.                 /* replace current field */
  632.                 sprintf(buf, "%s%s\n", field, line);
  633.         }
  634.         fputs(buf, fptmp);
  635.     }
  636.  
  637.     fclose(fpart);
  638.     fclose(fptmp);
  639.  
  640.     fptmp = ckfopen(temp2fname, "r");
  641.     fpart = ckfopen(fname, "w");
  642.  
  643.     i = strlen(field);
  644.     while (fgets(buf,BUFLEN,fptmp) != NULL)
  645.         fputs(buf, fpart);
  646.  
  647.     fclose(fpart);
  648.     fclose(fptmp);
  649.     unlink(temp2fname);
  650. }
  651.  
  652.  
  653. /* verify that newsgroup exists, and get number of entries */
  654. valid_ng(ng, maxart, minart, canpost)
  655. char *ng;
  656. long *maxart, *minart;
  657. char *canpost;
  658. {
  659.     char ng_check[BUFLEN], ng_read[BUFLEN];
  660.     char ACTIVE[FPATHLEN];
  661.     FILE *fp;
  662.  
  663.     *minart = 1; *canpost = 'y';
  664.     sprintf(ACTIVE, "%s/active", LIB);
  665.     fp = ckfopen(ACTIVE, "r");
  666.     while (fgets(ng_read, BUFLEN, fp) != NULL) {
  667.         sscanf(ng_read, "%s %ld %ld %c", ng_check, maxart, minart, canpost);
  668.         if (strcmp(ng_check, ng) == 0) {
  669.             fclose(fp);
  670.             if (*canpost == 'y')
  671.                 return TRUE;
  672.             else
  673.                 return FALSE;
  674.         }
  675.     }
  676.     *canpost = 'i';
  677.     *maxart = 0;
  678.     *minart = 0;
  679.     fclose(fp);
  680.     return FALSE;
  681. }
  682.  
  683. /* get the line specified by field from an article */
  684. article_line(article, field, line)
  685. char *article, *field, *line;
  686. {
  687.     FILE *fp;
  688.     char *c;
  689.     int i = strlen(field);
  690.  
  691.     fp = ckfopen(article,"r");
  692.     while ((c=fgets(line,BUFLEN,fp)) != NULL && strncmp(field,line,i) != 0)
  693.         ;
  694.     fclose(fp);
  695.     if (c != NULL) {
  696.         nstrip(line);
  697.         return TRUE;
  698.     } else {
  699.         line[0] = '\0';
  700.         return FALSE;
  701.     }
  702. }
  703.  
  704.  
  705. /* get the header information for a followup article */
  706. followup(baseart)
  707. register char *baseart;
  708. {
  709.     /* subject */
  710.     if (article_line(baseart, "Subject: ", buf)) {
  711.         if (!prefix(buf+9, "Re:"))
  712.             sprintf(subject, "Re: %s", buf+9);
  713.         else
  714.             strcpy(subject, buf+9);
  715.     } else
  716.         strcpy(subject, "Re: orphan response");
  717.  
  718.     /* newsgroup */
  719.     if (article_line(baseart, "Newsgroups: ", buf))
  720.         strcpy(newsgroups, buf+12);
  721.     if (ngmatch(newsgroups, "net.general"))
  722.         strcpy(newsgroups,"net.followup");
  723.  
  724.     /* distribution */
  725.     if (article_line(baseart, "Distribution: ", buf))
  726.         strcpy(distribution, buf+14);
  727.  
  728.     /* references */
  729.     if (article_line(baseart, "References: ", buf)) {
  730.         strcpy(references, buf+12);
  731.         strcat(references, " ");
  732.     }
  733.     if (article_line(baseart, "Message-ID: ", buf))
  734.         strcat(references, buf+12);
  735. }
  736.  
  737. encode(article)
  738. char *article;
  739. {
  740.     FILE *fpart, *fphead, *fpcoded;
  741.     char *headerfile = "/tmp/pheadXXXXXX";
  742.     char *codedfile = "/tmp/pcodeXXXXXX";
  743.  
  744.     mktemp(headerfile);
  745.     mktemp(codedfile);
  746.  
  747.     fpart = ckfopen(article, "r");
  748.  
  749.     /* place article header in "headerfile" file */
  750.     fphead = ckfopen(headerfile, "w");
  751.     while (fgets(buf, BUFLEN, fpart) != NULL) {
  752.         fputs(buf, fphead);
  753.         if (buf[0] == '\n')
  754.             break;
  755.     }
  756.     fclose(fphead);
  757.  
  758.     /* place article body in "codedfile" file */
  759.     fpcoded = ckfopen(codedfile, "w");
  760.     while (fgets(buf, BUFLEN, fpart) != NULL)
  761.         fputs(buf, fpcoded);
  762.     fclose(fpcoded);
  763.     fclose(fpart);
  764.  
  765.     /* encode body and put back together with header */
  766.     rename(headerfile, article);
  767.  
  768.     sprintf(buf,"exec %s/%s 13 < %s >> %s\n", LIB, "caesar", codedfile, article);
  769.     printf("Encoding article -- please stand by\n");
  770.     if (system(buf)) {
  771.         printf("encoding failed");
  772.         exit(2);
  773.     }
  774.     unlink(codedfile);
  775. }
  776.  
  777.  
  778. /*
  779.  * Print a recorded message warning the poor luser what he is doing
  780.  * and demand that he understands it before proceeding.  Only do
  781.  * this for newsgroups listed in LIBDIR/recording.
  782.  */
  783. recording(ngrps)
  784. char *ngrps;
  785. {
  786.     char recbuf[BUFLEN];
  787.     FILE *fd;
  788.     char nglist[BUFLEN], fname[BUFLEN];
  789.     int  c, n, yes;
  790.  
  791.     sprintf(recbuf, "%s/%s", LIB, "recording");
  792.     fd = fopen(recbuf, "r");
  793.     if (fd == NULL)
  794.         return 0;
  795.     while ((fgets(recbuf, sizeof recbuf, fd)) != NULL) {
  796.         sscanf(recbuf, "%s %s", nglist, fname);
  797.         if (ngmatch(ngrps, nglist)) {
  798.             fclose(fd);
  799.             if (fname[0] == '/')
  800.                 strcpy(recbuf, fname);
  801.             else
  802.                 sprintf(recbuf, "%s/%s", LIB, fname);
  803.             fd = fopen(recbuf, "r");
  804.             if (fd == NULL)
  805.                 return 0;
  806.             while ((c = getc(fd)) != EOF)
  807.                 putc(c, stderr);
  808.             fprintf(stderr, "Do you understand this?  Hit <return> to proceed, <BREAK> to abort: ");
  809.             n = read(2, recbuf, 100);
  810.             c = recbuf[0];
  811.             yes = (c=='y' || c=='Y' || c=='\n' || c=='\n' || c==0);
  812.             if (n <= 0 || !yes)
  813.                 return -1;
  814.         }
  815.     }
  816.     return 0;
  817. }
  818.  
  819. xxit(i)
  820. {
  821.     exit(i);
  822. }
  823.  
  824.  
  825. xerror(fmt, a1, a2, a3, a4)
  826.     char *fmt;
  827.     {
  828.     fprintf(stderr, fmt, a1, a2, a3, a4);
  829.     putc('\n', stderr);
  830.     xxit(1);
  831. }
  832. !E!O!F!
  833.  
  834. cat > postnews/postnm.1 <<\!E!O!F!
  835. .TH POSTNM 1
  836. .SH NAME
  837. postnm \- post mail or news
  838. .SH SYNOPSIS
  839. .B postnm
  840. [
  841. .B \-ixw
  842. ]
  843. [ header-options ]
  844. [ file ]
  845. .SH DESCRIPTION
  846. .I Postnm
  847. reads a message in ARPANET mail format from a file, or from the
  848. standard input if no file is given.
  849. It checks the message for validity.  If the message contains a
  850. Newsgroups line, the message is posted to USENET.
  851. If the message contains To, Cc, or Bcc lines, it is mailed to
  852. the indicated addresses.
  853. .P
  854. Users who wish to invoke
  855. .I postnm
  856. directly should place their article in a file and then run postnm on it.
  857. .I Postnm
  858. makes no attempt to save an article when an error occurs, so if you try
  859. to type an article directly into
  860. .I postnm
  861. from the terminal, you will be forced to start all over again if something
  862. goes wrong.
  863. Now that
  864. .I postnm
  865. exists, users should never invoke inews directly.
  866. .SS options
  867. .IP -i 6
  868. Identify.  If the file $HOME/.signature exists, append it to the message.
  869. .IP -x 6
  870. Causes postnm to print out the generated command lines without
  871. actually posting the message.
  872. .IP -w 6
  873. Causes postnm to wait for the postings to complete rather that running
  874. inews and mail in the background.
  875. .P
  876. Certain header lines can be inserted into the header of the article
  877. from the command line using the following options:
  878. .sp
  879. .nf
  880.     -c    Control:
  881.     -d    Distribution:
  882.     -f    From:
  883.     -n    Newsgroups:
  884.     -R    References:
  885.     -s    Subject:
  886.     -t    To:
  887. .fi
  888. .P
  889. The Command header is never passed to inews or mail.
  890. ``Command:\ reply''
  891. causes
  892. .I postnm
  893. to ignore the Newsgroups and Distribution lines, and ``Command:\ followup''
  894. causes
  895. .I postnm
  896. to ignore the To lines.
  897. .P
  898. If the environment variable NEWSARCHIVE is set, it specifies a file in which
  899. copies of all news (but not mail) articles are to be placed.
  900. .SH EXAMPLE
  901. postnm -c 'cancel <123@hou3c.UUCP>' -n net.misc /dev/null
  902. .in +5
  903. Cancel article <123@hou3c.UUCP>.
  904. .B /dev/null
  905. provides an empty article body.
  906. .in -5
  907. .SH FILES
  908. .nf
  909. /usr/lib/news/active            list of newsgroups
  910. /usr/lib/news/moderators        addresses of newsgroup moderators
  911. /usr/lib/news/distributions     list of distributions (optional)
  912. $HOME/.signature        signature file
  913. .SH AUTHOR
  914. Kenneth Almquist (ihnp4!hou3c!ka)
  915. !E!O!F!
  916.  
  917. cat > postnews/postnm.h <<\!E!O!F!
  918. /*
  919.  * This program handles the posting or mailing of replies and followups
  920.  * for vnews.  It can also be invoked directly.
  921.  *
  922.  * Copyright 1984 by Kenneth Almquist.  All rights reserved.
  923.  *     Permission is granted for anyone to use and distribute, but not
  924.  *     sell, this program provided that this copyright notice is retained.
  925.  */
  926.  
  927. #include <stdio.h>
  928. #include <ctype.h>
  929. #include <sys/types.h>
  930. #include <sys/stat.h>    /* for .signature kludge */
  931. #include <signal.h>
  932. #include "config.h"
  933. #include "newsdefs.h"
  934. #include "libextern.h"
  935. #include "str.h"
  936. #include "time.h"
  937.  
  938. extern char *scanp;        /* string scanning pointer */
  939.  
  940. #define MODGROUPS    "fa.all,mod.all,all.mod,all.announce"
  941.  
  942. /* types of header fields */
  943. #define HTTO 0
  944. #define HTCC 1
  945. #define HTBCC 2
  946. #define HTNEWSGROUPS 3
  947. #define HTINREPLYTO 5
  948. #define HTREFERENCES 6
  949. #define HTSUBJECT 7
  950. #define HTEXPIRES 8
  951. #define HTFROM 9
  952. #define HTREPLYTO 10
  953. #define HTFOLLOWUPTO 11
  954. #define HTDIST 12
  955. #define HTKEYWORDS 14
  956. #define HTCONTROL 15
  957. #define HTORGANIZATION 16
  958. #define HTSUMMARY 17
  959. #define HTAPPROVED 18
  960. #define HTCOMMENTS 19
  961. #define HTMESSAGEID 20
  962. #define HTCOMMAND 21
  963. #define HTUNKNOWN 27
  964.  
  965.  
  966. struct hline {
  967.       struct hline *next;
  968.       short type;
  969.       short linno;
  970.       char *text;
  971.       char *hdrname;
  972.       char *body;
  973.       char *fixed;
  974. };
  975.  
  976. #ifndef EXTERN
  977. #define EXTERN extern
  978. #define INIT(val)
  979. #else
  980. #define INIT(val) = val
  981. #endif
  982.  
  983. EXTERN struct hline *hfirst, *hlast;        /* list of header lines */
  984. EXTERN struct hline *hdrline[HTCOMMAND + 1];    /* indexed by type */
  985. EXTERN struct hline *curhdr;            /* current header line */
  986.  
  987.  
  988. #define MAXADDR 100
  989. EXTERN char *addrlist[MAXADDR + 1];    /* mail destinations */
  990. EXTERN char **addrp INIT(addrlist);    /* next entry in addrlist */
  991. EXTERN char *moderator;            /* address of moderator */
  992. EXTERN char *references;        /* specified on command line */
  993.  
  994. #include "setjmp.h"
  995.  
  996. #define setexit() setjmp(syntaxjmp)
  997. #define reset() longjmp(syntaxjmp, 1)
  998.  
  999. EXTERN jmp_buf syntaxjmp;        /* jump here on syntax errors */
  1000.  
  1001. EXTERN int nerrors;            /* number of errors */
  1002. EXTERN int linno;            /* input line number */
  1003. EXTERN FILE *infp INIT(stdin);        /* input file */
  1004. EXTERN FILE *newsfp, *mailfp;        /* temp files */
  1005. EXTERN char mailtemp[32], newstemp[32];    /* temp files */
  1006. #define MAXGRPS 10
  1007. EXTERN char *nglist[MAXGRPS + 1];    /* entries on Newsgroup line */
  1008. EXTERN char *follist[MAXGRPS + 1];    /* entries on Followup-To */
  1009. EXTERN char *nlist[MAXGRPS + 1];    /* temporary */
  1010. EXTERN int debug;            /* don't actuall post message */
  1011. EXTERN int verbose;            /* verbose flag */
  1012. EXTERN int background INIT(1);        /* run mail/inews in background */
  1013. EXTERN char *outp;            /* for generating output lines */
  1014. EXTERN int signit;            /* if set, append .signiture file */
  1015. EXTERN char signfile[FPATHLEN];        /* .signiture file */
  1016. EXTERN int signmode;            /* .signature kludge */
  1017. EXTERN char *envkludge[200];        /* organization kludge */
  1018.  
  1019.  
  1020. long atol();
  1021. FILE *ckfopen();
  1022. time_t cgtdate();
  1023. char *ckmalloc();
  1024.  
  1025. char *getval();
  1026. !E!O!F!
  1027.  
  1028. cat > postnews/postnm1.c <<\!E!O!F!
  1029. /*
  1030.  * Main routine and error printing routines.
  1031.  *
  1032.  * Copyright 1984 by Kenneth Almquist.  All rights reserved.
  1033.  *     Permission is granted for anyone to use and distribute, but not
  1034.  *     sell, this program provided that this copyright notice is retained.
  1035.  */
  1036.  
  1037. #define EXTERN
  1038. #include "postnm.h"
  1039.  
  1040.  
  1041. int xxit();
  1042.  
  1043.  
  1044. main(argc, argv)
  1045.       char **argv;
  1046.       {
  1047.       int c;
  1048.       FILE *fp;
  1049.       extern char *optarg;
  1050.       extern int optind;
  1051.  
  1052.       pathinit();
  1053.       getuser();
  1054.       while ((c = getopt(argc, argv, "ivBwxc:d:f:n:R:s:t:")) != EOF) {
  1055.             switch (c) {
  1056.             case 'i':
  1057.                   signit = 1;
  1058.                   break;
  1059.             case 'v':
  1060.                   verbose = 1;
  1061.                   break;
  1062.             case 'B':
  1063.                   background = 1;
  1064.                   break;
  1065.             case 'w':
  1066.                   background = 0;
  1067.                   break;
  1068.             case 'x':
  1069.                   debug++;
  1070.                   background = 0;
  1071.                   break;
  1072.             case 'c':
  1073.                   sprintf(bfr, "Control: %s\n", optarg);
  1074.                   prochdr(bfr, 1);
  1075.                   break;
  1076.             case 'd':
  1077.                   sprintf(bfr, "Distribution: %s\n", optarg);
  1078.                   prochdr(bfr, 1);
  1079.                   break;
  1080.             case 'f':
  1081.                   sprintf(bfr, "From: %s\n", optarg);
  1082.                   prochdr(bfr, 1);
  1083.                   break;
  1084.             case 'n':
  1085.                   sprintf(bfr, "Newsgroups: %s\n", optarg);
  1086.                   prochdr(bfr, 1);
  1087.                   break;
  1088.             case 'R':
  1089.                   sprintf(bfr, "%s\n", optarg);
  1090.                   references = savestr(bfr);
  1091.                   break;
  1092.             case 's':
  1093.                   sprintf(bfr, "Subject: %s\n", optarg);
  1094.                   prochdr(bfr, 1);
  1095.                   break;
  1096.             case 't':
  1097.                   if (hdrline[HTTO] == NULL) {
  1098.                         sprintf(bfr, "To: %s\n", optarg);
  1099.                   } else {
  1100.                         nstrip(hdrline[HTTO]->text);
  1101.                         sprintf(bfr, "%s, %s\n", hdrline[HTTO]->text, optarg);
  1102.                         hlzap(HTTO);
  1103.                   }
  1104.                   prochdr(bfr, 1);
  1105.                   break;
  1106.             case '?':
  1107.                   exit(1);
  1108.             }
  1109.       }
  1110.       if (optind < argc && (infp = fopen(argv[optind], "r")) == NULL)
  1111.             xerror("%s: cannot open", argv[optind]);
  1112.       if (debug) {
  1113.             strcpy(newstemp, "news.tmp");
  1114.             strcpy(mailtemp, "mail.tmp");
  1115.       } else {
  1116.             sprintf(newstemp, "/tmp/pnm%05dn", getpid());
  1117.             sprintf(mailtemp, "/tmp/pnm%05dm", getpid());
  1118.       }
  1119.  
  1120.       readheader();
  1121.  
  1122.       checkheader();
  1123.  
  1124.       if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  1125.             signal(SIGINT, xxit);
  1126.       if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
  1127.             signal(SIGHUP, xxit);
  1128.  
  1129.       if (addrp != addrlist) {
  1130.             mailfp = ckfopen(mailtemp, "w");
  1131.             genmailhdr(mailfp);
  1132.       }
  1133.       if (hdrline[HTNEWSGROUPS]) {
  1134.             newsfp = ckfopen(newstemp, "w");
  1135.             gennewshdr(newsfp);
  1136.       }
  1137.  
  1138.       if (newsfp == NULL && mailfp == NULL)
  1139.             xerror("No to or newsgroup line specified");
  1140.       if (nerrors)
  1141.             xxit(1);
  1142.  
  1143.       addbody(infp);
  1144.       if (signit) {
  1145.             getuser();
  1146.             sprintf(bfr, "%s/.signature", userhome);
  1147.             if ((fp = fopen(bfr, "r")) != NULL) {
  1148.                   if (mailfp)
  1149.                         fputs("--", mailfp);
  1150.                   if (newsfp)
  1151.                         fputs("-- ", newsfp);
  1152.                   addbody(fp);
  1153.             }
  1154.       }
  1155.       if (nerrors)
  1156.             xxit(1);
  1157.  
  1158.       if (background) {
  1159.             if (fork() > 0)
  1160.                   exit(0);
  1161.             signal(SIGINT, SIG_IGN);
  1162.             signal(SIGQUIT, SIG_IGN);
  1163.             signal(SIGHUP, SIG_IGN);
  1164. #ifdef SIGTSTP
  1165.             signal(SIGTSTP, SIG_IGN);
  1166. #endif
  1167.       }
  1168.       if (mailfp) {
  1169.             char **pp;
  1170.             char *arg[6];
  1171.  
  1172.             fclose(mailfp);
  1173.             for (pp = addrlist ; pp < addrp ; pp++) {
  1174.                   arg[0] = MAILER;
  1175.                   arg[1] = *pp;
  1176.                   arg[2] = NULL;
  1177.                   if (runp(arg, mailtemp))
  1178.                         xerror("Mailer failed");
  1179.             }
  1180.       }
  1181.       if (newsfp) {
  1182.             char *arg[6];
  1183.             struct stat statb;
  1184.  
  1185.             fclose(newsfp);
  1186.             /* suppress .signature */
  1187.             if (userhome == NULL)
  1188.                   getuser();
  1189.             sprintf(signfile, "%s/.signature", userhome);
  1190.             if (stat(signfile, &statb) >= 0) {
  1191.                   chmod(signfile, 0);        /* This is sick! */
  1192.                   signmode = statb.st_mode;
  1193.             }
  1194.             if (moderator) {
  1195.                   arg[0] = MAILER;
  1196.                   arg[1] = moderator;
  1197.                   arg[2] = NULL;
  1198.                   if (runp(arg, newstemp))
  1199.                         xerror("Mail to moderator failed");
  1200.             } else {
  1201.                   arg[0] = XINEWS;
  1202.                   arg[1] = "-h";
  1203.                   arg[2] = NULL;
  1204.                   if (runp(arg, newstemp))
  1205.                         xerror("inews failed");
  1206.             }
  1207.             savearticle() ;
  1208.       }
  1209.       xxit(nerrors? 1 : 0);
  1210. }
  1211.  
  1212.  
  1213.  
  1214. /*
  1215.  * Print an error message for an illegal header line.
  1216.  */
  1217.  
  1218. synerr(fmt, a1, a2, a3, a4)
  1219.       char *fmt;
  1220.       {
  1221.       fprintf(stderr, "postnm: ");
  1222.       if (curhdr)
  1223.             fprintf(stderr, "line %d: ", curhdr->linno);
  1224.       fprintf(stderr, fmt, a1, a2, a3, a4);
  1225.       putc('\n', stderr);
  1226.       nerrors++;
  1227. }
  1228.  
  1229.  
  1230. jsynerr(fmt, a1, a2, a3, a4)
  1231.       char *fmt;
  1232.       {
  1233.       synerr(fmt, a1, a2, a3, a4);
  1234.       reset();
  1235. }
  1236.  
  1237.  
  1238.  
  1239. xerror(fmt, a1, a2, a3, a4)
  1240.       char *fmt;
  1241.       {
  1242.       fprintf(stderr, "postnm: ");
  1243.       fprintf(stderr, fmt, a1, a2, a3, a4);
  1244.       putc('\n', stderr);
  1245.       xxit(2);
  1246. }
  1247.  
  1248.  
  1249.  
  1250. xxit(status) {
  1251.       if (!debug) {
  1252.             unlink(newstemp);
  1253.             unlink(mailtemp);
  1254.       }
  1255.       if (signmode) {
  1256.             chmod(signfile, signmode);
  1257.       }
  1258.       exit(status);
  1259. }
  1260.  
  1261.  
  1262.  
  1263. runp(arglist, infile)
  1264.       char **arglist;
  1265.       char *infile;
  1266.       {
  1267.       int pid, status, fd;
  1268.       extern char *optarg;
  1269.       extern int optind;
  1270.  
  1271.       if (debug) {
  1272.             while (*arglist)
  1273.                   printf("%s ", *arglist++);
  1274.             if (infile)
  1275.                   printf("< %s", infile);
  1276.             putchar('\n');
  1277.             return 0;
  1278.       }
  1279.       if (infile && (fd = open(infile, 0)) < 0)
  1280.             xerror("Can't open %s", infile);
  1281.       if ((pid = fork()) < 0)
  1282.             xerror("Can't fork");
  1283.       if (pid == 0) {
  1284.             if (infile && fd != 0) {
  1285.                   close(0);
  1286.                   if (dup(fd) != 0)
  1287.                         fputs("postnm: dup failed\n", stderr);
  1288.                   close(fd);
  1289.             }
  1290.             execv(arglist[0], arglist);
  1291.             fprintf(stderr, "Can't exec %s\n", arglist[0]);
  1292.             exit(127);
  1293.       }
  1294.       close(fd);
  1295.       while (wait(&status) != pid);
  1296.       if (status & 0177) {
  1297.             fprintf(stderr, "postnm: %s died with signal %d", arglist[0], status & 0177);
  1298.             if (status & 0200)
  1299.                   fprintf(stderr, " - core dumped");
  1300.             putc('\n', stderr);
  1301.       } else if (status) {
  1302.             fprintf(stderr, "postnm: exit status %d from %s\n", (unsigned) status >> 8, arglist[0]);
  1303.       }
  1304.       return status;
  1305. }
  1306.  
  1307.  
  1308.  
  1309. /*
  1310.  * Save a copy of the article in the users NEWSARCHIVE file.
  1311.  * The article is saved only if the user explicitly sets NEWSARCHIVE.
  1312.  * Currently, we save USENET articles but not mail, which is
  1313.  * rather questionable.
  1314.  */
  1315. savearticle() {
  1316.       register FILE *in, *out;
  1317.       register int c;
  1318.       time_t timenow, time();
  1319.       char *today, *ctime();
  1320.       char *ccname;
  1321.       char *getenv();
  1322.  
  1323.       if ((ccname = getenv("NEWSARCHIVE")) == NULL || ccname[0] == '\0')
  1324.             return;
  1325.       if ((in = fopen(newstemp, "r")) == NULL) {
  1326.             xerror("Can't reopen %s", newstemp);
  1327.       }
  1328.       if ((out = fopen(ccname, "a")) == NULL) {
  1329.             xerror("Can't open %s to save article", ccname);
  1330.       }
  1331.       timenow = time((time_t *)0);
  1332.       today = ctime(&timenow);
  1333.       fprintf(out, "From postreply %s", today);
  1334.       while ((c=getc(in)) != EOF)
  1335.             putc(c, out);
  1336.       putc('\n', out);
  1337.       fclose(in);
  1338.       fclose(out);
  1339. }
  1340. !E!O!F!
  1341.  
  1342. cat > postnews/postnm2.c <<\!E!O!F!
  1343. /*
  1344.  * Pass one:  read in the article.
  1345.  *
  1346.  * Copyright 1984 by Kenneth Almquist.  All rights reserved.
  1347.  *     Permission is granted for anyone to use and distribute, but not
  1348.  *     sell, this program provided that this copyright notice is retained.
  1349.  */
  1350.  
  1351. #include "postnm.h"
  1352.  
  1353. /* values of ht_legal */
  1354. #define DITTO    0        /* alias for previous name */
  1355. #define LEGAL    1        /* normal field */
  1356. #define ONCE    2        /* field may only occur once */
  1357. #define ILLEGAL    3        /* user not allowed to specify this field */
  1358.  
  1359. struct htype {
  1360.       char *ht_name;        /* name of header field */
  1361.       int   ht_legal;        /* whether field is legal */
  1362.       int   ht_type;        /* field type number */
  1363. }
  1364. htype[] = {
  1365.       "To",        LEGAL,    HTTO,
  1366.       "Cc",        LEGAL,    HTCC,
  1367.       "Bcc",        LEGAL,    HTBCC,
  1368.       "Newsgroups",    ONCE,    HTNEWSGROUPS,
  1369.       "Newsgroup",    DITTO,    HTNEWSGROUPS,
  1370.       "In-Reply-To",    ONCE,    HTINREPLYTO,
  1371.       "References",    ONCE,    HTREFERENCES,
  1372.       "Subject",    ONCE,    HTSUBJECT,
  1373.       "Expires",    ONCE,    HTEXPIRES,
  1374.       "From",        ONCE,    HTFROM,
  1375.       "Reply-To",    ONCE,    HTREPLYTO,
  1376.       "Followup-To",    ONCE,    HTFOLLOWUPTO,
  1377.       "Distribution",    ONCE,    HTDIST,
  1378.       "Dist",        DITTO,    HTDIST,
  1379.       "Keywords",    LEGAL,    HTKEYWORDS,
  1380.       "Control",    ONCE,    HTCONTROL,
  1381.       "Organization",    ONCE,    HTORGANIZATION,
  1382.       "Summary",    ONCE,    HTSUMMARY,
  1383.       "Approved",    ONCE,    HTAPPROVED,
  1384.       "Comments",    LEGAL,    HTCOMMENTS,
  1385.       "Message-Id",    ONCE,    HTMESSAGEID,
  1386.       "Command",    ONCE,    HTCOMMAND,
  1387.       "Article-I.d.",    ILLEGAL,HTUNKNOWN,
  1388.       "Sender",        ILLEGAL,HTUNKNOWN,
  1389.       "Resent-Sender",    ILLEGAL,HTUNKNOWN,
  1390.       "Path",        ILLEGAL,HTUNKNOWN,
  1391.       "Received",    ILLEGAL,HTUNKNOWN,
  1392.       NULL,        LEGAL,    HTUNKNOWN
  1393. };
  1394.  
  1395. /*
  1396.  * Read in the header.
  1397.  */
  1398.  
  1399. readheader() {
  1400.       setexit();
  1401.       while (gethline(infp) != EOF) {
  1402.             prochdr(bfr, 0);
  1403.       }
  1404. }
  1405.  
  1406.  
  1407.  
  1408. prochdr(val, faked)
  1409.       char *val;
  1410.       {
  1411.       char htname[64];
  1412.  
  1413.       curhdr = ckmalloc(sizeof *curhdr);
  1414.       curhdr->linno = !faked? linno : 0;
  1415.       curhdr->text = savestr(val);
  1416.       curhdr->fixed = NULL;
  1417.       scanp = curhdr->text;        /* scan input line */
  1418.       if (scnchr(" \t\n:") == 0)
  1419.             jsynerr("Illegal header line type");
  1420.       if (scanp - curhdr->text > 63)
  1421.             jsynerr("Header line type too long");
  1422.       getval(curhdr->text, scanp, htname);
  1423.       fixcase(htname);
  1424.       skipbl();
  1425.       if (*scanp++ != ':')
  1426.             jsynerr("No colon on header line\nDid you remember to leave a blank line after the article header?");
  1427.       skipbl();
  1428.       curhdr->body = scanp;
  1429.       if (*scanp == '\0')
  1430.             curhdr->body--;
  1431.  
  1432.       gettype(htname, curhdr);
  1433.  
  1434.       appheader(curhdr, hlast);
  1435. }
  1436.  
  1437.  
  1438.  
  1439. gettype(htname, hp)
  1440.       char *htname;
  1441.       struct hline *hp;
  1442.       {
  1443.       register struct htype *htp;
  1444.  
  1445.       for (htp = htype ; htp->ht_name != NULL && !equal(htp->ht_name, htname) ; htp++);
  1446.       if (htp->ht_name == NULL) {
  1447.             for (htp = htype ;
  1448.                  htp->ht_name && (htp->ht_legal == ILLEGAL || !misspells(htp->ht_name, htname)) ;
  1449.                  htp++);
  1450.             if (htp->ht_name != NULL)
  1451.                   synerr("You misspelled \"%s\"", htp->ht_name);
  1452.       }
  1453.       while (htp->ht_legal == DITTO)
  1454.             htp--;
  1455.       hp->type = htp->ht_type;
  1456.       if (htp->ht_name == NULL) {
  1457.             hp->hdrname = savestr(htname);
  1458.       } else {
  1459.             hp->hdrname = htp->ht_name;
  1460.             if (htp->ht_legal == ILLEGAL)
  1461.                   jsynerr("You cannot include a \"%s:\" header", htname);
  1462.             else if (htp->ht_legal == ONCE && hdrline[hp->type])
  1463.                   jsynerr("Only one \"%s:\" header line is permitted", htname);
  1464.             hdrline[hp->type] = hp;
  1465.       }
  1466. }
  1467.  
  1468.  
  1469. /*
  1470.  * Get the correct combination of upper case and lower case in a header
  1471.  * line name.  The first character and any character immediatly following
  1472.  * a minus sign is capitalized.  Everything else to lower case.
  1473.  */
  1474.  
  1475. fixcase(p)
  1476.       register char *p;
  1477.       {
  1478.       int flag = 1;
  1479.  
  1480.       while (*p) {
  1481.             if (*p == '-')
  1482.                   flag = 1;
  1483.             else if (flag) {
  1484.                   flag = 0;
  1485.                   if (islower(*p))
  1486.                         *p = toupper(*p);
  1487.             } else {
  1488.                   if (isupper(*p))
  1489.                         *p = tolower(*p);
  1490.             }
  1491.             p++;
  1492.       }
  1493. }
  1494.  
  1495.  
  1496.  
  1497. misspells(word, incorrect)
  1498.       char *word, *incorrect;
  1499.       {
  1500.       int i = strlen(word), j = strlen(incorrect);
  1501.       register char *p = word, *q = incorrect;
  1502.  
  1503.       if (j != i && j != i + 1 && j != i - 1)
  1504.             return 0;
  1505.       while (*p == *q && *p)
  1506.             p++, q++;
  1507.       if (i > j) {
  1508.             if (equal(p + 1, q))  return 1;
  1509.       } else if (i < j) {
  1510.             if (equal(p, q + 1))  return 1;
  1511.       } else {
  1512.             if (equal(p + 1, q + 1))  return 1;
  1513.             if (p[0] == q[1] && p[1] == q[0] && equal(p + 2, q + 2))
  1514.                   return 1;
  1515.       }
  1516.       return 0;
  1517. }
  1518.  
  1519.  
  1520.  
  1521.  
  1522. /*
  1523.  * Insert a header after the given header, or at the beginning of
  1524.  * the list if NULL is given.
  1525.  */
  1526.  
  1527. appheader(hp, after)
  1528.       struct hline *hp, *after;
  1529.       {
  1530.       struct hline **hpp;
  1531.  
  1532.       if (after)
  1533.             hpp = &after->next;
  1534.       else
  1535.             hpp = &hfirst;
  1536.       hp->next = *hpp;
  1537.       *hpp = hp;
  1538.       if (hlast == after)
  1539.             hlast = hp;
  1540. }
  1541.  
  1542.  
  1543.  
  1544.  
  1545. /*
  1546.  * Read a header line into bfr, handling continuations.
  1547.  */
  1548.  
  1549.  
  1550. gethline(fp)
  1551.       register FILE *fp;
  1552.       {
  1553.       char *p;
  1554.       register char *q;
  1555.       int c;
  1556.       static int nextlinno = 1;
  1557.  
  1558.       linno = nextlinno;
  1559.       if (feof(fp))
  1560.             return EOF;
  1561.       p = bfr;
  1562.       do {
  1563.             if (fgets(p, bfr + LBUFLEN - p, fp) == NULL)
  1564.                   return EOF;
  1565.             if (bfr[0] == '\n')
  1566.                   return EOF;
  1567.             for (q = p ; *q != '\0' && *q != '\n' ; q++) {
  1568.                   if ((*q < ' ' && *q != '\t') || *q > '~') {
  1569.                         fprintf(stderr, "postnm: line %d: illegal character (octal %o)\n",
  1570.                                 nextlinno, *(unsigned char *)q);
  1571.                         nerrors++;
  1572.                   }
  1573.             }
  1574.             if (*q == '\0') {
  1575.                   if (strlen(bfr) != LBUFLEN - 1)
  1576.                         xerror("line %d: EOF in middle of header line", nextlinno);
  1577.                   else
  1578.                         xerror("line %d: Line too long", nextlinno);
  1579.             }
  1580.             nextlinno++;
  1581.             p = q + 1;
  1582.       } while (ungetc(c = getc(fp), fp), c == ' ' || c == '\t');
  1583.       return 0;
  1584. }
  1585.  
  1586.  
  1587.  
  1588. /*
  1589.  * Copy the body of the article to the mail and/or news file.  We have to
  1590.  * check for:
  1591.  *    1) Control characters.
  1592.  *    2) Lines consisting of a single '.'
  1593.  *    3) Trailing blank lines, which we silently delete.
  1594.  *    4) Article which might trigger line eater bug.
  1595.  */
  1596.  
  1597. addbody(in)
  1598.       register FILE *in;
  1599.       {
  1600.       register int c;
  1601.       register FILE *mail = mailfp, *news = newsfp;
  1602.       int nlcount = 1;        /* force initial blank line */
  1603.       int dotflag = 0;        /* test for line containing single dot */
  1604.       int first = 1;        /* avoid line eater */
  1605.  
  1606.       linno++;
  1607.       if (!feof(in))
  1608.          while ((c = getc(in)) != EOF) {
  1609.             if (c == '\n') {
  1610.                   if (dotflag)
  1611.                         xerror("line %d: some versions of mail and news choke on lines consisting of a single dot", linno - nlcount);
  1612.                   linno++;
  1613.                   nlcount++;
  1614.             } else if (!isprint(c) && !isspace(c) && c != '\b') {
  1615.                   if (in == infp)
  1616.                         fprintf(stderr, "line %d: illegal character (octal %o)\n", linno, c);
  1617.                   else
  1618.                         fprintf(stderr, "illegal character in signature file (octal %o)\n", c);
  1619.                   nerrors++;
  1620.             } else {
  1621.                   if (nlcount) {
  1622.                         if (first) {
  1623.                               first = 0;
  1624.                               if ((c == '\t' || c == ' ') && nlcount == 1 && news && in == infp)
  1625.                                     putc('\n', news);
  1626.                         }
  1627.                         if (c == '.')
  1628.                               dotflag++;
  1629.                         do {
  1630.                               if (mail)
  1631.                                     putc('\n', mail);
  1632.                               if (news)
  1633.                                     putc('\n', news);
  1634.                         } while (--nlcount > 0);
  1635.                   } else
  1636.                         dotflag = 0;
  1637.                   if (mail)
  1638.                         putc(c, mail);
  1639.                   if (news)
  1640.                         putc(c, news);
  1641.             }
  1642.       }
  1643.       if (mail)
  1644.             putc('\n', mail);
  1645.       if (news)
  1646.             putc('\n', news);
  1647.       if (mail) {
  1648.             fflush(mail);
  1649.             if (ferror(mail))
  1650.                   xerror("write error on temp file");
  1651.       }
  1652.       if (news) {
  1653.             fflush(news);
  1654.             if (ferror(news))
  1655.                   xerror("write error on temp file");
  1656.       }
  1657. }
  1658. !E!O!F!
  1659.  
  1660. cat > postnews/postnm3.c <<\!E!O!F!
  1661. /*
  1662.  * Pass 2:  Check header lines for errors.
  1663.  *
  1664.  * Copyright 1984 by Kenneth Almquist.  All rights reserved.
  1665.  *     Permission is granted for anyone to use and distribute, but not
  1666.  *     sell, this program provided that this copyright notice is retained.
  1667.  */
  1668.  
  1669. #include "postnm.h"
  1670.  
  1671. char *cmsg[] = {        /* list of control messages */
  1672.       "cancel",
  1673.       "version",
  1674.       "ihave",
  1675.       "sendme",
  1676.  
  1677.       "newgroup",
  1678.       "rmgroup",
  1679.       "sendsys",
  1680.       "senduuname",
  1681.       "checkgroups",
  1682.       "delsub",
  1683.       NULL
  1684. };
  1685. #define PRIVCMSG (&cmsg[4])    /* start of priviledged control messages */
  1686.  
  1687.  
  1688.  
  1689. checkheader() {
  1690.       char *p, *q;
  1691.       struct hline *hp;
  1692.  
  1693.       curhdr = hdrline[HTSUBJECT];
  1694.       if (curhdr == NULL) {
  1695.             if (hdrline[HTCONTROL]) {
  1696.                   sprintf(bfr, "Subject: %s", hdrline[HTCONTROL]->body);
  1697.                   prochdr(bfr, 1);
  1698.             } else {
  1699.                   fprintf(stderr, "postnm: no subject line\n");
  1700.                   nerrors++;
  1701.             }
  1702.       }
  1703.       setexit();
  1704.       if (hdrline[HTCOMMAND]) {
  1705.             hlselect(hdrline[HTCOMMAND]);
  1706.             hlzap(HTCOMMAND);
  1707.             skipbl();
  1708.             p = scanp;
  1709.             scnchr(" \t\n");
  1710.             q = scanp;
  1711.             skipws(" \t\n");
  1712.             if (*scanp != '\0')
  1713. cmdjsynerr:        jsynerr("Command must be \"reply\", \"followup\", or \"both\"");
  1714.             *q = '\0';
  1715.             if (equal(p, "reply") || equal(p, "r"))
  1716.                   hlzap(HTNEWSGROUPS), hlzap(HTDIST);
  1717.             else if (equal(p, "followup") || equal(p, "f"))
  1718.                   hlzap(HTTO);
  1719.             else if (!equal(p, "both"))
  1720.                   goto cmdjsynerr;
  1721.       }
  1722.  
  1723.       for (hp = hfirst ; hp ; hp = hp->next) {
  1724.             fixheader(hp);
  1725.       }
  1726.       if (references && hdrline[HTNEWSGROUPS] && !hdrline[HTREFERENCES]) {
  1727.             sprintf(bfr, "References: %s", references);
  1728.             prochdr(bfr, 1);
  1729.       }
  1730.       hlzap(HTBCC);
  1731.       cknewsgroups();
  1732.       *addrp = NULL;
  1733. }
  1734.  
  1735.  
  1736.  
  1737. /*
  1738.  * Process a header line.
  1739.  */
  1740.  
  1741. fixheader(hp)
  1742.       struct hline *hp;
  1743.       {
  1744.       extern char *begaddr, *endaddr;
  1745.       char *p;
  1746.       register char **pp;
  1747.       time_t tim;
  1748.       struct tm *tm;
  1749.       static char month[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1750.                   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1751.  
  1752.       if (setexit())
  1753.             return;
  1754.       hlselect(hp);
  1755.       switch (hp->type) {
  1756.       case HTTO:
  1757.       case HTCC:
  1758.       case HTBCC:
  1759.             for (;;) {
  1760.                   skipws(" \t\n,");
  1761.                   if (*scanp == '\0')
  1762.                         break;
  1763.                   if (addrp >= &addrlist[MAXADDR])
  1764.                         jsynerr("too many mail addresses");
  1765.                   parseaddr();
  1766.                   *addrp++ = getval(begaddr, endaddr, NULL);
  1767.             }
  1768.             break;
  1769.  
  1770.       case HTDIST:
  1771.             commalist();
  1772.             for (pp = nlist ; *pp ; pp++)
  1773.                   if (equal(*pp, "net"))
  1774.                         *pp = "world";
  1775.             rmdup(nlist);
  1776.             if (nlist[0] == NULL || nlist[1] == NULL && equal(nlist[0], "world"))
  1777.                   hlzap(HTDIST);
  1778.             else
  1779.                   ckdist();
  1780.             for (pp = nlist ; *pp ; pp++)
  1781.                   if (equal(*pp, "world"))
  1782.                         *pp = "net";
  1783.             goto outlist;
  1784.  
  1785.       case HTFOLLOWUPTO:
  1786.             commalist();
  1787.             if (nlist[0] == NULL)
  1788.                   jsynerr("empty followup-to line");
  1789.             rmdup(nlist);
  1790.             bcopy((char *)nlist, (char *)follist, sizeof follist);
  1791.             goto outlist;
  1792.  
  1793.       case HTNEWSGROUPS:
  1794.             commalist();
  1795.             if (nlist[0] == NULL)
  1796.                   jsynerr("empty newsgroups line");
  1797.             rmdup(nlist);
  1798.             bcopy((char *)nlist, (char *)nglist, sizeof nglist);
  1799.  
  1800. outlist:    outp = bfr;
  1801.             if (nlist[0]) {
  1802.                   for (pp = nlist ; *pp ; pp++) {
  1803.                         if (outp != bfr)
  1804.                               outstr(",");
  1805.                         outstr(*pp);
  1806.                   }
  1807.                   outstr("\n");
  1808.                   hp->fixed = savestr(bfr);
  1809.             }
  1810.             break;
  1811.  
  1812.       case HTEXPIRES:
  1813.             if ((tim = cgtdate(scanp)) == -1L)
  1814.                   jsynerr("Can't parse date");
  1815.             tm = gmtime(&tim);
  1816.             sprintf(bfr, "%d %s %d %02d:%02d:%02d GMT\n", tm->tm_mday,
  1817.                     month[tm->tm_mon], tm->tm_year, tm->tm_hour, tm->tm_min,
  1818.                     tm->tm_sec);
  1819.             hp->fixed = savestr(bfr);
  1820.             break;
  1821.  
  1822.       case HTCONTROL:
  1823.             p = scanp;
  1824.             scnchr(" \t\n");
  1825.             getval(p, scanp, bfr);
  1826.             for (pp = cmsg ; *pp && !equal(*pp, bfr) ; pp++);
  1827.             if (*pp == NULL)
  1828.                   jsynerr("Unknown control message \"%s\"", bfr);
  1829.             else if (pp >= PRIVCMSG && !isadmin())
  1830.                   jsynerr("Get your system administrator to send this control message for you.");
  1831.             break;
  1832.  
  1833.       case HTSUBJECT:
  1834.             if (*scanp == '\n')
  1835.                   jsynerr("empty subject line");
  1836.             if (prefix(scanp, "cmsg "))
  1837.                   jsynerr("subjects beginning with cmsg confuse inews");
  1838.             if (prefix(scanp, "Re: ") && !hdrline[HTREFERENCES] && !references)
  1839.                   jsynerr("followup articles must have references lines");
  1840.             break;
  1841.  
  1842.       case HTREFERENCES:
  1843.             if (references && !equal(scanp, references)) {
  1844.                   fprintf(stderr, "postnm: line %d: you changed the references line but I fixed it\n", curhdr->linno);
  1845.                   curhdr->fixed = references;
  1846.             }
  1847.             break;
  1848.  
  1849.       case HTAPPROVED:
  1850.             ckapproved();
  1851.             break;
  1852.  
  1853.       case HTORGANIZATION:
  1854.             {
  1855.                   /* Inews ignores organization lines */
  1856.                   /* I shouldn't have to do this */
  1857.                   char **pp, **qq;
  1858.                   extern char **environ;
  1859.                   sprintf(bfr, "ORGANIZATION=%s", scanp);
  1860.                   nstrip(bfr);
  1861.                   envkludge[0] = savestr(bfr);
  1862.                   qq = envkludge + 1;
  1863.                   for (pp = environ ; pp < environ + 199 && *pp ; pp++)
  1864.                         if (!prefix(*pp, "ORGANIZATION="))
  1865.                               *qq++ = *pp;
  1866.                   *qq = 0;
  1867.                   environ = envkludge;
  1868.             }
  1869.             break;
  1870.  
  1871.       default:
  1872.             break;
  1873.       }
  1874. }
  1875.  
  1876.  
  1877.  
  1878. /*
  1879.  * Check that newsgroups or distributions are legal.
  1880.  */
  1881.  
  1882. checklist(file, thing)
  1883.       char *file, *thing;
  1884.       {
  1885.       char fname[FPATHLEN];
  1886.       FILE *fp;
  1887.       char *left[MAXGRPS + 1], **pp;
  1888.  
  1889.       sprintf(fname, "%s/%s", LIB, file);
  1890.       fp = ckfopen(fname, "r");
  1891.       bcopy((char *)nlist, (char *)left, sizeof(left));
  1892.       while (fgets(bfr, LBUFLEN, fp) != NULL) {
  1893.             scanp = bfr;
  1894.             scnchr(" \t");
  1895.             *scanp = '\0';
  1896.             for (pp = left ; *pp ; pp++) {
  1897.                   if (equal(*pp, bfr)) {
  1898.                         do *pp = *(pp + 1);
  1899.                         while (*pp++);
  1900.                         break;
  1901.                   }
  1902.             }
  1903.       }
  1904.       fclose(fp);
  1905.       for (pp = left ; *pp ; pp++)
  1906.             synerr("illegal %s %s", thing, *pp);
  1907. }
  1908.  
  1909.  
  1910.  
  1911. /*
  1912.  * Check distributions for validity.  If they aren't in the distributions
  1913.  * file, we try the active file.
  1914.  */
  1915.  
  1916. ckdist() {
  1917.       FILE *fp;
  1918.       char *p;
  1919.       char **pp;
  1920.       char *dlist[MAXGRPS + 1];
  1921.  
  1922.       bcopy((char *)nlist, (char *)dlist, sizeof dlist);
  1923.       sprintf(bfr, "%s/distributions", LIB);
  1924.       if ((fp = fopen(bfr, "r")) != NULL) {
  1925.             while (dlist[0] && fgets(bfr, LBUFLEN, fp)) {
  1926.                   if (bfr[0] == '\n' || bfr[0] == '#')
  1927.                         continue;
  1928.                   if ((p = strpbrk(bfr, " \t\n")) == NULL)
  1929.                         continue;
  1930.                   *p = '\0';
  1931.                   rmlist(bfr, dlist);
  1932.             }
  1933.             fclose(fp);
  1934.       }
  1935.       else  printf("postnm: warning: no distributions file\n");
  1936.       if (dlist[0]) {
  1937.             sprintf(bfr, "%s/active", LIB);
  1938.             fp = ckfopen(bfr, "r");
  1939.             while (dlist[0] && fgets(bfr, LBUFLEN, fp)) {
  1940.                   if ((p = strpbrk(bfr, " \n")) == NULL)
  1941.                         continue;
  1942.                   *p = '\0';
  1943.                   for (pp = dlist ; *pp ; ) {
  1944.                         if (ngmatch(bfr, *pp))
  1945.                               rmlist(*pp, dlist);
  1946.                         else
  1947.                               pp++;
  1948.                   }
  1949.             }
  1950.             fclose(fp);
  1951.       }
  1952.  
  1953.       prtbad(dlist, "distribution", curhdr);
  1954. }
  1955.  
  1956.  
  1957.  
  1958. /*
  1959.  * Check the newsgroups on the Newsgroups: and Followup-To: lines
  1960.  * for validity.  We do both at once so that we only have to scan
  1961.  * the active file once.
  1962.  */
  1963.  
  1964. cknewsgroups() {
  1965.       FILE *actfp;
  1966.       char *p;
  1967.       int didng = 0;
  1968.       char **pp;
  1969.  
  1970.       if (nglist[0] == NULL && follist[0] == NULL)
  1971.             return;
  1972.       bcopy((char *)nglist, (char *)nlist, sizeof nlist);
  1973.       sprintf(bfr, "%s/active", LIB);
  1974.       actfp = ckfopen(bfr, "r");
  1975.       while ((nlist[0] || follist[0]) && fgets(bfr, LBUFLEN, actfp)) {
  1976.             if ((p = index(bfr, ' ')) == NULL && (p = index(bfr, '\n')) == NULL)
  1977.                   continue;
  1978.             *p = '\0';
  1979.             if (rmlist(bfr, nlist) >= 0)
  1980.                   didng = 1;
  1981.             rmlist(bfr, follist);
  1982.       }
  1983.       fclose(actfp);
  1984.  
  1985.       /* Not all newsgroups on followup need be legal */
  1986.       if (nlist[0] && (!hdrline[HTREFERENCES] || !didng))
  1987.             prtbad(nlist, "newsgroup", hdrline[HTNEWSGROUPS]);
  1988.       else {
  1989.             /* check for moderated groups */
  1990.             for (pp = nglist ; *pp ; pp++) {
  1991.                   if (hdrline[HTAPPROVED] == NULL && ngmatch(*pp, MODGROUPS)) {
  1992.                         getmod(*pp);
  1993.                         break;
  1994.                   }
  1995.             }
  1996.       }
  1997.       prtbad(follist, "newsgroup", hdrline[HTFOLLOWUPTO]);
  1998. }
  1999.  
  2000.  
  2001. prtbad(pp, what, hl)
  2002.       char **pp;
  2003.       char *what;
  2004.       struct hline *hl;
  2005.       {
  2006.       hlselect(hl);
  2007.       while (*pp)
  2008.             synerr("Bad %s %s", what, *pp++);
  2009. }
  2010.  
  2011.  
  2012.  
  2013. /*
  2014.  * Remove an entry from an array of character strings.
  2015.  */
  2016.  
  2017. rmlist(entry, list)
  2018.       char *entry, **list;
  2019.       {
  2020.       register char **pp;
  2021.       register char **qq;
  2022.       int retval;
  2023.  
  2024.       qq = list;
  2025.       retval = -1;
  2026.       for (pp = list ; *pp ; pp++) {
  2027.             if (!equal(*pp, entry))
  2028.                   *qq++ = *pp;
  2029.             else
  2030.                   retval = 0;        /* removed one */
  2031.       }
  2032.       *qq = NULL;
  2033.       return retval;
  2034. }
  2035.  
  2036.  
  2037.  
  2038. /*
  2039.  * Check "Approved:" line.  Currently, we require an address which must
  2040.  * include an at sign and which cannot be enclosed in angle brackets.
  2041.  * An optional real name in parenthesis is allowed, but not other comments.
  2042.  */
  2043.  
  2044. ckapproved() {
  2045.       char *start = scanp;
  2046.       extern char *beglocal, *endlocal, *begdomain;
  2047.  
  2048.       if (*scanp == '(' || *scanp == ',' || *scanp == '\n')
  2049. bad:        jsynerr("Illegal address");
  2050.       parseaddr();
  2051.       if (beglocal != start || *endlocal != '@' || begdomain != endlocal + 1)
  2052.             goto bad;
  2053.       skipbl();
  2054.       if (*scanp)
  2055.             goto bad;
  2056. }
  2057.  
  2058.  
  2059.  
  2060. /*
  2061.  * Parse a comma separated list into its components.
  2062.  */
  2063.  
  2064. commalist() {
  2065.       char **pp;
  2066.       char *p;
  2067.  
  2068.       pp = nlist;
  2069.       for (;;) {
  2070.             skipws(" \t\n,");
  2071.             if (*scanp == '\0')
  2072.                   break;
  2073.             p = scanp;
  2074.             scnchr(" \t\n,(");
  2075.             if (pp >= nlist + MAXGRPS)
  2076.                   jsynerr("list too long");
  2077.             *pp++ = getval(p, scanp, NULL);
  2078.       }
  2079.       *pp = NULL;
  2080. }
  2081.  
  2082.  
  2083.  
  2084. outstr(str)
  2085.       char *str;
  2086.       {
  2087.       strcpy(outp, str);
  2088.       outp += strlen(str);
  2089. }
  2090.  
  2091.  
  2092.  
  2093. rmdup(list)
  2094.       char **list;
  2095.       {
  2096.       register char **pp, **qq;
  2097.  
  2098.       while (*list) {
  2099.             for (pp = qq = list + 1 ; *pp ; pp++)
  2100.                   if (!equal(*pp, *list))
  2101.                         *qq++ = *pp;
  2102.             *qq = NULL;
  2103.             list++;
  2104.       }
  2105. }
  2106.  
  2107.  
  2108.  
  2109. /*
  2110.  * Set up a given header for parsing.
  2111.  */
  2112.  
  2113. hlselect(hp)
  2114.       struct hline *hp;
  2115.       {
  2116.       curhdr = hp;
  2117.       scanp = hp->body;
  2118. }
  2119.  
  2120.  
  2121. hlzap(type)
  2122.       int type;
  2123.       {
  2124.       register struct hline *hp, **prev;
  2125.  
  2126.       prev = &hfirst;
  2127.       while ((hp = *prev) != NULL) {
  2128.             if (hp->type == type)
  2129.                   *prev = hp->next;    /* delete line */
  2130.             else
  2131.                   prev = &hp->next;
  2132.       }
  2133.       hdrline[type] = NULL;
  2134.       hlast = NULL;
  2135.       for (hp = hfirst ; hp ; hp = hp->next)
  2136.             hlast = hp;
  2137. }
  2138.  
  2139.  
  2140.  
  2141. /*
  2142.  * Figure out the moderator of a group.
  2143.  */
  2144.  
  2145. getmod(newsgroup)
  2146.       char *newsgroup;
  2147.       {
  2148.       FILE *fp;
  2149.       char *p;
  2150.       char *q;
  2151.       char s[BUFLEN];
  2152.       FILE *popen();
  2153.  
  2154.       sprintf(bfr, "%s/moderators", LIB);
  2155.       fp = ckfopen(bfr, "r");
  2156.       for (;;) {
  2157.             if (fgets(bfr, LBUFLEN, fp) == NULL)
  2158.                   goto notfound;
  2159.             if ((p = strpbrk(bfr, " \t")) == NULL)
  2160.                   continue;        /* "Can't happen" */
  2161.             *p++ = '\0';
  2162.             if (equal(bfr, newsgroup))
  2163.                   break;
  2164.       }
  2165.       fclose(fp);
  2166.       while (*p == ' ' || *p == '\t')
  2167.             p++;
  2168.       nstrip(p);
  2169.       moderator = savestr(p);
  2170.       return;
  2171.  
  2172. notfound:
  2173.       fclose(fp);
  2174.  
  2175.       /* try to use return path from old article */
  2176.       sprintf(bfr, "%s/active", LIB);
  2177.       fp = ckfopen(bfr, "r");
  2178.       while (fgets(bfr, LBUFLEN, fp) != NULL) {
  2179.             p = index(bfr, ' ');
  2180.             *p++ = '\0';
  2181.             if (equal(bfr, newsgroup))
  2182.                   break;
  2183.       }
  2184.       fclose(fp);
  2185.       if ((q = strpbrk(p, " \t\n")) != NULL)
  2186.             *q = '\0';
  2187.       scopyn(p, s, 32);
  2188.       dirname(newsgroup, bfr);
  2189.       sprintf(bfr + strlen(bfr), "/%ld", atol(s));
  2190.       printf("postnm: trying to find moderator for %s from %s\n", newsgroup, bfr);
  2191.       if ((fp = fopen(bfr, "r")) != NULL) {
  2192.             while (fgets(bfr, LBUFLEN, fp) != NULL && bfr[0] != '\n') {
  2193. #ifdef notdef /* "From:" line has same info */
  2194.                   if (prefix(bfr, "Path:")) {
  2195.                         scanp = bfr + 5;
  2196.                         s[0] = '\0';
  2197.                         for (;;) {
  2198.                               scchr("!:@^% \t\n");
  2199.                               p = scanp;
  2200.                               scnchr("!:@^% \t\n");
  2201.                               q = scanp;
  2202.                               scchr("!:@^% \t\n");
  2203.                               if (*scanp)
  2204.                                     getval(p, q, s);
  2205.                               else
  2206.                                     break;
  2207.                         }
  2208.                         if (s[0] == '\0')
  2209.                               continue;
  2210.                         if (index(s, '.') == NULL)
  2211.                               strcat(s, ".UUCP");
  2212.                         *q = '\0';
  2213.                         sprintf(bfr, "%s@%s", p, s);
  2214.                         moderator = savestr(bfr);
  2215.                         printf("postnm: assuming moderator for %s is %s\n", newsgroup, bfr);
  2216.                   }
  2217. #endif
  2218.                   if (prefix(bfr, "From:")) {
  2219.                         getaddr(bfr + 5, s);
  2220.                         moderator = savestr(s);
  2221.                   }
  2222.                   else if (prefix(bfr, "Approved:")) {
  2223.                         getaddr(bfr + 9, s);
  2224.                         moderator = savestr(s);
  2225.                   }
  2226.             }
  2227.       }
  2228.  
  2229.       if (fp != NULL)
  2230.             fclose(fp);
  2231.       if (! moderator)
  2232.             synerr("Can\'t find moderator for newsgroup %s", newsgroup);
  2233. #ifdef NOTIFY
  2234.       sprintf(bfr, "mail \'%s\'", NOTIFY);
  2235. #else
  2236.       sprintf(bfr, "mail \'%s\'", ADMIN);
  2237. #endif
  2238.       if ((fp = popen(bfr, "w")) == NULL)
  2239.             fprintf(stderr, "postnm: can\'t send mail to administrator\n");
  2240.       else {
  2241.             fprintf(fp, "Subject: missing moderator entry for %s\n\n", newsgroup);
  2242.             fprintf(fp, "From: The postnm program.\n");
  2243.             fprintf(fp, "\nThere is no entry for %s in %s/moderators\n",
  2244.                     newsgroup, LIB);
  2245.             if (moderator)
  2246.                   fprintf(fp, "The moderator is probably %s\n", moderator);
  2247.             pclose(fp);
  2248.       }
  2249. }
  2250. !E!O!F!
  2251.  
  2252. cat > postnews/postnm4.c <<\!E!O!F!
  2253. /*
  2254.  * Pass 3:  generate the news and mail headers.
  2255.  *
  2256.  * Copyright 1984 by Kenneth Almquist.  All rights reserved.
  2257.  *     Permission is granted for anyone to use and distribute, but not
  2258.  *     sell, this program provided that this copyright notice is retained.
  2259.  */
  2260.  
  2261. #include "postnm.h"
  2262.  
  2263.  
  2264. /*
  2265.  * Write out a header.
  2266.  */
  2267. gennewshdr(fp)
  2268.       FILE *fp;
  2269.       {
  2270.       int didmod = 0;
  2271.  
  2272.       for (curhdr = hfirst ; curhdr ; curhdr = curhdr->next) {
  2273.             if (setexit())  continue;
  2274.             switch (curhdr->type) {
  2275.             case HTFROM:
  2276.             case HTREPLYTO:
  2277.                   newsaddr(curhdr);
  2278.                   break;
  2279.  
  2280.             case HTTO:
  2281.                   fputs("To: ", fp);
  2282.                   if (moderator && ! didmod)
  2283.                         fprintf(fp, "%s (The Moderator), ", moderator);
  2284.                   fputs(curhdr->body, fp);
  2285.                   didmod = 1;
  2286.                   break;
  2287.  
  2288.             default:
  2289.                   if (curhdr->fixed)
  2290.                         fprintf(fp, "%s: %s", curhdr->hdrname, curhdr->fixed);
  2291.                   else
  2292.                         fprintf(fp, "%s: %s", curhdr->hdrname, curhdr->body);
  2293.                   break;
  2294.             }
  2295.       }
  2296.       if (moderator && !didmod)
  2297.             fprintf(fp, "To: %s (The Moderator)\n", moderator);
  2298. }
  2299.  
  2300.  
  2301. genmailhdr(fp)
  2302.       FILE *fp;
  2303.       {
  2304.       for (curhdr = hfirst ; curhdr ; curhdr = curhdr->next) {
  2305.             if (setexit())  continue;
  2306.             switch (curhdr->type) {
  2307.             case HTREFERENCES:
  2308.                   if (hdrline[HTINREPLYTO] == NULL) {
  2309.                         register char *p, *q;
  2310.                         p = curhdr->fixed? curhdr->fixed : curhdr->body;
  2311.                         if ((p = rindex(p, '<')) != NULL
  2312.                          && (q = index(p, '>')) != NULL) {
  2313.                               fprintf(fp, "In-reply-to: USENET article %s\n",
  2314.                                     getval(p, q + 1, bfr));
  2315.                         }
  2316.                   }
  2317.                   break;
  2318.  
  2319.             case HTEXPIRES:
  2320.                   fputs(curhdr->text, fp);
  2321.                   break;
  2322.  
  2323.             default:
  2324.                   if (curhdr->fixed)
  2325.                         fprintf(fp, "%s: %s", curhdr->hdrname, curhdr->fixed);
  2326.                   else
  2327.                         fputs(curhdr->text, fp);
  2328.                   break;
  2329.             }
  2330.       }
  2331. }
  2332.  
  2333.  
  2334.  
  2335. newsaddr(hp)
  2336.       struct hline *hp;
  2337.       {
  2338.       char domain[NAMELEN], local[NAMELEN];
  2339.       extern char *begreal, *endreal;        /* real name */
  2340.       extern char *beglocal, *endlocal;        /* local part */
  2341.       extern char *begdomain, *enddomain;    /* domain part */
  2342.  
  2343.       hlselect(hp);
  2344.       skipws(" \t\n,");
  2345.       parseaddr();
  2346.       skipws(" \t\n,");
  2347.       if (*scanp != '\0') {
  2348.             synerr("only one address permitted on \"%s:\" line", hp->hdrname);
  2349.             return;
  2350.       }
  2351.       if (begdomain)
  2352.             getval(begdomain, enddomain, domain);
  2353.       else
  2354.             sprintf(domain, "%s%s", FULLSYSNAME, MYDOMAIN);
  2355.       getval(beglocal, endlocal, local);
  2356.       fprintf(newsfp, "%s: %s@%s", hp->hdrname, local, domain);
  2357.       if (begreal) {
  2358.             getval(begreal, endreal, bfr);
  2359.             fprintf(newsfp, " (%s)", bfr);
  2360.       }
  2361.       putc('\n', newsfp);
  2362.       if (equal(hp->hdrname, "From")
  2363.        && (index(local, '!') || index(domain, '!') || index(bfr, '!')))
  2364.             synerr("Exclamation points on From: line break inews");
  2365. }
  2366. !E!O!F!
  2367.  
  2368. echo Part 5 of 7 extracted.
  2369.  
  2370.  
  2371.