<?php namespace App\Classes\File; 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 * * @property string $name * @property string $recvas * @property int $size */ class Item { private const LOGKEY = 'I--'; // 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); protected const IS_FLO = (1<<4); protected const IS_REQ = (1<<5); protected const IS_TIC = (1<<6); 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; /** 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 */ public function __construct($file,int $action) { switch ($action) { case self::I_SEND: 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); } else { if (! is_string($file)) throw new \Exception('Invalid object creation - file should be a string'); if (! file_exists($file)) throw new FileNotFoundException('Item doesnt exist: '.$file); if (! is_readable($file)) throw new UnreadableFileEncountered('Item cannot be read: '.$file); $this->file_name = $file; $x = stat($file); $this->file_size = $x['size']; $this->file_mtime = $x['mtime']; } break; case self::I_RECV; $keys = ['name','mtime','size']; if (! is_array($file) || array_diff(array_keys($file),$keys)) throw new \Exception('Invalid object creation - file is not a valid array :'.serialize(array_diff(array_keys($file),$keys))); $this->file_name = $file['name']; $this->file_size = $file['size']; $this->file_mtime = $file['mtime']; break; default: throw new \Exception('Unknown action: '.$action); } $this->action = $action; $this->type = $this->whatType(); } /** * @throws \Exception */ public function __get($key) { switch($key) { case 'mtime': case 'name': case 'size': if ($this->action & self::I_RECV|self::I_SEND) return $this->{'file_'.$key}; throw new \Exception('Invalid request for key: '.$key); case 'recvas': return $this->file_name; case 'sendas': return $this->file_name ? basename($this->file_name) : $this->filemodel->name; default: throw new \Exception('Unknown key: '.$key); } } protected function isType(int $type): bool { return $this->type & $type; } private function whatType(): int { static $ext = ['su','mo','tu','we','th','fr','sa','req']; $x = strrchr($this->file_name,'.'); if (! $x || (strlen(substr($x,1)) != 3)) return self::IS_FILE; if (strcasecmp(substr($x,2),'lo') === 0) return self::IS_FLO; if (strcasecmp(substr($x,1),'pkt') === 0) return self::IS_PKT; if (strcasecmp(substr($x,1),'req') === 0) return self::IS_REQ; if (strcasecmp(substr($x,1),'tic') === 0) return self::IS_TIC; for ($i=0;$i<count($ext);$i++) if (! strncasecmp($x,'.'.$ext[$i],strlen($ext[$i])) && (preg_match('/^[0-9a-z]/',strtolower(substr($x,3,1))))) return self::IS_ARC; return self::IS_FILE; } }