/*--------------------------------------------------------------------------*/
/*               The Opus Computer-Based Conversation System                */
/*       (c) Copyright 1986, Wynn Wagner III, All Rights Reserved           */
/*                                                                          */
/*         The original Sealink protocol is copyrighted by SEA, inc.        */
/*                                                                          */
/*                 This module was written by W.Wagner III                  */
/*        with modifications done by Bob Hartman and Rick Huebner           */
/*                                                                          */
/*                                                                          */
/*                                                                          */
/*         GENERAL PURPOSE FILE RECEPTION (Xmodem/YModem/Sealink)           */
/*                                                                          */
/*                                                                          */
/*  This module is similar to a routine used by Opus-Cbcs (1.00).  It is    */
/*  provided for your information only.  You will find routines that need   */
/*  to be coded and identifiers to be resolved.                             */
/*                                                                          */
/*  There is absolutely no guarantee that anything here will work.  If you  */
/*  break this routine, you own both pieces.                                */
/*                                                                          */
/*  USAGE:  You may use this material in any program with no obligation     */
/*          as long as there is no charge for your program.  For more       */
/*          information about commercial use, contact the "OPUSinfo HERE"   */
/*          BBS (124/111).                                                  */
/*                                                                          */
/*                                                                          */
/*--------------------------------------------------------------------------*/
#include <ctype.h>
#include <stdio.h>
#include "xfer.h"
#include "com.h"





static   void pascal SendACK(void);
static   void pascal SendNAK(void);
static   void pascal getblock(void);


/*--------------------------------------------------------------------------*/
/* EXTERNALS                                                                */
/*--------------------------------------------------------------------------*/
extern char *UNLINK_msg;
extern byte *local_CEOL;

char *ultoa();


/*--------------------------------------------------------------------------*/
/* LOCALS                                                                   */
/*--------------------------------------------------------------------------*/
static unsigned int       block_number;
static unsigned int       base_block;
static char              *stat_msg;
static int                sliding;
static int                block_size;
static int                do_chksum;
static int                errs;
static int                fsize1;
static int                fsize2;
static char              *buffer;
static FILE              *infile;
static char              *_fpath;
static struct zero_block *header;
static char               final_name[80];
static int                first_block;     /* 1 = first block/Matrix bundle */

static char              *receiving       = "Receiving  ";

#define two_s "%s%s"



/*--------------------------------------------------------------------------*/
/* SEND ACK                                                                 */
/*--------------------------------------------------------------------------*/
static void pascal SendACK()
   begin

      SENDBYTE( ACK );
      if (sliding)
         begin
            SENDBYTE(  block_number );
            SENDBYTE( ~block_number );
         end

      if (block_number <= fsize1)
         begin

            /* Non-nonsense status line.  Don't waste time will frills... */
            gotoxy(locate_x,locate_y);
            cputs( ultoa(((unsigned long )(block_number)),e_input,10) );

         end

      errs = 0;

   end /* SendACK */



/*--------------------------------------------------------------------------*/
/* SEND NAK                                                                 */
/*--------------------------------------------------------------------------*/
static void pascal SendNAK()
   begin
      int   i;

      errs++;
      if (errs>6)
         begin
            stat_msg = FUBAR_msg;
            return;
         end


      CLEAR_INBOUND();


      /*--------------------------------------------------------------------*/
      /* Let the connection cool its heels.                                 */
      /*--------------------------------------------------------------------*/
      if ((base_block!=block_number) or (errs>1))
         do
            begin
               i = TIMED_READ(1);
               if (!CARRIER)               return;
            end
         while(i>=0);



      if (block_number>base_block) SENDBYTE(NAK);
      else
         begin
            if ((errs<5) and (!do_chksum)) SENDBYTE('C');
            else
               begin
                  do_chksum = 1;
                  SENDBYTE(NAK);
               end
         end

      if (sliding)
         begin
            SENDBYTE(  block_number );
            SENDBYTE( ~block_number );
         end

      if (block_number <= fsize1)
         begin

            /* No time for anything but the bare essentials... */
            gotoxy(locate_x,locate_y);
            cputs( ultoa(((unsigned long )(block_number)),e_input,10) );

         end

   end /* SendNAK */



