diff --git a/xtrn/dd_arc_viewer/dd_arc_viewer.cfg b/xtrn/dd_arc_viewer/dd_arc_viewer.cfg index d3b82c6e12fd3262ffb75d5a7fb0a6a730c91695..b68ea19545a3e3963e3e1467eca8753961505a46 100644 --- a/xtrn/dd_arc_viewer/dd_arc_viewer.cfg +++ b/xtrn/dd_arc_viewer/dd_arc_viewer.cfg @@ -15,21 +15,21 @@ maxTextFileSize=5M [COLORS] ; The "Archive:" text -archiveFilenameHdrText=nwh +archiveFilenameHdrText=nwh ; Archive filename -archiveFilename=ngh +archiveFilename=ngh ; The column header line above the file list -headerLine=nyh +headerLine=nyh ; The line between the header line and the file list -headerSeparatorLine=nkh +headerSeparatorLine=nkh ; File numbers -fileNums=nmh +fileNums=nmh ; File information columns -fileSize=nw -fileDate=ng -fileTime=nr -filename=nc +fileSize=nw +fileDate=ng +fileTime=nr +filename=nc ; For subdirectories -subdir=ngh +subdir=ngh ; For lightbar mode, highlighted filenames will have this color: -highlightedFile=n4wh4 \ No newline at end of file +highlightedFile=nwh4 \ No newline at end of file diff --git a/xtrn/dd_arc_viewer/dd_arc_viewer.js b/xtrn/dd_arc_viewer/dd_arc_viewer.js index 4f0366b2e49fc882fd136a605bbb5bb86bbdd8f3..55fe66175ab75919548483762c28b02c0917f941 100644 --- a/xtrn/dd_arc_viewer/dd_arc_viewer.js +++ b/xtrn/dd_arc_viewer/dd_arc_viewer.js @@ -83,6 +83,9 @@ * 2022-05-17 Eric Oulashin When extracting an archive with Synchronet's * internal archiver, extracts with path information * to trust that filename characters are safe. + * 2023-08-08 Eric Oulashin Version 1.05 + * Refactored how the configuration files are loaded. + * Colors in dd_arc_viewer.cfg no longer need the control character. */ "use strict"; @@ -115,8 +118,8 @@ load(gStartupPath + "dd_arc_viewer_cleanup.js"); // Version information -var gDDArcViewerVersion = "1.04"; -var gDDArcViewerVerDate = "2022-05-14"; +var gDDArcViewerVersion = "1.05"; +var gDDArcViewerVerDate = "2023-08-08"; var gDDArcViewerProgName = "Digital Distortion Archive Viewer"; @@ -232,7 +235,6 @@ if (!configFileRead) } -// Now, the fun begins.. // gRootWorkDir will containing the name of the root work directory. This // is where we'll extract the archive. @@ -328,85 +330,37 @@ function ReadConfig(pCfgFilePath) { if (fileTypeCfgFile.length > 0) { + var allFileTypeCfg = fileTypeCfgFile.iniGetAllObjects(); fileTypeSettingsRead = true; - // Read each line from the config file and set the - // various options. - var pos = 0; // Index of = in the file lines - var fileLine = ""; - var filenameExt = ""; // Archive filename extension - var option = ""; // Configuration option - var optionValue = ""; // Configuration option value - var optionValueUpper; // Upper-cased configuration option value - var viewableFile = null; // Will be used to create & store viewable archive options - while (!fileTypeCfgFile.eof) + for (var i = 0; i < allFileTypeCfg.length; ++i) { - // Read the line from the config file, look for a =, and - // if found, read the option & value and set them - // in cfgObj. - fileLine = fileTypeCfgFile.readln(1024); - - // fileLine should be a string, but I've seen some cases - // where it isn't, so check its type. - if (typeof(fileLine) != "string") - continue; - - // If the line is blank or starts with with a semicolon - // (the comment character), then skip it. - if ((fileLine.length == 0) || (fileLine.substr(0, 1) == ";")) - continue; - - // Look for a file extension in square brackets ([ and ]). - // If found, then set filenameExt and continue onto the next line. - // Note: This regular expression allows whitespace around the [...]. - if (/^\s*\[.*\]\s*$/.test(fileLine)) + var filenameExt = allFileTypeCfg[i].name; // Filename extension + var viewableFile = new ViewableFile(); + viewableFile.extension = filenameExt; + for (var prop in allFileTypeCfg[i]) { - var startIndex = fileLine.indexOf("[") + 1; - var endIndex = fileLine.lastIndexOf("]"); - var ext = fileLine.substr(startIndex, endIndex-startIndex).toUpperCase(); - // If the filename extension is different than the last one - // we've seen, then: - // 1. If viewableFile is not null, then add it to gViewableFileTypes. - // 2. Create a new one (referenced as viewableFile). - if (ext != filenameExt) + var propUpper = prop.toUpperCase(); + if (propUpper == "VIEW") + viewableFile.viewCmd = allFileTypeCfg[i][prop]; + else if (propUpper == "EXTRACT") + viewableFile.extractCmd = allFileTypeCfg[i][prop]; + else if (propUpper == "ISTEXT") { - if ((viewableFile != null) && (viewableFile != undefined) && (filenameExt.length > 0)) + if (typeof(allFileTypeCfg[i][prop]) === "string") { - gViewableFileTypes[filenameExt] = viewableFile; + var valueUpper = allFileTypeCfg[i][prop].toUpperCase(); + viewableFile.isText = (valueUpper == "YES" || valueUpper == "TRUE"); } - filenameExt = ext; - viewableFile = new ViewableFile(); - viewableFile.extension = ext; + else if (typeof(allFileTypeCfg[i][prop]) === "boolean") + viewableFile.isText = allFileTypeCfg[i][prop]; } - continue; - } - - // If filenameExt is blank, then continue onto the next line. - if (filenameExt.length == 0) - continue; - - // If we're here, then filenameExt is set, and this is a valid - // line to process. - // Look for an = in the line, and if found, split into - // option & value. - pos = fileLine.indexOf("="); - if (pos > -1) - { - // Extract the option & value, trimming leading & trailing spaces. - option = trimSpaces(fileLine.substr(0, pos), true, false, true).toUpperCase(); - optionValue = trimSpaces(fileLine.substr(pos+1), true, false, true); - - if (option == "VIEW") - viewableFile.viewCmd = optionValue; - else if (option == "EXTRACT") - viewableFile.extractCmd = optionValue; - else if (option == "ISTEXT") - viewableFile.isText = (optionValue.toUpperCase() == "YES"); } + gViewableFileTypes[filenameExt] = viewableFile; } } - fileTypeCfgFile.close(); } + // Read the extractable and viewable file configuration from the Synchronet // configuration and add any that we haven't seen in dd_arc_viewer_file_types.cfg var SCFGFileCmds = getFileExtractAndViewCmdsFromSCFG(); @@ -436,93 +390,55 @@ function ReadConfig(pCfgFilePath) { if (genCfgFile.length > 0) { + var behaviorSettings = genCfgFile.iniGetObject("BEHAVIOR"); + var colorSettings = genCfgFile.iniGetObject("COLORS"); genSettingsRead = true; - var settingsMode = ""; - var fileLine = null; // A line read from the file - var equalsPos = 0; // Position of a = in the line - var commentPos = 0; // Position of the start of a comment - var setting = null; // A setting name (string) - var settingUpper = null; // Upper-case setting name - var value = null; // A value for a setting (string) - while (!genCfgFile.eof) - { - // Read the next line from the config file. - fileLine = genCfgFile.readln(1024); - - // fileLine should be a string, but I've seen some cases - // where it isn't, so check its type. - if (typeof(fileLine) != "string") - continue; - - // If the line starts with with a semicolon (the comment - // character) or is blank, then skip it. - if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0)) - continue; - // If in the "behavior" section, then set the behavior-related variables. - if (fileLine.toUpperCase() == "[BEHAVIOR]") + // General/behavior settings + for (var prop in behaviorSettings) + { + var propUpper = prop.toUpperCase(); + if (propUpper == "INTERFACESTYLE") { - settingsMode = "behavior"; - continue; + var valueUpper = behaviorSettings[prop].toUpperCase(); + // Ensure that the first character is uppercase and the + // rest is lower-case. + if ((valueUpper == "LIGHTBAR") || (valueUpper == "TRADITIONAL")) + { + gGenConfig.interfaceStyle = behaviorSettings[prop].substr(0, 1).toUpperCase() + + behaviorSettings[prop].substr(1).toLowerCase(); + } } - else if (fileLine.toUpperCase() == "[COLORS]") + else if (propUpper == "INPUTTIMEOUTMS") { - settingsMode = "colors"; - continue; + var timeoutMSInt = parseInt(behaviorSettings[prop]); + if (!isNaN(timeoutMSInt) && timeoutMSInt > 0) + gGenConfig.inputTimeoutMS = timeoutMSInt; } + else if (propUpper == "MAXARCFILESIZE") + gGenConfig.maxArcFileSize = sizeStrToBytes(behaviorSettings[prop]); + else if (propUpper == "MAXTEXTFILESIZE") + gGenConfig.maxTextFileSize = sizeStrToBytes(behaviorSettings[prop]); + } - // If settingsMode is blank, then skip this line. - if (settingsMode.length == 0) - continue; - - // If the line has a semicolon anywhere in it, then remove - // everything from the semicolon onward. - commentPos = fileLine.indexOf(";"); - if (commentPos > -1) - fileLine = fileLine.substr(0, commentPos); - - // Look for an equals sign, and if found, separate the line - // into the setting name (before the =) and the value (after the - // equals sign). - equalsPos = fileLine.indexOf("="); - if (equalsPos > 0) + // Color settings + var onlySyncAttrsRegexWholeWord = new RegExp("^[\x01krgybmcw01234567hinq,;\.dtlasz]+$", 'i'); + for (var prop in gGenConfig.colors) + { + if (colorSettings.hasOwnProperty(prop)) { - // Read the setting & value, and trim leading & trailing spaces. - setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true); - settingUpper = setting.toUpperCase(); - value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true).toUpperCase(); - - if (settingsMode == "behavior") - { - // Skip this one if the value is blank. - if (value.length == 0) - continue; - - // Set the appropriate value in the settings object. - if (settingUpper == "INTERFACESTYLE") - { - // Ensure that the first character is uppercase and the - // rest is lower-case. - if ((value == "LIGHTBAR") || (value == "TRADITIONAL")) - { - gGenConfig.interfaceStyle = value.substr(0, 1).toUpperCase() - + value.substr(1).toLowerCase(); - } - } - else if (settingUpper == "INPUTTIMEOUTMS") - gGenConfig.inputTimeoutMS = +value; - else if (settingUpper == "MAXARCFILESIZE") - gGenConfig.maxArcFileSize = sizeStrToBytes(value); - else if (settingUpper == "MAXTEXTFILESIZE") - gGenConfig.maxTextFileSize = sizeStrToBytes(value); - } - else if (settingsMode == "colors") - gGenConfig.colors[setting] = value; + // Make sure the value is a string (for attrCodeStr() etc; in some cases, such as a background attribute of 4, it will be a number) + var value = colorSettings[prop].toString(); + // If the value doesn't have any control characters, then add the control character + // before attribute characters + if (!/\x01/.test(value)) + value = attrCodeStr(value); + if (onlySyncAttrsRegexWholeWord.test(value)) + gGenConfig.colors[prop] = value; } } - - genCfgFile.close(); } + genCfgFile.close(); } return (fileTypeSettingsRead && genSettingsRead); @@ -1207,9 +1123,9 @@ function writeFileListHeader(pFilename) if (writeFileListHeader.topHelp3 == undefined) { writeFileListHeader.topHelp3 = gGenConfig.colors.headerSeparatorLine - + charStr(HORIZONTAL_SINGLE, 4) + " " + charStr(HORIZONTAL_SINGLE, 8) + " " - + charStr(HORIZONTAL_SINGLE, 10) + " " + charStr(HORIZONTAL_SINGLE, 5) + " " - + charStr(HORIZONTAL_SINGLE, console.screen_columns - 32); + + charStr(HORIZONTAL_SINGLE, 4) + " " + charStr(HORIZONTAL_SINGLE, 8) + " " + + charStr(HORIZONTAL_SINGLE, 10) + " " + charStr(HORIZONTAL_SINGLE, 5) + " " + + charStr(HORIZONTAL_SINGLE, console.screen_columns - 32); // Add line characters to the end of the screen. //for (var x = 30; x < console.screen_columns - 2; ++x) // writeFileListHeader.topHelp3 += HORIZONTAL_SINGLE; @@ -2337,4 +2253,28 @@ function charStr(pChar, pNumTimes) for (var i = 0; i < pNumTimes; ++i) str += pChar; return str; +} + +// Given a string of attribute characters, this function inserts the control code +// in front of each attribute character and returns the new string. +// +// Parameters: +// pAttrCodeCharStr: A string of attribute characters (i.e., "YH" for yellow high) +// +// Return value: A string with the control character inserted in front of the attribute characters +function attrCodeStr(pAttrCodeCharStr) +{ + if (typeof(pAttrCodeCharStr) !== "string") + return ""; + + var str = ""; + // See this page for Synchronet color attribute codes: + // http://wiki.synchro.net/custom:ctrl-a_codes + for (var i = 0; i < pAttrCodeCharStr.length; ++i) + { + var currentChar = pAttrCodeCharStr.charAt(i); + if (/[krgybmcwKRGYBMCWHhIiEeFfNn01234567]/.test(currentChar)) + str += "\x01" + currentChar; + } + return str; } \ No newline at end of file diff --git a/xtrn/dd_arc_viewer/readme.txt b/xtrn/dd_arc_viewer/readme.txt index f57b956454e6f664c6b2ce2a07bc8aa7ef05799e..fb6f2b893397f4b279b64f42554038faae981951 100644 --- a/xtrn/dd_arc_viewer/readme.txt +++ b/xtrn/dd_arc_viewer/readme.txt @@ -1,6 +1,6 @@ Digital Distortion Archive Viewer - Version 1.04 - Release date: 2022-05-16 + Version 1.05 + Release date: 2023-08-08 by @@ -280,8 +280,11 @@ folows: setting=value -where "setting" is the behavior setting or color, and "value" is the corresponding -value for the setting/color. The colors are Synchronet color codes. +where "setting" is the behavior setting or color, and "value" is the +corresponding value for the setting/color. The colors are Synchronet color +codes. The control character is not needed for the color codes; for sample, to +set a color of high-intensity green, you could use "gh" (or "ngh" if you want +to ensure that the normal attribute gets set first). Also, comments are allowed in the configuration file. Comments begin with a semicolon (;). diff --git a/xtrn/dd_arc_viewer/revision_history.txt b/xtrn/dd_arc_viewer/revision_history.txt index fb20d495ecfe21bb24d326901fd83da8f39f86ee..279d2b1b1c2a29798dcaed8cb51592a5212b0733 100644 --- a/xtrn/dd_arc_viewer/revision_history.txt +++ b/xtrn/dd_arc_viewer/revision_history.txt @@ -2,6 +2,9 @@ Revision History for Digital Distortion Archive Viewer ====================================================== Version Date Description ------- ---- ----------- +1.05 2023-08-08 Internal refactor of how the configuration files are + loaded. Colors in dd_arc_viewer.cfg no longer need the + control character. 1.04 2022-05-16 Updated to use the new Archive class in Synchronet 3.19. Now has built-in support for the same archive types that Synchronet has built-in (zip, 7z, tgz, etc.). Other diff --git a/xtrn/dd_upload_processor/ddup.js b/xtrn/dd_upload_processor/ddup.js index de7386451b39c0da442d9e2311ff38a32ce1fe47..2b43029646d1232f7fbd393425c13c3ea65bb48b 100644 --- a/xtrn/dd_upload_processor/ddup.js +++ b/xtrn/dd_upload_processor/ddup.js @@ -9,16 +9,23 @@ * BBS: Digital Distortion * BBS address: digdist.bbsindex.com * - * Date Author Version Description + * Date Author Description * 2009-12-25- - * 2009-12-28 Eric Oulashin Initial development - * 2009-12-29 Eric Oulashin 1.00 Initial public release - * 2022-06-08 Eric Oulashin 1.01 Made fixes to get the scanner functionality working properly in Linux - * 2022-06-11 Eric Oulashin 1.02 Improved file/dir permissions more: Set file permissions after extracting - * an archive so that they're all readable. - * 2022-06-11 Eric Oulashin 1.03 Removed the chmod stuff, as it is actually not needed. - * 2023-08-06 Eric Oulashin 1.04 Now uses Synchronet's built-in archiver (added in Synchronet 3.19), - * if available, to extract archives. + * 2009-12-28 Eric Oulashin Initial development + * 2009-12-29 Eric Oulashin Version 1.00 + * Initial public release + * 2022-06-08 Eric Oulashin Version 1.01 + Made fixes to get the scanner functionality working properly in Linux + * 2022-06-11 Eric Oulashin Version 1.02 + * Improved file/dir permissions more: Set file permissions after extracting + * an archive so that they're all readable. + * 2022-06-11 Eric Oulashin Version 1.03 + * Removed the chmod stuff, as it is actually not needed. + * 2023-08-06 Eric Oulashin Version 1.04 + * Now uses Synchronet's built-in archiver (added in Synchronet 3.19), + * if available, to extract archives. + * 2023-08-07 Eric Oulashin Version 1.05 + * Internal refactor of how the configuration files are read */ /* Command-line arguments: @@ -46,8 +53,8 @@ gStartupPath = backslash(gStartupPath.replace(/[\/\\][^\/\\]*$/,'')); load(gStartupPath + "ddup_cleanup.js"); // Version information -var gDDUPVersion = "1.04"; -var gDDUPVerDate = "2023-08-06"; +var gDDUPVersion = "1.05"; +var gDDUPVerDate = "2023-08-07"; // Store whether or not this is running in Windows var gRunningInWindows = /^WIN/.test(system.platform.toUpperCase()); @@ -96,14 +103,14 @@ else // Make sure the slashes in the filename are correct for the platform. if (gFileToScan.length > 0) - gFileToScan = fixPathSlashes(gFileToScan); + gFileToScan = fixPathSlashes(gFileToScan); // A filename must be specified as the first argument, so give an error and return // if not. if (gFileToScan.length == 0) { - console.print("\1n\1y\1hError: \1n\1cNo filename specified to process.\r\n\1p"); - exit(1); + console.print("\1n\1y\1hError: \1n\1cNo filename specified to process.\r\n\1p"); + exit(1); } // Create the global configuration objects. @@ -151,33 +158,33 @@ exit(main()); // Return value: An integer to return upon script exit. function main() { - // Output the program name & version information - console.print("\1n\r\n\1c\1hD\1n\1cigital \1hD\1n\1cistortion \1hU\1n\1cpload \1hP\1n\1crocessor \1w\1hv\1n\1g" + - gDDUPVersion); - // Originally I had this script output the version date, but now I'm not sure - // if I want to do that.. - //console.print(" \1w\1h(\1b" + gDDUPVerDate + "\1w)"); - console.print("\1n"); - console.crlf(); - - // Process the file - var exitCode = processFile(gFileToScan); - // Depending on the exit code, display a success or failure message. - console.crlf(); - if (exitCode == 0) - console.print(gOKStr + " \1n\1b\1hScan successful - The file passed.\r\n"); - else - console.print(gFailStr + " \1n\1y\1hScan failed!\r\n"); + // Output the program name & version information + console.print("\1n\r\n\1c\1hD\1n\1cigital \1hD\1n\1cistortion \1hU\1n\1cpload \1hP\1n\1crocessor \1w\1hv\1n\1g" + + gDDUPVersion); + // Originally I had this script output the version date, but now I'm not sure + // if I want to do that.. + //console.print(" \1w\1h(\1b" + gDDUPVerDate + "\1w)"); + console.attributes = "N"; + console.crlf(); + + // Process the file + var exitCode = processFile(gFileToScan); + // Depending on the exit code, display a success or failure message. + console.crlf(); + if (exitCode == 0) + console.print(gOKStr + " \1n\1b\1hScan successful - The file passed.\r\n"); + else + console.print(gFailStr + " \1n\1y\1hScan failed!\r\n"); - // If the option to pause at the end is enabled, then prompt the user for - // a keypress. - if (gGenCfg.pauseAtEnd) - { - console.print("\1n\1w\1hPress any key to continue:\1n"); - console.getkey(K_NOECHO); - } + // If the option to pause at the end is enabled, then prompt the user for + // a keypress. + if (gGenCfg.pauseAtEnd) + { + console.print("\1n\1w\1hPress any key to continue:\1n"); + console.getkey(K_NOECHO); + } - return exitCode; + return exitCode; } @@ -200,17 +207,17 @@ function main() // "always fail": Don't scan the file, and assume it's bad function ScannableFile(pExtension, pExtractCmd, pScanOption) { - this.extension = ""; // The archive filename extension - this.extractCmd = ""; // The command to extract the archive (if applicable) - this.scanOption = "scan"; // The scan option ("scan", "always pass", "always fail") - - // If the parameters are valid, then use them to set the object properties. - if ((pExtension != null) && (pExtension != undefined) && (typeof(pExtension) == "string")) - this.extension = pExtension; - if ((pExtractCmd != null) && (pExtractCmd != undefined) && (typeof(pExtractCmd) == "string")) - this.extractCmd = pExtractCmd; - if ((pScanOption != null) && (pScanOption != undefined) && (typeof(pScanOption) == "string")) - this.scanOption = pScanOption; + this.extension = ""; // The archive filename extension + this.extractCmd = ""; // The command to extract the archive (if applicable) + this.scanOption = "scan"; // The scan option ("scan", "always pass", "always fail") + + // If the parameters are valid, then use them to set the object properties. + if ((pExtension != null) && (typeof(pExtension) == "string")) + this.extension = pExtension; + if ((pExtractCmd != null) && (typeof(pExtractCmd) == "string")) + this.extractCmd = pExtractCmd; + if ((pScanOption != null) && (typeof(pScanOption) == "string")) + this.scanOption = pScanOption; } @@ -223,12 +230,12 @@ function ScannableFile(pExtension, pExtractCmd, pScanOption) // in the DOVE-Net Synchronet Discussion sub-board on December 20, 2009. function fixArgs(input) { - var patt1 = /\"[^\"]*\"|\S+/g; - var patt2 = /^\"?([^\"]*)\"?$/; - return input.join(' ').match(patt1).map(function(item) - { - return item.replace(patt2, "$1") - }); + var patt1 = /\"[^\"]*\"|\S+/g; + var patt2 = /^\"?([^\"]*)\"?$/; + return input.join(' ').match(patt1).map(function(item) + { + return item.replace(patt2, "$1") + }); } // Scans a file. @@ -243,7 +250,7 @@ function processFile(pFilename) // Display the program header stuff - The name of the file being scanned // and the status header line var justFilename = getFilenameFromPath(pFilename); - console.print("\1n\1w\1hScanning \1b" + justFilename.substr(0, 70)); + console.print("\1n\1w\1hScanning \1b" + justFilename); console.print("\1n\r\n\1b\1" + "7 File Scan Status \1n\r\n"); // If the skipScanIfSysop option is enabled and the user is a sysop, @@ -305,6 +312,7 @@ function processFile(pFilename) { // Extract the file to the work directory printf(gStatusPrintfStr, "\1m\1h", "Extracting the file..."); + console.crlf(); var errorStr = extractFileToDir(pFilename, workDir); if (errorStr.length == 0) { @@ -399,90 +407,86 @@ function processFile(pFilename) // file scan. function scanFilesInDir(pDir) { - // If pDir is unspecified, then just return. - if (typeof(pDir) != "string") - { - var retObj = new Object(); - retObj.cmdOutput = new Array(); - retObj.returnCode = -1; - return retObj; - } - if (pDir.length == 0) - { - var retObj = new Object(); - retObj.cmdOutput = new Array(); - retObj.returnCode = -2; - return retObj; - } - // Also, just return if gGenCfg.scanCmd is blank. - if (gGenCfg.scanCmd.length == 0) - { - var retObj = new Object(); - retObj.cmdOutput = new Array(); - retObj.returnCode = -3; - return retObj; - } + var retObj = { + cmdOutput: [], + returnCode: 0 + }; - // If the filename has a trailing slash, remove it. - if ((/\/$/.test(pDir)) || (/\\$/.test(pDir))) - pDir = pDir.substr(0, pDir.length-1); + // If pDir is unspecified, then just return. + if (typeof(pDir) != "string") + { + retObj.returnCode = -1; + return retObj; + } + if (pDir.length == 0) + { + retObj.returnCode = -2; + return retObj; + } + // Also, just return if gGenCfg.scanCmd is blank. + if (gGenCfg.scanCmd.length == 0) + { + retObj.returnCode = -3; + return retObj; + } - var retObj = null; // Will be used to capture the return from the scan commands + // If the filename has a trailing slash, remove it. + if ((/\/$/.test(pDir)) || (/\\$/.test(pDir))) + pDir = pDir.substr(0, pDir.length-1); - // If the virus scan command contains %FILESPEC%, then - // replace %FILESPEC% with pDir and run the scan command. - if (gGenCfg.scanCmd.indexOf("%FILESPEC%") > -1) - { - var scanCmd = gGenCfg.scanCmd.replace("%FILESPEC%", "\"" + fixPathSlashes(pDir) + "\""); - retObj = runExternalCmdWithOutput(scanCmd); - - // This is old code, for scanning each file individually (slow): - /* - // Get a list of the files, and scan them. - var files = directory(pDir + "/*"); - if (files.length > 0) - { - var scanCmd = null; // Will be used for the scan commands (string) - var counter = 0; // Loop variable - for (var i in files) - { - // If the file is a directory, then recurse into it. Otherwise, - // scan the file using the configured scan command. - if (file_isdir(files[i])) - retObj = scanFilesInDir(files[i]); - else - { - scanCmd = gGenCfg.scanCmd.replace("%FILESPEC%", "\"" + fixPathSlashes(files[i]) + "\""); - // Run the scan command and capture its output, in case the scan fails. - retObj = runExternalCmdWithOutput(scanCmd); - } - - // If there's a problem, then stop going through the list of files. - if (retObj.returnCode != 0) - break; - } - } - else - { - // There are no files. So create retObj with default settings - // for a good result. - retObj = new Object(); - retObj.returnCode = 0; - retObj.cmdOutput = new Array(); - } - */ - } - else - { - // gGenCfg.scanCmd doesn't contain %FILESPEC%, so set up - // retObj with a non-zero return code (for failure) - retObj = new Object(); - retObj.returnCode = -4; - retObj.cmdOutput = new Array(); - retObj.cmdOutput.push("The virus scanner is not set up correctly."); - } + // If the virus scan command contains %FILESPEC%, then + // replace %FILESPEC% with pDir and run the scan command. + if (gGenCfg.scanCmd.indexOf("%FILESPEC%") > -1) + { + var scanCmd = gGenCfg.scanCmd.replace("%FILESPEC%", "\"" + fixPathSlashes(pDir) + "\""); + retObj = runExternalCmdWithOutput(scanCmd); + + // This is old code, for scanning each file individually (slow): + /* + // Get a list of the files, and scan them. + var files = directory(pDir + "/*"); + if (files.length > 0) + { + var scanCmd = null; // Will be used for the scan commands (string) + var counter = 0; // Loop variable + for (var i in files) + { + // If the file is a directory, then recurse into it. Otherwise, + // scan the file using the configured scan command. + if (file_isdir(files[i])) + retObj = scanFilesInDir(files[i]); + else + { + scanCmd = gGenCfg.scanCmd.replace("%FILESPEC%", "\"" + fixPathSlashes(files[i]) + "\""); + // Run the scan command and capture its output, in case the scan fails. + retObj = runExternalCmdWithOutput(scanCmd); + } - return retObj; + // If there's a problem, then stop going through the list of files. + if (retObj.returnCode != 0) + break; + } + } + else + { + // There are no files. So create retObj with default settings + // for a good result. + retObj = { + returnCode: 0, + cmdOutput = [] + }; + } + */ + } + else + { + // gGenCfg.scanCmd doesn't contain %FILESPEC%, so set up + // retObj with a non-zero return code (for failure) + retObj.returnCode = -4; + retObj.cmdOutput.push("The virus scanner is not set up correctly."); + } + + return retObj; } // Reads the configuration file and returns an object containing the @@ -494,156 +498,70 @@ function scanFilesInDir(pDir) // Return value: Boolean - Whether or not the configuration was read. function ReadConfigFile(pCfgFilePath) { - // Read the file type settings. - var fileTypeSettingsRead = false; - var fileTypeCfgFile = new File(pCfgFilePath + "ddup_file_types.cfg"); - if (fileTypeCfgFile.open("r")) - { - if (fileTypeCfgFile.length > 0) - { - fileTypeSettingsRead = true; - // Read each line from the config file and set the - // various options. - var pos = 0; // Index of = in the file lines - var fileLine = ""; - var filenameExt = ""; // Archive filename extension - var option = ""; // Configuration option - var optionValue = ""; // Configuration option value - var optionValueUpper; // Upper-cased configuration option value - var scannableFile = null; // Will be used to create & store scannable file options - while (!fileTypeCfgFile.eof) - { - // Read the line from the config file, look for a =, and - // if found, read the option & value and set them - // in cfgObj. - fileLine = fileTypeCfgFile.readln(1024); - - // fileLine should be a string, but I've seen some cases - // where it isn't, so check its type. - if (typeof(fileLine) != "string") - continue; - - // If the line is blank or starts with with a semicolon - // (the comment character), then skip it. - if ((fileLine.length == 0) || (fileLine.substr(0, 1) == ";")) - continue; - - // Look for a file extension in square brackets ([ and ]). - // If found, then set filenameExt and continue onto the next line. - // Note: This regular expression allows whitespace around the [...]. - if (/^\s*\[.*\]\s*$/.test(fileLine)) - { - var startIndex = fileLine.indexOf("[") + 1; - var endIndex = fileLine.lastIndexOf("]"); - var ext = fileLine.substr(startIndex, endIndex-startIndex).toUpperCase(); - // If the filename extension is different than the last one - // we've seen, then: - // 1. If scannableFile is not null, then add it to gScannableFileTypes. - // 2. Create a new one (referenced as scannableFile). - if (ext != filenameExt) - { - if ((scannableFile != null) && (scannableFile != undefined) && - (filenameExt.length > 0)) - { - gFileTypeCfg[filenameExt] = scannableFile; - } - filenameExt = ext; - scannableFile = new ScannableFile(ext, "", "scan"); - } - continue; - } - - // If filenameExt is blank, then continue onto the next line. - if (filenameExt.length == 0) - continue; - - // If we're here, then filenameExt is set, and this is a valid - // line to process. - // Look for an = in the line, and if found, split into - // option & value. - pos = fileLine.indexOf("="); - if (pos > -1) - { - // Extract the option & value, trimming leading & trailing spaces. - option = trimSpaces(fileLine.substr(0, pos), true, false, true).toUpperCase(); - optionValue = trimSpaces(fileLine.substr(pos+1), true, false, true); - - if (option == "EXTRACT") - scannableFile.extractCmd = optionValue; - else if (option == "SCANOPTION") - scannableFile.scanOption = optionValue; - } - } - } - - fileTypeCfgFile.close(); - } - - // Read the general program configuration - var genSettingsRead = false; - var genCfgFile = new File(pCfgFilePath + "ddup.cfg"); - if (genCfgFile.open("r")) - { - if (genCfgFile.length > 0) - { - genSettingsRead = true; - var fileLine = null; // A line read from the file - var equalsPos = 0; // Position of a = in the line - var commentPos = 0; // Position of the start of a comment - var setting = null; // A setting name (string) - var settingUpper = null; // Upper-case setting name - var value = null; // A value for a setting (string) - while (!genCfgFile.eof) - { - // Read the next line from the config file. - fileLine = genCfgFile.readln(1024); - - // fileLine should be a string, but I've seen some cases - // where it isn't, so check its type. - if (typeof(fileLine) != "string") - continue; - - // If the line starts with with a semicolon (the comment - // character) or is blank, then skip it. - if ((fileLine.substr(0, 1) == ";") || (fileLine.length == 0)) - continue; - - // If the line has a semicolon anywhere in it, then remove - // everything from the semicolon onward. - commentPos = fileLine.indexOf(";"); - if (commentPos > -1) - fileLine = fileLine.substr(0, commentPos); - - // Look for an equals sign, and if found, separate the line - // into the setting name (before the =) and the value (after the - // equals sign). - equalsPos = fileLine.indexOf("="); - if (equalsPos > 0) - { - // Read the setting & value, and trim leading & trailing spaces. - setting = trimSpaces(fileLine.substr(0, equalsPos), true, false, true); - settingUpper = setting.toUpperCase(); - value = trimSpaces(fileLine.substr(equalsPos+1), true, false, true); - - // Skip this one if the value is blank. - if (value.length == 0) - continue; - - // Set the appropriate value in the settings object. - if (settingUpper == "SCANCMD") - gGenCfg.scanCmd = value; - else if (settingUpper == "SKIPSCANIFSYSOP") - gGenCfg.skipScanIfSysop = (value.toUpperCase() == "YES"); - else if (settingUpper == "PAUSEATEND") - gGenCfg.pauseAtEnd = (value.toUpperCase() == "YES"); - } - } + // Read the file type settings. + var fileTypeSettingsRead = false; + var fileTypeCfgFile = new File(pCfgFilePath + "ddup_file_types.cfg"); + if (fileTypeCfgFile.open("r")) + { + if (fileTypeCfgFile.length > 0) + { + var allFileTypeCfg = fileTypeCfgFile.iniGetAllObjects(); + fileTypeSettingsRead = true; + for (var i = 0; i < allFileTypeCfg.length; ++i) + { + var filenameExt = allFileTypeCfg[i].name; // Filename extension + var scannableFile = new ScannableFile(filenameExt, "", "scan"); + for (var prop in allFileTypeCfg[i]) + { + var propUpper = prop.toUpperCase(); + if (propUpper === "EXTRACT") + scannableFile.extractCmd = allFileTypeCfg[i][prop]; + else if (propUpper === "SCANOPTION") + scannableFile.scanOption = allFileTypeCfg[i][prop]; + } + gFileTypeCfg[filenameExt] = scannableFile; + } + } + fileTypeCfgFile.close(); + } - genCfgFile.close(); - } - } + // Read the general configuration + var genSettingsRead = false; + var genCfgFile = new File(pCfgFilePath + "ddup.cfg"); + if (genCfgFile.open("r")) + { + if (genCfgFile.length > 0) + { + var settingsObj = genCfgFile.iniGetObject(); + genSettingsRead = true; + for (var prop in settingsObj) + { + // Set the appropriate value in the settings object. + var settingUpper = prop.toUpperCase(); + if (settingUpper == "SCANCMD") + gGenCfg.scanCmd = settingsObj[prop]; + else if (settingUpper == "SKIPSCANIFSYSOP") + { + if (typeof(settingsObj[prop]) === "string") + gGenCfg.skipScanIfSysop = (settingsObj[prop].toUpperCase() == "YES"); + else if (typeof(settingsObj[prop]) === "boolean") + gGenCfg.skipScanIfSysop = settingsObj[prop]; + } + else if (settingUpper == "PAUSEATEND") + { + if (typeof(settingsObj[prop]) === "string") + { + var valueUpper = settingsObj[prop].toUpperCase(); + gGenCfg.pauseAtEnd = (valueUpper == "YES" || valueUpper == "TRUE"); + } + else if (typeof(settingsObj[prop]) === "boolean") + gGenCfg.pauseAtEnd = settingsObj[prop]; + } + } + } + } - return (fileTypeSettingsRead && genSettingsRead); + return (fileTypeSettingsRead && genSettingsRead); } // Removes multiple, leading, and/or trailing spaces @@ -872,7 +790,6 @@ function extractFileToDir(pFilename, pWorkDir) // due to certain characters in the filename. var numFilesExtracted = arcFile.extract(pWorkDir, true); builtInExtractSucceeded = true; - if (user.is_sysop) console.print("\x01n\r\n\x01gExtracted with Synchronet's built-in Archive support\x01n\r\n\r\n"); // Temporary } catch (e) { diff --git a/xtrn/dd_upload_processor/readme.txt b/xtrn/dd_upload_processor/readme.txt index ff0ef27df0369a0d8feeb15d451c03a12932f13c..da0b7c93f47e3e8caba5594f013926c83f860238 100644 --- a/xtrn/dd_upload_processor/readme.txt +++ b/xtrn/dd_upload_processor/readme.txt @@ -1,6 +1,6 @@ Digital Distortion Upload Processor - Version 1.04 - Release date: 2023-08-06 + Version 1.05 + Release date: 2023-08-07 by diff --git a/xtrn/dd_upload_processor/version_history.txt b/xtrn/dd_upload_processor/version_history.txt index 81cc8dae61a05a22e01c679b4ffb450648d65a35..c7a9fa699b85daae511f5389035a5e9deaec69a7 100644 --- a/xtrn/dd_upload_processor/version_history.txt +++ b/xtrn/dd_upload_processor/version_history.txt @@ -2,6 +2,8 @@ Revision History for Digital Distortion Upload Processor ======================================================== Version Date Description ------- ---- ----------- +1.05 2023-08-07 Internal refactor of how the configuration files are + read. Behavior is the same. 1.04 2023-08-06 Now uses Synchronet's built-in archiver (added in Synchronet 3.19), if available, to extract archives. 1.03 2022-06-11 Removed the chmod updates because they're actually not