private IEnumerable <SelectionCandidate> GetCandidates(FeedUri feedUri, Feed feed, Requirements requirements) { var feedPreferences = FeedPreferences.LoadForSafe(feedUri); foreach (var element in feed.Elements) { var packageImplementation = element as PackageImplementation; if (packageImplementation != null) { // Each <package-implementation> provides 0..n selection candidates var externalImplementations = _packageManager.Query(packageImplementation, requirements.Distributions.ToArray()); foreach (var externalImplementation in externalImplementations) { _externalImplementations[externalImplementation.ID] = externalImplementation; yield return(new SelectionCandidate(new FeedUri(FeedUri.FromDistributionPrefix + feedUri), feedPreferences, externalImplementation, requirements)); } } else if (requirements.Distributions.ContainsOrEmpty(Restriction.DistributionZeroInstall)) { var implementation = element as Implementation; if (implementation != null) { // Each <implementation> provides 1 selection candidate yield return(new SelectionCandidate(feedUri, feedPreferences, implementation, requirements, offlineUncached: (_config.NetworkUse == NetworkLevel.Offline) && !_isCached(implementation))); } } } }
/// <inheritdoc/> protected override ExitCode ExecuteHelper(ICategoryIntegrationManager integrationManager, FeedUri interfaceUri) { #region Sanity checks if (integrationManager == null) throw new ArgumentNullException("integrationManager"); if (interfaceUri == null) throw new ArgumentNullException("interfaceUri"); #endregion try { var entry = CreateAppEntry(integrationManager, ref interfaceUri); if (!CatalogManager.GetCachedSafe().ContainsFeed(entry.InterfaceUri)) WindowsUtils.BroadcastMessage(AddedNonCatalogAppWindowMessageID); // Notify Zero Install GUIs of changes return ExitCode.OK; } #region Error handling catch (WebException) { // WebException is a subclass of InvalidOperationException but we don't want to catch it here throw; } catch (InvalidOperationException ex) { // Application already in AppList Handler.OutputLow(Resources.DesktopIntegration, ex.Message); return ExitCode.NoChanges; } #endregion }
/// <summary> /// Returns a command-line for executing the "0install run" command. /// Generates and returns a stub EXE if possible, falls back to directly pointing to the "0install" EXE otherwise. /// </summary> /// <param name="target">The application to be launched.</param> /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param> /// <param name="machineWide"><c>true</c> place the generated stub in a machine-wide location; <c>false</c> to place it in the current user profile.</param> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception> /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception> public IReadOnlyList <string> GetRunCommandLine(FeedTarget target, string?command = null, bool machineWide = false) { string targetKey = target.Uri + "#" + command; var entryPoint = target.Feed.GetEntryPoint(command); bool gui = entryPoint is not { NeedsTerminal : true }; string targetHash = targetKey.Hash(SHA256.Create()); string exeName = (entryPoint == null) ? FeedUri.Escape(target.Feed.Name) : entryPoint.BinaryName ?? entryPoint.Command; string path = Path.Combine( IntegrationManager.GetDir(machineWide, "stubs", targetHash), exeName + ".exe"); #if !DEBUG try #endif { CreateOrUpdateRunStub(path, target, gui, command); return(new[] { path }); } #if !DEBUG catch (Exception ex) { var exe = GetExe(gui); Log.Error($"Failed to generate stub EXE for {targetKey}. Falling back to using '{exe}' directly.", ex); return(GetArguments(target.Uri, command, gui) .Prepend(Path.Combine(Locations.InstallBase, exe)) .ToList()); } #endif }
public void TestNormalizeLocalPath() { var localUri = new FeedUri(WindowsUtils.IsWindows ? @"C:\local\feed.xml" : "/local/feed.xml"); var implementation1 = new Implementation { ID = "./subdir" }; implementation1.Normalize(localUri); Assert.AreEqual( expected: WindowsUtils.IsWindows ? @"C:\local\.\subdir" : "/local/./subdir", actual: implementation1.ID); Assert.AreEqual( expected: WindowsUtils.IsWindows ? @"C:\local\.\subdir" : "/local/./subdir", actual: implementation1.LocalPath); var implementation2 = new Implementation { ID = "./wrong", LocalPath = "subdir" }; implementation2.Normalize(localUri); Assert.AreEqual( expected: "./wrong", actual: implementation2.ID); Assert.AreEqual( expected: WindowsUtils.IsWindows ? @"C:\local\subdir" : "/local/subdir", actual: implementation2.LocalPath); }
public void TestResolveAppAlias() { FeedUri uri = new FeedUri("http://example.com/test1.xml"); var appList = new AppList { Entries = { new AppEntry { AccessPoints = new AccessPointList{ Entries = { new AppAlias { Name = "foobar", Command = Command.NameTest } } }, InterfaceUri = uri } } }; appList.ResolveAlias("foobar").Should().Be(uri); appList.ResolveAlias("other").Should().BeNull(); }
/// <summary> /// Creates a new interface dialog. /// </summary> /// <param name="interfaceUri">The interface to modify the preferences for.</param> /// <param name="solveCallback">Called after <see cref="InterfacePreferences"/> have been changed and the <see cref="ISolver"/> needs to be rerun.</param> /// <param name="feedCache">The feed cache used to retrieve feeds for additional information about implementations.</param> private InterfaceDialog([NotNull] FeedUri interfaceUri, [NotNull] Func <Selections> solveCallback, [NotNull] IFeedCache feedCache) { #region Sanity checks if (interfaceUri == null) { throw new ArgumentNullException("interfaceUri"); } if (solveCallback == null) { throw new ArgumentNullException("solveCallback"); } if (feedCache == null) { throw new ArgumentNullException("feedCache"); } #endregion InitializeComponent(); comboBoxStability.Items.AddRange(new object[] { Resources.UseDefaultSetting, Stability.Stable, Stability.Testing, Stability.Developer }); dataColumnUserStability.Items.AddRange(Stability.Unset, Stability.Preferred, Stability.Packaged, Stability.Stable, Stability.Testing, Stability.Developer); _interfaceUri = interfaceUri; _mainFeed = feedCache.GetFeed(_interfaceUri); _solveCallback = solveCallback; _feedCache = feedCache; }
/// <summary> /// Creates a new application tile. /// </summary> /// <param name="interfaceUri">The interface URI of the application this tile represents.</param> /// <param name="appName">The name of the application this tile represents.</param> /// <param name="status">Describes whether the application is listed in the <see cref="AppList"/> and if so whether it is integrated.</param> /// <param name="iconCache">The icon cache used to retrieve icons specified in <see cref="Feed"/>; can be <see langword="null"/>.</param> /// <param name="machineWide">Apply operations machine-wide instead of just for the current user.</param> public AppTile([NotNull] FeedUri interfaceUri, [NotNull] string appName, AppStatus status, [CanBeNull] IIconCache iconCache = null, bool machineWide = false) { #region Sanity checks if (interfaceUri == null) { throw new ArgumentNullException("interfaceUri"); } if (appName == null) { throw new ArgumentNullException("appName"); } #endregion _machineWide = machineWide; InitializeComponent(); buttonRun.Text = _runButtonText; buttonAdd.Image = _addButton; buttonRemove.Image = _removeButton; buttonIntegrate.Image = _setupButton; toolTip.SetToolTip(buttonAdd, _addButtonTooltip); toolTip.SetToolTip(buttonRemove, _removeButtonTooltip); buttonSelectCommand.Text = _selectCommandButton; buttonSelectVersion.Text = _selectVersionButton; buttonUpdate.Text = _updateButtonText; InterfaceUri = interfaceUri; labelName.Text = appName; labelSummary.Text = ""; Status = status; _iconCache = iconCache; CreateHandle(); }
public ValidSignature CheckTrust(byte[] data, FeedUri uri, string localPath = null) { #region Sanity checks if (uri == null) throw new ArgumentNullException(nameof(uri)); if (data == null) throw new ArgumentNullException(nameof(data)); #endregion if (uri.IsFile) throw new UriFormatException(Resources.FeedUriLocal); var domain = new Domain(uri.Host); KeyImported: var signatures = FeedUtils.GetSignatures(_openPgp, data).ToList(); foreach (var signature in signatures.OfType<ValidSignature>()) if (_trustDB.IsTrusted(signature.FormatFingerprint(), domain)) return signature; foreach (var signature in signatures.OfType<ValidSignature>()) if (HandleNewKey(uri, signature.FormatFingerprint(), domain)) return signature; foreach (var signature in signatures.OfType<MissingKeySignature>()) { Log.Info("Missing key for " + signature.FormatKeyID()); AcquireMissingKey(signature, uri, localPath); goto KeyImported; } throw new SignatureException(string.Format(Resources.FeedNoTrustedSignatures, uri)); }
/// <inheritdoc/> public void Add(FeedUri feedUri, byte[] data) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException(nameof(feedUri)); } if (data == null) { throw new ArgumentNullException(nameof(data)); } #endregion if (!Directory.Exists(Path)) { Directory.CreateDirectory(Path); } try { string path = System.IO.Path.Combine(Path, feedUri.Escape()); Log.Debug($"Adding feed {feedUri.ToStringRfc()} to disk cache: {path}"); WriteToFile(data, path); } catch (PathTooLongException) { Log.Info("File path in feed cache too long. Using hash of feed URI to shorten path."); WriteToFile(data, System.IO.Path.Combine(Path, feedUri.AbsoluteUri.Hash(SHA256.Create()))); } }
/// <summary> /// Loads a feed and adds it to a dictionary if it is not already in it. Recursivley adds <see cref="Feed.Feeds"/> references. /// </summary> /// <param name="dictionary">The dictionary to add the feed to.</param> /// <param name="feedUri">The URI to load the feed from</param> /// <param name="requirements">Requirements to apply as a filter to <see cref="Feed.Feeds"/> references before following them.</param> private void AddFeedToDict([NotNull] IDictionary <FeedUri, Feed> dictionary, [CanBeNull] FeedUri feedUri, [NotNull] Requirements requirements) { if (feedUri == null || dictionary.ContainsKey(feedUri)) { return; } var feed = _feedManager[feedUri]; if (feed.MinInjectorVersion != null && FeedElement.ZeroInstallVersion < feed.MinInjectorVersion) { Log.Warn($"The solver version is too old. The feed '{feedUri}' requires at least version {feed.MinInjectorVersion} but the installed version is {FeedElement.ZeroInstallVersion}. Try updating Zero Install."); return; } dictionary.Add(feedUri, feed); foreach (var reference in feed.Feeds) { if (reference.Architecture.IsCompatible(requirements.Architecture) && (reference.Languages.Count == 0 || reference.Languages.ContainsAny(requirements.Languages, ignoreCountry: true))) { AddFeedToDict(dictionary, reference.Source, requirements); } } }
/// <inheritdoc/> public void AddSource(FeedUri uri) { var sources = GetSources().ToList(); sources.AddIfNew(uri); SetSources(sources); }
/// <inheritdoc/> public override ExitCode Execute() { var apps = AppList.LoadSafe(MachineWide); if (AdditionalArgs.Count > 0) { if (Uri.TryCreate(AdditionalArgs[0], UriKind.Absolute, out var uri)) { var feedUri = new FeedUri(uri); apps.Entries.RemoveAll(x => x.InterfaceUri != feedUri); } else { apps.Entries.RemoveAll(x => !x.Name.ContainsIgnoreCase(AdditionalArgs[0])); } } if (_xmlOutput) { Handler.Output(Resources.MyApps, apps.ToXmlString()); } else { Handler.Output(Resources.MyApps, apps.Entries); } return(ExitCode.OK); }
/// <summary> /// Detects attacks such as feed substitution or replay attacks. /// </summary> /// <param name="data">The content of the feed file as a byte array.</param> /// <param name="uri">The URI the feed originally came from.</param> /// <param name="signature">The first trusted signature for the feed.</param> /// <exception cref="ReplayAttackException">A replay attack was detected.</exception> /// <exception cref="UriFormatException"><see cref="Feed.Uri"/> is missing or does not match <paramref name="uri"/>.</exception> private void DetectAttacks(byte[] data, FeedUri uri, ValidSignature signature) { // Detect feed substitution var feed = XmlStorage.LoadXml <Feed>(new MemoryStream(data)); if (feed.Uri == null) { throw new UriFormatException(string.Format(Resources.FeedUriMissing, uri)); } if (feed.Uri != uri) { throw new UriFormatException(string.Format(Resources.FeedUriMismatch, feed.Uri, uri)); } // Detect replay attacks try { var oldSignature = _feedCache.GetSignatures(uri).OfType <ValidSignature>().FirstOrDefault(); if (oldSignature != null && signature.Timestamp < oldSignature.Timestamp) { throw new ReplayAttackException(uri, oldSignature.Timestamp, signature.Timestamp); } } catch (KeyNotFoundException) { // No existing feed to be replaced } }
/// <summary> /// Adds a feed to the list of custom feeds. /// </summary> private void AddCustomFeed(string input) { FeedUri feedUri; try { feedUri = new FeedUri(input); } catch (UriFormatException ex) { Msg.Inform(this, ex.Message, MsgSeverity.Error); return; } if (_interfaceUri.IsFile) { if (!File.Exists(_interfaceUri.LocalPath)) { Msg.Inform(this, string.Format(Resources.FileOrDirNotFound, _interfaceUri.LocalPath), MsgSeverity.Warn); return; } } else { Feed feed = null; try { using var handler = new DialogTaskHandler(this); handler.RunTask(new SimpleTask(Resources.CheckingFeed, delegate { using var webClient = new WebClientTimeout(); feed = XmlStorage.FromXmlString <Feed>(webClient.DownloadString(feedUri)); })); }
public void RemovePackageSource(string uri) { var feedUri = new FeedUri(uri); if (!CatalogManager.RemoveSource(feedUri)) Log.Warn(string.Format(Resources.CatalogNotRegistered, feedUri.ToStringRfc())); }
/// <inheritdoc/> public void ImportFeed(string path, FeedUri uri, FeedUri mirrorUrl = null) { #region Sanity checks if (uri == null) { throw new ArgumentNullException("uri"); } if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } #endregion if (uri.IsFile) { throw new UriFormatException(Resources.FeedUriLocal); } Log.Debug("Importing feed " + uri.ToStringRfc() + " from: " + path); var data = File.ReadAllBytes(path); var newSignature = _trustManager.CheckTrust(data, uri, mirrorUrl); DetectAttacks(data, uri, newSignature); // Add to cache and remember time _feedCache.Add(uri, data); var preferences = FeedPreferences.LoadForSafe(uri); preferences.LastChecked = DateTime.UtcNow; preferences.Normalize(); preferences.SaveFor(uri); }
//--------------------// #region Normalize /// <summary> /// Sets missing default values and handles legacy elements. /// </summary> /// <param name="feedUri">The feed the data was originally loaded from; can be <see langword="null"/>.</param> /// <remarks>This method should be called to prepare a <see cref="Feed"/> for solver processing. Do not call it if you plan on serializing the feed again since it may loose some of its structure.</remarks> public override void Normalize(FeedUri feedUri = null) { base.Normalize(feedUri); // Merge the version modifier into the normal version attribute if (!string.IsNullOrEmpty(VersionModifier)) { Version = new ImplementationVersion(Version + VersionModifier); VersionModifier = null; } // Default stability rating to testing if (Stability == Stability.Unset) { Stability = Stability.Testing; } // Make local paths absolute if (!string.IsNullOrEmpty(LocalPath)) { LocalPath = ModelUtils.GetAbsolutePath(LocalPath, feedUri); } else if (!string.IsNullOrEmpty(ID) && ID.StartsWith(".")) // Get local path from ID { LocalPath = ID = ModelUtils.GetAbsolutePath(ID, feedUri); } // Parse manifest digest from ID if missing if (!string.IsNullOrEmpty(ID)) { _manifestDigest.ParseID(ID); } }
/// <summary> /// Creates a new alias. /// </summary> /// <param name="aliasName">The name of the alias to create.</param> /// <param name="interfaceUri">The interface URI the alias shall point to.</param> /// <param name="command">A command within the interface the alias shall point to; can be <c>null</c>.</param> /// <returns>The exit status code to end the process with.</returns> private ExitCode CreateAlias(string aliasName, FeedUri interfaceUri, string command = null) { CheckInstallBase(); using (var integrationManager = new IntegrationManager(Handler, MachineWide)) { // Check this before modifying the environment bool needsReopenTerminal = NeedsReopenTerminal(integrationManager.MachineWide); var appEntry = GetAppEntry(integrationManager, ref interfaceUri); // Apply the new alias var alias = new AppAlias { Name = aliasName, Command = command }; integrationManager.AddAccessPoints(appEntry, FeedManager[interfaceUri], new AccessPoint[] { alias }); string message = string.Format(Resources.AliasCreated, aliasName, appEntry.Name); if (needsReopenTerminal) { message += Environment.NewLine + Resources.ReopenTerminal; } Handler.OutputLow(Resources.DesktopIntegration, message); return(ExitCode.OK); } }
public void TestNativeFeed() { var mainFeed = FeedTest.CreateTestFeed(); mainFeed.Elements.RemoveAt(1); mainFeed.Feeds.Clear(); _feedManagerMock.Setup(x => x.GetFeed(FeedTest.Test1Uri)).Returns(mainFeed); using (new LocationsRedirect("0install-unit-tests")) { var localUri = new FeedUri(Locations.GetSaveDataPath("0install.net", true, "native_feeds", mainFeed.Uri.PrettyEscape())); var subFeed = mainFeed.Clone(); subFeed.Uri = FeedTest.Sub1Uri; subFeed.Elements[0].Version = new ImplementationVersion("2.0"); subFeed.SaveXml(localUri.LocalPath); _feedManagerMock.Setup(x => x.GetFeed(localUri)).Returns(subFeed); var requirements = new Requirements(FeedTest.Test1Uri, Command.NameRun); CollectionAssert.AreEqual( expected: new[] { new SelectionCandidate(localUri, new FeedPreferences(), (Implementation)subFeed.Elements[0], requirements), new SelectionCandidate(FeedTest.Test1Uri, new FeedPreferences(), (Implementation)mainFeed.Elements[0], requirements) }, actual: Target.GetSortedCandidates(requirements)); } }
public void TestSitePackages() { var mainFeed = FeedTest.CreateTestFeed(); mainFeed.Feeds.Clear(); FeedManagerMock.Setup(x => x[FeedTest.Test1Uri]).Returns(mainFeed); using (new LocationsRedirect("0install-unit-tests")) { var pathComponents = mainFeed.Uri.EscapeComponent() .Prepend("site-packages") .Concat(new[] { "xyz", "0install", "feed.xml" }); var localUri = new FeedUri(Locations.GetSaveDataPath("0install.net", isFile: true, resource: pathComponents.ToArray())); var subFeed = mainFeed.Clone(); subFeed.Uri = FeedTest.Sub1Uri; subFeed.Elements[0].Version = new ImplementationVersion("2.0"); subFeed.SaveXml(localUri.LocalPath); FeedManagerMock.Setup(x => x[localUri]).Returns(subFeed); PackageManagerMock.Setup(x => x.Query((PackageImplementation)mainFeed.Elements[1])).Returns(Enumerable.Empty <ExternalImplementation>()); var requirements = new Requirements(FeedTest.Test1Uri, Command.NameRun); Target.GetSortedCandidates(requirements).Should().Equal( new SelectionCandidate(localUri, new FeedPreferences(), (Implementation)subFeed.Elements[0], requirements), new SelectionCandidate(FeedTest.Test1Uri, new FeedPreferences(), (Implementation)mainFeed.Elements[0], requirements)); } }
public static InterfacePreferences LoadForSafe([NotNull] FeedUri interfaceUri) { #region Sanity checks if (interfaceUri == null) { throw new ArgumentNullException("interfaceUri"); } #endregion try { return(LoadFor(interfaceUri)); } #region Error handling catch (IOException ex) { Log.Warn(string.Format(Resources.ErrorLoadingInterfacePrefs, interfaceUri)); Log.Warn(ex); return(new InterfacePreferences()); } catch (UnauthorizedAccessException ex) { Log.Warn(string.Format(Resources.ErrorLoadingInterfacePrefs, interfaceUri)); Log.Warn(ex); return(new InterfacePreferences()); } catch (InvalidDataException ex) { Log.Warn(string.Format(Resources.ErrorLoadingInterfacePrefs, interfaceUri)); Log.Warn(ex); return(new InterfacePreferences()); } #endregion }
/// <inheritdoc/> public void RemoveSource(FeedUri uri) { var sources = GetSources().ToList(); sources.Remove(uri); SetSources(sources); }
/// <summary> /// Removes a <see cref="FeedReference"/> from one or more <see cref="InterfacePreferences"/>. /// </summary> /// <returns>The interfaces that were actually affected.</returns> protected override ICollection <FeedUri> ApplyFeedToInterfaces(FeedUri feedUri, IEnumerable <FeedUri> interfaces) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException("feedUri"); } if (interfaces == null) { throw new ArgumentNullException("interfaces"); } #endregion var modified = new List <FeedUri>(); var reference = new FeedReference { Source = feedUri }; foreach (var interfaceUri in interfaces) { var preferences = InterfacePreferences.LoadFor(interfaceUri); if (preferences.Feeds.Remove(reference)) { modified.Add(interfaceUri); } preferences.SaveFor(interfaceUri); } return(modified); }
private void DownloadMissingKey(FeedUri uri, FeedUri mirrorUrl, MissingKeySignature signature) { var keyUri = new Uri(mirrorUrl ?? uri, signature.KeyID + ".gpg"); byte[] keyData; if (keyUri.IsFile) { // Load key file from local file keyData = File.ReadAllBytes(keyUri.LocalPath); } else { // Load key file from server try { using (var keyFile = new TemporaryFile("0install-key")) { _handler.RunTask(new DownloadFile(keyUri, keyFile)); keyData = File.ReadAllBytes(keyFile); } } #region Error handling catch (WebException ex) { // Wrap exception to add context information throw new SignatureException(string.Format(Resources.UnableToLoadKeyFile, uri), ex); } #endregion } Log.Info("Importing OpenPGP public key for " + signature.KeyID); _openPgp.ImportKey(keyData); }
/// <summary> /// Creates a new interface dialog. /// </summary> /// <param name="interfaceUri">The interface to modify the preferences for.</param> /// <param name="solveCallback">Called after <see cref="InterfacePreferences"/> have been changed and the <see cref="ISolver"/> needs to be rerun.</param> /// <param name="feedManager">The feed manager used to retrieve feeds for additional information about implementations.</param> private InterfaceDialog(FeedUri interfaceUri, Func <Selections> solveCallback, IFeedManager feedManager) { #region Sanity checks if (interfaceUri == null) { throw new ArgumentNullException(nameof(interfaceUri)); } if (solveCallback == null) { throw new ArgumentNullException(nameof(solveCallback)); } if (feedManager == null) { throw new ArgumentNullException(nameof(feedManager)); } #endregion InitializeComponent(); comboBoxStability.Items.AddRange(new object[] { Resources.UseDefaultSetting, Stability.Stable, Stability.Testing, Stability.Developer }); dataColumnUserStability.Items.AddRange(Stability.Unset, Stability.Preferred, Stability.Stable, Stability.Testing, Stability.Developer, Stability.Buggy, Stability.Insecure); _interfaceUri = interfaceUri; _mainFeed = feedManager[_interfaceUri]; _solveCallback = solveCallback; _feedManager = feedManager; }
/// <summary> /// Sets missing default values and handles legacy elements. /// </summary> /// <param name="feedUri">The feed the data was originally loaded from.</param> /// <remarks>This method should be called to prepare a <see cref="Feed"/> for solver processing. Do not call it if you plan on serializing the feed again since it may loose some of its structure.</remarks> public override void Normalize(FeedUri feedUri) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException(nameof(feedUri)); } #endregion base.Normalize(feedUri); // Apply if-0install-version filter RetrievalMethods.RemoveAll(FilterMismatch); var toRemove = new List <RetrievalMethod>(); foreach (var retrievalMethod in RetrievalMethods) { try { retrievalMethod.Normalize(feedUri); } #region Error handling catch (UriFormatException ex) { Log.Error(ex); toRemove.Add(retrievalMethod); } #endregion } RetrievalMethods.RemoveRange(toRemove); }
/// <summary> /// Returns a command-line for executing the "0install run" command. Generates and returns a stub EXE if possible, falls back to directly pointing to the "0install" binary otherwise. /// </summary> /// <param name="target">The application to be launched.</param> /// <param name="command">The command argument to be passed to the the "0install run" command; can be <c>null</c>.</param> /// <param name="machineWide"><c>true</c> place the generated stub in a machine-wide location; <c>false</c> to place it in the current user profile.</param> /// <returns></returns> /// <exception cref="OperationCanceledException">The user canceled the task.</exception> /// <exception cref="InvalidOperationException">There was a compilation error while generating the stub EXE.</exception> /// <exception cref="IOException">A problem occurred while writing to the filesystem.</exception> /// <exception cref="WebException">A problem occurred while downloading additional data (such as icons).</exception> /// <exception cref="UnauthorizedAccessException">Write access to the filesystem is not permitted.</exception> public IReadOnlyList <string> GetRunCommandLine(FeedTarget target, string?command = null, bool machineWide = false) { var entryPoint = target.Feed.GetEntryPoint(command); bool gui = entryPoint is not { NeedsTerminal : true }; try { #if NETFRAMEWORK string hash = (target.Uri + "#" + command).Hash(SHA256.Create()); string exeName = (entryPoint == null) ? FeedUri.Escape(target.Feed.Name) : entryPoint.BinaryName ?? entryPoint.Command; string path = Path.Combine( IntegrationManager.GetDir(machineWide, "stubs", hash), exeName + ".exe"); CreateOrUpdateRunStub(path, target, gui, command); return(new[] { path }); #else return(GetArguments(target.Uri, command, gui) .Prepend(GetBinary(gui)) .ToList()); #endif } #region Error handling catch (InvalidOperationException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } #endregion }
/// <inheritdoc/> public string GetPath(FeedUri feedUri) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException(nameof(feedUri)); } #endregion if (feedUri.IsFile) { throw new KeyNotFoundException("Feed cache does not handle local files: " + feedUri.ToStringRfc()); } string fileName = feedUri.Escape(); string path = Path.Combine(DirectoryPath, fileName); if (FileUtils.ExistsCaseSensitive(path)) { return(path); } else { throw new KeyNotFoundException(string.Format(Resources.FeedNotInCache, feedUri, path)); } }
private void Download(FeedUri url) { SetLastCheckAttempt(url); using (var feedFile = new TemporaryFile("0install-feed")) { try { _handler.RunTask(new DownloadFile(url, feedFile)); ImportFeed(feedFile, url); } catch (WebException ex) { Log.Warn(string.Format(Resources.FeedDownloadError, url)); if (url.IsLoopback) { throw; } Log.Info(Resources.TryingFeedMirror); try { DownloadMirror(url); } catch (WebException) { // Report the original problem instead of mirror errors throw ex; } } } }
/// <inheritdoc/> public async Task IntegrateAsync(FeedUri uri, IEnumerable <string>?add = null, IEnumerable <string>?remove = null, bool machineWide = false) { var args = new List <string> { "integrate", "--batch", uri.ToStringRfc() }; if (machineWide) { args.Add("--machine"); } void AddToArgs(string option, IEnumerable <string>?elements) { if (elements == null) { return; } foreach (string category in elements) { args.Add(option); args.Add(category); } } AddToArgs("--add", add); AddToArgs("--remove", remove); await Task.Run(() => _launcher.RunAndCapture(args.ToArray())); }
/// <inheritdoc/> public void Add(FeedUri feedUri, byte[] data) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException("feedUri"); } if (data == null) { throw new ArgumentNullException("data"); } #endregion // Add to underlying cache _backingCache.Add(feedUri, data); // Add to memory cache (replacing existing old versions) var feed = XmlStorage.LoadXml <Feed>(new MemoryStream(data)); feed.Normalize(feedUri); string key = feedUri.Escape(); lock (_feedDictionary) { _feedDictionary.Remove(key); _feedDictionary.Add(key, feed); } }
/// <summary> /// Creates a new selection candidate. /// </summary> /// <param name="feedUri">The file name or URL of the feed listing the implementation.</param> /// <param name="feedPreferences">The <see cref="FeedPreferences"/> for <see cref="FeedUri"/>.</param> /// <param name="implementation">The implementation this selection candidate references.</param> /// <param name="requirements">A set of requirements/restrictions the <paramref name="implementation"/> needs to fullfill for <see cref="IsSuitable"/> to be <c>true</c>.</param> /// <param name="offlineUncached">Mark this candidate as unsuitable because it is uncached and <see cref="Config.NetworkUse"/> is set to <see cref="NetworkLevel.Offline"/>.</param> /// <exception cref="InvalidDataException"><paramref name="implementation"/>'s <see cref="ImplementationBase.ID"/> is empty.</exception> public SelectionCandidate([NotNull] FeedUri feedUri, [NotNull] FeedPreferences feedPreferences, [NotNull] Implementation implementation, [NotNull] Requirements requirements, bool offlineUncached = false) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException(nameof(feedUri)); } if (feedPreferences == null) { throw new ArgumentNullException(nameof(feedPreferences)); } if (implementation == null) { throw new ArgumentNullException(nameof(implementation)); } if (requirements == null) { throw new ArgumentNullException(nameof(requirements)); } #endregion if (string.IsNullOrEmpty(implementation.ID)) { throw new InvalidDataException(string.Format(Resources.ImplementationMissingID, implementation, feedUri)); } FeedUri = feedUri; FeedPreferences = feedPreferences; Implementation = implementation; _implementationPreferences = feedPreferences[implementation.ID]; CheckSuitabilty(requirements, offlineUncached); }
//--------------------// #region Normalize /// <summary> /// Flattens the <see cref="Group"/> inheritance structure and sets missing default values in <see cref="Implementation"/>s. /// </summary> /// <param name="feedUri">The feed the data was originally loaded from; can be <see langword="null"/>.</param> /// <remarks>This method should be called to prepare a <see cref="Feed"/> for solver processing. Do not call it if you plan on serializing the feed again since it may loose some of its structure.</remarks> public override void Normalize(FeedUri feedUri = null) { #region Sanity checks if (feedUri == null) { throw new ArgumentNullException("feedUri"); } #endregion // Apply if-0install-version filter _elements.RemoveAll(FilterMismatch); var collapsedElements = new List <Element>(); foreach (var element in _elements) { element.InheritFrom(this); // Flatten structure in sub-groups, set missing default values in implementations element.Normalize(feedUri); var group = element as Group; if (group != null) { // Move implementations out of sub-groups collapsedElements.AddRange(group._elements); } else { collapsedElements.Add(element); } } _elements.Clear(); _elements.AddRange(collapsedElements); }
/// <inheritdoc/> public override void Normalize(FeedUri feedUri) { #region Sanity checks if (feedUri == null) throw new ArgumentNullException(nameof(feedUri)); #endregion base.Normalize(feedUri); if (Href != null) Href = ModelUtils.GetAbsoluteHref(Href, feedUri); }
public void AddPackageSource(string uri) { var feedUri = new FeedUri(uri); CatalogManager.DownloadCatalog(feedUri); if (CatalogManager.AddSource(feedUri)) CatalogManager.GetOnlineSafe(); else Log.Warn(string.Format(Resources.CatalogAlreadyRegistered, feedUri.ToStringRfc())); }
/// <inheritdoc/> public bool Contains(FeedUri feedUri) { #region Sanity checks if (feedUri == null) throw new ArgumentNullException(nameof(feedUri)); #endregion // Local files are passed through directly if (feedUri.IsFile) return File.Exists(feedUri.LocalPath); return FileUtils.ExistsCaseSensitive(Path.Combine(DirectoryPath, feedUri.Escape())); }
public override ExitCode Execute() { var uri = new FeedUri(AdditionalArgs[0]); if (CatalogManager.RemoveSource(uri)) return ExitCode.OK; else { Handler.OutputLow(Resources.CatalogSources, string.Format(Resources.CatalogNotRegistered, uri.ToStringRfc())); return ExitCode.NoChanges; } }
/// <summary> /// Returns a list of all interface URIs a given feed can be registered for. /// </summary> /// <param name="feedUri">The URI of the feed to register.</param> /// <param name="suggestedStabilityPolicy">Returns the suggested value for <see cref="InterfacePreferences.StabilityPolicy"/>.</param> /// <returns>A set of interface URIs.</returns> private IEnumerable<FeedUri> GetInterfaces(FeedUri feedUri, ref Stability suggestedStabilityPolicy) { var feed = FeedManager.GetFresh(feedUri); var interfaces = feed.FeedFor.Select(reference => reference.Target).WhereNotNull().ToList(); if (interfaces.Count == 0) throw new OptionException(string.Format(Resources.MissingFeedFor, feedUri), null); if (feed.Elements.Count == 1) { var singletonImplementation = feed.Elements[0] as Implementation; if (singletonImplementation != null) suggestedStabilityPolicy = singletonImplementation.Stability; } return interfaces; }
public override ExitCode Execute() { var uri = new FeedUri(AdditionalArgs[0]); if (!_skipVerify) CatalogManager.DownloadCatalog(uri); if (CatalogManager.AddSource(uri)) { if (!_skipVerify) CatalogManager.GetOnlineSafe(); return ExitCode.OK; } else { Handler.OutputLow(Resources.CatalogSources, string.Format(Resources.CatalogAlreadyRegistered, uri.ToStringRfc())); return ExitCode.NoChanges; } }
/// <summary> /// Creates a new interface dialog. /// </summary> /// <param name="interfaceUri">The interface to modify the preferences for.</param> /// <param name="solveCallback">Called after <see cref="InterfacePreferences"/> have been changed and the <see cref="ISolver"/> needs to be rerun.</param> /// <param name="feedManager">The feed manager used to retrieve feeds for additional information about implementations.</param> private InterfaceDialog([NotNull] FeedUri interfaceUri, [NotNull] Func<Selections> solveCallback, [NotNull] IFeedManager feedManager) { #region Sanity checks if (interfaceUri == null) throw new ArgumentNullException(nameof(interfaceUri)); if (solveCallback == null) throw new ArgumentNullException(nameof(solveCallback)); if (feedManager == null) throw new ArgumentNullException(nameof(feedManager)); #endregion InitializeComponent(); comboBoxStability.Items.AddRange(new object[] {Resources.UseDefaultSetting, Stability.Stable, Stability.Testing, Stability.Developer}); dataColumnUserStability.Items.AddRange(Stability.Unset, Stability.Preferred, Stability.Packaged, Stability.Stable, Stability.Testing, Stability.Developer); _interfaceUri = interfaceUri; _mainFeed = feedManager[_interfaceUri]; _solveCallback = solveCallback; _feedManager = feedManager; }
/// <summary> /// Loads the embedded configuration. /// </summary> private EmbeddedConfig() { var lines = typeof(EmbeddedConfig).GetEmbeddedString("EmbeddedConfig.txt").SplitMultilineText(); try { AppUri = new FeedUri(ConfigurationManager.AppSettings["app_uri"] ?? lines[0].TrimEnd()); Log.Info("Embedded config: AppUri: " + AppUri); AppName = ConfigurationManager.AppSettings["app_name"] ?? lines[1].TrimEnd(); Log.Info("Embedded config: AppName: " + AppName); AppMode = GetAppMode(ConfigurationManager.AppSettings["app_mode"] ?? lines[2].TrimEnd()); Log.Info("Embedded config: AppMode: " + AppMode); } catch (UriFormatException) { // No (valid) feed URI set } }
/// <inheritdoc/> public void Add(FeedUri feedUri, byte[] data) { #region Sanity checks if (feedUri == null) throw new ArgumentNullException("feedUri"); if (data == null) throw new ArgumentNullException("data"); #endregion // Add to underlying cache _backingCache.Add(feedUri, data); // Add to memory cache (replacing existing old versions) var feed = XmlStorage.LoadXml<Feed>(new MemoryStream(data)); feed.Normalize(feedUri); string key = feedUri.Escape(); lock (_feedDictionary) { _feedDictionary.Remove(key); _feedDictionary.Add(key, feed); } }
/// <inheritdoc/> protected override ExitCode ExecuteHelper(ICategoryIntegrationManager integrationManager, FeedUri interfaceUri) { #region Sanity checks if (interfaceUri == null) throw new ArgumentNullException("interfaceUri"); if (integrationManager == null) throw new ArgumentNullException("integrationManager"); #endregion try { integrationManager.RemoveApp(integrationManager.AppList[interfaceUri]); } #region Sanity checks catch (KeyNotFoundException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } #endregion return ExitCode.OK; }
/// <summary> /// Helper method for <see cref="GetHumanReadable"/> that recursivley writes information about <see cref="ImplementationSelection"/>s to a <see cref="StringBuilder"/>. /// </summary> /// <param name="selections">The selections to be displayed.</param> /// <param name="builder">The string builder to write the output to.</param> /// <param name="handled">A list of interface URIs that have already been handled; used to prevent infinite recursion.</param> /// <param name="store">A store to search for implementation storage locations.</param> /// <param name="indent">An indention prefix for the current recursion level (to create a visual hierachy).</param> /// <param name="interfaceUri">The <see cref="ImplementationSelection.InterfaceUri"/> to look for.</param> private static void PrintNode(Selections selections, StringBuilder builder, HashSet<FeedUri> handled, IStore store, string indent, FeedUri interfaceUri) { // Prevent infinite recursion if (handled.Contains(interfaceUri)) return; handled.Add(interfaceUri); builder.AppendLine(indent + "- URI: " + interfaceUri); try { var implementation = selections[interfaceUri]; builder.AppendLine(indent + " Version: " + implementation.Version); builder.AppendLine(indent + " Path: " + (implementation.LocalPath ?? implementation.GetPath(store) ?? Resources.NotCached)); builder.AppendLine(); indent += " "; // Recurse into regular dependencies foreach (var dependency in implementation.Dependencies) PrintNode(selections, builder, handled, store, indent, dependency.InterfaceUri); if (implementation.Commands.Count != 0) { var command = implementation.Commands[0]; // Recurse into command dependencies foreach (var dependency in command.Dependencies) PrintNode(selections, builder, handled, store, indent, dependency.InterfaceUri); // Recurse into runner dependency if (command.Runner != null) PrintNode(selections, builder, handled, store, indent, command.Runner.InterfaceUri); } } catch (KeyNotFoundException) { builder.AppendLine(indent + " " + Resources.NoSelectedVersion); } }
/// <summary> /// Sets missing default values and handles legacy elements. /// </summary> /// <param name="feedUri">The feed the data was originally loaded from.</param> /// <remarks>This method should be called to prepare a <see cref="Feed"/> for solver processing. Do not call it if you plan on serializing the feed again since it may loose some of its structure.</remarks> public override void Normalize(FeedUri feedUri) { #region Sanity checks if (feedUri == null) throw new ArgumentNullException(nameof(feedUri)); #endregion base.Normalize(feedUri); // Merge the version modifier into the normal version attribute if (!string.IsNullOrEmpty(VersionModifier)) { Version = new ImplementationVersion(Version + VersionModifier); VersionModifier = null; } // Default stability rating to testing if (Stability == Stability.Unset) Stability = Stability.Testing; // Make local paths absolute try { if (!string.IsNullOrEmpty(LocalPath)) LocalPath = ModelUtils.GetAbsolutePath(LocalPath, feedUri); else if (!string.IsNullOrEmpty(ID) && ID.StartsWith(".")) // Get local path from ID LocalPath = ID = ModelUtils.GetAbsolutePath(ID, feedUri); } #region Error handling catch (UriFormatException ex) { Log.Error(ex); LocalPath = null; } #endregion // Parse manifest digest from ID if missing if (!string.IsNullOrEmpty(ID)) _manifestDigest.ParseID(ID); }
/// <summary> /// Sets <see cref="Store.Model.Requirements.InterfaceUri"/> and applies <see cref="Requirements"/> options that need to be deferred to the end of the parsing process. /// </summary> protected void SetInterfaceUri(FeedUri uri) { Requirements.InterfaceUri = uri; if (_version != null) Requirements.ExtraRestrictions[Requirements.InterfaceUri] = _version; else if (_notBefore != null || _before != null) Requirements.ExtraRestrictions[Requirements.InterfaceUri] = new VersionRange(_notBefore, _before); }
public void TestNormalizeLocalPath() { var localUri = new FeedUri(WindowsUtils.IsWindows ? @"C:\local\feed.xml" : "/local/feed.xml"); var implementation1 = new Implementation {ID = "./subdir"}; implementation1.Normalize(localUri); Assert.AreEqual( expected: WindowsUtils.IsWindows ? @"C:\local\.\subdir" : "/local/./subdir", actual: implementation1.ID); Assert.AreEqual( expected: WindowsUtils.IsWindows ? @"C:\local\.\subdir" : "/local/./subdir", actual: implementation1.LocalPath); var implementation2 = new Implementation {ID = "./wrong", LocalPath = "subdir"}; implementation2.Normalize(localUri); Assert.AreEqual( expected: "./wrong", actual: implementation2.ID); Assert.AreEqual( expected: WindowsUtils.IsWindows ? @"C:\local\subdir" : "/local/subdir", actual: implementation2.LocalPath); }
private IEnumerable<SelectionCandidate> GenerateDummyCandidates(FeedUri feedUri) { if (feedUri.IsFromDistribution) return Enumerable.Empty<SelectionCandidate>(); try { var feed = _feedCache.GetFeed(feedUri); var feedPreferences = FeedPreferences.LoadForSafe(feedUri); return feed.Elements.OfType<Implementation>().Select(implementation => GenerateDummyCandidate(feedUri, feedPreferences, implementation)); } #region Error handling catch (KeyNotFoundException) { return Enumerable.Empty<SelectionCandidate>(); } #endregion }
private SelectionCandidate GenerateDummyCandidate(FeedUri feedUri, FeedPreferences feedPreferences, Implementation implementation) { return new SelectionCandidate(feedUri, feedPreferences, implementation, new Requirements(_interfaceUri, Command.NameRun, new Architecture(Architecture.CurrentSystem.OS, Cpu.All))); }
/// <summary> /// Adds a feed to the list of custom feeds. /// </summary> private void AddCustomFeed(string input) { FeedUri feedUri; try { feedUri = new FeedUri(input); } catch (UriFormatException ex) { Msg.Inform(this, ex.Message, MsgSeverity.Error); return; } if (_interfaceUri.IsFile) { if (!File.Exists(_interfaceUri.LocalPath)) { Msg.Inform(this, string.Format(Resources.FileOrDirNotFound, _interfaceUri.LocalPath), MsgSeverity.Warn); return; } } else { Feed feed = null; try { using (var handler = new GuiTaskHandler(this)) { handler.RunTask(new SimpleTask(Resources.CheckingFeed, delegate { using (var webClient = new WebClientTimeout()) feed = XmlStorage.FromXmlString<Feed>(webClient.DownloadString(feedUri)); })); } } #region Error handling catch (OperationCanceledException) { return; } catch (IOException ex) { Msg.Inform(this, ex.Message, MsgSeverity.Error); return; } catch (InvalidDataException ex) { Msg.Inform(this, ex.Message, MsgSeverity.Error); return; } catch (WebException ex) { Msg.Inform(this, ex.Message, MsgSeverity.Error); return; } catch (UnauthorizedAccessException ex) { Msg.Inform(this, ex.Message, MsgSeverity.Error); return; } #endregion // Ensure a matching <feed-for> is present for online feeds if (feed.FeedFor.All(entry => entry.Target != _interfaceUri)) if (!Msg.YesNo(this, Resources.IgnoreMissingFeedFor, MsgSeverity.Warn)) return; } var reference = new FeedReference {Source = feedUri}; if (_interfacePreferences.Feeds.Contains(reference)) return; _interfacePreferences.Feeds.Add(reference); listBoxFeeds.Items.Add(reference); }
/// <inheritdoc/> protected override ExitCode ExecuteHelper(ICategoryIntegrationManager integrationManager, FeedUri interfaceUri) { #region Sanity checks if (interfaceUri == null) throw new ArgumentNullException(nameof(interfaceUri)); if (integrationManager == null) throw new ArgumentNullException(nameof(integrationManager)); #endregion if (RemoveOnly()) { RemoveOnly(integrationManager, interfaceUri); return ExitCode.OK; } CheckInstallBase(); var appEntry = GetAppEntry(integrationManager, ref interfaceUri); var feed = FeedManager[interfaceUri]; if (NoSpecifiedIntegrations()) { var state = new IntegrationState(integrationManager, appEntry, feed); Retry: Handler.ShowIntegrateApp(state); try { state.ApplyChanges(); } #region Error handling catch (ConflictException ex) { if (Handler.Ask( Resources.IntegrateAppInvalid + Environment.NewLine + ex.Message + Environment.NewLine + Environment.NewLine + Resources.IntegrateAppRetry, defaultAnswer: false, alternateMessage: ex.Message)) goto Retry; } catch (InvalidDataException ex) { if (Handler.Ask( Resources.IntegrateAppInvalid + Environment.NewLine + ex.Message + Environment.NewLine + Environment.NewLine + Resources.IntegrateAppRetry, defaultAnswer: false, alternateMessage: ex.Message)) goto Retry; } #endregion return ExitCode.OK; } RemoveAndAdd(integrationManager, feed, appEntry); return ExitCode.OK; }
/// <inheritdoc/> public Catalog DownloadCatalog(FeedUri source) { #region Sanity checks if (source == null) throw new ArgumentNullException("source"); #endregion if (source.IsFile) return XmlStorage.LoadXml<Catalog>(source.LocalPath); var download = new DownloadMemory(source); _handler.RunTask(download); var data = download.GetData(); _trustManager.CheckTrust(data, source); return XmlStorage.LoadXml<Catalog>(new MemoryStream(data)); }
/// <inheritdoc/> public void RemoveTile(FeedUri interfaceUri) { #region Sanity checks if (interfaceUri == null) throw new ArgumentNullException(nameof(interfaceUri)); #endregion RemoveTile(_tileDictionary[interfaceUri]); }
/// <inheritdoc/> public IAppTile GetTile(FeedUri interfaceUri) { #region Sanity checks if (interfaceUri == null) throw new ArgumentNullException(nameof(interfaceUri)); #endregion return _tileDictionary.ContainsKey(interfaceUri) ? _tileDictionary[interfaceUri] : null; }
/// <inheritdoc/> public IAppTile QueueNewTile(FeedUri interfaceUri, string appName, AppStatus status, IIconCache iconCache = null, bool machineWide = false) { #region Sanity checks if (interfaceUri == null) throw new ArgumentNullException(nameof(interfaceUri)); if (appName == null) throw new ArgumentNullException(nameof(appName)); if (_tileDictionary.ContainsKey(interfaceUri)) throw new InvalidOperationException("Duplicate interface URI"); #endregion var tile = new AppTile(interfaceUri, appName, status, iconCache, machineWide) {Width = _flowLayout.Width}; if (appName.ContainsIgnoreCase(TextSearch.Text)) { _appTileQueueHeight += tile.Height; // Alternate between light and dark tiles tile.BackColor = _lastTileLight ? TileColorDark : TileColorLight; _lastTileLight = !_lastTileLight; } else tile.Visible = false; _appTileQueue.Add(tile); _tileDictionary.Add(interfaceUri, tile); return tile; }
/// <summary> /// Finds an existing <see cref="AppEntry"/> or creates a new one for a specific interface URI and feed. /// </summary> /// <param name="integrationManager">Manages desktop integration operations.</param> /// <param name="interfaceUri">The interface URI to create an <see cref="AppEntry"/> for. Will be updated if <see cref="Feed.ReplacedBy"/> is set and accepted by the user.</param> protected override AppEntry GetAppEntry(IIntegrationManager integrationManager, ref FeedUri interfaceUri) { #region Sanity checks if (integrationManager == null) throw new ArgumentNullException(nameof(integrationManager)); if (interfaceUri == null) throw new ArgumentNullException(nameof(interfaceUri)); #endregion var appEntry = base.GetAppEntry(integrationManager, ref interfaceUri); var feed = FeedManager.GetFresh(interfaceUri); // Detect feed changes that may make an AppEntry update necessary if (!appEntry.CapabilityLists.UnsequencedEquals(feed.CapabilityLists)) { string changedMessage = string.Format(Resources.CapabilitiesChanged, appEntry.Name); if (Handler.Ask( changedMessage + " " + Resources.AskUpdateCapabilities, defaultAnswer: false, alternateMessage: changedMessage)) integrationManager.UpdateApp(appEntry, feed); } return appEntry; }
/// <inheritdoc/> public bool AddSource(FeedUri uri) { var sources = GetSources().ToList(); if (!sources.AddIfNew(uri)) return false; SetSources(sources); return true; }
private LinkLabel CreateLinkLabel(FeedUri interfaceUri) { var linkLabel = new LinkLabel {Text = Resources.Change, Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft}; linkLabel.LinkClicked += delegate { InterfaceDialog.Show(this, interfaceUri, _solveCallback, _feedCache); }; return linkLabel; }
/// <inheritdoc/> public bool RemoveSource(FeedUri uri) { var sources = GetSources().ToList(); if (!sources.Remove(uri)) return false; SetSources(sources); // Remove relevant entries from cache var cached = GetCached(); if (cached != null) { cached.Feeds.RemoveAll(x => x.CatalogUri == uri); SaveCache(cached); } return true; }
/// <summary> /// Applies the <see cref="_removeCategories"/> specified by the user. /// </summary> private void RemoveOnly(ICategoryIntegrationManager integrationManager, FeedUri interfaceUri) { integrationManager.RemoveAccessPointCategories(integrationManager.AppList[interfaceUri], _removeCategories.ToArray()); }