* Additional helpers to MenuModule

* Gzip signature (.gz)
* Switch to sha-256 vs sha1 for internal file hashes
* Nearly complete callback / scan update support for scanFile()
* Fix data input issue after performing upload
* Support 'sz' recv (uploads)
This commit is contained in:
Bryan Ashby 2017-01-21 22:09:29 -07:00
parent fb176d3ab3
commit 8d51c7d47c
11 changed files with 349 additions and 195 deletions

View File

@ -278,6 +278,13 @@ function getDefaultConfig() {
exts : [ 'rar' ], exts : [ 'rar' ],
handler : '7Zip', handler : '7Zip',
desc : 'RAR Archive', desc : 'RAR Archive',
},
gzip : {
sig : '1f8b',
offset : 0,
exts : [ 'gz' ],
handler : '7Zip',
desc : 'Gzip Archive',
} }
} }
}, },
@ -294,7 +301,7 @@ function getDefaultConfig() {
], ],
recvCmd : 'rz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" recvCmd : 'rz', // Avail on Debian/Ubuntu based systems as the package "lrzsz"
recvArgs : [ recvArgs : [
'--zmodem', '--binary', '--restricted', // dumps to CWD which is set to {uploadDir} '--zmodem', '--binary', '--restricted', '--keep-uppercase', // dumps to CWD which is set to {uploadDir}
], ],
// :TODO: can we not just use --escape ? // :TODO: can we not just use --escape ?
escapeTelnet : true, // set to true to escape Telnet codes such as IAC escapeTelnet : true, // set to true to escape Telnet codes such as IAC

View File

