
/* Unfortunately this is not the version which uses the tree.   */
/* Let me look for that version, as it is the one worth having. */


/* This version pretty good.  Still slow but uses fossil successfully */
/*  and seems to be fairly robust.  Needs to have command line args   */
/*  reduced to not require the darned baud rate as an argument.       */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <process.h>
#include <conio.h>
#include <time.h>
#include <dos.h>  /* Dos-specific functions are used to get date/time. */

#include "common.h"    /* Definitions/routines Al uses lots. */
#include "modem.h"     /* Routines for accessing the modem.  */
#include "protocol.h"  /* Packet send/receive routines.      */

/*
Packet types:
  - Receiver:    'a'bort, 'e'of acknowledged, 's'end from byte x
  - Transmitter: 'a'bort, 'd'ata from byte x, 'f'ile header (size x)

State types:
  - Receiver:    waiting for 'h'eader, waiting for 'd'ata
  - Transmitter: waiting for header 'a'ck, 's'ending data, waiting for 'e'of ack
*/

#define PROTOCOL_STARTING_BLOCK_LENGTH     64
#define PROTOCOL_SUCCESSIVE_PACKETS_NEEDED  8

#define TIMEOUT_LONG 30   /* Longest timeout we're willing to endure.    */
#define TIMEOUT_MODEM 7   /* Longest time we expect EC modems to falter. */
#define RETRY_LIMIT   5   /* Max. number of retries to restart an xfer.  */

#define ERRORLEVEL_SUCCESS          0
#define ERRORLEVEL_BAD_PARAMETER    1
#define ERRORLEVEL_TRANSFER_FAILED  2

char packet_buffer[PROTOCOL_PACKET_MAX];

char current_state;  /* Used by xmit/rcv routines to hold current state. */

unsigned short packet_length,retry_count,repeat_count;
long bytes_in_file,temp_long,byte_no,next_byte_no_expected;
long file_fptr=0;  /* Position in the file we're at. */
time_t transfer_start_time; /* Hold time for CPS computation. */
int comm_port_number,current_timeout,resent_packet_count=0;


/*--------------------------- Subroutines ----------------------------------*/

long get_byte_no_from_packet_buffer()
{
long msb=0,msb2=0,lsb2=0,lsb=0;
if (packet_buffer[1] < 0)  msb  = (unsigned long)packet_buffer[1] + 256;  else msb  = (unsigned long)packet_buffer[1];
msb  <<= 24;
if (packet_buffer[2] < 0)  msb2 = (unsigned long)packet_buffer[2] + 256;  else msb2 = (unsigned long)packet_buffer[2];
msb2 <<= 16;
if (packet_buffer[3] < 0)  lsb2 = (unsigned long)packet_buffer[3] + 256;  else lsb2 = (unsigned long)packet_buffer[3];
lsb2 <<=  8;
if (packet_buffer[4] < 0)  lsb  = (unsigned long)packet_buffer[4] + 256;  else lsb  = (unsigned long)packet_buffer[4];
/*
printf("get_byte_from_packet_buffer()  %d  %d  %d  %d\n",packet_buffer[1],packet_buffer[2],packet_buffer[3],packet_buffer[4]);
printf("msb msb2 lsb2 lsb:   %ld %ld %ld %ld\n",msb,msb2,lsb2,lsb);
*/
return(msb | msb2 | lsb2 | lsb);
}

void put_byte_no_into_packet_buffer(long the_number)
{
unsigned char msb=0,msb2=0,lsb2=0,lsb=0;
msb  = (the_number >> 24) & 255;
packet_buffer[1] = msb;
msb2 = (the_number >> 16) & 255;
packet_buffer[2] = msb2;
lsb2 = (the_number >>  8) & 255;
packet_buffer[3] = lsb2;
lsb  = the_number         & 255;
packet_buffer[4] = lsb;
/*
printf("put_byte_into_packet_buffer()  %d  %d  %d  %d\n",packet_buffer[1],packet_buffer[2],packet_buffer[3],packet_buffer[4]);
*/
}

void tell_other_side_to_abort()
{
repeat_count = 10;   /* Send lots of these; the extra ones will be trashed. */
packet_buffer[0] = 'a';
while (repeat_count--)
  protocol_send_packet(comm_port_number,packet_buffer,1,common_debug_level_current+1);
}


/*----------------------------- Routines -----------------------------------*/

