AsiExtraField.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. namespace PhpZip\Model\Extra\Fields;
  3. use PhpZip\Constants\UnixStat;
  4. use PhpZip\Exception\Crc32Exception;
  5. use PhpZip\Model\Extra\ZipExtraField;
  6. use PhpZip\Model\ZipEntry;
  7. /**
  8. * ASi Unix Extra Field:
  9. * ====================.
  10. *
  11. * The following is the layout of the ASi extra block for Unix. The
  12. * local-header and central-header versions are identical.
  13. * (Last Revision 19960916)
  14. *
  15. * Value Size Description
  16. * ----- ---- -----------
  17. * (Unix3) 0x756e Short tag for this extra block type ("nu")
  18. * TSize Short total data size for this block
  19. * CRC Long CRC-32 of the remaining data
  20. * Mode Short file permissions
  21. * SizDev Long symlink'd size OR major/minor dev num
  22. * UID Short user ID
  23. * GID Short group ID
  24. * (var.) variable symbolic link filename
  25. *
  26. * Mode is the standard Unix st_mode field from struct stat, containing
  27. * user/group/other permissions, setuid/setgid and symlink info, etc.
  28. *
  29. * If Mode indicates that this file is a symbolic link, SizDev is the
  30. * size of the file to which the link points. Otherwise, if the file
  31. * is a device, SizDev contains the standard Unix st_rdev field from
  32. * struct stat (includes the major and minor numbers of the device).
  33. * SizDev is undefined in other cases.
  34. *
  35. * If Mode indicates that the file is a symbolic link, the final field
  36. * will be the name of the file to which the link points. The file-
  37. * name length can be inferred from TSize.
  38. *
  39. * [Note that TSize may incorrectly refer to the data size not counting
  40. * the CRC; i.e., it may be four bytes too small.]
  41. *
  42. * @see ftp://ftp.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip Info-ZIP version Specification
  43. */
  44. class AsiExtraField implements ZipExtraField
  45. {
  46. /** @var int Header id */
  47. const HEADER_ID = 0x756e;
  48. const USER_GID_PID = 1000;
  49. /** Bits used for permissions (and sticky bit). */
  50. const PERM_MASK = 07777;
  51. /** @var int Standard Unix stat(2) file mode. */
  52. private $mode;
  53. /** @var int User ID. */
  54. private $uid;
  55. /** @var int Group ID. */
  56. private $gid;
  57. /**
  58. * @var string File this entry points to, if it is a symbolic link.
  59. * Empty string - if entry is not a symbolic link.
  60. */
  61. private $link;
  62. /**
  63. * AsiExtraField constructor.
  64. *
  65. * @param int $mode
  66. * @param int $uid
  67. * @param int $gid
  68. * @param string $link
  69. */
  70. public function __construct($mode, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID, $link = '')
  71. {
  72. $this->mode = $mode;
  73. $this->uid = $uid;
  74. $this->gid = $gid;
  75. $this->link = $link;
  76. }
  77. /**
  78. * Returns the Header ID (type) of this Extra Field.
  79. * The Header ID is an unsigned short integer (two bytes)
  80. * which must be constant during the life cycle of this object.
  81. *
  82. * @return int
  83. */
  84. public function getHeaderId()
  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
  93. *
  94. * @throws Crc32Exception
  95. *
  96. * @return static
  97. */
  98. public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
  99. {
  100. $givenChecksum = unpack('V', $buffer)[1];
  101. $buffer = substr($buffer, 4);
  102. $realChecksum = crc32($buffer);
  103. if ($givenChecksum !== $realChecksum) {
  104. throw new Crc32Exception('Asi Unix Extra Filed Data', $givenChecksum, $realChecksum);
  105. }
  106. $data = unpack('vmode/VlinkSize/vuid/vgid', $buffer);
  107. $link = '';
  108. if ($data['linkSize'] > 0) {
  109. $link = substr($buffer, 10);
  110. }
  111. return new self($data['mode'], $data['uid'], $data['gid'], $link);
  112. }
  113. /**
  114. * Populate data from this array as if it was in central directory data.
  115. *
  116. * @param string $buffer the buffer to read data from
  117. * @param ZipEntry|null $entry
  118. *
  119. * @throws Crc32Exception
  120. *
  121. * @return AsiExtraField
  122. */
  123. public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
  124. {
  125. return self::unpackLocalFileData($buffer, $entry);
  126. }
  127. /**
  128. * The actual data to put into local file data - without Header-ID
  129. * or length specifier.
  130. *
  131. * @return string the data
  132. */
  133. public function packLocalFileData()
  134. {
  135. $data = pack(
  136. 'vVvv',
  137. $this->mode,
  138. \strlen($this->link),
  139. $this->uid,
  140. $this->gid
  141. ) . $this->link;
  142. return pack('V', crc32($data)) . $data;
  143. }
  144. /**
  145. * The actual data to put into central directory - without Header-ID or
  146. * length specifier.
  147. *
  148. * @return string the data
  149. */
  150. public function packCentralDirData()
  151. {
  152. return $this->packLocalFileData();
  153. }
  154. /**
  155. * Name of linked file.
  156. *
  157. * @return string name of the file this entry links to if it is a
  158. * symbolic link, the empty string otherwise
  159. */
  160. public function getLink()
  161. {
  162. return $this->link;
  163. }
  164. /**
  165. * Indicate that this entry is a symbolic link to the given filename.
  166. *
  167. * @param string $link name of the file this entry links to, empty
  168. * string if it is not a symbolic link
  169. */
  170. public function setLink($link)
  171. {
  172. $this->link = (string) $link;
  173. $this->mode = $this->getPermissionsMode($this->mode);
  174. }
  175. /**
  176. * Is this entry a symbolic link?
  177. *
  178. * @return bool true if this is a symbolic link
  179. */
  180. public function isLink()
  181. {
  182. return !empty($this->link);
  183. }
  184. /**
  185. * Get the file mode for given permissions with the correct file type.
  186. *
  187. * @param int $mode the mode
  188. *
  189. * @return int the type with the mode
  190. */
  191. protected function getPermissionsMode($mode)
  192. {
  193. $type = 0;
  194. if ($this->isLink()) {
  195. $type = UnixStat::UNX_IFLNK;
  196. } elseif (($mode & UnixStat::UNX_IFREG) !== 0) {
  197. $type = UnixStat::UNX_IFREG;
  198. } elseif (($mode & UnixStat::UNX_IFDIR) !== 0) {
  199. $type = UnixStat::UNX_IFDIR;
  200. }
  201. return $type | ($mode & self::PERM_MASK);
  202. }
  203. /**
  204. * Is this entry a directory?
  205. *
  206. * @return bool true if this entry is a directory
  207. */
  208. public function isDirectory()
  209. {
  210. return ($this->mode & UnixStat::UNX_IFDIR) !== 0 && !$this->isLink();
  211. }
  212. /**
  213. * @return int
  214. */
  215. public function getMode()
  216. {
  217. return $this->mode;
  218. }
  219. /**
  220. * @param int $mode
  221. */
  222. public function setMode($mode)
  223. {
  224. $this->mode = $this->getPermissionsMode($mode);
  225. }
  226. /**
  227. * @return int
  228. */
  229. public function getUserId()
  230. {
  231. return $this->uid;
  232. }
  233. /**
  234. * @param int $uid
  235. */
  236. public function setUserId($uid)
  237. {
  238. $this->uid = (int) $uid;
  239. }
  240. /**
  241. * @return int
  242. */
  243. public function getGroupId()
  244. {
  245. return $this->gid;
  246. }
  247. /**
  248. * @param int $gid
  249. */
  250. public function setGroupId($gid)
  251. {
  252. $this->gid = (int) $gid;
  253. }
  254. /**
  255. * @return string
  256. */
  257. public function __toString()
  258. {
  259. return sprintf(
  260. '0x%04x ASI: Mode=%o UID=%d GID=%d Link="%s',
  261. self::HEADER_ID,
  262. $this->mode,
  263. $this->uid,
  264. $this->gid,
  265. $this->link
  266. );
  267. }
  268. }