Fixes to EMSI/ZModem as a result of previous enhancements to items

This commit is contained in:
Deon George 2023-07-18 23:00:03 +10:00
parent c1ec4eff36
commit ee03604de3
2 changed files with 137 additions and 114 deletions

View File

@ -35,8 +35,8 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
private const EMSI_NAK = self::EMSI_BEG.'NAKEEC3'; private const EMSI_NAK = self::EMSI_BEG.'NAKEEC3';
private const EMSI_HBT = self::EMSI_BEG.'HBTEAEE'; private const EMSI_HBT = self::EMSI_BEG.'HBTEAEE';
private const CR = "\r"; private const CR = "\x0d"; // \r
private const NL = "\n"; private const NL = "\x0a"; // \n;
private const DEL = "\x08"; private const DEL = "\x08";
/* FEATURES */ /* FEATURES */
@ -58,7 +58,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
private const EMSI_LOG_OUT = 1; private const EMSI_LOG_OUT = 1;
/* FREQs flags */ /* FREQs flags */
private const FR_NOTHANDLED = (-1); private const FR_NOTHANDLED = -1;
private const FR_NOTAVAILABLE = 0; private const FR_NOTAVAILABLE = 0;
private const FR_AVAILABLE = 1; private const FR_AVAILABLE = 1;
@ -224,17 +224,19 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
'XA' // Nodelist Flags 'XA' // Nodelist Flags
); );
// TRAF - netmail/echomail traffic size (bytes) // TRAF - netmail & files size (bytes)
$makedata .= sprintf('{TRAF}{%lX %lX}',$this->send->mail_size,$this->send->size); $makedata .= sprintf('{TRAF}{%lX %lX}',$this->send->mail_size,$this->send->files_size);
// MOH# - Mail On Hold - bytes waiting // MOH# - Mail On Hold - bytes waiting
$makedata .= sprintf('{MOH#}{[%lX]}',$this->send->mail_size); $makedata .= sprintf('{MOH#}{[%lX]}',$this->send->total_size);
// EMD5 - MD5 unique string // EMD5 - MD5 unique string
// Transaction Number (Time in local time) // Transaction Number (Time in local time)
$makedata .= sprintf('{TRX#}{[%lX]}',Carbon::now()->timestamp+Carbon::now($this->node->node_timezone)->offset); $makedata .= sprintf('{TRX#}{[%lX]}',Carbon::now()->timestamp+Carbon::now($this->node->node_timezone)->offset);
// FREQ Time - NRQ (No Requests if this is not defined)
$makedata .= sprintf('{OHFR}{Always! CM}');
$makedata .= sprintf('{TZUTC}{[%+05d]}',-10*60); $makedata .= sprintf('{TZUTC}{[%+05d]}',-10*60);
// @todo Not sure what OHFR is for // @todo Not sure what OHFR is for
@ -506,7 +508,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
*/ */
private function emsi_recv(int $mode): int private function emsi_recv(int $mode): int
{ {
Log::debug(sprintf('%s:+ emsi_recv',self::LOGKEY)); Log::debug(sprintf('%s:+ EMSI receive handshake',self::LOGKEY));
Log::debug(sprintf('%s: - STEP 1',self::LOGKEY)); Log::debug(sprintf('%s: - STEP 1',self::LOGKEY));
/* /*
@ -610,7 +612,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
if (($ch === ord(self::CR)) || ($ch === ord(self::NL))) { if (($ch === ord(self::CR)) || ($ch === ord(self::NL))) {
if (! strncmp($p,self::EMSI_HBT,self::EMSI_SEQ_LEN)) { if (! strncmp($p,self::EMSI_HBT,self::EMSI_SEQ_LEN)) {
Log::debug(sprintf('%s: - Received EMSI_HBT',self::LOGKEY)); Log::debug(sprintf('%s:- Received EMSI_HBT',self::LOGKEY));
goto step3; goto step3;
} }
@ -646,7 +648,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
return self::OK; return self::OK;
} else { } else {
Log::debug(sprintf('%s: - EMSI_DAT didnt parse',self::LOGKEY)); Log::error(sprintf('%s:! EMSI_DAT didnt parse',self::LOGKEY));
goto step2; goto step2;
} }
@ -657,11 +659,11 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
} else { } else {
if (strlen($p) >= self::EMSI_BUF) { if (strlen($p) >= self::EMSI_BUF) {
Log::warning(sprintf('%s: ! EMSI_DAT packet too long.',self::LOGKEY)); Log::warning(sprintf('%s:! EMSI_DAT packet too long.',self::LOGKEY));
$rew = strstr($p,self::EMSI_BEG,TRUE); $rew = strstr($p,self::EMSI_BEG,TRUE);
if ($rew && $rew != $p) { if ($rew && $rew != $p) {
Log::notice(sprintf('%s: - Got EMSI_DAT at offset [%d].',self::LOGKEY,strlen($rew))); Log::notice(sprintf('%s:- Got EMSI_DAT at offset [%d].',self::LOGKEY,strlen($rew)));
$p = substr($p,strlen($rew)); $p = substr($p,strlen($rew));
} }
@ -685,7 +687,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
*/ */
private function emsi_send(): int private function emsi_send(): int
{ {
Log::debug(sprintf('%s:+ EMSI send',self::LOGKEY)); Log::debug(sprintf('%s:+ EMSI transmit Handshake',self::LOGKEY));
Log::debug(sprintf('%s: - STEP 1',self::LOGKEY)); Log::debug(sprintf('%s: - STEP 1',self::LOGKEY));
/* Step 1 /* Step 1
@ -763,7 +765,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
continue; continue;
if (! strncmp($p,self::EMSI_DAT,10)) { if (! strncmp($p,self::EMSI_DAT,10)) {
Log::warning(sprintf('%s: - Got unexpected EMSI_DAT - Argus?',self::LOGKEY)); Log::warning(sprintf('%s:! Got unexpected EMSI_DAT - Argus?',self::LOGKEY));
$this->client->buffer_add(self::EMSI_ACK); $this->client->buffer_add(self::EMSI_ACK);
$this->client->buffer_add(self::EMSI_ACK); $this->client->buffer_add(self::EMSI_ACK);
$this->client->buffer_flush(1); $this->client->buffer_flush(1);
@ -771,10 +773,10 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
$t2 = $this->client->timer_set($this->client->timer_rest($t2) >> 2); $t2 = $this->client->timer_set($this->client->timer_rest($t2) >> 2);
} else if (! strncmp($p,self::EMSI_REQ,self::EMSI_SEQ_LEN)) { } else if (! strncmp($p,self::EMSI_REQ,self::EMSI_SEQ_LEN)) {
Log::notice(sprintf('%s: - Got EMSI_REQ - skipping...',self::LOGKEY),['p'=>$p]); Log::notice(sprintf('%s:- Got EMSI_REQ - skipping...',self::LOGKEY),['p'=>$p]);
} else if (! strncmp($p,self::EMSI_ACK,self::EMSI_SEQ_LEN)) { } else if (! strncmp($p,self::EMSI_ACK,self::EMSI_SEQ_LEN)) {
Log::debug(sprintf('%s: - Got EMSI_ACK',self::LOGKEY)); Log::debug(sprintf('%s:- Got EMSI_ACK',self::LOGKEY));
Log::debug(sprintf('%s: - STEP 5',self::LOGKEY)); Log::debug(sprintf('%s: - STEP 5',self::LOGKEY));
/* Step 5 /* Step 5
@ -796,7 +798,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
$p .= chr($ch); $p .= chr($ch);
} else { } else {
Log::warning(sprintf('%s: ! EMSI packet too long',self::LOGKEY)); Log::warning(sprintf('%s:! EMSI packet too long',self::LOGKEY));
} }
} }
} /* goto step4; */ } /* goto step4; */
@ -825,7 +827,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
protected function protocol_init(): int protected function protocol_init(): int
{ {
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s:+ protocol_init',self::LOGKEY)); Log::debug(sprintf('%s:+ Starting EMSI Protocol INIT',self::LOGKEY));
$got = 0; $got = 0;
$tries = 0; $tries = 0;
@ -851,7 +853,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
while (TRUE) { while (TRUE) {
$ch = $this->client->read_ch(max( 1,min($this->client->timer_rest($t1),$this->client->timer_rest($t2)))); $ch = $this->client->read_ch(max( 1,min($this->client->timer_rest($t1),$this->client->timer_rest($t2))));
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - Got [%x] (%c)',self::LOGKEY,$ch,$ch)); Log::debug(sprintf('%s:- Got [%x] (%c)',self::LOGKEY,$ch,$ch));
if (($ch != self::TIMEOUT) && ($ch < 0)) if (($ch != self::TIMEOUT) && ($ch < 0))
return $ch; return $ch;
@ -870,7 +872,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
if (++$tries > 10) if (++$tries > 10)
return self::TIMEOUT; return self::TIMEOUT;
Log::debug(sprintf('%s: - Sending EMSI_INQ (Try %d of 10)...',self::LOGKEY,$tries)); Log::debug(sprintf('%s:- Sending EMSI_INQ (Try %d of 10)...',self::LOGKEY,$tries));
$this->client->buffer_add(self::EMSI_INQ.self::CR); $this->client->buffer_add(self::EMSI_INQ.self::CR);
} }
@ -885,7 +887,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
if (($ch === ord(self::CR)) || ($ch === ord(self::NL))) { if (($ch === ord(self::CR)) || ($ch === ord(self::NL))) {
if (strstr($p,self::EMSI_REQ)) { if (strstr($p,self::EMSI_REQ)) {
Log::info(sprintf('%s: - Got EMSI_REQ',self::LOGKEY)); Log::info(sprintf('%s:- Got EMSI_REQ',self::LOGKEY));
if ($gotreq++) if ($gotreq++)
return self::OK; return self::OK;
@ -893,7 +895,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
$this->client->buffer_flush(5); $this->client->buffer_flush(5);
} elseif ($p && strstr($p,self::EMSI_BEG) && strstr($p,self::EMSI_ARGUS1)) { } elseif ($p && strstr($p,self::EMSI_BEG) && strstr($p,self::EMSI_ARGUS1)) {
Log::info(sprintf('%s: - Got Intro [%s]',self::LOGKEY,$p)); Log::info(sprintf('%s:- Got Intro [%s]',self::LOGKEY,$p));
} }
continue; continue;
@ -917,7 +919,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
while (! $this->client->timer_expired($t1)) { while (! $this->client->timer_expired($t1)) {
$ch = $this->client->read_ch(max( 1,min($this->client->timer_rest($t1),$this->client->timer_rest($t2)))); $ch = $this->client->read_ch(max( 1,min($this->client->timer_rest($t1),$this->client->timer_rest($t2))));
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - Got [%x] (%c)',self::LOGKEY,$ch,$ch)); Log::debug(sprintf('%s:- Got [%x] (%c)',self::LOGKEY,$ch,$ch));
if (($ch != self::TIMEOUT) && ($ch < 0)) if (($ch != self::TIMEOUT) && ($ch < 0))
return $ch; return $ch;
@ -946,7 +948,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
$got = 0; $got = 0;
if (strstr($p, self::EMSI_INQ)) { if (strstr($p, self::EMSI_INQ)) {
Log::info(sprintf('%s: - Got EMSI_REQ',self::LOGKEY)); Log::info(sprintf('%s:- Got EMSI_REQ',self::LOGKEY));
return self::OK; return self::OK;
} }
@ -973,14 +975,14 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
{ {
// @todo introduce emsi_init() to perform the job of protocol_init. Only needs to be done when we originate a session // @todo introduce emsi_init() to perform the job of protocol_init. Only needs to be done when we originate a session
Log::debug(sprintf('%s:+ Starting EMSI Protocol Session',self::LOGKEY)); Log::debug(sprintf('%s:+ Starting EMSI Protocol SESSION',self::LOGKEY));
$was_req = 0; $was_req = 0;
$got_req = 0; $got_req = 0;
// Outbound session // Outbound session
if ($this->originate) { if ($this->originate) {
Log::debug(sprintf('%s: - Outbound session',self::LOGKEY)); Log::debug(sprintf('%s:- Outbound session',self::LOGKEY));
$this->optionSet(self::O_PUA); $this->optionSet(self::O_PUA);
//$emsi_lo |= ($this->is_freq_available() <= self::FR_NOTAVAILABLE ) ? self::O_NRQ : $emsi_lo; //$emsi_lo |= ($this->is_freq_available() <= self::FR_NOTAVAILABLE ) ? self::O_NRQ : $emsi_lo;
@ -992,11 +994,11 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
if ($rc < 0) if ($rc < 0)
return (self::S_REDIAL|self::S_ADDTRY); return (self::S_REDIAL|self::S_ADDTRY);
Log::info(sprintf('%s: - Starting outbound EMSI session to [%s]',self::LOGKEY,$this->client->address_remote)); Log::info(sprintf('%s:- Starting outbound EMSI session to [%s]',self::LOGKEY,$this->client->address_remote));
// Inbound session // Inbound session
} else { } else {
Log::debug(sprintf('%s: - Inbound session',self::LOGKEY)); Log::debug(sprintf('%s:- Inbound session',self::LOGKEY));
$rc = $this->emsi_recv(self::SM_INBOUND); $rc = $this->emsi_recv(self::SM_INBOUND);
if ($rc < 0) { if ($rc < 0) {
@ -1005,7 +1007,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
return (self::S_REDIAL|self::S_ADDTRY); return (self::S_REDIAL|self::S_ADDTRY);
} }
Log::info(sprintf('%s: - Starting inbound EMSI session from [%s]',self::LOGKEY,$this->client->address_remote)); Log::info(sprintf('%s:- Starting inbound EMSI session from [%s]',self::LOGKEY,$this->client->address_remote));
if ($this->node->aka_authed) { if ($this->node->aka_authed) {
$xproto = $this->is_freq_available(); $xproto = $this->is_freq_available();
@ -1016,7 +1018,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
foreach ($this->protocols as $p => $key) { foreach ($this->protocols as $p => $key) {
if ($this->node->optionGet($key)) { if ($this->node->optionGet($key)) {
Log::debug(sprintf('%s: - Remote supports [%s] (%x)',self::LOGKEY,$p,$key)); Log::debug(sprintf('%s:- Remote supports [%s] (%x)',self::LOGKEY,$p,$key));
$this->optionSet($key); $this->optionSet($key);
} }
} }
@ -1033,7 +1035,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
// @todo Lock Node AKAs // @todo Lock Node AKAs
Log::info(sprintf('%s: - We have %lu%s mail, %lu%s files',self::LOGKEY,$this->send->mail_size,'b',$this->send->files_size,'b')); Log::info(sprintf('%s:- We have [%lu%s] mail, [%lu%s] files',self::LOGKEY,$this->send->mail_size,'b',$this->send->files_size,'b'));
$proto = $this->originate ? $this->node->optionGet(self::P_MASK) : $this->optionGet(self::P_MASK); $proto = $this->originate ? $this->node->optionGet(self::P_MASK) : $this->optionGet(self::P_MASK);
@ -1084,9 +1086,9 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
$xproto = ($this->optionGet(self::O_RH1) && ($this->node->optionGet(self::O_RH1))); $xproto = ($this->optionGet(self::O_RH1) && ($this->node->optionGet(self::O_RH1)));
$x = (substr($t,1,1) === 'H' && $xproto ) ? 'x' : ''; $x = (substr($t,1,1) === 'H' && $xproto ) ? 'x' : '';
Log::info(sprintf('%s: = Using [%s]',self::LOGKEY,$t)); Log::info(sprintf('%s:- Using [%s]',self::LOGKEY,$t));
Log::debug(sprintf('%s: = Options: %s%s%s%s%s%s%s%s%s%s%s', Log::debug(sprintf('%s:/ Options: %s%s%s%s%s%s%s%s%s%s%s',
self::LOGKEY,$x,$t, self::LOGKEY,$x,$t,
($this->node->optionGet(self::O_LST)) ? '/LST' : '', ($this->node->optionGet(self::O_LST)) ? '/LST' : '',
($this->node->optionGet(self::O_PWD)) ? '/PWD' : '', ($this->node->optionGet(self::O_PWD)) ? '/PWD' : '',
@ -1179,7 +1181,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
*/ */
private function wazoorecv(int $zap): bool private function wazoorecv(int $zap): bool
{ {
Log::debug(sprintf('%s:+ wazoorecv',self::LOGKEY)); Log::debug(sprintf('%s:+ Start WAZOO Receive',self::LOGKEY));
// @todo If the node is not defined in the DB node->address is NULL. Need to figure out how to handle those nodes. // @todo If the node is not defined in the DB node->address is NULL. Need to figure out how to handle those nodes.
$rc = (new Zmodem)->zmodem_receive($this->client,$zap,$this->recv,$this->node->address); $rc = (new Zmodem)->zmodem_receive($this->client,$zap,$this->recv,$this->node->address);

View File

@ -13,6 +13,15 @@ use App\Interfaces\Zmodem as ZmodemInterface;
use App\Models\Address; use App\Models\Address;
use App\Traits\CRC as CRCTrait; use App\Traits\CRC as CRCTrait;
/**
* ZModem File Transfer protocol
*
* NOTES:
* + If a data packet follows a HEX header, it is protected with CRC-16.
* + A hex header begins with the sequence ZPAD, ZPAD, ZDLE, ZHEX. [2a 2a 18 42]
* + Type byte(2), 4 flag bytes(8), 2 bytes CRC16(4)
* + An XON character is appended to all HEX packets except ZACK and ZFIN. [11]
*/
final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
{ {
private const LOGKEY = 'Z--'; private const LOGKEY = 'Z--';
@ -28,7 +37,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* Special characters */ /* Special characters */
private const ZPAD = 0x2a; //* /* 052 Padding character begins frames */ private const ZPAD = 0x2a; //* /* 052 Padding character begins frames */
private const ZDLE = 0x18; /* Ctrl-X ZmodemReceive escape - `ala BISYNC DLE */ private const ZDLE = 0x18; /* ZDLE - Zmodem Data Link Escape: Ctrl-X ZmodemReceive escape - `ala BISYNC DLE \030 */
private const ZDLEE = (self::ZDLE^0x40); /* Escaped ZDLE as transmitted */ private const ZDLEE = (self::ZDLE^0x40); /* Escaped ZDLE as transmitted */
private const ZDEL = 0x7f; /* DEL character */ private const ZDEL = 0x7f; /* DEL character */
private const ZBIN = 0x41; //A /* Binary frame indicator (CRC-16) */ private const ZBIN = 0x41; //A /* Binary frame indicator (CRC-16) */
@ -41,26 +50,26 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
private const ZVBINR32 = 0x64; //d /* RLE packed Binary frame with 32 bit FCS and v.header */ private const ZVBINR32 = 0x64; //d /* RLE packed Binary frame with 32 bit FCS and v.header */
private const ZRESC = 0x7e; /* RLE flag/escape character */ private const ZRESC = 0x7e; /* RLE flag/escape character */
private const ZRQINIT = 0; /* Request receive init */ private const ZRQINIT = 0x00; /* Request receive init */
private const ZRINIT = 1; /* Receive init */ private const ZRINIT = 0x01; /* Receive init */
private const ZSINIT = 2; /* Send init sequence (optional) */ private const ZSINIT = 0x02; /* Send init sequence (optional) */
private const ZACK = 3; /* ACK to above */ private const ZACK = 0x03; /* ACK to above */
private const ZFILE = 4; /* File name from sender */ private const ZFILE = 0x04; /* File name from sender */
public const ZSKIP = 5; /* To sender: skip this file */ public const ZSKIP = 0x05; /* To sender: skip this file */
private const ZNAK = 6; /* Last packet was garbled */ private const ZNAK = 0x06; /* Last packet was garbled */
private const ZABORT = 7; /* Abort batch transfers */ private const ZABORT = 0x07; /* Abort batch transfers */
private const ZFIN = 8; /* Finish session */ private const ZFIN = 0x08; /* Finish session */
private const ZRPOS = 9; /* Resume data trans at this position */ private const ZRPOS = 0x09; /* Resume data trans at this position */
private const ZDATA = 10; /* Data packet(s) follow */ private const ZDATA = 0x0a; /* Data packet(s) follow */
private const ZEOF = 11; /* End of file */ private const ZEOF = 0x0b; /* End of file */
public const ZFERR = 12; /* Fatal Read or Write error Detected (we use for refuse -- suspend) */ public const ZFERR = 0x0c; /* Fatal Read or Write error Detected (we use for refuse -- suspend) */
private const ZCRC = 13; /* Request for file CRC and response */ private const ZCRC = 0x0d; /* Request for file CRC and response */
private const ZCHALLENGE = 14; /* Receiver's Challenge */ private const ZCHALLENGE = 0x0e; /* Receiver's Challenge */
private const ZCOMPL = 15; /* Request is complete */ private const ZCOMPL = 0x0f; /* Request is complete */
private const ZCAN = 16; /* Other end canned session with CAN*5 */ private const ZCAN = 0x10; /* Other end canned session with CAN*5 */
private const ZFREECNT = 17; /* Request for free bytes on filesystem (OK, we always send unlimited) */ private const ZFREECNT = 0x11; /* Request for free bytes on filesystem (OK, we always send unlimited) */
private const ZCOMMAND = 18; /* Command from sending program (NOT SUPPORTED!) */ private const ZCOMMAND = 0x12; /* Command from sending program (NOT SUPPORTED!) */
private const ZSTDERR = 19; /* Output to standard error, data follows (NOT SUPPORTED!) */ private const ZSTDERR = 0x13; /* Output to standard error, data follows (NOT SUPPORTED!) */
private const ZCRCE = 0x68; //h /* CRC next, frame ends, header packet follows */ private const ZCRCE = 0x68; //h /* CRC next, frame ends, header packet follows */
private const ZCRCG = 0x69; //i /* CRC next, frame continues nonstop */ private const ZCRCG = 0x69; //i /* CRC next, frame continues nonstop */
@ -237,7 +246,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
break; break;
default: default:
Log::error(sprintf('%s: ! init Strange cancap [%d]',self::LOGKEY,$canzap)); Log::error(sprintf('%s:! Strange canzap during init [%d]',self::LOGKEY,$canzap));
} }
/* Maximum block size -- by protocol, may be reduced by window size later */ /* Maximum block size -- by protocol, may be reduced by window size later */
@ -294,7 +303,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
*/ */
public function zmodem_receive(SocketClient $client,int $canzap,Receive $recv,Address $ao): int public function zmodem_receive(SocketClient $client,int $canzap,Receive $recv,Address $ao): int
{ {
Log::debug(sprintf('%s:+ zmodem_receive [%d]',self::LOGKEY,$canzap)); Log::debug(sprintf('%s:+ Starting ZModem Receive [%d]',self::LOGKEY,$canzap));
$opts = $this->init($client,$canzap); $opts = $this->init($client,$canzap);
@ -341,23 +350,23 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
} else { } else {
switch ($this->recv->open()) { switch ($this->recv->open()) {
case self::FOP_SKIP: case self::FOP_SKIP:
Log::info(sprintf('%s: = zmodem_receive Skip this file [%s]',self::LOGKEY,$this->recv->name)); Log::info(sprintf('%s: = zmodem_receive Skip this file [%s]',self::LOGKEY,$this->recv->nameas));
$frame = self::ZSKIP; $frame = self::ZSKIP;
break; break;
case self::FOP_SUSPEND: case self::FOP_SUSPEND:
Log::info(sprintf('%s: = zmodem_receive Suspend this file [%s]',self::LOGKEY,$this->recv->name)); Log::info(sprintf('%s: = zmodem_receive Suspend this file [%s]',self::LOGKEY,$this->recv->nameas));
$frame = self::ZFERR; $frame = self::ZFERR;
break; break;
case self::FOP_CONT: case self::FOP_CONT:
case self::FOP_OK: case self::FOP_OK:
Log::info(sprintf('%s: = zmodem_receive Receving [%s] from [%d]',self::LOGKEY,$this->recv->name,$this->recv->filepos)); Log::info(sprintf('%s: = zmodem_receive Receving [%s] from [%d]',self::LOGKEY,$this->recv->nameas,$this->recv->pos));
$frame = self::ZRINIT; $frame = self::ZRINIT;
switch (($rc=$this->ls_zrecvfile($recv->filepos))) { switch (($rc=$this->ls_zrecvfile($recv->pos))) {
case self::ZFERR: case self::ZFERR:
Log::debug(sprintf('%s: = zmodem_receive ZFERR',self::LOGKEY)); Log::debug(sprintf('%s: = zmodem_receive ZFERR',self::LOGKEY));
$this->recv->close(); $this->recv->close();
@ -671,7 +680,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* There was no ZDLE in stream, try to read one */ /* There was no ZDLE in stream, try to read one */
if (! $this->ls_GotZDLE) { if (! $this->ls_GotZDLE) {
do { do {
if (($c = $this->ls_readcanned($timeout)) < 0) if (($c=$this->ls_readcanned($timeout)) < 0)
return $c; return $c;
/* Check for unescaped XON/XOFF */ /* Check for unescaped XON/XOFF */
@ -696,7 +705,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* We will be here only in case of DLE */ /* We will be here only in case of DLE */
/* We have data */ /* We have data */
if (($c = $this->ls_readcanned($timeout)) >= 0) { if (($c=$this->ls_readcanned($timeout)) >= 0) {
$this->ls_GotZDLE = 0; $this->ls_GotZDLE = 0;
switch ($c) { switch ($c) {
@ -843,7 +852,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
*/ */
private function ls_zdonereceiver(): int private function ls_zdonereceiver(): int
{ {
Log::debug(sprintf('%s:+ ls_zdonereceiver',self::LOGKEY)); Log::debug(sprintf('%s:+ Finished Receiving',self::LOGKEY));
$trys = 0; $trys = 0;
$retransmit = 1; $retransmit = 1;
@ -887,7 +896,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
break; break;
default: default:
Log::error(sprintf('%s: ? ls_zdonereceiver Something strange [%d]',self::LOGKEY,$rc)); Log::error(sprintf('%s:! Unexpected read from client [%d]',self::LOGKEY,$rc));
if ($rc < 0) if ($rc < 0)
return $rc; return $rc;
@ -897,7 +906,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
} while ($trys < 10); } while ($trys < 10);
Log::error(sprintf('%s:? ls_zdonereceiver Something strange [%d]',self::LOGKEY,$rc)); Log::error(sprintf('%s:! Finished Receiving, we should have returned earlier [%d]',self::LOGKEY,$rc));
return $rc; return $rc;
} }
@ -1358,6 +1367,8 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
} }
/** /**
* Prepare to receive next file (and, any be skip or refuse current file)
*
* @param int $frame * @param int $frame
* @param int $first * @param int $first
* @return int * @return int
@ -1365,7 +1376,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
*/ */
private function ls_zrecvfinfo(int $frame,int $first,Address $ao): int private function ls_zrecvfinfo(int $frame,int $first,Address $ao): int
{ {
Log::debug(sprintf('%s:+ ls_zrecvfinfo - Frame [%d], First [%d]',self::LOGKEY,$frame,$first)); Log::debug(sprintf('%s:+ Preparing to receive file - Frame [%d], First [%d]',self::LOGKEY,$frame,$first));
$trys = 0; $trys = 0;
$retransmit = ($frame != self::ZRINIT); $retransmit = ($frame != self::ZRINIT);
@ -1389,12 +1400,12 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
do { do {
if ($retransmit) { if ($retransmit) {
if ($frame != self::ZRINIT) { if ($frame !== self::ZRINIT) {
if (($rc=$this->ls_zsendhhdr($frame,$this->ls_storelong($this->ls_SerialNum))) < 0) if (($rc=$this->ls_zsendhhdr($frame,$this->ls_storelong($this->ls_SerialNum))) < 0)
return $rc; return $rc;
} }
Log::debug(sprintf('%s: - ls_zrecvfinfo ZRINIT',self::LOGKEY)); Log::debug(sprintf('%s: - ZRINIT',self::LOGKEY));
$txHdr = []; $txHdr = [];
$txHdr[self::LSZ_P0] = ($win&0xff); $txHdr[self::LSZ_P0] = ($win&0xff);
@ -1409,6 +1420,8 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
$trys++; $trys++;
} }
dump(['ls_rxHdr'=>hex_dump(join('',$this->ls_rxHdr))]);
switch (($rc=$this->ls_zrecvhdr($this->ls_rxHdr,$this->ls_HeaderTimeout))) { switch (($rc=$this->ls_zrecvhdr($this->ls_rxHdr,$this->ls_HeaderTimeout))) {
/* Send ZRINIT again */ /* Send ZRINIT again */
case self::ZRQINIT: case self::ZRQINIT:
@ -1527,10 +1540,18 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
return self::TIMEOUT; return self::TIMEOUT;
} }
/**
* Receive a header
*
* @param array $hdr
* @param int $timeout
* @return int
* @throws SocketException
*/
private function ls_zrecvhdr(array &$hdr,int $timeout): int private function ls_zrecvhdr(array &$hdr,int $timeout): int
{ {
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s:+ ls_zrecvhdr with [%d] timeout',self::LOGKEY,$timeout)); Log::debug(sprintf('%s:+ Receive header with [%d] timeout',self::LOGKEY,$timeout));
$state = self::rhInit; $state = self::rhInit;
$readmode = self::rm7BIT; $readmode = self::rm7BIT;
@ -1547,7 +1568,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
if ($state === self::rhInit) { if ($state === self::rhInit) {
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - ls_zrecvhdr Init State',self::LOGKEY)); Log::debug(sprintf('%s:- ls_zrecvhdr Init State',self::LOGKEY));
$frametype = self::ERROR; $frametype = self::ERROR;
$crc = 0; $crc = 0;
@ -1559,27 +1580,30 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
$readmode = self::rm7BIT; $readmode = self::rm7BIT;
} }
while ($rc = $this->client->hasData($timeout)) { while ($rc=$this->client->hasData($timeout)) {
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - ls_zrecvhdr hasData - readmode [%d] - Garbage [%d]',self::LOGKEY,$readmode,$garbage)); Log::debug(sprintf('%s:- ls_zrecvhdr hasData - readmode [%d], garbage chars [%d], state [%d]',self::LOGKEY,$readmode,$garbage,$state));
switch ($readmode) { switch ($readmode) {
case self::rm8BIT: case self::rm8BIT:
$c = $this->ls_readcanned($timeout); $c = $this->ls_readcanned($timeout);
break; break;
case self::rm7BIT: case self::rm7BIT:
$c = $this->ls_read7bit($timeout); $c = $this->ls_read7bit($timeout);
break; break;
case self::rmZDLE: case self::rmZDLE:
$c = $this->ls_readzdle($timeout); $c = $this->ls_readzdle($timeout);
break; break;
case self::rmHEX: case self::rmHEX:
$c = $this->ls_readhex($timeout); $c = $this->ls_readhex($timeout);
break; break;
} }
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - ls_zrecvhdr %s [%x] (%c)',self::LOGKEY,$readmode,$c,$c)); Log::debug(sprintf('%s: - ls_zrecvhdr [%x] (%c)',self::LOGKEY,$c,$c));
/* Here is error */ /* Here is error */
if ($c < 0) if ($c < 0)
@ -1588,9 +1612,6 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* Strip high bits */ /* Strip high bits */
$c &= 0xff; $c &= 0xff;
if ($this->DEBUG)
Log::debug(sprintf('%s: = ls_zrecvhdr %x (%d)',self::LOGKEY,$c,$state));
switch ($state) { switch ($state) {
case self::rhInit: case self::rhInit:
if ($c === self::ZPAD) if ($c === self::ZPAD)
@ -1697,7 +1718,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
case self::rhBYTE: case self::rhBYTE:
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - ls_zrecvhdr [%02x] (%d)',self::LOGKEY,$c,$got)); Log::debug(sprintf('%s: / ls_zrecvhdr [%02x] (%d)',self::LOGKEY,$c,$got));
$hdr[$got] = $c; $hdr[$got] = $c;
@ -1710,7 +1731,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
case self::rhCRC: case self::rhCRC:
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - ls_zrecvhdr [%02x] (%d|%d)',self::LOGKEY,$c,$crcgot+1,$got)); Log::debug(sprintf('%s: %% ls_zrecvhdr [%02x] (%d|%d)',self::LOGKEY,$c,$crcgot+1,$got));
if ($crcl === 2) { if ($crcl === 2) {
$crc <<= 8; $crc <<= 8;
@ -1720,30 +1741,30 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
$crc |= ($c << ($crcgot*8)); $crc |= ($c << ($crcgot*8));
} }
/* Crc finished */ /* CRC finished */
if ($crcl === ++$crcgot) { if ($crcl === ++$crcgot) {
$state = self::rhInit; $state = self::rhInit;
$garbage = 0; $garbage = 0;
if ($crcl === 2) { if ($crcl === 2) {
if (($this->ls_Protocol&self::LSZ_OPTCRC32) && ($readmode != self::rmHEX)) if (($this->ls_Protocol&self::LSZ_OPTCRC32) && ($readmode !== self::rmHEX))
Log::error(sprintf('%s: - ls_zrecvhdr was CRC32, got CRC16 binary header',self::LOGKEY)); Log::error(sprintf('%s:! ls_zrecvhdr was CRC32, got CRC16 binary header',self::LOGKEY));
$crc &= 0xffff; $crc &= 0xffff;
if ($readmode != self::rmHEX) if ($readmode !== self::rmHEX)
$this->ls_Protocol &= (~self::LSZ_OPTCRC32); $this->ls_Protocol &= (~self::LSZ_OPTCRC32);
} else { } else {
if (! ($this->ls_Protocol&self::LSZ_OPTCRC32)) if (! ($this->ls_Protocol&self::LSZ_OPTCRC32))
Log::error(sprintf('%s: - ls_zrecvhdr was CRC16, got CRC32 binary header',self::LOGKEY)); Log::error(sprintf('%s:! ls_zrecvhdr was CRC16, got CRC32 binary header',self::LOGKEY));
$incrc = $this->CRC32_FINISH($incrc); $incrc = $this->CRC32_FINISH($incrc);
$this->ls_Protocol |= self::LSZ_OPTCRC32; $this->ls_Protocol |= self::LSZ_OPTCRC32;
} }
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s: - ls_zrecvhdr CRC%d got %08x - calculated %08x',self::LOGKEY,(($crcl==2) ? 16 : 32),$incrc,$crc)); Log::debug(sprintf('%s:- ls_zrecvhdr CRC%d got [%08x] - calculated [%08x]',self::LOGKEY,(($crcl==2) ? 16 : 32),$incrc,$crc));
if ($incrc != $crc) if ($incrc != $crc)
return self::LSZ_BADCRC; return self::LSZ_BADCRC;
@ -1768,7 +1789,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
case self::CR: case self::CR:
case self::CR|0x80: case self::CR|0x80:
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s:? ls_zrecvhdr rhCR, ignoring any remaining chars [%d]',self::LOGKEY,$c)); Log::debug(sprintf('%s:? ls_zrecvhdr rhCR, ignoring any remaining chars [%x]',self::LOGKEY,$c));
/* At this point, most implementations ignore checking for the remaining chars */ /* At this point, most implementations ignore checking for the remaining chars */
return $frametype; return $frametype;
@ -1782,12 +1803,12 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
case self::XON: case self::XON:
case self::XON|0x80: case self::XON|0x80:
if ($this->DEBUG) if ($this->DEBUG)
Log::debug(sprintf('%s:? ls_zrecvhdr rhCR, got XON without CR/LF [%d]',self::LOGKEY,$c)); Log::debug(sprintf('%s:? ls_zrecvhdr rhCR, got XON without CR/LF [%x]',self::LOGKEY,$c));
return $frametype; return $frametype;
default: default:
Log::error(sprintf('%s:? ls_zrecvhdr Something strange [%d]',self::LOGKEY,$c)); Log::error(sprintf('%s:? ls_zrecvhdr Something strange [%x]',self::LOGKEY,$c));
return self::LSZ_BADCRC; return self::LSZ_BADCRC;
} }
@ -1803,7 +1824,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
break; break;
default: default:
Log::error(sprintf('%s:? ls_zrecvhdr Something strange [%d]',self::LOGKEY,$c)); Log::error(sprintf('%s:? ls_zrecvhdr Something strange [%x]',self::LOGKEY,$c));
return self::LSZ_BADCRC; return self::LSZ_BADCRC;
} }
@ -1828,7 +1849,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
return $frametype; return $frametype;
default: default:
Log::error(sprintf('%s:! ls_zrecvhdr rhXON unexpcted [%d]',self::LOGKEY,$c)); Log::error(sprintf('%s:! ls_zrecvhdr rhXON unexpcted [%x]',self::LOGKEY,$c));
return self::LSZ_BADCRC; return self::LSZ_BADCRC;
} }
@ -1961,7 +1982,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
$this->ls_txLastACK = $newpos; $this->ls_txLastACK = $newpos;
if (! $send->seek($newpos)) { if (! $send->seek($newpos)) {
Log::error(sprintf('%s:! ZRPOS to [%ld] seek error',self::LOGKEY,$send->filepos)); Log::error(sprintf('%s:! ZRPOS to [%ld] seek error',self::LOGKEY,$send->pos));
return self::ERROR; return self::ERROR;
} }
@ -2117,15 +2138,15 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
*/ */
private function ls_zsendfile(Send $send,int $sernum,int $fileleft,int $bytesleft): int private function ls_zsendfile(Send $send,int $sernum,int $fileleft,int $bytesleft): int
{ {
Log::debug(sprintf('%s:+ ls_zsendfile [%s]',self::LOGKEY,$send->name)); Log::debug(sprintf('%s:+ ls_zsendfile [%s]',self::LOGKEY,$send->nameas));
$trys = 0; $trys = 0;
$needack = 0; $needack = 0;
switch (($rc = $this->ls_zsendfinfo($send,$sernum,$send->filepos,$fileleft,$bytesleft))) { switch (($rc = $this->ls_zsendfinfo($send,$sernum,$send->pos,$fileleft,$bytesleft))) {
/* Ok, It's OK! */ /* Ok, It's OK! */
case self::ZRPOS: case self::ZRPOS:
Log::debug(sprintf('%s: - ls_zsendfile ZRPOS to [%d]',self::LOGKEY,$send->filepos)); Log::debug(sprintf('%s: - ls_zsendfile ZRPOS to [%d]',self::LOGKEY,$send->pos));
break; break;
/* Skip it */ /* Skip it */
@ -2165,9 +2186,9 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* We need to send ZDATA if previous frame was ZCRCW /* We need to send ZDATA if previous frame was ZCRCW
Also, frame will be ZCRCW, if it is after RPOS */ Also, frame will be ZCRCW, if it is after RPOS */
if ($frame === self::ZCRCW) { if ($frame === self::ZCRCW) {
Log::debug(sprintf('%s: - ls_zsendfile send ZDATA at [%d]',self::LOGKEY,$send->filepos)); Log::debug(sprintf('%s: - ls_zsendfile send ZDATA at [%d]',self::LOGKEY,$send->pos));
if (($rc=$this->ls_zsendbhdr(self::ZDATA,$this->ls_storelong($send->filepos))) < 0) if (($rc=$this->ls_zsendbhdr(self::ZDATA,$this->ls_storelong($send->pos))) < 0)
return $rc; return $rc;
} }
@ -2200,7 +2221,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
break; break;
case self::sfBuffered: case self::sfBuffered:
if (($send->filepos + strlen($txbuf)) > $this->ls_txLastACK + $this->ls_txWinSize) { if (($send->pos + strlen($txbuf)) > $this->ls_txLastACK + $this->ls_txWinSize) {
$frame = self::ZCRCW; /* Last sub-frame in buffer */ $frame = self::ZCRCW; /* Last sub-frame in buffer */
} else { } else {
@ -2218,7 +2239,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
$trys = 0; $trys = 0;
do { do {
$needack = (self::ZCRCW === $frame) || ($this->ls_txWinSize && ($send->filepos > $this->ls_txLastACK + $this->ls_txWinSize)); $needack = (self::ZCRCW === $frame) || ($this->ls_txWinSize && ($send->pos > $this->ls_txLastACK + $this->ls_txWinSize));
switch (($rc=$this->ls_zrecvhdr($this->ls_rxHdr,$needack ? $this->ls_HeaderTimeout : 0))) { // @todo set timeout to 5 for debugging wtih POP switch (($rc=$this->ls_zrecvhdr($this->ls_rxHdr,$needack ? $this->ls_HeaderTimeout : 0))) { // @todo set timeout to 5 for debugging wtih POP
/* They don't need this file */ /* They don't need this file */
@ -2281,8 +2302,8 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* Here is window, and we send more than window without ACK*/ /* Here is window, and we send more than window without ACK*/
/* Frame was ZCRCW and here is no ACK for it */ /* Frame was ZCRCW and here is no ACK for it */
/* trys less than 10 */ /* trys less than 10 */
(($this->ls_txWinSize && ($send->filepos>($this->ls_txLastACK+$this->ls_txWinSize))) (($this->ls_txWinSize && ($send->pos>($this->ls_txLastACK+$this->ls_txWinSize)))
|| ((self::ZCRCW === $frame) && ($send->filepos>$this->ls_txLastACK))) || ((self::ZCRCW === $frame) && ($send->pos>$this->ls_txLastACK)))
&& ++$trys < 10); && ++$trys < 10);
if ($trys >= 10) if ($trys >= 10)
@ -2301,7 +2322,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
/* Ok, if here is EOF, send it and wait for ZRINIT or ZRPOS */ /* Ok, if here is EOF, send it and wait for ZRINIT or ZRPOS */
/* We do it here, because we coulde receive ZRPOS as answer */ /* We do it here, because we coulde receive ZRPOS as answer */
if ($send->feof()) { if ($send->feof()) {
if (($rc=$this->ls_zsendhhdr(self::ZEOF,$this->ls_storelong($send->filepos))) < 0) if (($rc=$this->ls_zsendhhdr(self::ZEOF,$this->ls_storelong($send->pos))) < 0)
return $rc; return $rc;
$trys = 0; $trys = 0;
@ -2382,7 +2403,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
*/ */
private function ls_zsendfinfo(Send $send,int $sernum,int $pos,int $fileleft,int $bytesleft): int private function ls_zsendfinfo(Send $send,int $sernum,int $pos,int $fileleft,int $bytesleft): int
{ {
Log::debug(sprintf('%s:+ ls_zsendfinfo [%s]',self::LOGKEY,$send->name)); Log::debug(sprintf('%s:+ ls_zsendfinfo [%s]',self::LOGKEY,$send->nameas));
$trys = 0; $trys = 0;
$retransmit = 1; $retransmit = 1;