From 3ecadebf9137b9e41e763d3c13d90628830671ae Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sat, 2 Jun 2018 16:06:04 -0600 Subject: [PATCH] Generic MIME types (file types) such as application/octet-stream can how have sub types for handlers (archive, info extract, ...) + Add Amiga DMS support via xdms --- core/archive_util.js | 82 +++++++++++++++++++++++++------------ core/config.js | 29 ++++++++++--- core/file_area_list.js | 14 ++++++- core/file_base_area.js | 17 ++++++-- docs/installation/manual.md | 6 +-- 5 files changed, 108 insertions(+), 40 deletions(-) diff --git a/core/archive_util.js b/core/archive_util.js index c3915389..71bad641 100644 --- a/core/archive_util.js +++ b/core/archive_util.js @@ -11,6 +11,7 @@ const resolveMimeType = require('./mime_util.js').resolveMimeType; const fs = require('graceful-fs'); const _ = require('lodash'); const pty = require('node-pty'); +const paths = require('path'); let archiveUtil; @@ -75,32 +76,56 @@ module.exports = class ArchiveUtil { } if(_.isObject(Config.fileTypes)) { + const updateSig = (ft) => { + ft.sig = Buffer.from(ft.sig, 'hex'); + ft.offset = ft.offset || 0; + + // :TODO: this is broken: sig is NOT this long, it's sig.length long; offset needs to allow for -negative values as well + const sigLen = ft.offset + ft.sig.length; + if(sigLen > this.longestSignature) { + this.longestSignature = sigLen; + } + }; + Object.keys(Config.fileTypes).forEach(mimeType => { const fileType = Config.fileTypes[mimeType]; - if(fileType.sig) { - fileType.sig = Buffer.from(fileType.sig, 'hex'); - fileType.offset = fileType.offset || 0; - - // :TODO: this is broken: sig is NOT this long, it's sig.length long; offset needs to allow for -negative values as well - const sigLen = fileType.offset + fileType.sig.length; - if(sigLen > this.longestSignature) { - this.longestSignature = sigLen; - } + if(Array.isArray(fileType)) { + fileType.forEach(ft => { + if(ft.sig) { + updateSig(ft); + } + }); + } else if(fileType.sig) { + updateSig(fileType); } }); } } - getArchiver(mimeTypeOrExtension) { - mimeTypeOrExtension = resolveMimeType(mimeTypeOrExtension); + getArchiver(mimeTypeOrExtension, justExtention) { + const mimeType = resolveMimeType(mimeTypeOrExtension); - if(!mimeTypeOrExtension) { // lookup returns false on failure + if(!mimeType) { // lookup returns false on failure return; } - const archiveHandler = _.get( Config, [ 'fileTypes', mimeTypeOrExtension, 'archiveHandler'] ); - if(archiveHandler) { - return _.get( Config, [ 'archives', 'archivers', archiveHandler ] ); + let fileType = _.get(Config, [ 'fileTypes', mimeType ] ); + + if(Array.isArray(fileType)) { + if(!justExtention) { + // need extention for lookup; ambiguous as-is :( + return; + } + // further refine by extention + fileType = fileType.find(ft => justExtention === ft.ext); + } + + if(!_.isObject(fileType)) { + return; + } + + if(fileType.archiveHandler) { + return _.get( Config, [ 'archives', 'archivers', fileType.archiveHandler ] ); } } @@ -127,18 +152,21 @@ module.exports = class ArchiveUtil { } const archFormat = _.findKey(Config.fileTypes, fileTypeInfo => { - if(!fileTypeInfo.sig) { - return false; - } + const fileTypeInfos = Array.isArray(fileTypeInfo) ? fileTypeInfo : [ fileTypeInfo ]; + return fileTypeInfos.find(fti => { + if(!fti.sig || !fti.archiveHandler) { + return false; + } - const lenNeeded = fileTypeInfo.offset + fileTypeInfo.sig.length; + const lenNeeded = fti.offset + fti.sig.length; - if(bytesRead < lenNeeded) { - return false; - } + if(bytesRead < lenNeeded) { + return false; + } - const comp = buf.slice(fileTypeInfo.offset, fileTypeInfo.offset + fileTypeInfo.sig.length); - return (fileTypeInfo.sig.equals(comp)); + const comp = buf.slice(fti.offset, fti.offset + fti.sig.length); + return (fti.sig.equals(comp)); + }); }); return cb(archFormat ? null : Errors.General('Unknown type'), archFormat); @@ -162,7 +190,7 @@ module.exports = class ArchiveUtil { } compressTo(archType, archivePath, files, cb) { - const archiver = this.getArchiver(archType); + const archiver = this.getArchiver(archType, paths.extname(archivePath)); if(!archiver) { return cb(Errors.Invalid(`Unknown archive type: ${archType}`)); @@ -196,7 +224,7 @@ module.exports = class ArchiveUtil { haveFileList = true; } - const archiver = this.getArchiver(archType); + const archiver = this.getArchiver(archType, paths.extname(archivePath)); if(!archiver) { return cb(Errors.Invalid(`Unknown archive type: ${archType}`)); @@ -236,7 +264,7 @@ module.exports = class ArchiveUtil { } listEntries(archivePath, archType, cb) { - const archiver = this.getArchiver(archType); + const archiver = this.getArchiver(archType, paths.extname(archivePath)); if(!archiver) { return cb(Errors.Invalid(`Unknown archive type: ${archType}`)); diff --git a/core/config.js b/core/config.js index 070a6920..70406e47 100644 --- a/core/config.js +++ b/core/config.js @@ -297,6 +297,16 @@ function getDefaultConfig() { '--filemodifydate', '--fileaccessdate', '--fileinodechangedate', '--createdate', '--modifydate', '--metadatadate', '--xmptoolkit' ] + }, + XDMS2Desc : { + // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html + cmd : 'xdms', + args : [ 'd', '{filePath}' ] + }, + XDMS2LongDesc : { + // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html + cmd : 'xdms', + args : [ 'f', '{filePath}' ] } }, @@ -426,13 +436,20 @@ function getDefaultConfig() { sig : '377abcaf271c', offset : 0, archiveHandler : '7Zip', - } + }, - // :TODO: update archives::formats to fall here - // * archive handler -> archiveHandler (consider archive if archiveHandler present) - // * sig, offset, ... - // * mime-db -> exts lookup - // * + // + // Generics that need further mapping + // + 'application/octet-stream' : [ + { + desc : 'Amiga DISKMASHER', + sig : '444d5321', // DMS! + ext : '.dms', + shortDescUtil : 'XDMS2Desc', + longDescUtil : 'XDMS2LongDesc', + } + ] }, archives : { diff --git a/core/file_area_list.js b/core/file_area_list.js index f42bf93e..b3d55e2c 100644 --- a/core/file_area_list.js +++ b/core/file_area_list.js @@ -24,6 +24,7 @@ const controlCodesToAnsi = require('./color_codes.js').controlCodesToAnsi; const async = require('async'); const _ = require('lodash'); const moment = require('moment'); +const paths = require('path'); exports.moduleInfo = { name : 'File Area List', @@ -252,7 +253,18 @@ exports.getModule = class FileAreaList extends MenuModule { if(entryInfo.archiveType) { const mimeType = resolveMimeType(entryInfo.archiveType); - entryInfo.archiveTypeDesc = mimeType ? _.get(Config, [ 'fileTypes', mimeType, 'desc' ] ) || mimeType : entryInfo.archiveType; + let desc; + if(mimeType) { + let fileType = _.get(Config, [ 'fileTypes', mimeType ] ); + + if(Array.isArray(fileType)) { + // further refine by extention + fileType = fileType.find(ft => paths.extname(currEntry.fileName) === ft.ext); + } + desc = fileType && fileType.desc; + } + entryInfo.archiveTypeDesc = desc || mimeType || entryInfo.archiveType; + //entryInfo.archiveTypeDesc = mimeType ? _.get(Config, [ 'fileTypes', mimeType, 'desc' ] ) || mimeType : entryInfo.archiveType; } else { entryInfo.archiveTypeDesc = 'N/A'; } diff --git a/core/file_base_area.js b/core/file_base_area.js index 5f3b974a..9cfb1cb9 100644 --- a/core/file_base_area.js +++ b/core/file_base_area.js @@ -487,8 +487,19 @@ function populateFileEntryWithArchive(fileEntry, filePath, stepInfo, iterator, c ); } -function getInfoExtractUtilForDesc(mimeType, descType) { - let util = _.get(Config, [ 'fileTypes', mimeType, `${descType}DescUtil` ]); +function getInfoExtractUtilForDesc(mimeType, filePath, descType) { + let fileType = _.get(Config, [ 'fileTypes', mimeType ] ); + + if(Array.isArray(fileType)) { + // further refine by extention + fileType = fileType.find(ft => paths.extname(filePath) === ft.ext); + } + + if(!_.isObject(fileType)) { + return; + } + + let util = _.get(fileType, `${descType}DescUtil`); if(!_.isString(util)) { return; } @@ -508,7 +519,7 @@ function populateFileEntryInfoFromFile(fileEntry, filePath, cb) { } async.eachSeries( [ 'short', 'long' ], (descType, nextDesc) => { - const util = getInfoExtractUtilForDesc(mimeType, descType); + const util = getInfoExtractUtilForDesc(mimeType, filePath, descType); if(!util) { return nextDesc(null); } diff --git a/docs/installation/manual.md b/docs/installation/manual.md index 9958c2e3..f03df762 100644 --- a/docs/installation/manual.md +++ b/docs/installation/manual.md @@ -50,7 +50,7 @@ ENiGMA BBS makes use of a few packages for unarchiving and modem support. They'r running ENiGMA, but without them you'll miss certain functionality. Once installed, they should be made available on your system path. -| Package | Description | Ubuntu Package | CentOS Package Name | Windows Package | +| Package | Description | Debian/Ubuntu Package (APT/DEP) | Red Hat Package (YUM/RPM) | Windows Package | |------------|-----------------------------------|--------------------------------------------|---------------------------------------------------|------------------------------------------------------------------| | arj | Unpacking arj archives | `arj` | n/a, binaries [here](http://arj.sourceforge.net/) | [ARJ](http://arj.sourceforge.net/) | | 7zip | Unpacking zip, rar, archives | `p7zip-full` | `p7zip-full` | [7-zip](http://www.7-zip.org/) | @@ -58,8 +58,8 @@ available on your system path. | Rar | Unpacking rar archives | `unrar` | n/a, binaries [here](https://www.rarlab.com/download.htm) | Unknown | | lrzsz | sz/rz: X/Y/Z modem support | `lrzsz` | `lrzsz` | Unknown | | sexyz | SexyZ modem support | [sexyz](https://l33t.codes/outgoing/sexyz) | [sexyz](https://l33t.codes/outgoing/sexyz) | Available with [Synchronet](http://wiki.synchro.net/install:win) | - - - exiftool & other external tools +| exiftool | [ExifTool](https://www.sno.phy.queensu.ca/~phil/exiftool/) | libimage-exiftool-perl | perl-Image-ExifTool | Unknown +| xdms | Unpack/view Amiga DMS | [xdms](http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html) | xdms | Unknown ## Config Files