901 lines
18 KiB
C
901 lines
18 KiB
C
|
#ifndef lint
|
|||
|
static const char rcsid[] = "$Id: zmodem.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Copyright (c) 1995 by Edward A. Falk
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
/**********
|
|||
|
*
|
|||
|
*
|
|||
|
* @@@@@ @ @ @@@ @@@@ @@@@@ @ @
|
|||
|
* @ @@ @@ @ @ @ @ @ @@ @@
|
|||
|
* @ @ @ @ @ @ @ @ @@@ @ @ @
|
|||
|
* @ @ @ @ @ @ @ @ @ @ @ @
|
|||
|
* @@@@@ @ @ @ @@@ @@@@ @@@@@ @ @ @
|
|||
|
*
|
|||
|
* ZMODEM - main logic parser for zmodem library
|
|||
|
*
|
|||
|
*
|
|||
|
* Routines provided here:
|
|||
|
*
|
|||
|
*
|
|||
|
* name (args)
|
|||
|
* Brief description.
|
|||
|
*
|
|||
|
* int ZmodemRcv(u_char *buffer, int len, ZModem *info)
|
|||
|
* Call whenever characters are received. If this function
|
|||
|
* returns ZmDone, previous function has completed successfully,
|
|||
|
* either call ZmodemTFile() to start next file, or call
|
|||
|
* ZmodemTFinish() to terminate the session.
|
|||
|
*
|
|||
|
*
|
|||
|
* int
|
|||
|
* ZmodemTimeout(ZModem *info)
|
|||
|
* Call whenever the timeout period expires and no
|
|||
|
* characters have been received.
|
|||
|
*
|
|||
|
* int
|
|||
|
* ZmodemAttention(ZModem *info)
|
|||
|
* Call whenever the attention sequence has been received
|
|||
|
* from the remote end. It is safe to call this function
|
|||
|
* from an interrupt handler.
|
|||
|
*
|
|||
|
* int
|
|||
|
* ZmodemAbort(ZModem *info)
|
|||
|
* Call to abort transfer. Physical connection remains
|
|||
|
* open until you close it.
|
|||
|
*
|
|||
|
*
|
|||
|
*
|
|||
|
*
|
|||
|
* Edward A. Falk
|
|||
|
*
|
|||
|
* January, 1995
|
|||
|
*
|
|||
|
*
|
|||
|
*
|
|||
|
**********/
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
/****
|
|||
|
*
|
|||
|
* Constants, typedefs, externals, globals, statics, macros, block data
|
|||
|
*
|
|||
|
****/
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* TODO: sample input before initial send */
|
|||
|
/* TODO: more intelligent timeout dispatch */
|
|||
|
/* TODO: read all pending input before sending next data packet out */
|
|||
|
/* TODO: if received ZDATA while waiting for ZFILE/ZFIN, it's probably
|
|||
|
leftovers */
|
|||
|
/* TODO: enable flow control for zmodem, disable for X/YModem */
|
|||
|
|
|||
|
#include <ctype.h>
|
|||
|
#include <errno.h>
|
|||
|
#include <string.h>
|
|||
|
|
|||
|
#include "zmodem.h"
|
|||
|
#include "crctab.h"
|
|||
|
|
|||
|
static u_char zeros[4] = {0,0,0,0} ;
|
|||
|
|
|||
|
|
|||
|
extern int YrcvChar( char c, register ZModem *info ) ;
|
|||
|
extern int YrcvTimeout( register ZModem *info ) ;
|
|||
|
extern void ZIdleStr(u_char *buffer, int len, ZModem *info) ;
|
|||
|
|
|||
|
|
|||
|
/* LEXICAL BOX: handle characters received from remote end.
|
|||
|
* These may be header, data or noise.
|
|||
|
*
|
|||
|
* This section is a finite state machine for parsing headers
|
|||
|
* and reading input data. The info->chrCount field is effectively
|
|||
|
* the state variable.
|
|||
|
*/
|
|||
|
|
|||
|
static int FinishChar( char c, register ZModem *info ) ;
|
|||
|
static int DataChar( u_char c, register ZModem *info ) ;
|
|||
|
static int HdrChar( u_char c, register ZModem *info ) ;
|
|||
|
static int IdleChar(u_char c, register ZModem *info) ;
|
|||
|
extern int YsendChar() ;
|
|||
|
|
|||
|
static int ZProtocol(), ZDataReceived() ;
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
ZmodemRcv( register u_char *str, int len, register ZModem *info )
|
|||
|
{
|
|||
|
register u_char c ;
|
|||
|
int err ;
|
|||
|
|
|||
|
info->rcvlen = len ;
|
|||
|
|
|||
|
while( --info->rcvlen >= 0 )
|
|||
|
{
|
|||
|
c = *str++ ;
|
|||
|
|
|||
|
if( c == CAN ) {
|
|||
|
if( ++info->canCount >= 5 ) {
|
|||
|
ZStatus(RmtCancel, 0, NULL) ;
|
|||
|
return ZmErrCancel ;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
info->canCount = 0 ;
|
|||
|
|
|||
|
if( info->InputState == Ysend ) {
|
|||
|
if( (err = YsendChar(c, info)) )
|
|||
|
return err ;
|
|||
|
}
|
|||
|
else if( info->InputState == Yrcv ) {
|
|||
|
if( (err = YrcvChar(c, info)) )
|
|||
|
return err ;
|
|||
|
}
|
|||
|
|
|||
|
else if( c != XON && c != XOFF )
|
|||
|
{
|
|||
|
/* now look at what we have */
|
|||
|
|
|||
|
switch( info->InputState )
|
|||
|
{
|
|||
|
case Idle:
|
|||
|
if( (err = IdleChar(c, info)) )
|
|||
|
return err ;
|
|||
|
break ;
|
|||
|
|
|||
|
case Inhdr:
|
|||
|
if( (err = HdrChar(c, info)) )
|
|||
|
return err ;
|
|||
|
break ;
|
|||
|
|
|||
|
case Indata:
|
|||
|
if( (err = DataChar(c, info)) )
|
|||
|
return err ;
|
|||
|
break ;
|
|||
|
|
|||
|
case Finish:
|
|||
|
if( (err = FinishChar(c, info)) )
|
|||
|
return err ;
|
|||
|
break ;
|
|||
|
|
|||
|
default:
|
|||
|
break ;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* handle character input while idling
|
|||
|
* looking for ZPAD-ZDLE sequence which introduces a header
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
IdleChar(u_char c, register ZModem *info)
|
|||
|
{
|
|||
|
if( info->chrCount == 0 )
|
|||
|
{
|
|||
|
if( c == ZPAD )
|
|||
|
++info->chrCount ;
|
|||
|
else if( info->state == Sending && ++info->noiseCount > MaxNoise )
|
|||
|
info->waitflag = 1 ;
|
|||
|
else if( info->state == TStart && (c == 'C' || c == 'G' || c == NAK) )
|
|||
|
{
|
|||
|
/* switch to ymodem */
|
|||
|
info->state = YTStart ;
|
|||
|
info->InputState = Ysend ;
|
|||
|
info->Protocol = YMODEM ;
|
|||
|
return YsendChar(c, info) ;
|
|||
|
}
|
|||
|
else
|
|||
|
ZIdleStr(&c, 1, info) ;
|
|||
|
}
|
|||
|
|
|||
|
else
|
|||
|
{
|
|||
|
switch( c ) {
|
|||
|
case ZPAD:
|
|||
|
++info->chrCount ;
|
|||
|
break ;
|
|||
|
case ZDLE:
|
|||
|
info->InputState = Inhdr ;
|
|||
|
info->chrCount=0 ;
|
|||
|
break ;
|
|||
|
default:
|
|||
|
while( --info->chrCount >= 0 )
|
|||
|
ZIdleStr((u_char *)"*", 1, info) ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
break ;
|
|||
|
}
|
|||
|
}
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static u_int
|
|||
|
rcvHex( u_int i, char c )
|
|||
|
{
|
|||
|
if( c <= '9' )
|
|||
|
c -= '0' ;
|
|||
|
else if( c <= 'F' )
|
|||
|
c -= 'A'-10 ;
|
|||
|
else
|
|||
|
c -= 'a'-10 ;
|
|||
|
return (i<<4)+c ;
|
|||
|
}
|
|||
|
|
|||
|
/* handle character input in a header */
|
|||
|
|
|||
|
static int
|
|||
|
HdrChar( u_char c, register ZModem *info )
|
|||
|
{
|
|||
|
int i ;
|
|||
|
int crc=0 ;
|
|||
|
|
|||
|
if( c == ZDLE ) {
|
|||
|
info->escape = 1 ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
if( info->escape ) {
|
|||
|
info->escape = 0 ;
|
|||
|
switch( c ) {
|
|||
|
case ZRUB0: c = 0177 ; break ;
|
|||
|
case ZRUB1: c = 0377 ; break ;
|
|||
|
default: c ^= 0100 ; break ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( info->chrCount == 0 ) { /* waiting for format */
|
|||
|
switch( c ) {
|
|||
|
case ZHEX:
|
|||
|
case ZBIN:
|
|||
|
case ZBIN32:
|
|||
|
info->DataType = c ;
|
|||
|
info->chrCount = 1 ;
|
|||
|
info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
|
|||
|
memset(info->hdrData,0,sizeof(info->hdrData)) ;
|
|||
|
break ;
|
|||
|
default:
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
}
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
switch( info->DataType ) {
|
|||
|
/* hex header is 14 hex digits, cr, lf. Optional xon is ignored */
|
|||
|
case ZHEX:
|
|||
|
if( info->chrCount <= 14 && !isxdigit(c) ) {
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
}
|
|||
|
|
|||
|
if( info->chrCount <= 14 ) {
|
|||
|
i = (info->chrCount-1)/2 ;
|
|||
|
info->hdrData[i] = rcvHex(info->hdrData[i], c) ;
|
|||
|
}
|
|||
|
|
|||
|
if( info->chrCount == 16 ) {
|
|||
|
crc = 0 ;
|
|||
|
for(i=0; i<7; ++i)
|
|||
|
crc = updcrc(info->hdrData[i], crc) ;
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
if( (crc&0xffff) != 0 )
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
else
|
|||
|
return ZProtocol(info) ;
|
|||
|
}
|
|||
|
else
|
|||
|
++info->chrCount ;
|
|||
|
break ;
|
|||
|
|
|||
|
|
|||
|
case ZBIN:
|
|||
|
/* binary header is type, 4 bytes data, 2 bytes CRC */
|
|||
|
info->hdrData[info->chrCount-1] = c ;
|
|||
|
info->crc = updcrc(c, info->crc) ;
|
|||
|
if( ++info->chrCount > 7 ) {
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
if( (crc&0xffff) != 0 )
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
else
|
|||
|
return ZProtocol(info) ;
|
|||
|
}
|
|||
|
break ;
|
|||
|
|
|||
|
|
|||
|
case ZBIN32:
|
|||
|
/* binary32 header is type, 4 bytes data, 4 bytes CRC */
|
|||
|
info->hdrData[info->chrCount-1] = c ;
|
|||
|
info->crc = UPDC32(c, info->crc) ;
|
|||
|
if( ++info->chrCount > 9 ) {
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
if( info->crc != 0xdebb20e3 ) /* see note below */
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
else
|
|||
|
return ZProtocol(info) ;
|
|||
|
}
|
|||
|
break ;
|
|||
|
}
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
/* handle character input in a data buffer */
|
|||
|
|
|||
|
static int
|
|||
|
DataChar( u_char c, register ZModem *info )
|
|||
|
{
|
|||
|
if( c == ZDLE ) {
|
|||
|
info->escape = 1 ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
if( info->escape ) {
|
|||
|
info->escape = 0 ;
|
|||
|
switch( c ) {
|
|||
|
case ZCRCE:
|
|||
|
case ZCRCG:
|
|||
|
case ZCRCQ:
|
|||
|
case ZCRCW:
|
|||
|
info->PacketType = c ;
|
|||
|
info->crcCount = (info->DataType == ZBIN32) ? 4 : 2 ;
|
|||
|
if( info->DataType == ZBIN )
|
|||
|
info->crc = updcrc(c, info->crc) ;
|
|||
|
else
|
|||
|
info->crc = UPDC32(c, info->crc) ;
|
|||
|
return 0 ;
|
|||
|
case ZRUB0: c = 0177 ; break ;
|
|||
|
case ZRUB1: c = 0377 ; break ;
|
|||
|
default: c ^= 0100 ; break ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
switch( info->DataType ) {
|
|||
|
/* TODO: are hex data packets ever used? */
|
|||
|
|
|||
|
case ZBIN:
|
|||
|
info->crc = updcrc(c, info->crc) ;
|
|||
|
if( info->crcCount == 0 )
|
|||
|
info->buffer[info->chrCount++] = c ;
|
|||
|
else if( --info->crcCount == 0 ) {
|
|||
|
return ZDataReceived(info, (info->crc&0xffff) == 0) ;
|
|||
|
}
|
|||
|
break ;
|
|||
|
|
|||
|
|
|||
|
case ZBIN32:
|
|||
|
info->crc = UPDC32(c, info->crc) ;
|
|||
|
if( info->crcCount == 0 )
|
|||
|
info->buffer[info->chrCount++] = c ;
|
|||
|
else if( --info->crcCount == 0 ) {
|
|||
|
return ZDataReceived(info, info->crc == 0xdebb20e3) ;
|
|||
|
}
|
|||
|
break ;
|
|||
|
}
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* wait for "OO" */
|
|||
|
|
|||
|
static int
|
|||
|
FinishChar( char c, register ZModem *info )
|
|||
|
{
|
|||
|
if( c == 'O' ) {
|
|||
|
if( ++info->chrCount >= 2 )
|
|||
|
return ZmDone ;
|
|||
|
}
|
|||
|
else
|
|||
|
info->chrCount = 0 ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int ZPF() ;
|
|||
|
int Ignore() ;
|
|||
|
int AnswerChallenge() ;
|
|||
|
int GotAbort() ;
|
|||
|
int GotCancel() ;
|
|||
|
int GotCommand() ;
|
|||
|
int GotStderr() ;
|
|||
|
int RetDone() ;
|
|||
|
static int GotCommandData() ;
|
|||
|
static int GotStderrData() ;
|
|||
|
|
|||
|
/* PROTOCOL LOGIC: This section of code handles the actual
|
|||
|
* protocol. This is also driven by a finite state machine
|
|||
|
*
|
|||
|
* State tables are sorted by approximate frequency order to
|
|||
|
* reduce search time.
|
|||
|
*/
|
|||
|
|
|||
|
/* Extra ZRINIT headers are the receiver trying to resync. */
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* If compiling for Send Only or Receive Only, convert table
|
|||
|
* entries to no-ops so we don't have to link zmodem[rt].o
|
|||
|
*/
|
|||
|
|
|||
|
#if SendOnly
|
|||
|
#define RStartOps DoneOps
|
|||
|
#define RSinitWaitOps DoneOps
|
|||
|
#define RFileNameOps DoneOps
|
|||
|
#define RCrcOps DoneOps
|
|||
|
#define RFileOps DoneOps
|
|||
|
#define RDataOps DoneOps
|
|||
|
#define RFinishOps DoneOps
|
|||
|
#define GotFileName Ignore
|
|||
|
#define ResendCrcReq Ignore
|
|||
|
#define GotSinitData Ignore
|
|||
|
#define ResendRpos Ignore
|
|||
|
#define GotFileData Ignore
|
|||
|
#define SendRinit Ignore
|
|||
|
#else
|
|||
|
extern StateTable RStartOps[] ;
|
|||
|
extern StateTable RSinitWaitOps[] ;
|
|||
|
extern StateTable RFileNameOps[] ;
|
|||
|
extern StateTable RCrcOps[] ;
|
|||
|
extern StateTable RFileOps[] ;
|
|||
|
extern StateTable RDataOps[] ;
|
|||
|
extern StateTable RFinishOps[] ;
|
|||
|
extern int GotFileName() ;
|
|||
|
extern int ResendCrcReq() ;
|
|||
|
extern int GotSinitData() ;
|
|||
|
extern int ResendRpos() ;
|
|||
|
extern int GotFileData() ;
|
|||
|
extern int SendRinit() ;
|
|||
|
#endif
|
|||
|
|
|||
|
#if RcvOnly
|
|||
|
#define TStartOps DoneOps
|
|||
|
#define TInitOps DoneOps
|
|||
|
#define FileWaitOps DoneOps
|
|||
|
#define CrcWaitOps DoneOps
|
|||
|
#define SendingOps DoneOps
|
|||
|
#define SendDoneOps DoneOps
|
|||
|
#define SendWaitOps DoneOps
|
|||
|
#define SendEofOps DoneOps
|
|||
|
#define TFinishOps DoneOps
|
|||
|
#define SendMoreFileData Ignore
|
|||
|
#else
|
|||
|
extern StateTable TStartOps[] ;
|
|||
|
extern StateTable TInitOps[] ;
|
|||
|
extern StateTable FileWaitOps[] ;
|
|||
|
extern StateTable CrcWaitOps[] ;
|
|||
|
extern StateTable SendingOps[] ;
|
|||
|
extern StateTable SendDoneOps[] ;
|
|||
|
extern StateTable SendWaitOps[] ;
|
|||
|
extern StateTable SendEofOps[] ;
|
|||
|
extern StateTable TFinishOps[] ;
|
|||
|
extern int SendMoreFileData() ;
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static StateTable CommandDataOps[] = {
|
|||
|
#ifdef COMMENT
|
|||
|
{ZRQINIT,f,1,1},
|
|||
|
{ZRINIT,f,1,1},
|
|||
|
{ZSINIT,f,1,1},
|
|||
|
{ZACK,f,1,1},
|
|||
|
{ZFILE,f,1,1},
|
|||
|
{ZSKIP,f,1,1},
|
|||
|
{ZNAK,f,1,1},
|
|||
|
{ZABORT,f,1,1,TFinish},
|
|||
|
{ZFIN,f,1,1},
|
|||
|
{ZRPOS,f,1,1},
|
|||
|
{ZDATA,f,1,1},
|
|||
|
{ZEOF,f,1,1},
|
|||
|
{ZFERR,f,1,1,TFinish},
|
|||
|
{ZCRC,f,1,1},
|
|||
|
{ZCHALLENGE,f,1,1},
|
|||
|
{ZCOMPL,f,1,1},
|
|||
|
{ZCAN,f,1,1},
|
|||
|
{ZFREECNT,f,1,1},
|
|||
|
{ZCOMMAND,f,1,1},
|
|||
|
{ZSTDERR,f,1,1},
|
|||
|
#endif /* COMMENT */
|
|||
|
{99,ZPF,0,0,CommandData},
|
|||
|
} ;
|
|||
|
|
|||
|
static StateTable CommandWaitOps[] = {
|
|||
|
#ifdef COMMENT
|
|||
|
{ZRQINIT,f,1,1},
|
|||
|
{ZRINIT,f,1,1},
|
|||
|
{ZSINIT,f,1,1},
|
|||
|
{ZACK,f,1,1},
|
|||
|
{ZFILE,f,1,1},
|
|||
|
{ZSKIP,f,1,1},
|
|||
|
{ZNAK,f,1,1},
|
|||
|
{ZABORT,f,1,1,TFinish},
|
|||
|
{ZFIN,f,1,1},
|
|||
|
{ZRPOS,f,1,1},
|
|||
|
{ZDATA,f,1,1},
|
|||
|
{ZEOF,f,1,1},
|
|||
|
{ZFERR,f,1,1,TFinish},
|
|||
|
{ZCRC,f,1,1},
|
|||
|
{ZCHALLENGE,f,1,1},
|
|||
|
{ZCOMPL,f,1,1},
|
|||
|
{ZCAN,f,1,1},
|
|||
|
{ZFREECNT,f,1,1},
|
|||
|
{ZCOMMAND,f,1,1},
|
|||
|
{ZSTDERR,f,1,1},
|
|||
|
#endif /* COMMENT */
|
|||
|
{99,ZPF,0,0,CommandWait},
|
|||
|
} ;
|
|||
|
|
|||
|
static StateTable StderrDataOps[] = {
|
|||
|
#ifdef COMMENT
|
|||
|
{ZRQINIT,f,1,1},
|
|||
|
{ZRINIT,f,1,1},
|
|||
|
{ZSINIT,f,1,1},
|
|||
|
{ZACK,f,1,1},
|
|||
|
{ZFILE,f,1,1},
|
|||
|
{ZSKIP,f,1,1},
|
|||
|
{ZNAK,f,1,1},
|
|||
|
{ZABORT,f,1,1,TFinish},
|
|||
|
{ZFIN,f,1,1},
|
|||
|
{ZRPOS,f,1,1},
|
|||
|
{ZDATA,f,1,1},
|
|||
|
{ZEOF,f,1,1},
|
|||
|
{ZFERR,f,1,1,TFinish},
|
|||
|
{ZCRC,f,1,1},
|
|||
|
{ZCHALLENGE,f,1,1},
|
|||
|
{ZCOMPL,f,1,1},
|
|||
|
{ZCAN,f,1,1},
|
|||
|
{ZFREECNT,f,1,1},
|
|||
|
{ZCOMMAND,f,1,1},
|
|||
|
{ZSTDERR,f,1,1},
|
|||
|
#endif /* COMMENT */
|
|||
|
{99,ZPF,0,0,StderrData},
|
|||
|
} ;
|
|||
|
|
|||
|
static StateTable DoneOps[] = {
|
|||
|
{99,ZPF,0,0,Done},
|
|||
|
} ;
|
|||
|
|
|||
|
|
|||
|
static StateTable *tables[] = {
|
|||
|
RStartOps,
|
|||
|
RSinitWaitOps,
|
|||
|
RFileNameOps,
|
|||
|
RCrcOps,
|
|||
|
RFileOps,
|
|||
|
RDataOps,
|
|||
|
RDataOps, /* RDataErr is the same as RData */
|
|||
|
RFinishOps,
|
|||
|
|
|||
|
TStartOps,
|
|||
|
TInitOps,
|
|||
|
FileWaitOps,
|
|||
|
CrcWaitOps,
|
|||
|
SendingOps,
|
|||
|
SendWaitOps,
|
|||
|
SendDoneOps,
|
|||
|
SendEofOps,
|
|||
|
TFinishOps,
|
|||
|
|
|||
|
CommandDataOps,
|
|||
|
CommandWaitOps,
|
|||
|
StderrDataOps,
|
|||
|
DoneOps,
|
|||
|
} ;
|
|||
|
|
|||
|
|
|||
|
char *hdrnames[] = {
|
|||
|
"ZRQINIT",
|
|||
|
"ZRINIT",
|
|||
|
"ZSINIT",
|
|||
|
"ZACK",
|
|||
|
"ZFILE",
|
|||
|
"ZSKIP",
|
|||
|
"ZNAK",
|
|||
|
"ZABORT",
|
|||
|
"ZFIN",
|
|||
|
"ZRPOS",
|
|||
|
"ZDATA",
|
|||
|
"ZEOF",
|
|||
|
"ZFERR",
|
|||
|
"ZCRC",
|
|||
|
"ZCHALLENGE",
|
|||
|
"ZCOMPL",
|
|||
|
"ZCAN",
|
|||
|
"ZFREECNT",
|
|||
|
"ZCOMMAND",
|
|||
|
"ZSTDERR",
|
|||
|
} ;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* This function is called (indirectly) by the ZmodemRcv()
|
|||
|
* function when a full header has been received.
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
ZProtocol( register ZModem *info )
|
|||
|
{
|
|||
|
register StateTable *table ;
|
|||
|
|
|||
|
zmodemlog("received %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
|
|||
|
hdrnames[info->hdrData[0]], info->hdrData[1],
|
|||
|
info->hdrData[2], info->hdrData[3], info->hdrData[4],
|
|||
|
ZDec4(info->hdrData+1)) ;
|
|||
|
|
|||
|
/* Flags are sent in F3 F2 F1 F0 order. Data is sent in P0 P1 P2 P3 */
|
|||
|
|
|||
|
info->timeoutCount = 0 ;
|
|||
|
info->noiseCount = 0 ;
|
|||
|
|
|||
|
table = tables[(int)info->state] ;
|
|||
|
while( table->type != 99 && table->type != info->hdrData[0] )
|
|||
|
++table ;
|
|||
|
|
|||
|
zmodemlog(" state %s => %s, iflush=%d, oflush=%d, call %x\n",
|
|||
|
sname(info), sname2(table->newstate), table->IFlush,
|
|||
|
table->OFlush, table->func) ;
|
|||
|
|
|||
|
info->state = table->newstate ;
|
|||
|
if( table->IFlush ) {info->rcvlen = 0 ; ZIFlush(info) ;}
|
|||
|
if( table->OFlush ) ZOFlush(info) ;
|
|||
|
return table->func(info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
ZDataReceived( register ZModem *info, int crcGood )
|
|||
|
{
|
|||
|
switch( info->state ) {
|
|||
|
case RSinitWait: return GotSinitData(info, crcGood) ;
|
|||
|
case RFileName: return GotFileName(info, crcGood) ;
|
|||
|
case RData: return GotFileData(info, crcGood) ;
|
|||
|
case CommandData: return GotCommandData(info, crcGood) ;
|
|||
|
case StderrData: return GotStderrData(info, crcGood) ;
|
|||
|
default: return ZPF(info) ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
ZmodemTimeout( register ZModem *info )
|
|||
|
{
|
|||
|
/* timed out while waiting for input */
|
|||
|
|
|||
|
++info->timeoutCount ;
|
|||
|
|
|||
|
zmodemlog("timeout %d [%s]\n", info->timeoutCount, sname(info) ) ;
|
|||
|
|
|||
|
switch( info->state ) {
|
|||
|
/* receive */
|
|||
|
case RStart: /* waiting for INIT frame from other end */
|
|||
|
if( info->timeoutCount > 4 )
|
|||
|
return YmodemRInit(info) ;
|
|||
|
|
|||
|
case RSinitWait:
|
|||
|
case RFileName:
|
|||
|
if( info->timeout > 0 )
|
|||
|
ZStatus(SndTimeout, info->timeoutCount, NULL) ;
|
|||
|
if( info->timeoutCount > 4 )
|
|||
|
return ZmErrRcvTo ;
|
|||
|
info->state = RStart ;
|
|||
|
return SendRinit(info) ;
|
|||
|
|
|||
|
case RCrc:
|
|||
|
case RFile:
|
|||
|
case RData:
|
|||
|
ZStatus(SndTimeout, info->timeoutCount, NULL) ;
|
|||
|
if( info->timeoutCount > 2 ) {
|
|||
|
info->timeoutCount = 0 ;
|
|||
|
info->state = RStart ;
|
|||
|
return SendRinit(info) ;
|
|||
|
}
|
|||
|
return info->state == RCrc ? ResendCrcReq(info) : ResendRpos(info) ;
|
|||
|
|
|||
|
case RFinish:
|
|||
|
ZStatus(SndTimeout, info->timeoutCount, NULL) ;
|
|||
|
return ZmDone ;
|
|||
|
|
|||
|
case YRStart:
|
|||
|
case YRDataWait:
|
|||
|
case YRData:
|
|||
|
case YREOF:
|
|||
|
return YrcvTimeout(info) ;
|
|||
|
|
|||
|
/* transmit */
|
|||
|
case TStart: /* waiting for INIT frame from other end */
|
|||
|
case TInit: /* sent INIT, waiting for ZACK */
|
|||
|
case FileWait: /* sent file header, waiting for ZRPOS */
|
|||
|
case CrcWait: /* sent file crc, waiting for ZRPOS */
|
|||
|
case SendWait: /* waiting for ZACK */
|
|||
|
case SendEof: /* sent EOF, waiting for ZACK */
|
|||
|
case TFinish: /* sent ZFIN, waiting for ZFIN */
|
|||
|
case YTStart:
|
|||
|
case YTFile:
|
|||
|
case YTDataWait:
|
|||
|
case YTData:
|
|||
|
case YTEOF:
|
|||
|
case YTFin:
|
|||
|
ZStatus(RcvTimeout,0,NULL) ;
|
|||
|
return ZmErrRcvTo ;
|
|||
|
|
|||
|
case Sending: /* sending data subpackets, ready for int */
|
|||
|
return SendMoreFileData(info) ;
|
|||
|
|
|||
|
/* general */
|
|||
|
case CommandData: /* waiting for command data */
|
|||
|
case StderrData: /* waiting for stderr data */
|
|||
|
return ZmErrSndTo ;
|
|||
|
case CommandWait: /* waiting for command to execute */
|
|||
|
return ZmErrCmdTo ;
|
|||
|
case Done:
|
|||
|
return ZmDone ;
|
|||
|
default:
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
ZmodemAttention( register ZModem *info )
|
|||
|
{
|
|||
|
/* attention received from remote end */
|
|||
|
if( info->state == Sending ) {
|
|||
|
ZOFlush(info) ;
|
|||
|
info->interrupt = 1 ;
|
|||
|
}
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
ZmodemAbort( register ZModem *info )
|
|||
|
{
|
|||
|
static u_char canistr[] = {
|
|||
|
CAN,CAN,CAN,CAN,CAN,CAN,CAN,CAN,8,8,8,8,8,8,8,8,8,8
|
|||
|
} ;
|
|||
|
info->state = Done ;
|
|||
|
ZIFlush(info) ;
|
|||
|
ZOFlush(info) ;
|
|||
|
return ZXmitStr(canistr, sizeof(canistr), info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* used to completely ignore headers */
|
|||
|
|
|||
|
int
|
|||
|
Ignore( ZModem *info )
|
|||
|
{
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* ignore header contents, return ZmDone */
|
|||
|
|
|||
|
int
|
|||
|
RetDone( ZModem *info )
|
|||
|
{
|
|||
|
return ZmDone ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* ignore header contents, return ZmErrCancel */
|
|||
|
|
|||
|
int
|
|||
|
GotCancel( ZModem *info )
|
|||
|
{
|
|||
|
return ZmErrCancel ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* utility: set up to receive a data packet */
|
|||
|
|
|||
|
int
|
|||
|
dataSetup( register ZModem *info )
|
|||
|
{
|
|||
|
info->InputState = Indata ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
info->crcCount = 0 ;
|
|||
|
info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* called when a remote command received. For now, we
|
|||
|
* refuse to execute commands. Send EPERM and ignore.
|
|||
|
*/
|
|||
|
int
|
|||
|
GotCommand( ZModem *info )
|
|||
|
{
|
|||
|
u_char rbuf[4] ;
|
|||
|
/* TODO: add command capability */
|
|||
|
|
|||
|
rbuf[0] = EPERM ;
|
|||
|
rbuf[1] = rbuf[2] = rbuf[3] = 0 ;
|
|||
|
return ZXmitHdrHex(ZCOMPL, rbuf, info) ;
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
GotCommandData( register ZModem *info )
|
|||
|
{
|
|||
|
/* TODO */
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* called when the remote system wants to put something to
|
|||
|
* stderr
|
|||
|
*/
|
|||
|
int
|
|||
|
GotStderr( register ZModem *info )
|
|||
|
{
|
|||
|
info->InputState = Indata ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
static int
|
|||
|
GotStderrData( register ZModem *info )
|
|||
|
{
|
|||
|
info->buffer[info->chrCount] = '\0' ;
|
|||
|
ZStatus(RemoteMessage, info->chrCount, (char *)info->buffer) ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Protocol failure: An unexpected packet arrived. This could
|
|||
|
* be from many sources, such as old pipelined info finally arriving
|
|||
|
* or a serial line with echo enabled. Report it and ignore it.
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
ZPF( ZModem *info )
|
|||
|
{
|
|||
|
info->waitflag = 1 ; /* pause any in-progress transmission */
|
|||
|
ZStatus(ProtocolErr, info->hdrData[0], NULL) ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
AnswerChallenge( register ZModem *info )
|
|||
|
{
|
|||
|
return ZXmitHdrHex(ZACK, info->hdrData+1, info) ;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
GotAbort( register ZModem *info )
|
|||
|
{
|
|||
|
ZStatus(RmtCancel, 0, NULL) ;
|
|||
|
return ZXmitHdrHex(ZFIN, zeros, info) ;
|
|||
|
}
|
|||
|
|