int send_file_header_packet(char filename_only[])
{
common_debug_message(common_debug_level_current,"Sending file header packet.");
strcpy(packet_buffer,"f");              /* Signifies a file header packet. */
put_byte_no_into_packet_buffer(bytes_in_file);
strcpy(packet_buffer+5,filename_only); /* Name.ext in string form..... */
if (protocol_send_packet(comm_port_number,packet_buffer,5+strlen(packet_buffer+5),common_debug_level_current+1))
  return(1);
return(0);
}

aom_file_send(char *filespec_to_send)
{
FILE *file_to_send;
int current_block_length,successive_good_packet_count,maximum_block_length;
unsigned short bytes_to_send;
char filename_only[16],*temp_char_ptr;

maximum_block_length = 1024;
printf("Maximum allowed block length to send is %d.\n",maximum_block_length);

temp_char_ptr = strrchr(filespec_to_send,'\\');  /* Simplify filespec to just file.ext. */
if (temp_char_ptr == NULL)
  strcpy(filename_only,filespec_to_send);
else
  strcpy(filename_only,temp_char_ptr+1);
file_to_send = fopen(filespec_to_send,"rb");  /* Open the file. */
if (file_to_send == NULL)
  {
  printf("!File to send %s not found.\n",filespec_to_send);
  return(1);   /* Error sending file. */
  }

fseek(file_to_send,0,SEEK_END);       /* Move to EOF.   */
bytes_in_file = ftell(file_to_send);  /* Save filesize. */
printf("Sending file %s (%ld bytes).\n",filename_only,bytes_in_file);
printf("Waiting %d seconds for receiver to come on-line before beginning.\n",TIMEOUT_MODEM);
printf("Hit ESC at any time to abort the transfer.\n");
current_block_length = PROTOCOL_STARTING_BLOCK_LENGTH;
successive_good_packet_count = 0;

current_state = 'a';              /* Set up to wait for a header ack.  Since  */
current_timeout = TIMEOUT_MODEM;  /* we haven't sent a header, a timeout will */
retry_count = 0;                  /* of course occur; then we'll send header. */

send_loop:
if (kbhit())        /* Keyboard abort? */
  {
  if (getch() != ESC_character)   /* Any key other than escape hit? */
    {
    printf("A key was pressed.  As it was not the escape key, it was ignored.%s\n",BELL_string);
    goto send_loop;
    }
  fclose(file_to_send);  /* ...whether or not it's open! */
  printf("!Keyboard abort; quitting transfer....\n");
  goto abort_transfer;
  }
if (!fossil_cd(fossil_current_port))   /* Carrier lost while sending? */
  {
  fclose(file_to_send);
  printf("?Carrier lost; aborting....\n");
  trash_modem_output(comm_port_number,0);
  return(1);
  }

/* See if the other end has anything to say to us. */
packet_length = protocol_get_packet(comm_port_number,current_timeout,packet_buffer,common_debug_level_current+1);

switch (current_state)  /* Depending on our state, see what to do, whether or not we received a packet. */
  {
  case 'a':     /* Waiting for file header ack. */
    {
    if (!packet_length)            /* Timeout on file header ack? */
      {
      if (retry_count++ == RETRY_LIMIT)
        {
        fclose(file_to_send);
        printf("?Retry count exceeded while waiting for file header ack; aborting....\n");
        return(1);
        }
      if (send_file_header_packet(filename_only))
        {
        printf("?Carrier lost; aborting....\n");
        goto abort_transfer;
        }
      goto send_loop;
      }
    switch (packet_buffer[0])
      {
      case 'a': printf("!Remote aborted\n");
                return(1);
      case 's': break;  /* Correct type; fall through. */
      default : printf("?Unexpected packet type %c received; aborting....\n",packet_buffer[0]);
                goto abort_transfer;
      }
    printf("Starting send of file data....\n");
    transfer_start_time = time(NULL);   /* For CPS calculation later. */
    file_fptr = get_byte_no_from_packet_buffer();
    goto send_data;
    }
  case 's':
    {
    if (!packet_length)  /* Receiver had nothing to complain about? */
      {
      if ((successive_good_packet_count++ >= PROTOCOL_SUCCESSIVE_PACKETS_NEEDED)
       && (current_block_length < maximum_block_length))
        {
        printf("Doubling block length to %d.\n",current_block_length*2);
        current_block_length *= 2;
        successive_good_packet_count = 0;
        }
      goto send_data;
      }
    switch (packet_buffer[0])   /* What kind of packet did we receive? */
      {
      case 's': break;
      case 'a': printf("?Remote requested abort; aborting....\n");
                fclose(file_to_send);
                wait(TIMEOUT_MODEM);
                trash_modem_output(comm_port_number,0);
                return(1);
      default : printf("?Invalid packet type %c received; aborting....\n",packet_buffer[0]);
                return(1);
      }
    file_fptr = get_byte_no_from_packet_buffer();
    printf("Resend requested at byte %ld\n",file_fptr);
    resent_packet_count++;
    successive_good_packet_count = 0;
    current_block_length = PROTOCOL_STARTING_BLOCK_LENGTH;
    goto send_data;
    }
  case 'e':
    {
    if (!packet_length)
      {
      if (retry_count++ == RETRY_LIMIT)   /* Timed out. Retry-limit yet? */
        {
        fclose(file_to_send);
        printf("?Timeout waiting for eof ack from receiver -- the status of\n");
        printf(" the file on the receiver's end is unknown; aborting....\n");
        goto abort_transfer;
        }
      /* Send a data packet with byte of -1 to ask receiver to request a  */
      /*  resend at whatever byte it wants, since it hasn't acked EOF.    */
      packet_buffer[0] = 'd';
      put_byte_no_into_packet_buffer(-1L);
      packet_buffer[5] = 0; /* Dummy data byte. */
      bytes_to_send = 1;
      if (protocol_send_packet(comm_port_number,packet_buffer,5+bytes_to_send,common_debug_level_current+1))
        {
        fclose(file_to_send);
        printf("?Carrier lost; aborting....\n");
        trash_modem_output(comm_port_number,0);
        return(1);
        }
      goto send_loop;
      }
    switch (packet_buffer[0])
      {
      case 'a': printf("?Remote requested abort; aborting....\n");
                fclose(file_to_send);
                wait(TIMEOUT_MODEM);
                trash_modem_output(comm_port_number,0);
                return(1);
      case 'e': fclose(file_to_send);
                printf("File successfully acknowledged by receiver; %d packets resent.\n",resent_packet_count);
                printf("Transfer CPS: %ld\n",bytes_in_file/(time(NULL)-transfer_start_time));
                wait(TIMEOUT_MODEM);
                trash_modem_output(comm_port_number,0);
                return(0);
      case 's': file_fptr = get_byte_no_from_packet_buffer();
                printf("Resend requested at byte %ld\n",file_fptr);
                resent_packet_count++;
                successive_good_packet_count = 0;
                current_block_length = PROTOCOL_STARTING_BLOCK_LENGTH;
                goto send_data;
      default : printf("?Invalid packet type %c received while waiting for eof ack; aborting....\n",packet_buffer[0]);
                goto abort_transfer;
      }
    }
  default:
    {
    printf("?Invalid current state %d\n",(int)current_state);
    return(1);
    }
  }

/*----- These paragraphs set up the proper state and go to the loop. -----*/
send_data:
printf("%ld / %ld %c",file_fptr,bytes_in_file,13);  /* Provide tiny progress display. */
packet_buffer[0] = 'd';         /* Start building outgoing data packet. */
put_byte_no_into_packet_buffer(file_fptr);
if ((bytes_in_file - file_fptr) >= current_block_length) /* Find size to send.*/
  bytes_to_send = current_block_length;
else
  bytes_to_send = bytes_in_file - file_fptr;
fseek(file_to_send,file_fptr,SEEK_SET);
fread(packet_buffer+5,bytes_to_send,1,file_to_send);
if (protocol_send_packet(comm_port_number,packet_buffer,5+bytes_to_send,common_debug_level_current+1))
  {
  fclose(file_to_send);
  printf("?Carrier lost; aborting....\n");
  trash_modem_output(comm_port_number,0);
  return(1);
  }
file_fptr += bytes_to_send;  /* Data sent; adjust this accordingly. */
if (file_fptr < bytes_in_file)  /* Have we sent the last byte of data yet? */
  {
  current_state = 's';
  current_timeout = 0;
  goto send_loop;
  }
/* We have sent last data byte.  This state can only occur at this point. */
current_state = 'e';
current_timeout = TIMEOUT_MODEM;
goto send_loop;

/*----- This is just for aborting transfers. -----*/
abort_transfer:
tell_other_side_to_abort();
wait(TIMEOUT_MODEM);
trash_modem_output(comm_port_number,0);
return(1);

}

