jcs
/subtext
/amendments
/182
zmodem: Add BSD-licensed ZMODEM implementation from TeraTerm
Lots of internal restructuring by me to keep all state within a
zmodem_session object with callbacks to network/modem functions to
actually do the data sending and receiving.
jcs made amendment 182 over 2 years ago
--- zmodem.c Mon Jul 4 23:44:50 2022
+++ zmodem.c Mon Jul 4 23:44:50 2022
@@ -0,0 +1,1534 @@
+/*
+ * Copyright (c) 2022 joshua stein <jcs@jcs.org>
+ *
+ * Copyright (C) 1994-1998 T. Teranishi
+ * (C) 2007- TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#if THINK_C
+#include "util.h"
+#else
+#include <stdbool.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#if THINK_C
+#include <unix.h>
+#endif
+
+#include "zmodem.h"
+#include "crc.h"
+
+#ifdef ZMODEM_DEBUG
+#include "logger.h"
+extern struct logger *logger;
+#endif
+
+#define LOBYTE(w) ((unsigned char)(((unsigned short)(w)) & 0xff))
+#define HIBYTE(w) ((unsigned char)((((unsigned short)(w)) >> 8) & 0xff))
+#define LOWORD(l) ((unsigned short)(l))
+#define HIWORD(l) ((unsigned short)(((unsigned long)(l) >> 16) & 0xFFFF))
+
+#define Z_RecvInit 1
+#define Z_RecvInit2 2
+#define Z_RecvData 3
+#define Z_RecvFIN 4
+#define Z_SendInit 5
+#define Z_SendInitHdr 6
+#define Z_SendInitDat 7
+#define Z_SendFileHdr 8
+#define Z_SendFileDat 9
+#define Z_SendDataHdr 10
+#define Z_SendDataDat 11
+#define Z_SendDataDat2 12
+#define Z_SendEOF 13
+#define Z_SendFIN 14
+#define Z_SendSkip 15
+#define Z_Cancel 16
+#define Z_End 17
+
+#define Z_PktGetPAD 1
+#define Z_PktGetDLE 2
+#define Z_PktHdrFrm 3
+#define Z_PktGetBin 4
+#define Z_PktGetHex 5
+#define Z_PktGetHexEOL 6
+#define Z_PktGetData 7
+#define Z_PktGetCRC 8
+
+#define ZPAD '*'
+#define ZDLE 0x18
+#define ZDLEE 0x58
+#define ZBIN 'A'
+#define ZHEX 'B'
+#define ZBIN32 'C'
+
+#define ZRQINIT 0
+#define ZRINIT 1
+#define ZSINIT 2
+#define ZACK 3
+#define ZFILE 4
+#define ZSKIP 5
+#define ZNAK 6
+#define ZABORT 7
+#define ZFIN 8
+#define ZRPOS 9
+#define ZDATA 10
+#define ZEOF 11
+#define ZFERR 12
+#define ZCRC 13
+#define ZCHALLENGE 14
+#define ZCOMPL 15
+#define ZCAN 16
+#define ZFREECNT 17
+#define ZCOMMAND 18
+#define ZSTDERR 19
+
+#define ZCRCE 'h'
+#define ZCRCG 'i'
+#define ZCRCQ 'j'
+#define ZCRCW 'k'
+#define ZRUB0 'l'
+#define ZRUB1 'm'
+
+#define ZF0 3
+#define ZF1 2
+#define ZF2 1
+#define ZF3 0
+#define ZP0 0
+#define ZP1 1
+#define ZP2 2
+#define ZP3 3
+
+#define CANFDX 0x01
+#define CANOVIO 0x02
+#define CANBRK 0x04
+#define CANCRY 0x08
+#define CANLZW 0x10
+#define CANFC32 0x20
+#define ESCCTL 0x40
+#define ESC8 0x80
+
+#define ZCBIN 1
+#define ZCNL 2
+
+#define XON 0x11
+
+short ZRead1Byte(struct zmodem_session *, unsigned char *);
+void ZPutHex(struct zmodem_session *, short *, unsigned char);
+void ZResetTimeout(struct zmodem_session *, short);
+void ZShHdr(struct zmodem_session *, unsigned char);
+void ZPutBin(struct zmodem_session *, short *, unsigned char);
+void ZSbHdr(struct zmodem_session *, unsigned char);
+void ZStoHdr(struct zmodem_session *, unsigned long);
+long ZRclHdr(struct zmodem_session *);
+void ZSendRInit(struct zmodem_session *);
+void ZSendRQInit(struct zmodem_session *);
+void ZSendRPOS(struct zmodem_session *);
+void ZSendACK(struct zmodem_session *);
+void ZSendNAK(struct zmodem_session *);
+void ZSendEOF(struct zmodem_session *);
+void ZSendFIN(struct zmodem_session *);
+void ZSendSkip(struct zmodem_session *);
+void ZSendCancel(struct zmodem_session *);
+void ZSendInitHdr(struct zmodem_session *);
+void ZSendInitDat(struct zmodem_session *);
+void ZSendFileHdr(struct zmodem_session *);
+void ZSendFileDat(struct zmodem_session *);
+void ZSendDataHdr(struct zmodem_session *);
+void ZSendDataDat(struct zmodem_session *);
+bool ZCheckHdr(struct zmodem_session *);
+void ZParseRInit(struct zmodem_session *);
+bool ZParseSInit(struct zmodem_session *);
+void ZParseHdr(struct zmodem_session *);
+bool FTCreateFile(struct zmodem_session *);
+bool ZParseFile(struct zmodem_session *);
+bool ZWriteData(struct zmodem_session *);
+void ZCheckData(struct zmodem_session *);
+
+#ifdef ZMODEM_DEBUG
+
+#define LOGBUFSIZE 256
+static char recvbuf[LOGBUFSIZE];
+static char sendbuf[LOGBUFSIZE];
+
+static const char *
+hdrtype_name(short type)
+{
+ static const char *s[] = {
+ "ZRQINIT",
+ "ZRINIT",
+ "ZSINIT",
+ "ZACK",
+ "ZFILE",
+ "ZSKIP",
+ "ZNAK",
+ "ZABORT",
+ "ZFIN",
+ "ZRPOS",
+ "ZDATA",
+ "ZEOF",
+ "ZFERR",
+ "ZCRC",
+ "ZCHALLENGE",
+ "ZCOMPL",
+ "ZCAN",
+ "ZFREECNT",
+ "ZCOMMAND",
+ "ZSTDERR",
+ };
+
+ if (type >= ZRQINIT && type <= ZSTDERR)
+ return (s[type]);
+ else
+ return NULL;
+}
+
+static const char *
+state_name(struct zmodem_session *zs)
+{
+ static const char *s[] = {
+ "",
+ "Z_RecvInit",
+ "Z_RecvInit2",
+ "Z_RecvData",
+ "Z_RecvFIN",
+ "Z_SendInit",
+ "Z_SendInitHdr",
+ "Z_SendInitDat",
+ "Z_SendFileHdr",
+ "Z_SendFileDat",
+ "Z_SendDataHdr",
+ "Z_SendDataDat",
+ "Z_SendDataDat2",
+ "Z_SendEOF",
+ "Z_SendFIN",
+ "Z_SendSkip",
+ "Z_Cancel",
+ "Z_End",
+ };
+
+ if (zs->ZState >= Z_RecvInit && zs->ZState <= Z_End)
+ return (s[zs->ZState]);
+ else
+ return NULL;
+}
+
+static void
+add_recvbuf(struct zmodem_session *zs, char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, arg);
+ strlcat(recvbuf, buf, sizeof(recvbuf));
+ va_end(arg);
+
+ logger_printf(logger, "recvbuf: %s (state %s)", recvbuf, state_name(zs));
+ recvbuf[0] = '\0';
+}
+
+static void
+add_sendbuf(struct zmodem_session *zs, char *fmt, ...)
+{
+ va_list arg;
+ char buf[128];
+
+ va_start(arg, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, arg);
+ strlcat(sendbuf, buf, sizeof(sendbuf));
+ va_end(arg);
+
+ logger_printf(logger, "recvbuf: %s (state %s)", recvbuf, state_name(zs));
+ sendbuf[0] = '\0';
+}
+
+#endif /* ZMODEM_DEBUG */
+
+struct zmodem_session *
+ZCreateSender(FILE *fp)
+{
+ struct zmodem_session *zs;
+
+ if (!fp)
+ return NULL;
+
+ zs = malloc(sizeof(struct zmodem_session));
+ if (zs == NULL) {
+ return NULL;
+ }
+ memset(zs, 0, sizeof(*zs));
+
+ zs->file = fp;
+ fseek(zs->file, 0, SEEK_END);
+ zs->file_size = ftell(zs->file);
+ zs->BinFlag = true;
+
+ zs->ZMode = IdZAutoS;
+
+ return zs;
+}
+
+struct zmodem_session *
+ZCreateReceiver(void)
+{
+ struct zmodem_session *zs;
+
+ zs = malloc(sizeof(struct zmodem_session));
+ if (zs == NULL) {
+ return NULL;
+ }
+ memset(zs, 0, sizeof(*zs));
+
+ zs->ZMode = IdZAutoR;
+
+ return zs;
+}
+
+short
+ZRead1Byte(struct zmodem_session *zs, unsigned char *b)
+{
+ if (zs->recv(zs->cookie, b) == 0)
+ return 0;
+
+ /* ignore 0x11, 0x13, 0x81 and 0x83 */
+ if (((*b & 0x7F) == 0x11) || ((*b & 0x7F) == 0x13))
+ return 0;
+
+ return 1;
+}
+
+void
+ZPutHex(struct zmodem_session *zs, short *i, unsigned char b)
+{
+ if (b <= 0x9f)
+ zs->PktOut[*i] = (b >> 4) + 0x30;
+ else
+ zs->PktOut[*i] = (b >> 4) + 0x57;
+
+ (*i)++;
+
+ if ((b & 0x0F) <= 0x09)
+ zs->PktOut[*i] = (b & 0x0F) + 0x30;
+ else
+ zs->PktOut[*i] = (b & 0x0F) + 0x57;
+
+ (*i)++;
+}
+
+void
+ZResetTimeout(struct zmodem_session *zs, short seconds)
+{
+ zs->TimeOutAt = time(NULL) + seconds;
+}
+
+bool
+ZHaveTimedOut(struct zmodem_session *zs)
+{
+ if (zs->TimeOutAt && time(NULL) >= zs->TimeOutAt)
+ return true;
+
+ return false;
+}
+
+void
+ZShHdr(struct zmodem_session *zs, unsigned char HdrType)
+{
+ short i;
+
+ zs->PktOut[0] = ZPAD;
+ zs->PktOut[1] = ZPAD;
+ zs->PktOut[2] = ZDLE;
+ zs->PktOut[3] = ZHEX;
+ zs->PktOutCount = 4;
+ ZPutHex(zs, &(zs->PktOutCount), HdrType);
+ zs->CRC = UpdateCRC(HdrType, 0);
+ for (i = 0; i <= 3; i++) {
+ ZPutHex(zs, &(zs->PktOutCount), zs->TxHdr[i]);
+ zs->CRC = UpdateCRC(zs->TxHdr[i], zs->CRC);
+ }
+ ZPutHex(zs, &(zs->PktOutCount), HIBYTE(zs->CRC));
+ ZPutHex(zs, &(zs->PktOutCount), LOBYTE(zs->CRC));
+ zs->PktOut[zs->PktOutCount] = '\r'; //0x8D;
+ zs->PktOutCount++;
+ zs->PktOut[zs->PktOutCount] = '\n'; //0x8A;
+ zs->PktOutCount++;
+
+ if ((HdrType != ZFIN) && (HdrType != ZACK)) {
+ zs->PktOut[zs->PktOutCount] = XON;
+ zs->PktOutCount++;
+ }
+
+ zs->PktOutPtr = 0;
+ zs->Sending = true;
+
+#ifdef ZMODEM_DEBUG
+ add_sendbuf(zs, "ZShHdr: %s", hdrtype_name(HdrType));
+
+ if (HdrType == ZRPOS) {
+ long pos;
+ memcpy(&pos, (unsigned long *)&zs->TxHdr[ZP0], 4);
+ add_sendbuf(zs, " offset=%ld", pos);
+ }
+#endif
+}
+
+/*
+ * In lrzsz, ZDLE (CAN), DLE, XON, XOFF, CR immediately after @, and these
+ * Characters with MSB are escaped.
+ * It seems that it was the same as lrzsz in Tera Term before, but for some
+ * reason CR is always changed to escape target.
+ *
+ * To avoid problems if you connect to ssh / telnet from the destination
+ * LF and GS are also added to the default escape target.
+ * ssh: ~ immediately after LF or CR is treated as an escape character
+ * telnet: GS is the escape character
+ */
+void
+ZPutBin(struct zmodem_session *zs, short *i, unsigned char b)
+{
+ switch (b) {
+ case 0x0D: // CR
+ case 0x8D: // CR | 0x80
+ // if (zs->CtlEsc || ((zs->LastSent & 0x7f) == '@')) {
+ zs->PktOut[*i] = ZDLE;
+ (*i)++;
+ b = b ^ 0x40;
+ // }
+ break;
+ case 0x0A: // LF
+ case 0x10: // DLE
+ case 0x11: // XON
+ case 0x13: // XOFF
+ case 0x1d: // GS
+ case ZDLE: // CAN(0x18)
+ case 0x8A: // LF | 0x80
+ case 0x90: // DLE | 0x80
+ case 0x91: // XON | 0x80
+ case 0x93: // XOFF | 0x80
+ case 0x9d: // GS | 0x80
+ zs->PktOut[*i] = ZDLE;
+ (*i)++;
+ b = b ^ 0x40;
+ break;
+ default:
+ if (zs->CtlEsc && ((b & 0x60) == 0)) {
+ zs->PktOut[*i] = ZDLE;
+ (*i)++;
+ b = b ^ 0x40;
+ }
+ }
+ zs->LastSent = b;
+ zs->PktOut[*i] = b;
+ (*i)++;
+}
+
+void
+ZSbHdr(struct zmodem_session *zs, unsigned char HdrType)
+{
+ short i;
+
+ zs->PktOut[0] = ZPAD;
+ zs->PktOut[1] = ZDLE;
+ zs->PktOut[2] = ZBIN;
+ zs->PktOutCount = 3;
+ ZPutBin(zs, &(zs->PktOutCount), HdrType);
+ zs->CRC = UpdateCRC(HdrType, 0);
+ for (i = 0; i <= 3; i++) {
+ ZPutBin(zs, &(zs->PktOutCount), zs->TxHdr[i]);
+ zs->CRC = UpdateCRC(zs->TxHdr[i], zs->CRC);
+ }
+ ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC));
+ ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC));
+
+ zs->PktOutPtr = 0;
+ zs->Sending = true;
+
+#ifdef ZMODEM_DEBUG
+ add_sendbuf(zs, "ZSbHdr: %s ", hdrtype_name(HdrType));
+#endif
+}
+
+void
+ZStoHdr(struct zmodem_session *zs, unsigned long Pos)
+{
+ zs->TxHdr[ZP0] = LOBYTE(LOWORD(Pos));
+ zs->TxHdr[ZP1] = HIBYTE(LOWORD(Pos));
+ zs->TxHdr[ZP2] = LOBYTE(HIWORD(Pos));
+ zs->TxHdr[ZP3] = HIBYTE(HIWORD(Pos));
+}
+
+long
+ZRclHdr(struct zmodem_session *zs)
+{
+ unsigned long L;
+
+ L = (unsigned char)(zs->RxHdr[ZP3]);
+ L = (L << 8) + (unsigned char)(zs->RxHdr[ZP2]);
+ L = (L << 8) + (unsigned char)(zs->RxHdr[ZP1]);
+ return ((L << 8) + (unsigned char)(zs->RxHdr[ZP0]));
+}
+
+void
+ZSendRInit(struct zmodem_session *zs)
+{
+ zs->Pos = 0;
+ ZStoHdr(zs, 0);
+ zs->TxHdr[ZF0] = /* CANFC32 | */ CANFDX | CANOVIO;
+ if (zs->CtlEsc)
+ zs->TxHdr[ZF0] = zs->TxHdr[ZF0] | ESCCTL;
+ ZShHdr(zs, ZRINIT);
+}
+
+void
+ZSendRQInit(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ ZShHdr(zs, ZRQINIT);
+}
+
+void
+ZSendRPOS(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, zs->Pos);
+ ZShHdr(zs, ZRPOS);
+ ZResetTimeout(zs, zs->TimeOut);
+}
+
+void
+ZSendACK(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ ZShHdr(zs, ZACK);
+ ZResetTimeout(zs, zs->TimeOut);
+}
+
+void
+ZSendNAK(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ ZShHdr(zs, ZNAK);
+}
+
+void
+ZSendEOF(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, zs->Pos);
+ ZShHdr(zs, ZEOF);
+ zs->ZState = Z_SendEOF;
+}
+
+void
+ZSendFIN(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ ZShHdr(zs, ZFIN);
+}
+
+void
+ZSendSkip(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ ZShHdr(zs, ZSKIP);
+}
+
+void
+ZSendCancel(struct zmodem_session *zs)
+{
+ short i;
+
+ for (i = 0; i <= 7; i++)
+ zs->PktOut[i] = ZDLE;
+ for (i = 8; i <= 17; i++)
+ zs->PktOut[i] = 0x08;
+ zs->PktOutCount = 18;
+ zs->PktOutPtr = 0;
+ zs->Sending = true;
+ zs->ZState = Z_Cancel;
+
+#ifdef ZMODEM_DEBUG
+ add_sendbuf(zs, "ZSendCancel");
+#endif
+}
+
+void
+ZSendInitHdr(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ if (zs->CtlEsc)
+ zs->TxHdr[ZF0] = ESCCTL;
+ ZShHdr(zs, ZSINIT);
+ zs->ZState = Z_SendInitHdr;
+}
+
+void
+ZSendInitDat(struct zmodem_session *zs)
+{
+ zs->CRC = 0;
+ zs->PktOutCount = 0;
+ ZPutBin(zs, &(zs->PktOutCount), 0);
+ zs->CRC = UpdateCRC(0, zs->CRC);
+
+ zs->PktOut[zs->PktOutCount] = ZDLE;
+ zs->PktOutCount++;
+ zs->PktOut[zs->PktOutCount] = ZCRCW;
+ zs->PktOutCount++;
+ zs->CRC = UpdateCRC(ZCRCW, zs->CRC);
+
+ ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC));
+ ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC));
+
+ zs->PktOutPtr = 0;
+ zs->Sending = true;
+ zs->ZState = Z_SendInitDat;
+
+#ifdef ZMODEM_DEBUG
+ add_sendbuf(zs, "ZSendInitDat");
+#endif
+}
+
+void
+ZSendFileHdr(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, 0);
+ if (zs->BinFlag)
+ zs->TxHdr[ZF0] = ZCBIN; /* binary file */
+ else
+ zs->TxHdr[ZF0] = ZCNL; /* text file, convert newline */
+ ZSbHdr(zs, ZFILE);
+ zs->ZState = Z_SendFileHdr;
+}
+
+void
+ZSendFileDat(struct zmodem_session *zs)
+{
+ short i, j;
+ struct stat sb;
+
+ if (!zs->file) {
+ ZSendCancel(zs);
+ return;
+ }
+
+ /* file name */
+ strlcpy((char *)zs->PktOut, zs->file_name, sizeof(zs->PktOut));
+ zs->PktOutCount = strlen((char *)zs->PktOut);
+ zs->CRC = 0;
+ for (i = 0; i <= zs->PktOutCount - 1; i++)
+ zs->CRC = UpdateCRC(zs->PktOut[i], zs->CRC);
+ ZPutBin(zs, &(zs->PktOutCount), 0);
+ zs->CRC = UpdateCRC(0, zs->CRC);
+
+ /*
+ * ZFILE fields:
+ * size (dec)
+ * mtime (oct)
+ * mode (oct),
+ * serial (oct)
+ * files remaining (dec)
+ * bytes remaining (dec)
+ */
+#if THINK_C
+ snprintf((char *)zs->PktOut + zs->PktOutCount,
+ sizeof(zs->PktOut) - zs->PktOutCount,
+ "%lu 0 100644 0 0", zs->file_size);
+#else
+ fstat(fileno(zs->file), &sb);
+ snprintf(&(zs->PktOut[zs->PktOutCount]),
+ sizeof(zs->PktOut) - zs->PktOutCount,
+ "%lu %lo %o", zs->file_size, (unsigned long)sb.st_mtim.tv_sec,
+ sb.st_mode);
+#endif
+ j = strlen((char *)zs->PktOut + zs->PktOutCount);
+ for (i = 0; i <= j; i++) {
+ zs->CRC = UpdateCRC(zs->PktOut[zs->PktOutCount], zs->CRC);
+ zs->PktOutCount++;
+ }
+
+ ZPutBin(zs, &(zs->PktOutCount), 0);
+ zs->CRC = UpdateCRC(0, zs->CRC);
+ zs->PktOut[zs->PktOutCount] = ZDLE;
+ zs->PktOutCount++;
+ zs->PktOut[zs->PktOutCount] = ZCRCW;
+ zs->PktOutCount++;
+ zs->CRC = UpdateCRC(ZCRCW, zs->CRC);
+
+ ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC));
+ ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC));
+
+ zs->PktOutPtr = 0;
+ zs->Sending = true;
+ zs->ZState = Z_SendFileDat;
+
+ fseek(zs->file, 0, SEEK_SET);
+
+#ifdef ZMODEM_DEBUG
+ add_sendbuf(zs, "ZSendFileDat: ZFILE: ZF0=%x ZF1=%x ZF2=%x file=%s size=%lu",
+ zs->TxHdr[ZF0], zs->TxHdr[ZF1],zs->TxHdr[ZF2],
+ zs->file_name, zs->file_size);
+#endif
+}
+
+void
+ZSendDataHdr(struct zmodem_session *zs)
+{
+ ZStoHdr(zs, zs->Pos);
+ ZSbHdr(zs, ZDATA);
+ zs->ZState = Z_SendDataHdr;
+}
+
+void
+ZSendDataDat(struct zmodem_session *zs)
+{
+ short c;
+ unsigned char b;
+
+ if (zs->Pos >= zs->file_size) {
+ zs->Pos = zs->file_size;
+ ZSendEOF(zs);
+ fclose(zs->file);
+ zs->file = NULL;
+ return;
+ }
+
+ if (zs->Pos != ftell(zs->file))
+ fseek(zs->file, zs->Pos, SEEK_SET);
+
+ zs->CRC = 0;
+ zs->PktOutCount = 0;
+ do {
+ c = fread(&b, 1, 1, zs->file);
+ if (c > 0) {
+ ZPutBin(zs, &(zs->PktOutCount), b);
+ zs->CRC = UpdateCRC(b, zs->CRC);
+ }
+ } while ((c != 0) && (zs->PktOutCount <= zs->MaxDataLen - 2));
+
+ zs->Pos = ftell(zs->file);
+
+ zs->PktOut[zs->PktOutCount] = ZDLE;
+ zs->PktOutCount++;
+ if (zs->Pos >= zs->file_size)
+ b = ZCRCE;
+ else if ((zs->WinSize >= 0) && (zs->Pos - zs->LastPos > zs->WinSize))
+ b = ZCRCQ;
+ else
+ b = ZCRCG;
+ zs->PktOut[zs->PktOutCount] = b;
+ zs->PktOutCount++;
+ zs->CRC = UpdateCRC(b, zs->CRC);
+
+ ZPutBin(zs, &(zs->PktOutCount), HIBYTE(zs->CRC));
+ ZPutBin(zs, &(zs->PktOutCount), LOBYTE(zs->CRC));
+
+ zs->PktOutPtr = 0;
+ zs->Sending = true;
+ if (b == ZCRCQ)
+ zs->ZState = Z_SendDataDat2; /* wait for response from receiver */
+ else
+ zs->ZState = Z_SendDataDat;
+
+#ifdef ZMODEM_DEBUG
+ /* add_sendbuf("ZSendDataDat pos=%ld", zs->Pos); */
+#endif
+}
+
+bool
+ZInit(struct zmodem_session *zs)
+{
+ zs->CtlEsc = 0;
+ zs->MaxDataLen = 1024;
+ zs->WinSize = 32767;
+
+ if (zs->ZMode == IdZAutoR || zs->ZMode == IdZAutoS) {
+ if (zs->ZMode == IdZAutoR) {
+ zs->ZMode = IdZReceive;
+ zs->send(zs->cookie, (unsigned char *)'0', 1);
+ } else {
+ zs->ZMode = IdZSend;
+ zs->send(zs->cookie, (unsigned char *)'1', 1);
+ }
+ zs->send(zs->cookie, (unsigned char *)'0', 1);
+ zs->send(zs->cookie, (unsigned char *)'B', 1);
+ zs->send(zs->cookie, (unsigned char *)ZDLE, 1);
+ zs->send(zs->cookie, (unsigned char *)ZPAD, 1);
+ }
+
+ zs->PktOutCount = 0;
+ zs->Pos = 0;
+ zs->LastPos = 0;
+ zs->ZPktState = Z_PktGetPAD;
+ zs->Sending = false;
+ zs->LastSent = 0;
+ zs->CanCount = 5;
+
+ if (zs->MaxDataLen <= 0)
+ zs->MaxDataLen = 1024;
+ if (zs->MaxDataLen < 64)
+ zs->MaxDataLen = 64;
+ if (zs->MaxDataLen > 1024)
+ zs->MaxDataLen = 1024;
+
+ zs->TimeOut = 10;
+ ZResetTimeout(zs, zs->TimeOut);
+
+ switch (zs->ZMode) {
+ case IdZReceive:
+ zs->ZState = Z_RecvInit;
+ ZSendRInit(zs);
+ break;
+ case IdZSend:
+ zs->ZState = Z_SendInit;
+#if 0
+ zs->output("rz\r", 3);
+#endif
+ ZSendRQInit(zs);
+ break;
+ }
+
+ return true;
+}
+
+void
+ZTimeOutProc(struct zmodem_session *zs)
+{
+ switch (zs->ZState) {
+ case Z_RecvInit:
+ ZSendRInit(zs);
+ break;
+ case Z_RecvInit2:
+ ZSendACK(zs); /* Ack for ZSINIT */
+ break;
+ case Z_RecvData:
+ ZSendRPOS(zs);
+ break;
+ case Z_RecvFIN:
+ zs->ZState = Z_End;
+ break;
+ }
+}
+
+bool
+ZCheckHdr(struct zmodem_session *zs)
+{
+ short i;
+ bool ok;
+
+ if (zs->CRC32) {
+ zs->CRC3 = 0xFFFFFFFF;
+ for (i = 0; i <= 8; i++)
+ zs->CRC3 = UpdateCRC32(zs->PktIn[i], zs->CRC3);
+ ok = (zs->CRC3 == 0xDEBB20E3);
+ } else {
+ zs->CRC = 0;
+ for (i = 0; i <= 6; i++)
+ zs->CRC = UpdateCRC(zs->PktIn[i], zs->CRC);
+ ok = (zs->CRC == 0);
+ }
+
+ if (!ok) {
+ switch (zs->ZState) {
+ case Z_RecvInit:
+ ZSendRInit(zs);
+ break;
+ case Z_RecvData:
+ ZSendRPOS(zs);
+ break;
+ }
+ }
+ zs->RxType = zs->PktIn[0];
+ for (i = 1; i <= 4; i++)
+ zs->RxHdr[i - 1] = zs->PktIn[i];
+
+ return ok;
+}
+
+void
+ZParseRInit(struct zmodem_session *zs)
+{
+ short max;
+
+ if ((zs->ZState != Z_SendInit) && (zs->ZState != Z_SendEOF))
+ return;
+
+ if (!zs->file) {
+ zs->ZState = Z_SendFIN;
+ ZSendFIN(zs);
+ return;
+ }
+
+ if (zs->CtlEsc) {
+ if ((zs->RxHdr[ZF0] & ESCCTL) == 0) {
+ zs->ZState = Z_SendInitHdr;
+ ZSendInitHdr(zs);
+ return;
+ }
+ } else
+ zs->CtlEsc = (zs->RxHdr[ZF0] & ESCCTL) != 0;
+
+ max = (zs->RxHdr[ZP1] << 8) + zs->RxHdr[ZP0];
+ if (max <= 0)
+ max = 1024;
+ if (zs->MaxDataLen > max)
+ zs->MaxDataLen = max;
+
+ zs->ZState = Z_SendFileHdr;
+ ZSendFileHdr(zs);
+}
+
+bool
+ZParseSInit(struct zmodem_session *zs)
+{
+ if (zs->ZState != Z_RecvInit)
+ return false;
+
+ zs->ZState = Z_RecvInit2;
+ zs->CtlEsc = zs->CtlEsc || ((zs->RxHdr[ZF0] & ESCCTL) != 0);
+
+ return true;
+}
+
+void
+ZParseHdr(struct zmodem_session *zs)
+{
+#ifdef ZMODEM_DEBUG
+ add_recvbuf(zs, "ZParseHdr: RxType %s ", hdrtype_name(zs->RxType));
+#endif
+
+ switch (zs->RxType) {
+ case ZRQINIT:
+ if (zs->ZState == Z_RecvInit)
+ ZSendRInit(zs);
+ break;
+ case ZRINIT:
+ ZParseRInit(zs);
+ break;
+ case ZSINIT:
+ zs->ZPktState = Z_PktGetData;
+ if (zs->ZState == Z_RecvInit)
+ ZResetTimeout(zs, zs->TimeOut);
+ break;
+ case ZACK:
+ switch (zs->ZState) {
+ case Z_SendInitDat:
+ ZSendFileHdr(zs);
+ break;
+ case Z_SendDataDat2:
+ zs->LastPos = ZRclHdr(zs);
+ ZResetTimeout(zs, zs->TimeOut);
+ if (zs->Pos == zs->LastPos)
+ ZSendDataDat(zs);
+ else {
+ zs->Pos = zs->LastPos;
+ ZSendDataHdr(zs);
+ }
+ break;
+ }
+ break;
+ case ZFILE:
+ zs->ZPktState = Z_PktGetData;
+ if ((zs->ZState == Z_RecvInit) || (zs->ZState == Z_RecvInit2)) {
+ zs->BinFlag = (zs->RxHdr[ZF0] != ZCNL);
+ ZResetTimeout(zs, zs->TimeOut);
+ }
+ break;
+ case ZSKIP:
+ if (zs->file) {
+ fclose(zs->file);
+ /*
+ * If you try to send a file that exists on the server
+ * side, ZParseRInit () will double close, so
+ * Drop the flag here. (2007.12.20 yutaka)
+ */
+ zs->file = NULL;
+ }
+ ZStoHdr(zs, 0);
+ if (zs->CtlEsc)
+ zs->RxHdr[ZF0] = ESCCTL;
+ zs->ZState = Z_SendInit;
+ ZParseRInit(zs);
+ break;
+ case ZNAK:
+ switch (zs->ZState) {
+ case Z_SendInitHdr:
+ case Z_SendInitDat:
+ ZSendInitHdr(zs);
+ break;
+ case Z_SendFileHdr:
+ case Z_SendFileDat:
+ ZSendFileHdr(zs);
+ break;
+ }
+ break;
+ case ZABORT:
+ case ZFERR:
+ if (zs->ZMode == IdZSend) {
+ zs->ZState = Z_SendFIN;
+ ZSendFIN(zs);
+ }
+ break;
+ case ZFIN:
+ if (zs->ZMode == IdZReceive) {
+ zs->ZState = Z_RecvFIN;
+ ZSendFIN(zs);
+ zs->CanCount = 2;
+ ZResetTimeout(zs, zs->TimeOut);
+ } else {
+ zs->ZState = Z_End;
+ zs->send(zs->cookie, (unsigned char *)"OO", 2);
+ }
+ break;
+ case ZRPOS:
+ switch (zs->ZState) {
+ case Z_SendFileDat:
+ case Z_SendDataHdr:
+ case Z_SendDataDat:
+ case Z_SendDataDat2:
+ case Z_SendEOF:
+ zs->Pos = ZRclHdr(zs);
+ zs->LastPos = zs->Pos;
+#ifdef ZMODEM_DEBUG
+ add_recvbuf(zs, " pos=%ld", zs->Pos);
+#endif
+ ZSendDataHdr(zs);
+ break;
+ }
+ break;
+ case ZDATA:
+ if (zs->Pos != ZRclHdr(zs)) {
+ ZSendRPOS(zs);
+ return;
+ } else {
+ ZResetTimeout(zs, zs->TimeOut);
+ zs->ZPktState = Z_PktGetData;
+ }
+ break;
+ case ZEOF:
+ if (zs->Pos != ZRclHdr(zs)) {
+ ZSendRPOS(zs);
+ return;
+ }
+
+ if (zs->file) {
+ if (zs->CRRecv) {
+ zs->CRRecv = false;
+ fwrite("\012", 1, 1, zs->file);
+ }
+ fclose(zs->file);
+ zs->file = NULL;
+ }
+ zs->ZState = Z_RecvInit;
+ ZSendRInit(zs);
+ break;
+ }
+ zs->Quoted = false;
+ zs->CRC = 0;
+ zs->CRC3 = 0xFFFFFFFF;
+ zs->PktInPtr = 0;
+ zs->PktInCount = 0;
+}
+
+bool
+FTCreateFile(struct zmodem_session *zs)
+{
+ /* check for existing file, bail if it exists */
+ zs->file = fopen(zs->file_name, "r");
+ if (zs->file != NULL) {
+ fclose(zs->file);
+ zs->file = NULL;
+ return false;
+ }
+
+ zs->file = fopen(zs->file_name, "wb+");
+ if (zs->file == NULL)
+ return false;
+
+ return true;
+}
+
+bool
+ZParseFile(struct zmodem_session *zs)
+{
+ unsigned char b;
+ short i;
+ char *p, *tfile_name;
+ unsigned long modtime, file_size;
+ short mode;
+ short ret;
+
+ if ((zs->ZState != Z_RecvInit) && (zs->ZState != Z_RecvInit2))
+ return false;
+
+ /* kill timer */
+ zs->TimeOutAt = 0;
+ zs->CRRecv = false;
+
+ /* file name */
+ zs->PktIn[zs->PktInPtr] = '\0';
+
+ /* don't allow paths in file name */
+ tfile_name = (char *)&zs->PktIn;
+ i = 0;
+ do {
+ if (tfile_name[i] == '\0')
+ break;
+ if (tfile_name[i] == '/') {
+ tfile_name += i + 1;
+ i = 0;
+ }
+ i++;
+ } while (1);
+
+ zs->file_name = xstrdup(tfile_name);
+ if (zs->file_name == NULL)
+ return false;
+ if (zs->file_name[0] == '\0') {
+ free(zs->file_name);
+ return false;
+ }
+
+ /* file open */
+ if (!FTCreateFile(zs)) {
+ free(zs->file_name);
+ zs->file_name = NULL;
+ return false;
+ }
+
+ /* file size */
+ i = strlen((char *)zs->PktIn) + 1;
+ file_size = 0;
+ do {
+ b = zs->PktIn[i];
+ if ((b >= 0x30) && (b <= 0x39))
+ file_size = (file_size * 10) + b - 0x30;
+ i++;
+ } while ((b >= 0x30) && (b <= 0x39));
+ zs->file_size = file_size;
+
+ /* file mtime */
+ p = (char *)zs->PktIn + i;
+ zs->file_mtime = 0;
+ if (*p) {
+ ret = sscanf(p, "%lo%o", &modtime, &mode);
+ if (ret >= 1)
+ zs->file_mtime = modtime;
+ }
+
+ zs->Pos = 0;
+ fseek(zs->file, 0, SEEK_SET);
+
+ ZStoHdr(zs, 0);
+ zs->ZState = Z_RecvData;
+
+ /* set timeout for data */
+ ZResetTimeout(zs, zs->TimeOut);
+
+ return true;
+}
+
+bool
+ZWriteData(struct zmodem_session *zs)
+{
+ short i;
+ unsigned char b;
+
+ if (zs->ZState != Z_RecvData)
+ return false;
+
+ /* kill timer */
+ zs->TimeOutAt = 0;
+
+ if (zs->BinFlag)
+ fwrite(zs->PktIn, zs->PktInPtr, 1, zs->file);
+ else
+ for (i = 0; i <= zs->PktInPtr - 1; i++) {
+ b = zs->PktIn[i];
+ if ((b == 0x0A) && (!zs->CRRecv))
+ fwrite("\015", 1, 1, zs->file);
+ if (zs->CRRecv && (b != 0x0A))
+ fwrite("\012", 1, 1, zs->file);
+ zs->CRRecv = b == 0x0D;
+ fwrite(&b, 1, 1, zs->file);
+ }
+
+ zs->Pos += zs->PktInPtr;
+ fseek(zs->file, zs->Pos, SEEK_SET);
+ ZStoHdr(zs, zs->Pos);
+
+ /* set timeout for data */
+ ZResetTimeout(zs, zs->TimeOut);
+
+ return true;
+}
+
+void
+ZCheckData(struct zmodem_session *zs)
+{
+ bool ok;
+
+ /* check CRC */
+ if ((zs->CRC32 && zs->CRC3 != 0xDEBB20E3) ||
+ (!zs->CRC32 && zs->CRC != 0)) {
+ switch (zs->ZState) {
+ case Z_RecvInit:
+ case Z_RecvInit2:
+ ZSendNAK(zs);
+ break;
+ case Z_RecvData:
+ ZSendRPOS(zs);
+ break;
+ }
+ zs->ZPktState = Z_PktGetPAD;
+ return;
+ }
+ /* parse data */
+ switch (zs->RxType) {
+ case ZSINIT:
+ ok = ZParseSInit(zs);
+ break;
+ case ZFILE:
+ ok = ZParseFile(zs);
+ break;
+ case ZDATA:
+ ok = ZWriteData(zs);
+ break;
+ default:
+ ok = false;
+ }
+
+ if (!ok) {
+ zs->ZPktState = Z_PktGetPAD;
+ zs->ZState = Z_SendSkip;
+ ZSendSkip(zs);
+ return;
+ }
+
+ if (zs->RxType == ZFILE)
+ ZShHdr(zs, ZRPOS);
+
+ /* next state */
+ switch (zs->TERM) {
+ case ZCRCE:
+ zs->ZPktState = Z_PktGetPAD;
+ break;
+ case ZCRCG:
+ zs->ZPktState = Z_PktGetData;
+ break;
+ case ZCRCQ:
+ zs->ZPktState = Z_PktGetData;
+ if (zs->RxType != ZFILE)
+ ZShHdr(zs, ZACK);
+ break;
+ case ZCRCW:
+ zs->ZPktState = Z_PktGetPAD;
+ if (zs->RxType != ZFILE)
+ ZShHdr(zs, ZACK);
+ break;
+ default:
+ zs->ZPktState = Z_PktGetPAD;
+ }
+
+ if (zs->ZPktState == Z_PktGetData) {
+ zs->Quoted = false;
+ zs->CRC = 0;
+ zs->CRC3 = 0xFFFFFFFF;
+ zs->PktInPtr = 0;
+ zs->PktInCount = 0;
+ }
+}
+
+bool
+ZParse(struct zmodem_session *zs)
+{
+ unsigned char b;
+ short c;
+
+ do {
+ /* Send packet */
+ if (zs->Sending) {
+ c = 1;
+ while ((c > 0) && (zs->PktOutCount > 0)) {
+ c = zs->send(zs->cookie, &(zs->PktOut[zs->PktOutPtr]),
+ zs->PktOutCount);
+ zs->PktOutPtr = zs->PktOutPtr + c;
+ zs->PktOutCount = zs->PktOutCount - c;
+ }
+ if (zs->PktOutCount <= 0)
+ zs->Sending = false;
+ if ((zs->ZMode == IdZReceive) && (zs->PktOutCount > 0))
+ return true;
+ }
+
+ c = ZRead1Byte(zs, &b);
+ while (c > 0) {
+ if (zs->ZState == Z_RecvFIN) {
+ if (b == 'O')
+ zs->CanCount--;
+ if (zs->CanCount <= 0) {
+ zs->ZState = Z_End;
+ return false;
+ }
+ } else {
+ switch (b) {
+ case ZDLE:
+ zs->CanCount--;
+ if (zs->CanCount <= 0) {
+ zs->ZState = Z_End;
+ return false;
+ }
+ break;
+ default:
+ zs->CanCount = 5;
+ }
+ }
+
+ switch (zs->ZPktState) {
+ case Z_PktGetPAD:
+ switch (b) {
+ case ZPAD:
+ zs->ZPktState = Z_PktGetDLE;
+ break;
+ }
+ break;
+ case Z_PktGetDLE:
+ switch (b) {
+ case ZPAD:
+ break;
+ case ZDLE:
+ zs->ZPktState = Z_PktHdrFrm;
+ break;
+ default:
+ zs->ZPktState = Z_PktGetPAD;
+ }
+ break;
+ case Z_PktHdrFrm: /* Get header format type */
+ switch (b) {
+ case ZBIN:
+ zs->CRC32 = false;
+ zs->PktInCount = 7;
+ zs->ZPktState = Z_PktGetBin;
+ break;
+ case ZHEX:
+ zs->HexLo = false;
+ zs->CRC32 = false;
+ zs->PktInCount = 7;
+ zs->ZPktState = Z_PktGetHex;
+ break;
+ case ZBIN32:
+ zs->CRC32 = true;
+ zs->PktInCount = 9;
+ zs->ZPktState = Z_PktGetBin;
+ break;
+ default:
+ zs->ZPktState = Z_PktGetPAD;
+ }
+ zs->Quoted = false;
+ zs->PktInPtr = 0;
+ break;
+ case Z_PktGetBin:
+ switch (b) {
+ case ZDLE:
+ zs->Quoted = true;
+ break;
+ default:
+ if (zs->Quoted) {
+ switch (b) {
+ case ZRUB0:
+ b = 0x7f;
+ break;
+ case ZRUB1:
+ b = 0xff;
+ break;
+ default:
+ b = b ^ 0x40;
+ }
+ zs->Quoted = false;
+ }
+ zs->PktIn[zs->PktInPtr] = b;
+ zs->PktInPtr++;
+ zs->PktInCount--;
+ if (zs->PktInCount == 0) {
+ zs->ZPktState = Z_PktGetPAD;
+ if (ZCheckHdr(zs))
+ ZParseHdr(zs);
+ }
+ }
+ break;
+ case Z_PktGetHex:
+ if (b <= '9')
+ b = b - 0x30;
+ else if ((b >= 'a') && (b <= 'f'))
+ b = b - 0x57;
+ else {
+ zs->ZPktState = Z_PktGetPAD;
+ return true;
+ }
+
+ if (zs->HexLo) {
+ zs->PktIn[zs->PktInPtr] =
+ zs->PktIn[zs->PktInPtr] + b;
+ zs->HexLo = false;
+ zs->PktInPtr++;
+ zs->PktInCount--;
+ if (zs->PktInCount <= 0) {
+ zs->ZPktState = Z_PktGetHexEOL;
+ zs->PktInCount = 2;
+ }
+ } else {
+ zs->PktIn[zs->PktInPtr] = b << 4;
+ zs->HexLo = true;
+ }
+ break;
+ case Z_PktGetHexEOL:
+ zs->PktInCount--;
+ if (zs->PktInCount <= 0) {
+ zs->ZPktState = Z_PktGetPAD;
+ if (ZCheckHdr(zs))
+ ZParseHdr(zs);
+ }
+ break;
+ case Z_PktGetData:
+ switch (b) {
+ case ZDLE:
+ zs->Quoted = true;
+ break;
+ default:
+ if (zs->Quoted) {
+ switch (b) {
+ case ZCRCE:
+ case ZCRCG:
+ case ZCRCQ:
+ case ZCRCW:
+ zs->TERM = b;
+ if (zs->CRC32)
+ zs->PktInCount = 4;
+ else
+ zs->PktInCount = 2;
+ zs->ZPktState = Z_PktGetCRC;
+ break;
+ case ZRUB0:
+ b = 0x7F;
+ break;
+ case ZRUB1:
+ b = 0xFF;
+ break;
+ default:
+ b = b ^ 0x40;
+ }
+ zs->Quoted = false;
+ }
+ if (zs->CRC32)
+ zs->CRC3 = UpdateCRC32(b, zs->CRC3);
+ else
+ zs->CRC = UpdateCRC(b, zs->CRC);
+ if (zs->ZPktState == Z_PktGetData) {
+ if (zs->PktInPtr < 1024) {
+ zs->PktIn[zs->PktInPtr] = b;
+ zs->PktInPtr++;
+ } else
+ zs->ZPktState = Z_PktGetPAD;
+ }
+ }
+ break;
+ case Z_PktGetCRC:
+ switch (b) {
+ case ZDLE:
+ zs->Quoted = true;
+ break;
+ default:
+ if (zs->Quoted) {
+ switch (b) {
+ case ZRUB0:
+ b = 0x7F;
+ break;
+ case ZRUB1:
+ b = 0xFF;
+ break;
+ default:
+ b = b ^ 0x40;
+ }
+ zs->Quoted = false;
+ }
+ if (zs->CRC32)
+ zs->CRC3 = UpdateCRC32(b, zs->CRC3);
+ else
+ zs->CRC = UpdateCRC(b, zs->CRC);
+ zs->PktInCount--;
+ if (zs->PktInCount <= 0)
+ ZCheckData(zs);
+ }
+ break;
+ }
+ c = ZRead1Byte(zs, &b);
+ }
+
+ if (!zs->Sending)
+ switch (zs->ZState) {
+ case Z_SendInitHdr:
+ ZSendInitDat(zs);
+ break;
+ case Z_SendFileHdr:
+ ZSendFileDat(zs);
+ break;
+ case Z_SendDataHdr:
+ case Z_SendDataDat:
+ ZSendDataDat(zs);
+ break;
+ case Z_Cancel:
+ zs->ZState = Z_End;
+ break;
+ }
+
+ if (zs->Sending && (zs->PktOutCount > 0))
+ return true;
+ } while (zs->Sending);
+
+ if (zs->ZState == Z_End)
+ return false;
+
+ return true;
+}
+
+void
+ZCancel(struct zmodem_session *zs)
+{
+ ZSendCancel(zs);
+}
+
+void
+ZDestroy(struct zmodem_session *zs)
+{
+ if (zs->file)
+ fclose(zs->file);
+ if (zs->file_name)
+ free(zs->file_name);
+ free(zs);
+}
--- zmodem.h Mon Jul 4 20:46:10 2022
+++ zmodem.h Mon Jul 4 20:46:10 2022
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 1994-1998 T. Teranishi
+ * (C) 2009- TeraTerm Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ZMODEM_H__
+#define __ZMODEM_H__
+
+//#include <stdbool.h>
+#include "util.h"
+
+/* #define ZMODEM_DEBUG */
+
+/* ZMODEM function id */
+#define IdZReceive 1
+#define IdZSend 2
+#define IdZAutoR 3
+#define IdZAutoS 4
+
+enum {
+ ZMODEM_MODE,
+ ZMODEM_BINFLAG
+};
+
+struct zmodem_session {
+ void *cookie;
+ short (*send)(void *cookie, unsigned char *data, size_t size);
+ short (*recv)(void *cookie, unsigned char *data);
+
+ FILE *file;
+ unsigned long file_size;
+ char *file_name;
+ unsigned long file_mtime;
+
+ unsigned char RxHdr[4], TxHdr[4];
+ unsigned char RxType, TERM;
+ unsigned char PktIn[1032], PktOut[1032];
+ short PktInPtr, PktOutPtr;
+ short PktInCount, PktOutCount;
+ short PktInLen;
+ bool BinFlag;
+ bool Sending;
+ short ZMode, ZState, ZPktState;
+ short MaxDataLen, TimeOut, CanCount;
+ bool CtlEsc, CRC32, HexLo, Quoted, CRRecv;
+ short CRC;
+ long CRC3, Pos, LastPos, WinSize;
+ unsigned char LastSent;
+
+ unsigned long TimeOutAt;
+};
+
+struct zmodem_session * ZCreateSender(FILE *fp);
+struct zmodem_session * ZCreateReceiver(void);
+
+bool ZInit(struct zmodem_session *zs);
+void ZDestroy(struct zmodem_session *zs);
+bool ZParse(struct zmodem_session *zs);
+void ZCancel(struct zmodem_session *zs);
+bool ZHaveTimedOut(struct zmodem_session *zs);
+void ZTimeOutProc(struct zmodem_session *zs);
+
+#endif