home *** CD-ROM | disk | FTP | other *** search
- /*
- * _KERMITR.C
- *
- * Contains: _KermitReceive()
- * _KermitGetInitPacket()
- * _KermitGetBorF()
- * _KermitReadFileData()
- * _KermitSendInitialAck()
- * _KermitSendAck()
- * _KermitResendAck()
- * _KermitResendInitialAck()
- * _KermitSaveBuffer()
- *
- * The Greenleaf Comm Library
- *
- * Copyright (C) 1989-90 Greenleaf Software Inc. All Rights Reserved.
- *
- */
-
- #include <stdio.h>
- #include <string.h>
- #include "asiports.h"
- #include "xfer.h"
- #include "_xfer.h"
-
- /*
- * Local prototypes for static procedures.
- */
- static int GF_CONV _KermitGetInitPacket( XFER *kermit );
- static int GF_CONV _KermitGetBorF( XFER *kermit );
- static int GF_CONV _KermitReadFileData( XFER *kermit );
- static int GF_CONV _KermitSendInitialAck( XFER *kermit );
- static int GF_CONV _KermitSendAck( XFER *kermit );
- static int GF_CONV _KermitResendAck( XFER *kermit );
- static int GF_CONV _KermitResendInitialAck( XFER *kermit );
- static GF_CONV _KermitSaveBuffer( XFER *kermit );
-
- /*
- * int _KermitReceive( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This routine is the private driver for performing a kermit file receive.
- * It is called by the public setup routine, KermitReceive(), which just
- * sets up the parameter block and then calls this guy. The logic of this
- * routine is pretty simple. It first gets the inital packet from the
- * remote end, which sets up a few of the parameters for the transfer.
- * It then sits in a loop receiving files, one after another, until the
- * remote end decides to abort, or something bad happens. It the returns
- * to the routine who called it, which is probably found in KERMIT.C, where
- * all the public routines are.
- *
- * For the record, kermit packet types have the following definitions:
- *
- * S: The S-Init packet. One of these comes at the startup phase
- * and tells us a few things about
- *
- * F: This is a file header packet. It tells me the name of the
- * file that is about to be sent.
- *
- * D: This is a data packet. Presumably it has stuff that is supposed
- * to go into the file I have just opened.
- *
- * Z: This is an EOF packet. It means I can close up the file.
- *
- * B: This is a break packet. It means I can now take the connection
- * down.
- *
- * E: This is an error packet. It contains error text from the
- * remote end.
- *
- * Y: This is an ACK packet. It usually contains no data, but
- * an ACK in response to an S packet should contain S data.
- *
- * N: This is a NAK packet. It contains no data.
- *
- * T: This is a timeout packet. A packet didn't really come, it
- * means the receiver couldn't get any data.
- *
- * Q: This is a questionable packet. A valid packet didn't really
- * come through. Something was wrong with it, like a bad checksum.
- *
- * A: This is an abort packet. Usually happens when the user hits
- * the abort key while reading data.
- *
- * SIDE EFFECTS
- *
- * File(s) may have been transfered.
- *
- * RETURNS
- *
- * The routine returns one of the defined XFER_RETURN codes defined
- * in XFER.H.
- *
- * AUTHOR
- * Mark Nelson 28-Aug-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
-
- int GF_CONV _KermitReceive( XFER *kermit )
- {
-
- kermit->sending = FALSE;
- if ( !_XferInitialize( kermit ) )
- return( FALSE );
- if ( !_KermitGetInitPacket( kermit ) )
- return( FALSE );
-
- for ( ; ; ) {
- if ( !_KermitGetBorF( kermit ) )
- return( FALSE );
- if ( !_KermitReadFileData( kermit ) )
- return( FALSE );
- }
- }
-
- /*
- * static int _KermitGetInitPacket( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This routine is a private routine called to read the initial packet
- * during a Kermit Receive. The logic of this routine is fairly simple.
- * It reads in packets until it sees an 'S' type packet. An 'S' packet
- * is what it is waiting for, so when this happens, it sets up some
- * parameters that it pulls out of the packet, returns, and everyone
- * is happy. Otherwise, it reads in wrong packet types until one of
- * several things causes him to give up and abort.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop.
- *
- *
- * AUTHOR
- * Mark Nelson 28-Aug-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitGetInitPacket( XFER *kermit )
- {
- int type;
-
- for ( ; ; ) {
- type = _KermitReceivePacket( kermit );
- switch ( type ) {
- /*
- * If I get an S packet, I load up the initialization parameters, and then
- * send the other end an ACK packet that contains my initialization params.
- */
- case 'S' :
- _XferMessage( kermit, "Received initial S packet" );
- _KermitLoadInitParameters( kermit );
- return( _KermitSendInitialAck( kermit ) );
- /*
- * T packets mean timeout. If I get too many of these I abort.
- */
- case 'T' :
- _XferMessage( kermit, "Timeout waiting for init packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitSendPacket( kermit, 'N', 0, "" ) )
- return( FALSE );
- break;
- /*
- * A Q packet is a funny packet, malformed or something like that.
- */
- case 'Q' :
- _XferMessage( kermit, "Funny packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitSendPacket( kermit, 'N', 0, "" ) )
- return( FALSE );
- break;
- /*
- * Anything else, like a B packet or an E packet and I give up entirely.
- */
- default :
- _XferMessage( kermit, "Protocol error" );
- kermit->return_status = XFER_RETURN_PROTOCOL_ERROR;
- _XferCleanup( kermit );
- return( FALSE );
- }
- }
- }
-
- /*
- * static int _KermitGetBorF( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * After I have read in the init packet, or after I have finished reading
- * in a file, I go to this routine. This guy is looking for a B packet
- * or an F packet. A B packet means Kermit is through, and it is time
- * to exit. An F packet means a new file is coming down the line, and
- * it is time to go ahead and receive the packets.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is over or aborted and it is time to stop.
- *
- *
- * AUTHOR
- * Mark Nelson 28-Aug-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitGetBorF( XFER *kermit )
- {
- int type;
-
- for ( ; ; ) {
- type = _KermitReceivePacket( kermit );
- switch ( type ) {
- /*
- * The only good reason for another S packet is that the remote end might
- * not have seen my ACK. Assuming this is true, I make sure I didn't
- * exceed my error count, then resend the S-ACK.
- */
- case 'S' :
- _XferMessage( kermit, "Received duplicate S-Init packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitResendInitialAck( kermit ) )
- return( FALSE );
- break;
- /*
- * The only good reason for another Z packet is the assumption that the
- * remote end might not have seen my ACK to his Z. So I try to send
- * it again.
- */
- case 'Z' :
- _XferMessage( kermit, "Received duplicate Z-End-of-file packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitResendAck( kermit ) )
- return( FALSE );
- break;
- /*
- * An F packet means it is time to get a new file. I copy the file
- * name into my buffer, open the file, and return a TRUE to keep
- * on going.
- */
- case 'F' :
- strcpy( kermit->filename, kermit->buffer+4 );
- if ( !_XferOpenFile( kermit ) )
- return( FALSE );
- if ( !_KermitSendAck( kermit ) )
- return( FALSE );
- else
- return( TRUE );
-
- case 'B' :
- _XferMessage( kermit, "Received break command" );
- _KermitSendAck( kermit );
- _XferCleanup( kermit );
- return( FALSE );
-
- case 'T' :
- _XferMessage( kermit, "Timeout waiting for B or F packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitSendPacket( kermit, 'N', 0, "" ) )
- return( FALSE );
- break;
- case 'Q' :
- _XferMessage( kermit, "Funny packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitSendPacket( kermit, 'N', 0, "" ) )
- return( FALSE );
- break;
- case 'A' :
- return( FALSE );
- case 'E' :
- _XferMessage( kermit, "Aborted from remote end" );
- kermit->return_status = XFER_RETURN_REMOTE_ABORT;
- _XferCleanup( kermit );
- return( FALSE );
- default :
- _XferMessage( kermit, "Protocol error" );
- kermit->return_status = XFER_RETURN_PROTOCOL_ERROR;
- _XferCleanup( kermit );
- return( FALSE );
- }
- }
- }
-
- /*
- * static int _KermitReadFileData( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This routine is called to read in all of the data that goes into
- * a file. It sits in a loop soaking up D packets until something
- * happens that causes it to return. A Z packet is the only good
- * reason to return, everything else is an error.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop. As long as the packets
- * are coming in okay, I sit in the while (TRUE) loop sucking them in.
- *
- *
- * AUTHOR
- * Mark Nelson 28-Aug-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitReadFileData( XFER *kermit )
- {
- int type;
-
- for ( ; ; ) {
- type = _KermitReceivePacket( kermit );
- switch ( type ) {
- /*
- * The only good reason I can think of for getting an F packet
- * here is that the remote end did not see the ACK. Guessing that
- * that might be the case, I try to resend the ack.
- */
- case 'F' :
- _XferMessage( kermit, "Received duplicate File Header packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitResendAck( kermit ) )
- return( FALSE );
- break;
- /*
- * If I get an EOF Message, everything is okay. I close the file, ACK
- * and return TRUE.
- */
- case 'Z' :
- _XferMessage( kermit, "Closing file" );
- if ( kermit->file != NULL ) {
- fclose( kermit->file );
- kermit->file = NULL;
- }
- if ( !_KermitSendAck( kermit ) )
- return( FALSE );
- else
- return( TRUE );
- case 'T' :
- _XferMessage( kermit, "Timeout waiting for Data packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitSendPacket( kermit, 'N', 0, "" ) )
- return( FALSE );
- break;
- case 'Q' :
- _XferMessage( kermit, "Funny packet" );
- if ( !_KermitUpdateErrorCount( kermit ) )
- return( FALSE );
- if ( !_KermitSendPacket( kermit, 'N', 0, "" ) )
- return( FALSE );
- break;
- case 'A' :
- return( FALSE );
- case 'E' :
- _XferMessage( kermit, "Aborted from remote end" );
- kermit->return_status = XFER_RETURN_REMOTE_ABORT;
- _XferCleanup( kermit );
- return( FALSE );
- case 'D' :
- if ( ( kermit->block_number & 0x3f ) ==
- kermit->current_block_number ) {
- _XferMessage( kermit, "Received block %ld",
- kermit->block_number );
- if ( !_KermitSaveBuffer( kermit ) )
- return( FALSE );
- if ( !_KermitSendAck( kermit ) )
- return( FALSE );
- }
- else {
- _XferMessage( kermit, "Received duplicate data block" );
- if ( !_KermitResendAck( kermit ) )
- return( FALSE );
- }
- break;
- default :
- _XferMessage( kermit, "Protocol error" );
- kermit->return_status = XFER_RETURN_PROTOCOL_ERROR;
- _XferCleanup( kermit );
- return( FALSE );
- }
- }
- }
-
- /*
- * static int _KermitSendInitialAck( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This routine is called to send an ACK to the intial S packet. This
- * is a little different from the
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop. The only reason for
- * a FALSE here is if you get an error transmitting, which means something
- * really bad has happened.
- *
- *
- * AUTHOR
- * Mark Nelson 28-Aug-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitSendInitialAck( XFER *kermit )
- {
- if ( !_KermitSendPacket( kermit, 'Y', 6, _KermitInitParameters() ) )
- return( FALSE );
- kermit->block_number++;
- return( TRUE );
- }
-
- /*
- * static int _KermitSendAck( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This little critter doesn't do much. But, I guess it is long enought
- * to justify a separate subroutine. It sends an ACK and increments the
- * block number.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop.
- *
- *
- * AUTHOR
- * Mark Nelson 29-Sep-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitSendAck( XFER *kermit )
- {
- if ( !_KermitSendPacket( kermit, 'Y', 0, "" ) )
- return( FALSE );
- kermit->block_number++;
- return( TRUE );
- }
-
- /*
- * static int _KermitResendAck( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This routine is a little more complicated than the one where I send
- * just a regular ACK. Since I am resending, it means there is an error
- * condition, and I have to figure out just how to handle it. I check to
- * see if the reason I am sending an ACK is because I got my last block
- * over again. If not, things are really fouled up and I just quit.
- * But if it is, I just go back and send the ACK again.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop.
- *
- *
- * AUTHOR
- * Mark Nelson 29-Sep-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitResendAck( XFER *kermit )
- {
- /*
- * Since I have a funny packet, I flush my input buffer. I don't feel
- * 100% confident about this line, and I may delete it.
- */
- if ( !_XferFlushInput( kermit ) )
- return( FALSE );
- /*
- * We are going to work on the assumption that the remote end just didn't
- * see my last ACK. So I back up my block number and thing about resending.
- * If the block numbers still don't match up, things are really fouled up.
- * I just exit if that is the case. One alternative would be to just
- * send a NAK, but I'm not sure if that would be a good idea.
- */
- kermit->block_number--;
- if ( ( kermit->block_number & 0x3f ) == kermit->current_block_number ) {
- if ( !_KermitSendAck( kermit ) )
- return( FALSE );
- } else {
- kermit->return_status = XFER_RETURN_PROTOCOL_ERROR;
- _XferCleanup( kermit );
- return( FALSE );
- }
- return( TRUE );
- }
-
- /*
- * static int _KermitResendInitialAck( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This is just like the routine about, _KermitResendAck(), with one small
- * difference. Here I have to resend my initial ACK, which has my init
- * data in it. Other than that, everything else is the same. Maybe both
- * of these routines will be combined into one routine some day.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop.
- *
- *
- * AUTHOR
- * Mark Nelson 29-Sep-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static int GF_CONV _KermitResendInitialAck( XFER *kermit )
- {
- if ( !_XferFlushInput( kermit ) )
- return( FALSE );
- /*
- * Once again, I check to see if our block numbers are just one off. If
- * they are, everything is okay, and I resend my ACK. If our block numbers
- * are not just one off, I am so concerned about things that I just quit.
- */
- kermit->block_number--;
- if ( ( kermit->block_number & 0x3f ) == kermit->current_block_number ) {
- if ( !_KermitSendInitialAck( kermit ) )
- return( FALSE );
- } else {
- kermit->return_status = XFER_RETURN_PROTOCOL_ERROR;
- _XferCleanup( kermit );
- return( FALSE );
- }
- return( TRUE );
- }
-
- /*
- * static _KermitSaveBuffer( XFER *kermit )
- *
- * ARGUMENTS
- *
- * XFER *kermit: This is a pointer to an XFER block that
- * keeps track of how things are set up
- * for the file transfer.
- *
- * DESCRIPTION
- *
- * This routine is the guy that has to save off a D packet full of data
- * into the file. Normally that might be pretty easy, but KERMIT
- * complicates things a little bit by performing quoting of control
- * characters.
- *
- * SIDE EFFECTS
- *
- * Packets have been read.
- *
- * RETURNS
- *
- * Like most of these kind of routines, it returns TRUE if everything is
- * okay and it is time to go on ahead. A return value of FALSE means
- * the transfer is aborted and it is time to stop. The only way to get
- * a FALSE out of this guy is with a write error to the file.
- *
- *
- * AUTHOR
- * Mark Nelson 29-Sep-1989 20:57:40.92
- *
- * MODIFICATIONS
- *
- */
-
- static GF_CONV _KermitSaveBuffer( XFER *kermit )
- {
- unsigned char c;
- int i;
-
- i=0;
- while ( i < kermit->current_block_size ) {
- c = kermit->buffer[ 4 + i++ ];
- if ( c == (unsigned char)kermit->x.kermit.quote_char ) {
- c = kermit->buffer[ 4 + i++ ];
- if ( ( c & 0x7f ) != kermit->x.kermit.quote_char )
- c = ctl( c );
- }
- if ( (unsigned char)putc( c, kermit->file ) != c ) {
- kermit->return_status = XFER_RETURN_FILE_ERROR;
- _XferCleanup( kermit );
- return( FALSE );
- }
- kermit->byte_count++;
- }
- return( TRUE );
- }
-