/*--------------------------------------------------------------------------*/
/* GET BLOCK                                                                */
/*--------------------------------------------------------------------------*/
static void pascal getblock()
   begin
      register int   i;
      register char *sptr;

      unsigned int   crc;
      int            loop;
      int            in_char;
      int            is_resend;
      int            blockerr;
      unsigned char  chksum;

      blockerr = loop = is_resend = 0;
      chksum = '\0';


      /*--------------------------------------------------------------------*/
      /* HEADER: Block number                                               */
      /*--------------------------------------------------------------------*/
      in_char = TIMED_READ(5);
      if ( in_char != (block_number & 0xff) )
         begin
            if (in_char < block_number )  is_resend = 1;
            else if ((block_number) or (in_char != 1))
               begin
                  blockerr++;
                  stat_msg = SYNC_msg;
               end
				else block_number = 1;
         end

      /*--------------------------------------------------------------------*/
      /* HEADER: Complement of the block number                             */
      /*--------------------------------------------------------------------*/
      i = TIMED_READ(5);
      if ( (i&0xff) != ((~in_char)&0xff) )
         begin
            blockerr++;
            stat_msg = CMPL_msg;
         end

      /*--------------------------------------------------------------------*/
      /* DATA                                                               */
      /*--------------------------------------------------------------------*/
      for (sptr=buffer, i=0; i<block_size; i++, sptr++ )
         begin
            if (!CARRIER)  return;

            in_char = TIMED_READ(5);
            if (in_char<0)
               begin
                  SendNAK();
                  stat_msg = TIME_msg;
                  return;
               end
            sptr[0] = (char )in_char;
         end

      /*--------------------------------------------------------------------*/
      /* CHECK                                                              */
      /*--------------------------------------------------------------------*/
      if (do_chksum)
         begin

            for(sptr=buffer,i=0; i<block_size; i++,sptr++ )
               chksum += sptr[0];

            if (TIMED_READ(5)!=chksum)
               begin
                  stat_msg = CHK_msg;
                  blockerr++;
               end
         end
      else
         begin
            unsigned int lsb, msb;

            for(sptr=buffer,i=crc=0; i<block_size; i++,sptr++)
               crc = crc_update(crc,(byte )sptr[0]);

            crc = crc_finish(crc);

            msb    = TIMED_READ(3);
            lsb    = TIMED_READ(3);
            if ((lsb<0)||(msb<0))
               begin
                  stat_msg = SHRT_msg;
                  if (!block_number) sliding = 0;
                  blockerr++;
               end
            else if ( ((msb<<8)|lsb) != crc )
               begin
                  stat_msg = CRC_msg;
                  blockerr++;
               end
         end

      if (blockerr) SendNAK();
      else
         begin
            SendACK();
            if (is_resend)    return;

            if (block_number)
               begin
                  if (block_number < fsize1)
                     fwrite( buffer, block_size, 1, infile );
                  else if (block_number == fsize1)
                     fwrite ( buffer, fsize2, 1, infile);
               end
            else
               begin
						if (!first_block)
							begin
                        sptr  = &(header->name[0]);
		                  for(i=0; ((sptr[i])&&(i<17)); i++)
      		               if (sptr[i]<=' ') sptr[i]='\0';

            		      if (sptr[0])
                  		   begin
                        		sprintf( final_name, two_s, _fpath, sptr );
                              sptr  = final_name;
                              fancy_str( sptr );

                              if (locate_y>1) gotoxy(0,locate_y-1);
		                        status_line("-%s", sptr );
      		               end
            		      else status_line("!Grunged hdr");
							end
                  else message(NULL);

                  if (header->size)
                     begin
                        fsize1 = (int) (header->size/128L);
                        if (fsize2 = (header->size % 128)) ++fsize1;
                        else                               fsize2 = 128;
                        cprintf("  %d blks", fsize1);
                     end

                  if (header->moi[0]) cprintf(" from %s", header->moi );
                  set_xy(receiving);

               end
            block_number++;
         end

   end  /* getblock */






/*--------------------------------------------------------------------------*/
/* GENERAL PURPOSE FILE RECEIPTION ROUTINE                                  */
/*--------------------------------------------------------------------------*/
char *receive_file( fpath, fname, protocol )
   char *fpath, *fname, protocol;
   begin
      char  tmpname[80];
      int   in_char;
		int	block_timer;

      brk_disable();
      XON_DISABLE();

      throughput(0,0L);
      CLEAR_IOERR();
      fsize1 = 32767;
		if (fname)
	      for(in_char=0;fname[in_char];in_char++)
   	      if ( (fname[in_char]=='*') || (fname[in_char]=='?') ) fname[0]='\0';

      _fpath      = fpath;
      sliding     = 1;
      first_block =
      base_block  =
      do_chksum   =
      errs        = 0;
      block_size  = 128;

      locate_y    = wherey();
      locate_x    = 3 + wherex();

      switch(protocol)
         begin
            case 'X' :  base_block=1; sliding=0;                        break;
            case 'Y' :  base_block=1; sliding=0; block_size=1024;       break;
            case 'S' :                                                  break;
            case 'T' :                                                  break;
            case 'M' :  base_block=1; sliding=0;                        break;
            default  :  cprintf("Protocol??\r\n");                return NULL;
         end

      block_number   = base_block;

      sprintf( tmpname,    "%s_TMP_.$$$", fpath  );
      sprintf( final_name,  two_s, _fpath,(fname&&fname[0])?fname:"UNKNOWN.$$$");

      infile = fopen( tmpname, write_binary );
      if (got_error(CREATE_msg,tmpname)) return(NULL);

      buffer   = malloc(1032);
      header   = (struct zero_block *) buffer;
      if (!buffer)
         begin
            status_line("!MEM:ulbuf");
            return(NULL);
         end

      set_xy(receiving);
      stat_msg  = NULL;

      SendNAK();

      do
         begin

				/* Pretty loose timer for things like satellite delays, etc. */
