This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
magicka/Xmodem/zmodemt.c
2016-04-03 09:26:17 +10:00

1153 lines
26 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef lint
static const char rcsid[] = "$Id: zmodemt.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ;
#endif
/*
* Copyright (c) 1995 by Edward A. Falk
*/
/**********
*
*
* @@@@@ @ @ @@@ @@@@ @@@@@ @ @ @@@@@
* @ @@ @@ @ @ @ @ @ @@ @@ @
* @ @ @ @ @ @ @ @ @@@ @ @ @ @
* @ @ @ @ @ @ @ @ @ @ @ @ @
* @@@@@ @ @ @ @@@ @@@@ @@@@@ @ @ @ @
*
* ZMODEMT - transmit side of zmodem protocol
*
* transmit side of zmodem protocol
*
* Caller sets flags defined in zmodem.h as appropriate.
* (default is basic zmodem)
*
* functions:
*
* ZmodemTInit(ZModem *info)
* YmodemTInit(ZModem *info)
* XmodemTInit(ZModem *info)
* Initiate a connection
*
* ZmodemTFile(char *filename, u_char flags[4], ZModem *info)
* Initiate a file transfer. Flags are as specified
* under "ZCBIN" in zmodem.h
*
* ZmodemTFinish(ZModem *info)
* last file
*
* ZmodemTAbort(ZModem *info)
* abort transfer
*
* all functions return 0 on success, 1 on abort
*
*
* Edward A. Falk
*
* January, 1995
*
*
*
**********/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include "zmodem.h"
#include "crctab.h"
int SendMoreFileData( register ZModem *info ) ;
extern int ZXmitData(int, int, u_char, u_char *data, ZModem *) ;
extern void ZFlowControl(int onoff, ZModem *info) ;
static int YXmitData() ;
static int YSendFilename() ;
static int YSendData() ;
static int YSendFin() ;
static int sendFilename() ;
static u_char zeros[4] = {0,0,0,0} ;
/* called by user to establish protocol */
int
ZmodemTInit( register ZModem *info )
{
int err ;
int i ;
info->state = TStart ;
info->Protocol = ZMODEM ;
info->crc32 = 0 ;
info->packetCount = 0 ;
info->errCount = 0 ;
info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ;
info->InputState = Idle ;
info->canCount = info->chrCount = 0 ;
info->windowCount = 0 ;
info->filename = NULL ;
info->bufsize = 0 ;
info->interrupt = 0 ;
info->waitflag = 0 ;
if( info->packetsize == 0 )
info->packetsize = 256 ;
/* we won't be receiving much data, pick a reasonable buffer
* size (largest packet will do)
*/
i = info->packetsize*2 ;
if( i < 1024 ) i = 1024 ;
info->buffer = (u_char *)malloc(i) ;
ZIFlush(info) ;
/* optional: send "rz\r" to remote end */
if( DoInitRZ ) {
if( (err = ZXmitStr((u_char *)"rz\r", 3, info)) )
return err ;
}
if( (err = ZXmitHdr(ZRQINIT, ZHEX, zeros, info)) ) /* nudge receiver */
return err ;
info->timeout = 60 ;
zmodemlog("ZmodemTInit[%s]: sent ZRQINIT\n", sname(info)) ;
return 0 ;
}
/* called by user to establish Ymodem protocol */
int
YmodemTInit( register ZModem *info )
{
info->state = YTStart ;
info->Protocol = YMODEM ;
info->errCount = 0 ;
info->InputState = Ysend ;
info->canCount = info->chrCount = 0 ;
info->windowCount = 0 ;
info->filename = NULL ;
if( info->packetsize != 1024 )
info->packetsize = 128 ;
info->buffer = (u_char *)malloc(1024) ;
ZIFlush(info) ;
ZFlowControl(0, info) ;
info->timeout = 60 ;
return 0 ;
}
/* called by user to establish Xmodem protocol */
int
XmodemTInit( register ZModem *info )
{
(void) YmodemTInit(info) ;
info->Protocol = XMODEM ;
return 0 ;
}
/* called by user to begin transmission of a file */
int
ZmodemTFile(
char *file,
char *rfile,
u_int f0,
u_int f1,
u_int f2,
u_int f3,
int filesRem,
int bytesRem,
ZModem *info)
{
if( file == NULL || (info->file = fopen(file, "r")) == NULL )
return ZmErrCantOpen ;
info->fileEof = 0 ;
info->filename = file ;
info->rfilename = (rfile != NULL) ? rfile : "noname" ;
info->filesRem = filesRem ;
info->bytesRem = bytesRem ;
info->fileFlags[3] = f0 ;
info->fileFlags[2] = f1 ;
info->fileFlags[1] = f2 ;
info->fileFlags[0] = f3 ;
info->offset = info->lastOffset = 0 ;
info->len = info->date = info->fileType = info->mode = 0 ;
if( info->filename != NULL )
{
struct stat buf ;
if( stat(info->filename, &buf) == 0 ) {
info->len = buf.st_size ;
info->date = buf.st_mtime ;
info->fileType = 0 ;
info->mode = (buf.st_mode&0777)|0100000 ;
}
}
if( info->Protocol == XMODEM )
return YSendData(info) ;
if( info->Protocol == YMODEM )
return YSendFilename(info) ;
info->state = FileWait ;
zmodemlog("ZmodemTFile[%s]: send ZFILE(%s)\n",
sname(info), info->rfilename) ;
return sendFilename(info) ;
}
/* send ZFILE header and filename & info. Wait for response
* from receiver.
*/
static int
sendFilename( ZModem *info )
{
int err ;
int i ;
u_char obuf[2048] ;
u_char *ptr = obuf ;
info->state = FileWait ;
if( (err = ZXmitHdr(ZFILE, ZBIN, info->fileFlags, info)) )
return err ;
i = strlen(info->rfilename) ;
memcpy(ptr, info->rfilename, i+1) ; ptr += i+1 ;
sprintf((char *)ptr, "%d %lo %o 0 %d %d 0", info->len, info->date,
info->mode, info->filesRem, info->bytesRem) ;
ptr += strlen((char *)ptr) ;
*ptr++ = '\0' ;
return ZXmitData(ZBIN, ptr-obuf, ZCRCW, obuf, info) ;
}
/* called by user when there are no more files to send */
int
ZmodemTFinish( ZModem *info )
{
if( info->Protocol == XMODEM )
return ZmDone ;
if( info->Protocol == YMODEM )
return YSendFin(info) ;
info->state = TFinish ;
if( info->buffer != NULL ) {
free(info->buffer) ;
info->buffer = NULL ;
}
zmodemlog("ZmodemTFinish[%s]: send ZFIN\n", sname(info)) ;
return ZXmitHdr(ZFIN, ZHEX, zeros, info) ;
}
extern int ZPF() ;
extern int AnswerChallenge() ;
extern int GotAbort() ;
extern int Ignore() ;
extern int RetDone() ;
extern int GotCommand() ;
extern int GotStderr() ;
static int GotRinit() ;
static int SendZSInit() ;
static int SendFileCrc() ;
static int GotSendAck() ;
static int GotSendDoneAck() ;
static int GotSendNak() ;
static int GotSendWaitAck() ;
static int SkipFile() ;
static int GotSendPos() ;
static int SendFileData() ;
static int ResendEof() ;
static int OverAndOut() ;
/* sent ZRQINIT, waiting for response */
StateTable TStartOps[] = {
{ZRINIT,GotRinit,1,1,TStart},
{ZCHALLENGE,AnswerChallenge,1,0,TStart},
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{ZNAK,Ignore,0,0,TStart},
{ZCOMMAND,GotCommand,0,0,CommandData},
{ZSTDERR,GotStderr,0,0,StderrData},
{99,ZPF,0,0,TStart},
} ;
/* sent ZSINIT, waiting for response */
StateTable TInitOps[] = {
{ZACK,RetDone,1,0,TInit},
{ZNAK,SendZSInit,1,0,TInit},
{ZRINIT,GotRinit,1,1,TInit}, /* redundant, but who cares */
{ZCHALLENGE,AnswerChallenge,1,0,TInit},
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{ZCOMMAND,GotCommand,0,0,CommandData},
{ZSTDERR,GotStderr,0,0,StderrData},
{99,ZPF,0,0,TInit},
} ;
/* sent ZFILE, waiting for response */
StateTable FileWaitOps[] = {
{ZRPOS,SendFileData,1,0,Sending},
{ZSKIP,SkipFile,1,0,FileWait},
{ZCRC,SendFileCrc,1,0,FileWait},
{ZNAK,sendFilename,1,0,FileWait},
{ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{ZCHALLENGE,AnswerChallenge,1,0,FileWait},
{ZCOMMAND,GotCommand,0,0,CommandData},
{ZSTDERR,GotStderr,0,0,StderrData},
{99,ZPF,0,0,FileWait},
} ;
/* sent file CRC, waiting for response */
StateTable CrcWaitOps[] = {
{ZRPOS,SendFileData,1,0,Sending},
{ZSKIP,SkipFile,1,0,FileWait},
{ZNAK,SendFileCrc,1,0,CrcWait},
{ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{ZCRC,SendFileCrc,0,0,CrcWait},
{ZCHALLENGE,AnswerChallenge,0,0,CrcWait},
{ZCOMMAND,GotCommand,0,0,CommandData},
{ZSTDERR,GotStderr,0,0,StderrData},
{99,ZPF,0,0,CrcWait},
} ;
/* sending data, interruptable */
StateTable SendingOps[] = {
{ZACK,GotSendAck,0,0,Sending},
{ZRPOS,GotSendPos,1,1,Sending},
{ZSKIP,SkipFile,1,1,FileWait},
{ZNAK,GotSendNak,1,1,Sending},
{ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{99,ZPF,0,0,SendWait},
} ;
/* sent data, need to send EOF */
StateTable SendDoneOps[] = {
{ZACK,GotSendDoneAck,0,0,SendWait},
{ZRPOS,GotSendPos,1,1,Sending},
{ZSKIP,SkipFile,1,1,FileWait},
{ZNAK,GotSendNak,1,1,Sending},
{ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{99,ZPF,0,0,SendWait},
} ;
/* sending data, waiting for ACK */
StateTable SendWaitOps[] = {
{ZACK,GotSendWaitAck,0,0,Sending},
{ZRPOS,GotSendPos,0,0,SendWait},
{ZSKIP,SkipFile,1,1,FileWait},
{ZNAK,GotSendNak,0,0,Sending},
{ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{99,ZPF,0,0,SendWait},
} ;
/* sent ZEOF, waiting for new RINIT */
StateTable SendEofOps[] = {
{ZRINIT,SkipFile,1,0,TStart}, /* successful completion */
{ZACK,Ignore,0,0,SendEof}, /* probably ACK from last packet */
{ZRPOS,GotSendPos,1,1,SendWait},
{ZSKIP,SkipFile,1,1,TStart},
{ZNAK,ResendEof,1,0,SendEof},
{ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{99,ZPF,0,0,SendEof},
} ;
StateTable TFinishOps[] = {
{ZFIN,OverAndOut,1,1,Done},
{ZNAK,ZmodemTFinish,1,1,TFinish},
{ZRINIT,ZmodemTFinish,1,1,TFinish},
{ZABORT,GotAbort,1,1,TFinish},
{ZFERR,GotAbort,1,1,TFinish},
{99,ZPF,0,0,TFinish},
} ;
extern char *hdrnames[] ;
/* Received Rinit. Usually received while in start state,
* this can also be an attempt to resync after a protocol
* failure
*/
static int
GotRinit( register ZModem *info )
{
info->bufsize = info->hdrData[1] + info->hdrData[2]*256 ;
info->zrinitflags = info->hdrData[4] + info->hdrData[3]*256 ;
info->crc32 = info->zrinitflags & CANFC32 ;
info->escCtrl = info->zrinitflags & ESCCTL ;
info->escHibit = info->zrinitflags & ESC8 ;
/* Full streaming: If receiver can overlap I/O, and if
* the sender can sample the reverse channel without hanging,
* and the receiver has not specified a buffer size, then we
* can simply blast away with ZCRCG packets. If the receiver
* detects an error, it sends an attn sequence and a new ZRPOS
* header to restart the file where the error occurred.
*
* [note that zmodem8.doc does not define how much noise is
* required to trigger a ZCRCW packet. We arbitrarily choose
* 64 bytes]
*
* If 'windowsize' is nonzero, and the receiver can do full
* duplex, ZCRCQ packets are sent instead of ZCRCG, to keep track
* of the number of characters in the queue. If the queue fills
* up, we pause and wait for a ZACK.
*
*
* Full Streaming with Reverse Interrupt: If sender cannot
* sample the input stream, then we define an Attn sequence
* that will be used to interrupt transmission.
*
*
* Full Streaming with Sliding Window: If sender cannot
* sample input stream or respond to Attn signal, we send
* several ZCRCQ packets until we're sure the receiver must
* have sent back at least one ZACK. Then we stop sending and
* read that ZACK. Then we send one more packet and so on.
*
*
* Segmented Streaming: If receiver cannot overlap I/O or can't do
* full duplex and has specified a maximum receive buffer size,
* whenever the buffer size is reached, we send a ZCRCW packet.
*
* TODO: what if receiver can't overlap, but hasn't set a buffer
* size?
*
* ZCRCE: CRC next, frame ends, header follows
* ZCRCG: CRC next, frame continues nonstop
* ZCRCQ: CRC next, send ZACK, frame continues nonstop
* ZCRCW: CRC next, send ZACK, frame ends, header follows
*/
ZFlowControl(1,info) ;
if( (info->zrinitflags & (CANFDX|CANOVIO)) == (CANFDX|CANOVIO) &&
(SendSample||SendAttn) &&
info->bufsize == 0 )
{
if( info->windowsize == 0 )
info->Streaming = Full ;
else
info->Streaming = StrWindow ;
}
else if( (info->zrinitflags & (CANFDX|CANOVIO)) == (CANFDX|CANOVIO) &&
info->bufsize == 0 )
{
info->Streaming = SlidingWindow ;
}
else
info->Streaming = Segmented ;
zmodemlog("GotRinit[%s]\n", sname(info)) ;
if( AlwaysSinit || info->zsinitflags != 0 || info->attn != NULL )
return SendZSInit(info) ;
else
return ZmDone ; /* caller now calls ZmodemTFile() */
}
static int
SendZSInit( ZModem *info )
{
int err ;
char *at = (info->attn != NULL) ? info->attn : "" ;
u_char fbuf[4] ;
/* TODO: zmodem8.doc states: "If the ZSINIT header specifies
* ESCCTL or ESC8, a HEX header is used, and the receiver
* activates the specified ESC modes before reading the following
* data subpacket." What does that mean?
*/
zmodemlog("SendZSInit[%s]\n", sname(info)) ;
info->state = TInit ;
fbuf[0] = fbuf[1] = fbuf[2] = 0 ;
fbuf[3] = info->zsinitflags ;
if( (err = ZXmitHdr(ZSINIT, ZBIN, fbuf, info)) ||
(err = ZXmitData(ZBIN, strlen(at)+1, ZCRCW, (u_char *)at, info)) )
return err ;
return 0 ;
}
static int
SendFileCrc( register ZModem *info )
{
u_long crc ;
crc = FileCrc(info->filename) ;
zmodemlog("SendFileCrc[%s]: %lx\n", sname(info), crc) ;
return ZXmitHdrHex(ZCRC, ZEnc4(crc), info) ;
}
/* Utility: start sending data. */
static int
startFileData( register ZModem *info )
{
int err ;
info->offset =
info->lastOffset =
info->zrposOffset = ZDec4(info->hdrData+1) ;
fseek(info->file, info->offset, 0) ;
/* TODO: what if fseek fails? Send EOF? */
zmodemlog("startFileData[%s]: %ld\n", sname(info), info->offset) ;
if( (err = ZXmitHdr(ZDATA, ZBIN, info->hdrData+1, info)) )
return err ;
return SendMoreFileData(info) ;
}
/* send a chunk of file data in response to a ZRPOS. Whether this
* is followed by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW depends on all
* sorts of protocol flags
*/
static int
SendFileData( register ZModem *info )
{
info->waitflag = 0 ;
return startFileData(info) ;
}
/* here if an ACK arrived while transmitting data. Update
* last known receiver offset, and try to send some more
* data.
*/
static int
GotSendAck( register ZModem *info )
{
u_long offset ;
offset = ZDec4(info->hdrData+1) ;
if( offset > info->lastOffset )
info->lastOffset = offset ;
zmodemlog("GotSendAck[%s]: %ld\n", sname(info), info->offset) ;
return 0 ; /* DONT send more data, that will happen
* later anyway */
}
/* here if an ACK arrived after last file packet sent. Send
* the EOF.
*/
static int
GotSendDoneAck( register ZModem *info )
{
u_long offset ;
offset = ZDec4(info->hdrData+1) ;
if( offset > info->lastOffset )
info->lastOffset = offset ;
zmodemlog("GotSendDoneAck[%s]: %ld\n", sname(info), info->offset) ;
info->state = SendEof ;
info->timeout = 60 ;
return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}
/* off to a bad start; ZDATA header was corrupt. Start
* from beginning
*/
static int
GotSendNak( register ZModem *info )
{
info->offset = info->zrposOffset ;
fseek(info->file, info->offset, 0) ;
/* TODO: what if fseek fails? Send EOF? */
zmodemlog("GotSendNak[%s]: %ld\n", sname(info), info->offset) ;
return SendMoreFileData(info) ;
}
/* got a ZSKIP, receiver doesn't want this file. */
static int
SkipFile( register ZModem *info )
{
zmodemlog("SkipFile[%s]\n", sname(info)) ;
fclose(info->file) ;
return ZmDone ;
}
/* got a ZRPOS packet in the middle of sending a file,
* set new offset and try again
*/
static int
GotSendPos( register ZModem *info )
{
ZStatus(DataErr, ++info->errCount, NULL) ;
info->waitflag = 1 ; /* next pkt should wait, to resync */
zmodemlog("GotSendPos[%s]\n", sname(info), info->offset) ;
return startFileData(info) ;
}
/* here if an ACK arrived while waiting while transmitting data.
* Update last known receiver offset, and try to send some more
* data.
*/
static int
GotSendWaitAck( register ZModem *info )
{
u_long offset ;
int err ;
offset = ZDec4(info->hdrData+1) ;
if( offset > info->lastOffset )
info->lastOffset = offset ;
zmodemlog("GotSendWaitAck[%s]\n", sname(info), offset) ;
if( (err = ZXmitHdr(ZDATA, ZBIN, ZEnc4(info->offset), info)) )
return err ;
return SendMoreFileData(info) ;
}
/* utility: send a chunk of file data. Whether this is followed
* by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW depends on all
* sorts of protocol flags, plus 'waitflag'. Exact amount of file
* data transmitted is variable, as escape sequences may pad out
* the buffer.
*/
int
SendMoreFileData( register ZModem *info )
{
int type ;
int qfull = 0 ;
int err ;
int len ; /* max # chars to send this packet */
long pending ; /* # of characters sent but not acknowledged */
/* ZCRCE: CRC next, frame ends, header follows
* ZCRCG: CRC next, frame continues nonstop
* ZCRCQ: CRC next, send ZACK, frame continues nonstop
* ZCRCW: CRC next, send ZACK, frame ends, header follows
*/
if( info->interrupt ) {
/* Bugger, receiver sent an interrupt. Enter a wait state
* and see what they want. Next header *should* be ZRPOS.
*/
info->state = SendWait ;
info->timeout = 60 ;
return 0 ;
}
/* Find out how many bytes we can transfer in the next packet */
len = info->packetsize ;
pending = info->offset - info->lastOffset ;
if( info->windowsize != 0 && info->windowsize - pending <= len ){
len = info->windowsize - pending ;
qfull = 1 ;
}
if( info->bufsize != 0 && info->bufsize - pending <= len ) {
len = info->bufsize - pending ;
qfull = 1 ;
}
if( len == 0 ) { /* window still full, keep waiting */
info->state = SendWait ;
info->timeout = 60 ;
return 0 ;
}
/* OK, we can safely transmit 'len' bytes of data. Start reading
* file until buffer is full.
*/
len -= 10 ; /* Pre-deduct 10 bytes for trailing CRC */
/* find out what kind of packet to send */
if( info->waitflag ) {
type = ZCRCW ;
info->waitflag = 0 ;
}
#ifdef COMMENT
else if( info->fileEof )
type = ZCRCE ;
#endif /* COMMENT */
else if( qfull )
type = ZCRCW ;
else
switch( info->Streaming ) {
case Full:
case Segmented: type = ZCRCG ; break ;
case StrWindow:
if( (info->windowCount += len) < info->windowsize/4 )
type = ZCRCG ;
else {
type = ZCRCQ ;
info->windowCount = 0 ;
}
break ;
default:
case SlidingWindow: type = ZCRCQ ; break ;
}
{
int crc32 = info->crc32 ;
register int c=0, c2, atSign=0 ;
register u_long crc ;
register u_char *ptr = info->buffer ;
crc = crc32 ? 0xffffffff : 0 ;
/* read characters from file and put into buffer until buffer is
* full or file is exhausted
*/
while( len > 0 && (c = getc(info->file)) != EOF )
{
if( !crc32 )
crc = updcrc(c, crc) ;
else
crc = UPDC32(c, crc) ;
/* zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and
* a CR following '@' be escaped. In addition, I escape '^]'
* to protect telnet, "<CR>~." to protect rlogin, and ESC for good
* measure.
*/
c2 = c & 0177 ;
if( c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 ||
c2 == 0177 || c2 == '\r' || c2 == '\n' || c2 == 033 ||
c2 == 035 || (c2 < 040 && info->escCtrl) )
{
*ptr++ = ZDLE ;
if( c == 0177 )
*ptr = ZRUB0 ;
else if( c == 0377 )
*ptr = ZRUB1 ;
else
*ptr = c^0100 ;
len -= 2 ;
}
else {
*ptr = c ;
--len ;
}
++ptr ;
atSign = c2 == '@' ;
++info->offset ;
}
/* if we've reached file end, a ZEOF header will follow. If
* there's room in the outgoing buffer for it, end the packet
* with ZCRCE and append the ZEOF header. If there isn't room,
* we'll have to do a ZCRCW
*/
if( (info->fileEof = (c == EOF)) ) {
if( qfull || (info->bufsize != 0 && len < 24) )
type = ZCRCW ;
else
type = ZCRCE ;
}
*ptr++ = ZDLE ;
if( !crc32 )
crc = updcrc(type, crc) ;
else
crc = UPDC32(type, crc) ;
*ptr++ = type ;
if( !crc32 ) {
crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
ptr = putZdle(ptr, (crc>>8)&0xff, info) ;
ptr = putZdle(ptr, crc&0xff, info) ;
}
else {
crc = ~crc ;
for(len=4; --len >= 0; crc >>= 8)
ptr = putZdle(ptr, crc&0xff, info) ;
}
len = ptr - info->buffer ;
}
ZStatus(SndByteCount, info->offset, NULL) ;
if( (err = ZXmitStr(info->buffer, len, info)) )
return err ;
#ifdef COMMENT
if( (err = ZXmitData(ZBIN, info->buffer, len, type, info)) )
return err ;
#endif /* COMMENT */
/* finally, do we want to wait after this packet? */
switch( type ) {
case ZCRCE:
info->state = SendEof ;
info->timeout = 60 ;
return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
case ZCRCW:
info->state = info->fileEof ? SendDone : SendWait ;
info->timeout = 60 ;
break ;
default:
info->state = Sending ;
info->timeout = 0 ;
break ;
}
#ifdef COMMENT
if( info->fileEof ) { /* Yes, file is done, send EOF and wait */
info->state = SendEof ;
info->timeout = 60 ;
return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}
else if( type == ZCRCW ) {
info->state = SendWait ;
info->timeout = 60 ;
}
else {
info->state = Sending ;
info->timeout = 0 ;
}
#endif /* COMMENT */
return 0 ;
}
static int
ResendEof( register ZModem *info )
{
return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}
static int
OverAndOut( ZModem *info )
{
ZXmitStr((u_char *)"OO", 2, info) ;
return ZmDone ;
}
/* YMODEM */
static u_char eotstr[1] = {EOT} ;
/* ymodem parser */
int
YsendChar( char c, register ZModem *info )
{
int err ;
if( info->canCount >= 2 ) {
ZStatus(RmtCancel, 0, NULL) ;
return ZmErrCancel ;
}
switch( info->state ) {
case YTStart: /* wait for 'G', 'C' or NAK */
switch( c ) {
case 'G': /* streaming YModem */
case 'C': /* CRC YModem */
case NAK: /* checksum YModem */
info->PacketType = c ;
return ZmDone ;
default:
return 0 ;
}
case YTFile: /* sent filename, waiting for ACK or NAK */
switch( c ) {
case NAK: /* resend */
case 'C':
case 'G':
ZStatus(DataErr, ++info->errCount, NULL) ;
return YSendFilename(info) ;
case ACK:
info->state = YTDataWait ;
default:
return 0 ;
}
case YTDataWait: /* sent filename, waiting for G,C or NAK */
switch( c ) {
case NAK:
case 'C':
case 'G':
info->chrCount = 0 ;
if( info->PacketType == 'G' ) /* send it all at once */
{
while( info->state == YTData )
if( (err = YSendData(info)) )
return err ;
return 0 ;
}
else
return YSendData(info) ;
default:
return 0 ;
}
case YTData: /* sent data, waiting for ACK or NAK */
switch( c ) {
case 'C':
case 'G': /* protocol failure, resend filename */
if( info->Protocol == YMODEM ) {
ZStatus(DataErr, ++info->errCount, NULL) ;
info->state = YTFile ;
rewind(info->file) ;
return YSendFilename(info) ;
}
/* else XModem, treat it like a NAK */
case NAK:
ZStatus(DataErr, ++info->errCount, NULL) ;
return YXmitData(info->buffer + info->bufp, info->ylen, info) ;
case ACK:
info->offset += info->ylen ;
info->bufp += info->ylen ;
info->chrCount -= info->ylen ;
ZStatus(SndByteCount, info->offset, NULL) ;
return YSendData(info) ;
default:
return 0 ;
}
case YTEOF: /* sent EOF, waiting for ACK or NAK */
switch( c ) {
case NAK:
return ZXmitStr(eotstr, 1, info) ;
case ACK:
info->state = info->Protocol == YMODEM ? YTStart : Done ;
return ZmDone ;
default:
return 0 ;
}
case YTFin: /* sent Fin, waiting for ACK or NAK */
switch( c ) {
case NAK:
return YSendFin(info) ;
case ACK:
return ZmDone ;
default:
return 0 ;
}
default:
return 0 ;
}
}
static int
YXmitData( u_char *buffer, int len, ZModem *info )
{
u_char hdr[3] ;
u_char trail[2] ;
u_long crc = 0 ;
int i, err ;
hdr[0] = len == 1024 ? STX : SOH ;
hdr[1] = info->packetCount ;
hdr[2] = ~hdr[1] ;
if( (err = ZXmitStr(hdr, 3, info)) ||
(err = ZXmitStr(buffer, len, info)) )
return err ;
if( info->PacketType == NAK ) { /* checksum */
for(i=0; i<len; ++i)
crc += buffer[i] ;
trail[0] = crc % 256 ;
return ZXmitStr(trail,1, info) ;
}
else {
for(i=0; i<len; ++i)
crc = updcrc(buffer[i], crc) ;
crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
trail[0] = crc / 256 ;
trail[1] = crc % 256 ;
return ZXmitStr(trail,2, info) ;
}
}
static int
YSendFilename( ZModem *info )
{
int i,len ;
u_char obuf[1024] ;
u_char *ptr = obuf ;
info->state = info->PacketType != 'G' ? YTFile : YTDataWait ;
info->packetCount = 0 ;
info->offset = 0 ;
i = strlen(info->rfilename) ;
memcpy(ptr, info->rfilename, i+1) ; ptr += i+1 ;
sprintf((char *)ptr, "%d %lo %o 0", info->len, info->date, info->mode);
ptr += strlen((char *)ptr) ;
*ptr++ = '\0' ;
/* pad out to 128 bytes or 1024 bytes */
i = ptr-obuf ;
len = i>128 ? 1024 : 128 ;
for(; i<len; ++i)
*ptr++ = '\0' ;
return YXmitData(obuf, len, info) ;
}
/* send next buffer of data */
static int
YSendData( ZModem *info )
{
int i ;
/* are there characters still in the read buffer? */
if( info->chrCount <= 0 ) {
info->bufp = 0 ;
info->chrCount = fread(info->buffer, 1, info->packetsize, info->file);
info->fileEof = feof(info->file) ;
}
if( info->chrCount <= 0 ) {
fclose(info->file) ;
info->state = YTEOF ;
return ZXmitStr(eotstr, 1, info) ;
}
/* pad out to 128 bytes if needed */
if( info->chrCount < 128 ) {
i = 128 - info->chrCount ;
memset(info->buffer + info->bufp + info->chrCount, 0x1a, i) ;
info->chrCount = 128 ;
}
info->ylen = info->chrCount >= 1024 ? 1024 : 128 ;
++info->packetCount ;
info->state = YTData ;
return YXmitData(info->buffer + info->bufp, info->ylen, info) ;
}
static int
YSendFin( ZModem *info )
{
u_char obuf[128] ;
info->state = YTFin ;
info->packetCount = 0 ;
memset(obuf,0,128) ;
return YXmitData(obuf, 128, info) ;
}