| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 |
- <?php
- namespace PhpZip\Model\Entry;
- use PhpZip\Exception\InvalidArgumentException;
- use PhpZip\Exception\ZipException;
- use PhpZip\Extra\ExtraFieldsCollection;
- use PhpZip\Extra\ExtraFieldsFactory;
- use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
- use PhpZip\Model\ZipEntry;
- use PhpZip\Util\DateTimeConverter;
- use PhpZip\Util\StringUtil;
- use PhpZip\ZipFileInterface;
- /**
- * Abstract ZIP entry.
- *
- * @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
- *
- * @author Ne-Lexa alexey@nelexa.ru
- * @license MIT
- */
- abstract class ZipAbstractEntry implements ZipEntry
- {
- /** @var int bit flags for init state */
- private $init;
- /** @var string Entry name (filename in archive) */
- private $name;
- /** @var int Made by platform */
- private $platform;
- /** @var int */
- private $versionNeededToExtract = 20;
- /** @var int Compression method */
- private $method;
- /** @var int */
- private $general;
- /** @var int Dos time */
- private $dosTime;
- /** @var int Crc32 */
- private $crc;
- /** @var int Compressed size */
- private $compressedSize = self::UNKNOWN;
- /** @var int Uncompressed size */
- private $size = self::UNKNOWN;
- /** @var int External attributes */
- private $externalAttributes;
- /** @var int relative Offset Of Local File Header */
- private $offset = self::UNKNOWN;
- /**
- * Collections of Extra Fields.
- * Keys from Header ID [int] and value Extra Field [ExtraField].
- * Should be null or may be empty if no Extra Fields are used.
- *
- * @var ExtraFieldsCollection
- */
- private $extraFieldsCollection;
- /** @var string comment field */
- private $comment;
- /** @var string entry password for read or write encryption data */
- private $password;
- /**
- * Encryption method.
- *
- * @see ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
- * @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
- * @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192
- * @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
- *
- * @var int
- */
- private $encryptionMethod = ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL;
- /** @var int */
- private $compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION;
- /**
- * ZipAbstractEntry constructor.
- */
- public function __construct()
- {
- $this->extraFieldsCollection = new ExtraFieldsCollection();
- }
- /**
- * @param ZipEntry $entry
- *
- * @throws ZipException
- */
- public function setEntry(ZipEntry $entry)
- {
- $this->setName($entry->getName());
- $this->setPlatform($entry->getPlatform());
- $this->setVersionNeededToExtract($entry->getVersionNeededToExtract());
- $this->setMethod($entry->getMethod());
- $this->setGeneralPurposeBitFlags($entry->getGeneralPurposeBitFlags());
- $this->setDosTime($entry->getDosTime());
- $this->setCrc($entry->getCrc());
- $this->setCompressedSize($entry->getCompressedSize());
- $this->setSize($entry->getSize());
- $this->setExternalAttributes($entry->getExternalAttributes());
- $this->setOffset($entry->getOffset());
- $this->setExtra($entry->getExtra());
- $this->setComment($entry->getComment());
- $this->setPassword($entry->getPassword());
- $this->setEncryptionMethod($entry->getEncryptionMethod());
- $this->setCompressionLevel($entry->getCompressionLevel());
- $this->setEncrypted($entry->isEncrypted());
- }
- /**
- * Returns the ZIP entry name.
- *
- * @return string
- */
- public function getName()
- {
- return $this->name;
- }
- /**
- * Set entry name.
- *
- * @param string $name New entry name
- *
- * @throws ZipException
- *
- * @return ZipEntry
- */
- public function setName($name)
- {
- $length = \strlen($name);
- if ($length < 0x0000 || $length > 0xffff) {
- throw new ZipException('Illegal zip entry name parameter');
- }
- $this->setGeneralPurposeBitFlag(self::GPBF_UTF8, true);
- $this->name = $name;
- return $this;
- }
- /**
- * Sets the indexed General Purpose Bit Flag.
- *
- * @param int $mask
- * @param bool $bit
- *
- * @return ZipEntry
- */
- public function setGeneralPurposeBitFlag($mask, $bit)
- {
- if ($bit) {
- $this->general |= $mask;
- } else {
- $this->general &= ~$mask;
- }
- return $this;
- }
- /**
- * @return int Get platform
- */
- public function getPlatform()
- {
- return $this->isInit(self::BIT_PLATFORM) ? $this->platform & 0xffff : self::UNKNOWN;
- }
- /**
- * Set platform.
- *
- * @param int $platform
- *
- * @throws ZipException
- *
- * @return ZipEntry
- */
- public function setPlatform($platform)
- {
- $known = $platform !== self::UNKNOWN;
- if ($known) {
- if ($platform < 0x00 || $platform > 0xff) {
- throw new ZipException('Platform out of range');
- }
- $this->platform = $platform;
- } else {
- $this->platform = 0;
- }
- $this->setInit(self::BIT_PLATFORM, $known);
- return $this;
- }
- /**
- * @param int $mask
- *
- * @return bool
- */
- protected function isInit($mask)
- {
- return ($this->init & $mask) !== 0;
- }
- /**
- * @param int $mask
- * @param bool $init
- */
- protected function setInit($mask, $init)
- {
- if ($init) {
- $this->init |= $mask;
- } else {
- $this->init &= ~$mask;
- }
- }
- /**
- * Version needed to extract.
- *
- * @return int
- */
- public function getVersionNeededToExtract()
- {
- return $this->versionNeededToExtract;
- }
- /**
- * Set version needed to extract.
- *
- * @param int $version
- *
- * @return ZipEntry
- */
- public function setVersionNeededToExtract($version)
- {
- $this->versionNeededToExtract = $version;
- return $this;
- }
- /**
- * @return bool
- */
- public function isZip64ExtensionsRequired()
- {
- return $this->getCompressedSize() >= 0xffffffff
- || $this->getSize() >= 0xffffffff;
- }
- /**
- * Returns the compressed size of this entry.
- *
- * @see int
- */
- public function getCompressedSize()
- {
- return $this->compressedSize;
- }
- /**
- * Sets the compressed size of this entry.
- *
- * @param int $compressedSize the Compressed Size
- *
- * @return ZipEntry
- */
- public function setCompressedSize($compressedSize)
- {
- $this->compressedSize = $compressedSize;
- return $this;
- }
- /**
- * Returns the uncompressed size of this entry.
- *
- * @see ZipEntry::setCompressedSize
- */
- public function getSize()
- {
- return $this->size;
- }
- /**
- * Sets the uncompressed size of this entry.
- *
- * @param int $size the (Uncompressed) Size
- *
- * @return ZipEntry
- */
- public function setSize($size)
- {
- $this->size = $size;
- return $this;
- }
- /**
- * Return relative Offset Of Local File Header.
- *
- * @return int
- */
- public function getOffset()
- {
- return $this->offset;
- }
- /**
- * @param int $offset
- *
- * @return ZipEntry
- */
- public function setOffset($offset)
- {
- $this->offset = $offset;
- return $this;
- }
- /**
- * Returns the General Purpose Bit Flags.
- *
- * @return int
- */
- public function getGeneralPurposeBitFlags()
- {
- return $this->general & 0xffff;
- }
- /**
- * Sets the General Purpose Bit Flags.
- *
- * @param mixed $general
- *
- * @throws ZipException
- *
- * @return ZipEntry
- *
- * @var int general
- */
- public function setGeneralPurposeBitFlags($general)
- {
- if ($general < 0x0000 || $general > 0xffff) {
- throw new ZipException('general out of range');
- }
- $this->general = $general;
- if ($this->method === ZipFileInterface::METHOD_DEFLATED) {
- $bit1 = $this->getGeneralPurposeBitFlag(self::GPBF_COMPRESSION_FLAG1);
- $bit2 = $this->getGeneralPurposeBitFlag(self::GPBF_COMPRESSION_FLAG2);
- if ($bit1 && !$bit2) {
- $this->compressionLevel = ZipFileInterface::LEVEL_BEST_COMPRESSION;
- } elseif (!$bit1 && $bit2) {
- $this->compressionLevel = ZipFileInterface::LEVEL_FAST;
- } elseif ($bit1 && $bit2) {
- $this->compressionLevel = ZipFileInterface::LEVEL_SUPER_FAST;
- } else {
- $this->compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION;
- }
- }
- return $this;
- }
- /**
- * Returns true if and only if this ZIP entry is encrypted.
- *
- * @return bool
- */
- public function isEncrypted()
- {
- return $this->getGeneralPurposeBitFlag(self::GPBF_ENCRYPTED);
- }
- /**
- * Returns the indexed General Purpose Bit Flag.
- *
- * @param int $mask
- *
- * @return bool
- */
- public function getGeneralPurposeBitFlag($mask)
- {
- return ($this->general & $mask) !== 0;
- }
- /**
- * Sets the encryption property to false and removes any other
- * encryption artifacts.
- *
- * @throws ZipException
- *
- * @return ZipEntry
- */
- public function disableEncryption()
- {
- $this->setEncrypted(false);
- $headerId = WinZipAesEntryExtraField::getHeaderId();
- if (isset($this->extraFieldsCollection[$headerId])) {
- /** @var WinZipAesEntryExtraField $field */
- $field = $this->extraFieldsCollection[$headerId];
- if ($this->getMethod() === self::METHOD_WINZIP_AES) {
- $this->setMethod($field === null ? self::UNKNOWN : $field->getMethod());
- }
- unset($this->extraFieldsCollection[$headerId]);
- }
- $this->password = null;
- return $this;
- }
- /**
- * Sets the encryption flag for this ZIP entry.
- *
- * @param bool $encrypted
- *
- * @return ZipEntry
- */
- public function setEncrypted($encrypted)
- {
- $encrypted = (bool) $encrypted;
- $this->setGeneralPurposeBitFlag(self::GPBF_ENCRYPTED, $encrypted);
- return $this;
- }
- /**
- * Returns the compression method for this entry.
- *
- * @return int
- */
- public function getMethod()
- {
- $isInit = $this->isInit(self::BIT_METHOD);
- return $isInit ?
- $this->method & 0xffff :
- self::UNKNOWN;
- }
- /**
- * Sets the compression method for this entry.
- *
- * @param int $method
- *
- * @throws ZipException if method is not STORED, DEFLATED, BZIP2 or UNKNOWN
- *
- * @return ZipEntry
- */
- public function setMethod($method)
- {
- if ($method === self::UNKNOWN) {
- $this->method = $method;
- $this->setInit(self::BIT_METHOD, false);
- return $this;
- }
- if ($method < 0x0000 || $method > 0xffff) {
- throw new ZipException('method out of range: ' . $method);
- }
- switch ($method) {
- case self::METHOD_WINZIP_AES:
- case ZipFileInterface::METHOD_STORED:
- case ZipFileInterface::METHOD_DEFLATED:
- case ZipFileInterface::METHOD_BZIP2:
- $this->method = $method;
- $this->setInit(self::BIT_METHOD, true);
- break;
- default:
- throw new ZipException($this->name . " (unsupported compression method {$method})");
- }
- return $this;
- }
- /**
- * Get Unix Timestamp.
- *
- * @return int
- */
- public function getTime()
- {
- if (!$this->isInit(self::BIT_DATE_TIME)) {
- return self::UNKNOWN;
- }
- return DateTimeConverter::toUnixTimestamp($this->getDosTime());
- }
- /**
- * Get Dos Time.
- *
- * @return int
- */
- public function getDosTime()
- {
- return $this->dosTime;
- }
- /**
- * Set Dos Time.
- *
- * @param int $dosTime
- *
- * @throws ZipException
- */
- public function setDosTime($dosTime)
- {
- $dosTime = (int) $dosTime;
- if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
- throw new ZipException('DosTime out of range');
- }
- $this->dosTime = $dosTime;
- $this->setInit(self::BIT_DATE_TIME, true);
- }
- /**
- * Set time from unix timestamp.
- *
- * @param int $unixTimestamp
- *
- * @throws ZipException
- *
- * @return ZipEntry
- */
- public function setTime($unixTimestamp)
- {
- $known = $unixTimestamp !== self::UNKNOWN;
- if ($known) {
- $this->dosTime = DateTimeConverter::toDosTime($unixTimestamp);
- } else {
- $this->dosTime = 0;
- }
- $this->setInit(self::BIT_DATE_TIME, $known);
- return $this;
- }
- /**
- * Returns the external file attributes.
- *
- * @return int the external file attributes
- */
- public function getExternalAttributes()
- {
- if (!$this->isInit(self::BIT_EXTERNAL_ATTR)) {
- return $this->isDirectory() ? 0x10 : 0;
- }
- return $this->externalAttributes;
- }
- /**
- * Sets the external file attributes.
- *
- * @param int $externalAttributes the external file attributes
- *
- * @return ZipEntry
- */
- public function setExternalAttributes($externalAttributes)
- {
- $known = $externalAttributes !== self::UNKNOWN;
- if ($known) {
- $this->externalAttributes = $externalAttributes;
- } else {
- $this->externalAttributes = 0;
- }
- $this->setInit(self::BIT_EXTERNAL_ATTR, $known);
- return $this;
- }
- /**
- * Returns true if and only if this ZIP entry represents a directory entry
- * (i.e. end with '/').
- *
- * @return bool
- */
- public function isDirectory()
- {
- return StringUtil::endsWith($this->name, '/');
- }
- /**
- * @return ExtraFieldsCollection
- */
- public function &getExtraFieldsCollection()
- {
- return $this->extraFieldsCollection;
- }
- /**
- * Returns a protective copy of the serialized Extra Fields.
- *
- * @throws ZipException
- *
- * @return string
- */
- public function getExtra()
- {
- return ExtraFieldsFactory::createSerializedData($this->extraFieldsCollection);
- }
- /**
- * Sets the serialized Extra Fields by making a protective copy.
- * Note that this method parses the serialized Extra Fields according to
- * the ZIP File Format Specification and limits its size to 64 KB.
- * Therefore, this property cannot not be used to hold arbitrary
- * (application) data.
- * Consider storing such data in a separate entry instead.
- *
- * @param string $data the byte array holding the serialized Extra Fields
- *
- * @throws ZipException if the serialized Extra Fields exceed 64 KB
- */
- public function setExtra($data)
- {
- $this->extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($data, $this);
- }
- /**
- * Returns comment entry.
- *
- * @return string
- */
- public function getComment()
- {
- return $this->comment !== null ? $this->comment : '';
- }
- /**
- * Set entry comment.
- *
- * @param $comment
- *
- * @throws ZipException
- *
- * @return ZipEntry
- */
- public function setComment($comment)
- {
- if ($comment !== null) {
- $commentLength = \strlen($comment);
- if ($commentLength < 0x0000 || $commentLength > 0xffff) {
- throw new ZipException('Comment too long');
- }
- }
- $this->setGeneralPurposeBitFlag(self::GPBF_UTF8, true);
- $this->comment = $comment;
- return $this;
- }
- /**
- * @return bool
- */
- public function isDataDescriptorRequired()
- {
- return ($this->getCrc() | $this->getCompressedSize() | $this->getSize()) === self::UNKNOWN;
- }
- /**
- * Return crc32 content or 0 for WinZip AES v2.
- *
- * @return int
- */
- public function getCrc()
- {
- return $this->crc;
- }
- /**
- * Set crc32 content.
- *
- * @param int $crc
- *
- * @return ZipEntry
- */
- public function setCrc($crc)
- {
- $this->crc = $crc;
- $this->setInit(self::BIT_CRC, true);
- return $this;
- }
- /**
- * @return string
- */
- public function getPassword()
- {
- return $this->password;
- }
- /**
- * Set password and encryption method from entry.
- *
- * @param string $password
- * @param int|null $encryptionMethod
- *
- * @throws ZipException
- *
- * @return ZipEntry
- */
- public function setPassword($password, $encryptionMethod = null)
- {
- $this->password = $password;
- if ($encryptionMethod !== null) {
- $this->setEncryptionMethod($encryptionMethod);
- }
- if (!empty($this->password)) {
- $this->setEncrypted(true);
- } else {
- $this->disableEncryption();
- }
- return $this;
- }
- /**
- * @return int
- */
- public function getEncryptionMethod()
- {
- return $this->encryptionMethod;
- }
- /**
- * Set encryption method.
- *
- * @param int $encryptionMethod
- *
- * @throws ZipException
- *
- * @return ZipEntry
- *
- * @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
- * @see ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
- * @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
- * @see ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192
- */
- public function setEncryptionMethod($encryptionMethod)
- {
- if ($encryptionMethod !== null) {
- if (
- $encryptionMethod !== ZipFileInterface::ENCRYPTION_METHOD_TRADITIONAL
- && $encryptionMethod !== ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128
- && $encryptionMethod !== ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192
- && $encryptionMethod !== ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
- ) {
- throw new ZipException('Invalid encryption method');
- }
- $this->encryptionMethod = $encryptionMethod;
- }
- return $this;
- }
- /**
- * @return int
- */
- public function getCompressionLevel()
- {
- return $this->compressionLevel;
- }
- /**
- * @param int $compressionLevel
- *
- * @return ZipEntry
- */
- public function setCompressionLevel($compressionLevel = ZipFileInterface::LEVEL_DEFAULT_COMPRESSION)
- {
- if ($compressionLevel < ZipFileInterface::LEVEL_DEFAULT_COMPRESSION ||
- $compressionLevel > ZipFileInterface::LEVEL_BEST_COMPRESSION
- ) {
- throw new InvalidArgumentException(
- 'Invalid compression level. Minimum level ' .
- ZipFileInterface::LEVEL_DEFAULT_COMPRESSION . '. Maximum level ' . ZipFileInterface::LEVEL_BEST_COMPRESSION
- );
- }
- $this->compressionLevel = $compressionLevel;
- return $this;
- }
- /**
- * Clone extra fields.
- */
- public function __clone()
- {
- $this->extraFieldsCollection = clone $this->extraFieldsCollection;
- }
- }
|