From b1b86ca04a99963c6a783e3d40f697e4e9b5b145 Mon Sep 17 00:00:00 2001 From: Deon George Date: Thu, 22 Jun 2023 17:36:22 +1000 Subject: [PATCH] Implemented file sending during BINKP and EMSI sessions --- app/Classes/FTN/Message.php | 1 + app/Classes/FTN/Packet.php | 3 + app/Classes/FTN/Process.php | 3 + app/Classes/FTN/Tic.php | 167 ++++++++++++++++++--------- app/Classes/File/Item.php | 34 ++++-- app/Classes/File/Send.php | 81 +++++++++++-- app/Classes/File/Tic.php | 38 ++++++ app/Classes/Protocol/Binkp.php | 7 +- app/Classes/Protocol/EMSI.php | 9 ++ app/Classes/Sock/SocketClient.php | 13 ++- app/Classes/Sock/SocketException.php | 3 + app/Jobs/TicProcess.php | 3 +- app/Models/Address.php | 26 +++++ app/Models/File.php | 2 +- 14 files changed, 308 insertions(+), 82 deletions(-) create mode 100644 app/Classes/File/Tic.php diff --git a/app/Classes/FTN/Message.php b/app/Classes/FTN/Message.php index b0a8016..b946764 100644 --- a/app/Classes/FTN/Message.php +++ b/app/Classes/FTN/Message.php @@ -18,6 +18,7 @@ use App\Traits\EncodeUTF8; /** * Class Message + * Represents the structure of a message in a packet * NOTE: FTN Echomail Messages are ZONE agnostic. * * @package App\Classes diff --git a/app/Classes/FTN/Packet.php b/app/Classes/FTN/Packet.php index dc26d11..90ce66a 100644 --- a/app/Classes/FTN/Packet.php +++ b/app/Classes/FTN/Packet.php @@ -12,6 +12,9 @@ use Symfony\Component\HttpFoundation\File\File; use App\Classes\FTN as FTNBase; use App\Models\{Address,Setup,Software,System,Zone}; +/** + * Represents the structure of a packet + */ class Packet extends FTNBase implements \Iterator, \Countable { private const LOGKEY = 'PKT'; diff --git a/app/Classes/FTN/Process.php b/app/Classes/FTN/Process.php index ab67b6a..184f5a1 100644 --- a/app/Classes/FTN/Process.php +++ b/app/Classes/FTN/Process.php @@ -4,6 +4,9 @@ namespace App\Classes\FTN; use Illuminate\Support\Arr; +/** + * Abstract class to hold the common functions for automatic responding to echomail/netmail messages + */ abstract class Process { private const LOGKEY = 'R--'; diff --git a/app/Classes/FTN/Tic.php b/app/Classes/FTN/Tic.php index 5d30bba..ec8e168 100644 --- a/app/Classes/FTN/Tic.php +++ b/app/Classes/FTN/Tic.php @@ -4,16 +4,18 @@ namespace App\Classes\FTN; use Carbon\Carbon; use Illuminate\Contracts\Filesystem\FileNotFoundException; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\UnableToWriteFile; use App\Classes\FTN as FTNBase; -use App\Models\{Address,File,Filearea}; +use App\Models\{Address,File,Filearea,Setup}; use App\Traits\EncodeUTF8; /** * Class TIC + * Used create the structure of TIC files * * @package App\Classes */ @@ -50,34 +52,96 @@ class Tic extends FTNBase 'pw' => FALSE, // Password ]; - private File $file; + private File $fo; private Filearea $area; - private ?string $areadesc = NULL; - private ?string $pw = NULL; + private Collection $values; private Address $origin; // Should be first address in Path private Address $from; // Should be last address in Path private Address $to; // Should be me - public function __construct(private string $filename) { + public function __construct() + { + $this->fo = new File; + + $this->fo->kludges = collect(); + $this->fo->set_path = collect(); + $this->fo->set_seenby = collect(); + $this->fo->rogue_path = collect(); + $this->fo->rogue_seenby = collect(); + + $this->values = collect(); + } + + /** + * Does this TIC file bring us a nodelist + * + * @return bool + */ + public function isNodelist(): bool + { + return (($this->fo->nodelist_filearea_id === $this->fo->filearea->domain->filearea_id) + && (preg_match(str_replace(['.','?'],['\.','.'],'#^'.$this->fo->filearea->domain->nodelist_filename.'$#i'),$this->fo->file))); + } + + /** + * Generate a TIC file for an address + * + * @param Address $ao + * @param File $fo + * @return string + */ + public function generate(Address $ao,File $fo): string + { + $sysaddress = Setup::findOrFail(config('app.id'))->system->match($ao->zone)->first(); + + $result = collect(); + + // Origin is the first address in our path + $result->put('ORIGIN',$fo->path->first()->ftn3d); + $result->put('FROM',$sysaddress->ftn3d); + $result->put('TO',$ao->ftn3d); + $result->put('FILE',$fo->file); + $result->put('SIZE',$fo->size); + if ($fo->description) + $result->put('DESC',$fo->description); + $result->put('AREA',$fo->filearea->name); + $result->put('AREADESC',$fo->filearea->description); + $result->put('PW',$ao->session('ticpass')); + $result->put('CRC',sprintf("%X",$fo->crc)); + + $out = ''; + foreach ($result as $key=>$value) + $out .= sprintf("%s %s\r\n",$key,$value); + + foreach ($fo->path as $o) + $out .= sprintf("PATH %s %s %s\r\n",$o->ftn3d,$o->pivot->datetime,$o->pivot->extra); + + foreach ($fo->seenby as $o) + $out .= sprintf("SEENBY %s\r\n",$o->ftn3d); + + return $out; + } + + /** + * Load a TIC file from an existing filename + * + * @param string $filename + * @return void + * @throws FileNotFoundException + */ + public function load(string $filename): void + { Log::info(sprintf('%s:Processing TIC file [%s]',self::LOGKEY,$filename)); - $fo = new File; - - $fo->kludges = collect(); - $fo->set_path = collect(); - $fo->set_seenby = collect(); - $fo->rogue_path = collect(); - $fo->rogue_seenby = collect(); - list($hex,$name) = explode('-',$filename); $hex = basename($hex); if (! file_exists($filename)) throw new FileNotFoundException(sprintf('%s:File [%s] doesnt exist',self::LOGKEY,realpath($filename))); - if (! is_writable($filename)) - throw new UnableToWriteFile(sprintf('%s:File [%s] is not writable',self::LOGKEY,realpath($filename))); + if (! is_readable($filename)) + throw new UnableToWriteFile(sprintf('%s:File [%s] is not readable',self::LOGKEY,realpath($filename))); $f = fopen($filename,'rb'); if (! $f) { @@ -112,38 +176,43 @@ class Tic extends FTNBase if (! Storage::disk('local')->exists($x=sprintf('%s/%s-%s',config('app.fido'),$hex,$matches[2]))) throw new FileNotFoundException(sprintf('File not found? [%s]',$x)); - $fo->{$k} = $matches[2]; - $fo->fullname = $x; + $this->fo->{$k} = $matches[2]; + $this->fo->fullname = $x; break; case 'areadesc': - case 'pw': - case 'created': - $this->{$k} = $matches[2]; + $areadesc = $matches[2]; break; - case 'lfile': - case 'size': + case 'created': + // ignored + break; + + case 'pw': + $pw = $matches[2]; + case 'desc': + case 'lfile': case 'magic': case 'replaces': - $fo->{$k} = $matches[2]; + case 'size': + $this->fo->{$k} = $matches[2]; break; case 'fullname': - $fo->lfile = $matches[2]; + $this->fo->lfile = $matches[2]; break; case 'date': - $fo->datetime = Carbon::create($matches[2]); + $this->fo->datetime = Carbon::create($matches[2]); break; case 'ldesc': - $fo->{$k} .= $matches[2]; + $this->fo->{$k} .= $matches[2]; break; case 'crc': - $fo->{$k} = hexdec($matches[2]); + $this->fo->{$k} = hexdec($matches[2]); break; case 'path': @@ -152,9 +221,9 @@ class Tic extends FTNBase $ao = Address::findFTN($x[1]); if (! $ao) { - $fo->rogue_path->push($matches[2]); + $this->fo->rogue_path->push($matches[2]); } else { - $fo->set_path->push(['address'=>$ao,'datetime'=>Carbon::createFromTimestamp($x[8]),'extra'=>$x[9]]); + $this->fo->set_path->push(['address'=>$ao,'datetime'=>Carbon::createFromTimestamp($x[8]),'extra'=>$x[9]]); } break; @@ -163,36 +232,36 @@ class Tic extends FTNBase $ao = Address::findFTN($matches[2]); if (! $ao) { - $fo->rogue_seenby->push($matches[2]); + $this->fo->rogue_seenby->push($matches[2]); } else { - $fo->set_seenby->push($ao->id); + $this->fo->set_seenby->push($ao->id); } break; } } else { - $fo->kludges->push($line); + $this->fo->kludges->push($line); } } fclose($f); - $f = fopen($x=Storage::disk('local')->path($fo->fullname),'rb'); + $f = fopen($x=Storage::disk('local')->path($this->fo->fullname),'rb'); $stat = fstat($f); fclose($f); // Validate Size - if ($fo->size !== ($y=$stat['size'])) - throw new \Exception(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$fo->size,$fo->fullname,$y)); + if ($this->fo->size !== ($y=$stat['size'])) + throw new \Exception(sprintf('TIC file size [%d] doesnt match file [%s] (%d)',$this->fo->size,$this->fo->fullname,$y)); // Validate CRC - if (sprintf('%08x',$fo->crc) !== ($y=hash_file('crc32b',$x))) - throw new \Exception(sprintf('TIC file CRC [%08x] doesnt match file [%s] (%s)',$fo->crc,$fo->fullname,$y)); + if (sprintf('%08x',$this->fo->crc) !== ($y=hash_file('crc32b',$x))) + throw new \Exception(sprintf('TIC file CRC [%08x] doesnt match file [%s] (%s)',$this->fo->crc,$this->fo->fullname,$y)); // Validate Password - if ($this->pw !== ($y=$this->from->session('ticpass'))) - throw new \Exception(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$this->pw,$this->from->ftn,$y)); + if ($pw !== ($y=$this->from->session('ticpass'))) + throw new \Exception(sprintf('TIC file PASSWORD [%s] doesnt match system [%s] (%s)',$pw,$this->from->ftn,$y)); // Validate Sender is linked (and permitted to send) if ($this->from->fileareas->search(function($item) { return $item->id === $this->area->id; }) === FALSE) @@ -200,7 +269,7 @@ class Tic extends FTNBase // If the filearea is to be autocreated, create it if (! $this->area->exists) { - $this->area->description = $this->areadesc; + $this->area->description = $areadesc; $this->area->active = TRUE; $this->area->public = FALSE; $this->area->notes = 'Autocreated'; @@ -208,21 +277,13 @@ class Tic extends FTNBase $this->area->save(); } - $fo->filearea_id = $this->area->id; - $fo->fftn_id = $this->origin->id; + $this->fo->filearea_id = $this->area->id; + $this->fo->fftn_id = $this->origin->id; // If the file create time is blank, we'll take the files - if (! $fo->datetime) - $fo->datetime = Carbon::createFromTimestamp($stat['ctime']); + if (! $this->fo->datetime) + $this->fo->datetime = Carbon::createFromTimestamp($stat['ctime']); - $fo->save(); - - $this->fo = $fo; - } - - public function isNodelist(): bool - { - return (($this->fo->nodelist_filearea_id === $this->fo->filearea->domain->filearea_id) - && (preg_match(str_replace(['.','?'],['\.','.'],'#^'.$this->fo->filearea->domain->nodelist_filename.'$#i'),$this->fo->file))); + $this->fo->save(); } } \ No newline at end of file diff --git a/app/Classes/File/Item.php b/app/Classes/File/Item.php index 641a288..a8eb7eb 100644 --- a/app/Classes/File/Item.php +++ b/app/Classes/File/Item.php @@ -4,8 +4,11 @@ namespace App\Classes\File; use Exception; use Illuminate\Contracts\Filesystem\FileNotFoundException; +use Illuminate\Support\Facades\Storage; use League\Flysystem\UnreadableFileEncountered; +use App\Models\File; + /** * A file we are sending or receiving * @@ -35,6 +38,7 @@ class Item protected int $file_mtime = 0; protected int $file_type = 0; protected int $action = 0; + protected File $filemodel; public bool $sent = FALSE; public bool $received = FALSE; @@ -51,19 +55,27 @@ class Item switch ($action) { case self::I_SEND: - if (! is_string($file)) - throw new Exception('Invalid object creation - file should be a string'); + if ($file instanceof File) { + $this->filemodel = $file; + // @todo We should catch any exceptions if the default storage is s3 (it is) and we cannot find the file, or the s3 call fails + $this->file_size = Storage::size($file->full_storage_path); + $this->file_mtime = Storage::lastModified($file->full_storage_path); - if (! file_exists($file)) - throw new FileNotFoundException('Item doesnt exist: '.$file); + } else { + if (! is_string($file)) + throw new Exception('Invalid object creation - file should be a string'); - if (! is_readable($file)) - throw new UnreadableFileEncountered('Item cannot be read: '.$file); + if (! file_exists($file)) + throw new FileNotFoundException('Item doesnt exist: '.$file); - $this->file_name = $file; - $x = stat($file); - $this->file_size = $x['size']; - $this->file_mtime = $x['mtime']; + if (! is_readable($file)) + throw new UnreadableFileEncountered('Item cannot be read: '.$file); + + $this->file_name = $file; + $x = stat($file); + $this->file_size = $x['size']; + $this->file_mtime = $x['mtime']; + } break; @@ -104,7 +116,7 @@ class Item return $this->file_name; case 'sendas': - return basename($this->file_name); + return $this->file_name ? basename($this->file_name) : $this->filemodel->file; default: throw new Exception('Unknown key: '.$key); diff --git a/app/Classes/File/Send.php b/app/Classes/File/Send.php index cc6214b..f2e22db 100644 --- a/app/Classes/File/Send.php +++ b/app/Classes/File/Send.php @@ -6,6 +6,7 @@ use Exception; use Illuminate\Contracts\Filesystem\FileNotFoundException; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Storage; use League\Flysystem\UnreadableFileEncountered; use App\Models\Address; @@ -52,12 +53,12 @@ final class Send extends Item case 'file_count': return $this->list - ->filter(function($item) { return $item->isType(self::IS_FILE); }) + ->filter(function($item) { return $item->isType(self::IS_FILE|self::IS_TIC); }) ->count(); case 'file_size': return $this->list - ->filter(function($item) { return $item->isType(self::IS_FILE); }) + ->filter(function($item) { return $item->isType(self::IS_FILE|self::IS_TIC); }) ->sum(function($item) { return $item->file_size; }); case 'filepos': @@ -162,7 +163,8 @@ final class Send extends Item Log::debug(sprintf('%s: - Closing [%s], sent in [%d]',self::LOGKEY,$this->sending->file_name,$end)); } - if (! $this->sending instanceof Mail) + // @todo This should be done better isType == file? + if ((! $this->sending instanceof Mail) && (! $this->sending->isType(self::IS_TIC))) fclose($this->f); $this->sending = NULL; @@ -177,7 +179,44 @@ final class Send extends Item */ public function feof(): bool { - return ($this->sending instanceof Mail) ? ($this->file_pos == $this->size) : feof($this->f); + return (($this->sending instanceof Mail) || ($this->sending->isType(self::IS_TIC))) + ? ($this->file_pos == $this->size) + : feof($this->f); + } + + /** + * Add our mail to the send queue + * + * @param Address $ao + * @return bool + * @throws Exception + * @todo We need to make this into a transaction, incase the transfer fails. + */ + public function files(Address $ao): bool + { + $file = FALSE; + + // If the node is marked as hold - dont send any files. + if ($ao->system->hold) { + Log::info(sprintf('%s: - System [%d] is marked as hold - not checking for files.',self::LOGKEY,$ao->system_id)); + + return FALSE; + } + + // Files + if (($x=$ao->getFiles())->count()) { + 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)); + } + + $file = TRUE; + } + + return $file; } /** @@ -212,14 +251,27 @@ final class Send extends Item $this->file_pos = 0; $this->start = time(); - $this->f = fopen($this->sending->file_name,'rb'); - if (! $this->f) { - Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->sending->file_name)); - return FALSE; + // If sending->file is a string, then we dont need to actually open anything + if ($this->sending->isType(self::IS_TIC)) { + $this->f = TRUE; + return TRUE; } - Log::info(sprintf('%s:= open - File [%s] opened with size [%d]',self::LOGKEY,$this->sending->file_name,$this->sending->file_size)); - return TRUE; + // If sending file is a File::class, then our file is s3 + if (! $this->sending->file_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'); + if (! $this->f) { + Log::error(sprintf('%s:! Unable to open file [%s] for reading',self::LOGKEY,$this->sending->file_name)); + return FALSE; + } + + Log::info(sprintf('%s:= open - File [%s] opened with size [%d]',self::LOGKEY,$this->sending->file_name,$this->sending->file_size)); + return TRUE; + } } /** @@ -236,7 +288,7 @@ final class Send extends Item // If the node is marked as hold - dont send any mail. if ($ao->system->hold) { - Log::info(sprintf('%s: - System [%d] mail is marked as hold - not checking for mail.',self::LOGKEY,$ao->system_id)); + Log::info(sprintf('%s: - System [%d] is marked as hold - not checking for mail.',self::LOGKEY,$ao->system_id)); return FALSE; } @@ -276,6 +328,11 @@ final class Send extends Item // We are sending mail if ($this->sending instanceof Mail) { $data = $this->sending->read($this->file_pos,$length); + + // We are sending a tic file + } else if ($this->sending->isType(self::IS_TIC)) { + $data = $this->sending->read($this->file_pos,$length); + } else { $data = fread($this->f,$length); } @@ -303,7 +360,7 @@ final class Send extends Item if (! $this->f) throw new Exception('No file open for seek'); - if ($this->sending instanceof Mail) { + if (($this->sending instanceof Mail) || $this->sending->isType(self::IS_TIC)) { $pos = ($pos < $this->size) ? $pos : $this->size; $rc = TRUE; diff --git a/app/Classes/File/Tic.php b/app/Classes/File/Tic.php new file mode 100644 index 0000000..b8a5137 --- /dev/null +++ b/app/Classes/File/Tic.php @@ -0,0 +1,38 @@ +action |= $action; + + $tic = new FTNTic; + + switch ($action) { + case self::I_SEND: + $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; + + break; + + default: + throw new \Exception('Unknown action: '.$action); + } + } + + public function read(int $start,int $length): string + { + return substr($this->file,$start,$length); + } +} \ No newline at end of file diff --git a/app/Classes/Protocol/Binkp.php b/app/Classes/Protocol/Binkp.php index ee191e9..ccbfed6 100644 --- a/app/Classes/Protocol/Binkp.php +++ b/app/Classes/Protocol/Binkp.php @@ -752,6 +752,7 @@ final class Binkp extends BaseProtocol foreach ($this->node->aka_remote_authed as $ao) { Log::debug(sprintf('%s: - M_eob Checking for any new mail to [%s]',self::LOGKEY,$ao->ftn)); $this->send->mail($ao); + $this->send->files($ao); } if ($this->send->total_count) @@ -920,7 +921,7 @@ final class Binkp extends BaseProtocol { // @todo Commit our mail transaction if the remote end confirmed receipt of the file. if ($this->sessionGet(self::SE_SENDFILE)) { - Log::debug(sprintf('%s:Packet [%s] sent. (%s)',self::LOGKEY,$this->send->sendas,$this->send->name)); + Log::debug(sprintf('%s:Packet/File [%s] sent. (%s)',self::LOGKEY,$this->send->sendas,$this->send->name)); $this->sessionClear(self::SE_SENDFILE); $this->send->close(TRUE); @@ -928,7 +929,7 @@ final class Binkp extends BaseProtocol } if ($this->sessionGet(self::SE_WAITGOT)) { - Log::debug(sprintf('%s:Packet [%s] sent. (%s)',self::LOGKEY,$this->send->sendas,$this->send->name)); + Log::debug(sprintf('%s:Packet/File [%s] sent. (%s)',self::LOGKEY,$this->send->sendas,$this->send->name)); $this->sessionClear(self::SE_WAITGOT); $this->send->close(TRUE); @@ -1107,6 +1108,7 @@ final class Binkp extends BaseProtocol if ($this->node->aka_authed) foreach ($this->node->aka_remote_authed as $ao) { $this->send->mail($ao); + $this->send->files($ao); } $this->msgs(self::BPM_NUL,sprintf('TRF %lu %lu',$this->send->mail_size,$this->send->file_size)); @@ -1191,6 +1193,7 @@ final class Binkp extends BaseProtocol if ($this->node->aka_authed) foreach ($this->node->aka_remote_authed as $ao) { $this->send->mail($ao); + $this->send->files($ao); } $this->msgs(self::BPM_NUL,sprintf('TRF %lu %lu',$this->send->mail_size,$this->send->file_size)); diff --git a/app/Classes/Protocol/EMSI.php b/app/Classes/Protocol/EMSI.php index 416bb22..67e645a 100644 --- a/app/Classes/Protocol/EMSI.php +++ b/app/Classes/Protocol/EMSI.php @@ -1186,12 +1186,21 @@ final class EMSI extends BaseProtocol implements CRCInterface,ZmodemInterface // Add our mail to the queue if we have authenticated if ($this->node->aka_authed) foreach ($this->node->aka_remote_authed as $ao) { + // Send mail while ($this->send->mail($ao)) { $z = new Zmodem; if (! $z->zmodem_sendinit($this->client,$zap) && $this->send->total_count) $z->zmodem_sendfile($this->send); } + + // Send files + while ($this->send->files($ao)) { + $z = new Zmodem; + + if (! $z->zmodem_sendinit($this->client,$zap) && $this->send->total_count) + $z->zmodem_sendfile($this->send); + } } Log::debug(sprintf('%s:- Finished sending',self::LOGKEY)); diff --git a/app/Classes/Sock/SocketClient.php b/app/Classes/Sock/SocketClient.php index 24219b8..d9b0f39 100644 --- a/app/Classes/Sock/SocketClient.php +++ b/app/Classes/Sock/SocketClient.php @@ -233,8 +233,17 @@ final class SocketClient { */ public function close(): void { - socket_shutdown($this->connection); - socket_close($this->connection); + try { + socket_shutdown($this->connection); + } catch (\ErrorException $e) { + Log::error(sprintf('%s:+ Shutting down socket [%s]',self::LOGKEY,$e->getMessage())); + } + + try { + socket_close($this->connection); + } catch (\ErrorException $e) { + Log::error(sprintf('%s:+ Closing socket [%s]',self::LOGKEY,$e->getMessage())); + } } /** diff --git a/app/Classes/Sock/SocketException.php b/app/Classes/Sock/SocketException.php index a9b7d68..20df8a9 100644 --- a/app/Classes/Sock/SocketException.php +++ b/app/Classes/Sock/SocketException.php @@ -2,6 +2,7 @@ namespace App\Classes\Sock; +// @todo Can we change this to use socket_strerr() && socket_last_error() final class SocketException extends \Exception { public const CANT_CREATE_SOCKET = 1; public const CANT_BIND_SOCKET = 2; @@ -11,6 +12,7 @@ final class SocketException extends \Exception { public const SOCKET_ERROR = 6; public const SOCKET_EAGAIN = 11; public const SOCKET_READ = 22; + public const CONNECTION_RESET = 104; private array $messages = [ self::CANT_CREATE_SOCKET => 'Can\'t create socket: "%s"', @@ -21,6 +23,7 @@ final class SocketException extends \Exception { self::SOCKET_ERROR => 'Socket Error: "%s"', self::SOCKET_EAGAIN => 'Socket Resource Temporarily Unavailable - Try again', self::SOCKET_READ => 'Unable to read from socket', + self::CONNECTION_RESET => 'Connection reset by peer', ]; public function __construct(int $code,string $params=NULL) { diff --git a/app/Jobs/TicProcess.php b/app/Jobs/TicProcess.php index 270b381..1ba3cfd 100644 --- a/app/Jobs/TicProcess.php +++ b/app/Jobs/TicProcess.php @@ -41,7 +41,8 @@ class TicProcess implements ShouldQueue */ public function handle() { - $to = new Tic($this->file); + $to = new Tic; + $to->load($this->file); Log::info(sprintf('%s:Processed [%s] storing [%s] as id [%d]',self::LOGKEY,$this->file,$to->fo->file,$to->fo->id)); diff --git a/app/Models/Address.php b/app/Models/Address.php index 7c3b0a4..8f80594 100644 --- a/app/Models/Address.php +++ b/app/Models/Address.php @@ -572,6 +572,32 @@ class Address extends Model return $pkt; } + /** + * Get files for this node (including it's children) + * + * @param bool $update + * @return Collection + */ + public function getFiles(bool $update=TRUE): Collection + { + if (($files=$this->filesWaiting()) + ->count()) + { + Log::debug(sprintf('%s:= Got [%d] files for [%s] for sending',self::LOGKEY,$files->count(),$this->ftn)); + + // @todo This should be transactional, incase the transfer fails + if ($files->count() && $update) + DB::table('file_seenby') + ->whereIn('file_id',$files->pluck('id')) + ->where('address_id',$this->id) + ->whereNull('sent_at') + ->whereNotNull('export_at') + ->update(['sent_at'=>Carbon::now()]); + } + + return $files; + } + /** * Get netmail for this node (including it's children) * diff --git a/app/Models/File.php b/app/Models/File.php index f38b125..9c98a31 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -154,7 +154,7 @@ class File extends Model public function path() { return $this->belongsToMany(Address::class,'file_path') - ->withPivot(['id','parent_id','extra']); + ->withPivot(['id','parent_id','datetime','extra']); } /* ATTRIBUTES */