mirror of
https://github.com/WowUp/WowUp.git
synced 2026-05-19 20:15:12 -04:00
Fix some issues with the CurseForge fingerprint scanner
This commit is contained in:
@@ -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=[\""\""']((?:(?<!\.\.).)+)[\""\""']\s*\/>/gi;
|
||||
return /<(?:Include|Script)\s+file=[\""\""']((?:(?<!\.\.).)+)[\""\""']\s*\/>/gis;
|
||||
}
|
||||
|
||||
private get bindingsXmlCommentsRegex() {
|
||||
return /<!--.*?-->/gs;
|
||||
return /<!--.*?-->/gims;
|
||||
}
|
||||
|
||||
async scanFolder(folderPath: string): Promise<CurseScanResult> {
|
||||
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<string, number>(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<string[]> {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user