/*->c.rzmodem */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <string.h>
#include <time.h>


#include "h.os"
#include "h.bbc"
#include "h.wimp"

#include "h.def"


#include "h.wos"
#include "h.timex"
#include "h.xext"

#include "h.serial"

#include "h.ftpglue"


#include "h.zmodem"
#include "h.zmextra"




/*****************************************************************************/

/*
 * Send a string to the modem, processing for \336 (sleep 1 sec)
 *   and \335 (break signal)
 */

static void zmputs(char * s)
{
 int c;

 while (*s)
 {
  switch(c=*s++)
  {
   /*               case '\336':
                       sleep(1); continue;
               case '\335':
                       sendbrk(); continue; */
   default:
           sendline(c);
  }
 }
}







/*
 * Process incoming file information header
 */

static int procheader(char * name,int * len)
{
 int   append=0;
 int   flags=0;
 char  *p;
 int   bn;

 /* set default parameters and overrides */

 Thisbinary = (!Rxascii) || Rxbinary;

 if(Lzmanag) zmanag=Lzmanag;

 /*
  *  Process ZMODEM remote file management requests
  */

                                                /* Remote ASCII override */
 if(!Rxbinary && zconv==ZCNL) Thisbinary = 0;

                                                /* Remote Binary override */
 if(zconv==ZCBIN)   Thisbinary = TRUE;
 else
 if(zmanag==ZMAPND) append=1;

 Bytesleft=0;
 Filemode=0;
 Modtime=0;

 p=name+1+strlen(name);
 if(*p)
 {                                  /* file coming from Unix or DOS system */
  sscanf(p,"%ld%lo%o",&Bytesleft, &Modtime, &Filemode);

  if(Filemode & UNIXFILE) ++Thisbinary;

  zdeb5("\nIncoming: %s %ld %lo %o\n", name, Bytesleft, Modtime, Filemode);
 }
 else
 {                                  /* File coming from CP/M system */
  for(p=name; *p; ++p)              /* change / to _ */
      if(*p=='/') *p = '_';

  if(*--p=='.')                     /* zap trailing period */
  *p=0;
 }


 zdeb3("Receiving %s %s \n",name,Thisbinary?"BIN":"ASCII");

 ftpinfo("Receiving %s file",Thisbinary?"binary":"ASCII");

 *len=0;

 if(AutoResume) flags=FTPOPENRESUME;


#ifdef NEVER
 {
  FILE * fp;
  fp=fopen("xx","wb");
  if(fp)
  {
   fwrite(name,1024,1,fp);
   fclose(fp);
  }
 }
#endif

 if(Rxcount==128)
  fout=ftpopenwrite(name,&bn,0,(int)Bytesleft,&flags,(int*)(name+124));
 else
  fout=ftpopenwrite(name,&bn,0,(int)Bytesleft,&flags,NULL);

 if(flags & FTPOPENRESUME)
 {
  if(fout)
  {
   ftpwritefseek(fout,0,SEEK_END);
   *len=ftpwritetell(fout);
  }
 }


 if(!fout) return(ERROR);
 else      return(OK);
}





/*
 * Putsec writes the n characters of buf to receive file fout.
 *  If not in binary mode, carriage returns, and all characters
 *  starting with CPMEOF are discarded.
 */


static int putsec(char * buf,int n)
{
 char * p;
 char * q;

 if(n==0) return(OK);

 if(Thisbinary)
 {
  ftpwrite(buf,1,n,fout);
 }
 else
 {
  if(Eofseen) return(OK);

  for(q=p=buf;n-->0;p++)
  {
   if(*p!='\r') *p=*q++;

   if(*p==CPMEOF)
   {
    Eofseen=TRUE;
    break;
   }
  }

  ftpwrite(buf,1,q-buf,fout);
 }

 return(OK);
}







/*
 * Ack a ZFIN packet, let byegones be byegones
 */

static void ackbibi(void)
{
 register n;

 zdeb1("ackbibi:");

 stohdr(0L);

 for(n=3;--n>=0;)
 {
  purgeline();
  zshhdr(ZFIN, Txhdr);
  switch (readline(100))
  {
          case 'O':
                   readline(1);    /* Discard 2nd 'O' */
                   zdeb1("ackbibi complete");
                   return;
 
         case RCDO:
                   return;

      case TIMEOUT:
           default:
                   break;
  }
 }
}











