Some BINKP optimisation, implemented crypt, implemented receiving compressed transfers
This commit is contained in:
parent
f9f9fb5345
commit
6f298d778f
97
app/Classes/Crypt.php
Normal file
97
app/Classes/Crypt.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Classes;
|
||||
|
||||
use App\Interfaces\CRC as CRCInterface;
|
||||
|
||||
class Crypt implements CRCInterface
|
||||
{
|
||||
private array $keys= [
|
||||
305419896,
|
||||
591751049,
|
||||
878082192,
|
||||
];
|
||||
|
||||
public function __construct(string $password)
|
||||
{
|
||||
$this->extend_key($password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update crc.
|
||||
*/
|
||||
private function crc32_cr(int $oldCrc, int $charAt): int
|
||||
{
|
||||
return (($oldCrc >> 8) & 0xFFFFFF) ^ self::crc32_tab[($oldCrc ^ $charAt) & 0xFF];
|
||||
}
|
||||
|
||||
public function decrypt(string $content): string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
foreach (unpack('C*', $content) as $byte) {
|
||||
$byte = ($byte ^ $this->decrypt_byte()) & 0xFF;
|
||||
$this->update_keys($byte);
|
||||
$result .= chr($byte);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt byte.
|
||||
*/
|
||||
private function decrypt_byte(): int
|
||||
{
|
||||
$temp = $this->keys[2] | 2;
|
||||
|
||||
return (($temp * ($temp ^ 1)) >> 8) & 0xFFFFFF;
|
||||
}
|
||||
|
||||
public function encrypt(string $content): string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
foreach (unpack('C*', $content) as $val)
|
||||
$result .= pack('c', $this->encrypt_byte($val));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function encrypt_byte(int $byte): int
|
||||
{
|
||||
$result = $byte ^ $this->decrypt_byte() & 0xFF;
|
||||
$this->update_keys($byte);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function extend_key(string $password): void
|
||||
{
|
||||
foreach (unpack('C*', $password) as $byte)
|
||||
$this->update_keys($byte);
|
||||
}
|
||||
|
||||
public static function toSignedInt32(int $int): int
|
||||
{
|
||||
if (\PHP_INT_SIZE === 8) {
|
||||
$int &= 0xFFFFFFFF;
|
||||
|
||||
if ($int & 0x80000000)
|
||||
return $int - 0x100000000;
|
||||
}
|
||||
|
||||
return $int;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update keys.
|
||||
*/
|
||||
private function update_keys(int $byte): void
|
||||
{
|
||||
$this->keys[0] = $this->crc32_cr($this->keys[0],$byte);
|
||||
$this->keys[1] += ($this->keys[0] & 0xFF);
|
||||
$this->keys[1] = $this->toSignedInt32($this->keys[1]*134775813+1);
|
||||
$this->keys[2] = $this->toSignedInt32($this->crc32_cr($this->keys[2],($this->keys[1] >> 24) & 0xFF));
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Classes\File;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\UnreadableFileEncountered;
|
||||
@ -23,6 +22,10 @@ class Item
|
||||
// For deep debugging
|
||||
protected bool $DEBUG = FALSE;
|
||||
|
||||
/** @var int max size of file to use compression */
|
||||
// @todo MAX_COMPSIZE hasnt been implemented in RECEIVE OR SEND
|
||||
protected const MAX_COMPSIZE = 0x1fff;
|
||||
|
||||
protected const IS_PKT = (1<<1);
|
||||
protected const IS_ARC = (1<<2);
|
||||
protected const IS_FILE = (1<<3);
|
||||
@ -30,29 +33,33 @@ class Item
|
||||
protected const IS_REQ = (1<<5);
|
||||
protected const IS_TIC = (1<<6);
|
||||
|
||||
protected const I_RECV = (1<<6);
|
||||
protected const I_SEND = (1<<7);
|
||||
protected const I_RECV = (1<<0);
|
||||
protected const I_SEND = (1<<1);
|
||||
|
||||
protected string $file_name = '';
|
||||
protected int $file_size = 0;
|
||||
protected int $file_mtime = 0;
|
||||
protected int $file_type = 0;
|
||||
protected int $action = 0;
|
||||
/** Current read/write pointer */
|
||||
protected int $file_pos = 0;
|
||||
/** File descriptor */
|
||||
protected mixed $f = NULL;
|
||||
protected int $type;
|
||||
protected int $action;
|
||||
protected File $filemodel;
|
||||
|
||||
public bool $sent = FALSE;
|
||||
public bool $received = FALSE;
|
||||
public bool $incomplete = FALSE;
|
||||
/** Time we started sending/receiving */
|
||||
protected int $start;
|
||||
|
||||
/**
|
||||
* @throws FileNotFoundException
|
||||
* @throws UnreadableFileEncountered
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($file,int $action)
|
||||
{
|
||||
$this->action |= $action;
|
||||
|
||||
switch ($action) {
|
||||
case self::I_SEND:
|
||||
if ($file instanceof File) {
|
||||
@ -63,7 +70,7 @@ class Item
|
||||
|
||||
} else {
|
||||
if (! is_string($file))
|
||||
throw new Exception('Invalid object creation - file should be a string');
|
||||
throw new \Exception('Invalid object creation - file should be a string');
|
||||
|
||||
if (! file_exists($file))
|
||||
throw new FileNotFoundException('Item doesnt exist: '.$file);
|
||||
@ -83,7 +90,7 @@ class Item
|
||||
$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)));
|
||||
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'];
|
||||
@ -92,14 +99,15 @@ class Item
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Unknown action: '.$action);
|
||||
throw new \Exception('Unknown action: '.$action);
|
||||
}
|
||||
|
||||
$this->file_type |= $this->whatType();
|
||||
$this->action = $action;
|
||||
$this->type = $this->whatType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
@ -107,10 +115,10 @@ class Item
|
||||
case 'mtime':
|
||||
case 'name':
|
||||
case 'size':
|
||||
if ($this->action & self::I_RECV)
|
||||
if ($this->action & self::I_RECV|self::I_SEND)
|
||||
return $this->{'file_'.$key};
|
||||
|
||||
throw new Exception('Invalid request for key: '.$key);
|
||||
throw new \Exception('Invalid request for key: '.$key);
|
||||
|
||||
case 'recvas':
|
||||
return $this->file_name;
|
||||
@ -119,13 +127,13 @@ class Item
|
||||
return $this->file_name ? basename($this->file_name) : $this->filemodel->name;
|
||||
|
||||
default:
|
||||
throw new Exception('Unknown key: '.$key);
|
||||
throw new \Exception('Unknown key: '.$key);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isType(int $type): bool
|
||||
{
|
||||
return $this->file_type & $type;
|
||||
return $this->type & $type;
|
||||
}
|
||||
|
||||
private function whatType(): int
|
||||
|
@ -15,8 +15,6 @@ class Mail extends Item
|
||||
*/
|
||||
public function __construct(Packet $mail,int $action)
|
||||
{
|
||||
$this->action |= $action;
|
||||
|
||||
switch ($action) {
|
||||
case self::I_SEND:
|
||||
$this->file = $mail;
|
||||
@ -29,6 +27,8 @@ class Mail extends Item
|
||||
default:
|
||||
throw new \Exception('Unknown action: '.$action);
|
||||
}
|
||||
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
public function read(int $start,int $length): string
|
||||
|
@ -5,10 +5,12 @@ namespace App\Classes\File;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
|
||||
use App\Classes\File;
|
||||
use App\Classes\{File,Protocol};
|
||||
use App\Classes\FTN\{InvalidPacketException,Packet};
|
||||
use App\Exceptions\FileGrewException;
|
||||
use App\Jobs\{MessageProcess,TicProcess};
|
||||
use App\Models\Address;
|
||||
|
||||
@ -23,22 +25,26 @@ final class Receive extends Item
|
||||
{
|
||||
private const LOGKEY = 'IR-';
|
||||
|
||||
private const compression = [
|
||||
'BZ2',
|
||||
'GZ',
|
||||
];
|
||||
|
||||
private Address $ao;
|
||||
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
|
||||
private string $file; // Local filename for file received
|
||||
/** @var ?string The compression used by the incoming file */
|
||||
private ?string $comp;
|
||||
/** @var string|null The compressed data received */
|
||||
private ?string $comp_data;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Initialise our variables
|
||||
$this->list = collect();
|
||||
$this->receiving = NULL;
|
||||
$this->file_pos = 0;
|
||||
$this->f = NULL;
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
@ -71,7 +77,7 @@ final class Receive extends Item
|
||||
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; });
|
||||
->sum(function($item) { return $item->size; });
|
||||
|
||||
default:
|
||||
throw new \Exception('Unknown key: '.$key);
|
||||
@ -85,114 +91,121 @@ final class Receive extends Item
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
if (! $this->f)
|
||||
if (! $this->receiving)
|
||||
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',self::LOGKEY,$this->receiving->file_name,$this->receiving->file_size-$this->file_pos));
|
||||
$this->receiving->incomplete = TRUE;
|
||||
}
|
||||
if ($this->f) {
|
||||
if ($this->file_pos !== $this->receiving->size) {
|
||||
Log::warning(sprintf('%s:- Closing [%s], but missing [%d] bytes',self::LOGKEY,$this->receiving->name,$this->receiving->size-$this->file_pos));
|
||||
$this->receiving->incomplete = TRUE;
|
||||
}
|
||||
|
||||
$this->receiving->received = TRUE;
|
||||
$this->receiving->received = TRUE;
|
||||
|
||||
$end = time()-$this->start;
|
||||
Log::debug(sprintf('%s: - Closing [%s], received in [%d]',self::LOGKEY,$this->receiving->file_name,$end));
|
||||
$end = time()-$this->start;
|
||||
Log::debug(sprintf('%s:- Closing [%s], received in [%d]',self::LOGKEY,$this->receiving->name,$end));
|
||||
|
||||
fclose($this->f);
|
||||
$this->file_pos = 0;
|
||||
$this->f = NULL;
|
||||
if ($this->comp)
|
||||
Log::info(sprintf('%s:= Compressed file using [%s] was [%d] bytes (%d). Compression rate [%3.2f%%]',self::LOGKEY,$this->comp,$x=strlen($this->comp_data),$this->receiving->size,$x/$this->receiving->size*100));
|
||||
|
||||
// If the packet has been received but not the right size, dont process it any more.
|
||||
fclose($this->f);
|
||||
// Set our mtime
|
||||
touch($this->file,$this->mtime);
|
||||
$this->file_pos = 0;
|
||||
$this->f = NULL;
|
||||
|
||||
// If we received a packet, we'll dispatch a job to process it
|
||||
if (! $this->receiving->incomplete)
|
||||
switch ($this->receiving->file_type) {
|
||||
case self::IS_ARC:
|
||||
case self::IS_PKT:
|
||||
Log::info(sprintf('%s: - Processing mail %s [%s]',self::LOGKEY,$this->receiving->file_type === self::IS_PKT ? 'PACKET' : 'ARCHIVE',$this->file));
|
||||
// If the packet has been received but not the right size, dont process it any more.
|
||||
|
||||
try {
|
||||
$f = new File($this->file);
|
||||
$processed = FALSE;
|
||||
// If we received a packet, we'll dispatch a job to process it
|
||||
if (! $this->receiving->incomplete)
|
||||
switch ($this->receiving->type) {
|
||||
case self::IS_ARC:
|
||||
case self::IS_PKT:
|
||||
Log::info(sprintf('%s:- Processing mail %s [%s]',self::LOGKEY,$this->receiving->type === self::IS_PKT ? 'PACKET' : 'ARCHIVE',$this->file));
|
||||
|
||||
foreach ($f as $packet) {
|
||||
$po = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->ao->system);
|
||||
try {
|
||||
$f = new File($this->file);
|
||||
$processed = FALSE;
|
||||
|
||||
// Check the messages are from the uplink
|
||||
if ($this->ao->system->addresses->search(function($item) use ($po) { return $item->id === $po->fftn_o->id; }) === FALSE) {
|
||||
Log::error(sprintf('%s: ! Packet [%s] is not from this link? [%d]',self::LOGKEY,$po->fftn_o->ftn,$this->ao->system_id));
|
||||
foreach ($f as $packet) {
|
||||
$po = Packet::process($packet,Arr::get(stream_get_meta_data($packet),'uri'),$f->itemSize(),$this->ao->system);
|
||||
|
||||
break;
|
||||
}
|
||||
// Check the messages are from the uplink
|
||||
if ($this->ao->system->addresses->search(function($item) use ($po) { return $item->id === $po->fftn_o->id; }) === FALSE) {
|
||||
Log::error(sprintf('%s:! Packet [%s] is not from this link? [%d]',self::LOGKEY,$po->fftn_o->ftn,$this->ao->system_id));
|
||||
|
||||
// Check the packet password
|
||||
if ($this->ao->session('pktpass') != $po->password) {
|
||||
Log::error(sprintf('%s: ! Packet from [%s] with password [%s] is invalid.',self::LOGKEY,$this->ao->ftn,$po->password));
|
||||
|
||||
// @todo Generate message to system advising invalid password - that message should be sent without a packet password!
|
||||
break;
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s: - Packet has [%d] messages',self::LOGKEY,$po->count()));
|
||||
|
||||
// Queue messages if there are too many in the packet.
|
||||
if ($queue = ($po->count() > config('app.queue_msgs')))
|
||||
Log::info(sprintf('%s: - Messages will be sent to the queue for processing',self::LOGKEY));
|
||||
|
||||
$count = 0;
|
||||
foreach ($po as $msg) {
|
||||
Log::info(sprintf('%s: - Mail from [%s] to [%s]',self::LOGKEY,$msg->fftn,$msg->tftn));
|
||||
|
||||
// @todo Quick check that the packet should be processed by us.
|
||||
// @todo validate that the packet's zone is in the domain.
|
||||
|
||||
try {
|
||||
// Dispatch job.
|
||||
if ($queue)
|
||||
MessageProcess::dispatch($msg,$f->pktName());
|
||||
else
|
||||
MessageProcess::dispatchSync($msg,$f->pktName());
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:! Got error dispatching message [%s] (%d:%s-%s).',self::LOGKEY,$msg->msgid,$e->getLine(),$e->getFile(),$e->getMessage()));
|
||||
break;
|
||||
}
|
||||
|
||||
$count++;
|
||||
// Check the packet password
|
||||
if ($this->ao->session('pktpass') != $po->password) {
|
||||
Log::error(sprintf('%s:! Packet from [%s] with password [%s] is invalid.',self::LOGKEY,$this->ao->ftn,$po->password));
|
||||
|
||||
// @todo Generate message to system advising invalid password - that message should be sent without a packet password!
|
||||
break;
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s:- Packet has [%d] messages',self::LOGKEY,$po->count()));
|
||||
|
||||
// Queue messages if there are too many in the packet.
|
||||
if ($queue = ($po->count() > config('app.queue_msgs')))
|
||||
Log::info(sprintf('%s:- Messages will be sent to the queue for processing',self::LOGKEY));
|
||||
|
||||
$count = 0;
|
||||
foreach ($po as $msg) {
|
||||
Log::info(sprintf('%s:- Mail from [%s] to [%s]',self::LOGKEY,$msg->fftn,$msg->tftn));
|
||||
|
||||
// @todo Quick check that the packet should be processed by us.
|
||||
// @todo validate that the packet's zone is in the domain.
|
||||
|
||||
try {
|
||||
// Dispatch job.
|
||||
if ($queue)
|
||||
MessageProcess::dispatch($msg,$f->pktName());
|
||||
else
|
||||
MessageProcess::dispatchSync($msg,$f->pktName());
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:! Got error dispatching message [%s] (%d:%s-%s).',self::LOGKEY,$msg->msgid,$e->getLine(),$e->getFile(),$e->getMessage()));
|
||||
}
|
||||
|
||||
$count++;
|
||||
}
|
||||
|
||||
if ($count === $po->count())
|
||||
$processed = TRUE;
|
||||
}
|
||||
|
||||
if ($count === $po->count())
|
||||
$processed = TRUE;
|
||||
if (! $processed) {
|
||||
Log::alert(sprintf('%s:- Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->file));
|
||||
|
||||
// If we want to keep the packet, we could do that logic here
|
||||
} elseif (! config('app.packet_keep')) {
|
||||
Log::debug(sprintf('%s:- Deleting processed packet [%s]',self::LOGKEY,$this->file));
|
||||
unlink($this->file);
|
||||
}
|
||||
|
||||
} catch (InvalidPacketException $e) {
|
||||
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->file),['e'=>$e->getMessage()]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:- Not deleting packet [%s], as it generated an uncaught exception',self::LOGKEY,$this->file),['e'=>$e->getMessage()]);
|
||||
}
|
||||
|
||||
if (! $processed) {
|
||||
Log::alert(sprintf('%s: - Not deleting packet [%s], it doesnt seem to be processed?',self::LOGKEY,$this->file));
|
||||
break;
|
||||
|
||||
// If we want to keep the packet, we could do that logic here
|
||||
} elseif (! config('app.packet_keep')) {
|
||||
Log::debug(sprintf('%s: - Deleting processed packet [%s]',self::LOGKEY,$this->file));
|
||||
unlink($this->file);
|
||||
}
|
||||
case self::IS_TIC:
|
||||
Log::info(sprintf('%s:- Processing TIC file [%s]',self::LOGKEY,$this->file));
|
||||
|
||||
} catch (InvalidPacketException $e) {
|
||||
Log::error(sprintf('%s: - Not deleting packet [%s], as it generated an InvalidPacketException',self::LOGKEY,$this->file),['e'=>$e->getMessage()]);
|
||||
// Queue the tic to be processed later, in case the referenced file hasnt been received yet
|
||||
TicProcess::dispatch($this->file);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s: - Not deleting packet [%s], as it generated an uncaught exception',self::LOGKEY,$this->file),['e'=>$e->getMessage()]);
|
||||
}
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case self::IS_TIC:
|
||||
Log::info(sprintf('%s: - Processing TIC file [%s]',self::LOGKEY,$this->file));
|
||||
|
||||
// Queue the tic to be processed later, in case the referenced file hasnt been received yet
|
||||
TicProcess::dispatch($this->file);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
Log::debug(sprintf('%s: - Leaving file [%s] in the inbound dir',self::LOGKEY,$this->file));
|
||||
}
|
||||
default:
|
||||
Log::debug(sprintf('%s:- Leaving file [%s] in the inbound dir',self::LOGKEY,$this->file));
|
||||
}
|
||||
}
|
||||
|
||||
$this->receiving = NULL;
|
||||
}
|
||||
@ -202,38 +215,66 @@ final class Receive extends Item
|
||||
*
|
||||
* @param Address $ao
|
||||
* @param bool $check
|
||||
* @return bool
|
||||
* @param string|null $comp If the incoming file will be compressed
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
* @todo $comp should be parsed, in case it contains other items
|
||||
*/
|
||||
public function open(Address $ao,bool $check=FALSE): bool
|
||||
public function open(Address $ao,bool $check=FALSE,string $comp=NULL): int
|
||||
{
|
||||
Log::debug(sprintf('%s:+ open [%d]',self::LOGKEY,$check));
|
||||
|
||||
// Check we can open this file
|
||||
// @todo
|
||||
// @todo implement return 2 - SKIP file
|
||||
// @todo implement return 4 - SUSPEND(?) file
|
||||
if ($check) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// @todo Change to use Storage::class()
|
||||
if (! $this->receiving)
|
||||
throw new \Exception('No files currently receiving');
|
||||
|
||||
$this->comp_data = '';
|
||||
$this->comp = $comp;
|
||||
|
||||
/*
|
||||
if ($this->receiving->size <= self::MAX_COMPSIZE) {
|
||||
$this->comp = $comp;
|
||||
|
||||
} else {
|
||||
Log::alert(sprintf('%s:- Compression [%s] disabled for file [%s], its size is too big [%d]',self::LOGKEY,$comp,$this->receiving->name,$this->receiving->size));
|
||||
}
|
||||
*/
|
||||
|
||||
if ($this->comp && (! in_array($this->comp,self::compression)))
|
||||
throw new \Exception('Unsupported compression:'.$this->comp);
|
||||
elseif ($this->comp)
|
||||
Log::debug(sprintf('%s:- Receiving file with [%s] compression',self::LOGKEY,$this->comp));
|
||||
|
||||
$this->ao = $ao;
|
||||
$this->file_pos = 0;
|
||||
$this->start = time();
|
||||
$this->file = sprintf('storage/app/%s/%04X-%s',config('app.fido'),$this->ao->id,$this->receiving->recvas);
|
||||
$this->file = sprintf('storage/app/%s',$this->local_path($ao));
|
||||
|
||||
Log::debug(sprintf('%s: - Opening [%s]',self::LOGKEY,$this->file));
|
||||
$this->f = fopen($this->file,'wb');
|
||||
if (! $this->f) {
|
||||
Log::error(sprintf('%s:! Unable to open file [%s] for writing',self::LOGKEY,$this->receiving->file_name));
|
||||
return 3; // @todo change to const
|
||||
if (file_exists($this->file)
|
||||
&& (Storage::disk('local')->lastModified($this->local_path($ao)) === $this->mtime)
|
||||
&& (Storage::disk('local')->size($this->local_path($ao)) === $this->size)) {
|
||||
Log::alert(sprintf('%s:- File already exists - skipping [%s]', self::LOGKEY, $this->file));
|
||||
return Protocol::FOP_SKIP;
|
||||
|
||||
} elseif (file_exists($this->file) && (Storage::disk('local')->size($this->local_path($ao)) > 0)) {
|
||||
Log::alert(sprintf('%s:- File exists with different details - skipping [%s]',self::LOGKEY,$this->file));
|
||||
return Protocol::FOP_SUSPEND;
|
||||
|
||||
} else {
|
||||
Log::debug(sprintf('%s:- Opening [%s]',self::LOGKEY,$this->file));
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s:= open - File [%s] opened for writing',self::LOGKEY,$this->receiving->file_name));
|
||||
return 0; // @todo change to const
|
||||
// If we are only checking, we'll return (NR mode)
|
||||
if ($check)
|
||||
return Protocol::FOP_OK;
|
||||
|
||||
$this->f = fopen($this->file,'wb');
|
||||
if (! $this->f) {
|
||||
Log::error(sprintf('%s:! Unable to open file [%s] for writing',self::LOGKEY,$this->receiving->name));
|
||||
return Protocol::FOP_ERROR;
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s:= open - File [%s] opened for writing',self::LOGKEY,$this->receiving->name));
|
||||
return Protocol::FOP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,29 +296,76 @@ final class Receive extends Item
|
||||
$this->receiving = $o;
|
||||
}
|
||||
|
||||
private function local_path(Address $ao): string
|
||||
{
|
||||
return sprintf('%s/%04X-%s',config('app.fido'),$ao->id,$this->receiving->recvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the file we are receiving
|
||||
*
|
||||
* @param string $buf
|
||||
* @return int
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function write(string $buf): int
|
||||
public function write(string $buf): bool
|
||||
{
|
||||
if (! $this->f)
|
||||
throw new \Exception('No file open for read');
|
||||
throw new \Exception('No file open for write');
|
||||
|
||||
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));
|
||||
$data = '';
|
||||
|
||||
$rc = fwrite($this->f,$buf);
|
||||
// If we are using compression mode, then we need to buffer the right until we have everything
|
||||
if ($this->comp) {
|
||||
$this->comp_data .= $buf;
|
||||
|
||||
// See if we can uncompress the data yet
|
||||
switch ($this->comp) {
|
||||
case 'BZ2':
|
||||
if (($data=bzdecompress($this->comp_data,TRUE)) === FALSE)
|
||||
throw new FileException('BZ2 decompression failed?');
|
||||
elseif (is_numeric($data))
|
||||
throw new FileException(sprintf('BZ2 decompression failed with (:%d)?',$data));
|
||||
|
||||
break;
|
||||
|
||||
case 'GZ':
|
||||
if (($data=gzdeflate($this->comp_data)) === FALSE)
|
||||
throw new FileException('BZ2 decompression failed?');
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Compressed file grew
|
||||
if (! strlen($data)) {
|
||||
if (strlen($this->comp_data) > $this->receiving->size) {
|
||||
fclose($this->f);
|
||||
$this->f = NULL;
|
||||
|
||||
throw new FileGrewException(sprintf('Error compressed file grew, rejecting [%d] -> [%d]', strlen($this->comp_data), $this->receiving->size));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} else {
|
||||
$data = $buf;
|
||||
}
|
||||
|
||||
if ($this->file_pos+strlen($data) > $this->receiving->size)
|
||||
throw new \Exception(sprintf('Too many bytes received [%d] (%d)?',$this->file_pos+strlen($buf),$this->receiving->size));
|
||||
|
||||
$rc = fwrite($this->f,$data);
|
||||
|
||||
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] (%d)',self::LOGKEY,$rc,$this->file_pos,$this->receiving->file_size,strlen($buf)));
|
||||
Log::debug(sprintf('%s:- Write [%d] bytes, file pos now [%d] of [%d] (%d)',self::LOGKEY,$rc,$this->file_pos,$this->receiving->size,strlen($buf)));
|
||||
|
||||
return $rc;
|
||||
if (strlen($this->comp_data) > $this->receiving->size)
|
||||
Log::alert(sprintf('%s:- Compression grew the file during transfer (%d->%d)',self::LOGKEY,$this->receiving->size,strlen($this->comp_data)));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
@ -15,9 +15,6 @@ use App\Models\Address;
|
||||
* 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
|
||||
@ -31,9 +28,7 @@ final class Send extends Item
|
||||
private ?Item $sending;
|
||||
private Collection $packets;
|
||||
|
||||
private mixed $f; // File descriptor
|
||||
private int $start; // Time we started sending
|
||||
private int $file_pos; // Current read pointer
|
||||
private string $comp_data;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -41,8 +36,6 @@ final class Send extends Item
|
||||
$this->list = collect();
|
||||
$this->packets = collect();
|
||||
$this->sending = NULL;
|
||||
$this->file_pos = 0;
|
||||
$this->f = NULL;
|
||||
}
|
||||
|
||||
public function __get($key)
|
||||
@ -51,15 +44,15 @@ final class Send extends Item
|
||||
case 'fd':
|
||||
return is_resource($this->f) ?: $this->f;
|
||||
|
||||
case 'file_count':
|
||||
case 'files_count':
|
||||
return $this->list
|
||||
->filter(function($item) { return $item->isType(self::IS_FILE|self::IS_TIC); })
|
||||
->count();
|
||||
|
||||
case 'file_size':
|
||||
case 'files_size':
|
||||
return $this->list
|
||||
->filter(function($item) { return $item->isType(self::IS_FILE|self::IS_TIC); })
|
||||
->sum(function($item) { return $item->file_size; });
|
||||
->sum(function($item) { return $item->size; });
|
||||
|
||||
case 'filepos':
|
||||
return $this->file_pos;
|
||||
@ -73,8 +66,8 @@ final class Send extends Item
|
||||
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; })
|
||||
+ $this->packets->sum(function($item) { return $item->file_size; });
|
||||
->sum(function($item) { return $item->size; })
|
||||
+ $this->packets->sum(function($item) { return $item->size; });
|
||||
|
||||
case 'sendas':
|
||||
return $this->sending ? $this->sending->{$key} : NULL;
|
||||
@ -95,10 +88,10 @@ final class Send extends Item
|
||||
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; })
|
||||
->sum(function($item) { return $item->size; })
|
||||
+ $this->packets
|
||||
->filter(function($item) { return ($item->action & self::I_SEND) && $item->sent === TRUE; })
|
||||
->sum(function($item) { return $item->file_size; });
|
||||
->sum(function($item) { return $item->size; });
|
||||
|
||||
case 'total_count':
|
||||
return $this->list
|
||||
@ -110,8 +103,8 @@ final class Send extends Item
|
||||
|
||||
case 'total_size':
|
||||
return $this->list
|
||||
->sum(function($item) { return $item->file_size; })
|
||||
+ $this->packets->sum(function($item) { return $item->file_size; });
|
||||
->sum(function($item) { return $item->size; })
|
||||
+ $this->packets->sum(function($item) { return $item->size; });
|
||||
|
||||
default:
|
||||
throw new Exception('Unknown key: '.$key);
|
||||
@ -146,6 +139,21 @@ final class Send extends Item
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private function compress(string $comp_mode): void
|
||||
{
|
||||
switch ($comp_mode) {
|
||||
case 'BZ2':
|
||||
$this->comp_data = bzcompress($buf);
|
||||
break;
|
||||
|
||||
case 'GZ':
|
||||
$this->comp_data = gzcompress($buf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Close the file descriptor of the file we are sending
|
||||
*
|
||||
@ -160,7 +168,7 @@ final class Send extends Item
|
||||
if ($successful) {
|
||||
$this->sending->sent = TRUE;
|
||||
$end = time()-$this->start;
|
||||
Log::debug(sprintf('%s: - Closing [%s], sent in [%d]',self::LOGKEY,$this->sending->file_name,$end));
|
||||
Log::debug(sprintf('%s: - Closing [%s], sent in [%d]',self::LOGKEY,$this->sending->name,$end));
|
||||
}
|
||||
|
||||
// @todo This should be done better isType == file?
|
||||
@ -208,9 +216,9 @@ final class Send extends Item
|
||||
Log::debug(sprintf('%s:- [%d] Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn));
|
||||
|
||||
// Add Files
|
||||
foreach ($x as $xx) {
|
||||
$this->list->push(new Item($xx,self::I_SEND));
|
||||
$this->list->push(new Tic($ao,$xx,self::I_SEND));
|
||||
foreach ($x as $fo) {
|
||||
$this->list->push(new Item($fo,self::I_SEND));
|
||||
$this->list->push(new Tic($ao,$fo,self::I_SEND));
|
||||
}
|
||||
|
||||
$file = TRUE;
|
||||
@ -222,10 +230,11 @@ final class Send extends Item
|
||||
/**
|
||||
* Open a file for sending
|
||||
*
|
||||
* @param string $compress
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function open(): bool
|
||||
public function open(string $compress=''): bool
|
||||
{
|
||||
Log::debug(sprintf('%s:+ open',self::LOGKEY));
|
||||
|
||||
@ -238,6 +247,11 @@ final class Send extends Item
|
||||
$this->start = time();
|
||||
$this->f = TRUE;
|
||||
|
||||
/*
|
||||
if ($compress)
|
||||
$this->comp_data = $this->compdata($compress);
|
||||
*/
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -258,18 +272,19 @@ final class Send extends Item
|
||||
}
|
||||
|
||||
// If sending file is a File::class, then our file is s3
|
||||
if (! $this->sending->file_name && $this->sending->filemodel) {
|
||||
if (! $this->sending->name && $this->sending->filemodel) {
|
||||
$this->f = Storage::readStream($this->sending->filemodel->full_storage_path);
|
||||
return TRUE;
|
||||
|
||||
} else {
|
||||
$this->f = fopen($this->sending->file_name,'rb');
|
||||
$this->f = fopen($this->sending->name,'rb');
|
||||
|
||||
if (! $this->f) {
|
||||
Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->sending->file_name));
|
||||
Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->sending->name));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Log::info(sprintf('%s:= open - File [%s] opened with size [%d]',self::LOGKEY,$this->sending->file_name,$this->sending->file_size));
|
||||
Log::info(sprintf('%s:= open - File [%s] opened with size [%d]',self::LOGKEY,$this->sending->name,$this->sending->size));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -343,7 +358,7 @@ final class Send extends Item
|
||||
Log::debug(sprintf('%s: - Read [%d] bytes, file pos now [%d]',self::LOGKEY,strlen($data),$this->file_pos));
|
||||
|
||||
if ($data === FALSE)
|
||||
throw new UnreadableFileEncountered('Error reading file: '.$this->sending->file_name);
|
||||
throw new UnreadableFileEncountered('Error reading file: '.$this->sending->name);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -7,19 +7,17 @@ use App\Models\{Address,File};
|
||||
|
||||
class Tic extends Item
|
||||
{
|
||||
private string $file;
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(Address $ao,File $fo,int $action)
|
||||
{
|
||||
$this->action |= $action;
|
||||
|
||||
$tic = new FTNTic;
|
||||
|
||||
switch ($action) {
|
||||
case self::I_SEND:
|
||||
$tic = new FTNTic;
|
||||
$this->file = $tic->generate($ao,$fo);
|
||||
$this->file_type = self::IS_TIC;
|
||||
$this->file_name = sprintf('%s.tic',sprintf('%08x',$fo->id));
|
||||
$this->file_size = strlen($this->file);
|
||||
$this->file_mtime = $fo->created_at->timestamp;
|
||||
@ -29,6 +27,9 @@ class Tic extends Item
|
||||
default:
|
||||
throw new \Exception('Unknown action: '.$action);
|
||||
}
|
||||
|
||||
$this->action = $action;
|
||||
$this->type = self::IS_TIC;
|
||||
}
|
||||
|
||||
public function read(int $start,int $length): string
|
||||
|
@ -17,12 +17,12 @@ abstract class Protocol
|
||||
|
||||
private const LOGKEY = 'P--';
|
||||
|
||||
/* CONSTS */
|
||||
|
||||
// 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
|
||||
@ -33,24 +33,57 @@ abstract class Protocol
|
||||
|
||||
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 needs to be 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 */
|
||||
/* O_ options - [First 9 bits are protocol P_* (Interfaces/ZModem)] */
|
||||
|
||||
/** 0000 0000 0000 0000 0010 0000 0000 BOTH - No file requests accepted by this system */
|
||||
protected const O_NRQ = 1<<9;
|
||||
/** 0000 0000 0000 0000 0100 0000 0000 BOTH - Hold file requests (not processed at this time). */
|
||||
protected const O_HRQ = 1<<10;
|
||||
/** 0000 0000 0000 0000 1000 0000 0000 - Filename conversion, transmitted files must be 8.3 */
|
||||
protected const O_FNC = 1<<11;
|
||||
/** 0000 0000 0000 0001 0000 0000 0000 - Supports other forms of compressed mail */
|
||||
protected const O_XMA = 1<<12;
|
||||
/** 0000 0000 0000 0010 0000 0000 0000 BOTH - Hold ALL files (Answering System) */
|
||||
protected const O_HAT = 1<<13;
|
||||
/** 0000 0000 0000 0100 0000 0000 0000 BOTH - Hold Mail traffic */
|
||||
protected const O_HXT = 1<<14;
|
||||
/** 0000 0000 0000 1000 0000 0000 0000 - No files pickup desired (Calling System) */
|
||||
protected const O_NPU = 1<<15;
|
||||
/** 0000 0000 0001 0000 0000 0000 0000 - Pickup files for primary address only */
|
||||
protected const O_PUP = 1<<16;
|
||||
/** 0000 0000 0010 0000 0000 0000 0000 EMSI - Pickup files for all presented addresses */
|
||||
protected const O_PUA = 1<<17;
|
||||
/** 0000 0000 0100 0000 0000 0000 0000 BINK - Node needs to be password validated */
|
||||
protected const O_PWD = 1<<18;
|
||||
/** 0000 0000 1000 0000 0000 0000 0000 BOTH - Node invalid password presented */
|
||||
protected const O_BAD = 1<<19;
|
||||
/** 0000 0001 0000 0000 0000 0000 0000 EMSI - Use RH1 for Hydra (files-after-freqs) */
|
||||
protected const O_RH1 = 1<<20;
|
||||
/** 0000 0010 0000 0000 0000 0000 0000 BOTH - Node is nodelisted */
|
||||
protected const O_LST = 1<<21;
|
||||
/** 0000 0100 0000 0000 0000 0000 0000 BOTH - Inbound session */
|
||||
protected const O_INB = 1<<22;
|
||||
/** 0000 1000 0000 0000 0000 0000 0000 BOTH - TCP session */
|
||||
protected const O_TCP = 1<<23;
|
||||
/** 0001 0000 0000 0000 0000 0000 0000 EMSI - Remote understands EMSI-II */
|
||||
protected const O_EII = 1<<24;
|
||||
|
||||
/* Negotiation Options */
|
||||
|
||||
/** 00 0000 I/They dont want a capability? */
|
||||
protected const O_NO = 0;
|
||||
/** 00 0001 - I want a capability, but can be persuaded */
|
||||
protected const O_WANT = 1<<0;
|
||||
/** 00 0010 - They want a capability and we want it too */
|
||||
protected const O_WE = 1<<1;
|
||||
/** 00 0100 - They want a capability */
|
||||
protected const O_THEY = 1<<2;
|
||||
/** 00 1000 - I want a capability, and wont compromise */
|
||||
protected const O_NEED = 1<<3;
|
||||
/** 01 0000 - Extra options set */
|
||||
protected const O_EXT = 1<<4;
|
||||
/** 10 0000 - We agree on a capability and we are set to use it */
|
||||
protected const O_YES = 1<<5;
|
||||
|
||||
// Session Status
|
||||
protected const S_OK = 0;
|
||||
@ -66,24 +99,35 @@ abstract class Protocol
|
||||
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;
|
||||
public const FOP_OK = 0;
|
||||
public const FOP_CONT = 1;
|
||||
public const FOP_SKIP = 2;
|
||||
public const FOP_ERROR = 3;
|
||||
public const FOP_SUSPEND = 4;
|
||||
public const FOP_GOT = 5;
|
||||
|
||||
public const TCP_SPEED = 115200;
|
||||
|
||||
protected SocketClient $client; /* Our socket details */
|
||||
protected ?Setup $setup; /* Our setup */
|
||||
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 */
|
||||
/** The list of files we are sending */
|
||||
protected Send $send;
|
||||
/** The list of files we are receiving */
|
||||
protected Receive $recv;
|
||||
|
||||
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 */
|
||||
/** @var int The active options for a session */
|
||||
private int $options;
|
||||
/** @var int Tracks the session state */
|
||||
private int $session;
|
||||
/** @var array Our negotiated capability for a protocol session */
|
||||
protected array $capability; // @todo make private
|
||||
/** @var bool Are we originating a connection */
|
||||
protected bool $originate;
|
||||
/** Our comms details */
|
||||
|
||||
private array $comms;
|
||||
|
||||
abstract protected function protocol_init(): int;
|
||||
|
||||
@ -91,8 +135,8 @@ abstract class Protocol
|
||||
|
||||
public function __construct(Setup $o=NULL)
|
||||
{
|
||||
if ($o && ! $o->system->addresses->count())
|
||||
throw new \Exception('We dont have any FTN addresses assigned');
|
||||
if ($o && ! $o->system->akas->count())
|
||||
throw new \Exception('We dont have any ACTIVE FTN addresses assigned');
|
||||
|
||||
$this->setup = $o;
|
||||
}
|
||||
@ -105,7 +149,6 @@ abstract class Protocol
|
||||
switch ($key) {
|
||||
case 'ls_SkipGuard': /* double-skip protection on/off */
|
||||
case 'rxOptions': /* Options from ZRINIT header */
|
||||
case 'socket_error':
|
||||
return $this->comms[$key] ?? 0;
|
||||
|
||||
case 'ls_rxAttnStr':
|
||||
@ -125,7 +168,6 @@ abstract class Protocol
|
||||
case 'ls_rxAttnStr':
|
||||
case 'ls_SkipGuard':
|
||||
case 'rxOptions':
|
||||
case 'socket_error':
|
||||
$this->comms[$key] = $value;
|
||||
break;
|
||||
|
||||
@ -134,6 +176,55 @@ abstract class Protocol
|
||||
}
|
||||
}
|
||||
|
||||
/* Capabilities are what we negotitate with the remote and are valid for the session */
|
||||
|
||||
/**
|
||||
* Clear a capability bit
|
||||
*
|
||||
* @param int $cap (F_*)
|
||||
* @param int $val (O_*)
|
||||
* @return void
|
||||
*/
|
||||
public function capClear(int $cap,int $val): void
|
||||
{
|
||||
if (! array_key_exists($cap,$this->capability))
|
||||
$this->capability[$cap] = 0;
|
||||
|
||||
$this->capability[$cap] &= ~$val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a session bit (SE_*)
|
||||
*
|
||||
* @param int $cap (F_*)
|
||||
* @param int $val (O_*)
|
||||
* @return bool
|
||||
*/
|
||||
protected function capGet(int $cap,int $val): bool
|
||||
{
|
||||
if (! array_key_exists($cap,$this->capability))
|
||||
$this->capability[$cap] = 0;
|
||||
|
||||
if ($val === self::O_WE)
|
||||
return $this->capGet($cap,self::O_WANT) && $this->capGet($cap,self::O_THEY);
|
||||
|
||||
return $this->capability[$cap] & $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a session bit (SE_*)
|
||||
*
|
||||
* @param int $cap (F_*)
|
||||
* @param int $val (O_*)
|
||||
*/
|
||||
protected function capSet(int $cap,int $val): void
|
||||
{
|
||||
if (! array_key_exists($cap,$this->capability) || $val === 0)
|
||||
$this->capability[$cap] = 0;
|
||||
|
||||
$this->capability[$cap] |= $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* We got an error, close anything we are have open
|
||||
*
|
||||
@ -162,22 +253,40 @@ abstract class Protocol
|
||||
if ($pid === -1)
|
||||
throw new SocketException(SocketException::CANT_ACCEPT,'Could not fork process');
|
||||
|
||||
Log::debug(sprintf('%s:= End [%d]',self::LOGKEY,$pid));
|
||||
|
||||
// Parent return ready for next connection
|
||||
return $pid;
|
||||
}
|
||||
|
||||
/* O_* determine what features processing is availabile */
|
||||
|
||||
/**
|
||||
* Clear an option bit (O_*)
|
||||
*
|
||||
* @param int $key
|
||||
* @return void
|
||||
*/
|
||||
protected function optionClear(int $key): void
|
||||
{
|
||||
$this->options &= ~$key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an option bit (O_*)
|
||||
*
|
||||
* @param int $key
|
||||
* @return int
|
||||
*/
|
||||
protected function optionGet(int $key): int
|
||||
{
|
||||
return ($this->options & $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an option bit (O_*)
|
||||
*
|
||||
* @param int $key
|
||||
* @return void
|
||||
*/
|
||||
protected function optionSet(int $key): void
|
||||
{
|
||||
$this->options |= $key;
|
||||
@ -197,12 +306,12 @@ abstract class Protocol
|
||||
|
||||
$addresses = $addresses->unique();
|
||||
|
||||
Log::debug(sprintf('%s: - Presenting limited AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||
Log::debug(sprintf('%s:- Presenting limited AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||
|
||||
} else {
|
||||
$addresses = $this->setup->system->addresses;
|
||||
|
||||
Log::debug(sprintf('%s: - Presenting ALL our AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||
Log::debug(sprintf('%s:- Presenting ALL our AKAs [%s]',self::LOGKEY,$addresses->pluck('ftn')->join(',')));
|
||||
}
|
||||
|
||||
return $addresses;
|
||||
@ -214,19 +323,18 @@ abstract class Protocol
|
||||
* @param int $type
|
||||
* @param SocketClient $client
|
||||
* @param Address|null $o
|
||||
* @return int
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function session(int $type,SocketClient $client,Address $o=NULL): int
|
||||
public function session(int $type,SocketClient $client,Address $o=NULL): void
|
||||
{
|
||||
if ($o->exists)
|
||||
Log::withContext(['ftn'=>$o->ftn]);
|
||||
|
||||
Log::debug(sprintf('%s:+ Start [%d]',self::LOGKEY,$type));
|
||||
|
||||
// This sessions options
|
||||
$this->options = 0;
|
||||
$this->session = 0;
|
||||
$this->capability = [];
|
||||
|
||||
// Our files that we are sending/receive
|
||||
$this->send = new Send;
|
||||
@ -240,7 +348,7 @@ abstract class Protocol
|
||||
|
||||
// If we are connecting to a node
|
||||
if ($o->exists) {
|
||||
Log::debug(sprintf('%s: + Originating a connection to [%s]',self::LOGKEY,$o->ftn));
|
||||
Log::debug(sprintf('%s:+ Originating a connection to [%s]',self::LOGKEY,$o->ftn));
|
||||
$this->node->originate($o);
|
||||
|
||||
} else {
|
||||
@ -255,38 +363,39 @@ abstract class Protocol
|
||||
switch ($type) {
|
||||
/** @noinspection PhpMissingBreakStatementInspection */
|
||||
case self::SESSION_AUTO:
|
||||
Log::debug(sprintf('%s: - Trying EMSI',self::LOGKEY));
|
||||
Log::debug(sprintf('%s:- Trying EMSI',self::LOGKEY));
|
||||
|
||||
$rc = $this->protocol_init();
|
||||
if ($rc < 0) {
|
||||
Log::error(sprintf('%s:! Unable to start EMSI [%d]',self::LOGKEY,$rc));
|
||||
|
||||
return self::S_REDIAL | self::S_ADDTRY;
|
||||
return;
|
||||
}
|
||||
|
||||
case self::SESSION_EMSI:
|
||||
Log::debug(sprintf('%s: - Starting EMSI',self::LOGKEY));
|
||||
Log::debug(sprintf('%s:- Starting EMSI',self::LOGKEY));
|
||||
$rc = $this->protocol_session();
|
||||
|
||||
break;
|
||||
|
||||
case self::SESSION_BINKP:
|
||||
Log::debug(sprintf('%s: - Starting BINKP',self::LOGKEY));
|
||||
Log::debug(sprintf('%s:- Starting BINKP',self::LOGKEY));
|
||||
$rc = $this->protocol_session();
|
||||
|
||||
break;
|
||||
|
||||
case self::SESSION_ZMODEM:
|
||||
Log::debug(sprintf('%s: - Starting ZMODEM',self::LOGKEY));
|
||||
$this->client->speed = SocketClient::TCP_SPEED;
|
||||
Log::debug(sprintf('%s:- Starting ZMODEM',self::LOGKEY));
|
||||
$this->client->speed = self::TCP_SPEED;
|
||||
$this->originate = FALSE;
|
||||
$this->protocol_session();
|
||||
|
||||
return $this->protocol_session();
|
||||
return;
|
||||
|
||||
default:
|
||||
Log::error(sprintf('%s: ! Unsupported session type [%d]',self::LOGKEY,$type));
|
||||
Log::error(sprintf('%s:! Unsupported session type [%d]',self::LOGKEY,$type));
|
||||
|
||||
return self::S_REDIAL | self::S_ADDTRY;
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo Unlock outbounds
|
||||
@ -302,7 +411,7 @@ abstract class Protocol
|
||||
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',
|
||||
Log::info(sprintf('%s:= Total: %s - %d:%02d:%02d online, (%d) %lu%s sent, (%d) %lu%s received - %s',
|
||||
self::LOGKEY,
|
||||
$this->node->address ? $this->node->address->ftn : 'Unknown',
|
||||
$this->node->session_time/3600,
|
||||
@ -343,12 +452,12 @@ abstract class Protocol
|
||||
|
||||
// @todo Optional after session includes mail event
|
||||
// if ($this->node->start_time && $this->setup->cfg('CFG_AFTERMAIL')) {}
|
||||
|
||||
return ($rc & ~self::S_ADDTRY);
|
||||
}
|
||||
|
||||
/* SE_* flags determine our session processing status, at any point in time */
|
||||
|
||||
/**
|
||||
* Clear a session bit
|
||||
* Clear a session bit (SE_*)
|
||||
*
|
||||
* @param int $key
|
||||
*/
|
||||
@ -358,7 +467,8 @@ abstract class Protocol
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a session bit
|
||||
* Get a session bit (SE_*)
|
||||
*
|
||||
* @param int $key
|
||||
* @return bool
|
||||
*/
|
||||
@ -368,7 +478,7 @@ abstract class Protocol
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a session bit (with SE_*)
|
||||
* Set a session bit (SE_*)
|
||||
*
|
||||
* @param int $key
|
||||
*/
|
||||
@ -381,6 +491,7 @@ abstract class Protocol
|
||||
* Set our client that we are communicating with
|
||||
*
|
||||
* @param SocketClient $client
|
||||
* @deprecated use __get()/__set()
|
||||
*/
|
||||
protected function setClient(SocketClient $client): void
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,10 @@ final class DNS extends BaseProtocol
|
||||
{
|
||||
private const LOGKEY = 'PD-';
|
||||
|
||||
/* CONSTS */
|
||||
|
||||
public const PORT = 53;
|
||||
|
||||
private const DEFAULT_TTL = 86400;
|
||||
private const TLD = 'ftn';
|
||||
|
||||
@ -249,6 +253,7 @@ final class DNS extends BaseProtocol
|
||||
* @param int $code
|
||||
* @param array $answer
|
||||
* @param array $authority
|
||||
* @param array $additional
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
@ -23,24 +23,27 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
use CRCTrait;
|
||||
|
||||
private const EMSI_BEG = '**EMSI_';
|
||||
private const EMSI_ARGUS1 = '-PZT8AF6-';
|
||||
private const EMSI_DAT = self::EMSI_BEG.'DAT';
|
||||
private const EMSI_REQ = self::EMSI_BEG.'REQA77E';
|
||||
private const EMSI_INQ = self::EMSI_BEG.'INQC816';
|
||||
private const EMSI_ACK = self::EMSI_BEG.'ACKA490';
|
||||
private const EMSI_NAK = self::EMSI_BEG.'NAKEEC3';
|
||||
private const EMSI_HBT = self::EMSI_BEG.'HBTEAEE';
|
||||
/* CONSTS */
|
||||
|
||||
private const CR = "\r";
|
||||
private const NL = "\n";
|
||||
private const DEL = "\x08";
|
||||
public const PORT = 60179;
|
||||
private const EMSI_BEG = '**EMSI_';
|
||||
private const EMSI_ARGUS1 = '-PZT8AF6-';
|
||||
private const EMSI_DAT = self::EMSI_BEG.'DAT';
|
||||
private const EMSI_REQ = self::EMSI_BEG.'REQA77E';
|
||||
private const EMSI_INQ = self::EMSI_BEG.'INQC816';
|
||||
private const EMSI_ACK = self::EMSI_BEG.'ACKA490';
|
||||
private const EMSI_NAK = self::EMSI_BEG.'NAKEEC3';
|
||||
private const EMSI_HBT = self::EMSI_BEG.'HBTEAEE';
|
||||
|
||||
private const EMSI_BUF = 8192;
|
||||
private const TMP_LEN = 1024;
|
||||
private const CR = "\r";
|
||||
private const NL = "\n";
|
||||
private const DEL = "\x08";
|
||||
|
||||
private const SM_INBOUND = 0;
|
||||
private const SM_OUTBOUND = 1;
|
||||
private const EMSI_BUF = 8192;
|
||||
private const TMP_LEN = 1024;
|
||||
|
||||
private const SM_INBOUND = 0;
|
||||
private const SM_OUTBOUND = 1;
|
||||
|
||||
private const EMSI_HSTIMEOUT = 60; /* Handshake timeout */
|
||||
private const EMSI_SEQ_LEN = 14;
|
||||
@ -54,6 +57,8 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
private const EMSI_RESEND_TO = 5;
|
||||
|
||||
protected const MO_CHAT = 4;
|
||||
|
||||
// Our session status
|
||||
private int $session;
|
||||
|
||||
@ -84,10 +89,9 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
if (! parent::onConnect($client)) {
|
||||
Log::withContext(['pid'=>getmypid()]);
|
||||
|
||||
// @todo Can this be SESSION_EMSI? if so, set an object class value that in EMSI of SESSION_EMSI, and move this method to the parent class
|
||||
$this->session(self::SESSION_AUTO,$client,(new Address));
|
||||
$this->client->close();
|
||||
|
||||
Log::info(sprintf('%s:= onConnect - Connection closed [%s]',self::LOGKEY,$client->address_remote));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -101,7 +105,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
*/
|
||||
private function emsi_banner(): void
|
||||
{
|
||||
Log::debug(sprintf('%s:+ emsi_banner',self::LOGKEY));
|
||||
Log::debug(sprintf('%s:+ Showing EMSI banner',self::LOGKEY));
|
||||
|
||||
$banner = 'This is a mail only system - unless you are a mailer, you should disconnect :)';
|
||||
$this->client->buffer_add(self::EMSI_REQ.str_repeat(self::DEL,strlen(self::EMSI_REQ)).$banner.self::CR);
|
||||
@ -212,7 +216,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
);
|
||||
|
||||
// TRAF - netmail/echomail traffic size (bytes)
|
||||
$makedata .= sprintf('{TRAF}{%lX %lX}',$this->send->mail_size,$this->send->file_size);
|
||||
$makedata .= sprintf('{TRAF}{%lX %lX}',$this->send->mail_size,$this->send->size);
|
||||
|
||||
// MOH# - Mail On Hold - bytes waiting
|
||||
$makedata .= sprintf('{MOH#}{[%lX]}',$this->send->mail_size);
|
||||
@ -955,6 +959,8 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
*/
|
||||
protected function protocol_session(): int
|
||||
{
|
||||
// @todo introduce emsi_init() to perform the job of protocol_init. Only needs to be done when we originate a session
|
||||
|
||||
Log::debug(sprintf('%s:+ Starting EMSI Protocol Session',self::LOGKEY));
|
||||
|
||||
$was_req = 0;
|
||||
@ -1015,7 +1021,7 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
// @todo Lock Node AKAs
|
||||
|
||||
Log::info(sprintf('%s: - We have %lu%s mail, %lu%s files',self::LOGKEY,$this->send->mail_size,'b',$this->send->file_size,'b'));
|
||||
Log::info(sprintf('%s: - We have %lu%s mail, %lu%s files',self::LOGKEY,$this->send->mail_size,'b',$this->send->files_size,'b'));
|
||||
|
||||
$proto = $this->originate ? $this->node->optionGet(self::P_MASK) : $this->optionGet(self::P_MASK);
|
||||
|
||||
|
@ -70,11 +70,8 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
private const ZRUB1 = 0x6d; //m /* Translate to rubout 0377 */
|
||||
|
||||
/* Return codes -- pseudo-characters */
|
||||
private const LSZ_OK = self::OK; /* 0 */
|
||||
private const LSZ_TIMEOUT = self::TIMEOUT; /* -2 */
|
||||
private const LSZ_RCDO = self::RCDO; /* -3 */
|
||||
private const LSZ_CAN = self::GCOUNT; /* -4 */
|
||||
private const LSZ_ERROR = self::ERROR; /* -5 */
|
||||
|
||||
private const LSZ_CAN = -4;
|
||||
private const LSZ_NOHEADER = -6;
|
||||
private const LSZ_BADCRC = -7;
|
||||
private const LSZ_XONXOFF = -8;
|
||||
@ -312,7 +309,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
Log::debug(sprintf('%s:= zmodem_receive ZFIN after INIT, empty batch',self::LOGKEY));
|
||||
$this->ls_zdonereceiver();
|
||||
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
case self::ZFILE:
|
||||
Log::debug(sprintf('%s: = zmodem_receive ZFILE after INIT',self::LOGKEY));
|
||||
@ -324,7 +321,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$this->ls_zabort();
|
||||
$this->ls_zdonereceiver();
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
while (TRUE) {
|
||||
@ -333,7 +330,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
Log::debug(sprintf('%s:= zmodem_receive ZFIN',self::LOGKEY));
|
||||
$this->ls_zdonereceiver();
|
||||
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
case self::ZFILE:
|
||||
if (! $this->recv->to_get) {
|
||||
@ -375,7 +372,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
break;
|
||||
|
||||
case self::LSZ_OK:
|
||||
case self::OK:
|
||||
Log::debug(sprintf('%s: = zmodem_receive OK',self::LOGKEY));
|
||||
$this->recv->close();
|
||||
|
||||
@ -385,7 +382,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
Log::error(sprintf('%s:! zmodem_receive OTHER [%d]',self::LOGKEY,$rc));
|
||||
$this->recv->close();
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
@ -400,7 +397,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$this->ls_zabort();
|
||||
$this->ls_zdonereceiver();
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
default:
|
||||
Log::error(sprintf('%s:? zmodem_receive Something strange [%d]',self::LOGKEY,$rc));
|
||||
@ -408,13 +405,13 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$this->ls_zabort();
|
||||
$this->ls_zdonereceiver();
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
$rc = $this->ls_zrecvfinfo($frame,1);
|
||||
}
|
||||
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -444,10 +441,10 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$this->client->buffer_add('OO');
|
||||
$this->client->buffer_flush(5);
|
||||
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
case self::ZNAK:
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
$retransmit = 1;
|
||||
|
||||
break;
|
||||
@ -512,7 +509,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$rc = $this->ls_zsendfile($send,$this->ls_SerialNum++,$send->total_count,$send->total_size);
|
||||
|
||||
switch ($rc) {
|
||||
case self::LSZ_OK:
|
||||
case self::OK:
|
||||
$send->close(TRUE);
|
||||
break;
|
||||
|
||||
@ -582,7 +579,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
}
|
||||
|
||||
if (ord($c) === 0)
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
return $c&0x7f;
|
||||
}
|
||||
@ -865,7 +862,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
/* Ok, GOOD */
|
||||
case ord('O'):
|
||||
$rc = $this->client->read_ch(0);
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
case self::XON:
|
||||
case self::XOFF:
|
||||
@ -883,7 +880,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
return $rc;
|
||||
|
||||
if (self::ZFIN != $rc)
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
$retransmit = 1;
|
||||
|
||||
@ -1014,7 +1011,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
return $this->ls_zsendsinit($attstr);
|
||||
|
||||
else
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
/* Return number to peer, he is paranoid */
|
||||
case self::ZCHALLENGE:
|
||||
@ -1027,7 +1024,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
/* Send ZRQINIT again */
|
||||
case self::ZNAK:
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
$retransmit = 1;
|
||||
|
||||
break;
|
||||
@ -1037,7 +1034,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
Log::debug(sprintf('%s: - ls_zinitsender ZFIN [%d]',self::LOGKEY,$zfins));
|
||||
|
||||
if (++$zfins === self::LSZ_TRUSTZFINS)
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
break;
|
||||
|
||||
@ -1053,7 +1050,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
/* Abort this session -- we trust in ABORT! */
|
||||
case self::ZABORT:
|
||||
Log::debug(sprintf('%s:- ls_zinitsender ZABORT',self::LOGKEY));
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
default:
|
||||
Log::error(sprintf('%s: ? ls_zinitsender Something strange [%d]',self::LOGKEY,$rc));
|
||||
@ -1084,7 +1081,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
$got = 0; /* Bytes total got */
|
||||
$crc = 0; /* Received CRC */
|
||||
$frametype = self::LSZ_ERROR; /* Type of frame - ZCRC(G|W|Q|E) */
|
||||
$frametype = self::ERROR; /* Type of frame - ZCRC(G|W|Q|E) */
|
||||
$rcvdata = 1; /* Data is being received NOW (not CRC) */
|
||||
|
||||
while ($rcvdata && (($c = $this->ls_readzdle($timeout)) >= 0)) {
|
||||
@ -1163,7 +1160,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
$got = 0; /* Bytes total got */
|
||||
$crc = 0; /* Received CRC */
|
||||
$frametype = self::LSZ_ERROR; /* Type of frame - ZCRC(G|W|Q|E) */
|
||||
$frametype = self::ERROR; /* Type of frame - ZCRC(G|W|Q|E) */
|
||||
$rcvdata = 1; /* Data is being received NOW (not CRC) */
|
||||
|
||||
while ($rcvdata && (($c=$this->ls_readzdle($timeout)) >= 0)) {
|
||||
@ -1289,7 +1286,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
break;
|
||||
|
||||
case self::LSZ_BADCRC:
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
if ($this->ls_rxAttnStr) {
|
||||
$this->client->buffer_add($this->ls_rxAttnStr);
|
||||
$this->client->buffer_flush(5);
|
||||
@ -1344,7 +1341,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
if (($rc=$this->ls_zsendhhdr(self::ZRINIT,$this->ls_storelong(0))) < 0)
|
||||
return $rc;
|
||||
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
}
|
||||
|
||||
Log::debug(sprintf('%s: - ls_zrecvfile ZDATA',self::LOGKEY));
|
||||
@ -1357,7 +1354,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
} while (TRUE);
|
||||
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1422,8 +1419,8 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
case self::ZNAK:
|
||||
Log::debug(sprintf('%s: - ls_zrecvfinfo ZNAK',self::LOGKEY));
|
||||
|
||||
case self::LSZ_TIMEOUT:
|
||||
Log::debug(sprintf('%s: - ls_zrecvfinfo LSZ_TIMEOUT',self::LOGKEY));
|
||||
case self::TIMEOUT:
|
||||
Log::debug(sprintf('%s: - ls_zrecvfinfo TIMEOUT',self::LOGKEY));
|
||||
$retransmit = 1;
|
||||
|
||||
break;
|
||||
@ -1527,7 +1524,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
} while ($trys < 10);
|
||||
|
||||
Log::error(sprintf('%s:? ls_zrecvfinfo Something strange or timeout',self::LOGKEY));
|
||||
return self::LSZ_TIMEOUT;
|
||||
return self::TIMEOUT;
|
||||
}
|
||||
|
||||
private function ls_zrecvhdr(array &$hdr,int $timeout): int
|
||||
@ -1538,7 +1535,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$state = self::rhInit;
|
||||
$readmode = self::rm7BIT;
|
||||
|
||||
static $frametype = self::LSZ_ERROR; /* Frame type */
|
||||
static $frametype = self::ERROR; /* Frame type */
|
||||
static $crcl = 2; /* Length of CRC (CRC16 is default) */
|
||||
static $crcgot = 0; /* Number of CRC bytes already got */
|
||||
static $incrc = 0; /* Calculated CRC */
|
||||
@ -1552,7 +1549,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
if ($this->DEBUG)
|
||||
Log::debug(sprintf('%s: - ls_zrecvhdr Init State',self::LOGKEY));
|
||||
|
||||
$frametype = self::LSZ_ERROR;
|
||||
$frametype = self::ERROR;
|
||||
$crc = 0;
|
||||
$crcl = 2;
|
||||
$crcgot = 0;
|
||||
@ -1858,7 +1855,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
switch (($rc=$this->ls_zrecvdata($buf,$len,$this->ls_DataTimeout,$this->ls_Protocol&self::LSZ_OPTCRC32))) {
|
||||
/* Ok, here it is */
|
||||
case self::ZCRCW:
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
case self::LSZ_BADCRC:
|
||||
Log::debug(sprintf('%s: - ls_zrecvcrcw got BADCRC',self::LOGKEY));
|
||||
@ -1866,7 +1863,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
return 1;
|
||||
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
Log::debug(sprintf('%s: - ls_zrecvcrcw got TIMEOUT',self::LOGKEY));
|
||||
break;
|
||||
|
||||
@ -1913,7 +1910,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
break;
|
||||
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
break;
|
||||
|
||||
case self::LSZ_BADCRC:
|
||||
@ -1933,7 +1930,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
} while (++$trys < 10);
|
||||
|
||||
Log::error(sprintf('%s:? ls_zrecvnewpos Something strange or timeout [%d]',self::LOGKEY,$rc));
|
||||
return self::LSZ_TIMEOUT;
|
||||
return self::TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1952,7 +1949,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
if (++$this->ls_txReposCount > 10) {
|
||||
Log::error(sprintf('%s:! ZRPOS to [%ld] limit reached',self::LOGKEY,$newpos));
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -1966,14 +1963,14 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
if (! $send->seek($newpos)) {
|
||||
Log::error(sprintf('%s:! ZRPOS to [%ld] seek error',self::LOGKEY,$send->filepos));
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
if ($this->ls_txCurBlockSize > 32)
|
||||
$this->ls_txCurBlockSize >>= 1;
|
||||
|
||||
$this->ls_txGoodBlocks = 0;
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2069,7 +2066,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
[($this->ls_Protocol&self::LSZ_OPTVHDR)==self::LSZ_OPTVHDR]
|
||||
[($this->ls_Protocol&self::LSZ_OPTRLE)==self::LSZ_OPTRLE]) < 0)
|
||||
{
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
/* Send *<DLE> and packet type */
|
||||
@ -2145,7 +2142,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
Log::debug(sprintf('%s: - ls_zsendfile ZABORT/ZFIN',self::LOGKEY));
|
||||
$this->ls_zsendhhdr(self::ZFIN,$this->ls_storelong(0));
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
default:
|
||||
if ($rc < 0)
|
||||
@ -2153,7 +2150,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
Log::debug(sprintf('%s:- ls_zsendfile Strange answer on ZFILE [%d]',self::LOGKEY,$rc));
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
/* Send file data */
|
||||
@ -2181,7 +2178,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
} catch (\Exception $e) {
|
||||
Log::error(sprintf('%s:! ls_zsendfile Read error',self::LOGKEY));
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
|
||||
/* Select sub-frame type */
|
||||
@ -2266,11 +2263,11 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
$this->ls_zsendhhdr(self::ZFIN,$this->ls_storelong(0));
|
||||
/* Fall through */
|
||||
|
||||
case self::LSZ_RCDO:
|
||||
case self::LSZ_ERROR:
|
||||
return self::LSZ_ERROR;
|
||||
case self::RCDO:
|
||||
case self::ERROR:
|
||||
return self::ERROR;
|
||||
|
||||
case self::LSZ_TIMEOUT: /* Ok! */
|
||||
case self::TIMEOUT: /* Ok! */
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2289,7 +2286,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
&& ++$trys < 10);
|
||||
|
||||
if ($trys >= 10)
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
/* Ok, increase block, if here is MANY good blocks was sent */
|
||||
if (++$this->ls_txGoodBlocks > 32) {
|
||||
@ -2329,7 +2326,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
/* OK! */
|
||||
case self::ZRINIT:
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
/* ACK for data -- it lost! */
|
||||
case self::ZACK:
|
||||
@ -2344,10 +2341,10 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
case self::ZFIN:
|
||||
/* Abort too */
|
||||
case self::ZCAN:
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
|
||||
/* Ok, here is no header */
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
$trys++;
|
||||
|
||||
break;
|
||||
@ -2366,7 +2363,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
if ($send->feof()) {
|
||||
Log::error(sprintf('%s:! ls_zsendfile To many tries waiting for ZEOF ACK',self::LOGKEY));
|
||||
|
||||
return self::LSZ_ERROR;
|
||||
return self::ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2477,7 +2474,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
break;
|
||||
|
||||
case self::ZNAK:
|
||||
case self::LSZ_TIMEOUT:
|
||||
case self::TIMEOUT:
|
||||
$retransmit = 1;
|
||||
|
||||
break;
|
||||
@ -2587,7 +2584,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
/* Ok */
|
||||
case self::ZACK:
|
||||
return self::LSZ_OK;
|
||||
return self::OK;
|
||||
|
||||
/* Return number to peer, he is paranoid */
|
||||
case self::ZCHALLENGE:
|
||||
@ -2605,7 +2602,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface
|
||||
|
||||
/* Retransmit */
|
||||
case ZNAK:
|
||||
case LSZ_TIMEOUT:
|
||||
case TIMEOUT:
|
||||
$retransmit = 1;
|
||||
|
||||
break;
|
||||
|
@ -30,13 +30,15 @@ class CommBinkpReceive extends Command
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws SocketException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::info('Listening for BINKP connections...');
|
||||
$o = Setup::findOrFail(config('app.id'));
|
||||
|
||||
$server = new SocketServer(Setup::BINKP_PORT,Setup::BINKP_BIND);
|
||||
$server->setConnectionHandler([new Binkp(Setup::findOrFail(config('app.id'))),'onConnect']);
|
||||
$server = new SocketServer($o->binkp_port,$o->binkp_bind);
|
||||
$server->handler = [new Binkp($o),'onConnect'];
|
||||
|
||||
try {
|
||||
$server->listen();
|
||||
|
@ -32,7 +32,7 @@ class CommBinkpSend extends Command
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::info('Call BINKP send');
|
||||
Log::info('CBS:- Call BINKP send');
|
||||
|
||||
$ao = Address::findFTN($this->argument('ftn'));
|
||||
if (! $ao)
|
||||
|
@ -30,13 +30,15 @@ class CommEMSIReceive extends Command
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::info('Listening for EMSI connections...');
|
||||
$o = Setup::findOrFail(config('app.id'));
|
||||
|
||||
$server = new SocketServer(Setup::EMSI_PORT,Setup::EMSI_BIND);
|
||||
$server->setConnectionHandler([new EMSI(Setup::findOrFail(config('app.id'))),'onConnect']);
|
||||
$server->handler = [new EMSI($o),'onConnect'];
|
||||
|
||||
try {
|
||||
$server->listen();
|
||||
|
@ -29,13 +29,14 @@ class CommZmodemReceive extends Command
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
* @throws SocketException
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::info('Listening for ZMODEM connections...');
|
||||
|
||||
$server = new SocketServer(60177,'0.0.0.0');
|
||||
$server->setConnectionHandler([new ZmodemClass,'onConnect']);
|
||||
$server->handler = [new ZmodemClass,'onConnect'];
|
||||
|
||||
try {
|
||||
$server->listen();
|
||||
|
@ -43,8 +43,8 @@ class ServerStart extends Command
|
||||
|
||||
if ($o->optionGet(Setup::O_BINKP))
|
||||
$start->put('binkp',[
|
||||
'address'=>Setup::BINKP_BIND,
|
||||
'port'=>Setup::BINKP_PORT,
|
||||
'address'=>$o->binkp_bind,
|
||||
'port'=>$o->binkp_port,
|
||||
'proto'=>SOCK_STREAM,
|
||||
'class'=>new Binkp($o),
|
||||
]);
|
||||
@ -90,7 +90,7 @@ class ServerStart extends Command
|
||||
Log::info(sprintf('%s: - Started [%s]',self::LOGKEY,$item));
|
||||
|
||||
$server = new SocketServer($config['port'],$config['address'],$config['proto']);
|
||||
$server->setConnectionHandler([$config['class'],'onConnect']);
|
||||
$server->handler = [$config['class'],'onConnect'];
|
||||
|
||||
try {
|
||||
$server->listen();
|
||||
|
9
app/Exceptions/FileGrewException.php
Normal file
9
app/Exceptions/FileGrewException.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FileGrewException extends Exception
|
||||
{
|
||||
}
|
@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Gate;
|
||||
|
||||
use App\Classes\File;
|
||||
use App\Classes\FTN\Packet;
|
||||
use App\Http\Requests\SetupRequest;
|
||||
use App\Models\{Address,Domain,Echomail,Netmail,Setup};
|
||||
|
||||
class HomeController extends Controller
|
||||
@ -151,19 +152,11 @@ class HomeController extends Controller
|
||||
*
|
||||
* @note: Protected by Route
|
||||
*/
|
||||
public function setup(Request $request)
|
||||
public function setup(SetupRequest $request)
|
||||
{
|
||||
$o = Setup::findOrNew(config('app.id'));
|
||||
|
||||
if ($request->post()) {
|
||||
$request->validate([
|
||||
'system_id' => 'required|exists:systems,id',
|
||||
'binkp' => 'nullable|array',
|
||||
'binkp.*' => 'nullable|numeric',
|
||||
'options' => 'nullable|array',
|
||||
'options.*' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
if (! $o->exists) {
|
||||
$o->id = config('app.id');
|
||||
$o->zmodem = 0;
|
||||
@ -172,9 +165,17 @@ class HomeController extends Controller
|
||||
$o->permissions = 0;
|
||||
}
|
||||
|
||||
$o->binkp = collect($request->post('binkp'))->sum();
|
||||
$servers = collect();
|
||||
|
||||
$binkp = collect();
|
||||
$binkp->put('options',collect($request->post('binkp'))->sum());
|
||||
$binkp->put('port',$request->post('binkp_port'));
|
||||
$binkp->put('bind',$request->post('binkp_bind'));
|
||||
$servers->put('binkp',$binkp);
|
||||
|
||||
$o->options = collect($request->post('options'))->sum();
|
||||
$o->system_id = $request->post('system_id');
|
||||
$o->servers = $servers;
|
||||
$o->save();
|
||||
}
|
||||
|
||||
|
35
app/Http/Requests/SetupRequest.php
Normal file
35
app/Http/Requests/SetupRequest.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class SetupRequest extends FormRequest
|
||||
{
|
||||
public function authorize()
|
||||
{
|
||||
return Gate::allows( 'admin');
|
||||
}
|
||||
|
||||
public function rules(Request $request)
|
||||
{
|
||||
if (! $request->isMethod('post'))
|
||||
return [];
|
||||
|
||||
return [
|
||||
'system_id' => 'required|exists:systems,id',
|
||||
'binkp' => 'nullable|array',
|
||||
'binkp.*' => 'nullable|numeric',
|
||||
//'dns' => 'required|array',
|
||||
'dns.*' => 'nullable|numeric',
|
||||
//'emsi' => 'required|array',
|
||||
'emsi.*' => 'nullable|numeric',
|
||||
'*_bind' => 'required|ip',
|
||||
'*_port' => 'required|numeric',
|
||||
'options' => 'nullable|array',
|
||||
'options.*' => 'nullable|numeric',
|
||||
];
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ use Illuminate\Validation\Rule;
|
||||
use App\Classes\FTN\Packet;
|
||||
use App\Models\System;
|
||||
|
||||
// @todo rename to SystemRegisterRequest
|
||||
class SystemRegister extends FormRequest
|
||||
{
|
||||
private System $so;
|
||||
|
@ -72,6 +72,16 @@ class Address extends Model
|
||||
->FTNorder();
|
||||
}
|
||||
|
||||
public function scopeTrashed($query)
|
||||
{
|
||||
return $query->select($this->getTable().'.*')
|
||||
->join('zones',['zones.id'=>'addresses.zone_id'])
|
||||
->join('domains',['domains.id'=>'zones.domain_id'])
|
||||
->orderBy('domains.name')
|
||||
->withTrashed()
|
||||
->FTNorder();
|
||||
}
|
||||
|
||||
public function scopeFTNOrder($query)
|
||||
{
|
||||
return $query
|
||||
@ -371,13 +381,18 @@ class Address extends Model
|
||||
* @return Address|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function findFTN(string $address,bool $create=FALSE,System $so=NULL): ?self
|
||||
public static function findFTN(string $address,bool $create=FALSE,System $so=NULL,bool $trashed=FALSE): ?self
|
||||
{
|
||||
$ftn = self::parseFTN($address);
|
||||
|
||||
// Are we looking for a region address
|
||||
if (($ftn['f'] === 0) && $ftn['p'] === 0) {
|
||||
$o = (new self)->active()
|
||||
$o = (new self)
|
||||
->when($trashed,function($query) {
|
||||
$query->trashed();
|
||||
},function($query) {
|
||||
$query->active();
|
||||
})
|
||||
->where('zones.zone_id',$ftn['z'])
|
||||
->where(function($q) use ($ftn) {
|
||||
return $q
|
||||
@ -400,7 +415,12 @@ class Address extends Model
|
||||
return $o;
|
||||
}
|
||||
|
||||
$o = (new self)->active()
|
||||
$o = (new self)
|
||||
->when($trashed,function($query) {
|
||||
$query->trashed();
|
||||
},function($query) {
|
||||
$query->active();
|
||||
})
|
||||
->where('zones.zone_id',$ftn['z'])
|
||||
->where(function($q) use ($ftn) {
|
||||
return $q->where(function($qq) use ($ftn) {
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
use App\Classes\Protocol\{Binkp,DNS,EMSI};
|
||||
|
||||
/**
|
||||
* This class represents our configuration.
|
||||
*
|
||||
@ -14,26 +16,17 @@ use Illuminate\Support\Facades\File;
|
||||
*
|
||||
* @package App\Models
|
||||
* @property Collection nodes
|
||||
* @property array binkp_options
|
||||
*/
|
||||
class Setup extends Model
|
||||
{
|
||||
public const S_DOMAIN = 1<<1; // Users can create Domains
|
||||
public const S_SYSTEM = 1<<2; // Users can create Systems
|
||||
public const PRODUCT_NAME = 'Clearing Houz';
|
||||
public const PRODUCT_ID = 0xAB8D;
|
||||
public const PRODUCT_VERSION_MAJ = 0;
|
||||
public const PRODUCT_VERSION_MIN = 0;
|
||||
|
||||
public const BINKP_OPT_CHT = 1<<1; /* CHAT mode - not implemented */
|
||||
public const BINKP_OPT_CR = 1<<2; /* Crypt mode - not implemented */
|
||||
public const BINKP_OPT_MB = 1<<3; /* Multi-Batch mode */
|
||||
public const BINKP_OPT_MD = 1<<4; /* CRAM-MD5 mode */
|
||||
public const BINKP_OPT_ND = 1<<5; /* http://ftsc.org/docs/fsp-1027.001: No-dupes mode */
|
||||
public const BINKP_OPT_NDA = 1<<6; /* http://ftsc.org/docs/fsp-1027.001: Asymmetric ND mode */
|
||||
public const BINKP_OPT_NR = 1<<7; /* http://ftsc.org/docs/fsp-1027.001: Non-Reliable mode */
|
||||
public const BINKP_OPT_MPWD = 1<<8; /* Multi-Password mode - not implemented */
|
||||
|
||||
public const BINKP_PORT = 24554;
|
||||
public const BINKP_BIND = '::';
|
||||
public const BIND = '::';
|
||||
public const EMSI_PORT = 60179;
|
||||
public const EMSI_BIND = self::BINKP_BIND;
|
||||
public const EMSI_BIND = self::BIND;
|
||||
public const DNS_PORT = 53;
|
||||
public const DNS_BIND = '::';
|
||||
|
||||
@ -42,25 +35,19 @@ class Setup extends Model
|
||||
public const O_DNS = 1<<3; /* List for DNS */
|
||||
public const O_HIDEAKA = 1<<4; /* Hide AKAs to different Zones */
|
||||
|
||||
public const PRODUCT_NAME = 'Clearing Houz';
|
||||
public const PRODUCT_ID = 0xAB8D;
|
||||
public const PRODUCT_VERSION_MAJ = 0;
|
||||
public const PRODUCT_VERSION_MIN = 0;
|
||||
|
||||
public const MAX_MSGS_PKT = 50;
|
||||
|
||||
public const hexdigitslower = '0123456789abcdef';
|
||||
|
||||
// Our non model attributes and values
|
||||
private array $internal = [];
|
||||
|
||||
protected $casts = [
|
||||
'servers' => 'array',
|
||||
];
|
||||
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
parent::__construct($attributes);
|
||||
|
||||
// @todo These option should be in setup?
|
||||
$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;
|
||||
@ -68,21 +55,30 @@ class Setup extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'binkp_bind':
|
||||
case 'dns_bind':
|
||||
case 'emsi_bind':
|
||||
return Arr::get($this->servers,str_replace('_','.',$key),self::BIND);
|
||||
|
||||
case 'binkp_port':
|
||||
return Arr::get($this->servers,str_replace('_','.',$key),Binkp::PORT);
|
||||
case 'dns_port':
|
||||
return Arr::get($this->servers,str_replace('_','.',$key),EMSI::PORT);
|
||||
case 'emsi_port':
|
||||
return Arr::get($this->servers,str_replace('_','.',$key),DNS::PORT);
|
||||
|
||||
case 'binkp_options':
|
||||
case 'dns_options':
|
||||
case 'emsi_options':
|
||||
return Arr::get($this->servers,'binkp.options');
|
||||
|
||||
case 'binkp_settings':
|
||||
case 'ignore_nrq':
|
||||
case 'opt_nr': // @todo - this keys are now in #binkp as bits
|
||||
case 'opt_nd':
|
||||
case 'opt_nda':
|
||||
case 'opt_md':
|
||||
case 'opt_cr':
|
||||
case 'opt_mb':
|
||||
case 'opt_cht':
|
||||
case 'opt_mpwd':
|
||||
case 'do_prevent':
|
||||
return $this->internal[$key] ?? FALSE;
|
||||
|
||||
@ -98,12 +94,23 @@ class Setup extends Model
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __set($key,$value)
|
||||
{
|
||||
switch ($key) {
|
||||
case 'binkp_bind':
|
||||
case 'binkp_port':
|
||||
case 'binkp_options':
|
||||
case 'dns_bind':
|
||||
case 'dns_port':
|
||||
case 'dns_options':
|
||||
case 'emsi_bind':
|
||||
case 'emsi_port':
|
||||
case 'emsi_options':
|
||||
return Arr::set($this->servers,str_replace('_','.',$key),$value);
|
||||
|
||||
case 'binkp_settings':
|
||||
case 'ignore_nrq':
|
||||
case 'opt_nr':
|
||||
case 'opt_nd':
|
||||
@ -127,7 +134,7 @@ class Setup extends Model
|
||||
*
|
||||
* @param int $c
|
||||
* @return string
|
||||
* @throws Exception
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function product_id(int $c=self::PRODUCT_ID): string
|
||||
{
|
||||
@ -165,37 +172,18 @@ class Setup extends Model
|
||||
|
||||
/* METHODS */
|
||||
|
||||
/* BINKP OPTIONS: BINKP_OPT_* */
|
||||
|
||||
public function binkpOptionClear(int $key): void
|
||||
public function optionClear(int $key,$index='options'): void
|
||||
{
|
||||
$this->binkp &= ~$key;
|
||||
$this->{$index} &= ~$key;
|
||||
}
|
||||
|
||||
public function binkpOptionGet(int $key): int
|
||||
public function optionGet(int $key,$index='options'): int
|
||||
{
|
||||
return ($this->binkp & $key);
|
||||
return ($this->{$index} & $key);
|
||||
}
|
||||
|
||||
public function binkpOptionSet(int $key): void
|
||||
public function optionSet(int $key,$index='options'): void
|
||||
{
|
||||
$this->binkp |= $key;
|
||||
}
|
||||
|
||||
/* GENERAL OPTIONS: O_* */
|
||||
|
||||
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;
|
||||
$this->{$index} |= $key;
|
||||
}
|
||||
}
|
@ -127,6 +127,11 @@ class System extends Model
|
||||
}
|
||||
}
|
||||
|
||||
public function getPhoneAttribute(string $val): string
|
||||
{
|
||||
return $val ?: '-Unpublished-';
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
public function echoareas()
|
||||
|
@ -9,6 +9,11 @@ trait CRC
|
||||
return (self::crc16usd_tab[(($crc >> 8) ^ $b) & 0xff] ^ (($crc & 0x00ff) << 8)) & 0xffff;
|
||||
}
|
||||
|
||||
private function CRC32_CR(int $c,int $b)
|
||||
{
|
||||
return (self::crc32_tab[((int)($c) ^ ($b)) & 0xff] ^ (($c) >> 8));
|
||||
}
|
||||
|
||||
private function CRC32_FINISH(int $crc)
|
||||
{
|
||||
return ~$crc & 0xffffffff;
|
||||
|
@ -8,6 +8,7 @@
|
||||
"php": "^8.1|8.2",
|
||||
"ext-pcntl": "*",
|
||||
"ext-sockets": "*",
|
||||
"ext-bz2": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-zstd": "*",
|
||||
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('setups',function (Blueprint $table) {
|
||||
$table->jsonb('servers')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('setups',function (Blueprint $table) {
|
||||
$table->dropColumn(['servers']);
|
||||
});
|
||||
}
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
<!-- $o = Setup::class -->
|
||||
@php
|
||||
use App\Models\Setup;
|
||||
use App\Classes\Protocol\Binkp;
|
||||
@endphp
|
||||
|
||||
@extends('layouts.app')
|
||||
@ -100,62 +101,94 @@ use App\Models\Setup;
|
||||
<div class="row">
|
||||
<!-- Binkp Settings -->
|
||||
<div class="col-6">
|
||||
<h3>BINKP Settings</h3>
|
||||
<p>BINKD has been configured to listen on <strong>{{ Setup::BINKP_BIND }}</strong>:<strong>{{ Setup::BINKP_PORT }}</strong></p>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h3>BINKP Settings</h3>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="startbinkd" name="options[binkd]" value="{{ Setup::O_BINKP }}" @if(old('options.binkd',$o->optionGet(Setup::O_BINKP))) checked @endif>
|
||||
<label class="form-check-label" for="startbinkd">Listen for BINKP connections</label>
|
||||
<div class="form-group row pt-0">
|
||||
<div class="col-9">
|
||||
<label class="col-form-label pt-2" for="binkp_bind">Listing Interface</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input class="form-control text-end @error('binkp_bind')is-invalid @enderror" type="text" id="binkp_bind" name="binkp_bind" value="{{ old('binkp_bind',$o->binkp_bind) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row pt-0">
|
||||
<div class="col-9">
|
||||
<label class="col-form-label pt-2" for="binkp_port">Listing Port</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input class="form-control text-end @error('binkp_port')is-invalid @enderror" type="text" id="binkp_port" name="binkp_port" value="{{ old('binkp_port',$o->binkp_port) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="startbinkd" name="options[binkd]" value="{{ Setup::O_BINKP }}" @if(old('options.binkd',$o->optionGet(Setup::O_BINKP))) checked @endif>
|
||||
<label class="form-check-label" for="startbinkd">Listen for BINKP connections</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_cht" name="binkp[cht]" value="{{ Binkp::F_CHAT }}" @if(old('binkp.cht',$o->optionGet(Binkp::F_CHAT,'binkp_options'))) checked @endif disabled>
|
||||
<label class="form-check-label" for="opt_cht">Chat Mode <sup>not implemented</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_comp" name="binkp[comp]" value="{{ Binkp::F_COMP }}" @if(old('binkp.comp',$o->optionGet(Binkp::F_COMP,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_comp">Compression Enabled <sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_md" name="binkp[md]" value="{{ Binkp::F_MD }}" @if(old('binkp.md',$o->optionGet(Binkp::F_MD,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_md">CRAM-MD5 Mode <sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<!-- @todo Force turning off this toggle, if md5 is not selected -->
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_mdforce" name="binkp[mdforce]" value="{{ Binkp::F_MDFORCE }}" @if(old('binkp.mdforce',$o->optionGet(Binkp::F_MDFORCE,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_mdforce">No Plaintext Passwords</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_cr" name="binkp[cr]" value="{{ Binkp::F_CRYPT }}" @if(old('binkp.cr',$o->optionGet(Binkp::F_CRYPT,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_cr">Crypt mode <sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_mb" name="binkp[mb]" value="{{ Binkp::F_MULTIBATCH }}" @if(old('binkp.mb',$o->optionGet(Binkp::F_MULTIBATCH,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_mb">Multi-Batch mode <sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_mpwd" name="binkp[mpwd]" value="{{ Binkp::F_MULTIPASS }}" @if(old('binkp.mpwd',$o->optionGet(Binkp::F_MULTIPASS,'binkp_options'))) checked @endif disabled>
|
||||
<label class="form-check-label" for="opt_mpwd">Multi-Password Mode <sup>not implemented</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_nd" name="binkp[nd]" value="{{ Binkp::F_NODUPE }}" @if(old('binkp.nd',$o->optionGet(Binkp::F_NODUPE,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_nd">No Dupes Mode</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_nda" name="binkp[nda]" value="{{ Binkp::F_NODUPEA }}" @if(old('binkp.nda',$o->optionGet(Binkp::F_NODUPEA,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_nda">No Dupes Mode - Asymmetric</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_nr" name="binkp[nr]" value="{{ Binkp::F_NOREL }}" @if(old('binkp.nr',$o->optionGet(Binkp::F_NOREL,'binkp_options'))) checked @endif>
|
||||
<label class="form-check-label" for="opt_nr">Non-Reliable Mode <sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<p class="pt-3"><sup>*</sup> Recommended Defaults</p>
|
||||
|
||||
<!-- @todo What's this for again? -->
|
||||
<table class="table monotable">
|
||||
{{--
|
||||
$this->binkp_options = ['m','d','r','b'];
|
||||
--}}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_cht" name="binkp[cht]" value="{{ Setup::BINKP_OPT_CHT }}" @if(old('binkp.cht',$o->binkpOptionGet(Setup::BINKP_OPT_CHT))) checked @endif disabled>
|
||||
<label class="form-check-label" for="opt_cht">Chat Mode <sup>not implemented</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_md" name="binkp[md]" value="{{ Setup::BINKP_OPT_MD }}" @if(old('binkp.md',$o->binkpOptionGet(Setup::BINKP_OPT_MD))) checked @endif>
|
||||
<label class="form-check-label" for="opt_md">CRAM-MD5 Mode</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_cr" name="binkp[cr]" value="{{ Setup::BINKP_OPT_CR }}" @if(old('binkp.cr',$o->binkpOptionGet(Setup::BINKP_OPT_CR))) checked @endif disabled>
|
||||
<label class="form-check-label" for="opt_cr">Crypt mode <sup>not implemented</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_mb" name="binkp[mb]" value="{{ Setup::BINKP_OPT_MB }}" @if(old('binkp.mb',$o->binkpOptionGet(Setup::BINKP_OPT_MB))) checked @endif>
|
||||
<label class="form-check-label" for="opt_mb">Multi-Batch mode<sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_mpwd" name="binkp[mpwd]" value="{{ Setup::BINKP_OPT_MPWD }}" @if(old('binkp.mpwd',$o->binkpOptionGet(Setup::BINKP_OPT_MPWD))) checked @endif disabled>
|
||||
<label class="form-check-label" for="opt_mpwd">Multi-Password Mode <sup>not implemented</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_nd" name="binkp[nd]" value="{{ Setup::BINKP_OPT_ND }}" @if(old('binkp.nd',$o->binkpOptionGet(Setup::BINKP_OPT_ND))) checked @endif>
|
||||
<label class="form-check-label" for="opt_nd">No Dupes Mode</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_nda" name="binkp[nda]" value="{{ Setup::BINKP_OPT_NDA }}" @if(old('binkp.nda',$o->binkpOptionGet(Setup::BINKP_OPT_NDA))) checked @endif>
|
||||
<label class="form-check-label" for="opt_nda">No Dupes Mode - Asymmetric<sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<div class="mt-1 form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="opt_nr" name="binkp[nr]" value="{{ Setup::BINKP_OPT_NR }}" @if(old('binkp.nr',$o->binkpOptionGet(Setup::BINKP_OPT_NR))) checked @endif>
|
||||
<label class="form-check-label" for="opt_nr">Non-Reliable Mode<sup>*</sup></label>
|
||||
</div>
|
||||
|
||||
<p class="pt-3"><sup>*</sup> Recommended Defaults</p>
|
||||
|
||||
<!-- @todo What's this for again? -->
|
||||
<table class="table monotable">
|
||||
{{--
|
||||
$this->binkp_options = ['m','d','r','b'];
|
||||
--}}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- EMSI Settings -->
|
||||
@ -163,7 +196,24 @@ use App\Models\Setup;
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h3>EMSI Settings</h3>
|
||||
<p>EMSI has been configured to listen on <strong>{{ Setup::EMSI_BIND }}</strong>:<strong>{{ Setup::EMSI_PORT }}</strong></p>
|
||||
|
||||
<div class="form-group row pt-0">
|
||||
<div class="col-9">
|
||||
<label class="col-form-label pt-2" for="emsi_bind">Listing Interface</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input class="form-control text-end @error('emsi_bind')is-invalid @enderror" type="text" id="emsi_bind" name="emsi_bind" value="{{ old('emsi_bind',$o->emsi_bind) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row pt-0">
|
||||
<div class="col-9">
|
||||
<label class="col-form-label pt-2" for="emsi_port">Listing Port</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input class="form-control text-end @error('emsi_port')is-invalid @enderror" type="text" id="emsi_port" name="emsi_port" value="{{ old('emsi_port',$o->emsi_port) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="startemsi" name="options[emsi]" value="{{ Setup::O_EMSI }}" @if(old('options.emsi',$o->optionGet(Setup::O_EMSI))) checked @endif>
|
||||
@ -175,7 +225,24 @@ use App\Models\Setup;
|
||||
<div class="row pt-5">
|
||||
<div class="col-12">
|
||||
<h3>DNS Settings</h3>
|
||||
<p>DNS has been configured to listen on <strong>{{ Setup::DNS_BIND }}</strong>:<strong>{{ Setup::DNS_PORT }}</strong></p>
|
||||
|
||||
<div class="form-group row pt-0">
|
||||
<div class="col-9">
|
||||
<label class="col-form-label pt-2" for="dns_bind">Listing Interface</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input class="form-control text-end @error('dns_bind')is-invalid @enderror" type="text" id="dns_bind" name="dns_bind" value="{{ old('dns_bind',$o->dns_bind) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row pt-0">
|
||||
<div class="col-9">
|
||||
<label class="col-form-label pt-2" for="dns_port">Listing Port</label>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<input class="form-control text-end @error('dns_port')is-invalid @enderror" type="text" id="dns_port" name="dns_port" value="{{ old('dns_port',$o->dns_port) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="startdns" name="options[dns]" value="{{ Setup::O_DNS }}" @if(old('options.dns',$o->optionGet(Setup::O_DNS))) checked @endif>
|
||||
@ -210,8 +277,13 @@ use App\Models\Setup;
|
||||
<span class="col-6 mt-auto mx-auto text-center align-bottom">
|
||||
@if($errors->count())
|
||||
<span class="btn btn-sm btn-danger" role="alert">
|
||||
There were errors with the submission.
|
||||
@dump($errors)
|
||||
<h3 class="text-white">There were errors with the submission.</h3>
|
||||
|
||||
<ul>
|
||||
@foreach ($errors->all() as $message)
|
||||
<li>{{ $message }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</span>
|
||||
@endif
|
||||
</span>
|
||||
@ -230,6 +302,15 @@ use App\Models\Setup;
|
||||
|
||||
@section('page-css')
|
||||
@css('select2')
|
||||
|
||||
<style>
|
||||
#content h3 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#content ul li:last-child {
|
||||
margin-bottom: inherit;
|
||||
}
|
||||
</style>
|
||||
@append
|
||||
@section('page-scripts')
|
||||
@js('select2')
|
||||
|
@ -388,6 +388,71 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Last Packets and Files -->
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="mail" data-bs-toggle="collapse" data-bs-target="#collapse_transfers" aria-expanded="false" aria-controls="collapse_mail">Packets and Files Sent</h3>
|
||||
|
||||
<div id="collapse_transfers" class="accordion-collapse collapse {{ ($flash=='transfers') ? 'show' : '' }}" aria-labelledby="mail" data-bs-parent="#accordion_homepage">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
The last Netmails sent:
|
||||
<table class="table monotable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>FTN</th>
|
||||
<th>Packet</th>
|
||||
<th>Sent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@dump($o->addresses->sortBy('zone.zone_id')->pluck('ftn'))
|
||||
@foreach (\App\Models\Netmail::where('tftn_id',$o->addresses->pluck('id'))->whereNotNull('sent_at')->orderBy('sent_at')->limit(10) as $no)
|
||||
<tr>
|
||||
<td>{{ $no->tftn->ftn }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
The last Echomails sent:
|
||||
<table class="table monotable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>FTN</th>
|
||||
<th>Packet</th>
|
||||
<th>Sent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach (\App\Models\Echomail::join('echomail_seenby',['echomail_seenby.echomail_id'=>'echomails.id'])
|
||||
->whereNotNull('echomail_seenby.sent_at')
|
||||
->whereIn('address_id',$o->addresses->pluck('id'))
|
||||
->orderBy('sent_at','DESC')
|
||||
->orderBy('packet')
|
||||
->limit(10)
|
||||
->get()
|
||||
->groupBy('packet') as $oo)
|
||||
<tr>
|
||||
<td>{{ \App\Models\Address::where('id',$oo->first()->address_id)->single()->ftn }}</td>
|
||||
<td>{{ $oo->first()->packet }}</td>
|
||||
<td>{{ $oo->first()->sent_at }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
@include('system.form-system')
|
||||
|
Loading…
Reference in New Issue
Block a user