From ffdccd5754bd47b99a235228b1364015fbcf74a4 Mon Sep 17 00:00:00 2001 From: jliddev Date: Tue, 8 Dec 2020 16:49:28 -0600 Subject: [PATCH] Fix some issues with the CurseForge fingerprint scanner --- .../src/common/curse/curse-folder-scanner.ts | 87 ++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/wowup-electron/src/common/curse/curse-folder-scanner.ts b/wowup-electron/src/common/curse/curse-folder-scanner.ts index 44e5b609..48cac8da 100644 --- a/wowup-electron/src/common/curse/curse-folder-scanner.ts +++ b/wowup-electron/src/common/curse/curse-folder-scanner.ts @@ -12,8 +12,43 @@ export class CurseFolderScanner { // This map is required for solving for case sensitive mismatches from addon authors on Linux private _fileMap: { [key: string]: string } = {}; + private readonly _invalidPathChars = [ + "|", + "\0", + "\u0001", + "\u0002", + "\u0003", + "\u0004", + "\u0005", + "\u0006", + "\b", + "\t", + "\n", + "\v", + "\f", + "\r", + "\u000e", + "\u000f", + "\u0010", + "\u0011", + "\u0012", + "\u0013", + "\u0014", + "\u0015", + "\u0016", + "\u0017", + "\u0018", + "\u0019", + "\u001a", + "\u001b", + "\u001c", + "\u001d", + "\u001e", + "\u001f", + ]; + private get tocFileCommentsRegex() { - return /\s*#.*$/gm; + return /\s*#.*$/gim; } private get tocFileIncludesRegex() { @@ -21,31 +56,32 @@ export class CurseFolderScanner { } private get tocFileRegex() { - return /^([^\/]+)[\\\/]\1\.toc$/i; + return /^([^\/]+)[\\\/]\1\.toc$/gim; } private get bindingsXmlRegex() { - return /^[^\/\\]+[\/\\]Bindings\.xml$/i; + return /^[^\/\\]+[\/\\]Bindings\.xml$/gim; } private get bindingsXmlIncludesRegex() { - return /<(?:Include|Script)\s+file=[\""\""']((?:(?/gi; + return /<(?:Include|Script)\s+file=[\""\""']((?:(?/gis; } private get bindingsXmlCommentsRegex() { - return //gs; + return //gims; } async scanFolder(folderPath: string): Promise { const fileList = await readDirRecursive(folderPath); fileList.forEach((fp) => (this._fileMap[fp.toLowerCase()] = fp)); - // log.debug("listAllFiles", folderPath, fileList.length); let matchingFiles = await this.getMatchingFiles(folderPath, fileList); - matchingFiles = _.sortBy(matchingFiles, (f) => f.toLowerCase()); + matchingFiles = _.orderBy(matchingFiles, [(f) => f.toLowerCase()], ["asc"]); // log.debug("matchingFiles", matchingFiles.length); + const sq = matchingFiles.map((mf) => mf.toLowerCase()).join("\n"); + let individualFingerprints = await async.mapLimit(matchingFiles, 4, async (path, callback) => { try { const fileHash = await this.getFileHash(path); @@ -72,7 +108,7 @@ export class CurseFolderScanner { } private async getMatchingFiles(folderPath: string, filePaths: string[]): Promise { - const parentDir = path.dirname(folderPath) + path.sep; + const parentDir = path.normalize(path.dirname(folderPath) + path.sep); const matchingFileList: string[] = []; const fileInfoList: string[] = []; for (let filePath of filePaths) { @@ -119,18 +155,27 @@ export class CurseFolderScanner { const dirname = path.dirname(nativePath); for (let include of inclusions) { + if (this.hasInvalidPathChars(include)) { + log.debug(`Invalid include file ${include}`); + break; + } + const fileName = path.join(dirname, include.replace(/\\/g, path.sep)); await this.processIncludeFile(matchingFileList, fileName); } } + private hasInvalidPathChars(path: string) { + return this._invalidPathChars.some((c) => path.indexOf(c) !== -1); + } + private getFileInclusionMatches(fileInfo: string, fileContent: string): string[] | null { const ext = path.extname(fileInfo); switch (ext) { case ".xml": - return this.matchAll(fileContent, this.bindingsXmlIncludesRegex); + return this.ripMatch(fileContent, () => this.bindingsXmlIncludesRegex); case ".toc": - return this.matchAll(fileContent, this.tocFileIncludesRegex); + return this.ripMatch(fileContent, () => this.tocFileIncludesRegex); default: return null; } @@ -148,6 +193,28 @@ export class CurseFolderScanner { } } + /** + * Recreate a strange behavior for .net regex regarding how it treats + * lines that end in \r\t vs lines with \r\n\t + */ + private ripMatch(str: string, regex: () => RegExp): string[] { + const splitStrings = str.split("\n"); + const matches = []; + try { + for (const splitStr of splitStrings) { + const trimmedStr = splitStr.trim(); + const match = regex().exec(trimmedStr); + if (match && match.length > 1) { + matches.push(match[1]); + } + } + } catch (e) { + log.error(e); + } + + return matches.map((s) => s.trim()); + } + private matchAll(str: string, regex: RegExp): string[] { const matches: string[] = []; let currentMatch: RegExpExecArray;