home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 31 / CDASC_31_1996_juillet_aout.iso / vrac / souper15.zip / SOURCE / NEWS.C < prev    next >
C/C++ Source or Header  |  1996-05-18  |  26KB  |  1,135 lines

  1. /* $Id: news.c 1.6 1996/05/18 21:06:26 cthuang Exp $
  2.  *
  3.  * Get news from NNTP server.
  4.  */
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include "socket.h"
  10. #include "nntp.h"
  11. #include "nntpcl.h"
  12. #include "souper.h"
  13.  
  14. /* article number range in the .newsrc file */
  15. typedef struct aRange {
  16.     struct aRange *next;    /* pointer to next */
  17.     ArticleNumber lo, hi;    /* article number range */
  18. } Range;
  19.  
  20. /* newsgroup entry in the .newsrc file */
  21. typedef struct aNewsrcGroup {
  22.     struct aNewsrcGroup *next;    /* pointer to next */
  23.     char *name;            /* newsgroup name */
  24.     Range *readList;        /* list of read article ranges */
  25.     char subscribed;        /* subscribed flag */
  26. } NewsrcGroup;
  27.  
  28. static NewsrcGroup *nrcList;    /* list of .newsrc entries. */
  29. static long byteCount;        /* current size of fetched news */
  30. static char killEnabled;    /* kill processing enabled for this group */
  31. static FILE *tmpF;        /* temporary file for article */
  32. static int groupCnt;        /* current group number */
  33.  
  34. #ifdef __WIN32__
  35. #include <conio.h>
  36. #endif
  37.  
  38. /* Read the article numbers from a .newsrc line. */
  39.  
  40. static Range *
  41. getReadList (FILE *nrcFile)
  42. {
  43.     static const char digits[] = "%[0123456789]";
  44.     Range *pLast, *rp, *head;
  45.     ArticleNumber lo, hi;
  46.     int c;
  47.     char *range;
  48.     char buf[20];
  49.  
  50.     /* Initialize subscription list */
  51.     pLast = NULL;
  52.     head = NULL;
  53.  
  54.     /* Expect [ \n] */
  55.     c = fgetc(nrcFile);
  56.  
  57.     while (c != '\n' && c != EOF) {
  58.     /* Expect number */
  59.     if (fscanf(nrcFile, digits, buf) != 1)
  60.         break;
  61.     lo = atol(buf);
  62.  
  63.     /* Get space for new list entry */
  64.     rp = (Range *)xmalloc(sizeof(Range));
  65.  
  66.     /* Expect [-,\n] */
  67.     c = fgetc(nrcFile);
  68.     if (c == '-') {
  69.         /* Is a range */
  70.         /* Expect number */
  71.         if (fscanf(nrcFile, digits, buf) != 1)
  72.         break;
  73.         hi = atol(buf);
  74.  
  75.         rp->lo = lo;
  76.         rp->hi = hi;
  77.  
  78.         if (lo != 1 || hi != 0) {
  79.         /* Reverse them in case they're backwards */
  80.         if (hi < lo) {
  81.             rp->lo = hi;
  82.             rp->hi = lo;
  83.         }
  84.         }
  85.  
  86.         /* Expect [,\n] */
  87.         c = fgetc(nrcFile);
  88.     } else {
  89.         /* Not a range */
  90.         rp->lo = rp->hi = lo;
  91.     }
  92.  
  93.     /* Check if range overlaps last one */
  94.     if (pLast != NULL && rp->lo <= pLast->hi + 1) {
  95.         /* Combine ranges */
  96.         if (rp->lo < pLast->lo) pLast->lo = rp->lo;
  97.         if (rp->hi > pLast->hi) pLast->hi = rp->hi;
  98.  
  99.         /* Free old one */
  100.         free(rp);
  101.     } else {
  102.         /* No overlap, update pointers */
  103.         if (pLast == NULL) {
  104.         head = rp;
  105.         } else {
  106.         pLast->next = rp;
  107.         }
  108.         rp->next = NULL;
  109.         pLast = rp;
  110.     }
  111.     }
  112.  
  113.     while (c != '\n' && c != EOF)
  114.     c = fgetc(nrcFile);
  115.  
  116.     return head;
  117. }
  118.  
  119. /* Read the .newsrc file and point nrcList to list of newsgroups entries.
  120.  * Return TRUE if successful.
  121.  */
  122. static int
  123. readNewsrc (void)
  124. {
  125.     FILE *nrcFile;
  126.     char group_name[BUFSIZ], ch;
  127.     NewsrcGroup *head, *np, *lnp;
  128.  
  129.     /* lnp points to last entry */
  130.     lnp = NULL;
  131.     head = NULL;
  132.  
  133.     /* Open it */
  134.     if ((nrcFile = fopen(newsrcFile, "r")) == NULL) {
  135.     fprintf(stderr, "%s: can't open %s\n", progname, newsrcFile);
  136.     return 0;
  137.     }
  138.  
  139.     /* Read newsgroup entry */
  140.     while (fscanf(nrcFile, "%[^:! \t\n]", group_name) == 1) {
  141.     if (group_name[0] == '\0')
  142.         break;
  143.  
  144.     /* Allocate a new entry */
  145.     np = (NewsrcGroup *)xmalloc(sizeof(NewsrcGroup));
  146.     ch = fgetc(nrcFile);
  147.     if (ch == '\n') {
  148.         /* The user didn't end the line with a colon. */
  149.         np->subscribed = 1;
  150.         np->readList = NULL;
  151.     } else {
  152.         /* Parse subscription list */
  153.         np->subscribed = (ch == ':');
  154.         np->readList = getReadList(nrcFile);
  155.     }
  156.     np->name = xstrdup(group_name);
  157.     np->next = NULL;
  158.  
  159.     /* Add to list */
  160.     if (lnp == NULL) {
  161.         head = np;
  162.     } else {
  163.         lnp->next = np;
  164.     }
  165.     lnp = np;
  166.     }
  167.  
  168.     fclose(nrcFile);
  169.     nrcList = head;
  170.     return 1;
  171. }
  172.  
  173. /* Write the article numbers for a .newsrc entry. */
  174.  
  175. static void
  176. putReadList (FILE *fd, Range *head)
  177. {
  178.     while (head != NULL) {
  179.     if (head->lo == head->hi)
  180.         fprintf(fd, "%ld", head->lo);
  181.     else
  182.         fprintf(fd, "%ld-%ld", head->lo, head->hi);
  183.     head = head->next;
  184.     if (head != NULL) fputc(',', fd);
  185.     }
  186.     fputc('\n', fd);
  187. }
  188.  
  189. /* Rewrite the updated .newsrc file. */
  190.  
  191. int
  192. writeNewsrc (void)
  193. {
  194.     char oldFile[FILENAME_MAX];
  195.     FILE *nrcFile;
  196.     NewsrcGroup *np;
  197.  
  198.     if (readOnly || !doNews || !nrcList) return 0;
  199.  
  200.     /* Back up old .newsrc file. */
  201.     sprintf(oldFile, "%s.old", newsrcFile);
  202.     remove(oldFile);
  203.     rename(newsrcFile, oldFile);
  204.  
  205.     if ((nrcFile = fopen(newsrcFile, "w")) == NULL) {
  206.     fprintf(stderr, "%s: can't write %s\n", progname, newsrcFile);
  207.     return 0;
  208.     }
  209.  
  210.     for (np = nrcList; np != NULL; np = np->next) {
  211.     fputs(np->name, nrcFile);
  212.     fputc(np->subscribed ? ':' : '!', nrcFile);
  213.     fputc(' ', nrcFile);
  214.     putReadList(nrcFile, np->readList);
  215.     }
  216.  
  217.     fclose(nrcFile);
  218.     return 1;
  219. }
  220.  
  221. /* Get first unread article number. */
  222.  
  223. static ArticleNumber
  224. firstUnread (Range *head, ArticleNumber lo)
  225. {
  226.     if (head == NULL)
  227.     return lo;
  228.     return head->hi + 1;
  229. }
  230.  
  231. /* Determine if the article number has been read */
  232.  
  233. static int
  234. isRead (ArticleNumber num, Range *head)
  235. {
  236.     /* Look through the list */
  237.     while (head != NULL) {
  238.     if (num < head->lo) return 0;
  239.     if (num >= head->lo && num <= head->hi) return 1;
  240.     head = head->next;
  241.     }
  242.     return 0;
  243. }
  244.  
  245. /* Mark article as read. */
  246.  
  247. static Range *
  248. markRead (ArticleNumber num, Range *head)
  249. {
  250.     Range *rp, *trp, *lrp;
  251.  
  252.     rp = head;
  253.  
  254.     /* If num is much lower than lowest range, or the list is
  255.        empty, we need new entry */
  256.     if (rp == NULL || num < rp->lo - 1) {
  257.     trp = (Range *)xmalloc(sizeof(Range));
  258.     trp->lo = trp->hi = num;
  259.     trp->next = rp;
  260.     return trp;
  261.     }
  262.  
  263.     /* lrp remembers last entry in case we need to add a new entry */
  264.     lrp = NULL;
  265.  
  266.     /* Find appropriate entry for this number */
  267.     while (rp != NULL) {
  268.     /* Have to squeeze one in before this one? */
  269.     if (num < rp->lo - 1) {
  270.         trp = (Range *)xmalloc(sizeof(Range));
  271.         trp->lo = trp->hi = num;
  272.         trp->next = rp;
  273.         lrp->next = trp;
  274.         return head;
  275.     }
  276.  
  277.     /* One less than entry's lo? */
  278.     if (num == rp->lo - 1) {
  279.         rp->lo = num;
  280.         return head;
  281.     }
  282.  
  283.     /* In middle of range, do nothing */
  284.     if (num >= rp->lo && num <= rp->hi) return head;
  285.  
  286.     /* One too high, must check if we merge with next entry */
  287.     if (num == rp->hi + 1) {
  288.         if (rp->next != NULL && num == rp->next->lo - 1) {
  289.         trp = rp->next;
  290.         rp->hi = trp->hi;
  291.         rp->next = trp->next;
  292.         free(trp);
  293.         return head;
  294.         } else {
  295.         /* No merge */
  296.         rp->hi = num;
  297.         return head;
  298.         }
  299.     }
  300.  
  301.     lrp = rp;
  302.     rp = rp->next;
  303.     }
  304.  
  305.     /* We flew off the end and need a new entry */
  306.     trp = (Range *)xmalloc(sizeof(Range));
  307.     trp->lo = trp->hi = num;
  308.     trp->next = NULL;
  309.     lrp->next = trp;
  310.  
  311.     return head;
  312. }
  313.  
  314. /* Sanity fixes to the read article number list */
  315.  
  316. static Range *
  317. fixReadList (NewsrcGroup *np, ArticleNumber lo, ArticleNumber hi)
  318. {
  319.     Range *head, *rp1, *rp2, *rp3;
  320.  
  321.     head = np->readList;
  322.     if (head != NULL) {
  323.     /* Go to last entry in list. */
  324.     for (rp1 = head; rp1->next != NULL; rp1 = rp1->next)
  325.         ;
  326.     if (rp1->hi > hi) {
  327.         /* The highest read article number is greater than the highest
  328.          * available article number.
  329.          */
  330. #if 0
  331.         printf("%s: Somebody reset %s -- assuming nothing read.\n",
  332.         progname, np->name);
  333. #endif
  334.  
  335.         /* Mark everything as read. */
  336.         head->lo = 1;
  337.         head->hi = hi;
  338.  
  339.         /* Free the rest */
  340.         rp2 = head->next;
  341.         while (rp2 != NULL) {
  342.         rp3 = rp2->next;
  343.         free(rp2);
  344.         rp2 = rp3;
  345.         }
  346.  
  347. #if 0
  348.         /* If lowest available article is 1, then leave read list empty,
  349.          * otherwise when group is reset, the first article would be
  350.          * skipped.
  351.          */
  352.         if (lo <= 1) {
  353.         free(head);
  354.         return NULL;
  355.         }
  356. #endif
  357.         head->next = NULL;
  358.         return head;
  359.     }
  360.  
  361.     /* Walk through the list and eliminate ranges lower than the lowest
  362.      * available article
  363.      */
  364.     rp1 = head;
  365.     while (rp1 != NULL) {
  366.         /* If the lowest available article falls within or is less than
  367.          * this range, all the rest of the ranges are unnecessary.
  368.          */
  369.         if (rp1->lo > lo || rp1->hi >= lo) {
  370.         rp1->lo = 1;
  371.         if (rp1->hi < lo) rp1->hi = lo - 1;
  372.  
  373.         /* Free the rest. */
  374.         rp2 = head;
  375.         while (rp2 != rp1) {
  376.             rp3 = rp2->next;
  377.             free(rp2);
  378.             rp2 = rp3;
  379.         }
  380.         return rp1;
  381.         }
  382.         rp1 = rp1->next;
  383.     }
  384.  
  385.     /* All the ranges are less than the lowest available article.
  386.      * Remove them.
  387.      */
  388.     while (head != NULL) {
  389.         rp3 = head->next;
  390.         free(head);
  391.         head = rp3;
  392.     }
  393.     }
  394.  
  395.     /* If lowest available article is 1, then leave read list empty,
  396.      * otherwise when a new group is started, the first article would
  397.      * be skipped.
  398.      */
  399.     if (lo <= 1)
  400.     return NULL;
  401.  
  402.     /* Make one new entry marking everything up to the lowest available
  403.      * article as read.
  404.      */
  405.     head = (Range *)xmalloc(sizeof(Range));
  406.     head->lo = 1;
  407.     head->hi = (lo > 0) ? (lo - 1) : 0;
  408.     head->next = NULL;
  409.     return head;
  410. }
  411.  
  412. /* Process an Xref line. */
  413.  
  414. static void
  415. processXref (char *s)
  416. {
  417.     char *c, *p, name[FILENAME_MAX];
  418.     ArticleNumber num;
  419.     NewsrcGroup *np;
  420.  
  421.     /* Skip the host field */
  422.     c = strtok(s, " \t");
  423.     if (c == NULL) return;
  424.  
  425.     /* Look through the rest of the fields */
  426.     while ((c = strtok(NULL, " \t")) != NULL) {
  427.     /* Replace : with space. */
  428.     if ((p = strchr(c, ':')) != NULL)
  429.         *p = ' ';
  430.  
  431.     if (sscanf(c, "%s %ld", name, &num) == 2) {
  432.         /* Find .newsrc entry for this group */
  433.         for (np = nrcList; np != NULL; np = np->next) {
  434.         if (stricmp(np->name, name) == 0) {
  435.             /* Mark as read */
  436.             np->readList = markRead(num, np->readList);
  437.             break;
  438.         }
  439.         }
  440.     }
  441.     }
  442. }
  443.  
  444. /* Fetch article from news server to temporary file.
  445.  * Return TRUE if the article was available.
  446.  */
  447. static int
  448. requestArticle (int socket, const char *artNum)
  449. {
  450.     char buf[BUFSIZ], *bufp;
  451.     char gotXref;
  452.  
  453.     /* Request article. */
  454.     SockPrintf(socket, "ARTICLE %s\r\n", artNum);
  455.     if (SockGets(socket, buf, sizeof(buf)) < 0)
  456.     return 0;
  457.  
  458.     if (buf[0] == CHAR_FATAL) {    /* Fatal error */
  459.     fprintf(stderr, "%s\n", buf);
  460.     exit(EXIT_FAILURE);
  461.     }
  462.  
  463.     if (buf[0] != CHAR_OK) {
  464. #ifdef DEBUG
  465.     printf("%s: article number %s is unavailable\n", progname, artNum);
  466.     printf("%s: %s\n", progname, buf);
  467. #endif
  468.     return 0;
  469.     }
  470.  
  471.     gotXref = 0;
  472.  
  473.     /* Get lines of article head. */
  474.     while (SockGets(socket, buf, sizeof(buf)) == 0) {
  475.     bufp = buf;
  476.     if (buf[0] == '.') {
  477.         if (*(++bufp) == '\0')
  478.         break;
  479.     }
  480.  
  481.     fputs(bufp, tmpF);
  482.     fputc('\n', tmpF);
  483.     if (*bufp == '\0')
  484.         break;
  485.  
  486.     if (doXref && !gotXref && strnicmp(bufp, "Xref: ", 6) == 0) {
  487.         processXref(bufp+6);
  488.         gotXref = 1;
  489.     }
  490.     }
  491.  
  492.     /* Retrieve article body. */
  493.     while (SockGets(socket, buf, sizeof(buf)) == 0) {
  494.     bufp = buf;
  495.     if (buf[0] == '.') {
  496.         if (*(++bufp) == '\0')
  497.         break;
  498.     }
  499.  
  500.     fputs(bufp, tmpF);
  501.     fputc('\n', tmpF);
  502.     }
  503.  
  504.     return 1;
  505. }
  506.  
  507. /* Copy article from temporary file to .MSG file.
  508.  * Return TRUE if article was copied.
  509.  */
  510. static int
  511. writeArticle (FILE *msgF)
  512. {
  513.     long artSize;
  514.     unsigned toRead, wasRead;
  515.     char buf[BUFSIZ];
  516.  
  517.     /* Get article size. */
  518.     artSize = ftell(tmpF);
  519.     if (artSize == 0)
  520.     return 0;    /* Skip empty articles */
  521.  
  522.     /* Update packet size.  Include size of rnews line. */
  523.     byteCount += artSize + 14;
  524.  
  525.     /* Write "rnews" line */
  526.     fprintf(msgF, "#! rnews %ld\n", artSize);
  527.  
  528.     /* Copy article body. */
  529.     fseek(tmpF, 0L, SEEK_SET);
  530.     while (artSize > 0) {
  531.     toRead = (artSize < sizeof(buf)) ? (unsigned)artSize : sizeof(buf);
  532.     wasRead = fread(buf, sizeof(char), toRead, tmpF);
  533.     if (wasRead == 0) {
  534.         perror("read article");
  535.         return 0;
  536.     }
  537.     if (fwrite(buf, sizeof(char), wasRead, msgF) != wasRead) {
  538.         perror("write article");
  539.         return 0;
  540.     }
  541.     artSize -= wasRead;
  542.     }
  543.  
  544.     return 1;
  545. }
  546.  
  547. /* Get the article and write it to the file stream.
  548.  * Return TRUE if the article was available.
  549.  */
  550. static int
  551. getArticle (int socket, ArticleNumber artNum, FILE *msgF)
  552. {
  553.     char buf[BUFSIZ], *bufp;
  554.     char gotXref, killed;
  555.  
  556.     /* Get article to temporary file. */
  557.     fseek(tmpF, 0L, SEEK_SET);
  558.  
  559.     if (killEnabled) {
  560.     /* Request article head. */
  561.     SockPrintf(socket, "HEAD %ld\r\n", artNum);
  562.     if (SockGets(socket, buf, sizeof(buf)) < 0) {
  563.         return 0;
  564.     }
  565.  
  566.     if (buf[0] == CHAR_FATAL) {    /* Fatal error */
  567.         fprintf(stderr, "%s\n", buf);
  568.         exit(EXIT_FAILURE);
  569.     }
  570.  
  571.     if (buf[0] != CHAR_OK) {
  572. #ifdef DEBUG
  573.         printf("%s: article number %ld is unavailable\n", progname, artNum);
  574.         printf("%s: %s\n", progname, buf);
  575. #endif
  576.         return 0;
  577.     }
  578.  
  579.     killed = 0;
  580.     gotXref = 0;
  581.  
  582.     /* Get lines of article head. */
  583.     while (SockGets(socket, buf, sizeof(buf)) == 0) {
  584.         bufp = buf;
  585.         if (buf[0] == '.') {
  586.         if (*(++bufp) == '\0')
  587.             break;
  588.         }
  589.  
  590.         fputs(bufp, tmpF);
  591.         fputc('\n', tmpF);
  592.  
  593.         if (killEnabled && !killed)
  594.         if (killLine(bufp)) {
  595.             printf("%s: killed %s\n", progname, bufp);
  596.             killed = 1;
  597.         }
  598.  
  599.         if (doXref && !gotXref && strnicmp(bufp, "Xref: ", 6) == 0) {
  600.         processXref(bufp+6);
  601.         gotXref = 1;
  602.         }
  603.     }
  604.  
  605.     /* Don't process anymore if article was killed. */
  606.     if (killed) {
  607.         return 1;
  608.     }
  609.  
  610.     /* Put empty line separating head from body. */
  611.     fputc('\n', tmpF);
  612.  
  613.     /* Retrieve article body. */
  614.     if (!nntpArticle(socket, "BODY", artNum, tmpF))
  615.         return 0;
  616.  
  617.     } else {
  618.     sprintf(buf, "%ld", artNum);
  619.         if (!requestArticle(socket, buf))
  620.         return 0;
  621.     }
  622.  
  623.     writeArticle(msgF);
  624.     return 1;
  625. }
  626.  
  627. /* Get articles from the newsgroup.
  628.  * Return TRUE if successful.
  629.  */
  630. static int
  631. getGroup (int socket, NewsrcGroup *np, int areaNum)
  632. {
  633.     ArticleNumber lo, hi, first, artNum, nextNum, j, n;
  634.     int percent, lastPercent;
  635.     char gotArt;
  636.     FILE *msgf;
  637.  
  638.     /* Select group name from news server. */
  639.     if (!nntpGroup(socket, np->name, &lo, &hi)) {
  640.     np->subscribed = 0;    /* Unsubscribe from invalid group. */
  641.     return 0;
  642.     }
  643.  
  644.     killEnabled = killGroup(np->name);
  645.     msgf = openMsgFile(areaNum, np->name, "un");
  646.  
  647.     /* Fix the read article number list. */
  648.     np->readList = fixReadList(np, lo, hi);
  649.  
  650.     first = firstUnread(np->readList, lo);
  651.     n = hi - first + 1;
  652.     if (n < 0) n = 0;
  653.     printf("%s: %4d unread article%c in %s\n", progname, n,
  654.     (n == 1) ? ' ' : 's', np->name);
  655.     lastPercent = 0;
  656.  
  657.     /* Look through unread articles */
  658.     gotArt = 0;
  659.     artNum = first;
  660.     while (artNum <= hi) {
  661. #ifdef DEBUG
  662.     printf("%d\r", artNum);
  663. #else
  664.     percent = ((artNum - first + 1) * 100) / n;
  665.     if (percent != lastPercent) {
  666.         printf("%d%%\r", percent);
  667.         fflush(stdout);
  668.         lastPercent = percent;
  669.     }
  670. #endif
  671.  
  672.     if (isRead(artNum, np->readList)) {
  673.         ++artNum;
  674.     } else {
  675.         /* Fetch the article */
  676.         if (getArticle(socket, artNum, msgf)) {
  677.         /* Mark as read */
  678.         np->readList = markRead(artNum++, np->readList);
  679.         gotArt = 1;
  680.         } else if (gotArt) {
  681.         /* Article not available.  Look for next available article. */
  682.         nextNum = hi + 1;
  683.             while (nntpNext(socket, &j)) {
  684.             if (j > artNum) {
  685.             nextNum = j;
  686.             break;
  687.             }
  688. #ifdef DEBUG
  689.             printf("%d\r", j);
  690. #endif
  691.         }
  692.  
  693.         /* Mark all article numbers to next article as read. */
  694.         while (artNum < nextNum) {
  695.             np->readList = markRead(artNum++, np->readList);
  696.         }
  697.         } else {
  698.         np->readList = markRead(artNum++, np->readList);
  699.         }
  700.  
  701.         /* Check if too many blocks already */
  702.         if (maxBytes > 0 && byteCount >= maxBytes) {
  703.         printf("%s: maximum packet size exceeded\n", progname);
  704.         break;
  705.         }
  706.  
  707. #ifdef __WIN32__
  708.             {
  709.                 int skip = 0;
  710.                 /* Check if they hit ^S (Skip) */
  711.                 while (kbhit()) {
  712.                     if (getch() == 0x13) skip = 1;
  713.                 }
  714.                 if (skip == 1) break;
  715.             }
  716. #endif      
  717.     }
  718.     }
  719.  
  720.     closeMsgFile();
  721.     return 1;
  722. }
  723.  
  724. /* Send mail listing new newsgroups. */
  725.  
  726. static void
  727. getNewGroups (int socket)
  728. {
  729.     static char mailMsgFile[] = "0000000.MSG";
  730.     char date[80], path[FILENAME_MAX], buf[BUFSIZ], *p;
  731.     char openedMail, gotMail;
  732.     FILE *dateF, *msgF;
  733.     time_t now;
  734.     NewsrcGroup *np, *last;
  735.  
  736.     /* Get current date/time from news server. */
  737.     if (!nntpDate(socket, buf)) {
  738.     /* News server doesn't support the DATE extension.
  739.      * Get time from the local system.
  740.      */
  741.     now = time(NULL);
  742.     strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", gmtime(&now));
  743.     }
  744.  
  745.     /* Get last date/time we checked for new newsgroups. */
  746. #ifdef __OS2__
  747.     sprintf(path, "%s/newstime", homeDir);
  748. #else
  749.     sprintf(path, "%s/.newstime", homeDir);
  750. #endif
  751.     if ((dateF = fopen(path, "r+")) == NULL) {
  752.     /* This is probably the first we checked for new newsgroups.
  753.      * Just save the current date/time and return.
  754.      */
  755.     if ((dateF = fopen(path, "w")) != NULL) {
  756.         fputs(buf, dateF);
  757.         fputc('\n', dateF);
  758.         fclose(dateF);
  759.     }
  760.     return;
  761.     }
  762.     fgets(date, sizeof(date), dateF);
  763.  
  764.     /* Save current date/time. */
  765.     fseek(dateF, 0L, SEEK_SET);
  766.     fputs(buf, dateF);
  767.     fputc('\n', dateF);
  768.     fclose(dateF);
  769.  
  770.     /* Request new newsgroups. */
  771.     SockPrintf(socket, "NEWGROUPS %-6.6s %-6.6s GMT\r\n", date+2, date+8);
  772.     if (SockGets(socket, buf, sizeof(buf)) < 0) {
  773.     return;
  774.     }
  775.  
  776.     if (buf[0] == CHAR_FATAL) {    /* Fatal error */
  777.     fprintf(stderr, "%s\n", buf);
  778.     exit(EXIT_FAILURE);
  779.     }
  780.  
  781.     if (buf[0] != CHAR_OK) {
  782.     return;
  783.     }
  784.  
  785.     openedMail = 0;
  786.     while (SockGets(socket, buf, sizeof(buf)) == 0) {
  787.     if (buf[0] == '.')
  788.         break;
  789.     if ((p = strchr(buf, ' ')) != NULL)
  790.         *p = '\0';
  791.  
  792.     /* Scan to see if we know about this one. */
  793.     for (last = NULL, np = nrcList; np != NULL; last = np, np = np->next)
  794.         if (strcmp(buf, np->name) == 0)
  795.         break;
  796.     if (np != NULL)
  797.         continue;
  798.  
  799.     /* Allocate a new entry */
  800.     np = (NewsrcGroup *)xmalloc(sizeof(NewsrcGroup));
  801.     np->subscribed = 0;
  802.     np->readList = NULL;
  803.     np->name = xstrdup(buf);
  804.     np->next = NULL;
  805.     last->next = np;
  806.  
  807.     if (!openedMail) {
  808.         char msgDate[80];
  809.  
  810.         /* Open message file. */
  811.         if ((msgF = fopen(mailMsgFile, "r+b")) == NULL) {
  812.         if ((msgF = openMsgFile(0, "Email", "mn")) == NULL)
  813.             return;
  814.         gotMail = 0;
  815.         } else {
  816.         fseek(msgF, 0L, SEEK_END);
  817.         gotMail = 1;
  818.         }
  819.  
  820.         /* Write message header. */
  821.         now = time(NULL);
  822.         fprintf(msgF, "From POPmail %s", ctime(&now));
  823.  
  824.         strftime(msgDate, sizeof(msgDate), "Date: %a, %d %b %Y %T %Z\n",
  825.              localtime(&now));
  826.         fputs(msgDate, msgF);
  827.  
  828.         fputs("To: SouperUser\n", msgF);
  829.         fputs("From: Souper\n", msgF);
  830.         fprintf(msgF,
  831.         "Subject: New newsgroups since %-4.4s-%-2.2s-%-2.2s\n\n",
  832.         (date), (date+4), (date+6));
  833.  
  834.         openedMail = 1;
  835.     }
  836.  
  837.     fputs(buf, msgF);
  838.     fputc('\n', msgF);
  839.     }
  840.  
  841.     if (openedMail) {
  842.     if (gotMail)
  843.         fclose(msgF);
  844.     else
  845.         closeMsgFile();
  846.     }
  847. }
  848.  
  849. static NewsrcGroup *
  850. findGroupByName (const char *name)
  851. {
  852.     NewsrcGroup *np;
  853.  
  854.     for (np = nrcList; np != NULL; np = np->next) {
  855.     if (stricmp(np->name, name) == 0)
  856.         return np;
  857.     }
  858.     return NULL;
  859. }
  860.  
  861. static void
  862. processSendme (int socket, FILE *cmdF)
  863. {
  864.     NewsrcGroup *np;
  865.     FILE *msgF;
  866.     ArticleNumber lo, hi;
  867.     int c;
  868.     char buf[BUFSIZ];
  869.         
  870.     /* Read newsgroup name. */
  871.     if (fscanf(cmdF, "%s", buf) != 1) {
  872.     fgets(buf, sizeof(buf), cmdF);
  873.     return;
  874.     }
  875.  
  876.     np = findGroupByName(buf);
  877.     if (np == NULL) {
  878.     fprintf(stderr, "%s: %s not in newsrc\n", progname, buf);
  879.     fgets(buf, sizeof(buf), cmdF);
  880.     return;
  881.     }
  882.  
  883.     /* Select group name from news server. */
  884.     if (!nntpGroup(socket, np->name, &lo, &hi)) {
  885.     fgets(buf, sizeof(buf), cmdF);
  886.     return;
  887.     }
  888.  
  889.     msgF = openMsgFile(++groupCnt, np->name, "un");
  890.  
  891.     c = fgetc(cmdF);
  892.     while (c != EOF && c != '\r' && c != '\n') {
  893.     if (fscanf(cmdF, "%[^ \t\r\n]", buf) != 1) {
  894.         closeMsgFile();
  895.         fgets(buf, sizeof(buf), cmdF);
  896.         return;
  897.     }
  898.  
  899.     /* Get article to temporary file. */
  900.     fseek(tmpF, 0L, SEEK_SET);
  901.     if (requestArticle(socket, buf)) {
  902.         writeArticle(msgF);
  903.     } else {
  904.         printf("%s: article not available: %s: %s\n", progname, np->name,
  905.            buf);
  906.     }
  907.  
  908.     c = fgetc(cmdF);
  909.     }
  910.  
  911.     closeMsgFile();
  912. }
  913.  
  914. /* Process command file containing sendme commands.
  915. */
  916. static void
  917. processCommands (int socket, FILE *cmdF)
  918. {
  919.     char buf[BUFSIZ];
  920.  
  921.     while (fscanf(cmdF, "%s", buf) == 1) {
  922.     if (stricmp(buf, "sendme") == 0)
  923.         processSendme(socket, cmdF);
  924.     else
  925.         fgets(buf, sizeof(buf), cmdF);
  926.     }
  927. }
  928.  
  929. /* If a COMMANDS file exists in the current directory, fetch the articles
  930.  * specified by the sendme commands in the file, otherwise fetch unread
  931.  * articles from newsgroups listed in the newsrc file.
  932.  */
  933. int
  934. getNews (void)
  935. {
  936.     static const char cmdFile[] = "COMMANDS";
  937.     FILE *cmdF;
  938.     NewsrcGroup *np;
  939.     int socket;
  940.  
  941.     /* Read .newsrc file */
  942.     if (!readNewsrc()) return 0;
  943.  
  944.     /* Read kill file. */
  945.     readKillFile();
  946.  
  947.     /* Open NNTP connection. */
  948.     if ((socket = nntpConnect()) < 0)
  949.     return 0;
  950.  
  951.     /* Check for new newsgroups. */
  952.     if (doNewGroups)
  953.     getNewGroups(socket);
  954.  
  955.     tmpF = tmpfile();
  956.     byteCount = 0;
  957.     groupCnt = 0;
  958.  
  959.     if ((cmdF = fopen(cmdFile, "rb")) != NULL) {
  960.     processCommands(socket, cmdF);
  961.     fclose(cmdF);
  962.     remove(cmdFile);
  963.     } else {
  964.     /* For each newsgroup in .newsrc file */
  965.     for (np = nrcList; np != NULL; np = np->next) {
  966.         if (np->subscribed) {
  967.         getGroup(socket, np, ++groupCnt);
  968.         if (maxBytes > 0 && byteCount >= maxBytes)
  969.             break;
  970.         }
  971.     }
  972.     }
  973.  
  974.     fclose(tmpF);
  975.     nntpClose(socket);
  976.     writeNewsrc();
  977.     return 1;
  978. }
  979.  
  980. /* Return next field in record. */
  981.  
  982. static char *
  983. nextField (char **ppCur)
  984. {
  985.     char *pEnd;
  986.     char *pStart = *ppCur;
  987.  
  988.     if ((pEnd = strchr(pStart, '\t')) != NULL) {
  989.     *pEnd++ = '\0';
  990.     *ppCur = pEnd;
  991.     }
  992.     return pStart;
  993. }
  994.  
  995. /* Create summary of articles in the newsgroup.
  996.  * Return TRUE if successful.
  997.  */
  998. static int
  999. sumGroup (int socket, NewsrcGroup *np, int areaNum)
  1000. {
  1001.     ArticleNumber lo, hi, first, n, artNum;
  1002.     FILE *idxF;
  1003.     char buf[2048], *cur, *s;
  1004.  
  1005.     /* Select group name from news server. */
  1006.     if (!nntpGroup(socket, np->name, &lo, &hi))
  1007.     return 0;
  1008.  
  1009.     /* Fix up the read article number list */
  1010.     np->readList = fixReadList(np, lo, hi);
  1011.     first = firstUnread(np->readList, lo);
  1012.  
  1013.     n = hi - first + 1;
  1014.     if (n < 0) n = 0;
  1015.     printf("%s: %4d unread article%c in %s\n", progname, n,
  1016.     (n == 1) ? ' ' : 's', np->name);
  1017.     if (first > hi)
  1018.     return 0;
  1019.  
  1020.     /* Request overview. */
  1021.     if (!nntpXover(socket, first, hi))
  1022.     return 0;
  1023.     idxF = openIdxFile(areaNum, np->name, "ic");
  1024.  
  1025.     while (SockGets(socket, buf, sizeof(buf)) == 0) {
  1026.     if (buf[0] == '.')
  1027.         break;
  1028.     cur = buf;
  1029.  
  1030.     artNum = atol(nextField(&cur));
  1031.     np->readList = markRead(artNum, np->readList);
  1032.  
  1033.     fputc('\t', idxF);            /* offset */
  1034.     s = nextField(&cur);
  1035.     fputs(s, idxF); fputc('\t', idxF);    /* Subject */
  1036.     s = nextField(&cur);
  1037.     fputs(s, idxF); fputc('\t', idxF);    /* From */
  1038.     s = nextField(&cur);
  1039.     fputs(s, idxF); fputc('\t', idxF);    /* Date */
  1040.     s = nextField(&cur);
  1041.     fputs(s, idxF); fputc('\t', idxF);    /* Message-ID */
  1042.     s = nextField(&cur);
  1043.     fputs(s, idxF); fputc('\t', idxF);    /* References */
  1044.     s = nextField(&cur);
  1045.     fputc('0', idxF); fputc('\t', idxF);    /* bytes */
  1046.     s = nextField(&cur);
  1047.     fputs(s, idxF); fputc('\t', idxF);    /* lines */
  1048.     fprintf(idxF, "%ld\n", artNum);        /* selector */
  1049.     }
  1050.  
  1051.     closeIdxFile();
  1052.     return 1;
  1053. }
  1054.  
  1055. /* Create news summary. */
  1056.  
  1057. int
  1058. sumNews (void)
  1059. {
  1060.     NewsrcGroup *np;
  1061.     int socket;
  1062.  
  1063.     /* Read .newsrc file */
  1064.     if (!readNewsrc()) return 0;
  1065.  
  1066.     /* Open NNTP connection. */
  1067.     if ((socket = nntpConnect()) < 0) return 0;
  1068.  
  1069.     groupCnt = 0;
  1070.  
  1071.     /* For each newsgroup in .newsrc file */
  1072.     for (np = nrcList; np != NULL; np = np->next) {
  1073.     if (np->subscribed) {
  1074.         sumGroup(socket, np, ++groupCnt);
  1075.     }
  1076.     }
  1077.  
  1078.     nntpClose(socket);
  1079.     writeNewsrc();
  1080.     return 1;
  1081. }
  1082.  
  1083. /* Catch up in subscribed newsgroups. */
  1084.  
  1085. int
  1086. catchupNews (int numKeep)
  1087. {
  1088.     NewsrcGroup *np;
  1089.     Range *head, *rp, *pNext;
  1090.     int socket;
  1091.     ArticleNumber lo, hi;
  1092.  
  1093.     /* Read .newsrc file */
  1094.     if (!readNewsrc()) return 0;
  1095.  
  1096.     /* Open NNTP connection. */
  1097.     if ((socket = nntpConnect()) < 0) return 0;
  1098.  
  1099.     /* For each newsgroup in .newsrc file */
  1100.     for (np = nrcList; np != NULL; np = np->next) {
  1101.     if (np->subscribed) {
  1102.         /* select group name from news server */
  1103.         if (nntpGroup(socket, np->name, &lo, &hi)) {
  1104.         hi -= numKeep;
  1105.         lo = firstUnread(np->readList, lo);
  1106.         if (hi < lo)
  1107.             hi = (lo > 0) ? (lo - 1) : 0;
  1108.  
  1109.         /* Mark article numbers 1 to hi as read. */
  1110.         head = np->readList;
  1111.         if (head == NULL) {
  1112.             head = (Range *)xmalloc(sizeof(Range));
  1113.             head->next = NULL;
  1114.             np->readList = head;
  1115.         }
  1116.         head->lo = 1;
  1117.         head->hi = hi;
  1118.         rp = head->next;
  1119.         head->next = NULL;
  1120.  
  1121.         /* Free rest of list */
  1122.         while (rp != NULL) {
  1123.             pNext = rp->next;
  1124.             free(rp);
  1125.             rp = pNext;
  1126.         }
  1127.         }
  1128.     }
  1129.     }
  1130.  
  1131.     nntpClose(socket);
  1132.     writeNewsrc();
  1133.     return 1;
  1134. }
  1135.