2021-04-01 10:59:15 +00:00
< ? php
namespace App\Classes\Protocol ;
use Illuminate\Support\Facades\Log ;
2023-07-19 02:32:41 +00:00
use App\Classes\ { Node , Protocol };
2021-06-25 03:43:55 +00:00
use App\Classes\Protocol\Zmodem as ZmodemClass ;
2021-04-01 10:59:15 +00:00
use App\Classes\File\ { Receive , Send };
use App\Classes\Sock\ { SocketClient , SocketException };
use App\Interfaces\CRC as CRCInterface ;
use App\Interfaces\Zmodem as ZmodemInterface ;
2021-07-18 13:37:44 +00:00
use App\Models\Address ;
2021-04-01 10:59:15 +00:00
use App\Traits\CRC as CRCTrait ;
2023-07-18 13:00:03 +00:00
/**
* 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 . [ 2 a 2 a 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 ]
*/
2021-04-01 10:59:15 +00:00
final class Zmodem extends Protocol implements CRCInterface , ZmodemInterface
{
2021-08-17 13:49:39 +00:00
private const LOGKEY = 'Z--' ;
2021-04-01 10:59:15 +00:00
use CRCTrait ;
/* ZModem constants */
private const LSZ_MAXHLEN = 16 ; /* Max header information length NEVER CHANGE */
private const LSZ_MAXATTNLEN = 32 ; /* Max length of Attn string */
private const LSZ_MAXFRAME = self :: ZSTDERR ;
private const LSZ_WINDOW = 1024 ;
private const LSZ_BLOCKSIZE = 1024 ;
/* Special characters */
private const ZPAD = 0x2a ; //* /* 052 Padding character begins frames */
2023-07-18 13:00:03 +00:00
private const ZDLE = 0x18 ; /* ZDLE - Zmodem Data Link Escape: Ctrl-X ZmodemReceive escape - `ala BISYNC DLE \030 */
2021-04-01 10:59:15 +00:00
private const ZDLEE = ( self :: ZDLE ^ 0x40 ); /* Escaped ZDLE as transmitted */
private const ZDEL = 0x7f ; /* DEL character */
private const ZBIN = 0x41 ; //A /* Binary frame indicator (CRC-16) */
private const ZHEX = 0x42 ; //B /* HEX frame indicator */
private const ZBIN32 = 0x43 ; //C /* Binary frame with 32 bit FCS */
private const ZBINR32 = 0x44 ; //D /* RLE packed Binary frame with 32 bit FCS */
private const ZVBIN = 0x61 ; //a /* Binary frame indicator (CRC-16) and v.header */
private const ZVHEX = 0x62 ; //b /* HEX frame indicator and v.header */
private const ZVBIN32 = 0x63 ; //c /* 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 */
2023-07-18 13:00:03 +00:00
private const ZRQINIT = 0x00 ; /* Request receive init */
private const ZRINIT = 0x01 ; /* Receive init */
private const ZSINIT = 0x02 ; /* Send init sequence (optional) */
private const ZACK = 0x03 ; /* ACK to above */
private const ZFILE = 0x04 ; /* File name from sender */
public const ZSKIP = 0x05 ; /* To sender: skip this file */
private const ZNAK = 0x06 ; /* Last packet was garbled */
private const ZABORT = 0x07 ; /* Abort batch transfers */
private const ZFIN = 0x08 ; /* Finish session */
private const ZRPOS = 0x09 ; /* Resume data trans at this position */
private const ZDATA = 0x0a ; /* Data packet(s) follow */
private const ZEOF = 0x0b ; /* End of file */
public const ZFERR = 0x0c ; /* Fatal Read or Write error Detected (we use for refuse -- suspend) */
private const ZCRC = 0x0d ; /* Request for file CRC and response */
private const ZCHALLENGE = 0x0e ; /* Receiver's Challenge */
private const ZCOMPL = 0x0f ; /* Request is complete */
private const ZCAN = 0x10 ; /* Other end canned session with CAN*5 */
private const ZFREECNT = 0x11 ; /* Request for free bytes on filesystem (OK, we always send unlimited) */
private const ZCOMMAND = 0x12 ; /* Command from sending program (NOT SUPPORTED!) */
private const ZSTDERR = 0x13 ; /* Output to standard error, data follows (NOT SUPPORTED!) */
2021-04-01 10:59:15 +00:00
private const ZCRCE = 0x68 ; //h /* CRC next, frame ends, header packet follows */
private const ZCRCG = 0x69 ; //i /* CRC next, frame continues nonstop */
private const ZCRCQ = 0x6a ; //j /* CRC next, frame continues, ZACK expected */
private const ZCRCW = 0x6b ; //k /* CRC next, ZACK expected, end of frame */
private const ZRUB0 = 0x6c ; //l /* Translate to rubout 0177 */
private const ZRUB1 = 0x6d ; //m /* Translate to rubout 0377 */
/* Return codes -- pseudo-characters */
2023-07-02 13:40:08 +00:00
private const LSZ_CAN = - 4 ;
2021-04-01 10:59:15 +00:00
private const LSZ_NOHEADER = - 6 ;
private const LSZ_BADCRC = - 7 ;
private const LSZ_XONXOFF = - 8 ;
private const LSZ_CRCE = ( self :: ZCRCE | 0x0100 );
private const LSZ_CRCG = ( self :: ZCRCG | 0x0100 );
private const LSZ_CRCQ = ( self :: ZCRCQ | 0x0100 );
private const LSZ_CRCW = ( self :: ZCRCW | 0x0100 );
/* Dirty hack */
private const LSZ_TRUSTZFINS = 3 ; /* We trust only in MANY ZFINs during initialization */
/* Byte positions within header array */
private const LSZ_F0 = 3 ; /* First flags byte */
private const LSZ_F1 = 2 ;
private const LSZ_F2 = 1 ;
private const LSZ_F3 = 0 ;
private const LSZ_P0 = 0 ; /* Low order 8 bits of position */
private const LSZ_P1 = 1 ;
private const LSZ_P2 = 2 ;
private const LSZ_P3 = 3 ; /* High order 8 bits of file position */
/* different protocol variations */
private const LSZ_OPTZEDZAP = 0x00000001 ; /* We could big blocks, ZModem variant (ZedZap) */
private const LSZ_OPTDIRZAP = 0x00000002 ; /* We escape nothing, ZModem variant (DirZap) */
private const LSZ_OPTESCAPEALL = 0x00000004 ; /* We must escape ALL contorlo characters */
private const LSZ_OPTCRC32 = 0x00000008 ; /* We must send CRC 32 */
private const LSZ_OPTVHDR = 0x00000010 ; /* We must send variable headers */
private const LSZ_OPTRLE = 0x00000020 ; /* We must send RLEd data (NOT SUPPORTED)! */
private const LSZ_OPTESC8 = 0x00000040 ; /* We must escape all 8-bit data (NOT SUPPORTED!) */
private const LSZ_OPTSKIPGUARD = 0x00000080 ; /* We use double-skip guard */
private const LSZ_OPTFIRSTBATCH = 0x00000100 ; /* It is first batch -- trust in first ZFIN */
/* Peer's capabilities from ZRINIT header */
private const LSZ_RXCANDUPLEX = 0x0001 ; /* Receiver can send and receive true FDX */
private const LSZ_RXCANOVIO = 0x0002 ; /* Receiver can receive data during disk I/O */
private const LSZ_RXCANBRK = 0x0004 ; /* Receiver can send a break signal */
private const LSZ_RXCANRLE = 0x0008 ; /* Receiver can decode RLE */
private const LSZ_RXCANLZW = 0x0010 ; /* Receiver can uncompress LZW */
private const LSZ_RXCANFC32 = 0x0020 ; /* Receiver can use 32 bit Frame Check */
private const LSZ_RXWNTESCCTL = 0x0040 ; /* Receiver expects ctl chars to be escaped */
private const LSZ_RXWNTESC8 = 0x0080 ; /* Receiver expects 8th bit to be escaped */
private const LSZ_RXCANVHDR = 0x0100 ; /* Receiver can variable headers */
/* Conversion options (ZF0 in ZFILE frame) */
private const LSZ_CONVBIN = 1 ; /* Binary transfer - inhibit conversion */
private const LSZ_CONVCNL = 2 ; /* Convert NL to local end of line convention */
private const LSZ_CONVRECOV = 3 ; /* Resume interrupted file transfer -- binary */
/* Read Mode */
private const rm8BIT = 0 ;
private const rm7BIT = 1 ;
private const rmZDLE = 2 ;
private const rmHEX = 3 ;
/* State */
private const rhInit = 0 ;
private const rhZPAD = 1 ;
private const rhXON = 2 ;
private const rhLF = 3 ;
private const rhCR = 4 ;
private const rhCRC = 5 ;
private const rhBYTE = 6 ;
private const rhFrameType = 7 ;
private const rhZHEX = 8 ;
private const rhZBIN = 9 ;
private const rhZBIN32 = 10 ;
private const rhZVHEX = 11 ;
private const rhZVBIN = 12 ;
private const rhZVBIN32 = 13 ;
private const rhZDLE = 14 ;
private const sfSlidingWindow = 0 ; /* Window requested, could do fullduplex (use stream with ZCRCQ and sliding window) */
private const sfStream = 1 ; /* No window requested (use ZCRCG) */
private const sfBuffered = 2 ; /* Window requested, couldn't use fullduplex (use frames ZCRCG + ZCRCW) */
private int $ls_GotZDLE = 0 ; /* We seen DLE as last character */
private int $ls_GotHexNibble = 0 ; /* We seen one hex digit as last character */
private int $ls_Protocol ; /* Plain/ZedZap/DirZap and other options */
private int $ls_CANCount = 0 ; /* Count of CANs to go */
private int $ls_SerialNum = 0 ; /* Serial number of file -- for Double-skip protection */
private int $ls_DataTimeout ; /* Timeout for data blocks */
private int $ls_HeaderTimeout ; /* Timeout for headers */
private int $ls_MaxBlockSize = 8192 ; /* Maximum block size */
/* Special table to FAST calculate header type */
/* CRC32,VAR,RLE */
private const HEADER_TYPE = [[[ self :: ZBIN , - 1 ],[ self :: ZVBIN , - 1 ]],[[ self :: ZBIN32 , self :: ZBINR32 ],[ self :: ZVBIN32 , self :: ZVBINR32 ]]];
/* Variables to control sender */
private array $ls_rxHdr = []; /* Receiver header */
private int $ls_rxCould = 0 ; /* Receiver could fullduplex/streamed IO (from ZRINIT) */
private int $ls_txWinSize = 0 ; /* Receiver Window/Buffer size (0 for streaming) */
private int $ls_txCurBlockSize ; /* Current block size */
private int $ls_txLastSent ; /* Last sent character -- for escaping */
private int $ls_txLastACK = 0 ; /* Last ACKed byte */
private int $ls_txLastRepos = 0 ; /* Last requested byte */
private int $ls_txReposCount = 0 ; /* Count of REPOSes on one position */
private int $ls_txGoodBlocks = 0 ; /* Good blocks sent */
private const NUL = 0x00 ;
private const ACK = 0x06 ;
private const BEL = 0x07 ;
private const BS = 0x08 ;
private const HT = 0x09 ;
private const LF = 0x0a ;
private const VT = 0x0b ;
private const CR = 0x0d ;
private const DLE = 0x10 ;
private const XON = 0x11 ; //(81&037); 'Q'
private const XOFF = 0x13 ; //(83&037); 'S'
private const CAN = 0x18 ;
private const ESC = 0x1b ;
private const RX_SKIP = 1 ;
private const RX_SUSPEND = 2 ;
private string $rxbuf = '' ;
private string $txbuf = '' ;
/**
* @ param SocketClient $client
* @ return null
* @ throws SocketException
*/
public function onConnect ( SocketClient $client ) : ? int
{
// If our parent returns a PID, we've forked
if ( ! parent :: onConnect ( $client )) {
2021-08-17 13:49:39 +00:00
Log :: withContext ([ 'pid' => getmypid ()]);
2021-04-01 10:59:15 +00:00
$this -> session ( self :: SESSION_ZMODEM , $client );
$this -> client -> close ();
2021-08-17 13:49:39 +00:00
Log :: info ( sprintf ( '%s:= onConnect - Connection closed [%s]' , self :: LOGKEY , $client -> address_remote ));
exit ( 0 );
2021-04-01 10:59:15 +00:00
}
return NULL ;
}
/**
* Initialise our session
*/
private function init ( SocketClient $client , int $canzap ) : void
{
$this -> client = $client ;
/* Set all options to requested state -- this may be alerted by other side in ZSINIT */
$this -> ls_Protocol = self :: LSZ_OPTCRC32 | self :: LSZ_OPTSKIPGUARD ;
switch ( $canzap & 0xff ) {
case 2 :
$this -> ls_Protocol |= self :: LSZ_OPTDIRZAP ;
/* FALL THROUGH */
case 1 :
$this -> ls_Protocol |= self :: LSZ_OPTZEDZAP ;
/* FALL THROUGH */
case 0 :
break ;
default :
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:! Strange canzap during init [%d]' , self :: LOGKEY , $canzap ));
2021-04-01 10:59:15 +00:00
}
/* Maximum block size -- by protocol, may be reduced by window size later */
$this -> ls_MaxBlockSize = ( $this -> ls_Protocol & self :: LSZ_OPTZEDZAP ) ? 8192 : 1024 ;
/* Calculate timeouts */
/* Timeout for header waiting, if no data sent -- 3*TransferTime or 10 seconds */
$this -> ls_HeaderTimeout = ( self :: LSZ_MAXHLEN * 30 ) / $this -> client -> speed ;
$this -> ls_HeaderTimeout = ( $this -> ls_HeaderTimeout > 10 ) ? $this -> ls_HeaderTimeout : 10 ;
/* Timeout for data packet (3*TransferTime or 60 seconds) */
$this -> ls_DataTimeout = ( $this -> ls_MaxBlockSize * 30 ) / $this -> client -> speed ;
$this -> ls_DataTimeout = ( $this -> ls_DataTimeout > 60 ) ? $this -> ls_DataTimeout : 60 ;
$this -> ls_SkipGuard = ( $this -> ls_Protocol & self :: LSZ_OPTSKIPGUARD ) ? 1 : 0 ;
}
public function protocol_init () : int
{
// Not used
2021-08-17 13:49:39 +00:00
return 0 ;
2021-04-01 10:59:15 +00:00
}
/**
* Setup our ZMODEM session
*
* @ return int
* @ throws \Exception
*/
public function protocol_session () : int
{
$proto = $this -> originate ? $this -> node -> optionGet ( self :: P_MASK ) : $this -> optionGet ( self :: P_MASK );
if ( $this -> originate ) {
2023-07-17 06:36:53 +00:00
if ( ! $z -> zmodem_sendinit ( $this -> client , $proto ) && $this -> send -> togo_count )
2021-04-01 10:59:15 +00:00
$this -> zmodem_sendfile ( $this -> send );
$rc = $this -> zmodem_senddone ();
} else {
2021-07-18 13:37:44 +00:00
$rc = $this -> zmodem_receive ( $this -> client , $proto , $this -> recv , $this -> node -> address );
2021-04-01 10:59:15 +00:00
}
return $rc ;
}
/**
* Receive files via Zmodem
*
* @ param SocketClient $client
* @ param Receive $recv
* @ param int $canzap
* @ return int
*/
2021-07-18 12:10:21 +00:00
public function zmodem_receive ( SocketClient $client , int $canzap , Receive $recv , Address $ao ) : int
2021-04-01 10:59:15 +00:00
{
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:+ Starting ZModem Receive [%d]' , self :: LOGKEY , $canzap ));
2021-04-01 10:59:15 +00:00
$opts = $this -> init ( $client , $canzap );
$this -> ls_txWinSize = self :: LSZ_WINDOW ;
$this -> recv = $recv ;
if ( $canzap & 0x0100 )
$opts |= self :: LSZ_OPTFIRSTBATCH ;
2023-07-17 06:36:53 +00:00
switch ( $rc = $this -> ls_zrecvfinfo ( self :: ZRINIT ,( $this -> ls_Protocol & self :: LSZ_OPTFIRSTBATCH ) ? 1 : 0 , $ao )) {
2021-04-01 10:59:15 +00:00
case self :: ZFIN :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:= zmodem_receive ZFIN after INIT, empty batch' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zdonereceiver ();
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
case self :: ZFILE :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: = zmodem_receive ZFILE after INIT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:! zmodem_receive Something strange after init [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
$this -> ls_zabort ();
$this -> ls_zdonereceiver ();
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
while ( TRUE ) {
switch ( $rc ) {
case self :: ZFIN :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:= zmodem_receive ZFIN' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zdonereceiver ();
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
case self :: ZFILE :
2023-07-17 06:36:53 +00:00
if ( ! $this -> recv -> togo_count ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ! zmodem_receive No files to get?' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$frame = self :: ZSKIP ;
} else {
2023-07-17 06:36:53 +00:00
switch ( $this -> recv -> open ()) {
2021-04-01 10:59:15 +00:00
case self :: FOP_SKIP :
2023-07-18 13:00:03 +00:00
Log :: info ( sprintf ( '%s: = zmodem_receive Skip this file [%s]' , self :: LOGKEY , $this -> recv -> nameas ));
2021-04-01 10:59:15 +00:00
$frame = self :: ZSKIP ;
break ;
case self :: FOP_SUSPEND :
2023-07-18 13:00:03 +00:00
Log :: info ( sprintf ( '%s: = zmodem_receive Suspend this file [%s]' , self :: LOGKEY , $this -> recv -> nameas ));
2021-04-01 10:59:15 +00:00
$frame = self :: ZFERR ;
break ;
case self :: FOP_CONT :
case self :: FOP_OK :
2023-07-18 13:00:03 +00:00
Log :: info ( sprintf ( '%s: = zmodem_receive Receving [%s] from [%d]' , self :: LOGKEY , $this -> recv -> nameas , $this -> recv -> pos ));
2021-04-01 10:59:15 +00:00
$frame = self :: ZRINIT ;
2023-07-18 13:00:03 +00:00
switch (( $rc = $this -> ls_zrecvfile ( $recv -> pos ))) {
2021-04-01 10:59:15 +00:00
case self :: ZFERR :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: = zmodem_receive ZFERR' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> recv -> close ();
$frame = self :: ZFERR ;
break ;
case self :: ZSKIP :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: = zmodem_receive ZSKIP' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> recv -> close ();
$frame = self :: ZSKIP ;
break ;
2023-07-02 13:40:08 +00:00
case self :: OK :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: = zmodem_receive OK' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> recv -> close ();
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:! zmodem_receive OTHER [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
$this -> recv -> close ();
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
break ;
}
}
break ;
case self :: ZABORT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:= zmodem_receive ZABORT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zabort ();
$this -> ls_zdonereceiver ();
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? zmodem_receive Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
$this -> ls_zabort ();
$this -> ls_zdonereceiver ();
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
2023-07-17 06:36:53 +00:00
$rc = $this -> ls_zrecvfinfo ( $frame , 1 , $ao );
2021-04-01 10:59:15 +00:00
}
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
}
/**
* Done sender -- good way
*
* @ return int
* @ throws \Exception
*/
public function zmodem_senddone () : int
{
$trys = 0 ;
$retransmit = 1 ;
$this -> txbuf = '' ;
do {
if ( $retransmit ) {
if (( $rc = $this -> ls_zsendhhdr ( self :: ZFIN , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
$trys ++ ;
$retransmit = 0 ;
}
switch (( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout ))) {
/* Ok, GOOD */
case self :: ZFIN :
$this -> client -> buffer_add ( 'OO' );
$this -> client -> buffer_flush ( 5 );
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
case self :: ZNAK :
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-04-01 10:59:15 +00:00
$retransmit = 1 ;
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? zmodem_senddone Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
$retransmit = 1 ;
}
} while ( $trys < 10 );
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? zmodem_senddone Something strange or timeeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
/**
* Init sending -- wrapper
*
* @ param SocketClient $client
* @ param int $canzap
* @ return int
*/
public function zmodem_sendinit ( SocketClient $client , int $canzap ) : int
{
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ zmodem_sendinit [%d]' , self :: LOGKEY , $canzap ));
2021-04-01 10:59:15 +00:00
$this -> init ( $client , $canzap );
if (( $rc = $this -> ls_zinitsender ( self :: LSZ_WINDOW , '' )) < 0 )
return $rc ;
2021-08-18 12:11:16 +00:00
Log :: debug ( sprintf ( '%s: - ZMODEM Link Options %d/%d, %s%s%s%s' ,
self :: LOGKEY ,
2021-04-01 10:59:15 +00:00
$this -> ls_MaxBlockSize ,
$this -> ls_txWinSize ,
( $this -> ls_Protocol & self :: LSZ_OPTCRC32 ) ? 'CRC32' : 'CRC16' ,
( $this -> ls_rxCould & self :: LSZ_RXCANDUPLEX ) ? ',DUPLEX' : '' ,
( $this -> ls_Protocol & self :: LSZ_OPTVHDR ) ? ',VHEADER' : '' ,
( $this -> ls_Protocol & self :: LSZ_OPTESCAPEALL ) ? ',ESCALL' : ''
));
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:= zmodem_sendinit [%x]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
/**
* Send a file with the Zmodem Protocol
*
* @ param Send $send
* @ return int
*/
2023-07-19 02:32:41 +00:00
public function zmodem_sendfile ( Send $send , Node $node ) : int
2021-04-01 10:59:15 +00:00
{
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ zmodem_sendfile' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
2023-07-17 06:36:53 +00:00
while ( $send -> togo_count && $send -> open ()) {
2021-07-17 05:48:07 +00:00
try {
2023-07-17 06:36:53 +00:00
$rc = $this -> ls_zsendfile ( $send , $this -> ls_SerialNum ++ , $send -> togo_count , $send -> total_size );
2021-07-17 05:48:07 +00:00
switch ( $rc ) {
2023-07-02 13:40:08 +00:00
case self :: OK :
2023-07-19 02:32:41 +00:00
case self :: ZSKIP :
$send -> close ( TRUE , $node );
2021-07-17 05:48:07 +00:00
break ;
case self :: ZFERR :
2023-07-19 02:32:41 +00:00
$send -> close ( FALSE , $node );
2021-07-17 05:48:07 +00:00
break ;
default :
2023-07-19 02:32:41 +00:00
$send -> close ( FALSE , $node );
2021-07-17 05:48:07 +00:00
$this -> ls_zabort ();
break ;
}
return $rc ;
} catch ( \Exception $e ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ! Error [%s]' , self :: LOGKEY , $e -> getMessage ()));
2021-04-01 10:59:15 +00:00
}
}
return self :: OK ;
}
/**
* Fetch long integer ( 4 bytes ) from buffer , as it must be stored in header
*
* @ param array $buf
* @ return int
*/
private function ls_fetchlong ( array $buf ) : int
{
$l = $buf [ self :: LSZ_P3 ];
$l <<= 8 ;
$l |= $buf [ self :: LSZ_P2 ];
$l <<= 8 ;
$l |= $buf [ self :: LSZ_P1 ];
$l <<= 8 ;
$l |= $buf [ self :: LSZ_P0 ];
return $l ;
}
/**
* Return 7 bit character , strip XON / XOFF if not DirZap , with timeout
*
* @ param int $timeout
* @ return int
* @ throws SocketException
*/
private function ls_read7bit ( int $timeout ) : int
{
$this -> ls_CANCount = 0 ;
do {
if (( $c = $this -> client -> read_ch ( $timeout )) < 0 )
return $c ;
2023-06-27 07:39:11 +00:00
} while (( ! ( $this -> ls_Protocol & self :: LSZ_OPTDIRZAP )) && (( $c === self :: XON ) || ( $c === self :: XOFF )));
2021-04-01 10:59:15 +00:00
2023-06-27 07:39:11 +00:00
if ( $c === self :: CAN ) {
if ( ++ $this -> ls_CANCount === 5 )
2021-04-01 10:59:15 +00:00
return self :: LSZ_CAN ;
} else {
$this -> ls_CANCount = 0 ;
}
2023-06-27 07:39:11 +00:00
if ( ord ( $c ) === 0 )
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
return $c & 0x7f ;
}
/**
* Read one character , check for five CANs
*
* @ param int $timeout
* @ return int
*/
private function ls_readcanned ( int $timeout ) : int
{
if (( $ch = $this -> client -> read_ch ( $timeout )) < 0 )
return $ch ;
2023-06-27 07:39:11 +00:00
if ( $ch === self :: CAN ) {
if ( ++ $this -> ls_CANCount === 5 )
2021-04-01 10:59:15 +00:00
return self :: LSZ_CAN ;
} else {
$this -> ls_CANCount = 0 ;
}
return $ch & 0xff ;
}
/**
* Read character as two hex digit
*
* @ param int $timeout
* @ return int
*/
private function ls_readhex ( int $timeout ) : int
{
static $c = 0 ;
if ( ! $this -> ls_GotHexNibble ) {
if (( $c = $this -> ls_readhexnibble ( $timeout )) < 0 )
return $c ;
$c <<= 4 ;
}
if (( $c2 = $this -> ls_readhexnibble ( $timeout )) >= 0 ) {
$this -> ls_GotHexNibble = 0 ;
return ( $c | $c2 );
} else {
$this -> ls_GotHexNibble = 1 ;
return $c2 ;
}
}
/**
* Read one hex character
*
* @ param int $timeout
* @ return int
*/
private function ls_readhexnibble ( int $timeout ) : int
{
if (( $c = $this -> ls_readcanned ( $timeout )) < 0 )
return $c ;
if ( chr ( $c ) >= '0' && chr ( $c ) <= '9' ) {
return $c - ord ( '0' );
} elseif ( chr ( $c ) >= 'a' && chr ( $c ) <= 'f' ) {
return $c - ord ( 'a' ) + 10 ;
} else {
/* will be CRC error */
return 0 ;
}
}
/**
* Return 8 bit character , strip < DLE >
*
* @ param int $timeout
* @ return int
*/
private function ls_readzdle ( int $timeout ) : int
{
$r = 0 ;
/* There was no ZDLE in stream, try to read one */
if ( ! $this -> ls_GotZDLE ) {
do {
2023-07-18 13:00:03 +00:00
if (( $c = $this -> ls_readcanned ( $timeout )) < 0 )
2021-04-01 10:59:15 +00:00
return $c ;
/* Check for unescaped XON/XOFF */
if ( ! ( $this -> ls_Protocol & self :: LSZ_OPTDIRZAP )) {
switch ( $c ) {
case self :: XON :
case self :: XON | 0x80 :
case self :: XOFF :
case self :: XOFF | 0x80 :
$c = self :: LSZ_XONXOFF ;
}
}
2023-06-27 07:39:11 +00:00
if ( $c === self :: ZDLE )
2021-04-01 10:59:15 +00:00
$this -> ls_GotZDLE = 1 ;
elseif ( $c != self :: LSZ_XONXOFF )
return $c & 0xff ;
2023-06-27 07:39:11 +00:00
} while ( $c === self :: LSZ_XONXOFF );
2021-04-01 10:59:15 +00:00
}
/* We will be here only in case of DLE */
/* We have data */
2023-07-18 13:00:03 +00:00
if (( $c = $this -> ls_readcanned ( $timeout )) >= 0 ) {
2021-04-01 10:59:15 +00:00
$this -> ls_GotZDLE = 0 ;
switch ( $c ) {
case self :: ZCRCE :
case self :: ZCRCG :
case self :: ZCRCW :
case self :: ZCRCQ :
$r = ( $c | 0x100 );
break ;
}
/*
* chat *
if ( $r && isset ( $rnode ) && $rnode -> opt & self :: MO_CHAT ) {
do {
$rr = $this -> ls_readcanned ( $timeout );
// @todo to implement
$this -> z_devrecv_c ( $rr , 0 );
2021-07-21 10:52:17 +00:00
} while ( $rr );
2021-04-01 10:59:15 +00:00
$this -> z_devsend_c ( 0 );
}
*/
if ( $r )
return $r ;
switch ( $c ) {
case self :: ZRUB0 :
return self :: ZDEL ;
case self :: ZRUB1 :
return ( self :: ZDEL | 0x80 );
default :
if (( $c & 0x60 ) != 0x40 )
return self :: LSZ_BADCRC ;
return ( $c ^ 0x40 ) & 0xff ;
}
}
return $c ;
}
/**
* Send one char with escaping
*
* @ param int $c
* @ throws \Exception
*/
private function ls_sendchar ( int $c ) : void
{
$c &= 0xff ;
/* We are Direct ZedZap -- escape only <DLE> */
if ( $this -> ls_Protocol & self :: LSZ_OPTDIRZAP ) {
2023-06-27 07:39:11 +00:00
$esc = ( self :: ZDLE === $c );
2021-04-01 10:59:15 +00:00
/* We are normal ZModem (may be ZedZap) */
} else {
/* Receiver want to escape ALL */
2023-06-27 07:39:11 +00:00
if (( $this -> ls_Protocol & self :: LSZ_OPTESCAPEALL ) && (( $c & 0x60 ) === 0 )) {
2021-04-01 10:59:15 +00:00
$esc = 1 ;
} else {
switch ( $c ) {
case self :: XON :
case ( self :: XON | 0x80 ) :
case self :: XOFF :
case ( self :: XOFF | 0x80 ) :
case self :: DLE :
case ( self :: DLE | 0x80 ) :
case self :: ZDLE :
$esc = 1 ;
break ;
default :
2023-06-27 07:39:11 +00:00
$esc = ((( $this -> ls_txLastSent & 0x7f ) === ord ( '@' )) && (( $c & 0x7f ) === self :: CR ));
2021-04-01 10:59:15 +00:00
break ;
}
}
}
if ( $esc ) {
$this -> client -> buffer_add ( chr ( self :: ZDLE ));
$c ^= 0x40 ;
}
$this -> client -> buffer_add ( chr ( $this -> ls_txLastSent = $c ));
}
/**
* Send one char as two hex digits
*
* @ param int $i
* @ throws \Exception
*/
2021-07-21 10:52:17 +00:00
private function ls_sendhex ( int $i ) : void
2021-04-01 10:59:15 +00:00
{
2021-07-21 10:52:17 +00:00
$str = hexstr ( $i );
$this -> ls_txLastSent = ord ( substr ( $str , - 1 ));
2021-04-01 10:59:15 +00:00
2021-07-21 10:52:17 +00:00
$this -> client -> buffer_add ( $str );
2021-04-01 10:59:15 +00:00
}
/**
* Store long integer ( 4 bytes ) in buffer , as it must be stored in header
*
* @ param int $l
* @ return array
*/
private function ls_storelong ( int $l ) : array
{
$buf [ self :: LSZ_P0 ] = ( $l ) & 0xff ;
$ buf [ self :: LSZ_P1 ] = ( $l >> 8 ) & 0xff ;
$buf [ self :: LSZ_P2 ] = ( $l >> 16 ) & 0xff ;
$buf [ self :: LSZ_P3 ] = ( $l >> 24 ) & 0xff ;
return $buf ;
}
/**
* Abort the session
*
* @ throws \Exception
*/
private function ls_zabort () : void
{
$this -> client -> buffer_flush ( $this -> ls_DataTimeout );
$this -> client -> buffer_add ( chr ( self :: XON ));
for ( $i = 0 ; $i < 8 ; $i ++ )
$this -> client -> buffer_add ( chr ( self :: CAN ));
$this -> client -> buffer_flush ( $this -> ls_DataTimeout );
}
/**
* Finished receiving
*
* @ return int
* @ throws SocketException
*/
private function ls_zdonereceiver () : int
{
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:+ Finished Receiving' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$trys = 0 ;
$retransmit = 1 ;
$this -> rxbuf = '' ;
do {
if ( $retransmit ) {
if (( $rc = $this -> ls_zsendhhdr ( self :: ZFIN , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
$retransmit = 0 ;
$trys ++ ;
}
switch ( $rc = $this -> client -> read_ch ( $this -> ls_HeaderTimeout )) {
/* Ok, GOOD */
case ord ( 'O' ) :
$rc = $this -> client -> read_ch ( 0 );
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
case self :: XON :
case self :: XOFF :
case self :: XON | 0x80 :
case self :: XOFF | 0x80 :
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zdonereceiver XON/XOFF, skip it' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
break ;
case self :: ZPAD :
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zdonereceiver ZPAD' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout )) < 0 )
return $rc ;
if ( self :: ZFIN != $rc )
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
$retransmit = 1 ;
break ;
default :
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:! Unexpected read from client [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
$retransmit = 1 ;
}
} while ( $trys < 10 );
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:! Finished Receiving, we should have returned earlier [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
/**
* Init sender , preapre to send files ( initialize timeouts too ! )
*
* @ param int $protocol
* @ param int $window
* @ return int
*/
private function ls_zinitsender ( int $window , string $attstr )
{
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zinitsender' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$trys = 0 ;
/* Options from ZRINIT header */
$this -> rxOptions = 0 ;
/* ZFIN counter -- we will trust only MAY of them on this stage */
$zfins = 0 ;
$this -> ls_SerialNum = 1 ;
/* Why we need to send this? Old, good times... */
$this -> client -> send ( " rz \r " , 5 );
$retransmit = 1 ;
do {
if ( $retransmit ) {
/* Send first ZRQINIT (do we need it?) */
if (( $rc = $this -> ls_zsendhhdr ( self :: ZRQINIT , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
$retransmit = 0 ;
$trys ++ ;
}
switch ( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout )) {
/* Ok, We got RINIT! */
case self :: ZRINIT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender ZRINIT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> rxOptions = (( $this -> ls_rxHdr [ self :: LSZ_F1 ] & 0xff ) << 8 ) | ( $this -> ls_rxHdr [ self :: LSZ_F0 ] & 0xff );
/* What receiver could (in hardware) -- Duplex, sim. I/O, send break signals */
$this -> ls_rxCould = ( $this -> rxOptions & ( self :: LSZ_RXCANDUPLEX | self :: LSZ_RXCANOVIO | self :: LSZ_RXCANBRK ));
/* Strip RLE from ls_Protocol, if peer could not use RLE (WE COULD NOT RLE in ANY CASE!) */
if ( ! ( $this -> rxOptions & self :: LSZ_RXCANRLE ))
$this -> ls_Protocol &= ( ~ self :: LSZ_OPTRLE );
/* Strip CRC32 from $this->ls_Protocol, if peer could not use CRC32 */
if ( ! ( $this -> rxOptions & self :: LSZ_RXCANFC32 ))
$this -> ls_Protocol &= ( ~ self :: LSZ_OPTCRC32 );
/* Set EscapeAll if peer want it */
if ( $this -> rxOptions & self :: LSZ_RXWNTESCCTL )
$this -> ls_Protocol |= self :: LSZ_OPTESCAPEALL ;
/* Strip VHeaders from $this->ls_Protocol, if peer could not use VHDR */
if ( ! ( $this -> rxOptions & self :: LSZ_RXCANVHDR ))
$this -> ls_Protocol &= ( ~ self :: LSZ_OPTVHDR );
/* Ok, options are ready */
/* Fetch window size */
$this -> ls_txWinSize = (( $this -> ls_rxHdr [ self :: LSZ_P1 ] & 0xff ) << 8 ) | ( $this -> ls_rxHdr [ self :: LSZ_P0 ] & 0xff );
/* Override empty or big window by our window (if not emty too) */
if ( $window && ( ! $this -> ls_txWinSize || ( $this -> ls_txWinSize > $window )))
$this -> ls_txWinSize = $window ;
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender ZRINIT OK - effproto [%08x], winsize [%d]' , self :: LOGKEY , $this -> ls_Protocol , $this -> ls_txWinSize ));
2021-04-01 10:59:15 +00:00
/* Ok, now we could calculate real max frame size and initial block size */
if ( $this -> ls_txWinSize && $this -> ls_MaxBlockSize > $this -> ls_txWinSize ) {
2023-06-27 07:39:11 +00:00
for ( $this -> ls_MaxBlockSize = 1 ; $this -> ls_MaxBlockSize < $this -> ls_txWinSize ; $this -> ls_MaxBlockSize <<= 1 ) {}
2021-04-01 10:59:15 +00:00
/*ls_MaxBlockSize >>= 1;*/
if ( $this -> ls_MaxBlockSize < 32 )
$this -> ls_txWinSize = $this -> ls_MaxBlockSize = 32 ;
}
if ( $this -> client -> speed < 2400 )
$this -> ls_txCurBlockSize = 256 ;
elseif ( $this -> client -> speed >= 2400 && $this -> client -> speed < 4800 )
$this -> ls_txCurBlockSize = 512 ;
else
$this -> ls_txCurBlockSize = 1024 ;
if ( $this -> ls_Protocol & self :: LSZ_OPTZEDZAP ) {
if ( $this -> client -> speed >= 7200 && $this -> client -> speed < 9600 )
$this -> ls_txCurBlockSize = 2048 ;
elseif ( $this -> client -> speed >= 9600 && $this -> client -> speed < 14400 )
$this -> ls_txCurBlockSize = 4096 ;
elseif ( $this -> client -> speed >= 14400 )
$this -> ls_txCurBlockSize = 8192 ;
}
if ( $this -> ls_txCurBlockSize > $this -> ls_MaxBlockSize )
$this -> ls_txCurBlockSize = $this -> ls_MaxBlockSize ;
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender ZRINIT OK - block sizes Max [%d] Current [%d]' , self :: LOGKEY , $this -> ls_MaxBlockSize , $this -> ls_txCurBlockSize ));
2021-04-01 10:59:15 +00:00
/* Send ZSINIT, if we need it */
if ( $attstr || ( ! ( $this -> rxOptions & self :: LSZ_RXWNTESCCTL ) && ( $this -> ls_Protocol & self :: LSZ_OPTESCAPEALL )))
return $this -> ls_zsendsinit ( $attstr );
else
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
/* Return number to peer, he is paranoid */
case self :: ZCHALLENGE :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender CHALLENGE' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zsendhhdr ( ZACK , $this -> ls_rxHdr )) < 0 )
return $rc ;
break ;
/* Send ZRQINIT again */
case self :: ZNAK :
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-04-01 10:59:15 +00:00
$retransmit = 1 ;
break ;
/* ZFIN from previous session? Or may be real one? */
case self :: ZFIN :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender ZFIN [%d]' , self :: LOGKEY , $zfins ));
2021-04-01 10:59:15 +00:00
2023-06-27 07:39:11 +00:00
if ( ++ $zfins === self :: LSZ_TRUSTZFINS )
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
break ;
/* Please, resend */
case self :: LSZ_BADCRC :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender LSZ_BADCRC' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
/* We don't support it! */
case self :: ZCOMMAND :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zinitsender ZCOMMAND' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zsendhhdr ( ZNAK , $this -> ls_storelong ( 0 ));
/* Abort this session -- we trust in ABORT! */
case self :: ZABORT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:- ls_zinitsender ZABORT' , self :: LOGKEY ));
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zinitsender Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
}
} while ( $trys < 10 );
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zinitsender Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
private function ls_zrecvdata ( & $data , & $len , $timeout , $crc32 ) {
return $crc32 ? $this -> ls_zrecvdata32 ( $data , $len , $timeout ) : $this -> ls_zrecvdata16 ( $data , $len , $timeout );
}
2021-07-19 12:15:09 +00:00
/**
* Receive data subframe with CRC16 , return frame type or error ( may be -- timeout )
*
* @ return mixed
*/
2021-07-21 10:52:17 +00:00
private function ls_zrecvdata16 ( string & $data , int & $len , int $timeout ) : int
2021-07-19 12:15:09 +00:00
{
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zrecvdata16' , self :: LOGKEY ),[ 'd' => $data ]);
2021-07-19 12:15:09 +00:00
$got = 0 ; /* Bytes total got */
$crc = 0 ; /* Received CRC */
2023-07-02 13:40:08 +00:00
$frametype = self :: ERROR ; /* Type of frame - ZCRC(G|W|Q|E) */
2021-07-19 12:15:09 +00:00
$rcvdata = 1 ; /* Data is being received NOW (not CRC) */
while ( $rcvdata && (( $c = $this -> ls_readzdle ( $timeout )) >= 0 )) {
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvdata16 got [%x] (%c)' , self :: LOGKEY , $c ,( $c < 31 ? 32 : $c )),[ 'c' => serialize ( $c )]);
2021-07-21 10:52:17 +00:00
if ( $c < 256 ) {
2021-07-19 12:15:09 +00:00
$data .= chr ( $c & 0xff );
if ( ++ $got > $this -> ls_MaxBlockSize )
return self :: LSZ_BADCRC ;
} else {
switch ( $c ) {
case self :: LSZ_CRCE :
case self :: LSZ_CRCG :
case self :: LSZ_CRCQ :
case self :: LSZ_CRCW :
$rcvdata = 0 ;
$frametype = ( $c & 0xff );
break ;
default :
return self :: LSZ_BADCRC ;
}
}
}
/* We finish loop by error in ls_readzdle() */
if ( $rcvdata ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvdata16 Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-07-19 12:15:09 +00:00
return $c ;
}
/* Loops ar unrolled */
if (( $c = $this -> ls_readzdle ( $timeout )) < 0 )
return $c ;
$crc = $c ;
if (( $c = $this -> ls_readzdle ( $timeout )) < 0 )
return $c ;
$crc <<= 8 ;
$crc |= $c ;
2021-07-21 10:52:17 +00:00
$incrc = crc16 ( $data . chr ( $frametype ));
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-18 12:11:16 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvdata16 CRC%d got %08x - calculated %08x' , self :: LOGKEY , 16 , $incrc , $crc ));
2021-07-19 12:15:09 +00:00
if ( $incrc != $crc )
return self :: LSZ_BADCRC ;
$len = $got ;
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:= ls_zrecvdata16 - frametype [%d]' , self :: LOGKEY , $frametype ));
2021-07-19 12:15:09 +00:00
return $frametype ;
}
2021-04-01 10:59:15 +00:00
/**
* Receive data subframe with CRC32 , return frame type or error ( may be -- timeout )
*
* @ param string $data
* @ param int $len
* @ param int $timeout
* @ return int
*/
private function ls_zrecvdata32 ( string & $data , int & $len , int $timeout ) : int
{
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zrecvdata32' , self :: LOGKEY ),[ 'd' => $data ]);
2021-04-01 10:59:15 +00:00
$got = 0 ; /* Bytes total got */
$crc = 0 ; /* Received CRC */
2023-07-02 13:40:08 +00:00
$frametype = self :: ERROR ; /* Type of frame - ZCRC(G|W|Q|E) */
2021-04-01 10:59:15 +00:00
$rcvdata = 1 ; /* Data is being received NOW (not CRC) */
while ( $rcvdata && (( $c = $this -> ls_readzdle ( $timeout )) >= 0 )) {
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvdata32 got [%x] (%c)' , self :: LOGKEY , $c ,( $c < 31 ? 32 : $c )),[ 'c' => serialize ( $c )]);
2021-04-01 10:59:15 +00:00
if ( $c < 256 ) {
$data .= chr ( $c & 0xff );
if ( ++ $got > $this -> ls_MaxBlockSize )
return self :: LSZ_BADCRC ;
} else {
switch ( $c ) {
case self :: LSZ_CRCE :
case self :: LSZ_CRCG :
case self :: LSZ_CRCQ :
case self :: LSZ_CRCW :
$rcvdata = 0 ;
$frametype = ( $c & 0xff );
break ;
default :
return self :: LSZ_BADCRC ;
}
}
}
/* We finish loop by error in ls_readzdle() */
if ( $rcvdata ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvdata32 Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $c ;
}
/* Loops ar unrolled */
if (( $c = $this -> ls_readzdle ( $timeout )) < 0 )
return $c ;
$crc |= ( $c << 0x00 );
if (( $c = $this -> ls_readzdle ( $timeout )) < 0 )
return $c ;
$crc |= ( $c << 0x08 );
if (( $c = $this -> ls_readzdle ( $timeout )) < 0 )
return $c ;
$crc |= ( $c << 0x10 );
if (( $c = $this -> ls_readzdle ( $timeout )) < 0 )
return $c ;
$crc |= ( $c << 0x18 );
2021-07-21 10:52:17 +00:00
$incrc = crc32 ( $data . chr ( $frametype ));
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvdata32 CRC%d got %08x - calculated %08x' , self :: LOGKEY , 32 , $incrc , $crc ));
2021-04-01 10:59:15 +00:00
if ( $incrc != $crc )
return self :: LSZ_BADCRC ;
$len = $got ;
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:= ls_zrecvdata32 - frametype [%d]' , self :: LOGKEY , $frametype ));
2021-04-01 10:59:15 +00:00
return $frametype ;
}
/**
* Receive one file
*
* @ param int $pos
* @ return int
* @ throws \Exception
*/
private function ls_zrecvfile ( int $pos ) : int
{
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zrecvfile - pos [%d]' , self :: LOGKEY , $pos ));
2021-04-01 10:59:15 +00:00
$needzdata = 1 ;
$len = 0 ;
$rxpos = $pos ;
$rxstatus = 0 ;
$this -> rxbuf = '' ;
if (( $rc = $this -> ls_zsendhhdr ( self :: ZRPOS , $this -> ls_storelong ( $rxpos ))) < 0 )
return $rc ;
do {
if ( ! $needzdata ) {
switch (( $rc = $this -> ls_zrecvdata ( $this -> rxbuf , $len , $this -> ls_DataTimeout , $this -> ls_Protocol & self :: LSZ_OPTCRC32 ))) {
case self :: ZCRCE :
$needzdata = 1 ;
case self :: ZCRCG :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfile ZCRC%s, [%d] bytes at [%d]' , self :: LOGKEY ,( $rc == self :: ZCRCE ? 'E' : 'G' ), $len , $rxpos ));
2021-04-01 10:59:15 +00:00
$rxpos += $len ;
if ( $len != $this -> recv -> write ( $this -> rxbuf ))
return self :: ZFERR ;
$this -> rxbuf = '' ;
break ;
case self :: ZCRCW :
$needzdata = 1 ;
case self :: ZCRCQ :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfile ZCRC%s, [%d] bytes at [%d]' , self :: LOGKEY ,( $rc == self :: ZCRCW ? 'W' : 'Q' ), $len , $rxpos ));
2021-04-01 10:59:15 +00:00
$rxpos += $len ;
if ( $len != $this -> recv -> write ( $this -> rxbuf ))
return self :: ZFERR ;
$this -> rxbuf = '' ;
$this -> ls_zsendhhdr ( self :: ZACK , $this -> ls_storelong ( $rxpos ));
break ;
case self :: LSZ_BADCRC :
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-04-01 10:59:15 +00:00
if ( $this -> ls_rxAttnStr ) {
$this -> client -> buffer_add ( $this -> ls_rxAttnStr );
$this -> client -> buffer_flush ( 5 );
}
$this -> client -> rx_purge ();
$this -> ls_zsendhhdr ( self :: ZRPOS , $this -> ls_storelong ( $rxpos ));
$needzdata = 1 ;
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zrecvfile Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
if ( $this -> ls_rxAttnStr ) {
$this -> client -> buffer_add ( $this -> ls_rxAttnStr );
$this -> client -> buffer_flush ( 5 );
}
$this -> client -> rx_purge ();
$this -> ls_zsendhhdr ( self :: ZRPOS , $this -> ls_storelong ( $rxpos ));
$needzdata = 1 ;
}
/* We need new position -- ZDATA (and may be ZEOF) */
} else {
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfile Want ZDATA/ZEOF at [%d]' , self :: LOGKEY , $rxpos ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zrecvnewpos ( $rxpos , $newpos )) < 0 )
return $rc ;
if ( $newpos != $rxpos ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: - ls_zrecvfile Bad new position [%d] in [%d]' , self :: LOGKEY , $newpos , $rc ));
2021-04-01 10:59:15 +00:00
if ( $this -> ls_rxAttnStr ) {
$this -> client -> buffer_add ( $this -> ls_rxAttnStr );
$this -> client -> buffer_flush ( 5 );
}
$this -> client -> rx_purge ();
if (( $rc = $this -> ls_zsendhhdr ( self :: ZRPOS , $this -> ls_storelong ( $rxpos ))) < 0 )
return $rc ;
} else {
2023-06-27 07:39:11 +00:00
if ( $rc === self :: ZEOF ) {
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfile ZEOF' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zsendhhdr ( self :: ZRINIT , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
}
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfile ZDATA' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$needzdata = 0 ;
}
}
if ( $rxstatus )
return ( $rxstatus == self :: RX_SKIP ) ? self :: ZSKIP : self :: ZFERR ;
2021-07-21 10:52:17 +00:00
} while ( TRUE );
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
}
/**
2023-07-18 13:00:03 +00:00
* Prepare to receive next file ( and , any be skip or refuse current file )
*
2021-04-01 10:59:15 +00:00
* @ param int $frame
* @ param int $first
* @ return int
* @ throws \Exception
*/
2023-07-17 06:36:53 +00:00
private function ls_zrecvfinfo ( int $frame , int $first , Address $ao ) : int
2021-04-01 10:59:15 +00:00
{
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:+ Preparing to receive file - Frame [%d], First [%d]' , self :: LOGKEY , $frame , $first ));
2021-04-01 10:59:15 +00:00
$trys = 0 ;
$retransmit = ( $frame != self :: ZRINIT );
$len = 0 ;
$rc = 0 ;
/* ZFIN counter -- we will trust only MAY of them on first stage */
$zfins = 0 ;
$win = $this -> ls_txWinSize ;
$flags = ( self :: LSZ_RXCANDUPLEX | self :: LSZ_RXCANOVIO );
if ( $this -> ls_Protocol & self :: LSZ_OPTCRC32 )
$flags |= self :: LSZ_RXCANFC32 ;
if ( $this -> ls_Protocol & self :: LSZ_OPTESCAPEALL )
$flags |= self :: LSZ_RXWNTESCCTL ;
if ( $this -> ls_Protocol & self :: LSZ_OPTESC8 )
$flags |= self :: LSZ_RXWNTESC8 ;
do {
if ( $retransmit ) {
2023-07-18 13:00:03 +00:00
if ( $frame !== self :: ZRINIT ) {
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zsendhhdr ( $frame , $this -> ls_storelong ( $this -> ls_SerialNum ))) < 0 )
return $rc ;
}
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s: - ZRINIT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$txHdr = [];
$txHdr [ self :: LSZ_P0 ] = ( $win & 0xff );
$txHdr [ self :: LSZ_P1 ] = (( $win >> 8 ) & 0xff );
$txHdr [ self :: LSZ_F1 ] = ( $this -> ls_Protocol & self :: LSZ_OPTVHDR ) ? self :: LSZ_RXCANVHDR : 0 ;
$txHdr [ self :: LSZ_F0 ] = $flags ;
if (( $rc = $this -> ls_zsendhhdr ( self :: ZRINIT , $txHdr )) < 0 )
return $rc ;
$retransmit = 0 ;
$trys ++ ;
}
2023-07-18 13:00:03 +00:00
dump ([ 'ls_rxHdr' => hex_dump ( join ( '' , $this -> ls_rxHdr ))]);
2021-04-01 10:59:15 +00:00
switch (( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout ))) {
/* Send ZRINIT again */
case self :: ZRQINIT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo ZRQINIT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
/* We will trust in first ZFIN after ZRQINIT */
$first = 1 ;
case self :: ZNAK :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo ZNAK' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo TIMEOUT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$retransmit = 1 ;
break ;
/* He want to set some options */
case self :: ZSINIT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo ZSINIT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zrecvcrcw ( $this -> rxbuf , $len )) < 0 )
return $rc ;
/* We will trust in first ZFIN after ZSINIT */
$first = 0 ;
/* Everything is OK */
if ( ! $rc ) {
$this -> ls_zsendhhdr ( self :: ZACK , $this -> ls_storelong ( 1 ));
$this -> ls_rxAttnStr = $this -> rxbuf ;
if ( $this -> ls_rxHdr [ self :: LSZ_F0 ] & self :: LSZ_TXWNTESCCTL )
$this -> ls_Protocol |= self :: LSZ_OPTESCAPEALL ;
if ( $this -> ls_rxHdr [ self :: LSZ_F0 ] & self :: LSZ_TXWNTESC8 )
$this -> ls_Protocol |= self :: LSZ_OPTESC8 ;
/* We could not receive ZCRCW subframe, but error is not fatal */
} else {
$trys ++ ;
}
break ;
/* Ok, File started! */
case self :: ZFILE :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo ZFILE' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zrecvcrcw ( $this -> rxbuf , $len )) < 0 )
return $rc ;
/* Everything is OK, decode frame */
if ( ! $rc ) {
$file = [];
$file [ 'name' ] = substr ( $this -> rxbuf , 0 , $x = strpos ( $this -> rxbuf , chr ( 0x00 )));
if ( sscanf ( substr ( $this -> rxbuf , $x + 1 ),
'%ld %lo %o %o %ld %ld' ,
$file [ 'size' ],
$file [ 'mtime' ],
$len , // @todo What is $len?
$ls_SerialNum , // @todo Do we use this?
$filesleft , // @todo Should track this
$bytesleft ) < 2 )
{
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ! ls_zrecvfinfo File info is corrupted [%s]' , self :: LOGKEY , $this -> rxbuf ));
2021-04-01 10:59:15 +00:00
$filesleft = - 1 ;
} else {
2023-07-17 06:36:53 +00:00
$this -> recv -> new ( $file , $ao );
2021-04-01 10:59:15 +00:00
}
return self :: ZFILE ;
/* We could not receive ZCRCW subframe, but error is not fatal */
} else {
$trys ++ ;
}
break ;
/* ZFIN from previous session? Or may be real one? */
case self :: ZFIN :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo ZFIN [%d], first [%d]' , self :: LOGKEY , $zfins , $first ));
2021-04-01 10:59:15 +00:00
2023-06-27 07:39:11 +00:00
if ( $first || ( ++ $zfins === self :: LSZ_TRUSTZFINS ))
2021-04-01 10:59:15 +00:00
return self :: ZFIN ;
break ;
/* Abort this session -- we trust in ABORT! */
case self :: ZABORT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:- ls_zrecvfinfo ZABORT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
return self :: ZABORT ;
case self :: LSZ_BADCRC :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvfinfo BADCRC' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
$retransmit = 1 ;
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zrecvfinfo Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
}
} while ( $trys < 10 );
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvfinfo Something strange or timeout' , self :: LOGKEY ));
2023-07-02 13:40:08 +00:00
return self :: TIMEOUT ;
2021-04-01 10:59:15 +00:00
}
2023-07-18 13:00:03 +00:00
/**
* Receive a header
*
* @ param array $hdr
* @ param int $timeout
* @ return int
* @ throws SocketException
*/
2021-04-01 10:59:15 +00:00
private function ls_zrecvhdr ( array & $hdr , int $timeout ) : int
{
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:+ Receive header with [%d] timeout' , self :: LOGKEY , $timeout ));
2021-04-01 10:59:15 +00:00
$state = self :: rhInit ;
$readmode = self :: rm7BIT ;
2023-07-18 13:00:03 +00:00
static $frametype = self :: ERROR ; /* Frame type */
2021-04-01 10:59:15 +00:00
static $crcl = 2 ; /* Length of CRC (CRC16 is default) */
static $crcgot = 0 ; /* Number of CRC bytes already got */
static $incrc = 0 ; /* Calculated CRC */
static $crc = 0 ; /* Received CRC */
static $len = 4 ; /* Length of header (4 is default) */
static $got = 0 ; /* Number of header bytes already got */
static $garbage = 0 ; /* Count of garbage characters */
$c = - 1 ;
2023-06-27 07:39:11 +00:00
if ( $state === self :: rhInit ) {
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:- ls_zrecvhdr Init State' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
$frametype = self :: ERROR ;
2021-04-01 10:59:15 +00:00
$crc = 0 ;
$crcl = 2 ;
$crcgot = 0 ;
$incrc = self :: LSZ_INIT_CRC16 ;
$len = 4 ;
$got = 0 ;
$readmode = self :: rm7BIT ;
}
2023-07-18 13:00:03 +00:00
while ( $rc = $this -> client -> hasData ( $timeout )) {
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:- ls_zrecvhdr hasData - readmode [%d], garbage chars [%d], state [%d]' , self :: LOGKEY , $readmode , $garbage , $state ));
2021-04-01 10:59:15 +00:00
switch ( $readmode ) {
case self :: rm8BIT :
$c = $this -> ls_readcanned ( $timeout );
break ;
2023-07-18 13:00:03 +00:00
2021-04-01 10:59:15 +00:00
case self :: rm7BIT :
$c = $this -> ls_read7bit ( $timeout );
break ;
2023-07-18 13:00:03 +00:00
2021-04-01 10:59:15 +00:00
case self :: rmZDLE :
$c = $this -> ls_readzdle ( $timeout );
break ;
2023-07-18 13:00:03 +00:00
2021-04-01 10:59:15 +00:00
case self :: rmHEX :
$c = $this -> ls_readhex ( $timeout );
break ;
}
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvhdr [%x] (%c)' , self :: LOGKEY , $c , $c ));
2021-04-01 10:59:15 +00:00
/* Here is error */
if ( $c < 0 )
return $c ;
/* Strip high bits */
$c &= 0xff ;
switch ( $state ) {
case self :: rhInit :
2023-06-27 07:39:11 +00:00
if ( $c === self :: ZPAD )
2021-04-01 10:59:15 +00:00
$state = self :: rhZPAD ;
else
$garbage ++ ;
break ;
case self :: rhZPAD :
switch ( $c ) {
case self :: ZPAD :
break ;
case self :: ZDLE :
$state = self :: rhZDLE ;
break ;
default :
$garbage ++ ;
$state = self :: rhInit ;
break ;
}
break ;
case self :: rhZDLE :
switch ( $c ) {
case self :: ZBIN :
$state = self :: rhZBIN ;
$readmode = self :: rmZDLE ;
break ;
case self :: ZHEX :
$state = self :: rhZHEX ;
$readmode = self :: rmHEX ;
break ;
case self :: ZBIN32 :
$state = self :: rhZBIN32 ;
$readmode = self :: rmZDLE ;
break ;
case self :: ZVBIN :
$state = self :: rhZVBIN ;
$readmode = self :: rmZDLE ;
break ;
case self :: ZVHEX :
$state = self :: rhZVHEX ;
$readmode = self :: rmHEX ;
break ;
case self :: ZVBIN32 :
$state = self :: rhZVBIN32 ;
$readmode = self :: rmZDLE ;
break ;
default :
$garbage ++ ;
$state = self :: rhInit ;
$readmode = self :: rm7BIT ;
}
break ;
case self :: rhZVBIN32 :
$crcl = 4 ;
/* Fall throught */
case self :: rhZVBIN :
case self :: rhZVHEX :
if ( $c > self :: LSZ_MAXHLEN ) {
$state = self :: rhInit ;
return self :: LSZ_BADCRC ;
}
$len = $c ;
$state = self :: rhFrameType ;
break ;
case self :: rhZBIN32 :
$crcl = 4 ;
/* Fall throught */
case self :: rhZBIN :
case self :: rhZHEX :
$len = 4 ;
case self :: rhFrameType :
if (( $c < 0 ) || ( $c > self :: LSZ_MAXFRAME )) {
$state = self :: rhInit ;
return self :: LSZ_BADCRC ;
}
$frametype = $c ;
2023-06-27 07:39:11 +00:00
$incrc = ( $crcl === 2 ) ? $this -> CRC16USD_UPDATE ( $c , self :: LSZ_INIT_CRC16 ) : $this -> CRC32_UPDATE ( $c , self :: LSZ_INIT_CRC32 );
2021-04-01 10:59:15 +00:00
$state = self :: rhBYTE ;
break ;
case self :: rhBYTE :
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s: / ls_zrecvhdr [%02x] (%d)' , self :: LOGKEY , $c , $got ));
2021-04-01 10:59:15 +00:00
$hdr [ $got ] = $c ;
2023-06-27 07:39:11 +00:00
if ( $len === ++ $got )
2021-04-01 10:59:15 +00:00
$state = self :: rhCRC ;
2023-06-27 07:39:11 +00:00
$incrc = ( $crcl === 2 ) ? $this -> CRC16USD_UPDATE ( $c , $incrc ) : $this -> CRC32_UPDATE ( $c , $incrc );
2021-04-01 10:59:15 +00:00
break ;
case self :: rhCRC :
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s: %% ls_zrecvhdr [%02x] (%d|%d)' , self :: LOGKEY , $c , $crcgot + 1 , $got ));
2021-04-01 10:59:15 +00:00
2023-06-27 07:39:11 +00:00
if ( $crcl === 2 ) {
2021-04-01 10:59:15 +00:00
$crc <<= 8 ;
$crc |= $c ;
} else {
$crc |= ( $c << ( $crcgot * 8 ));
}
2023-07-18 13:00:03 +00:00
/* CRC finished */
2023-06-27 07:39:11 +00:00
if ( $crcl === ++ $crcgot ) {
2021-04-01 10:59:15 +00:00
$state = self :: rhInit ;
$garbage = 0 ;
2023-06-27 07:39:11 +00:00
if ( $crcl === 2 ) {
2023-07-18 13:00:03 +00:00
if (( $this -> ls_Protocol & self :: LSZ_OPTCRC32 ) && ( $readmode !== self :: rmHEX ))
Log :: error ( sprintf ( '%s:! ls_zrecvhdr was CRC32, got CRC16 binary header' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$crc &= 0xffff ;
2023-07-18 13:00:03 +00:00
if ( $readmode !== self :: rmHEX )
2021-04-01 10:59:15 +00:00
$this -> ls_Protocol &= ( ~ self :: LSZ_OPTCRC32 );
} else {
if ( ! ( $this -> ls_Protocol & self :: LSZ_OPTCRC32 ))
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:! ls_zrecvhdr was CRC16, got CRC32 binary header' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$incrc = $this -> CRC32_FINISH ( $incrc );
$this -> ls_Protocol |= self :: LSZ_OPTCRC32 ;
}
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:- ls_zrecvhdr CRC%d got [%08x] - calculated [%08x]' , self :: LOGKEY ,(( $crcl == 2 ) ? 16 : 32 ), $incrc , $crc ));
2021-04-01 10:59:15 +00:00
if ( $incrc != $crc )
return self :: LSZ_BADCRC ;
/* We need to read <CR><LF> after HEX header */
2023-06-27 07:39:11 +00:00
if ( $readmode === self :: rmHEX ) {
2021-04-01 10:59:15 +00:00
$state = self :: rhCR ;
$readmode = self :: rm8BIT ;
} else {
return $frametype ;
}
}
break ;
case self :: rhCR :
$state = self :: rhInit ;
switch ( $c ) {
/* we need LF after <CR> */
case self :: CR :
case self :: CR | 0x80 :
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:? ls_zrecvhdr rhCR, ignoring any remaining chars [%x]' , self :: LOGKEY , $c ));
2021-04-01 10:59:15 +00:00
2023-07-18 13:00:03 +00:00
/* At this point, most implementations ignore checking for the remaining chars */
return $frametype ;
2021-04-01 10:59:15 +00:00
/* Ok, UNIX-like EOL */
case self :: LF :
case self :: LF | 0x80 :
$state = self :: rhXON ;
break ;
case self :: XON :
case self :: XON | 0x80 :
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:? ls_zrecvhdr rhCR, got XON without CR/LF [%x]' , self :: LOGKEY , $c ));
2021-04-01 10:59:15 +00:00
2023-07-18 13:00:03 +00:00
return $frametype ;
2021-04-01 10:59:15 +00:00
default :
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvhdr Something strange [%x]' , self :: LOGKEY , $c ));
2021-04-01 10:59:15 +00:00
return self :: LSZ_BADCRC ;
}
break ;
case self :: rhLF :
$state = self :: rhInit ;
switch ( $c ) {
case self :: LF :
case self :: LF | 0x80 :
$state = self :: rhXON ;
break ;
default :
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvhdr Something strange [%x]' , self :: LOGKEY , $c ));
2021-04-01 10:59:15 +00:00
return self :: LSZ_BADCRC ;
}
break ;
case self :: rhXON :
$state = self :: rhInit ;
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvhdr rhXON' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
switch ( $c ) {
case self :: ZPAD :
case self :: ZPAD | 0x80 :
$state = self :: rhZPAD ;
$got = 0 ;
$crcgot = 0 ;
break ;
case self :: XON :
case self :: XON | 0x80 :
return $frametype ;
default :
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:! ls_zrecvhdr rhXON unexpcted [%x]' , self :: LOGKEY , $c ));
2021-04-01 10:59:15 +00:00
return self :: LSZ_BADCRC ;
}
break ;
}
}
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvhdr Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
/**
* Internal function -- receive ZCRCW frame in 10 trys , send ZNAK / ZACK
*
* @ param string $buf
* @ param int $len
* @ return int
*/
private function ls_zrecvcrcw ( string & $buf , int $len ) : int
{
$trys = 0 ;
do {
switch (( $rc = $this -> ls_zrecvdata ( $buf , $len , $this -> ls_DataTimeout , $this -> ls_Protocol & self :: LSZ_OPTCRC32 ))) {
/* Ok, here it is */
case self :: ZCRCW :
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
case self :: LSZ_BADCRC :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvcrcw got BADCRC' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
return 1 ;
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zrecvcrcw got TIMEOUT' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zrecvcrcw Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
return 1 ;
}
} while ( ++ $trys < 10 );
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
return 1 ;
}
/**
* Receive ZDATA / ZEOF frame , do 10 trys , return position
*
* @ param $oldpos
* @ param $pos
* @ return int
*/
private function ls_zrecvnewpos ( $oldpos , & $pos ) : int
{
$rc = 0 ;
$trys = 0 ;
do {
switch (( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout ))) {
case self :: ZDATA :
case self :: ZEOF :
$pos = $this -> ls_fetchlong ( $this -> ls_rxHdr );
return $rc ;
case self :: ZNAK :
if (( $rc = $this -> ls_zsendhhdr ( self :: ZRPOS , $this -> ls_storelong ( $oldpos ))) < 0 )
return $rc ;
break ;
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-04-01 10:59:15 +00:00
break ;
case self :: LSZ_BADCRC :
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zrecvnewpos Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
$this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ));
}
} while ( ++ $trys < 10 );
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zrecvnewpos Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2023-07-02 13:40:08 +00:00
return self :: TIMEOUT ;
2021-04-01 10:59:15 +00:00
}
/**
* Internal function to process ZRPOS
*
* @ param Send $send
* @ param int $newpos
* @ return int
* @ throws \Exception
*/
private function ls_zrpos ( Send $send , int $newpos ) : int
{
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zrpos, newpos [%d]' , self :: LOGKEY , $newpos ));
2021-04-01 10:59:15 +00:00
2023-06-27 07:39:11 +00:00
if ( $newpos === $this -> ls_txLastRepos ) {
2021-04-01 10:59:15 +00:00
if ( ++ $this -> ls_txReposCount > 10 ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:! ZRPOS to [%ld] limit reached' , self :: LOGKEY , $newpos ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
} else {
$this -> ls_txReposCount = 0 ;
$this -> ls_txLastRepos = $newpos ;
}
/* Drop window */
$this -> ls_txLastACK = $newpos ;
if ( ! $send -> seek ( $newpos )) {
2023-07-18 13:00:03 +00:00
Log :: error ( sprintf ( '%s:! ZRPOS to [%ld] seek error' , self :: LOGKEY , $send -> pos ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
if ( $this -> ls_txCurBlockSize > 32 )
$this -> ls_txCurBlockSize >>= 1 ;
$this -> ls_txGoodBlocks = 0 ;
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
}
/**
* Send data block , with CRC16 and framing
*
* @ param string $data
* @ param int $frame
* @ return int
* @ throws \Exception
*/
private function ls_zsenddata ( string $data , int $frame ) : int
{
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2022-12-02 14:00:45 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zsenddata [%d] (%d)' , self :: LOGKEY , strlen ( $data ), $frame ));
2021-04-01 10:59:15 +00:00
if ( $this -> ls_Protocol & self :: LSZ_OPTCRC32 ) {
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsenddata CRC32' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
2021-07-21 10:52:17 +00:00
for ( $n = 0 ; $n < strlen ( $data ); $n ++ )
2021-04-01 10:59:15 +00:00
$this -> ls_sendchar ( ord ( $data [ $n ]));
$this -> client -> buffer_add ( chr ( self :: ZDLE ) . chr ( $frame ));
/*
* chat *
if ( FALSE AND $rnode -> opt & self :: MO_CHAT ) {
if ( $frame == self :: ZCRCG || $frame == self :: ZCRCW ) {
z_devsend_c ( 1 );
}
$this -> client -> buffer_add ( chr ( 0 ));
}
*/
2021-07-21 10:52:17 +00:00
$crc = crc32 ( $data . chr ( $frame ));
2021-04-01 10:59:15 +00:00
$this -> ls_sendchar ( $crc & 0xff );
$crc >>= 8 ;
$this -> ls_sendchar ( $crc & 0xff );
$crc >>= 8 ;
$this -> ls_sendchar ( $crc & 0xff );
$crc >>= 8 ;
$this -> ls_sendchar ( $crc & 0xff );
$crc >>= 8 ;
} else {
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsenddata CRC16' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
for ( $n = 0 ; $n < strlen ( $data ); $n ++ ) {
$this -> ls_sendchar ( ord ( $data [ $n ]));
}
$this -> client -> buffer_add ( chr ( self :: ZDLE ) . chr ( $frame ));
/*
* chat *
if ( FALSE AND $rnode -> opt & self :: MO_CHAT ) {
if ( $frame == self :: ZCRCG || $frame == self :: ZCRCW ) {
z_devsend_c ( 1 );
}
$this -> client -> buffer_add ( chr ( 0 ));
}
*/
2021-07-21 10:52:17 +00:00
$crc = crc16 ( $data . chr ( $frame ));
2021-04-01 10:59:15 +00:00
$this -> ls_sendchar ( $crc >> 8 );
$this -> ls_sendchar ( $crc & 0xff );
}
2023-06-27 07:39:11 +00:00
if ( ! ( $this -> ls_Protocol & self :: LSZ_OPTDIRZAP ) && self :: ZCRCW === $frame )
2021-04-01 10:59:15 +00:00
$this -> client -> buffer_add ( chr ( self :: XON ));
return $this -> client -> buffer_flush ( $this -> ls_DataTimeout );
}
/**
* Send binary header . Use proper CRC , send var . len . if could
*
* @ param int $frametype
* @ param array $hdr
* @ return int
* @ throws \Exception
*/
private function ls_zsendbhdr ( int $frametype , array $hdr ) : int
{
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zsendbhdr' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$crc = $this -> LSZ_INIT_CRC ();
/* First, calculate packet header byte */
if (( $type = self :: HEADER_TYPE
[( $this -> ls_Protocol & self :: LSZ_OPTCRC32 ) == self :: LSZ_OPTCRC32 ]
[( $this -> ls_Protocol & self :: LSZ_OPTVHDR ) == self :: LSZ_OPTVHDR ]
[( $this -> ls_Protocol & self :: LSZ_OPTRLE ) == self :: LSZ_OPTRLE ]) < 0 )
{
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
/* Send *<DLE> and packet type */
$this -> client -> buffer_add ( chr ( self :: ZPAD ) . chr ( self :: ZDLE ) . chr ( $type ));
/* Send length of header, if needed */
if ( $this -> ls_Protocol & self :: LSZ_OPTVHDR )
$this -> ls_sendchar ( count ( $hdr ));
/* Send type of frame */
$this -> ls_sendchar ( $frametype );
$crc = $this -> LSZ_UPDATE_CRC ( $frametype , $crc );
/* Send whole header */
for ( $n = 0 ; $n < count ( $hdr ); $n ++ ) {
$this -> ls_sendchar ( $hdr [ $n ]);
$crc = $this -> LSZ_UPDATE_CRC ( $hdr [ $n ], $crc );
}
$crc = $this -> LSZ_FINISH_CRC ( $crc );
if ( $this -> ls_Protocol & self :: LSZ_OPTCRC32 ) {
for ( $n = 0 ; $n < 4 ; $n ++ ) {
$this -> ls_sendchar ( $crc & 0xff );
$crc >>= 8 ;
}
} else {
$crc &= 0xffff ;
$this -> ls_sendchar ( $crc >> 8 );
$this -> ls_sendchar ( $crc & 0xff );
}
/* Clean buffer, do real send */
return $this -> client -> buffer_flush ( $this -> ls_HeaderTimeout );
}
/**
* Send one file to peer
*
* @ param Send $send
* @ param int $sernum
* @ param int $fileleft
* @ param int $bytesleft
* @ return int
* @ throws \Exception
*/
private function ls_zsendfile ( Send $send , int $sernum , int $fileleft , int $bytesleft ) : int
{
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zsendfile [%s]' , self :: LOGKEY , $send -> nameas ));
2021-04-01 10:59:15 +00:00
$trys = 0 ;
$needack = 0 ;
2023-07-19 02:32:41 +00:00
switch (( $rc = $this -> ls_zsendfinfo ( $send , $sernum , $send -> pos , $fileleft , $bytesleft ))) {
2021-04-01 10:59:15 +00:00
/* Ok, It's OK! */
case self :: ZRPOS :
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZRPOS to [%d]' , self :: LOGKEY , $send -> pos ));
2021-04-01 10:59:15 +00:00
break ;
/* Skip it */
case self :: ZSKIP :
/* Suspend it */
2023-07-19 02:32:41 +00:00
// @todo Should ZFERR be next to ZABORT?
2021-04-01 10:59:15 +00:00
case self :: ZFERR :
// @todo Mark the file as skipped
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZSKIP/ZFERR' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
return $rc ;
case self :: ZABORT :
/* Session is aborted */
case self :: ZFIN :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZABORT/ZFIN' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_zsendhhdr ( self :: ZFIN , $this -> ls_storelong ( 0 ));
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
default :
if ( $rc < 0 )
return $rc ;
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s:- ls_zsendfile Strange answer on ZFILE [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
/* Send file data */
if ( $this -> ls_txWinSize )
$mode = ( $this -> ls_rxCould & self :: LSZ_RXCANDUPLEX ) ? self :: sfSlidingWindow : self :: sfBuffered ;
else
$mode = self :: sfStream ;
$frame = self :: ZCRCW ;
while ( ! $send -> feof ()) {
/* We need to send ZDATA if previous frame was ZCRCW
Also , frame will be ZCRCW , if it is after RPOS */
2023-06-27 07:39:11 +00:00
if ( $frame === self :: ZCRCW ) {
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile send ZDATA at [%d]' , self :: LOGKEY , $send -> pos ));
2021-04-01 10:59:15 +00:00
2023-07-18 13:00:03 +00:00
if (( $rc = $this -> ls_zsendbhdr ( self :: ZDATA , $this -> ls_storelong ( $send -> pos ))) < 0 )
2021-04-01 10:59:15 +00:00
return $rc ;
}
/* Send frame of data */
try {
$txbuf = $send -> read ( $this -> ls_txCurBlockSize );
} catch ( \Exception $e ) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:! ls_zsendfile Read error' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
/* Select sub-frame type */
/* This is last sub-frame -- EOF */
if ( strlen ( $txbuf ) < $this -> ls_txCurBlockSize ) {
2023-06-27 07:39:11 +00:00
$frame = ( $mode === self :: sfStream ) ? self :: ZCRCE : self :: ZCRCW ;
2021-04-01 10:59:15 +00:00
/* This is not-last sub-frame */
} else {
switch ( $mode ) {
/* Simple sub-frame */
case self :: sfStream :
$frame = self :: ZCRCG ;
break ;
/* Simple sub-frame, but with SlWin */
case self :: sfSlidingWindow :
$frame = self :: ZCRCQ ;
break ;
case self :: sfBuffered :
2023-07-18 13:00:03 +00:00
if (( $send -> pos + strlen ( $txbuf )) > $this -> ls_txLastACK + $this -> ls_txWinSize ) {
2021-04-01 10:59:15 +00:00
$frame = self :: ZCRCW ; /* Last sub-frame in buffer */
} else {
$frame = self :: ZCRCG ; /* Simple sub-frame */
}
break ;
}
}
if (( $rc = $this -> ls_zsenddata ( $txbuf , $frame )) < 0 )
return $rc ;
/* Ok, now wait for ACKs if here is window, or sample for RPOSes */
$trys = 0 ;
do {
2023-07-18 13:00:03 +00:00
$needack = ( self :: ZCRCW === $frame ) || ( $this -> ls_txWinSize && ( $send -> pos > $this -> ls_txLastACK + $this -> ls_txWinSize ));
2021-04-01 10:59:15 +00:00
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 */
case self :: ZSKIP :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZSKIP' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
/* Problems occured -- suspend file */
case self :: ZFERR :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZFERR' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
return $rc ;
/* Ok, position ACK */
case self :: ZACK :
$this -> ls_txLastACK = $this -> ls_fetchlong ( $this -> ls_rxHdr );
2022-12-02 14:00:45 +00:00
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2022-12-02 14:00:45 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZACK' , self :: LOGKEY ),[ 'ls_rxHdr' => $this -> ls_rxHdr , 'ls_txLastACK' => $this -> ls_txLastACK , 'ls_rxHdr' => $this -> ls_rxHdr ]);
2021-04-01 10:59:15 +00:00
break ;
/* Repos */
case self :: ZRPOS :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ZRPOS' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zrpos ( $send , $this -> ls_fetchlong ( $this -> ls_rxHdr ))) < 0 )
return $rc ;
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ZRPOS [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
/* Force to retransmit ZDATA */
$frame = self :: ZCRCW ;
break ;
/* Abort transfer */
case self :: ZABORT :
/* Strange? Ok, abort too */
case self :: ZFIN :
/* Abort too */
case self :: ZCAN :
case self :: LSZ_CAN :
$this -> ls_zsendhhdr ( self :: ZFIN , $this -> ls_storelong ( 0 ));
/* Fall through */
2023-07-02 13:40:08 +00:00
case self :: RCDO :
case self :: ERROR :
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT : /* Ok! */
2021-04-01 10:59:15 +00:00
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zsendfile Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
}
2021-07-21 10:52:17 +00:00
} while (
2021-04-01 10:59:15 +00:00
/* Here is window, and we send more than window without ACK*/
/* Frame was ZCRCW and here is no ACK for it */
/* trys less than 10 */
2023-07-18 13:00:03 +00:00
(( $this -> ls_txWinSize && ( $send -> pos > ( $this -> ls_txLastACK + $this -> ls_txWinSize )))
|| (( self :: ZCRCW === $frame ) && ( $send -> pos > $this -> ls_txLastACK )))
2021-04-01 10:59:15 +00:00
&& ++ $trys < 10 );
if ( $trys >= 10 )
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
/* Ok, increase block, if here is MANY good blocks was sent */
if ( ++ $this -> ls_txGoodBlocks > 32 ) {
$this -> ls_txCurBlockSize <<= 1 ;
if ( $this -> ls_txCurBlockSize > $this -> ls_MaxBlockSize )
$this -> ls_txCurBlockSize = $this -> ls_MaxBlockSize ;
$this -> ls_txGoodBlocks = 0 ;
}
/* Ok, if here is EOF, send it and wait for ZRINIT or ZRPOS */
/* We do it here, because we coulde receive ZRPOS as answer */
if ( $send -> feof ()) {
2023-07-18 13:00:03 +00:00
if (( $rc = $this -> ls_zsendhhdr ( self :: ZEOF , $this -> ls_storelong ( $send -> pos ))) < 0 )
2021-04-01 10:59:15 +00:00
return $rc ;
$trys = 0 ;
do {
switch (( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout ))) {
/* They don't need this file */
case self :: ZSKIP :
/* Problems occured -- suspend file */
case self :: ZFERR :
return $rc ;
/* Repos */
case self :: ZRPOS :
if (( $rc = $this -> ls_zrpos ( $send , $this -> ls_fetchlong ( $this -> ls_rxHdr ))) < 0 )
return $rc ;
/* Force to retransmit ZDATA */
$frame = self :: ZCRCW ;
break ;
/* OK! */
case self :: ZRINIT :
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
/* ACK for data -- it lost! */
case self :: ZACK :
2021-08-17 13:49:39 +00:00
Log :: debug ( sprintf ( '%s: - ls_zsendfile ZACK after EOF' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
$this -> ls_txLastACK = $this -> ls_fetchlong ( $this -> ls_rxHdr );
break ;
/* Abort transfer */
case self :: ZABORT :
/* Strange? Ok, abort too */
case self :: ZFIN :
/* Abort too */
case self :: ZCAN :
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
/* Ok, here is no header */
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-04-01 10:59:15 +00:00
$trys ++ ;
break ;
default :
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s: ? ls_zsendfile Something strange after ZEOF [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
$trys ++ ;
}
} while ( $send -> feof () && $trys < 10 );
if ( $send -> feof ()) {
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:! ls_zsendfile To many tries waiting for ZEOF ACK' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
2023-07-02 13:40:08 +00:00
return self :: ERROR ;
2021-04-01 10:59:15 +00:00
}
}
}
2021-08-17 13:49:39 +00:00
Log :: error ( sprintf ( '%s:? ls_zsendfile Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
/**
* Send file information to peer , get start position from them .
* Return packet type -- ZRPOS , ZSKIP , ZFERR , ZABORT or ZFIN ( may be any error , too )
*
* @ param int $sernum
* @ param int $pos
* @ return int
*/
private function ls_zsendfinfo ( Send $send , int $sernum , int $pos , int $fileleft , int $bytesleft ) : int
{
2023-07-18 13:00:03 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zsendfinfo [%s]' , self :: LOGKEY , $send -> nameas ));
2021-04-01 10:59:15 +00:00
$trys = 0 ;
$retransmit = 1 ;
$crc = self :: LSZ_INIT_CRC32 ;
$buf = '' ;
2023-07-08 07:36:41 +00:00
$this -> client -> tx_purge ();
2023-07-17 06:36:53 +00:00
$buf = $send -> nameas . chr ( 0 );
2021-04-01 10:59:15 +00:00
$buf .= sprintf ( '%ld %lo %o %o %ld %ld' ,
$send -> size ,
$send -> mtime ,
0 ,
$sernum ,
$fileleft ,
$bytesleft
);
do {
if ( $retransmit ) {
$txHdr = [];
$txHdr [ self :: LSZ_F0 ] = ( self :: LSZ_CONVBIN | self :: LSZ_CONVRECOV );
$txHdr [ self :: LSZ_F1 ] = 0 ; /* No managment */
$txHdr [ self :: LSZ_F2 ] = 0 ; /* No compression/encryption */
$txHdr [ self :: LSZ_F3 ] = 0 ; /* No sparse files or variable headers */
if (( $rc = $this -> ls_zsendbhdr ( self :: ZFILE , $txHdr )) < 0 )
return $rc ;
if (( $rc = $this -> ls_zsenddata ( $buf , self :: ZCRCW )) < 0 )
return $rc ;
$retransmit = 0 ;
$trys ++ ;
}
2021-07-21 10:52:17 +00:00
switch ( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout )) {
2021-04-01 10:59:15 +00:00
/* Ok, he want our file */
case self :: ZRPOS :
$pos = $this -> ls_fetchlong ( $this -> ls_rxHdr );
return self :: ZRPOS ;
/* Skip */
case self :: ZSKIP :
/* Refuse */
case self :: ZFERR :
/* Check for double-skip protection */
$sn = $this -> ls_fetchlong ( $this -> ls_rxHdr );
/* Here is skip protection */
2023-06-27 07:39:11 +00:00
if ( $this -> ls_SkipGuard && $sn && ( $sn === $sernum - 1 )) {
2021-04-01 10:59:15 +00:00
if (( $rc = $this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
/* We don't need to skip this file */
break ;
} elseif ( $sn != $sernum ) {
$this -> ls_SkipGuard = 0 ;
}
/* Fall through */
/* Abort this session */
case self :: ZABORT :
/* Finish this session */
case self :: ZFIN :
return $rc ;
/* Send CRC to peer */
case self :: ZCRC :
$len = $this -> ls_fetchlong ( $this -> ls_rxHdr );
if ( ! $len )
$len = $send -> size ;
$cnt = 0 ;
$send -> seek ( 0 );
while (( $cnt ++ < $len ) && (( $c = $send -> read ( 1 )) > 0 ))
$crc = $this -> CRC32_UPDATE ( $c , $crc );
$crc = $this -> CRC32_FINISH ( $crc );
if (( $rc = $this -> ls_zsendhhdr ( self :: ZCRC , $this -> ls_storelong ( $crc ))) < 0 )
return $rc ;
break ;
case self :: ZRINIT :
break ;
case self :: ZNAK :
2023-07-02 13:40:08 +00:00
case self :: TIMEOUT :
2021-04-01 10:59:15 +00:00
$retransmit = 1 ;
break ;
case self :: LSZ_BADCRC :
if (( $rc = $this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
break ;
default :
2021-08-18 12:11:16 +00:00
Log :: error ( sprintf ( '%s: ? Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
}
} while ( $trys < 10 );
2021-08-18 12:11:16 +00:00
Log :: error ( sprintf ( '%s:? Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
/* Send HEX header. Use CRC16, send var. len. if could */
private function ls_zsendhhdr ( int $frametype , array $hdr ) : int
{
2023-07-19 00:27:47 +00:00
if ( static :: DEBUG )
2021-08-18 12:11:16 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zsendhhdr' , self :: LOGKEY ));
2021-04-01 10:59:15 +00:00
/* Send **<DLE> */
$this -> client -> buffer_add ( chr ( self :: ZPAD ) . chr ( self :: ZPAD ) . chr ( self :: ZDLE ));
/* Send header type */
if ( $this -> ls_Protocol & self :: LSZ_OPTVHDR ) {
$this -> client -> buffer_add ( chr ( self :: ZVHEX ));
$this -> ls_sendhex ( count ( $hdr ));
} else {
$this -> client -> buffer_add ( chr ( self :: ZHEX ));
}
$this -> ls_sendhex ( $frametype );
/* Send whole header */
for ( $n = 0 ; $n < count ( $hdr ); $n ++ ) {
$this -> ls_sendhex ( $hdr [ $n ]);
}
2021-07-21 10:52:17 +00:00
$crc = crc16 ( chr ( $frametype ) . join ( '' , array_map ( function ( $item ) { return chr ( $item ); }, $hdr )));
2021-04-01 10:59:15 +00:00
$this -> ls_sendhex ( $crc >> 8 );
$this -> ls_sendhex ( $crc & 0xff );
$this -> client -> buffer_add ( chr ( self :: CR ));
$this -> client -> buffer_add ( chr ( self :: LF | 0x80 ));
if ( $frametype != self :: ZACK && $frametype != self :: ZFIN )
$this -> client -> buffer_add ( chr ( self :: XON ));
/* Clean buffer, do real send */
return $this -> client -> buffer_flush ( $this -> ls_HeaderTimeout );
}
/**
* Send ZSINIT and wait for ZACK , skip ZRINIT , ZCOMMAND , answer on ZCHALLENGE
*
* @ param string $attstr
* @ return int
* @ throws \Exception
*/
private function ls_zsendsinit ( string $attstr ) : int
{
2021-08-18 12:11:16 +00:00
Log :: debug ( sprintf ( '%s:+ ls_zsendsinit [%s]' , self :: LOGKEY , $attrstr ));
2021-04-01 10:59:15 +00:00
$trys = 0 ;
$retransmit = 1 ;
if ( $attstr ) {
if ( strlen ( $attstr ) > self :: LSZ_MAXATTNLEN - 1 )
$attstr = substr ( $attrstr , LSZ_MAXATTNLEN );
$this -> txbuf = $attrstr ;
} else {
$this -> txbuf = '' ;
}
do {
if ( retransmit ) {
/* We don't support ESC8, so don't ask for it in any case */
$txHdr = [];
$txHdr [ self :: LSZ_F0 ] = ( $this -> ls_Protocol & self :: LSZ_OPTESCAPEALL ) ? self :: LSZ_TXWNTESCCTL : 0 ;
$txHdr [ self :: LSZ_F1 ] = $txHdr [ self :: LSZ_F2 ] = $txHdr [ self :: LSZ_F3 ] = 0 ;
if (( $rc = $this -> ls_zsendbhdr ( self :: ZSINIT , $txHdr )) < 0 )
return $rc ;
if ( $rc = $this -> ls_zsenddata ( $this -> txbuf , self :: ZCRCW ))
return $rc ;
$retransmit = 0 ;
$trys ++ ;
}
switch (( $rc = $this -> ls_zrecvhdr ( $this -> ls_rxHdr , $this -> ls_HeaderTimeout ))) {
/* Skip it */
case self :: ZRINIT :
break ;
/* Ok */
case self :: ZACK :
2023-07-02 13:40:08 +00:00
return self :: OK ;
2021-04-01 10:59:15 +00:00
/* Return number to peer, he is paranoid */
case self :: ZCHALLENGE :
if (( $rc = $this -> ls_zsendhhdr ( self :: ZACK , $this -> ls_rxHdr )) < 0 )
return $rc ;
break ;
case self :: LSZ_BADCRC :
case self :: ZCOMMAND :
if (( $rc = $this -> ls_zsendhhdr ( self :: ZNAK , $this -> ls_storelong ( 0 ))) < 0 )
return $rc ;
break ;
/* Retransmit */
case ZNAK :
2023-07-02 13:40:08 +00:00
case TIMEOUT :
2021-04-01 10:59:15 +00:00
$retransmit = 1 ;
break ;
default :
2021-08-18 12:11:16 +00:00
Log :: error ( sprintf ( '%s: ? Something strange [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
if ( $rc < 0 )
return $rc ;
}
} while ( $trys < 10 );
2021-08-18 12:11:16 +00:00
Log :: error ( sprintf ( '%s:? Something strange or timeout [%d]' , self :: LOGKEY , $rc ));
2021-04-01 10:59:15 +00:00
return $rc ;
}
}