/*--------------------------------------------------------------------------*/ /* The receive part of the Zmodem protocol. */ /* */ /* (C) Copyright M. Jose, 1990 -> */ /* See MPMODEM.DOC for more details about the usage of this source in your */ /* programs. */ /* Written by Mark Jose, Oct-Nov, 1990. */ /*---------------------------------------------------------------------------*/ [...] #include "compress.h" /*--------------------------------------------------------------------------*/ /* Local function prototypes */ /*--------------------------------------------------------------------------*/ static int pascal ReceiveData(BYTE *,int); /* If you don't have a seperate routine to handle the receipt of */ /* packets with CRC-16, then you must do so in order to implement */ /* compression. */ /* */ /* Some people implement Zmodem without the ability to run it in */ /* CRC-16 mode because it will always default to the safer CRC-32 */ /* mode. This is fine, but when you want to use it over quite */ /* secure lines, CRC-16 is quite adequate. */ static int pascal ReceiveData16(BYTE *, int); static int pascal ReceiveData32(BYTE *, int); [...] /* RECEIVE DATA This module decides what type of packet routine we should call. The type of packet was determined when we went into Z_GetHeader (in ZMISC.C) and set the flag of RxType. RxType Transmission process -------------------------------------------------------------------- 0 Normal Zmodem transmission using CRC-16. 1 CRC-32 binary transfer. 2 (Not used - next release) 3 CRC-32 binary transfer with compression (when needed) 4 CRC-16 binary transfer with compression (when needed) */ static int pascal ReceiveData(BYTE *buf, int length) { static int stat; switch (RxType) { /* Here we have two new options, for CRC-16 and CRC-32 compressed packets. In order to use this method, you must call the routines with CompBuff (the compression buffer which MUST be the same size as your normal TX/RX buffer). When the packet is received, the frame end indicator will decide whether the packet just received is a compressed packet. If it is, then the decompression routine is called and the result is that the data in CompBuff is expanded into "buf" (which is the buffer passed to this routine). If the packet is not compressed, then a quick memcpy is done to copy it from CompBuff to "buf". */ case 4: /* Possibly a compressed CRC-16 packet */ Compressed = 0; stat = ReceiveData16(CompBuff, length); break; case 3: /* Possibly a compressed CRC-32 packet */ Compressed = 0; stat = ReceiveData32(CompBuff, length); break; default: /* Well there shouldn't be any other type! */ case 2: /* Not used */ return ZERROR; case 1: /* CRC-32 plain binary */ return (ReceiveData32(buf, length)); case 0: /* CRC-16 plain binary */ return (ReceiveData16(buf, length)); } /* The only stuff to get here is compressed packets! */ /* Now, depending on the status of the "Compressed" flag, we either decompress the data or do a straight copy. */ if (Compressed) { BlockSize = DeCompress(CompBuff, buf, BlockSize); } else { memcpy(buf, CompBuff, BlockSize); } return stat; } /* In some cases, you may have to create this routine by splitting it from the original ReceiveData() routine. */ /* RZ_ReceiveData16 Handles 16 bit CRC data packets */ static int pascal ReceiveData16(BYTE *buf, int length) { register int c; static char *endpos; static int d; static WORD crc; crc = BlockSize = 0; endpos = (char *)buf + length; while ((char *)buf <= endpos) { if ((c = GetZDLE()) & ~0xFF) { CRCError: switch (c) { /* This bit of code determines whether the transmission was done using compression. If any of the following frame ends were received, they will set the "Compressed" flag which will notify the ReceiveData() routine. */ case GOTCRCE_C: /* ---** Add this **--- */ case GOTCRCG_C: /* ---** Add this **--- */ case GOTCRCQ_C: /* ---** Add this **--- */ case GOTCRCW_C: /* ---** Add this **--- */ Compressed = 1; /* ---** Add this **--- */ /* Fall through to normal process of CRC */ case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: /* CRCs */ d = c; crc = Z_UpdateCRC( (c & 0xFF), crc); if ((c = GetZDLE()) & ~0xFF) goto CRCError; crc = Z_UpdateCRC(c, crc); if ((c = GetZDLE()) & ~0xFF) goto CRCError; crc = Z_UpdateCRC(c, crc); if (crc & 0xFFFF) { /* Report a CRC error to main routine */ return ZERROR; } BlockSize = length - (int ) (endpos - (char *) buf); return d; case GOTCAN: /* Cancel */ return ZCAN; case ZTIMEOUT: /* Timeout */ return c; case RCDO: /* No carrier */ return c; default: /* What happened? */ return c; } } *buf++ = (BYTE ) c; crc = Z_UpdateCRC(c, crc); } return ZERROR; } /* ReceiveData32 Handles 32 bit CRC data packets */ static int pascal ReceiveData32(BYTE *buf, int length) { register int c, n; static int d; static char *endpos; static ULONG crc; BlockSize = 0; crc = 0xFFFFFFFFL; endpos = (char *)buf + length; while ((char *)buf <= endpos) { if ((c = GetZDLE()) & ~0xFF) { CRCError: switch (c) { /* This bit of code determines whether the transmission was done using compression. If any of the following frame ends were received, they will set the "Compressed" flag which will notify the ReceiveData() routine. */ case GOTCRCE_C: /* ---** Add this **--- */ case GOTCRCG_C: /* ---** Add this **--- */ case GOTCRCQ_C: /* ---** Add this **--- */ case GOTCRCW_C: /* ---** Add this **--- */ Compressed = 1; /* ---** Add this **--- */ /* Fall through to normal process of CRC */ case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: d = c; c &= 0377; crc = Z_UpdateCRC32(c, crc); for (n = 0; n < 4; n++) { if ((c = GetZDLE()) & ~0xFF) goto CRCError; crc = Z_UpdateCRC32(c, crc); } if (crc != 0xDEBB20E3) { /* Report a CRC error to main routine */ return ZERROR; } BlockSize = length - (int) (endpos - (char *)buf); return(d); case GOTCAN: return ZCAN; case ZTIMEOUT: return c; case RCDO: return c; default: return(c); } } *buf++ = (BYTE )c; crc = Z_UpdateCRC32(c, crc); } return(ZERROR); } /* INIT RECEIVER Initialize for Zmodem receive. */ static int pascal InitReceiver() { static int n, c, errors, cmdzack1flg; errors = cmdzack1flg = 0; modem_msg("Initializing file transfer."); for (n = 12; --n >= 0; ) { /* 12 attempts to get it started. */ [...] /*--------------------------------------------------------------*/ /* Set buffer length (0=unlimited, don't wait). */ /* Also set capability flags */ /*--------------------------------------------------------------*/ PutLongIntoHeader(0L); Txhdr[ZF0] = CANFDX|CANOVIO|CANBRK; /* Set fd, overlay IO & break */ if (WantCRC32) /* Have we overridden default on the command line? */ TxHdr[ZF0] |= CANFC32; if (ZCtlEsc) Txhdr[ZF0] |= TESCCTL; /* The following will tell the sender that we can and will accept a transfer using MpModem's fast method. But in order to tell the sender, you must set TryFast to non-0. You can accomplish this by placing an option in your command line routine for fast mode. MpModem uses the "-f" switch to make TryFast = 1. The default is TryFast = 0. */ if (TryFast) Txhdr[ZF1] |= CANFAST; /* The following tells the sender that we can accept a transfer with large blocks up to 4096 bytes long (as against the standard Zmodem length of 1024 bytes. To enable the "TryBig" switch, you must have set the switch "-l" on the command line. The default is TryBig = 0; */ if (TryBig) Txhdr[ZF1] |= CANBIG; /* The following will tell the sender that we can and will accept a transfer using MpModem's compression. The compression will ONLY be used when the resultant packet is smaller than the uncompressed packet. Before being able to tell the sender that we can use compression, you must set TryComp to a non- 0 number. In MpModem I use the switch "-c" on the command line to make TryComp = 1 and thus enable compression (when needed). The default value is TryComp = 0. */ if (TryComp) Txhdr[ZF1] |= CANCOMP; SendHexHeader(HdrType, Txhdr); if (HdrType == ZSKIP) HdrType = ZRINIT; Again: c = GetHeader(Rxhdr); Z_Frame(c); switch (c) { case ZRQINIT: case ZEOF: case ZTIMEOUT: continue; case ZFILE: /*-----------------------------------------*/ /* File name received */ /*-----------------------------------------*/ ZConv = Rxhdr[ZF0]; ZManage = Rxhdr[ZF1]; ZTrans = Rxhdr[ZF2]; /* Here we see what the Sender says about its ability to send using the fast method. If it can send using fast mode, then we can enable fast mode and we will both be happy. */ if (TryFast) UsingFast = ((Rxhdr[ZF3] & CANTURBO) != 0); /* Here we check to see what the sender thinks about sending large packets. If he says yes by including the mask CANBIG, then we are all set for the transfer of packets up to 4096 bytes. */ if (TryBig) WantBig = ((Rxhdr[ZF3] & CANBIG) != 0); else WantBig = 0; /* Here we see if the remote (sender) can do my MpModem's version of compression. Providing we wish to receive compression (ie, we have enable TryComp - see above), then we check to see the sender's response in Rxhdr and act accordingly. */ if (TryComp) { WantComp = ((Rxhdr[ZF3] & CANCOMP) != 0); } else WantComp = 0; if (WantComp) Warning("Compression will be attempted."); /* --- */ Tryzhdrtype = ZRINIT; c = RZ_ReceiveData(TxRxBuff,MAXPKTSIZE); ShowBlock(&BlockSize); /* We must cater for the new frames with the type of GOTCRCW_C */ if (c == GOTCRCW || c == GOTCRCW_C) /* ---** Add this **--- */ return ZFILE; SendHexHeader(ZNAK, Txhdr); goto Again; case ZSINIT: /* Send INIT sequence */ ZCtlEsc = TESCCTL & Rxhdr[ZF0]; c = ReceiveData(Attn, ZATTLEN); /* We must cater for the new frames with the type of GOTCRCW_C */ if (c == GOTCRCW || c == GOTCRCW_C) { /* ---** Add this **--- */ PutLongIntoHeader(1L); SendHexHeader(ZACK, Txhdr); } else SendHexHeader(ZNAK, Txhdr); goto Again; case ZFREECNT: /* Request for free chars on disk. */ modem_msg("Remote requested total disk space available."); PutLongIntoHeader(freespace(DriveNo)); SendHexHeader(ZACK, Txhdr); goto Again; case ZCOMMAND: command_flag = Rxhdr[ZF0]; c = ReceiveData(TxRxBuff,MAXPKTSIZE); /* We must cater for the new frames with the type of GOTCRCW_C */ if (c == GOTCRCW || c == GOTCRCW_C) { /* ---** Add this **--- */ [...] return ZCOMPL; } else SendHexHeader(ZNAK, Txhdr); goto Again; case ZCOMPL: /* Request is complete */ goto Again; default: continue; case ZFIN: /* Finished Session */ GoodBye(); return ZCOMPL; /* Request is complete */ case RCDO: case ZCAN: return c; /* Could also be ZERROR */ } } return ZERROR; } /* RECEIVE FILE Receive one file; assumes file name frame is preloaded in TxRxBuff */ static int pascal ReceiveFile() { static int c; int n; char *sptr; EOFSeen=FALSE; c = GetHeader(); if (c == ZERROR || c == ZSKIP) return (HdrType = ZSKIP); c = ZOK; /* Good to start off this way */ n = 20; Rxbytes = StartPos; Rxpos = StartPos; zcps = 0; while (1) { if (localabort) /* Break hit! */ return ZERROR; PutLongIntoHeader(Rxbytes); SendHexHeader(StartPkt, Txhdr); /* Could be ZRPOS or ZCRC */ NxtHdr: if (dropcar) return ZERROR; if (localabort) return ZCAN; c = GetHeader(Rxhdr); Z_Frame(c); switch (c) { [...] case ZFILE: /* Sender didn't see our ZRPOS yet ??? */ ReceiveData(TxRxBuff, MAXPKTSIZE); continue; case ZEOF: /* End of the file */ if (Rxpos != Rxbytes) goto NxtHdr; return c; [...] case ZDATA: /* Data Packet */ if (Rxpos != Rxbytes) { if ( --n < 0) { /* Too much garbage, give up! */ return ZERROR; } PutString(Attn); continue; /* Go & get another header */ } /* This is the main loop once the ZDATA packet has been receieved */ MoreData: if (localabort) return ZCAN; if (dropcar) return ZERROR; c = ReceiveData(TxRxBuff,MAXPKTSIZE); ShowBlock(&BlockSize); switch (c) { case ZCAN: /* CANcelled or lost Carrier */ case RCDO: goto Err; case ZERROR: /* CRC Error */ if (--n < 0) { /* Too many errors */ return ZERROR; } ClearOutbound(); ClearInbound(); PutString(Attn); continue; case ZTIMEOUT: if (--n < 0) { return ZERROR; } continue; case GOTCRCW_C: /* ---** Add this **--- */ case GOTCRCW: /* End of frame */ if (SaveToDisk(&Rxbytes) == ZERROR) return (ZERROR); [...] goto NxtHdr; case GOTCRCQ_C: /* ---** Add this **--- */ case GOTCRCQ: /* Zack expected */ if (SaveToDisk(&Rxbytes) == ZERROR) return ZERROR; [...] goto MoreData; case GOTCRCG_C: /* ---** Add this **--- */ case GOTCRCG: /* Non-stop */ if (SaveToDisk(&Rxbytes) == ZERROR) return ZERROR; goto MoreData; case GOTCRCE_C: /* ---** Add this **--- */ case GOTCRCE: /* Header to follow */ if (SaveToDisk(&Rxbytes) == ZERROR) return ZERROR; goto NxtHdr; } } } return ZERROR; }