unzip-crx-3
Version:
Unzip chrome extension files
88 lines (71 loc) • 2.73 kB
JavaScript
;
var fs = require("fs");
var path = require("path");
var jszip = require("jszip");
var mkdirp = require("mkdirp");
var promisify = require("yaku/lib/promisify");
var writeFile = promisify(fs.writeFile);
var readFile = promisify(fs.readFile);
var mkdir = promisify(mkdirp);
function crxToZip(buf) {
function calcLength(a, b, c, d) {
var length = 0;
length += a;
length += b << 8;
length += c << 16;
length += d << 24;
return length;
}
// 50 4b 03 04
// This is actually a zip file
if (buf[0] === 80 && buf[1] === 75 && buf[2] === 3 && buf[3] === 4) {
return buf;
}
// 43 72 32 34 (Cr24)
if (buf[0] !== 67 || buf[1] !== 114 || buf[2] !== 50 || buf[3] !== 52) {
throw new Error("Invalid header: Does not start with Cr24");
}
// 02 00 00 00
// or
// 03 00 00 00
var isV3 = buf[4] === 3;
var isV2 = buf[4] === 2;
if (!isV2 && !isV3 || buf[5] || buf[6] || buf[7]) {
throw new Error("Unexpected crx format version number.");
}
if (isV2) {
var publicKeyLength = calcLength(buf[8], buf[9], buf[10], buf[11]);
var signatureLength = calcLength(buf[12], buf[13], buf[14], buf[15]);
// 16 = Magic number (4), CRX format version (4), lengths (2x4)
var _zipStartOffset = 16 + publicKeyLength + signatureLength;
return buf.slice(_zipStartOffset, buf.length);
}
// v3 format has header size and then header
var headerSize = calcLength(buf[8], buf[9], buf[10], buf[11]);
var zipStartOffset = 12 + headerSize;
return buf.slice(zipStartOffset, buf.length);
}
function unzip(crxFilePath, destination) {
var filePath = path.resolve(crxFilePath);
var extname = path.extname(crxFilePath);
var basename = path.basename(crxFilePath, extname);
var dirname = path.dirname(crxFilePath);
destination = destination || path.resolve(dirname, basename);
return readFile(filePath).then(function (buf) {
return jszip.loadAsync(crxToZip(buf));
}).then(function (zip) {
var zipFileKeys = Object.keys(zip.files);
return Promise.all(zipFileKeys.map(function (filename) {
var isFile = !zip.files[filename].dir;
var fullPath = path.join(destination, filename);
var directory = isFile && path.dirname(fullPath) || fullPath;
var content = zip.files[filename].async("nodebuffer");
return mkdir(directory).then(function () {
return isFile ? content : false;
}).then(function (data) {
return data ? writeFile(fullPath, data) : true;
});
}));
});
}
module.exports = unzip;