Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/CarnetApp/CarnetNextcloud.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/nelexa/zip/src/PhpZip/Stream/ZipInputStream.php')
-rw-r--r--vendor/nelexa/zip/src/PhpZip/Stream/ZipInputStream.php603
1 files changed, 603 insertions, 0 deletions
diff --git a/vendor/nelexa/zip/src/PhpZip/Stream/ZipInputStream.php b/vendor/nelexa/zip/src/PhpZip/Stream/ZipInputStream.php
new file mode 100644
index 0000000..8c8adf5
--- /dev/null
+++ b/vendor/nelexa/zip/src/PhpZip/Stream/ZipInputStream.php
@@ -0,0 +1,603 @@
+<?php
+
+namespace PhpZip\Stream;
+
+use PhpZip\Crypto\TraditionalPkwareEncryptionEngine;
+use PhpZip\Crypto\WinZipAesEngine;
+use PhpZip\Exception\Crc32Exception;
+use PhpZip\Exception\InvalidArgumentException;
+use PhpZip\Exception\RuntimeException;
+use PhpZip\Exception\ZipCryptoException;
+use PhpZip\Exception\ZipException;
+use PhpZip\Exception\ZipUnsupportMethod;
+use PhpZip\Extra\ExtraFieldsCollection;
+use PhpZip\Extra\ExtraFieldsFactory;
+use PhpZip\Extra\Fields\ApkAlignmentExtraField;
+use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
+use PhpZip\Mapper\OffsetPositionMapper;
+use PhpZip\Mapper\PositionMapper;
+use PhpZip\Model\EndOfCentralDirectory;
+use PhpZip\Model\Entry\ZipSourceEntry;
+use PhpZip\Model\ZipEntry;
+use PhpZip\Model\ZipModel;
+use PhpZip\Util\PackUtil;
+use PhpZip\Util\StringUtil;
+use PhpZip\ZipFileInterface;
+
+/**
+ * Read zip file
+ *
+ * @author Ne-Lexa alexey@nelexa.ru
+ * @license MIT
+ */
+class ZipInputStream implements ZipInputStreamInterface
+{
+ /**
+ * @var resource
+ */
+ protected $in;
+ /**
+ * @var PositionMapper
+ */
+ protected $mapper;
+ /**
+ * @var int The number of bytes in the preamble of this ZIP file.
+ */
+ protected $preamble = 0;
+ /**
+ * @var int The number of bytes in the postamble of this ZIP file.
+ */
+ protected $postamble = 0;
+ /**
+ * @var ZipModel
+ */
+ protected $zipModel;
+
+ /**
+ * ZipInputStream constructor.
+ * @param resource $in
+ * @throws RuntimeException
+ */
+ public function __construct($in)
+ {
+ if (!is_resource($in)) {
+ throw new RuntimeException('$in must be resource');
+ }
+ $this->in = $in;
+ $this->mapper = new PositionMapper();
+ }
+
+ /**
+ * @return ZipModel
+ */
+ public function readZip()
+ {
+ $this->checkZipFileSignature();
+ $endOfCentralDirectory = $this->readEndOfCentralDirectory();
+ $entries = $this->mountCentralDirectory($endOfCentralDirectory);
+ $this->zipModel = ZipModel::newSourceModel($entries, $endOfCentralDirectory);
+ return $this->zipModel;
+ }
+
+ /**
+ * Check zip file signature
+ *
+ * @throws ZipException if this not .ZIP file.
+ */
+ protected function checkZipFileSignature()
+ {
+ rewind($this->in);
+ // Constraint: A ZIP file must start with a Local File Header
+ // or a (ZIP64) End Of Central Directory Record if it's empty.
+ $signatureBytes = fread($this->in, 4);
+ if (strlen($signatureBytes) < 4) {
+ throw new ZipException("Invalid zip file.");
+ }
+ $signature = unpack('V', $signatureBytes)[1];
+ if (
+ ZipEntry::LOCAL_FILE_HEADER_SIG !== $signature
+ && EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $signature
+ && EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $signature
+ ) {
+ throw new ZipException("Expected Local File Header or (ZIP64) End Of Central Directory Record! Signature: " . $signature);
+ }
+ }
+
+ /**
+ * @return EndOfCentralDirectory
+ * @throws ZipException
+ */
+ protected function readEndOfCentralDirectory()
+ {
+ $comment = null;
+ // Search for End of central directory record.
+ $stats = fstat($this->in);
+ $size = $stats['size'];
+ $max = $size - EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN;
+ $min = $max >= 0xffff ? $max - 0xffff : 0;
+ for ($endOfCentralDirRecordPos = $max; $endOfCentralDirRecordPos >= $min; $endOfCentralDirRecordPos--) {
+ fseek($this->in, $endOfCentralDirRecordPos, SEEK_SET);
+ // end of central dir signature 4 bytes (0x06054b50)
+ if (EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== unpack('V', fread($this->in, 4))[1]) {
+ continue;
+ }
+
+ // number of this disk - 2 bytes
+ // number of the disk with the start of the
+ // central directory - 2 bytes
+ // total number of entries in the central
+ // directory on this disk - 2 bytes
+ // total number of entries in the central
+ // directory - 2 bytes
+ // size of the central directory - 4 bytes
+ // offset of start of central directory with
+ // respect to the starting disk number - 4 bytes
+ // ZIP file comment length - 2 bytes
+ $data = unpack(
+ 'vdiskNo/vcdDiskNo/vcdEntriesDisk/vcdEntries/VcdSize/VcdPos/vcommentLength',
+ fread($this->in, 18)
+ );
+
+ if (0 !== $data['diskNo'] || 0 !== $data['cdDiskNo'] || $data['cdEntriesDisk'] !== $data['cdEntries']) {
+ throw new ZipException(
+ "ZIP file spanning/splitting is not supported!"
+ );
+ }
+ // .ZIP file comment (variable size)
+ if (0 < $data['commentLength']) {
+ $comment = fread($this->in, $data['commentLength']);
+ }
+ $this->preamble = $endOfCentralDirRecordPos;
+ $this->postamble = $size - ftell($this->in);
+
+ // Check for ZIP64 End Of Central Directory Locator.
+ $endOfCentralDirLocatorPos = $endOfCentralDirRecordPos - EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_LEN;
+
+ fseek($this->in, $endOfCentralDirLocatorPos, SEEK_SET);
+ // zip64 end of central dir locator
+ // signature 4 bytes (0x07064b50)
+ if (
+ 0 > $endOfCentralDirLocatorPos ||
+ ftell($this->in) === $size ||
+ EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG !== unpack('V', fread($this->in, 4))[1]
+ ) {
+ // Seek and check first CFH, probably requiring an offset mapper.
+ $offset = $endOfCentralDirRecordPos - $data['cdSize'];
+ fseek($this->in, $offset, SEEK_SET);
+ $offset -= $data['cdPos'];
+ if (0 !== $offset) {
+ $this->mapper = new OffsetPositionMapper($offset);
+ }
+ $entryCount = $data['cdEntries'];
+ return new EndOfCentralDirectory($entryCount, $comment);
+ }
+
+ // number of the disk with the
+ // start of the zip64 end of
+ // central directory 4 bytes
+ $zip64EndOfCentralDirectoryRecordDisk = unpack('V', fread($this->in, 4))[1];
+ // relative offset of the zip64
+ // end of central directory record 8 bytes
+ $zip64EndOfCentralDirectoryRecordPos = PackUtil::unpackLongLE(fread($this->in, 8));
+ // total number of disks 4 bytes
+ $totalDisks = unpack('V', fread($this->in, 4))[1];
+ if (0 !== $zip64EndOfCentralDirectoryRecordDisk || 1 !== $totalDisks) {
+ throw new ZipException("ZIP file spanning/splitting is not supported!");
+ }
+ fseek($this->in, $zip64EndOfCentralDirectoryRecordPos, SEEK_SET);
+ // zip64 end of central dir
+ // signature 4 bytes (0x06064b50)
+ $zip64EndOfCentralDirSig = unpack('V', fread($this->in, 4))[1];
+ if (EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG !== $zip64EndOfCentralDirSig) {
+ throw new ZipException("Expected ZIP64 End Of Central Directory Record!");
+ }
+ // size of zip64 end of central
+ // directory record 8 bytes
+ // version made by 2 bytes
+ // version needed to extract 2 bytes
+ fseek($this->in, 12, SEEK_CUR);
+ // number of this disk 4 bytes
+ $diskNo = unpack('V', fread($this->in, 4))[1];
+ // number of the disk with the
+ // start of the central directory 4 bytes
+ $cdDiskNo = unpack('V', fread($this->in, 4))[1];
+ // total number of entries in the
+ // central directory on this disk 8 bytes
+ $cdEntriesDisk = PackUtil::unpackLongLE(fread($this->in, 8));
+ // total number of entries in the
+ // central directory 8 bytes
+ $cdEntries = PackUtil::unpackLongLE(fread($this->in, 8));
+ if (0 !== $diskNo || 0 !== $cdDiskNo || $cdEntriesDisk !== $cdEntries) {
+ throw new ZipException("ZIP file spanning/splitting is not supported!");
+ }
+ if ($cdEntries < 0 || 0x7fffffff < $cdEntries) {
+ throw new ZipException("Total Number Of Entries In The Central Directory out of range!");
+ }
+ // size of the central directory 8 bytes
+ fseek($this->in, 8, SEEK_CUR);
+ // offset of start of central
+ // directory with respect to
+ // the starting disk number 8 bytes
+ $cdPos = PackUtil::unpackLongLE(fread($this->in, 8));
+ // zip64 extensible data sector (variable size)
+ fseek($this->in, $cdPos, SEEK_SET);
+ $this->preamble = $zip64EndOfCentralDirectoryRecordPos;
+ $entryCount = $cdEntries;
+ $zip64 = true;
+ return new EndOfCentralDirectory($entryCount, $comment, $zip64);
+ }
+ // Start recovering file entries from min.
+ $this->preamble = $min;
+ $this->postamble = $size - $min;
+ return new EndOfCentralDirectory(0, $comment);
+ }
+
+ /**
+ * Reads the central directory from the given seekable byte channel
+ * and populates the internal tables with ZipEntry instances.
+ *
+ * The ZipEntry's will know all data that can be obtained from the
+ * central directory alone, but not the data that requires the local
+ * file header or additional data to be read.
+ *
+ * @param EndOfCentralDirectory $endOfCentralDirectory
+ * @return ZipEntry[]
+ * @throws ZipException
+ */
+ protected function mountCentralDirectory(EndOfCentralDirectory $endOfCentralDirectory)
+ {
+ $numEntries = $endOfCentralDirectory->getEntryCount();
+ $entries = [];
+
+ for (; $numEntries > 0; $numEntries--) {
+ $entry = $this->readEntry();
+ // Re-load virtual offset after ZIP64 Extended Information
+ // Extra Field may have been parsed, map it to the real
+ // offset and conditionally update the preamble size from it.
+ $lfhOff = $this->mapper->map($entry->getOffset());
+ $lfhOff = PHP_INT_SIZE === 4 ? sprintf('%u', $lfhOff) : $lfhOff;
+ if ($lfhOff < $this->preamble) {
+ $this->preamble = $lfhOff;
+ }
+ $entries[$entry->getName()] = $entry;
+ }
+
+ if (0 !== $numEntries % 0x10000) {
+ throw new ZipException("Expected " . abs($numEntries) .
+ ($numEntries > 0 ? " more" : " less") .
+ " entries in the Central Directory!");
+ }
+
+ if ($this->preamble + $this->postamble >= fstat($this->in)['size']) {
+ assert(0 === $numEntries);
+ $this->checkZipFileSignature();
+ }
+
+ return $entries;
+ }
+
+ /**
+ * @return ZipEntry
+ * @throws InvalidArgumentException
+ */
+ public function readEntry()
+ {
+ // central file header signature 4 bytes (0x02014b50)
+ $fileHeaderSig = unpack('V', fread($this->in, 4))[1];
+ if (ZipOutputStreamInterface::CENTRAL_FILE_HEADER_SIG !== $fileHeaderSig) {
+ throw new InvalidArgumentException("Corrupt zip file. Can not read zip entry.");
+ }
+
+ // version made by 2 bytes
+ // version needed to extract 2 bytes
+ // general purpose bit flag 2 bytes
+ // compression method 2 bytes
+ // last mod file time 2 bytes
+ // last mod file date 2 bytes
+ // crc-32 4 bytes
+ // compressed size 4 bytes
+ // uncompressed size 4 bytes
+ // file name length 2 bytes
+ // extra field length 2 bytes
+ // file comment length 2 bytes
+ // disk number start 2 bytes
+ // internal file attributes 2 bytes
+ // external file attributes 4 bytes
+ // relative offset of local header 4 bytes
+ $data = unpack(
+ 'vversionMadeBy/vversionNeededToExtract/vgpbf/' .
+ 'vrawMethod/VrawTime/VrawCrc/VrawCompressedSize/' .
+ 'VrawSize/vfileLength/vextraLength/vcommentLength/' .
+ 'VrawInternalAttributes/VrawExternalAttributes/VlfhOff',
+ fread($this->in, 42)
+ );
+
+// $utf8 = 0 !== ($data['gpbf'] & self::GPBF_UTF8);
+
+ // See appendix D of PKWARE's ZIP File Format Specification.
+ $name = fread($this->in, $data['fileLength']);
+
+ $entry = new ZipSourceEntry($this);
+ $entry->setName($name);
+ $entry->setVersionNeededToExtract($data['versionNeededToExtract']);
+ $entry->setPlatform($data['versionMadeBy'] >> 8);
+ $entry->setMethod($data['rawMethod']);
+ $entry->setGeneralPurposeBitFlags($data['gpbf']);
+ $entry->setDosTime($data['rawTime']);
+ $entry->setCrc($data['rawCrc']);
+ $entry->setCompressedSize($data['rawCompressedSize']);
+ $entry->setSize($data['rawSize']);
+ $entry->setExternalAttributes($data['rawExternalAttributes']);
+ $entry->setOffset($data['lfhOff']); // must be unmapped!
+ if (0 < $data['extraLength']) {
+ $entry->setExtra(fread($this->in, $data['extraLength']));
+ }
+ if (0 < $data['commentLength']) {
+ $entry->setComment(fread($this->in, $data['commentLength']));
+ }
+ return $entry;
+ }
+
+ /**
+ * @param ZipEntry $entry
+ * @return string
+ * @throws ZipException
+ */
+ public function readEntryContent(ZipEntry $entry)
+ {
+ if ($entry->isDirectory()) {
+ return null;
+ }
+ if (!($entry instanceof ZipSourceEntry)) {
+ throw new InvalidArgumentException('entry must be ' . ZipSourceEntry::class);
+ }
+ $isEncrypted = $entry->isEncrypted();
+ if ($isEncrypted && null === $entry->getPassword()) {
+ throw new ZipException("Can not password from entry " . $entry->getName());
+ }
+
+ $pos = $entry->getOffset();
+ assert(ZipEntry::UNKNOWN !== $pos);
+ $pos = PHP_INT_SIZE === 4 ? sprintf('%u', $pos) : $pos;
+
+ $startPos = $pos = $this->mapper->map($pos);
+ fseek($this->in, $startPos);
+
+ // local file header signature 4 bytes (0x04034b50)
+ if (ZipEntry::LOCAL_FILE_HEADER_SIG !== unpack('V', fread($this->in, 4))[1]) {
+ throw new ZipException($entry->getName() . " (expected Local File Header)");
+ }
+ fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_FILE_NAME_LENGTH_POS);
+ // file name length 2 bytes
+ // extra field length 2 bytes
+ $data = unpack('vfileLength/vextraLength', fread($this->in, 4));
+ $pos += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $data['fileLength'] + $data['extraLength'];
+
+ assert(ZipEntry::UNKNOWN !== $entry->getCrc());
+
+ $method = $entry->getMethod();
+
+ fseek($this->in, $pos);
+
+ // Get raw entry content
+ $compressedSize = $entry->getCompressedSize();
+ $compressedSize = PHP_INT_SIZE === 4 ? sprintf('%u', $compressedSize) : $compressedSize;
+ if ($compressedSize > 0) {
+ $content = fread($this->in, $compressedSize);
+ } else {
+ $content = '';
+ }
+
+ $skipCheckCrc = false;
+ if ($isEncrypted) {
+ if (ZipEntry::METHOD_WINZIP_AES === $method) {
+ // Strong Encryption Specification - WinZip AES
+ $winZipAesEngine = new WinZipAesEngine($entry);
+ $content = $winZipAesEngine->decrypt($content);
+ /**
+ * @var WinZipAesEntryExtraField $field
+ */
+ $field = $entry->getExtraFieldsCollection()->get(WinZipAesEntryExtraField::getHeaderId());
+ $method = $field->getMethod();
+ $entry->setEncryptionMethod($field->getEncryptionMethod());
+ $skipCheckCrc = true;
+ } else {
+ // Traditional PKWARE Decryption
+ $zipCryptoEngine = new TraditionalPkwareEncryptionEngine($entry);
+ $content = $zipCryptoEngine->decrypt($content);
+ $entry->setEncryptionMethod(ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL);
+ }
+
+ if (!$skipCheckCrc) {
+ // Check CRC32 in the Local File Header or Data Descriptor.
+ $localCrc = null;
+ if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
+ // The CRC32 is in the Data Descriptor after the compressed size.
+ // Note the Data Descriptor's Signature is optional:
+ // All newer apps should write it (and so does TrueVFS),
+ // but older apps might not.
+ fseek($this->in, $pos + $compressedSize);
+ $localCrc = unpack('V', fread($this->in, 4))[1];
+ if (ZipEntry::DATA_DESCRIPTOR_SIG === $localCrc) {
+ $localCrc = unpack('V', fread($this->in, 4))[1];
+ }
+ } else {
+ fseek($this->in, $startPos + 14);
+ // The CRC32 in the Local File Header.
+ $localCrc = sprintf('%u', fread($this->in, 4)[1]);
+ $localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
+ }
+
+ $crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
+
+ if ($crc != $localCrc) {
+ throw new Crc32Exception($entry->getName(), $crc, $localCrc);
+ }
+ }
+ }
+
+ switch ($method) {
+ case ZipFileInterface::METHOD_STORED:
+ break;
+ case ZipFileInterface::METHOD_DEFLATED:
+ $content = gzinflate($content);
+ break;
+ case ZipFileInterface::METHOD_BZIP2:
+ if (!extension_loaded('bz2')) {
+ throw new ZipException('Extension bzip2 not install');
+ }
+ $content = bzdecompress($content);
+ break;
+ default:
+ throw new ZipUnsupportMethod($entry->getName() .
+ " (compression method " . $method . " is not supported)");
+ }
+ if (!$skipCheckCrc AND false) {
+ $localCrc = crc32($content);
+ $localCrc = PHP_INT_SIZE === 4 ? sprintf('%u', $localCrc) : $localCrc;
+ $crc = PHP_INT_SIZE === 4 ? sprintf('%u', $entry->getCrc()) : $entry->getCrc();
+ if ($crc != $localCrc) {
+ if ($isEncrypted) {
+ throw new ZipCryptoException("Wrong password");
+ }
+ throw new Crc32Exception($entry->getName(), $crc, $localCrc);
+ }
+ }
+ return $content;
+ }
+
+ /**
+ * @return resource
+ */
+ public function getStream()
+ {
+ return $this->in;
+ }
+
+ /**
+ * Copy the input stream of the LOC entry zip and the data into
+ * the output stream and zip the alignment if necessary.
+ *
+ * @param ZipEntry $entry
+ * @param ZipOutputStreamInterface $out
+ */
+ public function copyEntry(ZipEntry $entry, ZipOutputStreamInterface $out)
+ {
+ $pos = $entry->getOffset();
+ assert(ZipEntry::UNKNOWN !== $pos);
+ $pos = PHP_INT_SIZE === 4 ? sprintf('%u', $pos) : $pos;
+ $pos = $this->mapper->map($pos);
+
+ $nameLength = strlen($entry->getName());
+
+ fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
+ $sourceExtraLength = $destExtraLength = unpack('v', fread($this->in, 2))[1];
+
+ if ($sourceExtraLength > 0) {
+ // read Local File Header extra fields
+ fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength, SEEK_SET);
+ $extra = fread($this->in, $sourceExtraLength);
+ $extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($extra, $entry);
+ if (isset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]) && $this->zipModel->isZipAlign()) {
+ unset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]);
+ $destExtraLength = strlen(ExtraFieldsFactory::createSerializedData($extraFieldsCollection));
+ }
+ } else {
+ $extraFieldsCollection = new ExtraFieldsCollection();
+ }
+
+ $dataAlignmentMultiple = $this->zipModel->getZipAlign();
+ $copyInToOutLength = $entry->getCompressedSize();
+
+ fseek($this->in, $pos, SEEK_SET);
+
+ if (
+ $this->zipModel->isZipAlign() &&
+ !$entry->isEncrypted() &&
+ $entry->getMethod() === ZipFileInterface::METHOD_STORED
+ ) {
+ if (StringUtil::endsWith($entry->getName(), '.so')) {
+ $dataAlignmentMultiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
+ }
+
+ $dataMinStartOffset =
+ ftell($out->getStream()) +
+ ZipEntry::LOCAL_FILE_HEADER_MIN_LEN +
+ $destExtraLength +
+ $nameLength +
+ ApkAlignmentExtraField::ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES;
+ $padding =
+ ($dataAlignmentMultiple - ($dataMinStartOffset % $dataAlignmentMultiple))
+ % $dataAlignmentMultiple;
+
+ $alignExtra = new ApkAlignmentExtraField();
+ $alignExtra->setMultiple($dataAlignmentMultiple);
+ $alignExtra->setPadding($padding);
+ $extraFieldsCollection->add($alignExtra);
+
+ $extra = ExtraFieldsFactory::createSerializedData($extraFieldsCollection);
+
+ // copy Local File Header without extra field length
+ // from input stream to output stream
+ stream_copy_to_stream($this->in, $out->getStream(), ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2);
+ // write new extra field length (2 bytes) to output stream
+ fwrite($out->getStream(), pack('v', strlen($extra)));
+ // skip 2 bytes to input stream
+ fseek($this->in, 2, SEEK_CUR);
+ // copy name from input stream to output stream
+ stream_copy_to_stream($this->in, $out->getStream(), $nameLength);
+ // write extra field to output stream
+ fwrite($out->getStream(), $extra);
+ // skip source extraLength from input stream
+ fseek($this->in, $sourceExtraLength, SEEK_CUR);
+ } else {
+ $copyInToOutLength += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $sourceExtraLength + $nameLength;
+ ;
+ }
+ if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
+// crc-32 4 bytes
+// compressed size 4 bytes
+// uncompressed size 4 bytes
+ $copyInToOutLength += 12;
+ if ($entry->isZip64ExtensionsRequired()) {
+// compressed size +4 bytes
+// uncompressed size +4 bytes
+ $copyInToOutLength += 8;
+ }
+ }
+ // copy loc, data, data descriptor from input to output stream
+ stream_copy_to_stream($this->in, $out->getStream(), $copyInToOutLength);
+ }
+
+ /**
+ * @param ZipEntry $entry
+ * @param ZipOutputStreamInterface $out
+ */
+ public function copyEntryData(ZipEntry $entry, ZipOutputStreamInterface $out)
+ {
+ $offset = $entry->getOffset();
+ $offset = PHP_INT_SIZE === 4 ? sprintf('%u', $offset) : $offset;
+ $offset = $this->mapper->map($offset);
+ $nameLength = strlen($entry->getName());
+
+ fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
+ $extraLength = unpack('v', fread($this->in, 2))[1];
+
+ fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength + $extraLength, SEEK_SET);
+ // copy raw data from input stream to output stream
+ stream_copy_to_stream($this->in, $out->getStream(), $entry->getCompressedSize());
+ }
+
+ public function __destruct()
+ {
+ $this->close();
+ }
+
+ public function close()
+ {
+ if ($this->in != null) {
+ fclose($this->in);
+ $this->in = null;
+ }
+ }
+}