home *** CD-ROM | disk | FTP | other *** search
/ HAM Radio 1 / HamRadio.cdr / misc / tcpipsrc / nntpcli.c < prev    next >
C/C++ Source or Header  |  1990-12-09  |  20KB  |  724 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.  */
  32. #include <stdio.h>
  33. #include <sys/types.h>
  34. #include <time.h>
  35. #include <sys/timeb.h>
  36. #include <ctype.h>
  37. #include <string.h>  /* for strchr() */
  38. #ifdef    __TURBOC__
  39. #include <dir.h>
  40. #endif
  41. #include "global.h"
  42. #include "timer.h"
  43. #include "cmdparse.h"
  44. #include "commands.h"
  45. #include "socket.h"
  46. #include "usock.h"
  47. #include "netuser.h"
  48. #include "proc.h"
  49. #include "smtp.h"
  50. #include "files.h"
  51.  
  52. #define NNTPMAXLEN    512
  53.  
  54. static struct nntpservers {
  55.     struct timer nntpcli_t;
  56.     char *name;
  57.     char *groups;
  58.     int lowtime, hightime;  /* for connect window */
  59.     struct nntpservers *next;
  60. };
  61.  
  62. #define    NULLNNTP    (struct nntpservers *)NULL
  63.  
  64. #define MAXGROUPDIRS 10
  65.  
  66. static struct grouploc {
  67.     char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  68.     char *directory;     /* directory where these groups should be */
  69.     } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  70.  
  71. struct nntpservers *Nntpservers = NULLNNTP;
  72. static char *Nntpgroups = NULLCHAR;
  73. static unsigned short nntptrace = 1;
  74.  
  75. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  76.  
  77. static void nntptick __ARGS((void *tp));
  78. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  79. static int gettxt __ARGS((int s,FILE *fp));
  80. static int getreply __ARGS((int s));
  81. static int getarticle __ARGS((int s,char *msgid));
  82. static int dogroups __ARGS((int argc,char *argv[],void *p));
  83. static int doadds __ARGS((int argc,char *argv[],void *p));
  84. static int dodrops __ARGS((int argc,char *argv[],void *p));
  85. static int dokicks __ARGS((int argc,char *argv[],void *p));
  86. static int dolists __ARGS((int argc,char *argv[],void *p));
  87. static int donntrace __ARGS((int argc,char *argv[],void *p));
  88. static int dondir __ARGS((int argc,char *argv[],void *p));
  89.  
  90. /* Tracing levels:
  91.     0 - no tracing
  92.     1 - serious errors reported
  93.     2 - transient errors reported
  94.     3 - session progress reported
  95.     4 - actual received articles displayed
  96.  */
  97.  
  98. static struct cmds Nntpcmds[] = {
  99.     "addserver",    doadds,    0,    3,
  100.     "nntp addserver <nntpserver> <interval>",
  101.     "directory",    dondir,    0,    0,    NULLCHAR,
  102.     "dropserver",    dodrops,    0,    2,
  103.     "nntp dropserver <nntpserver>",
  104.     "groups",    dogroups,    0,    0,    NULLCHAR,
  105.     "kick",        dokicks,    0,    2,
  106.     "nntp kick <nntpserver>",
  107.     "listservers",    dolists,    0,    0,    NULLCHAR,
  108.     "trace",    donntrace,    0,    0,    NULLCHAR,
  109.     NULLCHAR,
  110. };
  111.  
  112. int
  113. donntp(argc,argv,p)
  114. int argc;
  115. char *argv[];
  116. void *p;
  117. {
  118.     return subcmd(Nntpcmds,argc,argv,p);
  119. }
  120.  
  121. static int
  122. doadds(argc,argv,p)
  123. int argc;
  124. char *argv[];
  125. void *p;
  126. {
  127.     struct nntpservers *np;
  128.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  129.         if(stricmp(np->name,argv[1]) == 0)
  130.             break;
  131.     if (np == NULLNNTP) {
  132.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  133.         np->name = strdup(argv[1]);
  134.         np->next = Nntpservers;
  135.         Nntpservers = np;
  136.         np->groups = NULLCHAR;
  137.         np->lowtime = np->hightime = -1;
  138.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  139.         np->nntpcli_t.arg = (void *)np;
  140.     }
  141.     if (argc > 3) {
  142.         int i;
  143.         if (np->groups == NULLCHAR) {
  144.             np->groups = mallocw(NNTPMAXLEN);
  145.             *np->groups = '\0';
  146.         }
  147.         for (i = 3; i < argc; ++i) {
  148.             if (isdigit(*argv[i])) {
  149.                 int lh, ll, hh, hl;
  150.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  151.                 np->lowtime = lh * 100 + ll;
  152.                 np->hightime = hh * 100 + hl;
  153.             } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  154.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  155.             else {  /* it's a group, and it fits... add it to list */
  156.                 if (*np->groups != '\0')
  157.                     strcat(np->groups, ",");
  158.                 strcat(np->groups, argv[i]);
  159.             }
  160.         }
  161.     }
  162.     /* set timer duration */
  163.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  164.     start_timer(&np->nntpcli_t);        /* and fire it up */
  165.     return 0;
  166. }
  167.  
  168. static int
  169. dodrops(argc,argv,p)
  170. int argc;
  171. char *argv[];
  172. void *p;
  173. {
  174.     struct nntpservers *np, *npprev = NULLNNTP;
  175.     for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  176.         if(stricmp(np->name,argv[1]) == 0) {
  177.             stop_timer(&np->nntpcli_t);
  178.             free(np->name);
  179.             if (np->groups)
  180.                 free(np->groups);
  181.             if(npprev != NULLNNTP)
  182.                 npprev->next = np->next;
  183.             else
  184.                 Nntpservers = np->next;
  185.             free((char *)np);
  186.             return 0;
  187.     }
  188.     tprintf("No such server enabled.\n");
  189.     return 0;
  190. }
  191.  
  192. static int
  193. dolists(argc,argv,p)
  194. int argc;
  195. char *argv[];
  196. void *p;
  197. {
  198.     struct nntpservers *np;
  199.     for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  200.         char tbuf[80];
  201.         if (np->lowtime != -1 && np->hightime != -1)
  202.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  203.         else
  204.             tbuf[0] = '\0';
  205.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  206.             read_timer(&np->nntpcli_t) /1000,
  207.             dur_timer(&np->nntpcli_t) /1000,
  208.             tbuf, np->groups ? np->groups : "");
  209.     }
  210.     return 0;
  211. }
  212.  
  213. static int donntrace(argc, argv, p)
  214. int argc;
  215. char *argv[];
  216. void *p;
  217. {
  218.     return setshort(&nntptrace,"NNTP tracing",argc,argv);
  219. }
  220.     
  221. static char *News_spool = NULL;
  222. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  223.  
  224. static int dondir(argc, argv, p)
  225. int argc;
  226. char *argv[];
  227. void *p;
  228. {
  229.     if (argc < 2) {
  230.         int i;
  231.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  232.         tprintf("control: %s\n", Newsdir);
  233.         for (i = 0; i < MAXGROUPDIRS; ++i)
  234.             if (groupdirs[i].prefix)
  235.                 tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  236.     } else {
  237.         char *p;
  238.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  239.             int i;
  240.             *p++ = '\0';
  241.             for (i = 0; i < MAXGROUPDIRS; ++i)
  242.                 if (groupdirs[i].prefix)
  243.                     if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  244.                         if (groupdirs[i].directory) {
  245.                             free(groupdirs[i].directory);
  246.                             groupdirs[i].directory = NULLCHAR;
  247.                             }
  248.                         if (*p == '\0') {
  249.                             free(groupdirs[i].prefix);
  250.                             groupdirs[i].prefix = NULLCHAR;
  251.                         } else
  252.                             groupdirs[i].directory = strdup(p);
  253.                         return 0;
  254.                     }
  255.             if (*p == '\0')  /* trashing a group that's not there */
  256.                 return 0;
  257.             for (i = 0; i < MAXGROUPDIRS; ++i){
  258.                 if (groupdirs[i].prefix == NULLCHAR) {
  259.                     groupdirs[i].prefix = strdup(argv[1]);
  260.                     if (groupdirs[i].directory) {
  261.                         free(groupdirs[i].directory);
  262.                         groupdirs[i].directory = NULL;
  263.                     }
  264.                     groupdirs[i].directory = strdup(p);
  265.                     return 0;
  266.                 }
  267.             }
  268.             tprintf("Directory table full\n");
  269.         }
  270.         else {  /* no '=', so just set default */
  271.             if (News_spool)
  272.                 free(News_spool);
  273.             News_spool = strdup(argv[1]);
  274.         }
  275.         if (argc > 2) {  /* they specified a newsdir as well */
  276.             if (np_all)
  277.                 free(Newsdir);
  278.             Newsdir = strdup(argv[2]);
  279.             np_all = 1;
  280.         }
  281.     }
  282.     return 0;
  283. }
  284.     
  285. static int
  286. dokicks(argc,argv,p)
  287. int argc;
  288. char *argv[];
  289. void *p;
  290. {
  291.     struct nntpservers *np;
  292.     for(np = Nntpservers; np != NULLNNTP; np = np->next)
  293.         if(stricmp(np->name,argv[1]) == 0) {
  294.             /* If the timer is not running, the timeout function has
  295.             * already been called and we don't want to call it again.
  296.             */
  297.             if(run_timer(&np->nntpcli_t)) {
  298.                 stop_timer(&np->nntpcli_t);
  299.                 nntptick((void *)np);
  300.             }
  301.             return 0;
  302.     }
  303.     tprintf("No such server enabled.\n");
  304.     return 0;
  305. }
  306.  
  307. static int
  308. dogroups(argc,argv,p)
  309. int argc;
  310. char *argv[];
  311. void *p;
  312. {
  313.     int i;
  314.     if(argc < 2) {
  315.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  316.             tprintf("All groups are currently enabled.\n");
  317.         else
  318.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  319.         return 0;
  320.     }
  321.     if(Nntpgroups == NULLCHAR)
  322.         Nntpgroups = mallocw(NNTPMAXLEN);
  323.     *Nntpgroups = '\0';
  324.     for(i=1; i < argc; ++i) {
  325.         if(i > 1)
  326.             strcat(Nntpgroups,",");
  327.         strcat(Nntpgroups,argv[i]);
  328.     }
  329.     return 0;
  330. }
  331.  
  332. /* This is the routine that gets called every so often to connect to
  333.  * NNTP servers.
  334.  */
  335. static void
  336. nntptick(tp)
  337. void *tp;
  338. {
  339.     newproc("NNTP client", 3072, nntp_job, 0, tp, NULL,0);
  340. }
  341.  
  342. static void
  343. nntp_job(i1,tp,v1)
  344. int i1;
  345. void *tp, *v1;
  346. {
  347.     FILE *fp, *tmpf;
  348.     int s = -1, i;
  349. /*    long pos; */
  350.     struct tm *ltm;
  351.     time_t t;
  352.     int now;
  353.     struct nntpservers *np = (struct nntpservers *) tp;
  354.     struct sockaddr_in fsocket;
  355.     char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *lastdate = NULLCHAR;
  356.     if (nntptrace >= 3)
  357.         tprintf("NNTP daemon entered, target = %s\n",np->name);
  358.     if(availmem() < Memthresh){
  359.         if (nntptrace >= 2)
  360.             tprintf("NNTP daemon quit -- low memory\n");
  361.         /* Memory is tight, don't do anything */
  362.         start_timer(&np->nntpcli_t);
  363.         return;
  364.     }
  365.  
  366.     time(&t);    /* more portable than gettime() */
  367.     ltm = localtime(&t);
  368.     now = ltm->tm_hour * 100 + ltm->tm_min;
  369.     if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  370.         if (now < np->lowtime || now >= np->hightime) {
  371.             if (nntptrace >= 3)
  372.                 tprintf("NNTP window to '%s' not open\n", np->name);
  373.             start_timer(&np->nntpcli_t);
  374.             return;
  375.         }
  376.     } else {
  377.         if (now < np->lowtime && now >= np->hightime) {
  378.             if (nntptrace >= 3)
  379.                 tprintf("NNTP window to '%s' not open\n", np->name);
  380.             start_timer(&np->nntpcli_t);
  381.             return;
  382.         }
  383.     }
  384.  
  385.     fsocket.sin_addr.s_addr = resolve(np->name);
  386.     if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  387.         if (nntptrace >= 2)
  388.             tprintf("NNTP can't resolve host '%s'\n", np->name);
  389.         /* Try again later */
  390.         start_timer(&np->nntpcli_t);
  391.         return;
  392.     }
  393.     fsocket.sin_family = AF_INET;
  394.     fsocket.sin_port = IPPORT_NNTP;
  395.  
  396.     s = socket(AF_INET,SOCK_STREAM,0);
  397.     sockmode(s,SOCK_ASCII);
  398.     if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  399.         cp = sockerr(s);
  400.         log(s,"NNTP %s Connect failed: %s",psocket(&fsocket),
  401.             cp != NULLCHAR ? cp : "");
  402.         if (nntptrace >= 2)
  403.             tprintf("NNTP %s Connect failed: %s\n",psocket(&fsocket),
  404.         cp != NULLCHAR ? cp : "");
  405.         goto quit;
  406.     }
  407.     /* Eat the banner */
  408.     i = getreply(s);
  409.     if(i == -1 || i >= 400) {
  410.         log(s,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
  411.         if (nntptrace >= 1)
  412.             tprintf("NNTP %s bad reply on banner (response was %d)\n",psocket(&fsocket),i);
  413.         goto quit;
  414.     }
  415.  
  416.     if (mlock(Newsdir, "nntp")) {
  417.         if (nntptrace >= 2)
  418.             tprintf("NNTP %s Connect failed: cannot lock nntp.dat\n", psocket(&fsocket));
  419.         goto quit;
  420.     }
  421.     sprintf(buf,"%s/nntp.dat",Newsdir);
  422.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  423.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket),
  424.             buf);
  425.         if (nntptrace >= 1)
  426.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  427.         rmlock(Newsdir, "nntp");
  428.         goto quit;
  429.     }
  430.     rewind(fp);
  431. /*    for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  432.     for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  433.         if((cp = strchr(buf,' ')) == NULLCHAR)
  434.             continue;    /* something wrong with this line, skip it */
  435.         *cp = '\0';
  436.         if(stricmp(buf,np->name) == 0) {
  437.             rip(cp+1);
  438.             lastdate = strdup(cp+1);
  439.             break;
  440.         }
  441.     }
  442.     fclose(fp);
  443.     rmlock(Newsdir, "nntp");
  444.  
  445.     if(lastdate == NULLCHAR)
  446.         lastdate = strdup("700101 000000");
  447.     /* snapshot the time for use later in re-writing nntp.dat */
  448.     time(&t);
  449.     ltm = localtime(&t);
  450.                 
  451.     /* Get a list of new message-id's */
  452.     if (np->groups) {
  453.         if (nntptrace >= 3)
  454.             tprintf("==>NEWNEWS %s %s\n", np->groups, lastdate);
  455.         usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
  456.     } else {
  457.         if (nntptrace >= 3)
  458.             tprintf("==>NEWNEWS %s %s\n", Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  459.         usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  460.     }
  461.     free(lastdate);
  462.     /* Get the response */
  463.     if((i = getreply(s)) != 230) { /* protocol error */
  464.         log(s,"NNTP %s protocol error (response was %d)",psocket(&fsocket),i);
  465.         if (nntptrace >= 1)
  466.             tprintf("NNTP %s protocol error (response was %d)\n",psocket(&fsocket),i);
  467.         goto quit;
  468.     }
  469.     if((tmpf = tmpfile()) == NULLFILE) {
  470.         if (nntptrace >= 1)
  471.             tprintf("NNTP %s Cannot open temp file\n", psocket(&fsocket));
  472.         goto quit;
  473.     }
  474.     if(gettxt(s,tmpf) == -1) {
  475.         log(s, "NNTP %s giving up: gettxt() failure",psocket(&fsocket));
  476.         if (nntptrace >= 1)
  477.             tprintf("NNTP %s giving up: gettxt() failure\n",psocket(&fsocket));
  478.         fclose(tmpf);
  479.         goto quit;
  480.     }
  481.  
  482.     /* Open the history file */
  483.     if (mlock(Newsdir, "history")) {
  484.         if (nntptrace >= 1)
  485.             tprintf("NNTP %s giving up: couldn't lock history file\n", psocket(&fsocket));
  486.         fclose(tmpf);
  487.         goto quit;
  488.     }
  489.     sprintf(buf,"%s/history",Newsdir);
  490.     if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  491.         log(s,"NNTP %s Connect failed: Cannot open %s",psocket(&fsocket), buf);
  492.         if (nntptrace >= 1)
  493.             tprintf("NNTP %s Connect failed: Cannot open %s\n",psocket(&fsocket), buf);
  494.         fclose(tmpf);
  495.         goto quit;
  496.     }
  497.     /* search through the history file for matching message id's */
  498.     rewind(tmpf);
  499.     while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  500.         i = 0;
  501.         rewind(fp);
  502.         while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  503.             if(stricmp(buf,tbuf) == 0) {
  504.                 i = 1;
  505.                 break;
  506.             }
  507.             pwait(NULL);
  508.         }
  509.         if(i == 0) {        /* not found, get the article */
  510.             if(getarticle(s,tbuf) == -1) {
  511.                 log(s,"NNTP %s Giving up: could not get article",psocket(&fsocket));
  512.                 if (nntptrace >= 2)
  513.                     tprintf("NNTP %s Giving up: could not get article\n",psocket(&fsocket));
  514.                 fclose(fp);
  515.                 rmlock(Newsdir, "history");
  516.                 fclose(tmpf);
  517.                 goto quit;
  518.             }
  519.             fprintf(fp,"%s",tbuf); /* add the new message id */
  520.         }
  521.     }
  522.     fclose(fp);
  523.     rmlock(Newsdir, "history");
  524.     fclose(tmpf);
  525.     if (nntptrace >= 3)
  526.         tprintf("==>QUIT\n");
  527.     usprintf(s,"QUIT\n");
  528.     /* Eat the response */
  529.     getreply(s);
  530.     /* NOW, update the nntp.dat file */
  531.     if (mlock(Newsdir, "nntp")) {
  532.         if (nntptrace >= 2)
  533.             tprintf("NNTP %s Could not lock nntp.dat for update\n", psocket(&fsocket));
  534.         goto quit;
  535.     }
  536.     sprintf(buf,"%s/nntp.dat",Newsdir);
  537.     fp = fopen(buf,READ_TEXT);
  538.     sprintf(buf, "%s/nntp.tmp",Newsdir);
  539.     if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  540.         if (nntptrace >= 1)
  541.             tprintf("NNTP %s Cannot create temp file '%s'\n", psocket(&fsocket), buf);
  542.     if (fp == NULLFILE || tmpf == NULLFILE) {
  543.         log(s,"NNTP %s Could not update %s", psocket(&fsocket), buf);
  544.         if (nntptrace >= 2)
  545.             tprintf("NNTP %s Could not update %s\n",psocket(&fsocket), buf);
  546.         if (fp)
  547.             fclose(fp);
  548.         if (tmpf)
  549.             fclose(tmpf);
  550.         rmlock(Newsdir, "nntp");
  551.         goto quit;
  552.     }
  553.     while (fgets(tbuf, sizeof(tbuf), fp))
  554.         if (strnicmp(tbuf, np->name, strlen(np->name)))
  555.             fputs(tbuf, tmpf);
  556.     fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
  557.         ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  558.     fclose(fp);
  559.     fclose(tmpf);
  560.     sprintf(buf, "%s/nntp.dat", Newsdir);
  561.     sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  562.     unlink(buf);
  563.     rename(tbuf, buf);
  564.     rmlock(Newsdir, "nntp");
  565. quit:
  566.     if (nntptrace >= 3)
  567.         tprintf("NNTP daemon exiting\n");
  568.     close_s(s);
  569.     /* Restart timer */
  570.     start_timer(&np->nntpcli_t);
  571.     return;
  572. }
  573.  
  574. static int
  575. gettxt(s,fp)
  576. int s;
  577. FILE *fp;
  578. {
  579.     char buf[NNTPMAXLEN];
  580.     int nlines;
  581.     for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  582.         if (nntptrace >= 4)
  583.             tprintf("<==%s", buf);
  584.         if(strcmp(buf,".\n") == 0) {
  585.             if (nntptrace >= 3)
  586.                 tprintf("NNTP received %d lines\n", nlines);
  587.             return 0;
  588.             }
  589.         /* check for escaped '.' characters */
  590.         if(strcmp(buf,"..\n") == 0)
  591.             fputs(".\n",fp);
  592.         else
  593.             fputs(buf,fp);
  594.     }
  595.     if (nntptrace >= 1)
  596.         tprintf("NNTP receive error after %d lines\n", nlines);
  597.     return -1;
  598. }
  599.  
  600. static int
  601. getreply(s)
  602. int s;
  603. {
  604.     char buf[NNTPMAXLEN];
  605.     int response;
  606.     while(recvline(s,buf,NNTPMAXLEN) != -1) {
  607.         /* skip informative messages and blank lines */
  608.         if(buf[0] == '\0' || buf[0] == '1')
  609.             continue;
  610.         sscanf(buf,"%d",&response);
  611.         if (nntptrace >= 3)
  612.             tprintf("<==%s\n", buf);
  613.         return response;
  614.     }
  615.     if (nntptrace >= 3)
  616.         tprintf("==No response\n");
  617.     return -1;
  618. }
  619.  
  620. static int
  621. getarticle(s,msgid)
  622. int s;
  623. char *msgid;
  624. {
  625.     char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  626.     FILE *fp, *tmpf;
  627.     int r;
  628.     char *cp;
  629.  
  630.     if (nntptrace >= 3)
  631.         tprintf("==>ARTICLE %s", msgid);
  632.     usprintf(s,"ARTICLE %s", msgid);
  633.     r = getreply(s);
  634.     if(r == -1 || r >= 500)
  635.         return -1;
  636.     if(r >= 400)
  637.         return 0;
  638.     if((tmpf = tmpfile()) == NULLFILE) {
  639.         if (nntptrace >= 1)
  640.             tprintf("NNTP Cannot open temp file for article\n");
  641.         return -1;
  642.     }
  643.     if(gettxt(s,tmpf) == -1) {
  644.         fclose(tmpf);
  645.         return -1;
  646.     }
  647.     /* convert the article into mail format */
  648.     rewind(tmpf);
  649.     froml[0] = '\0';
  650.     newgl[0] = '\0';
  651.     while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  652.         if(strncmp(buf,"From: ",6) == 0) {
  653.             struct timeb t;
  654.             ftime(&t);
  655.             rip(&buf[6]);
  656.             sprintf(froml,"From %s %ld\n",&buf[6], t.time);
  657.             if(newgl[0] != '\0')
  658.                 break;
  659.         }
  660.         if(strncmp(buf,"Newsgroups: ",12) == 0) {
  661.             strcpy(newgl,&buf[12]);
  662.             if(froml[0] != '\0')
  663.                 break;
  664.         }
  665.         /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  666.         if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  667. /*            fclose(fp); */
  668.             fclose(tmpf);
  669.             return 0;
  670.         }
  671.     }
  672.     sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  673.     for(cp=newgl;;++cp) {
  674.         if(*cp == '.') {
  675. #ifdef __TURBOC__
  676.             mkdir(buf); /* create a subdirectory, if necessary */
  677. #else
  678.             mkdir(buf,0755); /* create a subdirectory, if necessary */
  679. #endif
  680.             strcat(buf,"/");
  681.             continue;
  682.         }
  683.         if(*cp == ',' || *cp == '\n') {
  684.             char tempdir[80], prefix[20], *p;
  685.             strcpy(tempdir, buf);
  686.             if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  687.                 *p++ = '\0';
  688.                 strcpy(prefix, p);
  689.             }
  690.             if (mlock(tempdir, prefix)) {
  691.                 if (nntptrace >= 2)
  692.                     tprintf("NNTP group '%s' is locked\n", buf);
  693.                 return -1;
  694.             }
  695.             strcat(buf,".txt");
  696.             /* open the mail file */
  697.             if (nntptrace >= 3)
  698.                 tprintf("Writing article to '%s'\n", buf);
  699.             if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  700.                 fputs(froml,fp);
  701.                 rewind(tmpf);
  702.                 while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  703.                     /* for UNIX mail compatiblity */
  704.                     if(strncmp(buf,"From ",5) == 0)
  705.                         putc('>',fp);
  706.                     fputs(buf,fp);
  707.                 }
  708.                 putc('\n',fp);
  709.                 fclose(fp);
  710.             }
  711.             rmlock(tempdir, prefix);
  712.             if (*cp == '\n') 
  713.                 break;
  714.             else
  715.                 sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  716.             continue;
  717.         }
  718.         buf[strlen(buf)+1] = '\0';
  719.         buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  720.     }
  721.     fclose(tmpf);
  722.     return 0;
  723. }
  724.