home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / internet / tcpip / src205 / TCPIP_Src / NNTP / c / nntpcli next >
Encoding:
Text File  |  1995-04-17  |  42.6 KB  |  1,933 lines

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. #include <time.h>
  5. #include <ctype.h>
  6. #include <string.h>
  7.  
  8. #include "kernel.h"
  9. #include "global.h"
  10. #include "timer.h"
  11. #include "cmdparse.h"
  12. #include "netuser.h"
  13. #include "nntp.h"
  14. #include "smtp.h"
  15. #include "domain.h"
  16. #include "misc.h"
  17. #include "tcp.h"
  18. #include "mbuf.h"
  19. #include "arc.h"
  20. #include "Terminal.h"
  21. #include "vterm.h"
  22.  
  23. #include "alarm.h"
  24. #include "flex.h"
  25. #include "os.h"
  26. #include "swis.h"
  27. #include "visdelay.h"
  28.  
  29. #include "Reader.h"
  30.  
  31. #include "idhist.h"
  32.  
  33. #define ID_READERS 1    /* An ID added from ReaderS files */
  34. #define ID_PENDING 2    /* An ID requested, but not yet received */
  35.  
  36. typedef struct spdstat_str
  37. {
  38.   int start_t;
  39.   int last_t;
  40.   int count;
  41.   int speed;
  42.   int samples;
  43. } spdstat_str;
  44.  
  45. void spdstat_start(spdstat_str *sp);
  46. void spdstat_restart(spdstat_str *sp);
  47. int spdstat_update(spdstat_str *sp, int count);
  48. int spdstat_end(spdstat_str *sp);
  49.  
  50. #define NNTPMAXLEN      510
  51. #define BATCH_MAX        16
  52.  
  53. #define ID_LEN 28
  54.  
  55. #define NN_CMD             0
  56. #define NN_DAT          1
  57. #define NN_RDY          2
  58.  
  59. #define NN_OPEN          1
  60. #define NN_NEWG          2
  61. #define NN_NEWN          3
  62. #define NN_GETN          4
  63. #define NN_BYNUM         5
  64. #define NN_SELG         6
  65. #define NN_GNUM         7
  66. #define NN_NEXT_STAGE          8
  67. #define NN_POST         9
  68. #define NN_PREARTSEND_STAGE 10
  69. #define NN_POSTARTSEND_STAGE 11
  70. #define NN_QUIT          12
  71.  
  72. #define NN_ERROR  13
  73. #define NN_RETRY 14
  74.  
  75. #ifdef BUG
  76. typedef struct {
  77.   int  front;                        /* Most recently requested ID */
  78.   int  back;                         /* Least recently requested ID */
  79.   char ids[BATCH_MAX][NNTPMAXLEN+1]; /* List of ID strings */
  80. } nntp_idlist;
  81. #endif
  82.  
  83. struct nntpservers
  84. {
  85.   struct timer nntpcli_t;
  86.   char *name;
  87.   char *abbr;
  88.   char *temp;
  89.   int lowtime, hightime;  /* for connect window */
  90.   struct nntpservers *next;
  91.   struct tcb *tcb;        /* tcp task control buffer */
  92.   int32   ipdest;         /* address of forwarding system */
  93.   char    state;          /* state machine placeholder */
  94.   char    stage;          /* another state machine placeholder */
  95.   char    buf[NNTPMAXLEN];/* Input buffer */
  96.   char    cntovr;      /* Flag to determin of dot esc needs to be done */
  97.   int     cnt;            /* Length of input buffer */
  98.   FILE    *input1;
  99.   FILE    *input0;
  100.   FILE    *output;
  101.   FILE    *newsfile;
  102.   FILE    *id_file;
  103.   idhistory history;
  104.   int     histsaved;
  105.   int     batch;
  106.   time_t  thisfeed;       /* Time in GMT that this news feed started */
  107.   time_t  lastfeed;       /* Time in GMT that last news feed started */
  108.   long    file_len;
  109.   int     msg_tot;
  110.   char    goodrcpt;       /* are any of the rcpt ok */
  111.   char    cts;            /* clear to send state indication */
  112.   int     rcpts;          /* number of unacked rcpt commands */
  113.   struct  list    *errlog;
  114.   int     lastread;
  115.   struct  directory *topost;
  116.   long    rnewspos;
  117.   long    pollperiod;
  118.   int     retries;
  119.   Terminal *window;
  120.   int ids_count;
  121.   spdstat_str ids_spd;
  122.   int art_count;
  123.   spdstat_str art_spd;
  124. #ifdef BUG
  125.   nntp_idlist fetching;   /* List of ID's requested but not received yet */
  126. #endif
  127.   int     reqbuf[BATCH_MAX];  /* Effectively a circular buffer - see have_we() and we_have() */
  128.   int     reqptr;
  129.   int     rcvptr;
  130. };
  131.  
  132. struct directory
  133. {
  134.   struct directory *next;
  135.   char *name;
  136. };
  137.  
  138. #define NULLNNTP (struct nntpservers *)NULL
  139.  
  140. struct nntpservers *Nntpservers = NULLNNTP;
  141.  
  142. static void nntptick(void *);
  143. static int doadds(int, char **);
  144. static int doretry(int, char **);
  145. static int dobatch(int, char **);
  146. static int dodirect(int, char **);
  147. static int dodrops(int, char **);
  148. static int dodups(int, char **);
  149. static int dofudge(int, char **);
  150. static int dokicks(int, char **);
  151. static int dolists(int, char **);
  152. static int donewgrps(int argc, char **argv);
  153. static int dostop(int, char **);
  154. static int dotrim(int, char **);
  155. static int donntptrace(int, char **);
  156. static int dofudge(int argc, char *argv[]);
  157.  
  158. static void quit(struct nntpservers *, BOOL);
  159. static void nntp_rec(struct tcb *, int16);
  160. static void nntp_cts(struct tcb *, int16);
  161. static void nntp_state(struct tcb *, char, char);
  162. static void sendit(struct nntpservers *, char *, ...);
  163.  
  164. static int check_files(char *abbr);
  165. static int fillgroupbuf(FILE *fp, char *buf, int len);
  166.  
  167. static int ReaderS_Hist(struct nntpservers *cb);
  168. static void received_article(struct nntpservers *cb);
  169. static BOOL requested_article(char *line, struct nntpservers *cb);
  170. #ifdef BUG
  171. static void id_retrieved(struct nntpservers *cb);
  172. #endif
  173. static int history_init(struct nntpservers *cb);
  174. static int history_close(struct nntpservers *cb, int normal);
  175. static int history_open(struct nntpservers *cb);
  176. static void nntp_shutdown(void);
  177.  
  178. extern int lport;                     /* local port placeholder */
  179.  
  180. static int   nntptrace = 0;                  /* used for trace level */
  181. static int   newgroups = 1;   /* Update new groups listing */
  182.  
  183. static char quitcmd[] = "QUIT\r\n";
  184.  
  185. static char *duptab[] = { "auto", "ReaderS", "history" };
  186.  
  187. #define DUP_AUTO      0
  188. #define DUP_READERS   1
  189. #define DUP_HIST      2
  190.  
  191. static int dup_method = DUP_AUTO;
  192. static int trim_age   = 72;
  193. static int fudge      = 0;
  194. static int batch      = 4;
  195. static BOOL direct    = FALSE;
  196. static int nntp_retryt = 30;
  197. static int nntp_retrymax = 5;
  198.  
  199. struct cmds nntpcmds[] = {
  200.   "addserver",  doadds,      4, "nntp addserver <name> <time> <abbr>", NULLCHAR,
  201.   "batch",      dobatch,     1, "nntp batch <n>",                      NULLCHAR,
  202.   "direct",     dodirect,    1, "nntp direct <on|off>",                NULLCHAR,
  203.   "dropserver", dodrops,     2, "nntp dropserver <name|abbr>",         NULLCHAR,
  204.   "duplicates", dodups,      1, "nntp duplicates [<auto|readers|history>]",         NULLCHAR,
  205.   "fudge",      dofudge,     1, "nntp fudge [<time>]",                   NULLCHAR,
  206.   "kick",       dokicks,     2, "nntp kick <name|abbr>",               NULLCHAR,
  207.   "listserver", dolists,     0, NULLCHAR,                              NULLCHAR,
  208.   "newgroups",  donewgrps,   1, "nntp newsgroups [y|n]",               NULLCHAR,
  209.   "retry",      doretry,     0, "nntp retry <delay> <count>",          NULLCHAR,
  210.   "stop",       dostop,      0, "nntp stop <name|abbr>",               NULLCHAR,
  211.   "trim",       dotrim,      0, "nntp trim <hours>",                   NULLCHAR,
  212.   "trace",      donntptrace, 0, NULLCHAR,                              NULLCHAR,
  213.   NULLCHAR,
  214. };
  215.  
  216. int donntp(int argc, char **argv)
  217. {
  218.   return subcmd(nntpcmds,argc,argv);
  219. }
  220.  
  221. static int donntptrace(int argc, char **argv)
  222. {
  223.   if (argc < 2)
  224.     cwprintf(NULL, "%d\r\n", nntptrace);
  225.   else
  226.     nntptrace = atoi(argv[1]);
  227.   return 0;
  228. }
  229.  
  230. static int doretry(int argc, char **argv)
  231. {
  232.   if (argc < 2)
  233.   {
  234.     if (nntp_retryt)
  235.       cwprintf(NULL, "NNTP delay %d, retry %d\r\n", nntp_retryt, nntp_retrymax);
  236.     else
  237.       cwprintf(NULL, "NNTP auto retry is off\r\n");
  238.   }
  239.   else
  240.   {
  241.     if (argc>=2)
  242.       nntp_retryt = atoi(argv[1]);
  243.     if (argc>2)
  244.       nntp_retrymax = atoi(argv[2]);
  245.   }
  246.  
  247.   if (nntp_retryt==0 || nntp_retrymax==0)
  248.   {
  249.     nntp_retryt = 0;
  250.     nntp_retrymax = 0;
  251.   }
  252.  
  253.   return 0;
  254. }
  255.  
  256. static int donewgrps(int argc, char **argv)
  257. {
  258.   if (argc < 2)
  259.   {
  260.     cwprintf(NULL, "NNTP - new groups list update %s\n",(newgroups)?"on":"off");
  261.   }
  262.   else
  263.   {
  264.     if (*argv[1]=='y' || *argv[1]=='Y')
  265.       newgroups = 1;
  266.     else if (*argv[1]=='n' || *argv[1]=='N')
  267.       newgroups = 0;
  268.     else
  269.       return 1;
  270.   }
  271.   return 0;
  272. }
  273.  
  274. static int dofudge(int argc, char *argv[])
  275. {
  276.   if (argc < 2)
  277.   {
  278.     cwprintf(NULL, "NNTP - Fudge factor %d\n", fudge);
  279.   }
  280.   else
  281.   {
  282.     fudge = abs(atoi(argv[1]));
  283.   }
  284.   return 0;
  285. }
  286.  
  287. static int dobatch(int argc, char *argv[])
  288. {
  289.   if (argc < 2)
  290.   {
  291.     cwprintf(NULL, "NNTP - Batch %d\n", batch);
  292.   }
  293.   else if (atoi(argv[1]) > 0 && atoi(argv[1]) <= BATCH_MAX)
  294.   {
  295.     batch = abs(atoi(argv[1]));
  296.   }
  297.   return 0;
  298. }
  299.  
  300. static int dotrim(int argc, char *argv[])
  301. {
  302.   if (argc < 2)
  303.     cwprintf(NULL, "NNTP - Trim history to %d hours\n", trim_age);
  304.   else
  305.     trim_age = atoi(argv[1]);
  306.  
  307.   if (trim_age < (fudge+3600)/3600)
  308.     trim_age = (fudge+3600)/3600;
  309.  
  310.   return 0;
  311. }
  312.  
  313. static int dodirect(int argc, char *argv[])
  314. {
  315.   if (argc < 2)
  316.   {
  317.     cwprintf(NULL, "NNTP - direct %s\n", direct ? "on" : "off");
  318.   }
  319.   else if (stricmp(argv[1], "on") == 0)
  320.   {
  321.     direct = TRUE;
  322.   }
  323.   else
  324.   {
  325.     direct = FALSE;
  326.   }
  327.   return 0;
  328. }
  329.  
  330. static int dodups(int argc, char *argv[])
  331. {
  332.   int loop;
  333.  
  334.   if (argc < 2)
  335.   {
  336.     cwprintf(NULL, "%s\n", duptab[dup_method]);
  337.   }
  338.   else
  339.   {
  340.     for (loop = 0; loop < 3; loop++)
  341.     {
  342.       if (strnicmp(argv[1], duptab[loop], MIN(strlen(argv[1]), strlen(duptab[loop]))) == NULL)
  343.         break;
  344.     }
  345.     if (loop == 3)
  346.       loop = 0;
  347.     dup_method = loop;
  348.   }
  349.   return 0;
  350. }
  351.  
  352. static int doadds(int argc, char *argv[])
  353. {
  354.   struct nntpservers *np;
  355.  
  356.   for(np = Nntpservers; np != NULLNNTP; np = np->next)
  357.     if (stricmp(np->name, argv[1]) == 0)
  358.       break;
  359.   if (np == NULLNNTP)
  360.   {
  361.     if (check_files(argv[3]))
  362.     {
  363.       np = (struct nntpservers *) calloc(1, sizeof(struct nntpservers));
  364.       np->name    = strdup(argv[1]);
  365.       np->abbr    = strdup(argv[3]);
  366.       np->ipdest  = resolve(argv[1]);
  367.       np->next    = Nntpservers;
  368.       Nntpservers = np;
  369.       np->lowtime = np->hightime = -1;
  370.       np->nntpcli_t.func = nntptick;  /* what to call on timeout */
  371.       np->nntpcli_t.arg  = (void *) np;
  372.       np->state      = NN_RDY;
  373.       np->stage      = NN_OPEN;
  374.       np->input0     = NULL;
  375.       np->input1     = NULL;
  376.       np->output     = NULL;
  377.       np->newsfile   = NULL;
  378.       np->id_file    = NULL;
  379.       np->history    = NULL;
  380.       np->histsaved  = 1;
  381.       np->pollperiod = atol(argv[2]) * 1000L;
  382.       np->retries    = 0;
  383.       np->window     = NULL;
  384.  
  385. #ifdef BUG
  386.       /* Initialise the ID list */
  387.       np->fetching.front = 0;
  388.       np->fetching.back  = 0;
  389. #endif
  390.  
  391.       np->reqptr = 0;
  392.       np->rcvptr = 0;
  393.  
  394.       /* -ID-
  395.        * Add code to chekc that global IDs are initialised and that
  396.        * ReaderS ID are incorporated if needed
  397.        */
  398.       history_init(np);
  399.  
  400.       if (dup_method == DUP_AUTO || dup_method == DUP_READERS)
  401.         ReaderS_Hist(np);
  402.  
  403.       /* set timer duration */
  404.       set_timer(&(np->nntpcli_t), np->pollperiod);
  405.       start_timer(&(np->nntpcli_t));
  406.     }
  407.     else
  408.     {
  409.       cwprintf(NULL, "Error: Can't open %sGroup file for this server\r\n",argv[3]);
  410.     }
  411.   }
  412.  
  413.   return 0;
  414. }
  415.  
  416. static int dodrops(int argc, char *argv[])
  417. {
  418.   struct nntpservers *np, *npprev = NULLNNTP;
  419.   for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  420.   {
  421.     if (stricmp(np->name, argv[1]) == 0 || stricmp(np->abbr, argv[1]) == 0)
  422.     {
  423.       quit(np, FALSE); /* Added to emulate effect of dostop */
  424.       stop_timer(&np->nntpcli_t);
  425.       free(np->name);
  426.       free(np->abbr);
  427.       if (np->input1 != NULL)
  428.         fclose(np->input1);
  429.       if (np->input0 != NULL)
  430.         fclose(np->input0);
  431.       if (np->output != NULL)
  432.         fclose(np->output);
  433.       if (np->newsfile != NULL)
  434.         fclose(np->newsfile);
  435.       np->msg_tot = 0;
  436.       if(npprev != NULLNNTP)
  437.         npprev->next = np->next;
  438.       else
  439.           Nntpservers = np->next;
  440.       free((char *)np);
  441.       return 0;
  442.     }
  443.   }
  444.   cwprintf(NULL, "NNTP - No such server enabled.\r\n");
  445.   return 0;
  446. }
  447.  
  448. static int dokicks(int argc, char *argv[])
  449. {
  450.   struct nntpservers *np;
  451.   for (np = Nntpservers; np != NULLNNTP; np = np->next)
  452.   {
  453.     if (stricmp(np->name, argv[1]) == 0 || stricmp(np->abbr, argv[1]) == 0)
  454.     {
  455.       /* If the timer is not running, the timeout function has
  456.          already been called and we don't want to call it again. */
  457.       if (run_timer(&np->nntpcli_t))
  458.       {
  459.         np->state = NN_CMD;
  460.         np->stage = NN_OPEN;
  461.         stop_timer(&np->nntpcli_t);
  462.         np->retries = 0;
  463.         nntptick((void *)np);
  464.       }
  465.       else
  466.       {
  467.         cwprintf(NULL, "NNTP - Can't restart\r\n");
  468.       }
  469.       return 0;
  470.     }
  471.   }
  472.   cwprintf(NULL, "NNTP - No such server enabled.\r\n");
  473.   return 0;
  474. }
  475.  
  476. static int dostop(int argc, char *argv[])
  477. {
  478.   struct nntpservers *np;
  479.   for(np = Nntpservers; np != NULLNNTP; np = np->next)
  480.   {
  481.     if (stricmp(np->name, argv[1]) == 0 || stricmp(np->abbr, argv[1]) == 0)
  482.     {
  483.       quit(np, FALSE);
  484.       set_timer(&(np->nntpcli_t), np->pollperiod);
  485.       start_timer(&np->nntpcli_t);
  486.       return 0;
  487.     }
  488.   }
  489.   cwprintf(NULL, "NNTP - No such server enabled.\r\n");
  490.   return 0;
  491. }
  492.  
  493. static struct directory *make_dir(char *dir)
  494. {
  495.   struct directory *list=NULL, *entry;
  496.   char name[256];
  497.  
  498.   for (filedir(dir, 0, name); *name; filedir(dir, 1, name))
  499.   {
  500.     if (entry = (struct directory *) malloc(sizeof(struct directory)), entry == 0)
  501.       break;
  502.     entry->name = strdup(name);
  503.     entry->next = list;
  504.     list = entry;
  505.   }
  506.   return list;
  507. }
  508.  
  509. static void kill_dir(struct directory *dir)
  510. {
  511.   struct directory *entry;
  512.  
  513.   for (entry = dir; entry; )
  514.   {
  515.     dir = entry->next;
  516.     free(entry->name);
  517.     free(entry);
  518.     entry = dir;
  519.   }
  520. }
  521.  
  522. static void nntptick(void *tp)
  523. {
  524.   char title[256];
  525.   struct socket lsocket, fsocket;
  526.   register struct nntpservers *cb;
  527.  
  528.   cb = (struct nntpservers *) tp;
  529.   if (cb == NULL)
  530.     return;
  531.   if (cb->state != NN_CMD)
  532.     return;
  533.  
  534.   sprintf(title, "NNTP - %s", cb->name);
  535.   if (cb->window==NULL)
  536.   {
  537.     cb->window = Window_Open(NULL, title, term_NO_INPUT | term_DONT_DESTROY);
  538.     vterm_visible(cb->window->vt, 40, 8);
  539.     vterm_setflags(cb->window->vt, VTSW_WRAP, VTSW_WRAP);
  540.   }
  541.   else
  542.     vterm_open(cb->window->vt);
  543.   /* setup the socket */
  544.   fsocket.address = cb->ipdest;
  545.   fsocket.port    = NNTP_PORT;
  546.   lsocket.address = ip_addr;      /* our ip address */
  547.   lsocket.port    = lport++;      /* next unused port */
  548.  
  549.   if (nntptrace)
  550.   {
  551.     cwprintf(cb->window, "NNTP daemon entered\r\n",inet_ntoa(fsocket.address));
  552.   }
  553.  
  554.   if (nntptrace > 1)
  555.   {
  556.     cwprintf(cb->window, "NNTP trying Connection to %s\r\n",inet_ntoa(fsocket.address));
  557.   }
  558.  
  559.   stop_timer(&cb->nntpcli_t);
  560.  
  561.   cb->batch  = 0;
  562.   cb->thisfeed = time_readgmt();
  563.  
  564.   /*
  565.    * History is allready loaded
  566.    * but IDs are writen as we go anyway
  567.    * just in case something goes wrong with computer...
  568.    */
  569.   history_open(cb);
  570.   cb->reqptr = 0;
  571.   cb->rcvptr = 0;
  572.  
  573.   /* open nntp connection */
  574.   cb->stage = NN_OPEN;      /* init stage placeholder */
  575.   cb->state = NN_CMD;       /* init state placeholder */
  576.   cb->tcb = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, tcp_window,
  577.                      (void(*)())nntp_rec, (void(*)())nntp_cts, (void(*)())nntp_state, 0, (char *)cb);
  578.   cb->tcb->user = (char *)cb;     /* Upward pointer */
  579.  
  580.   spdstat_start(&cb->ids_spd);
  581.   spdstat_start(&cb->art_spd);
  582.   cb->ids_count = 0;
  583.   cb->art_count = 0;
  584. }
  585.  
  586. BOOL nntp_select(struct nntpservers *cb)
  587. {
  588.   BOOL ret = FALSE;
  589.   char buffer[256];
  590.  
  591.   if( !cb->input0 )
  592.   {
  593.     sprintf(buffer, "<NNTP$Dir>.%sNGroup", cb->abbr);
  594.     cb->input0 = fopen(buffer, "rb+");
  595.   }
  596.  
  597.   if (cb->input0)
  598.   {
  599.     if (fgets(buffer, LINELEN - 1, cb->input0) != 0 && strlen(buffer) > 12)
  600.     {
  601.       strtok(buffer, " \n");
  602.       cb->lastread = atoi(strtok(NULL, " \n"));
  603.  
  604.       if (nntptrace > 1)
  605.       {
  606.         cwprintf(cb->window, "NNTP - Selecting group %s\r\n", buffer);
  607.       }
  608.       rip(buffer);
  609.       sendit(cb,"GROUP %s\r\n", buffer);
  610.       cb->stage = NN_SELG;
  611.       cb->state = NN_CMD;
  612.       ret = TRUE;
  613.     }
  614.     else
  615.     {
  616.       fclose(cb->input0);
  617.       cb->input0 = NULL;
  618.       cb->stage  = NN_QUIT;
  619.       cb->state  = NN_RDY;
  620.     }
  621.   }
  622.   return(ret);
  623. }
  624.  
  625. static BOOL nntp_getnews(register struct nntpservers *cb, char *list)
  626. {
  627.   BOOL ret = FALSE;
  628.   char buffer[256], line[NNTPMAXLEN+1];
  629.  
  630.   if (cb->input1 == NULL && cb->batch == 0)
  631.   {
  632.     sprintf(buffer, "<NNTP$Dir>.%s%s", cb->abbr, list);
  633.     cb->input1 = fopen(buffer, "r");
  634.     cwprintf(cb->window, "\r\n");
  635.     if (cb->input1!=NULL && !strcmp(list, "Get"))
  636.       cwprintf(cb->window, "NNTP - Fetching requested articles\r\n");
  637.   }
  638.  
  639.   if (cb->input1 != NULL)
  640.   {
  641.     while (cb->batch < batch && cb->input1 != NULL && !feof(cb->input1))
  642.     {
  643. #ifdef BUG
  644.       char *line = cb->fetching.ids[cb->fetching.front];
  645. #endif
  646.       while(fgets(line, NNTPMAXLEN-1, cb->input1) != NULL && requested_article(line, cb))
  647.         ;
  648.       if (feof(cb->input1))
  649.       {
  650.         fclose(cb->input1);
  651.         cb->input1 = NULL;
  652.         sprintf(buffer, "<NNTP$Dir>.%s%s", cb->abbr, list);
  653.         remove(buffer);
  654.       }
  655.       else
  656.       {
  657.         rip(line);
  658.         sendit(cb, "ARTICLE %s\r\n", line);
  659.         cb->stage = NN_GETN;
  660.         cb->state = NN_CMD;
  661.         (cb->batch)++;
  662. #ifdef BUG
  663.         cb->fetching.front = (cb->fetching.front + 1) % BATCH_MAX;
  664. #endif
  665.         if (nntptrace > 2)
  666.         {
  667.           cwprintf(cb->window, "NNTP - Asking for article %s\r\n", line);
  668.         }
  669.       }
  670.     }
  671.   }
  672.   if (cb->batch > 0)
  673.   {
  674.     cb->stage = NN_GETN;
  675.     cb->state = NN_CMD;
  676.     ret = TRUE;
  677.   }
  678.   else
  679.   {
  680.     cb->stage = NN_NEWN;
  681.     cb->state = NN_RDY;
  682.     ret = FALSE;
  683.   }
  684.   return(ret);
  685. }
  686.  
  687. static BOOL nntp_newnews(register struct nntpservers *cb)
  688. {
  689.   BOOL ret = FALSE;
  690.   char buffer[256], line[NNTPMAXLEN + 1];
  691.   char td[20];
  692.  
  693.   if (cb->input0 == NULL)
  694.   {
  695.     sprintf(buffer, "<NNTP$Dir>.%sGroup", cb->abbr);
  696.     cb->input0 = fopen(buffer, "r");
  697.   }
  698.  
  699.   if (cb->input0 != NULL)
  700.   {
  701.     if (fillgroupbuf(cb->input0, line, NNTPMAXLEN-30))
  702.     {
  703.       cwprintf(cb->window, "\r\n");
  704.       time_ntoa(td, cb->lastfeed-fudge);
  705.       sendit(cb, "NEWNEWS %s %s GMT\r\n", line, td);
  706.       cb->stage = NN_NEWN;
  707.       cb->state = NN_CMD;
  708.  
  709.       if (nntptrace > 1)
  710.         cwprintf(cb->window, "NNTP - Checking group(s) %s\r\n", line);
  711.  
  712.       ret = TRUE;
  713.     }
  714.     else
  715.     {
  716.       FILE *temp;
  717.  
  718.       fclose(cb->input0);
  719.       cb->input0 = NULL;
  720.  
  721.       sprintf(buffer, "<NNTP$Dir>.%sLast", cb->abbr);
  722.       if (temp = fopen(buffer, "w"), temp != NULL)
  723.       {
  724.         time_ntoa(td, cb->thisfeed);
  725.         fprintf(temp, "%s\n", td);
  726.         fclose(temp);
  727.         ret = FALSE;
  728.       }
  729.       cb->state = NN_RDY;
  730.       cb->stage = NN_QUIT /* NN_BYNUM */;
  731.     }
  732.   }
  733.   else
  734.   {
  735.     cb->state = NN_RDY;
  736.     cb->stage = NN_QUIT /* NN_BYNUM */;
  737.   }
  738.   return(ret);
  739. }
  740.  
  741. static BOOL nntp_newgroups(register struct nntpservers *cb)
  742. {
  743.   BOOL ret = FALSE;
  744.   char buffer[256];
  745.   FILE *temp;
  746.   char td[20];
  747.  
  748.   sprintf(buffer, "<NNTP$Dir>.%sLast", cb->abbr);
  749.   if (temp = fopen(buffer, "r"), temp != NULL)
  750.   {
  751.     cb->lastfeed = 0;
  752.  
  753.     if (fgets(td, 19, temp) != NULL)
  754.     {
  755.       int n;
  756.       /*
  757.        * In case of old style last file
  758.        */
  759.       if (n = strlen(td), n<8)
  760.       {
  761.         strcat(td, " ");
  762.         fgets(td+n, 10, temp);
  763.       }
  764.       cb->lastfeed = time_aton(td);
  765.     }
  766.     if (cb->lastfeed==0)
  767.       cb->lastfeed = time_readgmt()-(24*60*60);
  768.  
  769.     fclose(temp);
  770.   }
  771.  
  772.   if (newgroups)
  773.   {
  774.     time_ntoa(td, cb->lastfeed-fudge);
  775.  
  776.     sendit(cb, "NEWGROUPS %s GMT\r\n", td);
  777.     ret = TRUE;
  778.     if (nntptrace > 1)
  779.       cwprintf(cb->window, "NNTP - Checking for new groups since last call\r\n");
  780.   }
  781.   return(ret);
  782. }
  783.  
  784. static BOOL nntp_next_command(register struct nntpservers *cb)
  785. {
  786.   BOOL ret = FALSE;
  787.  
  788.   switch(cb->stage)
  789.   {
  790.   case NN_POST:
  791.     sendit(cb, "POST\r\n");
  792.     cb->state = NN_CMD;
  793.     cb->stage = NN_POST;
  794.     ret = TRUE;
  795.     break;
  796.  
  797.   case NN_NEWG:
  798.     if (nntp_newgroups(cb))
  799.     {
  800.       cb->state = NN_CMD;
  801.       ret = TRUE;
  802.     }
  803.     else
  804.     {
  805.       cb->state = NN_RDY;
  806.       cb->stage = NN_NEWN;
  807.       ret = FALSE;
  808.     }
  809.     break;
  810.  
  811.   case NN_NEWN:
  812.     if (nntp_newnews(cb))
  813.     {
  814.       cb->state = NN_CMD;
  815.       ret = TRUE;
  816.     }
  817.     else if (nntp_getnews(cb, "Get"))
  818.     {
  819.       cb->state = NN_CMD;
  820.       ret = TRUE;
  821.     }
  822.     else
  823.     {
  824.       cb->state = NN_RDY;
  825.       cb->stage = NN_QUIT /* NN_SELG */;
  826.       cwprintf(NULL, "\r\nNNTP - Fetched %d IDs (%d bytes at %dcps)\r\n",
  827.                cb->ids_count, cb->ids_spd.count, spdstat_end(&cb->ids_spd) );
  828.       cwprintf(NULL, "       Fetched %d Articles (%d bytes at %dcps)\r\n",
  829.                cb->art_count, cb->art_spd.count, spdstat_end(&cb->art_spd) );
  830.       cwprintf(NULL, "       Skipped %d duplicates.\r\n",
  831.                cb->ids_count-cb->art_count);
  832.  
  833.       log_event(cb->tcb, "NNTP Fetched %d IDs (%d bytes at %dcps)",
  834.                 cb->ids_count, cb->ids_spd.count, spdstat_end(&cb->ids_spd) );
  835.       log_event(cb->tcb, "NNTP Fetched %d Articles (%d bytes at %dcps)",
  836.                 cb->art_count, cb->art_spd.count, spdstat_end(&cb->art_spd) );
  837.       log_event(cb->tcb, "NNTP Skipped %d duplicates",
  838.                 cb->ids_count-cb->art_count);
  839.  
  840.       ret = FALSE;
  841.     }
  842.     break;
  843.  
  844.   case NN_GETN:
  845.     if (nntp_getnews(cb, "New"))
  846.     {
  847.       cb->state = NN_CMD;
  848.       ret = TRUE;
  849.     }
  850.     else
  851.     {
  852.       cb->state = NN_RDY;
  853.       cb->stage = NN_NEWN;
  854.       ret = FALSE;
  855.     }
  856.     break;
  857.  
  858.   case NN_SELG:
  859.     if (nntp_select(cb))
  860.     {
  861.       cb->state = NN_CMD;
  862.       ret = TRUE;
  863.     }
  864.     else
  865.     {
  866.       cb->state = NN_RDY;
  867.       cb->stage = NN_QUIT;
  868.       ret = FALSE;
  869.     }
  870.     break;
  871.  
  872.   default:
  873.     quit(cb, cb->stage != NN_ERROR && cb->stage != NN_RETRY);
  874.     ret = TRUE;
  875.     break;
  876.   }
  877.   return(ret);
  878. }
  879.  
  880. static void save_news(register struct nntpservers *cb)
  881. {
  882.   if (direct)
  883.   {
  884.     FILE *out;
  885.     long item_len;
  886. #ifdef OPEN_EVERY
  887.     char buffer[256];
  888.  
  889.     sprintf(buffer, "<Mail$Dir>.Folder.%sNews", cb->abbr);
  890.     out = fopen(buffer, "r+");
  891.     fseek(out, 0, SEEK_END);
  892. #else
  893.     out = cb->newsfile;
  894. #endif
  895.     item_len = ftell(out);
  896.     fseek(out, cb->rnewspos, SEEK_SET);
  897.     fprintf(out, "#! rnews %07ld\n", item_len - cb->rnewspos - 17L);
  898.     fseek(out, 0, SEEK_END);
  899. #ifdef OPEN_EVERY
  900.      fclose(out);
  901. #endif
  902.   }
  903.   else
  904.   {
  905.     char line[NNTPMAXLEN + 1], buffer[256];
  906.     FILE *temp, *out;
  907.     long item_len;
  908.  
  909.     sprintf(buffer, "<Mail$Dir>.Folder.%sNews", cb->abbr);
  910.     temp = fopen(cb->temp, "r");
  911.     fseek(temp, 0, SEEK_END);
  912.     if (item_len = ftell(temp), item_len > 0)
  913.     {
  914. #ifdef OPEN_EVERY
  915.       out = fopen(buffer, "a");
  916. #else
  917.       if(cb->newsfile==NULL)
  918.          cb->newsfile = fopen(buffer, "a");
  919.       out = cb->newsfile;
  920. #endif
  921.       fprintf(out, "#! rnews %d\n", (int) item_len);
  922.       fseek(temp, 0, SEEK_SET);
  923.       while (fgets(line, NNTPMAXLEN, temp))
  924.         fputs(line, out);
  925. #ifdef OPEN_EVERY
  926.        fclose(out);
  927. #endif
  928.     }
  929.     fclose(temp);
  930.     remove(cb->temp);
  931.     free(cb->temp);
  932.     cb->temp = NULL;
  933.   }
  934.   if (cb->batch > 0)
  935.   {
  936. #ifdef BUG
  937.     id_retrieved(cb);
  938. #endif
  939.   received_article(cb);
  940.   }
  941.   cb->art_count++;
  942.   spdstat_restart(&cb->art_spd);
  943. }
  944.  
  945. static void nntp_post(register struct nntpservers *cb)
  946. {
  947.   char buffer[256], line[1024];
  948.   FILE *temp;
  949.  
  950.   if (nntptrace > 1)
  951.     cwprintf(cb->window, "NNTP - sending article %s\r\n", cb->topost->name );
  952.   sprintf( buffer, "%s.articles.%s", newsqueue, cb->topost->name );
  953.   if (temp = fopen(buffer, "r"), temp == NULL)
  954.   {
  955.     if (nntptrace > 1)
  956.     {
  957.       cwprintf(cb->window, "NNTP - strange, article file rejects to be opened\r\n" );
  958.       log_event(cb->tcb,"NNTP %s:%s - Cant open",cb->abbr,cb->topost->name);
  959.     }
  960.     sendit(cb, ".\r\n");
  961.     return;
  962.   }
  963.   while (fgets(line, 1020, temp))
  964.   {
  965.     rip(line);
  966.     if (line[0] == '.')
  967.     sendit(cb, ".");
  968.     sendit(cb, "%s\r\n", line);
  969.   }
  970.   sendit(cb, ".\r\n");
  971.   fclose(temp);
  972.   remove(buffer);
  973. }
  974.  
  975. void nntp_transaction(register struct nntpservers *cb)
  976. {
  977.   if (nntptrace > 4)
  978.     cwprintf(cb->window, "nntp_transaction() enter state=%u stage=%u\r\n", cb->state, cb->stage);
  979.   if (nntptrace > 3)
  980.   {
  981.     cwprintf(cb->window, "%s\r\n",cb->buf);
  982.   }
  983.   if (cb->state == NN_DAT)
  984.   {
  985.     tcp_output(cb->tcb);    /* Send ACK; disk I/O is slow */
  986.  
  987.     if (cb->cntovr==0 && cb->buf[0] == '.' && cb->buf[1] == '\n' && cb->buf[2] == '\0')
  988.     {
  989.       cb->state = NN_RDY;
  990.       if (cb->output != NULL)
  991.       {
  992.         fclose(cb->output);
  993.         cb->output = NULL;
  994.       }
  995.       switch(cb->stage)
  996.       {
  997.       case NN_NEWG:
  998.         cb->stage = NN_NEWN;
  999.         break;
  1000.  
  1001.       case NN_NEWN:
  1002.         cb->stage = NN_GETN;
  1003.         cb->batch = 0;
  1004.         break;
  1005.  
  1006.       case NN_GETN:
  1007.       case NN_GNUM:
  1008.         save_news(cb);
  1009.         break;
  1010.  
  1011.       case NN_SELG:
  1012.         cb->stage = NN_SELG;
  1013.         break;
  1014.       }
  1015.     }
  1016.     else
  1017.     {
  1018.       BOOL dots;
  1019.       FILE *out;
  1020.  
  1021.       /* Append to data file */
  1022.       dots = (strncmp(cb->buf, "..", 2) == 0);
  1023.       if(direct && ((cb->stage==NN_GETN) || (cb->stage==NN_GNUM)))
  1024.         out = cb->newsfile;
  1025.       else
  1026.         out = cb->output;
  1027.       if (cb->stage==NN_NEWN)
  1028.         cb->ids_count++;
  1029.  
  1030.       if (out != NULL)
  1031.       {
  1032.         if (fprintf(out, "%s", cb->buf+dots) < 0)
  1033.         {
  1034.           cb->state = NN_RDY;
  1035.           tprintf(cb->tcb, "File write error\n");
  1036.         }
  1037.       }
  1038.     }
  1039.   }
  1040.   if (cb->state == NN_CMD)
  1041.   {
  1042.     char buffer[256];
  1043.     struct directory *next;
  1044.  
  1045.     if (nntptrace > 2)
  1046.       cwprintf(cb->window, "%s\n",  cb->buf);
  1047.  
  1048.     switch(cb->stage)
  1049.     {
  1050.     case NN_OPEN:
  1051.       switch(atoi(cb->buf))
  1052.       {
  1053.       case 200:
  1054.         cb->retries = 0;
  1055.         sprintf(buffer, "%s.articles", newsqueue);
  1056.         if (cb->topost = make_dir(buffer), cb->topost != NULL)
  1057.           cb->stage = NN_POST;
  1058.         else
  1059.           cb->stage = NN_NEWG;
  1060.         cb->state = NN_RDY;
  1061.         break;
  1062.  
  1063.       case 201:
  1064.         cb->retries = 0;
  1065.         cb->stage = NN_NEWG;
  1066.         cb->state = NN_RDY;
  1067.         break;
  1068.  
  1069.       default:
  1070.         cb->state = NN_RDY;
  1071.         if (*cb->buf=='4' || *cb->buf=='5')
  1072.         {
  1073.           if (atoi(cb->buf)==400)
  1074.             cb->stage = NN_RETRY;
  1075.           else
  1076.             cb->stage = NN_ERROR;
  1077.         }
  1078.         else
  1079.           cb->stage = NN_QUIT;
  1080.         break;
  1081.       }
  1082.       break;
  1083.  
  1084.     case NN_POST:
  1085.       switch(atoi(cb->buf))
  1086.       {
  1087.       case 240:
  1088.         log_event(cb->tcb,"NNTP %s:%s Posted OK",cb->abbr,cb->topost->name);
  1089.  
  1090.         sprintf(buffer, "%s.badmarker.%s", newsqueue, cb->topost->name);
  1091.         remove(buffer);
  1092.  
  1093.         next = cb->topost->next;
  1094.         free(cb->topost->name);
  1095.         free(cb->topost);
  1096.         cb->topost = next;
  1097.         if (cb->topost != NULL)
  1098.           cb->stage = NN_POST;
  1099.         else
  1100.           cb->stage = NN_NEWG;
  1101.         cb->state = NN_RDY;
  1102.         newsbase_update(1);
  1103.         break;
  1104.  
  1105.       case 340:
  1106.         nntp_post(cb);
  1107.         cb->stage = NN_POST;
  1108.         cb->state = NN_CMD;
  1109.         break;
  1110.  
  1111.       default:
  1112.         log_event(cb->tcb,"NNTP %s:%s Error: %s",cb->abbr,cb->topost->name,cb->buf);
  1113.         kill_dir(cb->topost);
  1114.         cb->stage = NN_NEWG;
  1115.         cb->state = NN_RDY;
  1116.         newsbase_update(1);
  1117.         break;
  1118.       }
  1119.       break;
  1120.  
  1121.     case NN_NEWG:
  1122.       switch(atoi(cb->buf))
  1123.       {
  1124.       case 231:
  1125.         if (cb->output)
  1126.           fclose(cb->output);
  1127.         sprintf(buffer, "<Mail$Dir>.Folder.%sNG", cb->abbr);
  1128.         if (cb->output = fopen(buffer, "a"), cb->output != NULL)
  1129.         {
  1130.           cb->stage = NN_NEWG;
  1131.           cb->state = NN_DAT;
  1132.         }
  1133.         else
  1134.         {
  1135.           cb->stage = NN_NEWN;
  1136.           cb->state = NN_RDY;
  1137.         }
  1138.         break;
  1139.  
  1140.       default:
  1141.         cb->stage = NN_NEWN;
  1142.         cb->state = NN_RDY;
  1143.         break;
  1144.       }
  1145.       break;
  1146.  
  1147.     case NN_NEWN:
  1148.       switch(atoi(cb->buf))
  1149.       {
  1150.       case 230:
  1151.         if (cb->output)
  1152.           fclose(cb->output);
  1153.         sprintf(buffer, "<NNTP$Dir>.%sNew", cb->abbr);
  1154.         if (cb->output = fopen(buffer, "w"), cb->output != NULL)
  1155.         {
  1156.           cb->stage = NN_NEWN;
  1157.           cb->state = NN_DAT;
  1158.           spdstat_restart(&cb->ids_spd);
  1159.  
  1160.  
  1161.         }
  1162.         else
  1163.         {
  1164.           cb->stage = NN_BYNUM;
  1165.           cb->state = NN_RDY;
  1166.         }
  1167.         break;
  1168.       }
  1169.       break;
  1170.  
  1171.     case NN_GETN:
  1172.     case NN_GNUM:
  1173.       switch(atoi(cb->buf))
  1174.       {
  1175.       case 220:
  1176. #ifdef OPEN_EVERY
  1177.         if (cb->newsfile)
  1178.           fclose(cb->newsfile);
  1179. #endif
  1180.         if (cb->temp)
  1181.         {
  1182.           free(cb->temp);
  1183.           cb->temp = NULL;
  1184.         }
  1185.         if (direct)
  1186.         {
  1187.           char buffer[256];
  1188.  
  1189.           sprintf(buffer, "<Mail$Dir>.Folder.%sNews", cb->abbr);
  1190. #ifdef OPEN_EVERY
  1191.           cb->newsfile = fopen(buffer, "a");
  1192. #else
  1193.           if(cb->newsfile==NULL)
  1194.              cb->newsfile = fopen(buffer, "a");
  1195. #endif
  1196.           if (cb->newsfile != NULL)
  1197.           {
  1198.             cb->state = NN_DAT;
  1199.             spdstat_restart(&cb->art_spd);
  1200. #ifdef OPEN_EVERY
  1201.             fseek(cb->newsfile, 0, SEEK_END);
  1202. #endif
  1203.             cb->rnewspos = ftell(cb->newsfile);
  1204.             fprintf(cb->newsfile, "#! rnews %07d\n", 0);
  1205.           }
  1206.           else
  1207.           {
  1208.             cb->state = NN_RDY;
  1209. #ifdef BUG
  1210.             id_retrieved(cb);
  1211. #endif
  1212.             received_article(cb);
  1213.           }
  1214.         }
  1215.         else
  1216.         {
  1217.           cb->temp = strdup(tmpnam(NULL));
  1218.           if (cb->output = fopen(cb->temp, "w"), cb->output != NULL)
  1219.           {
  1220.             cb->state = NN_DAT;
  1221.             spdstat_restart(&cb->art_spd);
  1222.           }
  1223.           else
  1224.           {
  1225.             cb->state = NN_RDY;
  1226. #ifdef BUG
  1227.             id_retrieved(cb);
  1228. #endif
  1229.             received_article(cb);
  1230.           }
  1231.         }
  1232.         break;
  1233.  
  1234.       case 430:  /* No such article */
  1235.       case 423:  /* No such article */
  1236.         cb->state = NN_RDY;
  1237. #ifdef BUG
  1238.             id_retrieved(cb);
  1239. #endif
  1240.         received_article(cb);
  1241.         break;
  1242.  
  1243.       default:
  1244.         if (cb->stage == NN_GETN)
  1245.           cb->stage = NN_SELG;
  1246.         else
  1247.           cb->stage = NN_QUIT;
  1248.         cb->state = NN_RDY;
  1249.         break;
  1250.       }
  1251.       break;
  1252.  
  1253.     case NN_SELG:
  1254.       switch(atoi(cb->buf))
  1255.       {
  1256.       case 211:
  1257.         cb->stage = NN_SELG;
  1258.         cb->state = NN_RDY;
  1259.         break;
  1260.  
  1261.       default:
  1262.         cb->stage = NN_SELG;
  1263.         cb->state = NN_RDY;
  1264.         break;
  1265.       }
  1266.       break;
  1267.     }
  1268.   }
  1269.   if (cb->state == NN_RDY)
  1270.   {
  1271.     while (!nntp_next_command(cb))
  1272.       ;
  1273.   }
  1274. }
  1275.  
  1276. static void close_down(struct nntpservers *cb, BOOL normal)
  1277. {
  1278.   if (cb->input1   != NULL)
  1279.     fclose(cb->input1);
  1280.   if (cb->input0   != NULL)
  1281.     fclose(cb->input0);
  1282.   if (cb->output   != NULL)
  1283.     fclose(cb->output);
  1284.   if (cb->newsfile != NULL)
  1285.     fclose(cb->newsfile);
  1286.   if (cb->id_file != NULL)
  1287.     history_close(cb, normal);
  1288.  
  1289.   if (cb->temp != NULL)
  1290.   {
  1291.   /*  remove(cb->temp); */
  1292.     free(cb->temp);
  1293.     cb->temp = NULL;
  1294.   }
  1295.  
  1296.   cb->input1  = NULL;
  1297.   cb->input0  = NULL;
  1298.   cb->output  = NULL;
  1299.   cb->newsfile= NULL;
  1300.  
  1301.   if (cb->window)
  1302.   {
  1303.     cb->window->Attr = ATTR_REVERSE;
  1304.     cb->window->Flags.flags.dont_destroy = FALSE;
  1305.     if (normal)
  1306.       Window_Close(cb->window);
  1307.     else if (cb->stage!=NN_RETRY)
  1308.       cwprintf(cb->window, "\nThis session has finished, please close the window\n");
  1309.     if (cb->stage!=NN_RETRY)
  1310.       cb->window = NULL;
  1311.   }
  1312.  
  1313.   if (cb->stage==NN_RETRY)
  1314.   {
  1315.     if (nntp_retryt>0 && cb->retries++<nntp_retrymax)
  1316.     {
  1317.       set_timer(&cb->nntpcli_t, nntp_retryt*1000);
  1318.       cb->window->Flags.flags.dont_destroy = TRUE;
  1319.       cwprintf(cb->window, "Retrying in %d seconds\r\n", nntp_retryt);
  1320.     }
  1321.     else
  1322.     {
  1323.       set_timer(&cb->nntpcli_t, cb->pollperiod);
  1324.       cwprintf(cb->window, "Too many retries, please close this window\r\n");
  1325.       cb->window = NULL;
  1326.     }
  1327.   }
  1328.   else
  1329.     set_timer(&cb->nntpcli_t, cb->pollperiod);
  1330.   start_timer(&cb->nntpcli_t);
  1331.   cb->state = NN_CMD;
  1332. }
  1333.  
  1334. /* close down link after a failure */
  1335. static void quit(struct nntpservers *cb, BOOL normal)
  1336. {
  1337.   cb->state = NN_RDY;
  1338.   sendit(cb, quitcmd);            /* issue a quit command */
  1339.   close_tcp(cb->tcb);             /* close up connection */
  1340.  
  1341.   if (nntptrace)
  1342.   {
  1343.     cwprintf(cb->window, "NNTP Closing down (%s)\r\n", normal ? "Normal" : "Error");
  1344.     if (!normal && cb->buf && cb->buf[0])
  1345.       cwprintf(cb->window, "NNTP Error - %s\r\n", cb->buf);
  1346.   }
  1347.   close_down(cb, normal);
  1348.  
  1349. }
  1350.  
  1351. /* nntp receiver upcall routine.  fires up the state machine to parse input */
  1352. static void nntp_rec(struct tcb *tcb, int16 cnt)
  1353. {
  1354.   register struct nntpservers *cb;
  1355.   char c;
  1356.   struct mbuf *bp;
  1357.   int showspd = 0;
  1358.   int s;
  1359.  
  1360.   if ((cb = (struct nntpservers *) tcb->user) == NULL) /* point to our struct */
  1361.   {
  1362.     close_tcp(tcb);
  1363.     return;
  1364.   }
  1365.  
  1366.   if (nntptrace > 4)
  1367.     cwprintf(cb->window, "nntp_rec called\r\n");
  1368.  
  1369.   recv_tcp(tcb, &bp, cnt);  /* suck up chars from low level routine */
  1370.  
  1371.   if (cb->state==NN_DAT)
  1372.   {
  1373.     if (cb->stage==NN_GETN)
  1374.     {
  1375.       showspd = 1;
  1376.       s = spdstat_update(&cb->art_spd, cnt);
  1377.     }
  1378.     else if (cb->stage==NN_NEWN)
  1379.     {
  1380.       showspd = 2;
  1381.       s = spdstat_update(&cb->ids_spd, cnt);
  1382.     }
  1383.   }
  1384.  
  1385.   /* Assemble input line in buffer, return if incomplete */
  1386.   while(pullone(&bp,&c) == 1)
  1387.   {
  1388.     switch(c)
  1389.     {
  1390.     case '\r':      /* strip cr's */
  1391.       continue;
  1392.     case '\n':      /* line is finished, go do it! */
  1393.       cb->buf[cb->cnt++] = '\n';
  1394.       cb->buf[cb->cnt] = '\0';
  1395.       nntp_transaction(cb);
  1396.       cb->cnt = 0;
  1397.       cb->cntovr = 0;
  1398.       break;
  1399.     default:        /* other chars get added to buffer */
  1400.       cb->buf[cb->cnt++] = c;
  1401.       if(cb->cnt > (NNTPMAXLEN-2))
  1402.       {
  1403.         cb->buf[cb->cnt] = '\0';
  1404.         nntp_transaction(cb);
  1405.         cb->cntovr += 1;
  1406.         cb->cnt = 0;
  1407.       }
  1408.       break;
  1409.     }
  1410.   }
  1411.  
  1412.   if (cb->window && showspd == 1)
  1413.     cwprintf(cb->window, "Fetched %d Articles (%d bytes at %dcps)   \r", cb->art_count, cb->art_spd.count, s );
  1414.   else if (cb->window && showspd == 2)
  1415.     cwprintf(cb->window, "Fetched %d IDs (%d bytes at %dcps)        \r", cb->ids_count, cb->ids_spd.count, s );
  1416. }
  1417.  
  1418. /* nntp transmitter ready upcall routine.  twiddles cts flag */
  1419. static void nntp_cts(struct tcb *tcb, int16 cnt)
  1420. {
  1421.   register struct nntpservers *cb;
  1422.  
  1423.   cb = (struct nntpservers *)tcb->user;       /* point to our struct */
  1424.   if (nntptrace > 4)
  1425.   {
  1426.     cwprintf(cb->window, "nntp_cts called avail %d\r\n", cnt);
  1427.   }
  1428.  
  1429.   /* don't do anything until/unless we're supposed to be sending */
  1430.   if(cb->cts == 0)
  1431.     return;
  1432. }
  1433.  
  1434. /* nntp state change upcall routine. */
  1435. static void nntp_state(register struct tcb *tcb, char old, char new)
  1436. {
  1437.   register struct nntpservers *cb;
  1438.   extern char *tcpstates[];
  1439.  
  1440.   old = old;
  1441.  
  1442.   cb = (struct nntpservers *)tcb->user;
  1443.   if (nntptrace > 4)
  1444.     cwprintf(cb->window, "nntp_state called: %s\r\n",tcpstates[new]);
  1445.  
  1446.   switch(new)
  1447.   {
  1448.   case ESTABLISHED:
  1449.     cb->state = NN_CMD;
  1450.     cb->stage = NN_OPEN;
  1451.     cb->cnt = 0;
  1452.     cb->cntovr = 0;
  1453.     break;
  1454.  
  1455.   case CLOSE_WAIT:
  1456.     close_tcp(tcb);                 /* shut things down */
  1457.     break;
  1458.  
  1459.   case CLOSED:
  1460.     /* if this close was not done by us ie. a RST */
  1461.     if (tcb==cb->tcb)
  1462.     {
  1463.       if (cb->stage != NN_RETRY)
  1464.       {
  1465.         cwprintf(cb->window, "NNTP Closed\r\n");
  1466.         close_down(cb, FALSE);
  1467.       }
  1468.       cb->state = NN_CMD;
  1469.       cb->stage = NN_OPEN;
  1470.     }
  1471.     del_tcp(tcb);
  1472.     break;
  1473.   }
  1474. }
  1475.  
  1476. /* Send message back to server */
  1477. static void sendit(struct nntpservers *cb, char *fmt, ...)
  1478. {
  1479.   va_list argptr;
  1480.   struct mbuf *bp;
  1481.   char tmpstring[NNTPMAXLEN+2];
  1482.  
  1483.   va_start(argptr,fmt);
  1484.   vsprintf(tmpstring,fmt,argptr);
  1485.   va_end(argptr);
  1486.  
  1487.   if (nntptrace > 3)
  1488.   {
  1489.     cwprintf(cb->window, ">>> %s\r\n", tmpstring);
  1490.   }
  1491.   bp = qdata(tmpstring, strlen(tmpstring));
  1492.   send_tcp(cb->tcb, bp);
  1493. }
  1494.  
  1495. static int dolists(int argc, char *argv[])
  1496. {
  1497.   struct nntpservers *np;
  1498.  
  1499.   for (np = Nntpservers; np != NULLNNTP; np = np->next)
  1500.   {
  1501.     char tbuf[80];
  1502.  
  1503.     if (np->lowtime != -1 && np->hightime != -1)
  1504.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  1505.     else
  1506.       tbuf[0] = '\0';
  1507.     cwprintf(NULL, "%-32s (%lu/%lu%s)\r\n", np->name,
  1508.             read_timer(&np->nntpcli_t) /1000L,
  1509.             dur_timer(&np->nntpcli_t) /1000L,
  1510.             tbuf);
  1511.   }
  1512.   return 0;
  1513. }
  1514.  
  1515.  
  1516. static int check_files(char *abbr)
  1517. {
  1518.   char buf[32];
  1519.  
  1520.   FILE *fp = NULL;
  1521.  
  1522.   sprintf(buf, "<NNTP$Dir>.%sGroup", abbr);
  1523.   if (fp = fopen(buf, "r"), fp!=NULL)
  1524.     fclose(fp);
  1525.   else
  1526.     return FALSE;
  1527.  
  1528.   sprintf(buf, "<NNTP$Dir>.%sHist", abbr);
  1529.   if (fp = fopen(buf, "r"), fp!=NULL)
  1530.     fclose(fp);
  1531.   else
  1532.   {
  1533.     if (fp = fopen(buf, "w"), fp!=NULL)
  1534.     {
  1535.       fprintf(fp, "# size 0\n");
  1536.       fclose(fp);
  1537.     }
  1538.   }
  1539.  
  1540.   sprintf(buf, "<NNTP$Dir>.%sLast", abbr);
  1541.   if (fp = fopen(buf, "r"), fp!=NULL)
  1542.     fclose(fp);
  1543.   else
  1544.   {
  1545.     if (fp = fopen(buf, "w"), fp!=NULL)
  1546.     {
  1547.       time_ntoa(buf, time_readgmt()-(24*60*60));
  1548.       fprintf(fp, "%s\n", buf);
  1549.       fclose(fp);
  1550.     }
  1551.   }
  1552.   return TRUE;
  1553. }
  1554.  
  1555.  
  1556. static int history_init(struct nntpservers *cb)
  1557. {
  1558.   char histfile[32];
  1559.   static int atexit_registered = 0;
  1560.  
  1561.   if (!atexit_registered)
  1562.   {
  1563.     atexit(nntp_shutdown);
  1564.     atexit_registered = 1;
  1565.   }
  1566.  
  1567.   if (cb->history!=NULL)
  1568.     return (cb->history!=NULL);
  1569.  
  1570.   visdelay_begin();
  1571.  
  1572.   sprintf(histfile, "<NNTP$Dir>.%sHist", cb->abbr);
  1573.  
  1574.   if (cb->history = idhist_load(histfile /*, trim_age */), cb->history==NULL)
  1575.   {
  1576.     visdelay_end();
  1577.     return (cb->history!=NULL);
  1578.   }
  1579.   visdelay_end();
  1580.  
  1581.   cwprintf(NULL, "NNTP - %d IDs loaded\r\n", idhist_numids(cb->history));
  1582.  
  1583.   return (cb->history!=NULL);
  1584. }
  1585.  
  1586. /*
  1587.  * Add an ID to the store, without a timestamp
  1588.  * Dont butcher it!
  1589.  *
  1590.  * Called in two ways:
  1591.  *  1. pending = 1 when you have just requested the ID
  1592.  *  2. pending = 0 when you have actually received the ID
  1593.  *                 or had the request rejected.
  1594.  *
  1595.  * Returns: TRUE if allready requested the ID, or allready have it, else FALSE
  1596.  */
  1597. static BOOL requested_article(char *line, struct nntpservers *cb)
  1598. {
  1599.   char *p;
  1600.   char buf[256];
  1601.   int id;
  1602.  
  1603.   strcpy(buf, line);
  1604.  
  1605.   if (id = idhist_addid(cb->history, p = idhist_trimstr(buf), ID_PENDING), id<=0)  /* 1 is a temp marker */
  1606.   {
  1607.     if (nntptrace > 2)
  1608.       cwprintf(cb->window, "NNTP - Already requested %s\r\n", line);
  1609.     return(TRUE);
  1610.   }
  1611.   else
  1612.   {
  1613.     /* add id pointer into request pending buffer */
  1614.     cb->reqbuf[cb->reqptr] = id;
  1615.     cb->reqptr = (cb->reqptr + 1) % BATCH_MAX;
  1616.   }
  1617.   return(FALSE);
  1618. }
  1619.  
  1620. /*
  1621.  * Simply marks the net pending id ptr as received
  1622.  * by timestamping it with the news feed start time.
  1623.  */
  1624. static void received_article(struct nntpservers *cb)
  1625. {
  1626.   int id;
  1627.  
  1628.   /* timestamp next pending ID */
  1629.   id = cb->reqbuf[cb->rcvptr];
  1630.   cb->rcvptr = (cb->rcvptr + 1) % BATCH_MAX;
  1631.  
  1632.   idhist_stamp(cb->history, id, cb->thisfeed);
  1633.  
  1634.   cb->batch--;
  1635.   cb->histsaved = 0;
  1636.   if (cb->id_file!=NULL)
  1637.   {
  1638.     char buf[256];
  1639.     idhist_msgid(cb->history, id, buf);
  1640.     fprintf(cb->id_file, "%s\n", buf);
  1641.   }
  1642. }
  1643.  
  1644.  
  1645. #ifdef BUG
  1646. /*
  1647.  * Add an ID to the store
  1648.  *
  1649.  * Dont butcher it!
  1650.  */
  1651. static void id_retrieved(struct nntpservers *cb)
  1652. {
  1653.   char *p;
  1654.   char buf[256];
  1655.  
  1656.   strcpy(buf, cb->fetching.ids[cb->fetching.back]);
  1657.  
  1658.   if (idhist_addid(cb->history, p = idhist_trimstr(buf), cb->thisfeed) >= 0)
  1659.   {
  1660.     cb->histsaved = 0;
  1661.     if (cb->id_file!=NULL)
  1662.       fprintf(cb->id_file, "%s\n", p);
  1663.   }
  1664.  
  1665.   cb->batch--;
  1666.  
  1667.   cb->fetching.back = (cb->fetching.back + 1) % BATCH_MAX;
  1668.  
  1669.   return;
  1670. }
  1671. #endif
  1672.  
  1673. /*
  1674.  * Extract ID History from a a ReaderS Mail file.
  1675.  * abbr is the server abbreviation.
  1676.  * Returns number of IDs read.
  1677.  */
  1678. static int ReaderS_Hist(struct nntpservers *cb)
  1679. {
  1680.   int n;
  1681.   long len;
  1682.   char buf[256];
  1683.   message_data temp;
  1684.   FILE *fp;
  1685.  
  1686.   sprintf(buf, "<ReadBack$Dir>.%sMail", cb->abbr);
  1687.  
  1688.   if (fp = fopen(buf, "r"), fp==NULL)
  1689.     return 0;
  1690.  
  1691.   fseek(fp, 0, SEEK_END);
  1692.   len = ftell(fp);
  1693.   fseek(fp, 9, SEEK_SET);
  1694.  
  1695.   visdelay_begin();
  1696.  
  1697.   /*
  1698.    * This is based upon the ReaderS related parts of
  1699.    * the original make_got_table() function.
  1700.    */
  1701.   for (n=0; ftell(fp) < len; n++)
  1702.   {
  1703.     /*
  1704.      * Hopefully this should provide protection from
  1705.      * broken ReaderS files
  1706.      */
  1707.     if (fread(&temp, sizeof(message_data), 1, fp)<sizeof(message_data))
  1708.       break;
  1709.     if (fgets(buf, 250, fp)==NULL)
  1710.       break;
  1711.     if (fgets(buf, 250, fp)==NULL)
  1712.       break;
  1713.  
  1714.     rip(buf);
  1715.  
  1716.     /* ReaderS ID are added with zero time stamp
  1717.      * so that they get filtered out when the History
  1718.      * file is saved
  1719.      */
  1720.     idhist_addid(cb->history, idhist_trimstr(buf), ID_READERS);
  1721.  
  1722.     fseek(fp, temp.comp_bytes + 4L, SEEK_CUR);
  1723.   }
  1724.  
  1725.   fclose(fp);
  1726.  
  1727.   visdelay_end();
  1728.  
  1729.   return n;
  1730. }
  1731.  
  1732. /*
  1733.  * Fill a buffer with group names from a groups file
  1734.  * Each group in the file is separated by a newline or comma
  1735.  */
  1736. static int fillgroupbuf(FILE *fp, char *buf, int len)
  1737. {
  1738.   int n;
  1739.   long pos;
  1740.   char *p;
  1741.   char *bufp = buf;
  1742.   char line[512];
  1743.   int lastwaswild = 0;
  1744.   long filenonwild = ftell(fp);
  1745.   char *lastnonwild = buf;
  1746.  
  1747.   while (pos = ftell(fp), fgets(line, 512, fp)!=NULL)
  1748.   {
  1749.     /*
  1750.      * Ignore blank and other dodgy lines
  1751.      */
  1752.     if (*line=='#' || *line==' ' || *line=='\n')
  1753.       continue;
  1754.  
  1755.     n = strlen(line);
  1756.     if (n>0 && line[n-1]!='\n')
  1757.     {
  1758.       if (p = strrchr(line, ','), p!=NULL)
  1759.         *p = '\0';
  1760.       if (lastwaswild)
  1761.         fseek(fp, filenonwild, SEEK_SET);
  1762.       else
  1763.         fseek(fp, 1+strlen(line)-n, SEEK_CUR);
  1764.     }
  1765.  
  1766.     p = strtok(line, ",\n");
  1767.  
  1768.     while (p && *p!='\t' && *p!=' ')
  1769.     {
  1770.       if (!strpbrk(p, "*!"))
  1771.       {
  1772.         lastnonwild = bufp;
  1773.         filenonwild = pos+(p-line);
  1774.         lastwaswild = 0;
  1775.       }
  1776.       else
  1777.         lastwaswild = 1;
  1778.  
  1779.       if (n = strlen(p), n<(len-(bufp!=buf)) )
  1780.       {
  1781.         n = sprintf(bufp, "%s%s", (bufp==buf)?"":",", p);
  1782.         len -= n;
  1783.         bufp += n;
  1784.         if (!lastwaswild)
  1785.         {
  1786.           lastnonwild += n;
  1787.           filenonwild += n;
  1788.         }
  1789.       }
  1790.       else
  1791.       {
  1792.         if (lastwaswild && lastnonwild!=buf)
  1793.         {
  1794.           *lastnonwild = '\0';
  1795.           bufp = lastnonwild;
  1796.           fseek(fp, filenonwild, SEEK_SET);
  1797.         }
  1798.         else
  1799.           fseek(fp, pos+(p-line), SEEK_SET);
  1800.         return bufp-buf;
  1801.       }
  1802.       p = strtok(NULL, ",\n");
  1803.     }
  1804.   }
  1805.   return bufp-buf;
  1806. }
  1807.  
  1808. /*
  1809.  * Update stats. Returns smoothed speed
  1810.  */
  1811. int spdstat_update(spdstat_str *sp, int count)
  1812. {
  1813.   int s, d;
  1814.   int t = alarm_timenow();
  1815.  
  1816.   sp->count += count;
  1817.  
  1818.   d = alarm_timedifference(sp->start_t, t);
  1819.   if (!d)
  1820.     d = 1;
  1821.  
  1822.   sp->last_t = t;
  1823.  
  1824.   return sp->count*100/d;
  1825. }
  1826.  
  1827. /*
  1828.  * Initialise stats
  1829.  */
  1830. void spdstat_start(spdstat_str *sp)
  1831. {
  1832.   sp->start_t = alarm_timenow();
  1833.   sp->last_t = sp->start_t;
  1834.   sp->count = 0;
  1835. }
  1836.  
  1837. /*
  1838.  * Account for the duration of a pause
  1839.  */
  1840. void spdstat_restart(spdstat_str *sp)
  1841. {
  1842.   int t = alarm_timenow();
  1843.   sp->start_t += alarm_timedifference(sp->last_t,t);
  1844.   sp->last_t = t;
  1845. }
  1846.  
  1847. /*
  1848.  * End timing - returns average speed
  1849.  */
  1850. int spdstat_end(spdstat_str *sp)
  1851. {
  1852.   int d = alarm_timedifference(sp->start_t, sp->last_t);
  1853.   if (!d)
  1854.     d = 1;
  1855.   return sp->count*100/d;
  1856. }
  1857.  
  1858. static int history_open(struct nntpservers *cb)
  1859. {
  1860.   char histfile[32];
  1861.  
  1862.   sprintf(histfile, "<NNTP$Dir>.%sHist", cb->abbr);
  1863.  
  1864.   if (cb->id_file!=NULL)
  1865.     fclose(cb->id_file);
  1866.  
  1867.   /*
  1868.    * Part of backup
  1869.    */
  1870.   if (cb->id_file = fopen(histfile, "a"), cb->id_file!=NULL)
  1871.   {
  1872.     fprintf(cb->id_file, "# not trimmed\n");
  1873.     time_ntoa(histfile, cb->thisfeed);
  1874.     fprintf(cb->id_file, "# time %s\n", histfile);
  1875.   }
  1876.   return (cb->id_file!=NULL);
  1877. }
  1878.  
  1879. static int history_close(struct nntpservers *cb, int normal)
  1880. {
  1881.   int a;
  1882.   char histfile[32];
  1883.  
  1884.   if (cb->id_file!=NULL)
  1885.     fclose(cb->id_file);
  1886.  
  1887.   cb->id_file = NULL;
  1888.  
  1889.   if (!normal)
  1890.     return 0;
  1891.  
  1892.   sprintf(histfile, "<NNTP$Dir>.%sHist", cb->abbr);
  1893.  
  1894.   /*
  1895.    * The save operation does the trimming, so if the save
  1896.    * fails, the worst that can happen is that the ID file
  1897.    * doesn't get trimmed. (excepting HD dying on you...)
  1898.    */
  1899.   visdelay_begin();
  1900.  
  1901.   rename(histfile, "<NNTP$Dir>.HistTemp");
  1902.  
  1903.   /*
  1904.    * This effectievly removes all requested, but unrejected and
  1905.    * unreceived articles by marking them to be ignored by both
  1906.    * have_we() above and the save operation.
  1907.    */
  1908.   idhist_clean(cb->history);
  1909.  
  1910.   if (a = idhist_save(cb->history, histfile, trim_age), a!=0)
  1911.   {
  1912.     cb->histsaved = 1;
  1913.     remove("<NNTP$Dir>.HistTemp");
  1914.   }
  1915.   else
  1916.     rename("<NNTP$Dir>.HistTemp", histfile);
  1917.  
  1918.   visdelay_end();
  1919.  
  1920.   return a;
  1921. }
  1922.  
  1923. static void nntp_shutdown(void)
  1924. {
  1925.   struct nntpservers *np;
  1926.  
  1927.   for(np = Nntpservers; np != NULLNNTP; np = np->next)
  1928.   {
  1929.     if (!np->histsaved)
  1930.       history_close(np, TRUE);
  1931.   }
  1932. }
  1933.