Skip to content

Commit

Permalink
Merge pull request #331 from 5saviahv/master
Browse files Browse the repository at this point in the history
Modified addLocalFile so it will read file stats from file
  • Loading branch information
cthackers authored Nov 19, 2020
2 parents 9d2eb0b + 7db0eda commit a7e8932
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 82 deletions.
169 changes: 95 additions & 74 deletions adm-zip.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,26 @@ module.exports = function (/**String*/input) {
return null;
}

function fixPath(zipPath){
// convert windows file separators
zipPath = zipPath.split("\\").join("/");
// add separator if it wasnt given
if (zipPath.charAt(zipPath.length - 1) !== "/") {
zipPath += "/";
}
return zipPath;
}

return {
/**
* Extracts the given entry from the archive and returns the content as a Buffer object
* @param entry ZipEntry object or String with the full path of the entry
*
* @return Buffer or Null in case of error
*/
readFile: function (/**Object*/entry) {
readFile: function (/**Object*/entry, /*String, Buffer*/pass) {
var item = getEntry(entry);
return item && item.getData() || null;
return item && item.getData(pass) || null;
},

/**
Expand Down Expand Up @@ -208,23 +218,22 @@ module.exports = function (/**String*/input) {
* @param zipPath Optional path inside the zip
* @param zipName Optional name for the file
*/
addLocalFile: function (/**String*/localPath, /**String=*/zipPath, /**String=*/zipName) {
addLocalFile: function (/**String*/localPath, /**String=*/zipPath, /**String=*/zipName, /**String*/comment) {
if (fs.existsSync(localPath)) {
if (zipPath) {
zipPath = zipPath.split("\\").join("/");
if (zipPath.charAt(zipPath.length - 1) !== "/") {
zipPath += "/";
}
} else {
zipPath = "";
}
// fix ZipPath
zipPath = (zipPath) ? fixPath(zipPath) : "";

// p - local file name
var p = localPath.split("\\").join("/").split("/").pop();

if (zipName) {
this.addFile(zipPath + zipName, fs.readFileSync(localPath), "", 0)
} else {
this.addFile(zipPath + p, fs.readFileSync(localPath), "", 0)
}
// add file name into zippath
zipPath += (zipName) ? zipName : p;

// read file attributes
const _attr = fs.statSync(localPath);

// add file into zip file
this.addFile(zipPath, fs.readFileSync(localPath), comment, _attr)
} else {
throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath));
}
Expand All @@ -238,54 +247,47 @@ module.exports = function (/**String*/input) {
* @param filter optional RegExp or Function if files match will
* be included.
*/
addLocalFolder: function (/**String*/localPath, /**String=*/zipPath, /**=RegExp|Function*/filter) {
if (filter === undefined) {
filter = function () {
return true;
};
} else if (filter instanceof RegExp) {
filter = function (filter) {
return function (filename) {
return filter.test(filename);
}
}(filter);
}

if (zipPath) {
zipPath = zipPath.split("\\").join("/");
if (zipPath.charAt(zipPath.length - 1) !== "/") {
zipPath += "/";
}
} else {
zipPath = "";
}
// normalize the path first
localPath = pth.normalize(localPath);
localPath = localPath.split("\\").join("/"); //windows fix
if (localPath.charAt(localPath.length - 1) !== "/")
localPath += "/";

if (fs.existsSync(localPath)) {

var items = Utils.findFiles(localPath),
self = this;

if (items.length) {
items.forEach(function (path) {
var p = path.split("\\").join("/").replace(new RegExp(localPath.replace(/(\(|\)|\$)/g, '\\$1'), 'i'), ""); //windows fix
if (filter(p)) {
if (p.charAt(p.length - 1) !== "/") {
self.addFile(zipPath + p, fs.readFileSync(path), "", 0)
} else {
self.addFile(zipPath + p, Buffer.alloc(0), "", 0)
}
}
});
}
} else {
throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath));
}
},
addLocalFolder: function (/**String*/localPath, /**String=*/zipPath, /**=RegExp|Function*/filter) {
// Prepare filter
if (filter instanceof RegExp) { // if filter is RegExp wrap it
filter = (function (rx){
return function (filename) {
return rx.test(filename);
}
})(filter);
} else if ('function' !== typeof filter) { // if filter is not function we will replace it
filter = function () {
return true;
};
}

// fix ZipPath
zipPath = (zipPath) ? fixPath(zipPath) : "";

// normalize the path first
localPath = pth.normalize(localPath);

if (fs.existsSync(localPath)) {

var items = Utils.findFiles(localPath),
self = this;

if (items.length) {
items.forEach(function (filepath) {
var p = pth.relative(localPath, filepath).split("\\").join("/"); //windows fix
if (filter(p)) {
if (filepath.charAt(filepath.length - 1) !== pth.sep) {
self.addFile(zipPath + p, fs.readFileSync(filepath), "", fs.statSync(filepath));
} else {
self.addFile(zipPath + p + '/', Buffer.alloc(0), "", 0);
}
}
});
}
} else {
throw new Error(Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath));
}
},