/*
 * Initialize for Zmodem receive attempt, try to activate Zmodem sender
 *  Handles ZSINIT frame
 *  Return ZFILE if Zmodem filename received, -1 on error,
 *   ZCOMPL if transaction finished,  else 0
 */

static int tryz(void)
{
 register c, n;
/* register cmdzack1flg;  */

 for (n=15;--n>=0;)
 {
  /* Set buffer length (0) and capability flags */

  stohdr(0L);



#ifdef CANBREAK
               Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
#else
               Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
#endif
               if (Zctlesc)
                       Txhdr[ZF0] |= TESCCTL;
               zshhdr(tryzhdrtype, Txhdr);
               if (tryzhdrtype == ZSKIP)       /* Don't skip too far */
                       tryzhdrtype = ZRINIT;   /* CAF 8-21-87 */
again:
               switch (zgethdr(Rxhdr, 0)) {
               case ZRQINIT:
                       continue;
               case ZEOF:
                       continue;
               case TIMEOUT:
                       continue;
               case ZFILE:
                       zconv = Rxhdr[ZF0];
                       zmanag = Rxhdr[ZF1];
                       ztrans = Rxhdr[ZF2];
                       tryzhdrtype = ZRINIT;
                       c = zrdata(secbuf, 1024);
                       if (c == GOTCRCW)
                               return ZFILE;
                       zshhdr(ZNAK, Txhdr);
                       goto again;
               case ZSINIT:
                       Zctlesc = TESCCTL & Rxhdr[ZF0];
                       if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
                               stohdr(1L);
                               zshhdr(ZACK, Txhdr);
                               goto again;
                       }
                       zshhdr(ZNAK, Txhdr);
                       goto again;
               case ZFREECNT:
                       stohdr(getfree());
                       zshhdr(ZACK, Txhdr);
                       goto again;
               case ZCOMMAND:

#ifndef XXXXXXXXX
                       return ERROR;
#else
                       cmdzack1flg = Rxhdr[ZF0];
                       if (zrdata(secbuf, 1024) == GOTCRCW) {
                               if (cmdzack1flg & ZCACK1)
                                       stohdr(0L);
                               else
                                       stohdr((long)sys2(secbuf));
                               purgeline();    /* dump impatient questions */
                               do {
                                       zshhdr(ZCOMPL, Txhdr);
                               }
                               while (++errors<20 && zgethdr(Rxhdr,1) != ZFIN);
                               ackbibi();
                               if (cmdzack1flg & ZCACK1)
                                       exec2(secbuf);
                               return ZCOMPL;
                       }
                       zshhdr(ZNAK, Txhdr); goto again;
#endif

               case ZCOMPL:
                       goto again;
               default:
                       continue;
               case ZFIN:
                       ackbibi(); return ZCOMPL;
               case ZCAN:
                       return ERROR;
               }
       }
       return 0;
}










/*
 * Close the receive dataset, return OK or ERROR
 */

static int closeit(char * message,int error)
{
 if(ftpclosewrite(fout,message,error?FTPCLOSEERROR:0)==ERROR)  return ERROR;


#ifdef NEVER
       if (Modtime) {
               timep[0] = time(&q);
               timep[1] = Modtime;
               utime(Pathname, timep);
       }


       if ((Filemode&S_IFMT) == S_IFREG)
               chmod(Pathname, (07777 & Filemode));
       return OK;

#endif


 return OK;
}







/*
 * Receive a file with ZMODEM protocol
 *  Assumes file name frame is in secbuf
 */