/* block_timer = timerset(10);
*/

loop_top:
            if (((KEYPRESS()) and (READKB()==27)))
               begin
                  stat_msg = KBD_msg;
                  goto fubar;
               end

            in_char  = TIMED_READ(4);

            switch( in_char )
               begin
                  case SOH :  /*--------------------------------------------*/
                              /* SOH: 128 byte block header                 */
                              /*--------------------------------------------*/
                              block_size=128;
                              getblock();
                              break;

                  case STX :  /*--------------------------------------------*/
                              /* STX: YModem 1k block header                */
                              /*--------------------------------------------*/
                              block_size=1024;
                              getblock();
                              break;

                  case SYN :  /*--------------------------------------------*/
                              /* SYN: TeLink Zero block header              */
                              /*--------------------------------------------*/
                              do_chksum =  1;
                              getblock();
                              do_chksum =  0;
                              break;

                  case CAN :  /*--------------------------------------------*/
                              /* CAN                                        */
                              /*--------------------------------------------*/
                              if (TIMED_READ(2)==CAN)
                                 begin
                                    stat_msg = CAN_msg;
                                    goto fubar;
                                 end
                              break;

                  case EOT :  /*--------------------------------------------*/
                              /* EOT                                        */
                              /*--------------------------------------------*/
                              stat_msg = EOT_msg;
                              if (block_number) goto done;
                              else goto fubar;

                  default  :  /*--------------------------------------------*/
                              /* Default                                    */
                              /*--------------------------------------------*/
                              if (in_char>0)
                                 begin
                                    gotoxy(locate_x,locate_y);
                                    cprintf("??? [%Xh, blk %d] ",in_char,block_number);
                                    set_xy(receiving);
                                 end

                              /*--------------------------------------------*/
										/* Debris is tolerable for up to the amount   */
										/* of time we are willing to wait for a start */
										/* of a block.  Right now it is 10 seconds.   */
                              /*--------------------------------------------*/
/*										if ( (!timeup(block_timer)) and (CARRIER) )
											goto loop_top;
										else
*/

	                           SendNAK();
                              break;

               end /* switch */

            if (errs>14)
               begin
                  stat_msg = FUBAR_msg;
                  goto fubar;
               end

            else if (got_error(WRITE_msg,fname))
               begin
                  stat_msg = IO_msg;
                  goto fubar;
               end

            if (stat_msg)
               begin
                  cputs( stat_msg );
                  cputs( local_CEOL );
                  stat_msg = NULL;
               end

         end
      while(CARRIER);

      stat_msg = CARRIER_msg;


fubar:

      if (buffer) free(buffer);
      if (infile)
         begin
            fclose(infile);
            got_error(CLOSE_msg,tmpname);
            unlink(tmpname);
            got_error(UNLINK_msg,tmpname);
         end

      CLEAR_OUTBOUND();

      if (stat_msg != EOT_msg)
			begin
            for(in_char=0; in_char<5; in_char++)
				   SENDBYTE(CAN);

            status_line(":File not received: %s",stat_msg);
			end;

      CLEAR_INBOUND();
      return NULL;


done:

      SendACK();

      if (buffer) free(buffer);

      if (infile)
         begin
            int             i, j;
            struct FILEINFO dta;

            fclose(infile);
            got_error(CLOSE_msg,tmpname);

            i = strlen(tmpname)    -1;
            j = strlen(final_name) -1;

            if (tmpname[i]=='.')    tmpname[i]='\0';
            if (final_name[j]=='.') final_name[j]='\0';

            i = 0;
            while(rename(tmpname,final_name))
               begin
                  if (isdigit(final_name[j])) final_name[j]++;
                  else final_name[j]='0';
                  if (!isdigit(final_name[j])) return(tmpname);
                  i = 1;
               end
            CLEAR_IOERR();
            if (i) status_line("+Dupe file renamed: %s",final_name);

            if (!dfind(&dta,final_name,0))
               begin
                  if (locate_y) gotoxy(0,locate_y-1);
                  status_line("=UL-S %s",final_name);
                  throughput(1,dta.size);

                  dta.size /= 1024L;
#ifndef BINKLEY
                  cur_user.upld += 1 + (unsigned int )dta.size;

                  if (cur_user.dnldl>0)
                     cur_user.dnldl-= 1 + (unsigned int )dta.size;
#endif
                  strcpy( final_name, dta.name );
                  CLEAR_IOERR();
                  return final_name;             /* signal what file we got */
               end

         end
      CLEAR_IOERR();
      return NULL;

   end

/* END OF FILE: recv_sl.c */

