Esempio n. 1
0
        public void TestRemovedLocal()
        {
            using (var apApplied = new TemporaryFlagFile("ap-applied"))
                using (var apUnapplied = new TemporaryFlagFile("ap-unapplied"))
                {
                    var appListServer = new AppList
                    {
                        Entries =
                        {
                            new AppEntry
                            {
                                InterfaceUri = FeedTest.Test1Uri,
                                AccessPoints = new AccessPointList{
                                    Entries ={ new MockAccessPoint                   {
                                  ApplyFlagPath = apApplied, UnapplyFlagPath = apUnapplied
                              } }
                                }
                            }
                        }
                    };

                    TestSync(SyncResetMode.None, new AppList(), appListServer.Clone(), appListServer);

                    apApplied.Set.Should().BeFalse(because: "Locally removed access point should not be reapplied");
                    apUnapplied.Set.Should().BeFalse(because: "Locally removed access point should not be unapplied again");
                }
        }
        /// <summary>
        /// Tests the sync logic with custom <see cref="AppList"/>s.
        /// </summary>
        /// <param name="resetMode">The <see cref="SyncResetMode"/> to pass to <see cref="SyncIntegrationManager.Sync"/>.</param>
        /// <param name="appListLocal">The current local <see cref="AppList"/>.</param>
        /// <param name="appListLast">The state of the <see cref="AppList"/> after the last successful sync.</param>
        /// <param name="appListServer">The current server-side <see cref="AppList"/>.</param>
        private static void TestSync(SyncResetMode resetMode, AppList appListLocal, AppList?appListLast, AppList appListServer)
        {
            string appListLocalPath = AppList.GetDefaultPath();

            appListLocal.SaveXml(appListLocalPath);
            appListLast?.SaveXml(appListLocalPath + SyncIntegrationManager.AppListLastSyncSuffix);

            using var appListServerPath = new TemporaryFile("0install-unit-tests");
            {
                using (var stream = File.Create(appListServerPath))
                    appListServer.SaveXmlZip(stream, CryptoKey);

                using (var appListServerFile = File.OpenRead(appListServerPath))
                {
                    using var syncServer = new MicroServer("app-list", appListServerFile);
                    var config = new Config
                    {
                        SyncServer         = new FeedUri(syncServer.ServerUri),
                        SyncServerUsername = "******",
                        SyncServerPassword = "******",
                        SyncCryptoKey      = CryptoKey
                    };
                    using (var integrationManager = new SyncIntegrationManager(config, interfaceId => new Feed(), new SilentTaskHandler()))
                        integrationManager.Sync(resetMode);

                    appListServer = AppList.LoadXmlZip(syncServer.FileContent, CryptoKey);
                }
            }

            appListLocal = XmlStorage.LoadXml <AppList>(appListLocalPath);
            appListLast  = XmlStorage.LoadXml <AppList>(appListLocalPath + SyncIntegrationManager.AppListLastSyncSuffix);
            appListServer.Should().Be(appListLocal, because: "Server and local data should be equal after sync");
            appListLast.Should().Be(appListLocal, because: "Last sync snapshot and local data should be equal after sync");
        }
        public void TestAddedLocal()
        {
            using var apApplied    = new TemporaryFlagFile("ap-applied");
            using var apNotApplied = new TemporaryFlagFile("ap-not-applied");
            var appListLocal = new AppList
            {
                Entries =
                {
                    new AppEntry
                    {
                        InterfaceUri = FeedTest.Test1Uri,
                        AccessPoints = new AccessPointList{
                            Entries ={ new MockAccessPoint                   {
                          ApplyFlagPath = apApplied, UnapplyFlagPath = apNotApplied
                      } }
                        }
                    }
                }
            };

            TestSync(SyncResetMode.None, appListLocal, new AppList(), new AppList());

            apApplied.Set.Should().BeFalse(because: "Locally existing access point should not be reapplied");
            apNotApplied.Set.Should().BeFalse(because: "Locally existing access point should not be removed");
        }
Esempio n. 4
0
        public void TestAddedRemote()
        {
            using (var apApplied = new TemporaryFlagFile("ap-applied"))
                using (var apUnapplied = new TemporaryFlagFile("ap-unapplied"))
                {
                    var appListRemote = new AppList
                    {
                        Entries =
                        {
                            new AppEntry
                            {
                                InterfaceUri = FeedTest.Test1Uri,
                                AccessPoints = new AccessPointList{
                                    Entries ={ new MockAccessPoint                   {
                                  ApplyFlagPath = apApplied, UnapplyFlagPath = apUnapplied
                              }, new MockAccessPoint() }
                                }
                            }
                        }
                    };

                    TestSync(SyncResetMode.None, new AppList(), new AppList(), appListRemote);

                    apApplied.Set.Should().BeTrue(because: "New access point should be applied");
                    apUnapplied.Set.Should().BeFalse(because: "New access point should not be unapplied");
                }
        }