@ -260,7 +260,7 @@ const DB_INIT_TABLE = {
`CREATE TABLE IF NOT EXISTS file ( `CREATE TABLE IF NOT EXISTS file (
file_id INTEGER PRIMARY KEY, file_id INTEGER PRIMARY KEY,
area_tag VARCHAR NOT NULL, area_tag VARCHAR NOT NULL,
file_sha1 VARCHAR NOT NULL, file_sha256 VARCHAR NOT NULL,
file_name, /* FTS @ file_fts */ file_name, /* FTS @ file_fts */
storage_tag VARCHAR NOT NULL, storage_tag VARCHAR NOT NULL,
desc, /* FTS @ file_fts */ desc, /* FTS @ file_fts */

View File

@ -160,14 +160,14 @@ function getFileEntryPath(fileEntry) {
} }
} }
function getExistingFileEntriesBySha1(sha1, cb) { function getExistingFileEntriesBySha256(sha256, cb) {
const entries = []; const entries = [];
FileDb.each( FileDb.each(
`SELECT file_id, area_tag `SELECT file_id, area_tag
FROM file FROM file
WHERE file_sha1=?;`, WHERE file_sha256=?;`,
[ sha1 ], [ sha256 ],
(err, fileRow) => { (err, fileRow) => {
if(fileRow) { if(fileRow) {
entries.push({ entries.push({
@ -237,14 +237,38 @@ function attemptSetEstimatedReleaseDate(fileEntry) {
} }
} }
function populateFileEntryWithArchive(fileEntry, filePath, archiveType, cb) { function populateFileEntryWithArchive(fileEntry, filePath, stepInfo, iterator, cb) {
const archiveUtil = ArchiveUtil.getInstance(); const archiveUtil = ArchiveUtil.getInstance();
const archiveType = fileEntry.meta.archive_type; // we set this previous to populateFileEntryWithArchive()
async.waterfall( async.waterfall(
[ [
function getArchiveFileList(callback) { function getArchiveFileList(callback) {
archiveUtil.listEntries(filePath, archiveType, (err, entries) => { stepInfo.step = 'archive_list_start';
return callback(null, entries || []); // ignore any errors here
iterator(err => {
if(err) {
return callback(err);
}
archiveUtil.listEntries(filePath, archiveType, (err, entries) => {
if(err) {
stepInfo.step = 'archive_list_failed';
} else {
stepInfo.step = 'archive_list_finish';
stepInfo.archiveEntries = entries || [];
}
iterator(iterErr => {
return callback( iterErr, entries || [] ); // ignore original |err| here
});
});
});
},
function processDescFilesStart(entries, callback) {
stepInfo.step = 'desc_files_start';
iterator(err => {
return callback(err, entries);
}); });
}, },
function extractDescFiles(entries, callback) { function extractDescFiles(entries, callback) {
@ -320,7 +344,11 @@ function populateFileEntryWithArchive(fileEntry, filePath, archiveType, cb) {
function attemptReleaseYearEstimation(callback) { function attemptReleaseYearEstimation(callback) {
attemptSetEstimatedReleaseDate(fileEntry); attemptSetEstimatedReleaseDate(fileEntry);
return callback(null); return callback(null);
} },
function processDescFilesFinish(callback) {
stepInfo.step = 'desc_files_finish';
return iterator(callback);
},
], ],
err => { err => {
return cb(err); return cb(err);
@ -328,7 +356,7 @@ function populateFileEntryWithArchive(fileEntry, filePath, archiveType, cb) {
); );
} }
function populateFileEntryNonArchive(fileEntry, filePath, archiveType, cb) { function populateFileEntryNonArchive(fileEntry, filePath, stepInfo, iterator, cb) {
// :TODO: implement me! // :TODO: implement me!
return cb(null); return cb(null);
} }
@ -352,11 +380,17 @@ function updateFileEntry(fileEntry, filePath, cb) {
} }
function scanFile(filePath, options, cb) { const HASH_NAMES = [ 'sha1', 'sha256', 'md5', 'crc32' ];
if(_.isFunction(options) && !cb) { function scanFile(filePath, options, iterator, cb) {
cb = options;
options = {}; if(3 === arguments.length && _.isFunction(iterator)) {
cb = iterator;
iterator = null;
} else if(2 === arguments.length && _.isFunction(options)) {
cb = options;
iterator = null;
options = {};
} }
const fileEntry = new FileEntry({ const fileEntry = new FileEntry({
@ -367,42 +401,96 @@ function scanFile(filePath, options, cb) {
storageTag : options.storageTag, storageTag : options.storageTag,
}); });
const stepInfo = {
filePath : filePath,
fileName : paths.basename(filePath),
};
function callIter(next) {
if(iterator) {
return iterator(stepInfo, next);
} else {
return next(null);
}
}
function readErrorCallIter(origError, next) {
stepInfo.step = 'read_error';
stepInfo.error = origError.message;
callIter( () => {
return next(origError);
});
}
async.waterfall( async.waterfall(
[ [
function startScan(callback) {
fs.stat(filePath, (err, stats) => {
if(err) {
return readErrorCallIter(err, callback);
}
stepInfo.step = 'start';
stepInfo.byteSize = fileEntry.meta.byte_size = stats.size;
return callIter(callback);
});
},
function processPhysicalFileGeneric(callback) { function processPhysicalFileGeneric(callback) {
let byteSize = 0; stepInfo.bytesProcessed = 0;
const sha1 = crypto.createHash('sha1');
const sha256 = crypto.createHash('sha256'); const hashes = {
const md5 = crypto.createHash('md5'); sha1 : crypto.createHash('sha1'),
const crc32 = new CRC32(); sha256 : crypto.createHash('sha256'),
md5 : crypto.createHash('md5'),
crc32 : new CRC32(),
};
const stream = fs.createReadStream(filePath); const stream = fs.createReadStream(filePath);
stream.on('data', data => { stream.on('data', data => {
byteSize += data.length; stream.pause(); // until iterator compeltes
sha1.update(data); stepInfo.bytesProcessed += data.length;
sha256.update(data); stepInfo.step = 'hash_update';
md5.update(data);
crc32.update(data); callIter(err => {
if(err) {
stream.destroy(); // cancel read
return callback(err);
}
async.each( HASH_NAMES, (hashName, nextHash) => {
hashes[hashName].update(data);
return nextHash(null);
}, () => {
return stream.resume();
});
});
}); });
stream.on('end', () => { stream.on('end', () => {
fileEntry.meta.byte_size = byteSize; fileEntry.meta.byte_size = stepInfo.bytesProcessed;
// sha-1 is in basic file entry async.each(HASH_NAMES, (hashName, nextHash) => {
fileEntry.fileSha1 = sha1.digest('hex'); if('sha256' === hashName) {
stepInfo.sha256 = fileEntry.fileSha256 = hashes.sha256.digest('hex');
} else if('sha1' === hashName || 'md5' === hashName) {
stepInfo[hashName] = fileEntry.meta[`file_${hashName}`] = hashes[hashName].digest('hex');
} else if('crc32' === hashName) {
stepInfo.crc32 = fileEntry.meta.crc32 = hashes.crc32.finalize().toString(16);
}
// others are meta return nextHash(null);
fileEntry.meta.file_sha256 = sha256.digest('hex'); }, () => {
fileEntry.meta.file_md5 = md5.digest('hex'); stepInfo.step = 'hash_finish';
fileEntry.meta.file_crc32 = crc32.finalize().toString(16); return callIter(callback);
});
return callback(null);
}); });
stream.on('error', err => { stream.on('error', err => {
return callback(err); return readErrorCallIter(err, callback);
}); });
}, },
function processPhysicalFileByType(callback) { function processPhysicalFileByType(callback) {
@ -413,9 +501,9 @@ function scanFile(filePath, options, cb) {
// save this off // save this off
fileEntry.meta.archive_type = archiveType; fileEntry.meta.archive_type = archiveType;
populateFileEntryWithArchive(fileEntry, filePath, archiveType, err => { populateFileEntryWithArchive(fileEntry, filePath, stepInfo, callIter, err => {
if(err) { if(err) {
populateFileEntryNonArchive(fileEntry, filePath, err => { populateFileEntryNonArchive(fileEntry, filePath, stepInfo, callIter, err => {
// :TODO: log err // :TODO: log err
return callback(null); // ignore err return callback(null); // ignore err
}); });
@ -424,7 +512,7 @@ function scanFile(filePath, options, cb) {
} }
}); });
} else { } else {
populateFileEntryNonArchive(fileEntry, filePath, err => { populateFileEntryNonArchive(fileEntry, filePath, stepInfo, callIter, err => {
// :TODO: log err // :TODO: log err
return callback(null); // ignore err return callback(null); // ignore err
}); });
@ -432,92 +520,21 @@ function scanFile(filePath, options, cb) {
}); });
}, },
function fetchExistingEntry(callback) { function fetchExistingEntry(callback) {
getExistingFileEntriesBySha1(fileEntry.fileSha1, (err, existingEntries) => { getExistingFileEntriesBySha256(fileEntry.fileSha256, (err, dupeEntries) => {
return callback(err, existingEntries); return callback(err, dupeEntries);
}); });
} }
], ],
(err, existingEntries) => { (err, dupeEntries) => {
if(err) { if(err) {
return cb(err); return cb(err);
} }
return cb(null, fileEntry, existingEntries); return cb(null, fileEntry, dupeEntries);
} }
); );
} }
/*
function addOrUpdateFileEntry(areaInfo, storageLocation, fileName, options, cb) {
const fileEntry = new FileEntry({
areaTag : areaInfo.areaTag,
meta : options.meta,
hashTags : options.hashTags, // Set() or Array
fileName : fileName,
storageTag : storageLocation.storageTag,
});
const filePath = paths.join(storageLocation.dir, fileName);
async.waterfall(
[
function processPhysicalFile(callback) {
let byteSize = 0;
const sha1 = crypto.createHash('sha1');
const sha256 = crypto.createHash('sha256');
const md5 = crypto.createHash('md5');
const crc32 = new CRC32();
const stream = fs.createReadStream(filePath);
stream.on('data', data => {
byteSize += data.length;
sha1.update(data);
sha256.update(data);
md5.update(data);
crc32.update(data);
});
stream.on('end', () => {
fileEntry.meta.byte_size = byteSize;
// sha-1 is in basic file entry
fileEntry.fileSha1 = sha1.digest('hex');
// others are meta
fileEntry.meta.file_sha256 = sha256.digest('hex');
fileEntry.meta.file_md5 = md5.digest('hex');
fileEntry.meta.file_crc32 = crc32.finalize().toString(16);
return callback(null);
});
stream.on('error', err => {
return callback(err);
});
},
function fetchExistingEntry(callback) {
getExistingFileEntriesBySha1(fileEntry.fileSha1, (err, existingEntries) => {
return callback(err, existingEntries);
});
},
function addOrUpdate(existingEntries, callback) {
if(existingEntries.length > 0) {
} else {
return addNewFileEntry(fileEntry, filePath, callback);
}
},
],
err => {
return cb(err);
}
);
}
*/
function scanFileAreaForChanges(areaInfo, cb) { function scanFileAreaForChanges(areaInfo, cb) {
const storageLocations = getAreaStorageLocations(areaInfo); const storageLocations = getAreaStorageLocations(areaInfo);
@ -551,13 +568,13 @@ function scanFileAreaForChanges(areaInfo, cb) {
areaTag : areaInfo.areaTag, areaTag : areaInfo.areaTag,
storageTag : storageLoc.storageTag storageTag : storageLoc.storageTag
}, },
(err, fileEntry, existingEntries) => { (err, fileEntry, dupeEntries) => {
if(err) { if(err) {
// :TODO: Log me!!! // :TODO: Log me!!!
return nextFile(null); // try next anyway return nextFile(null); // try next anyway
} }
if(existingEntries.length > 0) { if(dupeEntries.length > 0) {
// :TODO: Handle duplidates -- what to do here??? // :TODO: Handle duplidates -- what to do here???
} else { } else {
addNewFileEntry(fileEntry, fullPath, err => { addNewFileEntry(fileEntry, fullPath, err => {

View File

@ -12,7 +12,7 @@ const _ = require('lodash');
const paths = require('path'); const paths = require('path');
const FILE_TABLE_MEMBERS = [ const FILE_TABLE_MEMBERS = [
'file_id', 'area_tag', 'file_sha1', 'file_name', 'storage_tag', 'file_id', 'area_tag', 'file_sha256', 'file_name', 'storage_tag',
'desc', 'desc_long', 'upload_timestamp' 'desc', 'desc_long', 'upload_timestamp'
]; ];
@ -21,7 +21,7 @@ const FILE_WELL_KNOWN_META = {
upload_by_username : null, upload_by_username : null,
upload_by_user_id : null, upload_by_user_id : null,
file_md5 : null, file_md5 : null,
file_sha256 : null, file_sha1 : null,
file_crc32 : null, file_crc32 : null,
est_release_year : (y) => parseInt(y) || new Date().getFullYear(), est_release_year : (y) => parseInt(y) || new Date().getFullYear(),
dl_count : (d) => parseInt(d) || 0, dl_count : (d) => parseInt(d) || 0,
@ -100,9 +100,9 @@ module.exports = class FileEntry {
}, },
function storeEntry(callback) { function storeEntry(callback) {
fileDb.run( fileDb.run(
`REPLACE INTO file (area_tag, file_sha1, file_name, storage_tag, desc, desc_long, upload_timestamp) `REPLACE INTO file (area_tag, file_sha256, file_name, storage_tag, desc, desc_long, upload_timestamp)
VALUES(?, ?, ?, ?, ?, ?, ?);`, VALUES(?, ?, ?, ?, ?, ?, ?);`,
[ self.areaTag, self.fileSha1, self.fileName, self.storageTag, self.desc, self.descLong, getISOTimestampString() ], [ self.areaTag, self.fileSha256, self.fileName, self.storageTag, self.desc, self.descLong, getISOTimestampString() ],
function inserted(err) { // use non-arrow func for 'this' scope / lastID function inserted(err) { // use non-arrow func for 'this' scope / lastID
if(!err) { if(!err) {
self.fileId = this.lastID; self.fileId = this.lastID;

View File

@ -1,17 +1,18 @@
/* jslint node: true */ /* jslint node: true */
'use strict'; 'use strict';
var PluginModule = require('./plugin_module.js').PluginModule; const PluginModule = require('./plugin_module.js').PluginModule;
var theme = require('./theme.js'); const theme = require('./theme.js');
var ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
var ViewController = require('./view_controller.js').ViewController; const ViewController = require('./view_controller.js').ViewController;
var menuUtil = require('./menu_util.js'); const menuUtil = require('./menu_util.js');
var Config = require('./config.js').config; const Config = require('./config.js').config;
const stringFormat = require('../core/string_format.js');
// deps // deps
var async = require('async'); const async = require('async');
var assert = require('assert'); const assert = require('assert');
var _ = require('lodash'); const _ = require('lodash');
exports.MenuModule = MenuModule; exports.MenuModule = MenuModule;
@ -387,3 +388,27 @@ MenuModule.prototype.prepViewControllerWithArt = function(name, formId, options,
} }
); );
}; };
MenuModule.prototype.setViewText = function(formName, mciId, text) {
const view = this.viewControllers[formName].getView(mciId);
if(view) {
view.setText(text);
}
};
MenuModule.prototype.updateCustomViewTextsWithFilter = function(formName, startId, fmtObj, filter) {
let textView;
let customMciId = startId;
const config = this.menuConfig.config;
while( (textView = this.viewControllers[formName].getView(customMciId)) ) {
const key = `${formName}InfoFormat${customMciId}`;
const format = config[key];
if(format && (!filter || filter.find(f => format.indexOf(f) > - 1))) {
textView.setText(stringFormat(format, fmtObj));
}
++customMciId;
}
};

View File

@ -195,6 +195,17 @@ function getPredefinedMCIValue(client, code) {
// //
// :TODO: System stat log for total ul/dl, total ul/dl bytes // :TODO: System stat log for total ul/dl, total ul/dl bytes
// :TODO: PT - Messages posted *today* (Obv/2)
// :TODO: NT - New users today (Obv/2)
// :TODO: CT - Calls *today* (Obv/2)
// :TODO: TF - Total files on the system (Obv/2)
// :TODO: FT - Files uploaded/added *today* (Obv/2)
// :TODO: DD - Files downloaded *today* (iNiQUiTY)
// :TODO: TP - total message/posts on the system (Obv/2)
// :TODO: LC - name of last caller to system (Obv/2)
// :TODO: TZ - Average *system* post/call ratio (iNiQUiTY)
// //
// Special handling for XY // Special handling for XY
// //

View File

@ -439,12 +439,12 @@ function TelnetClient(input, output) {
}; };
this.setTemporaryDataHandler = function(handler) { this.setTemporaryDataHandler = function(handler) {
this.input.removeAllListeners(); this.input.removeAllListeners('data');
this.input.on('data', handler); this.input.on('data', handler);
}; };
this.restoreDataHandler = function() { this.restoreDataHandler = function() {
this.input.removeAllListeners(); this.input.removeAllListeners('data');
this.input.on('data', this.dataHandler); this.input.on('data', this.dataHandler);
}; };

View File

@ -351,7 +351,13 @@ exports.getModule = class TransferFileModule extends MenuModule {
}); });
this.client.setTemporaryDataHandler(data => { this.client.setTemporaryDataHandler(data => {
externalProc.write(data); // needed for things like sz/rz
if(external.escapeTelnet) {
const tmp = data.toString('binary').replace(/\xff{2}/g, '\xff'); // de-escape
externalProc.write(new Buffer(tmp, 'binary'));
} else {
externalProc.write(data);
}
}); });
//this.client.term.output.pipe(externalProc); //this.client.term.output.pipe(externalProc);
@ -359,7 +365,7 @@ exports.getModule = class TransferFileModule extends MenuModule {
externalProc.on('data', data => { externalProc.on('data', data => {
// needed for things like sz/rz // needed for things like sz/rz
if(external.escapeTelnet) { if(external.escapeTelnet) {
const tmp = data.toString('binary').replace(/\xff/g, '\xff\xff'); const tmp = data.toString('binary').replace(/\xff/g, '\xff\xff'); // escape
this.client.term.rawWrite(new Buffer(tmp, 'binary')); this.client.term.rawWrite(new Buffer(tmp, 'binary'));
} else { } else {
this.client.term.rawWrite(data); this.client.term.rawWrite(data);
@ -484,7 +490,6 @@ exports.getModule = class TransferFileModule extends MenuModule {
StatLog.incrementSystemStat('ul_total_count', uploadCount); StatLog.incrementSystemStat('ul_total_count', uploadCount);
StatLog.incrementSystemStat('ul_total_bytes', uploadBytes); StatLog.incrementSystemStat('ul_total_bytes', uploadBytes);
return cb(null); return cb(null);
}); });
} }
@ -556,12 +561,16 @@ exports.getModule = class TransferFileModule extends MenuModule {
self.client.log.warn( { error : err.message }, 'File transfer error'); self.client.log.warn( { error : err.message }, 'File transfer error');
} }
return self.prevMenu();
/*
// Wait for a key press - attempt to avoid issues with some terminals after xfer // Wait for a key press - attempt to avoid issues with some terminals after xfer
// :TODO: display ANSI if it exists else prompt -- look @ Obv/2 for filename // :TODO: display ANSI if it exists else prompt -- look @ Obv/2 for filename
self.client.term.pipeWrite('|00|07\nTransfer(s) complete. Press a key\n'); self.client.term.pipeWrite('|00|07\nTransfer(s) complete. Press a key\n');
self.client.waitForKeyPress( () => { self.client.waitForKeyPress( () => {
return self.prevMenu(); return self.prevMenu();
}); });
*/
} }
); );
} }

View File

@ -1,10 +1,7 @@
/* jslint node: true */ /* jslint node: true */
'use strict'; 'use strict';
let uuid = require('node-uuid'); const createHash = require('crypto').createHash;
let assert = require('assert');
let _ = require('lodash');
let createHash = require('crypto').createHash;
exports.createNamedUUID = createNamedUUID; exports.createNamedUUID = createNamedUUID;
@ -13,9 +10,9 @@ function createNamedUUID(namespaceUuid, key) {
// v5 UUID generation code based on the work here: // v5 UUID generation code based on the work here:
// https://github.com/download13/uuidv5/blob/master/uuid.js // https://github.com/download13/uuidv5/blob/master/uuid.js
// //
if(!Buffer.isBuffer(namespaceUuid)) { if(!Buffer.isBuffer(namespaceUuid)) {
namespaceUuid = new Buffer(namespaceUuid); namespaceUuid = new Buffer(namespaceUuid);
} }
if(!Buffer.isBuffer(key)) { if(!Buffer.isBuffer(key)) {
key = new Buffer(key); key = new Buffer(key);

View File

@ -46,26 +46,30 @@ const FormIds = {
const MciViewIds = { const MciViewIds = {
browse : { browse : {
desc : 1, desc : 1,
navMenu : 2, navMenu : 2,
// 10+ = customs
customRangeStart : 10, // 10+ = customs
}, },
details : { details : {
navMenu : 1, navMenu : 1,
infoXyTop : 2, // %XY starting position for info area infoXyTop : 2, // %XY starting position for info area
infoXyBottom : 3, infoXyBottom : 3,
// 10+ = customs
customRangeStart : 10, // 10+ = customs
}, },
detailsGeneral : { detailsGeneral : {
// 10+ = customs customRangeStart : 10, // 10+ = customs
}, },
detailsNfo : { detailsNfo : {
nfo : 1, nfo : 1,
// 10+ = customs
customRangeStart : 10, // 10+ = customs
}, },
detailsFileList : { detailsFileList : {
fileList : 1, fileList : 1,
// 10+ = customs
customRangeStart : 10, // 10+ = customs
}, },
}; };
@ -163,7 +167,7 @@ exports.getModule = class FileAreaList extends MenuModule {
areaTag : currEntry.areaTag, areaTag : currEntry.areaTag,
areaName : area.name || 'N/A', areaName : area.name || 'N/A',
areaDesc : area.desc || 'N/A', areaDesc : area.desc || 'N/A',
fileSha1 : currEntry.fileSha1, fileSha256 : currEntry.fileSha256,
fileName : currEntry.fileName, fileName : currEntry.fileName,
desc : currEntry.desc || '', desc : currEntry.desc || '',
descLong : currEntry.descLong || '', descLong : currEntry.descLong || '',
@ -220,9 +224,10 @@ exports.getModule = class FileAreaList extends MenuModule {
} }
populateCustomLabels(category, startId) { populateCustomLabels(category, startId) {
return this.updateCustomLabelsWithFilter(category, startId); return this.updateCustomViewTextsWithFilter(category, startId, this.currentFileEntry.entryInfo);
} }
/*
updateCustomLabelsWithFilter(category, startId, filter) { updateCustomLabelsWithFilter(category, startId, filter) {
let textView; let textView;
let customMciId = startId; let customMciId = startId;
@ -239,6 +244,7 @@ exports.getModule = class FileAreaList extends MenuModule {
++customMciId; ++customMciId;
} }
} }
*/
displayArtAndPrepViewController(name, options, cb) { displayArtAndPrepViewController(name, options, cb) {
const self = this; const self = this;
@ -342,7 +348,7 @@ exports.getModule = class FileAreaList extends MenuModule {
descView.setText( self.currentFileEntry.desc ); descView.setText( self.currentFileEntry.desc );
self.updateQueueIndicator(); self.updateQueueIndicator();
self.populateCustomLabels('browse', 10); self.populateCustomLabels('browse', MciViewIds.browse.customRangeStart);
return callback(null); return callback(null);
} }
@ -350,7 +356,7 @@ exports.getModule = class FileAreaList extends MenuModule {
} }
} else { } else {
self.updateQueueIndicator(); self.updateQueueIndicator();
self.populateCustomLabels('browse', 10); self.populateCustomLabels('browse', MciViewIds.browse.customRangeStart);
return callback(null); return callback(null);
} }
@ -373,7 +379,7 @@ exports.getModule = class FileAreaList extends MenuModule {
return self.displayArtAndPrepViewController('details', { clearScreen : true }, callback); return self.displayArtAndPrepViewController('details', { clearScreen : true }, callback);
}, },
function populateViews(callback) { function populateViews(callback) {
self.populateCustomLabels('details', 10); self.populateCustomLabels('details', MciViewIds.details.customRangeStart);
return callback(null); return callback(null);
}, },
function prepSection(callback) { function prepSection(callback) {
@ -438,7 +444,11 @@ exports.getModule = class FileAreaList extends MenuModule {
); );
}, },
function updateActiveViews(callback) { function updateActiveViews(callback) {
self.updateCustomLabelsWithFilter( 'browse', 10, [ '{webDlLink}', '{webDlExpire}' ] ); self.updateCustomViewTextsWithFilter(
'browse',
MciViewIds.browse.customRangeStart, self.currentFileEntry.entryInfo,
[ '{webDlLink}', '{webDlExpire}' ]
);
return callback(null); return callback(null);
} }
], ],
@ -458,7 +468,12 @@ exports.getModule = class FileAreaList extends MenuModule {
isNotQueuedIndicator isNotQueuedIndicator
); );
this.updateCustomLabelsWithFilter( 'browse', 10, [ '{isQueued}' ] ); this.updateCustomViewTextsWithFilter(
'browse',
MciViewIds.browse.customRangeStart,
this.currentFileEntry.entryInfo,
[ '{isQueued}' ]
);
} }
cacheArchiveEntries(cb) { cacheArchiveEntries(cb) {
@ -564,7 +579,7 @@ exports.getModule = class FileAreaList extends MenuModule {
break; break;
} }
self.populateCustomLabels(name, 10); self.populateCustomLabels(name, MciViewIds[name].customRangeStart);
return callback(null); return callback(null);
} }
], ],

View File

@ -40,15 +40,16 @@ const MciViewIds = {
}, },
processing : { processing : {
// 10+ = customs stepIndicator : 1,
customRangeStart : 10, // 10+ = customs
}, },
fileDetails : { fileDetails : {
desc : 1, // defaults to 'desc' (e.g. from FILE_ID.DIZ) desc : 1, // defaults to 'desc' (e.g. from FILE_ID.DIZ)
tags : 2, // tag(s) for item tags : 2, // tag(s) for item
estYear : 3, estYear : 3,
accept : 4, // accept fields & continue accept : 4, // accept fields & continue
// 10+ = customs customRangeStart : 10, // 10+ = customs
} }
}; };
@ -151,8 +152,66 @@ exports.getModule = class UploadModule extends MenuModule {
if(this.isFileTransferComplete()) { if(this.isFileTransferComplete()) {
return this.processUploadedFiles(); return this.processUploadedFiles();
} }
}
updateScanStepInfoViews(stepInfo) {
// :TODO: add some blinking (e.g. toggle items) indicators - see OBV.DOC
const fmtObj = Object.assign( {}, stepInfo);
let stepIndicatorFmt = '';
switch(stepInfo.step) {
case 'start' :
stepIndicatorFmt = this.menuConfig.config.scanningStartFormat || 'Scanning {fileName}';
break;
case 'hash_update' :
stepIndicatorFmt = this.menuConfig.calcHashFormat || 'Calculating hash/checksums: {calcHashPercent}%';
this.scanStatus.hashUpdateCount += 1;
fmtObj.calcHashPercent = Math.round(((stepInfo.bytesProcessed / stepInfo.byteSize) * 100)).toString();
if(this.scanStatus.hashUpdateCount % 2) {
fmtObj.calcHashIndicator = this.menuConfig.config.hashUpdateIndicator1Fmt || '-';
} else {
fmtObj.calcHashIndicator = this.menuConfig.config.hashUpdateIndicator2Fmt || '*';
}
break;
case 'hash_finish' :
stepIndicatorFmt = this.menuConfig.calcHashCompleteFormat || 'Finished calculating hash/checksums';
break;
case 'archive_list_start' :
stepIndicatorFmt = this.menuConfig.extractArchiveListFormat || 'Extracting archive list';
break;
case 'archive_list_finish' :
fmtObj.archivedFileCount = stepInfo.archiveEntries.length;
stepIndicatorFmt = this.menuConfig.extractArchiveListFinishFormat || 'Archive list extracted ({archivedFileCount} files)';
break;
case 'archive_list_failed' :
stepIndicatorFmt = this.menuConfig.extractArchiveListFailedFormat || 'Archive list extraction failed';
break;
case 'desc_files_start' :
stepIndicatorFmt = this.menuConfig.processingDescFilesFormat || 'Processing description files';
break;
case 'desc_files_finish' :
stepIndicatorFmt = this.menuConfig.processingDescFilesFinishFormat || 'Finished processing description files';
break;
}
const stepIndicatorText = stringFormat(stepIndicatorFmt, fmtObj);
if(this.hasProcessingArt) {
this.setViewText('processing', MciViewIds.processing.stepIndicator, stepIndicatorText);
this.updateCustomViewTextsWithFilter('processing', MciViewIds.processing.customRangeStart, fmtObj);
} else {
this.client.term.pipeWrite(`${stepIndicatorText}\n`);
}
} }
scanFiles(cb) { scanFiles(cb) {
@ -166,35 +225,36 @@ exports.getModule = class UploadModule extends MenuModule {
async.eachSeries(this.recvFilePaths, (filePath, nextFilePath) => { async.eachSeries(this.recvFilePaths, (filePath, nextFilePath) => {
// :TODO: virus scanning/etc. should occur around here // :TODO: virus scanning/etc. should occur around here
// :TODO: update scanning status art or display line "scanning {fileName}..." type of thing self.scanStatus = {
hashUpdateCount : 0,
};
self.client.term.pipeWrite(`|00|07\nScanning ${paths.basename(filePath)}...`); const scanOpts = {
areaTag : self.areaInfo.areaTag,
storageTag : self.areaInfo.storageTags[0],
};
scanFile( function handleScanStep(stepInfo, nextScanStep) {
filePath, self.updateScanStepInfoViews(stepInfo);
{ return nextScanStep(null);
areaTag : self.areaInfo.areaTag, }
storageTag : self.areaInfo.storageTags[0],
},
(err, fileEntry, existingEntries) => {
if(err) {
return nextFilePath(err);
}
self.client.term.pipeWrite(' done\n'); scanFile(filePath, scanOpts, handleScanStep, (err, fileEntry, dupeEntries) => {
if(err) {
// new or dupe? return nextFilePath(err);
if(existingEntries.length > 0) {
// 1:n dupes found
results.dupes = results.dupes.concat(existingEntries);
} else {
// new one
results.newEntries.push(fileEntry);
}
return nextFilePath(null);
} }
);
// new or dupe?
if(dupeEntries.length > 0) {
// 1:n dupes found
results.dupes = results.dupes.concat(dupeEntries);
} else {
// new one
results.newEntries.push(fileEntry);
}
return nextFilePath(null);
});
}, err => { }, err => {
return cb(err, results); return cb(err, results);
}); });
@ -258,7 +318,11 @@ exports.getModule = class UploadModule extends MenuModule {
} }
], ],
err => { err => {
console.log('eh'); // :TODO: remove me :) if(err) {
self.client.log.warn('File upload error encountered', { error : err.message } );
}
return self.prevMenu();
} }
); );
} }
@ -312,8 +376,17 @@ exports.getModule = class UploadModule extends MenuModule {
} }
displayProcessingPage(cb) { displayProcessingPage(cb) {
// :TODO: If art is supplied, display & start processing + update status/etc.; if no art, we'll just write each status update on a new line return this.prepViewControllerWithArt(
return cb(null); 'processing',
FormIds.processing,
{ clearScreen : true, trailingLF : false },
err => {
// note: this art is not required
this.hasProcessingArt = !err;
return cb(null);
}
);
} }
fileEntryHasDetectedDesc(fileEntry) { fileEntryHasDetectedDesc(fileEntry) {