/*---------------------------------------------------------------------------*/ /* The send 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" [...] /* Private routines */ [...] static int pascal SendData(register BYTE *, register int, int); /* If you don't have a separate routine to handle crc-16 transfers, then you will have to create one. 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 void pascal SendData16(register BYTE *, register int, int); static void pascal SendData32(register BYTE *, register int, int); [...] /* SEND BINARY HEADER Send ZMODEM binary header hdr of type type */ static int pascal SendBinaryHeader(int type, BYTE *hdr) { static int n; BufferByte(ZPAD); BufferByte(ZDLE); /* TxType is the variable that determines the type of transfer. This variable can change during a file transfer (for hex headers) and must be re-instated here or else we will have a mix up in the type of transfer mode being used. TxMaster is determined in the negotiation phase of the transfer. For a better understanding of its values see that routine (called GetReceiver().) */ switch(TxType = TxMaster) { /* If the TxType is 4 or 3 then the transmission will perhaps involve compressed data. We must signal the receiver that this is our intention. We do this by sending either a ZBINC or ZBINC32 frame header. */ case 4: SendBinaryHeader16(hdr, type, ZBINC); break; case 3: SendBinaryHeader32(hdr, type, ZBINC32); break; /* --- */ case 2: default: return -1; case 1: SendBinaryHeader32(hdr, type, ZBIN32); break; case 0: SendBinaryHeader16(hdr, type, ZBIN); break; } n = UnbufferBytes(); return n; } /* Send binary header with standard CRC-16 */ static void pascal SendBinaryHeader16(BYTE *hdr, int type, int flavour) { static unsigned short crc; register int n; BufferByte(flavour); ZS_SendByte(type); crc = Z_UpdateCRC(type, 0); for (n = 4; --n >= 0;) { ZS_SendByte(*hdr); crc = Z_UpdateCRC(((unsigned short)(*hdr++ & 0xFF)), crc); } ZS_SendByte((BYTE ) (crc >> 8)); ZS_SendByte((BYTE ) crc); } /* Send binary header with CRC-32 */ static void pascal SendBinaryHeader32(BYTE *hdr, int type, int flavour) { register int n; static ULONG crc; BufferByte(flavour); ZS_SendByte(type); crc = Z_UpdateCRC32(type, 0xFFFFFFFFL); for (n = 4; --n >= 0; ) { ZS_SendByte(*hdr); crc = Z_UpdateCRC32((0xFF & *hdr++), crc); } crc = ~crc; for (n = 4; --n >= 0;) { ZS_SendByte((int )crc); crc >>= 8; } } /* Send binary data in buf (or Compbuff if compressed) */ static int pascal SendData(register BYTE *buf, register int length, int frameend) { static int n; static int comp_lgh; /* This routine determines which routine we should branch off to depending upon the type of CRC checking that was negotiated at startup. If the transmission allows for compressed data, then the TxType will be either 4 or 3. In which case, we will attempt to compress the data into a block smaller than the "length" passed to us. If this is not possible then we will send the packet normally. What we do below is determine if the compressed length is smaller than the length of the uncompressed block. If it is, we flag the transmission as a "Compressed" transmission and increment the frame-end by 8. For example: ZCRCE => 'h' => 104 + 8 = 112 => 'p' => ZCRCE_C ZCRCG => 'i' => 105 + 8 = 113 => 'q' => ZCRCG_C ZCRCQ => 'j' => 106 + 8 = 114 => 'r' => ZCRCQ_C ZCRCW => 'k' => 107 + 8 = 115 => 's' => ZCRCW_C This then makes the current frame a compressed frame as well as allowing us to keep its original status. */ if (TxType == 3 || TxType == 4) { comp_lgh = Compress(buf, CompBuff, length); if (comp_lgh < length) { Compressed = 1; frameend += 8; /* Bumps up the frame end to indicate compression */ } else { Compressed = 0; comp_lgh = length; } } switch(TxType) { /* Right, now we process the compressed or uncompressed buffer. If the data was able to be compressed into less space than the uncompressed buffer, then the "Compressed" flag would have been set to 1 above. In this case, the data we need to send is contained in "CompBuff". If the data could not be compressed then "Compressed" would be set to 0 and we will send the data from the "buf" buffer. Simple, hey?! */ case 4: SendData16(Compressed ? CompBuff : buf, comp_lgh, frameend); break; case 3: SendData32(Compressed ? CompBuff : buf, comp_lgh, frameend); break; /* --- */ case 2: default: return ZERROR; case 1: SendData32(buf, length, frameend); break; default: SendData16(buf, length, frameend); break; } n = UnbufferBytes(); [...] return n; } /* SEND DATA with 16 bit CRC (and compression if necessary). */ static void pascal SendData16(register BYTE *buf, register int length, int frameend) { static WORD crc; crc = 0; for (;--length >= 0;) { SendByte(*buf); crc = UpdateCRC(((WORD )(0xFF & *buf++)), crc); } BufferByte(ZDLE); BufferByte(frameend); crc = UpdateCRC(frameend, crc); SendByte(crc >> 8); SendByte(crc); } /* SEND DATA with 32 bit CRC (and compressed if necessary) */ static void pascal SendData32(register BYTE *buf, register int length, int frameend) { static int c; static ULONG crc; crc = 0xFFFFFFFFL; for (;--length >= 0;) { c = *buf & 0xFF; if ((c & 0140)) { BufferByte((lastsent = c)); } else { SendByte(c); } crc = UpdateCRC32(( (WORD )(*buf++) ), crc); } BufferByte(ZDLE); BufferByte(frameend); crc = UpdateCRC32(frameend, crc); crc = ~crc; for (length = 4; --length >= 0;) { SendByte((int )crc); crc >>= 8; } } /* Send character c with ZMODEM escape sequence encoding. Don't encode special characters if FAST mode is being used. */ static void pascal SendByte(BYTE ch) { /* Here is the core routine for sending data to the remote without any form of escape encoding other than for the Zmodem required ZDLE. This means the transmission can be faster if (for example) you have an 8 bit connect and no need for software transmission control. */ if (UsingFast) { if ((ch & 0xFF) == ZDLE) { BufferByte(ZDLE); lastsent = (ZDLE ^ 0x40); } else lastsent = ch & 0xFF; BufferByte(lastsent); return; } if ((ch & 0x60)) { lastsent = ch; } else { switch (ch & 0x7F) { case ZDLE: case 16: case 17: case 19: BufferByte(ZDLE); lastsent = (ch ^ 0x40); break; case 13: if (ZCtlEsc && ((lastsent & 0x7f) != '@')) lastsent = ch; else { BufferByte(ZDLE); lastsent = (ch ^ 0x40); } break; default: if (ZCtlEsc && (ch & 0x60) <= 0) { BufferByte(ZDLE); lastsent = ch ^ 0x40; } else lastsent = ch; } } BufferByte(lastsent); } /* Get the receiver's init parameters */ static int pascal GetReceiverInfo() { int n, c; modem_msg("Waiting to send file(s)."); for (n = 10; --n >= 0;) { if (localabort) return ZCAN; if (dropcar) return RCDO; c = GetHeader(Rxhdr); switch (c) { case ZCHALLENGE: /* Echo receiver's challenge number */ PutLongIntoHeader(Rxpos); SendHexHeader(ZACK, Txhdr); continue; case ZCOMMAND: /* They didn't see our ZRQINIT */ PutLongIntoHeader(0L); /* Put 128 in here */ SendHexHeader(ZRQINIT, Txhdr); continue; case ZRINIT: Rxflags = (Rxhdr[ZF0] & 0xFF); /* Check to see if the receiver can receive in Fast mode. If we can mask off to a positive value then we want to TRY the transfer in fast mode. Of course, it all depends on whether we want to send in Fast mode, doesn't it?! */ WantFast = ((Rxhdr[ZF1] & CANFAST) != 0); /* Check to see if the receiver can send large blocks. */ WantBig = ((Rxhdr[ZF1] & CANBIG) != 0); /* Check to see if the receiver wants to try compression when possible. */ WantComp = ((Rxhdr[ZF1] & CANCOMP) != 0); /* Here is where we set up the TxMaster flag. In its bare-bones configuration it will be either 0 (for a transmission using CRC-16) or 1 (for a transmission using CRC-32). WantCRC32 is defined on the command line. Its default is true (ie, we want to send with a CRC of 32 bit). */ TxMaster = ((Rxflags & CANFC32) && WantCRC32); [...] LzTrans = (char )0; return (SendInit()); case RCDO: case ZCAN: return c; case ZTIMEOUT: PutString("rz\r"); /* Sends out rz\r to get remote going */ SendHexHeader(ZRQINIT, Txhdr); continue; /* Maximum of 10 timeouts allowed */ case ZRQINIT: if (Rxhdr[ZF0] == ZCOMMAND) continue; default: Z_SendHexHeader(ZNAK, Txhdr); continue; } } return ZERROR; } /* Send ZFILE frame and begin sending ZDATA frame */ static int pascal SendFile(int blen) { static int ch; long lastcrcrq = -1; static ULONG crc; int errors; [...] for (errors = 0; ++errors < 20;) { if (localabort) return ZCAN; if (dropcar) return ZERROR; Txhdr[ZF0] = LzConv; /* Default is to resume with Binary */ Txhdr[ZF1] = LzManage; /* Default file management mode */ [...] /* Here we send off whether we want to send Fast or not. Now this factor is determined by the GetReceiver() routine and a switch off the command line that when activated sets TryFast to 1. In the GetReceiver routine, we determine whether the receiver wants to try a fast transfer (WantFast = 1). If both of these conditions are met then the stage is set for a transmission of data without escape encoding of useless bytes. We duly set the "UsingFast" flag depending upon whether the receiver and sender want to transmit with fast mode active. This "UsingFast" flag is then used by the actual send routine to transmit without escape encoding. */ if (TryFast && WantFast) { Txhdr[ZF3] |= CANFAST; UsingFast = 1; } else UsingFast = 0; /* Now we need to send confirmation to the receiver that we can also send big packets. We do this ONLY if the switch "-l" was part of the command line (sets TryBig to TRUE) and if the receiver has already signalled its intention to try to send big packets (WantBig = TRUE). When the "-l" switch is detected on the command line, you should set the MaxPktSize (the maximum number of bytes to be passed to the SendData() routines) to 4096. This MaxPktSize should serve also to allocate enough memory to hold the new buffer of 4096 bytes. You'll get major problems if you leave your buffer at 1024 bytes and then try to receive a packet of 4096 bytes into it!! If the receiver does not want (or can't) send large blocks, then we will set the flag "UsingBig" to 0 and reset the MaxPktSize to the default standard used by all Zmodems; 1024 bytes. */ if (TryBig && WantBig) { Txhdr[ZF3] |= CANBIG; /* Confirm to RX that we want big blocks */ UsingBig = 1; } else { MaxPktSize = 1024; /* Reinstate original block size */ UsingBig = 0; } /* BlockSize is a variable I use to determine the CURRENT blocksize. This means that the starting packet will contain 4096 bytes (if UsingBig is set to 1) or else we send the standard 1024 bytes to the send routine. BE AWARE that even though you send 1024 or 4096 or whatever bytes to the send routine, there may be many more bytes escaped (ZDLE ZDLE) making the ACTUAL number transmitted many more */ BlockSize = MaxPktSize; /* Now we must determine whether we want to send in compressed mode (when applicable). To do this we must have had the "TryComp" switch set off the command line. MpModem does this when it is invoked with the "-c" switch. The "WantComp" flag is determined by the status of the receiver. The receiver will cause the sender to set this flag if the receiver wants a transmission which could include compression. In order for this to happen the receiver would (just like the sender) have to be invoked with the "-c" switch from the command line. Once we are satisfied that both of us want to send/receive in compressed mode, we set the TxMaster accordingly. If the existing TxMaster is 0 (a transmission using CRC-16), then we set it to 4 which means a transmission using CRC-16 PLUS compression. If the existing TxMaster is 1 (a transmission using CRC-32), then we set it to 3 which means a transmission using CRC-16 plus compression. I am sorry that it is arse/ass-about, but I had a brain drain when I conjured it up! You can, of course, change these values to whatever you wish. */ if (TryComp && WantComp) { Txhdr[ZF3] |= CANCOMP; if (TxMaster == 0) /* They want crc-16 */ TxMaster = 4; /* Transfer compression with 16 bit CRC */ else TxMaster = 3; /* Transfer compression with 32 bit CRC */ } (void )SendBinaryHeader(ZFILE, Txhdr); (void )SendData(TxRxBuff, blen, ZCRCW); /* Leave this alone! */ ShowBlock(&blen); Again: if (dropcar) return ZERROR; if (localabort) return ZCAN; ch = GetHeader(Rxhdr); switch (ch) { case ZRINIT: while ((ch = GetByte(50)) > 0) if (ch == ZPAD) goto Again; case ZERROR: case ZNAK: default: continue; [...] } } return ZERROR; } /*--------------------------------------------------------------------------*/ /* SEND FILE DATA */ /* Send the data in the file */ /*--------------------------------------------------------------------------*/ static int pascal SendFileData(void) { static int Packet, Items, ch; WORD newctr = 0; int too_much_rubbish = 0; /* Counts garbage chars received by TX */ int OKblocks = 0; modem_msg("Sending file."); SomeMore: if (input_char_ready()) { WaitForAck: if (dropcar) return ZERROR; if (localabort) return ZCAN; too_much_rubbish = 0; ch = SyncWithReceiver(0); /* changes to 0 */ gotack: if (localabort) return ZCAN; if (dropcar) return RCDO; switch (ch) { case RCDO: case ZSKIP: /* Skip this file */ return ch; case ZACK: case ZRPOS: /* Resume at this position */ OKblocks = 0; break; [...] } /* end switch case */ [...] } /* end of if */ (void )SendBinaryHeader(ZDATA, Txhdr); do { Items = fread(TxRxBuff,1,BlockSize,fptr); if (Items < BlockSize) EOFSeen = 1; ShowBlock(&Items); /* !!!NOTE!!! Do not change the following frame end values - allow the actual send routines to determine whether the packet is a compressed or not! (Frankly it is impossible from here, but just in case somebody thinks they can cut corners!) */ if (EOFSeen) Packet = ZCRCE; else if (too_much_rubbish > 3) Packet = ZCRCW; else if (bytecnt == LastSync) Packet = ZCRCW; else if (Rxbuflen && ((newctr -= Items) <= 0)) Packet = ZCRCW; else Packet = ZCRCG; SendData(TxRxBuff, Items, Packet); [...] /* Here we check to see if the block size has been cut in half because of repositioning (things like line noise for example), and we have since received 4 good blocks. If this has occurred, we then double the blocksize - attempting to return to the original block size before the reposition occurred. */ if ((BlockSize < MaxPktSize) && /* Indicates we've had an error */ (++OKblocks > 4)) { BlockSize *= 2; /* Double current block size */ if (BlockSize > MaxPktSize) /* Make sure it doesn't exceed the */ BlockSize = MaxPktSize; /* maximum packet size. */ OKblocks = 0; } if (Packet == ZCRCW) goto WaitForAck; } /* Get back in sync with the receiver. */ static int pascal SyncWithReceiver(flag) { int ch; while (1) { ch = GetHeader(Rxhdr); switch (ch) { case ZTIMEOUT: /* This used to return Zerror which causes the */ /* program to stop there and then */ if (++ctr < 10) /* Allow for 10 timeouts max. */ continue; /* Fall through as an error */ case ZABORT: case ZFIN: return ZERROR; case ZRPOS: EOFSeen = 0; [...] /* Here we cut the block in half. We have received an error and when sending blocks of 4096, the amount of time lost repositioning is a pain. So we cut down the block size (say to 2048) and try again. If we get another error, we'll cut it in half again and so on. You should have some other mechanism to cut blocks in half if there are more than a few errors. If you do have this mechanism as well as the code below, you'll have to use a flag so that you don't cut the block in half twice. (You'll realise what I mean when you code the thing. :-> ) */ if (UseBig && (BlockSize > 32)) BlockSize /= 2; clearerr(fptr); /* Clear the EOF we might have got */ if (fseek(fptr, Rxpos, 0)) { /* Report seeking error */ return ZERROR; } /* Report positioning of file back to RXPOS */ /* Fall through */ case RCDO: case ZCAN: case ZSKIP: case ZRINIT: return ch; case ZACK: if (flag || (Txpos == Rxpos)) return ZACK; continue; [...] } } } }