/*--------- Now the receive routine. --------*/

int aom_file_receive(char *file_to_receive)
{
FILE *incoming_file;
char incoming_filename[128];
time_t time_of_last_send_request=0;

printf("Waiting for file header packet from transmitter.\n");
current_state = 'h';
current_timeout = TIMEOUT_LONG;

receive_loop:
if (kbhit())        /* Keyboard abort? */
  {
  if (getch() != ESC_character)
    {
    printf("A key we pressed.  As it was not the escape key, it was ignored.%s\n",BELL_string);
    goto receive_loop;
    }
  fclose(incoming_file);  /* ...whether it's open or not! */
  printf("!Keyboard abort; quitting transfer....\n");
  goto abort_transfer;
  }
if (!fossil_cd(fossil_current_port))   /* Was carrier lost? */
  {
  fclose(incoming_file);
  printf("?Carrier lost; aborting....\n");
  trash_modem_output(comm_port_number,0);
  return(1);
  }
packet_length = protocol_get_packet(comm_port_number,current_timeout,packet_buffer,common_debug_level_current+1);

switch (current_state)
  {
  case 'h':
    {
    if (!packet_length)            /* Timeout on file header? */
      return(1);
    switch (packet_buffer[0])
      {
      case 'a': printf("!Remote aborted send; aborting....\n");
                return(1);
      case 'f': break;  /* Correct type; fall through. */
      default : printf("?Unexpected packet type %c received; aborting....\n",packet_buffer[0]);
                goto abort_transfer;
      }
    transfer_start_time = time(NULL);   /* For CPS calculation later. */
/*    common_debug_message(common_debug_level_current,"Got file header packet from transmitter."); */
    bytes_in_file = get_byte_no_from_packet_buffer();
    if (file_to_receive[0])
      strcpy(incoming_filename,file_to_receive);
    else
      strcpy(incoming_filename,packet_buffer+5);
    incoming_file = fopen(incoming_filename,"rb");
    if (incoming_file != NULL)
      {
      fclose(incoming_file);
      printf("!Aborting receive of existing file %s.\n",incoming_filename);
      goto abort_transfer;
      }
    incoming_file = fopen(incoming_filename,"wb");
    if (incoming_file == NULL)
      {
      printf("!Couldn't open incoming file %s for write.\n",incoming_filename);
      goto abort_transfer;
      }
    printf("Receiving file %s (%ld bytes)\n",incoming_filename,bytes_in_file);
    next_byte_no_expected = 0;   /* We expect the file to start! */
    printf("Syncing with sender.\n");
    goto request_send_at_byte;
    }
  case 'd':
    {
    if (!packet_length)
      {
      if (retry_count++ == RETRY_LIMIT)   /* Timed out. Retry-limit yet? */
        {
        fclose(incoming_file);
        printf("?Retry count exceeded while waiting for data; aborting....\n");
        goto abort_transfer;
        }
      goto receive_loop;
      }
    switch (packet_buffer[0])   /* What kind of packet did we receive? */
      {
      case 'f': printf("Skipping extra file header packet.\n");
                goto wait_for_data;  /* Skip any late file header packets. */
      case 'd': break;               /* Correct type; fall through.   */
      case 'a': printf("?Remote requested abort; aborting....\n");
                fclose(incoming_file);
                wait(TIMEOUT_MODEM);
                trash_modem_output(comm_port_number,0);
                return(1);
      default : printf("?Invalid packet type %c received; aborting....\n",packet_buffer[0]);
                return(1);
      }
    byte_no = get_byte_no_from_packet_buffer();   /* We got a data packet. */
    if (byte_no == -1)
      {
      printf("Received position inquiry from transmitter; requesting resend at byte %ld.\n",next_byte_no_expected);
      goto request_send_at_byte;
      }
    printf("%ld / %ld %c",byte_no,bytes_in_file,13);  /* Provide tiny progress display. */
    if (byte_no < next_byte_no_expected)
      {
      printf("Skipping already-received data block.\n");
      goto wait_for_data;                         /* Ignore.                */
      }
    if (byte_no != next_byte_no_expected)         /* Not the right one?     */
      {                                           /* Must be future packet. */
      /* common_debug_message_w_no(common_debug_level_current+1,"Requesting resend at byte ",next_byte_no_expected); */
      goto request_send_at_byte;
      }
    /* common_debug_message_w_no(common_debug_level_current,"Writing data block to incoming file at byte ",byte_no); */
    fseek(incoming_file,byte_no,SEEK_SET);
    fwrite(packet_buffer+5,packet_length-5,1,incoming_file);
    if (ftell(incoming_file) == bytes_in_file)
      {
      fclose(incoming_file);
      printf("Sending file acknowledgement packet to transmitter.\n");
      packet_buffer[0] = 'e';
      repeat_count = 10;     /* Sends lots; extras will be trashed. */
      while (repeat_count--)
        protocol_send_packet(comm_port_number,packet_buffer,1,common_debug_level_current+1);
      printf("Successful receive of %s; %d resends requested.\n",incoming_filename,resent_packet_count-1);
      printf("Transfer CPS: %ld\n",bytes_in_file/(time(NULL)-transfer_start_time));
      wait(TIMEOUT_MODEM);
      trash_modem_output(comm_port_number,0);
      return(0);
      }
    if (ftell(incoming_file) > bytes_in_file)
      {
      printf("?Incoming file has more bytes than original; aborting....\n");
      return(1);
      }
    next_byte_no_expected += packet_length - 5; /* Packet_length - 5 = block size.*/
    /* common_debug_message_w_no(common_debug_level_current,"Wrote to incoming file up to byte ",next_byte_no_expected); */
    goto wait_for_data;
    }
  default:
    {
    printf("?Invalid current state %d\n",(int)current_state);
    return(1);
    }
  }

/*----- These paragraphs set up the proper state and go to the loop. -----*/
request_send_at_byte:
if ((time(NULL) - time_of_last_send_request) < TIMEOUT_MODEM)  /* Too soon to do another resend request. */
  {
  /* common_debug_message(common_debug_level_current,"Too early to request a resend."); */
  goto wait_for_data;     /* Just skip this resend request; buffers might be emptying. */
  }
printf("Requesting send from byte %ld.\n",next_byte_no_expected);
packet_buffer[0] = 's';
put_byte_no_into_packet_buffer(next_byte_no_expected);
if (protocol_send_packet(comm_port_number,packet_buffer,5,common_debug_level_current+1))
  {
  fclose(incoming_file);
  printf("?Carrier lost; aborting....\n");
  trash_modem_output(comm_port_number,0);
  return(1);
  }
resent_packet_count++;
time_of_last_send_request = time(NULL);
goto wait_for_data;

wait_for_data:
current_state = 'd';
current_timeout = TIMEOUT_MODEM;
retry_count = 0;
goto receive_loop;

/*----- This is just for aborting transfers. -----*/
abort_transfer:
tell_other_side_to_abort();
wait(TIMEOUT_MODEM);
trash_modem_output(comm_port_number,0);
return(1);

}
/*---------------------------- The Program ---------------------------------*/