/**
* Asynchronous addLocalFile
Expand Down Expand Up @@ -376,19 +378,38 @@ module.exports = function (/**String*/input) {
* @param attr
*/
addFile: function (/**String*/entryName, /**Buffer*/content, /**String*/comment, /**Number*/attr) {
// prepare new entry
var entry = new ZipEntry();
entry.entryName = entryName;
entry.comment = comment || "";

if (!attr) {
if (entry.isDirectory) {
attr = (0o40755 << 16) | 0x10; // (permissions drwxr-xr-x) + (MS-DOS directory flag)
} else {
attr = 0o644 << 16; // permissions -r-wr--r--
var isStat = ('object' === typeof attr) && (attr instanceof fs.Stats);

// last modification time from file stats
if (isStat){
entry.header.time = attr.mtime;
}

// Set file attribute
var fileattr = (entry.isDirectory) ? 0x10 : 0; // (MS-DOS directory flag)

// extended attributes field for Unix
if('win32' !== process.platform){
// set file type either S_IFDIR / S_IFREG
var unix = (entry.isDirectory) ? 0x4000 : 0x8000;

if (isStat) { // File attributes from file stats
unix |= (0xfff & attr.mode)
}else if ('number' === typeof attr){ // attr from given attr values
unix |= (0xfff & attr);
}else{ // Default values:
unix |= (entry.isDirectory) ? 0o755 : 0o644; // permissions (drwxr-xr-x) or (-r-wr--r--)
}

fileattr = (fileattr | (unix << 16)) >>> 0; // add attributes
}

entry.attr = attr;
entry.attr = fileattr;

entry.setData(content);
_zip.setEntry(entry);
Expand Down Expand Up @@ -482,7 +503,7 @@ module.exports = function (/**String*/input) {
* Test the archive
*
*/
test: function () {
test: function (pass) {
if (!_zip) {
return false;
}
Expand All @@ -492,7 +513,7 @@ module.exports = function (/**String*/input) {
if (entry.isDirectory) {
continue;
}
var content = _zip.entries[entry].getData();
var content = _zip.entries[entry].getData(pass);
if (!content) {
return false;
}
Expand All @@ -510,7 +531,7 @@ module.exports = function (/**String*/input) {
* @param overwrite If the file already exists at the target path, the file will be overwriten if this is true.
* Default is FALSE
*/
extractAllTo: function (/**String*/targetPath, /**Boolean*/overwrite) {
extractAllTo: function (/**String*/targetPath, /**Boolean*/overwrite, /*String, Buffer*/pass) {
overwrite = overwrite || false;
if (!_zip) {
throw new Error(Utils.Errors.NO_ZIP);
Expand All @@ -521,7 +542,7 @@ module.exports = function (/**String*/input) {
Utils.makeDir(entryName);
return;
}
var content = entry.getData();
var content = entry.getData(pass);
if (!content) {
throw new Error(Utils.Errors.CANT_EXTRACT_FILE);
}
Expand Down
22 changes: 20 additions & 2 deletions headers/entryHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var Utils = require("../util"),

/* The central directory file header */
module.exports = function () {
var _verMade = 0x0A,
var _verMade = 0x14,
_version = 0x0A,
_flags = 0,
_method = 0,
Expand All @@ -20,6 +20,15 @@ module.exports = function () {
_attr = 0,
_offset = 0;

switch(process.platform){
case 'win32':
_verMade |= 0x0A00;
case 'darwin':
_verMade |= 0x1300;
default:
_verMade |= 0x0300;
}

var _dataHeader = {};

function setTime(val) {
Expand Down Expand Up @@ -47,7 +56,16 @@ module.exports = function () {
set flags (val) { _flags = val; },

get method () { return _method; },
set method (val) { _method = val; },
set method (val) {
switch (val){
case Constants.STORED:
this.version = 10;
case Constants.DEFLATED:
default:
this.version = 20;
}
_method = val;
},

get time () { return new Date(
((_time >> 25) & 0x7f) + 1980,
Expand Down
3 changes: 2 additions & 1 deletion methods/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
exports.Deflater = require("./deflater");
exports.Inflater = require("./inflater");
exports.Inflater = require("./inflater");
exports.ZipCrypto = require("./zipcrypto");
77 changes: 77 additions & 0 deletions methods/zipcrypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// generate CRC32 lookup table
const crctable = (new Uint32Array(256)).map((t,crc)=>{
for(let j=0;j<8;j++){
if (0 !== (crc & 1)){
crc = (crc >>> 1) ^ 0xEDB88320
}else{
crc >>>= 1
}
}
return crc>>>0;
});

function make_decrypter(/*Buffer*/pwd){
// C-style uInt32 Multiply
const uMul = (a,b) => Math.imul(a, b) >>> 0;
// Initialize keys with default values
const keys = new Uint32Array([0x12345678, 0x23456789, 0x34567890]);
// crc32 byte update
const crc32update = (pCrc32, bval) => {
return crctable[(pCrc32 ^ bval) & 0xff] ^ (pCrc32 >>> 8);
}
// update keys with byteValues
const updateKeys = (byteValue) => {
keys[0] = crc32update(keys[0], byteValue);
keys[1] += keys[0] & 0xff;
keys[1] = uMul(keys[1], 134775813) + 1;
keys[2] = crc32update(keys[2], keys[1] >>> 24);
}

// 1. Stage initialize key
const pass = (Buffer.isBuffer(pwd)) ? pwd : Buffer.from(pwd);
for(let i=0; i< pass.length; i++){
updateKeys(pass[i]);
}

// return decrypter function
return function (/*Buffer*/data){
if (!Buffer.isBuffer(data)){
throw 'decrypter needs Buffer'
}
// result - we create new Buffer for results
const result = Buffer.alloc(data.length);
let pos = 0;
// process input data
for(let c of data){
const k = (keys[2] | 2) >>> 0; // key
c ^= (uMul(k, k^1) >> 8) & 0xff; // decode
result[pos++] = c; // Save Value
updateKeys(c); // update keys with decoded byte
}
return result;
}
}

function decrypt(/*Buffer*/ data, /*Object*/header, /*String, Buffer*/ pwd){
if (!data || !Buffer.isBuffer(data) || data.length < 12) {
return Buffer.alloc(0);
}

// We Initialize and generate decrypting function
const decrypter = make_decrypter(pwd);

// check - for testing password
const check = header.crc >>> 24;
// decrypt salt what is always 12 bytes and is a part of file content
const testbyte = decrypter(data.slice(0, 12))[11];

// does password meet expectations
if (check !== testbyte){
throw 'ADM-ZIP: Wrong Password';
}

// decode content
return decrypter(data.slice(12));
}

module.exports = {decrypt};
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
"util",
"zipEntry.js",
"zipFile.js",
"MIT-LICENSE.txt"
"LICENSE"
],
"main": "adm-zip.js",
"repository": {
"type": "git",
"url": "https://github.com/cthackers/adm-zip.git"
},
"engines": {
"node": ">=0.3.0"
"node": ">=6.0"
},
"devDependencies": {
"chai": "^4.1.2",
Expand Down
4 changes: 2 additions & 2 deletions util/fattr.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ module.exports = function(/*String*/path) {
_obj.directory = _stat.isDirectory();
_obj.mtime = _stat.mtime;
_obj.atime = _stat.atime;
_obj.executable = !!(1 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0]));
_obj.readonly = !!(2 & parseInt ((_stat.mode & parseInt ("777", 8)).toString (8)[0]));
_obj.executable = (0o111 & _stat.mode) != 0; // file is executable who ever har right not just owner
_obj.readonly = (0o200 & _stat.mode) == 0; // readonly if owner has no write right
_obj.hidden = pth.basename(_path)[0] === ".";
} else {
console.warn("Invalid path: " + _path)
Expand Down
Loading

0 comments on commit a7e8932

Please sign in to comment.