/// <summary> /// Process an actual PackageManifest result, either from a list or just a standalone one. /// </summary> internal static void ProcessManifest(PackageManifest manifest, string hostUsername, string repoName) { if (manifest == null) { return; } if (string.IsNullOrEmpty(manifest.author) || !string.Equals(hostUsername, manifest.author, StringComparison.OrdinalIgnoreCase)) { manifest.author = hostUsername; } manifest.repository = repoName; var state = GetStateForGuid(manifest.GUID); if (state == GuidFilterState.Blacklist) { return; } if (state == GuidFilterState.BrokenList) { manifest.m_installState = InstallState.NotWorking; } if (s_webManifests.ContainsKey(manifest.GUID)) { Console.WriteLine("Duplicate web manifests found! Skipping this one: " + manifest.GUID); return; } s_webManifests.Add(manifest.GUID, manifest); if (LocalPackageManager.TryGetInstalledPackage(manifest.GUID) is PackageManifest installed) { if (installed.version == manifest.version) { // Same version, but manifest may have been updated. // Ensure the local manifest matches the web one. var path = installed.IsDisabled ? Folders.MEFINO_DISABLED_FOLDER : Folders.OUTWARD_PLUGINS; path += $@"\{installed.GUID}\{LocalPackageManager.PKG_MANIFEST_FILENAME}"; if (File.Exists(path)) { File.Delete(path); } File.WriteAllText(path, manifest.ToJsonObject().ToString(true)); //LocalPackageManager.RefreshInstalledPackages(); } else { installed.m_installState = installed.CompareVersionAgainst(manifest); } } }
private static bool CheckFork(string hostUsername, PackageManifest manifest, string definedAuthor = null) { if (string.IsNullOrEmpty(hostUsername)) { return(false); } definedAuthor = definedAuthor ?? manifest.author; return(string.Equals(hostUsername, definedAuthor, StringComparison.OrdinalIgnoreCase)); }
/// <summary> /// Check a package for its dependencies before enabling it. /// </summary> /// <param name="missing">If there are dependencies which are not installed at all, they will be added to this list.</param> /// <returns><see langword="true"/> if successful, otherwise <see langword="false"/></returns> internal static bool PreEnableDependencyCheck(PackageManifest package, List <string> missing) { if (!package.TryEnableAllDependencies(false)) { missing.RemoveAll(it => s_enabledPackages.ContainsKey(it)); if (missing.Any()) { string miss = ""; foreach (var entry in missing) { miss += $"\n{entry}"; } var result = MessageBox.Show($"To enable '{package.name}', the following dependencies need to be installed:" + $"\n" + $"{miss}" + $"\n\n" + $"Do you want to install them?", "Missing dependencies", MessageBoxButtons.OKCancel); if (result == DialogResult.Cancel) { return(false); } else { var check = package.TryEnableAllDependencies(true); if (check == false) { Console.WriteLine("Check returned false!"); MessageBox.Show("Unable to install all dependencies!"); return(false); } } } } return(true); }
/// <summary> /// Deserialize a package from a given JSON string (eg, the result of <see cref="JsonObject.ToString"/>). /// </summary> /// <param name="jsonString">A valid JSON string to deserialize.</param> /// <returns>A deserialized package if successful, otherwise <see langword="null"/>.</returns> internal static PackageManifest FromManifestJson(string jsonString) { try { var json = JsonReader.Parse(jsonString); var ret = new PackageManifest { author = json[nameof(author)].AsString, repository = json[nameof(repository)].AsString, name = json[nameof(name)].AsString, version = json[nameof(version)].AsString, required_version = json[nameof(required_version)].AsString, description = json[nameof(description)].AsString, download_filename = json[nameof(download_filename)].AsString, require_sync = json[nameof(require_sync)].AsBoolean, }; if (json[nameof(tags)].AsJsonArray is JsonArray tag) { ret.tags = tag.Select(it => it.AsString)?.ToArray(); } if (json[nameof(dependencies)].AsJsonArray is JsonArray deps) { ret.dependencies = deps.Select(it => it.AsString)?.ToArray(); } if (json[nameof(conflicts_with)].AsJsonArray is JsonArray conflicts) { ret.conflicts_with = conflicts.Select(it => it.AsString)?.ToArray(); } return(ret); } catch (Exception ex) { Console.WriteLine("Exception parsing PackageManifest from Json!"); Console.WriteLine(ex); //Console.WriteLine("Json string: " + jsonString); return(default);
/// <summary> /// Check a package for enabled dependant packages before removing/disabling it. /// </summary> /// <param name="package"></param> /// <param name="disabling">Is the package just being disabled? (Just used for the dialog prompt)</param> /// <returns><see langword="true"/> if successful or no dependants, otherwise <see langword="false"/></returns> internal static bool PreRemovalDependencyCheck(PackageManifest package, bool disabling) { var dependencies = package.GetCurrentlyEnabledDependantPackages(); bool ret = true; if (dependencies.Any()) { string msg = ""; foreach (var dep in dependencies) { msg += $"\n{dep}"; } string action = disabling ? "disable" : "remove"; var msgResult = MessageBox.Show($"The following packages depend on {package.name}:" + $"\n{msg}" + $"\n\nThese will be disabled. Really {action} it?", "Dependency conflict!", MessageBoxButtons.OKCancel); if (msgResult != DialogResult.OK) { ret = false; } else { foreach (var guid in dependencies) { TryDisablePackage(guid, true); } } } return(ret); }
/// <summary> /// Check a package for conflicts before enabling it. /// </summary> /// <returns><see langword="true"/> if successful or no conflicts, otherwise <see langword="false"/></returns> internal static bool PreEnableConflictCheck(PackageManifest package) { // Check for packages which declare a conflict with this package. HashSet <string> conflicts = new HashSet <string>(); if (package.HasAnyEnabledConflicts(out List <string> normConflicts)) { foreach (var c in normConflicts) { if (!conflicts.Contains(c)) { conflicts.Add(c); } } } var altConflicts = package.GetEnabledConflictsAlternate(); if (altConflicts.Any()) { foreach (var c in altConflicts) { if (!conflicts.Contains(c)) { conflicts.Add(c); } } } if (!conflicts.Any()) { return(true); } var conflictTxt = ""; foreach (var conflict in conflicts) { conflictTxt += $"\n{conflict}"; } var msgBox = MessageBox.Show($"The following packages conflict with {package.name} and need to be disabled:\n" + $"{conflictTxt}\n\n" + $"Disable them and continue?", "Conflicts detected", MessageBoxButtons.OKCancel); if (msgBox == DialogResult.OK) { foreach (var pkg in conflicts) { if (!TryDisablePackage(pkg, true)) { return(false); } } return(true); } else { return(false); } }
/// <summary> /// Actually download and install the provided PackageManifest instance, which would presumably be a web manifest. /// </summary> /// <param name="manifest">The PackageManifest to install.</param> /// <returns><see langword="true"/> if successful, otherwise <see langword="false"/></returns> internal static bool DownloadAndInstallPackage(PackageManifest manifest) { try { MefinoGUI.SetProgressBarActive(true); MefinoGUI.DisableSensitiveControls(); var dirPath = $@"{Folders.MEFINO_DISABLED_FOLDER}\{manifest.GUID}"; if (Directory.Exists(dirPath)) { Directory.Delete(dirPath, true); } var tempFile = TemporaryFile.CreateFile(); var zipUrl = $"{manifest.GithubURL}/releases/latest/download/{manifest.DownloadFileName}"; Web.WebClientManager.DownloadFileAsync(zipUrl, tempFile); while (Web.WebClientManager.IsBusy) { Thread.Sleep(20); MefinoApp.SendAsyncProgress(Web.WebClientManager.LastDownloadProgress); } MefinoGUI.SetProgressMessage($"Installing '{manifest.GUID}' (version {manifest.version})"); if (ZipHelper.ExtractZip(tempFile, dirPath)) { var manifestPath = $@"{dirPath}\mefino-manifest.json"; if (File.Exists(manifestPath)) { File.Delete(manifestPath); } File.WriteAllText(manifestPath, manifest.ToJsonObject().ToString(true)); //Console.WriteLine($"Installed package: {manifest.GUID} {manifest.version}"); MefinoGUI.SetProgressBarActive(false); MefinoGUI.ReEnableSensitiveControls(); return(true); } else { throw new Exception("Zip extraction failed!"); } } catch (Exception ex) { MefinoGUI.SetProgressBarActive(false); MefinoGUI.ReEnableSensitiveControls(); Console.WriteLine("Exception isntalling package '" + manifest.GUID + "'"); Console.WriteLine($"{ex.GetType()}: {ex.Message}"); //Mefino.SendAsyncCompletion(false); return(false); } }
/// <summary> /// Load the cached web manifests from disk. /// </summary> internal static void LoadWebManifestCache() { s_webManifests.Clear(); if (!File.Exists(MANIFEST_CACHE_FILEPATH)) { return; } try { JsonValue manifests = JsonReader.ParseFile(MANIFEST_CACHE_FILEPATH); var input = manifests.AsJsonObject; var times = input["cachetimes"].AsJsonObject; if (times != null) { s_repoCacheTimes.Clear(); foreach (var entry in times) { var time = entry.Value.AsDateTime; if (time == null) { continue; } if (s_repoCacheTimes.ContainsKey(entry.Key)) { continue; } s_repoCacheTimes.Add(entry.Key, (DateTime)time); } } var items = input["manifests"].AsJsonArray; foreach (var entry in items) { var manifest = PackageManifest.FromManifestJson(entry.AsJsonObject.ToString()); if (manifest == default) { continue; } if (s_webManifests.ContainsKey(manifest.GUID)) { Console.WriteLine("Duplicate manifest in web cache! Skipping: " + manifest.GUID); continue; } manifest.m_installState = InstallState.NotInstalled; s_webManifests.Add(manifest.GUID, manifest); } } catch (Exception ex) { Console.WriteLine("Exception parsing manifest file cache!"); Console.WriteLine(ex); } }
/// <summary> /// Process a `mefino-manifest.json` file, which may be a list of manifests or just one. /// </summary> internal static void ProcessManifestFile(string manifestString, string hostUsername, string repoName, bool isFork) { var jsonVal = JsonReader.Parse(manifestString); if (jsonVal == default) { return; } var json = jsonVal.AsJsonObject; if (json == null) { return; } // Console.WriteLine("processing json " + manifestString); var array = json["packages"]; if (array != JsonValue.Null && array.AsJsonArray is JsonArray packages) { string definedAuthor; var name = json["author"]; if (name != JsonValue.Null && name.AsString is string) { definedAuthor = name.AsString; } else { definedAuthor = null; } foreach (var entry in packages) { var manifest = PackageManifest.FromManifestJson(entry.ToString()); if (isFork && !CheckFork(hostUsername, manifest, definedAuthor)) { continue; } if (manifest != null) { ProcessManifest(manifest, hostUsername, repoName); } } } else { var manifest = PackageManifest.FromManifestJson(manifestString); if (isFork && !CheckFork(hostUsername, manifest)) { return; } if (manifest != null) { ProcessManifest(manifest, hostUsername, repoName); } } }