Esempio n. 5
0
        /// <summary>
        /// Checks new <see cref="AccessPoint"/> candidates for conflicts with existing ones.
        /// </summary>
        /// <param name="appList">The <see cref="AppList"/> containing the existing <see cref="AccessPoint"/>s.</param>
        /// <param name="accessPoints">The set of <see cref="AccessPoint"/>s candidates to check.</param>
        /// <param name="appEntry">The <see cref="AppEntry"/> the <paramref name="accessPoints"/> are intended for.</param>
        /// <exception cref="KeyNotFoundException">An <see cref="AccessPoint"/> reference to a <see cref="Capability"/> is invalid.</exception>
        /// <exception cref="ConflictException">One or more of the <paramref name="accessPoints"/> would cause a conflict with the existing <see cref="AccessPoint"/>s in <see cref="AppList"/>.</exception>
        public static void CheckForConflicts(this AppList appList, IEnumerable <AccessPoint> accessPoints, AppEntry appEntry)
        {
            #region Sanity checks
            if (appList == null)
            {
                throw new ArgumentNullException(nameof(appList));
            }
            if (accessPoints == null)
            {
                throw new ArgumentNullException(nameof(accessPoints));
            }
            if (appEntry == null)
            {
                throw new ArgumentNullException(nameof(appEntry));
            }
            #endregion

            var newConflictData      = accessPoints.GetConflictData(appEntry);
            var existingConflictData = appList.Entries.GetConflictData();

            foreach ((string conflictId, var newEntry) in newConflictData)
            {
                if (existingConflictData.TryGetValue(conflictId, out var existingEntry))
                {
                    // Ignore conflicts that are actually just re-applications of existing access points
                    if (existingEntry != newEntry)
                    {
                        throw ConflictException.NewConflict(existingEntry, newEntry);
                    }
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Merges a new <see cref="IntegrationManagerBase.AppList"/> with the existing data. Performs a three-way merge using <see cref="_appListLastSync"/> as the base.
        /// </summary>
        /// <param name="remoteAppList">The remote <see cref="AppList"/> to merge in.</param>
        /// <param name="resetClient">Set to <c>true</c> to completely replace the contents of <see cref="IIntegrationManager.AppList"/> with <paramref name="remoteAppList"/> instead of merging the two.</param>
        private void MergeData(AppList remoteAppList, bool resetClient)
        {
            var toAdd    = new List <AppEntry>();
            var toRemove = new List <AppEntry>();

            if (resetClient)
            {
                Merge.TwoWay(
                    theirs: remoteAppList.Entries,
                    mine: AppList.Entries,
                    added: toAdd, removed: toRemove);
            }
            else
            {
                Merge.ThreeWay(
                    reference: _appListLastSync.Entries,
                    theirs: remoteAppList.Entries,
                    mine: AppList.Entries,
                    added: toAdd, removed: toRemove);
            }

            void AddApp(AppEntry prototype) => AddAppInternal(prototype, _feedRetriever);

            Handler.RunTask(new SimpleTask(Resources.ApplyingChanges, () =>
            {
                toRemove.ApplyWithRollback(RemoveAppInternal, AddApp);
                toAdd.ApplyWithRollback(AddApp, RemoveAppInternal);
            }));
        }
        public void TestAddedLocal()
        {
            using (var apApplied = new TemporaryFlagFile("ap-applied"))
                using (var apUnapplied = new TemporaryFlagFile("ap-unapplied"))
                {
                    var appListLocal = new AppList
                    {
                        Entries =
                        {
                            new AppEntry
                            {
                                InterfaceUri = FeedTest.Test1Uri,
                                AccessPoints = new AccessPointList{
                                    Entries ={ new MockAccessPoint                   {
                                  ApplyFlagPath = apApplied, UnapplyFlagPath = apUnapplied
                              } }
                                }
                            }
                        }
                    };

                    TestSync(SyncResetMode.None, appListLocal, new AppList(), new AppList());

                    Assert.IsFalse(apApplied.Set, "Locally existing access point should not be reapplied");
                    Assert.IsFalse(apUnapplied.Set, "Locally existing access point should not be removed");
                }
        }
Esempio n. 8
0
        /// <summary>
        /// Checks new <see cref="AccessPoint"/> candidates for conflicts with existing ones.
        /// </summary>
        /// <param name="appList">The <see cref="AppList"/> containing the existing <see cref="AccessPoint"/>s.</param>
        /// <param name="accessPoints">The set of <see cref="AccessPoint"/>s candidates to check.</param>
        /// <param name="appEntry">The <see cref="AppEntry"/> the <paramref name="accessPoints"/> are intended for.</param>
        /// <exception cref="KeyNotFoundException">An <see cref="AccessPoint"/> reference to a <see cref="Store.Model.Capabilities.Capability"/> is invalid.</exception>
        /// <exception cref="ConflictException">One or more of the <paramref name="accessPoints"/> would cause a conflict with the existing <see cref="AccessPoint"/>s in <see cref="AppList"/>.</exception>
        public static void CheckForConflicts([NotNull] this AppList appList, [NotNull, ItemNotNull, InstantHandle] IEnumerable <AccessPoint> accessPoints, [NotNull] AppEntry appEntry)
        {
            #region Sanity checks
            if (appList == null)
            {
                throw new ArgumentNullException(nameof(appList));
            }
            if (accessPoints == null)
            {
                throw new ArgumentNullException(nameof(accessPoints));
            }
            if (appEntry == null)
            {
                throw new ArgumentNullException(nameof(appEntry));
            }
            #endregion

            var newConflictData      = accessPoints.GetConflictData(appEntry);
            var existingConflictData = appList.Entries.GetConflictData();

            foreach (var pair in newConflictData)
            {
                ConflictData existingEntry, newEntry = pair.Value;
                if (existingConflictData.TryGetValue(pair.Key, out existingEntry))
                {
                    // Ignore conflicts that are actually just re-applications of existing access points
                    if (existingEntry != newEntry)
                    {
                        throw ConflictException.NewConflict(existingEntry, newEntry);
                    }
                }
            }
        }
Esempio n. 9
0
        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>
        /// Tests the sync logic with custom <see cref="AppList"/>s.
        /// </summary>
        /// <param name="resetMode">The <see cref="SyncResetMode"/> to pass to <see cref="SyncIntegrationManager.Sync"/>.</param>
        /// <param name="appListLocal">The current local <see cref="AppList"/>.</param>
        /// <param name="appListLast">The state of the <see cref="AppList"/> after the last successful sync.</param>
        /// <param name="appListServer">The current server-side <see cref="AppList"/>.</param>
        private void TestSync(SyncResetMode resetMode, AppList appListLocal, AppList appListLast, AppList appListServer)
        {
            appListLocal.SaveXml(_appListPath);
            if (appListLast != null)
            {
                appListLast.SaveXml(_appListPath + SyncIntegrationManager.AppListLastSyncSuffix);
            }

            using (var stream = File.Create(_appListPath + ".zip"))
                appListServer.SaveXmlZip(stream);

            using (var appListServerFile = File.OpenRead(_appListPath + ".zip"))
                using (var syncServer = new MicroServer("app-list", appListServerFile))
                {
                    using (var integrationManager = new SyncIntegrationManager(_appListPath, new SyncServer {
                        Uri = syncServer.ServerUri
                    }, interfaceId => new Feed(), new SilentTaskHandler()))
                        integrationManager.Sync(resetMode);

                    appListServer = AppList.LoadXmlZip(syncServer.FileContent);
                }

            appListLocal = XmlStorage.LoadXml <AppList>(_appListPath);
            appListLast  = XmlStorage.LoadXml <AppList>(_appListPath + SyncIntegrationManager.AppListLastSyncSuffix);
            Assert.AreEqual(appListLocal, appListServer, "Server and local data should be equal after sync");
            Assert.AreEqual(appListLocal, appListLast, "Last sync snapshot and local data should be equal after sync");
        }
        public void NoConflicts()
        {
            var accessPointA = new MockAccessPoint {
                ID = "a"
            };
            var appEntry1 = new AppEntry
            {
                Name         = "App1", InterfaceUri = FeedTest.Test1Uri,
                AccessPoints = new AccessPointList {
                    Entries = { accessPointA }
                }
            };
            var accessPointB = new MockAccessPoint {
                ID = "b"
            };
            var appEntry2 = new AppEntry {
                Name = "App2", InterfaceUri = FeedTest.Test2Uri
            };

            var appList = new AppList {
                Entries = { appEntry1 }
            };

            appList.CheckForConflicts(new[] { accessPointB }, appEntry2);
        }
        /// <summary>
        /// Creates a new sync manager for a custom <see cref="AppList"/> file. Used for testing.
        /// </summary>
        /// <param name="appListPath">The storage location of the <see cref="AppList"/> file.</param>
        /// <param name="server">Access information for the sync server.</param>
        /// <param name="feedRetriever">Callback method used to retrieve additional <see cref="Feed"/>s on demand.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <param name="machineWide">Apply operations machine-wide instead of just for the current user.</param>
        /// <exception cref="IOException">A problem occurs while accessing the <see cref="AppList"/> file.</exception>
        /// <exception cref="UnauthorizedAccessException">Read or write access to the <see cref="AppList"/> file is not permitted or if another desktop integration class is currently active.</exception>
        /// <exception cref="InvalidDataException">A problem occurs while deserializing the XML data.</exception>
        public SyncIntegrationManager([NotNull] string appListPath, SyncServer server, [NotNull] Converter <FeedUri, Feed> feedRetriever, [NotNull] ITaskHandler handler, bool machineWide = false)
            : base(appListPath, handler, machineWide)
        {
            #region Sanity checks
            if (server.Uri == null)
            {
                throw new ArgumentNullException("server");
            }
            if (feedRetriever == null)
            {
                throw new ArgumentNullException("feedRetriever");
            }
            #endregion

            _server        = server;
            _feedRetriever = feedRetriever;

            if (File.Exists(AppListPath + AppListLastSyncSuffix))
            {
                _appListLastSync = XmlStorage.LoadXml <AppList>(AppListPath + AppListLastSyncSuffix);
            }
            else
            {
                _appListLastSync = new AppList();
                _appListLastSync.SaveXml(AppListPath + AppListLastSyncSuffix);
            }
        }
        /// <summary>
        /// Merges a new <see cref="IntegrationManagerBase.AppList"/> with the existing data.
        /// </summary>
        /// <param name="remoteAppList">The remote <see cref="AppList"/> to merge in.</param>
        /// <param name="resetClient">Set to <see langword="true"/> to completly replace the contents of <see cref="IIntegrationManager.AppList"/> with <paramref name="remoteAppList"/> instead of merging the two.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="KeyNotFoundException">An <see cref="AccessPoint"/> reference to a <see cref="Store.Model.Capabilities.Capability"/> is invalid.</exception>
        /// <exception cref="InvalidDataException">One of the <see cref="AccessPoint"/>s or <see cref="Store.Model.Capabilities.Capability"/>s is invalid.</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception>
        /// <exception cref="ConflictException">One or more new <see cref="AccessPoint"/> would cause a conflict with the existing <see cref="AccessPoint"/>s in <see cref="AppList"/>.</exception>
        /// <exception cref="WebException">A problem occured while downloading additional data (such as icons).</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        /// <remarks>Performs a three-way merge using <see cref="_appListLastSync"/> as base.</remarks>
        private void MergeData([NotNull] AppList remoteAppList, bool resetClient)
        {
            #region Sanity checks
            if (remoteAppList == null)
            {
                throw new ArgumentNullException("remoteAppList");
            }
            #endregion

            var toAdd    = new List <AppEntry>();
            var toRemove = new List <AppEntry>();

            if (resetClient)
            {
                Merge.TwoWay(
                    theirs: remoteAppList.Entries,
                    mine: AppList.Entries,
                    added: toAdd, removed: toRemove);
            }
            else
            {
                Merge.ThreeWay(
                    reference: _appListLastSync.Entries,
                    theirs: remoteAppList.Entries,
                    mine: AppList.Entries,
                    added: toAdd, removed: toRemove);
            }

            Handler.RunTask(new SimpleTask(Resources.ApplyingChanges, () =>
            {
                toRemove.ApplyWithRollback(RemoveAppInternal, AddAppHelper);
                toAdd.ApplyWithRollback(AddAppHelper, RemoveAppInternal);
            }));
        }
        /// <summary>
        /// Creates a new integration manager using a custom <see cref="DesktopIntegration.AppList"/>. Do not use directly except for testing purposes!
        /// </summary>
        /// <param name="appListPath">The storage location of the <see cref="AppList"/> file.</param>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <param name="machineWide">Apply operations machine-wide instead of just for the current user.</param>
        /// <exception cref="IOException">A problem occurs while accessing the <see cref="AppList"/> file.</exception>
        /// <exception cref="UnauthorizedAccessException">Read or write access to the <see cref="AppList"/> file is not permitted.</exception>
        /// <exception cref="InvalidDataException">A problem occurs while deserializing the XML data.</exception>
        public IntegrationManager([NotNull] string appListPath, [NotNull] ITaskHandler handler, bool machineWide = false) : base(handler)
        {
            #region Sanity checks
            if (appListPath == null)
            {
                throw new ArgumentNullException("appListPath");
            }
            if (handler == null)
            {
                throw new ArgumentNullException("handler");
            }
            #endregion

            MachineWide = machineWide;
            AppListPath = appListPath;

            if (File.Exists(AppListPath))
            {
                Log.Debug("Loading AppList from: " + AppListPath);
                AppList = XmlStorage.LoadXml <AppList>(AppListPath);
            }
            else
            {
                Log.Debug("Creating new AppList: " + AppListPath);
                AppList = new AppList();
                AppList.SaveXml(AppListPath);
            }
        }
        /// <summary>
        /// Tests the sync logic with pre-defined <see cref="AppList"/>s.
        /// local add: appEntry1, local remove: appEntry2, remote add: appEntry3, remote remove: appEntry4
        /// </summary>
        /// <param name="resetMode">The <see cref="SyncResetMode"/> to pass to <see cref="SyncIntegrationManager.Sync"/>.</param>
        /// <param name="ap1Applied">The flag file used to indicate that <see cref="MockAccessPoint.Apply"/> was called for appEntry1.</param>
        /// <param name="ap1NotApplied">The flag file used to indicate that <see cref="MockAccessPoint.Unapply"/> was called for appEntry1.</param>
        /// <param name="ap2Applied">The flag file used to indicate that <see cref="MockAccessPoint.Apply"/> was called for appEntry2.</param>
        /// <param name="ap2NotApplied">The flag file used to indicate that <see cref="MockAccessPoint.Unapply"/> was called for appEntry2.</param>
        /// <param name="ap3Applied">The flag file used to indicate that <see cref="MockAccessPoint.Apply"/> was called for appEntry3.</param>
        /// <param name="ap3NotApplied">The flag file used to indicate that <see cref="MockAccessPoint.Unapply"/> was called for appEntry3.</param>
        /// <param name="ap4Applied">The flag file used to indicate that <see cref="MockAccessPoint.Apply"/> was called for appEntry4.</param>
        /// <param name="ap4NotApplied">The flag file used to indicate that <see cref="MockAccessPoint.Unapply"/> was called for appEntry4.</param>
        private static void TestSync(SyncResetMode resetMode,
                                     TemporaryFlagFile ap1Applied,
                                     TemporaryFlagFile ap1NotApplied,
                                     TemporaryFlagFile ap2Applied,
                                     TemporaryFlagFile ap2NotApplied,
                                     TemporaryFlagFile ap3Applied,
                                     TemporaryFlagFile ap3NotApplied,
                                     TemporaryFlagFile ap4Applied,
                                     TemporaryFlagFile ap4NotApplied)
        {
            var appEntry1 = new AppEntry
            {
                InterfaceUri = FeedTest.Test1Uri,
                AccessPoints = new AccessPointList {
                    Entries = { new MockAccessPoint {
                                    ApplyFlagPath = ap1Applied, UnapplyFlagPath = ap1NotApplied
                                } }
                }
            };
            var appEntry2 = new AppEntry
            {
                InterfaceUri = FeedTest.Test2Uri,
                AccessPoints = new AccessPointList {
                    Entries = { new MockAccessPoint {
                                    ApplyFlagPath = ap2Applied, UnapplyFlagPath = ap2NotApplied
                                } }
                }
            };
            var appEntry3 = new AppEntry
            {
                InterfaceUri = FeedTest.Test3Uri,
                AccessPoints = new AccessPointList {
                    Entries = { new MockAccessPoint {
                                    ApplyFlagPath = ap3Applied, UnapplyFlagPath = ap3NotApplied
                                } }
                }
            };
            var appEntry4 = new AppEntry
            {
                InterfaceUri = new FeedUri("http://example.com/test4.xml"),
                AccessPoints = new AccessPointList {
                    Entries = { new MockAccessPoint {
                                    ApplyFlagPath = ap4Applied, UnapplyFlagPath = ap4NotApplied
                                } }
                }
            };
            var appListLocal = new AppList {
                Entries = { appEntry1, appEntry4 }
            };
            var appListLast = new AppList {
                Entries = { appEntry2, appEntry4 }
            };
            var appListServer = new AppList {
                Entries = { appEntry2, appEntry3 }
            };

            TestSync(resetMode, appListLocal, appListLast, appListServer);
        }
        /// <inheritdoc/>
        protected override void Finish()
        {
            Log.Debug("Saving AppList to: " + AppListPath);
            // Retry to handle race conditions with read-only access to the file
            ExceptionUtils.Retry <IOException>(delegate { AppList.SaveXml(AppListPath); });

            WindowsUtils.NotifyAssocChanged();                     // Notify Windows Explorer of changes
            WindowsUtils.BroadcastMessage(ChangedWindowMessageID); // Notify Zero Install GUIs of changes
        }
Esempio n. 17
0
        private static void TestClone(AppList appList)
        {
            var appList2 = appList.Clone();

            // Ensure data stayed the same
            appList2.Should().Be(appList, because: "Cloned objects should be equal.");
            appList2.GetHashCode().Should().Be(appList.GetHashCode(), because: "Cloned objects' hashes should be equal.");
            appList2.Should().NotBeSameAs(appList, because: "Cloning should not return the same reference.");
        }
Esempio n. 18
0
        private static void TestClone(AppList appList)
        {
            var appList2 = appList.Clone();

            // Ensure data stayed the same
            Assert.AreEqual(appList, appList2, "Cloned objects should be equal.");
            Assert.AreEqual(appList.GetHashCode(), appList2.GetHashCode(), "Cloned objects' hashes should be equal.");
            Assert.IsFalse(ReferenceEquals(appList, appList2), "Cloning should not return the same reference.");
        }
Esempio n. 19
0
        //--------------------//

        #region Sync
        /// <summary>
        /// Synchronize the <see cref="AppList"/> with the sync server and (un)apply <see cref="AccessPoint"/>s accordingly.
        /// </summary>
        /// <param name="resetMode">Controls how synchronization data is reset.</param>
        /// <exception cref="OperationCanceledException">The user canceled the task.</exception>
        /// <exception cref="InvalidDataException">A problem occurred while deserializing the XML data or the specified crypto key was wrong.</exception>
        /// <exception cref="WebException">A problem occured while communicating with the sync server or while downloading additional data (such as icons).</exception>
        /// <exception cref="IOException">A problem occurs while writing to the filesystem or registry.</exception>
        /// <exception cref="UnauthorizedAccessException">Write access to the filesystem or registry is not permitted.</exception>
        public void Sync(SyncResetMode resetMode = SyncResetMode.None)
        {
            if (!_server.IsValid)
            {
                throw new InvalidDataException(Resources.PleaseConfigSync);
            }

            var appListUri = new Uri(_server.Uri, new Uri(MachineWide ? "app-list-machine" : "app-list", UriKind.Relative));

            using (var webClient = new WebClientTimeout
            {
                Credentials = _server.Credentials,
                CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore)
            })
            {
                ExceptionUtils.Retry <WebRaceConditionException>(delegate
                {
                    Handler.CancellationToken.ThrowIfCancellationRequested();

                    byte[] appListData;
                    try
                    {
                        appListData = DownloadAppList(appListUri, webClient, resetMode);
                    }
                    #region Error handling
                    catch (WebException ex)
                        when(ex.Status == WebExceptionStatus.ProtocolError && (ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.Unauthorized)
                        {
                            Handler.CancellationToken.ThrowIfCancellationRequested();
                            throw new WebException(Resources.SyncCredentialsInvalid, ex, ex.Status, ex.Response);
                        }
                    #endregion

                    HandleDownloadedAppList(resetMode, appListData);

                    try
                    {
                        UploadAppList(appListUri, webClient, resetMode);
                    }
                    #region Error handling
                    catch (WebException ex)
                        when(ex.Status == WebExceptionStatus.ProtocolError && (ex.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.PreconditionFailed)
                        {
                            Handler.CancellationToken.ThrowIfCancellationRequested();
                            throw new WebRaceConditionException(ex);
                        }
                    #endregion
                }, maxRetries: 3);
            }

            // Save reference point for future syncs
            AppList.SaveXml(AppListPath + AppListLastSyncSuffix);

            Handler.CancellationToken.ThrowIfCancellationRequested();
        }
        public void TestModifiedLocal()
        {
            using var apLocalApplied     = new TemporaryFlagFile("ap-local-applied");
            using var apLocalNotApplied  = new TemporaryFlagFile("ap-local-not-applied");
            using var apRemoteApplied    = new TemporaryFlagFile("ap-remote-applied");
            using var apRemoteNotApplied = new TemporaryFlagFile("ap-remote-not-applied");
            var appListLocal = new AppList
            {
                Entries =
                {
                    new AppEntry
                    {
                        InterfaceUri = FeedTest.Test1Uri,
                        AutoUpdate   = true,
                        Timestamp    = new DateTime(2001, 1, 1),
                        AccessPoints = new AccessPointList{
                            Entries ={ new MockAccessPoint                   {
                          ApplyFlagPath = apLocalApplied, UnapplyFlagPath = apLocalNotApplied
                      } }
                        }
                    }
                }
            };

            var appListServer = new AppList
            {
                Entries =
                {
                    new AppEntry
                    {
                        InterfaceUri = FeedTest.Test1Uri,
                        AutoUpdate   = false,
                        Timestamp    = new DateTime(2000, 1, 1),
                        AccessPoints = new AccessPointList{
                            Entries ={ new MockAccessPoint                   {
                          ApplyFlagPath = apRemoteApplied, UnapplyFlagPath = apRemoteNotApplied
                      } }
                        }
                    }
                }
            };

            TestSync(SyncResetMode.None, appListLocal, null, appListServer);

            apLocalApplied.Set.Should().BeFalse(because: "Up-to-date access point should not be reapplied");
            apLocalNotApplied.Set.Should().BeFalse(because: "Up-to-date access point should not be removed");
            apRemoteApplied.Set.Should().BeFalse(because: "Outdated access point should not be reapplied");
            apRemoteNotApplied.Set.Should().BeFalse(because: "Outdated access point should not be removed");
        }
        /// <inheritdoc/>
        protected override void AddAccessPointsInternal(AppEntry appEntry, Feed feed, IEnumerable <AccessPoint> accessPoints)
        {
            #region Sanity checks
            if (appEntry == null)
            {
                throw new ArgumentNullException("appEntry");
            }
            if (feed == null)
            {
                throw new ArgumentNullException("feed");
            }
            if (accessPoints == null)
            {
                throw new ArgumentNullException("accessPoints");
            }
            if (appEntry.AccessPoints != null && appEntry.AccessPoints.Entries == accessPoints)
            {
                throw new ArgumentException("Must not be equal to appEntry.AccessPoints.Entries", "accessPoints");
            }
            #endregion

            // Skip entries with mismatching hostname
            if (appEntry.Hostname != null && !Regex.IsMatch(Environment.MachineName, appEntry.Hostname))
            {
                return;
            }

            if (appEntry.AccessPoints == null)
            {
                appEntry.AccessPoints = new AccessPointList();
            }

            AppList.CheckForConflicts(accessPoints, appEntry);

            accessPoints.ApplyWithRollback(
                accessPoint => accessPoint.Apply(appEntry, feed, Handler, MachineWide),
                accessPoint =>
            {
                // Don't perform rollback if the access point was already applied previously and this was only a refresh
                if (!appEntry.AccessPoints.Entries.Contains(accessPoint))
                {
                    accessPoint.Unapply(appEntry, MachineWide);
                }
            });

            appEntry.AccessPoints.Entries.RemoveRange(accessPoints); // Replace pre-existing entries
            appEntry.AccessPoints.Entries.AddRange(accessPoints);
            appEntry.Timestamp = DateTime.UtcNow;
        }
Esempio n. 22
0
        private static void TestSaveLoad(AppList appList)
        {
            AppList appList2;

            using (var tempFile = new TemporaryFile("0install-unit-tests"))
            {
                // Write and read file
                appList.SaveXml(tempFile);
                appList2 = XmlStorage.LoadXml <AppList>(tempFile);
            }

            // Ensure data stayed the same
            appList2.Should().Be(appList, because: "Serialized objects should be equal.");
            appList2.GetHashCode().Should().Be(appList.GetHashCode(), because: "Serialized objects' hashes should be equal.");
            appList2.Should().NotBeSameAs(appList, because: "Serialized objects should not return the same reference.");
        }
Esempio n. 23
0
        public void TestModifiedRemote()
        {
            using (var apLocalApplied = new TemporaryFlagFile("ap-local-applied"))
                using (var apLocalUnapplied = new TemporaryFlagFile("ap-local-unapplied"))
                    using (var apRemoteApplied = new TemporaryFlagFile("ap-remote-applied"))
                        using (var apRemoteUnapplied = new TemporaryFlagFile("ap-remote-unapplied"))
                        {
                            var appListLocal = new AppList
                            {
                                Entries =
                                {
                                    new AppEntry
                                    {
                                        InterfaceUri = FeedTest.Test1Uri, AutoUpdate = true, Timestamp = new DateTime(1999, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                                        AccessPoints = new AccessPointList{
                                            Entries ={ new MockAccessPoint                   {
                                          ApplyFlagPath = apLocalApplied, UnapplyFlagPath = apLocalUnapplied
                                      } }
                                        }
                                    }
                                }
                            };

                            var appListServer = new AppList
                            {
                                Entries =
                                {
                                    new AppEntry
                                    {
                                        InterfaceUri = FeedTest.Test1Uri, AutoUpdate = false, Timestamp = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                                        AccessPoints = new AccessPointList{
                                            Entries ={ new MockAccessPoint                   {
                                          ApplyFlagPath = apRemoteApplied, UnapplyFlagPath = apRemoteUnapplied
                                      } }
                                        }
                                    }
                                }
                            };

                            TestSync(SyncResetMode.None, appListLocal, null, appListServer);

                            apLocalApplied.Set.Should().BeFalse(because: "Outdated access point should not be reapplied");
                            apLocalUnapplied.Set.Should().BeTrue(because: "Outdated access point should be removed");
                            apRemoteApplied.Set.Should().BeTrue(because: "New access point should be applied");
                            apRemoteUnapplied.Set.Should().BeFalse(because: "New access point should not be unapplied");
                        }
        }
Esempio n. 24
0
        /// <summary>
        /// Creates a new integration manager using the default <see cref="DesktopIntegration.AppList"/> (creating a new one if missing). Performs Mutex-based locking!
        /// </summary>
        /// <param name="handler">A callback object used when the the user is to be informed about the progress of long-running operations such as downloads.</param>
        /// <param name="machineWide">Apply operations machine-wide instead of just for the current user.</param>
        /// <exception cref="IOException">A problem occurs while accessing the <see cref="AppList"/> file.</exception>
        /// <exception cref="UnauthorizedAccessException">Read or write access to the <see cref="AppList"/> file is not permitted or another desktop integration class is currently active.</exception>
        /// <exception cref="InvalidDataException">A problem occurs while deserializing the XML data.</exception>
        public IntegrationManager([NotNull] ITaskHandler handler, bool machineWide = false)
            : base(handler, machineWide)
        {
            #region Sanity checks
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler));
            }
            #endregion

            try
            {
                AquireMutex();
            }
            #region Error handling
            catch (UnauthorizedAccessException)
            {
                // Replace exception to add more context
                throw new UnauthorizedAccessException(Resources.IntegrationMutex);
            }
            #endregion

            try
            {
                AppListPath = AppList.GetDefaultPath(machineWide);
                if (File.Exists(AppListPath))
                {
                    Log.Debug("Loading AppList for IntegrationManager from: " + AppListPath);
                    AppList = XmlStorage.LoadXml <AppList>(AppListPath);
                }
                else
                {
                    Log.Debug("Creating new AppList for IntegrationManager: " + AppListPath);
                    AppList = new AppList();
                    AppList.SaveXml(AppListPath);
                }
            }
            #region Error handling
            catch
            {
                // Avoid abandoned mutexes
                Dispose();
                throw;
            }
            #endregion
        }
        /// <inheritdoc/>
        protected override void Finish()
        {
            try
            {
                Log.Debug("Saving AppList to: " + AppListPath);
                AppList.SaveXml(AppListPath);
            }
            catch (IOException)
            {
                Log.Info("Race condition encountered while saving AppList. Waiting for a moment and then retrying.");
                Thread.Sleep(_random.Next(250, 1500));
                AppList.SaveXml(AppListPath);
            }

            WindowsUtils.NotifyAssocChanged();                     // Notify Windows Explorer of changes
            WindowsUtils.BroadcastMessage(ChangedWindowMessageID); // Notify Zero Install GUIs of changes
        }
Esempio n. 26
0
        public void TestFindAppAlias()
        {
            var appAlias = new AppAlias {
                Name = "foobar"
            };
            var appEntry = new AppEntry {
                AccessPoints = new AccessPointList {
                    Entries = { appAlias }
                }
            };
            var appList = new AppList {
                Entries = { appEntry }
            };

            appList.FindAppAlias("foobar").Should().Be((appAlias, appEntry));

            appList.FindAppAlias("other").Should().BeNull();
        }
Esempio n. 27
0
        private static void TestSaveLoad(AppList appList)
        {
            Assert.That(appList, Is.XmlSerializable);

            AppList appList2;

            using (var tempFile = new TemporaryFile("0install-unit-tests"))
            {
                // Write and read file
                appList.SaveXml(tempFile);
                appList2 = XmlStorage.LoadXml <AppList>(tempFile);
            }

            // Ensure data stayed the same
            Assert.AreEqual(appList, appList2, "Serialized objects should be equal.");
            Assert.AreEqual(appList.GetHashCode(), appList2.GetHashCode(), "Serialized objects' hashes should be equal.");
            Assert.IsFalse(ReferenceEquals(appList, appList2), "Serialized objects should not return the same reference.");
        }
        //--------------------//

        #region Apps
        /// <inheritdoc/>
        protected override AppEntry AddAppInternal(FeedTarget target)
        {
            // Prevent double entries
            if (AppList.ContainsEntry(target.Uri))
            {
                throw new InvalidOperationException(string.Format(Resources.AppAlreadyInList, target.Feed.Name));
            }

            // Get basic metadata and copy of capabilities from feed
            var appEntry = new AppEntry {
                InterfaceUri = target.Uri, Name = target.Feed.Name, Timestamp = DateTime.UtcNow
            };

            appEntry.CapabilityLists.AddRange(target.Feed.CapabilityLists.CloneElements());

            AppList.Entries.Add(appEntry);
            WriteAppDir(appEntry);
            return(appEntry);
        }
        private void HandleDownloadedAppList(SyncResetMode resetMode, [NotNull] byte[] appListData)
        {
            if (appListData.Length == 0)
            {
                return;
            }

            AppList serverList;

            try
            {
                serverList = AppList.LoadXmlZip(new MemoryStream(appListData), _cryptoKey);
            }
            #region Error handling
            catch (ZipException ex)
            {
                // Wrap exception to add context information
                if (ex.Message == "Invalid password")
                {
                    throw new InvalidDataException(Resources.SyncCryptoKeyInvalid);
                }
                throw new InvalidDataException(Resources.SyncServerDataDamaged, ex);
            }
            #endregion

            Handler.CancellationToken.ThrowIfCancellationRequested();
            try
            {
                MergeData(serverList, resetClient: (resetMode == SyncResetMode.Client));
            }
            catch (KeyNotFoundException ex)
            {
                // Wrap exception since only certain exception types are allowed
                throw new InvalidDataException(ex.Message, ex);
            }
            finally
            {
                Finish();
            }

            Handler.CancellationToken.ThrowIfCancellationRequested();
        }
Esempio n. 30
0
        public void TestSearch()
        {
            var appA = new AppEntry {
                InterfaceUri = FeedTest.Test1Uri, Name = "AppA"
            };
            var appB = new AppEntry {
                InterfaceUri = FeedTest.Test2Uri, Name = "AppB"
            };
            var lib = new AppEntry {
                InterfaceUri = FeedTest.Test3Uri, Name = "Lib"
            };
            var appList = new AppList {
                Entries = { appA, appB, lib }
            };

            appList.Search("").Should().Equal(appA, appB, lib);
            appList.Search("App").Should().Equal(appA, appB);
            appList.Search("AppA").Should().Equal(appA);
            appList.Search("AppB").Should().Equal(appB);
        }