Add Zmodem/BINKP/EMSI
This commit is contained in:
parent
619cabb751
commit
b94e39c7af
@ -5,6 +5,7 @@ namespace App\Classes;
|
|||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
use App\Exceptions\InvalidFidoPacketException;
|
use App\Exceptions\InvalidFidoPacketException;
|
||||||
|
use App\Traits\GetNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class FTNMessage
|
* Class FTNMessage
|
||||||
@ -14,6 +15,8 @@ use App\Exceptions\InvalidFidoPacketException;
|
|||||||
*/
|
*/
|
||||||
class FTNMessage extends FTN
|
class FTNMessage extends FTN
|
||||||
{
|
{
|
||||||
|
use GetNode;
|
||||||
|
|
||||||
private $src = NULL; // SRC N/F from packet
|
private $src = NULL; // SRC N/F from packet
|
||||||
private $dst = NULL; // DST N/F from packet
|
private $dst = NULL; // DST N/F from packet
|
||||||
|
|
||||||
@ -106,6 +109,7 @@ class FTNMessage extends FTN
|
|||||||
case 'fp': return ftn_address_split($this->_fqfa,'p');
|
case 'fp': return ftn_address_split($this->_fqfa,'p');
|
||||||
|
|
||||||
case 'fqfa': return $this->_fqfa;
|
case 'fqfa': return $this->_fqfa;
|
||||||
|
case 'fqda': return $this->_fqda;
|
||||||
|
|
||||||
// Echomails dont have a fully qualified from address
|
// Echomails dont have a fully qualified from address
|
||||||
case 'tz': return ftn_address_split($this->_fqda,'z');
|
case 'tz': return ftn_address_split($this->_fqda,'z');
|
||||||
@ -136,7 +140,7 @@ class FTNMessage extends FTN
|
|||||||
{
|
{
|
||||||
case 'fqfa':
|
case 'fqfa':
|
||||||
case 'fqda':
|
case 'fqda':
|
||||||
$this->{'_'.$k} = $v;
|
$this->{'_'.$k} = $this->get_node(ftn_address_split($v),TRUE);
|
||||||
|
|
||||||
if ($this->_fqfa AND $this->_fqda)
|
if ($this->_fqfa AND $this->_fqda)
|
||||||
$this->intl = sprintf('%s %s',$this->_fqda,$this->_fqfa);
|
$this->intl = sprintf('%s %s',$this->_fqda,$this->_fqfa);
|
||||||
@ -178,6 +182,9 @@ class FTNMessage extends FTN
|
|||||||
$return .= $this->from."\00";
|
$return .= $this->from."\00";
|
||||||
$return .= $this->subject."\00";
|
$return .= $this->subject."\00";
|
||||||
|
|
||||||
|
if ($this->type == 'echomail')
|
||||||
|
$return .= "AREA:".$this->echoarea."\r";
|
||||||
|
|
||||||
// Add some kludges
|
// Add some kludges
|
||||||
$return .= "\01MSGID ".$this->_fqfa." 1"."\r";
|
$return .= "\01MSGID ".$this->_fqfa." 1"."\r";
|
||||||
|
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Classes;
|
namespace App\Classes;
|
||||||
|
|
||||||
use App\Exceptions\InvalidFidoPacketException;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
use App\Exceptions\InvalidFidoPacketException;
|
||||||
|
use App\Traits\GetNode;
|
||||||
|
|
||||||
class FTNPacket extends FTN
|
class FTNPacket extends FTN
|
||||||
{
|
{
|
||||||
|
use GetNode;
|
||||||
|
|
||||||
public $pktsrc = NULL;
|
public $pktsrc = NULL;
|
||||||
public $pktdst = NULL;
|
public $pktdst = NULL;
|
||||||
private $pktver = NULL;
|
private $pktver = NULL;
|
||||||
@ -92,12 +96,18 @@ class FTNPacket extends FTN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @note - messages in this object have the same next destination
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
// @todo - is this appropriate to set here
|
// @todo - is this appropriate to set here
|
||||||
$this->date = now();
|
$this->date = now();
|
||||||
$this->pktsrc = '10:1/5.0';
|
$this->pktsrc = (string)$this->get_node(ftn_address_split('10:1/5.0'),TRUE);
|
||||||
$this->pktdst = '10:1/0.0';
|
|
||||||
|
// @todo
|
||||||
|
if ($this->messages->first()->type == 'echomail')
|
||||||
|
$this->pktdst = (string)$this->messages->first()->fqfa->uplink;
|
||||||
|
else
|
||||||
|
$this->pktdst = (string)$this->messages->first()->fqda->uplink;
|
||||||
|
|
||||||
$this->software['prodcode-lo'] = 0x00;
|
$this->software['prodcode-lo'] = 0x00;
|
||||||
$this->software['prodcode-hi'] = 0xde;
|
$this->software['prodcode-hi'] = 0xde;
|
||||||
@ -165,6 +175,7 @@ class FTNPacket extends FTN
|
|||||||
|
|
||||||
public function addMessage(FTNMessage $o)
|
public function addMessage(FTNMessage $o)
|
||||||
{
|
{
|
||||||
|
// @todo Check that this message is for the same uplink.
|
||||||
$this->messages->push($o);
|
$this->messages->push($o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
app/Classes/File/Item.php
Normal file
135
app/Classes/File/Item.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\File;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||||
|
use League\Flysystem\UnreadableFileException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A file we are sending or receiving
|
||||||
|
*
|
||||||
|
* @property string $name
|
||||||
|
* @property string $recvas
|
||||||
|
* @property int $size
|
||||||
|
*/
|
||||||
|
class Item
|
||||||
|
{
|
||||||
|
protected const IS_PKT = (1<<1);
|
||||||
|
protected const IS_ARC = (1<<2);
|
||||||
|
protected const IS_FILE = (1<<3);
|
||||||
|
protected const IS_FLO = (1<<4);
|
||||||
|
protected const IS_REQ = (1<<5);
|
||||||
|
|
||||||
|
protected const I_RECV = (1<<6);
|
||||||
|
protected const I_SEND = (1<<7);
|
||||||
|
|
||||||
|
protected string $file_name = '';
|
||||||
|
protected int $file_size = 0;
|
||||||
|
protected int $file_mtime = 0;
|
||||||
|
protected int $file_type = 0;
|
||||||
|
protected int $action = 0;
|
||||||
|
|
||||||
|
public bool $sent = FALSE;
|
||||||
|
public bool $received = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
* @throws UnreadableFileException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __construct($file,int $action)
|
||||||
|
{
|
||||||
|
$this->action |= $action;
|
||||||
|
|
||||||
|
switch ($action) {
|
||||||
|
case self::I_SEND:
|
||||||
|
if (! is_string($file))
|
||||||
|
throw new Exception('Invalid object creation - file should be a string');
|
||||||
|
|
||||||
|
if (! file_exists($file))
|
||||||
|
throw new FileNotFoundException('Item doesnt exist: '.$file);
|
||||||
|
|
||||||
|
if (! is_readable($file))
|
||||||
|
throw new UnreadableFileException('Item cannot be read: '.$file);
|
||||||
|
|
||||||
|
$this->file_name = $file;
|
||||||
|
$x = stat($file);
|
||||||
|
$this->file_size = $x['size'];
|
||||||
|
$this->file_mtime = $x['mtime'];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::I_RECV;
|
||||||
|
$keys = ['name','mtime','size'];
|
||||||
|
|
||||||
|
if (! is_array($file) || array_diff(array_keys($file),$keys))
|
||||||
|
throw new Exception('Invalid object creation - file is not a valid array :'.serialize(array_diff(array_keys($file),$keys)));
|
||||||
|
|
||||||
|
$this->file_name = $file['name'];
|
||||||
|
$this->file_size = $file['size'];
|
||||||
|
$this->file_mtime = $file['mtime'];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown action: '.$action);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->file_type |= $this->whatType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch($key) {
|
||||||
|
case 'mtime':
|
||||||
|
case 'name':
|
||||||
|
case 'size':
|
||||||
|
if ($this->action & self::I_RECV)
|
||||||
|
return $this->{'file_'.$key};
|
||||||
|
|
||||||
|
throw new Exception('Invalid request for key: '.$key);
|
||||||
|
|
||||||
|
case 'recvas':
|
||||||
|
return '/tmp/'.$this->file_name; // @todo this should be inbound temp
|
||||||
|
|
||||||
|
case 'sendas':
|
||||||
|
return basename($this->file_name);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function isType(int $type): bool
|
||||||
|
{
|
||||||
|
return $this->file_type & $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function whatType(): int
|
||||||
|
{
|
||||||
|
static $ext = ['su','mo','tu','we','th','fr','sa','req'];
|
||||||
|
|
||||||
|
$x = strrchr($this->file_name,'.');
|
||||||
|
if (! $x || (strlen(substr($x,1)) != 3))
|
||||||
|
return self::IS_FILE;
|
||||||
|
|
||||||
|
if (strcasecmp(substr($x,2),'lo') == 0)
|
||||||
|
return self::IS_FLO;
|
||||||
|
|
||||||
|
if (strcasecmp(substr($x,1),'pkt') == 0)
|
||||||
|
return self::IS_PKT;
|
||||||
|
|
||||||
|
if (strcasecmp(substr($x,1),'req') == 0)
|
||||||
|
return self::IS_REQ;
|
||||||
|
|
||||||
|
for ($i=0;$i<count($ext);$i++)
|
||||||
|
if (! strncasecmp($x,'.'.$ext[$i],strlen($ext[$i])) && (preg_match('/^[0-9a-z]/',strtolower(substr($x,3,1)))))
|
||||||
|
return self::IS_ARC;
|
||||||
|
|
||||||
|
return self::IS_FILE;
|
||||||
|
}
|
||||||
|
}
|
173
app/Classes/File/Receive.php
Normal file
173
app/Classes/File/Receive.php
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\File;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing the files we are receiving
|
||||||
|
*
|
||||||
|
* @property-read resource $fd
|
||||||
|
* @property-read int total_recv
|
||||||
|
* @property-read int total_recv_bytes
|
||||||
|
*/
|
||||||
|
final class Receive extends Item
|
||||||
|
{
|
||||||
|
private Collection $list;
|
||||||
|
private ?Item $receiving;
|
||||||
|
|
||||||
|
private mixed $f; // File descriptor
|
||||||
|
private int $start; // Time we started receiving
|
||||||
|
private int $file_pos; // Current write pointer
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Initialise our variables
|
||||||
|
$this->list = collect();
|
||||||
|
$this->receiving = NULL;
|
||||||
|
$this->file_pos = 0;
|
||||||
|
$this->f = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'fd':
|
||||||
|
return is_resource($this->f);
|
||||||
|
|
||||||
|
case 'filepos':
|
||||||
|
return $this->file_pos;
|
||||||
|
|
||||||
|
case 'mtime':
|
||||||
|
case 'name':
|
||||||
|
case 'size':
|
||||||
|
return $this->receiving ? $this->receiving->{'file_'.$key} : NULL;
|
||||||
|
|
||||||
|
case 'to_get':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_RECV) && $item->received === FALSE; })
|
||||||
|
->count();
|
||||||
|
|
||||||
|
case 'total_recv':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_RECV) && $item->received === TRUE; })
|
||||||
|
->count();
|
||||||
|
|
||||||
|
case 'total_recv_bytes':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_RECV) && $item->received === TRUE; })
|
||||||
|
->sum(function($item) { return $item->file_size; });
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the file descriptor for our incoming file
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function close(): void
|
||||||
|
{
|
||||||
|
if (! $this->f)
|
||||||
|
throw new Exception('No file to close');
|
||||||
|
|
||||||
|
if (! $this->file_pos != $this->receiving->file_size)
|
||||||
|
Log::warning(sprintf('%s: - Closing [%s], but missing [%d] bytes',__METHOD__,$this->receiving->file_name,$this->receiving->file_size-$this->file_pos));
|
||||||
|
|
||||||
|
$this->receiving->received = TRUE;
|
||||||
|
|
||||||
|
$end = time()-$this->start;
|
||||||
|
Log::debug(sprintf('%s: - Closing [%s], received in [%d]',__METHOD__,$this->receiving->file_name,$end));
|
||||||
|
|
||||||
|
fclose($this->f);
|
||||||
|
$this->file_pos = 0;
|
||||||
|
$this->receiving = NULL;
|
||||||
|
$this->f = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the file descriptor to receive a file
|
||||||
|
*
|
||||||
|
* @param bool $check
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function open(bool $check=FALSE): bool
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start [%d]',__METHOD__,$check));
|
||||||
|
|
||||||
|
// Check we can open this file
|
||||||
|
// @todo
|
||||||
|
// @todo implement return 2 - SKIP file
|
||||||
|
// @todo implement return 4 - SUSPEND(?) file
|
||||||
|
if ($check) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $this->receiving)
|
||||||
|
throw new Exception('No files currently receiving');
|
||||||
|
|
||||||
|
$this->file_pos = 0;
|
||||||
|
$this->start = time();
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: - Opening [%s]',__METHOD__,$this->receiving->recvas));
|
||||||
|
$this->f = fopen($this->receiving->recvas,'wb');
|
||||||
|
if (! $this->f) {
|
||||||
|
Log::error(sprintf('%s: ! Unable to open file [%s] for writing',__METHOD__,$this->receiving->file_name));
|
||||||
|
return 3; // @todo change to const
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info(sprintf('%s: = End - File [%s] opened for writing',__METHOD__,$this->receiving->file_name));
|
||||||
|
return 0; // @todo change to const
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new file to receive
|
||||||
|
*
|
||||||
|
* @param array $file
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function new(array $file): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start',__METHOD__),['file'=>$file]);
|
||||||
|
|
||||||
|
if ($this->receiving)
|
||||||
|
throw new Exception('Can only have 1 file receiving at a time');
|
||||||
|
|
||||||
|
$o = new Item($file,self::I_RECV);
|
||||||
|
$this->list->push($o);
|
||||||
|
|
||||||
|
$this->receiving = $o;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the file we are receiving
|
||||||
|
*
|
||||||
|
* @param string $buf
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function write(string $buf): int
|
||||||
|
{
|
||||||
|
if (! $this->f)
|
||||||
|
throw new Exception('No file open for read');
|
||||||
|
|
||||||
|
if ($this->file_pos+strlen($buf) > $this->receiving->file_size)
|
||||||
|
throw new Exception(sprintf('Too many bytes received [%d] (%d)?',$this->file_pos+strlen($buf),$this->receiving->file_size));
|
||||||
|
|
||||||
|
$rc = fwrite($this->f,$buf);
|
||||||
|
|
||||||
|
if ($rc === FALSE)
|
||||||
|
throw new FileException('Error while writing to file');
|
||||||
|
|
||||||
|
$this->file_pos += $rc;
|
||||||
|
Log::debug(sprintf('%s: - Write [%d] bytes, file pos now [%d] of [%d]',__METHOD__,$rc,$this->file_pos,$this->receiving->file_size));
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
}
|
235
app/Classes/File/Send.php
Normal file
235
app/Classes/File/Send.php
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\File;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use League\Flysystem\UnreadableFileException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing the files we are sending
|
||||||
|
*
|
||||||
|
* @property-read resource fd
|
||||||
|
* @property-read int file_mtime
|
||||||
|
* @property-read int file_size
|
||||||
|
* @property-read string file_name
|
||||||
|
* @property-read int mail_size
|
||||||
|
* @property-read int total_count
|
||||||
|
* @property-read int total_sent
|
||||||
|
* @property-read int total_sent_bytes
|
||||||
|
*/
|
||||||
|
final class Send extends Item
|
||||||
|
{
|
||||||
|
private Collection $list;
|
||||||
|
private ?Item $sending;
|
||||||
|
|
||||||
|
private mixed $f; // File descriptor
|
||||||
|
private int $start; // Time we started sending
|
||||||
|
private int $file_pos; // Current read pointer
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Initialise our variables
|
||||||
|
$this->list = collect();
|
||||||
|
$this->sending = NULL;
|
||||||
|
$this->file_pos = 0;
|
||||||
|
$this->f = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'fd':
|
||||||
|
return is_resource($this->f);
|
||||||
|
|
||||||
|
case 'file_count':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return $item->isType(self::IS_FILE); })
|
||||||
|
->count();
|
||||||
|
|
||||||
|
case 'file_size':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return $item->isType(self::IS_FILE); })
|
||||||
|
->sum(function($item) { return $item->file_size; });
|
||||||
|
|
||||||
|
case 'filepos':
|
||||||
|
return $this->file_pos;
|
||||||
|
|
||||||
|
case 'mail_count':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return $item->isType(self::IS_ARC|self::IS_PKT); })
|
||||||
|
->count();
|
||||||
|
|
||||||
|
case 'mail_size':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return $item->isType(self::IS_ARC|self::IS_PKT); })
|
||||||
|
->sum(function($item) { return $item->file_size; });
|
||||||
|
|
||||||
|
case 'sendas':
|
||||||
|
return $this->sending ? $this->sending->{$key} : NULL;
|
||||||
|
|
||||||
|
case 'name':
|
||||||
|
case 'mtime':
|
||||||
|
case 'size':
|
||||||
|
return $this->sending ? $this->sending->{'file_'.$key} : NULL;
|
||||||
|
|
||||||
|
case 'total_sent':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_SEND) && $item->sent === TRUE; })
|
||||||
|
->count();
|
||||||
|
|
||||||
|
case 'total_sent_bytes':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_SEND) && $item->sent === TRUE; })
|
||||||
|
->sum(function($item) { return $item->file_size; });
|
||||||
|
|
||||||
|
case 'total_count':
|
||||||
|
return $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_SEND) && $item->sent === FALSE; })
|
||||||
|
->count();
|
||||||
|
|
||||||
|
case 'total_size':
|
||||||
|
return $this->list
|
||||||
|
->sum(function($item) { return $item->file_size; });
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a file to the list of files to send
|
||||||
|
*
|
||||||
|
* @param string $file
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function add(string $file): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start [%s]',__METHOD__,$file));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->list->push(new Item($file,self::I_SEND));
|
||||||
|
|
||||||
|
} catch (FileNotFoundException) {
|
||||||
|
Log::error(sprintf('%s: ! Item [%s] doesnt exist',__METHOD__,$file));
|
||||||
|
return;
|
||||||
|
|
||||||
|
} catch (UnreadableFileException) {
|
||||||
|
Log::error(sprintf('%s: ! Item [%s] cannot be read',__METHOD__,$file));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Uncaught, rethrow the error
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new Exception($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the file descriptor of the file we are sending
|
||||||
|
*
|
||||||
|
* @param bool $successful
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function close(bool $successful): void
|
||||||
|
{
|
||||||
|
if (! $this->f)
|
||||||
|
throw new Exception('No file to close');
|
||||||
|
|
||||||
|
if ($successful) {
|
||||||
|
$this->sending->sent = TRUE;
|
||||||
|
$end = time()-$this->start;
|
||||||
|
Log::debug(sprintf('%s: - Closing [%s], sent in [%d]',__METHOD__,$this->sending->file_name,$end));
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($this->f);
|
||||||
|
$this->sending = NULL;
|
||||||
|
$this->file_pos = 0;
|
||||||
|
$this->f = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if we are at the end of the file
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function feof(): bool
|
||||||
|
{
|
||||||
|
return feof($this->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a file for sending
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function open(): bool
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start',__METHOD__));
|
||||||
|
|
||||||
|
$this->sending = $this->list
|
||||||
|
->filter(function($item) { return ($item->action & self::I_SEND) && $item->sent === FALSE; })
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $this->sending)
|
||||||
|
throw new Exception('No files to open');
|
||||||
|
|
||||||
|
$this->file_pos = 0;
|
||||||
|
$this->start = time();
|
||||||
|
|
||||||
|
$this->f = fopen($this->sending->file_name,'rb');
|
||||||
|
if (! $this->f) {
|
||||||
|
Log::error(sprintf('%s: ! Unable to open file [%s] for reading',__METHOD__,$this->sending->file_name));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info(sprintf('%s: = End - File [%s] opened with size [%d]',__METHOD__,$this->sending->file_name,$this->sending->file_size));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read bytes of the sending file
|
||||||
|
*
|
||||||
|
* @param int $length
|
||||||
|
* @return string|null
|
||||||
|
* @throws UnreadableFileException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function read(int $length): ?string
|
||||||
|
{
|
||||||
|
if (! $this->f)
|
||||||
|
throw new Exception('No file open for read');
|
||||||
|
|
||||||
|
$data = fread($this->f,$length);
|
||||||
|
$this->file_pos += strlen($data);
|
||||||
|
Log::debug(sprintf('%s: - Read [%d] bytes, file pos now [%d]',__METHOD__,strlen($data),$this->file_pos));
|
||||||
|
|
||||||
|
if ($data === FALSE)
|
||||||
|
throw new UnreadableFileException('Error reading file: '.$this->sending->file_name);
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek to a specific position of our file
|
||||||
|
*
|
||||||
|
* @param int $pos
|
||||||
|
* @return bool
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function seek(int $pos): bool
|
||||||
|
{
|
||||||
|
if (! $this->f)
|
||||||
|
throw new Exception('No file open for seek');
|
||||||
|
|
||||||
|
$rc = (fseek($this->f,$pos,SEEK_SET) === 0);
|
||||||
|
if ($rc)
|
||||||
|
$this->file_pos = $pos;
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: - Seeked to [%d]',__METHOD__,$this->file_pos));
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
}
|
20
app/Classes/FileReceive.php
Normal file
20
app/Classes/FileReceive.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FileReceive
|
||||||
|
* @package App\Classes
|
||||||
|
*
|
||||||
|
* This class represents a file being received.
|
||||||
|
*/
|
||||||
|
class FileReceive {
|
||||||
|
public int $soff = 0;
|
||||||
|
public int $toff = 0;
|
||||||
|
public int $foff = 0;
|
||||||
|
public int $ttot = 0;
|
||||||
|
public int $stot = 0;
|
||||||
|
public int $nf = 0;
|
||||||
|
public int $allf = 0;
|
||||||
|
public int $start = 0;
|
||||||
|
}
|
21
app/Classes/FileSend.php
Normal file
21
app/Classes/FileSend.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FileSend
|
||||||
|
* @package App\Classes
|
||||||
|
*
|
||||||
|
* This class represents a file being sent.
|
||||||
|
*/
|
||||||
|
class FileSend {
|
||||||
|
public int $soff = 0;
|
||||||
|
public int $stot = 0;
|
||||||
|
public int $toff = 0;
|
||||||
|
public int $foff = 0;
|
||||||
|
public int $ttot = 0;
|
||||||
|
public int $nf = 0;
|
||||||
|
public int $allf = 0;
|
||||||
|
public int $cps = 1;
|
||||||
|
public int $start = 0;
|
||||||
|
}
|
260
app/Classes/Node.php
Normal file
260
app/Classes/Node.php
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\Node as NodeModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object representing the node we are communicating with
|
||||||
|
*
|
||||||
|
* @property int aka_authed
|
||||||
|
* @property int aka_num
|
||||||
|
* @property string ftn
|
||||||
|
* @property string password
|
||||||
|
* @property Carbon node_time
|
||||||
|
* @property int session_time
|
||||||
|
* @property int ver_major
|
||||||
|
* @property int ver_minor
|
||||||
|
*/
|
||||||
|
class Node
|
||||||
|
{
|
||||||
|
// Remote Version
|
||||||
|
private int $version_major = 0;
|
||||||
|
private int $version_minor = 0;
|
||||||
|
|
||||||
|
private Carbon $start_time; // The time our connection started
|
||||||
|
// @todo Change this to Carbon
|
||||||
|
private string $node_time; // Current node's time
|
||||||
|
|
||||||
|
private Collection $ftns; // The FTNs of the remote system
|
||||||
|
private Collection $ftns_authed; // The FTNs we have validated
|
||||||
|
|
||||||
|
private int $options; // This nodes capabilities/options
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->options = 0;
|
||||||
|
$this->start_time = Carbon::now();
|
||||||
|
$this->ftns = collect();
|
||||||
|
$this->ftns_authed = collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
// Number of AKAs the remote has
|
||||||
|
case 'aka_num':
|
||||||
|
return $this->ftns->count();
|
||||||
|
|
||||||
|
// Number of AKAs we have validated
|
||||||
|
case 'aka_authed':
|
||||||
|
return $this->ftns_authed->count();
|
||||||
|
|
||||||
|
case 'ftn':
|
||||||
|
return ($x=$this->ftns->first()) ? $x->ftn : 'Unknown';
|
||||||
|
|
||||||
|
// The nodes password
|
||||||
|
case 'password':
|
||||||
|
return ($this->ftns_authed->count() && $x=$this->ftns_authed->first()->sespass) ? $x : '-';
|
||||||
|
|
||||||
|
// Return how long our session has been connected
|
||||||
|
case 'session_time':
|
||||||
|
return Carbon::now()->diffInSeconds($this->start_time);
|
||||||
|
|
||||||
|
case 'system':
|
||||||
|
case 'sysop':
|
||||||
|
case 'location':
|
||||||
|
case 'phone':
|
||||||
|
case 'flags':
|
||||||
|
case 'message':
|
||||||
|
case 'files':
|
||||||
|
case 'netmail':
|
||||||
|
// The current session speed
|
||||||
|
case 'speed':
|
||||||
|
// The time our session started.
|
||||||
|
case 'start_time':
|
||||||
|
case 'software':
|
||||||
|
return $this->{$key};
|
||||||
|
// Client version
|
||||||
|
case 'ver_major':
|
||||||
|
return $this->version_major;
|
||||||
|
case 'ver_minor':
|
||||||
|
return $this->version_minor;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __set($key,$value)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'ftn':
|
||||||
|
if (! is_object($value) OR ! $value instanceof NodeModel)
|
||||||
|
throw new Exception('Not a node object: '.(is_object($value) ? get_class($value) : serialize($value)));
|
||||||
|
|
||||||
|
// Ignore any duplicate FTNs that we get
|
||||||
|
if ($this->ftns->search(function($item) use ($value) { return $item->id === $value->id; }) !== FALSE) {
|
||||||
|
Log::debug(sprintf('%s: - Ignoring Duplicate FTN [%s]',__METHOD__,$value->ftn));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->ftns->push($value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'system':
|
||||||
|
case 'sysop':
|
||||||
|
case 'location':
|
||||||
|
case 'phone':
|
||||||
|
case 'flags':
|
||||||
|
case 'message':
|
||||||
|
case 'files':
|
||||||
|
case 'netmail':
|
||||||
|
case 'software':
|
||||||
|
case 'speed':
|
||||||
|
case 'start_time':
|
||||||
|
case 'node_time':
|
||||||
|
|
||||||
|
case 'ver_major':
|
||||||
|
case 'ver_minor':
|
||||||
|
$this->{$key} = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown variable: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticate the AKAs that the node provided
|
||||||
|
*
|
||||||
|
* @param string $password
|
||||||
|
* @param string $challenge
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function auth(string $password,string $challenge=''): int
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start [%s]',__METHOD__,$password));
|
||||||
|
|
||||||
|
// Make sure we havent been here already
|
||||||
|
if ($this->ftns_authed->count())
|
||||||
|
throw new Exception('Already authed');
|
||||||
|
|
||||||
|
foreach ($this->ftns as $o) {
|
||||||
|
if (! $o->sespass)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If we have challenge, then we are doing MD5
|
||||||
|
$exp_pwd = $challenge ? $this->md5_challenge($o->sespass,$challenge) : $o->sespass;
|
||||||
|
|
||||||
|
if ($exp_pwd === $password)
|
||||||
|
$this->ftns_authed->push($o);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: = End [%d]',__METHOD__,$this->ftns_authed->count()));
|
||||||
|
return $this->ftns_authed->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we originate a connection and need to send our MD5 Challenge response
|
||||||
|
*
|
||||||
|
* @param string $challenge
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function get_md5chal(string $challenge): string
|
||||||
|
{
|
||||||
|
return $this->md5_challenge($this->password,$challenge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the remotes BINKP version as a int
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function get_versionint(): int
|
||||||
|
{
|
||||||
|
return $this->ver_major*100+$this->ver_minor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the response to an MD5 challenge, using the nodes password
|
||||||
|
*
|
||||||
|
* @param $pwd
|
||||||
|
* @param $challenge
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function md5_challenge($pwd,$challenge): string
|
||||||
|
{
|
||||||
|
$x = $pwd.str_repeat(chr(0x00),64-strlen($pwd));
|
||||||
|
|
||||||
|
return md5($this->str_xor($x,0x5c).md5($this->str_xor($x,0x36).$challenge,true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we originate a call to a node, we need to store the node we are connecting with in the ftns_authed, so
|
||||||
|
* authentication proceeds when we send our M_pwd
|
||||||
|
*
|
||||||
|
* @param NodeModel $o
|
||||||
|
*/
|
||||||
|
public function originate(NodeModel $o): void
|
||||||
|
{
|
||||||
|
$this->ftns->push($o);
|
||||||
|
$this->ftns_authed->push($o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that our received FTNs match who we called
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function originate_check(): bool
|
||||||
|
{
|
||||||
|
if ($this->ftns_authed->count() !== 1 || ! $this->ftns->count())
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
$ftn = $this->ftns_authed->first()->ftn;
|
||||||
|
|
||||||
|
return $this->ftns->search(function($item) use ($ftn) {
|
||||||
|
return $item->ftn == $ftn;
|
||||||
|
}) !== FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function optionClear(int $key): void
|
||||||
|
{
|
||||||
|
$this->options &= ~$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function optionGet(int $key): int
|
||||||
|
{
|
||||||
|
return ($this->options & $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function optionSet(int $key): void
|
||||||
|
{
|
||||||
|
$this->options |= $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function str_xor(string $string,int $val): string
|
||||||
|
{
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
for ($i=0;$i<strlen($string);$i++)
|
||||||
|
$result .= chr(ord($string[$i]) ^ $val);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
337
app/Classes/Protocol.php
Normal file
337
app/Classes/Protocol.php
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\File\{Receive,Send};
|
||||||
|
use App\Classes\Sock\SocketClient;
|
||||||
|
use App\Classes\Sock\SocketException;
|
||||||
|
use App\Models\Node as NodeModel;
|
||||||
|
use App\Models\Setup;
|
||||||
|
|
||||||
|
abstract class Protocol
|
||||||
|
{
|
||||||
|
// Our product code
|
||||||
|
// @todo Move These to a config file
|
||||||
|
protected const product_code = 'AB8D';
|
||||||
|
protected const setup = 1;
|
||||||
|
|
||||||
|
// Enable extra debugging
|
||||||
|
protected bool $DEBUG = FALSE;
|
||||||
|
|
||||||
|
// Return constants
|
||||||
|
protected const OK = 0;
|
||||||
|
protected const EOF = -1;
|
||||||
|
protected const TIMEOUT = -2;
|
||||||
|
protected const RCDO = -3;
|
||||||
|
protected const GCOUNT = -4;
|
||||||
|
protected const ERROR = -5;
|
||||||
|
|
||||||
|
// Our sessions Types
|
||||||
|
public const SESSION_AUTO = 0;
|
||||||
|
public const SESSION_EMSI = 1;
|
||||||
|
public const SESSION_BINKP = 2;
|
||||||
|
public const SESSION_ZMODEM = 3;
|
||||||
|
|
||||||
|
protected const MAX_PATH = 1024;
|
||||||
|
|
||||||
|
/* 9 most right bits are zeros */
|
||||||
|
private const O_BASE = 9; /* First 9 bits are protocol */
|
||||||
|
protected const O_NRQ = (1<<self::O_BASE); /* 0000 0000 0000 0000 0010 0000 0000 BOTH - No file requests accepted by this system */
|
||||||
|
protected const O_HRQ = (1<<(self::O_BASE+1)); /* 0000 0000 0000 0000 0100 0000 0000 BOTH - Hold file requests (not processed at this time). */
|
||||||
|
protected const O_FNC = (1<<(self::O_BASE+2)); /* 0000 0000 0000 0000 1000 0000 0000 - Filename conversion, transmitted files must be 8.3 */
|
||||||
|
protected const O_XMA = (1<<(self::O_BASE+3)); /* 0000 0000 0000 0001 0000 0000 0000 - Supports other forms of compressed mail */
|
||||||
|
protected const O_HAT = (1<<(self::O_BASE+4)); /* 0000 0000 0000 0010 0000 0000 0000 BOTH - Hold ALL files (Answering System) */
|
||||||
|
protected const O_HXT = (1<<(self::O_BASE+5)); /* 0000 0000 0000 0100 0000 0000 0000 BOTH - Hold Mail traffic */
|
||||||
|
protected const O_NPU = (1<<(self::O_BASE+6)); /* 0000 0000 0000 1000 0000 0000 0000 - No files pickup desired (Calling System) */
|
||||||
|
protected const O_PUP = (1<<(self::O_BASE+7)); /* 0000 0000 0001 0000 0000 0000 0000 - Pickup files for primary address only */
|
||||||
|
protected const O_PUA = (1<<(self::O_BASE+8)); /* 0000 0000 0010 0000 0000 0000 0000 EMSI - Pickup files for all presented addresses */
|
||||||
|
protected const O_PWD = (1<<(self::O_BASE+9)); /* 0000 0000 0100 0000 0000 0000 0000 BINK - Node password validated */
|
||||||
|
protected const O_BAD = (1<<(self::O_BASE+10)); /* 0000 0000 1000 0000 0000 0000 0000 BOTH - NOde invalid password presented */
|
||||||
|
protected const O_RH1 = (1<<(self::O_BASE+11)); /* 0000 0001 0000 0000 0000 0000 0000 EMSI - Use RH1 for Hydra (files-after-freqs) */
|
||||||
|
protected const O_LST = (1<<(self::O_BASE+12)); /* 0000 0010 0000 0000 0000 0000 0000 BOTH - Node is nodelisted */
|
||||||
|
protected const O_INB = (1<<(self::O_BASE+13)); /* 0000 0100 0000 0000 0000 0000 0000 BOTH - Inbound session */
|
||||||
|
protected const O_TCP = (1<<(self::O_BASE+14)); /* 0000 1000 0000 0000 0000 0000 0000 BOTH - TCP session */
|
||||||
|
protected const O_EII = (1<<(self::O_BASE+15)); /* 0001 0000 0000 0000 0000 0000 0000 EMSI - Remote understands EMSI-II */
|
||||||
|
|
||||||
|
// Session Status
|
||||||
|
protected const S_OK = 0;
|
||||||
|
protected const S_NODIAL = 1;
|
||||||
|
protected const S_REDIAL = 2;
|
||||||
|
protected const S_BUSY = 3;
|
||||||
|
protected const S_FAILURE = 4;
|
||||||
|
protected const S_MASK = 7;
|
||||||
|
protected const S_HOLDR = 8;
|
||||||
|
protected const S_HOLDX = 16;
|
||||||
|
protected const S_HOLDA = 32;
|
||||||
|
protected const S_ADDTRY = 64;
|
||||||
|
protected const S_ANYHOLD = (self::S_HOLDR|self::S_HOLDX|self::S_HOLDA);
|
||||||
|
|
||||||
|
// File transfer status
|
||||||
|
protected const FOP_OK = 0;
|
||||||
|
protected const FOP_CONT = 1;
|
||||||
|
protected const FOP_SKIP = 2;
|
||||||
|
protected const FOP_ERROR = 3;
|
||||||
|
protected const FOP_SUSPEND = 4;
|
||||||
|
|
||||||
|
protected const MO_CHAT = 4;
|
||||||
|
|
||||||
|
protected SocketClient $client; /* Our socket details */
|
||||||
|
protected Setup $setup; /* Our setup */
|
||||||
|
protected Node $node; /* The node we are communicating with */
|
||||||
|
protected Send $send; /* The list of files we are sending */
|
||||||
|
protected Receive $recv; /* The list of files we are receiving */
|
||||||
|
|
||||||
|
private int $options; /* Our options for a session */
|
||||||
|
private int $session; /* Tracks where we are up to with this session */
|
||||||
|
protected bool $originate; /* Are we originating a connection */
|
||||||
|
private array $comms; /* Our comms details */
|
||||||
|
|
||||||
|
abstract protected function protocol_init(): int;
|
||||||
|
abstract protected function protocol_session(): int;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'ls_SkipGuard': /* double-skip protection on/off */
|
||||||
|
case 'rxOptions': /* Options from ZRINIT header */
|
||||||
|
return $this->comms[$key] ?? 0;
|
||||||
|
|
||||||
|
case 'ls_rxAttnStr':
|
||||||
|
return $this->comms[$key] ?? '';
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __set($key,$value)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'ls_rxAttnStr':
|
||||||
|
case 'ls_SkipGuard':
|
||||||
|
case 'rxOptions':
|
||||||
|
$this->comms[$key] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown key: '.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We got an error, close anything we are have open
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function error_close(): void
|
||||||
|
{
|
||||||
|
if ($this->send->fd)
|
||||||
|
$this->send->close(FALSE);
|
||||||
|
|
||||||
|
if ($this->recv->fd)
|
||||||
|
$this->recv->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming Protocol session
|
||||||
|
*
|
||||||
|
* @param SocketClient $client
|
||||||
|
* @return int|null
|
||||||
|
* @throws SocketException
|
||||||
|
*/
|
||||||
|
public function onConnect(SocketClient $client): ?int
|
||||||
|
{
|
||||||
|
$pid = pcntl_fork();
|
||||||
|
|
||||||
|
if ($pid == -1)
|
||||||
|
throw new SocketException(SocketException::CANT_ACCEPT,'Could not fork process');
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: = End [%d]',__METHOD__,$pid));
|
||||||
|
|
||||||
|
// Parent return ready for next connection
|
||||||
|
return $pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function optionClear(int $key): void
|
||||||
|
{
|
||||||
|
$this->options &= ~$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function optionGet(int $key): int
|
||||||
|
{
|
||||||
|
return ($this->options & $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function optionSet(int $key): void
|
||||||
|
{
|
||||||
|
$this->options |= $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise our Session
|
||||||
|
*
|
||||||
|
* @param int $type
|
||||||
|
* @param SocketClient $client
|
||||||
|
* @param NodeModel|null $o
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function session(int $type,SocketClient $client,NodeModel $o=NULL): int
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start [%d]',__METHOD__,$type));
|
||||||
|
|
||||||
|
// This sessions options
|
||||||
|
$this->options = 0;
|
||||||
|
$this->session = 0;
|
||||||
|
|
||||||
|
// Our files that we are sending/receive
|
||||||
|
$this->send = new Send;
|
||||||
|
$this->recv = new Receive;
|
||||||
|
|
||||||
|
if ($o) {
|
||||||
|
// Our configuration and initialise values
|
||||||
|
$this->setup = Setup::findOrFail(self::setup);
|
||||||
|
|
||||||
|
// The node we are communicating with
|
||||||
|
$this->node = new Node;
|
||||||
|
|
||||||
|
$this->originate = $o->exists;
|
||||||
|
|
||||||
|
// If we are connecting to a node
|
||||||
|
if ($o->exists) {
|
||||||
|
$this->node->originate($o);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$this->optionSet(self::O_INB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are an IP node
|
||||||
|
$this->optionSet(self::O_TCP);
|
||||||
|
$this->setClient($client);
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
/** @noinspection PhpMissingBreakStatementInspection */
|
||||||
|
case self::SESSION_AUTO:
|
||||||
|
Log::debug(sprintf('%s: - Trying EMSI',__METHOD__));
|
||||||
|
|
||||||
|
$rc = $this->protocol_init();
|
||||||
|
if ($rc < 0) {
|
||||||
|
Log::error(sprintf('%s: ! Unable to start EMSI [%d]',__METHOD__,$rc));
|
||||||
|
|
||||||
|
return self::S_REDIAL | self::S_ADDTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
case self::SESSION_EMSI:
|
||||||
|
Log::debug(sprintf('%s: - Starting EMSI',__METHOD__));
|
||||||
|
$rc = $this->protocol_session();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::SESSION_BINKP:
|
||||||
|
Log::debug(sprintf('%s: - Starting BINKP',__METHOD__));
|
||||||
|
$rc = $this->protocol_session();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::SESSION_ZMODEM:
|
||||||
|
Log::debug(sprintf('%s: - Starting ZMODEM',__METHOD__));
|
||||||
|
$this->client->speed = SocketClient::TCP_SPEED;
|
||||||
|
$this->originate = FALSE;
|
||||||
|
|
||||||
|
// @todo While Debugging
|
||||||
|
$this->send->add('/tmp/aa');
|
||||||
|
|
||||||
|
return $this->protocol_session();
|
||||||
|
|
||||||
|
default:
|
||||||
|
Log::error(sprintf('%s: ! Unsupported session type [%d]',__METHOD__,$type));
|
||||||
|
|
||||||
|
return self::S_REDIAL | self::S_ADDTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Unlock outbounds
|
||||||
|
|
||||||
|
// @todo These flags determine when we connect to the remote.
|
||||||
|
// If the remote indicated that they dont support file requests (NRQ) or temporarily hold them (HRQ)
|
||||||
|
if (($this->node->optionGet(self::O_NRQ) && (! $this->setup->ignore_nrq)) || $this->node->optionGet(self::O_HRQ))
|
||||||
|
$rc |= self::S_HOLDR;
|
||||||
|
|
||||||
|
if ($this->optionGet(self::O_HXT))
|
||||||
|
$rc |= self::S_HOLDX;
|
||||||
|
|
||||||
|
if ($this->optionGet(self::O_HAT))
|
||||||
|
$rc |= self::S_HOLDA;
|
||||||
|
|
||||||
|
Log::info(sprintf('%s: Total: %s - %d:%02d:%02d online, (%d) %lu%s sent, (%d) %lu%s received - %s',
|
||||||
|
__METHOD__,
|
||||||
|
$this->node->ftn,
|
||||||
|
$this->node->session_time/3600,
|
||||||
|
$this->node->session_time%3600/60,
|
||||||
|
$this->node->session_time%60,
|
||||||
|
$this->send->total_sent,$this->send->total_sent_bytes,'b',
|
||||||
|
$this->recv->total_recv,$this->recv->total_recv_bytes,'b',
|
||||||
|
(($rc & self::S_MASK) == self::S_OK) ? 'Successful' : 'Failed',
|
||||||
|
));
|
||||||
|
|
||||||
|
// @todo Log to history log in the DB.
|
||||||
|
//if ($this->node->start_time && $this->setup->cfg('CFG_HISTORY')) {}
|
||||||
|
|
||||||
|
// @todo Optional after session execution event
|
||||||
|
// if ($this->node->start_time && $this->setup->cfg('CFG_AFTERSESSION')) {}
|
||||||
|
|
||||||
|
// @todo Optional after session includes mail event
|
||||||
|
// if ($this->node->start_time && $this->setup->cfg('CFG_AFTERMAIL')) {}
|
||||||
|
|
||||||
|
return ($rc & ~self::S_ADDTRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a session bit
|
||||||
|
*
|
||||||
|
* @param int $key
|
||||||
|
*/
|
||||||
|
protected function sessionClear(int $key): void
|
||||||
|
{
|
||||||
|
$this->session &= ~$key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a session bit
|
||||||
|
* @param int $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function sessionGet(int $key): bool
|
||||||
|
{
|
||||||
|
return ($this->session & $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a session bit (with SE_*)
|
||||||
|
*
|
||||||
|
* @param int $key
|
||||||
|
*/
|
||||||
|
protected function sessionSet(int $key): void
|
||||||
|
{
|
||||||
|
$this->session |= $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set our client that we are communicating with
|
||||||
|
*
|
||||||
|
* @param SocketClient $client
|
||||||
|
*/
|
||||||
|
private function setClient(SocketClient $client): void
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
}
|
1296
app/Classes/Protocol/Binkd.php
Normal file
1296
app/Classes/Protocol/Binkd.php
Normal file
File diff suppressed because it is too large
Load Diff
43
app/Classes/Protocol/BinkpMessage.php
Normal file
43
app/Classes/Protocol/BinkpMessage.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes\Protocol;
|
||||||
|
|
||||||
|
final class BinkpMessage
|
||||||
|
{
|
||||||
|
public const BLK_HDR_SIZE = 2; /* header size */
|
||||||
|
|
||||||
|
private int $id;
|
||||||
|
private string $body;
|
||||||
|
|
||||||
|
public function __construct(int $id,string $body)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->body = $body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'len':
|
||||||
|
return strlen($this->body)+1+self::BLK_HDR_SIZE;
|
||||||
|
|
||||||
|
case 'msg':
|
||||||
|
$buf = self::mkheader((strlen($this->body)+1) | 0x8000);
|
||||||
|
$buf .= chr($this->id);
|
||||||
|
$buf .= $this->body;
|
||||||
|
|
||||||
|
return $buf;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception('Unknown key :'.$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function mkheader(int $l): string
|
||||||
|
{
|
||||||
|
$buf = chr(($l>>8)&0xff);
|
||||||
|
$buf .= chr($l&0xff);
|
||||||
|
|
||||||
|
return $buf;
|
||||||
|
}
|
||||||
|
}
|
1177
app/Classes/Protocol/EMSI.php
Normal file
1177
app/Classes/Protocol/EMSI.php
Normal file
File diff suppressed because it is too large
Load Diff
2552
app/Classes/Protocol/Zmodem.php
Normal file
2552
app/Classes/Protocol/Zmodem.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,23 +2,199 @@
|
|||||||
|
|
||||||
namespace App\Classes\Sock;
|
namespace App\Classes\Sock;
|
||||||
|
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class SocketClient
|
||||||
|
*
|
||||||
|
* @package App\Classes\Sock
|
||||||
|
* @property int speed
|
||||||
|
* @property int cps
|
||||||
|
*/
|
||||||
final class SocketClient {
|
final class SocketClient {
|
||||||
public \Socket $connection; // @todo make private
|
// For deep debugging
|
||||||
|
private bool $DEBUG = FALSE;
|
||||||
|
|
||||||
|
private \Socket $connection;
|
||||||
private string $address = '';
|
private string $address = '';
|
||||||
private int $port = 0;
|
private int $port = 0;
|
||||||
|
|
||||||
public function __construct (\Socket $connection) {
|
// Our session state
|
||||||
|
private array $session = [];
|
||||||
|
|
||||||
|
private const OK = 0;
|
||||||
|
private const EOF = -1;
|
||||||
|
private const TIMEOUT = -2;
|
||||||
|
private const RCDO = -3;
|
||||||
|
private const GCOUNT = -4;
|
||||||
|
private const ERROR = -5;
|
||||||
|
|
||||||
|
private const TTY_SUCCESS = self::OK;
|
||||||
|
private const TTY_TIMEOUT = self::TIMEOUT;
|
||||||
|
private const TTY_HANGUP = self::RCDO;
|
||||||
|
private const TTY_ERROR = self::ERROR;
|
||||||
|
|
||||||
|
public const TCP_SPEED = 115200;
|
||||||
|
|
||||||
|
// Buffer for sending
|
||||||
|
private const TX_BUF_SIZE = (0x8100);
|
||||||
|
private int $tx_ptr = 0;
|
||||||
|
private int $tx_free = self::TX_BUF_SIZE;
|
||||||
|
private int $tty_status = 0;
|
||||||
|
private string $tx_buf = '';
|
||||||
|
|
||||||
|
// Buffer for receiving
|
||||||
|
private const RX_BUF_SIZE = (0x8100);
|
||||||
|
private int $rx_ptr = 0;
|
||||||
|
private int $rx_left = 0;
|
||||||
|
private string $rx_buf = '';
|
||||||
|
|
||||||
|
public function __construct (\Socket $connection,int $speed=self::TCP_SPEED) {
|
||||||
socket_getsockname($connection,$this->address,$this->port);
|
socket_getsockname($connection,$this->address,$this->port);
|
||||||
Log::info(sprintf('Connection from [%s] on port [%d]',$this->address,$this->port),['m'=>__METHOD__]);
|
Log::info(sprintf('%s: + Connection from [%s] on port [%d]',__METHOD__,$this->address,$this->port));
|
||||||
|
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __get($key) {
|
||||||
|
switch ($key) {
|
||||||
|
case 'cps':
|
||||||
|
case 'speed':
|
||||||
|
return Arr::get($this->session,$key);
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception(sprintf('%s: Unknown key [%s]:',__METHOD__,$key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __set($key,$value) {
|
||||||
|
switch ($key) {
|
||||||
|
case 'cps':
|
||||||
|
case 'speed':
|
||||||
|
return $this->session[$key] = $value;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new \Exception(sprintf('%s: Unknown key [%s]:',__METHOD__,$key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We'll add to our transmit buffer and if doesnt have space, we'll empty it first
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function buffer_add(string $data): void
|
||||||
|
{
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: + Start [%s] (%d)',__METHOD__,$data,strlen($data)));
|
||||||
|
|
||||||
|
//$rc = self::OK;
|
||||||
|
//$tx_ptr = self::TX_BUF_SIZE-$this->tx_free;
|
||||||
|
$ptr = 0;
|
||||||
|
$num_bytes = strlen($data);
|
||||||
|
$this->tty_status = self::TTY_SUCCESS;
|
||||||
|
|
||||||
|
while ($num_bytes) {
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - Num Bytes [%d]: TX Free [%d]',__METHOD__,$num_bytes,$this->tx_free));
|
||||||
|
|
||||||
|
if ($num_bytes > $this->tx_free) {
|
||||||
|
do {
|
||||||
|
$this->buffer_flush(5);
|
||||||
|
|
||||||
|
if ($this->tty_status == self::TTY_SUCCESS) {
|
||||||
|
$n = min($this->tx_free,$num_bytes);
|
||||||
|
$this->tx_buf = substr($data,$ptr,$n);
|
||||||
|
$this->tx_free -= $n;
|
||||||
|
$num_bytes -= $n;
|
||||||
|
$ptr += $n;
|
||||||
|
}
|
||||||
|
|
||||||
|
} while ($this->tty_status != self::TTY_SUCCESS);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - Remaining data to send [%d]',__METHOD__,$num_bytes));
|
||||||
|
|
||||||
|
$this->tx_buf .= substr($data,$ptr,$num_bytes);
|
||||||
|
$this->tx_free -= $num_bytes;
|
||||||
|
$num_bytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: = End [%s]',__METHOD__,strlen($this->tx_buf)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear our TX buffer
|
||||||
|
*/
|
||||||
|
public function buffer_clear(): void
|
||||||
|
{
|
||||||
|
$this->tx_buf = '';
|
||||||
|
$this->tx_free = self::TX_BUF_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Empty our TX buffer
|
||||||
|
*
|
||||||
|
* @param int $timeout
|
||||||
|
* @return int
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function buffer_flush(int $timeout): int
|
||||||
|
{
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: + Start [%d]',__METHOD__,$timeout));
|
||||||
|
|
||||||
|
$rc = self::OK;
|
||||||
|
$tx_ptr = 0;
|
||||||
|
$restsize = self::TX_BUF_SIZE-$this->tx_free;
|
||||||
|
|
||||||
|
$tm = $this->timer_set($timeout);
|
||||||
|
while (self::TX_BUF_SIZE != $this->tx_free) {
|
||||||
|
$tv = $this->timer_rest($tm);
|
||||||
|
|
||||||
|
if ($rc = $this->canSend($tv)>0) {
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - Sending [%d]',__METHOD__,$restsize));
|
||||||
|
$rc = $this->send(substr($this->tx_buf,$tx_ptr,$restsize),0);
|
||||||
|
Log::debug(sprintf('%s: - Sent [%d] (%s)',__METHOD__,$rc,Str::limit($this->tx_buf,15)));
|
||||||
|
|
||||||
|
if ($rc == $restsize) {
|
||||||
|
$this->tx_buf = '';
|
||||||
|
$tx_ptr = 0;
|
||||||
|
$this->tx_free += $rc;
|
||||||
|
$this->buffer_clear();
|
||||||
|
|
||||||
|
} else if ($rc > 0) {
|
||||||
|
$tx_ptr += $rc;
|
||||||
|
$restsize -= $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo Enable a delay for slow clients
|
||||||
|
//sleep(1);
|
||||||
|
if ($this->timer_expired($tm))
|
||||||
|
return self::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: = End [%d]',__METHOD__,$rc));
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @return int
|
* @return int
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function canSend(int $timeout): int
|
public function canSend(int $timeout): int
|
||||||
{
|
{
|
||||||
@ -38,11 +214,15 @@ final class SocketClient {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a client socket
|
* Create a client socket
|
||||||
|
* @param string $address
|
||||||
|
* @param int $port
|
||||||
|
* @param int $speed
|
||||||
* @return static
|
* @return static
|
||||||
|
* @throws SocketException
|
||||||
*/
|
*/
|
||||||
public static function create(string $address,int $port): self
|
public static function create(string $address,int $port,int $speed=self::TCP_SPEED): self
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('Creating connection to [%s:%d]',$address,$port));
|
Log::debug(sprintf('%s: + Creating connection to [%s:%d]',__METHOD__,$address,$port));
|
||||||
|
|
||||||
$address = gethostbyname($address);
|
$address = gethostbyname($address);
|
||||||
|
|
||||||
@ -55,13 +235,14 @@ final class SocketClient {
|
|||||||
if ($result === FALSE)
|
if ($result === FALSE)
|
||||||
throw new SocketException(SocketException::CANT_CONNECT,socket_strerror(socket_last_error($socket)));
|
throw new SocketException(SocketException::CANT_CONNECT,socket_strerror(socket_last_error($socket)));
|
||||||
|
|
||||||
return new self($socket);
|
return new self($socket,$speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the client's address
|
* Return the client's address
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
* @todo change to __get()
|
||||||
*/
|
*/
|
||||||
public function getAddress(): string
|
public function getAddress(): string
|
||||||
{
|
{
|
||||||
@ -72,6 +253,7 @@ final class SocketClient {
|
|||||||
* Return the port in use
|
* Return the port in use
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
|
* @todo change to __get()
|
||||||
*/
|
*/
|
||||||
public function getPort(): int
|
public function getPort(): int
|
||||||
{
|
{
|
||||||
@ -82,17 +264,96 @@ final class SocketClient {
|
|||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @return int
|
* @return int
|
||||||
* @note use socketSelect()
|
* @note use socketSelect()
|
||||||
* @todo Node used by bink yet?
|
* @throws \Exception
|
||||||
* @todo to test
|
|
||||||
*/
|
*/
|
||||||
public function hasData(int $timeout): int
|
public function hasData(int $timeout): int
|
||||||
{
|
{
|
||||||
$read = [$this->connection];
|
$read = [$this->connection];
|
||||||
$write = $except = NULL;
|
|
||||||
|
|
||||||
//$rc = socket_select($read,$write,$except,$timeout);
|
return $this->rx_left ?: $this->socketSelect($read,NULL,NULL,$timeout);
|
||||||
//return $rc;
|
}
|
||||||
return $this->socketSelect($read,NULL,NULL,$timeout);
|
|
||||||
|
/**
|
||||||
|
* Read data from the socket.
|
||||||
|
* If we only want 1 character, we'll return the ASCII value of the data received
|
||||||
|
*
|
||||||
|
* @param int $timeout
|
||||||
|
* @param int $len
|
||||||
|
* @return int|string
|
||||||
|
* @throws SocketException
|
||||||
|
*/
|
||||||
|
public function read(int $timeout,int $len=1024)
|
||||||
|
{
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: + Start [%d] (%d)',__METHOD__,$len,$timeout));
|
||||||
|
|
||||||
|
if ($timeout AND ($this->hasData($timeout) === 0))
|
||||||
|
return '';
|
||||||
|
|
||||||
|
$buf = '';
|
||||||
|
$rc = socket_recv($this->connection,$buf, $len,MSG_DONTWAIT);
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: - Read [%d]',__METHOD__,$rc));
|
||||||
|
|
||||||
|
if ($rc === FALSE)
|
||||||
|
throw new SocketException($x=socket_last_error($this->connection),socket_strerror($x));
|
||||||
|
|
||||||
|
return is_null($buf) ? '' : $buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a character from the remote.
|
||||||
|
* We'll buffer everything received
|
||||||
|
*
|
||||||
|
* @param int $timeout
|
||||||
|
* @return int
|
||||||
|
* @throws SocketException
|
||||||
|
*/
|
||||||
|
public function read_ch(int $timeout): int
|
||||||
|
{
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: + Start [%d]',__METHOD__,$timeout),['rx_left'=>$this->rx_left,'rx_ptr'=>$this->rx_ptr]);
|
||||||
|
|
||||||
|
// If our buffer is empty, we'll try and read from the remote
|
||||||
|
if ($this->rx_left == 0) {
|
||||||
|
if ($this->hasData($timeout) > 0) {
|
||||||
|
try {
|
||||||
|
if (! strlen($this->rx_buf = $this->read(0,self::RX_BUF_SIZE))) {
|
||||||
|
Log::debug(sprintf('%s: - Nothing read',__METHOD__));
|
||||||
|
|
||||||
|
return self::TTY_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return ($e->getCode() == 11) ? self::TTY_TIMEOUT : self::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::info(sprintf('%s: - Read [%d] bytes',__METHOD__,strlen($this->rx_buf)));
|
||||||
|
|
||||||
|
$this->rx_ptr = 0;
|
||||||
|
$this->rx_left = strlen($this->rx_buf);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return self::TTY_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rc = ord(substr($this->rx_buf,$this->rx_ptr,1));
|
||||||
|
|
||||||
|
$this->rx_left--;
|
||||||
|
$this->rx_ptr++;
|
||||||
|
|
||||||
|
if ($this->DEBUG)
|
||||||
|
Log::debug(sprintf('%s: = Return [%x] (%c)',__METHOD__,$rc,$rc));
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rx_purge(): void
|
||||||
|
{
|
||||||
|
$this->rx_ptr = $this->rx_left = 0;
|
||||||
|
$this->rx_buf = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,8 +363,10 @@ final class SocketClient {
|
|||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @param null $length
|
* @param null $length
|
||||||
* @return false|int
|
* @return false|int
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function send($message,int $timeout,$length=NULL) {
|
public function send($message,int $timeout,$length=NULL)
|
||||||
|
{
|
||||||
if ($timeout AND (! $rc = $this->canSend($timeout)))
|
if ($timeout AND (! $rc = $this->canSend($timeout)))
|
||||||
return $rc;
|
return $rc;
|
||||||
|
|
||||||
@ -113,33 +376,77 @@ final class SocketClient {
|
|||||||
return socket_write($this->connection,$message,$length);
|
return socket_write($this->connection,$message,$length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function socketSelect(?array $read,?array $write,?array $except,int $timeout): int
|
/**
|
||||||
|
* Set our speed
|
||||||
|
*
|
||||||
|
* @param int $value
|
||||||
|
* @todo change to __set()
|
||||||
|
*/
|
||||||
|
public function setSpeed(int $value): void
|
||||||
{
|
{
|
||||||
return socket_select($read,$write,$except,$timeout);
|
$this->speed = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read data from the socket.
|
* Wait for data on a socket
|
||||||
* If we only want 1 character, we'll return the ASCII value of the data received
|
|
||||||
*
|
*
|
||||||
|
* @param array|null $read
|
||||||
|
* @param array|null $write
|
||||||
|
* @param array|null $except
|
||||||
* @param int $timeout
|
* @param int $timeout
|
||||||
* @param int $len
|
* @return int
|
||||||
* @return false|int|string|null
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function read(int $timeout,int $len=1024)
|
private function socketSelect(?array $read,?array $write,?array $except,int $timeout): int
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('+ Start [%d]',$len),['m'=>__METHOD__]);
|
$rc = socket_select($read,$write,$except,$timeout);
|
||||||
|
|
||||||
|
if ($rc === FALSE)
|
||||||
|
throw new \Exception('Socket Error: '.socket_strerror(socket_last_error()));
|
||||||
|
|
||||||
if ($timeout AND (! $rc = $this->hasData($timeout)))
|
|
||||||
return $rc;
|
return $rc;
|
||||||
|
|
||||||
if (($buf=socket_read($this->connection,$len,PHP_BINARY_READ)) === FALSE) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log::debug(sprintf(' - Read [%d]',strlen($buf)),['m'=>__METHOD__]);
|
/**
|
||||||
|
* Return our speed in bps
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* @todo change to __get()
|
||||||
|
*/
|
||||||
|
public function speed(): int
|
||||||
|
{
|
||||||
|
return $this->speed;
|
||||||
|
}
|
||||||
|
|
||||||
// For single character reads, we'll return the ASCII value of the buf
|
public function timer_expired(int $timer): int
|
||||||
return ($len == 1 and (ord($buf) != 0)) ? ord($buf) : $buf;
|
{
|
||||||
|
return (time()>=$timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timer_rest(int $timer): int
|
||||||
|
{
|
||||||
|
return (($timer)-time());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timer_set(int $expire): int
|
||||||
|
{
|
||||||
|
return (time()+$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if we there is data waiting to collect, or if we can send
|
||||||
|
*
|
||||||
|
* @param bool $read
|
||||||
|
* @param bool $write
|
||||||
|
* @param int $timeout
|
||||||
|
* @return int
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function ttySelect(bool $read,bool $write, int $timeout): int
|
||||||
|
{
|
||||||
|
$read = $read ? [$this->connection] : NULL;
|
||||||
|
$write = $write ? [$this->connection] : NULL;
|
||||||
|
|
||||||
|
return $this->socketSelect($read,$write,NULL,$timeout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ final class SocketException extends \Exception {
|
|||||||
public const CANT_LISTEN = 3;
|
public const CANT_LISTEN = 3;
|
||||||
public const CANT_ACCEPT = 4;
|
public const CANT_ACCEPT = 4;
|
||||||
public const CANT_CONNECT = 5;
|
public const CANT_CONNECT = 5;
|
||||||
|
public const SOCKET_ERROR = 6;
|
||||||
|
public const SOCKET_EAGAIN = 11;
|
||||||
|
|
||||||
private array $messages = [
|
private array $messages = [
|
||||||
self::CANT_CREATE_SOCKET => 'Can\'t create socket: "%s"',
|
self::CANT_CREATE_SOCKET => 'Can\'t create socket: "%s"',
|
||||||
@ -15,6 +17,8 @@ final class SocketException extends \Exception {
|
|||||||
self::CANT_LISTEN => 'Can\'t listen: "%s"',
|
self::CANT_LISTEN => 'Can\'t listen: "%s"',
|
||||||
self::CANT_ACCEPT => 'Can\'t accept connections: "%s"',
|
self::CANT_ACCEPT => 'Can\'t accept connections: "%s"',
|
||||||
self::CANT_CONNECT => 'Can\'t connect: "%s"',
|
self::CANT_CONNECT => 'Can\'t connect: "%s"',
|
||||||
|
self::SOCKET_ERROR => 'Socket Error: "%s"',
|
||||||
|
self::SOCKET_EAGAIN => 'Socket Resource Temporarily Unavailable - Try again',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(int $code,string $params=NULL) {
|
public function __construct(int $code,string $params=NULL) {
|
||||||
|
918
app/Classes/TTY.php
Normal file
918
app/Classes/TTY.php
Normal file
@ -0,0 +1,918 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Classes;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Sock\SocketClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class TTY
|
||||||
|
* @package App\Classes
|
||||||
|
*
|
||||||
|
* This base class handles all the comms with the remote
|
||||||
|
*/
|
||||||
|
class TTY
|
||||||
|
{
|
||||||
|
protected SocketClient $client; /* Our incoming client socket */
|
||||||
|
|
||||||
|
private const RX_BUF_SIZE = (0x8100);
|
||||||
|
private const TX_BUF_SIZE = (0x8100);
|
||||||
|
|
||||||
|
private const MSG_BUFFER = 2048;
|
||||||
|
|
||||||
|
protected const OK = 0;
|
||||||
|
private const EOF = -1;
|
||||||
|
protected const TIMEOUT = -2;
|
||||||
|
protected const RCDO = -3;
|
||||||
|
protected const GCOUNT = -4;
|
||||||
|
protected const ERROR = -5;
|
||||||
|
|
||||||
|
private const TTY_SUCCESS = self::OK;
|
||||||
|
private const TTY_TIMEOUT = self::TIMEOUT;
|
||||||
|
private const TTY_HANGUP = self::RCDO;
|
||||||
|
private const TTY_ERROR = self::ERROR;
|
||||||
|
|
||||||
|
protected const QC_RECVD = 'c';
|
||||||
|
protected const QC_SENDD = 'd';
|
||||||
|
protected const QC_EMSID = 'g';
|
||||||
|
|
||||||
|
private const QR_POLL = 'A';
|
||||||
|
private const QR_REQ = 'B';
|
||||||
|
private const QR_SEND = 'D';
|
||||||
|
private const QR_STS = 'E';
|
||||||
|
private const QR_CONF = 'F';
|
||||||
|
private const QR_QUIT = 'G';
|
||||||
|
private const QR_INFO = 'H';
|
||||||
|
private const QR_SCAN = 'I';
|
||||||
|
private const QR_KILL = 'J';
|
||||||
|
private const QR_QUEUE = 'K';
|
||||||
|
private const QR_SKIP = 'L';
|
||||||
|
private const QR_REFUSE = 'M';
|
||||||
|
private const QR_HANGUP = 'N';
|
||||||
|
private const QR_RESTMR = 'O';
|
||||||
|
private const QR_CHAT = 'P';
|
||||||
|
private const QR_SET = 'S';
|
||||||
|
private const QR_STYPE = 'T';
|
||||||
|
|
||||||
|
private const HUP_NONE = 0;
|
||||||
|
private const HUP_LINE = 1;
|
||||||
|
private const HUP_OPERATOR = 2;
|
||||||
|
private const HUP_SESLIMIT = 3;
|
||||||
|
private const HUP_CPS = 4;
|
||||||
|
|
||||||
|
// @tod these FOP has been duplicated from Protocol - added back here for optimising EMSI
|
||||||
|
protected const FOP_OK = 0;
|
||||||
|
protected const FOP_CONT = 1;
|
||||||
|
protected const FOP_SKIP = 2;
|
||||||
|
protected const FOP_ERROR = 3;
|
||||||
|
protected const FOP_SUSPEND = 4;
|
||||||
|
|
||||||
|
protected int $tty_rx_left = 0; /* Number of bytes in receive buffer */
|
||||||
|
private string $tty_tx_buf = ''; /* Data in the buffer to send */
|
||||||
|
private int $tty_tx_ptr = 0; /* Pointer to next byte to send out in transmit buffer */
|
||||||
|
private int $tty_tx_free = self::TX_BUF_SIZE;/* Number of byte left in transmit buffer */
|
||||||
|
|
||||||
|
private int $tty_status = self::TTY_SUCCESS;
|
||||||
|
private int $tty_gothup = 0;
|
||||||
|
//protected int $rxstatus = 0;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->sendf = new FileSend; // @todo these should be delcared private if they are staying here
|
||||||
|
$this->recvf = new FileReceive;
|
||||||
|
$this->rnode = new rnode;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function check_cps(): void
|
||||||
|
{
|
||||||
|
Log::debug('- Start',['m'=>__METHOD__]);
|
||||||
|
|
||||||
|
$cpsdelay=10; // $cpsdelay=cfgi(CFG_MINCPSDELAY);
|
||||||
|
$ncps = 38400/1000; $this->speed/1000;
|
||||||
|
$r=1; //$r=cfgi(CFG_REALMINCPS);
|
||||||
|
|
||||||
|
if(!($this->sendf->cps=time()-$this->sendf->start)) {
|
||||||
|
$this->sendf->cps=1;
|
||||||
|
} else {
|
||||||
|
$this->sendf->cps=($this->sendf->foff-$this->sendf->soff)/$this->sendf->cps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!($this->recvf->cps=time()-$this->recvf->start)) {
|
||||||
|
$this->recvf->cps=1;
|
||||||
|
} else {
|
||||||
|
$this->recvf->cps=($this->recvf->foff-$this->recvf->soff)/$this->recvf->cps;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->sendf->start&&(true ? 0 : cfgi(CFG_MINCPSOUT))>0&&(time()-$this->sendf->start)>$cpsdelay&&$this->sendf->cps<($r?$cci:$cci*$ncps)) {
|
||||||
|
//write_log("mincpsout=%d reached, aborting session",r?cci:cci*ncps);
|
||||||
|
$tty_gothup = self::HUP_CPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->recvf->start&&(true ? 0 : cfgi(CFG_MINCPSIN))>0&&(time()-$this->recvf->start)>$cpsdelay&&$this->recvf->cps<($r?$cci:$cci*$ncps)) {
|
||||||
|
//write_log("mincpsin=%d reached, aborting session",r?cci:cci*ncps);
|
||||||
|
$tty_gothup = self::HUP_CPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->getevt();
|
||||||
|
|
||||||
|
Log::debug('- End',['m'=>__METHOD__]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo no longer used?
|
||||||
|
protected function getevt(): void
|
||||||
|
{
|
||||||
|
Log::debug('+ Start', ['m' => __METHOD__]);
|
||||||
|
$qsndbuflen = 0;
|
||||||
|
|
||||||
|
while($this->qrecvpkt($qrcv_buf)) {
|
||||||
|
Log::debug(' - qrecvpkt Returned', ['m' => __METHOD__,'qrcv_buf'=>$qrcv_buf]);
|
||||||
|
|
||||||
|
switch($qrcv_buf[2]) { // @todo this doesnt seem right?
|
||||||
|
case self::QR_SKIP:
|
||||||
|
//$this->rxstatus=self::RX_SKIP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::QR_REFUSE:
|
||||||
|
//$this->rxstatus=self::RX_SUSPEND;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::QR_HANGUP:
|
||||||
|
$tty_gothup = self::HUP_OPERATOR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case self::QR_CHAT:
|
||||||
|
/*
|
||||||
|
if($qrcv_buf[3]) {
|
||||||
|
xstrcpy((qsnd_buf+qsndbuflen),(qrcv_buf+3),self::CHAT_BUF-$qsndbuflen);
|
||||||
|
$qsndbuflen+=strlen((qrcv_buf+3));
|
||||||
|
if($qsndbuflen>self::CHAT_BUF-128) {
|
||||||
|
$qsndbuflen=self::CHAT_BUF-128;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$i=$chatprot;
|
||||||
|
$chatprot=-1;
|
||||||
|
chatsend(qsnd_buf);
|
||||||
|
if($chatlg) {
|
||||||
|
chatlog_done();
|
||||||
|
}
|
||||||
|
$chatlg=0;
|
||||||
|
$chatprot=i;
|
||||||
|
xstrcat($qsnd_buf,"\n * Chat closed\n",CHAT_BUF);
|
||||||
|
chatsend($qsnd_buf);
|
||||||
|
if($chattimer>1) {
|
||||||
|
qlcerase();
|
||||||
|
}
|
||||||
|
$qsndbuflen=0;
|
||||||
|
$chattimer=1;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$qsnd_buf[$qsndbuflen]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($qsndbuflen>0)
|
||||||
|
if(! $this->chatsend($qsnd_buf)) {
|
||||||
|
$qsndbuflen=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('- End', ['m' => __METHOD__]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function qrecvpkt(&$str): int
|
||||||
|
{
|
||||||
|
Log::debug('+ Start',['m'=>__METHOD__,'str'=>$str]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (! $this->xsend_cb) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
$rc = $this->xrecv($this->client->connection,$str,self::MSG_BUFFER-1,0);
|
||||||
|
Log::debug(sprintf(' - qrecvpkt Got [%x] (%d)',$rc,$rc),['m'=>__METHOD__]);
|
||||||
|
if ( $rc < 0 && $this->errno != 11 /*MSG_EAGAIN*/ ) {
|
||||||
|
if ($this->errno == self::ECONNREFUSED) {
|
||||||
|
$xsend_cb = NULL;
|
||||||
|
}
|
||||||
|
//DEBUG(('I',1,"can't recv (fd=%d): %s",ssock,strerror(errno)));
|
||||||
|
}
|
||||||
|
Log::debug(sprintf(' - qrecvpkt Got [%x] (%d)',$rc,$rc),['m'=>__METHOD__,'str'=>$str,'len'=>strlen($str)]);
|
||||||
|
if ($rc < 3 || ! substr($str,0,2)) {
|
||||||
|
Log::debug('+ End',['m'=>__METHOD__,'rc'=>0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//str[rc] = '\0';
|
||||||
|
if (! $rc)
|
||||||
|
$str = '';
|
||||||
|
|
||||||
|
Log::debug('+ End',['m'=>__METHOD__,'rc'=>$rc]);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function rxclose(&$f, int $what): int
|
||||||
|
{
|
||||||
|
Log::debug('+ Start',['m'=>__METHOD__,'what'=>$what,'f'=>$f]);
|
||||||
|
|
||||||
|
$cps=time()-$this->recvf->start;
|
||||||
|
$ss = '';
|
||||||
|
|
||||||
|
if(!$f || !$f) {
|
||||||
|
Log::debug('= End',['m'=>__METHOD__,'rc'=>self::FOP_ERROR]);
|
||||||
|
return self::FOP_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->recvf->toff+=$this->recvf->foff;
|
||||||
|
$this->recvf->stot+=$this->recvf->soff;
|
||||||
|
$p2=0;
|
||||||
|
if(! $cps) {
|
||||||
|
$cps=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cps=($this->recvf->foff-$this->recvf->soff)/$cps;
|
||||||
|
/*
|
||||||
|
IFPerl(if((ss=perl_end_recv(what))) {
|
||||||
|
if(!$ss) {
|
||||||
|
$what=self::FOP_SKIP;
|
||||||
|
} else {
|
||||||
|
$p2 = $ss; //xstrcpy(p2,ss,MAX_PATH);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
switch($what) {
|
||||||
|
case self::FOP_SUSPEND:
|
||||||
|
$ss="suspended";
|
||||||
|
break;
|
||||||
|
case self::FOP_SKIP:
|
||||||
|
$ss="skipped";
|
||||||
|
break;
|
||||||
|
case self::FOP_ERROR:
|
||||||
|
$ss="error";
|
||||||
|
break;
|
||||||
|
case self::FOP_OK:
|
||||||
|
$ss="ok";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ss="";
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(' -',['m'=>__METHOD__,'soff'=>$this->recvf->soff,'ss'=>$ss]);
|
||||||
|
if($this->recvf->soff) {
|
||||||
|
//write_log("rcvd: %s, %lu bytes (from %lu), %ld cps [%s]",
|
||||||
|
// recvf.fname, (long) recvf.foff, (long) recvf.soff, cps, ss);
|
||||||
|
} else {
|
||||||
|
//write_log("rcvd: %s, %lu bytes, %ld cps [%s]",
|
||||||
|
// recvf.fname, (long) recvf.foff, cps, ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($f);
|
||||||
|
$f='';
|
||||||
|
|
||||||
|
/*
|
||||||
|
snprintf(p, MAX_PATH, "%s/tmp/%s", cfgs(CFG_INBOUND), recvf.fname);
|
||||||
|
if($p2) {
|
||||||
|
if($p2!='/'&&*$p2=='.') {
|
||||||
|
$ss=xstrdup($p2);
|
||||||
|
snprintf($p2,MAX_PATH,"%s/%s",cfgs(CFG_INBOUND),$ss);
|
||||||
|
xfree($ss);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snprintf(p2, MAX_PATH, "%s/%s", cfgs(CFG_INBOUND), recvf.fname);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//$ut->actime=$ut->modtime=$this->recvf->mtime;
|
||||||
|
$this->recvf->foff=0;
|
||||||
|
switch($what) {
|
||||||
|
case self::FOP_SKIP:
|
||||||
|
unlink($p);
|
||||||
|
break;
|
||||||
|
case self::FOP_SUSPEND:
|
||||||
|
case self::FOP_ERROR:
|
||||||
|
/*
|
||||||
|
if($this->whattype($p)==self::IS_PKT&&cfgi(self::CFG_KILLBADPKT)) {
|
||||||
|
unlink($p);
|
||||||
|
} else {
|
||||||
|
//utime(p,&ut);
|
||||||
|
touch ($this->recvf->name,octdec($this->recvf->mtime));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
case self::FOP_OK:
|
||||||
|
$rc=isset($receive_callback)?receive_callback($p):0;
|
||||||
|
|
||||||
|
if($rc) {
|
||||||
|
//lunlink(p);
|
||||||
|
} else {
|
||||||
|
$ss=$p2+strlen($p2)-1;
|
||||||
|
$overwrite=0;
|
||||||
|
/*
|
||||||
|
for(i=cfgsl(CFG_ALWAYSOVERWRITE); i; i=i->next)
|
||||||
|
if(!xfnmatch(i->str,recvf.fname,FNM_PATHNAME)) {
|
||||||
|
$overwrite=1;
|
||||||
|
}
|
||||||
|
while(!$overwrite&&!stat(p2, &sb)&&p2[0]) {
|
||||||
|
if(sifname(ss)) {
|
||||||
|
ss--;
|
||||||
|
while('.' == *ss && ss >= p2) {
|
||||||
|
ss--;
|
||||||
|
}
|
||||||
|
if(ss < p2) {
|
||||||
|
write_log("can't find suitable name for %s: leaving in temporary directory",p);
|
||||||
|
p2[0] = '\x00';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(p2[0]) {
|
||||||
|
if(overwrite) {
|
||||||
|
lunlink(p2);
|
||||||
|
}
|
||||||
|
if(rename(p, p2)) {
|
||||||
|
write_log("can't rename %s to %s: %s",p,p2,strerror(errno));
|
||||||
|
} else {
|
||||||
|
utime(p2,&ut);
|
||||||
|
chmod(p2,cfgi(CFG_DEFPERM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
Log::debug(sprintf('Recevied [%s] with mtime [%s]',$this->f->name,$this->recvf->mtime),['m'=>__METHOD__]);
|
||||||
|
touch('/tmp/tmp/'.$this->f->name,$this->recvf->mtime);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($what==self::FOP_SKIP||$what==self::FOP_SUSPEND) {
|
||||||
|
$skipiftic=$what;
|
||||||
|
}
|
||||||
|
$this->recvf->start=0;
|
||||||
|
$this->recvf->ftot=0;
|
||||||
|
//$this->rxstatus=0;
|
||||||
|
Log::debug('= End',['m'=>__METHOD__,'rc'=>$what]);
|
||||||
|
return $what;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function rxopen(string $name,int $rtime,int $rsize,string &$f): int
|
||||||
|
{
|
||||||
|
Log::debug('+ Start',['m'=>__METHOD__,'name'=>$name,'rtime'=>$rtime,'rsize'=>$rsize,'f'=>$f]);
|
||||||
|
|
||||||
|
$ccs = '/tmp'; // @todo Base path needs to be a config item
|
||||||
|
$this->speed = 38400;
|
||||||
|
|
||||||
|
$prevcps = ($this->recvf->start&&(time()-$this->recvf->start>2))?$this->recvf->cps:$this->speed/10;
|
||||||
|
|
||||||
|
if(! $name) {
|
||||||
|
return self::FOP_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bn = basename($name);//xstrcpy($bn, qbasename($name), self::MAX_PATH);
|
||||||
|
Log::debug(sprintf(' - bn[%s]',$bn),['m'=>__METHOD__]);
|
||||||
|
//mapname((char*)bn, cfgs(CFG_MAPIN), MAX_PATH);
|
||||||
|
|
||||||
|
//$this->recvf->start=(int)decoct(time());
|
||||||
|
$this->recvf->start=time();
|
||||||
|
//xfree(recvf.fname);
|
||||||
|
$this->recvf->fname=$bn; //xstrdup($bn);
|
||||||
|
//dd(['rtime'=>$rtime,'start'=>$this->recvf->start]);
|
||||||
|
$this->recvf->mtime=$rtime; //-gmtoff($this->recvf->start);
|
||||||
|
$this->recvf->ftot=$rsize;
|
||||||
|
if($this->recvf->toff+$rsize > $this->recvf->ttot) {
|
||||||
|
$this->recvf->ttot+=$rsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->recvf->nf++;
|
||||||
|
if($this->recvf->nf > $this->recvf->allf) {
|
||||||
|
$this->recvf->allf++;
|
||||||
|
}
|
||||||
|
//IFPerl(if((rc=perl_on_recv())!=FOP_OK)return rc);
|
||||||
|
/*
|
||||||
|
if($this->whattype($name)==self::IS_PKT&&($rsize==60||!$rsize)&&cfgi(self::CFG_KILLBADPKT)) {
|
||||||
|
return self::FOP_SKIP;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
$rc=$skipiftic = 0; // @todo
|
||||||
|
$skipiftic=0;
|
||||||
|
if($rc&&istic($bn)&&cfgi(self::CFG_AUTOTICSKIP)) {
|
||||||
|
//write_log($rc==self::FOP_SKIP?$weskipstr:$wesusstr,$this->recvf->fname,"auto");
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
// @todo
|
||||||
|
/*
|
||||||
|
for($i=cfgsl(self::CFG_AUTOSKIP); $i; $i=$i->next)
|
||||||
|
if(!$this->xfnmatch($i->str,$bn, self::FNM_PATHNAME)) {
|
||||||
|
//write_log(weskipstr,$this->recvf.fname,"");
|
||||||
|
$skipiftic=self::FOP_SKIP;
|
||||||
|
return self::FOP_SKIP;
|
||||||
|
}
|
||||||
|
for($i=cfgsl(self::CFG_AUTOSUSPEND); $i; $i=$i->next)
|
||||||
|
if(!$this->xfnmatch($i->str, $bn, self::FNM_PATHNAME)) {
|
||||||
|
//write_log(wesusstr,$this->recvf->fname,"");
|
||||||
|
$skipiftic=self::FOP_SUSPEND;
|
||||||
|
return self::FOP_SUSPEND;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
$p = '/tmp/tmp/'; //@todo snprintf(p, MAX_PATH, "%s/tmp/", cfgs(CFG_INBOUND));
|
||||||
|
|
||||||
|
//if ($sb = stat($p)) // if(stat($p, &sb))
|
||||||
|
if(! is_dir($p) AND ! mkdir($p)) { // && $errno!=EEXIST
|
||||||
|
Log::debug(sprintf(' - dir doesnt exist and cannot make it? [%s]',$p),['m'=>__METHOD__,'rc'=>self::FOP_SUSPEND]);
|
||||||
|
//write_log("can't make directory %s: %s", p, strerror(errno));
|
||||||
|
//write_log(wesusstr,$this->recvf.fname,"");
|
||||||
|
$skipiftic=self::FOP_SUSPEND;
|
||||||
|
return self::FOP_SUSPEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
$p = sprintf('%s/%s',$ccs,$bn);// snprintf($p, self::MAX_PATH, "%s/%s", $ccs, $bn);
|
||||||
|
|
||||||
|
if(file_exists($p) AND ($sb=stat($p)) && $sb['size']==$rsize) {//if(!stat(p, &sb) && sb.st_size==rsize) {
|
||||||
|
Log::debug(sprintf(' - file exists and size is same? [%s]',$p),['m'=>__METHOD__,'sb'=>$sb,'rsize'=>$rsize,'rc'=>self::FOP_SKIP]);
|
||||||
|
//write_log(weskipstr,$this->recvf.fname,"");
|
||||||
|
$skipiftic=self::FOP_SKIP;
|
||||||
|
return self::FOP_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
//dd(['maxpath'=>self::MAX_PATH,'bn'=>$bn]);
|
||||||
|
//snprintf($p, self::MAX_PATH, "%s/tmp/%s", $ccs, $bn);
|
||||||
|
$p = sprintf('%s/tmp/%s',$ccs,$bn);
|
||||||
|
|
||||||
|
// If the file exists
|
||||||
|
if (file_exists($p) AND $sb=stat($p)) {//if(!stat(p, &sb)) {
|
||||||
|
Log::debug(sprintf(' - file exists... [%s]',$p),['m'=>__METHOD__,'sb'=>$sb,'rsize'=>$rsize,
|
||||||
|
'mtime'=>$this->recvf->mtime,
|
||||||
|
//'mtime-decopt'=>(int)decoct($this->recvf->mtime),
|
||||||
|
//'mtime-octdec'=>(int)octdec($this->recvf->mtime),
|
||||||
|
'sbmtime'=>$sb['mtime'],
|
||||||
|
'sbmtime-decopt'=>(int)decoct($sb['mtime']),
|
||||||
|
//'sbmtime-octdec'=>(int)octdec($sb['mtime']),
|
||||||
|
]);
|
||||||
|
// @todo binkp doesnt use octal.
|
||||||
|
if($sb['size']<$rsize && $sb['mtime']==(int)$this->recvf->mtime) {
|
||||||
|
Log::debug(sprintf(' - attempt open for append [%s]',$p),['m'=>__METHOD__]);
|
||||||
|
|
||||||
|
$f=fopen($p, "ab");
|
||||||
|
if(!$f) {
|
||||||
|
Log::debug(sprintf(' - attempt open for append FAILED [%s]',$p),['m'=>__METHOD__,'rc'=>self::FOP_SUSPEND]);
|
||||||
|
//write_log("can't open file %s for writing: %s", p,strerror(errno));
|
||||||
|
//write_log(wesusstr,$this->recvf.fname,"");
|
||||||
|
$skipiftic=self::FOP_SUSPEND;
|
||||||
|
return self::FOP_SUSPEND;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf(' - FTELL REPORTS [%s]',serialize(ftell($f))),['m'=>__METHOD__]);
|
||||||
|
// ftell() gives undefined results for append-only streams (opened with "a" flag).
|
||||||
|
$this->recvf->foff = $this->recvf->soff = $sb['size']; //ftell($f);
|
||||||
|
Log::debug(sprintf(' - open for append [%s] at [%d]',$p,$this->recvf->soff),['m'=>__METHOD__,'rc'=>self::FOP_CONT]);
|
||||||
|
//if(cfgi(self::CFG_ESTIMATEDTIME)) {
|
||||||
|
//write_log("start recv: %s, %lu bytes (from %lu), estimated time %s",
|
||||||
|
// $this->recvf.fname, (long) rsize, (long) $this->recvf.soff, estimatedtime(rsize-$this->recvf.soff,prevcps,effbaud));
|
||||||
|
//}
|
||||||
|
return self::FOP_CONT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$f=fopen($p, "wb");
|
||||||
|
if(!$f) {
|
||||||
|
//write_log("can't open file %s for writing: %s", p,strerror(errno));
|
||||||
|
//write_log(wesusstr,$this->recvf.fname,"");
|
||||||
|
$skipiftic=self::FOP_SUSPEND;
|
||||||
|
return self::FOP_SUSPEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
//dd(['sb'=>$sb,'recvf'=>$this->recvf]);
|
||||||
|
Log::debug(sprintf(' - new file created [%s]',$p),['m'=>__METHOD__,'rc'=>self::FOP_OK]);
|
||||||
|
$this->recvf->foff = $this->recvf->soff = 0;
|
||||||
|
//if(cfgi(self::CFG_ESTIMATEDTIME)) {
|
||||||
|
//write_log("start recv: %s, %lu bytes, estimated time %s",
|
||||||
|
// $this->recvf.fname, (long) rsize, estimatedtime(rsize,prevcps,effbaud));
|
||||||
|
//}
|
||||||
|
return self::FOP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setClient(SocketClient $client): void
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timer_expired(int $timer): int
|
||||||
|
{
|
||||||
|
return (time()>=$timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timer_rest(int $timer): int
|
||||||
|
{
|
||||||
|
return (($timer)-time());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timer_set(int $expire): int
|
||||||
|
{
|
||||||
|
return (time()+$expire);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function txclose(&$f, int $what):int
|
||||||
|
{
|
||||||
|
$cps=time()-$this->sendf->start;
|
||||||
|
|
||||||
|
if(!$f) {
|
||||||
|
return self::FOP_ERROR;
|
||||||
|
}
|
||||||
|
$this->sendf->toff+=$this->sendf->foff;
|
||||||
|
$this->sendf->stot+=$this->sendf->soff;
|
||||||
|
|
||||||
|
if(!$cps) {
|
||||||
|
$cps=1;
|
||||||
|
}
|
||||||
|
$cps=($this->sendf->foff-$this->sendf->soff)/$cps;
|
||||||
|
//IFPerl(perl_end_send(what));
|
||||||
|
switch($what) {
|
||||||
|
case self::FOP_SUSPEND:
|
||||||
|
$ss="suspended";
|
||||||
|
break;
|
||||||
|
case self::FOP_SKIP:
|
||||||
|
$ss="skipped";
|
||||||
|
break;
|
||||||
|
case self::FOP_ERROR:
|
||||||
|
$ss="error";
|
||||||
|
break;
|
||||||
|
case self::FOP_OK:
|
||||||
|
$ss="ok";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$ss="";
|
||||||
|
}
|
||||||
|
if($this->sendf->soff) {}
|
||||||
|
//write_log("sent: %s, %lu bytes (from %lu), %ld cps [%s]", sendf.fname, (long) sendf.foff, (long) sendf.soff, cps, ss);
|
||||||
|
else {}
|
||||||
|
//write_log("sent: %s, %lu bytes, %ld cps [%s]",sendf.fname, (long) sendf.foff, cps, ss);
|
||||||
|
$this->sendf->foff=0;
|
||||||
|
$this->sendf->ftot=0;
|
||||||
|
$this->sendf->start=0;
|
||||||
|
fclose($f);
|
||||||
|
$f=NULL;
|
||||||
|
return $what;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function xrecv($sock,&$buf,int $len,int $wait):int
|
||||||
|
{
|
||||||
|
Log::debug('+ Start',['m'=> __METHOD__]);
|
||||||
|
|
||||||
|
$l = 0;
|
||||||
|
|
||||||
|
if (! $sock) {
|
||||||
|
$this->errno = self::EBADF;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $wait) {
|
||||||
|
Log::debug(' - Not wait',['m'=> __METHOD__]);
|
||||||
|
$tv_tv_sec = 0;
|
||||||
|
$tv_tv_usec = 0;
|
||||||
|
$rfd = 0; //FD_ZERO(&rfd);
|
||||||
|
//FD_SET(sock, &rfd);
|
||||||
|
$read = [$sock];
|
||||||
|
$write = [];
|
||||||
|
$except = [];
|
||||||
|
$rc = socket_select($read,$write,$except,0,0);
|
||||||
|
//$foo = '';
|
||||||
|
//$rc = socket_recv($this->client->connection,$foo,1,MSG_PEEK | MSG_DONTWAIT);
|
||||||
|
Log::debug(' - socket_select',['m'=> __METHOD__,'rc'=>$rc,'read'=>$read,'write'=>$write,'except'=>$except]);
|
||||||
|
//$rc = $this->client->hasData(0);
|
||||||
|
if ($rc < 1) {
|
||||||
|
if (! $rc) {
|
||||||
|
$this->errno = 11; //MSG_EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(sprintf(' - doing a read now for [%d].',$len));
|
||||||
|
$rc = socket_recv($read[0],$l,$len,MSG_PEEK | MSG_DONTWAIT);
|
||||||
|
Log::debug(' - socket_recv PEEK', ['m' => __METHOD__,'l'=>$l,'rc'=>$rc]);
|
||||||
|
|
||||||
|
if ($rc <= 0) {
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($rc == 2) {
|
||||||
|
return 2;
|
||||||
|
//l = I2H16(l);
|
||||||
|
// $l = unpack('s',$l);
|
||||||
|
$l = ((ord($l[0])&0x7f)<<8)+ord($l[1]);
|
||||||
|
|
||||||
|
// dd(['l'=>$l,'0'=>ord($l[0])&0xf,'00'=>((ord($l[0])&0x7f)<<8)+ord($l[1]),'1'=>ord($l[1]),'hex'=>sprintf('%x',unpack('v',$l)),'len'=>$len]);
|
||||||
|
if (! $l) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($l > $len) {
|
||||||
|
$l = $len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug(' - L is ',['m' => __METHOD__,'l'=>min($l+$rc,$len)]);
|
||||||
|
$rc = socket_recv($sock,$buf,min($l+$rc,$len),MSG_WAITALL);
|
||||||
|
Log::debug(' - socket_recv GOT', ['m' => __METHOD__,'buf'=>$buf,'len'=>strlen($buf),'rc'=>$rc]);
|
||||||
|
if ($rc <= 0) {
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
$rc = min($rc - 2, strlen($buf));
|
||||||
|
if ($rc < 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ($rc >= $len) {
|
||||||
|
$rc = $len - 2;
|
||||||
|
}
|
||||||
|
$buf = substr($buf,2,$rc); //memcpy(buf, buf + 2, rc);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tty_bufc(int $ch): int
|
||||||
|
{
|
||||||
|
return $this->tty_bufblock( chr($ch), 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// SocketClient::buffer_add()
|
||||||
|
public function tty_bufblock(string $data, int $nbytes): int
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start [%s] (%d)',__METHOD__,$data,$nbytes));
|
||||||
|
$rc = self::OK;
|
||||||
|
$txptr = self::TX_BUF_SIZE - $this->tty_tx_free;
|
||||||
|
$nptr = 0;
|
||||||
|
|
||||||
|
$this->tty_status = self::TTY_SUCCESS;
|
||||||
|
|
||||||
|
while ( $nbytes ) {
|
||||||
|
Log::debug(sprintf(' - Num Bytes [%d]: TX Free [%d]',$nbytes,$this->tty_tx_free));
|
||||||
|
|
||||||
|
if ( $nbytes > $this->tty_tx_free ) {
|
||||||
|
do {
|
||||||
|
$this->tty_bufflush( 5 );
|
||||||
|
if ( $this->tty_status == self::TTY_SUCCESS ) {
|
||||||
|
$n = min($this->tty_tx_free,$nbytes);
|
||||||
|
$this->tty_tx_buf = substr($data,$nptr,$n);
|
||||||
|
$this->tty_tx_free -= $n;
|
||||||
|
$nbytes -= $n;
|
||||||
|
$nptr += $n;
|
||||||
|
}
|
||||||
|
} while ( $this->tty_status != self::TTY_SUCCESS );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log::debug(sprintf(' -'),['data'=>$data,'nptr'=>$nptr,'txptr'=>$txptr,'tx_buff'=>substr($data,$nptr+$txptr,$nbytes)]);
|
||||||
|
|
||||||
|
$this->tty_tx_buf .= $data;// memcpy( (void *) (tty_tx_buf + txptr), nptr, nbytes );
|
||||||
|
$this->tty_tx_free -= $nbytes;
|
||||||
|
$nbytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('= End',['m'=>__METHOD__,'rc'=>$rc]);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tty_bufclear(): void
|
||||||
|
{
|
||||||
|
$this->tty_tx_ptr = 0;
|
||||||
|
$this->tty_tx_free = self::TX_BUF_SIZE;
|
||||||
|
$this->tty_tx_buf = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tty_bufflush(int $tsec): int
|
||||||
|
{
|
||||||
|
Log::debug('+ Start',['m'=>__METHOD__,'tsec'=>$tsec,'txfree'=>$this->tty_tx_free,'txptr'=>$this->tty_tx_ptr,'txbuff'=>$this->tty_tx_buf]);
|
||||||
|
|
||||||
|
$rc = self::OK;
|
||||||
|
$restsize = self::TX_BUF_SIZE - $this->tty_tx_free - $this->tty_tx_ptr;
|
||||||
|
|
||||||
|
$tm = $this->timer_set( $tsec );
|
||||||
|
while (self::TX_BUF_SIZE != $this->tty_tx_free ) {
|
||||||
|
$wd = true;
|
||||||
|
$tv = $this->timer_rest( $tm );
|
||||||
|
|
||||||
|
if (( $rc = $this->client->canSend($tv) > 0 && $wd )) {
|
||||||
|
|
||||||
|
Log::debug(sprintf(' - Sending [%d]: Buffer [%s] Size [%d]',substr($this->tty_tx_buf,$this->tty_tx_ptr,$restsize),$this->tty_tx_buf,$restsize));
|
||||||
|
$rc = $this->client->send(substr($this->tty_tx_buf,$this->tty_tx_ptr,$restsize),0,$restsize);
|
||||||
|
Log::debug(sprintf(' - Sent [%d]: Buffer [%s] Size [%d]',$rc,$this->tty_tx_buf,$restsize));
|
||||||
|
|
||||||
|
if ($rc == $restsize ) {
|
||||||
|
$this->tty_bufclear();
|
||||||
|
} else if ( $rc > 0 ) {
|
||||||
|
$this->tty_tx_ptr += $rc;
|
||||||
|
$restsize -= $rc;
|
||||||
|
} else if ( $rc < 0 && $this->tty_status != self::TTY_TIMEOUT ) {
|
||||||
|
return self::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->timer_expired( $tm )) {
|
||||||
|
return self::ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('= End',['m'=>__METHOD__,'rc'=>$rc]);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tty_getc(int $timeout): int
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('%s: + Start [%d]',__METHOD__,$timeout),['rx_left'=>$this->tty_rx_left]);
|
||||||
|
|
||||||
|
if ($this->tty_rx_left == 0 ) {
|
||||||
|
if ($this->client->hasData($timeout) > 0) {
|
||||||
|
if (! ($this->tty_rx_buf = $this->client->read(0,self::RX_BUF_SIZE))) {
|
||||||
|
Log::debug(sprintf('%s: - Nothing read',__METHOD__));
|
||||||
|
|
||||||
|
return ($this->EWBOEA()) ? self::TTY_TIMEOUT : self::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info(sprintf('%s: - Read [%d]',__METHOD__,strlen($this->tty_rx_buf)));
|
||||||
|
$this->tty_rx_ptr = 0;
|
||||||
|
$this->tty_rx_left = strlen($this->tty_rx_buf);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return ( $this->tty_gothup ? self::TTY_HANGUP : self::TTY_TIMEOUT );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rc = ord(substr($this->tty_rx_buf,$this->tty_rx_ptr,1)); //tty_rx_buf[tty_rx_ptr++];
|
||||||
|
|
||||||
|
$this->tty_rx_left--;
|
||||||
|
$this->tty_rx_ptr++;
|
||||||
|
|
||||||
|
Log::debug(sprintf('%s: = Return [%x] (%c)',__METHOD__,$rc,$rc));
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tty_getc_timed(int $timeout): int
|
||||||
|
{
|
||||||
|
$t = time();
|
||||||
|
|
||||||
|
$rc = $this->tty_getc($timeout);
|
||||||
|
$timeout -= (time() - $t);
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tty_purge(): void
|
||||||
|
{
|
||||||
|
//DEBUG(('M',3,"tty_purge"));
|
||||||
|
|
||||||
|
$this->tty_rx_ptr = $this->tty_rx_left = 0;
|
||||||
|
/*
|
||||||
|
if ( isatty( tty_fd )) {
|
||||||
|
tio_flush_queue( tty_fd, TIO_Q_IN );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tty_purgeout(): void
|
||||||
|
{
|
||||||
|
//DEBUG(('M',3,"tty_purgeout"));
|
||||||
|
|
||||||
|
$this->tty_bufclear();
|
||||||
|
/*
|
||||||
|
if ( isatty( tty_fd )) {
|
||||||
|
tio_flush_queue( tty_fd, TIO_Q_OUT );
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private function tty_putc(string $ch):int
|
||||||
|
{
|
||||||
|
$this->tty_bufblock($ch,1);
|
||||||
|
return $this->tty_bufflush(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tty_select($rd,$wd,int $tval): int
|
||||||
|
{
|
||||||
|
//DEBUG(('T',2,"tty_select"));
|
||||||
|
$rfd = $this->client->connection;
|
||||||
|
$wfd = $this->client->connection;
|
||||||
|
//dump($rfd,$wfd);
|
||||||
|
|
||||||
|
//FD_ZERO( &rfd );
|
||||||
|
//FD_ZERO( &wfd );
|
||||||
|
//if ($rd && $rd) {
|
||||||
|
//FD_SET($tty_fd,$rfd);
|
||||||
|
$rd = FALSE;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//if ($wd && $wd ) {
|
||||||
|
//FD_SET($tty_fd,$wfd);
|
||||||
|
$wd = FALSE;
|
||||||
|
//}
|
||||||
|
|
||||||
|
$tty_error = 0;
|
||||||
|
$read = [$this->client->connection];
|
||||||
|
$write = [$this->client->connection];
|
||||||
|
$except = [];
|
||||||
|
dump('calling socket_select',['timeout'=>$tval,'read'=>$read,'write'=>$write]);
|
||||||
|
$rc = socket_select($read, $write, $except,($tval ?: NULL));
|
||||||
|
dump('done socket_select',$tval);
|
||||||
|
|
||||||
|
$tty_error = socket_last_error();
|
||||||
|
$tty_status = self::TTY_SUCCESS;
|
||||||
|
|
||||||
|
if ($rc < 0 ) {
|
||||||
|
if (EWBOEA()) {
|
||||||
|
$tty_status = self::TTY_TIMEOUT;
|
||||||
|
|
||||||
|
} else if ($errno == self::EINTR) {
|
||||||
|
$tty_status = ($tty_online && $tty_gothup ) ? self::TTY_HANGUP : self::TTY_TIMEOUT;
|
||||||
|
} else if ($errno == self::EPIPE) {
|
||||||
|
$tty_gothup = self::HUP_LINE;
|
||||||
|
$tty_status = self::TTY_HANGUP;
|
||||||
|
} else {
|
||||||
|
$tty_status = self::TTY_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ($rc == 0) {
|
||||||
|
$tty_status = self::TTY_TIMEOUT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
} else {
|
||||||
|
if ($rd /*&& FD_ISSET( tty_fd, &rfd )*) {
|
||||||
|
$rd = TRUE;
|
||||||
|
}
|
||||||
|
if ($wd /*&& FD_ISSET( tty_fd, &wfd )*) {
|
||||||
|
$wd = TRUE;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//DEBUG(('T',2,"tty_select: fd=%d rc=%i (rd=%s, wd=%s)", tty_fd, rc, FDS( rd ), FDS( wd )));
|
||||||
|
|
||||||
|
return $rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function BUFCHAR(int $c)
|
||||||
|
{
|
||||||
|
$this->tty_bufc($c);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function BUFFLUSH(int $tsec): int
|
||||||
|
{
|
||||||
|
return $this->tty_bufflush($tsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo this should go into SocketCLient?
|
||||||
|
protected function EWBOEA(): bool
|
||||||
|
{
|
||||||
|
$errno = socket_last_error($this->client->connection);
|
||||||
|
Log::debug('+ Start',['m'=> __METHOD__,'errno'=>$errno]);
|
||||||
|
return $errno === 11 /*MSG_EAGAIN*/;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GETCHAR(int $t): int
|
||||||
|
{
|
||||||
|
return $this->tty_getc($t);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function GETCHART($t): int
|
||||||
|
{
|
||||||
|
return $this->tty_getc_timed($t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function NOTTO(string $ch): int
|
||||||
|
{
|
||||||
|
return (($ch)==self::ERROR || ($ch)==self::RCDO || ($ch)==self::EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function PUTSTR(string $s):void
|
||||||
|
{
|
||||||
|
$this->tty_bufblock($s,strlen($s));
|
||||||
|
$this->BUFFLUSH( 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function PURGEALL(): void
|
||||||
|
{
|
||||||
|
$this->tty_purge();
|
||||||
|
$this->tty_purgeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function PUTCHAR(string $c)
|
||||||
|
{
|
||||||
|
$this->tty_putc( $c );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function PUTSTRCR(string $str)
|
||||||
|
{
|
||||||
|
$this->tty_bufblock($str."\r",strlen($str)+1);
|
||||||
|
return $this->tty_bufflush(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class rnode
|
||||||
|
{
|
||||||
|
public $starttime = 0;
|
||||||
|
public $options = 0;
|
||||||
|
public $netmail = 0;
|
||||||
|
public $files = 0;
|
||||||
|
public $ewboea = 0;
|
||||||
|
public $phone = '';
|
||||||
|
}
|
51
app/Console/Commands/BinkpReceive.php
Normal file
51
app/Console/Commands/BinkpReceive.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Sock\SocketException;
|
||||||
|
use App\Classes\Sock\SocketServer;
|
||||||
|
|
||||||
|
use App\Classes\Protocol\Binkd as BinkdClass;
|
||||||
|
|
||||||
|
class BinkpReceive extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'binkp:receive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'BINKP receive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info('Listening for BINKP connections...');
|
||||||
|
|
||||||
|
$server = new SocketServer(24554,'0.0.0.0');
|
||||||
|
$server->setConnectionHandler([new BinkdClass,'onConnect']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server->listen();
|
||||||
|
|
||||||
|
} catch (SocketException $e) {
|
||||||
|
if ($e->getMessage() == 'Can\'t accept connections: "Success"')
|
||||||
|
Log::debug('Server Terminated');
|
||||||
|
else
|
||||||
|
Log::emergency('Uncaught Message: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
app/Console/Commands/BinkpSend.php
Normal file
48
app/Console/Commands/BinkpSend.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Protocol\Binkd as BinkdClass;
|
||||||
|
use App\Classes\Sock\SocketClient;
|
||||||
|
use App\Models\Node;
|
||||||
|
|
||||||
|
class BinkpSend extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'binkp:send {ftn : FTN to Send to}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Binkp send';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @throws \App\Classes\Sock\SocketException
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info('Call BINKP send');
|
||||||
|
|
||||||
|
$no = Node::findFTN($this->argument('ftn'));
|
||||||
|
|
||||||
|
$client = SocketClient::create($no->address,$no->port);
|
||||||
|
|
||||||
|
$o = new BinkdClass;
|
||||||
|
$o->session(BinkdClass::SESSION_BINKP,$client,$no);
|
||||||
|
|
||||||
|
Log::info(sprintf('Connection ended: %s',$client->getAddress()),['m'=>__METHOD__]);
|
||||||
|
}
|
||||||
|
}
|
51
app/Console/Commands/EMSIReceive.php
Normal file
51
app/Console/Commands/EMSIReceive.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Sock\SocketException;
|
||||||
|
use App\Classes\Sock\SocketServer;
|
||||||
|
|
||||||
|
use App\Classes\Protocol\EMSI as EMSIClass;
|
||||||
|
|
||||||
|
class EMSIReceive extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'emsi:receive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'EMSI receive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info('Listening for EMSI connections...');
|
||||||
|
|
||||||
|
$server = new SocketServer(60179,'0.0.0.0');
|
||||||
|
$server->setConnectionHandler([new EMSIClass,'onConnect']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server->listen();
|
||||||
|
|
||||||
|
} catch (SocketException $e) {
|
||||||
|
if ($e->getMessage() == 'Can\'t accept connections: "Success"')
|
||||||
|
Log::debug('Server Terminated');
|
||||||
|
else
|
||||||
|
Log::emergency('Uncaught Message: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
app/Console/Commands/EMSISend.php
Normal file
48
app/Console/Commands/EMSISend.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Node;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Sock\SocketClient;
|
||||||
|
|
||||||
|
use App\Classes\Protocol\EMSI as EMSIClass;
|
||||||
|
|
||||||
|
class EMSISend extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'emsi:send {ftn : FTN to Send to}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'EMSI send';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
* @throws \App\Classes\Sock\SocketException
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info('Call EMSI send');
|
||||||
|
|
||||||
|
$no = Node::findFTN($this->argument('ftn'));
|
||||||
|
|
||||||
|
$client = SocketClient::create($no->address,$no->port,38400);
|
||||||
|
|
||||||
|
$o = new EMSIClass;
|
||||||
|
$o->session(EMSIClass::SESSION_AUTO,$client,$no);
|
||||||
|
|
||||||
|
Log::info(sprintf('Connection ended: %s',$client->getAddress()),['m'=>__METHOD__]);
|
||||||
|
}
|
||||||
|
}
|
@ -60,7 +60,11 @@ class ImportPacket extends Command
|
|||||||
'msgid'=>$o->msgid,
|
'msgid'=>$o->msgid,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$oo->area = $o->echoarea;
|
if (md5(utf8_decode($eo->message)) == md5($o->message))
|
||||||
|
{
|
||||||
|
$this->warn(sprintf('Duplicate message: %s@%s with id: %s',$o->from,$o->fqfa,$o->msgid));
|
||||||
|
break 2;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
50
app/Console/Commands/ZmodemReceive.php
Normal file
50
app/Console/Commands/ZmodemReceive.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Sock\SocketException;
|
||||||
|
use App\Classes\Sock\SocketServer;
|
||||||
|
use App\Classes\Protocol\Zmodem as ZmodemClass;
|
||||||
|
|
||||||
|
class ZmodemReceive extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'zmodem:receive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'ZMODEM receive';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info('Listening for ZMODEM connections...');
|
||||||
|
|
||||||
|
$server = new SocketServer(60177,'0.0.0.0');
|
||||||
|
$server->setConnectionHandler([new ZmodemClass,'onConnect']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$server->listen();
|
||||||
|
|
||||||
|
} catch (SocketException $e) {
|
||||||
|
if ($e->getMessage() == 'Can\'t accept connections: "Success"')
|
||||||
|
Log::debug('Server Terminated');
|
||||||
|
else
|
||||||
|
Log::emergency('Uncaught Message: '.$e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
app/Console/Commands/ZmodemSend.php
Normal file
45
app/Console/Commands/ZmodemSend.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Classes\Protocol;
|
||||||
|
use App\Classes\Sock\SocketClient;
|
||||||
|
use App\Classes\Protocol\Zmodem as ZmodemClass;
|
||||||
|
|
||||||
|
class ZmodemSend extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'zmodem:send {ip}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'ZMODEM send';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Log::info('Call ZMODEM send');
|
||||||
|
|
||||||
|
[$address,$service_port] = explode(':',$this->argument('ip'),2);
|
||||||
|
$client = SocketClient::create($address,$service_port);
|
||||||
|
|
||||||
|
$o = new ZmodemClass;
|
||||||
|
$o->session(Protocol::SESSION_ZMODEM,$client);
|
||||||
|
|
||||||
|
Log::info(sprintf('Connection ended: %s',$client->getAddress()),['m'=>__METHOD__]);
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,8 @@ class DomainController extends Controller
|
|||||||
public function add_edit(Request $request,Domain $o)
|
public function add_edit(Request $request,Domain $o)
|
||||||
{
|
{
|
||||||
if ($request->post()) {
|
if ($request->post()) {
|
||||||
|
// @todo Add validation that we can only have 1 "default" domain for a zone.
|
||||||
|
// The default domain is used when a node connects and doesnt use a full 5D address, eg: 10:999/1 vs 10:999/1@private
|
||||||
foreach (['name','dnsdomain','active','notes'] as $key)
|
foreach (['name','dnsdomain','active','notes'] as $key)
|
||||||
$o->{$key} = $request->post($key);
|
$o->{$key} = $request->post($key);
|
||||||
|
|
||||||
|
84
app/Interfaces/CRC.php
Normal file
84
app/Interfaces/CRC.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
interface CRC
|
||||||
|
{
|
||||||
|
const CRC16USD_INIT = 0x0000;
|
||||||
|
const LSZ_INIT_CRC16 = self::CRC16USD_INIT;
|
||||||
|
|
||||||
|
const CRC32_INIT = (0xffffffff);
|
||||||
|
const LSZ_INIT_CRC32 = self::CRC32_INIT;
|
||||||
|
|
||||||
|
/* CRC polynomial 0x1021 -- CCITT upside-down CRC16. EMSI,ZModem,Janus. */
|
||||||
|
public const crc16usd_tab = [
|
||||||
|
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
|
||||||
|
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
|
||||||
|
0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
|
||||||
|
0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
|
||||||
|
0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
|
||||||
|
0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
|
||||||
|
0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
|
||||||
|
0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
|
||||||
|
0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
|
||||||
|
0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
|
||||||
|
0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
|
||||||
|
0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
|
||||||
|
0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
|
||||||
|
0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
|
||||||
|
0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
|
||||||
|
0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
|
||||||
|
0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
|
||||||
|
0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
|
||||||
|
0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
|
||||||
|
0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
|
||||||
|
0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
|
||||||
|
0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
|
||||||
|
0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
|
||||||
|
0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
|
||||||
|
0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
|
||||||
|
0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
|
||||||
|
0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
|
||||||
|
0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
|
||||||
|
0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
|
||||||
|
0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
|
||||||
|
0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
|
||||||
|
0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
|
||||||
|
];
|
||||||
|
|
||||||
|
/* CRC polynomial 0xedb88320 -- CCITT CRC32. */
|
||||||
|
public const crc32_tab = [
|
||||||
|
0x00000000,0x77073096,0xee0e612c,0x990951ba,0x076dc419,0x706af48f,0xe963a535,0x9e6495a3,
|
||||||
|
0x0edb8832,0x79dcb8a4,0xe0d5e91e,0x97d2d988,0x09b64c2b,0x7eb17cbd,0xe7b82d07,0x90bf1d91,
|
||||||
|
0x1db71064,0x6ab020f2,0xf3b97148,0x84be41de,0x1adad47d,0x6ddde4eb,0xf4d4b551,0x83d385c7,
|
||||||
|
0x136c9856,0x646ba8c0,0xfd62f97a,0x8a65c9ec,0x14015c4f,0x63066cd9,0xfa0f3d63,0x8d080df5,
|
||||||
|
0x3b6e20c8,0x4c69105e,0xd56041e4,0xa2677172,0x3c03e4d1,0x4b04d447,0xd20d85fd,0xa50ab56b,
|
||||||
|
0x35b5a8fa,0x42b2986c,0xdbbbc9d6,0xacbcf940,0x32d86ce3,0x45df5c75,0xdcd60dcf,0xabd13d59,
|
||||||
|
0x26d930ac,0x51de003a,0xc8d75180,0xbfd06116,0x21b4f4b5,0x56b3c423,0xcfba9599,0xb8bda50f,
|
||||||
|
0x2802b89e,0x5f058808,0xc60cd9b2,0xb10be924,0x2f6f7c87,0x58684c11,0xc1611dab,0xb6662d3d,
|
||||||
|
0x76dc4190,0x01db7106,0x98d220bc,0xefd5102a,0x71b18589,0x06b6b51f,0x9fbfe4a5,0xe8b8d433,
|
||||||
|
0x7807c9a2,0x0f00f934,0x9609a88e,0xe10e9818,0x7f6a0dbb,0x086d3d2d,0x91646c97,0xe6635c01,
|
||||||
|
0x6b6b51f4,0x1c6c6162,0x856530d8,0xf262004e,0x6c0695ed,0x1b01a57b,0x8208f4c1,0xf50fc457,
|
||||||
|
0x65b0d9c6,0x12b7e950,0x8bbeb8ea,0xfcb9887c,0x62dd1ddf,0x15da2d49,0x8cd37cf3,0xfbd44c65,
|
||||||
|
0x4db26158,0x3ab551ce,0xa3bc0074,0xd4bb30e2,0x4adfa541,0x3dd895d7,0xa4d1c46d,0xd3d6f4fb,
|
||||||
|
0x4369e96a,0x346ed9fc,0xad678846,0xda60b8d0,0x44042d73,0x33031de5,0xaa0a4c5f,0xdd0d7cc9,
|
||||||
|
0x5005713c,0x270241aa,0xbe0b1010,0xc90c2086,0x5768b525,0x206f85b3,0xb966d409,0xce61e49f,
|
||||||
|
0x5edef90e,0x29d9c998,0xb0d09822,0xc7d7a8b4,0x59b33d17,0x2eb40d81,0xb7bd5c3b,0xc0ba6cad,
|
||||||
|
0xedb88320,0x9abfb3b6,0x03b6e20c,0x74b1d29a,0xead54739,0x9dd277af,0x04db2615,0x73dc1683,
|
||||||
|
0xe3630b12,0x94643b84,0x0d6d6a3e,0x7a6a5aa8,0xe40ecf0b,0x9309ff9d,0x0a00ae27,0x7d079eb1,
|
||||||
|
0xf00f9344,0x8708a3d2,0x1e01f268,0x6906c2fe,0xf762575d,0x806567cb,0x196c3671,0x6e6b06e7,
|
||||||
|
0xfed41b76,0x89d32be0,0x10da7a5a,0x67dd4acc,0xf9b9df6f,0x8ebeeff9,0x17b7be43,0x60b08ed5,
|
||||||
|
0xd6d6a3e8,0xa1d1937e,0x38d8c2c4,0x4fdff252,0xd1bb67f1,0xa6bc5767,0x3fb506dd,0x48b2364b,
|
||||||
|
0xd80d2bda,0xaf0a1b4c,0x36034af6,0x41047a60,0xdf60efc3,0xa867df55,0x316e8eef,0x4669be79,
|
||||||
|
0xcb61b38c,0xbc66831a,0x256fd2a0,0x5268e236,0xcc0c7795,0xbb0b4703,0x220216b9,0x5505262f,
|
||||||
|
0xc5ba3bbe,0xb2bd0b28,0x2bb45a92,0x5cb36a04,0xc2d7ffa7,0xb5d0cf31,0x2cd99e8b,0x5bdeae1d,
|
||||||
|
0x9b64c2b0,0xec63f226,0x756aa39c,0x026d930a,0x9c0906a9,0xeb0e363f,0x72076785,0x05005713,
|
||||||
|
0x95bf4a82,0xe2b87a14,0x7bb12bae,0x0cb61b38,0x92d28e9b,0xe5d5be0d,0x7cdcefb7,0x0bdbdf21,
|
||||||
|
0x86d3d2d4,0xf1d4e242,0x68ddb3f8,0x1fda836e,0x81be16cd,0xf6b9265b,0x6fb077e1,0x18b74777,
|
||||||
|
0x88085ae6,0xff0f6a70,0x66063bca,0x11010b5c,0x8f659eff,0xf862ae69,0x616bffd3,0x166ccf45,
|
||||||
|
0xa00ae278,0xd70dd2ee,0x4e048354,0x3903b3c2,0xa7672661,0xd06016f7,0x4969474d,0x3e6e77db,
|
||||||
|
0xaed16a4a,0xd9d65adc,0x40df0b66,0x37d83bf0,0xa9bcae53,0xdebb9ec5,0x47b2cf7f,0x30b5ffe9,
|
||||||
|
0xbdbdf21c,0xcabac28a,0x53b39330,0x24b4a3a6,0xbad03605,0xcdd70693,0x54de5729,0x23d967bf,
|
||||||
|
0xb3667a2e,0xc4614ab8,0x5d681b02,0x2a6f2b94,0xb40bbe37,0xc30c8ea1,0x5a05df1b,0x2d02ef8d
|
||||||
|
];
|
||||||
|
}
|
30
app/Interfaces/Zmodem.php
Normal file
30
app/Interfaces/Zmodem.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Interfaces;
|
||||||
|
|
||||||
|
interface Zmodem
|
||||||
|
{
|
||||||
|
/* ZMODEM PROTOCOLS
|
||||||
|
* # 1 - ZModem
|
||||||
|
* # Z - ZedZap (ZModem with 8k blocks)
|
||||||
|
* # D - DirZap (ZModem with 8k blocks and minimal escaping)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public const P_NONE = 0x0000; /* 0 0000 0000 */
|
||||||
|
public const P_NCP = 0x0001; /* 0 0000 0001 */
|
||||||
|
public const P_BINKP = 0x0001; /* 0 0000 0001 */
|
||||||
|
public const P_ZMODEM = 0x0002; /* 0 0000 0010 */
|
||||||
|
public const P_ZEDZAP = 0x0004; /* 0 0000 0100 */
|
||||||
|
public const P_DIRZAP = 0x0008; /* 0 0000 1000 */
|
||||||
|
public const P_HYDRA = 0x0010; /* 0 0001 0000 */
|
||||||
|
public const P_JANUS = 0x0020; /* 0 0010 0000 */
|
||||||
|
public const P_HYDRA4 = 0x0040; /* 0 0100 0000 */
|
||||||
|
public const P_HYDRA8 = 0x0080; /* 0 1000 0000 */
|
||||||
|
public const P_HYDRA16 = 0x0100; /* 1 0000 0000 */
|
||||||
|
public const P_MASK = 0x01FF; /* 1 1111 1111 */
|
||||||
|
|
||||||
|
/* canzap constants */
|
||||||
|
public const CZ_ZEDZIP = 0;
|
||||||
|
public const CZ_ZEDZAP = 1;
|
||||||
|
public const CZ_DIRZAP = 2;
|
||||||
|
}
|
@ -2,23 +2,30 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
|
||||||
|
use App\Traits\ScopeActive;
|
||||||
|
|
||||||
class Node extends Model
|
class Node extends Model
|
||||||
{
|
{
|
||||||
|
use ScopeActive;
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'is_zc'=>'boolean',
|
'is_zc'=>'boolean',
|
||||||
'is_rc'=>'boolean',
|
'is_rc'=>'boolean',
|
||||||
'is_hub'=>'boolean',
|
'is_hub'=>'boolean',
|
||||||
'is_host'=>'boolean',
|
'is_host'=>'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $fillable = ['zone_id','host_id','node_id','point_id'];
|
protected $fillable = ['zone_id','host_id','node_id','point_id'];
|
||||||
|
|
||||||
/* SCOPES */
|
/* SCOPES */
|
||||||
|
|
||||||
public function scopeHost()
|
public function scopeHost()
|
||||||
{
|
{
|
||||||
|
// @todo
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RELATIONS */
|
/* RELATIONS */
|
||||||
@ -26,7 +33,7 @@ class Node extends Model
|
|||||||
/**
|
/**
|
||||||
* Node nodelist flags
|
* Node nodelist flags
|
||||||
*
|
*
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
* @return BelongsToMany
|
||||||
*/
|
*/
|
||||||
public function flags()
|
public function flags()
|
||||||
{
|
{
|
||||||
@ -48,7 +55,7 @@ class Node extends Model
|
|||||||
public function getFTNAttribute()
|
public function getFTNAttribute()
|
||||||
{
|
{
|
||||||
return $this->zone_id
|
return $this->zone_id
|
||||||
? sprintf('%s:%s/%s.%s',$this->zone->zone_id,$this->host_id,$this->node_id,$this->point_id)
|
? sprintf('%d:%d/%d.%d@%s',$this->zone->zone_id,$this->host_id,$this->node_id,$this->point_id,$this->zone->domain->name)
|
||||||
: '-';
|
: '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +70,51 @@ class Node extends Model
|
|||||||
|
|
||||||
/* METHODS */
|
/* METHODS */
|
||||||
|
|
||||||
public function hasFlag($relation,$model)
|
/**
|
||||||
|
* Find a record in the DB for a node string, eg: 10:1/1.0
|
||||||
|
*
|
||||||
|
* @param string $ftn
|
||||||
|
* @return Node|null
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function findFTN(string $ftn): ?self
|
||||||
|
{
|
||||||
|
$matches = [];
|
||||||
|
|
||||||
|
// @todo domain can have more chars.
|
||||||
|
if (! preg_match('#^([0-9]+):([0-9]+)/([0-9]+)(.([0-9]+))?(@([a-z]{0,8}))?$#',strtolower($ftn),$matches))
|
||||||
|
throw new Exception('Invalid FTN: '.$ftn);
|
||||||
|
|
||||||
|
// Check our numbers are correct.
|
||||||
|
foreach ([1,2,3] as $i) {
|
||||||
|
if (! $matches[$i] || ($matches[$i] > 0xffff))
|
||||||
|
throw new Exception('Invalid FTN: '.$ftn);
|
||||||
|
}
|
||||||
|
if (isset($matches[5]) AND $matches[5] > 0xffff)
|
||||||
|
throw new Exception('Invalid FTN: '.$ftn);
|
||||||
|
|
||||||
|
return (new self)->active()
|
||||||
|
->select('nodes.*')
|
||||||
|
->where('zones.zone_id',$matches[1])
|
||||||
|
->where(function($query) use ($matches) {
|
||||||
|
$query->where('hub_id',$matches[2])
|
||||||
|
->orWhere('host_id',$matches[2]);
|
||||||
|
})
|
||||||
|
->join('zones',['zones.id'=>'nodes.zone_id'])
|
||||||
|
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||||
|
->where('zones.active',TRUE)
|
||||||
|
->where('node_id',$matches[3])
|
||||||
|
->where('point_id',(isset($matches[5]) AND $matches[5]) ? $matches[5] : 0)
|
||||||
|
->when(isset($matches[7]),function($query) use ($matches) {
|
||||||
|
$query->where('domains.name',$matches[7]);
|
||||||
|
})
|
||||||
|
->when((! isset($matches[7]) OR ! $matches[7]),function($query) {
|
||||||
|
$query->where('domains.default',TRUE);
|
||||||
|
})
|
||||||
|
->single();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasFlag($relation,$model): bool
|
||||||
{
|
{
|
||||||
return (bool) $this->{$relation}()
|
return (bool) $this->{$relation}()
|
||||||
->wherePivot($model->getForeignKey(),$model->{$model->getKeyName()})
|
->wherePivot($model->getForeignKey(),$model->{$model->getKeyName()})
|
||||||
|
125
app/Models/Setup.php
Normal file
125
app/Models/Setup.php
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Setup
|
||||||
|
*
|
||||||
|
* @package App\Models
|
||||||
|
* @property Collection nodes
|
||||||
|
* @property array binkp_options
|
||||||
|
*/
|
||||||
|
class Setup extends Model
|
||||||
|
{
|
||||||
|
// Our non model attributes and values
|
||||||
|
private array $internal = [];
|
||||||
|
|
||||||
|
public function __construct(array $attributes = [])
|
||||||
|
{
|
||||||
|
parent::__construct($attributes);
|
||||||
|
|
||||||
|
// @todo This should go in a config file in the config dir
|
||||||
|
$this->opt_cht = 0; /* CHAT mode - not implemented*/
|
||||||
|
$this->opt_cr = 0; /* Crypt mode - not implemented*/
|
||||||
|
$this->opt_mb = 1; /* Multi-Batch mode */
|
||||||
|
$this->opt_md = 0; /* CRAM-MD5 mode */
|
||||||
|
$this->opt_nd = 0; /* http://ftsc.org/docs/fsp-1027.001: No-dupes mode */
|
||||||
|
$this->opt_nda = 1; /* http://ftsc.org/docs/fsp-1027.001: Asymmetric ND mode */
|
||||||
|
$this->opt_mpwd = 0; /* Multi-Password mode - not implemented */
|
||||||
|
$this->opt_nr = 1; /* http://ftsc.org/docs/fsp-1027.001: Non-Reliable mode */
|
||||||
|
$this->binkp_options = ['m','d','r','b'];
|
||||||
|
|
||||||
|
/* EMSI SETTINGS */
|
||||||
|
$this->do_prevent = 1; /* EMSI - send an immediate EMSI_INQ on connect */
|
||||||
|
$this->ignore_nrq = 0;
|
||||||
|
$this->options = 0; /* EMSI - our capabilities */
|
||||||
|
|
||||||
|
/* EMSI - the order of protocols we are able to accept */
|
||||||
|
$this->inbound = '/tmp';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function nodes()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Node::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
|
public function getLocationAttribute()
|
||||||
|
{
|
||||||
|
return $this->nodes->first()->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSysopAttribute()
|
||||||
|
{
|
||||||
|
return $this->nodes->first()->sysop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSystemNameAttribute()
|
||||||
|
{
|
||||||
|
return $this->nodes->first()->system;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* METHODS */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __get($key)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'binkp_options':
|
||||||
|
case 'ignore_nrq':
|
||||||
|
case 'inbound':
|
||||||
|
case 'opt_nr':
|
||||||
|
case 'opt_nd':
|
||||||
|
case 'opt_nda':
|
||||||
|
case 'opt_md':
|
||||||
|
case 'opt_cr':
|
||||||
|
case 'opt_mb':
|
||||||
|
case 'opt_cht':
|
||||||
|
case 'do_prevent':
|
||||||
|
case 'options':
|
||||||
|
return $this->internal[$key] ?? FALSE;
|
||||||
|
|
||||||
|
case 'version':
|
||||||
|
return File::exists('VERSION') ? chop(File::get('VERSION')) : 'dev';
|
||||||
|
|
||||||
|
default:
|
||||||
|
return parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __set($key,$value)
|
||||||
|
{
|
||||||
|
switch ($key) {
|
||||||
|
case 'binkp_options':
|
||||||
|
case 'ignore_nrq':
|
||||||
|
case 'inbound':
|
||||||
|
case 'opt_nr':
|
||||||
|
case 'opt_nd':
|
||||||
|
case 'opt_nda':
|
||||||
|
case 'opt_md':
|
||||||
|
case 'opt_cr':
|
||||||
|
case 'opt_mb':
|
||||||
|
case 'opt_cht':
|
||||||
|
case 'do_prevent':
|
||||||
|
case 'options':
|
||||||
|
$this->internal[$key] = $value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
parent::__get($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
|
63
app/Traits/CRC.php
Normal file
63
app/Traits/CRC.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
trait CRC
|
||||||
|
{
|
||||||
|
private function CRC16USD(string $string): int
|
||||||
|
{
|
||||||
|
$crc = self::CRC16USD_INIT;
|
||||||
|
|
||||||
|
for ($c=0;$c<strlen($string);$c++)
|
||||||
|
$crc = $this->CRC16USD_UPDATE(ord($string[$c]),$crc);
|
||||||
|
|
||||||
|
return $crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function CRC16USD_UPDATE($b,$crc): int
|
||||||
|
{
|
||||||
|
return (self::crc16usd_tab[(($crc >> 8) ^ $b) & 0xff] ^ (($crc & 0x00ff) << 8)) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate CRC32
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @param bool $finish
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function CRC32(string $string,bool $finish=TRUE): int
|
||||||
|
{
|
||||||
|
$crc = 0xffffffff;
|
||||||
|
|
||||||
|
for ($i=0;$i<strlen($string);$i++)
|
||||||
|
$crc = (self::crc32_tab[($crc^ord($string[$i])) & 0xff] ^ (($crc>>8) & 0x00ffffff)) & 0xffffffff;
|
||||||
|
|
||||||
|
return $finish ? $this->CRC32_FINISH($crc) : $crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function CRC32_FINISH($crc)
|
||||||
|
{
|
||||||
|
return ~$crc & 0xffffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function CRC32_UPDATE($b,$crc)
|
||||||
|
{
|
||||||
|
return ((self::crc32_tab[($crc^$b) & 0xff] ^ (($crc>>8) & 0x00ffffff)) & 0xffffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function LSZ_INIT_CRC()
|
||||||
|
{
|
||||||
|
return ($this->ls_Protocol & self::LSZ_OPTCRC32) ? self::LSZ_INIT_CRC32 : self::LSZ_INIT_CRC16;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function LSZ_FINISH_CRC($crc)
|
||||||
|
{
|
||||||
|
return ($this->ls_Protocol & self::LSZ_OPTCRC32) ? $this->CRC32_FINISH($crc) : $crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function LSZ_UPDATE_CRC($b,$crc)
|
||||||
|
{
|
||||||
|
return ($this->ls_Protocol & self::LSZ_OPTCRC32) ? $this->CRC32_UPDATE($b,$crc) : $this->CRC16USD_UPDATE($b,$crc);
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,6 @@ trait ScopeActive
|
|||||||
*/
|
*/
|
||||||
public function scopeActive()
|
public function scopeActive()
|
||||||
{
|
{
|
||||||
return $this->where('active',TRUE);
|
return $this->where($this->getTable().'.active',TRUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ class CreateDomains extends Migration
|
|||||||
$table->string('name',8)->unique();
|
$table->string('name',8)->unique();
|
||||||
$table->string('dnsdomain')->nullable();
|
$table->string('dnsdomain')->nullable();
|
||||||
$table->string('notes')->nullable();
|
$table->string('notes')->nullable();
|
||||||
|
$table->boolean('default')->default(FALSE);
|
||||||
$table->boolean('active');
|
$table->boolean('active');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ class InitialSetupSeeder extends Seeder
|
|||||||
|
|
||||||
DB::table('domains')->insert([
|
DB::table('domains')->insert([
|
||||||
'name'=>'private',
|
'name'=>'private',
|
||||||
|
'default'=>TRUE,
|
||||||
'active'=>TRUE,
|
'active'=>TRUE,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -46,19 +47,36 @@ class InitialSetupSeeder extends Seeder
|
|||||||
DB::table('nodes')->insert([
|
DB::table('nodes')->insert([
|
||||||
'zone_id'=>'1',
|
'zone_id'=>'1',
|
||||||
'host_id'=>'999',
|
'host_id'=>'999',
|
||||||
'node_id'=>'999',
|
'node_id'=>'2',
|
||||||
'is_host'=>TRUE,
|
'is_host'=>TRUE,
|
||||||
'active'=>TRUE,
|
'active'=>TRUE,
|
||||||
'system'=>'FTN Clearing House Dev',
|
'system'=>'FTN Clearing House Dev',
|
||||||
'sysop'=>'Deon George',
|
'sysop'=>'Deon George',
|
||||||
'location'=>'Parkdale, AUS',
|
'location'=>'Parkdale, AUS',
|
||||||
'email'=>'deon@leenooks.net',
|
'email'=>'deon@leenooks.net',
|
||||||
'address'=>'fidohub.leenooks.net',
|
'address'=>'10.1.3.165',
|
||||||
'port'=>24554,
|
'port'=>24554,
|
||||||
'protocol_id'=>1,
|
'protocol_id'=>1,
|
||||||
'software_id'=>1,
|
'software_id'=>1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
DB::table('nodes')->insert([
|
||||||
|
'zone_id'=>'1',
|
||||||
|
'host_id'=>'999',
|
||||||
|
'node_id'=>'1',
|
||||||
|
'is_host'=>TRUE,
|
||||||
|
'active'=>TRUE,
|
||||||
|
'system'=>'Alterant MailHUB DEV',
|
||||||
|
'sysop'=>'Deon George',
|
||||||
|
'location'=>'Parkdale, AUS',
|
||||||
|
'email'=>'deon@leenooks.net',
|
||||||
|
'address'=>'d-1-4.ipv4.leenooks.vpn',
|
||||||
|
'port'=>14554,
|
||||||
|
'sespass'=>'PASSWORD',
|
||||||
|
'protocol_id'=>1,
|
||||||
|
'software_id'=>1,
|
||||||
|
]);
|
||||||
|
|
||||||
DB::table('setups')->insert([
|
DB::table('setups')->insert([
|
||||||
'opt_md'=>'1',
|
'opt_md'=>'1',
|
||||||
]);
|
]);
|
||||||
@ -67,5 +85,11 @@ class InitialSetupSeeder extends Seeder
|
|||||||
'node_id'=>'1',
|
'node_id'=>'1',
|
||||||
'setup_id'=>'1',
|
'setup_id'=>'1',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
DB::table('users')->insert([
|
||||||
|
'name'=>'Deon George',
|
||||||
|
'email'=>'deon@leenooks.net',
|
||||||
|
'password'=>'$2y$10$bJQDLfxnKrh6o5Sa02MZOukXcLTNQiByXSTJ7fTr.kHMpV2wxbG6.',
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user