OldUnixExtraField.php 8.2 KB

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