Compress.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. var rawdeflate = require("zlibjs/bin/rawdeflate.min"),
  2. rawinflate = require("zlibjs/bin/rawinflate.min"),
  3. zlibAndGzip = require("zlibjs/bin/zlib_and_gzip.min"),
  4. zip = require("zlibjs/bin/zip.min"),
  5. unzip = require("zlibjs/bin/unzip.min"),
  6. bzip2 = require("../lib/bzip2.js");
  7. var Zlib = {
  8. RawDeflate: rawdeflate.Zlib.RawDeflate,
  9. RawInflate: rawinflate.Zlib.RawInflate,
  10. Deflate: zlibAndGzip.Zlib.Deflate,
  11. Inflate: zlibAndGzip.Zlib.Inflate,
  12. Gzip: zlibAndGzip.Zlib.Gzip,
  13. Gunzip: zlibAndGzip.Zlib.Gunzip,
  14. Zip: zip.Zlib.Zip,
  15. Unzip: unzip.Zlib.Unzip,
  16. };
  17. /**
  18. * Compression operations.
  19. *
  20. * @author n1474335 [n1474335@gmail.com]
  21. * @copyright Crown Copyright 2016
  22. * @license Apache-2.0
  23. *
  24. * @namespace
  25. */
  26. var Compress = module.exports = {
  27. /**
  28. * @constant
  29. * @default
  30. */
  31. COMPRESSION_TYPE: ["Dynamic Huffman Coding", "Fixed Huffman Coding", "None (Store)"],
  32. /**
  33. * @constant
  34. * @default
  35. */
  36. INFLATE_BUFFER_TYPE: ["Adaptive", "Block"],
  37. /**
  38. * @constant
  39. * @default
  40. */
  41. COMPRESSION_METHOD: ["Deflate", "None (Store)"],
  42. /**
  43. * @constant
  44. * @default
  45. */
  46. OS: ["MSDOS", "Unix", "Macintosh"],
  47. /**
  48. * @constant
  49. * @default
  50. */
  51. RAW_COMPRESSION_TYPE_LOOKUP: {
  52. "Fixed Huffman Coding" : Zlib.RawDeflate.CompressionType.FIXED,
  53. "Dynamic Huffman Coding" : Zlib.RawDeflate.CompressionType.DYNAMIC,
  54. "None (Store)" : Zlib.RawDeflate.CompressionType.NONE,
  55. },
  56. /**
  57. * Raw Deflate operation.
  58. *
  59. * @param {byteArray} input
  60. * @param {Object[]} args
  61. * @returns {byteArray}
  62. */
  63. runRawDeflate: function(input, args) {
  64. var deflate = new Zlib.RawDeflate(input, {
  65. compressionType: Compress.RAW_COMPRESSION_TYPE_LOOKUP[args[0]]
  66. });
  67. return Array.prototype.slice.call(deflate.compress());
  68. },
  69. /**
  70. * @constant
  71. * @default
  72. */
  73. INFLATE_INDEX: 0,
  74. /**
  75. * @constant
  76. * @default
  77. */
  78. INFLATE_BUFFER_SIZE: 0,
  79. /**
  80. * @constant
  81. * @default
  82. */
  83. INFLATE_RESIZE: false,
  84. /**
  85. * @constant
  86. * @default
  87. */
  88. INFLATE_VERIFY: false,
  89. /**
  90. * @constant
  91. * @default
  92. */
  93. RAW_BUFFER_TYPE_LOOKUP: {
  94. "Adaptive" : Zlib.RawInflate.BufferType.ADAPTIVE,
  95. "Block" : Zlib.RawInflate.BufferType.BLOCK,
  96. },
  97. /**
  98. * Raw Inflate operation.
  99. *
  100. * @param {byteArray} input
  101. * @param {Object[]} args
  102. * @returns {byteArray}
  103. */
  104. runRawInflate: function(input, args) {
  105. // Deal with character encoding issues
  106. input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
  107. var inflate = new Zlib.RawInflate(input, {
  108. index: args[0],
  109. bufferSize: args[1],
  110. bufferType: Compress.RAW_BUFFER_TYPE_LOOKUP[args[2]],
  111. resize: args[3],
  112. verify: args[4]
  113. }),
  114. result = Array.prototype.slice.call(inflate.decompress());
  115. // Raw Inflate somethimes messes up and returns nonsense like this:
  116. // ]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]...
  117. // e.g. Input data of [8b, 1d, dc, 44]
  118. // Look for the first two square brackets:
  119. if (result.length > 158 && result[0] === 93 && result[5] === 93) {
  120. // If the first two square brackets are there, check that the others
  121. // are also there. If they are, throw an error. If not, continue.
  122. var valid = false;
  123. for (var i = 0; i < 155; i += 5) {
  124. if (result[i] !== 93) {
  125. valid = true;
  126. }
  127. }
  128. if (!valid) {
  129. throw "Error: Unable to inflate data";
  130. }
  131. }
  132. // Trust me, this is the easiest way...
  133. return result;
  134. },
  135. /**
  136. * @constant
  137. * @default
  138. */
  139. ZLIB_COMPRESSION_TYPE_LOOKUP: {
  140. "Fixed Huffman Coding" : Zlib.Deflate.CompressionType.FIXED,
  141. "Dynamic Huffman Coding" : Zlib.Deflate.CompressionType.DYNAMIC,
  142. "None (Store)" : Zlib.Deflate.CompressionType.NONE,
  143. },
  144. /**
  145. * Zlib Deflate operation.
  146. *
  147. * @param {byteArray} input
  148. * @param {Object[]} args
  149. * @returns {byteArray}
  150. */
  151. runZlibDeflate: function(input, args) {
  152. var deflate = new Zlib.Deflate(input, {
  153. compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
  154. });
  155. return Array.prototype.slice.call(deflate.compress());
  156. },
  157. /**
  158. * @constant
  159. * @default
  160. */
  161. ZLIB_BUFFER_TYPE_LOOKUP: {
  162. "Adaptive" : Zlib.Inflate.BufferType.ADAPTIVE,
  163. "Block" : Zlib.Inflate.BufferType.BLOCK,
  164. },
  165. /**
  166. * Zlib Inflate operation.
  167. *
  168. * @param {byteArray} input
  169. * @param {Object[]} args
  170. * @returns {byteArray}
  171. */
  172. runZlibInflate: function(input, args) {
  173. // Deal with character encoding issues
  174. input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
  175. var inflate = new Zlib.Inflate(input, {
  176. index: args[0],
  177. bufferSize: args[1],
  178. bufferType: Compress.ZLIB_BUFFER_TYPE_LOOKUP[args[2]],
  179. resize: args[3],
  180. verify: args[4]
  181. });
  182. return Array.prototype.slice.call(inflate.decompress());
  183. },
  184. /**
  185. * @constant
  186. * @default
  187. */
  188. GZIP_CHECKSUM: false,
  189. /**
  190. * Gzip operation.
  191. *
  192. * @param {byteArray} input
  193. * @param {Object[]} args
  194. * @returns {byteArray}
  195. */
  196. runGzip: function(input, args) {
  197. var filename = args[1],
  198. comment = args[2],
  199. options = {
  200. deflateOptions: {
  201. compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
  202. },
  203. flags: {
  204. fhcrc: args[3]
  205. }
  206. };
  207. if (filename.length) {
  208. options.flags.fname = true;
  209. options.filename = filename;
  210. }
  211. if (comment.length) {
  212. options.flags.fcommenct = true;
  213. options.comment = comment;
  214. }
  215. var gzip = new Zlib.Gzip(input, options);
  216. return Array.prototype.slice.call(gzip.compress());
  217. },
  218. /**
  219. * Gunzip operation.
  220. *
  221. * @param {byteArray} input
  222. * @param {Object[]} args
  223. * @returns {byteArray}
  224. */
  225. runGunzip: function(input, args) {
  226. // Deal with character encoding issues
  227. input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
  228. var gunzip = new Zlib.Gunzip(input);
  229. return Array.prototype.slice.call(gunzip.decompress());
  230. },
  231. /**
  232. * @constant
  233. * @default
  234. */
  235. PKZIP_FILENAME: "file.txt",
  236. /**
  237. * @constant
  238. * @default
  239. */
  240. ZIP_COMPRESSION_METHOD_LOOKUP: {
  241. "Deflate" : Zlib.Zip.CompressionMethod.DEFLATE,
  242. "None (Store)" : Zlib.Zip.CompressionMethod.STORE
  243. },
  244. /**
  245. * @constant
  246. * @default
  247. */
  248. ZIP_OS_LOOKUP: {
  249. "MSDOS" : Zlib.Zip.OperatingSystem.MSDOS,
  250. "Unix" : Zlib.Zip.OperatingSystem.UNIX,
  251. "Macintosh" : Zlib.Zip.OperatingSystem.MACINTOSH
  252. },
  253. /**
  254. * Zip operation.
  255. *
  256. * @param {byteArray} input
  257. * @param {Object[]} args
  258. * @returns {byteArray}
  259. */
  260. runPkzip: function(input, args) {
  261. var password = Utils.strToByteArray(args[2]),
  262. options = {
  263. filename: Utils.strToByteArray(args[0]),
  264. comment: Utils.strToByteArray(args[1]),
  265. compressionMethod: Compress.ZIP_COMPRESSION_METHOD_LOOKUP[args[3]],
  266. os: Compress.ZIP_OS_LOOKUP[args[4]],
  267. deflateOption: {
  268. compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[5]]
  269. },
  270. },
  271. zip = new Zlib.Zip();
  272. if (password.length)
  273. zip.setPassword(password);
  274. zip.addFile(input, options);
  275. return Array.prototype.slice.call(zip.compress());
  276. },
  277. /**
  278. * @constant
  279. * @default
  280. */
  281. PKUNZIP_VERIFY: false,
  282. /**
  283. * Unzip operation.
  284. *
  285. * @param {byteArray} input
  286. * @param {Object[]} args
  287. * @returns {string}
  288. */
  289. runPkunzip: function(input, args) {
  290. var options = {
  291. password: Utils.strToByteArray(args[0]),
  292. verify: args[1]
  293. },
  294. unzip = new Zlib.Unzip(input, options),
  295. filenames = unzip.getFilenames(),
  296. files = [];
  297. filenames.forEach(function(fileName) {
  298. var contents = unzip.decompress(fileName);
  299. contents = Utils.byteArrayToUtf8(contents);
  300. var file = {
  301. fileName: fileName,
  302. size: contents.length,
  303. };
  304. var isDir = contents.length === 0 && fileName.endsWith("/");
  305. if (!isDir) {
  306. file.contents = contents;
  307. }
  308. files.push(file);
  309. });
  310. return Utils.displayFilesAsHTML(files);
  311. },
  312. /**
  313. * Bzip2 Decompress operation.
  314. *
  315. * @param {byteArray} input
  316. * @param {Object[]} args
  317. * @returns {string}
  318. */
  319. runBzip2Decompress: function(input, args) {
  320. var compressed = new Uint8Array(input),
  321. bzip2Reader,
  322. plain = "";
  323. bzip2Reader = bzip2.array(compressed);
  324. plain = bzip2.simple(bzip2Reader);
  325. return plain;
  326. },
  327. /**
  328. * @constant
  329. * @default
  330. */
  331. TAR_FILENAME: "file.txt",
  332. /**
  333. * Tar pack operation.
  334. *
  335. * @author tlwr [toby@toby.codes]
  336. *
  337. * @param {byteArray} input
  338. * @param {Object[]} args
  339. * @returns {byteArray}
  340. */
  341. runTar: function(input, args) {
  342. var Tarball = function() {
  343. this.bytes = new Array(512);
  344. this.position = 0;
  345. };
  346. Tarball.prototype.addEmptyBlock = function() {
  347. var filler = new Array(512);
  348. filler.fill(0);
  349. this.bytes = this.bytes.concat(filler);
  350. };
  351. Tarball.prototype.writeBytes = function(bytes) {
  352. var self = this;
  353. if (this.position + bytes.length > this.bytes.length) {
  354. this.addEmptyBlock();
  355. }
  356. Array.prototype.forEach.call(bytes, function(b, i) {
  357. if (typeof b.charCodeAt !== "undefined") {
  358. b = b.charCodeAt();
  359. }
  360. self.bytes[self.position] = b;
  361. self.position += 1;
  362. });
  363. };
  364. Tarball.prototype.writeEndBlocks = function() {
  365. var numEmptyBlocks = 2;
  366. for (var i = 0; i < numEmptyBlocks; i++) {
  367. this.addEmptyBlock();
  368. }
  369. };
  370. var fileSize = Utils.padLeft(input.length.toString(8), 11, "0");
  371. var currentUnixTimestamp = Math.floor(Date.now() / 1000);
  372. var lastModTime = Utils.padLeft(currentUnixTimestamp.toString(8), 11, "0");
  373. var file = {
  374. fileName: Utils.padBytesRight(args[0], 100),
  375. fileMode: Utils.padBytesRight("0000664", 8),
  376. ownerUID: Utils.padBytesRight("0", 8),
  377. ownerGID: Utils.padBytesRight("0", 8),
  378. size: Utils.padBytesRight(fileSize, 12),
  379. lastModTime: Utils.padBytesRight(lastModTime, 12),
  380. checksum: " ",
  381. type: "0",
  382. linkedFileName: Utils.padBytesRight("", 100),
  383. USTARFormat: Utils.padBytesRight("ustar", 6),
  384. version: "00",
  385. ownerUserName: Utils.padBytesRight("", 32),
  386. ownerGroupName: Utils.padBytesRight("", 32),
  387. deviceMajor: Utils.padBytesRight("", 8),
  388. deviceMinor: Utils.padBytesRight("", 8),
  389. fileNamePrefix: Utils.padBytesRight("", 155),
  390. };
  391. var checksum = 0;
  392. for (var key in file) {
  393. var bytes = file[key];
  394. Array.prototype.forEach.call(bytes, function(b) {
  395. if (typeof b.charCodeAt !== "undefined") {
  396. checksum += b.charCodeAt();
  397. } else {
  398. checksum += b;
  399. }
  400. });
  401. }
  402. checksum = Utils.padBytesRight(Utils.padLeft(checksum.toString(8), 7, "0"), 8);
  403. file.checksum = checksum;
  404. var tarball = new Tarball();
  405. tarball.writeBytes(file.fileName);
  406. tarball.writeBytes(file.fileMode);
  407. tarball.writeBytes(file.ownerUID);
  408. tarball.writeBytes(file.ownerGID);
  409. tarball.writeBytes(file.size);
  410. tarball.writeBytes(file.lastModTime);
  411. tarball.writeBytes(file.checksum);
  412. tarball.writeBytes(file.type);
  413. tarball.writeBytes(file.linkedFileName);
  414. tarball.writeBytes(file.USTARFormat);
  415. tarball.writeBytes(file.version);
  416. tarball.writeBytes(file.ownerUserName);
  417. tarball.writeBytes(file.ownerGroupName);
  418. tarball.writeBytes(file.deviceMajor);
  419. tarball.writeBytes(file.deviceMinor);
  420. tarball.writeBytes(file.fileNamePrefix);
  421. tarball.writeBytes(Utils.padBytesRight("", 12));
  422. tarball.writeBytes(input);
  423. tarball.writeEndBlocks();
  424. return tarball.bytes;
  425. },
  426. /**
  427. * Untar unpack operation.
  428. *
  429. * @author tlwr [toby@toby.codes]
  430. *
  431. * @param {byteArray} input
  432. * @param {Object[]} args
  433. * @returns {html}
  434. */
  435. runUntar: function(input, args) {
  436. var Stream = function(input) {
  437. this.bytes = input;
  438. this.position = 0;
  439. };
  440. Stream.prototype.readString = function(numBytes) {
  441. var result = "";
  442. for (var i = this.position; i < this.position + numBytes; i++) {
  443. var currentByte = this.bytes[i];
  444. if (currentByte === 0) break;
  445. result += String.fromCharCode(currentByte);
  446. }
  447. this.position += numBytes;
  448. return result;
  449. };
  450. Stream.prototype.readInt = function(numBytes, base) {
  451. var string = this.readString(numBytes);
  452. return parseInt(string, base);
  453. };
  454. Stream.prototype.hasMore = function() {
  455. return this.position < this.bytes.length;
  456. };
  457. var stream = new Stream(input),
  458. files = [];
  459. while (stream.hasMore()) {
  460. var dataPosition = stream.position + 512;
  461. var file = {
  462. fileName: stream.readString(100),
  463. fileMode: stream.readString(8),
  464. ownerUID: stream.readString(8),
  465. ownerGID: stream.readString(8),
  466. size: parseInt(stream.readString(12), 8), // Octal
  467. lastModTime: new Date(1000 * stream.readInt(12, 8)), // Octal
  468. checksum: stream.readString(8),
  469. type: stream.readString(1),
  470. linkedFileName: stream.readString(100),
  471. USTARFormat: stream.readString(6).indexOf("ustar") >= 0,
  472. };
  473. if (file.USTARFormat) {
  474. file.version = stream.readString(2);
  475. file.ownerUserName = stream.readString(32);
  476. file.ownerGroupName = stream.readString(32);
  477. file.deviceMajor = stream.readString(8);
  478. file.deviceMinor = stream.readString(8);
  479. file.filenamePrefix = stream.readString(155);
  480. }
  481. stream.position = dataPosition;
  482. if (file.type === "0") {
  483. // File
  484. files.push(file);
  485. var endPosition = stream.position + file.size;
  486. if (file.size % 512 !== 0) {
  487. endPosition += 512 - (file.size % 512);
  488. }
  489. file.contents = "";
  490. while (stream.position < endPosition) {
  491. file.contents += stream.readString(512);
  492. }
  493. } else if (file.type === "5") {
  494. // Directory
  495. files.push(file);
  496. } else {
  497. // Symlink or empty bytes
  498. }
  499. }
  500. return Utils.displayFilesAsHTML(files);
  501. },
  502. };