static int rzfile(void)
{
 int c;
 int n;
 int rxbytes=0;

 Eofseen=FALSE;

 if(procheader(secbuf,&rxbytes)==ERROR)
 {
  return(tryzhdrtype=ZSKIP);
 }

 /* file now open */

 n=20;

 /* if we're going to resume, then set rxbytes to current posn */

 while(1)
 {
  stohdr(rxbytes);
  zshhdr(ZRPOS, Txhdr);

nxthdr:
  switch(c=zgethdr(Rxhdr,0))
  {
         default:
                 zdeb2("rzfile: zgethdr returned %d", c);
                 closeit("Error",1);
                 return(ERROR);

      case ZNAK:
   case TIMEOUT:
                if(--n<0)
                {
                 zdeb2("rzfile: zgethdr returned %d", c);
                 closeit("Too many retries",1);
                 return(ERROR);
                }

     case ZFILE:
                zrdata(secbuf, 1024);
                continue;
  
      case ZEOF:
                if(rclhdr(Rxhdr)!= rxbytes)
                {
                 /*
                  * Ignore eof if it's at wrong place - force
                  *  a timeout because the eof might have gone
                  *  out before we sent our zrpos.
                  */
                  errors=0;
                  goto nxthdr;
                 }

                 if(closeit("OK",0))
                 {
                  tryzhdrtype = ZFERR;
                  zdeb1("rzfile: closeit returned <> 0");
                  return(ERROR);
                 }

                 zdeb1("rzfile: normal EOF");
                 ftpinfo("Transfer complete");
                 return(c);

     case ERROR:     /* Too much garbage in header search error */
                if(--n<0)
                {
                 zdeb2("rzfile: zgethdr returned %d", c);
                 closeit("Too many retries",1);
                 return(ERROR);
                }
                       zmputs(Attn);
                       continue;

     case ZSKIP:
                closeit("Skipped",0);
                zdeb1("rzfile: Sender SKIPPED file");
                return(c);

     case ZDATA:
                if(rclhdr(Rxhdr)!= rxbytes)
                {
                 if(--n<0)
                 {
                  closeit("Too many retries",1);
                  return(ERROR);
                 }
                 zmputs(Attn);
                 continue;
                }
moredata:
                zdeb3("%7ld ZMODEM%s ",rxbytes, Crc32?" CRC-32":"");

                ftpsetmode(Crc32?"CRC32":"CRC16");

                switch (c = zrdata(secbuf, 1024))
                {
                     case ZCAN:
                               zdeb2("rzfile: zgethdr returned %d", c);
                               closeit("Cancelled",1);
                               return(ERROR);

                    case ERROR:     /* CRC error */
                               if(--n<0)
                               {
                                zdeb2("rzfile: zgethdr returned %d", c);
                                closeit("Too many retries",1);
                                return(ERROR);
                               }
                               zmputs(Attn);
                               continue;

                  case TIMEOUT:
                               if(--n<0)
                               {
                                zdeb2("rzfile: zgethdr returned %d", c);
                                closeit("Too many retries",1);
                                return(ERROR);
                               }
                               continue;

                  case GOTCRCW:
                               n=20;
                               putsec(secbuf, Rxcount);
                               rxbytes+=Rxcount;
                               stohdr(rxbytes);
                               zshhdr(ZACK, Txhdr);
                               sendline(XON);
                               goto nxthdr;

                  case GOTCRCQ:
                               n=20;

                               putsec(secbuf, Rxcount);

                               rxbytes+=Rxcount;
                               stohdr(rxbytes);
                               zshhdr(ZACK, Txhdr);
                               goto moredata;

                  case GOTCRCG:
                               n=20;

                               putsec(secbuf, Rxcount);
                               rxbytes+= Rxcount;
                               goto moredata;

                  case GOTCRCE:
                               n=20;

                               putsec(secbuf, Rxcount);
                               rxbytes += Rxcount;
                               goto nxthdr;
                }
  }
 }
}








/*
 * Receive 1 or more files with ZMODEM protocol
 */

static int rzfiles(void)
{
 int c;

 while(1)
 {
  switch(c=rzfile())
  {
     case ZEOF:
    case ZSKIP:
               switch(tryz())
               {
                case ZCOMPL:
                            return OK;
                    default:
                            return ERROR;
                 case ZFILE:
                            break;
                }
               continue;

       default:
               return c;

    case ERROR:
               return(ERROR);
  }
 }
}






/*
 * Let's receive something already.
 */


static int wcreceives(void)
{
 int c;

 if((c=tryz())!=NULL)
 {
  if(c==ZCOMPL) return(OK);

  if(c==ERROR)  goto fubar;
  c=rzfiles();
  if(c)         goto fubar;
  return(OK);
 } 


fubar:
      ftpcanit();
      if(fout) closeit("Error",1);

 return(ERROR);
}



int wcreceive(void)
{
 ftpdirectreceive(NULL);
 return(wcreceives());
}


int rzmodem(int fp)
{
 ftpdirectreceive(stringptr(stack[fp]));
 wcreceives();
 return(0);
}


