<?php namespace App\Classes\File; use Exception; use Illuminate\Support\Facades\Log; use League\Flysystem\UnreadableFileEncountered; use App\Classes\File\Send\Dynamic; use App\Classes\Node; use App\Models\Address; /** * Object representing the files we are sending * * @property-read resource fd * @property-read int files_size The size of the files waiting to be sent * @property-read int mail_size The size of the mail waiting to be sent * @property-read int togo_count The total number of items that havent been sent yet * @property-read int total_size The size of the items waiting to be sent * @property-read int total_sent * @property-read int total_sent_bytes */ class Send extends Base { private const LOGKEY = 'IS-'; public const T_NONE = 0; /** @var int This file contains a file from the DB */ public const T_FILE = (1<<0); /** @var int This file contains a bundle of Netmail */ public const T_NETMAIL = (1<<1); /** @var int This file contains a bundle of Echomail */ public const T_ECHOMAIL = (1<<2); private string $comp_data; protected int $size = 0; public function __construct() { // Initialise our variables if (get_class($this) === self::class) { $this->list = collect(); $this->f = NULL; } } public function __get($key) { switch ($key) { case 'completed': return $this->list ->filter(function($item) { return $item->complete === TRUE; }); case 'fd': return ! is_null($this->index); case 'files_count': return $this->list ->filter(function($item) { return $item->isType(self::IS_FILE|self::IS_TIC); }) ->count(); case 'files_size': return $this->list ->filter(function($item) { return $item->isType(self::IS_FILE|self::IS_TIC); }) ->sum(function($item) { return $item->size; }); case 'mail_count': return $this->list ->filter(function($item) { return $item->isType(self::IS_ARC|self::IS_PKT); }) ->count(); case 'mail_size': return $this->list ->filter(function($item) { return $item->isType(self::IS_ARC|self::IS_PKT); }) ->sum(function($item) { return $item->size; }); case 'name': case 'nameas': case 'mtime': case 'size': case 'type': return $this->sending->{$key}; case 'pos': return $this->{$key}; case 'sending': return $this->list->get($this->index); case 'togo_count': return $this->list ->filter(function($item) { return $item->complete === FALSE; }) ->count(); case 'total_sent': return $this->completed ->count(); case 'total_sent_bytes': return $this->completed ->sum(function($item) { return $item->size; }); case 'total_size': return $this->list ->sum(function($item) { return $item->size; }); default: throw new Exception('Unknown key: '.$key); } } /** * Close the file descriptor of the file we are sending * * @param bool $successful * @param Node $node * @throws Exception */ public function close(bool $successful,Node $node): void { if (! $this->fd) throw new Exception('No file to close'); if ($successful) { $end = time()-$this->start; Log::info(sprintf('%s:- Closing [%s], sent in [%d] with [%s] items',self::LOGKEY,$this->sending->nameas,$end,$this->sending->dbids->count())); } $this->sending->close($successful,$node); $this->index = NULL; } public function dynamic(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->dynamicWaiting())->count()) { Log::debug(sprintf('%s:- [%d] Dynamic Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn)); // Add Files foreach ($x as $do) $this->list->push(new Dynamic($do,$ao,self::T_FILE)); $file = TRUE; } return $file; } /* 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; } } */ /** * Check if we are at the end of the file * * @return bool */ public function feof(): bool { return $this->sending->feof(); } /** * Add our mail to the send queue * * @param Address $ao * @return bool * @throws Exception */ 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->filesWaiting())->count()) { Log::info(sprintf('%s:- [%d] Files(s) added for sending to [%s]',self::LOGKEY,$x->count(),$ao->ftn)); // Add Files foreach ($x as $fo) { $this->list->push(new File($fo,self::T_FILE)); $this->list->push(new Tic($fo,$ao,self::T_NONE)); } $file = TRUE; } return $file; } /** * Open a file for sending * * @param string $compress * @return bool * @throws Exception */ public function open(string $compress=''): bool { Log::debug(sprintf('%s:+ File Open to send',self::LOGKEY)); if ((($this->index=$this->list->search(function($item) { return $item->complete === FALSE; })) !== FALSE) && $this->sending->open()) { Log::info(sprintf('%s:- Content Send item [#%d] (%s) with size [%d]',self::LOGKEY,$this->index,$this->sending->nameas,$this->sending->size)); $this->pos = 0; $this->start = time(); /* if ($compress) $this->comp_data = $this->compdata($compress); */ return TRUE; } else { throw new Exception('No files to open'); } } /** * Add our mail to the send queue * * @param Address $ao * @return bool * @throws Exception */ public function mail(Address $ao): bool { $mail = FALSE; // If the node is marked as hold - dont send any mail. if ($ao->system->hold) { Log::info(sprintf('%s:- System [%d] is marked as hold - not checking for mail.',self::LOGKEY,$ao->system_id)); return FALSE; } // Netmail if ($x=$ao->getNetmail()) { Log::debug(sprintf('%s:- Netmail(s) added for sending to [%s]',self::LOGKEY,$ao->ftn)); $this->list->push(new Mail($x,self::T_NETMAIL)); $mail = TRUE; } // Echomail if ($x=$ao->getEchomail()) { Log::debug(sprintf('%s:- Echomail(s) added for sending to [%s]',self::LOGKEY,$ao->ftn)); $this->list->push(new Mail($x,self::T_ECHOMAIL)); $mail = TRUE; } return $mail; } /** * Read bytes of the sending file * * @param int $length * @return string|null * @throws UnreadableFileEncountered * @throws Exception */ public function read(int $length): ?string { if (! $this->fd) throw new Exception('No file open for read'); $data = $this->sending->read($length); if ($data === FALSE) throw new UnreadableFileEncountered('Error reading file: '.$this->sending->name); $this->pos += strlen($data); Log::debug(sprintf('%s:- Content Read [%d] bytes, pos now [%d]',self::LOGKEY,strlen($data),$this->pos)); return $data; } /** * Seek to a specific position of our file * * @param int $pos * @return bool * @throws Exception */ public function seek(int $pos): bool { if (! $this->fd) throw new Exception('No file open for seek'); if ($this->sending->seek($pos)) { $this->pos = $pos; Log::debug(sprintf('%s:= Content Seek to [%d]',self::LOGKEY,$this->pos)); return TRUE; } else { Log::error(sprintf('%s:! Failed to seek to [%d]',self::LOGKEY,$pos)); return FALSE; } } }