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