diff --git a/CHANGELOG.md b/CHANGELOG.md index 62920c3a7d..4069a7f3c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ All notable changes to this project will be documented in this file. - [Netkan] Coerce GitHub URLs into the authenticated API in Netkan (#2946 by: HebaruSan; reviewed: DasSkelett) - [Netkan] Request fewer GitHub releases and cache string URLs in Netkan (#2950 by: HebaruSan) - [GUI] Create ~/.local/share/applications/ if it doesn't exist on Linux (#1848 by: DinCahill; reviewed: ayan4ml, politas, dannydi12) +- [Netkan] Catch nested GameData folders in Netkan (#2948 by: HebaruSan; reviewed: techman83) ## v1.26.6 (Leonov) diff --git a/Netkan/ConsoleUser.cs b/Netkan/ConsoleUser.cs index 4540d88520..a8cec710e3 100644 --- a/Netkan/ConsoleUser.cs +++ b/Netkan/ConsoleUser.cs @@ -20,7 +20,7 @@ public class ConsoleUser : IUser /// /// Initializes a new instance of the class. /// - /// If set to true, supress interactive dialogs like Yes/No-Dialog or SelectionDialog. + /// If set to true, suppress interactive dialogs like Yes/No-Dialog or SelectionDialog. public ConsoleUser (bool headless) { Headless = headless; diff --git a/Netkan/Services/IModuleService.cs b/Netkan/Services/IModuleService.cs index 1ac9302051..062e10fc02 100644 --- a/Netkan/Services/IModuleService.cs +++ b/Netkan/Services/IModuleService.cs @@ -13,5 +13,6 @@ internal interface IModuleService IEnumerable GetConfigFiles(CkanModule module, ZipFile zip); + IEnumerable FileDestinations(CkanModule module, string filePath); } } diff --git a/Netkan/Services/ModuleService.cs b/Netkan/Services/ModuleService.cs index 6303933d79..7c4f3a30ab 100644 --- a/Netkan/Services/ModuleService.cs +++ b/Netkan/Services/ModuleService.cs @@ -72,6 +72,13 @@ public IEnumerable GetConfigFiles(CkanModule module, ZipFile zi .Where(instF => cfgRegex.IsMatch(instF.source.Name)); } + public IEnumerable FileDestinations(CkanModule module, string filePath) + { + return ModuleInstaller + .FindInstallableFiles(module, filePath, new KSP("/", "dummy", null, false)) + .Select(f => f.destination); + } + /// /// Return a parsed JObject from a stream. /// diff --git a/Netkan/Validators/InstallsFilesValidator.cs b/Netkan/Validators/InstallsFilesValidator.cs index 5a404126ac..a03b346eeb 100644 --- a/Netkan/Validators/InstallsFilesValidator.cs +++ b/Netkan/Validators/InstallsFilesValidator.cs @@ -1,3 +1,4 @@ +using System.Linq; using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; @@ -20,7 +21,6 @@ public void Validate(Metadata metadata) var file = _http.DownloadPackage(metadata.Download, metadata.Identifier, metadata.RemoteTimestamp); // Make sure this would actually generate an install. - if (!_moduleService.HasInstallableFiles(mod, file)) { throw new Kraken(string.Format( @@ -28,6 +28,16 @@ public void Validate(Metadata metadata) mod.DescribeInstallStanzas() )); } + + // Make sure no paths include GameData other than at the start + var gamedatas = _moduleService.FileDestinations(mod, file) + .Where(p => p.StartsWith("GameData") && p.LastIndexOf("/GameData/") > 0) + .ToList(); + if (gamedatas.Any()) + { + var badPaths = string.Join("\r\n", gamedatas); + throw new Kraken($"GameData directory found within GameData:\r\n{badPaths}"); + } } } } diff --git a/Netkan/Validators/LicensesValidator.cs b/Netkan/Validators/LicensesValidator.cs index fdfe90e3af..a46e2e01ed 100644 --- a/Netkan/Validators/LicensesValidator.cs +++ b/Netkan/Validators/LicensesValidator.cs @@ -33,7 +33,7 @@ public void Validate(Metadata metadata) { if (licenses == null || licenses.Count < 1) { - throw new Kraken("License should match spec. Set `x_netkan_license_ok` to supress"); + throw new Kraken("License should match spec. Set `x_netkan_license_ok` to suppress"); } else foreach (var lic in licenses) { @@ -44,7 +44,7 @@ public void Validate(Metadata metadata) } catch { - throw new Kraken($"License {lic} should match spec. Set `x_netkan_license_ok` to supress"); + throw new Kraken($"License {lic} should match spec. Set `x_netkan_license_ok` to suppress"); } } }