From d2b24c8dabe91420538e94f64870484ff635c503 Mon Sep 17 00:00:00 2001 From: Andrew Pamment Date: Sun, 3 Apr 2016 09:26:17 +1000 Subject: [PATCH] Added Experimental File areas --- Makefile | 5 +- Xmodem/LICENSE | 19 + Xmodem/Makefile | 42 ++ Xmodem/checkcrc.c | 197 ++++++ Xmodem/crc.c | 182 ++++++ Xmodem/crctab.c | 154 +++++ Xmodem/crctab.h | 13 + Xmodem/main.c | 913 +++++++++++++++++++++++++++ Xmodem/network.c | 155 +++++ Xmodem/receive.c | 600 ++++++++++++++++++ Xmodem/send.c | 327 ++++++++++ Xmodem/seriallog.c | 86 +++ Xmodem/seriallog.h | 11 + Xmodem/utils.c | 133 ++++ Xmodem/xmodem.h | 111 ++++ Xmodem/xmodemr.c | 417 +++++++++++++ Xmodem/xmodemt.c | 410 +++++++++++++ Xmodem/zmodem.c | 900 +++++++++++++++++++++++++++ Xmodem/zmodem.h | 709 +++++++++++++++++++++ Xmodem/zmodemdump.c | 582 ++++++++++++++++++ Xmodem/zmodemr.c | 750 +++++++++++++++++++++++ Xmodem/zmodemsys.c | 21 + Xmodem/zmodemt.c | 1152 +++++++++++++++++++++++++++++++++++ Xmodem/zmutil.c | 408 +++++++++++++ ansis_default/filemenu.ans | 14 + ansis_default/mainmenu.ans | 2 +- bbs.c | 7 + bbs.h | 4 + config_default/filesgen.ini | 1 + files.c | 736 ++++++++++++++++++++++ main_menu.c | 6 +- 31 files changed, 9063 insertions(+), 4 deletions(-) create mode 100644 Xmodem/LICENSE create mode 100644 Xmodem/Makefile create mode 100644 Xmodem/checkcrc.c create mode 100644 Xmodem/crc.c create mode 100644 Xmodem/crctab.c create mode 100644 Xmodem/crctab.h create mode 100644 Xmodem/main.c create mode 100644 Xmodem/network.c create mode 100644 Xmodem/receive.c create mode 100644 Xmodem/send.c create mode 100644 Xmodem/seriallog.c create mode 100644 Xmodem/seriallog.h create mode 100644 Xmodem/utils.c create mode 100644 Xmodem/xmodem.h create mode 100644 Xmodem/xmodemr.c create mode 100644 Xmodem/xmodemt.c create mode 100644 Xmodem/zmodem.c create mode 100644 Xmodem/zmodem.h create mode 100644 Xmodem/zmodemdump.c create mode 100644 Xmodem/zmodemr.c create mode 100644 Xmodem/zmodemsys.c create mode 100644 Xmodem/zmodemt.c create mode 100644 Xmodem/zmutil.c create mode 100644 ansis_default/filemenu.ans create mode 100644 files.c diff --git a/Makefile b/Makefile index b131934..d0ae990 100644 --- a/Makefile +++ b/Makefile @@ -2,13 +2,14 @@ CC=cc CFLAGS=-I/usr/local/include DEPS = bbs.h JAMLIB = jamlib/jamlib.a +ZMODEM = Xmodem/libzmodem.a -OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o +OBJ = inih/ini.o bbs.o main.o users.o main_menu.o mail_menu.o doors.o bbs_list.o chat_system.o email.o files.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) magicka: $(OBJ) - $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3 $(JAMLIB) -lutil + $(CC) -o magicka -o $@ $^ $(CFLAGS) -L/usr/local/lib -lsqlite3 $(JAMLIB) $(ZMODEM) -lutil .PHONY: clean diff --git a/Xmodem/LICENSE b/Xmodem/LICENSE new file mode 100644 index 0000000..baadbe3 --- /dev/null +++ b/Xmodem/LICENSE @@ -0,0 +1,19 @@ +Copyright © 2001 Edward A. Falk + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/Xmodem/Makefile b/Xmodem/Makefile new file mode 100644 index 0000000..37d2557 --- /dev/null +++ b/Xmodem/Makefile @@ -0,0 +1,42 @@ +# $Id: Makefile,v 1.2 2001/10/25 23:56:29 efalk Exp $ + +INC = -I.. + +#OPT = -g +OPT = -O + +CFLAGS = $(OPT) $(INC) -DHAVE_STRDUP + +AR = ar +RANLIB = ranlib + +HDRS = crctab.h seriallog.h xmodem.h zmodem.h + +SRCS = crctab.c seriallog.c zmodem.c zmodemr.c zmodemsys.c zmodemt.c zmutil.c + +OBJS = $(SRCS:.c=.o) + +libzmodem.a: $(OBJS) + -rm -f libzmodem.a + $(AR) cru libzmodem.a $(OBJS) + $(RANLIB) libzmodem.a + +clean: + rm -f *.o *.s *.i + +clobber: clean + rm -f *.a tags + +tags: $(SRCS) $(HDRS) + ctags *.[ch] + +.SUFFIXES: .i .s + +.c.i: + $(CC) -E $(CFLAGS) $*.c > $@ + +.c.s: + $(CC) -S $(CFLAGS) $*.c + +depend: + makedepend -- $(CFLAGS) -- $(SRCS) diff --git a/Xmodem/checkcrc.c b/Xmodem/checkcrc.c new file mode 100644 index 0000000..1104bde --- /dev/null +++ b/Xmodem/checkcrc.c @@ -0,0 +1,197 @@ +#ifndef lint +static const char rcsid[] = "$Id: checkcrc.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + +#include +#include + +#include "crctab.h" + + +static u_char msg0[] = { + 0x2a, 0x18, 0x43, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0xef, 0x92, 0x8c,} ; +static u_char msg0c[] = { + 0x2a, 0x18, 0x43, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0xef, 0x92, 0x8c, 0x0b, 0xdf,} ; +static u_char msg1[] = { + 0x0a, 0x23, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x73, + 0x79, 0x73, 0x2f, 0x74, 0x65, 0x72, 0x6d, 0x69, + 0x6f, 0x73, 0x2e, 0x68, 0x3e, 0x0a, 0x23, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x22, + 0x78, 0x6d, 0x6f, 0x64, 0x65, 0x6d, 0x2e, 0x68, + 0x22, 0x0a, 0x0a, 0x0a, 0x69, 0x6e, 0x74, 0x0a, + 0x73, 0x65, 0x6e, 0x64, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x28, 0x29, 0x0a, 0x7b, 0x0a, 0x09, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x73, + 0x65, 0x6e, 0x64, 0x46, 0x6c, 0x75, 0x73, 0x68, + 0x28, 0x43, 0x41, 0x4e, 0x29, 0x20, 0x7c, 0x7c, + 0x20, 0x73, 0x65, 0x6e, 0x64, 0x46, 0x6c, 0x75, + 0x73, 0x68, 0x28, 0x43, 0x41, 0x4e, 0x29, 0x20, + 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x0a, 0x09, 0x2f, + 0x2a, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6f, + 0x6e, 0x65, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x2c, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, 0x6f, 0x6e, + 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x6f, 0x6e, 0x20, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x2a, 0x2f, + 0x0a, 0x69, 0x6e, 0x74, 0x0a, 0x73, 0x65, 0x6e, + 0x64, 0x46, 0x6c, 0x75, 0x73, 0x68, 0x28, 0x63, + 0x29, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x09, + 0x63, 0x20, 0x3b, 0x0a, 0x7b, 0x0a, 0x09, 0x2f, + 0x2a, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x2c, + 0x20, 0x66, 0x6c, 0x75, 0x73, 0x68, 0x20, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, 0x6f, 0x72, + 0x74, 0x20, 0x2a, 0x2f, 0x0a, 0x09, 0x2f, 0x2a, + 0x20, 0x54, 0x4f, 0x44, 0x4f, 0x3a, 0x20, 0x63, + 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, 0x61, 0x20, + 0x77, 0x61, 0x79, 0x20, 0x18, 0x69, 0x4c, 0x5b, + 0x62, 0x0f, + 0x2a, 0x18, 0x43, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0xbc, 0xef, 0x92, 0x8c, 0x0a, 0x23, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, + 0x3c, 0x73, 0x79, 0x73, 0x2f, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6f, 0x73, 0x2e, 0x68, 0x3e, 0x0a, + 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x20, 0x22, 0x78, 0x6d, 0x6f, 0x64, 0x65, 0x6d, + 0x2e, 0x68, 0x22, 0x0a, 0x0a, 0x0a, 0x69, 0x6e, + 0x74, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x28, 0x29, 0x0a, 0x7b, + 0x0a, 0x09, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x20, 0x73, 0x65, 0x6e, 0x64, 0x46, 0x6c, 0x75, + 0x73, 0x68, 0x28, 0x43, 0x41, 0x4e, 0x29, 0x20, + 0x7c, 0x7c, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x46, + 0x6c, 0x75, 0x73, 0x68, 0x28, 0x43, 0x41, 0x4e, + 0x29, 0x20, 0x3b, 0x0a, 0x7d, 0x0a, 0x0a, 0x0a, + 0x09, 0x2f, 0x2a, 0x20, 0x73, 0x65, 0x6e, 0x64, + 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2c, 0x20, + 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x6e, + 0x6f, 0x6e, 0x7a, 0x65, 0x72, 0x6f, 0x20, 0x6f, + 0x6e, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x2a, 0x2f, 0x0a, 0x69, 0x6e, 0x74, 0x0a, 0x73, + 0x65, 0x6e, 0x64, 0x46, 0x6c, 0x75, 0x73, 0x68, + 0x28, 0x63, 0x29, 0x0a, 0x09, 0x63, 0x68, 0x61, + 0x72, 0x09, 0x63, 0x20, 0x3b, 0x0a, 0x7b, 0x0a, + 0x09, 0x2f, 0x2a, 0x20, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x2c, 0x20, 0x66, 0x6c, 0x75, 0x73, 0x68, + 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x20, 0x70, + 0x6f, 0x72, 0x74, 0x20, 0x2a, 0x2f, 0x0a, 0x09, + 0x2f, 0x2a, 0x20, 0x54, 0x4f, 0x44, 0x4f, 0x3a, + 0x20, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x20, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x20, + 0x61, 0x20, 0x77, 0x61, 0x79, 0x20, 0x18, 0x6b, + 0x60, 0x3a, 0x6c, 0xe1, + } ; +static u_char msg2[] = {1} ; + +#ifdef COMMENT +#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) +#define updcrc(cp, crc) ( crctab[((crc>>8)^cp) & 255] ^ (crc<<8) ) +#endif /* COMMENT */ + +static int addcrc(int data, int crc) ; + + +main() +{ + u_short crc16 ; + u_long crc32 ; + int i,j ; + u_long k ; + u_char *msg = msg0 ; + int len = sizeof(msg0) ; + + + /* crc16 of message */ + + crc16 = 0 ; + crc32 = 0xffffffff ; + + for(i=0; i>8, crc16) ; + crc16 = addcrc(j&0xff, crc16) ; + + printf("crc16=%4.4hx, ~crc16=%4.4hx\n", crc16, ~crc16&0xffff) ; + + + +#ifdef COMMENT + crc16 = 0 ; + crc32 = 0xffffffff ; + + for(i=0; i>8) ; +#endif /* COMMENT */ + + i = crc16 ; + crc16 = updcrc(i&0xff, crc16) ; + crc16 = updcrc(i>>8, crc16) ; + + k = ~crc32 ; + crc32 = updcrc(k&0xff, crc32) ; + crc32 = updcrc((k>>8)&0xff, crc32) ; + crc32 = updcrc((k>>16)&0xff, crc32) ; + crc32 = updcrc((k>>24)&0xff, crc32) ; + + printf("crc16=%4.4hx, ~crc16=%4.4hx\n", crc16, ~crc16&0xffff) ; + printf("crc32=%8.8x, ~crc=%8.8x\n", crc32,~crc32) ; +#endif /* COMMENT */ + + exit(0) ; +} + + +static int +addcrc(int data, int crc) +{ + int j ; + crc ^= (unsigned short) data<<8; + for(j=0; j<8; ++j) { + if( crc & 0x8000 ) + crc = (crc<<1) ^ 0x1021; + else + crc <<= 1 ; + } + return crc & 0xffff ; +} diff --git a/Xmodem/crc.c b/Xmodem/crc.c new file mode 100644 index 0000000..cd25ea8 --- /dev/null +++ b/Xmodem/crc.c @@ -0,0 +1,182 @@ +#ifndef lint +static const char rcsid[] = "$Id: crc.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + +/*% cc -O -K -dos % -o crc.exe +*/ + +/* + * Crc - 32 BIT ANSI X3.66 CRC checksum files + */ +#include +#define OK 0 +#define ERROR (-1) +#define LINT_ARGS + +/**********************************************************************\ +|* *| +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +|* *| +\**********************************************************************/ + +/* Need an unsigned type capable of holding 32 bits; */ +typedef unsigned long int UNS_32_BITS; + +/* + * 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: */ +/* */ +/* 1. 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. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. 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. */ + +static UNS_32_BITS crc_32_tab[] = { /* 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(octet, crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) + +int Block = 0; /* Pad file with 032's to multiple of Block */ + +main(argc, argv) +char **argv; +{ + register errors = 0; + + if (! strcmp(argv[1], "-x")) { + Block = 128; --argc; ++argv; + } + if (! strcmp(argv[1], "-k")) { + Block = 1024; --argc; ++argv; + } + while( --argc > 0) + errors |= crc32file( *++argv); + exit(errors != 0); +} + +crc32file(name) +char *name; +{ + register FILE *fin; + register unsigned long oldcrc32; + register unsigned long crc32; + register unsigned long oldcrc; + register c; + register long charcnt; + register long l; + + oldcrc32 = 0xFFFFFFFF; charcnt = 0; +#ifdef M_I86SM + if ((fin=fopen(name, "rb"))==NULL) +#else + if ((fin=fopen(name, "r"))==NULL) +#endif + { + perror(name); + return ERROR; + } + while ((c=getc(fin))!=EOF) { + ++charcnt; + oldcrc32 = UPDC32(c, oldcrc32); + } + + if (ferror(fin)) { + perror(name); + fclose(fin); return ERROR; + } + else { + if (Block) { + for (l = charcnt; l % Block; ++l) + oldcrc32 = UPDC32(032, oldcrc32); + } + crc32 = oldcrc32; oldcrc = oldcrc32 = ~oldcrc32; + + printf("%08lX %7ld ", oldcrc, charcnt); + if (Block == 128) + printf("%5ld+%3ld ", charcnt/Block, charcnt%Block); + if (Block == 1024) + printf("%5ld+%4ld ", charcnt/Block, charcnt%Block); + printf(" %s\n", name); + } + + fclose(fin); return OK; +} + diff --git a/Xmodem/crctab.c b/Xmodem/crctab.c new file mode 100644 index 0000000..86654ab --- /dev/null +++ b/Xmodem/crctab.c @@ -0,0 +1,154 @@ +#ifndef lint +static const char rcsid[] = "$Id: crctab.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + +/* + * 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 }; + +#ifdef NFGM +long +UPDC32(b, c) +long c; +{ + return (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF)); +} + +#else + +#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF)) +#endif + +/* End of crctab.c */ diff --git a/Xmodem/crctab.h b/Xmodem/crctab.h new file mode 100644 index 0000000..478a5ca --- /dev/null +++ b/Xmodem/crctab.h @@ -0,0 +1,13 @@ +/* @(#)crctab.h 1.2 96/09/13 */ + +/* + * Crc calculation stuff. See crctab.c + */ + +extern unsigned short crctab[256] ; + +#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) + +extern unsigned long cr3tab[] ; + +#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF)) diff --git a/Xmodem/main.c b/Xmodem/main.c new file mode 100644 index 0000000..587ae68 --- /dev/null +++ b/Xmodem/main.c @@ -0,0 +1,913 @@ +#ifndef lint +static const char rcsid[] = "$Id: main.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ; +#endif + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @ @ @@@ @@@ @ @ + * @@ @@ @ @ @ @@ @ + * @ @ @ @@@@@ @ @ @ @ + * @ @ @ @ @ @ @ @@ + * @ @ @ @ @ @@@ @ @ + * + * MAIN - demonstration zmodem program + * + * This program implements the x,y,z-modem protocols, using the + * zmodem library. + * + * Edward A. Falk + * + * Jan, 1995 + * + * + * + **********/ + +static char usage [] = +"usage: zmodem -r [options]\n\ + zmodem -s [options] file [file ...]\n\ + -r receive files\n\ + -s send files\n\ + - file allows you to transmit files with '-' in their names\n\ + -v verbose, more v's increase messages.\n\ + -l /dev/ttyX use specified serial port instead of /dev/tty\n\ + -b baud set baud rate\n\ + -x file use xmodem, specify filename\n\ + -y use ymodem\n\ + -k use 1k, packets (ymodem, xmodem)\n\ + -win nn set sliding window to nn bytes (send)\n\ + -buf nn set input buffer size to nn bytes (receive)\n\ + -L nn set packet length\n\ + -e escape control characters\n\ + -a ascii text\n\ + -i image (binary data)\n\ + -resume continue an interrupted transfer\n\ + -new transfer only if newer\n\ + -newl transfer only if newer or longer\n\ + -diff transfer only if dates or lengths differ\n\ + -crc transfer only if length or crc different\n\ + -apnd append to existing file\n\ + -clob force overwrite of existing files\n\ + -prot do not overwrite existing files\n\ + -chng change filename if destination exists\n\ + -noloc do not transfer unless destination exists\n\ + -[hq] this list\n" ; + +#include + +/**** + * + * Constants, typedefs, externals, globals, statics, macros, block data + * + ****/ + + + + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "zmodem.h" +#include "seriallog.h" + +extern int errno ; +extern char *getenv() ; + + + /* compile-time parameters */ + +#define ESCCTRL 0 /* control characters need to be escaped */ + +#if !defined(CRTSCTS) && defined(CNEW_RTSCTS) +#define CRTSCTS CNEW_RTSCTS +#endif + +static int baud = 0 ; /* requested baud rate */ +static char *line = NULL ; /* requested serial line */ +static int verbose = 0 ; +static int send = 0 ; /* send files */ +static int receive = 0 ; /* receive */ +static int xmodem = 0 ; /* use xmodem */ +static int ymodem = 0 ; /* use ymodem */ +static int escCtrl = ESCCTRL ; +static int ascii = 0 ; /* translate line endings */ +static int binary = 0 ; /* don't translate line endings */ +static int resume = 0 ; /* resume interrupted transfers */ +static int xferType = 0 ; /* new,newl,diff,crc, etc. */ +static int noloc = 0 ; +static int doCancel = 0 ; +static int doLogging = 0 ; + +static int Corrupt = 0 ; /* deliberately corrupt data */ + +static int fileErrs ; /* used to track errors per file */ +static int fileSent ; /* track amount sent */ + +static ZModem info ; + +static int begun = 0 ; +static struct termios old_settings, new_settings ; + +static int FinishXmit(ZModem *info) ; +static int DoReceive(ZModem *info) ; +static int InitXmit(ZModem *info) ; +static int XmitFile(char *filename, int f0, int f1, ZModem *info) ; +static void SendFile() ; +static void RcvFiles() ; +static void RcvXmodem() ; +static int getBaud() ; +static void resetCom() ; +static char *basename(char *name) ; + +#ifdef SVr4 +static void sighandle(int) ; +#else +static void sighandle() ; +#endif + +extern FILE *SerialLogFile ; +extern FILE *zmodemlogfile ; + +int +main(int argc, char **argv) +{ + char *progname ; + +#ifdef COMMENT + printf("%d\n", getpid()) ; +#endif /* COMMENT */ + + info.ifd = info.ofd = -1 ; + info.zrinitflags = 0 ; + info.zsinitflags = 0 ; + info.attn = NULL ; + info.packetsize = 0 ; + info.windowsize = 0 ; + info.bufsize = 0 ; + + progname = basename(argv[0]) ; + if( strcmp(progname,"rz") == 0 ) + receive = 1 ; + else if( strcmp(progname, "sz") == 0 ) + send = 1 ; + + ++argv, --argc ; /* skip program name */ + + /* parse options */ + + for(; argc > 0 && **argv == '-'; ++argv, --argc ) + { + if( strcmp(*argv, "-r") == 0 ) + receive = 1 ; + else if( strcmp(*argv, "-s") == 0 ) + send = 1 ; + else if( strncmp(*argv, "-v", 2) == 0 ) + verbose += strlen(*argv)+1 ; + else if( strcmp(*argv, "-l") == 0 && --argc > 0 ) + line = *++argv ; + else if( strcmp(*argv, "-b") == 0 && --argc > 0 ) + baud = getBaud(atoi(*++argv)) ; + else if( strcmp(*argv, "-x") == 0 ) + xmodem = 1 ; + else if( strcmp(*argv, "-y") == 0 ) + ymodem = 1 ; + else if( strcmp(*argv, "-win") == 0 && --argc > 0 ) + info.windowsize = atoi(*++argv) ; + else if( strcmp(*argv, "-buf") == 0 && --argc > 0 ) + info.bufsize = atoi(*++argv) ; + else if( strcmp(*argv, "-L") == 0 && --argc > 0 ) + info.packetsize = atoi(*++argv) ; + else if( strcmp(*argv, "-k") == 0 ) + info.packetsize = 1024 ; + else if( strcmp(*argv, "-e") == 0 ) + escCtrl = ESCCTL ; + else if( strcmp(*argv, "-a") == 0 ) + ascii = 1 ; + else if( strcmp(*argv, "-i") == 0 ) + binary = 1 ; + else if( strcmp(*argv, "-resume") == 0 ) + resume = 1 ; + else if( strcmp(*argv, "-new") == 0 ) + xferType = ZMNEW ; + else if( strcmp(*argv, "-newl") == 0 ) + xferType = ZMNEWL ; + else if( strcmp(*argv, "-diff") == 0 ) + xferType = ZMDIFF ; + else if( strcmp(*argv, "-crc") == 0 ) + xferType = ZMCRC ; + else if( strcmp(*argv, "-apnd") == 0 ) + xferType = ZMAPND ; + else if( strcmp(*argv, "-clob") == 0 ) + xferType = ZMCLOB ; + else if( strcmp(*argv, "-prot") == 0 ) + xferType = ZMPROT ; + else if( strcmp(*argv, "-chng") == 0 ) + xferType = ZMCHNG ; + else if( strcmp(*argv, "-noloc") == 0 ) + noloc = ZMSKNOLOC ; + else if( strcmp(*argv, "-h") == 0 || + strcmp(*argv, "-q") == 0 ) + { + fprintf(stderr, usage) ; + exit(0) ; + } + else if( strcmp(*argv, "-corrupt") == 0 ) + Corrupt = 1 ; + else if( strcmp(*argv, "-log") == 0 ) + doLogging = 1 ; + else if( strcmp(*argv, "-") == 0 ) { + ++argv, --argc ; break ; + } + else { + fprintf(stderr, "unknown argument '%s' or missing value\n%s", + *argv, usage) ; + exit(2) ; + } + } + + + if( doLogging ) { + if( (SerialLogFile = fopen("xfer.log", "w")) == NULL ) + perror("xfer.log") ; + if( (zmodemlogfile = fopen("zmodem.log","w")) == NULL ) + perror("zmodem.log") ; + } + + + if( send ) { + for(; argc > 0; ++argv, --argc ) /* process filenames */ + SendFile(*argv) ; + FinishXmit(&info) ; + } + + else if( receive ) + { + if( !xmodem ) + RcvFiles() ; + else + for(; argc > 0; ++argv, --argc ) /* process filenames */ + RcvXmodem(*argv) ; + } + + + else { + fprintf(stderr, + "either -s (send) or -r (receive) must be specified\n%s", usage) ; + exit(2) ; + } + + resetCom() ; + exit(0) ; +} + + +static void +sighandle(int arg) +{ + doCancel = 1 ; +} + + + +static void +openCom() +{ + char *ptr ; + + if( line == NULL ) + line = getenv("RZSZLINE") ; + + if( baud == 0 && (ptr = getenv("RZSZBAUD")) != NULL ) + baud = getBaud(atoi(ptr)) ; + + if( line == NULL ) { + info.ifd = 0 ; + info.ofd = 1 ; + } + else if( (info.ifd = info.ofd = open(line, O_RDWR)) == -1 ) { + fprintf(stderr, + "cannot open %s, %s, use \"zmodem -h\" for more info\n", + line, strerror(errno)) ; + exit(2) ; + } + + /* assumption: setting attributes for one half of channel will + * change both halves. This fails if different physical + * devices are used. + */ + tcgetattr(info.ifd,&old_settings) ; + new_settings = old_settings ; + +#ifdef IUCLC + new_settings.c_iflag &= + ~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ; +#else + new_settings.c_iflag &= + ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXOFF|IMAXBEL) ; +#endif + new_settings.c_oflag &= ~OPOST ; + new_settings.c_cflag &= ~(CSIZE|PARENB) ; + new_settings.c_cflag |= CS8 ; + if( baud != 0 ) { + cfsetospeed(&new_settings, baud) ; + cfsetispeed(&new_settings, baud) ; + } + else + baud = cfgetospeed(&old_settings) ; + new_settings.c_lflag = 0 ; + new_settings.c_cc[VMIN] = 32 ; + new_settings.c_cc[VTIME] = 1 ; + tcsetattr(info.ifd,TCSADRAIN, &new_settings) ; + + info.zrinitflags = CANFDX|CANOVIO|CANBRK|CANFC32|escCtrl ; + info.zsinitflags = escCtrl ; + if( info.packetsize == 0 ) { + if( xmodem || ymodem ) + info.packetsize = 128 ; + /* TODO: what about future high-speed interfaces? */ + else if( baud < B2400 ) + info.packetsize = 256 ; + else if( baud == B2400 ) + info.packetsize = 512 ; + else + info.packetsize = 1024 ; + } +} + + +static void +resetCom() +{ + tcsetattr(info.ifd,TCSADRAIN, &old_settings) ; +} + + + +static void +SendFile(char *filename) +{ + int f0, f1 ; + + fileErrs = 0 ; + + if( !begun ) /* establish connection */ + { + openCom() ; + + signal(SIGINT, sighandle) ; + signal(SIGTERM, sighandle) ; + signal(SIGHUP, sighandle) ; + + if( InitXmit(&info) ) { + fprintf(stderr, "connect failed\n") ; + resetCom() ; + exit(1) ; + } + + begun = 1 ; + } + + if( ascii ) + f0 = ZCNL ; + else if( binary ) + f0 = ZCBIN ; + else if( resume ) + f0 = ZCRESUM ; + else + f0 = 0 ; + + f1 = xferType | noloc ; + + if( XmitFile(filename, f0,f1, &info) ) { + fprintf(stderr, "connect failed\n") ; + resetCom() ; + exit(1) ; + } +} + + + +static void +RcvFiles() +{ + openCom() ; + + signal(SIGINT, sighandle) ; + signal(SIGTERM, sighandle) ; + + if( DoReceive(&info) ) { + fprintf(stderr, "connect failed\n") ; + resetCom() ; + exit(1) ; + } +} + + + +static void +RcvXmodem(char *filename) +{ + /* TODO: */ +} + + +static int +getBaud(int b) +{ + switch(b) { + case 50: return B50 ; + case 75: return B75 ; + case 110: return B110 ; + case 134: return B134 ; + case 150: return B150 ; + case 200: return B200 ; + case 300: return B300 ; + case 600: return B600 ; + case 1200: return B1200 ; + case 1800: return B1800 ; + case 2400: return B2400 ; + case 4800: return B4800 ; + case 9600: return B9600 ; + case 19200: return B19200 ; + case 38400: return B38400 ; + default: return 0 ; + } +} + + + + /* TEST: randomly corrupt every 1000th byte */ +static void +corrupt(u_char *buffer, int len) +{ + extern double drand48() ; + + while( --len >= 0 ) { + if( drand48() < 1./1000. ) + *buffer ^= 0x81 ; + ++buffer ; + } +} + + +static int +doIO(ZModem *info) +{ + fd_set readfds ; + struct timeval timeout ; + int i ; + int len ; + u_char buffer[1024] ; + int done = 0 ; + + while(!done) + { + FD_ZERO(&readfds) ; + FD_SET(info->ifd, &readfds) ; + timeout.tv_sec = info->timeout ; + timeout.tv_usec = 0 ; + i = select(info->ifd+1, &readfds,NULL,NULL, &timeout) ; + if( doCancel ) { + ZmodemAbort(info) ; + fprintf(stderr, "cancelled by user\n") ; + resetCom() ; + exit(3) ; + } + if( i<0 ) + perror("select") ; + else if( i==0 ) + done = ZmodemTimeout(info) ; + else { + len = read(info->ifd, buffer, sizeof(buffer)) ; + if( Corrupt ) + corrupt(buffer, len) ; + if( SerialLogFile != NULL ) + SerialLog(buffer, len, 1) ; + done = ZmodemRcv(buffer, len, info) ; + } + } + if( SerialLogFile != NULL ) + SerialLogFlush() ; + return done ; +} + + +static int +InitXmit(ZModem *info) +{ + int done ; + + if( xmodem ) + done = XmodemTInit(info) ; + else if( ymodem ) + done = YmodemTInit(info) ; + else + done = ZmodemTInit(info) ; + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + +static int +XmitFile(char *filename, int f0, int f1, ZModem *info) +{ + int done ; + + done = ZmodemTFile(filename,filename, f0,f1,0,0, + 0,0, info) ; + switch( done ) { + case 0: + if( verbose ) + fprintf(stderr, "Sending: \"%s\"\n", filename) ; + break ; + + case ZmErrCantOpen: + fprintf(stderr, "cannot open file \"%s\": %s\n", + filename, strerror(errno)) ; + return 0 ; + + case ZmFileTooLong: + fprintf(stderr, "filename \"%s\" too long, skipping...\n", + filename) ; + return 0 ; + + case ZmDone: + return 0 ; + + default: + return 1 ; + } + + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + + +static int +FinishXmit(ZModem *info) +{ + int done ; + + done = ZmodemTFinish(info) ; + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + + +static int +DoReceive(ZModem *info) +{ + int done ; + + if( ymodem ) + done = YmodemRInit(info) ; + else + done = ZmodemRInit(info) ; + + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + +int +ZXmitStr(u_char *buffer, int len, ZModem *info) +{ + u_char b2[1024] ; + + /* TEST: randomly corrupt every 1000th byte */ + if( Corrupt ) + { + bcopy(buffer, b2, len) ; + corrupt(b2, len) ; + buffer = b2 ; + } + + if( SerialLogFile != NULL ) + SerialLog(buffer, len, 0) ; + + if( write(info->ofd, buffer, len) != len ) + return ZmErrSys ; + + return 0 ; +} + + +void +ZIFlush(ZModem *info) +{ + if( SerialLogFile != NULL ) + SerialLogFlush() ; + + if( tcflush(info->ifd, TCIFLUSH) != 0 ) + perror("TCIFLUSH") ; +} + +void +ZOFlush(ZModem *info) +{ + if( SerialLogFile != NULL ) + SerialLogFlush() ; + + if( tcflush(info->ifd, TCOFLUSH) != 0 ) + perror("TCOFLUSH") ; +} + +void +ZStatus(int type, int j, char *str) +{ + switch( type ) { + case RcvByteCount: + if( verbose >= 2 ) + fprintf(stderr, "received: %6d bytes\n", j) ; + break ; + case SndByteCount: + fileSent = j ; + if( verbose >= 2 ) + fprintf(stderr, "sent: %6d bytes\n", j) ; + break ; + case RcvTimeout: + if( verbose ) + fprintf(stderr, "receiver timeouts: %2d\n", j) ; + break ; + case SndTimeout: + if( verbose ) + fprintf(stderr, "sender timeouts: %2d\n", j) ; + break ; + case RmtCancel: + fprintf(stderr, "transfer cancelled by remote\n") ; + break ; + case ProtocolErr: + if( verbose >= 3 ) + fprintf(stderr, "protocol error: %2.2d\n", j) ; + break ; + case RemoteMessage: + fprintf(stderr, "remote message: %s\n",str) ; + break ; + case DataErr: + if( verbose >= 3 ) + fprintf(stderr, "data errors: %2d\n", j) ; +#ifdef COMMENT + if( ++fileErrs > 20 ) + { + /* something's wrong */ + ZmodemAbort(&info) ; + } +#endif /* COMMENT */ + break ; + case FileErr: + fprintf(stderr, "cannot write file: %s\n", strerror(errno)) ; + break ; + } +} + + +int +ZAttn(ZModem *info) +{ + char *ptr ; + + if( info->attn == NULL ) + return 0 ; + + for(ptr = info->attn; *ptr != '\0'; ++ptr) { + if( *ptr == ATTNBRK ) + tcsendbreak(info->ifd, 0) ; + else if( *ptr == ATTNPSE ) + sleep(1) ; + else + write(info->ifd, ptr, 1) ; + } + return 0 ; +} + + +FILE * +ZOpenFile(char *name, u_long crc, ZModem *info) +{ + struct stat buf ; + int exists ; /* file already exists */ +static int changeCount = 0 ; + char name2[MAXPATHLEN] ; + int apnd = 0 ; + int f0,f1 ; + FILE *rval ; + + /* TODO: if absolute path, do we want to allow it? + * if relative path, do we want to prepend something? + */ + + if( *name == '/' ) /* for now, disallow absolute paths */ + return NULL ; + + if( stat(name, &buf) == 0 ) + exists = 1 ; + else if( errno == ENOENT ) + exists = 0 ; + else + return NULL ; + + + /* if remote end has not specified transfer flags, we can + * accept the local definitions + */ + + if( (f0=info->f0) == 0 ) { + if( ascii ) + f0 = ZCNL ; + else if( binary ) + f0 = ZCBIN ; + else if( resume ) + f0 = ZCRESUM ; + else + f0 = 0 ; + } + + if( (f1=info->f1) == 0 ) + f1 = xferType | noloc ; + + if( f0 == ZCRESUM ) { /* if exists, and we already have it, return */ + if( exists && buf.st_size == info->len ) + return NULL ; + apnd = 1 ; + } + + /* reject if file not found and it most be there (ZMSKNOLOC) */ + if( !exists && (f1 & ZMSKNOLOC) ) + return NULL ; + + switch( f1 & ZMMASK ) { + case 0: /* Implementation-dependent. In this case, we + * reject if file exists (and ZMSKNOLOC not set) */ + if( exists && !(f1 & ZMSKNOLOC) ) { + fprintf(stderr, "%s already exists\n", name) ; + return NULL ; + } + break ; + + case ZMNEWL: /* take if newer or longer than file on disk */ + if( exists && info->date <= buf.st_mtime && + info->len <= buf.st_size ) { + fprintf(stderr, "%s already exists\n", name) ; + return NULL ; + } + break ; + + case ZMCRC: /* take if different CRC or length */ + if( exists && info->len == buf.st_size && crc == FileCrc(name) ) + { + fprintf(stderr, "%s already exists\n", name) ; + return NULL ; + } + break ; + + case ZMAPND: /* append */ + apnd = 1 ; + case ZMCLOB: /* unconditional replace */ + break ; + + case ZMNEW: /* take if newer than file on disk */ + if( exists && info->date <= buf.st_mtime ) { + fprintf(stderr, "%s already exists and is newer\n", name) ; + return NULL ; + } + break ; + + case ZMDIFF: /* take if different date or length */ + if( exists && info->date == buf.st_mtime && + info->len == buf.st_size ) + { + fprintf(stderr, "%s already exists\n", name) ; + return NULL ; + } + break ; + + case ZMPROT: /* only if dest does not exist */ + if( exists ) + { + fprintf(stderr, "%s already exists\n", name) ; + return NULL ; + } + break ; + + case ZMCHNG: /* invent new filename if exists */ + if( exists ) { + while( exists ) { + sprintf(name2, "%s_%d", name, changeCount++) ; + exists = stat(name2, &buf) == 0 || errno != ENOENT ; + } + name = name2 ; + } + break ; + } + + /* here if we've decided to accept */ +#ifdef COMMENT + if( exists && !apnd && unlink(name) != 0 ) + return NULL ; +#endif /* COMMENT */ + + /* TODO: build directory path if needed */ + + if( verbose ) + fprintf(stderr, "Receiving: \"%s\"\n", name) ; + + rval = fopen(name, apnd ? "a" : "w") ; + if( rval == NULL ) + perror(name) ; + return rval ; +} + + +int +ZWriteFile(u_char *buffer, int len, FILE *file, ZModem *info) +{ + /* TODO: if ZCNL set in info->f0, convert + * newlines to local convention + */ + + return fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys ; +} + + + +int +ZCloseFile(ZModem *info) +{ + struct timeval tvp[2] ; + fclose(info->file) ; + if( ymodem ) + truncate(info->filename, info->len) ; + if( info->date != 0 ) { + tvp[0].tv_sec = tvp[1].tv_sec = info->date ; + tvp[0].tv_usec = tvp[1].tv_usec = 0 ; + utimes(info->filename, tvp) ; + } + if( info->mode & 01000000 ) + chmod(info->filename, info->mode&0777) ; + return 0 ; +} + + +void +ZIdleStr(u_char *buffer, int len, ZModem *info) +{ +#ifdef COMMENT + fwrite(buffer, 1,len, stdout) ; +#endif /* COMMENT */ +} + +void +ZFlowControl(int onoff, ZModem *info) +{ + if( onoff ) { + new_settings.c_iflag |= IXON|IXANY|IXOFF ; +#ifdef COMMENT + new_settings.c_cflag |= CRTSCTS ; +#endif /* COMMENT */ + } + else { + new_settings.c_iflag &= ~(IXON|IXANY|IXOFF) ; + new_settings.c_cflag &= ~CRTSCTS ; + } + tcsetattr(info->ifd,TCSADRAIN, &new_settings) ; +} + + + +static char * +basename(char *name) +{ + char *ptr ; + + if( (ptr = strrchr(name, '/')) == NULL ) + return name ; + else + return ++ptr ; +} diff --git a/Xmodem/network.c b/Xmodem/network.c new file mode 100644 index 0000000..70adcb0 --- /dev/null +++ b/Xmodem/network.c @@ -0,0 +1,155 @@ +#ifndef lint +static const char rcsid[] = "$Id: network.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @ @ @@@@@ @@@@@ @ @ @@@ @@@@ @ @ + * @@ @ @ @ @ @ @ @ @ @ @ @ + * @ @ @ @@@ @ @ @ @ @ @ @@@@ @@@ + * @ @@ @ @ @ @ @ @ @ @ @ @ @ + * @ @ @@@@@ @ @ @ @@@ @ @ @ @ + * + * NETWORK - Make network connection + * + * Routines provided here: + * + * + * void + * IpConnect() + * Make IP connection according to parameters in gcomm.h + * + * void + * IpDisConnect() + * Close IP connection. + * + * + * + * Edward A. Falk + * + * January, 1995 + * + * + * + **********/ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef COMMENT +#include +#include +#endif /* COMMENT */ +#include +#include + +#include /* stuff used by tcp/ip */ +#include +#include + +#include +#include + +#include "gcomm.h" + +extern int errno ; +#ifdef COMMENT +extern char *sys_errlist[] ; +#endif /* COMMENT */ + + + +void +IpConnect() +{ + int sockfd ; + int i,j ; + struct hostent *hostent ; + struct sockaddr_in sockaddr ; + int port ; + + WindowStatus("Connecting...") ; + + port = PortLookup(hostPort) ; + if( port == -1 ) + return ; + + bzero(&sockaddr, sizeof(sockaddr)) ; + sockaddr.sin_family = AF_INET ; + sockaddr.sin_port = htons(port) ; + + hostent = gethostbyname(hostId) ; + if( hostent != NULL ) + { + sockaddr.sin_addr = *(struct in_addr *) hostent->h_addr ; + } + else if( isdigit(hostId[0]) ) + { + i = sscanf(hostId, "%d.%d.%d.%d", + &sockaddr.sin_addr.S_un.S_un_b.s_b1, + &sockaddr.sin_addr.S_un.S_un_b.s_b2, + &sockaddr.sin_addr.S_un.S_un_b.s_b3, + &sockaddr.sin_addr.S_un.S_un_b.s_b4) ; + if( i != 4 ) { + WindowStatus("Host address must be in dd.dd.dd.dd format") ; + return ; + } + } + else { + WindowStatus("Hostname not found") ; + return ; + } + + sockfd = socket(AF_INET, SOCK_STREAM, 0) ; + if( sockfd < 0 ) { + WindowStatus("Cannot open socket: %s", sys_errlist[errno]) ; + return ; + } + + i = connect(sockfd, &sockaddr, sizeof(sockaddr)) ; + if( i < 0 ) { + WindowStatus("Cannot connect to host: %s", sys_errlist[errno]) ; + close(sockfd) ; + return ; + } + + ifd = ofd = sockfd ; + + i = fcntl(ifd, F_GETFL, 0) ; + j = fcntl(ifd, F_SETFL, i|O_NDELAY) ; + + connectionActive = 1 ; + connectTime0 = time(NULL) ; + WindowStatus("Connected") ; +} + + +void +IpDisConnect() +{ + if( ifd == -1 ) + return ; + + close(ifd) ; + ifd = ofd = -1 ; + + connectionActive = 0 ; +} diff --git a/Xmodem/receive.c b/Xmodem/receive.c new file mode 100644 index 0000000..704c669 --- /dev/null +++ b/Xmodem/receive.c @@ -0,0 +1,600 @@ +#ifndef lint +static const char rcsid[] = "$Id: receive.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + + +#define TEST /* standalone test */ + +#include +#include +#include "zmodem.h" +#include + +#include +#include +#include +#include +#include /* define TCSBRK */ +#include + +extern int errno ; + +static u_char zeros[4] = {0,0,0,0} ; + +main(argc,argv) + int argc ; + char **argv ; +{ + struct termios old_settings, new_settings ; + fd_set readfds ; + struct timeval timeout ; + int i ; + int len ; + char buffer[1024] ; + int done = 0 ; + int filecount = 0 ; + ZModem info ; + +#ifdef TEST + +static u_char Amsg0[] = { + 0x72, 0x7a, 0x0d, 0x2a, 0x2a, 0x18, 0x42, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x0d, 0x0a, 0x11, + } ; +static u_char Amsg1[] = { + 0x2a, 0x18, 0x43, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0xa4, 0xe2, 0xbc, 0x00, 0x18, 0x6b, 0x2f, + 0xaa, 0xb9, 0x9b, 0x2a, 0x18, 0x43, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x7d, 0xa4, 0xe2, 0xbc, + } ; +static u_char Amsg2[] = { + 0x00, 0x18, 0x6b, 0x2f, 0xaa, 0xb9, 0x9b, 0x2a, + 0x18, 0x43, 0x04, 0x00, 0x00, 0x04, 0x00, 0xd9, + 0x94, 0xce, 0x57, + } ; +static u_char Amsg3[] = { + 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x2e, + 0x63, 0x00, 0x31, 0x36, 0x39, 0x38, 0x36, 0x20, + 0x35, 0x37, 0x30, 0x30, 0x36, 0x36, 0x37, 0x36, + 0x37, 0x30, 0x20, 0x31, 0x30, 0x30, 0x36, 0x36, + 0x34, 0x20, 0x30, 0x20, 0x30, 0x20, 0x30, 0x20, + 0x30, 0x00, 0x18, 0x6b, 0x57, 0xed, 0x76, 0xb6, + } ; +static u_char Amsg4[] = { + 0x2a, 0x18, 0x43, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0xbc, 0xef, 0x92, 0x8c, 0x0a, 0x23, 0x64, 0x65, + 0x66, 0x69, 0x6e, 0x65, 0x09, 0x54, 0x45, 0x53, + 0x54, 0x09, 0x2f, 0x2a, 0x20, 0x73, 0x74, 0x61, + 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x20, 0x2a, 0x2f, 0x0a, + 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x20, 0x3c, 0x75, 0x6e, 0x69, 0x73, 0x74, + 0x64, 0x2e, 0x68, 0x3e, 0x0a, 0x23, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x73, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x68, 0x3e, + 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x20, 0x22, 0x7a, 0x6d, 0x6f, 0x64, 0x65, + 0x6d, 0x2e, 0x68, 0x22, 0x0a, 0x23, 0x69, 0x6e, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x73, + 0x79, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x2e, + 0x68, 0x3e, 0x0a, 0x0a, 0x23, 0x69, 0x6e, 0x63, + 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x66, 0x63, + 0x6e, 0x74, 0x6c, 0x2e, 0x68, 0x3e, 0x0a, 0x23, + 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, + 0x3c, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x2e, 0x68, + 0x3e, 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, + 0x64, 0x65, 0x20, 0x3c, 0x73, 0x79, 0x73, 0x2f, + 0x74, 0x69, 0x6d, 0x65, 0x2e, 0x68, 0x3e, 0x0a, + 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, + 0x20, 0x3c, 0x73, 0x79, 0x73, 0x2f, 0x74, 0x65, + 0x72, 0x6d, 0x69, 0x6f, 0x73, 0x2e, 0x68, 0x3e, + 0x0a, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x20, 0x3c, 0x73, 0x79, 0x73, 0x2f, 0x74, + 0x65, 0x72, 0x6d, 0x69, 0x6f, 0x2e, 0x68, 0x3e, + 0x09, 0x09, 0x2f, 0x2a, 0x20, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x20, 0x54, 0x43, 0x53, 0x42, + 0x52, 0x4b, 0x20, 0x2a, 0x2f, 0x0a, 0x23, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, + 0x73, 0x79, 0x73, 0x2f, 0x70, 0x61, 0x72, 0x61, + 0x6d, 0x2e, 0x68, 0x3e, 0x0a, 0x0a, 0x65, 0x78, + 0x74, 0x65, 0x72, 0x6e, 0x09, 0x69, 0x6e, 0x74, + 0x09, 0x65, 0x72, 0x72, 0x6e, 0x6f, 0x20, 0x3b, + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, + 0x09, 0x75, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x09, + 0x7a, 0x65, 0x72, 0x6f, 0x73, 0x5b, 0x34, 0x5d, + 0x20, 0x3d, 0x20, 0x7b, 0x30, 0x2c, 0x30, 0x2c, + 0x30, 0x2c, 0x30, 0x7d, 0x20, 0x3b, 0x0a, 0x0a, + 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x61, 0x72, 0x67, + 0x63, 0x2c, 0x61, 0x72, 0x67, 0x76, 0x29, 0x0a, + 0x09, 0x69, 0x6e, 0x74, 0x09, 0x61, 0x72, 0x67, + 0x63, 0x20, 0x3b, 0x0a, 0x09, 0x63, 0x68, 0x61, + 0x72, 0x09, 0x2a, 0x2a, 0x61, 0x72, 0x67, 0x76, + 0x20, 0x3b, 0x0a, 0x7b, 0x0a, 0x09, 0x73, 0x74, + 0x72, 0x75, 0x63, 0x74, 0x09, 0x74, 0x65, 0x72, + 0x6d, 0x69, 0x6f, 0x73, 0x09, 0x6f, 0x6c, 0x64, + 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x2c, 0x20, 0x6e, 0x65, 0x77, 0x5f, 0x73, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, + 0x3b, 0x0a, 0x09, 0x66, 0x64, 0x5f, 0x73, 0x65, + 0x74, 0x09, 0x72, 0x65, 0x61, 0x64, 0x66, 0x64, + 0x73, 0x20, 0x3b, 0x0a, 0x09, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x20, 0x74, 0x69, 0x6d, 0x65, + 0x76, 0x61, 0x6c, 0x20, 0x74, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x20, 0x3b, 0x0a, 0x09, 0x69, + 0x6e, 0x74, 0x09, 0x69, 0x20, 0x3b, 0x0a, 0x09, + 0x69, 0x6e, 0x74, 0x09, 0x6c, 0x65, 0x6e, 0x20, + 0x3b, 0x0a, 0x09, 0x63, 0x68, 0x61, 0x72, 0x09, + 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5b, 0x31, + 0x30, 0x32, 0x34, 0x5d, 0x20, 0x3b, 0x0a, 0x09, + 0x69, 0x6e, 0x74, 0x09, 0x64, 0x6f, 0x6e, 0x65, + 0x20, 0x3d, 0x20, 0x30, 0x20, 0x3b, 0x0a, 0x09, + 0x69, 0x6e, 0x74, 0x09, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x3d, 0x20, + 0x30, 0x20, 0x3b, 0x0a, 0x09, 0x5a, 0x4d, 0x6f, + 0x64, 0x65, 0x6d, 0x09, 0x69, 0x6e, 0x66, 0x6f, + 0x20, 0x3b, 0x0a, 0x0a, 0x23, 0x69, 0x66, 0x64, + 0x65, 0x66, 0x09, 0x54, 0x45, 0x53, 0x54, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x09, + 0x75, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x09, 0x41, + 0x6d, 0x73, 0x67, 0x30, 0x5b, 0x5d, 0x20, 0x3d, + 0x20, 0x7b, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, + 0x37, 0x32, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x61, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x64, 0x2c, 0x20, + 0x30, 0x78, 0x32, 0x61, 0x2c, 0x20, 0x30, 0x78, + 0x32, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x31, 0x38, + 0x2c, 0x20, 0x30, 0x78, 0x34, 0x32, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x0a, 0x09, + 0x20, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, + 0x2c, 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, + 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x64, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x61, 0x2c, 0x20, + 0x30, 0x78, 0x31, 0x31, 0x2c, 0x20, 0x0a, 0x09, + 0x7d, 0x20, 0x3b, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x09, 0x75, 0x5f, 0x63, 0x68, 0x61, + 0x72, 0x09, 0x41, 0x6d, 0x73, 0x67, 0x31, 0x5b, + 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x32, 0x61, 0x2c, 0x20, 0x30, + 0x78, 0x31, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x34, + 0x33, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x34, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x64, + 0x64, 0x2c, 0x20, 0x30, 0x78, 0x35, 0x31, 0x2c, + 0x20, 0x30, 0x78, 0x61, 0x32, 0x2c, 0x20, 0x30, + 0x78, 0x33, 0x33, 0x2c, 0x20, 0x30, 0x78, 0x37, + 0x35, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x34, 0x2c, + 0x20, 0x30, 0x78, 0x36, 0x39, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x63, 0x2c, 0x20, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x37, 0x33, 0x2c, 0x20, 0x30, + 0x78, 0x32, 0x65, 0x2c, 0x20, 0x30, 0x78, 0x36, + 0x33, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x31, 0x2c, 0x20, 0x30, + 0x78, 0x33, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x37, 0x2c, + 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x32, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x35, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x37, 0x2c, 0x20, 0x30, + 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x35, 0x2c, 0x20, 0x30, + 0x78, 0x33, 0x36, 0x2c, 0x20, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x33, 0x31, 0x2c, 0x20, 0x30, + 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x34, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x18, 0x6a, 0xf8, 0x0e, + 0xd1, 0x2f, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x36, 0x2c, 0x20, 0x0a, + 0x09, 0x20, 0x20, 0x30, 0x78, 0x33, 0x36, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x34, 0x2c, 0x20, 0x30, + 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, + 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x30, 0x2c, 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, + 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x30, 0x78, 0x31, 0x38, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x62, 0x2c, 0x20, 0x30, 0x78, 0x63, + 0x63, 0x2c, 0x20, 0x30, 0x78, 0x38, 0x38, 0x2c, + 0x20, 0x30, 0x78, 0x38, 0x36, 0x2c, 0x20, 0x0a, + 0x09, 0x20, 0x20, 0x30, 0x78, 0x31, 0x61, 0x2c, + 0x20, 0x0a, 0x09, 0x7d, 0x20, 0x3b, 0x0a, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x63, 0x09, 0x75, 0x5f, + 0x63, 0x68, 0x61, 0x72, 0x09, 0x41, 0x6d, 0x73, + 0x67, 0x32, 0x5b, 0x5d, 0x20, 0x3d, 0x20, 0x7b, + 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x32, 0x61, + 0x2c, 0x20, 0x30, 0x78, 0x31, 0x38, 0x2c, 0x20, + 0x30, 0x78, 0x34, 0x33, 0x2c, 0x20, 0x30, 0x78, + 0x30, 0x34, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x2c, 0x20, 0x0a, 0x09, 0x20, 0x20, + 0x30, 0x78, 0x64, 0x64, 0x2c, 0x20, 0x30, 0x78, + 0x35, 0x31, 0x2c, 0x20, 0x30, 0x78, 0x61, 0x32, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x33, 0x2c, 0x20, + 0x30, 0x78, 0x37, 0x35, 0x2c, 0x20, 0x30, 0x78, + 0x37, 0x34, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x39, + 0x2c, 0x20, 0x30, 0x78, 0x36, 0x63, 0x2c, 0x20, + 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x37, 0x33, + 0x2c, 0x20, 0x30, 0x78, 0x32, 0x65, 0x2c, 0x20, + 0x30, 0x78, 0x36, 0x33, 0x2c, 0x20, 0x30, 0x78, + 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x31, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x31, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x37, 0x2c, 0x20, 0x0a, 0x09, 0x20, 0x20, + 0x30, 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x35, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x37, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x35, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x36, 0x2c, 0x20, + 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x33, 0x31, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x34, 0x2c, 0x20, 0x30, 0x78, + 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, 0x31, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x36, 0x2c, 0x20, 0x0a, 0x09, 0x20, 0x20, + 0x30, 0x78, 0x33, 0x36, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x34, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x33, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x32, 0x30, + 0x2c, 0x20, 0x30, 0x78, 0x33, 0x30, 0x2c, 0x20, + 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, + 0x31, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x62, + 0x2c, 0x20, 0x30, 0x78, 0x63, 0x63, 0x2c, 0x20, + 0x30, 0x78, 0x38, 0x38, 0x2c, 0x20, 0x30, 0x78, + 0x38, 0x36, 0x2c, 0x20, 0x0a, 0x09, 0x20, 0x20, + 0x30, 0x78, 0x31, 0x61, 0x2c, 0x20, 0x0a, 0x09, + 0x7d, 0x20, 0x3b, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x63, 0x09, 0x75, 0x5f, 0x63, 0x68, 0x61, + 0x72, 0x09, 0x41, 0x6d, 0x73, 0x67, 0x33, 0x5b, + 0x5d, 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x32, 0x61, 0x2c, 0x20, 0x30, + 0x78, 0x31, 0x38, 0x2c, 0x20, 0x30, 0x78, 0x34, + 0x33, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x61, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x30, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x30, 0x2c, + 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x62, + 0x63, 0x2c, 0x20, 0x30, 0x78, 0x65, 0x66, 0x2c, + 0x20, 0x30, 0x78, 0x39, 0x32, 0x2c, 0x20, 0x30, + 0x78, 0x38, 0x63, 0x2c, 0x20, 0x30, 0x78, 0x30, + 0x61, 0x2c, 0x20, 0x30, 0x78, 0x32, 0x33, 0x2c, + 0x20, 0x30, 0x78, 0x36, 0x39, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x65, 0x2c, 0x20, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x36, 0x33, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x63, 0x2c, 0x20, 0x30, 0x78, 0x37, + 0x35, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x34, 0x2c, + 0x20, 0x30, 0x78, 0x36, 0x35, 0x2c, 0x20, 0x30, + 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, 0x78, 0x33, + 0x63, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x33, 0x2c, + 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x37, + 0x39, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x33, 0x2c, + 0x20, 0x30, 0x78, 0x32, 0x66, 0x2c, 0x20, 0x30, + 0x78, 0x37, 0x34, 0x2c, 0x20, 0x30, 0x78, 0x36, + 0x35, 0x2c, 0x20, 0x30, 0x78, 0x37, 0x32, 0x2c, + 0x20, 0x30, 0x78, 0x36, 0x64, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x39, 0x2c, 0x20, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x36, 0x66, 0x2c, 0x20, 0x30, + 0x78, 0x37, 0x33, 0x2c, 0x20, 0x30, 0x78, 0x32, + 0x65, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x38, 0x2c, + 0x20, 0x30, 0x78, 0x33, 0x65, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x32, + 0x33, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x39, 0x2c, + 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x36, + 0x65, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x33, 0x2c, + 0x20, 0x30, 0x78, 0x36, 0x63, 0x2c, 0x20, 0x30, + 0x78, 0x37, 0x35, 0x2c, 0x20, 0x30, 0x78, 0x36, + 0x34, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x35, 0x2c, + 0x20, 0x30, 0x78, 0x32, 0x30, 0x2c, 0x20, 0x30, + 0x78, 0x32, 0x32, 0x2c, 0x20, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x37, 0x38, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x64, 0x2c, 0x20, 0x30, 0x78, 0x36, + 0x66, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x34, 0x2c, + 0x20, 0x30, 0x78, 0x36, 0x35, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x64, 0x2c, 0x20, 0x30, 0x78, 0x32, + 0x65, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x38, 0x2c, + 0x20, 0x0a, 0x09, 0x20, 0x20, 0x30, 0x78, 0x32, + 0x32, 0x2c, 0x20, 0x30, 0x78, 0x30, 0x61, 0x2c, + 0x20, 0x30, 0x78, 0x30, 0x61, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x61, 0x2c, 0x20, 0x30, 0x78, 0x36, + 0x39, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x65, 0x2c, + 0x20, 0x30, 0x78, 0x37, 0x34, 0x2c, 0x20, 0x30, + 0x78, 0x30, 0x61, 0x2c, 0x20, 0x0a, 0x09, 0x20, + 0x20, 0x30, 0x78, 0x37, 0x33, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x35, 0x2c, 0x20, 0x30, 0x78, 0x36, + 0x65, 0x2c, 0x20, 0x30, 0x78, 0x36, 0x34, 0x2c, + 0x20, 0x30, 0x78, 0x34, 0x33, 0x2c, 0x20, 0x30, + 0x78, 0x36, 0x18, 0x6a, 0x4a, 0xe5, 0x72, 0x71, + } ; + +#define E(b) {b, sizeof(b)} +static struct {u_char *am; int len} Amsgs[] = { + E(Amsg0), + E(Amsg1), + E(Amsg2), + E(Amsg3), + E(Amsg4),} ; + +#endif /* TEST */ + + /* try to trap uninit. variables */ + memset(&info,0xa5,sizeof(info)) ; + + info.zrinitflags = CANFDX|CANOVIO|CANBRK|CANFC32 ; + info.packetsize = 128 ; + info.bufsize = 0 ; + +#ifdef TEST + done = ZmodemRInit(&info) ; + for(i=0; !done; ++i ) + done = ZmodemRcv(Amsgs[i].am, Amsgs[i].len, &info) ; + +#else + if( argc < 2 ) + exit(2) ; + + info.ifd = open(argv[1], O_RDWR) ; + + if( info.ifd == -1 ) + exit(1) ; + + tcgetattr(info.ifd,&old_settings) ; + new_settings = old_settings ; + + new_settings.c_iflag &= + ~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ; + new_settings.c_oflag = 0 ; + new_settings.c_cflag = B300|CS8|CREAD|CLOCAL ; + new_settings.c_lflag = 0 ; + new_settings.c_cc[VMIN] = 32 ; + new_settings.c_cc[VTIME] = 1 ; + tcsetattr(info.ifd,TCSADRAIN, &new_settings) ; + + done = ZmodemRInit(&info) ; + + while(!done) + { + FD_ZERO(&readfds) ; + FD_SET(info.ifd, &readfds) ; + timeout.tv_sec = info.timeout ; + timeout.tv_usec = 0 ; + i = select(info.ifd+1, &readfds,NULL,NULL, &timeout) ; + if( i<0 ) + perror("select") ; + else if( i==0 ) + done = ZmodemTimeout(&info) ; + else { + len = read(info.ifd, buffer, sizeof(buffer)) ; + done = ZmodemRcv(buffer, len, &info) ; + } + } + + tcsetattr(info.ifd,TCSADRAIN, &old_settings) ; +#endif /* TEST */ + exit(0) ; +} + + + + +int +ZXmitStr(buffer, len, info) + u_char *buffer ; + int len ; + ZModem *info ; +{ + int i,j ; + u_char c ; +extern double drand48() ; + +#ifdef TEST + for(i=0; i= 0177) ? '.' : c ) ; + printf("|\n") ; + } +#else + +#ifdef COMMENT +/* TEST: randomly corrupt one out of every 300 bytes */ +for(i=0; iifd, buffer, len) != len ) + return ZmErrSys ; +#endif /* TEST */ + + return 0 ; +} + + +void +ZIFlush(info) + ZModem *info ; +{ +} + +void +ZOFlush(info) + ZModem *info ; +{ +} + +void +ZStatus(i,j,str) +{ + fprintf(stderr,"status %d=%d\n",i,j) ; +} + + +int +ZAttn(info) + ZModem *info ; +{ + char *ptr ; + int i = 0 ; + + for(ptr = info->attn; *ptr != '\0'; ++ptr) { + if( *ptr == ATTNBRK ) + ioctl(info->ifd, TCSBRK, 0) ; + else if( *ptr == ATTNPSE ) + sleep(1) ; + else + write(info->ifd, ptr, 1) ; + } + return 0 ; +} + + +FILE * +ZOpenFile(name, crc, info) + char *name ; + u_long crc ; + ZModem *info ; +{ + struct stat buf ; + int exists ; /* file already exists */ +static int changeCount = 0 ; + char name2[MAXPATHLEN] ; + int apnd = 0 ; + int f0,f1,f2,f3 ; + + /* TODO: if absolute path, do we want to allow it? + * if relative path, do we want to prepend something? + */ + + if( *name == '/' ) /* for now, disallow absolute paths */ + return NULL ; + + if( stat(name, &buf) == 0 ) + exists = 1 ; + else if( errno == ENOENT ) + exists = 0 ; + else + return NULL ; + + + /* if remote end has not specified transfer flags, we can + * accept the local definitions + */ + + if( f0 == ZCRESUM ) { /* if exists, and we already have it, return */ + if( exists && buf.st_size == info->len ) + return NULL ; + apnd = 1 ; + } + + /* reject if file not found and it most be there (ZMSKNOLOC) */ + if( !exists && (f1 & ZMSKNOLOC) ) + return NULL ; + + switch( f1 & ZMMASK ) { + case 0: /* Implementation-dependent. In this case, we + * reject if file exists (and ZMSKNOLOC not set) */ + if( exists && !(f1 & ZMSKNOLOC) ) + return NULL ; + break ; + + case ZMNEWL: /* take if newer or longer than file on disk */ + if( exists && info->date <= buf.st_mtime && + info->len <= buf.st_size ) + return NULL ; + break ; + + case ZMCRC: /* take if different CRC or length */ + if( exists && info->len == buf.st_size && crc == FileCrc(name) ) + return NULL ; + break ; + + case ZMAPND: /* append */ + apnd = 1 ; + case ZMCLOB: /* unconditional replace */ + break ; + + case ZMNEW: /* take if newer than file on disk */ + if( exists && info->date <= buf.st_mtime ) + return NULL ; + break ; + + case ZMDIFF: /* take if different date or length */ + if( exists && info->date == buf.st_mtime && + info->len == buf.st_size ) + return NULL ; + break ; + + case ZMPROT: /* only if dest does not exist */ + if( exists ) + return NULL ; + break ; + + case ZMCHNG: /* invent new filename if exists */ + if( exists ) { + while( exists ) { + sprintf(name2, "%s_%d", name, changeCount++) ; + exists = stat(name2, &buf) == 0 || errno != ENOENT ; + } + name = name2 ; + } + break ; + } + + /* here if we've decided to accept */ + if( exists && !apnd && unlink(name) != 0 ) + return NULL ; + + /* TODO: build directory path if needed */ + + return fopen(name, apnd ? "a" : "w") ; +} + + +int +ZWriteFile(buffer, len, file, info) + u_char *buffer ; + int len ; + FILE *file ; + ZModem *info ; +{ + /* TODO: if ZCNL set in info->f0, convert + * newlines to local convention + */ + + return fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys ; +} + + +int +ZCloseFile(info) + ZModem *info ; +{ + fclose(info->file) ; + chmod(info->filename, info->mode&0777) ; +} diff --git a/Xmodem/send.c b/Xmodem/send.c new file mode 100644 index 0000000..a86447c --- /dev/null +++ b/Xmodem/send.c @@ -0,0 +1,327 @@ +#ifndef lint +static const char rcsid[] = "$Id: send.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + + +#define TEST /* standalone test */ + +#include +#include +#include "zmodem.h" +#include + +#include +#include +#include +#include +#include /* define TCSBRK */ +#include + +extern int errno ; + +static u_char zeros[4] = {0,0,0,0} ; + +main(argc,argv) + int argc ; + char **argv ; +{ + struct termios old_settings, new_settings ; + fd_set readfds ; + struct timeval timeout ; + int i ; + int len ; + char buffer[1024] ; + int done = 0 ; + int filecount = 0 ; + ZModem info ; + +#ifdef TEST + +static u_char Amsg0[] = { + 0x43, + } ; +static u_char Amsg1[] = { + 0x06, 0x43, + } ; +static u_char Amsg2[] = { + 0x06, + } ; +static u_char Amsg3[] = { + 0x06, + } ; +static u_char Amsg4[] = { + 0x06, 0x43, + } ; +static u_char Amsg5[] = { + 0x06, 0x43, + } ; +static u_char Amsg6[] = { + 0x06, + } ; +static u_char Amsg7[] = { + 0x06, + } ; +static u_char Amsg8[] = { + 0x06, + } ; +static u_char Amsg9[] = { + 0x06, 0x43, + } ; + + +#define E(b) {b, sizeof(b)} +static struct {u_char *bm; int len} Bmsgs[] = { + E(Amsg0), + E(Amsg1), + E(Amsg2), + E(Amsg3), + E(Amsg4), + E(Amsg5), + E(Amsg6), + E(Amsg7), + E(Amsg8), + E(Amsg9),} ; + + +#endif /* TEST */ + + /* try to trap uninit. variables */ + memset(&info,0xa5,sizeof(info)) ; + + info.zsinitflags = 0 ; + info.attn = "\17\336" ; + info.packetsize = 1024 ; + info.windowsize = 4096 ; + +#ifdef TEST + done = YmodemTInit(&info) ; + for(i=0; !done; ++i ) + done = ZmodemRcv(Bmsgs[i].bm, Bmsgs[i].len, &info) ; + + done = ZmodemTFile("utils.c","utils.c", 0,0,0,0, 1,0, &info) ; + for(; !done; ++i ) + done = ZmodemRcv(Bmsgs[i].bm, Bmsgs[i].len, &info) ; + + done = ZmodemTFile("foo.c","foo.c", 0,0,0,0, 1,0, &info) ; + for(; !done; ++i ) + done = ZmodemRcv(Bmsgs[i].bm, Bmsgs[i].len, &info) ; + + done = ZmodemTFinish(info) ; + +#else + if( argc < 2 ) + exit(2) ; + + info.ifd = open(argv[1], O_RDWR) ; + + if( info.ifd == -1 ) + exit(1) ; + + tcgetattr(info.ifd,&old_settings) ; + new_settings = old_settings ; + + new_settings.c_iflag &= + ~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ; + new_settings.c_oflag = 0 ; + new_settings.c_cflag = B300|CS8|CREAD|CLOCAL ; + new_settings.c_lflag = 0 ; + new_settings.c_cc[VMIN] = 32 ; + new_settings.c_cc[VTIME] = 1 ; + tcsetattr(info.ifd,TCSADRAIN, &new_settings) ; + + InitXmit(&info) ; + XmitFile("utils.c",1,0,&info) ; +#ifdef COMMENT + XmitFile("foo",1,0,&info) ; + XmitFile("foo.c",1,0,&info) ; + XmitFile("raw",1,0,&info) ; +#endif /* COMMENT */ + FinishXmit(&info) ; + + tcsetattr(info.ifd,TCSADRAIN, &old_settings) ; +#endif /* TEST */ + exit(0) ; +} + + +static int +doIO(info) + ZModem *info ; +{ + fd_set readfds ; + struct timeval timeout ; + int i ; + int len ; + char buffer[1024] ; + int done = 0 ; + + while(!done) + { + FD_ZERO(&readfds) ; + FD_SET(info->ifd, &readfds) ; + timeout.tv_sec = info->timeout ; + timeout.tv_usec = 0 ; + i = select(info->ifd+1, &readfds,NULL,NULL, &timeout) ; + if( i<0 ) + perror("select") ; + else if( i==0 ) + done = ZmodemTimeout(info) ; + else { + len = read(info->ifd, buffer, sizeof(buffer)) ; + done = ZmodemRcv(buffer, len, info) ; + } + } + return done ; +} + + +int +InitXmit(info) + ZModem *info ; +{ + int done ; + + done = ZmodemTInit(info) ; + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + +XmitFile(filename,nfiles,nbytes,info) + char *filename ; + int nfiles, nbytes ; + ZModem *info ; +{ + int done ; + + done = ZmodemTFile(filename,filename, 0,ZMCLOB,0,0, + nfiles,nbytes, info) ; + if( done == ZmErrCantOpen || done == ZmFileTooLong ) + return 0 ; + + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + + +FinishXmit(info) + ZModem *info ; +{ + int done ; + + done = ZmodemTFinish(info) ; + if( !done ) + done = doIO(info) ; + + return done != ZmDone ; +} + + +int +ZXmitStr(buffer, len, info) + u_char *buffer ; + int len ; + ZModem *info ; +{ + int i,j ; + u_char c ; +extern double drand48() ; + +#ifdef TEST + for(i=0; i= 0177) ? '.' : c ) ; + printf("|\n") ; + } +#else + +#ifdef COMMENT +/* TEST: randomly corrupt one out of every 300 bytes */ +for(i=0; iifd, buffer, len) != len ) + return ZmErrSys ; +#endif /* TEST */ + + return 0 ; +} + + +void +ZIFlush(info) + ZModem *info ; +{ +} + +void +ZOFlush(info) + ZModem *info ; +{ +} + +void +ZStatus(i,j,str) +{ + fprintf(stderr,"status %d=%d\n",i,j) ; +} + + +int +ZAttn(info) + ZModem *info ; +{ + char *ptr ; + int i = 0 ; + + for(ptr = info->attn; *ptr != '\0'; ++ptr) { + if( *ptr == ATTNBRK ) + ioctl(info->ifd, TCSBRK, 0) ; + else if( *ptr == ATTNPSE ) + sleep(1) ; + else + write(info->ifd, ptr, 1) ; + } + return 0 ; +} + + +FILE * +ZOpenFile(name, f0,f1,f2,f3, len, date, mode, filesRem, bytesRem, crc) + char *name ; + int f0,f1,f2,f3, len, mode, filesRem, bytesRem ; + long date ; + u_long crc ; +{ +} + + +int +ZWriteFile() +{} + + +ZCloseFile(info) + ZModem *info ; +{} + +ZFlowControl(info) + ZModem *info ; +{} diff --git a/Xmodem/seriallog.c b/Xmodem/seriallog.c new file mode 100644 index 0000000..02c6f67 --- /dev/null +++ b/Xmodem/seriallog.c @@ -0,0 +1,86 @@ +#ifndef lint +static const char rcsid[] = "$Id: seriallog.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + +/* seriallog routines. Write data to log file, tagged with timestamp + * and channel. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "seriallog.h" + +static struct timeval timestamp = {0,0} ; + +#define DTIME 1500 /* timeout, milliseconds */ + +static int which = -1 ; /* last transmitted */ + +static char obuf[4096] ; +static int olen = -1 ; + + FILE *SerialLogFile = NULL ; + + /* flush out buffered text */ + +void +SerialLogFlush() +{ + int i ; + + if( SerialLogFile == NULL ) + return ; + + if( which != -1 && olen > 0 ) + { + i = fwrite((char *)&which, sizeof(int), 1, SerialLogFile) ; + i = fwrite((char *)×tamp, sizeof(timestamp), 1, SerialLogFile) ; + i = fwrite((char *)&olen, sizeof(int), 1, SerialLogFile) ; + i = fwrite(obuf, 1, olen, SerialLogFile) ; + } + fflush(SerialLogFile) ; + + which = -1 ; + olen = 0 ; +} + + +void +SerialLog(const void *data, int len, int w) +{ + struct timezone z ; + struct timeval time ; + int dt ; + int i ; + + if( SerialLogFile == NULL ) + return ; + + gettimeofday(&time, &z) ; + dt = (time.tv_sec - timestamp.tv_sec)*1000 + + (time.tv_usec - timestamp.tv_usec)/1000 ; + + if( w != which || dt > DTIME ) { + SerialLogFlush() ; + which = w ; + timestamp = time ; + } + + while( len > 0 ) + { + if( olen+len > sizeof(obuf) ) + SerialLogFlush() ; + + i = len > sizeof(obuf) ? sizeof(obuf) : len ; + bcopy(data, obuf+olen, i) ; + olen += i ; + len -= i ; + } +} diff --git a/Xmodem/seriallog.h b/Xmodem/seriallog.h new file mode 100644 index 0000000..ea9e78d --- /dev/null +++ b/Xmodem/seriallog.h @@ -0,0 +1,11 @@ +/* $Id: seriallog.h,v 1.2 2001/10/25 23:56:29 efalk Exp $ */ + +#ifndef SERIALLOG_H +#define SERIALLOG_H + + +extern FILE *SerialLogFile ; +extern void SerialLogFlush() ; +extern void SerialLog(const void *data, int len, int w) ; + +#endif diff --git a/Xmodem/utils.c b/Xmodem/utils.c new file mode 100644 index 0000000..7496def --- /dev/null +++ b/Xmodem/utils.c @@ -0,0 +1,133 @@ +#ifndef lint +static const char rcsid[] = "$Id: utils.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @ @ @@@@@ @@@ @ @@@@ + * @ @ @ @ @ @ + * @ @ @ @ @ @@@ + * @ @ @ @ @ @ + * @@@ @ @@@ @@@@@ @@@@ + * + * UTILS - utilities used by xmodem library. + * + * Routines provided here: + * + * + * name (args) + * Brief description. + * + * int sendCancel() + * + * send cancel string + * + * + * int sendFlush(c) + * char c ; + * + * flush input, send one character, return nonzero on error + * + * + * int sendChr(c) + * char c ; + * + * send one character, return nonzero on error + * + * + * int sendStr(str, len) + * char *str ; + * int len ; + * + * send string, return nonzero on error + * + * + * int calcChecksum(ptr, count) + * char *ptr ; + * int count ; + * + * compute checksum (used by xmodem) + * + * + * Edward A. Falk + * + * January, 1995 + * + * + * + **********/ + + + + +#include +#include +#include "xmodem.h" + + +int +sendCancel() +{ + return sendFlush(CAN) || sendFlush(CAN) ; +} + + + /* send one character, return nonzero on error */ +int +sendFlush(char c) +{ + /* first, flush input port */ + /* TODO: caller provide a way to do this? */ + + if( xmRfd == -1 ) + return XmErrNotOpen ; + + /* TODO: caller provides flush */ + if( ioctl(xmRfd, TCFLSH, TCIFLUSH) == -1 ) + return XmErrSys ; + + return sendChr(c) ; +} + + + /* send one character, return nonzero on error */ + +int +sendChr(char c) +{ + /* TODO: caller provide character output func? */ + if( xmTfd == -1 ) + return XmErrNotOpen ; + + return write(xmTfd, &c, 1) ==1 ? 0 : XmErrSys ; +} + + + /* send multiple characters, return nonzero on error */ + +int +sendStr(char *str, int len) +{ + /* TODO: caller provide character output func? */ + if( xmTfd == -1 ) + return XmErrNotOpen ; + + return write(xmTfd, str, len) == len ? 0 : XmErrSys ; +} + + /* compute checksum */ + +int +calcChecksum(char *ptr, int count) +{ + register int csum = 0 ; + while( --count >= 0 ) + csum += (u_char) *ptr++ ; + return csum & 255 ; +} diff --git a/Xmodem/xmodem.h b/Xmodem/xmodem.h new file mode 100644 index 0000000..a70b683 --- /dev/null +++ b/Xmodem/xmodem.h @@ -0,0 +1,111 @@ +/* $Id: xmodem.h,v 1.2 2001/10/25 23:56:29 efalk Exp $ */ + +#include + + +typedef int bool ; + +typedef enum { + Xmodem=0, + XmodemCrc=1, + WXmodem=2, + Ymodem=3, + YmodemG=4, + } Protocol ; + + +extern bool xmodem1k ; /* 1k blocks supported */ +extern bool fileInfo ; /* ymodem: send file attributes? */ +extern Protocol protocol ; +extern int xmTfd ; /* transmit file descriptor */ +extern int xmRfd ; /* receive file descriptor */ + +extern int xmTimeout ; /* timeout, seconds */ + +extern char xmDefPath[MAXPATHLEN] ; /* default location (ymodem) */ +extern char xmFilename[MAXPATHLEN] ; /* current filename */ + + /* error code definitions */ + +#define XmDone -1 /* done */ +#define XmErrInt -2 /* internal error */ +#define XmErrSys -3 /* system error, see errno */ +#define XmErrNotOpen -4 /* communication channel not open */ +#define XmErrCantOpen -5 /* can't open file, see errno */ +#define XmErrInitTo -10 /* transmitter failed to respond to init req. */ +#define XmErrSequence -11 /* packet received out of sequence */ +#define XmErrCancel -12 /* cancelled by remote end */ +#define XmErrRcvTo -13 /* remote end timed out during transfer */ + + +#ifdef __STDC__ + +extern int XmodemRInit(char *path, Protocol p) ; + /* start receive protocol */ +extern int XmodemRRcv(char c) ; /* call for each received char. */ +extern int XmodemRTimeout() ; /* call if xmTimeout expires */ +extern int XmodemRAbort() ; /* call to abort protocol */ + +extern int XmodemTInit(char *path, Protocol p) ; + /* start transmit protocol */ +extern int XmodemTRcv(char c) ; /* call for each received char. */ +extern int XmodemTTimeout() ; /* call if xmTimeout expires */ +extern int XmodemTAbort() ; /* call to abort protocol */ +extern int XmodemTFinish() ; /* call after last file sent (ymodem) */ + +#else + +extern int XmodemRInit() ; /* start receive protocol */ +extern int XmodemRRcv() ; /* call for each received char. */ +extern int XmodemRTimeout() ; /* call if xmTimeout expires */ +extern int XmodemRAbort() ; /* call to abort protocol */ + +extern int XmodemTInit() ; /* start transmit protocol */ +extern int XmodemTRcv() ; /* call for each received char. */ +extern int XmodemTTimeout() ; /* call if xmTimeout expires */ +extern int XmodemTAbort() ; /* call to abort protocol */ + +#endif + + + + /* INTERNAL */ + +#define SOH 1 /* ^A */ +#define STX 2 /* ^B */ +#define EOT 4 /* ^D */ +#define ACK 6 /* ^F */ +#define DLE 16 /* ^P */ +#define XON 17 /* ^Q */ +#define XOFF 19 /* ^S */ +#define NAK 21 /* ^U */ +#define SYN 22 /* ^V */ +#define CAN 24 /* ^X */ + +#ifndef False +#define False 0 +#define True 1 +#endif + +#define MAXERROR 10 +#define INITTO 10 /* initialization timeout, basic xmodem */ +#define INITTO2 3 /* initialization timeout */ +#define PKTTO 5 /* in-packet receive timeout */ + +#define MAXPACKET 1024 /* max packet length */ + + +#ifdef __STDC__ + +extern int sendCancel(), sendFlush(char), + sendChr(char), sendStr(char *,int) ; +extern int calcrc(char *ptr, int count) ; +extern int calcChecksum(char *ptr, int count) ; + +#else + +extern int sendCancel(), sendFlush(), sendChr(), sendStr() ; +extern int calcrc() ; +extern int calcChecksum() ; + +#endif diff --git a/Xmodem/xmodemr.c b/Xmodem/xmodemr.c new file mode 100644 index 0000000..cff6971 --- /dev/null +++ b/Xmodem/xmodemr.c @@ -0,0 +1,417 @@ +#ifndef lint +static const char rcsid[] = "$Id: xmodemr.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ; +#endif lint + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @ @ @ @ @@@ @@@@ @@@@@ @ @ @@@@ + * @ @ @@ @@ @ @ @ @ @ @@ @@ @ @ + * @ @ @ @ @ @ @ @ @@@ @ @ @ @@@@ + * @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ + * @ @ @ @ @ @@@ @@@@ @@@@@ @ @ @ @ @ + * + * XMODEMR - receiver side of xmodem/ymodem protocol + * + * Caller sets flags defined in xmodem.h as appropriate. + * (default is basic xmodem) + * + * This code is designed to be called from inside a larger + * program, so it is implemented as a state machine where + * practical. + * + * + * functions: + * + * XmodemRInit(char *filename, Protocol p) + * Initiate a receive + * + * XmodemRTimeout() + * called after timeout expired + * + * XmodemRRcv(char c) + * called after character received + * + * XmodemRAbort() + * abort transfer + * + * all functions return 0 on success, 1 on abort + * + * + * + * Edward A. Falk + * + * January, 1995 + * + * + * + **********/ + + + + +#include +#include +#include "xmodem.h" + + + /* TODO: WXmodem */ + + + bool xmodem1k = False ; + Protocol protocol = Xmodem ; + int xmTfd = -1 ; + int xmRfd = -1 ; + + int xmTimeout = 0 ; + + char xmDefPath[MAXPATHLEN] ; + char xmFilename[MAXPATHLEN] ; + +typedef enum { + Start, /* waiting to begin */ + Init, /* sent initial NAK, 'C' or 'W' */ + Packet, /* receiving a packet */ + Wait, /* wait for start of next packet */ + } XmodemState ; + +static bool ymodem ; +static XmodemState state = Start ; +static int errorCount = 0 ; +static int errorCount2 ; +static int ignoreCount ; +static int eotCount ; /* count EOT's, reject first one */ +static int inCount ; /* characters received this packet */ +static int pktLen ; /* length of this packet data */ +static int pktHdrLen ; /* id, cmpl, checksum or crc */ +static char packet[MAXPACKET+5], *optr ; +static int packetId ; /* id of last received packet */ +static int packetCount ; /* # packets received */ + +static FILE *ofile ; /* output file fd */ +static int fileLen, fileDate, fileMode ; + +static int XmodemRStart() ; +static int processPacket() ; +static int rejectPacket() ; +static int acceptPacket() ; + + +int +XmodemRInit( char *file, Protocol prot ) +{ + 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 ; + + return 0 ; +} + + + /* send startup character */ + +static int +XmodemRStart() +{ +static char pchars[5] = {NAK,'C','W','C','C'} ; +static int timeouts[5] = {INITTO, INITTO2, INITTO2, INITTO, INITTO} ; + char c = pchars[(int)protocol] ; + int err ; + + if( err=sendFlush(c) ) + return err ; + + xmTimeout = timeouts[(int)protocol] ; + + return 0 ; +} + + +int +XmodemRRcv(char c) +{ + errorCount = 0 ; + + switch( state ) { + case Start: /* shouldn't happen, ignore */ + if( c == CAN ) + return XmErrCancel ; + break ; + + case Init: /* waiting */ + case Wait: + switch( c ) { + case SOH: + case STX: + pktLen = c == STX ? 1024 : 128 ; + inCount = 0 ; + optr = packet ; + state = Packet ; + xmTimeout = PKTTO ; + break ; + + case EOT: + if( ++eotCount > 1 ) { + sendFlush(ACK) ; + if( ymodem ) + return XmodemRInit() ; /* restart protocol */ + else + return XmDone ; + } + else + return rejectPacket() ; /* make xmitter try again */ + + case CAN: return XmErrCancel ; + + default: /* ignore all others */ + if( ++ignoreCount > 1030 ) { + ignoreCount = 0 ; + return sendFlush(NAK) ; + } + break ; + } + break ; + + + case Packet: /* mid packet */ + *optr++ = c ; + if( ++inCount >= pktLen + pktHdrLen ) + ProcessPacket() ; + break ; + } + return 0 ; +} + + +int +XmodemRTimeout() +{ + if( ++errorCount > MAXERROR ) + return state == Init ? XmErrInitTo : XmErrRcvTo ; + + switch( state ) { + case Start: return -1 ; /* shouldn't happen */ + case Init: + if( ++errorCount2 >= 3 ) + switch( protocol ) { + case WXmodem: protocol = XmodemCrc ; errorCount2 = 0 ; break ; + case XmodemCrc: protocol = Xmodem ; pktHdrLen = 3 ; break ; + } + return XmodemRStart() ; + + case Wait: /* timeout while waiting */ + case Packet: /* timeout in mid packet */ + return rejectPacket() ; + } +} + +int +XmodemRAbort() +{ + return sendCancel() ; +} + + +static int +ProcessPacket() +{ + int id = (u_char)packet[0] ; + int idc = (u_char)packet[1] ; + int i ; + + if( idc != 255-id ) + return rejectPacket() ; + + if( id == packetId ) /* duplicate */ + return acceptPacket() ; + + if( id != (packetId+1)%256 ) { /* out of sequence */ + (void) sendCancel() ; + return XmErrSequence ; + } + + if( protocol == Xmodem ) + { + /* compute checksum */ + register int csum = calcChecksum(packet+2, pktLen) ; + if( csum != (u_char) packet[2+pktLen] ) + return rejectPacket() ; + } + else + { + int crc0 = (u_char)packet[pktLen+2] << 8 | (u_char)packet[pktLen+3] ; + int crc1 = calcrc(packet+2, pktLen) ; + if( crc0 != crc1 ) + return rejectPacket() ; + } + + /* it's a good packet */ + packetId = (packetId+1)%256 ; + + + /* is this the first packet? */ + + if( packetCount == 0 ) + { + if( ymodem ) + { + if( packet[2] == '\0' ) /* last file */ + { + (void) acceptPacket() ; + return XmDone ; + } + + if( packet[2] == '/' ) + strcpy(xmFilename, packet+2) ; + else { + strcpy(xmFilename, xmDefPath) ; + strcat(xmFilename, packet+2) ; + } + + fileLen = fileDate = fileMode = -1 ; + sscanf(packet+2+strlen(packet)+1, "%d %o %o", + &fileLen, &fileDate, &fileMode) ; + } + + if( (ofile = fopen(xmFilename, "w")) == NULL ) { + sendCancel() ; + return XmErrCantOpen ; + } + + if( ymodem ) { + packetCount = 1 ; + (void) acceptPacket() ; + return sendFlush('C') ; + } + else + state = Packet ; + } + + ++packetCount ; + + /* TODO: ymodem: if this is last packet, truncate it */ + if( (i=fwrite(packet+2, 1, pktLen, ofile)) != pktLen ) + { + sendCancel() ; + return XmErrSys ; + } + else + return acceptPacket() ; +} + + +static int +rejectPacket() +{ + state = Wait ; + xmTimeout = INITTO ; + return sendFlush(NAK) ; +} + +static int +acceptPacket() +{ + state = Wait ; + xmTimeout = INITTO ; + return sendFlush(ACK) ; +} + + + +#ifdef COMMENT /* stand-alone testing */ + +#include +#include +#include +#include +#include + + +main(argc,argv) + int argc ; + char **argv ; +{ + struct termios old_settings, new_settings ; + fd_set readfds ; + struct timeval timeout ; + int i ; + int len ; + char buffer[1024] ; + bool done = False ; + + if( argc < 2 ) + exit(2) ; + + xmTfd = xmRfd = open(argv[1], O_RDWR) ; + + if( xmTfd == -1 ) + exit(1) ; + + tcgetattr(xmTfd,&old_settings) ; + new_settings = old_settings ; + + new_settings.c_iflag &= + ~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ; + new_settings.c_oflag = 0 ; + new_settings.c_cflag = B300|CS8|CREAD|CLOCAL ; + new_settings.c_lflag = 0 ; + new_settings.c_cc[VMIN] = 32 ; + new_settings.c_cc[VTIME] = 1 ; + tcsetattr(xmTfd,TCSADRAIN, &new_settings) ; + + + xmodem1k = 0 ; + done = XmodemRInit("foo", XmodemCrc) != 0 ; +#ifdef COMMENT + xmodem1k = 1 ; + done = XmodemRInit("./", Ymodem) != 0 ; +#endif /* COMMENT */ + + while(!done) + { + FD_ZERO(&readfds) ; + FD_SET(xmTfd, &readfds) ; + timeout.tv_sec = xmTimeout ; + timeout.tv_usec = 0 ; + i = select(xmTfd+1, &readfds,NULL,NULL, &timeout) ; + if( i<0 ) + perror("select") ; + else if( i==0 ) + done = XmodemRTimeout() != 0 ; + else { + len = read(xmTfd, buffer, sizeof(buffer)) ; + for(i=0; !done && i +#include +#include "xmodem.h" + + + bool fileInfo = False ; + + /* TODO: WXmodem, YmodemG */ + +typedef enum { + File, /* waiting for initial protocol character */ + FileWait, /* sent file header, waiting for ACK */ + Start, /* waiting to begin */ + Wait, /* sent a packet, waiting for ACK */ + Eot, /* sent an EOT, waiting for ACK */ + EndWait, /* sent null filename, waiting for ACK */ + } XmodemState ; + +static XmodemState state = Start ; +static bool ymodem ; +static bool useCrc ; /* receiver wants crc */ +static int pktLen ; /* length of this packet data */ +static int pktMaxLen ; +static char packet[MAXPACKET] ; +static char pktHdr[3], pktCrc[2] ; +static int packetId ; /* id of last received packet */ +static int packetCount ; /* # packets received */ + +static FILE *ifile = NULL ; /* input file fd */ +static int fileLen, fileDate, fileMode ; + +static int sendFilename() ; +static int sendPacket() ; +static int buildPacket() ; +static int resendPacket() ; + + +int +XmodemTInit( char *file, Protocol prot ) +{ + int err ; + + protocol = prot ; + ymodem = prot == Ymodem || prot == YmodemG ; + state = ymodem ? File : Start ; + + strcpy(xmFilename, file) ; + + if( (ifile = fopen(xmFilename, "r")) == NULL ) { + sendCancel() ; + return XmErrCantOpen ; + } + + packetId = 1 ; + packetCount = 0 ; + pktMaxLen = xmodem1k ? 1024 : 128 ; + + xmTimeout = 60 ; + + return 0 ; +} + + +int +XmodemTRcv(char c) +{ + if( c == CAN ) { + if( ifile != NULL ) + fclose(ifile) ; + return XmErrCancel ; + } + + switch( state ) { + case File: /* waiting for command, ymodem */ + switch( c ) { + case NAK: useCrc = False ; return sendFilename() ; + case 'C': useCrc = True ; return sendFilename() ; + } + break ; + + case FileWait: /* waiting for filename ACK */ + switch( c ) { + case NAK: return resendPacket() ; + case ACK: state = Start ; return 0 ; + } + + case Start: /* waiting for command, data */ + switch( c ) { + case NAK: /* wants checksums */ + if( !ymodem ) + protocol = Xmodem ; + useCrc = False ; + return sendPacket() ; + + case 'C': useCrc = True ; return sendPacket() ; + + case 'W': + if( !ymodem ) { + protocol = WXmodem ; + useCrc = True ; + /* TODO: WXmodem */ + } + } + break ; + + + case Wait: /* waiting for ACK */ + switch( c ) { + case ACK: return sendPacket() ; + case NAK: return resendPacket() ; + } + break ; /* ignore all other characters */ + + case Eot: /* waiting for ACK after EOT */ + switch( c ) { + case ACK: return XmDone ; + case NAK: return sendChr(EOT) ; + } + break ; + + case EndWait: /* waiting for filename ACK */ + switch( c ) { + case NAK: return resendPacket() ; + case ACK: return XmDone ; + } + } + return 0 ; +} + +static int +sendFilename() +{ + int i ; + char *ptr ; + + pktLen = 128 ; + + /* TODO: protect against long filenames */ + strcpy(packet, xmFilename) ; + ptr = packet + strlen(packet) + 1 ; + /* TODO: get file info */ + if( fileInfo ) { + sprintf(ptr, "%d %o %o %o", 0,0,0100644) ; + ptr += strlen(ptr) + 1 ; + } + + /* TODO: what if file desc buffer too big? */ + + if( ptr > packet+128 ) + pktLen = 1024 ; + + i = pktLen - (ptr-packet) ; + bzero(ptr, i) ; + + state = FileWait ; + packetId = 0 ; + return buildPacket() ; +} + + +int +XmodemTFinish() +{ + int i ; + char *ptr ; + + pktLen = 128 ; + bzero(packet, pktLen) ; + + state = EndWait ; + packetId = 0 ; + return buildPacket() ; +} + + +static char *bufptr ; +static int buflen = 0 ; + +static int +sendPacket() +{ + int i ; + + /* This code assumes that a incomplete reads can only happen + * after EOF. This will fail with pipes. + * TODO: try to make pipes work. + */ + + state = Wait ; + + if( buflen > 0 ) /* previous incomplete packet */ + { + memcpy(packet, bufptr, 128) ; + bufptr += 128 ; + if( buflen < 128 ) + for(i=buflen; i<128; ++i) + packet[i] = 0x1a ; + buflen -= 128 ; + pktLen = 128 ; + return buildPacket() ; + } + + if( (i=fread(packet, 1,pktMaxLen, ifile)) <= 0 ) /* EOF */ + { + state = Eot ; + return sendChr(EOT) ; + } + + else if( i == pktMaxLen ) /* full buffer */ + { + pktLen = i ; + return buildPacket() ; + } + + buflen = i ; + bufptr = packet ; + pktLen = 128 ; + return buildPacket() ; +} + +static int +buildPacket() +{ + int i ; + + pktHdr[0] = pktLen == 128 ? SOH : STX ; + pktHdr[1] = (char)packetId ; + pktHdr[2] = (char)(255-packetId) ; + ++packetId ; + + if( useCrc ) { + i = calcrc(packet, pktLen) ; + pktCrc[0] = (char) (i>>8) ; + pktCrc[1] = (char) (i & 0xff) ; + } + else + pktCrc[0] = (char) calcChecksum(packet, pktLen) ; + + return resendPacket() ; +} + + +static int +resendPacket() +{ + int i ; + + (i=sendStr(pktHdr, 3)) || (i=sendStr(packet, pktLen)) || + (i=sendStr(pktCrc, useCrc?2:1)) ; + return i ; +} + + +int +XmodemTTimeout() +{ + switch( state ) { + case File: + case Start: + return XmErrInitTo ; + case FileWait: + case Wait: + case Eot: + return XmErrRcvTo ; + } +} + +int +XmodemTAbort() +{ + return sendCancel() ; +} + + + +#include +#include +#include +#include +#include + + +main(argc,argv) + int argc ; + char **argv ; +{ + struct termios old_settings, new_settings ; + fd_set readfds ; + struct timeval timeout ; + int i ; + int len ; + char buffer[1024] ; + bool done = False ; + int filecount = 0 ; + + if( argc < 2 ) + exit(2) ; + + xmTfd = xmRfd = open(argv[1], O_RDWR) ; + + if( xmTfd == -1 ) + exit(1) ; + + tcgetattr(xmTfd,&old_settings) ; + new_settings = old_settings ; + + new_settings.c_iflag &= + ~(ISTRIP|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXOFF|IMAXBEL) ; + new_settings.c_oflag = 0 ; + new_settings.c_cflag = B300|CS8|CREAD|CLOCAL ; + new_settings.c_lflag = 0 ; + new_settings.c_cc[VMIN] = 32 ; + new_settings.c_cc[VTIME] = 1 ; + tcsetattr(xmTfd,TCSADRAIN, &new_settings) ; + + + xmodem1k = 1 ; + done = XmodemTInit("xmodem.h", Ymodem) != 0 ; + + while(!done) + { + FD_ZERO(&readfds) ; + FD_SET(xmTfd, &readfds) ; + timeout.tv_sec = xmTimeout ; + timeout.tv_usec = 0 ; + i = select(xmTfd+1, &readfds,NULL,NULL, &timeout) ; + if( i<0 ) + perror("select") ; + else if( i==0 ) + done = XmodemTTimeout() != 0 ; + else { + len = read(xmTfd, buffer, sizeof(buffer)) ; + for(i=0; !done && i + +/**** + * + * Constants, typedefs, externals, globals, statics, macros, block data + * + ****/ + + + +/* TODO: sample input before initial send */ +/* TODO: more intelligent timeout dispatch */ +/* TODO: read all pending input before sending next data packet out */ +/* TODO: if received ZDATA while waiting for ZFILE/ZFIN, it's probably + leftovers */ +/* TODO: enable flow control for zmodem, disable for X/YModem */ + +#include +#include +#include + +#include "zmodem.h" +#include "crctab.h" + +static u_char zeros[4] = {0,0,0,0} ; + + +extern int YrcvChar( char c, register ZModem *info ) ; +extern int YrcvTimeout( register ZModem *info ) ; +extern void ZIdleStr(u_char *buffer, int len, ZModem *info) ; + + + /* LEXICAL BOX: handle characters received from remote end. + * These may be header, data or noise. + * + * This section is a finite state machine for parsing headers + * and reading input data. The info->chrCount field is effectively + * the state variable. + */ + +static int FinishChar( char c, register ZModem *info ) ; +static int DataChar( u_char c, register ZModem *info ) ; +static int HdrChar( u_char c, register ZModem *info ) ; +static int IdleChar(u_char c, register ZModem *info) ; +extern int YsendChar() ; + +static int ZProtocol(), ZDataReceived() ; + + +int +ZmodemRcv( register u_char *str, int len, register ZModem *info ) +{ +register u_char c ; + int err ; + + info->rcvlen = len ; + + while( --info->rcvlen >= 0 ) + { + c = *str++ ; + + if( c == CAN ) { + if( ++info->canCount >= 5 ) { + ZStatus(RmtCancel, 0, NULL) ; + return ZmErrCancel ; + } + } + else + info->canCount = 0 ; + + if( info->InputState == Ysend ) { + if( (err = YsendChar(c, info)) ) + return err ; + } + else if( info->InputState == Yrcv ) { + if( (err = YrcvChar(c, info)) ) + return err ; + } + + else if( c != XON && c != XOFF ) + { + /* now look at what we have */ + + switch( info->InputState ) + { + case Idle: + if( (err = IdleChar(c, info)) ) + return err ; + break ; + + case Inhdr: + if( (err = HdrChar(c, info)) ) + return err ; + break ; + + case Indata: + if( (err = DataChar(c, info)) ) + return err ; + break ; + + case Finish: + if( (err = FinishChar(c, info)) ) + return err ; + break ; + + default: + break ; + } + } + } + + return 0 ; +} + + + + + + + + /* handle character input while idling + * looking for ZPAD-ZDLE sequence which introduces a header + */ + +static int +IdleChar(u_char 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((u_char *)"*", 1, info) ; + info->chrCount = 0 ; + break ; + } + } + return 0 ; +} + + + +static u_int +rcvHex( u_int i, char c ) +{ + if( c <= '9' ) + c -= '0' ; + else if( c <= 'F' ) + c -= 'A'-10 ; + else + c -= 'a'-10 ; + return (i<<4)+c ; +} + + /* handle character input in a header */ + +static int +HdrChar( u_char 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 ; +} + + /* handle character input in a data buffer */ + +static int +DataChar( u_char 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 ; + } + } + + + 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 ; +} + + + /* wait for "OO" */ + +static int +FinishChar( char c, register ZModem *info ) +{ + if( c == 'O' ) { + if( ++info->chrCount >= 2 ) + return ZmDone ; + } + else + info->chrCount = 0 ; + return 0 ; +} + + + + + + + int ZPF() ; + int Ignore() ; + int AnswerChallenge() ; + int GotAbort() ; + int GotCancel() ; + int GotCommand() ; + int GotStderr() ; + int RetDone() ; +static int GotCommandData() ; +static int GotStderrData() ; + + /* PROTOCOL LOGIC: This section of code handles the actual + * protocol. This is also driven by a finite state machine + * + * State tables are sorted by approximate frequency order to + * reduce search time. + */ + + /* Extra ZRINIT headers are the receiver trying to resync. */ + + + + /* If compiling for Send Only or Receive Only, convert table + * entries to no-ops so we don't have to link zmodem[rt].o + */ + +#if SendOnly +#define RStartOps DoneOps +#define RSinitWaitOps DoneOps +#define RFileNameOps DoneOps +#define RCrcOps DoneOps +#define RFileOps DoneOps +#define RDataOps DoneOps +#define RFinishOps DoneOps +#define GotFileName Ignore +#define ResendCrcReq Ignore +#define GotSinitData Ignore +#define ResendRpos Ignore +#define GotFileData Ignore +#define SendRinit Ignore +#else +extern StateTable RStartOps[] ; +extern StateTable RSinitWaitOps[] ; +extern StateTable RFileNameOps[] ; +extern StateTable RCrcOps[] ; +extern StateTable RFileOps[] ; +extern StateTable RDataOps[] ; +extern StateTable RFinishOps[] ; +extern int GotFileName() ; +extern int ResendCrcReq() ; +extern int GotSinitData() ; +extern int ResendRpos() ; +extern int GotFileData() ; +extern int SendRinit() ; +#endif + +#if RcvOnly +#define TStartOps DoneOps +#define TInitOps DoneOps +#define FileWaitOps DoneOps +#define CrcWaitOps DoneOps +#define SendingOps DoneOps +#define SendDoneOps DoneOps +#define SendWaitOps DoneOps +#define SendEofOps DoneOps +#define TFinishOps DoneOps +#define SendMoreFileData Ignore +#else +extern StateTable TStartOps[] ; +extern StateTable TInitOps[] ; +extern StateTable FileWaitOps[] ; +extern StateTable CrcWaitOps[] ; +extern StateTable SendingOps[] ; +extern StateTable SendDoneOps[] ; +extern StateTable SendWaitOps[] ; +extern StateTable SendEofOps[] ; +extern StateTable TFinishOps[] ; +extern int SendMoreFileData() ; +#endif + + + +static StateTable CommandDataOps[] = { +#ifdef COMMENT + {ZRQINIT,f,1,1}, + {ZRINIT,f,1,1}, + {ZSINIT,f,1,1}, + {ZACK,f,1,1}, + {ZFILE,f,1,1}, + {ZSKIP,f,1,1}, + {ZNAK,f,1,1}, + {ZABORT,f,1,1,TFinish}, + {ZFIN,f,1,1}, + {ZRPOS,f,1,1}, + {ZDATA,f,1,1}, + {ZEOF,f,1,1}, + {ZFERR,f,1,1,TFinish}, + {ZCRC,f,1,1}, + {ZCHALLENGE,f,1,1}, + {ZCOMPL,f,1,1}, + {ZCAN,f,1,1}, + {ZFREECNT,f,1,1}, + {ZCOMMAND,f,1,1}, + {ZSTDERR,f,1,1}, +#endif /* COMMENT */ + {99,ZPF,0,0,CommandData}, + } ; + +static StateTable CommandWaitOps[] = { +#ifdef COMMENT + {ZRQINIT,f,1,1}, + {ZRINIT,f,1,1}, + {ZSINIT,f,1,1}, + {ZACK,f,1,1}, + {ZFILE,f,1,1}, + {ZSKIP,f,1,1}, + {ZNAK,f,1,1}, + {ZABORT,f,1,1,TFinish}, + {ZFIN,f,1,1}, + {ZRPOS,f,1,1}, + {ZDATA,f,1,1}, + {ZEOF,f,1,1}, + {ZFERR,f,1,1,TFinish}, + {ZCRC,f,1,1}, + {ZCHALLENGE,f,1,1}, + {ZCOMPL,f,1,1}, + {ZCAN,f,1,1}, + {ZFREECNT,f,1,1}, + {ZCOMMAND,f,1,1}, + {ZSTDERR,f,1,1}, +#endif /* COMMENT */ + {99,ZPF,0,0,CommandWait}, + } ; + +static StateTable StderrDataOps[] = { +#ifdef COMMENT + {ZRQINIT,f,1,1}, + {ZRINIT,f,1,1}, + {ZSINIT,f,1,1}, + {ZACK,f,1,1}, + {ZFILE,f,1,1}, + {ZSKIP,f,1,1}, + {ZNAK,f,1,1}, + {ZABORT,f,1,1,TFinish}, + {ZFIN,f,1,1}, + {ZRPOS,f,1,1}, + {ZDATA,f,1,1}, + {ZEOF,f,1,1}, + {ZFERR,f,1,1,TFinish}, + {ZCRC,f,1,1}, + {ZCHALLENGE,f,1,1}, + {ZCOMPL,f,1,1}, + {ZCAN,f,1,1}, + {ZFREECNT,f,1,1}, + {ZCOMMAND,f,1,1}, + {ZSTDERR,f,1,1}, +#endif /* COMMENT */ + {99,ZPF,0,0,StderrData}, + } ; + +static StateTable DoneOps[] = { + {99,ZPF,0,0,Done}, + } ; + + +static StateTable *tables[] = { + RStartOps, + RSinitWaitOps, + RFileNameOps, + RCrcOps, + RFileOps, + RDataOps, + RDataOps, /* RDataErr is the same as RData */ + RFinishOps, + + TStartOps, + TInitOps, + FileWaitOps, + CrcWaitOps, + SendingOps, + SendWaitOps, + SendDoneOps, + SendEofOps, + TFinishOps, + + CommandDataOps, + CommandWaitOps, + StderrDataOps, + DoneOps, + } ; + + + char *hdrnames[] = { + "ZRQINIT", + "ZRINIT", + "ZSINIT", + "ZACK", + "ZFILE", + "ZSKIP", + "ZNAK", + "ZABORT", + "ZFIN", + "ZRPOS", + "ZDATA", + "ZEOF", + "ZFERR", + "ZCRC", + "ZCHALLENGE", + "ZCOMPL", + "ZCAN", + "ZFREECNT", + "ZCOMMAND", + "ZSTDERR", + } ; + + + + /* This function is called (indirectly) by the ZmodemRcv() + * function when a full header has been received. + */ + +static int +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 ; + + 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 table->func(info) ; +} + + +static int +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 +ZmodemTimeout( register 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 +ZmodemAttention( register ZModem *info ) +{ + /* attention received from remote end */ + if( info->state == Sending ) { + ZOFlush(info) ; + info->interrupt = 1 ; + } + return 0 ; +} + + +int +ZmodemAbort( register ZModem *info ) +{ +static u_char canistr[] = { + CAN,CAN,CAN,CAN,CAN,CAN,CAN,CAN,8,8,8,8,8,8,8,8,8,8 + } ; + info->state = Done ; + ZIFlush(info) ; + ZOFlush(info) ; + return ZXmitStr(canistr, sizeof(canistr), info) ; +} + + + + /* used to completely ignore headers */ + +int +Ignore( ZModem *info ) +{ + return 0 ; +} + + + /* ignore header contents, return ZmDone */ + +int +RetDone( ZModem *info ) +{ + return ZmDone ; +} + + + /* ignore header contents, return ZmErrCancel */ + +int +GotCancel( ZModem *info ) +{ + return ZmErrCancel ; +} + + + /* utility: set up to receive a data packet */ + +int +dataSetup( register ZModem *info ) +{ + info->InputState = Indata ; + info->chrCount = 0 ; + info->crcCount = 0 ; + info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ; + return 0 ; +} + + + /* called when a remote command received. For now, we + * refuse to execute commands. Send EPERM and ignore. + */ +int +GotCommand( ZModem *info ) +{ + u_char rbuf[4] ; + /* TODO: add command capability */ + + rbuf[0] = EPERM ; + rbuf[1] = rbuf[2] = rbuf[3] = 0 ; + return ZXmitHdrHex(ZCOMPL, rbuf, info) ; +} + +static int +GotCommandData( register ZModem *info ) +{ +/* TODO */ +return 0 ; +} + + + + /* called when the remote system wants to put something to + * stderr + */ +int +GotStderr( register ZModem *info ) +{ + info->InputState = Indata ; + info->chrCount = 0 ; + return 0 ; +} + +static int +GotStderrData( register ZModem *info ) +{ + info->buffer[info->chrCount] = '\0' ; + ZStatus(RemoteMessage, info->chrCount, (char *)info->buffer) ; + return 0 ; +} + + + + + /* Protocol failure: An unexpected packet arrived. This could + * be from many sources, such as old pipelined info finally arriving + * or a serial line with echo enabled. Report it and ignore it. + */ + +int +ZPF( ZModem *info ) +{ + info->waitflag = 1 ; /* pause any in-progress transmission */ + ZStatus(ProtocolErr, info->hdrData[0], NULL) ; + return 0 ; +} + + +int +AnswerChallenge( register ZModem *info ) +{ + return ZXmitHdrHex(ZACK, info->hdrData+1, info) ; +} + +int +GotAbort( register ZModem *info ) +{ + ZStatus(RmtCancel, 0, NULL) ; + return ZXmitHdrHex(ZFIN, zeros, info) ; +} + diff --git a/Xmodem/zmodem.h b/Xmodem/zmodem.h new file mode 100644 index 0000000..8f57786 --- /dev/null +++ b/Xmodem/zmodem.h @@ -0,0 +1,709 @@ +/* $Id: zmodem.h,v 1.2 2001/10/25 23:56:29 efalk Exp $ */ + +#ifndef ZMODEM_H +#define ZMODEM_H + + +/* Master header file for Zmodem protocol driver routines */ + +#if 0 + + These routines are intended to be incorportated into other programs, + and do not constitute a zmodem program by themselves, although a + demo zmodem program that uses these routines has been included. + + All information pertaining to a transfer session is kept in a data + structure defined by the caller. This makes it possible for the + caller to drive multiple simultaneous sessions. + + Caller provides I/O, timing and query routines. Caller remains + in control at all times. Typical use is to call these routines + from a select(2) loop. + + Overview: + + Transmit files: + + 1 Init ZModem structure and open communications channel. + 2 Call ZmodemTInit() to begin protocol. + 3 Read characters from remote end and pass them to + ZmodemRcv() until ZmodemRcv() returns ZmDone or an error. + 4 Call ZmodemTFile() to begin transfer of a file. + 5 Read characters from remote end and pass them to + ZmodemRcv() until ZmodemRcv() returns ZmDone or an error. + 6 repeat steps 4&5 for all files. + 7 Call ZmodemTFinish() to indicate that all files have been + transfered. + 8 Read characters from remote end and pass them to + ZmodemRcv() until ZmodemRcv() returns ZmDone or an error. + + Receive files: + + 1 Init ZModem structure and open communications channel. + 2 Call ZmodemRInit() to begin protocol. + 3 Read characters from remote end and pass them to + ZmodemRcv() until ZmodemRcv() returns ZmDone or an error. + + + + In detail: + + 1) Create a ZModem structure as defined below and fill it in. + + 'ifd', 'ofd' are the file descriptors used for input and output. + The interpretation of ifd and ofd is entirely up to the calling + routines, since the caller will be providing I/O functions. + + 'zrinitflags' is composed of the flags described below. + They describe the receive channel capabilities + and affect how the protocol will be carried out. + Define these to receive files. When sending files, these + flags will be defined from the remote end. + + 'zsinitflags' is composed of the flags described below. + They describe the transmit channel capabilities + and affect how the protocol will be carried out. + Define these to send files. When receiving files, these + flags will be defined from the remote end. + + 'attn': For transmit, this is the optional nul-terminated + attention string to be sent by the receiver to interrupt the + transmission. For example, it might simply contain ^O to flush + the buffers, or it might interrupt the sending program. Caller + needs to handle this interrupt properly and call ZmodemAttention(). + + For receive, this is the optional nul-terminated attention string + defined at the remote end. Caller needs to provide a function that + can execute this sequence. + + 'timeout' is read-only, set by the zmodem package. It is the + timeout value in seconds. If this much time passes without + anything being received, caller should call the ZmodemTimeout() + function. Note that timeout may be zero, in which case the + ZTimeout function should be called immediately if no characters + are ready to be received. + + 'packetsize' is set to the preferred data packet size. Define + this when sending files. Recommended length values are 256 + bytes below 2400 bps, 512 at 2400 bps, and 1024 above 4800 bps + or when the data link is known to be relatively error free. + Ignored during receive. + + 'bufsize' is set to the size of the receive buffer size. Define + this when receiving files. When sending files, this will be + defined at the other end. + + 'windowsize' is used to prevent network connections from + buffering too many characters during transmit. Setting + 'windowsize' to nonzero causes zmodem to request status reports + from the receiver during transmit, and to pause if more than + this many bytes have been sent but not yet acknowledged. Set + to zero to turn off windowing. Ignored during receive. + + Other fields are used to track the internal state of the zmodem + driver functions. + + Since this is a source package, you are, of course, free to extend + the ZModem structure as you see fit. + + + 2) Define the following functions: + + int + ZXmitStr(u_char *str, int len, ZModem *info) + Transmit a buffer. Return 0 on success, ZmErrSys on error. + + void + ZIFlush(ZModem *info) + Flush all unread input on receive channel. Do nothing if + this is not possible. + + void + ZOFlush(ZModem *info) + Flush all buffered but not-yet-transmitted output + on transmit channel. Do nothing if this is not possible. + + int + ZAttn(ZModem *info) + Send attention signal defined in ZModem->attn. Do + nothing if this field is NULL. Otherwise, this field + is a nul-terminated character string to be + transmitted. There are two special characters defined + below: ATTNBRK (0335) indicates that a BREAK signal + should be sent, and ATTNPSE (0336) represents a + 1-second pause. + + void + ZFlowControl(int onoff, ZModem *info) + Turn flow control on or off depending on the onoff flag. + + void + ZStatus(int type, int value, char *status) + Called to provide status information. Ignore or display + at your option. Status string is not static, so copy + it if you need it beyond this call. Type is defined + below under "ZStatus() types". + + FILE * + ZOpenFile(char *name, u_long crc, ZModem *info) + + Called when receiving files, this function decides + whether or not to accept the specified file, and + if so, opens it for writing and returns the stdio + file handle. If this function decides not to accept + the file, or cannot open the file, it returns NULL + and the remote sender is told to skip this file. + + info->f0-f3 are the transfer flags, described below under + "ZFILE transfer flags". These describe the type of + transfer desired (binary/ascii), and conditions for the + transfer, such as 'transfer if source newer or longer'. + + info->len is the length of the file in bytes. info->date is + the last modification date of the file, in seconds since + 1-jan-1970. info->mode is the unix file mode + 01000000, or + zero if not known. info->filesRem and info->bytesRem are the + number of files and bytes remaining to be transferred + if known, zero otherwise. 'crc' is the file crc-32 value. + This is only provided if F1 contains ZMCRC. + + int + ZWriteFile(u_char *buffer, int len, FILE *file, ZModem *info) + Write a buffer of data to the file. Normally, you would + simply call fwrite(buffer, 1, len, file), but you may + want to translate line endings, etc. File transfer + flags are available as info->f0,f1,f2,f3. + + Return 0 on success, ZmErrSys on failure, with errno + describing the failure. + + + int + ZCloseFile(ZModem *info) + Close file after successful completion. File modification + date and modes should be set at this time. + + void + ZIdleStr(u_char *buffer, int len, ZModem *info) + Called to pass text that is received out-of-protocol. + This function may ignore or display this text at your + option. + + + 3) Open the communications channel. + + If possible, this should be a full-duplex channel with full + 8-bit transmission. Hardware flow control and/or XON/XOFF + flow control should be enabled. + + + 4) Call these routines: + + All functions return 0 on success, nonzero on failure. See + "error code definitions", below. + + Send: + + int + ZmodemTInit(ZModem *info) + Begin a Zmodem transmit session. + + int + ZmodemRcv(u_char *buffer, int len, ZModem *info) + Call whenever characters are received. If this function + returns ZmDone, previous function has completed successfully, + either call ZmodemTFile() to start next file, or call + ZmodemTFinish() to terminate the session. + + int + ZmodemTimeout(ZModem *info) + Call whenever the timeout period expires and no + characters have been received. + + int + ZmodemAttention(ZModem *info) + Call whenever the attention sequence has been received + from the remote end. It is safe to call this function + from an interrupt handler. + + int + ZmodemTFile(char *filename, char *rfilename, + u_char f0,f1,f2,f3, int filesRem, int bytesRem, ZModem *info) + Begin transmitting a file. If filename is not NULL, + then this function will open it. Otherwise, info->file + must point to a stdio stream that is open for input. + It is preferable to provide the filename, so that + Zmodem can transmit file size and other information. + 'rfilename' is the filename given to the remote end. + This may be the same as filename, the file part of + filename, or something else alltogether. 'rfilename' + must not be longer than the smallest data packet the + remote end might be willing to receive (about 200 + characters). f0-f3 are transfer flags, see "ZCBIN" + below. 'filesRem' and 'bytesRem' are the number of files + and bytes remaining to be transmitted, if known; zero if + not. + + If 'filename' cannot be accessed, ZmodemTFile() returns + ZmErrCantOpen. The link is still established, so you + need to either proceed with the next file or call + ZmodemTFinish(). + + int + ZmodemTFinish(ZModem *info) + Call after final file transfer has completed successfully. + + int + ZmodemAbort(ZModem *info) + Call to abort transfer. Physical connection remains + open until you close it. + + + Receive: + + int + ZmodemRInit(ZModem *info) + Call to get ready to receive first file. This function + will inform the sender that we are ready to receive. + + int + ZmodemRcv(u_char *buffer, int len, ZModem *info) + Call whenever characters are received. If this + function returns ZmDone, all file transfers have + completed successfully. + + int + ZmodemTimeout(ZModem *info) + Call whenever the timeout period expires and no + characters have been received. + + int + ZmodemAbort(ZModem *info) + Call to abort transfer. + + Ymodem and Xmodem: + + int YmodemTInit(ZModem *info) + int XmodemTInit(ZModem *info) + int YmodemRInit(ZModem *info) + int XmodemRInit(ZModem *info) + Same semantics as ZmodemTInit and ZmodemRInit. It is + not normally necessary to call the Ymodem*Init() functions + as the Zmodem protocol will automatically switch to Ymodem + when needed. + + + Utility: + + u_long + FileCrc(char *name) + Return CRC-32 of file. + + 5) Return Values: + + ZmDone Done. Proceed with next file or ZmodemTFinish + (transmit) or exit (receive). + + ZmErrInt Internal error. Link has been closed. + ZmErrSys System error, see errno. Link is closed. + ZmErrNotOpen not used. + ZmFileTooLong not used. + ZmFileCantWrite not used. + ZmErrCantOpen Can not open file, see errno. Link is still open. + ZmErrInitTo Transmitter failed to respond to init req. Link closed. + ZmErrSequence Packet received out of sequence. Link is closed. + ZmErrCancel Cancelled by remote end. Link is closed. + ZmErrRcvTo Remote end timed out during transfer. Link is closed. + ZmErrSndTo Remote end timed out during transfer. Link is closed. + ZmErrCmdTo Remote end timed out during transfer. Link is closed. + + Note that "link is closed" means that the remote end is (presumably) + no longer operating. The actual communications channel is not + closed unless you close it. "Link is still open" means that the + remote end is still ready to receive the next file. + +#endif + + /* PARAMETERS + * + * The following #defines control the behavior of the Zmodem + * package. Note that these may be replaced with variables + * if you like. For example, "#define DoInitRZ" may be replaced + * with "extern int DoInitRz" to use a global variable, or with + * "#define DoInitRZ (info->doInitRz)" to use a variable you + * add to the ZModem structure. + * + * It is assumed that the compiler is good enough to optimize + * "if( 0 )" and "if( 1 )" cases. If not, you may wish to modify + * the source code to use #ifdef instead. + */ + +#define DoInitRZ 1 /* send initial "rz\r" when transmitting */ +#define AllowCommand 0 /* allow remote end to execute commands */ +#define SendSample 1 /* sender can sample reverse channel */ +#define SendAttn 1 /* sender can be interrupted with Attn signal */ +#define ResponseTime 10 /* reasonable response time for sender to + * respond to requests from receiver */ +#define SerialNo 1 /* receiver serial # */ +#define MaxNoise 64 /* max "noise" characters before transmission + * pauses */ +#define MaxErrs 20 /* Max receive errors before cancel */ +#define AlwaysSinit 1 /* always send ZSINIT header, even if not + * needed, this makes protocol more robust */ + +#define SendOnly 0 /* compiles smaller version for send only */ +#define RcvOnly 0 /* compiles smaller version for receive only */ + + /* constants */ + + +#include +#include + + + + + /* Internal State */ + +typedef enum zmstate { + /* receive */ + RStart, /* sent RINIT, waiting for ZFILE or SINIT */ + RSinitWait, /* got SINIT, waiting for data */ + RFileName, /* got ZFILE, waiting for filename & info */ + RCrc, /* got filename, want crc too */ + RFile, /* got filename, ready to read */ + RData, /* reading data */ + RDataErr, /* encountered error, ignoring input */ + RFinish, /* sent ZFIN, waiting for 'OO' */ + + /* transmit */ + TStart, /* waiting for INIT frame from other end */ + TInit, /* received INIT, sent INIT, waiting for ZACK */ + FileWait, /* sent file header, waiting for ZRPOS */ + CrcWait, /* sent file crc, waiting for ZRPOS */ + Sending, /* sending data subpackets, ready for int */ + SendWait, /* waiting for ZACK */ + SendDone, /* file finished, need to send EOF */ + SendEof, /* sent EOF, waiting for ZACK */ + TFinish, /* sent ZFIN, waiting for ZFIN */ + + /* general */ + CommandData, /* waiting for command data */ + CommandWait, /* waiting for command to execute */ + StderrData, /* waiting for stderr data */ + Done, + + /* x/ymodem transmit */ + YTStart, /* waiting for 'G', 'C' or NAK */ + YTFile, /* sent filename, waiting for ACK */ + YTDataWait, /* ready to send data, waiting for 'C' */ + YTData, /* sent data, waiting for ACK */ + YTEOF, /* sent eof, waiting for ACK */ + YTFin, /* sent null filename, waiting for ACK */ + + /* x/ymodem receive */ + YRStart, /* sent 'C', waiting for filename */ + YRDataWait, /* received filename, waiting for data */ + YRData, /* receiving filename or data */ + YREOF /* received first EOT, waiting for 2nd */ + + } ZMState ; + + + + +typedef struct { + int ifd ; /* input fd, for use by caller's routines */ + int ofd ; /* output fd, for use by caller's routines */ + FILE *file ; /* file being transfered */ + int zrinitflags ; /* receiver capabilities, see below */ + int zsinitflags ; /* sender capabilities, see below */ + char *attn ; /* attention string, see below */ + int timeout ; /* timeout value, in seconds */ + int bufsize ; /* receive buffer size, bytes */ + int packetsize ; /* preferred transmit packet size */ + int windowsize ; /* max window size */ + + /* file attributes: read-only */ + + int filesRem, bytesRem ; + u_char f0,f1,f2,f3 ; /* file flags */ + int len,mode,fileType ; /* file flags */ + u_long date ; /* file date */ + + /* From here down, internal to Zmodem package */ + + ZMState state ; /* protocol internal state */ + char *filename ; /* filename */ + char *rfilename ; /* remote filename */ + int crc32 ; /* use 32-bit crc */ + int pktLen ; /* length of this packet */ + int DataType ; /* input data type */ + int PacketType ; /* type of this packet */ + int rcvlen ; + int chrCount ; /* chars received in current header/buffer */ + int crcCount ; /* crc characters remaining at end of buffer */ + int canCount ; /* how many CAN chars received? */ + int noiseCount ; /* how many noise chars received? */ + int errorFlush ; /* ignore incoming data because of error */ + u_char *buffer ; /* data buffer */ + u_long offset ; /* file offset */ + u_long lastOffset ; /* last acknowledged offset */ + u_long zrposOffset ; /* last offset specified w/zrpos */ + int ylen, bufp ; /* len,location of last Ymodem packet */ + int fileEof ; /* file eof reached */ + int packetCount ; /* # packets received */ + int errCount ; /* how many data errors? */ + int timeoutCount ; /* how many times timed out? */ + int windowCount ; /* how much data sent in current window */ + int atSign ; /* last char was '@' */ + int lastCR ; /* last char was CR */ + int escCtrl ; /* other end requests ctrl chars be escaped */ + int escHibit ; /* other end requests hi bit be escaped */ + int escape ; /* next character is escaped */ + int interrupt ; /* received attention signal */ + int waitflag ; /* next send should wait */ + /* parser state */ + enum {Idle, Padding, Inhdr, Indata, Finish, Ysend, Yrcv} InputState ; + enum {XMODEM, YMODEM, ZMODEM} Protocol ; + u_char hdrData[9] ; /* header type and data */ + u_char fileFlags[4] ; /* file xfer flags */ + u_long crc ; /* crc of incoming header/data */ + enum {Full, StrWindow, SlidingWindow, Segmented} Streaming ; + } ZModem ; + + + /* ZRINIT flags. Describe receiver capabilities */ + +#define CANFDX 1 /* Rx is Full duplex */ +#define CANOVIO 2 /* Rx can overlap I/O */ +#define CANBRK 4 /* Rx can send a break */ +#define CANCRY 010 /* Rx can decrypt */ +#define CANLZW 020 /* Rx can uncompress */ +#define CANFC32 040 /* Rx can use 32-bit crc */ +#define ESCCTL 0100 /* Rx needs control chars escaped */ +#define ESC8 0200 /* Rx needs 8th bit escaped. */ + + /* ZSINIT flags. Describe sender capabilities */ + +#define TESCCTL 0100 /* Tx needs control chars escaped */ +#define TESC8 0200 /* Tx needs 8th bit escaped. */ + + + /* ZFILE transfer flags */ + + /* F0 */ +#define ZCBIN 1 /* binary transfer */ +#define ZCNL 2 /* convert NL to local eol convention */ +#define ZCRESUM 3 /* resume interrupted file xfer, or append to a + growing file. */ + + /* F1 */ +#define ZMNEWL 1 /* transfer if source newer or longer */ +#define ZMCRC 2 /* transfer if different CRC or length */ +#define ZMAPND 3 /* append to existing file, if any */ +#define ZMCLOB 4 /* replace existing file */ +#define ZMNEW 5 /* transfer if source is newer */ +#define ZMDIFF 6 /* transfer if dates or lengths different */ +#define ZMPROT 7 /* protect: transfer only if dest doesn't exist */ +#define ZMCHNG 8 /* change filename if destination exists */ +#define ZMMASK 037 /* mask for above. */ +#define ZMSKNOLOC 0200 /* skip if not present at Rx end */ + + /* F2 */ +#define ZTLZW 1 /* lzw compression */ +#define ZTRLE 3 /* run-length encoding */ + + /* F3 */ +#define ZCANVHDR 1 /* variable headers ok */ +#define ZRWOVR 4 /* byte position for receive window override/256 */ +#define ZXSPARS 64 /* encoding for sparse file ops. */ + + + + /* ATTN string special characters. All other characters sent verbose */ + +#define ATTNBRK '\335' /* send break signal */ +#define ATTNPSE '\336' /* pause for one second */ + + + /* ZStatus() types */ + +#define RcvByteCount 0 /* value is # bytes received */ +#define SndByteCount 1 /* value is # bytes sent */ +#define RcvTimeout 2 /* receiver did not respond, aborting */ +#define SndTimeout 3 /* value is # of consecutive send timeouts */ +#define RmtCancel 4 /* remote end has cancelled */ +#define ProtocolErr 5 /* protocol error has occurred, val=hdr */ +#define RemoteMessage 6 /* message from remote end */ +#define DataErr 7 /* data error, val=error count */ +#define FileErr 8 /* error writing file, val=errno */ +#define FileBegin 9 /* file transfer begins, str=name */ +#define FileEnd 10 /* file transfer ends, str=name */ +#define FileSkip 11 /* file being skipped, str=name */ + + + /* error code definitions [O] means link still open */ + +#define ZmDone -1 /* done */ +#define ZmErrInt -2 /* internal error */ +#define ZmErrSys -3 /* system error, see errno */ +#define ZmErrNotOpen -4 /* communication channel not open */ +#define ZmErrCantOpen -5 /* can't open file, see errno [O] */ +#define ZmFileTooLong -6 /* remote filename too long [O] */ +#define ZmFileCantWrite -7 /* could not write file, see errno */ +#define ZmDataErr -8 /* too many data errors */ +#define ZmErrInitTo -10 /* transmitter failed to respond to init req. */ +#define ZmErrSequence -11 /* packet received out of sequence */ +#define ZmErrCancel -12 /* cancelled by remote end */ +#define ZmErrRcvTo -13 /* remote receiver timed out during transfer */ +#define ZmErrSndTo -14 /* remote sender timed out during transfer */ +#define ZmErrCmdTo -15 /* remote command timed out */ + + + /* zmodem-supplied functions: */ + + +extern int ZmodemTInit(ZModem *info) ; +extern int ZmodemTFile(char *file, char *rmtname, + u_int f0, u_int f1, u_int f2, u_int f3, + int filesRem, int bytesRem, ZModem *info) ; +extern int ZmodemTFinish(ZModem *info) ; +extern int ZmodemAbort(ZModem *info) ; +extern int ZmodemRInit(ZModem *info) ; +extern int ZmodemRcv(u_char *str, int len, ZModem *info) ; +extern int ZmodemTimeout(ZModem *info) ; +extern int ZmodemAttention(ZModem *info) ; + +extern int YmodemTInit(ZModem *info) ; +extern int XmodemTInit(ZModem *info) ; +extern int YmodemRInit(ZModem *info) ; +extern int XmodemRInit(ZModem *info) ; + +extern u_long FileCrc(char *name) ; +extern char *sname(ZModem *) ; +extern char *sname2(ZMState) ; + +#ifdef DEBUG +extern FILE *zmodemlogfile ; +extern void zmodemlog(const char *, ...) ; +#else +#define zmodemlog +#endif + + + + + /* caller-supplied functions: */ + + +extern int ZXmitChr(u_char c, ZModem *info) ; +extern int ZXmitStr(u_char *str, int len, ZModem *info) ; +extern void ZIFlush(ZModem *info) ; +extern void ZOFlush(ZModem *info) ; +extern int ZAttn(ZModem *info) ; +extern void ZStatus(int type, int value, char *status) ; +extern FILE *ZOpenFile(char *name, u_long crc, ZModem *info) ; + + + /* From here on down, internal to Zmodem package */ + + + /* ZModem character definitions */ + +#define ZDLE 030 /* zmodem escape is CAN */ +#define ZPAD '*' /* pad */ +#define ZBIN 'A' /* introduces 16-bit crc binary header */ +#define ZHEX 'B' /* introduces 16-bit crc hex header */ +#define ZBIN32 'C' /* introduces 32-bit crc binary header */ +#define ZBINR32 'D' /* introduces RLE packed binary frame w/32-bit crc */ +#define ZVBIN 'a' /* alternate ZBIN */ +#define ZVHEX 'b' /* alternate ZHEX */ +#define ZVBIN32 'c' /* alternate ZBIN32 */ +#define ZVBINR32 'd' /* alternate ZBINR32 */ +#define ZRESC 0177 /* RLE flag/escape character */ + + + + /* ZModem header type codes */ + +#define ZRQINIT 0 /* request receive init */ +#define ZRINIT 1 /* receive init */ +#define ZSINIT 2 /* send init sequence, define Attn */ +#define ZACK 3 /* ACK */ +#define ZFILE 4 /* file name, from sender */ +#define ZSKIP 5 /* skip file command, from receiver */ +#define ZNAK 6 /* last packet was garbled */ +#define ZABORT 7 /* abort */ +#define ZFIN 8 /* finish session */ +#define ZRPOS 9 /* resume file from this position, from receiver */ +#define ZDATA 10 /* data packets to follow, from sender */ +#define ZEOF 11 /* end of file, from sender */ +#define ZFERR 12 /* fatal i/o error, from receiver */ +#define ZCRC 13 /* request for file crc, from receiver */ +#define ZCHALLENGE 14 /* "send this number back to me", from receiver */ +#define ZCOMPL 15 /* request is complete */ +#define ZCAN 16 /* other end cancelled with CAN-CAN-CAN-CAN-CAN */ +#define ZFREECNT 17 /* request for free bytes on filesystem */ +#define ZCOMMAND 18 /* command, from sending program */ +#define ZSTDERR 19 /* output this message to stderr */ + + + /* ZDLE escape sequences */ + + +#define ZCRCE 'h' /* CRC next, frame ends, header follows */ +#define ZCRCG 'i' /* CRC next, frame continues nonstop */ +#define ZCRCQ 'j' /* CRC next, send ZACK, frame continues nonstop */ +#define ZCRCW 'k' /* CRC next, send ZACK, frame ends */ +#define ZRUB0 'l' /* translate to 0177 */ +#define ZRUB1 'm' /* translate to 0377 */ + + + /* ascii definitions */ + +#define SOH 1 /* ^A */ +#define STX 2 /* ^B */ +#define EOT 4 /* ^D */ +#define ACK 6 /* ^F */ +#define DLE 16 /* ^P */ +#define XON 17 /* ^Q */ +#define XOFF 19 /* ^S */ +#define NAK 21 /* ^U */ +#define SYN 22 /* ^V */ +#define CAN 24 /* ^X */ + + + +extern int ZXmitHdr() ; +extern int ZXmitHdrHex() ; +extern int ZXmitHdrBin() ; +extern int ZXmitHdrBin32() ; +extern u_char *putZdle( u_char *ptr, u_char c, ZModem *info ) ; + +extern u_char *ZEnc4() ; +extern u_long ZDec4() ; + + + /* state table entry. There is one row of the table per + * possible state. Each row is a row of all reasonable + * inputs for this state. The entries are sorted so that + * the most common inputs occur first, to reduce search time + * Unexpected input is reported and ignored, as it might be + * caused by echo or something. + * + * Extra ZRINIT headers are the receiver trying to resync. + */ + +typedef struct { + int type ; /* frame type */ + int (*func)() ; /* transition function */ + int IFlush ; /* flag: flush input first */ + int OFlush ; /* flag: flush output first */ + ZMState newstate ; /* new state. May be overridden by func */ + } StateTable ; + + + + +#endif diff --git a/Xmodem/zmodemdump.c b/Xmodem/zmodemdump.c new file mode 100644 index 0000000..44f8f85 --- /dev/null +++ b/Xmodem/zmodemdump.c @@ -0,0 +1,582 @@ +#ifndef lint +static const char rcsid[] = "$Id: zmodemdump.c,v 1.2 2001/10/25 23:56:29 efalk Exp $" ; +#endif + + + /* variation on serialmon companion program serialdump, which + * interprets data as a zmodem dialog + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "crctab.h" + +#define MAXBUF 2048 +#define MAXLINE 16 +#define MAXHBUF 128 +#define OLINELEN (MAXLINE*2) + +#define CAN 030 +#define XON 021 + +#define ZDLE 030 +#define ZPAD '*' +#define ZBIN 'A' +#define ZHEX 'B' +#define ZBIN32 'C' +#define ZBINR32 'D' +#define ZVBIN 'a' +#define ZVHEX 'b' +#define ZVBIN32 'c' +#define ZVBINR32 'd' +#define ZRESC 0177 + + +#define ZRQINIT 0 /* request receive init */ +#define ZRINIT 1 /* receive init */ +#define ZSINIT 2 /* send init sequence, define Attn */ +#define ZACK 3 /* ACK */ +#define ZFILE 4 /* file name, from sender */ +#define ZSKIP 5 /* skip file command, from receiver */ +#define ZNAK 6 /* last packet was garbled */ +#define ZABORT 7 /* abort */ +#define ZFIN 8 /* finish session */ +#define ZRPOS 9 /* resume file from this position, from receiver */ +#define ZDATA 10 /* data packets to follow, from sender */ +#define ZEOF 11 /* end of file, from sender */ +#define ZFERR 12 /* fatal i/o error, from receiver */ +#define ZCRC 13 /* request for file crc, from receiver */ +#define ZCHALLENGE 14 /* "send this number back to me", from receiver */ +#define ZCOMPL 15 /* request is complete */ +#define ZCAN 16 /* other end cancelled with CAN-CAN-CAN-CAN-CAN */ +#define ZFREECNT 17 /* request for free bytes on filesystem */ +#define ZCOMMAND 18 /* command, from sending program */ +#define ZSTDERR 19 /* output this message to stderr */ + +#define ZCRCE 'h' +#define ZCRCG 'i' +#define ZCRCQ 'j' +#define ZCRCW 'k' +#define ZRUB0 'l' +#define ZRUB1 'm' + +typedef enum {Idle, Padding, HexHeader, Header16, Header32, + InData, InCrc} ZState ; + +typedef struct { + ZState state ; + int headertype ; + int data[4] ; + int crcBytes[4] ; + int count ; + int zdlePend ; /* ZDLE received */ + int crcCmd ; + int crclen ; + u_long crc ; + } Zinfo ; + + u_char buffer[MAXBUF] ; + u_char line[MAXLINE] ; + u_char hbuffer[MAXHBUF] ; + int linecnt = 0 ; + int hbufcnt = 0 ; + + +main( int argc, char **argv ) +{ + int i,j ; + int len ; + int which ; + struct timeval timestamp, oldtime ; + struct tm *tm ; + + Zinfo Ainfo, Binfo ; + + printf("serial log. 'A' is application, 'B' is serial port\n\n") ; + + oldtime.tv_sec = 0 ; + + Ainfo.state = Binfo.state = Idle ; + Ainfo.zdlePend = Binfo.zdlePend = 0 ; + + while( (i=fread((char *)&which, sizeof(which), 1, stdin)) > 0 ) + { + i = fread((char *)×tamp, sizeof(timestamp), 1, stdin) ; + i = fread((char *)&len, sizeof(len), 1, stdin) ; + if( timestamp.tv_sec != oldtime.tv_sec || + timestamp.tv_usec != oldtime.tv_usec ) + { + if( linecnt > 0 ) + dumpLine() ; + + tm = localtime(×tamp.tv_sec) ; + printf("%c: %2d:%2.2d:%2.2d.%2.2d\n", 'A'+which, + tm->tm_hour,tm->tm_min,tm->tm_sec, timestamp.tv_usec/10000 ) ; + + oldtime = timestamp ; + } + + + while( len > 0 ) + { + i = MAXBUF ; + if( len < i ) i = len ; + j = fread(buffer, 1, i, stdin) ; +assert(j <= MAXBUF) ; + len -= j ; + parseData(which ? &Binfo : &Ainfo, j) ; + } + + while( len > 0 ) + { + i = MAXLINE - linecnt ; + if( len < i ) i = len ; +assert(linecnt+i <= MAXLINE) ; + j = fread(line+linecnt, 1, i, stdin) ; +assert(linecnt+j <= MAXLINE) ; + len -= j ; + linecnt += j ; + if( linecnt >= MAXLINE ) + dumpLine() ; + } + } + if( linecnt > 0 ) + dumpLine() ; + + exit(0) ; +} + +char +toprintable(char c) +{ + c &= 0177 ; + if( c >= 0x20 && c <= 0x7e ) + return c ; + else + return '.' ; +} + +dumpLine() +{ + int i,j ; + + if( linecnt <= 0 ) + return ; + + if( linecnt > MAXLINE ) linecnt = MAXLINE ; + printf(" ") ; + for(i=0; i= 0; ptr++) { +assert(ptr >= buffer && ptr < buffer+MAXBUF) ; + c = *ptr ; + if( c != XON ) + switch( info->state ) { + case Idle: + if( c != ZPAD ) + dataChar(c) ; + else { + info->state = Padding ; + info->count = 1 ; + hbuffer[0] = c ; + } + break ; + + case Padding: + if( c == ZDLE ) { + info->zdlePend = 1 ; + } + else if( info->zdlePend ) { + info->zdlePend = 0 ; + info->count = 0 ; + switch(c) { + case ZHEX: + info->state = HexHeader ; + info->crclen=2 ; + info->crc = 0 ; + break ; + case ZBIN: + info->state = Header16 ; + info->crclen=2 ; + info->crc = 0 ; + break ; + case ZBIN32: + info->state = Header32 ; + info->crclen=4 ; + info->crc = 0xffffffff ; + break ; + default: + cancelHeader(info) ; break ; + } + } + else if( c == ZPAD ) + { + if( info->count < 2 ) { +assert(info->count < MAXHBUF) ; + hbuffer[info->count++] = c ; + } + else + dataChar(c) ; + } + else + cancelHeader(info) ; break ; + break ; + + case HexHeader: + if( c == ZDLE && !info->zdlePend ) { + info->zdlePend = 1 ; + break ; + } + + if( info->zdlePend ) { + c = zdle(c) ; + info->zdlePend = 0 ; + } + + idx = info->count++ ; +assert(idx < MAXHBUF) ; + hbuffer[idx] = c ; + if( info->count >= 16 ) { /* end of header */ + info->headertype = hex2(hbuffer+0) ; + info->data[0] = hex2(hbuffer+2) ; + info->data[1] = hex2(hbuffer+4) ; + info->data[2] = hex2(hbuffer+6) ; + info->data[3] = hex2(hbuffer+8) ; + info->crcBytes[0] = hex2(hbuffer+10) ; + info->crcBytes[1] = hex2(hbuffer+12) ; + displayHeader(info) ; + } + break ; + + case Header16: + if( c == ZDLE && !info->zdlePend ) { + info->zdlePend = 1 ; + break ; + } + + if( info->zdlePend ) { + c = zdle(c) ; + info->zdlePend = 0 ; + } + + idx = info->count++ ; +assert(idx < MAXHBUF) ; + hbuffer[idx] = c ; + if( info->count >= 7 ) { /* end of header */ + info->headertype = hbuffer[0] ; + info->data[0] = hbuffer[1] ; + info->data[1] = hbuffer[2] ; + info->data[2] = hbuffer[3] ; + info->data[3] = hbuffer[4] ; + info->crcBytes[0] = hbuffer[5] ; + info->crcBytes[1] = hbuffer[6] ; + displayHeader(info) ; + } + break ; + + case Header32: + if( c == ZDLE && !info->zdlePend ) { + info->zdlePend = 1 ; + break ; + } + + if( info->zdlePend ) { + c = zdle(c) ; + info->zdlePend = 0 ; + } + + idx = info->count++ ; +assert(idx < MAXHBUF) ; + hbuffer[idx] = c ; + if( info->count >= 9 ) { /* end of header */ + info->headertype = hbuffer[0] ; + info->data[0] = hbuffer[1] ; + info->data[1] = hbuffer[2] ; + info->data[2] = hbuffer[3] ; + info->data[3] = hbuffer[4] ; + info->crcBytes[0] = hbuffer[5] ; + info->crcBytes[1] = hbuffer[6] ; + info->crcBytes[2] = hbuffer[7] ; + info->crcBytes[3] = hbuffer[8] ; + displayHeader(info) ; + } + break ; + + case InData: + if( info->zdlePend ) + { + info->zdlePend = 0 ; + switch( c ) { + case ZCRCE: + case ZCRCW: + case ZCRCG: + case ZCRCQ: + info->crcCmd = c ; + dumpLine() ; + info->state = InCrc ; + info->count = 0 ; + break ; + default: + c = zdle(c) ; + dataChar(c) ; + break ; + } + } + else if( c == ZDLE ) + info->zdlePend = 1 ; + else + dataChar(c) ; + break ; + + case InCrc: + if( info->zdlePend ) { + c = zdle(c) ; + info->zdlePend = 0 ; + } + + if( c == ZDLE ) + info->zdlePend = 1 ; + else + { + dataChar(c) ; + if( ++info->count >= info->crclen ) + { + dumpCrc() ; + switch( info->crcCmd ) { + case ZCRCE: + printf(" ZCRCE: end of frame, header follows\n") ; + info->state = Idle ; + break ; + case ZCRCW: + printf(" ZCRCW: end of frame, send ZACK\n") ; + info->state = Idle ; + break ; + case ZCRCG: + printf(" ZCRCG: more data follows:\n") ; + info->state = InData ; + break ; + case ZCRCQ: + printf(" ZCRCQ: send ZACK, more data follows:\n") ; + info->state = InData ; + break ; + } + } + } + break ; + } + } +} + + + /* handle a character that's not part of the protocol */ + +dataChar(int c) +{ +assert(linecnt < MAXLINE) ; + line[linecnt++] = c ; + if( linecnt >= MAXLINE ) + dumpLine() ; +} + + + /* here if we thought we were in a header, but were wrong */ + +cancelHeader( Zinfo *info ) +{ + int i ; + + for(i=0; icount; ++i) { +assert(i < MAXHBUF) ; + dataChar(hbuffer[i]) ; + } + + info->state = Idle ; +} + + + /* here to display a full header. CRC's not currently checked */ + +displayHeader( Zinfo *info ) +{ + int i ; + u_long crc ; + int h32 = info->state == Header32 ; +static char *names[] = { + "ZRQINIT", "ZRINIT", "ZSINIT", "ZACK", "ZFILE", "ZSKIP", + "ZNAK", "ZABORT", "ZFIN", "ZRPOS", "ZDATA", "ZEOF", "ZFERR", + "ZCRC", "ZCHALLENGE", "ZCOMPL", "ZCAN", "ZFREECNT", + "ZCOMMAND", "ZSTDERR",} ; + + dumpLine() ; + + printf(" ") ; + switch( info->state ) { + case HexHeader: printf("hex header") ; break ; + case Header16: printf("bin header") ; break ; + case Header32: printf("bin32 header") ; break ; + } + printf(" %d: %s: d=[%x %x %x %x]", info->headertype, + info->headertype <= ZSTDERR ? names[info->headertype] : "BAD HEADER", + info->data[0], info->data[1], info->data[2], info->data[3]) ; + switch( info->state ) { + case HexHeader: + case Header16: + printf(" crc=[%x %x]", info->crcBytes[0], info->crcBytes[1]) ; + break ; + case Header32: + printf(" crc=[%x %x %x %x]", + info->crcBytes[0], info->crcBytes[1], + info->crcBytes[2], info->crcBytes[3]) ; + break ; + } + + switch( info->headertype ) { + case ZRQINIT: + case ZRINIT: + case ZACK: + case ZSKIP: + case ZNAK: + case ZABORT: + case ZFIN: + case ZRPOS: + case ZEOF: + case ZFERR: + case ZCRC: + case ZCHALLENGE: + case ZCOMPL: + case ZCAN: + case ZFREECNT: + case ZCOMMAND: + printf("\n") ; + info->state = Idle ; + break ; + + case ZSINIT: + case ZFILE: + case ZDATA: + case ZSTDERR: + printf(", data follows:\n") ; + info->state = InData ; + info->count = 0 ; + break ; + } + + if( !h32 ) + { + crc = 0 ; + crc = updcrc(info->headertype, crc) ; + crc = updcrc(info->data[0], crc) ; + crc = updcrc(info->data[1], crc) ; + crc = updcrc(info->data[2], crc) ; + crc = updcrc(info->data[3], crc) ; + crc = updcrc(info->crcBytes[0], crc) ; + crc = updcrc(info->crcBytes[1], crc) ; + if( crc&0xffff != 0 ) + printf(" CRC ERROR\n") ; + } + else + { + crc = 0xffffffff ; + crc = UPDC32(info->headertype, crc) ; + crc = UPDC32(info->data[0], crc) ; + crc = UPDC32(info->data[1], crc) ; + crc = UPDC32(info->data[2], crc) ; + crc = UPDC32(info->data[3], crc) ; + crc = UPDC32(info->crcBytes[0], crc) ; + crc = UPDC32(info->crcBytes[1], crc) ; + crc = UPDC32(info->crcBytes[2], crc) ; + crc = UPDC32(info->crcBytes[3], crc) ; + if( crc != 0xdebb20e3 ) + printf(" CRC ERROR\n") ; + } +} + +dumpCrc() +{ + int i,j ; + + if( linecnt <= 0 ) + return ; + + if( linecnt > MAXLINE ) linecnt = MAXLINE ; + printf(" crc: ") ; + for(i=0; i 9 ) + chr -= 'A'-'0'-10 ; + if( chr > 15 ) + chr -= 'a' - 'A' ; + + return chr ; +} + + + /* apply ZDLE to chr */ + +int +zdle(int chr) +{ + switch( chr ) { + case ZRUB0: return 0177 ; + case ZRUB1: return 0377 ; + default: + if( (chr & 0140) == 0100 ) + return chr ^ 0100 ; + return -1 ; + } +} diff --git a/Xmodem/zmodemr.c b/Xmodem/zmodemr.c new file mode 100644 index 0000000..0956155 --- /dev/null +++ b/Xmodem/zmodemr.c @@ -0,0 +1,750 @@ +#ifndef lint +static const char rcsid[] = "$Id: zmodemr.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ; +#endif + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @@@@@ @ @ @@@ @@@@ @@@@@ @ @ @@@@ + * @ @@ @@ @ @ @ @ @ @@ @@ @ @ + * @ @ @ @ @ @ @ @ @@@ @ @ @ @@@@ + * @ @ @ @ @ @ @ @ @ @ @ @ @ @ + * @@@@@ @ @ @ @@@ @@@@ @@@@@ @ @ @ @ @ + * + * ZMODEMR - receive side of zmodem protocol + * + * receive side of zmodem protocol + * + * This code is designed to be called from inside a larger + * program, so it is implemented as a state machine where + * practical. + * + * functions: + * + * ZmodemRInit(ZModem *info) + * Initiate a connection + * + * ZmodemRAbort(ZModem *info) + * abort transfer + * + * all functions return 0 on success, 1 on failure + * + * + * Edward A. Falk + * + * January, 1995 + * + * + * + **********/ + +#include +#include +#include +#include +#include + +#include "zmodem.h" +#include "crctab.h" + +extern int errno ; + +extern int ZWriteFile(u_char *buffer, int len, FILE *, ZModem *); +extern int ZCloseFile(ZModem *info) ; +extern void ZFlowControl(int onoff, ZModem *info) ; + +static u_char zeros[4] = {0,0,0,0} ; + + + + + +int +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 ; + + info->buffer = (u_char *)malloc(8192) ; + + 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 +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 = (u_char *)malloc(1024) ; + + info->state = YRStart ; + info->packetCount = -1 ; + info->timeoutCount = 0 ; + info->timeout = 10 ; + info->offset = 0 ; + + ZIFlush(info) ; + + return ZXmitStr((u_char *)"C", 1, info) ; +} + + + + + +extern int ZPF() ; +extern int Ignore() ; +extern int GotCommand() ; +extern int GotStderr() ; + + int SendRinit() ; +static int GotSinit() ; +static int GotFile() ; +static int GotFin() ; +static int GotData() ; +static int GotEof() ; +static int GotFreecnt() ; +static int GotFileCrc() ; + int ResendCrcReq() ; + int ResendRpos() ; + + /* sent ZRINIT, waiting for ZSINIT or ZFILE */ + StateTable RStartOps[] = { + {ZSINIT,GotSinit,0,1,RSinitWait}, /* SINIT, wait for attn str */ + {ZFILE,GotFile,0,0,RFileName}, /* FILE, wait for filename */ + {ZRQINIT,SendRinit,0,1,RStart}, /* sender confused, resend */ + {ZFIN,GotFin,1,0,RFinish}, /* sender shutting down */ + {ZNAK,SendRinit,1,0,RStart}, /* RINIT was bad, resend */ +#ifdef TODO + {ZCOMPL,f,1,1,s}, +#endif /* TODO */ + {ZFREECNT,GotFreecnt,0,0,RStart}, /* sender wants free space */ + {ZCOMMAND,GotCommand,0,0,CommandData}, /* sender wants command */ + {ZSTDERR,GotStderr,0,0,StderrData}, /* sender wants to send msg */ + {99,ZPF,0,0,RStart}, /* anything else is an error */ + } ; + + StateTable RSinitWaitOps[] = { /* waiting for data */ + {99,ZPF,0,0,RSinitWait}, + } ; + + StateTable RFileNameOps[] = { /* waiting for file name */ + {99,ZPF,0,0,RFileName}, + } ; + + StateTable RCrcOps[] = { /* waiting for CRC */ + {ZCRC,GotFileCrc,0,0,RFile}, /* sender sent it */ + {ZNAK,ResendCrcReq,0,0,RCrc}, /* ZCRC was bad, resend */ + {ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */ + {ZFIN,GotFin,1,1,RFinish}, /* sender signing off */ + {99,ZPF,0,0,RCrc}, + } ; + + StateTable RFileOps[] = { /* waiting for ZDATA */ + {ZDATA,GotData,0,0,RData}, /* got it */ + {ZNAK,ResendRpos,0,0,RFile}, /* ZRPOS was bad, resend */ + {ZEOF,GotEof,0,0,RStart}, /* end of file */ + {ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */ + {ZFILE,ResendRpos,0,0,RFile}, /* ZRPOS was bad, resend */ + {ZFIN,GotFin,1,1,RFinish}, /* sender signing off */ + {99,ZPF,0,0,RFile}, + } ; + + /* waiting for data, but a packet could possibly arrive due + * to error recovery or something + */ + StateTable RDataOps[] = { + {ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */ + {ZFILE,GotFile,0,1,RFileName}, /* start a new file (??) */ + {ZNAK,ResendRpos,1,1,RFile}, /* ZRPOS was bad, resend */ + {ZFIN,GotFin,1,1,RFinish}, /* sender signing off */ + {ZDATA,GotData,0,1,RData}, /* file data follows */ + {ZEOF,GotEof,1,1,RStart}, /* end of file */ + {99,ZPF,0,0,RData}, + } ; + + /* here if we've sent ZFERR or ZABORT. Waiting for ZFIN */ + + StateTable RFinishOps[] = { + {ZRQINIT,SendRinit,1,1,RStart}, /* sender confused, restart */ + {ZFILE,GotFile,1,1,RFileName}, /* start a new file */ + {ZNAK,GotFin,1,1,RFinish}, /* resend ZFIN */ + {ZFIN,GotFin,1,1,RFinish}, /* sender signing off */ + {99,ZPF,0,0,RFinish}, + } ; + + + + +extern char *hdrnames[] ; + +extern int dataSetup() ; + + + /* RECEIVE-RELATED STUFF BELOW HERE */ + + + /* resend ZRINIT header in response to ZRQINIT or ZNAK header */ + +int +SendRinit( register ZModem *info ) +{ + u_char dbuf[4] ; + +#ifdef COMMENT + if( info->timeoutCount >= 5 ) + /* TODO: switch to Ymodem */ +#endif /* COMMENT */ + + 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) ; +} + + + + /* received a ZSINIT header in response to ZRINIT */ + +static int +GotSinit( register 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) ; +} + + /* received rest of ZSINIT packet */ + +int +GotSinitData( register 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) ; +} + + + /* got ZFILE. Cache flags and set up to receive filename */ + +static int +GotFile( register 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) ; +} + + + /* utility: see if ZOpenFile wants this file, and if + * so, request it from sender. + */ + +static int +requestFile( register ZModem *info, u_long 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, 0, info->filename) ; + return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ; + } +} + + + /* parse filename info. */ + +static void +parseFileName( register 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) ; +} + + + /* got filename. Parse arguments from it and execute + * policy function ZOpenFile(), provided by caller + */ + +int +GotFileName( register 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 +ResendCrcReq(ZModem *info) +{ + zmodemlog("ResendCrcReq[%s]: send ZCRC\n", sname(info)) ; + return ZXmitHdrHex(ZCRC, zeros, info) ; +} + + + /* received file CRC, now we're ready to accept or reject */ + +static int +GotFileCrc( register ZModem *info ) +{ + zmodemlog("GotFileCrc[%s]: call requestFile\n", sname(info)) ; + return requestFile(info, ZDec4(info->hdrData+1)) ; +} + + + /* last ZRPOS was bad, resend it */ + +int +ResendRpos( register ZModem *info ) +{ + zmodemlog("ResendRpos[%s]: send ZRPOS(%ld)\n", + sname(info), info->offset) ; + return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ; +} + + + /* recevied ZDATA header */ + +static int +GotData( register 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) ; +} + + + /* Utility: flush input, send attn, send specified header */ + +static int +fileError( register 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) ; +} + + /* received file data */ + +int +GotFileData( register 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, errno, NULL) ; + info->state = RFinish ; + info->InputState = Idle ; + info->chrCount=0 ; + return fileError(info, ZFERR, errno) ; + } + + 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 ; +} + + + /* received ZEOF packet, file is now complete */ + +static int +GotEof( register ZModem *info ) +{ + zmodemlog("GotEof[%s]: offset=%ld\n", sname(info), info->offset) ; + if( ZDec4(info->hdrData+1) != info->offset ) { + 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) ; +} + + + + /* got ZFIN, respond in kind */ + +static int +GotFin( register ZModem *info ) +{ + zmodemlog("GotFin[%s]: send ZFIN\n", sname(info)) ; + info->InputState = Finish ; + info->chrCount = 0 ; + if( info->filename != NULL ) + free(info->filename) ; + return ZXmitHdrHex(ZFIN, zeros, info) ; +} + + + +static int +GotFreecnt( register ZModem *info ) +{ + /* TODO: how do we find free space on system? */ + return ZXmitHdrHex(ZACK, ZEnc4(0xffffffff), info) ; +} + + + + /* YMODEM */ + +static u_char AckStr[1] = {ACK} ; +static u_char NakStr[1] = {NAK} ; +static u_char CanStr[2] = {CAN,CAN} ; + +static int ProcessPacket() ; +static int acceptPacket() ; +static int rejectPacket() ; +static int calcCrc() ; + +int +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((u_char *)"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 +YrcvTimeout( register ZModem *info ) +{ + switch( info->state ) + { + case YRStart: + if( info->timeoutCount >= 10 ) { + (void) ZXmitStr(CanStr, 2, info) ; + return ZmErrInitTo ; + } + return ZXmitStr((u_char *)"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 ; + } +} + + + +static int +ProcessPacket( register ZModem *info ) +{ + int idx = (u_char) info->buffer[0] ; + int idxc = (u_char) 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 = (u_char)info->buffer[info->pktLen-2] << 8 | + (u_char)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((u_char *)"C", 1, info) ; + } + + + if( ZWriteFile(info->buffer+2, info->pktLen-4, info->file, info) ) { + ZStatus(FileErr, errno, NULL) ; + (void) ZXmitStr(CanStr, 2, info) ; + return ZmErrSys ; + } + info->offset += info->pktLen-4 ; + ZStatus(RcvByteCount, info->offset, NULL) ; + + (void) acceptPacket(info) ; + return 0 ; +} + + +static int +rejectPacket( register ZModem *info ) +{ + info->timeout = 10 ; + return ZXmitStr(NakStr, 1, info) ; +} + + +static int +acceptPacket( register ZModem *info ) +{ + info->state = YRDataWait ; + info->timeout = 10 ; + return ZXmitStr(AckStr, 1, info) ; +} + + +static int +calcCrc( u_char *str, int len ) +{ + int crc = 0 ; + while( --len >= 0 ) + crc = updcrc(*str++, crc) ; + crc = updcrc(0,crc) ; crc = updcrc(0,crc) ; + return crc & 0xffff ; +} diff --git a/Xmodem/zmodemsys.c b/Xmodem/zmodemsys.c new file mode 100644 index 0000000..11d79c2 --- /dev/null +++ b/Xmodem/zmodemsys.c @@ -0,0 +1,21 @@ +/* @(#)zmodemsys.c 1.1 95/06/28 Edward Falk */ + +#include + + /* small utilities for porting between systems */ + + + +#ifndef HAVE_STRDUP + +char * +strdup( char *str ) +{ + char *rval ; + int len = strlen(str) + 1 ; + rval = (char *)malloc(len) ; + strcpy(rval,str) ; + return rval ; +} + +#endif diff --git a/Xmodem/zmodemt.c b/Xmodem/zmodemt.c new file mode 100644 index 0000000..9b605ed --- /dev/null +++ b/Xmodem/zmodemt.c @@ -0,0 +1,1152 @@ +#ifndef lint +static const char rcsid[] = "$Id: zmodemt.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ; +#endif + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @@@@@ @ @ @@@ @@@@ @@@@@ @ @ @@@@@ + * @ @@ @@ @ @ @ @ @ @@ @@ @ + * @ @ @ @ @ @ @ @ @@@ @ @ @ @ + * @ @ @ @ @ @ @ @ @ @ @ @ @ + * @@@@@ @ @ @ @@@ @@@@ @@@@@ @ @ @ @ + * + * ZMODEMT - transmit side of zmodem protocol + * + * transmit side of zmodem protocol + * + * Caller sets flags defined in zmodem.h as appropriate. + * (default is basic zmodem) + * + * functions: + * + * ZmodemTInit(ZModem *info) + * YmodemTInit(ZModem *info) + * XmodemTInit(ZModem *info) + * Initiate a connection + * + * ZmodemTFile(char *filename, u_char flags[4], ZModem *info) + * Initiate a file transfer. Flags are as specified + * under "ZCBIN" in zmodem.h + * + * ZmodemTFinish(ZModem *info) + * last file + * + * ZmodemTAbort(ZModem *info) + * abort transfer + * + * all functions return 0 on success, 1 on abort + * + * + * Edward A. Falk + * + * January, 1995 + * + * + * + **********/ + + + +#include +#include +#include +#include +#include +#include "zmodem.h" +#include "crctab.h" + + int SendMoreFileData( register ZModem *info ) ; +extern int ZXmitData(int, int, u_char, u_char *data, ZModem *) ; +extern void ZFlowControl(int onoff, ZModem *info) ; + +static int YXmitData() ; +static int YSendFilename() ; +static int YSendData() ; +static int YSendFin() ; + +static int sendFilename() ; + +static u_char zeros[4] = {0,0,0,0} ; + + + /* called by user to establish protocol */ + +int +ZmodemTInit( register ZModem *info ) +{ + int err ; + int i ; + + info->state = TStart ; + info->Protocol = ZMODEM ; + info->crc32 = 0 ; + info->packetCount = 0 ; + info->errCount = 0 ; + info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ; + info->InputState = Idle ; + info->canCount = info->chrCount = 0 ; + info->windowCount = 0 ; + info->filename = NULL ; + info->bufsize = 0 ; + info->interrupt = 0 ; + info->waitflag = 0 ; + + if( info->packetsize == 0 ) + info->packetsize = 256 ; + + /* we won't be receiving much data, pick a reasonable buffer + * size (largest packet will do) + */ + + i = info->packetsize*2 ; + if( i < 1024 ) i = 1024 ; + info->buffer = (u_char *)malloc(i) ; + + ZIFlush(info) ; + + /* optional: send "rz\r" to remote end */ + if( DoInitRZ ) { + if( (err = ZXmitStr((u_char *)"rz\r", 3, info)) ) + return err ; + } + + if( (err = ZXmitHdr(ZRQINIT, ZHEX, zeros, info)) ) /* nudge receiver */ + return err ; + + info->timeout = 60 ; + + zmodemlog("ZmodemTInit[%s]: sent ZRQINIT\n", sname(info)) ; + + return 0 ; +} + + + /* called by user to establish Ymodem protocol */ + +int +YmodemTInit( register ZModem *info ) +{ + info->state = YTStart ; + info->Protocol = YMODEM ; + info->errCount = 0 ; + info->InputState = Ysend ; + info->canCount = info->chrCount = 0 ; + info->windowCount = 0 ; + info->filename = NULL ; + + if( info->packetsize != 1024 ) + info->packetsize = 128 ; + + info->buffer = (u_char *)malloc(1024) ; + + ZIFlush(info) ; + ZFlowControl(0, info) ; + + info->timeout = 60 ; + + return 0 ; +} + + + /* called by user to establish Xmodem protocol */ + +int +XmodemTInit( register ZModem *info ) +{ + (void) YmodemTInit(info) ; + info->Protocol = XMODEM ; + return 0 ; +} + + + + /* called by user to begin transmission of a file */ + +int +ZmodemTFile( + char *file, + char *rfile, + u_int f0, + u_int f1, + u_int f2, + u_int f3, + int filesRem, + int bytesRem, + ZModem *info) +{ + if( file == NULL || (info->file = fopen(file, "r")) == NULL ) + return ZmErrCantOpen ; + + info->fileEof = 0 ; + info->filename = file ; + info->rfilename = (rfile != NULL) ? rfile : "noname" ; + info->filesRem = filesRem ; + info->bytesRem = bytesRem ; + info->fileFlags[3] = f0 ; + info->fileFlags[2] = f1 ; + info->fileFlags[1] = f2 ; + info->fileFlags[0] = f3 ; + info->offset = info->lastOffset = 0 ; + info->len = info->date = info->fileType = info->mode = 0 ; + if( info->filename != NULL ) + { + struct stat buf ; + if( stat(info->filename, &buf) == 0 ) { + info->len = buf.st_size ; + info->date = buf.st_mtime ; + info->fileType = 0 ; + info->mode = (buf.st_mode&0777)|0100000 ; + } + } + + if( info->Protocol == XMODEM ) + return YSendData(info) ; + + if( info->Protocol == YMODEM ) + return YSendFilename(info) ; + + info->state = FileWait ; + zmodemlog("ZmodemTFile[%s]: send ZFILE(%s)\n", + sname(info), info->rfilename) ; + return sendFilename(info) ; +} + + + + /* send ZFILE header and filename & info. Wait for response + * from receiver. + */ +static int +sendFilename( ZModem *info ) +{ + int err ; + int i ; + u_char obuf[2048] ; + u_char *ptr = obuf ; + + info->state = FileWait ; + + if( (err = ZXmitHdr(ZFILE, ZBIN, info->fileFlags, info)) ) + return err ; + + i = strlen(info->rfilename) ; + memcpy(ptr, info->rfilename, i+1) ; ptr += i+1 ; + sprintf((char *)ptr, "%d %lo %o 0 %d %d 0", info->len, info->date, + info->mode, info->filesRem, info->bytesRem) ; + ptr += strlen((char *)ptr) ; + *ptr++ = '\0' ; + + return ZXmitData(ZBIN, ptr-obuf, ZCRCW, obuf, info) ; +} + + + + /* called by user when there are no more files to send */ + +int +ZmodemTFinish( ZModem *info ) +{ + if( info->Protocol == XMODEM ) + return ZmDone ; + + if( info->Protocol == YMODEM ) + return YSendFin(info) ; + + info->state = TFinish ; + if( info->buffer != NULL ) { + free(info->buffer) ; + info->buffer = NULL ; + } + zmodemlog("ZmodemTFinish[%s]: send ZFIN\n", sname(info)) ; + return ZXmitHdr(ZFIN, ZHEX, zeros, info) ; +} + + + + + +extern int ZPF() ; +extern int AnswerChallenge() ; +extern int GotAbort() ; +extern int Ignore() ; +extern int RetDone() ; +extern int GotCommand() ; +extern int GotStderr() ; + +static int GotRinit() ; +static int SendZSInit() ; +static int SendFileCrc() ; +static int GotSendAck() ; +static int GotSendDoneAck() ; +static int GotSendNak() ; +static int GotSendWaitAck() ; +static int SkipFile() ; +static int GotSendPos() ; +static int SendFileData() ; +static int ResendEof() ; +static int OverAndOut() ; + + + + /* sent ZRQINIT, waiting for response */ + StateTable TStartOps[] = { + {ZRINIT,GotRinit,1,1,TStart}, + {ZCHALLENGE,AnswerChallenge,1,0,TStart}, + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {ZNAK,Ignore,0,0,TStart}, + {ZCOMMAND,GotCommand,0,0,CommandData}, + {ZSTDERR,GotStderr,0,0,StderrData}, + {99,ZPF,0,0,TStart}, + } ; + + /* sent ZSINIT, waiting for response */ + StateTable TInitOps[] = { + {ZACK,RetDone,1,0,TInit}, + {ZNAK,SendZSInit,1,0,TInit}, + {ZRINIT,GotRinit,1,1,TInit}, /* redundant, but who cares */ + {ZCHALLENGE,AnswerChallenge,1,0,TInit}, + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {ZCOMMAND,GotCommand,0,0,CommandData}, + {ZSTDERR,GotStderr,0,0,StderrData}, + {99,ZPF,0,0,TInit}, + } ; + + /* sent ZFILE, waiting for response */ + StateTable FileWaitOps[] = { + {ZRPOS,SendFileData,1,0,Sending}, + {ZSKIP,SkipFile,1,0,FileWait}, + {ZCRC,SendFileCrc,1,0,FileWait}, + {ZNAK,sendFilename,1,0,FileWait}, + {ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */ + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {ZCHALLENGE,AnswerChallenge,1,0,FileWait}, + {ZCOMMAND,GotCommand,0,0,CommandData}, + {ZSTDERR,GotStderr,0,0,StderrData}, + {99,ZPF,0,0,FileWait}, + } ; + + /* sent file CRC, waiting for response */ + StateTable CrcWaitOps[] = { + {ZRPOS,SendFileData,1,0,Sending}, + {ZSKIP,SkipFile,1,0,FileWait}, + {ZNAK,SendFileCrc,1,0,CrcWait}, + {ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */ + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {ZCRC,SendFileCrc,0,0,CrcWait}, + {ZCHALLENGE,AnswerChallenge,0,0,CrcWait}, + {ZCOMMAND,GotCommand,0,0,CommandData}, + {ZSTDERR,GotStderr,0,0,StderrData}, + {99,ZPF,0,0,CrcWait}, + } ; + + /* sending data, interruptable */ + StateTable SendingOps[] = { + {ZACK,GotSendAck,0,0,Sending}, + {ZRPOS,GotSendPos,1,1,Sending}, + {ZSKIP,SkipFile,1,1,FileWait}, + {ZNAK,GotSendNak,1,1,Sending}, + {ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */ + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {99,ZPF,0,0,SendWait}, + } ; + + /* sent data, need to send EOF */ + StateTable SendDoneOps[] = { + {ZACK,GotSendDoneAck,0,0,SendWait}, + {ZRPOS,GotSendPos,1,1,Sending}, + {ZSKIP,SkipFile,1,1,FileWait}, + {ZNAK,GotSendNak,1,1,Sending}, + {ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */ + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {99,ZPF,0,0,SendWait}, + } ; + + /* sending data, waiting for ACK */ + StateTable SendWaitOps[] = { + {ZACK,GotSendWaitAck,0,0,Sending}, + {ZRPOS,GotSendPos,0,0,SendWait}, + {ZSKIP,SkipFile,1,1,FileWait}, + {ZNAK,GotSendNak,0,0,Sending}, + {ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */ + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {99,ZPF,0,0,SendWait}, + } ; + + /* sent ZEOF, waiting for new RINIT */ + StateTable SendEofOps[] = { + {ZRINIT,SkipFile,1,0,TStart}, /* successful completion */ + {ZACK,Ignore,0,0,SendEof}, /* probably ACK from last packet */ + {ZRPOS,GotSendPos,1,1,SendWait}, + {ZSKIP,SkipFile,1,1,TStart}, + {ZNAK,ResendEof,1,0,SendEof}, + {ZRINIT,sendFilename,1,1,FileWait}, /* rcvr confused, retry file */ + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {99,ZPF,0,0,SendEof}, + } ; + + StateTable TFinishOps[] = { + {ZFIN,OverAndOut,1,1,Done}, + {ZNAK,ZmodemTFinish,1,1,TFinish}, + {ZRINIT,ZmodemTFinish,1,1,TFinish}, + {ZABORT,GotAbort,1,1,TFinish}, + {ZFERR,GotAbort,1,1,TFinish}, + {99,ZPF,0,0,TFinish}, + } ; + + +extern char *hdrnames[] ; + + + + /* Received Rinit. Usually received while in start state, + * this can also be an attempt to resync after a protocol + * failure + */ + +static int +GotRinit( register ZModem *info ) +{ + info->bufsize = info->hdrData[1] + info->hdrData[2]*256 ; + info->zrinitflags = info->hdrData[4] + info->hdrData[3]*256 ; + info->crc32 = info->zrinitflags & CANFC32 ; + info->escCtrl = info->zrinitflags & ESCCTL ; + info->escHibit = info->zrinitflags & ESC8 ; + + /* Full streaming: If receiver can overlap I/O, and if + * the sender can sample the reverse channel without hanging, + * and the receiver has not specified a buffer size, then we + * can simply blast away with ZCRCG packets. If the receiver + * detects an error, it sends an attn sequence and a new ZRPOS + * header to restart the file where the error occurred. + * + * [note that zmodem8.doc does not define how much noise is + * required to trigger a ZCRCW packet. We arbitrarily choose + * 64 bytes] + * + * If 'windowsize' is nonzero, and the receiver can do full + * duplex, ZCRCQ packets are sent instead of ZCRCG, to keep track + * of the number of characters in the queue. If the queue fills + * up, we pause and wait for a ZACK. + * + * + * Full Streaming with Reverse Interrupt: If sender cannot + * sample the input stream, then we define an Attn sequence + * that will be used to interrupt transmission. + * + * + * Full Streaming with Sliding Window: If sender cannot + * sample input stream or respond to Attn signal, we send + * several ZCRCQ packets until we're sure the receiver must + * have sent back at least one ZACK. Then we stop sending and + * read that ZACK. Then we send one more packet and so on. + * + * + * Segmented Streaming: If receiver cannot overlap I/O or can't do + * full duplex and has specified a maximum receive buffer size, + * whenever the buffer size is reached, we send a ZCRCW packet. + * + * TODO: what if receiver can't overlap, but hasn't set a buffer + * size? + * + * ZCRCE: CRC next, frame ends, header follows + * ZCRCG: CRC next, frame continues nonstop + * ZCRCQ: CRC next, send ZACK, frame continues nonstop + * ZCRCW: CRC next, send ZACK, frame ends, header follows + */ + + ZFlowControl(1,info) ; + + if( (info->zrinitflags & (CANFDX|CANOVIO)) == (CANFDX|CANOVIO) && + (SendSample||SendAttn) && + info->bufsize == 0 ) + { + if( info->windowsize == 0 ) + info->Streaming = Full ; + else + info->Streaming = StrWindow ; + } + + else if( (info->zrinitflags & (CANFDX|CANOVIO)) == (CANFDX|CANOVIO) && + info->bufsize == 0 ) + { + info->Streaming = SlidingWindow ; + } + + else + info->Streaming = Segmented ; + + zmodemlog("GotRinit[%s]\n", sname(info)) ; + + if( AlwaysSinit || info->zsinitflags != 0 || info->attn != NULL ) + return SendZSInit(info) ; + else + return ZmDone ; /* caller now calls ZmodemTFile() */ +} + + +static int +SendZSInit( ZModem *info ) +{ + int err ; + char *at = (info->attn != NULL) ? info->attn : "" ; + u_char fbuf[4] ; + + /* TODO: zmodem8.doc states: "If the ZSINIT header specifies + * ESCCTL or ESC8, a HEX header is used, and the receiver + * activates the specified ESC modes before reading the following + * data subpacket." What does that mean? + */ + + zmodemlog("SendZSInit[%s]\n", sname(info)) ; + + info->state = TInit ; + fbuf[0] = fbuf[1] = fbuf[2] = 0 ; + fbuf[3] = info->zsinitflags ; + if( (err = ZXmitHdr(ZSINIT, ZBIN, fbuf, info)) || + (err = ZXmitData(ZBIN, strlen(at)+1, ZCRCW, (u_char *)at, info)) ) + return err ; + return 0 ; +} + + +static int +SendFileCrc( register ZModem *info ) +{ + u_long crc ; + + crc = FileCrc(info->filename) ; + + zmodemlog("SendFileCrc[%s]: %lx\n", sname(info), crc) ; + + return ZXmitHdrHex(ZCRC, ZEnc4(crc), info) ; +} + + + + /* Utility: start sending data. */ + +static int +startFileData( register ZModem *info ) +{ + int err ; + + info->offset = + info->lastOffset = + info->zrposOffset = ZDec4(info->hdrData+1) ; + + fseek(info->file, info->offset, 0) ; + + /* TODO: what if fseek fails? Send EOF? */ + + zmodemlog("startFileData[%s]: %ld\n", sname(info), info->offset) ; + + if( (err = ZXmitHdr(ZDATA, ZBIN, info->hdrData+1, info)) ) + return err ; + return SendMoreFileData(info) ; +} + + + + /* send a chunk of file data in response to a ZRPOS. Whether this + * is followed by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW depends on all + * sorts of protocol flags + */ + +static int +SendFileData( register ZModem *info ) +{ + info->waitflag = 0 ; + return startFileData(info) ; +} + + + + + + + /* here if an ACK arrived while transmitting data. Update + * last known receiver offset, and try to send some more + * data. + */ + +static int +GotSendAck( register ZModem *info ) +{ + u_long offset ; + + offset = ZDec4(info->hdrData+1) ; + + if( offset > info->lastOffset ) + info->lastOffset = offset ; + + zmodemlog("GotSendAck[%s]: %ld\n", sname(info), info->offset) ; + + return 0 ; /* DONT send more data, that will happen + * later anyway */ +} + + + + /* here if an ACK arrived after last file packet sent. Send + * the EOF. + */ + +static int +GotSendDoneAck( register ZModem *info ) +{ + u_long offset ; + + offset = ZDec4(info->hdrData+1) ; + + if( offset > info->lastOffset ) + info->lastOffset = offset ; + + zmodemlog("GotSendDoneAck[%s]: %ld\n", sname(info), info->offset) ; + + info->state = SendEof ; + info->timeout = 60 ; + return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ; +} + + + /* off to a bad start; ZDATA header was corrupt. Start + * from beginning + */ + +static int +GotSendNak( register ZModem *info ) +{ + info->offset = info->zrposOffset ; + + fseek(info->file, info->offset, 0) ; + + /* TODO: what if fseek fails? Send EOF? */ + + zmodemlog("GotSendNak[%s]: %ld\n", sname(info), info->offset) ; + + return SendMoreFileData(info) ; +} + + + /* got a ZSKIP, receiver doesn't want this file. */ + +static int +SkipFile( register ZModem *info ) +{ + zmodemlog("SkipFile[%s]\n", sname(info)) ; + fclose(info->file) ; + return ZmDone ; +} + + + /* got a ZRPOS packet in the middle of sending a file, + * set new offset and try again + */ + +static int +GotSendPos( register ZModem *info ) +{ + ZStatus(DataErr, ++info->errCount, NULL) ; + info->waitflag = 1 ; /* next pkt should wait, to resync */ + zmodemlog("GotSendPos[%s]\n", sname(info), info->offset) ; + return startFileData(info) ; +} + + + + /* here if an ACK arrived while waiting while transmitting data. + * Update last known receiver offset, and try to send some more + * data. + */ + +static int +GotSendWaitAck( register ZModem *info ) +{ + u_long offset ; + int err ; + + offset = ZDec4(info->hdrData+1) ; + + if( offset > info->lastOffset ) + info->lastOffset = offset ; + + zmodemlog("GotSendWaitAck[%s]\n", sname(info), offset) ; + + if( (err = ZXmitHdr(ZDATA, ZBIN, ZEnc4(info->offset), info)) ) + return err ; + + return SendMoreFileData(info) ; +} + + + + + /* utility: send a chunk of file data. Whether this is followed + * by a ZCRCE, ZCRCG, ZCRCQ or ZCRCW depends on all + * sorts of protocol flags, plus 'waitflag'. Exact amount of file + * data transmitted is variable, as escape sequences may pad out + * the buffer. + */ + +int +SendMoreFileData( register ZModem *info ) +{ + int type ; + int qfull = 0 ; + int err ; + int len ; /* max # chars to send this packet */ + long pending ; /* # of characters sent but not acknowledged */ + + /* ZCRCE: CRC next, frame ends, header follows + * ZCRCG: CRC next, frame continues nonstop + * ZCRCQ: CRC next, send ZACK, frame continues nonstop + * ZCRCW: CRC next, send ZACK, frame ends, header follows + */ + + if( info->interrupt ) { + /* Bugger, receiver sent an interrupt. Enter a wait state + * and see what they want. Next header *should* be ZRPOS. + */ + info->state = SendWait ; + info->timeout = 60 ; + return 0 ; + } + + /* Find out how many bytes we can transfer in the next packet */ + + len = info->packetsize ; + + pending = info->offset - info->lastOffset ; + + if( info->windowsize != 0 && info->windowsize - pending <= len ){ + len = info->windowsize - pending ; + qfull = 1 ; + } + if( info->bufsize != 0 && info->bufsize - pending <= len ) { + len = info->bufsize - pending ; + qfull = 1 ; + } + + if( len == 0 ) { /* window still full, keep waiting */ + info->state = SendWait ; + info->timeout = 60 ; + return 0 ; + } + + + /* OK, we can safely transmit 'len' bytes of data. Start reading + * file until buffer is full. + */ + + len -= 10 ; /* Pre-deduct 10 bytes for trailing CRC */ + + + /* find out what kind of packet to send */ + if( info->waitflag ) { + type = ZCRCW ; + info->waitflag = 0 ; + } +#ifdef COMMENT + else if( info->fileEof ) + type = ZCRCE ; +#endif /* COMMENT */ + else if( qfull ) + type = ZCRCW ; + else + switch( info->Streaming ) { + case Full: + case Segmented: type = ZCRCG ; break ; + + case StrWindow: + if( (info->windowCount += len) < info->windowsize/4 ) + type = ZCRCG ; + else { + type = ZCRCQ ; + info->windowCount = 0 ; + } + break ; + + default: + case SlidingWindow: type = ZCRCQ ; break ; + } + + + { + int crc32 = info->crc32 ; + register int c=0, c2, atSign=0 ; + register u_long crc ; + register u_char *ptr = info->buffer ; + + crc = crc32 ? 0xffffffff : 0 ; + + /* read characters from file and put into buffer until buffer is + * full or file is exhausted + */ + + + while( len > 0 && (c = getc(info->file)) != EOF ) + { + if( !crc32 ) + crc = updcrc(c, crc) ; + else + crc = UPDC32(c, crc) ; + + /* zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and + * a CR following '@' be escaped. In addition, I escape '^]' + * to protect telnet, "~." to protect rlogin, and ESC for good + * measure. + */ + c2 = c & 0177 ; + if( c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 || + c2 == 0177 || c2 == '\r' || c2 == '\n' || c2 == 033 || + c2 == 035 || (c2 < 040 && info->escCtrl) ) + { + *ptr++ = ZDLE ; + if( c == 0177 ) + *ptr = ZRUB0 ; + else if( c == 0377 ) + *ptr = ZRUB1 ; + else + *ptr = c^0100 ; + len -= 2 ; + } + else { + *ptr = c ; + --len ; + } + ++ptr ; + + atSign = c2 == '@' ; + ++info->offset ; + } + + /* if we've reached file end, a ZEOF header will follow. If + * there's room in the outgoing buffer for it, end the packet + * with ZCRCE and append the ZEOF header. If there isn't room, + * we'll have to do a ZCRCW + */ + if( (info->fileEof = (c == EOF)) ) { + if( qfull || (info->bufsize != 0 && len < 24) ) + type = ZCRCW ; + else + type = ZCRCE ; + } + + *ptr++ = ZDLE ; + if( !crc32 ) + crc = updcrc(type, crc) ; + else + crc = UPDC32(type, crc) ; + *ptr++ = type ; + + if( !crc32 ) { + crc = updcrc(0,crc) ; crc = updcrc(0,crc) ; + ptr = putZdle(ptr, (crc>>8)&0xff, info) ; + ptr = putZdle(ptr, crc&0xff, info) ; + } + else { + crc = ~crc ; + for(len=4; --len >= 0; crc >>= 8) + ptr = putZdle(ptr, crc&0xff, info) ; + } + + len = ptr - info->buffer ; + } + + ZStatus(SndByteCount, info->offset, NULL) ; + + if( (err = ZXmitStr(info->buffer, len, info)) ) + return err ; + +#ifdef COMMENT + if( (err = ZXmitData(ZBIN, info->buffer, len, type, info)) ) + return err ; +#endif /* COMMENT */ + + /* finally, do we want to wait after this packet? */ + + switch( type ) { + case ZCRCE: + info->state = SendEof ; + info->timeout = 60 ; + return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ; + case ZCRCW: + info->state = info->fileEof ? SendDone : SendWait ; + info->timeout = 60 ; + break ; + default: + info->state = Sending ; + info->timeout = 0 ; + break ; + } + + +#ifdef COMMENT + if( info->fileEof ) { /* Yes, file is done, send EOF and wait */ + info->state = SendEof ; + info->timeout = 60 ; + return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ; + } + else if( type == ZCRCW ) { + info->state = SendWait ; + info->timeout = 60 ; + } + else { + info->state = Sending ; + info->timeout = 0 ; + } +#endif /* COMMENT */ + return 0 ; +} + + +static int +ResendEof( register ZModem *info ) +{ + return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ; +} + +static int +OverAndOut( ZModem *info ) +{ + ZXmitStr((u_char *)"OO", 2, info) ; + return ZmDone ; +} + + + + + + /* YMODEM */ + + +static u_char eotstr[1] = {EOT} ; + + /* ymodem parser */ + +int +YsendChar( char c, register ZModem *info ) +{ + int err ; + + if( info->canCount >= 2 ) { + ZStatus(RmtCancel, 0, NULL) ; + return ZmErrCancel ; + } + + switch( info->state ) { + case YTStart: /* wait for 'G', 'C' or NAK */ + switch( c ) { + case 'G': /* streaming YModem */ + case 'C': /* CRC YModem */ + case NAK: /* checksum YModem */ + info->PacketType = c ; + return ZmDone ; + default: + return 0 ; + } + + case YTFile: /* sent filename, waiting for ACK or NAK */ + switch( c ) { + case NAK: /* resend */ + case 'C': + case 'G': + ZStatus(DataErr, ++info->errCount, NULL) ; + return YSendFilename(info) ; + case ACK: + info->state = YTDataWait ; + default: + return 0 ; + } + + case YTDataWait: /* sent filename, waiting for G,C or NAK */ + switch( c ) { + case NAK: + case 'C': + case 'G': + info->chrCount = 0 ; + if( info->PacketType == 'G' ) /* send it all at once */ + { + while( info->state == YTData ) + if( (err = YSendData(info)) ) + return err ; + return 0 ; + } + else + return YSendData(info) ; + default: + return 0 ; + } + + case YTData: /* sent data, waiting for ACK or NAK */ + switch( c ) { + case 'C': + case 'G': /* protocol failure, resend filename */ + if( info->Protocol == YMODEM ) { + ZStatus(DataErr, ++info->errCount, NULL) ; + info->state = YTFile ; + rewind(info->file) ; + return YSendFilename(info) ; + } + /* else XModem, treat it like a NAK */ + case NAK: + ZStatus(DataErr, ++info->errCount, NULL) ; + return YXmitData(info->buffer + info->bufp, info->ylen, info) ; + case ACK: + info->offset += info->ylen ; + info->bufp += info->ylen ; + info->chrCount -= info->ylen ; + ZStatus(SndByteCount, info->offset, NULL) ; + return YSendData(info) ; + default: + return 0 ; + } + + case YTEOF: /* sent EOF, waiting for ACK or NAK */ + switch( c ) { + case NAK: + return ZXmitStr(eotstr, 1, info) ; + case ACK: + info->state = info->Protocol == YMODEM ? YTStart : Done ; + return ZmDone ; + default: + return 0 ; + } + + case YTFin: /* sent Fin, waiting for ACK or NAK */ + switch( c ) { + case NAK: + return YSendFin(info) ; + case ACK: + return ZmDone ; + default: + return 0 ; + } + default: + return 0 ; + } +} + +static int +YXmitData( u_char *buffer, int len, ZModem *info ) +{ + u_char hdr[3] ; + u_char trail[2] ; + u_long crc = 0 ; + int i, err ; + + hdr[0] = len == 1024 ? STX : SOH ; + hdr[1] = info->packetCount ; + hdr[2] = ~hdr[1] ; + if( (err = ZXmitStr(hdr, 3, info)) || + (err = ZXmitStr(buffer, len, info)) ) + return err ; + + if( info->PacketType == NAK ) { /* checksum */ + for(i=0; istate = 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(; ichrCount <= 0 ) { + info->bufp = 0 ; + info->chrCount = fread(info->buffer, 1, info->packetsize, info->file); + info->fileEof = feof(info->file) ; + } + + if( info->chrCount <= 0 ) { + fclose(info->file) ; + info->state = YTEOF ; + return ZXmitStr(eotstr, 1, info) ; + } + + /* pad out to 128 bytes if needed */ + if( info->chrCount < 128 ) { + i = 128 - info->chrCount ; + memset(info->buffer + info->bufp + info->chrCount, 0x1a, i) ; + info->chrCount = 128 ; + } + + info->ylen = info->chrCount >= 1024 ? 1024 : 128 ; + ++info->packetCount ; + + info->state = YTData ; + + return YXmitData(info->buffer + info->bufp, info->ylen, info) ; +} + + + +static int +YSendFin( ZModem *info ) +{ + u_char obuf[128] ; + + info->state = YTFin ; + info->packetCount = 0 ; + + memset(obuf,0,128) ; + + return YXmitData(obuf, 128, info) ; +} diff --git a/Xmodem/zmutil.c b/Xmodem/zmutil.c new file mode 100644 index 0000000..8206291 --- /dev/null +++ b/Xmodem/zmutil.c @@ -0,0 +1,408 @@ +#ifndef lint +static const char rcsid[] = "$Id: zmutil.c,v 1.1.1.1 2001/03/08 00:01:48 efalk Exp $" ; +#endif + +/* + * Copyright (c) 1995 by Edward A. Falk + */ + + +/********** + * + * + * @@@@@ @ @ @ @ @@@@@ @@@ @ + * @ @@ @@ @ @ @ @ @ + * @ @ @ @ @ @ @ @ @ + * @ @ @ @ @ @ @ @ @ + * @@@@@ @ @ @ @@@ @ @@@ @@@@@ + * + * ZMUTIL - utilties used by zmodem protocol. + * + * Routines provided here: + * + * + * int ZXmitHdrHex(type, data, info) + * int type ; + * u_char data[4] ; + * ZModem *info ; + * + * transmit zmodem header in hex. + * + * + * int ZXmitHdrBin(type, data, info) + * int type ; + * u_char data[4] ; + * ZModem *info ; + * + * transmit zmodem header in binary. + * + * + * int ZXmitHdrBin32(type, data, info) + * int type ; + * u_char data[4] ; + * ZModem *info ; + * + * transmit zmodem header in binary with 32-bit crc. + * + * + * int ZXmitHdr(type, format, data, info) + * int type, format ; + * u_char data[4] ; + * ZModem *info ; + * + * transmit zmodem header + * + * + * int ZXmitData(format, data, len, term, info) + * int format, len ; + * u_char term ; + * u_char *data ; + * ZModem *info ; + * + * transmit buffer of data. + * + * + * u_long FileCrc(name) + * char *name ; + * + * compute 32-bit crc for a file, returns 0 on not found + * + * + * u_char *ZEnc4(n) + * u_long n ; + * + * convert u_long to 4 bytes. + * + * u_long ZDec4(buf) + * u_char buf[4] ; + * + * convert 4 bytes to u_long + * + * + * + * Edward A. Falk + * + * January, 1995 + * + * + * + **********/ + + + + + +#include +#include +#include +#include +#include +#include + +#include "zmodem.h" +#include "crctab.h" + + +static char hexChars[] = "0123456789abcdef" ; + +extern char *hdrnames[] ; + + FILE *zmodemlogfile = NULL ; + + + /* put a number as two hex digits */ + +static u_char * +putHex( u_char *ptr, u_char c ) +{ + *ptr++ = hexChars[(c>>4)&0xf] ; + *ptr++ = hexChars[c&0xf] ; + return ptr ; +} + + + /* put a number with ZDLE escape if needed */ + +u_char * +putZdle( register u_char *ptr, register u_char c, register ZModem *info ) +{ +register u_char c2 = c & 0177 ; + + if( c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 || + c2 == 0177 || (c2 == 015 && 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 ; +} + + +int +ZXmitHdrHex( int type, u_char data[4], ZModem *info ) +{ + u_char buffer[128] ; +register u_char *ptr = buffer ; +register u_int 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 +ZXmitHdrBin( int type, u_char data[4], register ZModem *info ) +{ + u_char buffer[128] ; +register u_char *ptr = buffer ; +register u_int 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 +ZXmitHdrBin32( int type, u_char data[4], ZModem *info ) +{ + u_char buffer[128] ; +register u_char *ptr = buffer ; +register u_long 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) ; +} + + +int +ZXmitHdr( int type, int format, u_char 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 ; + } +} + + + + + /* TODO: if input is not a file, need to keep old data + * for possible retransmission */ + +int +ZXmitData( int format, int len, u_char term, u_char *data, ZModem *info) +{ +register u_char *ptr = info->buffer ; +register u_int 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) ; +} + + + /* compute 32-bit crc for a file, returns 0 on not found */ + +u_long +FileCrc( char *name ) +{ + u_long crc ; + FILE *ifile = fopen(name, "r") ; + 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 ; +} + + +u_char * +ZEnc4( u_long n ) +{ +static u_char 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 ; +} + +u_long +ZDec4( u_char buf[4] ) +{ + return buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24) ; +} + + +char * +sname2(ZMState state) +{ + static 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] ; + +} + + +char * +sname(ZModem *info) +{ + return sname2(info->state) ; +} + + + +#ifdef DEBUG +void +zmodemlog(const char *fmt, ... ) +{ + va_list ap; + struct timeval tv ; + struct tm *tm ; +static int do_ts = 1 ; + + 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) ; +} +#endif /* DEBUG */ diff --git a/ansis_default/filemenu.ans b/ansis_default/filemenu.ans new file mode 100644 index 0000000..eefc21e --- /dev/null +++ b/ansis_default/filemenu.ans @@ -0,0 +1,14 @@ + +ÜÜÜÜÜÜÜ ÜÜ ÜÜ ÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜÜÜÜÜ ÜÜÜÜÜÜÜ ÜÜÜÜÜÜ ÜÜ Ü ÜÜ +²ßÜÜ ßß ÛÛ ²ß Ûßßß ²ßÜÜ ßß ²ß°Ü ²ß°Ü ²Û ²ßÜÜ ßß ²ß°Ü ²Û ²ß Û Û² +±² ÜÜÛ² ²² ±ÛÜß ±ß ±²Ü° ²ß °² ² °² ² ±ß ±²Ü° ²ß °² ² ±ß °²Üß ²  +ÛÛ ßßßß ßß °ßßßßßß ßßßßßß ßß ß ßß ß °Û ßßßßßß ßß ß °Û ßßßßßß + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ + ³ }. Advance 1 Directory³ L. List Files³ + ³ {. Retreat 1 Directory³ C. Clear Tagged List³ + ³ ]. Advance 1 Subdirectory³ D. Download Files Tagged³ + ³ [. Retreat 1 Subdirectory³ U. Upload a File³ + ³ I. Select Directory³³ + ³ S. Select Subdirectory³ Q. Quit to Main Menu³ + ³³ G. Goodbye (Log Off)³ + ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ diff --git a/ansis_default/mainmenu.ans b/ansis_default/mainmenu.ans index 14f102c..758414e 100644 --- a/ansis_default/mainmenu.ans +++ b/ansis_default/mainmenu.ans @@ -5,7 +5,7 @@ ßß ß ßß ß °Û ßß ß °Û ßß ßß ß °Û ßß ß ßß ß °Û ßßßßßß ßß ß °Û ßßßßßß ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ³ M. Message Areas³ L. BBS List³ - ³ T. File Areas³ C. Chat System³ + ³ T. File Transfer Area³ C. Chat System³ ³ B. Bulletins³ U. User List³ ³ D. Door Games & Utilites³ 1. Last 10 Callers³ ³ A. Text/ANSI File Collection ³³ diff --git a/bbs.c b/bbs.c index d5ca38a..6e40248 100644 --- a/bbs.c +++ b/bbs.c @@ -166,6 +166,8 @@ static int file_sub_handler(void* user, const char* section, const char* name, fd->file_subs[i]->download_sec_level = atoi(value); } else if (strcasecmp(name, "database") == 0) { fd->file_subs[i]->database = strdup(value); + } else if (strcasecmp(name, "upload path") == 0) { + fd->file_subs[i]->upload_path = strdup(value); } return 1; } @@ -185,6 +187,8 @@ static int file_sub_handler(void* user, const char* section, const char* name, fd->file_subs[fd->file_sub_count]->download_sec_level = atoi(value); } else if (strcasecmp(name, "database") == 0) { fd->file_subs[fd->file_sub_count]->database = strdup(value); + } else if (strcasecmp(name, "upload path") == 0) { + fd->file_subs[fd->file_sub_count]->upload_path = strdup(value); } fd->file_sub_count++; } @@ -412,6 +416,9 @@ char s_getchar(int socket) { len = read(socket, &c, 1); if (len == 0) { disconnect(socket); + } else if (c == 255) { + usertimeout = 10; + return c; } len = read(socket, &c, 1); if (len == 0) { diff --git a/bbs.h b/bbs.h index 60451ce..b6276f5 100644 --- a/bbs.h +++ b/bbs.h @@ -64,6 +64,7 @@ struct mail_conference { struct file_sub { char *name; char *database; + char *upload_path; int upload_sec_level; int download_sec_level; }; @@ -82,6 +83,7 @@ struct bbs_config { char *ansi_path; char *bbs_path; + char *default_tagline; char *irc_server; @@ -158,4 +160,6 @@ extern void chat_system(int sock, struct user_record *user); extern int mail_getemailcount(struct user_record *user); extern void send_email(int socket, struct user_record *user); extern void list_emails(int socket, struct user_record *user); + +extern int file_menu(int socket, struct user_record *user); #endif diff --git a/config_default/filesgen.ini b/config_default/filesgen.ini index 01545ea..8d210f1 100644 --- a/config_default/filesgen.ini +++ b/config_default/filesgen.ini @@ -5,3 +5,4 @@ Visible Sec Level = 10 Database = files_misc Download Sec Level = 10 Upload Sec Level = 10 +Upload Path = /home/andrew/MagickaBBS/files/misc diff --git a/files.c b/files.c new file mode 100644 index 0000000..06ea86a --- /dev/null +++ b/files.c @@ -0,0 +1,736 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Xmodem/zmodem.h" +#include "bbs.h" + +extern struct bbs_config conf; + +static int doCancel = 0; + +struct file_entry { + char *filename; + char *description; + int size; + int dlcount; +}; + +char **tagged_files; +int tagged_count = 0; + +int ZXmitStr(u_char *str, int len, ZModem *info) { + int i; + + for (i=0;iofd, &str[i], 1) == 0) { + return ZmErrSys; + } + } + if (write(info->ofd, &str[i], 1) == 0) { + return ZmErrSys; + } + } + + return 0; +} + +void ZIFlush(ZModem *info) { +} + +void ZOFlush(ZModem *info) { +} + +int ZAttn(ZModem *info) { + char *ptr ; + + if( info->attn == NULL ) + return 0 ; + + for(ptr = info->attn; *ptr != '\0'; ++ptr) { + if( *ptr == ATTNBRK ) { + + } else if( *ptr == ATTNPSE ) { + sleep(1); + } else { + write(info->ifd, ptr, 1) ; + } + } + return 0; +} + +void ZFlowControl(int onoff, ZModem *info) { +} + +void ZStatus(int type, int value, char *status) { +} + + +char *upload_path; +char upload_filename[1024]; + +FILE *ZOpenFile(char *name, u_long crc, ZModem *info) { + + FILE *fptr; + struct stat s; + + snprintf(upload_filename, 1023, "%s/%s", upload_path, basename(name)); + fprintf(stderr, "%s\n", upload_filename); + if (stat(upload_filename, &s) == 0) { + return NULL; + } + + fptr = fopen(upload_filename, "wb"); + + return fptr; +} + +int ZWriteFile(u_char *buffer, int len, FILE *file, ZModem *info) { + return fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys; +} + +int ZCloseFile(ZModem *info) { + fclose(info->file); + return 0; +} + +void ZIdleStr(u_char *buffer, int len, ZModem *info) { +} + +int doIO(ZModem *zm) { + fd_set readfds; + struct timeval timeout; + u_char buffer[2048]; + u_char buffer2[1024]; + int len; + int pos; + int done = 0; + int i; + int j; + + while(!done) { + FD_ZERO(&readfds); + FD_SET(zm->ifd, &readfds) ; + timeout.tv_sec = zm->timeout ; + timeout.tv_usec = 0 ; + i = select(zm->ifd+1, &readfds,NULL,NULL, &timeout) ; + + if( i==0 ) { + done = ZmodemTimeout(zm) ; + } else if (i > 0) { + len = read(zm->ifd, buffer, 2048); + if (len == 0) { + disconnect(zm->ifd); + } + + pos = 0; + for (j=0;j 0) { + done = ZmodemRcv(buffer2, pos, zm) ; + } + } else { + // SIG INT catch + if (errno != EINTR) { + printf("SELECT ERROR %s\n", strerror(errno)); + } + } + } + return done; +} + +void upload_zmodem(int socket, struct user_record *user) { + ZModem zm; + int done; + + + upload_path = conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->upload_path; + + zm.attn = NULL; + zm.windowsize = 0; + zm.bufsize = 0; + + zm.ifd = socket; + zm.ofd = socket; + + zm.zrinitflags = 0; + zm.zsinitflags = 0; + + zm.packetsize = 1024; + + done = ZmodemRInit(&zm); + + doIO(&zm); +} + +void upload(int socket, struct user_record *user) { + char buffer[331]; + char buffer2[66]; + char buffer3[256]; + int i; + char *create_sql = "CREATE TABLE IF NOT EXISTS files (" + "Id INTEGER PRIMARY KEY," + "filename TEXT," + "description TEXT," + "size INTEGER," + "dlcount INTEGER," + "approved INTEGER);"; + char *sql = "INSERT INTO files (filename, description, size, dlcount, approved) VALUES(?, ?, ?, 0, 0)"; + sqlite3 *db; + sqlite3_stmt *res; + int rc; + struct stat s; + char *err_msg = NULL; + + upload_zmodem(socket, user); + + s_putstring(socket, "\r\nPlease enter a description:\r\n"); + buffer[0] = '\0'; + for (i=0;i<5;i++) { + sprintf(buffer2, "\r\n%d: ", i); + s_putstring(socket, buffer2); + s_readstring(socket, buffer2, 65); + if (strlen(buffer2) == 0) { + break; + } + strcat(buffer, buffer2); + strcat(buffer, "\n"); + } + + sprintf(buffer3, "%s/%s.sq3", conf.bbs_path, conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->database); + + rc = sqlite3_open(buffer3, &db); + + if (rc != SQLITE_OK) { + fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + exit(1); + } + + rc = sqlite3_exec(db, create_sql, 0, 0, &err_msg); + if (rc != SQLITE_OK ) { + fprintf(stderr, "SQL error: %s\n", err_msg); + sqlite3_free(err_msg); + sqlite3_close(db); + return; + } + rc = sqlite3_prepare_v2(db, sql, -1, &res, 0); + + if (rc == SQLITE_OK) { + stat(upload_filename, &s); + + sqlite3_bind_text(res, 1, upload_filename, -1, 0); + sqlite3_bind_text(res, 2, buffer, -1, 0); + sqlite3_bind_int(res, 3, s.st_size); + } else { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + sqlite3_finalize(res); + sqlite3_close(db); + return; + } + + rc = sqlite3_step(res); + + if (rc != SQLITE_DONE) { + printf("execution failed: %s", sqlite3_errmsg(db)); + sqlite3_finalize(res); + sqlite3_close(db); + return; + } + sqlite3_finalize(res); + sqlite3_close(db); +} + +void download_zmodem(int socket, struct user_record *user, char *filename) { + ZModem zm; + int done ; + fd_set readfds; + struct timeval timeout; + int i; + int j; + int len; + int pos; + + u_char buffer[2048]; + u_char buffer2[1024]; + + printf("Attempting to upload %s\n", filename); + + zm.attn = NULL; + zm.windowsize = 0; + zm.bufsize = 0; + + zm.ifd = socket; + zm.ofd = socket; + + zm.zrinitflags = 0; + zm.zsinitflags = 0; + + zm.packetsize = 1024; + + + ZmodemTInit(&zm) ; + done = doIO(&zm); + if ( done != ZmDone ) { + return; + } + + done = ZmodemTFile(filename, basename(filename), ZCBIN,0,0,0,0,0, &zm) ; + + switch( done ) { + case 0: + break ; + + case ZmErrCantOpen: + fprintf(stderr, "cannot open file \"%s\": %s\n", filename, strerror(errno)) ; + return; + + case ZmFileTooLong: + fprintf(stderr, "filename \"%s\" too long, skipping...\n", filename) ; + return; + + case ZmDone: + return; + + default: + return; + } + + if (!done) { + done = doIO(&zm); + } + + if ( done != ZmDone ) { + return; + } + + done = ZmodemTFinish(&zm); + + if (!done) { + done = doIO(&zm); + } +} + +void download(int socket, struct user_record *user) { + int i; + char *ssql = "select dlcount from files where filename like ?"; + char *usql = "update files set dlcount=? where filename like ?"; + char buffer[256]; + int dloads; + char *err_msg = NULL; + sqlite3 *db; + sqlite3_stmt *res; + int rc; + + for (i=0;icur_file_dir]->file_subs[user->cur_file_sub]->database); + + rc = sqlite3_open(buffer, &db); + + if (rc != SQLITE_OK) { + fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + exit(1); + } + rc = sqlite3_prepare_v2(db, ssql, -1, &res, 0); + + if (rc == SQLITE_OK) { + sqlite3_bind_text(res, 1, tagged_files[i], -1, 0); + } else { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + } + + rc = sqlite3_step(res); + + if (rc != SQLITE_ROW) { + fprintf(stderr, "Downloaded a file not in database!!!!!"); + sqlite3_finalize(res); + sqlite3_close(db); + exit(1); + } + + dloads = sqlite3_column_int(res, 0); + dloads++; + sqlite3_finalize(res); + + rc = sqlite3_prepare_v2(db, usql, -1, &res, 0); + + if (rc == SQLITE_OK) { + sqlite3_bind_int(res, 1, dloads); + sqlite3_bind_text(res, 2, tagged_files[i], -1, 0); + } else { + fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db)); + } + + rc = sqlite3_step(res); + + sqlite3_finalize(res); + sqlite3_close(db); + } + + for (i=0;icur_file_dir]->file_subs[user->cur_file_sub]->database); + + rc = sqlite3_open(buffer, &db); + if (rc != SQLITE_OK) { + fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + + exit(1); + } + rc = sqlite3_prepare_v2(db, sql, -1, &res, 0); + + if (rc != SQLITE_OK) { + sqlite3_finalize(res); + sqlite3_close(db); + s_putstring(socket, "\r\nNo files in this area!\r\n"); + return; + } + + + files_c = 0; + + while (sqlite3_step(res) == SQLITE_ROW) { + if (files_c == 0) { + files_e = (struct file_entry **)malloc(sizeof(struct file_entry *)); + } else { + files_e = (struct file_entry **)realloc(files_e, sizeof(struct file_entry *) * (files_c + 1)); + } + files_e[files_c] = (struct file_entry *)malloc(sizeof(struct file_entry)); + files_e[files_c]->filename = strdup((char *)sqlite3_column_text(res, 0)); + files_e[files_c]->description = strdup((char *)sqlite3_column_text(res, 1)); + files_e[files_c]->size = sqlite3_column_int(res, 2); + files_e[files_c]->dlcount = sqlite3_column_int(res, 3); + + files_c++; + } + sqlite3_finalize(res); + sqlite3_close(db); + + if (files_c == 0) { + s_putstring(socket, "\r\nNo files in this area!\r\n"); + return; + } + s_putstring(socket, "\r\n"); + for (i=0;isize; + if (file_size > 1024 * 1024 * 1024) { + file_size = file_size / 1024 / 1024 / 1024; + file_unit = 'G'; + } else if (file_size > 1024 * 1024) { + file_size = file_size / 1024 / 1024; + file_unit = 'M'; + } else if (file_size > 1024) { + file_size = file_size / 1024; + file_unit = 'K'; + } else { + file_unit = 'b'; + } + sprintf(buffer, "\e[1;30m[\e[1;34m%3d\e[1;30m] \e[1;33m%3ddloads \e[1;36m%4d%c \e[1;37m%-58s\r\n \e[0;32m", i, files_e[i]->dlcount, file_size, file_unit, basename(files_e[i]->filename)); + s_putstring(socket, buffer); + lines++; + for (j=0;jdescription);j++) { + if (files_e[i]->description[j] == '\n') { + s_putstring(socket, "\r\n"); + lines++; + if (lines % 22 == 0 && lines != 0) { + while (1) { + s_putstring(socket, "\r\n\e[0mEnter # to tag, Q to quit, Enter to continue: "); + s_readstring(socket, buffer, 5); + if (strlen(buffer) == 0) { + break; + } else if (tolower(buffer[0]) == 'q') { + for (z=0;zfilename); + free(files_e[z]->description); + free(files_e[z]); + } + free(files_e); + s_putstring(socket, "\r\n"); + return; + } else { + z = atoi(buffer); + if (z >= 0 && z < files_c) { + if (conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->download_sec_level <= user->sec_level) { + match = 0; + for (k=0;kfilename) == 0) { + match = 1; + break; + } + } + if (match == 0) { + if (tagged_count == 0) { + tagged_files = (char **)malloc(sizeof(char *)); + } else { + tagged_files = (char **)realloc(tagged_files, sizeof(char *) * (tagged_count + 1)); + } + tagged_files[tagged_count] = strdup(files_e[z]->filename); + tagged_count++; + sprintf(buffer, "\r\nTagged %s\r\n", basename(files_e[z]->filename)); + s_putstring(socket, buffer); + } else { + s_putstring(socket, "\r\nAlready Tagged\r\n"); + } + } else { + s_putstring(socket, "\r\nSorry, you don't have permission to download from this area\r\n"); + } + } + } + } + } else { + s_putstring(socket, " \e[0;32m"); + } + } else { + s_putchar(socket, files_e[i]->description[j]); + } + } + } + while (1) { + s_putstring(socket, "\r\n\e[0mEnter # to tag, Enter to quit: "); + s_readstring(socket, buffer, 5); + if (strlen(buffer) == 0) { + for (z=0;zfilename); + free(files_e[z]->description); + free(files_e[z]); + } + free(files_e); + s_putstring(socket, "\r\n"); + return; + } else { + z = atoi(buffer); + if (z >= 0 && z < files_c) { + if (conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->download_sec_level <= user->sec_level) { + match = 0; + for (k=0;kfilename) == 0) { + match = 1; + break; + } + } + if (match == 0) { + if (tagged_count == 0) { + tagged_files = (char **)malloc(sizeof(char *)); + } else { + tagged_files = (char **)realloc(tagged_files, sizeof(char *) * (tagged_count + 1)); + } + tagged_files[tagged_count] = strdup(files_e[z]->filename); + tagged_count++; + sprintf(buffer, "\r\nTagged %s\r\n", basename(files_e[z]->filename)); + s_putstring(socket, buffer); + } else { + s_putstring(socket, "\r\nAlready Tagged\r\n"); + } + } else { + s_putstring(socket, "\r\nSorry, you don't have permission to download from this area\r\n"); + } + } + } + } +} + +int file_menu(int socket, struct user_record *user) { + int doquit = 0; + int dofiles = 0; + char c; + int i; + int j; + char prompt[256]; + + while (!dofiles) { + s_displayansi(socket, "filemenu"); + + sprintf(prompt, "\e[0m\r\nDir: (%d) %s\r\nSub: (%d) %s\r\nTL: %dm :> ", user->cur_file_dir, conf.file_directories[user->cur_file_dir]->name, user->cur_file_sub, conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->name, user->timeleft); + s_putstring(socket, prompt); + + c = s_getc(socket); + switch(tolower(c)) { + case 'i': + { + s_putstring(socket, "\r\n\r\nFile Directories:\r\n\r\n"); + for (i=0;isec_level <= user->sec_level) { + sprintf(prompt, " %d. %s\r\n", i, conf.file_directories[i]->name); + s_putstring(socket, prompt); + } + if (i != 0 && i % 22 == 0) { + s_putstring(socket, "Press any key to continue...\r\n"); + c = s_getc(socket); + } + } + s_putstring(socket, "Enter the directory number: "); + s_readstring(socket, prompt, 5); + if (tolower(prompt[0]) != 'q') { + j = atoi(prompt); + if (j < 0 || j >= conf.file_directory_count || conf.file_directories[j]->sec_level > user->sec_level) { + s_putstring(socket, "\r\nInvalid directory number!\r\n"); + } else { + s_putstring(socket, "\r\n"); + user->cur_file_dir = j; + user->cur_file_sub = 0; + } + } + } + break; + case 's': + { + s_putstring(socket, "\r\n\r\nFile Subdirectories:\r\n\r\n"); + for (i=0;icur_file_dir]->file_sub_count;i++) { + sprintf(prompt, " %d. %s\r\n", i, conf.file_directories[user->cur_file_dir]->file_subs[i]->name); + s_putstring(socket, prompt); + + if (i != 0 && i % 22 == 0) { + s_putstring(socket, "Press any key to continue...\r\n"); + c = s_getc(socket); + } + } + s_putstring(socket, "Enter the sub directory number: "); + s_readstring(socket, prompt, 5); + if (tolower(prompt[0]) != 'q') { + j = atoi(prompt); + if (j < 0 || j >= conf.file_directories[user->cur_file_dir]->file_sub_count) { + s_putstring(socket, "\r\nInvalid sub directiry number!\r\n"); + } else { + s_putstring(socket, "\r\n"); + user->cur_file_sub = j; + } + } + } + break; + case 'l': + list_files(socket, user); + break; + case 'u': + { + if (user->sec_level >= conf.file_directories[user->cur_file_dir]->file_subs[user->cur_file_sub]->upload_sec_level) { + upload(socket, user); + } else { + s_putstring(socket, "Sorry, you don't have permission to upload in this Sub\r\n"); + } + } + break; + case 'd': + download(socket, user); + break; + case 'c': + { + // Clear tagged files + if (tagged_count > 0) { + for (i=0;icur_file_dir;isec_level <= user->sec_level) { + user->cur_file_dir = i + 1; + user->cur_file_sub = 0; + break; + } + } + } + break; + case '{': + { + for (i=user->cur_file_dir;i>=0;i--) { + if (i - 1 == -1) { + i = conf.file_directory_count; + } + if (conf.file_directories[i-1]->sec_level <= user->sec_level) { + user->cur_file_dir = i - 1; + user->cur_file_sub = 0; + break; + } + } + } + break; + case ']': + { + i=user->cur_file_sub; + if (i + 1 == conf.file_directories[user->cur_file_dir]->file_sub_count) { + i = -1; + } + user->cur_mail_area = i + 1; + } + break; + case '[': + { + i=user->cur_file_sub; + if (i - 1 == -1) { + i = conf.file_directories[user->cur_file_dir]->file_sub_count; + } + user->cur_mail_area = i - 1; + } + break; + case 'q': + dofiles = 1; + break; + case 'g': + { + s_putstring(socket, "\r\nAre you sure you want to log off? (Y/N)"); + c = s_getc(socket); + if (tolower(c) == 'y') { + dofiles = 1; + doquit = 1; + } + } + break; + } + } + return doquit; +} diff --git a/main_menu.c b/main_menu.c index 8fa8df9..d1e9506 100644 --- a/main_menu.c +++ b/main_menu.c @@ -114,7 +114,11 @@ void main_menu(int socket, struct user_record *user) { } } break; - + case 't': + { + doquit = file_menu(socket, user); + } + break; } } }