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