From 08a8cb97fb443cdbaf6d7d082c8ecb3f7e80aa35 Mon Sep 17 00:00:00 2001 From: Adrian Negreanu Date: Wed, 4 Sep 2024 14:42:24 +0300 Subject: [PATCH 1/6] setup.py: use open instead of file When doing a pip3 install, the `file` from long_description is not defined. Collecting https://github.com/enix223/modem/archive/refs/heads/multi-protocol.zip (from -r requirements.txt (line 5)) Downloading https://github.com/enix223/modem/archive/refs/heads/multi-protocol.zip \ 184kB 2.4MB/s Complete output from command python setup.py egg_info: Traceback (most recent call last): File "", line 1, in File "/tmp/pip-2kc3v90y-build/setup.py", line 10, in long_description = file('doc/source/about.rst').read(), NameError: name 'file' is not defined --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cc62d01..2750146 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ author_email = 'maze@pyth0n.org', url = 'https://maze.io/', description = ('Modem implementations for XMODEM, YMODEM and ZMODEM'), - long_description = file('doc/source/about.rst').read(), + long_description = open('doc/source/about.rst').read(), license = 'MIT', keywords = 'xmodem ymodem zmodem protocol', packages = ['modem'], From 235284582a5a5f53c57b6f3027cc47e1c506de58 Mon Sep 17 00:00:00 2001 From: Adrian Negreanu Date: Wed, 4 Sep 2024 15:03:30 +0300 Subject: [PATCH 2/6] setup.py: add modem/protocol in packages packages should include the subdirectories/sub-modules. Otherwise, only the files in modem/ will be packaged (without subdirs). --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 2750146..73d14b9 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ long_description = open('doc/source/about.rst').read(), license = 'MIT', keywords = 'xmodem ymodem zmodem protocol', - packages = ['modem'], - package_data = {'': ['doc/*.TXT']}, + packages = ["modem", "modem/protocol"], + package_data = {'': ['doc/*.txt']}, ) From 9879cdfa216b61145f3a799985f93652e5087546 Mon Sep 17 00:00:00 2001 From: Adrian Negreanu Date: Wed, 4 Sep 2024 15:47:50 +0300 Subject: [PATCH 3/6] zmodem: add more logging --- modem/protocol/zmodem.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modem/protocol/zmodem.py b/modem/protocol/zmodem.py index 1d378f1..1d207ef 100644 --- a/modem/protocol/zmodem.py +++ b/modem/protocol/zmodem.py @@ -103,6 +103,7 @@ def _recv(self, timeout): def _recv_raw(self, timeout): char = self.getc(1, timeout) + log.debug('Receive RAW: %x' % char) if char == '': return const.TIMEOUT if char is not const.TIMEOUT: @@ -204,6 +205,7 @@ def _recv_header(self, timeout, errors=10): error_count = 0 char = None while header_length == 0: + log.debug('Receiving header') # Frist ZPAD while char != const.ZPAD: char = self._recv_raw(timeout) From 6e887aeb180f3ad461743dfb5a43dfa6722e39e6 Mon Sep 17 00:00:00 2001 From: Adrian Negreanu Date: Wed, 4 Sep 2024 22:24:10 +0300 Subject: [PATCH 4/6] base: called crc functions already iterate the input --- modem/base.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modem/base.py b/modem/base.py index 40b2abe..c1c7324 100644 --- a/modem/base.py +++ b/modem/base.py @@ -34,8 +34,7 @@ def calc_crc16(self, data, crc=0): '0xd5e3' ''' - for byte in data: - crc = crc16(byte, crc) + crc = crc16(data, crc) return crc def calc_crc32(self, data, crc=0): @@ -49,8 +48,7 @@ def calc_crc32(self, data, crc=0): '0x20ad' ''' - for byte in data: - crc = crc32(byte, crc) + crc = crc32(data, crc) return crc def _check_crc(self, data, crc_mode): From 65b779a588b6d2ccdf33faed48f7f9037e1b12b8 Mon Sep 17 00:00:00 2001 From: Adrian Negreanu Date: Thu, 5 Sep 2024 10:24:10 +0300 Subject: [PATCH 5/6] crc32: use already available CRC32_map instead of zlib --- modem/tools.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/modem/tools.py b/modem/tools.py index b655314..9e04dcc 100644 --- a/modem/tools.py +++ b/modem/tools.py @@ -1,7 +1,6 @@ import struct import logging -from zlib import crc32 as _crc32 -from modem.const import CRC16_MAP +from modem.const import CRC16_MAP,CRC32_MAP from collections.abc import Iterable @@ -30,9 +29,9 @@ def calc(byte, crc=0): if isinstance(byte, bytes): b = struct.unpack('B', byte) else: - b = byte + b = int(byte) - crc = (crc << 8) ^ CRC16_MAP[((crc >> 0x08) ^ b) & 0xff] + crc = (crc << 8) ^ CRC16_MAP[((crc >> 8) ^ b) & 0xff] return crc if isinstance(data, Iterable): @@ -54,4 +53,19 @@ def crc32(data, crc=0): 0x1b851995 ''' - return _crc32(data, crc) & 0xffffffff + def calc(byte, crc=0): + if isinstance(byte, bytes): + b = struct.unpack('B', byte) + else: + b = int(byte) + + crc = crc ^ 0xffffffff + crc = (CRC32_MAP[(crc ^ b) & 0xff] ^ (crc >> 8)) ^ 0xffffffff + return crc + + if isinstance(data, Iterable): + for byte in data: + crc = calc(byte, crc) + else: + crc = calc(data, crc) + return crc & 0xffffffff From 5f62acca96fa5e2112f72ee0dff24591540baf09 Mon Sep 17 00:00:00 2001 From: Adrian Negreanu Date: Tue, 17 Sep 2024 12:04:19 +0300 Subject: [PATCH 6/6] split-me --- Makefile | 4 +- modem/const.py | 1 - modem/protocol/zmodem.py | 313 +++++++++++++++++++++++---------------- test/test-recv.py | 60 ++++---- test/test-send.py | 29 ++-- test/test.py | 31 ++-- 6 files changed, 257 insertions(+), 181 deletions(-) diff --git a/Makefile b/Makefile index fa25e10..cffb356 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ tests: - PYTHONPATH=. python test/test.py test/test.py PYTHONPATH=. python test/test-recv.py - PYTHONPATH=. python test/test-send.py +# PYTHONPATH=. python test/test-send.py +# PYTHONPATH=. python test/test.py test/test.py upload: python setup.py sdist upload diff --git a/modem/const.py b/modem/const.py index c68f948..34675ae 100644 --- a/modem/const.py +++ b/modem/const.py @@ -289,7 +289,6 @@ CAN = b'\x18' CRC = b'\x43' -TIMEOUT = None ZPAD = 0x2a ZDLE = 0x18 ZDLEE = 0x58 diff --git a/modem/protocol/zmodem.py b/modem/protocol/zmodem.py index 1d207ef..b5ef232 100644 --- a/modem/protocol/zmodem.py +++ b/modem/protocol/zmodem.py @@ -11,6 +11,9 @@ class ZMODEM(Modem): ZMODEM protocol implementation, expects an object to read from and an object to write to. ''' + def __init__(self, getc, putc): + super().__init__(getc, putc) + self.ncans = 0 def recv(self, basedir, retry=16, timeout=60, delay=1): ''' @@ -34,55 +37,62 @@ def recv(self, basedir, retry=16, timeout=60, delay=1): kind = self._recv_header(timeout)[0] log.info('ZMODEM connection established') + log.info('starting file transfer') - # Receive files - while kind != const.ZFIN: + while True: if kind == const.ZFILE: - self._recv_file(basedir, timeout, retry) - kind = const.TIMEOUT + if not self._recv_file(basedir, timeout, retry): + log.error("session aborted") + return 0 elif kind == const.ZFIN: - continue - else: log.info('Did not get a file offer? Sending position header') self._send_pos_header(const.ZCOMPL, 0, timeout) - kind = const.TIMEOUT - while kind is const.TIMEOUT: + while True: self._send_zrinit(timeout) kind = self._recv_header(timeout)[0] + if kind != const.TIMEOUT: break + + if kind == const.ZFIN: + log.debug("got ZFIN") + break # Acknowledge the ZFIN - log.info('Received ZFIN, done receiving files') - self._send_hex_header([const.ZFIN, 0, 0, 0, 0], timeout) + log.info('closing the session') + self._send_zfin(timeout) # Wait for the over and out sequence - while kind not in [ord('O'), const.TIMEOUT]: - kind = self._recv(timeout) + while True: + char,e = self._recv(timeout) + if char == 'O': break + if e: return 0 + + while True: + char,e = self._recv(timeout) + if char == 'O': break + if e: return 0 + + log.info('session closed') + return 1 - if kind is not const.TIMEOUT: - while kind not in [ord('O'), const.TIMEOUT]: - kind = self._recv(timeout) def _recv(self, timeout): # Outer loop while True: while True: - char = self._recv_raw(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT - + char,e = self._rx_raw(timeout) + if e: return char,e if char == const.ZDLE: break elif char in [0x11, 0x91, 0x13, 0x93]: continue else: # Regular character - return char + return char, None # ZDLE encoded sequence or session abort - char = self._recv_raw(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char,e = self._rx_raw(timeout) + if e: return char,e if char in [0x11, 0x91, 0x13, 0x93, const.ZDLE]: # Drop @@ -90,25 +100,33 @@ def _recv(self, timeout): # Special cases if char in [const.ZCRCE, const.ZCRCG, const.ZCRCQ, const.ZCRCW]: - return char | const.ZDLEESC + return char|const.ZDLEESC, None elif char == const.ZRUB0: - return 0x7f + return const.ZRUB0, None elif char == const.ZRUB1: - return 0xff + return const.ZRUB1, None else: # Escape sequence if char & 0x60 == 0x40: - return char ^ 0x40 + return char^0x40, None break + return 0, None - def _recv_raw(self, timeout): + def _rx_raw(self, timeout): char = self.getc(1, timeout) - log.debug('Receive RAW: %x' % char) - if char == '': - return const.TIMEOUT + if len(char) == 0: + return 0, const.TIMEOUT + + if char == const.CAN: + self.ncans += 1 + if self.ncans >= 5: + return 0, const.ZABORT + else: + self.ncans = 0 + if char is not const.TIMEOUT: char = ord(char) - return char + return char, None def _recv_data(self, ack_file_pos, timeout): # zack_header = [const.ZACK, 0, 0, 0, 0] @@ -122,8 +140,7 @@ def _recv_data(self, ack_file_pos, timeout): raise TypeError('Invalid _recv_bits size') # Update file positions - if sub_frame_kind is const.TIMEOUT: - return const.TIMEOUT, None + if sub_frame_kind in [const.TIMEOUT, const.ZABORT]: return sub_frame_kind, None else: pos += len(data) @@ -142,63 +159,75 @@ def _recv_data(self, ack_file_pos, timeout): self._send_pos_header(const.ZACK, pos, timeout) return const.ENDOFFRAME, data else: - return False, data + return sub_frame_kind, data def _recv_16_data(self, timeout): char = 0 data = [] mine = 0 while char < 0x100: - char = self._recv(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT, '' + char,e = self._recv(timeout) + if e: return e, None elif char < 0x100: - mine = self.calc_crc16(chr(char & 0xff), mine) - data.append(chr(char)) + mine = self.calc_crc16(char & 0xff, mine) + data.append(char) # Calculate our crc, unescape the sub_frame_kind sub_frame_kind = char ^ const.ZDLEESC - mine = self.calc_crc16(chr(sub_frame_kind), mine) + mine = self.calc_crc16(sub_frame_kind, mine) # Read their crc - rcrc = self._recv(timeout) << 0x08 - rcrc |= self._recv(timeout) + char,e = self._recv(timeout) + if e: return e, None + rcrc = char << 0x08 + + char,e = self._recv(timeout) + if e: return e, None + rcrc |= char log.debug('My CRC = %08x, theirs = %08x' % (mine, rcrc)) if mine != rcrc: - log.error('Invalid CRC32') - return timeout, '' - else: - return sub_frame_kind, ''.join(data) + log.error('Invalid CRC16') + return timeout, None + return sub_frame_kind, bytearray(data) def _recv_32_data(self, timeout): char = 0 data = [] mine = 0 while char < 0x100: - char = self._recv(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT, '' + char,e = self._recv(timeout) + if e: return e, None elif char < 0x100: - mine = self.calc_crc32(chr(char & 0xff), mine) - data.append(chr(char)) + mine = self.calc_crc32(char & 0xff, mine) + data.append(char) # Calculate our crc, unescape the sub_frame_kind sub_frame_kind = char ^ const.ZDLEESC - mine = self.calc_crc32(chr(sub_frame_kind), mine) + mine = self.calc_crc32(sub_frame_kind, mine) # Read their crc - rcrc = self._recv(timeout) - rcrc |= self._recv(timeout) << 0x08 - rcrc |= self._recv(timeout) << 0x10 - rcrc |= self._recv(timeout) << 0x18 + char,e = self._recv(timeout) + if e: return e, None + rcrc = char + + char,e = self._recv(timeout) + if e: return e, None + rcrc |= char << 0x08 + + char,e = self._recv(timeout) + if e: return e, None + rcrc |= char << 0x10 + + char,e = self._recv(timeout) + if e: return e, None + rcrc |= char << 0x18 log.debug('My CRC = %08x, theirs = %08x' % (mine, rcrc)) if mine != rcrc: log.error('Invalid CRC32') - return timeout, '' - else: - return sub_frame_kind, ''.join(data) + return timeout, None + return sub_frame_kind, bytearray(data) def _recv_header(self, timeout, errors=10): header_length = 0 @@ -206,28 +235,26 @@ def _recv_header(self, timeout, errors=10): char = None while header_length == 0: log.debug('Receiving header') - # Frist ZPAD + # First ZPAD while char != const.ZPAD: - char = self._recv_raw(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char,e = self._rx_raw(timeout) + if e: return [e] # Second ZPAD - char = self._recv_raw(timeout) + char,e = self._rx_raw(timeout) + if e: return [e] if char == const.ZPAD: # Get raw character - char = self._recv_raw(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char,e = self._rx_raw(timeout) + if e: return [e] # Spurious ZPAD check if char != const.ZDLE: continue # Read header style - char = self._recv_raw(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char,e = self._rx_raw(timeout) + if e: return [e] if char == const.ZBIN: header_length, header = self._recv_bin16_header(timeout) @@ -241,7 +268,8 @@ def _recv_header(self, timeout, errors=10): else: error_count += 1 if error_count > errors: - return const.TIMEOUT + raise "TOO MANY ERRORS" + return [const.ZABORT] continue # We received a valid header @@ -265,15 +293,19 @@ def _recv_bin16_header(self, timeout): header = [] mine = 0 for x in range(0, 5): - char = self._recv(timeout) - if char is const.TIMEOUT: - return 0, False + char,e = self._recv(timeout) + if e: return 0, False else: mine = self.calc_crc16(chr(char), mine) header.append(char) - rcrc = self._recv(timeout) << 0x08 - rcrc |= self._recv(timeout) + char,e = self._recv(timeout) + if e: return 0, False + rcrc = char << 0x08 + + char,e = self._recv(timeout) + if e: return 0, False + rcrc |= char if mine != rcrc: log.error('Invalid CRC16 in header') @@ -288,18 +320,28 @@ def _recv_bin32_header(self, timeout): header = [] mine = 0 for x in range(0, 5): - char = self._recv(timeout) - if char is const.TIMEOUT: - return 0, False + char,e = self._recv(timeout) + if e: return 0, False else: - mine = self.calc_crc32(chr(char), mine) + mine = self.calc_crc32(char, mine) header.append(char) # Read their crc - rcrc = self._recv(timeout) - rcrc |= self._recv(timeout) << 0x08 - rcrc |= self._recv(timeout) << 0x10 - rcrc |= self._recv(timeout) << 0x18 + char,e = self._recv(timeout) + if e: return 0, False + rcrc = char + + char,e = self._recv(timeout) + if e: return 0, False + rcrc |= char << 0x08 + + char,e = self._recv(timeout) + if e: return 0, False + rcrc |= char << 0x10 + + char,e = self._recv(timeout) + if e: return 0, False + rcrc |= char << 0x18 log.debug('My CRC = %08x, theirs = %08x' % (mine, rcrc)) if mine != rcrc: @@ -315,20 +357,17 @@ def _recv_hex_header(self, timeout): header = [] mine = 0 for x in range(0, 5): - char = self._recv_hex(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT - mine = self.calc_crc16(chr(char), mine) + char,e = self._recv_hex(timeout) + if e: return 0, False + mine = self.calc_crc16(char, mine) header.append(char) # Read their crc - char = self._recv_hex(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char,e = self._recv_hex(timeout) + if e: return 0, False rcrc = char << 0x08 - char = self._recv_hex(timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char,e = self._recv_hex(timeout) + if e: return 0, False rcrc |= char log.debug('My CRC = %04x, theirs = %04x' % (mine, rcrc)) @@ -344,33 +383,30 @@ def _recv_hex_header(self, timeout): return 5, header - def _recv_hex(self, timeout): - n1 = self._recv_hex_nibble(timeout) - if n1 is const.TIMEOUT: - return const.TIMEOUT - n0 = self._recv_hex_nibble(timeout) - if n0 is const.TIMEOUT: - return const.TIMEOUT - return (n1 << 0x04) | n0 + def _recv_hex(self, timeout) -> int: + n1,e = self._recv_hex_nibble(timeout) + if e: return n1,e + n0,e = self._recv_hex_nibble(timeout) + if e: return n0,e + return (n1 << 0x04)|n0, None def _recv_hex_nibble(self, timeout): char = self.getc(1, timeout) - if char is const.TIMEOUT: - return const.TIMEOUT + char = int.from_bytes(char, byteorder='little') + if char in [const.TIMEOUT]: return char - if char > '9': - if char < 'a' or char > 'f': + if char > ord('9'): + if char < ord('a') or char > ord('f'): # Illegal character - return const.TIMEOUT - return ord(char) - ord('a') + 10 + return char, const.TIMEOUT + return char - ord('a') + 10, None else: - if char < '0': + if char < ord('0'): # Illegal character - return const.TIMEOUT - return ord(char) - ord('0') + return const.TIMEOUT, None + return char - ord('0'), None def _recv_file(self, basedir, timeout, retry): - log.info('Abort to receive a file in %s' % (basedir,)) pos = 0 # Read the data subpacket containing the file information @@ -383,16 +419,20 @@ def _recv_file(self, basedir, timeout, retry): return False # We got the file name - part = data.split('\x00') + data_utf8 = bytes(data).decode('utf-8') + part = data_utf8.split('\x00') filename = part[0] filepath = os.path.join(basedir, os.path.basename(filename)) fp = open(filepath, 'wb') part = part[1].split(' ') log.info('Meta %r' % (part,)) size = int(part[0]) + # ZIP doesn't support dates before 1980 # Date is octal (!?) - date = datetime.datetime.fromtimestamp(int(part[1], 8)) - # We ignore mode and serial number, whatever, dude :-) + if part[1] < oct(315536400): + date = datetime.datetime.now() + else: + date = datetime.datetime.fromtimestamp(int(part[1], 8)) log.info('Receiving file "%s" with size %d, mtime %s' % (filename, size, date)) @@ -401,20 +441,35 @@ def _recv_file(self, basedir, timeout, retry): start = time.time() kind = None total_size = 0 - while total_size < size: + while True: kind, chunk_size = self._recv_file_data(fp.tell(), fp, timeout) total_size += chunk_size - if kind == const.ZEOF: + if size > 0 and total_size >= size: + break + if kind == const.ENDOFFRAME: + log.debug("got ENDOFFRAME") break + elif kind == const.ZEOF: + log.debug("got ZEOF") + break + if kind == const.TIMEOUT: + log.debug("got TIMEOUT") + return False + if kind == const.ZABORT: + log.debug("got ZABORT") + return False + else: + log.debug("frame_type:%x" % kind) # End of file speed = (total_size / (time.time() - start)) - log.info('Receiving file "%s" done at %.02f bps' % (filename, speed)) + log.info('Received file "%s" done at %.02f bps' % (filename, speed)) # Update file metadata fp.close() mtime = time.mktime(date.timetuple()) os.utime(filepath, (mtime, mtime)) + return True def _recv_file_data(self, pos, fp, timeout): self._send_pos_header(const.ZRPOS, pos, timeout) @@ -422,8 +477,8 @@ def _recv_file_data(self, pos, fp, timeout): dpos = -1 while dpos != pos: while kind != const.ZDATA: - if kind is const.TIMEOUT: - return const.TIMEOUT, '' + if kind in [const.TIMEOUT, const.ZABORT]: + return kind, 0 else: header = self._recv_header(timeout) kind = header[0] @@ -439,10 +494,11 @@ def _recv_file_data(self, pos, fp, timeout): kind = const.FRAMEOK size = 0 while kind == const.FRAMEOK: - kind, chunk = self._recv_data(pos, timeout) + kind, data = self._recv_data(pos, timeout) if kind in [const.ENDOFFRAME, const.FRAMEOK]: - fp.write(chunk) - size += len(chunk) + fp.write(bytes(data)) + size += len(data) + if kind in [const.TIMEOUT, const.ZABORT]: return kind, 0 return kind, size @@ -461,9 +517,11 @@ def _send_esc(self, char, timeout): self.putc(chr(char ^ 0x40), timeout) def _send_znak(self, pos, timeout): + log.debug('Sending ZNAK') self._send_pos_header(const.ZNAK, pos, timeout) def _send_pos_header(self, kind, pos, timeout): + log.debug('Sending POS header') header = [] header.append(kind) header.append(pos & 0xff) @@ -482,6 +540,7 @@ def _send_hex_nibble(self, nibble, timeout): self.putc('%x' % nibble, timeout) def _send_hex_header(self, header, timeout): + log.debug(f'Sending HEX header {header}') self.putc(chr(const.ZPAD), timeout) self.putc(chr(const.ZPAD), timeout) self.putc(chr(const.ZDLE), timeout) @@ -490,7 +549,7 @@ def _send_hex_header(self, header, timeout): # Update CRC for char in header: - mine = self.calc_crc16(chr(char), mine) + mine = self.calc_crc16(char, mine) self._send_hex(char, timeout) # Transmit the CRC @@ -506,3 +565,7 @@ def _send_zrinit(self, timeout): header = [const.ZRINIT, 0, 0, 0, 4 | const.ZF0_CANFDX | const.ZF0_CANOVIO | const.ZF0_CANFC32] self._send_hex_header(header, timeout) + + def _send_zfin(self, timeout): + log.debug('Sending ZFIN header') + self._send_hex_header([const.ZFIN, 0, 0, 0, 0], timeout) diff --git a/test/test-recv.py b/test/test-recv.py index a707468..0aa4c25 100644 --- a/test/test-recv.py +++ b/test/test-recv.py @@ -5,25 +5,25 @@ import shutil import subprocess import sys -import StringIO +import io import tempfile from modem import * -def run(modem='xmodem'): +def run(modem='zmodem'): if modem.lower().startswith('xmodem'): pipe = subprocess.Popen(['sz', '--xmodem', '--quiet', __file__], stdin=subprocess.PIPE, stdout=subprocess.PIPE) si, so = (pipe.stdin, pipe.stdout) - stream = StringIO.StringIO() + stream = io.StringIO() elif modem.lower() == 'ymodem': pipe = subprocess.Popen(['sz', '--ymodem', '--quiet', __file__], stdin=subprocess.PIPE, stdout=subprocess.PIPE) si, so = (pipe.stdin, pipe.stdout) - stream = StringIO.StringIO() + stream = io.StringIO() elif modem.lower() == 'zmodem': if len(sys.argv) > 2: @@ -31,28 +31,30 @@ def run(modem='xmodem'): else: files = [__file__] #pipe = subprocess.Popen(['zmtx', '-d', '-v'] + files, - pipe = subprocess.Popen(['sz', '--zmodem', '--try-8k'] + files, - stdin=subprocess.PIPE, stdout=subprocess.PIPE) + cmd = ['sz', '--zmodem', '--try-8k'] + files + print(f"{cmd}") + pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) si, so = (pipe.stdin, pipe.stdout) - stream = StringIO.StringIO() + stream = io.StringIO() def getc(size, timeout=3): - w,t,f = select.select([so], [], [], timeout) - if w: - data = so.read(size) - else: - data = None - - #print datetime.datetime.now(), 'getc(', repr(data), ')' + data = so.read(size) return data def putc(data, timeout=3): w,t,f = select.select([], [si], [], timeout) if t: - si.write(data) + if isinstance(data, str): + si.write(bytes(data,'utf-8')) + size = len(data) + elif isinstance(data, int): + si.write(data.to_bytes(1,'little')) + size = 1 + else: + size = len(data) + si.write(data) si.flush() - size = len(data) else: size = None @@ -62,27 +64,27 @@ def putc(data, timeout=3): if modem.lower().startswith('xmodem'): xmodem = globals()[modem.upper()](getc, putc) nbytes = xmodem.recv(stream, retry=8) - print >> sys.stderr, 'received', nbytes, 'bytes' - print >> sys.stderr, stream.getvalue() + print('received', nbytes, 'bytes', file=sys.stderr) + print(stream.getvalue(), file=sys.stderr) elif modem.lower() == 'ymodem': ymodem = globals()[modem.upper()](getc, putc) basedr = tempfile.mkdtemp() nfiles = ymodem.recv(basedr, retry=8) - print >> sys.stderr, 'received', nfiles, 'files in', basedr - print >> sys.stderr, subprocess.Popen(['ls', '-al', basedr], - stdout=subprocess.PIPE).communicate()[0] + print('received', nfiles, 'files in', basedr, file=sys.stderr) + print(subprocess.Popen(['ls', '-al', basedr], + stdout=subprocess.PIPE).communicate()[0], file=sys.stderr) shutil.rmtree(basedr) elif modem.lower() == 'zmodem': - ymodem = globals()[modem.upper()](getc, putc) + zmodem = globals()[modem.upper()](getc, putc) basedr = tempfile.mkdtemp() - nfiles = ymodem.recv(basedr, retry=8) - print >> sys.stderr, 'received', nfiles, 'files in', basedr - print >> sys.stderr, subprocess.Popen(['ls', '-al', basedr], - stdout=subprocess.PIPE).communicate()[0] - print >> sys.stderr, subprocess.Popen(['md5'] + glob.glob(basedr+'/*'), - stdout=subprocess.PIPE).communicate()[0] + nfiles = zmodem.recv(basedr, retry=8) + print('received', nfiles, 'files in', basedr, file=sys.stderr) + print(subprocess.Popen(['ls', '-al', basedr], + stdout=subprocess.PIPE).communicate()[0], file=sys.stderr) + print(subprocess.Popen(['md5sum'] + glob.glob(basedr+'/*'), + stdout=subprocess.PIPE).communicate()[0], file=sys.stderr) shutil.rmtree(basedr) @@ -91,5 +93,5 @@ def putc(data, timeout=3): for modem in sys.argv[1:]: run(modem.upper()) else: - for modem in ['XMODEM', 'YMODEM']: + for modem in ['ZMODEM']: #, 'YMODEM']: run(modem) diff --git a/test/test-send.py b/test/test-send.py index f5c7b18..19ead42 100644 --- a/test/test-send.py +++ b/test/test-send.py @@ -3,25 +3,25 @@ import shutil import subprocess import sys -import StringIO +import io import tempfile from modem import * def run(modem='XMODEM'): - print 'Testing', modem.upper(), 'modem' + print('Testing', modem.upper(), 'modem') fn = None if modem.lower().startswith('xmodem'): fd, fn = tempfile.mkstemp() flag = '--xmodem' - print 'Calling rz %s %s' % (flag, fn) + print('Calling rz %s %s' % (flag, fn)) pipe = subprocess.Popen(['rz', '--errors', '1200', '-p', flag, fn], stdin=subprocess.PIPE, stdout=subprocess.PIPE) si, so = (pipe.stdin, pipe.stdout) elif modem.lower() == 'ymodem': flag = '--ymodem' - print 'Calling rz %s' % (flag,) + print('Calling rz %s' % (flag,)) pipe = subprocess.Popen(['rz', '--errors', '1200', '-p', flag], stdin=subprocess.PIPE, stdout=subprocess.PIPE) si, so = (pipe.stdin, pipe.stdout) @@ -33,7 +33,7 @@ def getc(size, timeout=3): else: data = None - print 'getc(', repr(data), ')' + print('getc(', repr(data), ')') return data def putc(data, timeout=3): @@ -45,13 +45,13 @@ def putc(data, timeout=3): else: size = None - print 'putc(', repr(data), repr(size), ')' + print('putc(', repr(data), repr(size), ')') return size if modem.lower().startswith('xmodem'): stream = open(__file__, 'rb') xmodem = globals()[modem.upper()](getc, putc) - print 'Modem instance', xmodem + print('Modem instance', xmodem) status = xmodem.send(stream, retry=8) stream.close() @@ -59,11 +59,18 @@ def putc(data, timeout=3): fd, fn = tempfile.mkstemp() shutil.copy(__file__, fn) ymodem = YMODEM(getc, putc) - print 'Modem instance', ymodem + print('Modem instance', ymodem) status = ymodem.send(fn, retry=8) - print >> sys.stderr, 'sent', status - print >> sys.stderr, file(fn).read() + elif modem.lower() == 'zmodem': + fd, fn = tempfile.mkstemp() + shutil.copy(__file__, fn) + zmodem = ZMODEM(getc, putc) + print('Modem instance', zmodem) + status = zmodem.send(fn, retry=8) + + print('sent', status, file=sys.stderr) + print(file(fn).read(), file=sys.stderr) if fn: os.unlink(fn) @@ -74,5 +81,5 @@ def putc(data, timeout=3): for modem in sys.argv[1:]: run(modem.upper()) else: - for modem in ['XMODEM', 'YMODEM']: + for modem in ['ZMODEM']:#, 'YMODEM']: run(modem) diff --git a/test/test.py b/test/test.py index cefd0d1..c5c09ad 100644 --- a/test/test.py +++ b/test/test.py @@ -1,12 +1,13 @@ import threading +import traceback import time -import StringIO -import Queue +from io import StringIO +import queue import sys from xmodem import XMODEM class FakeIO(object): - streams = [Queue.Queue(), Queue.Queue()] + streams = [queue.Queue(), queue.Queue()] stdin = [] stdot = [] delay = 0.01 # simulate modem delays @@ -14,22 +15,27 @@ class FakeIO(object): def putc(self, data, q=0): for char in data: self.streams[1-q].put(char) - print 'p%d(0x%x)' % (q, ord(char)), + print('PUT%d(0x%x)' % (q, char), end=' ') sys.stdout.flush() return len(data) def getc(self, size, q=0): data = [] + #traceback.print_stack() while size: try: char = self.streams[q].get() - print 'r%d(0x%x)' % (q, ord(char)), + print('GET%d(0x%x)' % (q, char), end=' ') sys.stdout.flush() data.append(char) size -= 1 - except Queue.Empty: + except queue.Empty: return None - return ''.join(data) + print() + bdata = bytearray(data) + s = bytes(bdata).decode('utf-8') + print(f"{s}") + return s class Client(threading.Thread): def __init__(self, io, server, filename): @@ -46,13 +52,13 @@ def putc(self, data, timeout=0): def run(self): self.xmodem = XMODEM(self.getc, self.putc) - print 'c.send', self.xmodem.send(self.stream) + print('c.send', self.xmodem.send(self.stream)) class Server(FakeIO, threading.Thread): def __init__(self, io): threading.Thread.__init__(self) self.io = io - self.stream = StringIO.StringIO() + self.stream = StringIO() def getc(self, data, timeout=0): return self.io.getc(data, 1) @@ -62,9 +68,9 @@ def putc(self, data, timeout=0): def run(self): self.xmodem = XMODEM(self.getc, self.putc) - print 's.recv', self.xmodem.recv(self.stream) - print 'got' - print self.stream.getvalue() + print('s.recv', self.xmodem.recv(self.stream)) + print('got') + print(self.stream.getvalue()) if __name__ == '__main__': i = FakeIO() @@ -72,4 +78,3 @@ def run(self): c = Client(i, s, sys.argv[1]) s.start() c.start() -