entryHeader.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. var Utils = require("../util"),
  2. Constants = Utils.Constants;
  3. /* The central directory file header */
  4. module.exports = function () {
  5. var _verMade = 20, // v2.0
  6. _version = 10, // v1.0
  7. _flags = 0,
  8. _method = 0,
  9. _time = 0,
  10. _crc = 0,
  11. _compressedSize = 0,
  12. _size = 0,
  13. _fnameLen = 0,
  14. _extraLen = 0,
  15. _comLen = 0,
  16. _diskStart = 0,
  17. _inattr = 0,
  18. _attr = 0,
  19. _offset = 0;
  20. _verMade |= Utils.isWin ? 0x0a00 : 0x0300;
  21. // Set EFS flag since filename and comment fields are all by default encoded using UTF-8.
  22. // Without it file names may be corrupted for other apps when file names use unicode chars
  23. _flags |= Constants.FLG_EFS;
  24. var _dataHeader = {};
  25. function setTime(val) {
  26. val = new Date(val);
  27. _time =
  28. (((val.getFullYear() - 1980) & 0x7f) << 25) | // b09-16 years from 1980
  29. ((val.getMonth() + 1) << 21) | // b05-08 month
  30. (val.getDate() << 16) | // b00-04 hour
  31. // 2 bytes time
  32. (val.getHours() << 11) | // b11-15 hour
  33. (val.getMinutes() << 5) | // b05-10 minute
  34. (val.getSeconds() >> 1); // b00-04 seconds divided by 2
  35. }
  36. setTime(+new Date());
  37. return {
  38. get made() {
  39. return _verMade;
  40. },
  41. set made(val) {
  42. _verMade = val;
  43. },
  44. get version() {
  45. return _version;
  46. },
  47. set version(val) {
  48. _version = val;
  49. },
  50. get flags() {
  51. return _flags;
  52. },
  53. set flags(val) {
  54. _flags = val;
  55. },
  56. get method() {
  57. return _method;
  58. },
  59. set method(val) {
  60. switch (val) {
  61. case Constants.STORED:
  62. this.version = 10;
  63. case Constants.DEFLATED:
  64. default:
  65. this.version = 20;
  66. }
  67. _method = val;
  68. },
  69. get time() {
  70. return new Date(((_time >> 25) & 0x7f) + 1980, ((_time >> 21) & 0x0f) - 1, (_time >> 16) & 0x1f, (_time >> 11) & 0x1f, (_time >> 5) & 0x3f, (_time & 0x1f) << 1);
  71. },
  72. set time(val) {
  73. setTime(val);
  74. },
  75. get crc() {
  76. return _crc;
  77. },
  78. set crc(val) {
  79. _crc = Math.max(0, val) >>> 0;
  80. },
  81. get compressedSize() {
  82. return _compressedSize;
  83. },
  84. set compressedSize(val) {
  85. _compressedSize = Math.max(0, val) >>> 0;
  86. },
  87. get size() {
  88. return _size;
  89. },
  90. set size(val) {
  91. _size = Math.max(0, val) >>> 0;
  92. },
  93. get fileNameLength() {
  94. return _fnameLen;
  95. },
  96. set fileNameLength(val) {
  97. _fnameLen = val;
  98. },
  99. get extraLength() {
  100. return _extraLen;
  101. },
  102. set extraLength(val) {
  103. _extraLen = val;
  104. },
  105. get commentLength() {
  106. return _comLen;
  107. },
  108. set commentLength(val) {
  109. _comLen = val;
  110. },
  111. get diskNumStart() {
  112. return _diskStart;
  113. },
  114. set diskNumStart(val) {
  115. _diskStart = Math.max(0, val) >>> 0;
  116. },
  117. get inAttr() {
  118. return _inattr;
  119. },
  120. set inAttr(val) {
  121. _inattr = Math.max(0, val) >>> 0;
  122. },
  123. get attr() {
  124. return _attr;
  125. },
  126. set attr(val) {
  127. _attr = Math.max(0, val) >>> 0;
  128. },
  129. // get Unix file permissions
  130. get fileAttr() {
  131. return _attr ? (((_attr >>> 0) | 0) >> 16) & 0xfff : 0;
  132. },
  133. get offset() {
  134. return _offset;
  135. },
  136. set offset(val) {
  137. _offset = Math.max(0, val) >>> 0;
  138. },
  139. get encripted() {
  140. return (_flags & 1) === 1;
  141. },
  142. get entryHeaderSize() {
  143. return Constants.CENHDR + _fnameLen + _extraLen + _comLen;
  144. },
  145. get realDataOffset() {
  146. return _offset + Constants.LOCHDR + _dataHeader.fnameLen + _dataHeader.extraLen;
  147. },
  148. get dataHeader() {
  149. return _dataHeader;
  150. },
  151. loadDataHeaderFromBinary: function (/*Buffer*/ input) {
  152. var data = input.slice(_offset, _offset + Constants.LOCHDR);
  153. // 30 bytes and should start with "PK\003\004"
  154. if (data.readUInt32LE(0) !== Constants.LOCSIG) {
  155. throw new Error(Utils.Errors.INVALID_LOC);
  156. }
  157. _dataHeader = {
  158. // version needed to extract
  159. version: data.readUInt16LE(Constants.LOCVER),
  160. // general purpose bit flag
  161. flags: data.readUInt16LE(Constants.LOCFLG),
  162. // compression method
  163. method: data.readUInt16LE(Constants.LOCHOW),
  164. // modification time (2 bytes time, 2 bytes date)
  165. time: data.readUInt32LE(Constants.LOCTIM),
  166. // uncompressed file crc-32 value
  167. crc: data.readUInt32LE(Constants.LOCCRC),
  168. // compressed size
  169. compressedSize: data.readUInt32LE(Constants.LOCSIZ),
  170. // uncompressed size
  171. size: data.readUInt32LE(Constants.LOCLEN),
  172. // filename length
  173. fnameLen: data.readUInt16LE(Constants.LOCNAM),
  174. // extra field length
  175. extraLen: data.readUInt16LE(Constants.LOCEXT)
  176. };
  177. },
  178. loadFromBinary: function (/*Buffer*/ data) {
  179. // data should be 46 bytes and start with "PK 01 02"
  180. if (data.length !== Constants.CENHDR || data.readUInt32LE(0) !== Constants.CENSIG) {
  181. throw new Error(Utils.Errors.INVALID_CEN);
  182. }
  183. // version made by
  184. _verMade = data.readUInt16LE(Constants.CENVEM);
  185. // version needed to extract
  186. _version = data.readUInt16LE(Constants.CENVER);
  187. // encrypt, decrypt flags
  188. _flags = data.readUInt16LE(Constants.CENFLG);
  189. // compression method
  190. _method = data.readUInt16LE(Constants.CENHOW);
  191. // modification time (2 bytes time, 2 bytes date)
  192. _time = data.readUInt32LE(Constants.CENTIM);
  193. // uncompressed file crc-32 value
  194. _crc = data.readUInt32LE(Constants.CENCRC);
  195. // compressed size
  196. _compressedSize = data.readUInt32LE(Constants.CENSIZ);
  197. // uncompressed size
  198. _size = data.readUInt32LE(Constants.CENLEN);
  199. // filename length
  200. _fnameLen = data.readUInt16LE(Constants.CENNAM);
  201. // extra field length
  202. _extraLen = data.readUInt16LE(Constants.CENEXT);
  203. // file comment length
  204. _comLen = data.readUInt16LE(Constants.CENCOM);
  205. // volume number start
  206. _diskStart = data.readUInt16LE(Constants.CENDSK);
  207. // internal file attributes
  208. _inattr = data.readUInt16LE(Constants.CENATT);
  209. // external file attributes
  210. _attr = data.readUInt32LE(Constants.CENATX);
  211. // LOC header offset
  212. _offset = data.readUInt32LE(Constants.CENOFF);
  213. },
  214. dataHeaderToBinary: function () {
  215. // LOC header size (30 bytes)
  216. var data = Buffer.alloc(Constants.LOCHDR);
  217. // "PK\003\004"
  218. data.writeUInt32LE(Constants.LOCSIG, 0);
  219. // version needed to extract
  220. data.writeUInt16LE(_version, Constants.LOCVER);
  221. // general purpose bit flag
  222. data.writeUInt16LE(_flags, Constants.LOCFLG);
  223. // compression method
  224. data.writeUInt16LE(_method, Constants.LOCHOW);
  225. // modification time (2 bytes time, 2 bytes date)
  226. data.writeUInt32LE(_time, Constants.LOCTIM);
  227. // uncompressed file crc-32 value
  228. data.writeUInt32LE(_crc, Constants.LOCCRC);
  229. // compressed size
  230. data.writeUInt32LE(_compressedSize, Constants.LOCSIZ);
  231. // uncompressed size
  232. data.writeUInt32LE(_size, Constants.LOCLEN);
  233. // filename length
  234. data.writeUInt16LE(_fnameLen, Constants.LOCNAM);
  235. // extra field length
  236. data.writeUInt16LE(_extraLen, Constants.LOCEXT);
  237. return data;
  238. },
  239. entryHeaderToBinary: function () {
  240. // CEN header size (46 bytes)
  241. var data = Buffer.alloc(Constants.CENHDR + _fnameLen + _extraLen + _comLen);
  242. // "PK\001\002"
  243. data.writeUInt32LE(Constants.CENSIG, 0);
  244. // version made by
  245. data.writeUInt16LE(_verMade, Constants.CENVEM);
  246. // version needed to extract
  247. data.writeUInt16LE(_version, Constants.CENVER);
  248. // encrypt, decrypt flags
  249. data.writeUInt16LE(_flags, Constants.CENFLG);
  250. // compression method
  251. data.writeUInt16LE(_method, Constants.CENHOW);
  252. // modification time (2 bytes time, 2 bytes date)
  253. data.writeUInt32LE(_time, Constants.CENTIM);
  254. // uncompressed file crc-32 value
  255. data.writeUInt32LE(_crc, Constants.CENCRC);
  256. // compressed size
  257. data.writeUInt32LE(_compressedSize, Constants.CENSIZ);
  258. // uncompressed size
  259. data.writeUInt32LE(_size, Constants.CENLEN);
  260. // filename length
  261. data.writeUInt16LE(_fnameLen, Constants.CENNAM);
  262. // extra field length
  263. data.writeUInt16LE(_extraLen, Constants.CENEXT);
  264. // file comment length
  265. data.writeUInt16LE(_comLen, Constants.CENCOM);
  266. // volume number start
  267. data.writeUInt16LE(_diskStart, Constants.CENDSK);
  268. // internal file attributes
  269. data.writeUInt16LE(_inattr, Constants.CENATT);
  270. // external file attributes
  271. data.writeUInt32LE(_attr, Constants.CENATX);
  272. // LOC header offset
  273. data.writeUInt32LE(_offset, Constants.CENOFF);
  274. // fill all with
  275. data.fill(0x00, Constants.CENHDR);
  276. return data;
  277. },
  278. toJSON: function () {
  279. const bytes = function (nr) {
  280. return nr + " bytes";
  281. };
  282. return {
  283. made: _verMade,
  284. version: _version,
  285. flags: _flags,
  286. method: Utils.methodToString(_method),
  287. time: this.time,
  288. crc: "0x" + _crc.toString(16).toUpperCase(),
  289. compressedSize: bytes(_compressedSize),
  290. size: bytes(_size),
  291. fileNameLength: bytes(_fnameLen),
  292. extraLength: bytes(_extraLen),
  293. commentLength: bytes(_comLen),
  294. diskNumStart: _diskStart,
  295. inAttr: _inattr,
  296. attr: _attr,
  297. offset: _offset,
  298. entryHeaderSize: bytes(Constants.CENHDR + _fnameLen + _extraLen + _comLen)
  299. };
  300. },
  301. toString: function () {
  302. return JSON.stringify(this.toJSON(), null, "\t");
  303. }
  304. };
  305. };