From 8261881e3e1278fc5898c682bd491bfc7aef16b8 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 31 Jan 2017 23:10:17 -0700 Subject: [PATCH] Initial support for non-blind aka non-batch uploads --- core/config.js | 6 +- core/enig_error.js | 1 + core/transfer_file.js | 49 +++++++++-- mods/file_transfer_protocol_select.js | 15 +++- mods/upload.js | 112 +++++++++++++++----------- 5 files changed, 123 insertions(+), 60 deletions(-) diff --git a/core/config.js b/core/config.js index 24490237..816149e1 100644 --- a/core/config.js +++ b/core/config.js @@ -379,7 +379,6 @@ function getDefaultConfig() { ], // :TODO: can we not just use --escape ? escapeTelnet : true, // set to true to escape Telnet codes such as IAC - supportsBatch : true, } }, @@ -396,8 +395,10 @@ function getDefaultConfig() { recvArgs : [ '-telnet', '-8', 'rz', '{uploadDir}' ], + recvArgsNonBatch : [ + '-telnet', '-8', 'rz', '{fileName}' + ], escapeTelnet : false, // -telnet option does this for us - supportsBatch : true, } } @@ -478,6 +479,7 @@ function getDefaultConfig() { '\\b[0-3]?[0-9][\\-\\/\\.](?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december)[\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})\\b', '\\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december),?\\s[0-9]+(?:st|nd|rd|th)?,?\\s((?:[0-9]{2})?[0-9]{2})\\b', // November 29th, 1997 // :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc. + // :TODO: "Copyright YYYY someone" ], web : { diff --git a/core/enig_error.js b/core/enig_error.js index 103a98b2..27453227 100644 --- a/core/enig_error.js +++ b/core/enig_error.js @@ -29,6 +29,7 @@ exports.Errors = { Invalid : (reason, reasonCode) => new EnigError('Invalid', -32004, reason, reasonCode), ExternalProcess : (reason, reasonCode) => new EnigError('External process error', -32005, reason, reasonCode), MissingConfig : (reason, reasonCode) => new EnigError('Missing configuration', -32006, reason, reasonCode), + UnexpectedState : (reason, reasonCode) => new EnigError('Unexpected state', -32007, reason, reasonCode), }; exports.ErrorReasons = { diff --git a/core/transfer_file.js b/core/transfer_file.js index 20cddeac..406acfaa 100644 --- a/core/transfer_file.js +++ b/core/transfer_file.js @@ -61,7 +61,7 @@ exports.getModule = class TransferFileModule extends MenuModule { } if(options.extraArgs.recvFileName) { - this.recvFileName = options.extraArgs.recvFiles; + this.recvFileName = options.extraArgs.recvFileName; } if(options.extraArgs.recvDirectory) { @@ -117,6 +117,28 @@ exports.getModule = class TransferFileModule extends MenuModule { } } + sendFiles(cb) { + // assume *sending* can always batch + // :TODO: Look into this further + const allFiles = this.sendQueue.map(f => f.path); + this.executeExternalProtocolHandlerForSend(allFiles, err => { + if(err) { + this.client.log.warn( { files : allFiles, error : err.message }, 'Error sending file(s)' ); + } else { + const sentFiles = []; + this.sendQueue.forEach(f => { + f.sent = true; + sentFiles.push(f.path); + + }); + + this.client.log.info( { sentFiles : sentFiles }, `Successfully sent ${sentFiles.length} file(s)` ); + } + return cb(err); + }); + } + + /* sendFiles(cb) { // :TODO: built in/native protocol support @@ -155,6 +177,7 @@ exports.getModule = class TransferFileModule extends MenuModule { }); } } + */ moveFileWithCollisionHandling(src, dst, cb) { // @@ -208,11 +231,23 @@ exports.getModule = class TransferFileModule extends MenuModule { this.recvFilePaths = []; if(this.recvFileName) { + // // file name specified - we expect a single file in |this.recvDirectory| - - // :TODO: support non-blind: Move file to dest path, add to recvFilePaths, etc. + // by the name of |this.recvFileName| + // + const recvFullPath = paths.join(this.recvDirectory, this.recvFileName); + fs.stat(recvFullPath, (err, stats) => { + if(err) { + return cb(err); + } - return cb(null); + if(!stats.isFile()) { + return cb(Errors.Invalid('Expected file entry in recv directory')); + } + + this.recvFilePaths.push(recvFullPath); + return cb(null); + }); } else { // // Blind Upload (recv): files in |this.recvDirectory| should be named appropriately already @@ -254,8 +289,7 @@ exports.getModule = class TransferFileModule extends MenuModule { } prepAndBuildSendArgs(filePaths, cb) { - const external = this.protocolConfig.external; - const externalArgs = external[`${this.direction}Args`]; + const externalArgs = this.protocolConfig.external['sendArgs']; async.waterfall( [ @@ -300,7 +334,8 @@ exports.getModule = class TransferFileModule extends MenuModule { } prepAndBuildRecvArgs(cb) { - const externalArgs = this.protocolConfig.external[`${this.direction}Args`]; + const argsKey = this.recvFileName ? 'recvArgsNonBatch' : 'recvArgs'; + const externalArgs = this.protocolConfig.external[argsKey]; const args = externalArgs.map(arg => stringFormat(arg, { uploadDir : this.recvDirectory, fileName : this.recvFileName || '', diff --git a/mods/file_transfer_protocol_select.js b/mods/file_transfer_protocol_select.js index 1d250d2e..a25b8709 100644 --- a/mods/file_transfer_protocol_select.js +++ b/mods/file_transfer_protocol_select.js @@ -36,8 +36,6 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { this.config.direction = this.config.direction || 'send'; - this.loadAvailProtocols(); - this.extraArgs = options.extraArgs; if(_.has(options, 'lastMenuResult.sentFileIds')) { @@ -50,6 +48,8 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { this.fallbackOnly = options.lastMenuResult ? true : false; + this.loadAvailProtocols(); + this.menuMethods = { selectProtocol : (formData, extraArgs, cb) => { const protocol = this.protocols[formData.value.protocol]; @@ -130,12 +130,21 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { loadAvailProtocols() { this.protocols = _.map(Config.fileTransferProtocols, (protInfo, protocol) => { - return { + return { protocol : protocol, name : protInfo.name, + hasBatch : _.has(protInfo, 'external.recvArgs'), + hasNonBatch : _.has(protInfo, 'external.recvArgsNonBatch'), }; }); + // Filter out batch vs non-batch only protocols + if(this.extraArgs.recvFileName) { // non-batch aka non-blind + this.protocols = this.protocols.filter( prot => prot.hasNonBatch ); + } else { + this.protocols = this.protocols.filter( prot => prot.hasBatch ); + } + this.protocols.sort( (a, b) => a.name.localeCompare(b.name) ); } }; diff --git a/mods/upload.js b/mods/upload.js index 748ecf9c..4169f34b 100644 --- a/mods/upload.js +++ b/mods/upload.js @@ -11,6 +11,7 @@ const ansiGoto = require('../core/ansi_term.js').goto; const moveFileWithCollisionHandling = require('../core/file_util.js').moveFileWithCollisionHandling; const pathWithTerminatingSeparator = require('../core/file_util.js').pathWithTerminatingSeparator; const Log = require('../core/logger.js').log; +const Errors = require('../core/enig_error.js').Errors; // deps const async = require('async'); @@ -69,41 +70,28 @@ exports.getModule = class UploadModule extends MenuModule { this.menuMethods = { optionsNavContinue : (formData, extraArgs, cb) => { - if(this.isBlindUpload()) { - return this.performBlindUpload(cb); - } - - // non-blind - // jump to fileDetails form - // :TODO: support non-blind: collect info/filename -> upload -> complete + return this.performUpload(cb); }, fileDetailsContinue : (formData, extraArgs, cb) => { - - - // see notes in displayFileDetailsPageForEntry() about this hackery: - cb(null); + // see displayFileDetailsPageForUploadEntry() for this hackery: + cb(null); return this.fileDetailsCurrentEntrySubmitCallback(null, formData.value); // move on to the next entry, if any } }; } getSaveState() { - const saveState = { + return { uploadType : this.uploadType, - tempRecvDirectory : this.tempRecvDirectory + tempRecvDirectory : this.tempRecvDirectory, + areaInfo : this.availAreas[ this.viewControllers.options.getView(MciViewIds.options.area).getData() ], }; - - if(this.isBlindUpload()) { - const areaSelectView = this.viewControllers.options.getView(MciViewIds.options.area); - saveState.areaInfo = this.availAreas[areaSelectView.getData()]; - } - - return saveState; } restoreSavedState(savedState) { if(savedState.areaInfo) { + this.uploadType = savedState.uploadType; this.areaInfo = savedState.areaInfo; this.tempRecvDirectory = savedState.tempRecvDirectory; } @@ -151,7 +139,7 @@ exports.getModule = class UploadModule extends MenuModule { super.leave(); } - performBlindUpload(cb) { + performUpload(cb) { temptmp.mkdir( { prefix : 'enigul-' }, (err, tempRecvDirectory) => { if(err) { return cb(err); @@ -167,6 +155,10 @@ exports.getModule = class UploadModule extends MenuModule { } }; + if(!this.isBlindUpload()) { + modOpts.extraArgs.recvFileName = this.viewControllers.options.getView(MciViewIds.options.fileName).getData(); + } + // // Move along to protocol selection -> file transfer // Upon completion, we'll re-enter the module with some file paths handed to us @@ -176,7 +168,11 @@ exports.getModule = class UploadModule extends MenuModule { modOpts, cb ); - }); + }); + } + + continueNonBlindUpload(cb) { + return cb(null); } updateScanStepInfoViews(stepInfo) { @@ -343,6 +339,34 @@ exports.getModule = class UploadModule extends MenuModule { }); } + prepDetailsForUpload(scanResults, cb) { + async.eachSeries(scanResults.newEntries, (newEntry, nextEntry) => { + this.displayFileDetailsPageForUploadEntry(newEntry, (err, newValues) => { + if(err) { + return nextEntry(err); + } + + // if the file entry did *not* have a desc, take the user desc + if(!this.fileEntryHasDetectedDesc(newEntry)) { + newEntry.desc = newValues.shortDesc.trim(); + } + + if(newValues.estYear.length > 0) { + newEntry.meta.est_release_year = newValues.estYear; + } + + if(newValues.tags.length > 0) { + newEntry.setHashTags(newValues.tags); + } + + return nextEntry(err); + }); + }, err => { + delete this.fileDetailsCurrentEntrySubmitCallback; + return cb(err, scanResults); + }); + } + processUploadedFiles() { // // For each file uploaded, we need to process & gather information @@ -351,6 +375,22 @@ exports.getModule = class UploadModule extends MenuModule { async.waterfall( [ + function prepNonBlind(callback) { + if(self.isBlindUpload()) { + return callback(null); + } + + // + // For non-blind uploads, batch is not supported, we expect a single file + // in |recvFilePaths|. If not, it's an error (we don't want to process the wrong thing) + // + if(self.recvFilePaths.length > 1) { + self.client.log.warn( { recvFilePaths : self.recvFilePaths }, 'Non-blind upload received 2:n files' ); + return callback(Errors.UnexpectedState(`Non-blind upload expected single file but got received ${self.recvFilePaths.length}`)); + } + + return callback(null); + }, function scan(callback) { return self.scanFiles(callback); }, @@ -374,31 +414,7 @@ exports.getModule = class UploadModule extends MenuModule { return callback(null, scanResults); }, function prepDetails(scanResults, callback) { - async.eachSeries(scanResults.newEntries, (newEntry, nextEntry) => { - self.displayFileDetailsPageForEntry(newEntry, (err, newValues) => { - if(err) { - return nextEntry(err); - } - - // if the file entry did *not* have a desc, take the user desc - if(!self.fileEntryHasDetectedDesc(newEntry)) { - newEntry.desc = newValues.shortDesc.trim(); - } - - if(newValues.estYear.length > 0) { - newEntry.meta.est_release_year = newValues.estYear; - } - - if(newValues.tags.length > 0) { - newEntry.setHashTags(newValues.tags); - } - - return nextEntry(err); - }); - }, err => { - delete self.fileDetailsCurrentEntrySubmitCallback; - return callback(err, scanResults); - }); + return self.prepDetailsForUpload(scanResults, callback); }, function startMovingAndPersistingToDatabase(scanResults, callback) { // @@ -486,7 +502,7 @@ exports.getModule = class UploadModule extends MenuModule { return (fileEntry.desc && fileEntry.desc.length > 0); } - displayFileDetailsPageForEntry(fileEntry, cb) { + displayFileDetailsPageForUploadEntry(fileEntry, cb) { const self = this; async.series(