ZipAbstractEntry.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. <?php
  2. namespace PhpZip\Model\Entry;
  3. use PhpZip\Exception\InvalidArgumentException;
  4. use PhpZip\Exception\ZipException;
  5. use PhpZip\Extra\DefaultExtraField;
  6. use PhpZip\Extra\ExtraField;
  7. use PhpZip\Extra\ExtraFields;
  8. use PhpZip\Extra\WinZipAesEntryExtraField;
  9. use PhpZip\Model\CentralDirectory;
  10. use PhpZip\Model\ZipEntry;
  11. use PhpZip\Util\DateTimeConverter;
  12. use PhpZip\Util\PackUtil;
  13. use PhpZip\ZipFile;
  14. /**
  15. * Abstract ZIP entry.
  16. *
  17. * @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
  18. * @author Ne-Lexa alexey@nelexa.ru
  19. * @license MIT
  20. */
  21. abstract class ZipAbstractEntry implements ZipEntry
  22. {
  23. /**
  24. * @var CentralDirectory
  25. */
  26. private $centralDirectory;
  27. /**
  28. * @var int Bit flags for init state.
  29. */
  30. private $init;
  31. /**
  32. * @var string Entry name (filename in archive)
  33. */
  34. private $name;
  35. /**
  36. * @var int Made by platform
  37. */
  38. private $platform;
  39. /**
  40. * @var int
  41. */
  42. private $versionNeededToExtract = 20;
  43. /**
  44. * @var int
  45. */
  46. private $general;
  47. /**
  48. * @var int Compression method
  49. */
  50. private $method;
  51. /**
  52. * @var int Dos time
  53. */
  54. private $dosTime;
  55. /**
  56. * @var int Crc32
  57. */
  58. private $crc;
  59. /**
  60. * @var int Compressed size
  61. */
  62. private $compressedSize = self::UNKNOWN;
  63. /**
  64. * @var int Uncompressed size
  65. */
  66. private $size = self::UNKNOWN;
  67. /**
  68. * @var int External attributes
  69. */
  70. private $externalAttributes;
  71. /**
  72. * @var int Relative Offset Of Local File Header.
  73. */
  74. private $offset = self::UNKNOWN;
  75. /**
  76. * The map of Extra Fields.
  77. * Maps from Header ID [Integer] to Extra Field [ExtraField].
  78. * Should be null or may be empty if no Extra Fields are used.
  79. *
  80. * @var ExtraFields
  81. */
  82. private $fields;
  83. /**
  84. * @var string Comment field.
  85. */
  86. private $comment;
  87. /**
  88. * @var string Entry password for read or write encryption data.
  89. */
  90. private $password;
  91. /**
  92. * Encryption method.
  93. * @see ZipFile::ENCRYPTION_METHOD_TRADITIONAL
  94. * @see ZipFile::ENCRYPTION_METHOD_WINZIP_AES
  95. * @var int
  96. */
  97. private $encryptionMethod = ZipFile::ENCRYPTION_METHOD_TRADITIONAL;
  98. /**
  99. * @var int
  100. */
  101. private $compressionLevel = ZipFile::LEVEL_DEFAULT_COMPRESSION;
  102. /**
  103. * @param int $mask
  104. * @return bool
  105. */
  106. private function isInit($mask)
  107. {
  108. return 0 !== ($this->init & $mask);
  109. }
  110. /**
  111. * @param int $mask
  112. * @param bool $init
  113. */
  114. private function setInit($mask, $init)
  115. {
  116. if ($init) {
  117. $this->init |= $mask;
  118. } else {
  119. $this->init &= ~$mask;
  120. }
  121. }
  122. /**
  123. * @return CentralDirectory
  124. */
  125. public function getCentralDirectory()
  126. {
  127. return $this->centralDirectory;
  128. }
  129. /**
  130. * @param CentralDirectory $centralDirectory
  131. * @return ZipEntry
  132. */
  133. public function setCentralDirectory(CentralDirectory $centralDirectory)
  134. {
  135. $this->centralDirectory = $centralDirectory;
  136. return $this;
  137. }
  138. /**
  139. * Returns the ZIP entry name.
  140. *
  141. * @return string
  142. */
  143. public function getName()
  144. {
  145. return $this->name;
  146. }
  147. /**
  148. * Set entry name.
  149. *
  150. * @param string $name New entry name
  151. * @return ZipEntry
  152. * @throws ZipException
  153. */
  154. public function setName($name)
  155. {
  156. $length = strlen($name);
  157. if (0x0000 > $length || $length > 0xffff) {
  158. throw new ZipException('Illegal zip entry name parameter');
  159. }
  160. $this->setGeneralPurposeBitFlag(self::GPBF_UTF8, true);
  161. $this->name = $name;
  162. return $this;
  163. }
  164. /**
  165. * @return int Get platform
  166. */
  167. public function getPlatform()
  168. {
  169. return $this->isInit(self::BIT_PLATFORM) ? $this->platform & 0xffff : self::UNKNOWN;
  170. }
  171. /**
  172. * Set platform
  173. *
  174. * @param int $platform
  175. * @return ZipEntry
  176. * @throws ZipException
  177. */
  178. public function setPlatform($platform)
  179. {
  180. $known = self::UNKNOWN !== $platform;
  181. if ($known) {
  182. if (0x00 > $platform || $platform > 0xff) {
  183. throw new ZipException("Platform out of range");
  184. }
  185. $this->platform = $platform;
  186. } else {
  187. $this->platform = 0;
  188. }
  189. $this->setInit(self::BIT_PLATFORM, $known);
  190. return $this;
  191. }
  192. /**
  193. * Version needed to extract.
  194. *
  195. * @return int
  196. */
  197. public function getVersionNeededToExtract()
  198. {
  199. return $this->versionNeededToExtract;
  200. }
  201. /**
  202. * Set version needed to extract.
  203. *
  204. * @param int $version
  205. * @return ZipEntry
  206. */
  207. public function setVersionNeededToExtract($version)
  208. {
  209. $this->versionNeededToExtract = $version;
  210. return $this;
  211. }
  212. /**
  213. * @return bool
  214. */
  215. public function isZip64ExtensionsRequired()
  216. {
  217. // Offset MUST be considered in decision about ZIP64 format - see
  218. // description of Data Descriptor in ZIP File Format Specification!
  219. return 0xffffffff <= $this->getCompressedSize()
  220. || 0xffffffff <= $this->getSize()
  221. || 0xffffffff <= $this->getOffset();
  222. }
  223. /**
  224. * Returns the compressed size of this entry.
  225. *
  226. * @see int
  227. */
  228. public function getCompressedSize()
  229. {
  230. return $this->compressedSize;
  231. }
  232. /**
  233. * Sets the compressed size of this entry.
  234. *
  235. * @param int $compressedSize The Compressed Size.
  236. * @return ZipEntry
  237. * @throws ZipException
  238. */
  239. public function setCompressedSize($compressedSize)
  240. {
  241. if (self::UNKNOWN != $compressedSize) {
  242. if (0 > $compressedSize || $compressedSize > 0x7fffffffffffffff) {
  243. throw new ZipException("Compressed size out of range - " . $this->name);
  244. }
  245. }
  246. $this->compressedSize = $compressedSize;
  247. return $this;
  248. }
  249. /**
  250. * Returns the uncompressed size of this entry.
  251. *
  252. * @see ZipEntry::setCompressedSize
  253. */
  254. public function getSize()
  255. {
  256. return $this->size;
  257. }
  258. /**
  259. * Sets the uncompressed size of this entry.
  260. *
  261. * @param int $size The (Uncompressed) Size.
  262. * @return ZipEntry
  263. * @throws ZipException
  264. */
  265. public function setSize($size)
  266. {
  267. if (self::UNKNOWN != $size) {
  268. if (0 > $size || $size > 0x7fffffffffffffff) {
  269. throw new ZipException("Uncompressed Size out of range - " . $this->name);
  270. }
  271. }
  272. $this->size = $size;
  273. return $this;
  274. }
  275. /**
  276. * Return relative Offset Of Local File Header.
  277. *
  278. * @return int
  279. */
  280. public function getOffset()
  281. {
  282. return $this->offset;
  283. }
  284. /**
  285. * @param int $offset
  286. * @return ZipEntry
  287. * @throws ZipException
  288. */
  289. public function setOffset($offset)
  290. {
  291. if (0 > $offset || $offset > 0x7fffffffffffffff) {
  292. throw new ZipException("Offset out of range - " . $this->name);
  293. }
  294. $this->offset = $offset;
  295. return $this;
  296. }
  297. /**
  298. * Returns true if and only if this ZIP entry represents a directory entry
  299. * (i.e. end with '/').
  300. *
  301. * @return bool
  302. */
  303. public function isDirectory()
  304. {
  305. return $this->name[strlen($this->name) - 1] === '/';
  306. }
  307. /**
  308. * Returns the General Purpose Bit Flags.
  309. *
  310. * @return bool
  311. */
  312. public function getGeneralPurposeBitFlags()
  313. {
  314. return $this->general & 0xffff;
  315. }
  316. /**
  317. * Sets the General Purpose Bit Flags.
  318. *
  319. * @var int general
  320. * @return ZipEntry
  321. * @throws ZipException
  322. */
  323. public function setGeneralPurposeBitFlags($general)
  324. {
  325. if (0x0000 > $general || $general > 0xffff) {
  326. throw new ZipException('general out of range');
  327. }
  328. $this->general = $general;
  329. return $this;
  330. }
  331. /**
  332. * Returns the indexed General Purpose Bit Flag.
  333. *
  334. * @param int $mask
  335. * @return bool
  336. */
  337. public function getGeneralPurposeBitFlag($mask)
  338. {
  339. return 0 !== ($this->general & $mask);
  340. }
  341. /**
  342. * Sets the indexed General Purpose Bit Flag.
  343. *
  344. * @param int $mask
  345. * @param bool $bit
  346. * @return ZipEntry
  347. */
  348. public function setGeneralPurposeBitFlag($mask, $bit)
  349. {
  350. if ($bit)
  351. $this->general |= $mask;
  352. else
  353. $this->general &= ~$mask;
  354. return $this;
  355. }
  356. /**
  357. * Returns true if and only if this ZIP entry is encrypted.
  358. *
  359. * @return bool
  360. */
  361. public function isEncrypted()
  362. {
  363. return $this->getGeneralPurposeBitFlag(self::GPBF_ENCRYPTED);
  364. }
  365. /**
  366. * Sets the encryption property to false and removes any other
  367. * encryption artifacts.
  368. *
  369. * @return ZipEntry
  370. */
  371. public function clearEncryption()
  372. {
  373. $this->setEncrypted(false);
  374. if (null !== $this->fields) {
  375. $field = $this->fields->get(WinZipAesEntryExtraField::getHeaderId());
  376. if (null !== $field) {
  377. /**
  378. * @var WinZipAesEntryExtraField $field
  379. */
  380. $this->removeExtraField(WinZipAesEntryExtraField::getHeaderId());
  381. }
  382. if (self::METHOD_WINZIP_AES === $this->getMethod()) {
  383. $this->setMethod(null === $field ? self::UNKNOWN : $field->getMethod());
  384. }
  385. }
  386. $this->password = null;
  387. return $this;
  388. }
  389. /**
  390. * Sets the encryption flag for this ZIP entry.
  391. *
  392. * @param bool $encrypted
  393. * @return ZipEntry
  394. */
  395. public function setEncrypted($encrypted)
  396. {
  397. $this->setGeneralPurposeBitFlag(self::GPBF_ENCRYPTED, $encrypted);
  398. return $this;
  399. }
  400. /**
  401. * Returns the compression method for this entry.
  402. *
  403. * @return int
  404. */
  405. public function getMethod()
  406. {
  407. return $this->isInit(self::BIT_METHOD) ? $this->method & 0xffff : self::UNKNOWN;
  408. }
  409. /**
  410. * Sets the compression method for this entry.
  411. *
  412. * @param int $method
  413. * @return ZipEntry
  414. * @throws ZipException If method is not STORED, DEFLATED, BZIP2 or UNKNOWN.
  415. */
  416. public function setMethod($method)
  417. {
  418. if (0x0000 > $method || $method > 0xffff) {
  419. throw new ZipException('method out of range');
  420. }
  421. switch ($method) {
  422. case self::METHOD_WINZIP_AES:
  423. $this->method = $method;
  424. $this->setInit(self::BIT_METHOD, true);
  425. $this->setEncryptionMethod(ZipFile::ENCRYPTION_METHOD_WINZIP_AES);
  426. break;
  427. case ZipFile::METHOD_STORED:
  428. case ZipFile::METHOD_DEFLATED:
  429. case ZipFile::METHOD_BZIP2:
  430. $this->method = $method;
  431. $this->setInit(self::BIT_METHOD, true);
  432. break;
  433. case self::UNKNOWN:
  434. $this->method = ZipFile::METHOD_STORED;
  435. $this->setInit(self::BIT_METHOD, false);
  436. break;
  437. default:
  438. throw new ZipException($this->name . " (unsupported compression method $method)");
  439. }
  440. return $this;
  441. }
  442. /**
  443. * Get Unix Timestamp
  444. *
  445. * @return int
  446. */
  447. public function getTime()
  448. {
  449. if (!$this->isInit(self::BIT_DATE_TIME)) {
  450. return self::UNKNOWN;
  451. }
  452. return DateTimeConverter::toUnixTimestamp($this->getDosTime());
  453. }
  454. /**
  455. * Set time from unix timestamp.
  456. *
  457. * @param int $unixTimestamp
  458. * @return ZipEntry
  459. */
  460. public function setTime($unixTimestamp)
  461. {
  462. $known = self::UNKNOWN != $unixTimestamp;
  463. if ($known) {
  464. $this->dosTime = DateTimeConverter::toDosTime($unixTimestamp);
  465. } else {
  466. $this->dosTime = 0;
  467. }
  468. $this->setInit(self::BIT_DATE_TIME, $known);
  469. return $this;
  470. }
  471. /**
  472. * Get Dos Time
  473. *
  474. * @return int
  475. */
  476. public function getDosTime()
  477. {
  478. return $this->dosTime & 0xffffffff;
  479. }
  480. /**
  481. * Set Dos Time
  482. * @param int $dosTime
  483. * @throws ZipException
  484. */
  485. public function setDosTime($dosTime)
  486. {
  487. if (0x00000000 > $dosTime || $dosTime > 0xffffffff) {
  488. throw new ZipException('DosTime out of range');
  489. }
  490. $this->dosTime = $dosTime;
  491. $this->setInit(self::BIT_DATE_TIME, true);
  492. }
  493. /**
  494. * Returns the external file attributes.
  495. *
  496. * @return int The external file attributes.
  497. */
  498. public function getExternalAttributes()
  499. {
  500. if (!$this->isInit(self::BIT_EXTERNAL_ATTR)) {
  501. return $this->isDirectory() ? 0x10 : 0;
  502. }
  503. return $this->externalAttributes & 0xffffffff;
  504. }
  505. /**
  506. * Sets the external file attributes.
  507. *
  508. * @param int $externalAttributes the external file attributes.
  509. * @return ZipEntry
  510. * @throws ZipException
  511. */
  512. public function setExternalAttributes($externalAttributes)
  513. {
  514. $known = self::UNKNOWN != $externalAttributes;
  515. if ($known) {
  516. if (0x00000000 > $externalAttributes || $externalAttributes > 0xffffffff) {
  517. throw new ZipException("external file attributes out of range - " . $this->name);
  518. }
  519. $this->externalAttributes = $externalAttributes;
  520. } else {
  521. $this->externalAttributes = 0;
  522. }
  523. $this->setInit(self::BIT_EXTERNAL_ATTR, $known);
  524. return $this;
  525. }
  526. /**
  527. * Return extra field from header id.
  528. *
  529. * @param int $headerId
  530. * @return ExtraField|null
  531. */
  532. public function getExtraField($headerId)
  533. {
  534. return $this->fields === null ? null : $this->fields->get($headerId);
  535. }
  536. /**
  537. * Add extra field.
  538. *
  539. * @param ExtraField $field
  540. * @return ExtraField
  541. * @throws ZipException
  542. */
  543. public function addExtraField($field)
  544. {
  545. if (null === $field) {
  546. throw new ZipException("extra field null");
  547. }
  548. if (null === $this->fields) {
  549. $this->fields = new ExtraFields();
  550. }
  551. return $this->fields->add($field);
  552. }
  553. /**
  554. * Return exists extra field from header id.
  555. *
  556. * @param int $headerId
  557. * @return bool
  558. */
  559. public function hasExtraField($headerId)
  560. {
  561. return $this->fields === null ? false : $this->fields->has($headerId);
  562. }
  563. /**
  564. * Remove extra field from header id.
  565. *
  566. * @param int $headerId
  567. * @return ExtraField|null
  568. */
  569. public function removeExtraField($headerId)
  570. {
  571. return null !== $this->fields ? $this->fields->remove($headerId) : null;
  572. }
  573. /**
  574. * Returns a protective copy of the serialized Extra Fields.
  575. *
  576. * @return string A new byte array holding the serialized Extra Fields.
  577. * null is never returned.
  578. */
  579. public function getExtra()
  580. {
  581. return $this->getExtraFields(false);
  582. }
  583. /**
  584. * @param bool $zip64
  585. * @return string
  586. * @throws ZipException
  587. */
  588. private function getExtraFields($zip64)
  589. {
  590. if ($zip64) {
  591. $field = $this->composeZip64ExtraField();
  592. if (null !== $field) {
  593. if (null === $this->fields) {
  594. $this->fields = new ExtraFields();
  595. }
  596. $this->fields->add($field);
  597. }
  598. } else {
  599. assert(null === $this->fields || null === $this->fields->get(ExtraField::ZIP64_HEADER_ID));
  600. }
  601. return null === $this->fields ? null : $this->fields->getExtra();
  602. }
  603. /**
  604. * Composes a ZIP64 Extended Information Extra Field from the properties
  605. * of this entry.
  606. * If no ZIP64 Extended Information Extra Field is required it is removed
  607. * from the collection of Extra Fields.
  608. *
  609. * @return ExtraField|null
  610. */
  611. private function composeZip64ExtraField()
  612. {
  613. $handle = fopen('php://memory', 'r+b');
  614. // Write out Uncompressed Size.
  615. $size = $this->getSize();
  616. if (0xffffffff <= $size) {
  617. fwrite($handle, PackUtil::packLongLE($size));
  618. }
  619. // Write out Compressed Size.
  620. $compressedSize = $this->getCompressedSize();
  621. if (0xffffffff <= $compressedSize) {
  622. fwrite($handle, PackUtil::packLongLE($compressedSize));
  623. }
  624. // Write out Relative Header Offset.
  625. $offset = $this->getOffset();
  626. if (0xffffffff <= $offset) {
  627. fwrite($handle, PackUtil::packLongLE($offset));
  628. }
  629. // Create ZIP64 Extended Information Extra Field from serialized data.
  630. $field = null;
  631. if (ftell($handle) > 0) {
  632. $field = new DefaultExtraField(ExtraField::ZIP64_HEADER_ID);
  633. $field->readFrom($handle, 0, ftell($handle));
  634. } else {
  635. $field = null;
  636. }
  637. return $field;
  638. }
  639. /**
  640. * Sets the serialized Extra Fields by making a protective copy.
  641. * Note that this method parses the serialized Extra Fields according to
  642. * the ZIP File Format Specification and limits its size to 64 KB.
  643. * Therefore, this property cannot not be used to hold arbitrary
  644. * (application) data.
  645. * Consider storing such data in a separate entry instead.
  646. *
  647. * @param string $data The byte array holding the serialized Extra Fields.
  648. * @throws ZipException if the serialized Extra Fields exceed 64 KB
  649. * @return ZipEntry
  650. * or do not conform to the ZIP File Format Specification
  651. */
  652. public function setExtra($data)
  653. {
  654. if (null !== $data) {
  655. $length = strlen($data);
  656. if (0x0000 > $length || $length > 0xffff) {
  657. throw new ZipException("Extra Fields too large");
  658. }
  659. }
  660. if (null === $data || strlen($data) <= 0) {
  661. $this->fields = null;
  662. } else {
  663. $this->setExtraFields($data, false);
  664. }
  665. return $this;
  666. }
  667. /**
  668. * @param string $data
  669. * @param bool $zip64
  670. */
  671. private function setExtraFields($data, $zip64)
  672. {
  673. if (null === $this->fields) {
  674. $this->fields = new ExtraFields();
  675. }
  676. $handle = fopen('php://memory', 'r+b');
  677. fwrite($handle, $data);
  678. rewind($handle);
  679. $this->fields->readFrom($handle, 0, strlen($data));
  680. $result = false;
  681. if ($zip64) {
  682. $result = $this->parseZip64ExtraField();
  683. }
  684. if ($result) {
  685. $this->fields->remove(ExtraField::ZIP64_HEADER_ID);
  686. if ($this->fields->size() <= 0) {
  687. if (0 !== $this->fields->size()) {
  688. $this->fields = null;
  689. }
  690. }
  691. }
  692. fclose($handle);
  693. }
  694. /**
  695. * Parses the properties of this entry from the ZIP64 Extended Information
  696. * Extra Field, if present.
  697. * The ZIP64 Extended Information Extra Field is not removed.
  698. *
  699. * @return bool
  700. * @throws ZipException
  701. */
  702. private function parseZip64ExtraField()
  703. {
  704. if (null === $this->fields) {
  705. return false;
  706. }
  707. $ef = $this->fields->get(ExtraField::ZIP64_HEADER_ID);
  708. if (null === $ef) {
  709. return false;
  710. }
  711. $dataBlockHandle = $ef->getDataBlock();
  712. $off = 0;
  713. // Read in Uncompressed Size.
  714. $size = $this->getSize();
  715. if (0xffffffff <= $size) {
  716. assert(0xffffffff === $size);
  717. fseek($dataBlockHandle, $off);
  718. $this->setSize(PackUtil::unpackLongLE(fread($dataBlockHandle, 8)));
  719. $off += 8;
  720. }
  721. // Read in Compressed Size.
  722. $compressedSize = $this->getCompressedSize();
  723. if (0xffffffff <= $compressedSize) {
  724. assert(0xffffffff === $compressedSize);
  725. fseek($dataBlockHandle, $off);
  726. $this->setCompressedSize(PackUtil::unpackLongLE(fread($dataBlockHandle, 8)));
  727. $off += 8;
  728. }
  729. // Read in Relative Header Offset.
  730. $offset = $this->getOffset();
  731. if (0xffffffff <= $offset) {
  732. assert(0xffffffff, $offset);
  733. fseek($dataBlockHandle, $off);
  734. $this->setOffset(PackUtil::unpackLongLE(fread($dataBlockHandle, 8)));
  735. //$off += 8;
  736. }
  737. fclose($dataBlockHandle);
  738. return true;
  739. }
  740. /**
  741. * Returns comment entry
  742. *
  743. * @return string
  744. */
  745. public function getComment()
  746. {
  747. return null != $this->comment ? $this->comment : "";
  748. }
  749. /**
  750. * Set entry comment.
  751. *
  752. * @param $comment
  753. * @return ZipEntry
  754. * @throws ZipException
  755. */
  756. public function setComment($comment)
  757. {
  758. if (null !== $comment) {
  759. $commentLength = strlen($comment);
  760. if (0x0000 > $commentLength || $commentLength > 0xffff) {
  761. throw new ZipException("Comment too long");
  762. }
  763. }
  764. $this->setGeneralPurposeBitFlag(self::GPBF_UTF8, true);
  765. $this->comment = $comment;
  766. return $this;
  767. }
  768. /**
  769. * @return bool
  770. */
  771. public function isDataDescriptorRequired()
  772. {
  773. return self::UNKNOWN == ($this->getCrc() | $this->getCompressedSize() | $this->getSize());
  774. }
  775. /**
  776. * Return crc32 content or 0 for WinZip AES v2
  777. *
  778. * @return int
  779. */
  780. public function getCrc()
  781. {
  782. return $this->crc & 0xffffffff;
  783. }
  784. /**
  785. * Set crc32 content.
  786. *
  787. * @param int $crc
  788. * @return ZipEntry
  789. * @throws ZipException
  790. */
  791. public function setCrc($crc)
  792. {
  793. if (0x00000000 > $crc || $crc > 0xffffffff) {
  794. throw new ZipException("CRC-32 out of range - " . $this->name);
  795. }
  796. $this->crc = $crc;
  797. $this->setInit(self::BIT_CRC, true);
  798. return $this;
  799. }
  800. /**
  801. * @return string
  802. */
  803. public function getPassword()
  804. {
  805. return $this->password;
  806. }
  807. /**
  808. * Set password and encryption method from entry
  809. *
  810. * @param string $password
  811. * @param null|int $encryptionMethod
  812. * @return ZipEntry
  813. */
  814. public function setPassword($password, $encryptionMethod = null)
  815. {
  816. $this->password = $password;
  817. if (null !== $encryptionMethod) {
  818. $this->setEncryptionMethod($encryptionMethod);
  819. }
  820. $this->setEncrypted(!empty($this->password));
  821. return $this;
  822. }
  823. /**
  824. * @return int
  825. */
  826. public function getEncryptionMethod()
  827. {
  828. return $this->encryptionMethod;
  829. }
  830. /**
  831. * @return int
  832. */
  833. public function getCompressionLevel()
  834. {
  835. return $this->compressionLevel;
  836. }
  837. /**
  838. * @param int $compressionLevel
  839. * @return ZipEntry
  840. * @throws InvalidArgumentException
  841. */
  842. public function setCompressionLevel($compressionLevel = ZipFile::LEVEL_DEFAULT_COMPRESSION)
  843. {
  844. if ($compressionLevel < ZipFile::LEVEL_DEFAULT_COMPRESSION ||
  845. $compressionLevel > ZipFile::LEVEL_BEST_COMPRESSION
  846. ) {
  847. throw new InvalidArgumentException('Invalid compression level. Minimum level ' .
  848. ZipFile::LEVEL_DEFAULT_COMPRESSION . '. Maximum level ' . ZipFile::LEVEL_BEST_COMPRESSION);
  849. }
  850. $this->compressionLevel = $compressionLevel;
  851. return $this;
  852. }
  853. /**
  854. * Set encryption method
  855. *
  856. * @see ZipFile::ENCRYPTION_METHOD_TRADITIONAL
  857. * @see ZipFile::ENCRYPTION_METHOD_WINZIP_AES
  858. *
  859. * @param int $encryptionMethod
  860. * @return ZipEntry
  861. * @throws ZipException
  862. */
  863. public function setEncryptionMethod($encryptionMethod)
  864. {
  865. if (
  866. ZipFile::ENCRYPTION_METHOD_TRADITIONAL !== $encryptionMethod &&
  867. ZipFile::ENCRYPTION_METHOD_WINZIP_AES !== $encryptionMethod
  868. ) {
  869. throw new ZipException('Invalid encryption method');
  870. }
  871. $this->encryptionMethod = $encryptionMethod;
  872. $this->setEncrypted(true);
  873. return $this;
  874. }
  875. /**
  876. * Clone extra fields
  877. */
  878. function __clone()
  879. {
  880. $this->fields = $this->fields !== null ? clone $this->fields : null;
  881. }
  882. }