diff options
Diffstat (limited to 'vendor/maennchen/zipstream-php/src/File.php')
-rw-r--r-- | vendor/maennchen/zipstream-php/src/File.php | 420 |
1 files changed, 0 insertions, 420 deletions
diff --git a/vendor/maennchen/zipstream-php/src/File.php b/vendor/maennchen/zipstream-php/src/File.php deleted file mode 100644 index 0462196e2..000000000 --- a/vendor/maennchen/zipstream-php/src/File.php +++ /dev/null @@ -1,420 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace ZipStream; - -use Closure; -use DateTimeInterface; -use DeflateContext; -use RuntimeException; -use ZipStream\Exception\FileSizeIncorrectException; -use ZipStream\Exception\OverflowException; -use ZipStream\Exception\ResourceActionException; -use ZipStream\Exception\SimulationFileUnknownException; -use ZipStream\Exception\StreamNotReadableException; -use ZipStream\Exception\StreamNotSeekableException; - -/** - * @internal - */ -class File -{ - private const CHUNKED_READ_BLOCK_SIZE = 0x1000000; - - private Version $version; - - private int $compressedSize = 0; - - private int $uncompressedSize = 0; - - private int $crc = 0; - - private int $generalPurposeBitFlag = 0; - - private readonly string $fileName; - - /** - * @var resource|null - */ - private $stream; - - /** - * @param Closure $dataCallback - * @psalm-param Closure(): resource $dataCallback - */ - public function __construct( - string $fileName, - private readonly Closure $dataCallback, - private readonly OperationMode $operationMode, - private readonly int $startOffset, - private readonly CompressionMethod $compressionMethod, - private readonly string $comment, - private readonly DateTimeInterface $lastModificationDateTime, - private readonly int $deflateLevel, - private readonly ?int $maxSize, - private readonly ?int $exactSize, - private readonly bool $enableZip64, - private readonly bool $enableZeroHeader, - private readonly Closure $send, - private readonly Closure $recordSentBytes, - ) { - $this->fileName = self::filterFilename($fileName); - $this->checkEncoding(); - - if ($this->enableZeroHeader) { - $this->generalPurposeBitFlag |= GeneralPurposeBitFlag::ZERO_HEADER; - } - - $this->version = $this->compressionMethod === CompressionMethod::DEFLATE ? Version::DEFLATE : Version::STORE; - } - - public function cloneSimulationExecution(): self - { - return new self( - $this->fileName, - $this->dataCallback, - OperationMode::NORMAL, - $this->startOffset, - $this->compressionMethod, - $this->comment, - $this->lastModificationDateTime, - $this->deflateLevel, - $this->maxSize, - $this->exactSize, - $this->enableZip64, - $this->enableZeroHeader, - $this->send, - $this->recordSentBytes, - ); - } - - public function process(): string - { - $forecastSize = $this->forecastSize(); - - if ($this->enableZeroHeader) { - // No calculation required - } elseif ($this->isSimulation() && $forecastSize !== null) { - $this->uncompressedSize = $forecastSize; - $this->compressedSize = $forecastSize; - } else { - $this->readStream(send: false); - if (rewind($this->unpackStream()) === false) { - throw new ResourceActionException('rewind', $this->unpackStream()); - } - } - - $this->addFileHeader(); - - $detectedSize = $forecastSize ?? ($this->compressedSize > 0 ? $this->compressedSize : null); - - if ( - $this->isSimulation() && - $detectedSize !== null - ) { - ($this->recordSentBytes)($detectedSize); - } else { - $this->readStream(send: true); - } - - $this->addFileFooter(); - return $this->getCdrFile(); - } - - /** - * @return resource - */ - private function unpackStream() - { - if ($this->stream) { - return $this->stream; - } - - if ($this->operationMode === OperationMode::SIMULATE_STRICT) { - throw new SimulationFileUnknownException(); - } - - $this->stream = ($this->dataCallback)(); - - if (!$this->enableZeroHeader && !stream_get_meta_data($this->stream)['seekable']) { - throw new StreamNotSeekableException(); - } - if (!( - str_contains(stream_get_meta_data($this->stream)['mode'], 'r') - || str_contains(stream_get_meta_data($this->stream)['mode'], 'w+') - || str_contains(stream_get_meta_data($this->stream)['mode'], 'a+') - || str_contains(stream_get_meta_data($this->stream)['mode'], 'x+') - || str_contains(stream_get_meta_data($this->stream)['mode'], 'c+') - )) { - throw new StreamNotReadableException(); - } - - return $this->stream; - } - - private function forecastSize(): ?int - { - if ($this->compressionMethod !== CompressionMethod::STORE) { - return null; - } - if ($this->exactSize !== null) { - return $this->exactSize; - } - $fstat = fstat($this->unpackStream()); - if (!$fstat || !array_key_exists('size', $fstat) || $fstat['size'] < 1) { - return null; - } - - if ($this->maxSize !== null && $this->maxSize < $fstat['size']) { - return $this->maxSize; - } - - return $fstat['size']; - } - - /** - * Create and send zip header for this file. - */ - private function addFileHeader(): void - { - $forceEnableZip64 = $this->enableZeroHeader && $this->enableZip64; - - $footer = $this->buildZip64ExtraBlock($forceEnableZip64); - - $zip64Enabled = $footer !== ''; - - if ($zip64Enabled) { - $this->version = Version::ZIP64; - } - - if ($this->generalPurposeBitFlag & GeneralPurposeBitFlag::EFS) { - // Put the tricky entry to - // force Linux unzip to lookup EFS flag. - $footer .= Zs\ExtendedInformationExtraField::generate(); - } - - $data = LocalFileHeader::generate( - versionNeededToExtract: $this->version->value, - generalPurposeBitFlag: $this->generalPurposeBitFlag, - compressionMethod: $this->compressionMethod, - lastModificationDateTime: $this->lastModificationDateTime, - crc32UncompressedData: $this->crc, - compressedSize: $zip64Enabled - ? 0xFFFFFFFF - : $this->compressedSize, - uncompressedSize: $zip64Enabled - ? 0xFFFFFFFF - : $this->uncompressedSize, - fileName: $this->fileName, - extraField: $footer, - ); - - - ($this->send)($data); - } - - /** - * Strip characters that are not legal in Windows filenames - * to prevent compatibility issues - */ - private static function filterFilename( - /** - * Unprocessed filename - */ - string $fileName - ): string { - // strip leading slashes from file name - // (fixes bug in windows archive viewer) - $fileName = ltrim($fileName, '/'); - - return str_replace(['\\', ':', '*', '?', '"', '<', '>', '|'], '_', $fileName); - } - - private function checkEncoding(): void - { - // Sets Bit 11: Language encoding flag (EFS). If this bit is set, - // the filename and comment fields for this file - // MUST be encoded using UTF-8. (see APPENDIX D) - if (mb_check_encoding($this->fileName, 'UTF-8') && - mb_check_encoding($this->comment, 'UTF-8')) { - $this->generalPurposeBitFlag |= GeneralPurposeBitFlag::EFS; - } - } - - private function buildZip64ExtraBlock(bool $force = false): string - { - $outputZip64ExtraBlock = false; - - $originalSize = null; - if ($force || $this->uncompressedSize > 0xFFFFFFFF) { - $outputZip64ExtraBlock = true; - $originalSize = $this->uncompressedSize; - } - - $compressedSize = null; - if ($force || $this->compressedSize > 0xFFFFFFFF) { - $outputZip64ExtraBlock = true; - $compressedSize = $this->compressedSize; - } - - // If this file will start over 4GB limit in ZIP file, - // CDR record will have to use Zip64 extension to describe offset - // to keep consistency we use the same value here - $relativeHeaderOffset = null; - if ($this->startOffset > 0xFFFFFFFF) { - $outputZip64ExtraBlock = true; - $relativeHeaderOffset = $this->startOffset; - } - - if (!$outputZip64ExtraBlock) { - return ''; - } - - if (!$this->enableZip64) { - throw new OverflowException(); - } - - return Zip64\ExtendedInformationExtraField::generate( - originalSize: $originalSize, - compressedSize: $compressedSize, - relativeHeaderOffset: $relativeHeaderOffset, - diskStartNumber: null, - ); - } - - private function addFileFooter(): void - { - if (($this->compressedSize > 0xFFFFFFFF || $this->uncompressedSize > 0xFFFFFFFF) && $this->version !== Version::ZIP64) { - throw new OverflowException(); - } - - if (!$this->enableZeroHeader) { - return; - } - - if ($this->version === Version::ZIP64) { - $footer = Zip64\DataDescriptor::generate( - crc32UncompressedData: $this->crc, - compressedSize: $this->compressedSize, - uncompressedSize: $this->uncompressedSize, - ); - } else { - $footer = DataDescriptor::generate( - crc32UncompressedData: $this->crc, - compressedSize: $this->compressedSize, - uncompressedSize: $this->uncompressedSize, - ); - } - - ($this->send)($footer); - } - - private function readStream(bool $send): void - { - $this->compressedSize = 0; - $this->uncompressedSize = 0; - $hash = hash_init('crc32b'); - - $deflate = $this->compressionInit(); - - while ( - !feof($this->unpackStream()) && - ($this->maxSize === null || $this->uncompressedSize < $this->maxSize) && - ($this->exactSize === null || $this->uncompressedSize < $this->exactSize) - ) { - $readLength = min( - ($this->maxSize ?? PHP_INT_MAX) - $this->uncompressedSize, - ($this->exactSize ?? PHP_INT_MAX) - $this->uncompressedSize, - self::CHUNKED_READ_BLOCK_SIZE - ); - - $data = fread($this->unpackStream(), $readLength); - - hash_update($hash, $data); - - $this->uncompressedSize += strlen($data); - - if ($deflate) { - $data = deflate_add( - $deflate, - $data, - feof($this->unpackStream()) ? ZLIB_FINISH : ZLIB_NO_FLUSH - ); - } - - $this->compressedSize += strlen($data); - - if ($send) { - ($this->send)($data); - } - } - - if ($this->exactSize !== null && $this->uncompressedSize !== $this->exactSize) { - throw new FileSizeIncorrectException(expectedSize: $this->exactSize, actualSize: $this->uncompressedSize); - } - - $this->crc = hexdec(hash_final($hash)); - } - - private function compressionInit(): ?DeflateContext - { - switch ($this->compressionMethod) { - case CompressionMethod::STORE: - // Noting to do - return null; - case CompressionMethod::DEFLATE: - $deflateContext = deflate_init( - ZLIB_ENCODING_RAW, - ['level' => $this->deflateLevel] - ); - - if (!$deflateContext) { - // @codeCoverageIgnoreStart - throw new RuntimeException("Can't initialize deflate context."); - // @codeCoverageIgnoreEnd - } - - // False positive, resource is no longer returned from this function - return $deflateContext; - default: - // @codeCoverageIgnoreStart - throw new RuntimeException('Unsupported Compression Method ' . print_r($this->compressionMethod, true)); - // @codeCoverageIgnoreEnd - } - } - - private function getCdrFile(): string - { - $footer = $this->buildZip64ExtraBlock(); - - return CentralDirectoryFileHeader::generate( - versionMadeBy: ZipStream::ZIP_VERSION_MADE_BY, - versionNeededToExtract: $this->version->value, - generalPurposeBitFlag: $this->generalPurposeBitFlag, - compressionMethod: $this->compressionMethod, - lastModificationDateTime: $this->lastModificationDateTime, - crc32: $this->crc, - compressedSize: $this->compressedSize > 0xFFFFFFFF - ? 0xFFFFFFFF - : $this->compressedSize, - uncompressedSize: $this->uncompressedSize > 0xFFFFFFFF - ? 0xFFFFFFFF - : $this->uncompressedSize, - fileName: $this->fileName, - extraField: $footer, - fileComment: $this->comment, - diskNumberStart: 0, - internalFileAttributes: 0, - externalFileAttributes: 32, - relativeOffsetOfLocalHeader: $this->startOffset > 0xFFFFFFFF - ? 0xFFFFFFFF - : $this->startOffset, - ); - } - - private function isSimulation(): bool - { - return $this->operationMode === OperationMode::SIMULATE_LAX || $this->operationMode === OperationMode::SIMULATE_STRICT; - } -} |