/*

     Catchup v2.1 NNTP Newsreader - downloads all unread news and store them
     in files. Needs file...
       $CUPATH/.subscription  or $HOME/.subscription if CUPATH is undefined
       has the form:

first.news.group 0
second.news.group 0
third.news.group 0
fourth.news.group 0

             The numbers indicate last read article.

     This program is postcardware. Please send a postcard to...
       Gard Eggesboe Abrahamsen
       PO Box 71, 6092 Eggesboenes
       Norway.

     Current email (March 25th 1994): ga@mrih.no

Log: March 25th: Finished this version. Compiles and works fine on
                 HP/UX. Ported to Linux.

*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include <fcntl.h>

#define MAXPATH 256
#define DEFAULT_NNTPSERVER "ugle.unit.no"
#define NNTPPORT 119
#define GROUPMAXSIZE 30

#define stricmp strcasecmp

#define longtoascii(string,number) sprintf(string,"%li",number);

readarticle(infile,outfile)
  int infile,outfile;
{
 char t;
 int actflag,key;

 key=actflag=0;
 while (actflag!=-1)
 {
  key=read(infile,&t,1);
  if(key!=1)
   actflag=-1;
  else {
   switch(actflag)
   {
    case 0:
            if ((t>='0' && t<='9') || t==10 || t==13)
            {
              while(t!=10 && t!=13)read(infile,&t,1);
            } else {
              write(outfile,"From ugle.unit.no!ahaavie Wed Jan  5 14:55:14 1994\n",51);
              actflag=1; write(outfile,&t,1);
            } break;

    case 1:
            switch(t)
            {
              case 13:
              case 10: actflag=2;
            }
            write(outfile,&t,1);
            break;
    case 2:
            switch(t)
            {
             case '.': actflag=3; break;
             case 10: write(outfile,&t,1);
             case 13: break;
             default: actflag=1; write(outfile,&t,1);
            } break;
    case 3:
            switch(t)
            {
             case 10:
             case 13: write(outfile,"\n",1); actflag=-1;
                      break;
             default: write(outfile,".",1); write(outfile,&t,1); actflag=1;
            } break;
   }
  }
 }

}

putnum(i)
  long i;
{
  if(i<10)putchar(' ');
  if(i<100)putchar(' ');
  if(i<1000)putchar(' ');
  if(i<10000)putchar(' ');
  printf("%li",i);
}

skipline(handle)
  int handle;
{
  char r;
  while(r!=13 && r!=10)
   if(read(handle,&r,1)!=1)
      r='\n';
}

int readnum(handle)
  int handle;
{
  int n,f;
  char r;

  f=1; n=-1;
  while(f>0)
  {
   if(1!=read(handle,&r,1))f=0;
   switch(f)
   {
    case 1: if(r>='0' && r<='9')
              {n=r-'0';f++;}
            break;
    case 2: if(r>='0' && r<='9')
              n=n*10+r-'0';
            else
              f=0;
   }
  }
  return n;
}

int call_socket(hostname,callport)
  char *hostname;
  int callport;
{
  struct sockaddr_in sa;
  struct hostent     *hp;
  int    a, s;

  if ((hp=gethostbyname(hostname))==NULL) {
    return(-1);
  }

  bzero(&sa, sizeof(sa));
  bcopy(hp->h_addr, (char *)&sa.sin_addr, hp->h_length);
  sa.sin_family = hp->h_addrtype;
  sa.sin_port = htons((u_short)callport);
  if((s=socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)
    return(-1);
  if(connect(s,(struct sockaddr *) &sa, sizeof(struct sockaddr_in)) < 0) {
    close(s);
    return(-1);
  }
  return(s);
}

main(argc,argv,env)
 int argc;
 char **argv,**env;
{
 char *home,cupath[MAXPATH+1],*cufile;
 char groupname[MAXPATH+1];
 int handle,newsub;
 long subsize,didread,totread=0;

 long artlo,arthi,a,b,c,lastread;

 int pipemode=0,bellmode=0,errormode=0,output=1;

 for(a=1; a<argc; a++)
 {
  if(!stricmp(argv[a],"-c")) pipemode=1;
  else if(!stricmp(argv[a],"+pipe")) pipemode=1;
  else if(!stricmp(argv[a],"-b")) bellmode=1;
  else if(!stricmp(argv[a],"+bell")) bellmode=1;
  else if(!stricmp(argv[a],"-e")) errormode=1;
  else if(!stricmp(argv[a],"+error")) errormode=1;
  else if(!stricmp(argv[a],"-h") || !stricmp(argv[a],"+help")){
printf("\n%c[1mCatchup%c[0m v2.1 NNTP client for UNIX hackers with small watches",27,27);
puts("\n(C) 1994 Gard Eggesboe Abrahamsen, PO Box 71");
puts("         6092 Eggesboenes, Norway");
puts("This program is postcardware. Please send me a postcard.\n");
printf("Usage: %s [flags]\n\n",argv[0]);
printf("%c[1m  flags    descriptions%c[0m\n",27,27);
puts("-c +pipe  Sends all read articles to stdout for use in piping, like...");
printf("           %s -c | gzip -c > articles.gz\n",argv[0]);
puts("-e +error Sends all read articles to stderr for - well....  ;-)");
puts("-b +bell  Makes catchup ring a bell when finished...");
puts("-h +help  Prints this page\n");
printf("%c[1mvariables  descriptions%c[0m\n",27,27);
puts("  CUPATH  The path used for working. Output files and .subscription");
puts("           are present in this directory.");
puts("  HOME    Your ~ directory. Will be used if CUPATH isn't defined");
puts("  BELL    If this variable is defined, then bell is activated without");
puts("           having to set the flag\n");
return;
  }
 }

 if(pipemode || errormode)output=0;

 if(output)puts("Catchup v2.1 NNTP client for UNIX hackers with little time\n(C) 1994 Gard Eggesboe Abrahamsen,                 postcardware.\n         PO Box 71, 6092 Eggesboenes,           send me a postcard\n         Norway.\n");

 if(!(home=getenv("CUPATH")))
   home=getenv("HOME");
 if(strlen(home)>MAXPATH-15)
 {
   printf("%s: path too long\n",argv[0]);
   return;
 }
 strcpy(cupath,home); strcat(cupath,"/.subscription");

 handle=open(cupath,O_RDONLY);
 if(handle<0)
 {
   printf("%s: Can't open %s\n",argv[0],cupath);
   return;
 }

 subsize=lseek(handle,0,SEEK_END);
 if(subsize<0)
 {
   printf("%s: Error seeking %s\n",argv[0],cupath);
   close(handle); return;
 } 

 if(output)printf("%s is %li bytes\n",cupath,subsize);
 if(lseek(handle,0,SEEK_SET)!=0)
 {
   printf("%s: Error seeking %s\n",argv[0],cupath);
   close(handle); return;
 }

 cufile=(char*)malloc(subsize);
 if(!cufile){
   printf("%s: Out of memory",argv[0]);
   close(handle); return;
 }

 if(read(handle,cufile,subsize)!=subsize)
 {
   printf("%s: Error while reading %s\n",argv[0],cupath);
   close(handle); free(cufile); return;
 }

 close(handle);

 home=getenv("NNTPSERVER");
 if(!home)
 {
   if(output)printf("Connecting to %s\n",DEFAULT_NNTPSERVER);
   handle=call_socket(DEFAULT_NNTPSERVER,NNTPPORT);
 }
 else
 {
   if(output)printf("Connecting to %s\n",home);
   handle=call_socket(home,NNTPPORT);
 }

 if(handle==-1){
   puts("Unable to connect.");
   free(cufile); return;
 }

 home=getenv("CUPATH");
 if(!home)home=getenv("HOME");
 if(!home)groupname[0]=0; else strcpy(groupname,home);
 strcat(groupname,"/"); strcat(groupname,".subscription~");

 if(0!=rename(cupath,groupname))
 {
  printf("Unable to make copy of subscription file. Abort abort!\n");
  close(handle);
  return;
 }

 if(output)putchar('\n');

 newsub=creat(cupath,493);

 /* Begin download... run through file, read groups... */
 a=0;
 while(a<subsize)
 {
  home=getenv("CUPATH");
  if(!home)home=getenv("HOME");
  if(!home)groupname[0]=0; else strcpy(groupname,home);
  c=strlen(groupname); groupname[c]='/'; c++; groupname[c]=0;
  write(handle,"group ",6); b=0;
  while(a<subsize && cufile[a]!=' ')
  {
   if(output)putchar(cufile[a]);
   groupname[c]=cufile[a]; b++;
   write(handle,&cufile[a],1); write(newsub,&cufile[a],1);
   a++; c++;
  }
  write(newsub," ",1);
  groupname[c]=0;
  if(output)for(;b<GROUPMAXSIZE;b++)putchar(' ');
  fflush(stdout);
  a++; /* Skip the space */
  lastread=0;
  while(a<subsize && cufile[a]!='\n')
  {
   lastread=lastread*10+cufile[a]-'0';
   a++;
  }
  a++; /* Skip the newline */
  write(handle,"\n",1);
  b=1;
  while(b>0)
  {
   switch(b=readnum(handle))
   {
    case 200: skipline(handle); break;
    case 211: readnum(handle);
              artlo=readnum(handle); arthi=readnum(handle); b=0;
              skipline(handle); break;
    case 411: if(output)printf("NA: "); skipline(handle);
              arthi=artlo=0; b=-1; break;
    default: b=-1;
   }
  }
  if(arthi<lastread)lastread=artlo-1;
  if(artlo<=lastread)artlo=lastread+1;
  lastread=arthi-artlo+1;
  if(!b && lastread>0)
  {
   char num[20];
   int ohandle;

   if(errormode)ohandle=fileno(stderr);
   else if(pipemode)ohandle=fileno(stdout);
   else {
    ohandle=open(groupname,O_WRONLY);
    if(ohandle<0)ohandle=creat(groupname,493);
    if(ohandle<0)ohandle=open("/dev/null",O_WRONLY);
    lseek(ohandle,0,SEEK_END);
   }
   if(output){
     putnum(lastread); printf(" articles"); fflush(stdout);
   }
   didread=0;
   for(lastread=artlo; lastread<=arthi; lastread++)
   {
    sprintf(num,"article %li\n",lastread);
    write(handle,num,strlen(num));
    b=1;
    while(b>0)
    {
     switch(b=readnum(handle))
     {
      case 412:
      case 423: skipline(handle); b=0; break;
      case 220: skipline(handle);
                readarticle(handle,ohandle); b=0;
                didread++; totread++; break;
     }
    }
    if(output){
      for(b=14; b>0; b--)putchar(8);
      if(arthi==lastread) {
        printf("Read "); putnum(didread);
      } else {
        putnum(arthi-lastread);
      }
      printf(" articles"); fflush(stdout);
    }
   }
   if(output)close(ohandle);
   longtoascii(num,arthi); strcat(num,"\n");
   write(newsub,num,strlen(num));
   if(output)putchar('\n');
  } else {
   char num[20];
   longtoascii(num,arthi);
   strcat(num,"\n");
   if(output)puts("Skip...");
   write(newsub,num,strlen(num));
  }
 }
 write(handle,"quit\n",5);
 close(newsub); close(handle);
 free(cufile);

 if(output)printf("\nDownloaded %li articles and subscrition file updated\n",totread);

 if(bellmode || getenv("BELL"))for(a=0; a<16; a++)putchar(7);
 fflush(stdout);
}
