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/ZipOutputStream.php')
-rw-r--r--vendor/nelexa/zip/src/PhpZip/Stream/ZipOutputStream.php528
1 files changed, 528 insertions, 0 deletions
diff --git a/vendor/nelexa/zip/src/PhpZip/Stream/ZipOutputStream.php b/vendor/nelexa/zip/src/PhpZip/Stream/ZipOutputStream.php
new file mode 100644
index 0000000..c1c875d
--- /dev/null
+++ b/vendor/nelexa/zip/src/PhpZip/Stream/ZipOutputStream.php
@@ -0,0 +1,528 @@
+<?php
+
+namespace PhpZip\Stream;
+
+use PhpZip\Crypto\TraditionalPkwareEncryptionEngine;
+use PhpZip\Crypto\WinZipAesEngine;
+use PhpZip\Exception\InvalidArgumentException;
+use PhpZip\Exception\RuntimeException;
+use PhpZip\Exception\ZipException;
+use PhpZip\Extra\ExtraFieldsFactory;
+use PhpZip\Extra\Fields\ApkAlignmentExtraField;
+use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
+use PhpZip\Extra\Fields\Zip64ExtraField;
+use PhpZip\Model\EndOfCentralDirectory;
+use PhpZip\Model\Entry\OutputOffsetEntry;
+use PhpZip\Model\Entry\ZipChangesEntry;
+use PhpZip\Model\Entry\ZipSourceEntry;
+use PhpZip\Model\ZipEntry;
+use PhpZip\Model\ZipModel;
+use PhpZip\Util\PackUtil;
+use PhpZip\Util\StringUtil;
+use PhpZip\ZipFileInterface;
+
+/**
+ * Write
+ * ip file
+ *
+ * @author Ne-Lexa alexey@nelexa.ru
+ * @license MIT
+ */
+class ZipOutputStream implements ZipOutputStreamInterface
+{
+ /**
+ * @var resource
+ */
+ protected $out;
+ /**
+ * @var ZipModel
+ */
+ protected $zipModel;
+
+ /**
+ * ZipOutputStream constructor.
+ * @param resource $out
+ * @param ZipModel $zipModel
+ * @throws InvalidArgumentException
+ */
+ public function __construct($out, ZipModel $zipModel)
+ {
+ if (!is_resource($out)) {
+ throw new InvalidArgumentException('$out must be resource');
+ }
+ $this->out = $out;
+ $this->zipModel = $zipModel;
+ }
+
+ public function writeZip()
+ {
+ $entries = $this->zipModel->getEntries();
+ $outPosEntries = [];
+ foreach ($entries as $entry) {
+ $outPosEntries[] = new OutputOffsetEntry(ftell($this->out), $entry);
+ $this->writeEntry($entry);
+ }
+ $centralDirectoryOffset = ftell($this->out);
+ foreach ($outPosEntries as $outputEntry) {
+ $this->writeCentralDirectoryHeader($outputEntry);
+ }
+ $this->writeEndOfCentralDirectoryRecord($centralDirectoryOffset);
+ }
+
+ /**
+ * @param ZipEntry $entry
+ * @throws ZipException
+ */
+ public function writeEntry(ZipEntry $entry)
+ {
+ if ($entry instanceof ZipSourceEntry) {
+ $entry->getInputStream()->copyEntry($entry, $this);
+ return;
+ }
+
+ $entryContent = $this->entryCommitChangesAndReturnContent($entry);
+
+ $offset = ftell($this->out);
+ $compressedSize = $entry->getCompressedSize();
+
+ $extra = $entry->getExtra();
+
+ $nameLength = strlen($entry->getName());
+ $extraLength = strlen($extra);
+
+ // zip align
+ if (
+ $this->zipModel->isZipAlign() &&
+ !$entry->isEncrypted() &&
+ $entry->getMethod() === ZipFileInterface::METHOD_STORED
+ ) {
+ $dataAlignmentMultiple = $this->zipModel->getZipAlign();
+ if (StringUtil::endsWith($entry->getName(), '.so')) {
+ $dataAlignmentMultiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
+ }
+ $dataMinStartOffset =
+ $offset +
+ ZipEntry::LOCAL_FILE_HEADER_MIN_LEN +
+ $extraLength +
+ $nameLength +
+ ApkAlignmentExtraField::ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES;
+
+ $padding =
+ ($dataAlignmentMultiple - ($dataMinStartOffset % $dataAlignmentMultiple))
+ % $dataAlignmentMultiple;
+
+ $alignExtra = new ApkAlignmentExtraField();
+ $alignExtra->setMultiple($dataAlignmentMultiple);
+ $alignExtra->setPadding($padding);
+
+ $extraFieldsCollection = clone $entry->getExtraFieldsCollection();
+ $extraFieldsCollection->add($alignExtra);
+
+ $extra = ExtraFieldsFactory::createSerializedData($extraFieldsCollection);
+ $extraLength = strlen($extra);
+ }
+
+ $size = $nameLength + $extraLength;
+ if (0xffff < $size) {
+ throw new ZipException(
+ $entry->getName() . " (the total size of " . $size .
+ " bytes for the name, extra fields and comment " .
+ "exceeds the maximum size of " . 0xffff . " bytes)"
+ );
+ }
+
+ $dd = $entry->isDataDescriptorRequired();
+ fwrite(
+ $this->out,
+ pack(
+ 'VvvvVVVVvv',
+ // local file header signature 4 bytes (0x04034b50)
+ ZipEntry::LOCAL_FILE_HEADER_SIG,
+ // version needed to extract 2 bytes
+ $entry->getVersionNeededToExtract(),
+ // general purpose bit flag 2 bytes
+ $entry->getGeneralPurposeBitFlags(),
+ // compression method 2 bytes
+ $entry->getMethod(),
+ // last mod file time 2 bytes
+ // last mod file date 2 bytes
+ $entry->getDosTime(),
+ // crc-32 4 bytes
+ $dd ? 0 : $entry->getCrc(),
+ // compressed size 4 bytes
+ $dd ? 0 : $entry->getCompressedSize(),
+ // uncompressed size 4 bytes
+ $dd ? 0 : $entry->getSize(),
+ // file name length 2 bytes
+ $nameLength,
+ // extra field length 2 bytes
+ $extraLength
+ )
+ );
+ if ($nameLength > 0) {
+ fwrite($this->out, $entry->getName());
+ }
+ if ($extraLength > 0) {
+ fwrite($this->out, $extra);
+ }
+
+ if ($entry instanceof ZipChangesEntry && !$entry->isChangedContent()) {
+ $entry->getSourceEntry()->getInputStream()->copyEntryData($entry->getSourceEntry(), $this);
+ } elseif (null !== $entryContent) {
+ fwrite($this->out, $entryContent);
+ }
+
+ assert(ZipEntry::UNKNOWN !== $entry->getCrc());
+ assert(ZipEntry::UNKNOWN !== $entry->getSize());
+ if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
+ // data descriptor signature 4 bytes (0x08074b50)
+ // crc-32 4 bytes
+ fwrite($this->out, pack('VV', ZipEntry::DATA_DESCRIPTOR_SIG, $entry->getCrc()));
+ // compressed size 4 or 8 bytes
+ // uncompressed size 4 or 8 bytes
+ if ($entry->isZip64ExtensionsRequired()) {
+ fwrite($this->out, PackUtil::packLongLE($compressedSize));
+ fwrite($this->out, PackUtil::packLongLE($entry->getSize()));
+ } else {
+ fwrite($this->out, pack('VV', $entry->getCompressedSize(), $entry->getSize()));
+ }
+ } elseif ($entry->getCompressedSize() != $compressedSize) {
+ throw new ZipException(
+ $entry->getName() . " (expected compressed entry size of "
+ . $entry->getCompressedSize() . " bytes, " .
+ "but is actually " . $compressedSize . " bytes)"
+ );
+ }
+ }
+
+ /**
+ * @param ZipEntry $entry
+ * @return null|string
+ * @throws ZipException
+ */
+ protected function entryCommitChangesAndReturnContent(ZipEntry $entry)
+ {
+ if (ZipEntry::UNKNOWN === $entry->getPlatform()) {
+ $entry->setPlatform(ZipEntry::PLATFORM_UNIX);
+ }
+ if (ZipEntry::UNKNOWN === $entry->getTime()) {
+ $entry->setTime(time());
+ }
+ $method = $entry->getMethod();
+
+ $encrypted = $entry->isEncrypted();
+ // See appendix D of PKWARE's ZIP File Format Specification.
+ $utf8 = true;
+
+ if ($encrypted && null === $entry->getPassword()) {
+ throw new ZipException("Can not password from entry " . $entry->getName());
+ }
+
+ // Compose General Purpose Bit Flag.
+ $general = ($encrypted ? ZipEntry::GPBF_ENCRYPTED : 0)
+ | ($entry->isDataDescriptorRequired() ? ZipEntry::GPBF_DATA_DESCRIPTOR : 0)
+ | ($utf8 ? ZipEntry::GPBF_UTF8 : 0);
+
+ $entryContent = null;
+ $extraFieldsCollection = $entry->getExtraFieldsCollection();
+ if (!($entry instanceof ZipChangesEntry && !$entry->isChangedContent())) {
+ $entryContent = $entry->getEntryContent();
+
+ if ($entryContent !== null) {
+ $entry->setSize(strlen($entryContent));
+ $entry->setCrc(crc32($entryContent));
+
+ if ($encrypted && ZipEntry::METHOD_WINZIP_AES === $method) {
+ /**
+ * @var WinZipAesEntryExtraField $field
+ */
+ $field = $extraFieldsCollection->get(WinZipAesEntryExtraField::getHeaderId());
+ if (null !== $field) {
+ $method = $field->getMethod();
+ }
+ }
+
+ switch ($method) {
+ case ZipFileInterface::METHOD_STORED:
+ break;
+
+ case ZipFileInterface::METHOD_DEFLATED:
+ $entryContent = gzdeflate($entryContent, $entry->getCompressionLevel());
+ break;
+
+ case ZipFileInterface::METHOD_BZIP2:
+ $compressionLevel = $entry->getCompressionLevel() === ZipFileInterface::LEVEL_DEFAULT_COMPRESSION ?
+ ZipEntry::LEVEL_DEFAULT_BZIP2_COMPRESSION :
+ $entry->getCompressionLevel();
+ $entryContent = bzcompress($entryContent, $compressionLevel);
+ if (is_int($entryContent)) {
+ throw new ZipException('Error bzip2 compress. Error code: ' . $entryContent);
+ }
+ break;
+
+ case ZipEntry::UNKNOWN:
+ $entryContent = $this->determineBestCompressionMethod($entry, $entryContent);
+ $method = $entry->getMethod();
+ break;
+
+ default:
+ throw new ZipException($entry->getName() . " (unsupported compression method " . $method . ")");
+ }
+
+ if (ZipFileInterface::METHOD_DEFLATED === $method) {
+ $bit1 = false;
+ $bit2 = false;
+ switch ($entry->getCompressionLevel()) {
+ case ZipFileInterface::LEVEL_BEST_COMPRESSION:
+ $bit1 = true;
+ break;
+
+ case ZipFileInterface::LEVEL_FAST:
+ $bit2 = true;
+ break;
+
+ case ZipFileInterface::LEVEL_SUPER_FAST:
+ $bit1 = true;
+ $bit2 = true;
+ break;
+ }
+
+ $general |= ($bit1 ? ZipEntry::GPBF_COMPRESSION_FLAG1 : 0);
+ $general |= ($bit2 ? ZipEntry::GPBF_COMPRESSION_FLAG2 : 0);
+ }
+
+ if ($encrypted) {
+ if (in_array($entry->getEncryptionMethod(), [
+ ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128,
+ ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192,
+ ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256,
+ ], true)) {
+ $keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($entry->getEncryptionMethod()); // size bits
+ $field = ExtraFieldsFactory::createWinZipAesEntryExtra();
+ $field->setKeyStrength($keyStrength);
+ $field->setMethod($method);
+ $size = $entry->getSize();
+ if (20 <= $size && ZipFileInterface::METHOD_BZIP2 !== $method) {
+ $field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_1);
+ } else {
+ $field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_2);
+ $entry->setCrc(0);
+ }
+ $extraFieldsCollection->add($field);
+ $entry->setMethod(ZipEntry::METHOD_WINZIP_AES);
+
+ $winZipAesEngine = new WinZipAesEngine($entry);
+ $entryContent = $winZipAesEngine->encrypt($entryContent);
+ } elseif ($entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL) {
+ $zipCryptoEngine = new TraditionalPkwareEncryptionEngine($entry);
+ $entryContent = $zipCryptoEngine->encrypt($entryContent);
+ }
+ }
+
+ $compressedSize = strlen($entryContent);
+ $entry->setCompressedSize($compressedSize);
+ }
+ }
+
+ // Commit changes.
+ $entry->setGeneralPurposeBitFlags($general);
+
+ if ($entry->isZip64ExtensionsRequired()) {
+ $extraFieldsCollection->add(ExtraFieldsFactory::createZip64Extra($entry));
+ } elseif ($extraFieldsCollection->has(Zip64ExtraField::getHeaderId())) {
+ $extraFieldsCollection->remove(Zip64ExtraField::getHeaderId());
+ }
+ return $entryContent;
+ }
+
+ /**
+ * @param ZipEntry $entry
+ * @param string $content
+ * @return string
+ * @throws ZipException
+ */
+ protected function determineBestCompressionMethod(ZipEntry $entry, $content)
+ {
+ if (null !== $content) {
+ $entryContent = gzdeflate($content, $entry->getCompressionLevel());
+ if (strlen($entryContent) < strlen($content)) {
+ $entry->setMethod(ZipFileInterface::METHOD_DEFLATED);
+ return $entryContent;
+ }
+ $entry->setMethod(ZipFileInterface::METHOD_STORED);
+ }
+ return $content;
+ }
+
+ /**
+ * Writes a Central File Header record.
+ *
+ * @param OutputOffsetEntry $outEntry
+ * @throws RuntimeException
+ * @internal param OutPosEntry $entry
+ */
+ protected function writeCentralDirectoryHeader(OutputOffsetEntry $outEntry)
+ {
+ $entry = $outEntry->getEntry();
+ $compressedSize = $entry->getCompressedSize();
+ $size = $entry->getSize();
+ // This test MUST NOT include the CRC-32 because VV_AE_2 sets it to
+ // UNKNOWN!
+ if (ZipEntry::UNKNOWN === ($compressedSize | $size)) {
+ throw new RuntimeException("invalid entry");
+ }
+ $extra = $entry->getExtra();
+ $extraSize = strlen($extra);
+
+ $commentLength = strlen($entry->getComment());
+ fwrite(
+ $this->out,
+ pack(
+ 'VvvvvVVVVvvvvvVV',
+ // central file header signature 4 bytes (0x02014b50)
+ self::CENTRAL_FILE_HEADER_SIG,
+ // version made by 2 bytes
+ ($entry->getPlatform() << 8) | 63,
+ // version needed to extract 2 bytes
+ $entry->getVersionNeededToExtract(),
+ // general purpose bit flag 2 bytes
+ $entry->getGeneralPurposeBitFlags(),
+ // compression method 2 bytes
+ $entry->getMethod(),
+ // last mod file datetime 4 bytes
+ $entry->getDosTime(),
+ // crc-32 4 bytes
+ $entry->getCrc(),
+ // compressed size 4 bytes
+ $entry->getCompressedSize(),
+ // uncompressed size 4 bytes
+ $entry->getSize(),
+ // file name length 2 bytes
+ strlen($entry->getName()),
+ // extra field length 2 bytes
+ $extraSize,
+ // file comment length 2 bytes
+ $commentLength,
+ // disk number start 2 bytes
+ 0,
+ // internal file attributes 2 bytes
+ 0,
+ // external file attributes 4 bytes
+ $entry->getExternalAttributes(),
+ // relative offset of local header 4 bytes
+ $outEntry->getOffset()
+ )
+ );
+ // file name (variable size)
+ fwrite($this->out, $entry->getName());
+ if (0 < $extraSize) {
+ // extra field (variable size)
+ fwrite($this->out, $extra);
+ }
+ if (0 < $commentLength) {
+ // file comment (variable size)
+ fwrite($this->out, $entry->getComment());
+ }
+ }
+
+ protected function writeEndOfCentralDirectoryRecord($centralDirectoryOffset)
+ {
+ $centralDirectoryEntriesCount = count($this->zipModel);
+ $position = ftell($this->out);
+ $centralDirectorySize = $position - $centralDirectoryOffset;
+ $centralDirectoryEntriesZip64 = $centralDirectoryEntriesCount > 0xffff;
+ $centralDirectorySizeZip64 = $centralDirectorySize > 0xffffffff;
+ $centralDirectoryOffsetZip64 = $centralDirectoryOffset > 0xffffffff;
+ $centralDirectoryEntries16 = $centralDirectoryEntriesZip64 ? 0xffff : (int)$centralDirectoryEntriesCount;
+ $centralDirectorySize32 = $centralDirectorySizeZip64 ? 0xffffffff : $centralDirectorySize;
+ $centralDirectoryOffset32 = $centralDirectoryOffsetZip64 ? 0xffffffff : $centralDirectoryOffset;
+ $zip64 // ZIP64 extensions?
+ = $centralDirectoryEntriesZip64
+ || $centralDirectorySizeZip64
+ || $centralDirectoryOffsetZip64;
+ if ($zip64) {
+ // [zip64 end of central directory record]
+ // relative offset of the zip64 end of central directory record
+ $zip64EndOfCentralDirectoryOffset = $position;
+ // zip64 end of central dir
+ // signature 4 bytes (0x06064b50)
+ fwrite($this->out, pack('V', EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_SIG));
+ // size of zip64 end of central
+ // directory record 8 bytes
+ fwrite($this->out, PackUtil::packLongLE(EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_MIN_LEN - 12));
+ // version made by 2 bytes
+ // version needed to extract 2 bytes
+ // due to potential use of BZIP2 compression
+ // number of this disk 4 bytes
+ // number of the disk with the
+ // start of the central directory 4 bytes
+ fwrite($this->out, pack('vvVV', 63, 46, 0, 0));
+ // total number of entries in the
+ // central directory on this disk 8 bytes
+ fwrite($this->out, PackUtil::packLongLE($centralDirectoryEntriesCount));
+ // total number of entries in the
+ // central directory 8 bytes
+ fwrite($this->out, PackUtil::packLongLE($centralDirectoryEntriesCount));
+ // size of the central directory 8 bytes
+ fwrite($this->out, PackUtil::packLongLE($centralDirectorySize));
+ // offset of start of central
+ // directory with respect to
+ // the starting disk number 8 bytes
+ fwrite($this->out, PackUtil::packLongLE($centralDirectoryOffset));
+ // zip64 extensible data sector (variable size)
+
+ // [zip64 end of central directory locator]
+ // signature 4 bytes (0x07064b50)
+ // number of the disk with the
+ // start of the zip64 end of
+ // central directory 4 bytes
+ fwrite($this->out, pack('VV', EndOfCentralDirectory::ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG, 0));
+ // relative offset of the zip64
+ // end of central directory record 8 bytes
+ fwrite($this->out, PackUtil::packLongLE($zip64EndOfCentralDirectoryOffset));
+ // total number of disks 4 bytes
+ fwrite($this->out, pack('V', 1));
+ }
+ $comment = $this->zipModel->getArchiveComment();
+ $commentLength = strlen($comment);
+ fwrite(
+ $this->out,
+ pack(
+ 'VvvvvVVv',
+ // end of central dir signature 4 bytes (0x06054b50)
+ EndOfCentralDirectory::END_OF_CENTRAL_DIRECTORY_RECORD_SIG,
+ // number of this disk 2 bytes
+ 0,
+ // number of the disk with the
+ // start of the central directory 2 bytes
+ 0,
+ // total number of entries in the
+ // central directory on this disk 2 bytes
+ $centralDirectoryEntries16,
+ // total number of entries in
+ // the central directory 2 bytes
+ $centralDirectoryEntries16,
+ // size of the central directory 4 bytes
+ $centralDirectorySize32,
+ // offset of start of central
+ // directory with respect to
+ // the starting disk number 4 bytes
+ $centralDirectoryOffset32,
+ // .ZIP file comment length 2 bytes
+ $commentLength
+ )
+ );
+ if ($commentLength > 0) {
+ // .ZIP file comment (variable size)
+ fwrite($this->out, $comment);
+ }
+ }
+
+ /**
+ * @return resource
+ */
+ public function getStream()
+ {
+ return $this->out;
+ }
+}