751 lines
17 KiB
C
751 lines
17 KiB
C
|
#ifndef lint
|
|||
|
static const char rcsid[] = "$Id: zmodemr.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Copyright (c) 1995 by Edward A. Falk
|
|||
|
*/
|
|||
|
|
|||
|
|
|||
|
/**********
|
|||
|
*
|
|||
|
*
|
|||
|
* @@@@@ @ @ @@@ @@@@ @@@@@ @ @ @@@@
|
|||
|
* @ @@ @@ @ @ @ @ @ @@ @@ @ @
|
|||
|
* @ @ @ @ @ @ @ @ @@@ @ @ @ @@@@
|
|||
|
* @ @ @ @ @ @ @ @ @ @ @ @ @ @
|
|||
|
* @@@@@ @ @ @ @@@ @@@@ @@@@@ @ @ @ @ @
|
|||
|
*
|
|||
|
* ZMODEMR - receive side of zmodem protocol
|
|||
|
*
|
|||
|
* receive side of zmodem protocol
|
|||
|
*
|
|||
|
* This code is designed to be called from inside a larger
|
|||
|
* program, so it is implemented as a state machine where
|
|||
|
* practical.
|
|||
|
*
|
|||
|
* functions:
|
|||
|
*
|
|||
|
* ZmodemRInit(ZModem *info)
|
|||
|
* Initiate a connection
|
|||
|
*
|
|||
|
* ZmodemRAbort(ZModem *info)
|
|||
|
* abort transfer
|
|||
|
*
|
|||
|
* all functions return 0 on success, 1 on failure
|
|||
|
*
|
|||
|
*
|
|||
|
* Edward A. Falk
|
|||
|
*
|
|||
|
* January, 1995
|
|||
|
*
|
|||
|
*
|
|||
|
*
|
|||
|
**********/
|
|||
|
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <string.h>
|
|||
|
#include <ctype.h>
|
|||
|
#include <errno.h>
|
|||
|
|
|||
|
#include "zmodem.h"
|
|||
|
#include "crctab.h"
|
|||
|
|
|||
|
extern int errno ;
|
|||
|
|
|||
|
extern int ZWriteFile(u_char *buffer, int len, FILE *, ZModem *);
|
|||
|
extern int ZCloseFile(ZModem *info) ;
|
|||
|
extern void ZFlowControl(int onoff, ZModem *info) ;
|
|||
|
|
|||
|
static u_char zeros[4] = {0,0,0,0} ;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
ZmodemRInit(ZModem *info)
|
|||
|
{
|
|||
|
info->packetCount = 0 ;
|
|||
|
info->offset = 0 ;
|
|||
|
info->errCount = 0 ;
|
|||
|
info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ;
|
|||
|
info->InputState = Idle ;
|
|||
|
info->canCount = info->chrCount = 0 ;
|
|||
|
info->filename = NULL ;
|
|||
|
info->interrupt = 0 ;
|
|||
|
info->waitflag = 0 ;
|
|||
|
info->attn = NULL ;
|
|||
|
info->file = NULL ;
|
|||
|
|
|||
|
info->buffer = (u_char *)malloc(8192) ;
|
|||
|
|
|||
|
info->state = RStart ;
|
|||
|
info->timeoutCount = 0 ;
|
|||
|
|
|||
|
ZIFlush(info) ;
|
|||
|
|
|||
|
/* Don't send ZRINIT right away, there might be a ZRQINIT in
|
|||
|
* the input buffer. Instead, set timeout to zero and return.
|
|||
|
* This will allow ZmodemRcv() to check the input stream first.
|
|||
|
* If nothing found, a ZRINIT will be sent immediately.
|
|||
|
*/
|
|||
|
info->timeout = 0 ;
|
|||
|
|
|||
|
zmodemlog("ZmodemRInit[%s]: flush input, new state = RStart\n",
|
|||
|
sname(info)) ;
|
|||
|
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
int
|
|||
|
YmodemRInit(ZModem *info)
|
|||
|
{
|
|||
|
info->errCount = 0 ;
|
|||
|
info->InputState = Yrcv ;
|
|||
|
info->canCount = info->chrCount = 0 ;
|
|||
|
info->noiseCount = 0 ;
|
|||
|
info->filename = NULL ;
|
|||
|
info->file = NULL ;
|
|||
|
|
|||
|
if( info->buffer == NULL )
|
|||
|
info->buffer = (u_char *)malloc(1024) ;
|
|||
|
|
|||
|
info->state = YRStart ;
|
|||
|
info->packetCount = -1 ;
|
|||
|
info->timeoutCount = 0 ;
|
|||
|
info->timeout = 10 ;
|
|||
|
info->offset = 0 ;
|
|||
|
|
|||
|
ZIFlush(info) ;
|
|||
|
|
|||
|
return ZXmitStr((u_char *)"C", 1, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
extern int ZPF() ;
|
|||
|
extern int Ignore() ;
|
|||
|
extern int GotCommand() ;
|
|||
|
extern int GotStderr() ;
|
|||
|
|
|||
|
int SendRinit() ;
|
|||
|
static int GotSinit() ;
|
|||
|
static int GotFile() ;
|
|||
|
static int GotFin() ;
|
|||
|
static int GotData() ;
|
|||
|
static int GotEof() ;
|
|||
|
static int GotFreecnt() ;
|
|||
|
static int GotFileCrc() ;
|
|||
|
int ResendCrcReq() ;
|
|||
|
int ResendRpos() ;
|
|||
|
|
|||
|
/* sent ZRINIT, waiting for ZSINIT or ZFILE */
|
|||
|
StateTable RStartOps[] = {
|
|||
|
{ZSINIT,GotSinit,0,1,RSinitWait}, /* SINIT, wait for attn str */
|
|||
|
{ZFILE,GotFile,0,0,RFileName}, /* FILE, wait for filename */
|
|||
|
{ZRQINIT,SendRinit,0,1,RStart}, /* sender confused, resend */
|
|||
|
{ZFIN,GotFin,1,0,RFinish}, /* sender shutting down */
|
|||
|
{ZNAK,SendRinit,1,0,RStart}, /* RINIT was bad, resend */
|
|||
|
#ifdef TODO
|
|||
|
{ZCOMPL,f,1,1,s},
|
|||
|
#endif /* TODO */
|
|||
|
{ZFREECNT,GotFreecnt,0,0,RStart}, /* sender wants free space */
|
|||
|
{ZCOMMAND,GotCommand,0,0,CommandData}, /* sender wants command */
|
|||
|
{ZSTDERR,GotStderr,0,0,StderrData}, /* sender wants to send msg */
|
|||
|
{99,ZPF,0,0,RStart}, /* anything else is an error */
|
|||
|
} ;
|
|||
|
|
|||
|
StateTable RSinitWaitOps[] = { /* waiting for data */
|
|||
|
{99,ZPF,0,0,RSinitWait},
|
|||
|
} ;
|
|||
|
|
|||
|
StateTable RFileNameOps[] = { /* waiting for file name */
|
|||
|
{99,ZPF,0,0,RFileName},
|
|||
|
} ;
|
|||
|
|
|||
|
StateTable RCrcOps[] = { /* waiting for CRC */
|
|||
|
{ZCRC,GotFileCrc,0,0,RFile}, /* sender sent it */
|
|||
|
{ZNAK,ResendCrcReq,0,0,RCrc}, /* ZCRC was bad, resend */
|
|||
|
{ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */
|
|||
|
{ZFIN,GotFin,1,1,RFinish}, /* sender signing off */
|
|||
|
{99,ZPF,0,0,RCrc},
|
|||
|
} ;
|
|||
|
|
|||
|
StateTable RFileOps[] = { /* waiting for ZDATA */
|
|||
|
{ZDATA,GotData,0,0,RData}, /* got it */
|
|||
|
{ZNAK,ResendRpos,0,0,RFile}, /* ZRPOS was bad, resend */
|
|||
|
{ZEOF,GotEof,0,0,RStart}, /* end of file */
|
|||
|
{ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */
|
|||
|
{ZFILE,ResendRpos,0,0,RFile}, /* ZRPOS was bad, resend */
|
|||
|
{ZFIN,GotFin,1,1,RFinish}, /* sender signing off */
|
|||
|
{99,ZPF,0,0,RFile},
|
|||
|
} ;
|
|||
|
|
|||
|
/* waiting for data, but a packet could possibly arrive due
|
|||
|
* to error recovery or something
|
|||
|
*/
|
|||
|
StateTable RDataOps[] = {
|
|||
|
{ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */
|
|||
|
{ZFILE,GotFile,0,1,RFileName}, /* start a new file (??) */
|
|||
|
{ZNAK,ResendRpos,1,1,RFile}, /* ZRPOS was bad, resend */
|
|||
|
{ZFIN,GotFin,1,1,RFinish}, /* sender signing off */
|
|||
|
{ZDATA,GotData,0,1,RData}, /* file data follows */
|
|||
|
{ZEOF,GotEof,1,1,RStart}, /* end of file */
|
|||
|
{99,ZPF,0,0,RData},
|
|||
|
} ;
|
|||
|
|
|||
|
/* here if we've sent ZFERR or ZABORT. Waiting for ZFIN */
|
|||
|
|
|||
|
StateTable RFinishOps[] = {
|
|||
|
{ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */
|
|||
|
{ZFILE,GotFile,1,1,RFileName}, /* start a new file */
|
|||
|
{ZNAK,GotFin,1,1,RFinish}, /* resend ZFIN */
|
|||
|
{ZFIN,GotFin,1,1,RFinish}, /* sender signing off */
|
|||
|
{99,ZPF,0,0,RFinish},
|
|||
|
} ;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
extern char *hdrnames[] ;
|
|||
|
|
|||
|
extern int dataSetup() ;
|
|||
|
|
|||
|
|
|||
|
/* RECEIVE-RELATED STUFF BELOW HERE */
|
|||
|
|
|||
|
|
|||
|
/* resend ZRINIT header in response to ZRQINIT or ZNAK header */
|
|||
|
|
|||
|
int
|
|||
|
SendRinit( register ZModem *info )
|
|||
|
{
|
|||
|
u_char dbuf[4] ;
|
|||
|
|
|||
|
#ifdef COMMENT
|
|||
|
if( info->timeoutCount >= 5 )
|
|||
|
/* TODO: switch to Ymodem */
|
|||
|
#endif /* COMMENT */
|
|||
|
|
|||
|
zmodemlog("SendRinit[%s]: send ZRINIT\n", sname(info)) ;
|
|||
|
|
|||
|
info->timeout = ResponseTime ;
|
|||
|
dbuf[0] = info->bufsize&0xff ;
|
|||
|
dbuf[1] = (info->bufsize>>8)&0xff ;
|
|||
|
dbuf[2] = 0 ;
|
|||
|
dbuf[3] = info->zrinitflags ;
|
|||
|
return ZXmitHdrHex(ZRINIT, dbuf, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* received a ZSINIT header in response to ZRINIT */
|
|||
|
|
|||
|
static int
|
|||
|
GotSinit( register ZModem *info )
|
|||
|
{
|
|||
|
zmodemlog("GotSinit[%s]: call dataSetup\n", sname(info)) ;
|
|||
|
|
|||
|
info->zsinitflags = info->hdrData[4] ;
|
|||
|
info->escCtrl = info->zsinitflags & TESCCTL ;
|
|||
|
info->escHibit = info->zsinitflags & TESC8 ;
|
|||
|
ZFlowControl(1, info) ;
|
|||
|
return dataSetup(info) ;
|
|||
|
}
|
|||
|
|
|||
|
/* received rest of ZSINIT packet */
|
|||
|
|
|||
|
int
|
|||
|
GotSinitData( register ZModem *info, int crcGood )
|
|||
|
{
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount=0 ;
|
|||
|
info->state = RStart ;
|
|||
|
|
|||
|
zmodemlog("GotSinitData[%s]: crcGood=%d\n", sname(info), crcGood) ;
|
|||
|
|
|||
|
if( !crcGood )
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
|
|||
|
if( info->attn != NULL )
|
|||
|
free(info->attn) ;
|
|||
|
info->attn = NULL ;
|
|||
|
if( info->buffer[0] != '\0' )
|
|||
|
info->attn = strdup((char *)info->buffer) ;
|
|||
|
return ZXmitHdrHex(ZACK, ZEnc4(SerialNo), info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* got ZFILE. Cache flags and set up to receive filename */
|
|||
|
|
|||
|
static int
|
|||
|
GotFile( register ZModem *info )
|
|||
|
{
|
|||
|
zmodemlog("GotFile[%s]: call dataSetup\n", sname(info)) ;
|
|||
|
|
|||
|
info->errCount = 0 ;
|
|||
|
info->f0 = info->hdrData[4] ;
|
|||
|
info->f1 = info->hdrData[3] ;
|
|||
|
info->f2 = info->hdrData[2] ;
|
|||
|
info->f3 = info->hdrData[1] ;
|
|||
|
return dataSetup(info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* utility: see if ZOpenFile wants this file, and if
|
|||
|
* so, request it from sender.
|
|||
|
*/
|
|||
|
|
|||
|
static int
|
|||
|
requestFile( register ZModem *info, u_long crc )
|
|||
|
{
|
|||
|
info->file = ZOpenFile((char *)info->buffer, crc, info) ;
|
|||
|
|
|||
|
if( info->file == NULL ) {
|
|||
|
zmodemlog("requestFile[%s]: send ZSKIP\n", sname(info)) ;
|
|||
|
|
|||
|
info->state = RStart ;
|
|||
|
ZStatus(FileSkip, 0, info->filename) ;
|
|||
|
return ZXmitHdrHex(ZSKIP, zeros, info) ;
|
|||
|
}
|
|||
|
else {
|
|||
|
zmodemlog("requestFile[%s]: send ZRPOS(%ld)\n",
|
|||
|
sname(info), info->offset) ;
|
|||
|
info->offset = info->f0 == ZCRESUM ? ftell(info->file) : 0 ;
|
|||
|
info->state = RFile ;
|
|||
|
ZStatus(FileBegin, 0, info->filename) ;
|
|||
|
return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* parse filename info. */
|
|||
|
|
|||
|
static void
|
|||
|
parseFileName( register ZModem *info, char *fileinfo )
|
|||
|
{
|
|||
|
char *ptr ;
|
|||
|
int serial=0 ;
|
|||
|
|
|||
|
info->len = info->mode = info->filesRem =
|
|||
|
info->bytesRem = info->fileType = 0 ;
|
|||
|
ptr = fileinfo + strlen(fileinfo) + 1 ;
|
|||
|
if( info->filename != NULL )
|
|||
|
free(info->filename) ;
|
|||
|
info->filename = strdup(fileinfo) ;
|
|||
|
sscanf(ptr, "%d %lo %o %o %d %d %d", &info->len, &info->date,
|
|||
|
&info->mode, &serial, &info->filesRem, &info->bytesRem,
|
|||
|
&info->fileType) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* got filename. Parse arguments from it and execute
|
|||
|
* policy function ZOpenFile(), provided by caller
|
|||
|
*/
|
|||
|
|
|||
|
int
|
|||
|
GotFileName( register ZModem *info, int crcGood )
|
|||
|
{
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount=0 ;
|
|||
|
|
|||
|
if( !crcGood ) {
|
|||
|
zmodemlog("GotFileName[%s]: bad crc, send ZNAK\n", sname(info)) ;
|
|||
|
info->state = RStart ;
|
|||
|
return ZXmitHdrHex(ZNAK, zeros, info) ;
|
|||
|
}
|
|||
|
|
|||
|
parseFileName(info, (char *)info->buffer) ;
|
|||
|
|
|||
|
if( (info->f1 & ZMMASK) == ZMCRC ) {
|
|||
|
info->state = RCrc ;
|
|||
|
return ZXmitHdrHex(ZCRC, zeros, info) ;
|
|||
|
}
|
|||
|
|
|||
|
zmodemlog("GotFileName[%s]: good crc, call requestFile\n",
|
|||
|
sname(info)) ;
|
|||
|
info->state = RFile ;
|
|||
|
return requestFile(info,0) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
ResendCrcReq(ZModem *info)
|
|||
|
{
|
|||
|
zmodemlog("ResendCrcReq[%s]: send ZCRC\n", sname(info)) ;
|
|||
|
return ZXmitHdrHex(ZCRC, zeros, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* received file CRC, now we're ready to accept or reject */
|
|||
|
|
|||
|
static int
|
|||
|
GotFileCrc( register ZModem *info )
|
|||
|
{
|
|||
|
zmodemlog("GotFileCrc[%s]: call requestFile\n", sname(info)) ;
|
|||
|
return requestFile(info, ZDec4(info->hdrData+1)) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* last ZRPOS was bad, resend it */
|
|||
|
|
|||
|
int
|
|||
|
ResendRpos( register ZModem *info )
|
|||
|
{
|
|||
|
zmodemlog("ResendRpos[%s]: send ZRPOS(%ld)\n",
|
|||
|
sname(info), info->offset) ;
|
|||
|
return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* recevied ZDATA header */
|
|||
|
|
|||
|
static int
|
|||
|
GotData( register ZModem *info )
|
|||
|
{
|
|||
|
int err ;
|
|||
|
|
|||
|
zmodemlog("GotData[%s]:\n", sname(info)) ;
|
|||
|
|
|||
|
if( ZDec4(info->hdrData+1) != info->offset ) {
|
|||
|
if( info->attn != NULL && (err=ZAttn(info)) != 0 )
|
|||
|
return err ;
|
|||
|
zmodemlog(" bad, send ZRPOS(%ld)\n", info->offset);
|
|||
|
return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
|
|||
|
}
|
|||
|
|
|||
|
/* Let's do it! */
|
|||
|
zmodemlog(" call dataSetup\n");
|
|||
|
return dataSetup(info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* Utility: flush input, send attn, send specified header */
|
|||
|
|
|||
|
static int
|
|||
|
fileError( register ZModem *info, int type, int data )
|
|||
|
{
|
|||
|
int err ;
|
|||
|
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount=0 ;
|
|||
|
|
|||
|
if( info->attn != NULL && (err=ZAttn(info)) != 0 )
|
|||
|
return err ;
|
|||
|
return ZXmitHdrHex(type, ZEnc4(data), info) ;
|
|||
|
}
|
|||
|
|
|||
|
/* received file data */
|
|||
|
|
|||
|
int
|
|||
|
GotFileData( register ZModem *info, int crcGood )
|
|||
|
{
|
|||
|
/* OK, now what? Fushing the buffers and executing the
|
|||
|
* attn sequence has likely chopped off the input stream
|
|||
|
* mid-packet. Now we switch to idle mode and treat all
|
|||
|
* incoming stuff like noise until we get a new valid
|
|||
|
* packet.
|
|||
|
*/
|
|||
|
|
|||
|
if( !crcGood ) { /* oh bugger, an error. */
|
|||
|
zmodemlog(
|
|||
|
"GotFileData[%s]: bad crc, send ZRPOS(%ld), new state = RFile\n",
|
|||
|
sname(info), info->offset) ;
|
|||
|
ZStatus(DataErr, ++info->errCount, NULL) ;
|
|||
|
if( info->errCount > MaxErrs ) {
|
|||
|
ZmodemAbort(info) ;
|
|||
|
return ZmDataErr ;
|
|||
|
}
|
|||
|
else {
|
|||
|
info->state = RFile ;
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount=0 ;
|
|||
|
return fileError(info, ZRPOS, info->offset) ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( ZWriteFile(info->buffer, info->chrCount, info->file, info) )
|
|||
|
{
|
|||
|
/* RED ALERT! Could not write the file. */
|
|||
|
ZStatus(FileErr, errno, NULL) ;
|
|||
|
info->state = RFinish ;
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount=0 ;
|
|||
|
return fileError(info, ZFERR, errno) ;
|
|||
|
}
|
|||
|
|
|||
|
zmodemlog("GotFileData[%s]: %ld.%d,",
|
|||
|
sname(info), info->offset, info->chrCount) ;
|
|||
|
info->offset += info->chrCount ;
|
|||
|
ZStatus(RcvByteCount, info->offset, NULL) ;
|
|||
|
|
|||
|
/* if this was the last data subpacket, leave data mode */
|
|||
|
if( info->PacketType == ZCRCE || info->PacketType == ZCRCW ) {
|
|||
|
zmodemlog(" ZCRCE|ZCRCW, new state RFile") ;
|
|||
|
info->state = RFile ;
|
|||
|
info->InputState = Idle ;
|
|||
|
info->chrCount=0 ;
|
|||
|
}
|
|||
|
else {
|
|||
|
zmodemlog(" call dataSetup") ;
|
|||
|
(void) dataSetup(info) ;
|
|||
|
}
|
|||
|
|
|||
|
if( info->PacketType == ZCRCQ || info->PacketType == ZCRCW ) {
|
|||
|
zmodemlog(", send ZACK\n") ;
|
|||
|
return ZXmitHdrHex(ZACK, ZEnc4(info->offset), info) ;
|
|||
|
}
|
|||
|
else
|
|||
|
zmodemlog("\n") ;
|
|||
|
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* received ZEOF packet, file is now complete */
|
|||
|
|
|||
|
static int
|
|||
|
GotEof( register ZModem *info )
|
|||
|
{
|
|||
|
zmodemlog("GotEof[%s]: offset=%ld\n", sname(info), info->offset) ;
|
|||
|
if( ZDec4(info->hdrData+1) != info->offset ) {
|
|||
|
zmodemlog(" bad length, state = RFile\n") ;
|
|||
|
info->state = RFile ;
|
|||
|
return 0 ; /* it was probably spurious */
|
|||
|
}
|
|||
|
|
|||
|
/* TODO: if we can't close the file, send a ZFERR */
|
|||
|
|
|||
|
ZCloseFile(info) ; info->file = NULL ;
|
|||
|
ZStatus(FileEnd, 0, info->filename) ;
|
|||
|
if( info->filename != NULL ) {
|
|||
|
free(info->filename) ;
|
|||
|
info->filename = NULL ;
|
|||
|
}
|
|||
|
return SendRinit(info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* got ZFIN, respond in kind */
|
|||
|
|
|||
|
static int
|
|||
|
GotFin( register ZModem *info )
|
|||
|
{
|
|||
|
zmodemlog("GotFin[%s]: send ZFIN\n", sname(info)) ;
|
|||
|
info->InputState = Finish ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
if( info->filename != NULL )
|
|||
|
free(info->filename) ;
|
|||
|
return ZXmitHdrHex(ZFIN, zeros, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
GotFreecnt( register ZModem *info )
|
|||
|
{
|
|||
|
/* TODO: how do we find free space on system? */
|
|||
|
return ZXmitHdrHex(ZACK, ZEnc4(0xffffffff), info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* YMODEM */
|
|||
|
|
|||
|
static u_char AckStr[1] = {ACK} ;
|
|||
|
static u_char NakStr[1] = {NAK} ;
|
|||
|
static u_char CanStr[2] = {CAN,CAN} ;
|
|||
|
|
|||
|
static int ProcessPacket() ;
|
|||
|
static int acceptPacket() ;
|
|||
|
static int rejectPacket() ;
|
|||
|
static int calcCrc() ;
|
|||
|
|
|||
|
int
|
|||
|
YrcvChar( char c, register ZModem *info )
|
|||
|
{
|
|||
|
int err ;
|
|||
|
|
|||
|
if( info->canCount >= 2 ) {
|
|||
|
ZStatus(RmtCancel, 0, NULL) ;
|
|||
|
return ZmErrCancel ;
|
|||
|
}
|
|||
|
|
|||
|
switch( info->state ) {
|
|||
|
case YREOF:
|
|||
|
if( c == EOT ) {
|
|||
|
ZCloseFile(info) ; info->file = NULL ;
|
|||
|
ZStatus(FileEnd, 0, info->filename) ;
|
|||
|
if( info->filename != NULL )
|
|||
|
free(info->filename) ;
|
|||
|
if( (err = acceptPacket(info)) != 0 )
|
|||
|
return err ;
|
|||
|
info->packetCount = -1 ;
|
|||
|
info->offset = 0 ;
|
|||
|
info->state = YRStart ;
|
|||
|
return ZXmitStr((u_char *)"C", 1, info) ;
|
|||
|
}
|
|||
|
/* else, drop through */
|
|||
|
|
|||
|
case YRStart:
|
|||
|
case YRDataWait:
|
|||
|
switch( c ) {
|
|||
|
case SOH:
|
|||
|
case STX:
|
|||
|
info->pktLen = c == SOH ? (128+4) : (1024+4) ;
|
|||
|
info->state = YRData ;
|
|||
|
info->chrCount = 0 ;
|
|||
|
info->timeout = 1 ;
|
|||
|
info->noiseCount = 0 ;
|
|||
|
info->crc = 0 ;
|
|||
|
break ;
|
|||
|
|
|||
|
case EOT:
|
|||
|
/* ignore first EOT to protect against false eot */
|
|||
|
info->state = YREOF ;
|
|||
|
return rejectPacket(info) ;
|
|||
|
|
|||
|
default:
|
|||
|
if( ++info->noiseCount > 135 )
|
|||
|
return ZXmitStr(NakStr, 1, info) ;
|
|||
|
break ;
|
|||
|
}
|
|||
|
break ;
|
|||
|
|
|||
|
case YRData:
|
|||
|
info->buffer[info->chrCount++] = c ;
|
|||
|
if( info->chrCount >= info->pktLen )
|
|||
|
return ProcessPacket(info) ;
|
|||
|
break ;
|
|||
|
|
|||
|
default:
|
|||
|
break ;
|
|||
|
}
|
|||
|
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int
|
|||
|
YrcvTimeout( register ZModem *info )
|
|||
|
{
|
|||
|
switch( info->state )
|
|||
|
{
|
|||
|
case YRStart:
|
|||
|
if( info->timeoutCount >= 10 ) {
|
|||
|
(void) ZXmitStr(CanStr, 2, info) ;
|
|||
|
return ZmErrInitTo ;
|
|||
|
}
|
|||
|
return ZXmitStr((u_char *)"C", 1, info) ;
|
|||
|
|
|||
|
case YRDataWait:
|
|||
|
case YREOF:
|
|||
|
case YRData:
|
|||
|
if( info->timeoutCount >= 10 ) {
|
|||
|
(void) ZXmitStr(CanStr, 2, info) ;
|
|||
|
return ZmErrRcvTo ;
|
|||
|
}
|
|||
|
return ZXmitStr(NakStr, 1, info) ;
|
|||
|
default: return 0 ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
ProcessPacket( register ZModem *info )
|
|||
|
{
|
|||
|
int idx = (u_char) info->buffer[0] ;
|
|||
|
int idxc = (u_char) info->buffer[1] ;
|
|||
|
int crc0, crc1 ;
|
|||
|
int err ;
|
|||
|
|
|||
|
info->state = YRDataWait ;
|
|||
|
|
|||
|
if( idxc != 255 - idx ) {
|
|||
|
ZStatus(DataErr, ++info->errCount, NULL) ;
|
|||
|
return rejectPacket(info) ;
|
|||
|
}
|
|||
|
|
|||
|
if( idx == (info->packetCount%256) ) /* quietly ignore dup */
|
|||
|
return acceptPacket(info) ;
|
|||
|
|
|||
|
if( idx != (info->packetCount+1)%256 ) { /* out of sequence */
|
|||
|
(void) ZXmitStr(CanStr, 2, info) ;
|
|||
|
return ZmErrSequence ;
|
|||
|
}
|
|||
|
|
|||
|
crc0 = (u_char)info->buffer[info->pktLen-2] << 8 |
|
|||
|
(u_char)info->buffer[info->pktLen-1] ;
|
|||
|
crc1 = calcCrc(info->buffer+2, info->pktLen-4) ;
|
|||
|
if( crc0 != crc1 ) {
|
|||
|
ZStatus(DataErr, ++info->errCount, NULL) ;
|
|||
|
return rejectPacket(info) ;
|
|||
|
}
|
|||
|
|
|||
|
++info->packetCount ;
|
|||
|
|
|||
|
if( info->packetCount == 0 ) /* packet 0 is filename */
|
|||
|
{
|
|||
|
if( info->buffer[2] == '\0' ) { /* null filename is FIN */
|
|||
|
(void) acceptPacket(info) ;
|
|||
|
return ZmDone ;
|
|||
|
}
|
|||
|
|
|||
|
parseFileName(info, (char *)info->buffer+2) ;
|
|||
|
info->file = ZOpenFile(info->filename, 0, info) ;
|
|||
|
if( info->file == NULL ) {
|
|||
|
(void) ZXmitStr(CanStr, 2, info) ;
|
|||
|
return ZmErrCantOpen ;
|
|||
|
}
|
|||
|
if( (err = acceptPacket(info)) != 0 )
|
|||
|
return err ;
|
|||
|
return ZXmitStr((u_char *)"C", 1, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if( ZWriteFile(info->buffer+2, info->pktLen-4, info->file, info) ) {
|
|||
|
ZStatus(FileErr, errno, NULL) ;
|
|||
|
(void) ZXmitStr(CanStr, 2, info) ;
|
|||
|
return ZmErrSys ;
|
|||
|
}
|
|||
|
info->offset += info->pktLen-4 ;
|
|||
|
ZStatus(RcvByteCount, info->offset, NULL) ;
|
|||
|
|
|||
|
(void) acceptPacket(info) ;
|
|||
|
return 0 ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
rejectPacket( register ZModem *info )
|
|||
|
{
|
|||
|
info->timeout = 10 ;
|
|||
|
return ZXmitStr(NakStr, 1, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
acceptPacket( register ZModem *info )
|
|||
|
{
|
|||
|
info->state = YRDataWait ;
|
|||
|
info->timeout = 10 ;
|
|||
|
return ZXmitStr(AckStr, 1, info) ;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static int
|
|||
|
calcCrc( u_char *str, int len )
|
|||
|
{
|
|||
|
int crc = 0 ;
|
|||
|
while( --len >= 0 )
|
|||
|
crc = updcrc(*str++, crc) ;
|
|||
|
crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
|
|||
|
return crc & 0xffff ;
|
|||
|
}
|