2016-02-24 04:56:22 +00:00
|
|
|
/* jslint node: true */
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
// ENiGMA½
|
2016-08-06 22:30:56 +00:00
|
|
|
const Config = require('./config.js').config;
|
2016-02-24 04:56:22 +00:00
|
|
|
|
|
|
|
// base/modules
|
2016-08-06 22:30:56 +00:00
|
|
|
const fs = require('fs');
|
|
|
|
const _ = require('lodash');
|
|
|
|
const pty = require('ptyw.js');
|
2016-02-24 04:56:22 +00:00
|
|
|
|
|
|
|
module.exports = class ArchiveUtil {
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.archivers = {};
|
|
|
|
this.longestSignature = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
init() {
|
|
|
|
//
|
|
|
|
// Load configuration
|
|
|
|
//
|
|
|
|
if(_.has(Config, 'archivers')) {
|
|
|
|
Object.keys(Config.archivers).forEach(archKey => {
|
|
|
|
const arch = Config.archivers[archKey];
|
|
|
|
if(!_.isString(arch.sig) ||
|
|
|
|
!_.isString(arch.compressCmd) ||
|
|
|
|
!_.isString(arch.decompressCmd) ||
|
|
|
|
!_.isArray(arch.compressArgs) ||
|
|
|
|
!_.isArray(arch.decompressArgs))
|
|
|
|
{
|
|
|
|
// :TODO: log warning
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const archiver = {
|
|
|
|
compressCmd : arch.compressCmd,
|
|
|
|
compressArgs : arch.compressArgs,
|
|
|
|
decompressCmd : arch.decompressCmd,
|
|
|
|
decompressArgs : arch.decompressArgs,
|
|
|
|
sig : new Buffer(arch.sig, 'hex'),
|
|
|
|
offset : arch.offset || 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.archivers[archKey] = archiver;
|
|
|
|
|
|
|
|
if(archiver.offset + archiver.sig.length > this.longestSignature) {
|
|
|
|
this.longestSignature = archiver.offset + archiver.sig.length;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2016-02-29 05:04:03 +00:00
|
|
|
|
2016-03-04 05:54:32 +00:00
|
|
|
getArchiver(archType) {
|
2016-02-29 05:04:03 +00:00
|
|
|
if(!archType) {
|
2016-03-04 05:54:32 +00:00
|
|
|
return;
|
2016-02-29 05:04:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
archType = archType.toLowerCase();
|
2016-03-04 05:54:32 +00:00
|
|
|
return this.archivers[archType];
|
|
|
|
}
|
|
|
|
|
|
|
|
haveArchiver(archType) {
|
|
|
|
return this.getArchiver(archType) ? true : false;
|
2016-02-29 05:04:03 +00:00
|
|
|
}
|
2016-02-24 04:56:22 +00:00
|
|
|
|
|
|
|
detectType(path, cb) {
|
|
|
|
fs.open(path, 'r', (err, fd) => {
|
|
|
|
if(err) {
|
|
|
|
cb(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let buf = new Buffer(this.longestSignature);
|
|
|
|
fs.read(fd, buf, 0, buf.length, 0, (err, bytesRead) => {
|
|
|
|
if(err) {
|
2016-08-06 22:30:56 +00:00
|
|
|
return cb(err);
|
2016-02-24 04:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// return first match
|
2016-03-01 05:32:51 +00:00
|
|
|
const detected = _.findKey(this.archivers, arch => {
|
2016-02-24 04:56:22 +00:00
|
|
|
const lenNeeded = arch.offset + arch.sig.length;
|
|
|
|
|
2016-08-06 22:30:56 +00:00
|
|
|
if(bytesRead < lenNeeded) {
|
2016-02-24 04:56:22 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const comp = buf.slice(arch.offset, arch.offset + arch.sig.length);
|
|
|
|
return (arch.sig.equals(comp));
|
|
|
|
});
|
|
|
|
|
|
|
|
cb(detected ? null : new Error('Unknown type'), detected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-08-06 22:30:56 +00:00
|
|
|
spawnHandler(comp, action, cb) {
|
|
|
|
// pty.js doesn't currently give us a error when things fail,
|
|
|
|
// so we have this horrible, horrible hack:
|
|
|
|
let err;
|
|
|
|
comp.once('data', d => {
|
|
|
|
if(_.isString(d) && d.startsWith('execvp(3) failed.: No such file or directory')) {
|
|
|
|
err = new Error(`${action} failed: ${d.trim()}`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
comp.once('exit', exitCode => {
|
|
|
|
if(exitCode) {
|
|
|
|
return cb(new Error(`${action} failed with exit code: ${exitCode}`));
|
|
|
|
}
|
|
|
|
if(err) {
|
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
return cb(null);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-02-24 04:56:22 +00:00
|
|
|
compressTo(archType, archivePath, files, cb) {
|
2016-03-04 05:54:32 +00:00
|
|
|
const archiver = this.getArchiver(archType);
|
2016-02-29 05:04:03 +00:00
|
|
|
|
2016-02-24 04:56:22 +00:00
|
|
|
if(!archiver) {
|
2016-03-04 05:54:32 +00:00
|
|
|
return cb(new Error(`Unknown archive type: ${archType}`));
|
2016-02-24 04:56:22 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 05:54:32 +00:00
|
|
|
let args = _.clone(archiver.compressArgs); // don't muck with orig
|
2016-02-24 04:56:22 +00:00
|
|
|
for(let i = 0; i < args.length; ++i) {
|
|
|
|
args[i] = args[i].format({
|
|
|
|
archivePath : archivePath,
|
|
|
|
fileList : files.join(' '),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-03-04 05:54:32 +00:00
|
|
|
let comp = pty.spawn(archiver.compressCmd, args, this.getPtyOpts());
|
2016-02-24 04:56:22 +00:00
|
|
|
|
2016-08-06 22:30:56 +00:00
|
|
|
return this.spawnHandler(comp, 'Compression', cb);
|
2016-02-24 04:56:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extractTo(archivePath, extractPath, archType, cb) {
|
2016-03-04 05:54:32 +00:00
|
|
|
const archiver = this.getArchiver(archType);
|
|
|
|
|
|
|
|
if(!archiver) {
|
|
|
|
return cb(new Error(`Unknown archive type: ${archType}`));
|
|
|
|
}
|
|
|
|
|
|
|
|
let args = _.clone(archiver.decompressArgs); // don't muck with orig
|
|
|
|
for(let i = 0; i < args.length; ++i) {
|
|
|
|
args[i] = args[i].format({
|
|
|
|
archivePath : archivePath,
|
|
|
|
extractPath : extractPath,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let comp = pty.spawn(archiver.decompressCmd, args, this.getPtyOpts());
|
2016-08-06 22:30:56 +00:00
|
|
|
|
|
|
|
return this.spawnHandler(comp, 'Decompression', cb);
|
2016-03-04 05:54:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getPtyOpts() {
|
|
|
|
return {
|
|
|
|
// :TODO: cwd
|
|
|
|
name : 'enigma-archiver',
|
|
|
|
cols : 80,
|
|
|
|
rows : 24,
|
|
|
|
env : process.env,
|
|
|
|
};
|
2016-02-24 04:56:22 +00:00
|
|
|
}
|
2016-08-06 22:30:56 +00:00
|
|
|
};
|