summaryrefslogtreecommitdiff
path: root/src/terminal/internal/fqterm_zmodem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal/internal/fqterm_zmodem.cpp')
-rw-r--r--src/terminal/internal/fqterm_zmodem.cpp3056
1 files changed, 3056 insertions, 0 deletions
diff --git a/src/terminal/internal/fqterm_zmodem.cpp b/src/terminal/internal/fqterm_zmodem.cpp
new file mode 100644
index 0000000..afb99c9
--- /dev/null
+++ b/src/terminal/internal/fqterm_zmodem.cpp
@@ -0,0 +1,3056 @@
+/***************************************************************************
+ * fqterm, a terminal emulator for both BBS and *nix. *
+ * Copyright (C) 2008 fqterm development group. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. *
+ ***************************************************************************/
+
+#include <QApplication>
+#include <QString>
+#include <QFileDialog>
+#include <QFileInfo>
+
+#include "fqterm.h"
+#include "fqterm_config.h"
+#include "fqterm_param.h"
+#include "fqterm_path.h"
+#include "fqterm_telnet.h"
+#include "fqterm_zmodem.h"
+#include "fqterm_filedialog.h"
+
+#ifdef FQTERM_ZMODEM_DEBUG
+#include <sys/time.h>
+#endif
+
+namespace FQTerm {
+
+static const uchar zeros[4] = {
+ 0, 0, 0, 0
+};
+
+static const char hexChars[] = "0123456789abcdef";
+
+static const uchar AckStr[1] = {
+ ACK
+};
+static const uchar NakStr[1] = {
+ NAK
+};
+static const uchar CanStr[2] = {
+ CAN, CAN
+};
+static const uchar eotstr[1] = {
+ EOT
+};
+
+
+/*
+ * Crc calculation stuff
+ */
+
+/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
+unsigned short crctab[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129,
+ 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273,
+ 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a,
+ 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6,
+ 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf,
+ 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695,
+ 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc,
+ 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4,
+ 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf,
+ 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5,
+ 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a,
+ 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32,
+ 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59,
+ 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9,
+ 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290,
+ 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8,
+ 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481,
+ 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f,
+ 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676,
+ 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a,
+ 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75,
+ 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f,
+ 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64,
+ 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c,
+ 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93,
+ 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+/*
+ * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
+ * NOTE: First argument must be in range 0 to 255.
+ * Second argument is referenced twice.
+ *
+ * Programmers may incorporate any or all code into their programs,
+ * giving proper credit within the source. Publication of the
+ * source routines is permitted so long as proper credit is given
+ * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
+ * Omen Technology.
+ */
+
+#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
+
+/*
+ * Copyright (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/* First, the polynomial itself and its table of feedback terms. The */
+/* polynomial is */
+/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+/* Note that we take it "backwards" and put the highest-order term in */
+/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
+/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
+/* the MSB being 1. */
+
+/* Note that the usual hardware shift register implementation, which */
+/* is what we're using (we're merely optimizing it by doing eight-bit */
+/* chunks at a time) shifts bits into the lowest-order term. In our */
+/* implementation, that means shifting towards the right. Why do we */
+/* do it this way? Because the calculated CRC must be transmitted in */
+/* order from highest-order term to lowest-order term. UARTs transmit */
+/* characters in order from LSB to MSB. By storing the CRC this way, */
+/* we hand it to the UART in the order low-byte to high-byte; the UART */
+/* sends each low-bit to hight-bit; and the result is transmission bit */
+/* by bit from highest- to lowest-order term without requiring any bit */
+/* shuffling on our part. Reception works similarly. */
+
+/* The feedback terms table consists of 256, 32-bit entries. Notes: */
+/* */
+/* The table can be generated at runtime if desired; code to do so */
+/* is shown later. It might not be obvious, but the feedback */
+/* terms simply represent the results of eight shift/xor opera- */
+/* tions for all combinations of data and CRC register values. */
+/* */
+/* The values must be right-shifted by eight bits by the "updcrc" */
+/* logic; the shift must be unsigned (bring in zeroes). On some */
+/* hardware you could probably optimize the shift in assembler by */
+/* using byte-swap instructions. */
+
+unsigned long cr3tab[] = {
+ /* CRC polynomial 0xedb88320 */
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+ };
+
+#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF))
+
+#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
+
+
+
+#if 1
+StateTable FQTermZmodem::RStartOps[] = {
+ {ZSINIT, &FQTermZmodem::GotSinit, 0, 1, RSinitWait},
+ /* SINIT, wait for attn str */
+ {ZFILE, &FQTermZmodem::GotFile, 0, 0, RFileName},
+ /* FILE, wait for filename */
+ {ZRQINIT, &FQTermZmodem::SendRinit, 0, 1, RStart},
+ /* sender confused, resend */
+ {ZFIN, &FQTermZmodem::GotFin, 1, 0, RFinish},
+ /* sender shutting down */
+ {ZNAK, &FQTermZmodem::SendRinit, 1, 0, RStart},
+ /* RINIT was bad, resend */
+ {ZFREECNT, &FQTermZmodem::GotFreecnt, 0, 0, RStart},
+ /* sender wants free space */
+ {ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData},
+ /* sender wants command */
+ {ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData},
+ /* sender wants to send msg */
+ {ZRINIT, &FQTermZmodem::ZmodemTInit, 1, 1, TStart},
+ {99, &FQTermZmodem::ZPF, 0, 0, RStart}
+ /* anything else is an error */
+};
+
+StateTable FQTermZmodem::RSinitWaitOps[] = {
+ /* waiting for data */
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, RSinitWait
+ },
+};
+
+StateTable FQTermZmodem::RFileNameOps[] = {
+ /* waiting for file name */
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, RFileName
+ },
+};
+
+StateTable FQTermZmodem::RCrcOps[] = {
+ /* waiting for CRC */
+ {
+ ZCRC, &FQTermZmodem::GotFileCrc, 0, 0, RFile
+ }, { /* sender sent it */
+ ZNAK, &FQTermZmodem::ResendCrcReq, 0, 0, RCrc
+ }, { /* ZCRC was bad, resend */
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ }, { /* sender confused, restart */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ 99, &FQTermZmodem::ZPF, 0, 0, RCrc
+ },
+};
+
+
+StateTable FQTermZmodem::RFileOps[] = {
+ /* waiting for ZDATA */
+ {
+ ZDATA, &FQTermZmodem::GotData, 0, 0, RData
+ }, { /* got it */
+ ZNAK, &FQTermZmodem::ResendRpos, 0, 0, RFile
+ }, { /* ZRPOS was bad, resend */
+ ZEOF, &FQTermZmodem::GotEof, 0, 0, RStart
+ }, { /* end of file */
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ }, { /* sender confused, restart */
+ ZFILE, &FQTermZmodem::ResendRpos, 0, 0, RFile
+ }, { /* ZRPOS was bad, resend */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ 99, &FQTermZmodem::ZPF, 0, 0, RFile
+ },
+};
+
+/* waiting for data, but a packet could possibly arrive due
+ * to error recovery or something
+ */
+StateTable FQTermZmodem::RDataOps[] = {
+ {
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ }, { /* sender confused, restart */
+ ZFILE, &FQTermZmodem::GotFile, 0, 1, RFileName
+ }, { /* start a new file (??) */
+ ZNAK, &FQTermZmodem::ResendRpos, 1, 1, RFile
+ }, { /* ZRPOS was bad, resend */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ ZDATA, &FQTermZmodem::GotData, 0, 1, RData
+ }, { /* file data follows */
+ ZEOF, &FQTermZmodem::GotEof, 1, 1, RStart
+ }, { /* end of file */
+ 99, &FQTermZmodem::ZPF, 0, 0, RData
+ },
+};
+
+/* here if we've sent ZFERR or ZABORT. Waiting for ZFIN */
+
+StateTable FQTermZmodem::RFinishOps[] = {
+
+ {
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ } , { /* sender confused, restart */
+ ZFILE, &FQTermZmodem::GotFile, 1, 1, RFileName
+ }, { /* start a new file */
+ ZNAK, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* resend ZFIN */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ 99, &FQTermZmodem::ZPF, 0, 0, RFinish
+ },
+};
+
+
+/* sent ZRQINIT, waiting for response */
+StateTable FQTermZmodem::TStartOps[] = {
+ {
+ ZRINIT, &FQTermZmodem::GotRinit, 1, 1, TStart
+ }, {
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 1, 0, TStart
+ }, {
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZNAK, &FQTermZmodem::Ignore, 0, 0, TStart
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, TStart
+ },
+};
+
+/* sent ZSINIT, waiting for response */
+StateTable FQTermZmodem::TInitOps[] = {
+ {
+ ZACK, &FQTermZmodem::RetDone, 1, 0, TInit
+ }, {
+ ZNAK, &FQTermZmodem::SendZSInit, 1, 0, TInit
+ }, {
+ ZRINIT, &FQTermZmodem::GotRinit, 1, 1, TInit
+ }, { /* redundant, but who cares */
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 1, 0, TInit
+ }, {
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, TInit
+ },
+};
+
+/* sent ZFILE, waiting for response */
+StateTable FQTermZmodem::FileWaitOps[] = {
+ {
+ ZRPOS, &FQTermZmodem::SendFileData, 1, 1, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 0, TStart
+ }, {
+ ZCRC, &FQTermZmodem::SendFileCrc, 1, 0, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::sendFilename, 1, 0, FileWait
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 1, 0, FileWait
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ ZACK, &FQTermZmodem::SendFileData, 1, 0, Sending
+ }, { // receiver always sends ZACK back
+ 99, &FQTermZmodem::ZPF, 0, 0, FileWait
+ },
+};
+
+/* sent file CRC, waiting for response */
+StateTable FQTermZmodem::CrcWaitOps[] = {
+ {
+ ZRPOS, &FQTermZmodem::SendFileData, 1, 0, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 0, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::SendFileCrc, 1, 0, CrcWait
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZCRC, &FQTermZmodem::SendFileCrc, 0, 0, CrcWait
+ }, {
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 0, 0, CrcWait
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, CrcWait
+ },
+};
+
+/* sending data, interruptable */
+StateTable FQTermZmodem::SendingOps[] = {
+ {
+ ZACK, &FQTermZmodem::GotSendAck, 0, 0, Sending
+ }, {
+ ZRPOS, &FQTermZmodem::GotSendPos, 1, 1, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::GotSendNak, 1, 1, Sending
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ },{ /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendWait
+ },
+};
+
+/* sent data, need to send EOF */
+StateTable FQTermZmodem::SendDoneOps[] = {
+ {
+ ZACK, &FQTermZmodem::GotSendDoneAck, 0, 0, SendWait
+ }, {
+ ZRPOS, &FQTermZmodem::GotSendPos, 1, 1, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::GotSendNak, 1, 1, Sending
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendWait
+ },
+};
+
+/* sending data, waiting for ACK */
+StateTable FQTermZmodem::SendWaitOps[] = {
+ {
+ ZACK, &FQTermZmodem::GotSendWaitAck, 0, 0, Sending
+ }, {
+ ZRPOS, &FQTermZmodem::GotSendPos, 0, 0, SendWait
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::GotSendNak, 0, 0, Sending
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendWait
+ },
+};
+
+/* sent ZEOF, waiting for new RINIT */
+StateTable FQTermZmodem::SendEofOps[] = {
+ {
+ ZRINIT, &FQTermZmodem::SkipFile, 1, 0, TFinish
+ }, { /* successful completion */
+ ZACK, &FQTermZmodem::Ignore, 0, 0, SendEof
+ }, { /* probably ACK from last packet */
+ ZRPOS, &FQTermZmodem::GotSendPos, 1, 1, SendWait
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, TStart
+ }, {
+ ZNAK, &FQTermZmodem::ResendEof, 1, 0, SendEof
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendEof
+ },
+};
+
+StateTable FQTermZmodem::TFinishOps[] = {
+ {
+ ZFIN, &FQTermZmodem::OverAndOut, 1, 1, Done
+ }, {
+ ZNAK, &FQTermZmodem::ZmodemTFinish, 1, 1, TFinish
+ }, {
+ ZRINIT, &FQTermZmodem::ZmodemTFinish, 1, 1, TFinish
+ }, {
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, TFinish
+ },
+};
+
+StateTable FQTermZmodem::CommandDataOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, CommandData
+ },
+};
+
+StateTable FQTermZmodem::CommandWaitOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, CommandWait
+ },
+};
+
+StateTable FQTermZmodem::StderrDataOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, StderrData
+ },
+};
+
+StateTable FQTermZmodem::DoneOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, Done
+ },
+};
+
+StateTable *FQTermZmodem::tables[] = {
+ FQTermZmodem::RStartOps, FQTermZmodem::RSinitWaitOps, FQTermZmodem::RFileNameOps,
+ FQTermZmodem::RCrcOps, FQTermZmodem::RFileOps, FQTermZmodem::RDataOps,
+ FQTermZmodem::RDataOps, /* RDataErr is the same as RData */
+ FQTermZmodem::RFinishOps,
+
+ FQTermZmodem::TStartOps, FQTermZmodem::TInitOps, FQTermZmodem::FileWaitOps,
+ FQTermZmodem::CrcWaitOps, FQTermZmodem::SendingOps, FQTermZmodem::SendWaitOps,
+ FQTermZmodem::SendDoneOps, FQTermZmodem::SendEofOps, FQTermZmodem::TFinishOps,
+
+ FQTermZmodem::CommandDataOps, FQTermZmodem::CommandWaitOps,
+ FQTermZmodem::StderrDataOps, FQTermZmodem::DoneOps,
+};
+
+const char *FQTermZmodem::hdrnames[] = {
+ "ZRQINIT", "ZRINIT", "ZSINIT", "ZACK", "ZFILE", "ZSKIP", "ZNAK", "ZABORT",
+ "ZFIN", "ZRPOS", "ZDATA", "ZEOF", "ZFERR", "ZCRC", "ZCHALLENGE", "ZCOMPL",
+ "ZCAN", "ZFREECNT", "ZCOMMAND", "ZSTDERR",
+};
+
+#endif
+
+FQTermZmodem::FQTermZmodem(FQTermConfig *config, FQTermTelnet *netinterface, int type, int serverEncoding) {
+
+ //now set network interface Telnet
+
+ connectionType = type;
+
+ serverEncodingID = serverEncoding;
+
+ switch (connectionType) {
+ case 0:
+ case 1:
+ case 2:
+ telnet_ = netinterface;
+ break;
+ default:
+ FQ_TRACE("zmodem", 0) << "connection type unknown! Expect segmentation fault!";
+ telnet_ = netinterface;
+ break;
+ }
+
+ config_ = config;
+
+ sending = false;
+
+ transferstate = notransfer;
+
+ zmodemTimer = new QTimer(this);
+ FQ_VERIFY(connect(zmodemTimer, SIGNAL(timeout()), this, SLOT(ZmodemTimeout())));
+
+#ifdef FQTERM_ZMODEM_DEBUG
+ zmodemlogfile = fopen("zmodem.log", "w+");
+ fprintf(zmodemlogfile, "%s", "\n================================\n");
+ fclose(zmodemlogfile);
+#endif
+
+
+ //init struct INFO
+ info.zrinitflags = CANFDX | CANOVIO | CANBRK | CANFC32;
+ info.zsinitflags = 0;
+ info.attn = NULL;
+ info.bufsize = 0; /* full streaming */
+ info.buffer = NULL;
+
+ zerrno = 0;
+ lastPullByte = 0;
+
+ ZmodemReset(&info);
+ // other init function not complete
+
+}
+
+FQTermZmodem::~FQTermZmodem(){}
+
+int FQTermZmodem::ZmodemTInit(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 = 8096;
+ info->windowsize = 0;
+ //we won't be receiving much data, pick a reasonable buffer
+ // size (largest packet will do)
+ //
+
+ i = info->packetsize *2;
+
+ if (info->buffer != NULL) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+ //since in the constructor function buffer is malloc
+
+ if (i < 1024) {
+ i = 1024;
+ }
+ info->buffer = (uchar*)malloc(i);
+
+ ZIFlush(info);
+
+// FQTermConfig *config = new FQTermConfig(getPath(USER_CONFIG) + "fqterm.cfg");
+// QString strPrevSave = config->getItemValue("global", "previous");
+
+// if (strPrevSave.isEmpty()) {
+// strFileList = QFileDialog::getOpenFileNames(0, "Choose the files",
+// getPath(USER_CONFIG), "All files(*)");
+// } else {
+// strFileList = QFileDialog::getOpenFileNames(0, "Choose the files",
+// strPrevSave, "All files(*)");
+// }
+ FQTermFileDialog fileDialog(config_);
+ strFileList = fileDialog.getOpenNames("Choose a file to upload", "");
+ if (strFileList.count() != 0) {
+ QStringList::Iterator itFile = strFileList.begin();
+ QFileInfo fi(*itFile);
+ }
+
+ this->transferstate = transferstop;
+
+ // optional: send "rz\r" to remote end
+ if (DoInitRZ) {
+ if ((err = ZXmitStr((uchar*)"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;
+}
+
+
+int FQTermZmodem::ZmodemTFile(char *file, char *rfile, uint f0, uint f1, uint f2,
+ uint f3, int filesRem, int bytesRem, ZModem *info) {
+ if (file == NULL || (info->file = fopen(file, "rb")) == NULL) {
+ return ZmErrCantOpen;
+ }
+
+ info->fileEof = 0;
+ info->filename = file;
+ if (rfile != NULL) {
+ info->rfilename = rfile;
+ } else {
+ info->rfilename = strdup("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;
+ ZStatus(FileBegin, info->bytesRem, info->rfilename);
+ zmodemlog("ZmodemTFile[%s]: send ZFILE(%s)\n", sname(info), info->rfilename);
+ return sendFilename(info);
+}
+
+
+int FQTermZmodem::ZmodemTFinish(ZModem *info) {
+ int i;
+ 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));
+ i = ZXmitHdr(ZFIN, ZHEX, zeros, info);
+
+ return i;
+}
+
+
+
+int FQTermZmodem::ZmodemAbort(ZModem *info) {
+ uchar canistr[] = {
+ CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0
+ };
+ info->state = Done;
+ ZIFlush(info);
+ ZOFlush(info);
+
+ transferstate = transferstop; // transfer complete
+ ZmodemReset(info); //Tranfer complete, zmodem return to receive state
+
+ zmodemlog("ZmodemAbort[%s]: send CAN\n", sname(info));
+ return ZXmitStr(canistr, sizeof(canistr), info);
+}
+
+
+int FQTermZmodem::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;
+
+ if (info->buffer != NULL) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+
+ info->buffer = (uchar*)malloc(1024);
+
+ 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 FQTermZmodem::ZmodemRcv(uchar *str, int len, ZModem *info,
+ int &consumed_bytes) {
+ register uchar c;
+ int err;
+
+ zmodemlog("zmodemRcv called");
+
+ info->rcvlen = len;
+
+ while (--info->rcvlen >= 0) {
+ c = *str++;
+
+ if (c == CAN) {
+ if (++info->canCount >= 5) {
+ ZStatus(RmtCancel, 0, NULL);
+ consumed_bytes = len - info->rcvlen - 1;
+ return ZmErrCancel;
+ }
+ } else {
+ info->canCount = 0;
+ }
+
+ if (info->InputState == Ysend) {
+ if ((err = YsendChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ } else if (info->InputState == Yrcv) {
+ if ((err = YrcvChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ } else if (c != XON && c != XOFF) {
+ /* now look at what we have */
+ switch (info->InputState) {
+ case Idle:
+ if ((err = IdleChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ case Inhdr:
+ if ((err = HdrChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ case Indata:
+ if ((err = DataChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ case Finish:
+ if ((err = FinishChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ consumed_bytes = len - info->rcvlen - 1;
+ return 0;
+}
+
+
+
+
+int FQTermZmodem::ZmodemTimeout( /*ZModem *info*/) {
+ /* timed out while waiting for input */
+
+ ++info.timeoutCount;
+
+ zmodemlog("timeout %d [%s]\n", info.timeoutCount, sname(&info));
+
+ switch (info.state) {
+ /* receive */
+ case RStart:
+ /* waiting for INIT frame from other end */
+ if (info.timeoutCount > 4) {
+ return YmodemRInit(&info);
+ }
+
+ case RSinitWait:
+ case RFileName:
+ if (info.timeout > 0) {
+ ZStatus(SndTimeout, info.timeoutCount, NULL);
+ }
+ if (info.timeoutCount > 4) {
+ return ZmErrRcvTo;
+ }
+ info.state = RStart;
+ return SendRinit(&info);
+
+ case RCrc:
+ case RFile:
+ case RData:
+ ZStatus(SndTimeout, info.timeoutCount, NULL);
+ if (info.timeoutCount > 2) {
+ info.timeoutCount = 0;
+ info.state = RStart;
+ return SendRinit(&info);
+ }
+ return info.state == RCrc ? ResendCrcReq(&info): ResendRpos(&info);
+
+ case RFinish:
+ ZStatus(SndTimeout, info.timeoutCount, NULL);
+ return ZmDone;
+
+ case YRStart:
+ case YRDataWait:
+ case YRData:
+ case YREOF:
+ return YrcvTimeout(&info);
+
+ /* transmit */
+ case TStart:
+ /* waiting for INIT frame from other end */
+ case TInit:
+ /* sent INIT, waiting for ZACK */
+ case FileWait:
+ /* sent file header, waiting for ZRPOS */
+ case CrcWait:
+ /* sent file crc, waiting for ZRPOS */
+ case SendWait:
+ /* waiting for ZACK */
+ case SendEof:
+ /* sent EOF, waiting for ZACK */
+ case TFinish:
+ /* sent ZFIN, waiting for ZFIN */
+ case YTStart:
+ case YTFile:
+ case YTDataWait:
+ case YTData:
+ case YTEOF:
+ case YTFin:
+ ZStatus(RcvTimeout, 0, NULL);
+ return ZmErrRcvTo;
+
+ case Sending:
+ /* sending data subpackets, ready for int */
+ return SendMoreFileData(&info);
+
+ /* general */
+ case CommandData:
+ /* waiting for command data */
+ case StderrData:
+ /* waiting for stderr data */
+ return ZmErrSndTo;
+ case CommandWait:
+ /* waiting for command to execute */
+ return ZmErrCmdTo;
+ case Done:
+ return ZmDone;
+ default:
+ return 0;
+ }
+}
+
+
+
+int FQTermZmodem::ZmodemAttention(ZModem *info) {
+ /* attention received from remote end */
+ if (info->state == Sending) {
+ ZOFlush(info);
+ info->interrupt = 1;
+ }
+ return 0;
+}
+
+int FQTermZmodem::YmodemTInit(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 = (uchar*)malloc(1024);
+
+ ZIFlush(info);
+ ZFlowControl(0, info);
+
+ info->timeout = 60;
+
+ return 0;
+}
+
+
+int FQTermZmodem::XmodemTInit(ZModem *info) {
+ (void)YmodemTInit(info);
+ info->Protocol = XMODEM;
+ return 0;
+}
+
+
+int FQTermZmodem::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 = (uchar*)malloc(1024);
+ }
+
+ info->state = YRStart;
+ info->packetCount = -1;
+ info->timeoutCount = 0;
+ info->timeout = 10;
+ info->offset = 0;
+
+ ZIFlush(info);
+
+ return ZXmitStr((uchar*)"C", 1, info);
+}
+
+
+int FQTermZmodem::XmodemRInit(ZModem *info) {
+#if 0
+ int err;
+
+ state = Start;
+ protocol = prot;
+ ymodem = prot == Ymodem || prot == YmodemG;
+
+ if (ymodem) {
+ strcpy(xmDefPath, file);
+ } else {
+ strcpy(xmFilename, file);
+ }
+
+ eotCount = errorCount = errorCount2 = 0;
+
+ if (err = XmodemRStart()) {
+ return err;
+ }
+
+ state = Init;
+ packetId = ymodem ? 255 : 0;
+ packetCount = 0;
+
+ pktHdrLen = protocol == Xmodem ? 3 : 4;
+#endif
+ return 0;
+}
+
+
+ulong FQTermZmodem::FileCrc(char *name) {
+ ulong crc;
+ FILE *ifile = fopen(name, "rb");
+ int i;
+
+ if (ifile == NULL)
+ /* shouldn't happen, since we did access(2) */ {
+ return 0;
+ }
+
+ crc = 0xffffffff;
+
+ while ((i = fgetc(ifile)) != EOF) {
+ crc = UPDC32(i, crc);
+ }
+
+ fclose(ifile);
+ return ~crc;
+}
+
+
+
+const char *FQTermZmodem::sname(ZModem *info) {
+ return sname2(info->state);
+}
+
+const char *FQTermZmodem::sname2(ZMState state) {
+ const char *names[] = {
+ "RStart", "RSinitWait", "RFileName", "RCrc", "RFile", "RData", "RDataErr",
+ "RFinish", "TStart", "TInit", "FileWait", "CrcWait", "Sending", "SendWait",
+ "SendDone", "SendEof", "TFinish", "CommandData", "CommandWait", "StderrData",
+ "Done", "YTStart", "YTFile", "YTDataWait", "YTData", "YTEOF", "YTFin",
+ "YRStart", "YRDataWait", "YRData", "YREOF"
+ };
+
+ return names[(int)state];
+
+}
+
+
+int FQTermZmodem::ZXmitChr(uchar c, ZModem *info) {
+ // to be completed
+ return 0;
+}
+
+int FQTermZmodem::ZXmitStr(const uchar *str, int len, ZModem *info) {
+ //to be completed
+ telnet_->write((const char*)str, (uint)len);
+ return 0;
+}
+
+void FQTermZmodem::ZIFlush(ZModem *info) {
+ //to be completed
+}
+
+void FQTermZmodem::ZOFlush(ZModem *info) {
+ //to be completed
+}
+
+int FQTermZmodem::ZAttn(ZModem *info) {
+ //to be completed
+ return 0;
+}
+
+void FQTermZmodem::ZStatus(int type, int value, const char *status) {
+ emit ZmodemState(type, value, status);
+ switch (type) {
+ case RcvByteCount:
+ FQ_TRACE("zmodem", 5) << value << " bytes received.";
+ break;
+ case SndByteCount:
+ FQ_TRACE("zmodem", 5) << value << " bytes sent.";
+ break;
+ case RcvTimeout:
+ /* receiver did not respond, aborting */
+ FQ_TRACE("zmodem", 0) << "Zmodem time out!";
+ break;
+ case SndTimeout:
+ /* value is # of consecutive send timeouts */
+ FQ_TRACE("zmodem", 0) << "Zmodem time out after trying "
+ << value << " times";
+ break;
+ case RmtCancel:
+ /* remote end has cancelled */
+ FQ_TRACE("zmodem", 1) << "Zmodem canceled by remote peer";
+ break;
+ case ProtocolErr:
+ /* protocol error has occurred, val=hdr */
+ FQ_TRACE("zmodem", 0) << "Unhandled header " << value
+ << " at state " << status;
+ break;
+ case RemoteMessage:
+ /* message from remote end */
+ FQ_TRACE("zmodem", 0) << "Message from remote peer: " << status;
+ break;
+ case DataErr:
+ /* data error, val=error count */
+ FQ_TRACE("zmodem", 0) << "Data errors " << value;
+ break;
+ case FileErr:
+ /* error writing file, val=errno */
+ FQ_TRACE("zmodem", 0) << "Falied to write file.";
+ break;
+ case FileBegin:
+ /* file transfer begins, str=name */
+ FQ_TRACE("zmodem", 0) << "Starting file: " << status;
+ break;
+ case FileEnd:
+ /* file transfer ends, str=name */
+ FQ_TRACE("zmodem", 0) << "Finishing file: " << status;
+ break;
+ case FileSkip:
+ /* file being skipped, str=name */
+ FQ_TRACE("zmodem", 0) << "Skipping file: " << status;
+ break;
+ }
+}
+
+FILE *FQTermZmodem::ZOpenFile(char *name, ulong crc, ZModem *info) {
+ //to be complete
+ FILE *rval;
+ int apnd = 0;
+ QString strText;
+ strText = encoding2unicode(name, serverEncodingID);
+ QString str = FQTermPref::getInstance()->zmodemDir_ + strText;
+
+ QString zpath = QFileInfo(FQTermPref::getInstance()->zmodemDir_).absoluteFilePath();
+ QString path = QFileInfo(str).absoluteFilePath();
+
+ lastDownloadedFilename_ = path;
+
+ if (path.startsWith(zpath)) {
+ // Ensure that file is only saved under zmodemDir_.
+ // TODO: lazy, should use bbs2unicode
+ rval = fopen(str.toLocal8Bit(), apnd ? "ab" : "wb");
+ } else {
+ rval = NULL;
+ }
+
+ if (rval == NULL) {
+ perror(name);
+ }
+
+ return rval;
+}
+
+int FQTermZmodem::ZXmitHdr(int type, int format, const uchar data[4], ZModem *info) {
+ if (format == ZBIN && info->crc32) {
+ format = ZBIN32;
+ }
+
+ switch (format) {
+ case ZHEX:
+ return ZXmitHdrHex(type, data, info);
+
+ case ZBIN:
+ return ZXmitHdrBin(type, data, info);
+
+ case ZBIN32:
+ return ZXmitHdrBin32(type, data, info);
+
+ default:
+ return 0;
+ }
+}
+
+
+int FQTermZmodem::ZXmitHdrHex(int type, const uchar data[4], ZModem *info) {
+ uchar buffer[128];
+ uchar *ptr = buffer;
+ uint crc;
+ int i;
+
+ zmodemlog("sending %s: %2.2x %2.2x %2.2x %2.2x = %lx\n", hdrnames[type],
+ data[0], data[1], data[2], data[3], ZDec4(data));
+
+ *ptr++ = ZPAD;
+ *ptr++ = ZPAD;
+ *ptr++ = ZDLE;
+ *ptr++ = ZHEX;
+
+ ptr = putHex(ptr, type);
+ crc = updcrc(type, 0);
+ for (i = 4; --i >= 0; ++data) {
+ ptr = putHex(ptr, *data);
+ crc = updcrc(*data, crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ ptr = putHex(ptr, (crc >> 8) &0xff);
+ ptr = putHex(ptr, crc &0xff);
+ *ptr++ = '\r';
+ *ptr++ = '\n';
+ if (type != ZACK && type != ZFIN) {
+ *ptr++ = XON;
+ }
+
+ return ZXmitStr(buffer, ptr - buffer, info);
+}
+
+
+int FQTermZmodem::ZXmitHdrBin(int type, const uchar data[4], ZModem *info) {
+ uchar buffer[128];
+ uchar *ptr = buffer;
+ uint crc;
+ int len;
+
+ zmodemlog("sending %s: %2.2x %2.2x %2.2x %2.2x = %lx\n", hdrnames[type],
+ data[0], data[1], data[2], data[3], ZDec4(data));
+
+ *ptr++ = ZPAD;
+ *ptr++ = ZDLE;
+ *ptr++ = ZBIN;
+
+ ptr = putZdle(ptr, type, info);
+ crc = updcrc(type, 0);
+ for (len = 4; --len >= 0; ++data) {
+ ptr = putZdle(ptr, *data, info);
+ crc = updcrc(*data, crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ ptr = putZdle(ptr, (crc >> 8) &0xff, info);
+ ptr = putZdle(ptr, crc &0xff, info);
+
+ len = ptr - buffer;
+ return ZXmitStr(buffer, len, info);
+}
+
+int FQTermZmodem::ZXmitHdrBin32(int type, const uchar data[4], ZModem *info) {
+ uchar buffer[128];
+ uchar *ptr = buffer;
+ ulong crc;
+ int len;
+
+ zmodemlog("sending %s: %2.2x %2.2x %2.2x %2.2x = %lx\n", hdrnames[type],
+ data[0], data[1], data[2], data[3], ZDec4(data));
+
+ *ptr++ = ZPAD;
+ *ptr++ = ZDLE;
+ *ptr++ = ZBIN32;
+ ptr = putZdle(ptr, type, info);
+ crc = UPDC32(type, 0xffffffffL);
+ for (len = 4; --len >= 0; ++data) {
+ ptr = putZdle(ptr, *data, info);
+ crc = UPDC32(*data, crc);
+ }
+ crc = ~crc;
+ for (len = 4; --len >= 0; crc >>= 8) {
+ ptr = putZdle(ptr, crc &0xff, info);
+ }
+
+ len = ptr - buffer;
+ return ZXmitStr(buffer, len, info);
+}
+
+
+uchar *FQTermZmodem::putZdle(uchar *ptr, uchar c, ZModem *info) {
+ uchar c2 = c &0177;
+
+ if (c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 || c2 == 0177 || (c2 ==
+ 015 && connectionType == 0 /*&& info->atSign*/) ||
+#ifdef COMMENT
+ c2 == 035 || (c2 == '~' && info->lastCR) ||
+#endif /* COMMENT */
+ c2 == 035 || (c2 < 040 && info->escCtrl)) {
+ *ptr++ = ZDLE;
+ if (c == 0177) {
+ *ptr = ZRUB0;
+ } else if (c == 0377) {
+ *ptr = ZRUB1;
+ } else {
+ *ptr = c ^ 0100;
+ }
+ } else {
+ *ptr = c;
+ }
+
+ info->atSign = c2 == '@';
+ info->lastCR = c2 == '\r';
+
+ return ++ptr;
+}
+
+
+uchar *FQTermZmodem::ZEnc4(ulong n) {
+ static uchar buf[4];
+ buf[0] = n &0xff;
+ n >>= 8;
+ buf[1] = n &0xff;
+ n >>= 8;
+ buf[2] = n &0xff;
+ n >>= 8;
+ buf[3] = n &0xff;
+ return buf;
+}
+
+ulong FQTermZmodem::ZDec4(const uchar buf[4]) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+
+
+
+int FQTermZmodem::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((uchar*)"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 FQTermZmodem::YrcvTimeout(ZModem *info) {
+ switch (info->state) {
+ case YRStart:
+ if (info->timeoutCount >= 10) {
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrInitTo;
+ }
+ return ZXmitStr((uchar*)"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;
+ }
+}
+
+
+void FQTermZmodem::ZIdleStr(uchar *buffer, int len, ZModem *info) {
+ //to be completed
+}
+
+
+int FQTermZmodem::FinishChar(char c, register ZModem *info) {
+ if (c == 'O') {
+ if (++info->chrCount >= 2) {
+ return ZmDone;
+ }
+ } else {
+ info->chrCount = 0;
+ }
+ return 0;
+}
+
+
+
+int FQTermZmodem::DataChar(uchar c, register ZModem *info) {
+ if (c == ZDLE) {
+ info->escape = 1;
+ return 0;
+ }
+
+ if (info->escape) {
+ info->escape = 0;
+ switch (c) {
+ case ZCRCE:
+ case ZCRCG:
+ case ZCRCQ:
+ case ZCRCW:
+ info->PacketType = c;
+ info->crcCount = (info->DataType == ZBIN32) ? 4 : 2;
+ if (info->DataType == ZBIN) {
+ info->crc = updcrc(c, info->crc);
+ } else {
+ info->crc = UPDC32(c, info->crc);
+ }
+ return 0;
+ case ZRUB0:
+ c = 0177;
+ break;
+ case ZRUB1:
+ c = 0377;
+ break;
+ default:
+ c ^= 0100;
+ break;
+ }
+ }
+ if (connectionType == 0) {
+ if (lastPullByte == 0x0d && c == 0x00) {
+ lastPullByte = 0;
+ return 0;
+ } else if (lastPullByte == 0xff && c == 0xff) {
+ lastPullByte = 0;
+ return 0;
+ }
+ }
+ lastPullByte = c;
+
+ switch (info->DataType) {
+ /* TODO: are hex data packets ever used? */
+ case ZBIN:
+ info->crc = updcrc(c, info->crc);
+ if (info->crcCount == 0) {
+ info->buffer[info->chrCount++] = c;
+ } else if (--info->crcCount == 0) {
+ return ZDataReceived(info, (info->crc &0xffff) == 0);
+ }
+ break;
+
+
+ case ZBIN32:
+ info->crc = UPDC32(c, info->crc);
+ if (info->crcCount == 0) {
+ info->buffer[info->chrCount++] = c;
+ } else if (--info->crcCount == 0) {
+ return ZDataReceived(info, info->crc == 0xdebb20e3);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+int FQTermZmodem::HdrChar(uchar c, register ZModem *info) {
+ int i;
+ int crc = 0;
+
+ if (c == ZDLE) {
+ info->escape = 1;
+ return 0;
+ }
+
+ if (info->escape) {
+ info->escape = 0;
+ switch (c) {
+ case ZRUB0:
+ c = 0177;
+ break;
+ case ZRUB1:
+ c = 0377;
+ break;
+ default:
+ c ^= 0100;
+ break;
+ }
+ }
+
+ if (info->chrCount == 0) {
+ /* waiting for format */
+ switch (c) {
+ case ZHEX:
+ case ZBIN:
+ case ZBIN32:
+ info->DataType = c;
+ info->chrCount = 1;
+ info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL;
+ memset(info->hdrData, 0, sizeof(info->hdrData));
+ break;
+ default:
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ }
+ return 0;
+ }
+
+
+ switch (info->DataType) {
+ /* hex header is 14 hex digits, cr, lf. Optional xon is ignored */
+ case ZHEX:
+ if (info->chrCount <= 14 && !isxdigit(c)) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ }
+
+ if (info->chrCount <= 14) {
+ i = (info->chrCount - 1) / 2;
+ info->hdrData[i] = rcvHex(info->hdrData[i], c);
+ }
+
+ if (info->chrCount == 16) {
+ crc = 0;
+ for (i = 0; i < 7; ++i) {
+ crc = updcrc(info->hdrData[i], crc);
+ }
+ info->InputState = Idle;
+ info->chrCount = 0;
+ if ((crc &0xffff) != 0) {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ } else {
+ return ZProtocol(info);
+ }
+ }
+ else {
+ ++info->chrCount;
+ }
+ break;
+
+
+ case ZBIN:
+ /* binary header is type, 4 bytes data, 2 bytes CRC */
+ info->hdrData[info->chrCount - 1] = c;
+ info->crc = updcrc(c, info->crc);
+ if (++info->chrCount > 7) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ if ((crc &0xffff) != 0) {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ } else {
+ return ZProtocol(info);
+ }
+ }
+ break;
+
+
+ case ZBIN32:
+ /* binary32 header is type, 4 bytes data, 4 bytes CRC */
+ info->hdrData[info->chrCount - 1] = c;
+ info->crc = UPDC32(c, info->crc);
+ if (++info->chrCount > 9) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ if (info->crc != 0xdebb20e3)
+ /* see note below */ {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ } else {
+ return ZProtocol(info);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+
+
+
+int FQTermZmodem::IdleChar(uchar c, register ZModem *info) {
+ if (info->chrCount == 0) {
+ if (c == ZPAD) {
+ ++info->chrCount;
+ } else if (info->state == Sending && ++info->noiseCount > MaxNoise) {
+ info->waitflag = 1;
+ } else if (info->state == TStart && (c == 'C' || c == 'G' || c == NAK)) {
+ /* switch to ymodem */
+ info->state = YTStart;
+ info->InputState = Ysend;
+ info->Protocol = YMODEM;
+ return YsendChar(c, info);
+ } else {
+ ZIdleStr(&c, 1, info);
+ }
+ }
+
+ else {
+ switch (c) {
+ case ZPAD:
+ ++info->chrCount;
+ break;
+ case ZDLE:
+ info->InputState = Inhdr;
+ info->chrCount = 0;
+ break;
+ default:
+ while (--info->chrCount >= 0) {
+ ZIdleStr((uchar*)"*", 1, info);
+ }
+ info->chrCount = 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+
+
+int FQTermZmodem::YsendChar(char c, 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);
+ }
+ break;
+ 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;
+ }
+}
+
+
+
+int FQTermZmodem::ZProtocol(register ZModem *info) {
+ register StateTable *table;
+
+ zmodemlog("received %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
+ hdrnames[info->hdrData[0]], info->hdrData[1], info->hdrData[2],
+ info->hdrData[3], info->hdrData[4], ZDec4(info->hdrData + 1));
+
+ /* Flags are sent in F3 F2 F1 F0 order. Data is sent in P0 P1 P2 P3 */
+
+ info->timeoutCount = 0;
+ info->noiseCount = 0;
+
+ // zmodemTimer->start(info->timeout*1000);
+
+ table = tables[(int)info->state];
+ while (table->type != 99 && table->type != info->hdrData[0]) {
+ ++table;
+ }
+
+ zmodemlog(" state %s => %s, iflush=%d, oflush=%d, call %x\n", sname(info),
+ sname2(table->newstate), table->IFlush, table->OFlush, table->func);
+
+ info->state = table->newstate;
+
+ if (table->IFlush) {
+ info->rcvlen = 0;
+ ZIFlush(info);
+ }
+ if (table->OFlush) {
+ ZOFlush(info);
+ }
+ return (this->*(table->func))(info);
+}
+
+
+int FQTermZmodem::ZDataReceived(register ZModem *info, int crcGood) {
+ switch (info->state) {
+ case RSinitWait:
+ return GotSinitData(info, crcGood);
+ case RFileName:
+ return GotFileName(info, crcGood);
+ case RData:
+ return GotFileData(info, crcGood);
+ case CommandData:
+ return GotCommandData(info, crcGood);
+ case StderrData:
+ return GotStderrData(info, crcGood);
+ default:
+ return ZPF(info);
+ }
+}
+
+
+int FQTermZmodem::ZPF(ZModem *info) {
+ info->waitflag = 1; /* pause any in-progress transmission */
+ ZStatus(ProtocolErr, info->hdrData[0], sname(info));
+ return 0;
+}
+
+
+int FQTermZmodem::Ignore(ZModem *info) {
+ return 0;
+}
+
+
+int FQTermZmodem::AnswerChallenge(register ZModem *info) {
+ return ZXmitHdrHex(ZACK, info->hdrData + 1, info);
+}
+
+
+int FQTermZmodem::GotAbort(register ZModem *info) {
+ ZStatus(RmtCancel, 0, NULL);
+ return ZXmitHdrHex(ZFIN, zeros, info);
+}
+
+
+int FQTermZmodem::GotCancel(ZModem *info) {
+ return ZmErrCancel;
+}
+
+
+int FQTermZmodem::GotCommand(ZModem *info) {
+ uchar rbuf[4];
+ /* TODO: add command capability */
+
+
+ /// EPERM not defined????????
+
+ rbuf[0] = EPERM;
+ rbuf[1] = rbuf[2] = rbuf[3] = 0;
+ return ZXmitHdrHex(ZCOMPL, rbuf, info);
+}
+
+int FQTermZmodem::GotStderr(register ZModem *info) {
+ info->InputState = Indata;
+ info->chrCount = 0;
+ return 0;
+}
+
+
+int FQTermZmodem::RetDone(ZModem *info) {
+
+
+ return ZmDone;
+}
+
+
+int FQTermZmodem::GotCommandData(register ZModem *info, int crcGood) {
+ /* TODO */
+ return 0;
+}
+
+
+int FQTermZmodem::GotStderrData(register ZModem *info, int crcGood) {
+ info->buffer[info->chrCount] = '\0';
+ ZStatus(RemoteMessage, info->chrCount, (char*)info->buffer);
+ return 0;
+}
+
+
+int FQTermZmodem::GotFileName(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 FQTermZmodem::ResendCrcReq(ZModem *info) {
+ zmodemlog("ResendCrcReq[%s]: send ZCRC\n", sname(info));
+ return ZXmitHdrHex(ZCRC, zeros, info);
+}
+
+
+int FQTermZmodem::GotSinitData(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);
+}
+
+
+int FQTermZmodem::ResendRpos(ZModem *info) {
+ zmodemlog("ResendRpos[%s]: send ZRPOS(%ld)\n", sname(info), info->offset);
+ return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info);
+}
+
+
+int FQTermZmodem::GotFileData(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, zerrno, NULL);
+ info->state = RFinish;
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return fileError(info, ZFERR, zerrno);
+ }
+
+ 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;
+}
+
+int FQTermZmodem::SendRinit(ZModem *info) {
+ uchar dbuf[4];
+
+
+
+#ifdef COMMENT
+ if (info->timeoutCount >= 5)
+ /* TODO: switch to Ymodem */
+#endif /* COMMENT */
+ {
+ transferstate = transferstart;
+ }
+ //transfer would be active, it must be set to false when transfer complete or abort
+ 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);
+}
+
+
+int FQTermZmodem::SendMoreFileData(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;
+ int c = 0, c2, atSign = 0;
+ ulong crc;
+ uchar *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, len, uchar(type), info->buffer, 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;
+}
+
+
+uint FQTermZmodem::rcvHex(uint i, char c) {
+ if (c <= '9') {
+ c -= '0';
+ } else if (c <= 'F') {
+ c -= 'A' - 10;
+ } else {
+ c -= 'a' - 10;
+ }
+ return (i << 4) + c;
+}
+
+
+int FQTermZmodem::dataSetup(register ZModem *info) {
+ info->InputState = Indata;
+ info->chrCount = 0;
+ info->crcCount = 0;
+ info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL;
+ return 0;
+}
+
+int FQTermZmodem::ZWriteFile(uchar *buffer, int len, FILE *file, ZModem*) {
+ return (int)fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys;
+}
+
+int FQTermZmodem::ZCloseFile(ZModem *info) {
+ //to be completed
+ fclose(info->file);
+ return 0;
+}
+
+void FQTermZmodem::ZFlowControl(int onoff, ZModem *info) {
+ //to be completed
+}
+
+int FQTermZmodem::GotSinit(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);
+}
+
+int FQTermZmodem::GotFile(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);
+}
+
+int FQTermZmodem::GotFin(ZModem *info) {
+ int i;
+ zmodemlog("GotFin[%s]: send ZFIN\n", sname(info));
+ info->InputState = Finish;
+ info->chrCount = 0;
+ if (info->filename != NULL) {
+ free(info->filename);
+ }
+ i = ZXmitHdrHex(ZFIN, zeros, info);
+ ZmodemReset(info);
+ transferstate = transferstop; // transfer complete
+ return i;
+}
+
+
+int FQTermZmodem::GotData(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);
+}
+
+int FQTermZmodem::GotEof(ZModem *info) {
+ zmodemlog("GotEof[%s]: offset=%ld\n", sname(info), info->offset);
+ if (ZDec4(info->hdrData + 1) != info->offset) {
+ zmodemlog("zdec4(info->hdrdata+1)=%ld\n", ZDec4(info->hdrData + 1));
+ 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);
+}
+
+int FQTermZmodem::GotFreecnt(ZModem *info) {
+ /* TODO: how do we find free space on system? */
+ return ZXmitHdrHex(ZACK, ZEnc4(0xffffffff), info);
+}
+
+
+int FQTermZmodem::GotFileCrc(ZModem *info) {
+ zmodemlog("GotFileCrc[%s]: call requestFile\n", sname(info));
+ return requestFile(info, ZDec4(info->hdrData + 1));
+}
+
+int FQTermZmodem::requestFile(ZModem *info, ulong 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, info->len, info->filename);
+ return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info);
+ }
+}
+
+void FQTermZmodem::parseFileName(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);
+}
+
+
+
+int FQTermZmodem::fileError(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);
+}
+
+
+int FQTermZmodem::ProcessPacket(ZModem *info) {
+ int idx = (uchar)info->buffer[0];
+ int idxc = (uchar)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 = (uchar)info->buffer[info->pktLen - 2] << 8
+ | (uchar)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((uchar*)"C", 1, info);
+ }
+
+
+ if (ZWriteFile(info->buffer + 2, info->pktLen - 4, info->file, info)) {
+ ZStatus(FileErr, zerrno, NULL);
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrSys;
+ }
+ info->offset += info->pktLen - 4;
+ ZStatus(RcvByteCount, info->offset, NULL);
+
+ (void)acceptPacket(info);
+ return 0;
+}
+
+int FQTermZmodem::rejectPacket(ZModem *info) {
+ info->timeout = 10;
+ return ZXmitStr(NakStr, 1, info);
+}
+
+
+int FQTermZmodem::acceptPacket(ZModem *info) {
+ info->state = YRDataWait;
+ info->timeout = 10;
+ return ZXmitStr(AckStr, 1, info);
+}
+
+
+int FQTermZmodem::calcCrc(uchar *str, int len) {
+ int crc = 0;
+ while (--len >= 0) {
+ crc = updcrc(*str++, crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ return crc &0xffff;
+}
+
+
+char *FQTermZmodem::strdup(const char *str) {
+ char *rval;
+ int len = strlen(str) + 1;
+ rval = (char*)malloc(len);
+ strcpy(rval, str);
+ return rval;
+}
+
+
+int FQTermZmodem::ZXmitData(int format, int len, uchar term, uchar *data, ZModem
+ *info) {
+ uchar *ptr = info->buffer;
+ uint crc;
+
+ if (format == ZBIN && info->crc32) {
+ format = ZBIN32;
+ }
+
+ zmodemlog("ZXmiteData: fmt=%c, len=%d, term=%c\n", format, len, term);
+
+ crc = (format == ZBIN) ? 0 : 0xffffffff;
+
+ while (--len >= 0) {
+ if (format == ZBIN) {
+ crc = updcrc(*data, crc);
+ } else {
+ crc = UPDC32(*data, crc);
+ }
+ ptr = putZdle(ptr, *data++, info);
+ }
+
+ *ptr++ = ZDLE;
+ if (format == ZBIN) {
+ crc = updcrc(term, crc);
+ } else {
+ crc = UPDC32(term, crc);
+ }
+ *ptr++ = term;
+ if (format == ZBIN) {
+ 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);
+ }
+ }
+
+ return ZXmitStr(info->buffer, ptr - info->buffer, info);
+}
+
+
+
+int FQTermZmodem::YXmitData(uchar *buffer, int len, ZModem *info) {
+ uchar hdr[3];
+ uchar trail[2];
+ ulong 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);
+ }
+}
+
+
+int FQTermZmodem::YSendFilename(ZModem *info) {
+ int i, len;
+ uchar obuf[1024];
+ uchar *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);
+}
+
+int FQTermZmodem::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);
+}
+
+
+int FQTermZmodem::YSendFin(ZModem *info) {
+ uchar obuf[128];
+
+ info->state = YTFin;
+ info->packetCount = 0;
+
+ memset(obuf, 0, 128);
+
+ return YXmitData(obuf, 128, info);
+}
+
+int FQTermZmodem::sendFilename(ZModem *info) {
+ int err;
+ int i;
+ uchar obuf[2048];
+ uchar *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);
+}
+
+int FQTermZmodem::GotRinit(ZModem *info) {
+ if (strFileList.count() == 0) {
+ return ZmodemTFinish(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
+ */
+
+ transferstate = transferstart;
+
+ 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;
+ }
+ // get filenames to transfer
+ zmodemlog("GotRinit[%s]\n", sname(info));
+
+ if (AlwaysSinit || info->zsinitflags != 0 || info->attn != NULL) {
+ SendZSInit(info);
+ }
+
+
+ itFile = strFileList.begin();
+ QFileInfo fi(*itFile);
+ FQ_TRACE("zmodem", 0) << "Number of files to be transfered: "
+ << strFileList.count();
+ char *filename = strdup(fi.absoluteFilePath().toLatin1());
+ char *rfilename = strdup(fi.fileName().toLatin1());
+ ZmodemTFile(filename, rfilename, 0, 0, 0, 0, strFileList.count(), fi.size(),
+ info);
+ strFileList.erase(itFile);
+ return ZmDone;
+}
+
+
+int FQTermZmodem::SendZSInit(ZModem *info) {
+ char tmp = '\0';
+ int err;
+ //char *at = (info->attn != NULL) ? info->attn : "" ;
+ char *at;
+ if (info->attn != NULL) {
+ at = info->attn;
+ } else {
+ at = &tmp;
+ }
+ uchar 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, (uchar*)at, info))) {
+ return err;
+ }
+ return 0;
+}
+
+
+int FQTermZmodem::SendFileCrc(ZModem *info) {
+ ulong crc;
+
+ crc = FileCrc(info->filename);
+
+ zmodemlog("SendFileCrc[%s]: %lx\n", sname(info), crc);
+
+ return ZXmitHdrHex(ZCRC, ZEnc4(crc), info);
+}
+
+int FQTermZmodem::GotSendAck(ZModem *info) {
+ ulong offset;
+
+ offset = ZDec4(info->hdrData + 1);
+
+ if (offset > info->lastOffset) {
+ info->lastOffset = offset;
+ }
+
+ zmodemlog("GotSendAck[%s]: %lx\n", sname(info), info->offset);
+
+ return 0; /* DONT send more data, that will happen
+ * later anyway */
+}
+
+int FQTermZmodem::GotSendDoneAck(ZModem *info) {
+ ulong 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);
+}
+
+int FQTermZmodem::GotSendNak(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);
+}
+
+int FQTermZmodem::GotSendWaitAck(ZModem *info) {
+
+ ulong offset;
+ int err;
+
+ offset = ZDec4(info->hdrData + 1);
+
+ FQ_TRACE("zmodem", 10) << "last = " << info->lastOffset
+ << ", now = " << offset;
+
+ if (offset > info->lastOffset) {
+ info->lastOffset = offset;
+ }
+
+ //receiver return -1 without setting this flag, kingson 00:07 14-07-04
+ info->waitflag = 1;
+
+ zmodemlog("GotSendWaitAck[%s]\n", sname(info), offset);
+
+ if ((err = ZXmitHdr(ZDATA, ZBIN, info->hdrData + 1, info))) {
+ return err;
+ }
+ return SendMoreFileData(info);
+}
+
+int FQTermZmodem::SkipFile(ZModem *info) {
+ zmodemlog("SkipFile[%s]\n", sname(info));
+ ZStatus(FileEnd, 0, info->rfilename);
+ fclose(info->file);
+
+ // stupid SMTH doesnt send further command, kick
+ // lets send files in the list
+ info->state = TStart;
+ return GotRinit(info);
+}
+
+int FQTermZmodem::GotSendPos(ZModem *info) {
+ ZStatus(DataErr, ++info->errCount, NULL);
+ info->waitflag = 1; /* next pkt should wait, to resync */
+ FQ_TRACE("zmodem", 9) << "GotSendPos, offset = " << info->offset;
+ zmodemlog("GotSendPos[%s] %lx\n", sname(info), info->offset);
+ return startFileData(info);
+}
+
+int FQTermZmodem::SendFileData(ZModem *info) {
+ info->waitflag = 0;
+ return startFileData(info);
+}
+
+int FQTermZmodem::ResendEof(ZModem *info) {
+ return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info);
+}
+
+int FQTermZmodem::OverAndOut(ZModem *info) {
+ zmodemlog("OverAndOut[%s]\n", sname(info));
+
+ ZXmitStr((uchar*)"OO", 2, info);
+
+ transferstate = transferstop; // transfer complete
+
+ ZmodemReset(info); //Tranfer complete, zmodem return to receive state
+
+ return ZmDone;
+}
+
+int FQTermZmodem::startFileData(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]: %lx\n", sname(info), info->offset);
+
+ if ((err = ZXmitHdr(ZDATA, ZBIN, ZEnc4(info->offset), info))) {
+ return err;
+ }
+ return SendMoreFileData(info);
+}
+
+uchar *FQTermZmodem::putHex(uchar *ptr, uchar c) {
+ *ptr++ = hexChars[(c >> 4) &0xf];
+ *ptr++ = hexChars[c &0xf];
+ return ptr;
+}
+
+
+int FQTermZmodem::ZmodemReset(ZModem *info) {
+ zmodemlog("\nZmodemReset\n");
+
+ ZmodemRInit(info);
+
+ return 0;
+}
+
+
+void FQTermZmodem::zmodemlog(const char *fmt, ...) {
+ // only for debug
+#ifdef FQTERM_ZMODEM_DEBUG
+ va_list ap;
+ struct timeval tv;
+ struct tm *tm;
+ static int do_ts = 1;
+
+ zmodemlogfile = fopen("zmodem.log", "a");
+
+ if (zmodemlogfile == NULL) {
+ return ;
+ }
+
+ if (do_ts) {
+ gettimeofday(&tv, NULL);
+ tm = localtime(&tv.tv_sec);
+ fprintf(zmodemlogfile, "%2.2d:%2.2d:%2.2d.%2.2ld: ", tm->tm_hour, tm->tm_min,
+ tm->tm_sec, tv.tv_usec / 10000);
+ }
+ do_ts = strchr(fmt, '\n') != NULL;
+
+ va_start(ap, fmt);
+ vfprintf(zmodemlogfile, fmt, ap);
+ va_end(ap);
+
+ fclose(zmodemlogfile);
+#endif
+
+}
+
+void FQTermZmodem::zmodemCancel() {
+ ZmodemAbort(&info);
+}
+
+} // namespace FQTerm
+
+#include "fqterm_zmodem.moc"
+