home *** CD-ROM | disk | FTP | other *** search
/ High Voltage Shareware / high1.zip / high1 / DIR3 / KA9Q212.ZIP / NNTPCLI.C < prev    next >
C/C++ Source or Header  |  1993-07-16  |  57KB  |  2,125 lines

  1. /*
  2.  *    Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *    Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *
  8.  *    Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *    Permission granted for non-commercial copying and use, provided
  10.  *    this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  *    20 May 92    1.2        GT    Output file in "snews" batch format.
  32.  *    21 May 92    1.3        GT    Fix 1.2.
  33.  *    07 Jun 92    1.4        GT    "connect_wait_val" timeout on connect () call.
  34.  *    11 Jun 92    1.5        GT    Booked out in error.
  35.  *    15 Jun 92    1.6        GT    Flush history file write.
  36.  *
  37.  *    21-Jul-92    Chris Sowden (csowden@mouse.demon.co.uk)
  38.  *                    Added NNTP BATCH and NNTP VERBOSE commands.
  39.  *                    Re-wrote article fetching section to allow overlapped
  40.  *                    operation (batch mode).
  41.  *                    Added a 64K bit history hash table.
  42.  *                    Added 2 mins time adjustment to allow for a fast clock.
  43.  *                    Added status message showing articles, bytes and rate.
  44.  *                    Lock "nntp.dat" for duration of session to prevent races.
  45.  *                    Numerous changes to speed things up.
  46.  *
  47.  *    08 Aug 92    1.8        MM    Force disc writes of history file.
  48.  *
  49.  *    11-Sep-92    Chris Sowden (csowden@mouse.demon.co.uk)
  50.  *                    Look for newsgroup list in data file first.
  51.  *                    Allow multiple NEWNEWS requests.
  52.  *                    Added NNTP NEWGROUPS command.
  53.  *                    Write directly to batch file in newsbatch mode.
  54.  *                    Additional messages in verbose mode.
  55.  *                    Remove zero length files.
  56.  *
  57.  *    14 Sep 92    GT    Use GMT form of NEWNEWS and NEWGROUPS commands.
  58.  *
  59.  *    14-Sep-92    Chris Sowden (csowden@mouse.demon.co.uk)
  60.  *                    Added counting to verbose received mesage.
  61.  *
  62.  *    15 Sep 92    Chris Sowden (csowden@mouse.demon.co.uk)
  63.  *                    Fixed reentrancy problem.
  64.  *
  65.  *    15 Sep 92    GT    Fixed race condition on GMT vs. local time reading
  66.  *                    between news and mail.
  67.  *                    
  68.  * 13-Nov-92    Chris Sowden (csowden@mouse.demon.co.uk)
  69.  *                    Added SAFETY command, expanded BATCH command
  70.  *                    Uses child processes for initialisation and requests
  71.  *                    New KILL file and GET file for selective article fetching
  72.  *                    Uses correct "#! rnews nnnn" style newsbatch separator
  73.  *                    Improved history file handling (especially for duplicates)
  74.  *                    Added progress messages/statistics in verbose mode
  75.  *                    Now logs all error messages and news summary
  76.  *                    
  77.  * 01-Dec-92    Chris Sowden (csowden@mouse.demon.co.uk)
  78.  *                    Improved the opening sequence of KILL and GET files
  79.  *                    Corrected error levels of file opens
  80.  *                    Close temporary new message file
  81.  *                    Tried to reduce likelihood of lock files being left around
  82.  *                    Removed extra linefeed from batch files
  83.  *
  84.  *    08 May 93    1.13    GT    Fix warnings.
  85.  *                            Reinstate 2 minute fudge factor.
  86.  *                            Drop the word "article" from the "news
  87.  *                            arrived" message.
  88.  *                            Fix signed variables problem.
  89.  *
  90.  $Id: nntpcli.c 1.13 93/07/16 11:47:04 ROOT_DOS Exp $
  91.  */
  92.  
  93. #include <stdio.h>
  94. #include <sys/types.h>
  95. #include <time.h>
  96. #include <sys/timeb.h>
  97. #include <ctype.h>
  98. #include <string.h>  /* for strchr() */
  99. #include <io.h>
  100. #ifdef MSDOS
  101. #include <dir.h>
  102. #endif
  103. #include "global.h"
  104. #include "timer.h"
  105. #include "cmdparse.h"
  106. #include "commands.h"
  107. #include "socket.h"
  108. #include "usock.h"
  109. #include "netuser.h"
  110. #include "proc.h"
  111. #include "smtp.h"
  112. #include "files.h"
  113. #include "main.h" /* for main_exit */
  114.  
  115. /* The grouplist must fit on a line like "NEWNEWS <grouplist> 000000 000000 GMT\n" */
  116.  
  117. #define NNTPMAXLEN    512
  118. #define NNTPMAXGROUPLIST NNTPMAXLEN-28
  119.  
  120. static struct nntpservers {
  121.     struct timer nntpcli_t;
  122.     char *name;
  123.     char *groups;
  124.     int lowtime, hightime;  /* for connect window */
  125.     struct nntpservers *next;
  126. };
  127.  
  128. #define    NULLNNTP    (struct nntpservers *)NULL
  129.  
  130. #define NEWSBATCH TRUE    /* output for batching newsreader */
  131. #define NEWSBATCHFORMAT "#! rnews %010lu\n"
  132.  
  133. #if !NEWSBATCH
  134. #define MAXGROUPDIRS 10
  135. static struct grouploc {
  136.     char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  137.     char *directory;     /* directory where these groups should be */
  138.     } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  139. #endif
  140.  
  141. struct nntpservers *Nntpservers = NULLNNTP;
  142. static char *Nntpgroups = NULLCHAR;
  143. static unsigned short nntptrace = 1;
  144. static int nntpbatch = 0;
  145. static int nntpbatchsize = 2;
  146. static int nntpnewgroups = 0;
  147. static int nntpsafety = 1;
  148. static int nntpverbose = 1;
  149.  
  150. #if    !NEWSBATCH
  151. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  152. #endif
  153.  
  154. ext_dokicks(void);
  155. static void nntptick __ARGS((void *tp));
  156.  
  157. static int dobatch __ARGS((int argc,char *argv[],void *p));
  158. static int doadds __ARGS((int argc,char *argv[],void *p));
  159. static int dondir __ARGS((int argc,char *argv[],void *p));
  160. static int dodrops __ARGS((int argc,char *argv[],void *p));
  161. static int dogroups __ARGS((int argc,char *argv[],void *p));
  162. static int dokicks __ARGS((int argc,char *argv[],void *p));
  163. static int dolists __ARGS((int argc,char *argv[],void *p));
  164. static int donewgroups __ARGS((int argc,char *argv[],void *p));
  165. static int dosafety __ARGS((int argc,char *argv[],void *p));
  166. static int donntrace __ARGS((int argc,char *argv[],void *p));
  167. static int doverbose __ARGS((int argc,char *argv[],void *p));
  168.  
  169. static void error __ARGS((int level,char *text));
  170.  
  171. static void freetextlinelist __ARGS((struct textline **list));
  172.  
  173. static void createkilllists __ARGS((void));
  174. static void scankilllists __ARGS((char *line,int *status));
  175.  
  176. static unsigned int hashmsgid __ARGS((char *msgid,unsigned int modulus));
  177. static void createhashtable __ARGS((void));
  178. static int isinhashtable __ARGS((char *msgid));
  179. static int isinhistory __ARGS((char *msgid));
  180. static void addtohashtable __ARGS((char *msgid));
  181. static void addtohistory __ARGS((char *msgid));
  182.  
  183. static int stripgroups __ARGS((char *s));
  184. static unsigned long unitspersecond __ARGS((unsigned long units,unsigned long milliseconds));
  185.  
  186. static int getreply __ARGS((void));
  187. static int gettxt __ARGS((FILE *fp,struct nntprequest *request));
  188. static int putarticle __ARGS((FILE *msgf,long int *msgfpos,struct nntprequest *request));
  189. static int getheader __ARGS((struct nntprequest *request));
  190. static void moveheadertofile __ARGS((FILE *fp,struct nntprequest *request));
  191.  
  192. static void nntp_init __ARGS((int argc,void *argv,void *parentproc));
  193. static void nntp_send __ARGS((int argc,void *argv,void *parentproc));
  194. static void nntp_main __ARGS((int i1,void *tp,void *v1));
  195. static void nntp_reg __ARGS((void));
  196.  
  197.  
  198. /* Tracing levels:
  199.     0 - no tracing
  200.     1 - serious errors reported
  201.     2 - transient errors reported
  202.     3 - session progress reported
  203.     4 - actual received articles displayed
  204.  */
  205.  
  206. static struct cmds Nntpcmds[] = {
  207.     { "addserver",    doadds,    0,    3,
  208.     "nntp addserver <nntpserver> <interval>" },
  209.     { "batch",    dobatch,    0,    0,    NULLCHAR },
  210.     { "directory",    dondir,    0,    0,    NULLCHAR },
  211.     { "dropserver",    dodrops,    0,    2,
  212.     "nntp dropserver <nntpserver>" },
  213.     { "groups",    dogroups,    0,    0,    NULLCHAR },
  214.     { "kick",        dokicks,    0,    0,
  215.     "nntp kick <nntpserver>" },
  216.     { "listservers",    dolists,    0,    0,    NULLCHAR },
  217.     { "newgroups",donewgroups, 0, 0, NULLCHAR },
  218.     { "safety",    dosafety,    0,    0,    NULLCHAR },
  219.     { "trace",    donntrace,    0,    0,    NULLCHAR },
  220.     { "verbose",    doverbose,    0,    0,    NULLCHAR },
  221.     { NULLCHAR },
  222. };
  223.  
  224. int
  225. donntp(argc,argv,p)
  226. int argc;
  227. char *argv[];
  228. void *p;
  229. {
  230.     return subcmd(Nntpcmds,argc,argv,p);
  231. }
  232.  
  233. static int
  234. doadds(argc,argv,p)
  235. int argc;
  236. char *argv[];
  237. void *p;
  238. {
  239.     struct nntpservers *np;
  240.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  241.         if(stricmp(np->name,argv[1]) == 0)
  242.             break;
  243.     if (np == NULLNNTP) {
  244.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  245.         np->name = strdup(argv[1]);
  246.         np->next = Nntpservers;
  247.         Nntpservers = np;
  248.         np->groups = NULLCHAR;
  249.         np->lowtime = np->hightime = -1;
  250.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  251.         np->nntpcli_t.arg = (void *)np;
  252.     }
  253.     if (argc > 3) {
  254.         int i;
  255.         if (np->groups == NULLCHAR) {
  256.             np->groups = mallocw(NNTPMAXLEN);
  257.             *np->groups = '\0';
  258.         }
  259.         for (i = 3; i < argc; ++i) {
  260.             if (isdigit(*argv[i])) {
  261.                 int lh, ll, hh, hl;
  262.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  263.                 np->lowtime = lh * 100 + ll;
  264.                 np->hightime = hh * 100 + hl;
  265.             } else if ((strlen(np->groups)+1+strlen(argv[i])) > NNTPMAXGROUPLIST)
  266.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  267.             else {  /* it's a group, and it fits... add it to list */
  268.                 if (*np->groups != '\0')
  269.                     strcat(np->groups, ",");
  270.                 strcat(np->groups, argv[i]);
  271.             }
  272.         }
  273.         if (*np->groups == '\0') {    /* No groups specified? */
  274.             free(np->groups);
  275.             np->groups = NULLCHAR;
  276.         }
  277.     }
  278.     /* set timer duration */
  279.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  280.     start_timer(&np->nntpcli_t);        /* and fire it up */
  281.     return 0;
  282. }
  283.  
  284. static int
  285. dodrops(argc,argv,p)
  286. int argc;
  287. char *argv[];
  288. void *p;
  289. {
  290.     struct nntpservers *np, *npprev = NULLNNTP;
  291.     for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  292.         if(stricmp(np->name,argv[1]) == 0) {
  293.             stop_timer(&np->nntpcli_t);
  294.             free(np->name);
  295.             if (np->groups)
  296.                 free(np->groups);
  297.             if(npprev != NULLNNTP)
  298.                 npprev->next = np->next;
  299.             else
  300.                 Nntpservers = np->next;
  301.             free((char *)np);
  302.             return 0;
  303.     }
  304.     tprintf("No such server enabled\n");
  305.     return 0;
  306. }
  307.  
  308. static int
  309. dolists(argc,argv,p)
  310. int argc;
  311. char *argv[];
  312. void *p;
  313. {
  314.     struct nntpservers *np;
  315.     for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  316.         char tbuf[80];
  317.         if (np->lowtime != -1 && np->hightime != -1)
  318.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  319.         else
  320.             tbuf[0] = '\0';
  321.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  322.             read_timer(&np->nntpcli_t) /1000L,
  323.             dur_timer(&np->nntpcli_t) /1000L,
  324.             tbuf, np->groups ? np->groups : "");
  325.     }
  326.     return 0;
  327. }
  328.  
  329. static int donntrace(argc, argv, p)
  330. int argc;
  331. char *argv[];
  332. void *p;
  333. {
  334.     return setshort(&nntptrace,"NNTP tracing",argc,argv);
  335. }
  336.     
  337. static char *News_spool = NULL;
  338. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  339.  
  340. static int dondir(argc, argv, p)
  341. int argc;
  342. char *argv[];
  343. void *p;
  344. {
  345.     if (argc < 2) {
  346. #if !NEWSBATCH
  347.         int i;
  348. #endif
  349.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  350.         tprintf("control: %s\n", Newsdir);
  351. #if !NEWSBATCH
  352.         for (i = 0; i < MAXGROUPDIRS; ++i)
  353.             if (groupdirs[i].prefix)
  354.                 tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  355. #endif
  356.     } else {
  357. #if    NEWSBATCH
  358.         if (strchr(argv[1], '=') != NULLCHAR) {  /* set a groupdir */
  359. #endif
  360. #if !NEWSBATCH
  361.         char *p;
  362.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  363.             int i;
  364.             *p++ = '\0';
  365.             for (i = 0; i < MAXGROUPDIRS; ++i)
  366.                 if (groupdirs[i].prefix)
  367.                     if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  368.                         if (groupdirs[i].directory) {
  369.                             free(groupdirs[i].directory);
  370.                             groupdirs[i].directory = NULLCHAR;
  371.                             }
  372.                         if (*p == '\0') {
  373.                             free(groupdirs[i].prefix);
  374.                             groupdirs[i].prefix = NULLCHAR;
  375.                         } else
  376.                             groupdirs[i].directory = strdup(p);
  377.                         return 0;
  378.                     }
  379.             if (*p == '\0')  /* trashing a group that's not there */
  380.                 return 0;
  381.             for (i = 0; i < MAXGROUPDIRS; ++i){
  382.                 if (groupdirs[i].prefix == NULLCHAR) {
  383.                     groupdirs[i].prefix = strdup(argv[1]);
  384.                     if (groupdirs[i].directory) {
  385.                         free(groupdirs[i].directory);
  386.                         groupdirs[i].directory = NULL;
  387.                     }
  388.                     groupdirs[i].directory = strdup(p);
  389.                     return 0;
  390.                 }
  391.             }
  392.             tprintf("Directory table full\n");
  393. #else
  394.             tprintf("Directory table not supported\n");
  395. #endif
  396.         }
  397.         else {  /* no '=', so just set default */
  398.             if (News_spool)
  399.                 free(News_spool);
  400.             News_spool = strdup(argv[1]);
  401.         }
  402.         if (argc > 2) {  /* they specified a newsdir as well */
  403.             if (np_all)
  404.                 free(Newsdir);
  405.             Newsdir = strdup(argv[2]);
  406.             np_all = 1;
  407.         }
  408.     }
  409.     return 0;
  410. }
  411.  
  412. int
  413. ext_dokicks(void)
  414. {
  415.     char *args[] = { "" };
  416.     
  417.     dokicks(1, args, NULL);
  418.     return 0;
  419. }
  420.  
  421. static int
  422. dokicks(argc,argv,p)
  423. int argc;
  424. char *argv[];
  425. void *p;
  426. {
  427.     struct nntpservers *np;
  428.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  429.         if(argc == 1 || stricmp(np->name,argv[1]) == 0) {
  430.             /* If the timer is not running, the timeout function has
  431.             * already been called and we don't want to call it again.
  432.             */
  433.             if(run_timer(&np->nntpcli_t)) {
  434.                 stop_timer(&np->nntpcli_t);
  435.                 nntptick((void *)np);
  436.             }
  437.             if (argc != 1)
  438.                 return 0;
  439.     }
  440.     if (argc != 1)
  441.         tprintf("No such server enabled.\n");
  442.     return 0;
  443. }
  444.  
  445. static int
  446. dogroups(argc,argv,p)
  447. int argc;
  448. char *argv[];
  449. void *p;
  450. {
  451.     char grouplist[NNTPMAXLEN];
  452.     int i;
  453.  
  454.     if(argc < 2) {
  455.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  456.             tprintf("All groups are currently enabled\n");
  457.         else
  458.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  459.         return 0;
  460.     }
  461.     if (Nntpgroups != NULLCHAR)
  462.         free(Nntpgroups);
  463.     *grouplist = '\0';
  464.     for(i=1; i < argc; ++i) {
  465.         if ((strlen(grouplist)+1+strlen(argv[i])) > NNTPMAXGROUPLIST)
  466.             tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  467.         else {
  468.             if(i > 1)
  469.                 strcat(grouplist,",");
  470.             strcat(grouplist,argv[i]);
  471.         }
  472.     }
  473.     Nntpgroups = strdup(grouplist);
  474.     return 0;
  475. }
  476.  
  477. static int
  478. dobatch(argc,argv,p)
  479. int argc;
  480. char *argv[];
  481. void *p;
  482. {
  483.     if (argc < 2) {
  484.         tprintf("NNTP batch mode: %s, buffers: %d\n",nntpbatch ? "on":"off",nntpbatchsize);
  485.         return 0;
  486.     }
  487.     if (argc > 2) {
  488.         nntpbatchsize = atoi(argv[2]);
  489.     }
  490.     return setbool(&nntpbatch,"NNTP batch mode",argc,argv);
  491. }
  492.  
  493. static int
  494. dosafety(argc,argv,p)
  495. int argc;
  496. char *argv[];
  497. void *p;
  498. {
  499.     return setbool(&nntpsafety,"NNTP safety mode",argc,argv);
  500. }
  501.  
  502. static int
  503. doverbose(argc,argv,p)
  504. int argc;
  505. char *argv[];
  506. void *p;
  507. {
  508.     return setbool(&nntpverbose,"NNTP verbose mode",argc,argv);
  509. }
  510.  
  511. static int
  512. donewgroups(argc,argv,p)
  513. int argc;
  514. char *argv[];
  515. void *p;
  516. {
  517.     return setbool(&nntpnewgroups,"NNTP get new groups",argc,argv);
  518. }
  519.  
  520. /* This is the routine that gets called every so often to connect to
  521.  * NNTP servers.
  522.  */
  523.  
  524. static void
  525. nntptick(tp)
  526. void *tp;
  527. {
  528.     newproc("NNTP client main", 3072, nntp_main, 0, tp, NULL,0);
  529. }
  530.  
  531.  
  532. struct textline {
  533.     struct textline *nextline;                /* pointer to next text line */
  534.     char text[NNTPMAXLEN];                    /* array holding text (shrunk to fit) */
  535. };
  536.  
  537. #define NULLTEXTLINE (struct textline *) 0
  538. #define MINTEXTLINESIZE (sizeof(struct textline)-NNTPMAXLEN)
  539.  
  540. struct nntprequest {
  541.     char msgid[NNTPMAXLEN];                    /* id of requested header/body/article */
  542.     struct textline *header;                /* header text */
  543.     unsigned int lines;                        /* lines in header/body/article */
  544.     unsigned long chars;                        /* characters in header/body/article */
  545.     enum {
  546.         reqempty = 0,                            /* vacant request */
  547.         reqgetarticle,                            /* article from get file */
  548.         reqnewarticle,                            /* article from new file */
  549.         reqheader,                                /* header only */
  550.         reqbody,                                    /* body to accompany header */
  551.         reqkill,                                    /* kill this header */
  552.         reqkeep,                                    /* get a body to accompany this header */
  553.         reqend,                                    /* no more requests */
  554.         reqquit                                    /* close connection */
  555.     } status;
  556.     struct nntprequest *nextrequest;        /* pointer to next request */
  557. };
  558.  
  559. #define NULLNNTPREQUEST (struct nntprequest *) 0
  560.  
  561. struct nntpsession {
  562.     int s;                                        /* NNTP socket */
  563.  
  564.     struct proc *childproc;                    /* child process, either init or send */
  565.  
  566.     FILE *getmsgf;                                /* file holding messages to get */
  567.     FILE *newmsgf;                                /* temporary file for NEWNEWS response */
  568.     FILE *historyf;                            /* history file */
  569.     unsigned char *hashtable;                /* history hash table */
  570.  
  571.     struct nntprequest
  572.         *requesthead,                            /* request pointer used by sender */
  573.         *requesttail;                            /* request pointer used by receiver */
  574.  
  575.     struct textline
  576.         *killlist,                                /* kill patterns */
  577.         *keeplist;                                /* keep patterns */
  578.  
  579.     int quit;                                    /* if true quit immediately */
  580.  
  581.     unsigned int historyentries;            /* number of entries in history file */
  582.     unsigned int historyscans;                /* number of complete history file scans */
  583.     unsigned int getarticles;                /* number of lines in get file */
  584.     unsigned int getinvalid;                /* number of invalid ids from get file */
  585.     unsigned int getunavailable;            /* number of articles from get file unavailable */
  586.     unsigned int getreceived;                /* number of complete articles received */
  587.     unsigned int newarticles;                /* number of new article message ids received */
  588.     unsigned int newduplicates;            /* number of duplicate articles */
  589.     unsigned int newunavailable;            /* number of articles unavailalable */
  590.     unsigned int newheadersreceived;        /* number of headers only received */
  591.     unsigned int newreceived;                /* number of complete articles received */
  592.     unsigned int groupsreceived;            /* number of new groups received */
  593.     unsigned long receivedarticlechars;    /* number of characters in articles received */
  594.     unsigned long receivednewschars;        /* number of characters in news altogether */
  595.     unsigned long receivedtotalchars;    /* number of characters received altogether */
  596.     unsigned long startsession;            /* clock value at start of session */
  597.     unsigned long startarticles;            /* clock value at start of article fetch */
  598.     unsigned long endsession;                 /* clock value at end of session */
  599. };
  600.  
  601. #define NULLNNTPSESSION (struct nntpsession *) 0
  602.  
  603. static struct nntpsession *nntp = NULLNNTPSESSION;
  604.  
  605.  
  606. static void
  607. error(level,text)
  608. int level;
  609. char *text;
  610. {
  611.     if (level <= 2)
  612.         log(nntp->s,text);
  613.     if (level <= nntptrace)
  614.         tprintf("%s\n",text);
  615. }
  616.  
  617.  
  618. static void
  619. freetextlinelist(list)
  620. struct textline **list;
  621. {
  622.     struct textline *lastline, *thisline = *list;
  623.  
  624.     while (thisline != NULLTEXTLINE) {
  625.         lastline = thisline;
  626.         thisline = thisline->nextline;
  627.         free(lastline);
  628.     }
  629.     *list = NULLTEXTLINE;
  630. }
  631.  
  632.  
  633. static void
  634. createkilllists()
  635. {
  636.     FILE *killf;
  637.     char buf[NNTPMAXLEN], *s, *d;
  638.     int keep, n, keeplines = 0, killlines = 0;
  639.     struct textline *newline, *lastkillline = NULLTEXTLINE, *lastkeepline = NULLTEXTLINE;    
  640.  
  641.     /* If it exists, open the kill file */
  642.     sprintf(buf,"%s/kill",Newsdir);
  643.     if (access(buf,0) == 0) {
  644.  
  645.         if (mlock(Newsdir,"kill")) {
  646.             sprintf(buf,"NNTP Can't lock file %s/kill",Newsdir);
  647.             error(2,buf);
  648.             nntp->quit = TRUE;
  649.             return;
  650.         }
  651.         if ((killf = fopen(buf,READ_TEXT)) == NULLFILE) {
  652.             sprintf(buf,"NNTP Can't open file %s/kill",Newsdir);
  653.             error(1,buf);
  654.             rmlock(Newsdir,"kill");
  655.             nntp->quit = TRUE;
  656.             return;
  657.         }
  658.  
  659.         while (fgets(buf,NNTPMAXLEN,killf) != NULLCHAR) {
  660.  
  661.             /* strip out extra characters and force lower case */
  662.             s = d = buf;
  663.             n = 0;
  664.             keep = (*s == '!');
  665.             if (keep)
  666.                 s++;
  667.             while (*s != '\n' && *s != '\0') {
  668.                 if (isspace(*s)) {
  669.                     do s++; while (isspace(*s));
  670.                     *d++ = ' ';
  671.                 } else {
  672.                     *d++ = tolower(*s++);
  673.                 }
  674.                 n++;
  675.             }
  676.             *d = '\0';
  677.  
  678.             if ((newline = (struct textline *) malloc(MINTEXTLINESIZE+n+1)) == NULLTEXTLINE) {
  679.                 error(2,"NNTP Can't allocate kill buffer");
  680.                 fclose(killf);
  681.                 rmlock(Newsdir,"kill");
  682.                 nntp->quit = TRUE;
  683.                 return;
  684.             }
  685.             newline->nextline = NULLTEXTLINE;
  686.             memcpy(newline->text,buf,n+1);
  687.  
  688.             if (keep) {
  689.                 if (lastkeepline == NULLTEXTLINE)
  690.                     nntp->keeplist = newline;
  691.                 else
  692.                     lastkeepline->nextline = newline;
  693.                 lastkeepline = newline;
  694.                 keeplines++;
  695.             } else {
  696.                 if (lastkillline == NULLTEXTLINE)
  697.                     nntp->killlist = newline;
  698.                 else
  699.                     lastkillline->nextline = newline;
  700.                 lastkillline = newline;
  701.                 killlines++;
  702.             }
  703.  
  704.             if (!nntp->quit && !main_exit)
  705.                 pwait(NULL);
  706.         }
  707.         if (nntptrace >= 3)
  708.             tprintf("NNTP Kill file read: %d kill lines, %d keep lines\n",killlines,keeplines);
  709.  
  710.         fclose(killf);
  711.         rmlock(Newsdir,"kill");
  712.     }
  713. }
  714.  
  715.  
  716. static void
  717. scankilllists(line,status)
  718. char *line;
  719. int *status;
  720. {
  721.     char buf[NNTPMAXLEN], *s, *d;
  722.     struct textline *thisline;
  723.  
  724.     /* strip space and convert to lower case */
  725.     s = line;
  726.     d = buf;
  727.     while (*s != '\n' && *s != '\0') {
  728.         if (isspace(*s)) {
  729.             do s++; while (isspace(*s));
  730.             *d++ = ' ';
  731.         } else {
  732.             *d++ = tolower(*s++);
  733.         }
  734.     }
  735.     *d = '\0';
  736.  
  737.     thisline = nntp->keeplist;
  738.     while (thisline != NULLTEXTLINE) {
  739.         if (wildmat(buf,thisline->text,NULLCHARP)) {
  740.             if (nntptrace >= 3)
  741.                 tprintf("Keep: %s\n      %s\n",buf,thisline->text);
  742.             *status = reqkeep;
  743.             return;
  744.         }
  745.         thisline = thisline->nextline;
  746.     }
  747.     if (*status != reqkill) {
  748.         thisline = nntp->killlist;
  749.         while (thisline != NULLTEXTLINE) {
  750.             if (wildmat(buf,thisline->text,NULLCHARP)) {
  751.                 if (nntptrace >= 3)
  752.                     tprintf("Kill: %s\n      %s\n",buf,thisline->text);
  753.                 *status = reqkill;
  754.                 return;
  755.             }
  756.             thisline = thisline->nextline;
  757.         }
  758.     }
  759. }
  760.  
  761.  
  762. #define HASH1 32749
  763. #define HASH2 32719
  764.  
  765. unsigned int
  766. hashmsgid(msgid, modulus)
  767. char *msgid;
  768. unsigned int modulus;
  769. {
  770.     unsigned long int i = 0;
  771.  
  772.     while (*msgid != '\0') {
  773.         do i = (i<<8)|*msgid++; while (*msgid != '\0' && i < (1L<<24));
  774.         i %= modulus;
  775.     }
  776.     return (unsigned int) i;
  777. }
  778.  
  779.  
  780. static void
  781. createhashtable()
  782. {
  783.     char msgid[NNTPMAXLEN];
  784.     unsigned char *hashtable = NULL;
  785.     unsigned int i, lines = 0, collisions = 0;
  786.     int found;
  787.  
  788.     /* If there is sufficient memory then build a 64K bit hash table from
  789.      *  the history file.
  790.      */
  791.  
  792.     if (availmem() > Memthresh+(8192*sizeof(unsigned char)) &&
  793.             (hashtable = (unsigned char *) calloc(8192,sizeof(unsigned char))) != NULL) {
  794.         rewind(nntp->historyf);
  795.         while (fgets(msgid,NNTPMAXLEN,nntp->historyf) != NULL) {
  796.             lines++;
  797.             found = 1;
  798.             i = hashmsgid(msgid,HASH1);
  799.             if (!(hashtable[i/4]&(0x01<<(i%4)))) {
  800.                 hashtable[i/4] |= 0x01<<(i%4);
  801.                 found = 0;
  802.             }
  803.             i = hashmsgid(msgid,HASH2);
  804.             if (!(hashtable[i/4]&(0x10<<(i%4)))) {
  805.                 hashtable[i/4] |= 0x10<<(i%4);
  806.                 found = 0;
  807.             }
  808.             if (found)
  809.                 collisions++;
  810.             if (!nntp->quit && !main_exit)
  811.                 pwait(NULL);
  812.         }
  813.         rewind(nntp->historyf);
  814.         if (nntptrace >= 3)
  815.             tprintf("NNTP Hash table created: %u ids, %u collisions\n", lines, collisions);
  816.     }
  817.     nntp->hashtable = hashtable;
  818.     nntp->historyentries = lines;
  819.     nntp->historyscans = 1;
  820. }
  821.  
  822.  
  823. static int
  824. isinhashtable(msgid)
  825. char *msgid;
  826. {
  827.     unsigned int i, j;
  828.  
  829.     if (nntp->hashtable == NULL)
  830.         return 1;
  831.     else {
  832.         i = hashmsgid(msgid,HASH1);
  833.         j = hashmsgid(msgid,HASH2);
  834.         if ((nntp->hashtable[i/4]&(0x01<<(i%4))) &&
  835.              (nntp->hashtable[j/4]&(0x10<<(j%4))))
  836.             return 1;
  837.     }
  838.     return 0;
  839. }
  840.  
  841.  
  842. static int
  843. isinhistory(msgid)
  844. char *msgid;
  845. {
  846.     char historyid[NNTPMAXLEN];
  847.     long historyfpos;
  848.  
  849.     if (isinhashtable(msgid)) {
  850.         historyfpos = ftell(nntp->historyf);
  851.         while(fgets(historyid,NNTPMAXLEN,nntp->historyf) != NULLCHAR) {
  852.             if(strcmp(historyid,msgid) == 0)
  853.                 return 1;
  854.             if (!nntp->quit && !main_exit)
  855.                 pwait(NULL);
  856.         }
  857.         nntp->historyscans++;
  858.         rewind(nntp->historyf);
  859.         while(fgets(historyid,NNTPMAXLEN,nntp->historyf) != NULLCHAR) {
  860.             if(strcmp(historyid,msgid) == 0)
  861.                 return 1;
  862.             if (ftell(nntp->historyf) >= historyfpos)
  863.                 break;
  864.             if (!nntp->quit && !main_exit)
  865.                 pwait(NULL);
  866.         }
  867.     }
  868.     return 0;
  869. }
  870.     
  871.  
  872. static void
  873. addtohashtable(msgid)
  874. char *msgid;
  875. {
  876.     unsigned int i, j;
  877.  
  878.     if (nntp->hashtable != NULL) {
  879.         i = hashmsgid(msgid,HASH1);
  880.         nntp->hashtable[i/4] |= 0x01<<(i%4);
  881.         j = hashmsgid(msgid,HASH2);
  882.         nntp->hashtable[j/4] |= 0x10<<(j%4);
  883.     }
  884. }
  885.  
  886.  
  887. static void
  888. addtohistory(msgid)
  889. char *msgid;
  890. {
  891.     long historyfpos;
  892.     int duphandle;
  893.  
  894.     addtohashtable(msgid);
  895.  
  896.     historyfpos = ftell(nntp->historyf);
  897.     fseek(nntp->historyf,0L,SEEK_END);
  898.     fputs(msgid,nntp->historyf);
  899.     fflush(nntp->historyf);
  900.     if (nntpsafety) {
  901.         duphandle = dup(fileno(nntp->historyf));
  902.         close(duphandle);
  903.     }
  904.     fseek(nntp->historyf,historyfpos,SEEK_SET);
  905.     nntp->historyentries++;
  906. }
  907.  
  908.  
  909. /*
  910. *    stripgroups reformats a group list in situ for a NEWNEWS request
  911. *
  912. *    returns -1 if there is leading space,
  913. *                0 if line is blank otherwise
  914. *                n the number of characters in the resultant string
  915. */
  916.  
  917. static int
  918. stripgroups(s)
  919. char *s;
  920. {
  921.     char *d = s;
  922.     int count = 0;
  923.  
  924.     if (!isspace(*s)) return -1;
  925.  
  926.     do s++; while (*s == ',' || isspace(*s));
  927.  
  928.     while (*s != '\0') {
  929.         if (count != 0) {*d++ = ','; count++;}
  930.         do {
  931.             *d++ = *s++; count++;
  932.         } while (*s != '\0' && *s != ',' && !isspace(*s));
  933.         while (*s == ',' || isspace(*s)) s++;
  934.     }
  935.     *d = '\0';
  936.     return count;
  937. }
  938.  
  939.  
  940. static unsigned long
  941. unitspersecond(units,milliseconds)
  942. unsigned long units,milliseconds;
  943. {
  944.     if (milliseconds == 0) { /* Avoid divide-by-zero */
  945.         return 0;
  946.     } else {
  947.         if(units < 4294967UL) {
  948.             return (units*1000UL)/milliseconds;
  949.         } else {    /* Avoid overflow */
  950.             return units/(milliseconds/1000UL);
  951.         }
  952.     }
  953. }
  954.  
  955.  
  956. static int
  957. getreply()
  958. {
  959.     char buf[NNTPMAXLEN];
  960.     int n, response;
  961.  
  962.     while((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
  963.         nntp->receivedtotalchars += n;
  964.         if (nntptrace >= 3)
  965.             tprintf("<==%s", buf);
  966.         /* skip informative messages and blank lines */
  967.         if(buf[0] == '\n' || buf[0] == '1')
  968.             continue;
  969.         sscanf(buf,"%d",&response);
  970.         return response;
  971.     }
  972.     return -1;
  973. }
  974.  
  975.  
  976. static int
  977. getheader(request)
  978. struct nntprequest *request;
  979. {
  980.     char buf[NNTPMAXLEN];
  981.     int n, nlines = 0;
  982.     unsigned long nchars = 0L;
  983.     struct textline *newline, *lastheaderline = NULLTEXTLINE;
  984.     unsigned long endtime, starttime = msclock();
  985.  
  986.     while ((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
  987.  
  988.         nntp->receivedtotalchars += n;
  989.         nchars += n;
  990.  
  991.         if (nntptrace >= 4)
  992.             tprintf("<==%s", buf);
  993.  
  994.         if(strcmp(buf,".\n") == 0) {
  995.             request->lines += nlines;
  996.             if (nntptrace >= 3) {
  997.                 endtime = msclock();
  998.                 tprintf("<--%d lines, %lu chars, %lu sec (%lu bytes/sec)\n",
  999.                     nlines,
  1000.                     nchars,
  1001.                     (endtime-starttime)/1000UL,
  1002.                     unitspersecond(nchars,endtime-starttime));
  1003.             }
  1004.             /* default to getting the body */
  1005.             if (request->status != reqkill)
  1006.                 request->status = reqkeep;
  1007.             return nlines;
  1008.         }
  1009.  
  1010.         nlines++;
  1011.  
  1012.         /* check for escaped '.' characters */
  1013.         if(strcmp(buf,"..\n") == 0) {
  1014.             buf[1] = '\n';
  1015.             buf[2] = '\0';
  1016.             n--;
  1017.         }
  1018.  
  1019.         request->chars += n;
  1020.  
  1021.         if ((newline = (struct textline *) malloc(MINTEXTLINESIZE+n+1)) == NULLTEXTLINE) {
  1022.             error(2,"NNTP Can't allocate header buffer");
  1023.             return -1;
  1024.         }
  1025.         newline->nextline = NULLTEXTLINE;
  1026.         memcpy(newline->text,buf,n+1);
  1027.  
  1028.         if (lastheaderline == NULLTEXTLINE)
  1029.             request->header = newline;
  1030.         else
  1031.             lastheaderline->nextline = newline;
  1032.         lastheaderline = newline;
  1033.  
  1034.         if (request->status != reqkeep)
  1035.             scankilllists(buf,(int *) &(request->status));
  1036.     }
  1037.     sprintf(buf,"NNTP Receive error after %d lines", nlines);
  1038.     error(2,buf);
  1039.     return -1;
  1040. }
  1041.  
  1042.  
  1043. static void
  1044. moveheadertofile(fp,request)
  1045. FILE *fp;
  1046. struct nntprequest *request;
  1047. {
  1048.     struct textline *doneline, *thisline = request->header;
  1049.  
  1050.     while (thisline != NULLTEXTLINE) {
  1051.         fputs(thisline->text,fp);
  1052.         doneline = thisline;
  1053.         thisline = thisline->nextline;
  1054.         free(doneline);
  1055.     }
  1056.     request->header = NULLTEXTLINE;
  1057. }
  1058.  
  1059.  
  1060. static int
  1061. gettxt(fp,request)
  1062. FILE *fp;
  1063. struct nntprequest *request;
  1064. {
  1065.     char buf[NNTPMAXLEN];
  1066.     int n, nlines = 0;
  1067.     unsigned long nchars = 0L;
  1068.     unsigned long endtime, starttime = msclock();
  1069.  
  1070.     while ((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
  1071.  
  1072.         nntp->receivedtotalchars += n;
  1073.         nchars += n;
  1074.  
  1075.         if (nntptrace >= 4)
  1076.             tprintf("<==%s", buf);
  1077.  
  1078.         if(strcmp(buf,".\n") == 0) {
  1079.             if (request != NULLNNTPREQUEST)
  1080.                 request->lines += nlines;
  1081.             if (nntptrace >= 3) {
  1082.                 endtime = msclock();
  1083.                 tprintf("<--%d lines, %lu chars, %lu sec (%lu bytes/sec)\n",
  1084.                     nlines,
  1085.                     nchars,
  1086.                     (endtime-starttime)/1000UL,
  1087.                     unitspersecond(nchars,endtime-starttime));
  1088.             }
  1089.             return nlines;
  1090.         }
  1091.  
  1092.         nlines++;
  1093.  
  1094.         /* check for escaped '.' characters */
  1095.         if(strcmp(buf,"..\n") == 0) {
  1096.             buf[1] = '\n';
  1097.             buf[2] = '\0';
  1098.             n--;
  1099.         }
  1100.  
  1101.         if (request != NULLNNTPREQUEST)
  1102.             request->chars += n;
  1103.  
  1104.         (void) fputs(buf,fp);
  1105.     }
  1106.     sprintf(buf,"NNTP Receive error after %d lines", nlines);
  1107.     error(2,buf);
  1108.     return -1;
  1109. }
  1110.  
  1111.  
  1112. static int
  1113. putarticle(msgf,msgfpos,request)
  1114. FILE *msgf;
  1115. long int *msgfpos;
  1116. struct nntprequest *request;
  1117.  
  1118. #if NEWSBATCH /* articles saved for batching news reader */
  1119. {
  1120.     char buf[NNTPMAXLEN];
  1121.     int duphandle;
  1122.  
  1123.     (void) fflush(msgf);
  1124.  
  1125.     if (ferror(msgf)) {
  1126.         error(1,"NNTP Error writing newsbatch file");
  1127.         return -1;
  1128.     }
  1129.  
  1130.     (void) fseek(msgf,*msgfpos,SEEK_SET);
  1131.     fprintf(msgf,NEWSBATCHFORMAT,request->chars);
  1132.     (void) fflush(msgf);
  1133.  
  1134.     if (nntpsafety) {
  1135.         duphandle = dup(fileno(msgf));
  1136.         close(duphandle);
  1137.     }
  1138.  
  1139.     if (nntpverbose) {
  1140.         (void) fseek(msgf,*msgfpos,SEEK_SET);
  1141.         while(fgets(buf,NNTPMAXLEN,msgf) != NULLCHAR)
  1142.             if(buf[0] == '\n' || strnicmp(buf,"Newsgroups: ",12) == 0)
  1143.                 break;
  1144.         if (strnicmp(buf,"Newsgroups: ",12) == 0)
  1145.             rip(buf+12);
  1146.         else
  1147.             buf[12] = '\0';
  1148.         tprintf("News arrived (%u/%u): %s, %s",
  1149.             nntp->getreceived + nntp->newheadersreceived + nntp->newreceived + 1,
  1150.             (nntp->getarticles - nntp->getinvalid - nntp->getunavailable) + 
  1151.                 (nntp->newarticles - nntp->newduplicates - nntp->newunavailable),
  1152.             buf+12,
  1153.             request->msgid);
  1154.     }
  1155.  
  1156.     /* set new length */
  1157.     (void) fseek(msgf,0L,SEEK_END);
  1158.     *msgfpos = ftell(msgf);
  1159.     fprintf(msgf,NEWSBATCHFORMAT,0UL);
  1160.  
  1161.     return 0;
  1162. }
  1163. #else /* articles saved for mail style news reader */
  1164. {
  1165.     char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  1166.     FILE *fp;
  1167.     char *cp;
  1168.     int lines;
  1169.  
  1170.     /* convert the article into mail format */
  1171.     rewind(msgf);
  1172.     froml[0] = '\0';
  1173.     newgl[0] = '\0';
  1174.     while(fgets(buf,NNTPMAXLEN,msgf) != NULLCHAR) {
  1175.         if(strnicmp(buf,"From: ",6) == 0) {
  1176.             time_t t;
  1177.             rip(&buf[6]);
  1178.             time(&t);
  1179.             sprintf(froml,"From %s %s",&buf[6], ctime(&t));
  1180.             if(newgl[0] != '\0')
  1181.                 break;
  1182.         }
  1183.         if(strnicmp(buf,"Newsgroups: ",12) == 0) {
  1184.             strcpy(newgl,&buf[12]);
  1185.             rip(newgl);
  1186.             if(froml[0] != '\0')
  1187.                 break;
  1188.         }
  1189.         /* invalid article - missing 'From:' or 'Newsgroups:' line */
  1190.         if (strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  1191.             if (nntptrace >= 2)
  1192.                 tprintf("NNTP Invalid article received: %s, article %s",newgl,request->msgid);
  1193.             return 0;
  1194.         }
  1195.     }
  1196.     sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  1197.     for(cp=newgl;;++cp) {
  1198.         if(*cp == '.') {
  1199. #ifdef UNIX
  1200.             mkdir(buf,0755); /* create a subdirectory, if necessary */
  1201. #else
  1202.             mkdir(buf); /* create a subdirectory, if necessary */
  1203. #endif
  1204.             strcat(buf,"/");
  1205.             continue;
  1206.         }
  1207.         if(*cp == ',' || *cp == '\0') {
  1208.             char tempdir[80], prefix[20], *p;
  1209.             strcpy(tempdir, buf);
  1210.             if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  1211.                 *p++ = '\0';
  1212.                 strcpy(prefix, p);
  1213.             }
  1214.             if (mlock(tempdir, prefix)) {
  1215.                 if (nntptrace >= 2)
  1216.                     tprintf("NNTP Group '%s' is locked\n", buf);
  1217.                 return -1;
  1218.             }
  1219.             strcat(buf,".txt");
  1220.             /* open the mail file */
  1221.             if (nntptrace >= 2)
  1222.                 tprintf("Writing article to '%s'\n", buf);
  1223.             if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  1224.                 fputs(froml,fp);
  1225.                 rewind(msgf);
  1226.                 lines = request->lines;
  1227.                 while((fgets(buf,NNTPMAXLEN,msgf) != NULLCHAR) && lines--) {
  1228.                     /* for UNIX mail compatiblity */
  1229.                     if(strncmp(buf,"From ",5) == 0)
  1230.                         putc('>',fp);
  1231.                     fputs(buf,fp);
  1232.                 }
  1233.                 putc('\n',fp);
  1234.                 fclose(fp);
  1235.             }
  1236.             rmlock(tempdir, prefix);
  1237.             if (*cp == '\0') 
  1238.                 break;
  1239.             else
  1240.                 sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  1241.             continue;
  1242.         }
  1243.         buf[strlen(buf)+1] = '\0';
  1244.         buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  1245.     }
  1246.     rewind(msgf);
  1247.  
  1248.     if (nntpverbose)
  1249.         tprintf("News arrived (%u/%u): %s, %s",
  1250.             nntp->getreceived + nntp->newheadersreceived + nntp->newreceived + 1,
  1251.             (nntp->getarticles - nntp->getinvalid - nntp->getunavailable) + 
  1252.                 (nntp->newarticles - nntp->newduplicates - nntp->newunavailable),
  1253.             buf+12,
  1254.             request->msgid);
  1255.  
  1256.     return 0;
  1257. }
  1258. #endif
  1259.  
  1260.  
  1261. static void
  1262. nntp_init(argc,argv,parentproc)
  1263. int argc;
  1264. void *argv,*parentproc;
  1265. {
  1266.     createhashtable();
  1267.     createkilllists();    
  1268.  
  1269.     psignal(parentproc,0);
  1270.     nntp->childproc = NULLPROC;
  1271. }
  1272.  
  1273.  
  1274. static void
  1275. nntp_send(argc,argv,parentproc)
  1276. int argc;
  1277. void *argv,*parentproc;
  1278. {
  1279.     int quit = FALSE;
  1280.  
  1281.     if (nntp->getmsgf != NULLFILE)
  1282.         rewind(nntp->getmsgf);
  1283.     rewind(nntp->newmsgf);
  1284.  
  1285.     while (!quit) {
  1286.  
  1287.         switch (nntp->requesthead->status) {
  1288.  
  1289.             case reqempty:
  1290.                 if (nntp->getmsgf != NULLFILE &&
  1291.                         fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR) {
  1292.                     do { /* Find a line with a valid message id */
  1293.                         char *cp = nntp->requesthead->msgid;
  1294.                         nntp->getarticles++;
  1295.                         if (*cp++ != '<') {
  1296.                             nntp->getinvalid++;
  1297.                             if (!nntp->quit && !main_exit)
  1298.                                 pwait(NULL);
  1299.                             continue;
  1300.                         }
  1301.                         while (*cp != '>' && isgraph(*cp))
  1302.                             cp++;
  1303.                         if (*cp++ == '>') {
  1304.                             *cp++ = '\n';
  1305.                             *cp = '\0';
  1306.                             nntp->requesthead->status = reqgetarticle;
  1307.                             break;
  1308.                         }
  1309.                         nntp->getinvalid++;
  1310.                         if (!nntp->quit && !main_exit)
  1311.                             pwait(NULL);
  1312.                     } while (fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR);
  1313.                 }
  1314.                 if (nntp->requesthead->status == reqempty &&
  1315.                         fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->newmsgf) != NULLCHAR) {
  1316.                     do {
  1317.                         if (!isinhistory(nntp->requesthead->msgid)) {
  1318.                             if (nntp->killlist == NULLTEXTLINE)
  1319.                                 nntp->requesthead->status = reqnewarticle;
  1320.                             else
  1321.                                 nntp->requesthead->status = reqheader;
  1322.                             break;
  1323.                         }
  1324.                         if (nntpverbose)
  1325.                             tprintf("News duplicate: article %s",nntp->requesthead->msgid);
  1326.                         nntp->newduplicates++;
  1327.                         if (!nntp->quit && !main_exit)
  1328.                             pwait(NULL);
  1329.                     } while (fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->newmsgf) != NULLCHAR);
  1330.                 }
  1331.                 if (nntp->requesthead->status == reqempty)
  1332.                     nntp->requesthead->status = reqend;
  1333.                 break;
  1334.  
  1335.             case reqkeep:
  1336.                 nntp->requesthead->status = reqbody;
  1337.                 break;
  1338.  
  1339.             case reqend:
  1340.                 nntp->requesthead->status = reqquit;
  1341.                 break;
  1342.  
  1343.             default: /* should never get here */
  1344.                 error(2,"NNTP Send internal error 1");
  1345.                 nntp->requesthead->status = reqquit;
  1346.         }
  1347.  
  1348.         if (!nntpbatch && nntp->requesthead != nntp->requesttail)
  1349.             pwait(Curproc);
  1350.  
  1351.         if (nntp->quit || main_exit) {
  1352.             psignal(parentproc,0);
  1353.             break;
  1354.         }
  1355.  
  1356.         switch (nntp->requesthead->status) {
  1357.             case reqend:
  1358.                 break;
  1359.             case reqquit:
  1360.                 if (nntptrace >= 3)
  1361.                     tprintf("==>QUIT\n");
  1362.                 usprintf(nntp->s,"QUIT\n");
  1363.                 quit = TRUE;
  1364.                 break;
  1365.             case reqgetarticle:
  1366.             case reqnewarticle:
  1367.                 if (nntptrace >= 3)
  1368.                     tprintf("==>ARTICLE %s",nntp->requesthead->msgid);
  1369.                 usprintf(nntp->s,"ARTICLE %s",nntp->requesthead->msgid);
  1370.                 break;
  1371.             case reqheader:
  1372.                 if (nntptrace >= 3)
  1373.                     tprintf("==>HEAD %s",nntp->requesthead->msgid);
  1374.                 usprintf(nntp->s,"HEAD %s",nntp->requesthead->msgid);
  1375.                 break;
  1376.             case reqbody:
  1377.                 if (nntptrace >= 3)
  1378.                     tprintf("==>BODY %s",nntp->requesthead->msgid);
  1379.                 usprintf(nntp->s,"BODY %s",nntp->requesthead->msgid);
  1380.                 break;
  1381.             default: /* should never get here */
  1382.                 error(2,"NNTP Send internal error 2");
  1383.                 if (nntptrace >= 3)
  1384.                     tprintf("==>QUIT\n");
  1385.                 usprintf(nntp->s,"QUIT\n");
  1386.                 quit = TRUE;
  1387.         }
  1388.  
  1389.         if (nntpbatch && nntp->requesthead->nextrequest == nntp->requesttail)
  1390.             pwait(Curproc);
  1391.  
  1392.         nntp->requesthead = nntp->requesthead->nextrequest;
  1393.         psignal(parentproc,0);
  1394.  
  1395.     }
  1396.  
  1397.     /* let the folks know we've gone */
  1398.     nntp->childproc = NULLPROC;
  1399. }
  1400.  
  1401.  
  1402. static void
  1403. nntp_main(i1,tp,v1)
  1404. int i1;
  1405. void *tp, *v1;
  1406. {
  1407.     struct nntpservers *np = (struct nntpservers *) tp;
  1408.     struct sockaddr_in fsocket;
  1409.     int reply, lines, i;
  1410.     char buf1[NNTPMAXLEN], buf2[NNTPMAXLEN], *cp;
  1411.     FILE *tmpf1, *tmpf2, *gettmpf, *msgf = NULLFILE;
  1412.     long int tmpfpos, msgfpos;
  1413.     char datenow[14], lastdate[14];
  1414.  
  1415.     if (nntptrace >= 3)
  1416.         tprintf("NNTP daemon entered, target = %s\n",np->name);
  1417.  
  1418.     if (nntp != NULLNNTPSESSION) { /* nntp already active */
  1419.         error(2,"NNTP Already active");
  1420.         start_timer(&np->nntpcli_t);
  1421.         return;
  1422.     }
  1423.  
  1424.     { /* check the time - keep a record for updating the data file */
  1425.         time_t t;
  1426.         struct tm *ltm;
  1427.         int now;
  1428.  
  1429.         time(&t);    /* more portable than gettime() */
  1430.         t -= 120L;                        /* 2 minute fudge factor            */
  1431.         ltm = localtime(&t);
  1432.         now = ltm->tm_hour * 100 + ltm->tm_min;
  1433.         if ((np->lowtime < np->hightime) ? /* doesn't cross midnight */
  1434.             (now < np->lowtime || now >= np->hightime) :
  1435.             (now < np->lowtime && now >= np->hightime)) {
  1436.                 sprintf(buf2,"NNTP Window to '%s' not open", np->name);
  1437.                 error(2,buf2);
  1438.                 start_timer(&np->nntpcli_t);
  1439.                 return;
  1440.             }
  1441.         ltm = gmtime(&t);
  1442.         sprintf(datenow,"%02d%02d%02d %02d%02d%02d",ltm->tm_year%100,ltm->tm_mon+1,
  1443.                 ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  1444.     }
  1445.  
  1446.     if (availmem() < Memthresh+sizeof(struct nntpsession) ||
  1447.         (nntp = (struct nntpsession *) calloc(1,sizeof(struct nntpsession))) == NULLNNTPSESSION) {
  1448.         error(2,"NNTP Memory too low");
  1449.         start_timer(&np->nntpcli_t);
  1450.         return;
  1451.     }
  1452.     nntp->s = -1;
  1453.  
  1454.     if (nntpverbose)
  1455.         tprintf("Trying to connect to news server %s ....\n",np->name);
  1456.  
  1457.     /* set up a circular list of request buffers */
  1458.     {
  1459.         struct nntprequest *head, *tail;
  1460.         if (availmem() < Memthresh+sizeof(struct nntprequest) ||
  1461.             (head = tail = 
  1462.                 (struct nntprequest *) calloc(1,sizeof(struct nntprequest))) == NULLNNTPREQUEST) {
  1463.             error(2,"NNTP Memory too low");
  1464.             goto quit;
  1465.         }
  1466.         for (i = 1; i < nntpbatchsize || i < 2; i++) {
  1467.             if (availmem() < Memthresh+sizeof(struct nntprequest) ||
  1468.                 (tail->nextrequest =
  1469.                     (struct nntprequest *) calloc(1,sizeof(struct nntprequest))) == NULLNNTPREQUEST) {
  1470.                 error(2,"NNTP Memory too low");
  1471.                 goto quit;
  1472.             }
  1473.             tail = tail->nextrequest;
  1474.         }
  1475.         nntp->requesttail = nntp->requesthead = tail->nextrequest = head;
  1476.     }
  1477.  
  1478.  
  1479.     /* Find the server and try to connect */
  1480.     fsocket.sin_addr.s_addr = resolve(np->name);
  1481.     if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  1482.         sprintf(buf2,"NNTP Can't resolve host: %s", np->name);
  1483.         error(2,buf2);
  1484.         goto quit;
  1485.     }
  1486.     fsocket.sin_family = AF_INET;
  1487.     fsocket.sin_port = IPPORT_NNTP;
  1488.     nntp->s = socket(AF_INET,SOCK_STREAM,0);
  1489.     sockmode(nntp->s,SOCK_ASCII);
  1490.  
  1491.     alarm (connect_wait_val);
  1492.     if(connect(nntp->s,(char *)&fsocket,SOCKSIZE) == -1){
  1493.         if (!run_timer(&Curproc->alarm))
  1494.             sprintf(buf2,"NNTP Connect failed: timeout");
  1495.         else {
  1496.             alarm (0L);
  1497.             cp = sockerr(nntp->s);
  1498.             sprintf(buf2,"NNTP Connect failed: %s",cp != NULLCHAR ? cp : "reason unknown");
  1499.         }
  1500.         error(2,buf2);
  1501.         goto quit;
  1502.     }
  1503.     alarm (0L);
  1504.     nntp->startsession = msclock();
  1505.  
  1506.     if (nntpverbose)
  1507.         tprintf("Connected to news server\n");
  1508.  
  1509.  
  1510.     /* Open the history file */
  1511.     sprintf(buf1,"%s/history",Newsdir);
  1512.     if (mlock(Newsdir,"history")) {
  1513.         sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1514.         error(2,buf2);
  1515.         goto quit;
  1516.     }
  1517.     if((nntp->historyf = fopen(buf1,APPEND_TEXT)) == NULLFILE) {
  1518.         sprintf(buf2,"NNTP Can't open file %s",buf1);
  1519.         error(1,buf2);
  1520.         rmlock(Newsdir, "history");
  1521.         goto quit;
  1522.     }
  1523.  
  1524.     /* Build internal tables */
  1525.     nntp->childproc = newproc("NNTP client init", 1024, nntp_init, 0, NULL, Curproc, 0);
  1526.  
  1527.  
  1528.     /* Eat the banner */
  1529.     reply = getreply();
  1530.     if(!(reply == 200 || reply == 201)) {
  1531.         sprintf(buf2,"NNTP Bad reply on banner (response was %d)",reply);
  1532.         error(1,buf2);
  1533.         goto quit;
  1534.     }
  1535.  
  1536.  
  1537.     /* Open nntp.dat */
  1538.     sprintf(buf1,"%s/nntp.dat",Newsdir);
  1539.     if (mlock(Newsdir,"nntp")) {
  1540.         sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1541.         error(2,buf2);
  1542.         goto quit;
  1543.     }
  1544.     if ((tmpf1 = fopen(buf1,APPEND_TEXT)) == NULLFILE) {
  1545.         sprintf(buf2,"NNTP Can't open file %s",buf1);
  1546.         error(1,buf2);
  1547.         rmlock(Newsdir,"nntp");
  1548.         goto quit;
  1549.     }
  1550.  
  1551.     /* Open temporary file for message ids */
  1552.     if ((nntp->newmsgf = tmpfile()) == NULLFILE) {
  1553.         error(1,"NNTP Can't open temporary file");
  1554.         fclose(tmpf1);
  1555.         rmlock(Newsdir,"nntp");
  1556.         goto quit;
  1557.     }
  1558.  
  1559.     /* Look for a line in the data file with a matching server name */
  1560.     rewind(tmpf1);
  1561.     strcpy(lastdate,"700101 000000");
  1562.     i = strlen(np->name);
  1563.     cp = buf1+i;
  1564.     while (fgets(buf1,NNTPMAXLEN,tmpf1) != NULLCHAR) {
  1565.         if (strnicmp(buf1,np->name,i) == 0 && isspace(*cp)) {
  1566.             unsigned long date, time;
  1567.             if (sscanf(cp," %6lu %6lu",&date,&time) == 2)
  1568.                 sprintf(lastdate,"%06lu %06lu",date,time);
  1569.             break;
  1570.         }
  1571.     }
  1572.  
  1573.     /* Look for a newsgroup list in the data file */
  1574.     i = -1;
  1575.     while (fgets(buf2,NNTPMAXLEN,tmpf1) != NULLCHAR)
  1576.         if ((i = stripgroups(buf2)) != 0)
  1577.             break;
  1578.  
  1579.     /* if there is a newsgroup list in the data file */
  1580.     if (i > 0) {
  1581.  
  1582.         do {
  1583.             if (i > NNTPMAXGROUPLIST) {
  1584.                 error(1,"NNTP Newsgroup line too long");
  1585.                 fclose(tmpf1);
  1586.                 rmlock(Newsdir,"nntp");
  1587.                 goto quit;
  1588.             }
  1589.             strcpy(buf1,buf2); i = -1;
  1590.  
  1591.             /* add other entries until no more entries, blank line or the list is full */
  1592.             while ((fgets(buf2,NNTPMAXLEN,tmpf1) != NULLCHAR) && ((i = stripgroups(buf2)) > 0))
  1593.                 if (strlen(buf1)+i >= NNTPMAXGROUPLIST)
  1594.                     break;
  1595.                 else {
  1596.                     strcat(buf1,",");
  1597.                     strcat(buf1,buf2); i = -1;
  1598.                 }
  1599.  
  1600.             /* send the request and get the response */
  1601.             if (nntptrace >= 3)
  1602.                 tprintf("==>NEWNEWS %s %s GMT\n", buf1, lastdate);
  1603.             usprintf(nntp->s,"NEWNEWS %s %s GMT\n", buf1, lastdate);
  1604.  
  1605.             if ((reply = getreply()) != 230 || (lines = gettxt(nntp->newmsgf,NULL)) == -1) {
  1606.                 sprintf(buf2,"NNTP Error while getting NEWNEWS (response was %d)",reply);
  1607.                 error(1,buf2);
  1608.                 fclose(tmpf1);
  1609.                 rmlock(Newsdir,"nntp");
  1610.                 goto quit;
  1611.             }
  1612.             nntp->newarticles += lines;
  1613.  
  1614.             /* if we're on a blank line then look for more newsgroup entries */
  1615.             if (i == 0) {
  1616.                 i = -1;
  1617.                 while (fgets(buf2,NNTPMAXLEN,tmpf1) != NULLCHAR)
  1618.                     if ((i = stripgroups(buf2)) != 0)
  1619.                         break;
  1620.             }
  1621.         } while (i > 0);
  1622.  
  1623.     } else { /* use the addservers entry, or the groups entry, or get everything (!) */
  1624.  
  1625.         if (nntptrace >= 3)
  1626.             tprintf("==>NEWNEWS %s %s GMT\n",
  1627.                 np->groups ? np->groups : (Nntpgroups ? Nntpgroups : "*"), lastdate);
  1628.         nntp_reg ();
  1629.         usprintf(nntp->s,"NEWNEWS %s %s GMT\n",
  1630.             np->groups ? np->groups : (Nntpgroups ? Nntpgroups : "*"), lastdate);
  1631.  
  1632.         if ((reply = getreply()) != 230 || (lines = gettxt(nntp->newmsgf,NULL)) == -1) {
  1633.             sprintf(buf2,"NNTP Error while getting NEWNEWS (response was %d)",reply);
  1634.             error(1,buf2);
  1635.             fclose(tmpf1);
  1636.             rmlock(Newsdir,"nntp");
  1637.             goto quit;
  1638.         }
  1639.         nntp->newarticles = lines;
  1640.     }
  1641.  
  1642.     fclose(tmpf1);
  1643.     rmlock(Newsdir,"nntp");
  1644.  
  1645.     if (nntp->quit || main_exit)
  1646.         goto quit;
  1647.  
  1648.     if (nntpverbose && nntp->newarticles != 0)
  1649.         tprintf("News available: %d articles\n",nntp->newarticles);
  1650.  
  1651.  
  1652.     if (nntpnewgroups) {
  1653.  
  1654.         /* Open the newgroups file */
  1655.         sprintf(buf1,"%s/newgroup",Newsdir);
  1656.         if (mlock(Newsdir,"newgroup")) {
  1657.             sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1658.             error(2,buf2);
  1659.             goto quit;
  1660.         }
  1661.         if((tmpf1 = fopen(buf1,APPEND_TEXT)) == NULLFILE) {
  1662.             sprintf(buf2,"NNTP Can't open file %s",buf1);
  1663.             error(1,buf2);
  1664.             rmlock(Newsdir,"newgroup");
  1665.             goto quit;
  1666.         }
  1667.  
  1668.         if (nntptrace >= 3)
  1669.             tprintf("==>NEWGROUPS %s GMT\n", lastdate);
  1670.         usprintf(nntp->s,"NEWGROUPS %s GMT\n", lastdate);
  1671.  
  1672.         if ((reply = getreply()) != 231 || (lines = gettxt(tmpf1,NULL)) == -1) {
  1673.             sprintf(buf2,"NNTP Error while getting NEWGROUPS (response was %d)",reply);
  1674.             error(1,buf2);
  1675.             fclose(tmpf1);
  1676.             rmlock(Newsdir,"newgroup");
  1677.             goto quit;
  1678.         }
  1679.  
  1680.         fflush(tmpf1);
  1681.         fseek(tmpf1,0L,SEEK_END);
  1682.         tmpfpos = ftell(tmpf1);
  1683.         fclose(tmpf1);
  1684.         if (tmpfpos == 0L) {
  1685.             sprintf(buf1,"%s/newgroup",Newsdir);
  1686.             remove(buf1);
  1687.         }
  1688.         rmlock(Newsdir,"newgroup");
  1689.   
  1690.         nntp->groupsreceived = lines;
  1691.         if (nntpverbose && nntp->groupsreceived != 0)
  1692.             tprintf("News available: %d new groups\n",nntp->groupsreceived);
  1693.     }
  1694.  
  1695.  
  1696.     /* If it exists, open the get file */
  1697.     sprintf(buf1,"%s/get",Newsdir);
  1698.     if (access(buf1,0) == 0) {
  1699.         if (mlock(Newsdir,"get")) {
  1700.             sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1701.             error(2,buf2);
  1702.         } else {
  1703.             if ((nntp->getmsgf = fopen(buf1,READ_TEXT)) == NULLFILE) {
  1704.                 sprintf(buf2,"NNTP Can't open file %s",buf1);
  1705.                 error(1,buf2);
  1706.                 rmlock(Newsdir,"get");
  1707.             } else {
  1708.                 /* Open temporary file for unavailable message ids */
  1709.                 sprintf(buf1,"%s/get.tmp",Newsdir);
  1710.                 if((gettmpf = fopen(buf1,WRITE_TEXT)) == NULLFILE) {
  1711.                     sprintf(buf2,"NNTP Can't open file %s",buf1);
  1712.                     error(1,buf2);
  1713.                     fclose(nntp->getmsgf);
  1714.                     nntp->getmsgf = NULLFILE;
  1715.                     rmlock(Newsdir,"get");
  1716.                 }
  1717.             }
  1718.         }
  1719.     }
  1720.  
  1721. #if NEWSBATCH
  1722.     /* Open the batch file */
  1723.     sprintf(buf1,"%s/batch.txt",News_spool ? News_spool : Mailspool);
  1724.     if (mlock(News_spool ? News_spool : Mailspool, "batch")) {
  1725.         sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1726.         error(2,buf2);
  1727.         goto quit;
  1728.     }
  1729.     if((msgf = fopen(buf1,RW_LOOKUP_TEXT)) == NULLFILE &&
  1730.         (msgf = fopen(buf1,RW_CREATE_TEXT)) == NULLFILE) {
  1731.         sprintf(buf2,"NNTP Can't open file %s",buf1);
  1732.         error(1,buf2);
  1733.         rmlock(News_spool ? News_spool : Mailspool, "batch");
  1734.         goto quit;
  1735.     }
  1736.     fseek(msgf,0L,SEEK_END);
  1737.     msgfpos = ftell(msgf);
  1738.     fprintf(msgf,NEWSBATCHFORMAT,0UL);
  1739. #else
  1740.     /* Open temporary file for messages */
  1741.     if((msgf = tmpfile()) == NULLFILE) {
  1742.         error(1,"NNTP Can't open temporary file");
  1743.         goto quit;
  1744.     }
  1745. #endif
  1746.  
  1747.  
  1748.     /* wait for the tables to be built then start up the tx proc */
  1749.     if (nntp->childproc != NULLPROC)
  1750.         if (pwait(Curproc) != 0)
  1751.             goto quit;
  1752.  
  1753.     if (nntp->quit || main_exit)
  1754.         goto quit;
  1755.  
  1756.     nntp->childproc = newproc("NNTP client send", 1024, nntp_send, 0, NULL, Curproc, 0);
  1757.  
  1758.     nntp->receivednewschars = nntp->receivedtotalchars;
  1759.     nntp->startarticles = msclock();
  1760.  
  1761.     while (!nntp->quit && !main_exit) {
  1762.  
  1763.         if (nntp->requesthead == nntp->requesttail) {
  1764.             if (nntp->childproc == NULLPROC) {
  1765.                 error(2,"NNTP Receive internal error");
  1766.                 goto quit;
  1767.             }
  1768.             if (pwait(Curproc) != 0)
  1769.                 goto quit;
  1770.         }
  1771.  
  1772.         if (nntp->requesttail->status != reqend) {
  1773.  
  1774.             reply = getreply();
  1775.             switch (reply) {
  1776.  
  1777.                 case 205: /* closing connection */
  1778.                     if (nntp->requesttail->status != reqquit) {
  1779.                         error(2,"NNTP Receive internal error (reply was 205)");
  1780.                         goto quit;
  1781.                     }
  1782.                     sprintf(buf1,"%s/nntp.dat",Newsdir);
  1783.                     if (mlock(Newsdir,"nntp")) {
  1784.                         sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1785.                         error(2,buf2);
  1786.                         goto quit;
  1787.                     }
  1788.                     if((tmpf1 = fopen(buf1,READ_TEXT)) == NULLFILE) {
  1789.                         sprintf(buf2,"NNTP Can't open file %s",buf1);
  1790.                         error(1,buf2);
  1791.                         rmlock(Newsdir,"nntp");
  1792.                         goto quit;
  1793.                     }
  1794.                     sprintf(buf1,"%s/nntp.tmp",Newsdir);
  1795.                     if((tmpf2 = fopen(buf1,WRITE_TEXT)) == NULLFILE) {
  1796.                         sprintf(buf2,"NNTP Can't open file %s",buf1);
  1797.                         error(1,buf2);
  1798.                         fclose(tmpf1);
  1799.                         rmlock(Newsdir,"nntp");
  1800.                         goto quit;
  1801.                     }
  1802.                     sprintf(buf1,"%s %s\n",np->name,datenow);
  1803.                     i = strlen(np->name);
  1804.                     cp = buf2+i;
  1805.                     while (fgets(buf2,NNTPMAXLEN,tmpf1) != NULLCHAR)
  1806.                         if (strnicmp(buf2,np->name,i) != 0 || !isspace(*cp))
  1807.                             fputs(buf2, tmpf2);
  1808.                         else {
  1809.                             fputs(buf1,tmpf2);
  1810.                             buf1[0] = '\0';
  1811.                         }
  1812.                     if (buf1[0] != '\0')
  1813.                         fputs(buf1,tmpf2);
  1814.                         fclose(tmpf1);
  1815.                         sprintf(buf1, "%s/nntp.dat", Newsdir);
  1816.                         sprintf(buf2, "%s/nntp.tmp", Newsdir);
  1817.                     if (ferror(tmpf2)) {
  1818.                         fclose(tmpf2);
  1819.                         remove(buf2);
  1820.                         error(1,"Error writing new nntp.dat file");
  1821.                     } else {
  1822.                         fclose(tmpf2);
  1823.                         remove(buf1);
  1824.                         rename(buf2, buf1);
  1825.                     }
  1826.                     rmlock(Newsdir,"nntp");
  1827.                     goto quit;
  1828.  
  1829.  
  1830.                 case 220: /* header and body follows */
  1831.                     if (nntp->requesttail->status != reqgetarticle &&
  1832.                         nntp->requesttail->status != reqnewarticle) {
  1833.                         error(2,"NNTP Receive internal error (reply was 220)");
  1834.                         goto quit;
  1835.                     }
  1836.                     nntp->requesttail->lines = 0;
  1837.                     nntp->requesttail->chars = 0UL;
  1838.                     if (gettxt(msgf,nntp->requesttail) == -1)
  1839.                         goto quit;
  1840.                     if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
  1841.                         goto quit;
  1842.  
  1843.                     /*
  1844.                     *    The following is a compromise: if there is a hash
  1845.                     *    collision then the id will erroneously not get added.
  1846.                     *    However, scanning the history file here will cause
  1847.                     *    problems as the child might be doing the same.
  1848.                     */
  1849.  
  1850.                     if (nntp->requesttail->status == reqgetarticle) {
  1851.                         if (!isinhashtable(nntp->requesttail->msgid))
  1852.                             addtohistory(nntp->requesttail->msgid);
  1853.                         nntp->getreceived++;
  1854.                     } else {
  1855.                         addtohistory(nntp->requesttail->msgid);
  1856.                         nntp->newreceived++;
  1857.                     }
  1858.                     nntp->receivedarticlechars += nntp->requesttail->chars;
  1859.                     nntp->requesttail->status = reqempty;
  1860.                     break;
  1861.  
  1862.  
  1863.                 case 221: /* header follows */
  1864.                     if (nntp->requesttail->status != reqheader) {
  1865.                         error(2,"NNTP Receive internal error (reply was 221)");
  1866.                         goto quit;
  1867.                     }
  1868.                     nntp->requesttail->lines = 0;
  1869.                     nntp->requesttail->chars = 0UL;
  1870.                     if (getheader(nntp->requesttail) == -1)
  1871.                         goto quit;
  1872.                     if (nntp->requesttail->status == reqkill) {
  1873.                         moveheadertofile(msgf,nntp->requesttail);
  1874.                         if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
  1875.                             goto quit;
  1876.                         addtohistory(nntp->requesttail->msgid);
  1877.                         nntp->newheadersreceived++;
  1878.                         nntp->receivedarticlechars += nntp->requesttail->chars;
  1879.                         nntp->requesttail->status = reqempty;
  1880.                     }
  1881.                     break;
  1882.  
  1883.  
  1884.                 case 222: /* body follows */
  1885.                     if (nntp->requesttail->status != reqbody) {
  1886.                         error(2,"NNTP Receive internal error (reply was 222)");
  1887.                         goto quit;
  1888.                     }
  1889.                     moveheadertofile(msgf,nntp->requesttail);
  1890.                     fputc('\n',msgf);
  1891.                     nntp->requesttail->lines++;
  1892.                     nntp->requesttail->chars++;
  1893.                     if (gettxt(msgf,nntp->requesttail) == -1)
  1894.                         goto quit;
  1895.                     if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
  1896.                         goto quit;
  1897.                     addtohistory(nntp->requesttail->msgid);
  1898.                     nntp->newreceived++;
  1899.                     nntp->receivedarticlechars += nntp->requesttail->chars;
  1900.                     nntp->requesttail->status = reqempty;
  1901.                     break;
  1902.  
  1903.  
  1904.                 case 430: /* no such article */
  1905.                     if (nntp->requesttail->status != reqgetarticle &&
  1906.                          nntp->requesttail->status != reqnewarticle &&
  1907.                          nntp->requesttail->status != reqheader) {
  1908.                         error(2,"NNTP Receive internal error (reply was 430)");
  1909.                         goto quit;
  1910.                     }
  1911.                     if (nntp->requesttail->status == reqgetarticle) {
  1912.                         fputs(nntp->requesttail->msgid,gettmpf);
  1913.                         nntp->getunavailable++;
  1914.                     } else
  1915.                         nntp->newunavailable++;
  1916.                     if (nntpverbose)
  1917.                         tprintf("News unavailable: article %s",nntp->requesttail->msgid);
  1918.                     nntp->requesttail->status = reqempty;
  1919.                     break;
  1920.  
  1921.  
  1922.                 case -1: /* error */
  1923.                     error(2,"NNTP Receive error");
  1924.                     goto quit;
  1925.  
  1926.  
  1927.                 default:
  1928.                     sprintf(buf2,"NNTP Unexpected reply from server (reply was %d)",reply);
  1929.                     error(2,buf2);
  1930.                     goto quit;
  1931.  
  1932.             }
  1933.         }
  1934.  
  1935.         /* move on to next request */
  1936.         nntp->requesttail = nntp->requesttail->nextrequest;
  1937.         psignal(nntp->childproc,0);
  1938.     }
  1939.         
  1940. quit:
  1941.  
  1942.     nntp->endsession = msclock();
  1943.     nntp->receivednewschars = nntp->receivedtotalchars - nntp->receivednewschars;
  1944.  
  1945.     /* tidy up as necessary */
  1946.  
  1947. #if NEWSBATCH
  1948.     if (msgf != NULLFILE) {
  1949.         fflush(msgf);
  1950.         chsize(fileno(msgf),msgfpos);
  1951.         fclose(msgf);
  1952.         if (msgfpos == 0L) {
  1953.             sprintf(buf1,"%s/batch.txt",News_spool ? News_spool : Mailspool);
  1954.             (void) remove(buf1);
  1955.         }
  1956.         rmlock(News_spool ? News_spool : Mailspool, "batch");
  1957.     }
  1958. #else
  1959.     if (msgf != NULLFILE) {
  1960.         fclose(msgf);
  1961.     }
  1962. #endif
  1963.  
  1964.     nntp->quit = TRUE;
  1965.     if (nntp->childproc != NULLPROC)
  1966.         pwait(NULL);
  1967.     if (nntp->childproc != NULLPROC)
  1968.         killproc(nntp->childproc);
  1969.  
  1970.     /* Update get file with message ids we didn't get */
  1971.     if (nntp->getmsgf != NULLFILE) {
  1972.         rewind(nntp->getmsgf);
  1973.         for (i = 0; i < (nntp->getinvalid + nntp->getunavailable + nntp->getreceived); i++)
  1974.             fgets(buf1,NNTPMAXLEN,nntp->getmsgf);
  1975.         while (fgets(buf1,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR)
  1976.             fputs(buf1, gettmpf);
  1977.         fclose(nntp->getmsgf);
  1978.         sprintf(buf1, "%s/get", Newsdir);
  1979.         sprintf(buf2, "%s/get.tmp", Newsdir);
  1980.         if (ferror(gettmpf)) {
  1981.             fclose(gettmpf);
  1982.             remove(buf2);
  1983.             error(1,"Error writing new get file");
  1984.         } else {
  1985.             remove(buf1);
  1986.             fflush(gettmpf);
  1987.             fseek(gettmpf,0L,SEEK_END);
  1988.             tmpfpos = ftell(gettmpf);
  1989.             fclose(gettmpf);
  1990.             if (tmpfpos == 0L)
  1991.                 remove(buf2);
  1992.             else
  1993.                 rename(buf2, buf1);
  1994.         }
  1995.         rmlock(Newsdir,"get");
  1996.     }            
  1997.  
  1998.     if (nntp->newmsgf != NULLFILE)
  1999.         fclose(nntp->newmsgf);
  2000.  
  2001.     if (nntp->historyf != NULLFILE) {
  2002.         fclose(nntp->historyf);
  2003.         rmlock(Newsdir, "history");
  2004.     }
  2005.     if (nntp->hashtable != NULL)
  2006.         free(nntp->hashtable);
  2007.  
  2008.  
  2009.     /* Free all dynamically allocated buffer space */
  2010.     {
  2011.         struct nntprequest
  2012.             *tail = nntp->requesthead,
  2013.             *head = tail->nextrequest;
  2014.         tail->nextrequest = NULLNNTPREQUEST;
  2015.         while (head != NULLNNTPREQUEST) {
  2016.             freetextlinelist(&head->header);
  2017.             tail = head;
  2018.             head = head->nextrequest;
  2019.             free(tail);
  2020.         }
  2021.         freetextlinelist(&nntp->killlist);
  2022.         freetextlinelist(&nntp->keeplist);
  2023.     }
  2024.  
  2025.  
  2026.     /* Output statistics */
  2027.     if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0) {
  2028.         sprintf(buf2,"News summary: %u articles (%lu bytes) in %lu sec (%lu bytes/sec)",
  2029.                 nntp->getreceived + nntp->newheadersreceived + nntp->newreceived,
  2030.                 nntp->receivedarticlechars,
  2031.                 (nntp->endsession - nntp->startarticles)/1000UL,
  2032.                 unitspersecond(nntp->receivedarticlechars,nntp->endsession - nntp->startarticles));
  2033.         log(nntp->s,buf2);
  2034.         tprintf(nntpverbose ? "\n%s\n" : "%s\n",buf2);
  2035.     }
  2036.  
  2037.     if (nntp->getarticles != 0) {
  2038.         sprintf(buf2,"Get articles: %u invalid, %u unavailable, %u received",
  2039.             nntp->getinvalid,
  2040.             nntp->getunavailable,
  2041.             nntp->getreceived);
  2042.         log(nntp->s,buf2);
  2043.         if (nntpverbose)
  2044.             tprintf("%s\n",buf2);
  2045.     }
  2046.  
  2047.     if (nntp->newarticles != 0) {
  2048.         sprintf(buf2,"New articles: %u duplicate, %u unavailable, %u headers, %u complete",
  2049.             nntp->newduplicates,
  2050.             nntp->newunavailable,
  2051.             nntp->newheadersreceived,
  2052.             nntp->newreceived);
  2053.         log(nntp->s,buf2);
  2054.         if (nntpverbose)
  2055.             tprintf("%s\n",buf2);
  2056.     }
  2057.  
  2058.     if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0 ||
  2059.             nntp->getarticles != 0 || nntp->newarticles != 0) {
  2060.         sprintf(buf2,"History file: %u entries, %u complete scans",
  2061.             nntp->historyentries,
  2062.             nntp->historyscans);
  2063.         log(nntp->s,buf2);
  2064.         if (nntpverbose)
  2065.             tprintf("%s\n",buf2);
  2066.         sprintf(buf2,"Throughput  : %lu/%lu bytes in %lu/%lu sec (%lu/%lu bytes/sec)",
  2067.             nntp->receivednewschars,
  2068.             nntp->receivedtotalchars,
  2069.             (nntp->endsession - nntp->startarticles)/1000UL,
  2070.             (nntp->endsession - nntp->startsession)/1000UL,
  2071.             unitspersecond(nntp->receivednewschars,nntp->endsession - nntp->startarticles),
  2072.             unitspersecond(nntp->receivedtotalchars,nntp->endsession - nntp->startsession));
  2073.         log(nntp->s,buf2);
  2074.         if (nntpverbose)
  2075.             tprintf("%s\n\n",buf2);
  2076.     }
  2077.  
  2078.     if (nntpverbose)
  2079.         tprintf("Closing news session\n");
  2080.  
  2081.     close_s(nntp->s);
  2082.     free(nntp);
  2083.     nntp = NULLNNTPSESSION;
  2084.  
  2085.     if (nntptrace >= 3)
  2086.         tprintf("NNTP daemon exiting\n");
  2087.  
  2088.     /* Restart timer */
  2089.     start_timer(&np->nntpcli_t);
  2090.  
  2091.     return;
  2092. }
  2093.  
  2094.  
  2095. static void nntp_reg (void)
  2096.     {
  2097.     struct sockaddr_in sock;
  2098.     struct socket lsocket;
  2099.     struct socket rsocket;
  2100.     struct usock *usp;
  2101.     struct sockaddr_in *sinp;
  2102.     struct mbuf *bp;
  2103.     char buf[64];
  2104.     int s;
  2105.  
  2106.     sock.sin_family = AF_INET;
  2107.     sock.sin_port = 2911;
  2108.     if ((sock.sin_addr.s_addr = resolve ("passwd.demon.co.uk")) == 0 ||
  2109.         (s = socket (AF_INET, SOCK_DGRAM, 0)) == -1 ||
  2110.         connect (s, (char *) &sock, sizeof (sock)) == -1)
  2111.         return;
  2112.  
  2113.     (void) sprintf (buf, "%s|%s|%s\n", Version, __DATE__, __TIME__);
  2114.     bp = ambufw (strlen (buf) + 1);
  2115.     (void) strcpy (bp->data, buf);
  2116.     usp = itop(s);
  2117.     sinp = (struct sockaddr_in *) usp->name;
  2118.     lsocket.address = sinp->sin_addr.s_addr;
  2119.     lsocket.port = sinp->sin_port;
  2120.     sinp = (struct sockaddr_in *)usp->peername;
  2121.     rsocket.address = sinp->sin_addr.s_addr;
  2122.     (void) send_udp (&lsocket, &rsocket, 0, 64, bp, bp->size, 0, 0);
  2123.     close_s (s);
  2124.     }    /* static void nntp_reg (void) */
  2125.