int main(int argc, char *argv[])
{
char arg_port[64],arg_function[64],arg_file_to_transfer[128];
int errcode;

common_debug_level_screen = 0;    /* Let's try this for now. */

printf("AOM Transfer Protocol          Date: 11/19/1995 \n");
if (argc < 3)
  {
  printf("?Illegal number of arguments.\n");
  goto bad_parameter;
  }
strcpy(arg_port,    argv[1]);
strcpy(arg_function,argv[2]);

comm_port_number  = atoi(arg_port);  /* Open the modem port.  There is no corresponding close  */

if (!strcmp(arg_function,"s"))    /* Sending a file? */
  {
  if (argc < 4)
    {
    printf("?Send option specified, but no filename given!\n");
    goto bad_parameter;
    }
  if (initSerial(comm_port_number))
    {
    printf("?Unable to initialize modem port; is it correct?\n");
    goto bad_parameter;
    }
  port_set_handshaking(comm_port_number,1);  /* Enable hardware handshaking. */
  strcpy(arg_file_to_transfer,argv[3]);
  errcode = aom_file_send(arg_file_to_transfer);
  deinitSerial(comm_port_number,1);
  wait(2);
  return(errcode);
  }

if (!strcmp(arg_function,"r"))         /* Receiving a file? */
  {
  if (initSerial(comm_port_number))
    {
    printf("?Unable to initialize modem port; is it correct?\n");
    goto bad_parameter;
    }
  port_set_handshaking(comm_port_number,1);  /* Enable hardware handshaking. */
  arg_file_to_transfer[0] = 0;
  if (argc == 4)   /* Was file specified on command line? */
    strcpy(arg_file_to_transfer,argv[3]);
  errcode = aom_file_receive(arg_file_to_transfer);
  deinitSerial(comm_port_number,1);
  wait(1);
  return(errcode);
  }

printf("?The invalid function code %s was received!  's' or 'r' are valid.\n",arg_function);

bad_parameter:
printf("\n");
printf("Command syntax:                                 \n");
printf("    AOM [port(1|2)] [s|r] [file_to_xfer]        \n");
printf("  where s is for send or r is for receive.      \n");
printf("     e.g.  AOM 1 s help.doc                     \n");
printf("  The filename may be omitted when receiving.   \n");
printf("Exiting with errorlevel %d.\n",ERRORLEVEL_BAD_PARAMETER);
return(ERRORLEVEL_BAD_PARAMETER);
}


