OldUnixExtraField.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of the nelexa/zip package.
  5. * (c) Ne-Lexa <https://github.com/Ne-Lexa/php-zip>
  6. * For the full copyright and license information, please view the LICENSE
  7. * file that was distributed with this source code.
  8. */
  9. namespace PhpZip\Model\Extra\Fields;
  10. use PhpZip\Model\Extra\ZipExtraField;
  11. use PhpZip\Model\ZipEntry;
  12. /**
  13. * Info-ZIP Unix Extra Field (type 1):
  14. * ==================================.
  15. *
  16. * The following is the layout of the old Info-ZIP extra block for
  17. * Unix. It has been replaced by the extended-timestamp extra block
  18. * (0x5455) and the Unix type 2 extra block (0x7855).
  19. * (Last Revision 19970118)
  20. *
  21. * Local-header version:
  22. *
  23. * Value Size Description
  24. * ----- ---- -----------
  25. * (Unix1) 0x5855 Short tag for this extra block type ("UX")
  26. * TSize Short total data size for this block
  27. * AcTime Long time of last access (UTC/GMT)
  28. * ModTime Long time of last modification (UTC/GMT)
  29. * UID Short Unix user ID (optional)
  30. * GID Short Unix group ID (optional)
  31. *
  32. * Central-header version:
  33. *
  34. * Value Size Description
  35. * ----- ---- -----------
  36. * (Unix1) 0x5855 Short tag for this extra block type ("UX")
  37. * TSize Short total data size for this block
  38. * AcTime Long time of last access (GMT/UTC)
  39. * ModTime Long time of last modification (GMT/UTC)
  40. *
  41. * The file access and modification times are in standard Unix signed-
  42. * long format, indicating the number of seconds since 1 January 1970
  43. * 00:00:00. The times are relative to Coordinated Universal Time
  44. * (UTC), also sometimes referred to as Greenwich Mean Time (GMT). To
  45. * convert to local time, the software must know the local timezone
  46. * offset from UTC/GMT. The modification time may be used by non-Unix
  47. * systems to support inter-timezone freshening and updating of zip
  48. * archives.
  49. *
  50. * The local-header extra block may optionally contain UID and GID
  51. * info for the file. The local-header TSize value is the only
  52. * indication of this. Note that Unix UIDs and GIDs are usually
  53. * specific to a particular machine, and they generally require root
  54. * access to restore.
  55. *
  56. * This extra field type is obsolete, but it has been in use since
  57. * mid-1994. Therefore future archiving software should continue to
  58. * support it.
  59. */
  60. final class OldUnixExtraField implements ZipExtraField
  61. {
  62. /** @var int Header id */
  63. public const HEADER_ID = 0x5855;
  64. /** @var int|null Access timestamp */
  65. private ?int $accessTime;
  66. /** @var int|null Modify timestamp */
  67. private ?int $modifyTime;
  68. /** @var int|null User id */
  69. private ?int $uid;
  70. /** @var int|null Group id */
  71. private ?int $gid;
  72. public function __construct(?int $accessTime, ?int $modifyTime, ?int $uid, ?int $gid)
  73. {
  74. $this->accessTime = $accessTime;
  75. $this->modifyTime = $modifyTime;
  76. $this->uid = $uid;
  77. $this->gid = $gid;
  78. }
  79. /**
  80. * Returns the Header ID (type) of this Extra Field.
  81. * The Header ID is an unsigned short integer (two bytes)
  82. * which must be constant during the life cycle of this object.
  83. */
  84. public function getHeaderId(): int
  85. {
  86. return self::HEADER_ID;
  87. }
  88. /**
  89. * Populate data from this array as if it was in local file data.
  90. *
  91. * @param string $buffer the buffer to read data from
  92. * @param ZipEntry|null $entry optional zip entry
  93. *
  94. * @return OldUnixExtraField
  95. */
  96. public static function unpackLocalFileData(string $buffer, ?ZipEntry $entry = null): self
  97. {
  98. $length = \strlen($buffer);
  99. $accessTime = $modifyTime = $uid = $gid = null;
  100. if ($length >= 4) {
  101. $accessTime = unpack('V', $buffer)[1];
  102. }
  103. if ($length >= 8) {
  104. $modifyTime = unpack('V', substr($buffer, 4, 4))[1];
  105. }
  106. if ($length >= 10) {
  107. $uid = unpack('v', substr($buffer, 8, 2))[1];
  108. }
  109. if ($length >= 12) {
  110. $gid = unpack('v', substr($buffer, 10, 2))[1];
  111. }
  112. return new self($accessTime, $modifyTime, $uid, $gid);
  113. }
  114. /**
  115. * Populate data from this array as if it was in central directory data.
  116. *
  117. * @param string $buffer the buffer to read data from
  118. * @param ZipEntry|null $entry optional zip entry
  119. *
  120. * @return OldUnixExtraField
  121. */
  122. public static function unpackCentralDirData(string $buffer, ?ZipEntry $entry = null): self
  123. {
  124. $length = \strlen($buffer);
  125. $accessTime = $modifyTime = null;
  126. if ($length >= 4) {
  127. $accessTime = unpack('V', $buffer)[1];
  128. }
  129. if ($length >= 8) {
  130. $modifyTime = unpack('V', substr($buffer, 4, 4))[1];
  131. }
  132. return new self($accessTime, $modifyTime, null, null);
  133. }
  134. /**
  135. * The actual data to put into local file data - without Header-ID
  136. * or length specifier.
  137. *
  138. * @return string the data
  139. */
  140. public function packLocalFileData(): string
  141. {
  142. $data = '';
  143. if ($this->accessTime !== null) {
  144. $data .= pack('V', $this->accessTime);
  145. if ($this->modifyTime !== null) {
  146. $data .= pack('V', $this->modifyTime);
  147. if ($this->uid !== null) {
  148. $data .= pack('v', $this->uid);
  149. if ($this->gid !== null) {
  150. $data .= pack('v', $this->gid);
  151. }
  152. }
  153. }
  154. }
  155. return $data;
  156. }
  157. /**
  158. * The actual data to put into central directory - without Header-ID or
  159. * length specifier.
  160. *
  161. * @return string the data
  162. */
  163. public function packCentralDirData(): string
  164. {
  165. $data = '';
  166. if ($this->accessTime !== null) {
  167. $data .= pack('V', $this->accessTime);
  168. if ($this->modifyTime !== null) {
  169. $data .= pack('V', $this->modifyTime);
  170. }
  171. }
  172. return $data;
  173. }
  174. public function getAccessTime(): ?int
  175. {
  176. return $this->accessTime;
  177. }
  178. public function setAccessTime(?int $accessTime): void
  179. {
  180. $this->accessTime = $accessTime;
  181. }
  182. public function getAccessDateTime(): ?\DateTimeInterface
  183. {
  184. try {
  185. return $this->accessTime === null ? null
  186. : new \DateTimeImmutable('@' . $this->accessTime);
  187. } catch (\Exception $e) {
  188. return null;
  189. }
  190. }
  191. public function getModifyTime(): ?int
  192. {
  193. return $this->modifyTime;
  194. }
  195. public function setModifyTime(?int $modifyTime): void
  196. {
  197. $this->modifyTime = $modifyTime;
  198. }
  199. public function getModifyDateTime(): ?\DateTimeInterface
  200. {
  201. try {
  202. return $this->modifyTime === null ? null
  203. : new \DateTimeImmutable('@' . $this->modifyTime);
  204. } catch (\Exception $e) {
  205. return null;
  206. }
  207. }
  208. public function getUid(): ?int
  209. {
  210. return $this->uid;
  211. }
  212. public function setUid(?int $uid): void
  213. {
  214. $this->uid = $uid;
  215. }
  216. public function getGid(): ?int
  217. {
  218. return $this->gid;
  219. }
  220. public function setGid(?int $gid): void
  221. {
  222. $this->gid = $gid;
  223. }
  224. public function __toString(): string
  225. {
  226. $args = [self::HEADER_ID];
  227. $format = '0x%04x OldUnix:';
  228. if (($modifyTime = $this->getModifyDateTime()) !== null) {
  229. $format .= ' Modify:[%s]';
  230. $args[] = $modifyTime->format(\DATE_ATOM);
  231. }
  232. if (($accessTime = $this->getAccessDateTime()) !== null) {
  233. $format .= ' Access:[%s]';
  234. $args[] = $accessTime->format(\DATE_ATOM);
  235. }
  236. if ($this->uid !== null) {
  237. $format .= ' UID=%d';
  238. $args[] = $this->uid;
  239. }
  240. if ($this->gid !== null) {
  241. $format .= ' GID=%d';
  242. $args[] = $this->gid;
  243. }
  244. return vsprintf($format, $args);
  245. }
  246. }