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