home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 024 / psi110g.zip / NNTPCLI.C < prev    next >
C/C++ Source or Header  |  1994-08-26  |  31KB  |  966 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.  *     Jun 30, 1994  n5knx  Added timestamp to history file to aid expiration,
  32.  *            and build index if newsgroup is stored under Mailspool.  Create
  33.  *            ng.inf timestamp file if ng is in areas file.
  34.  *     Jul 1994  n5knx  reimplemented newsgroup name mapping.  Disallow kicking
  35.  *            a running process.  Avoid --more-- wait when !nntpquiet tracing.
  36.  *            Add LZW support by N8FOW.  Compensate for GMT.
  37.  */
  38. #include <sys/types.h>
  39. #include <time.h>
  40. #include <sys/timeb.h>
  41. #include <ctype.h>
  42. #ifdef  __TURBOC__
  43. #include <dir.h>
  44. #endif
  45. #include "global.h"
  46. #ifdef  NNTP
  47. #include "timer.h"
  48. #include "cmdparse.h"
  49. #include "commands.h"
  50. #include "socket.h"
  51. #include "usock.h"
  52. #include "netuser.h"
  53. #include "proc.h"
  54. #include "session.h"
  55. #include "smtp.h"
  56. #include "mailutil.h"
  57. #include "files.h"
  58. #include "bm.h"
  59. #include "index.h"
  60. #ifdef LZW
  61. #include "lzw.h"
  62. #endif
  63.   
  64. #define NNTPMAXLEN  512
  65.   
  66. struct nntpservers {
  67.     struct timer nntpcli_t;
  68.     char *name;
  69.     char *groups;
  70.     int lowtime, hightime;  /* for connect window */
  71.     struct nntpservers *next;
  72. };
  73.   
  74. #define NULLNNTP    (struct nntpservers *)NULL
  75.  
  76. struct ngmap {
  77.     char *prefix;        /* e.g. comp, rec.radio, net, talk, alt ... */
  78.     char *newname;       /* what prefix should be changed to */
  79.     struct ngmap *next;  /* link to next entry */
  80. };
  81.   
  82. static struct nntpservers *Nntpservers = NULLNNTP;
  83. static char *Nntpgroups = NULLCHAR;
  84. static unsigned short nntptrace = 1;
  85. static int nntpquiet = 0;
  86. static int Nntpfirstpoll = 5;
  87. static char *News_spool = NULL;
  88. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  89. static struct ngmap *ngmaphead = NULL;
  90.   
  91. static char validchars[] = "abcdefghijklmnopqrstuvwxyz0123456789-_.";
  92. static char WOpenMsg[] = "NNTP window to '%s' not open\n";
  93. static char CFailMsg[] = "NNTP %s: Connect failed: %s";
  94. static char BadBannerMsg[] = "NNTP %s: bad reply on banner (response was %d)";
  95. static char NoLockMsg[] = "NNTP %s: Cannot lock %s";
  96. static char NoOpenMsg[] = "NNTP %s: Cannot open %s";
  97. static char NoUpdateMsg[] = "NNTP %s: Could not update %s";
  98. static char ProtoErrMsg[] = "NNTP %s: Protocol error (response was %d)";
  99. static char GTFailMsg[] = "NNTP %s: Giving up: gettxt() failure";
  100. static char GAFailMsg[] = "NNTP %s: Giving up: could not get article";
  101.   
  102. static void nntptick __ARGS((void *tp));
  103. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  104. static int gettxt __ARGS((int s,FILE *fp));
  105. static int getreply __ARGS((int s));
  106. static int getarticle __ARGS((int s,char *msgid, char *nglist));
  107. static int dogroups __ARGS((int argc,char *argv[],void *p));
  108. static int doadds __ARGS((int argc,char *argv[],void *p));
  109. static int dodrops __ARGS((int argc,char *argv[],void *p));
  110. static int dokicks __ARGS((int argc,char *argv[],void *p));
  111. static int dolists __ARGS((int argc,char *argv[],void *p));
  112. static int donntrace __ARGS((int argc,char *argv[],void *p));
  113. static int donnquiet __ARGS((int argc,char *argv[],void *p));
  114. static int dondir __ARGS((int argc,char *argv[],void *p));
  115. static int donnfirstpoll __ARGS((int argc,char *argv[],void *p));
  116. static void make_time_string(char *string,time_t *timep);
  117.  
  118. #ifdef LZW
  119. static int donnlzw __ARGS((int argc,char *argv[],void *p));
  120. int LzwActive = 1;
  121. #endif
  122.   
  123. /* Tracing levels:
  124.     0 - no tracing
  125.     1 - serious errors reported
  126.     2 - transient errors reported
  127.     3 - session progress reported
  128.     4 - actual received articles displayed
  129.  */
  130.   
  131. static struct cmds Nntpcmds[] = {
  132.     "addserver",    doadds,     0,  3,
  133.         "nntp addserver <nntpserver> <interval> [<groups>]",
  134.     "directory",    dondir,     0,  0,  NULLCHAR,
  135.     "dropserver",   dodrops,    0,  2,  "nntp dropserver <nntpserver>",
  136.     "firstpoll",    donnfirstpoll, 0, 0, NULLCHAR,
  137.     "groups",       dogroups,   0,  0,  NULLCHAR,
  138.     "kick",         dokicks,    0,  2,  "nntp kick <nntpserver>",
  139.     "listservers",  dolists,    0,  0,  NULLCHAR,
  140. #ifdef LZW
  141.     "lzw",          donnlzw,    0,  0,  NULLCHAR,
  142. #endif
  143.     "quiet",        donnquiet,  0,  0,  NULLCHAR,
  144.     "trace",        donntrace,  0,  0,  NULLCHAR,
  145.     NULLCHAR,
  146. };
  147.   
  148. int
  149. donntp(argc,argv,p)
  150. int argc;
  151. char *argv[];
  152. void *p;
  153. {
  154.     return subcmd(Nntpcmds,argc,argv,p);
  155. }
  156.   
  157. static int
  158. doadds(argc,argv,p)
  159. int argc;
  160. char *argv[];
  161. void *p;
  162. {
  163.     struct nntpservers *np;
  164.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  165.         if(stricmp(np->name,argv[1]) == 0)
  166.             break;
  167.     if (np == NULLNNTP) {
  168.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  169.         np->name = strdup(argv[1]);
  170.         np->next = Nntpservers;
  171.         Nntpservers = np;
  172.         np->groups = NULLCHAR;
  173.         np->lowtime = np->hightime = -1;
  174.         np->nntpcli_t.func = nntptick;  /* what to call on timeout */
  175.         np->nntpcli_t.arg = (void *)np;
  176.     }
  177.     if (argc > 3) {
  178.         int i;
  179.         if (np->groups == NULLCHAR) {
  180.             np->groups = mallocw(NNTPMAXLEN);
  181.             *np->groups = '\0';
  182.         }
  183.         for (i = 3; i < argc; ++i) {
  184.             if (isdigit(*argv[i])) {
  185.                 int lh, ll, hh, hl;
  186.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  187.                 np->lowtime = lh * 100 + ll;
  188.                 np->hightime = hh * 100 + hl;
  189.             } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  190.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  191.             else {  /* it's a group, and it fits... add it to list */
  192.                 if (*np->groups != '\0')
  193.                     strcat(np->groups, ",");
  194.                 strcat(np->groups, argv[i]);
  195.             }
  196.         }
  197.         if (*np->groups == '\0') {  /* No groups specified? */
  198.             free(np->groups);
  199.             np->groups = NULLCHAR;
  200.         }
  201.     }
  202.     /* set timer duration */
  203.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  204.     start_timer(&np->nntpcli_t);        /* and fire it up */
  205.     return 0;
  206. }
  207.   
  208. static int
  209. dodrops(argc,argv,p)
  210. int argc;
  211. char *argv[];
  212. void *p;
  213. {
  214.     struct nntpservers *np, *npprev = NULLNNTP;
  215.     for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  216.         if(stricmp(np->name,argv[1]) == 0) {
  217.             stop_timer(&np->nntpcli_t);
  218.             free(np->name);
  219.             if (np->groups)
  220.                 free(np->groups);
  221.             if(npprev != NULLNNTP)
  222.                 npprev->next = np->next;
  223.             else
  224.                 Nntpservers = np->next;
  225.             free((char *)np);
  226.             return 0;
  227.         }
  228.     tputs("No such server enabled.\n");
  229.     return 0;
  230. }
  231.   
  232. static int
  233. dolists(argc,argv,p)
  234. int argc;
  235. char *argv[];
  236. void *p;
  237. {
  238.     struct nntpservers *np;
  239.     for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  240.         char tbuf[80];
  241.         if (np->lowtime != -1 && np->hightime != -1)
  242.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  243.         else
  244.             tbuf[0] = '\0';
  245.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  246.         read_timer(&np->nntpcli_t) /1000L,
  247.         dur_timer(&np->nntpcli_t) /1000L,
  248.         tbuf, np->groups ? np->groups : "");
  249.     }
  250.     return 0;
  251. }
  252.   
  253. #ifdef LZW
  254. /* Sets LzwActive flag */
  255. static int
  256. donnlzw(argc,argv,p)
  257. int argc;
  258. char *argv[];
  259. void *p;
  260. {
  261.     return setbool(&LzwActive,"NNTP lzw",argc,argv);
  262. }
  263. #endif /* LZW */
  264.   
  265. static int donntrace(argc, argv, p)
  266. int argc;
  267. char *argv[];
  268. void *p;
  269. {
  270.     return setshort(&nntptrace,"NNTP tracing",argc,argv);
  271. }
  272.   
  273. static int donnquiet(argc, argv, p)
  274. int argc;
  275. char *argv[];
  276. void *p;
  277. {
  278.     return setbool(&nntpquiet,"NNTP quiet",argc,argv);
  279. }
  280.   
  281. static int
  282. donnfirstpoll(argc,argv,p)    /* from G8FSL */
  283. int argc;
  284. char *argv[];
  285. void *p;
  286. {
  287.     return setint(&Nntpfirstpoll,"NNTP polls new server for news over last <n> days",argc,argv);
  288. }
  289.  
  290. static void
  291. make_time_string(char *string,time_t *timep)
  292. {
  293.     struct tm *stm;
  294.  
  295.     stm = localtime(timep);
  296.     sprintf(string,"%02d%02d%02d %02d%02d%02d",
  297.       stm->tm_year%100,stm->tm_mon + 1,stm->tm_mday,stm->tm_hour,
  298.       stm->tm_min,stm->tm_sec);
  299. }
  300.  
  301. static int dondir(argc, argv, p)
  302. int argc;
  303. char *argv[];
  304. void *p;
  305. {
  306.     struct ngmap *ngp, *prev_ngp;
  307.  
  308.     if (argc < 2) {
  309.         int i;
  310.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  311.         tprintf("control: %s\n", Newsdir);
  312.  
  313.         for (ngp = ngmaphead; ngp != NULL; ngp=ngp->next)
  314.              if (ngp->prefix) tprintf("%s = %s\n", ngp->prefix, ngp->newname);
  315.     } else {
  316.         char *p;
  317.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a prefix mapping */
  318.             int i;
  319.             *p++ = '\0';
  320.             for (ngp=ngmaphead, prev_ngp=NULL; ngp != NULL; prev_ngp=ngp, ngp=ngp->next)
  321.                 if (ngp->prefix)
  322.                     if (!stricmp(ngp->prefix, argv[1])) {
  323.                         if (ngp->newname) {
  324.                             free(ngp->newname);
  325.                             ngp->newname = NULLCHAR;
  326.                         }
  327.                         if (*p == '=') {
  328.                             free(ngp->prefix);
  329.                             ngp->prefix = NULLCHAR;
  330.                         } else
  331.                             ngp->newname = strdup(p);
  332.                         return 0;
  333.                     }
  334.             if (*p == '=')  /* trashing a mapping that's not there */
  335.                 return 0;
  336.             ngp=mallocw(sizeof (struct ngmap));
  337.             ngp->prefix = strdup(argv[1]);
  338.             ngp->newname = strdup(p);
  339.             ngp->next = (struct ngmap *)NULL;
  340.             if (prev_ngp) prev_ngp->next = ngp;
  341.             else ngmaphead = ngp;
  342.             return 0;
  343.         }
  344.         else  /* no '=', so just set default */
  345.         {
  346.             if (News_spool)
  347.                 free(News_spool);
  348.             News_spool = strdup(argv[1]);
  349.         }
  350.         if (argc > 2) {  /* they specified a newsdir as well */
  351.             if (np_all)
  352.                 free(Newsdir);
  353.             Newsdir = strdup(argv[2]);
  354.             np_all = 1;
  355.         }
  356.     }
  357.     return 0;
  358. }
  359.   
  360. static int
  361. dokicks(argc,argv,p)
  362. int argc;
  363. char *argv[];
  364. void *p;
  365. {
  366.     struct nntpservers *np;
  367.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  368.         if(stricmp(np->name,argv[1]) == 0) {
  369.             /* If the timer is not running, the timeout function has
  370.             * already been called, so we don't want to start another process.
  371.             */
  372.             if(run_timer(&np->nntpcli_t)) {
  373.                 stop_timer(&np->nntpcli_t);
  374.                 nntptick((void *)np);
  375.             }
  376.             return 0;
  377.         }
  378.     tputs("No such server enabled.\n");
  379.     return 1;
  380. }
  381.   
  382. static int
  383. dogroups(argc,argv,p)
  384. int argc;
  385. char *argv[];
  386. void *p;
  387. {
  388.     int i;
  389.     if(argc < 2) {
  390.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  391.             tputs("All groups are currently enabled.\n");
  392.         else
  393.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  394.         return 0;
  395.     }
  396.     if(Nntpgroups == NULLCHAR)
  397.         Nntpgroups = mallocw(NNTPMAXLEN);
  398.     *Nntpgroups = '\0';
  399.     for(i=1; i < argc; ++i) {
  400.         if(i > 1)
  401.             strcat(Nntpgroups,",");
  402.         strcat(Nntpgroups,argv[i]);
  403.     }
  404.     return 0;
  405. }
  406.   
  407. /* This is the routine that gets called every so often to connect to
  408.  * NNTP servers.
  409.  */
  410. static void
  411. nntptick(tp)
  412. void *tp;
  413. {
  414.     if (newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0) == NULLPROC)
  415.         start_timer(&((struct nntpservers *)tp)->nntpcli_t);    /* N5KNX: retry later */
  416.  
  417. }
  418.   
  419. static void
  420. nntp_job(i1,tp,v1)
  421. int i1;
  422. void *tp, *v1;
  423. {
  424.     FILE *fp, *tmpf;
  425.     int s = -1, i;
  426. /*  long pos; */
  427.     struct tm *ltm;
  428.     time_t t;
  429.     int now;
  430.     struct nntpservers *np = (struct nntpservers *) tp;
  431.     struct sockaddr_in fsocket;
  432.     char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
  433.     char currdate[20];
  434.     char GMT[] = " GMT";
  435. #ifdef LZW
  436.     int lzwmode, lzwbits, ret;
  437.     extern int16 Lzwbits;
  438.     extern int Lzwmode;
  439. #endif /* LZW */
  440.  
  441.     if (nntptrace >= 3)
  442.         tprintf("NNTP daemon entered, target = %s\n",np->name);
  443.     if(availmem() < Memthresh + 4000L){
  444.         if (nntptrace >= 2)
  445.             tputs("NNTP daemon quit -- low memory\n");
  446.         /* Memory is tight, don't do anything */
  447.         start_timer(&np->nntpcli_t);
  448.         return;
  449.     }
  450.   
  451.     time(&t);   /* more portable than gettime() */
  452.     ltm = localtime(&t);
  453.     now = ltm->tm_hour * 100 + ltm->tm_min;
  454.     if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  455.         if (now < np->lowtime || now >= np->hightime) {
  456.             if (nntptrace >= 3)
  457.                 tprintf(WOpenMsg, np->name);
  458.             start_timer(&np->nntpcli_t);
  459.             return;
  460.         }
  461.     } else {
  462.         if (now < np->lowtime && now >= np->hightime) {
  463.             if (nntptrace >= 3)
  464.                 tprintf(WOpenMsg, np->name);
  465.             start_timer(&np->nntpcli_t);
  466.             return;
  467.         }
  468.     }
  469.   
  470.     fsocket.sin_addr.s_addr = resolve(np->name);
  471.     if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  472.         if (nntptrace >= 2)
  473.             tprintf("NNTP can't resolve host '%s'\n", np->name);
  474.         /* Try again later */
  475.         start_timer(&np->nntpcli_t);
  476.         return;
  477.     }
  478.     fsocket.sin_family = AF_INET;
  479.     fsocket.sin_port = IPPORT_NNTP;
  480.   
  481.     s = socket(AF_INET,SOCK_STREAM,0);
  482.     sockmode(s,SOCK_ASCII);
  483.     if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  484.         cp = sockerr(s);
  485.         if (!cp) cp="";
  486.         log(s,CFailMsg,psocket(&fsocket),cp);
  487.         if (nntptrace >= 2)
  488.             tprintf(CFailMsg,psocket(&fsocket),cp), tputc('\n');
  489.         goto quit;
  490.     }
  491.     /* Eat the banner */
  492.     i = getreply(s);
  493.     if(i == -1 || i >= 400) {
  494.         log(s,BadBannerMsg,psocket(&fsocket),i);
  495.         if (nntptrace >= 1)
  496.             tprintf(BadBannerMsg,psocket(&fsocket),i), tputc('\n');
  497.         goto quit;
  498.     }
  499.   
  500. #ifdef LZW
  501.     if(LzwActive) {
  502.         usprintf(s,"XLZW %d %d\n",Lzwbits,Lzwmode);
  503.         usflush(s);
  504.         if(recvline(s,buf,NNTPMAXLEN) == -1)
  505.             goto quit;
  506.         rip(buf);
  507.  
  508.         ret = lzwmode = lzwbits = 0;
  509.         sscanf(buf,"%d %d %d",&ret,&lzwbits,&lzwmode);
  510.  
  511.         /* Eat negative response */
  512.         if((ret >= 200) && (ret < 300)) {
  513.             if(lzwmode != Lzwmode || lzwbits != Lzwbits) {
  514.                 lzwmode = LZWCOMPACT;
  515.                 lzwbits = LZWBITS;
  516.             }
  517.             lzwinit(s,lzwbits,lzwmode);
  518.         }
  519.         /* else not supported */
  520.     }
  521. #endif /* LZW */
  522.  
  523.     if (mlock(Newsdir, "nntp")) {
  524.         log(s,NoLockMsg, psocket(&fsocket), "nntp.dat");
  525.         if (nntptrace >= 2)
  526.             tprintf(NoLockMsg, psocket(&fsocket), "nntp.dat\n");
  527.         goto quit;
  528.     }
  529.     sprintf(buf,"%s/nntp.dat",Newsdir);
  530.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  531.         log(s,NoOpenMsg,psocket(&fsocket),buf);
  532.         if (nntptrace >= 1)
  533.             tprintf(NoOpenMsg,psocket(&fsocket),buf), tputc('\n');
  534.         rmlock(Newsdir, "nntp");
  535.         goto quit;
  536.     }
  537.     rewind(fp);
  538. /*  for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  539.     for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  540.         if((cp = strchr(buf,' ')) == NULLCHAR)
  541.             continue;   /* something wrong with this line, skip it */
  542.         *cp = '\0';
  543.         if(stricmp(buf,np->name) == 0) {
  544.             rip(cp+1);
  545.             lastdate = strdup(cp+1);
  546.             break;
  547.         }
  548.     }
  549.     fclose(fp);
  550.     rmlock(Newsdir, "nntp");
  551.   
  552.     if(lastdate == NULLCHAR) {
  553.         if (Nntpfirstpoll < 0)
  554.             lastdate = strdup("900101 000000");
  555.         else {    /* from G8FSL */
  556.             lastdate = mallocw(15);
  557.             time(&t);
  558.             t -= Nntpfirstpoll*86400L;
  559.             make_time_string(lastdate,&t);
  560.         }
  561.     }
  562.  
  563.     /* snapshot the time for use later in re-writing nntp.dat */
  564.     time(&t);
  565. /* N5KNX: we must tell the nntp server if our reference time is UTC/GMT or local time. */
  566. #ifdef MSDOS
  567.     ltm = localtime(&t);   /* localtime calls tzset() which sets timezone */
  568.     if (timezone == 0L) strcpy(GMT, " GMT");  /* you get EST if TZ env var is unset */
  569.     else GMT[0] = '\0';
  570. #else
  571.     ltm = gmtime(&t);   /* BSD vs SysV vs Linux vs Apple vs ??? */
  572.     i = ltm->tm_hour;
  573.     ltm = localtime(&t);
  574.     if (i == ltm->tm_hour) strcpy(GMT, " GMT");
  575.     else GMT[0] = '\0';
  576. #endif
  577.  
  578.     sprintf(currdate,"%02d%02d%02d %02d%02d%02d",ltm->tm_year%100,ltm->tm_mon+1,
  579.         ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  580.   
  581.     /* Get a list of new message-id's */
  582.     if (np->groups) cp=np->groups;
  583.     else cp=Nntpgroups != NULLCHAR ? Nntpgroups : "*";
  584.     if (nntptrace >= 3)
  585.         tprintf("==>NEWNEWS %s %s%s\n", cp, lastdate, GMT);
  586.     usprintf(s,"NEWNEWS %s %s%s\n", cp, lastdate, GMT);
  587.     free(lastdate);
  588.  
  589.     /* Get the response */
  590.     if((i = getreply(s)) != 230) { /* protocol error */
  591.         log(s,ProtoErrMsg,psocket(&fsocket),i);
  592.         if (nntptrace >= 1)
  593.             tprintf(ProtoErrMsg,psocket(&fsocket),i), tputc('\n');
  594.         goto quit;
  595.     }
  596.     if((tmpf = tmpfile()) == NULLFILE) {
  597.         if (nntptrace >= 1)
  598.             tprintf(NoOpenMsg, psocket(&fsocket), "tmpfile\n");
  599.         goto quit;
  600.     }
  601.     if(gettxt(s,tmpf) == -1) {
  602.         log(s, GTFailMsg, psocket(&fsocket));
  603.         if (nntptrace >= 1)
  604.             tprintf(GTFailMsg,psocket(&fsocket)), tputc('\n');
  605.         fclose(tmpf);
  606.         goto quit;
  607.     }
  608.   
  609.     /* Open the history file */
  610.     i=0;
  611.     while (mlock(History, NULLCHAR)) {
  612.         pause(1000L);
  613.         if (++i == 60) {
  614.             log(s, NoLockMsg, psocket(&fsocket), History);
  615.             if (nntptrace >= 1)
  616.                 tprintf(NoLockMsg, psocket(&fsocket), History), tputc('\n');
  617.             fclose(tmpf);
  618.             goto quit;
  619.         }
  620.     }
  621.     if((fp = fopen(History,APPEND_TEXT)) == NULLFILE) {
  622.         log(s,NoOpenMsg, psocket(&fsocket), History);
  623.         if (nntptrace >= 1)
  624.             tprintf(NoOpenMsg,psocket(&fsocket),History), tputc('\n');
  625.         fclose(tmpf);
  626.         goto quit;
  627.     }
  628.     /* search through the history file for matching message id's */
  629.     rewind(tmpf);
  630.     while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  631.         rip(tbuf);
  632.         i = 0;
  633.         rewind(fp);
  634.         while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  635.             if(strnicmp(buf,tbuf,strlen(tbuf)) == 0) {
  636.                 i = 1;
  637.                 break;
  638.             }
  639.             pwait(NULL);
  640.         }
  641.         if(i == 0) {        /* not found, get the article */
  642.             if(getarticle(s,tbuf,cp) == -1) {
  643.                 log(s,GAFailMsg,psocket(&fsocket));
  644.                 if (nntptrace >= 2)
  645.                     tprintf(GAFailMsg,psocket(&fsocket)), tputc('\n');
  646.                 fclose(fp);
  647.                 rmlock(History, NULLCHAR);
  648.                 fclose(tmpf);
  649.                 goto quit;
  650.             }
  651.             fprintf(fp,"%s %s\n",tbuf,currdate); /* add the new message id + timestamp */
  652.         }
  653.     }
  654.     fclose(fp);
  655.     rmlock(History, NULLCHAR);
  656.     fclose(tmpf);
  657.     if (nntptrace >= 3)
  658.         tputs("==>QUIT\n");
  659.     usputs(s,"QUIT\n");
  660.     /* Eat the response */
  661.     getreply(s);
  662.     /* NOW, update the nntp.dat file */
  663.     if (mlock(Newsdir, "nntp")) {
  664.         if (nntptrace >= 2)
  665.             tprintf(NoLockMsg, psocket(&fsocket), "nntp.dat (update)\n");
  666.         goto quit;
  667.     }
  668.     sprintf(buf,"%s/nntp.dat",Newsdir);
  669.     fp = fopen(buf,READ_TEXT);
  670.     sprintf(buf, "%s/nntp.tmp",Newsdir);
  671.     if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  672.         if (nntptrace >= 1)
  673.             tprintf(NoOpenMsg,psocket(&fsocket),buf), tputc('\n');
  674.     if (fp == NULLFILE || tmpf == NULLFILE) {
  675.         log(s,NoUpdateMsg, psocket(&fsocket), buf);
  676.         if (nntptrace >= 2)
  677.             tprintf(NoUpdateMsg,psocket(&fsocket),buf), tputc('\n');
  678.         if (fp)
  679.             fclose(fp);
  680.         if (tmpf)
  681.             fclose(tmpf);
  682.         rmlock(Newsdir, "nntp");
  683.         goto quit;
  684.     }
  685.     while (fgets(tbuf, sizeof(tbuf), fp))
  686.         if (strnicmp(tbuf, np->name, strlen(np->name)))
  687.             fputs(tbuf, tmpf);
  688.     fprintf(tmpf,"%s %s\n",np->name,currdate);
  689.     fclose(fp);
  690.     fclose(tmpf);
  691.     sprintf(buf, "%s/nntp.dat", Newsdir);
  692.     sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  693.     unlink(buf);
  694.     rename(tbuf, buf);
  695.     rmlock(Newsdir, "nntp");
  696.     quit:
  697.     if (nntptrace >= 3)
  698.         tputs("NNTP daemon exiting\n");
  699.     close_s(s);
  700.     /* Restart timer */
  701.     start_timer(&np->nntpcli_t);
  702.     return;
  703. }
  704.   
  705. static int
  706. gettxt(s,fp)
  707. int s;
  708. FILE *fp;
  709. {
  710.     char buf[NNTPMAXLEN];
  711.     int nlines;
  712.     for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  713.         if (nntptrace >= 4)
  714.             tprintf("<==%s", buf);
  715.         if(strcmp(buf,".\n") == 0) {
  716.             if (nntptrace >= 3)
  717.                 tprintf("NNTP received %d lines\n", nlines);
  718.             return 0;
  719.         }
  720.         /* check for escaped '.' characters */
  721.         if(strcmp(buf,"..\n") == 0)
  722.             fputs(".\n",fp);
  723.         else
  724.             fputs(buf,fp);
  725.     }
  726.     if (nntptrace >= 1)
  727.         tprintf("NNTP receive error after %d lines\n", nlines);
  728.     return -1;
  729. }
  730.   
  731. static int
  732. getreply(s)
  733. int s;
  734. {
  735.     char buf[NNTPMAXLEN];
  736.     int response;
  737.     while(recvline(s,buf,NNTPMAXLEN) != -1) {
  738.         /* skip informative messages and blank lines */
  739.         if(buf[0] == '\0' || buf[0] == '1')
  740.             continue;
  741.         sscanf(buf,"%d",&response);
  742.         if (nntptrace >= 3)
  743.             tprintf("<==%s\n", buf);
  744.         return response;
  745.     }
  746.     if (nntptrace >= 3)
  747.         tputs("==No response\n");
  748.     return -1;
  749. }
  750.   
  751. static int
  752. getarticle(s,msgid,nglist)
  753. int s;
  754. char *msgid, *nglist;
  755. {
  756.     char buf[NNTPMAXLEN], *froml=NULLCHAR, *newgl=NULLCHAR;
  757.     char ng[FILE_PATH_SIZE], *cp, *cp1;
  758.     FILE *fp, *tmpf;
  759.     int i;
  760.     extern int Smtpquiet;
  761.     time_t t;
  762.     long start;
  763.     struct mailindex ind;
  764.   
  765.     if (nntptrace >= 3)
  766.         tprintf("==>ARTICLE %s\n", msgid);
  767.     usprintf(s,"ARTICLE %s\n", msgid);
  768.     i = getreply(s);
  769.     if(i == -1 || i >= 500)
  770.         return -1;
  771.     if(i >= 400)
  772.         return 0;
  773.     if((tmpf = tmpfile()) == NULLFILE) {
  774.         if (nntptrace >= 1)
  775.             tputs("NNTP Cannot open temp file for article\n");
  776.         return -1;
  777.     }
  778.     if(gettxt(s,tmpf) == -1) {
  779.         fclose(tmpf);
  780.         return -1;
  781.     }
  782.     /* convert the article into mail format */
  783.     rewind(tmpf);
  784.     while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  785.         if(strncmp(buf,"From: ",6) == 0) {
  786.             rip(&buf[6]);
  787.             froml=strdup(&buf[6]);
  788.             if(newgl != NULLCHAR)
  789.                 break;
  790.         }
  791.         if(strncmp(buf,"Newsgroups: ",12) == 0) {
  792.             newgl=strdup(&buf[12]);
  793.             if(froml != NULLCHAR)
  794.                 break;
  795.         }
  796.         /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  797.         if(strcmp(buf,"\n") == 0 && (froml == NULLCHAR || newgl == NULLCHAR)) {
  798. /*          fclose(fp); */
  799.             fclose(tmpf);
  800.             free(froml);
  801.             free(newgl);
  802.             return 0;
  803.         }
  804.     }
  805.     /* Clear the index to start */
  806.     memset(&ind,0,sizeof(ind));
  807.   
  808.     for(i=0,cp=newgl;*cp;++cp) {
  809.         if(*cp == ',' || *cp == '\n') {
  810.             char *tempdir=NULLCHAR, *prefix, *p;
  811.             struct ngmap *ngp;
  812.  
  813.             ng[i] = '\0';
  814.  
  815.             /* is this article's newsgroup in our list? */
  816.             for(cp1=nglist; *cp1; ) {
  817.                 int j;
  818.                 if (*cp1 == '*') break;  /* '*' always matches */
  819.                 if ((j=strcspn(cp1,",")) != i || strnicmp(cp1, ng, i)) {
  820.                     cp1 += j;
  821.                     if (*cp1 == ',') cp1++;
  822.                 }
  823.                 else break;   /* matches */
  824.             }
  825.             if (! *cp1) goto skipng;
  826.  
  827.             /* map name of newsgroup as required */
  828.             for (ngp=ngmaphead; ngp!=NULL; ngp=ngp->next) {
  829.                 int j;
  830.                 if (ngp->prefix) {
  831.                     if ((j=strlen(ngp->prefix)) > i) continue;
  832.                     if (strnicmp(ng, ngp->prefix, j)) continue;
  833.                     cp1=strdup(&ng[j]);
  834.                     strcpy(ng, ngp->newname);
  835.                     strcat(ng, cp1);
  836.                     free(cp1);
  837.                     i=strlen(ng);
  838.                     /* we could 'break' here, but might be useful to allow further remaps */
  839.                 }
  840.             }
  841.  
  842.             /* make dirs associated with ng */
  843.             sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  844.             for(cp1=ng; *cp1; ) {
  845.                 if ((p=strchr(cp1,'.')) != NULLCHAR) {
  846.                     *p = '\0';
  847.                     strcat(buf, cp1);
  848. #ifdef __TURBOC__
  849.                     mkdir(buf); /* create a subdirectory, if necessary */
  850. #else
  851.                     mkdir(buf,0755); /* create a subdirectory, if necessary */
  852. #endif
  853.                     *p = '/';
  854.                     cp1=p;
  855.                 }
  856.                 else {
  857.                     prefix=cp1;
  858.                     if (*prefix == '/') prefix++;   /* first char after dot->slash */
  859.                     break;
  860.                 }
  861.             }
  862.  
  863.             if ( (i=strlen(buf)) > 1 && buf[--i] == '/') buf[i] = '\0';
  864.             tempdir=strdup(buf);
  865.             strcat(buf,"/");
  866.             strcat(buf,prefix);
  867.             if (mlock(tempdir, prefix)) {
  868.                 if (nntptrace >= 2)
  869.                     tprintf("NNTP group '%s' is locked\n", ng);
  870.                 free(froml);
  871.                 free(newgl);
  872.                 free(tempdir);
  873.                 return -1;
  874.             }
  875.             if (! News_spool) SyncIndex(ng); /* ensure index file is current */
  876.  
  877.             strcat(buf,".txt");
  878.             /* open the mail file */
  879.             if (nntptrace >= 3)
  880.                 tprintf("Writing article to '%s'\n", buf);
  881.             if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  882.                 default_index(ng,&ind);
  883.                 fseek(fp,0,SEEK_END);
  884.                 start = ftell(fp);
  885.                 time(&t);
  886.                 fprintf(fp, "From %s %ld\n", froml, t);
  887. #ifdef USERLOG
  888.                 /* If the userlog code is enabled, we need a
  889.                  * "Received: " line to get the message id
  890.                  * that is used in it - WG7J
  891.                  */
  892.                 ind.mydate=t;
  893.                 ind.msgid = get_msgid();
  894.                 fprintf(fp,Hdrs[RECEIVED]);
  895.                 fprintf(fp,"by %s with NNTP\n\tid AA%ld ; %s",
  896.                     Hostname, ind.msgid, ptime(&t));
  897. #endif
  898.                 rewind(tmpf);
  899.                 while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  900.                     if(buf[0] == '\n') {    /* End of headers */
  901.                         putc('\n',fp);
  902.                         break;
  903.                     }
  904.                     fputs(buf,fp);
  905.  
  906.                     rip(buf);
  907.                     set_index(buf,&ind);
  908.                 }
  909.                 /* Now the remaining data */
  910.                 while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  911.                     /* for UNIX mail compatiblity */
  912.                     if(strncmp(buf,"From ",5) == 0)
  913.                         putc('>',fp);
  914.                     fputs(buf,fp);
  915.                 }
  916.                 putc('\n',fp);
  917.                 ind.size = ftell(fp) - start;
  918.                 fclose(fp);
  919.                 if(! News_spool) {   /* working under Mailspool? */
  920. #ifdef USERLOG
  921.                     /* Now touch the timestamp file if it's an area */
  922.                     if(isarea(ng)) {     /* ve3lum 051494, n5knx */
  923.                         sprintf(buf,"%s/%s.inf", tempdir, prefix);
  924.                         fclose(fopen(buf,"w"));
  925.                     }
  926. #endif
  927.                     /* Update the index file */
  928.                     if (write_index(ng,&ind) == -1)
  929.                         log(s,"NNTP can't update index for %s", ng);
  930.                 }
  931.             }
  932.             rmlock(tempdir, prefix);
  933.             free(tempdir);
  934. skipng:
  935.             if (*cp == '\n')
  936.                 break;
  937.             i=0;
  938.             continue;
  939.         }
  940.         ng[i++] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  941.     }
  942.  
  943.     default_index("",&ind);    /* Free remaining data in index structure */
  944.     fclose(tmpf);
  945.     rip(newgl);         /* remove trailing new-line */
  946.     if(!nntpquiet) {
  947. /* If we use tprintf here, instead of printf, flowcontrol
  948.  * in the command screen is used; if the system is unattended for
  949.  * more than 24 articles coming in, it will lock up NNTP.
  950.  * Make sure this only goes to the command screen - WG7J/N5KNX
  951.  */
  952. #ifdef LINUX
  953. /* true, but we defeat that when using the trace interface anyway.  KF8NH */
  954.                     CmdOverride = 1;
  955.                     tcmdprintf(stdout,"New news arrived: %s, article %s%c\n",newgl,msgid,Smtpquiet?' ':'\007');
  956. #else
  957.                     if(Current->output == Command->output)
  958.                         printf("New news arrived: %s, article %s%c\n",newgl,msgid,Smtpquiet?' ':'\007');
  959. #endif
  960.     }
  961.     free(froml);
  962.     free(newgl);
  963.     return 0;
  964. }
  965. #endif  /* NNTP */
  966.