Пример #1
0
        private void PurgeHashes(TxFileManager tx_file, string file)
        {
            tx_file.Delete($"{file}.sha1");
            tx_file.Delete($"{file}.sha256");

            sha1Cache.Remove(file);
            sha256Cache.Remove(file);
        }
Пример #2
0
        /// <summary>
        /// Removes the given URL from the cache.
        /// Returns true if any work was done, false otherwise.
        /// This method is filesystem transaction aware.
        /// </summary>
        public bool Remove(Uri url)
        {
            string file = GetCachedFilename(url);

            if (file != null)
            {
                tx_file.Delete(file);
                return(true);
            }

            return(false);
        }
Пример #3
0
        /// <summary>
        /// Removes the given URL from the cache.
        /// Returns true if any work was done, false otherwise.
        /// This method is filesystem transaction aware.
        /// </summary>
        public bool Remove(Uri url)
        {
            TxFileManager tx_file = new TxFileManager();

            string file = GetCachedFilename(url);

            if (file != null)
            {
                tx_file.Delete(file);
                tx_file.Delete($"{file}.sha1");
                tx_file.Delete($"{file}.sha256");

                // We've changed our cache, so signal that immediately.
                cachedFiles?.Remove(CreateURLHash(url));
                sha1Cache.Remove(file);
                sha256Cache.Remove(file);

                return(true);
            }

            return(false);
        }
Пример #4
0
        public static string Download(string url, string filename = null, IUser user = null)
        {
            user = user ?? new ConsoleUser();
            user.RaiseMessage("Downloading {0}", url);

            // Generate a temporary file if none is provided.
            if (filename == null)
            {
                filename = file_transaction.GetTempFileName();
            }

            log.DebugFormat("Downloading {0} to {1}", url, filename);

            var agent = new WebClient();

            agent.Headers.Add("user-agent", UserAgentString);

            try
            {
                agent.DownloadFile(url, filename);
            }
            catch (Exception ex)
            {
                // Clean up our file, it's unlikely to be complete.
                // We do this even though we're using transactional files, as we may not be in a transaction.
                // It's okay if this fails.
                try
                {
                    log.DebugFormat("Removing {0} after web error failure", filename);
                    file_transaction.Delete(filename);
                }
                catch
                {
                    // Apparently we need a catch, even if we do nothing.
                }

                // Look for an exception regarding the authentication.
                if (Regex.IsMatch(ex.ToString(), "The authentication or decryption has failed."))
                {
                    throw new MissingCertificateKraken("Failed downloading " + url, ex);
                }

                // Not the exception we were looking for! Throw it further upwards!
                throw;
            }

            return(filename);
        }
Пример #5
0
        /// <summary>
        /// Removes the given URL from the cache.
        /// Returns true if any work was done, false otherwise.
        /// This method is filesystem transaction aware.
        /// </summary>
        public bool Remove(Uri url)
        {
            string file = GetCachedFilename(url);

            if (file != null)
            {
                tx_file.Delete(file);

                // We've changed our cache, so signal that immediately.
                OnCacheChanged();

                return(true);
            }

            return(false);
        }
Пример #6
0
        /// <summary>
        /// Create the CKAN directory and any supporting files.
        /// </summary>
        private void SetupCkanDirectories(bool scan = true)
        {
            log.InfoFormat("Initialising {0}", CkanDir());

            // TxFileManager knows if we are in a transaction
            TxFileManager txFileMgr = new TxFileManager();

            if (!Directory.Exists(CkanDir()))
            {
                User.RaiseMessage("Setting up CKAN for the first time...");
                User.RaiseMessage("Creating {0}", CkanDir());
                txFileMgr.CreateDirectory(CkanDir());

                if (scan)
                {
                    User.RaiseMessage("Scanning for installed mods...");
                    Scan();
                }
            }

            playTime = TimeLog.Load(TimeLog.GetPath(CkanDir())) ?? new TimeLog();

            if (!Directory.Exists(InstallHistoryDir()))
            {
                User.RaiseMessage("Creating {0}", InstallHistoryDir());
                txFileMgr.CreateDirectory(InstallHistoryDir());
            }

            // Clear any temporary files we find. If the directory
            // doesn't exist, then no sweat; FilesystemTransaction
            // will auto-create it as needed.
            // Create our temporary directories, or clear them if they
            // already exist.
            if (Directory.Exists(TempDir()))
            {
                var directory = new DirectoryInfo(TempDir());
                foreach (FileInfo file in directory.GetFiles())
                {
                    txFileMgr.Delete(file.FullName);
                }
                foreach (DirectoryInfo subDirectory in directory.GetDirectories())
                {
                    txFileMgr.DeleteDirectory(subDirectory.FullName);
                }
            }
            log.InfoFormat("Initialised {0}", CkanDir());
        }
Пример #7
0
        /// <summary>
        /// Retrieve available modules from the URL given.
        /// </summary>
        private static List <CkanModule> UpdateRegistry(Uri repo, KSP ksp, IUser user, out SortedDictionary <string, int> downloadCounts, out string currentETag)
        {
            TxFileManager file_transaction = new TxFileManager();

            downloadCounts = null;

            // Use this opportunity to also update the build mappings... kind of hacky
            ServiceLocator.Container.Resolve <IKspBuildMap>().Refresh();

            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo, out currentETag);
            }
            catch (System.Net.WebException ex)
            {
                user.RaiseMessage("Failed to download {0}: {1}", repo, ex.ToString());
                currentETag = null;
                return(null);
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            List <CkanModule> newAvailable = null;

            switch (type)
            {
            case FileType.TarGz:
                newAvailable = UpdateRegistryFromTarGz(repo_file, out downloadCounts);
                break;

            case FileType.Zip:
                newAvailable = UpdateRegistryFromZip(repo_file);
                break;
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);

            return(newAvailable);
        }
Пример #8
0
        /// <summary>
        /// Retrieve available modules from the URL given.
        /// </summary>
        private static List <CkanModule> UpdateRegistry(Uri repo, GameInstance ksp, IUser user, out SortedDictionary <string, int> downloadCounts, out string currentETag)
        {
            TxFileManager file_transaction = new TxFileManager();

            downloadCounts = null;

            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo, out currentETag);
            }
            catch (System.Net.WebException ex)
            {
                user.RaiseError("Failed to download {0}: {1}", repo, ex.Message);
                currentETag = null;
                return(null);
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            List <CkanModule> newAvailable = null;

            switch (type)
            {
            case FileType.TarGz:
                newAvailable = UpdateRegistryFromTarGz(repo_file, out downloadCounts);
                break;

            case FileType.Zip:
                newAvailable = UpdateRegistryFromZip(repo_file);
                break;
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);

            return(newAvailable);
        }
Пример #9
0
        /// <summary>
        /// Stores all of our files in the cache once done.
        /// Called by NetAsyncDownloader on completion.
        /// Called with all nulls on download cancellation.
        /// </summary>
        private void ModuleDownloadsComplete(NetFileCache cache, Uri[] urls, string[] filenames,
                                             Exception[] errors)
        {
            if (urls != null)
            {
                for (int i = 0; i < errors.Length; i++)
                {
                    if (errors[i] != null)
                    {
                        User.RaiseError("Failed to download \"{0}\" - error: {1}", urls[i], errors[i].Message);
                    }
                    else
                    {
                        // Even if some of our downloads failed, we want to cache the
                        // ones which succeeded.
                        cache.Store(urls[i], filenames[i], modules[i].StandardName());
                    }
                }
            }

            // TODO: If we've had our download cancelled, how do we clean our tmpfiles?

            if (filenames != null)
            {
                // Finally, remove all our temp files.
                // We probably *could* have used Store's integrated move function above, but if we managed
                // to somehow get two URLs the same in our download set, that could cause right troubles!

                foreach (string tmpfile in filenames)
                {
                    log.DebugFormat("Cleaning up {0}", tmpfile);
                    file_transaction.Delete(tmpfile);
                }
            }

            // Signal that we're done.
            lock (download_complete_lock)
            {
                Monitor.Pulse(download_complete_lock);
            }
            User.RaiseDownloadsCompleted(urls, filenames, errors);
        }
Пример #10
0
        /// <summary>
        /// Retrieve available modules from the URL given.
        /// </summary>
        private static List <CkanModule> UpdateRegistry(Uri repo, KSP ksp, IUser user)
        {
            // Use this opportunity to also update the build mappings... kind of hacky
            ServiceLocator.Container.Resolve <IKspBuildMap>().Refresh();

            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo);
            }
            catch (System.Net.WebException)
            {
                user.RaiseMessage("Connection to {0} could not be established.", repo);
                return(null);
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            List <CkanModule> newAvailable = null;

            switch (type)
            {
            case FileType.TarGz:
                newAvailable = UpdateRegistryFromTarGz(repo_file);
                break;

            case FileType.Zip:
                newAvailable = UpdateRegistryFromZip(repo_file);
                break;
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);

            return(newAvailable);
        }
Пример #11
0
 private void SafeDeleteDirectory(string directoryPath)
 {
     if (Directory.Exists(directoryPath))
     {
         //try delete whole directory
         try
         {
             _txFileManager.DeleteDirectory(directoryPath);
         }
         //Because some folder can be locked by ASP.NET Bundles file monitor we should ignore IOException
         catch (IOException)
         {
             //If fail need to delete directory content first
             //Files
             foreach (var file in Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.AllDirectories))
             {
                 _txFileManager.Delete(file);
             }
             //Dirs
             foreach (var subDirectory in Directory.EnumerateDirectories(directoryPath, "*", SearchOption.AllDirectories))
             {
                 try
                 {
                     _txFileManager.DeleteDirectory(subDirectory);
                 }
                 catch (IOException)
                 {
                 }
             }
             //Then try to delete main directory itself
             try
             {
                 _txFileManager.DeleteDirectory(directoryPath);
             }
             catch (IOException)
             {
             }
         }
     }
 }
Пример #12
0
        // Delete group
        private void cmdDelete_Click(object sender, EventArgs e)
        {
            resetSelection();

            try
            {
                string configPath   = Path.Combine(Paths.ConfigPath, Category.Name);
                string shortcutPath = Path.Combine(Paths.ShortcutsPath, Regex.Replace(Category.Name, @"(_)+", " ") + ".lnk");

                var dir = new DirectoryInfo(configPath);

                try
                {
                    IFileManager fm = new TxFileManager();
                    using (TransactionScope scope1 = new TransactionScope())
                    {
                        fm.DeleteDirectory(configPath);
                        fm.Delete(shortcutPath);
                        //this.Hide();
                        //this.Dispose();
                        this.Close();

                        Client.Reload(); //flush and reload category panels
                        scope1.Complete();
                    }
                }
                catch (Exception)
                {
                    MessageBox.Show("Please close all programs used within the taskbar group in order to delete!");
                    return;
                }
            }
            catch (IOException ex)
            {
                MessageBox.Show(ex.Message);
            }
            Client.Reset();
        }
Пример #13
0
        static bool AddToCatalog(IResource resource, List <IResource> resources, TxFileManager fileManager, XElement xml)
        {
            var index   = resources.IndexOf(resource);
            var updated = false;

            if (index != -1)
            {
                var existing = resources[index];
                if (!string.Equals(existing.FilePath, resource.FilePath, StringComparison.CurrentCultureIgnoreCase))
                {
                    fileManager.Delete(existing.FilePath);
                }
                resources.RemoveAt(index);
                updated = true;
            }
            resource.GetInputsOutputs(xml);
            resource.ReadDataList(xml);
            resource.SetIsNew(xml);
            resource.UpdateErrorsBasedOnXML(xml);

            resources.Add(resource);
            return(updated);
        }
Пример #14
0
        /// <summary>
        /// Updates the supplied registry from the URL given.
        /// This will *clear* the registry of available modules first.
        /// This does not *save* the registry. For that, you probably want Repo.Update
        /// </summary>
        internal static void UpdateRegistry(Uri repo, Registry registry)
        {
            log.InfoFormat("Downloading {0}", repo);

            string repo_file = Net.Download(repo);

            using (var zipfile = new ZipFile(repo_file))
            {
                // Clear our list of known modules.
                registry.ClearAvailable();

                // Walk the archive, looking for .ckan files.
                string filter = @"\.ckan$";

                foreach (ZipEntry entry in zipfile)
                {
                    string filename = entry.Name;

                    // Skip things we don't want.
                    if (!Regex.IsMatch(filename, filter))
                    {
                        log.DebugFormat("Skipping archive entry {0}", filename);
                        continue;
                    }

                    log.DebugFormat("Reading CKAN data from {0}", filename);

                    // Read each file into a string.
                    string metadata_json;
                    using (var stream = new StreamReader(zipfile.GetInputStream(entry)))
                    {
                        metadata_json = stream.ReadToEnd();
                        stream.Close();
                    }

                    log.Debug("Converting from JSON...");

                    try
                    {
                        CkanModule module = CkanModule.FromJson(metadata_json);
                        log.InfoFormat("Found {0} version {1}", module.identifier, module.version);
                        registry.AddAvailable(module);
                    }
                    catch (Kraken kraken)
                    {
                        if (kraken is UnsupportedKraken || kraken is BadMetadataKraken)
                        {
                            // Either of these can be caused by data meant for future
                            // clients, so they're not really warnings, they're just
                            // informational.

                            log.InfoFormat("Skipping {0} : {1}", filename, kraken.Message);
                        }

                        // This is not the kraken we're looking for.
                        throw;
                    }
                }

                zipfile.Close();
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);
        }
Пример #15
0
Файл: Net.cs Проект: embix/CKAN
        public static string Download(string url, out string etag, string filename = null, IUser user = null)
        {
            TxFileManager FileTransaction = new TxFileManager();

            user = user ?? new NullUser();
            user.RaiseMessage("Downloading {0}", url);

            // Generate a temporary file if none is provided.
            if (filename == null)
            {
                filename = FileTransaction.GetTempFileName();
            }

            log.DebugFormat("Downloading {0} to {1}", url, filename);

            try
            {
                var agent = new RedirectingTimeoutWebClient();
                agent.DownloadFile(url, filename);
                etag = agent.ResponseHeaders.Get("ETag")?.Replace("\"", "");
            }
            catch (Exception exc)
            {
                var wexc = exc as WebException;
                if (wexc?.Status == WebExceptionStatus.ProtocolError)
                {
                    // Get redirect if redirected.
                    // This is needed when redirecting from HTTPS to HTTP on .NET Core.
                    var response = wexc.Response as HttpWebResponse;
                    if (response?.StatusCode == HttpStatusCode.Redirect)
                    {
                        return(Download(response.GetResponseHeader("Location"), out etag, filename, user));
                    }
                    // Otherwise it's a valid failure from the server (probably a 404), keep it
                }

                // Clean up our file, it's unlikely to be complete.
                // We do this even though we're using transactional files, as we may not be in a transaction.
                // It's okay if this fails.
                try
                {
                    log.DebugFormat("Removing {0} after web error failure", filename);
                    FileTransaction.Delete(filename);
                }
                catch
                {
                    // Apparently we need a catch, even if we do nothing.
                }

                // Look for an exception regarding the authentication.
                if (Regex.IsMatch(exc.ToString(), "The authentication or decryption has failed."))
                {
                    throw new MissingCertificateKraken("Failed downloading " + url, exc);
                }

                // Not the exception we were looking for! Throw it further upwards!
                throw;
            }

            return(filename);
        }
Пример #16
0
        // Save group
        private void cmdSave_Click(object sender, EventArgs e)
        {
            resetSelection();

            //List <Directory> directories =

            if (txtGroupName.Text == "Name the new group...") // Verify category name
            {
                lblErrorTitle.Text    = "Must select a name";
                lblErrorTitle.Visible = true;
            }
            else if (IsNew && Directory.Exists(Path.Combine(Paths.ConfigPath, txtGroupName.Text)) ||
                     !IsNew && Category.Name != txtGroupName.Text && Directory.Exists(Path.Combine(Paths.ConfigPath, txtGroupName.Text)))
            {
                lblErrorTitle.Text    = "There is already a group with that name";
                lblErrorTitle.Visible = true;
            }
            else if (!new Regex("^[0-9a-zA-Z \b]+$").IsMatch(txtGroupName.Text))
            {
                lblErrorTitle.Text    = "Name must not have any special characters";
                lblErrorTitle.Visible = true;
            }
            else if (cmdAddGroupIcon.BackgroundImage ==
                     global::client.Properties.Resources.AddWhite) // Verify icon
            {
                lblErrorIcon.Text    = "Must select group icon";
                lblErrorIcon.Visible = true;
            }
            else if (Category.ShortcutList.Count == 0) // Verify shortcuts
            {
                lblErrorShortcut.Text    = "Must select at least one shortcut";
                lblErrorShortcut.Visible = true;
            }
            else
            {
                try
                {
                    foreach (ProgramShortcut shortcutModifiedItem in shortcutChanged)
                    {
                        if (!Directory.Exists(shortcutModifiedItem.WorkingDirectory))
                        {
                            shortcutModifiedItem.WorkingDirectory = getProperDirectory(shortcutModifiedItem.FilePath);
                        }
                    }


                    if (!IsNew)
                    {
                        //
                        // Delete old config
                        //
                        string configPath   = Path.Combine(Paths.ConfigPath, Category.Name);
                        string shortcutPath = Path.Combine(Paths.ShortcutsPath, Regex.Replace(Category.Name, @"(_)+", " ") + ".lnk");

                        try
                        {
                            IFileManager fm = new TxFileManager();
                            using (TransactionScope scope1 = new TransactionScope())
                            {
                                fm.DeleteDirectory(configPath);
                                fm.Delete(shortcutPath);
                                scope1.Complete();
                            }
                        } catch (Exception)
                        {
                            MessageBox.Show("Please close all programs used within the taskbar group in order to save!");
                            return;
                        }
                    }
                    //
                    // Creating new config
                    //
                    //int width = int.Parse(lblNum.Text);

                    Category.Width = int.Parse(lblNum.Text);

                    //Category category = new Category(txtGroupName.Text, Category.ShortcutList, width, System.Drawing.ColorTranslator.ToHtml(CategoryColor), Category.Opacity); // Instantiate category

                    // Normalize string so it can be used in path; remove spaces
                    Category.Name = Regex.Replace(txtGroupName.Text, @"\s+", "_");

                    Category.CreateConfig(cmdAddGroupIcon.BackgroundImage);             // Creating group config files
                    Client.LoadCategory(Path.Combine(Paths.ConfigPath, Category.Name)); // Loading visuals

                    this.Dispose();
                    Client.Reload();
                }
                catch (IOException ex)
                {
                    MessageBox.Show(ex.Message);
                }

                Client.Reset();
            }
        }
Пример #17
0
        public static string Download(string url, out string etag, string filename = null, IUser user = null)
        {
            TxFileManager FileTransaction = new TxFileManager();

            user = user ?? new NullUser();
            user.RaiseMessage("Downloading {0}", url);

            // Generate a temporary file if none is provided.
            if (filename == null)
            {
                filename = FileTransaction.GetTempFileName();
            }

            log.DebugFormat("Downloading {0} to {1}", url, filename);

            try
            {
                var agent = MakeDefaultHttpClient();
                agent.DownloadFile(url, filename);
                etag = agent.ResponseHeaders.Get("ETag")?.Replace("\"", "");
            }
            // Explicit redirect handling. This is needed when redirecting from HTTPS to HTTP on .NET Core.
            catch (WebException ex)
            {
                if (ex.Status != WebExceptionStatus.ProtocolError)
                {
                    throw;
                }

                HttpWebResponse response = ex.Response as HttpWebResponse;
                if (response.StatusCode != HttpStatusCode.Redirect)
                {
                    throw;
                }

                return(Net.Download(response.GetResponseHeader("Location"), out etag, filename, user));
            }
            catch (Exception ex)
            {
                log.InfoFormat("Download failed, trying with curlsharp...");
                etag = null;

                try
                {
                    Curl.Init();

                    using (FileStream stream = File.OpenWrite(filename))
                        using (var curl = Curl.CreateEasy(url, stream))
                        {
                            CurlCode result = curl.Perform();
                            if (result != CurlCode.Ok)
                            {
                                throw new Kraken("curl download of " + url + " failed with CurlCode " + result);
                            }
                            else
                            {
                                log.Debug("curlsharp download successful");
                            }
                        }

                    Curl.CleanUp();
                    return(filename);
                }
                catch
                {
                    // D'oh, failed again. Fall through to clean-up handling.
                }

                // Clean up our file, it's unlikely to be complete.
                // We do this even though we're using transactional files, as we may not be in a transaction.
                // It's okay if this fails.
                try
                {
                    log.DebugFormat("Removing {0} after web error failure", filename);
                    FileTransaction.Delete(filename);
                }
                catch
                {
                    // Apparently we need a catch, even if we do nothing.
                }

                // Look for an exception regarding the authentication.
                if (Regex.IsMatch(ex.ToString(), "The authentication or decryption has failed."))
                {
                    throw new MissingCertificateKraken("Failed downloading " + url, ex);
                }

                // Not the exception we were looking for! Throw it further upwards!
                throw;
            }

            return(filename);
        }
Пример #18
0
        static void RunStressTest()
        {
            Stopwatch sw = new Stopwatch();

            sw.Start();

            {
                // Pre-test checks
                string       tempDir;
                IFileManager fm = new TxFileManager();
                using (TransactionScope s1 = new TransactionScope())
                {
                    tempDir = (new DirectoryInfo(fm.CreateTempDirectory())).Parent.FullName;
                    Console.WriteLine("Temp path: " + tempDir);
                }

                string[] directories = Directory.GetDirectories(tempDir);
                string[] files       = Directory.GetFiles(tempDir);
                if (directories.Length > 0 || files.Length > 0)
                {
                    Console.WriteLine(string.Format("ERROR  Please ensure temp path {0} has no children before running this test.", tempDir));
                    return;
                }
            }

            // Start each test in its own thread and repeat for a few interations
            const int    numThreads = 10;
            const int    iterations = 250;
            const string text       = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

            long count = 0;

            IList <Thread> threads = new List <Thread>();

            for (int i = 0; i < numThreads; i++)
            {
                Thread t = new Thread(() =>
                {
                    IFileManager fm = new TxFileManager();
                    for (int j = 0; j < iterations; j++)
                    {
                        using (TransactionScope s1 = new TransactionScope())
                        {
                            TransactionOptions to = new TransactionOptions();
                            to.Timeout            = TimeSpan.FromMinutes(30);

                            long myCount = Interlocked.Increment(ref count);
                            if (myCount % 250 == 0)
                            {
                                Console.WriteLine(myCount + " (" + myCount * 100 / (numThreads * iterations) + " %)");
                            }

                            string f1 = fm.CreateTempFileName();
                            string f2 = fm.CreateTempFileName();
                            string d1 = fm.CreateTempDirectory();

                            if (i % 100 == 0)
                            {
                                Console.WriteLine(i);
                            }

                            fm.AppendAllText(f1, text);
                            fm.Copy(f1, f2, false);

                            fm.CreateDirectory(d1);
                            fm.Delete(f2);
                            fm.DeleteDirectory(d1);
                            bool b1   = fm.DirectoryExists(d1);
                            bool b2   = fm.FileExists(f1);
                            string f3 = fm.CreateTempFileName();
                            fm.Move(f1, f3);
                            string f4 = fm.CreateTempFileName();
                            fm.Snapshot(f4);
                            fm.WriteAllBytes(f4, new byte[] { 64, 65 });
                            string f5 = fm.CreateTempFileName();
                            fm.WriteAllText(f5, text);

                            fm.Delete(f1);
                            fm.Delete(f2);
                            fm.Delete(f3);
                            fm.Delete(f4);
                            fm.Delete(f5);
                            fm.DeleteDirectory(d1);
                            s1.Complete();
                        }
                    }
                });

                threads.Add(t);
            }

            foreach (Thread t in threads)
            {
                t.Start();
            }

            Console.WriteLine("All threads started.");

            foreach (Thread t in threads)
            {
                t.Join();
            }

            sw.Stop();

            Console.WriteLine("All threads joined. Elapsed: {0}.", sw.ElapsedMilliseconds);
        }
Пример #19
0
        public static string Download(string url, out string etag, string filename = null, IUser user = null)
        {
            TxFileManager FileTransaction = new TxFileManager();

            user = user ?? new NullUser();
            user.RaiseMessage("Downloading {0}", url);

            // Generate a temporary file if none is provided.
            if (filename == null)
            {
                filename = FileTransaction.GetTempFileName();
            }

            log.DebugFormat("Downloading {0} to {1}", url, filename);

            try
            {
                var agent = MakeDefaultHttpClient();
                agent.DownloadFile(url, filename);
                etag = agent.ResponseHeaders.Get("ETag")?.Replace("\"", "");
            }
            // Explicit redirect handling. This is needed when redirecting from HTTPS to HTTP on .NET Core.
            catch (WebException ex)
            {
                if (ex.Status != WebExceptionStatus.ProtocolError)
                {
                    throw;
                }

                HttpWebResponse response = ex.Response as HttpWebResponse;
                if (response?.StatusCode != HttpStatusCode.Redirect)
                {
                    throw;
                }

                return(Download(response.GetResponseHeader("Location"), out etag, filename, user));
            }
            catch (Exception e)
            {
                log.InfoFormat("Native download failed, trying with CurlSharp...");
                etag = null;

                try
                {
                    Curl.Init();

                    using (FileStream stream = File.OpenWrite(filename))
                    {
                        string header = string.Empty;

                        var client = Curl.CreateEasy(url, stream, delegate(byte[] buf, int size, int nmemb, object extraData)
                        {
                            header += Encoding.UTF8.GetString(buf);
                            return(size * nmemb);
                        });

                        using (client)
                        {
                            var result     = client.Perform();
                            var returnCode = client.ResponseCode;

                            if (result != CurlCode.Ok || returnCode >= 300)
                            {
                                // Always log if it's an error.
                                log.ErrorFormat("Response from {0}:\r\n\r\n{1}\r\n{2}", url, header, "Content not logged because it is likely a file.");

                                WebException curlException =
                                    new WebException($"Curl download failed with status {returnCode}.");
                                throw new NativeAndCurlDownloadFailedKraken(
                                          new List <Exception> {
                                    e, curlException
                                },
                                          url.ToString(), header, null, returnCode
                                          );
                            }
                            else
                            {
                                // Only log if debug flag is set.
                                log.DebugFormat("Response from {0}:\r\n\r\n{1}\r\n{2}", url, header, "Content not logged because it is likely a file.");
                            }
                        }
                    }

                    Curl.CleanUp();
                    return(filename);
                }
                catch
                {
                    // D'oh, failed again. Fall through to clean-up handling.
                }

                // Clean up our file, it's unlikely to be complete.
                // We do this even though we're using transactional files, as we may not be in a transaction.
                // It's okay if this fails.
                try
                {
                    log.DebugFormat("Removing {0} after web error failure", filename);
                    FileTransaction.Delete(filename);
                }
                catch
                {
                    // Apparently we need a catch, even if we do nothing.
                }

                // Look for an exception regarding the authentication.
                if (Regex.IsMatch(e.ToString(), "The authentication or decryption has failed."))
                {
                    throw new MissingCertificateKraken("Failed downloading " + url, e);
                }

                // Not the exception we were looking for! Throw it further upwards!
                throw;
            }

            return(filename);
        }
Пример #20
0
        /// <summary>
        /// Updates the supplied registry from the URL given.
        /// This does not *save* the registry. For that, you probably want Repo.Update
        /// </summary>
        internal static void UpdateRegistry(Uri repo, Registry registry, KSP ksp, IUser user, Boolean clear = true)
        {
            // Use this opportunity to also update the build mappings... kind of hacky
            ServiceLocator.Container.Resolve <IKspBuildMap>().Refresh();

            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo);
            }
            catch (System.Net.WebException)
            {
                user.RaiseMessage("Connection to {0} could not be established.", repo);
                return;
            }

            // Clear our list of known modules.
            if (clear)
            {
                registry.ClearAvailable();
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            switch (type)
            {
            case FileType.TarGz:
                UpdateRegistryFromTarGz(repo_file, registry);
                break;

            case FileType.Zip:
                UpdateRegistryFromZip(repo_file, registry);
                break;

            default:
                break;
            }

            List <CkanModule> metadataChanges = new List <CkanModule>();

            foreach (var installedModule in registry.InstalledModules)
            {
                var identifier = installedModule.identifier;

                var installedVersion = registry.InstalledVersion(identifier);
                if (!(registry.available_modules.ContainsKey(identifier)))
                {
                    log.InfoFormat("UpdateRegistry, module {0}, version {1} not in repository ({2})", identifier, installedVersion, repo);
                    continue;
                }

                if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion))
                {
                    continue;
                }

                // if the mod is installed and the metadata is different we have to reinstall it
                var metadata = registry.available_modules[identifier].module_version[installedVersion];

                var oldMetadata = registry.InstalledModule(identifier).Module;

                bool same = true;
                if ((metadata.install == null) != (oldMetadata.install == null) ||
                    (metadata.install != null && metadata.install.Length != oldMetadata.install.Length))
                {
                    same = false;
                }
                else
                {
                    if (metadata.install != null)
                    {
                        for (int i = 0; i < metadata.install.Length; i++)
                        {
                            if (metadata.install[i].file != oldMetadata.install[i].file)
                            {
                                same = false;
                                break;
                            }

                            if (metadata.install[i].install_to != oldMetadata.install[i].install_to)
                            {
                                same = false;
                                break;
                            }

                            if (metadata.install[i].@as != oldMetadata.install[i].@as)
                            {
                                same = false;
                                break;
                            }

                            if ((metadata.install[i].filter == null) != (oldMetadata.install[i].filter == null))
                            {
                                same = false;
                                break;
                            }


                            if (metadata.install[i].filter != null)
                            {
                                if (!metadata.install[i].filter.SequenceEqual(oldMetadata.install[i].filter))
                                {
                                    same = false;
                                    break;
                                }
                            }

                            if ((metadata.install[i].filter_regexp == null) != (oldMetadata.install[i].filter_regexp == null))
                            {
                                same = false;
                                break;
                            }

                            if (metadata.install[i].filter_regexp != null)
                            {
                                if (!metadata.install[i].filter_regexp.SequenceEqual(oldMetadata.install[i].filter_regexp))
                                {
                                    same = false;
                                    break;
                                }
                            }
                        }
                    }
                }

                if (!RelationshipsAreEquivalent(metadata.conflicts, oldMetadata.conflicts))
                {
                    same = false;
                }

                if (!RelationshipsAreEquivalent(metadata.depends, oldMetadata.depends))
                {
                    same = false;
                }

                if (!RelationshipsAreEquivalent(metadata.recommends, oldMetadata.recommends))
                {
                    same = false;
                }

                if (metadata.provides != oldMetadata.provides)
                {
                    if (metadata.provides == null || oldMetadata.provides == null)
                    {
                        same = false;
                    }
                    else if (!metadata.provides.OrderBy(i => i).SequenceEqual(oldMetadata.provides.OrderBy(i => i)))
                    {
                        same = false;
                    }
                }

                if (!same)
                {
                    metadataChanges.Add(registry.available_modules[identifier].module_version[installedVersion]);
                }
            }

            if (metadataChanges.Any())
            {
                var sb = new StringBuilder();

                for (var i = 0; i < metadataChanges.Count; i++)
                {
                    var module = metadataChanges[i];

                    sb.AppendLine(string.Format("- {0} {1}", module.identifier, module.version));
                }

                if (user.RaiseYesNoDialog(string.Format(@"The following mods have had their metadata changed since last update:

{0}
You should reinstall them in order to preserve consistency with the repository.

Do you wish to reinstall now?", sb)))
                {
                    ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser());
                    // New upstream metadata may break the consistency of already installed modules
                    // e.g. if user installs modules A and B and then later up A is made to conflict with B
                    // This is perfectly normal and shouldn't produce an error, therefore we skip enforcing
                    // consistency. However, we will show the user any inconsistencies later on.

                    // Use the identifiers so we use the overload that actually resolves relationships
                    // Do each changed module one at a time so a failure of one doesn't cause all the others to fail
                    foreach (var changedIdentifier in metadataChanges.Select(i => i.identifier))
                    {
                        try
                        {
                            installer.Upgrade(
                                new[] { changedIdentifier },
                                new NetAsyncModulesDownloader(new NullUser()),
                                enforceConsistency: false
                                );
                        }
                        // Thrown when a dependency couldn't be satisfied
                        catch (ModuleNotFoundKraken)
                        {
                            log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier);
                            user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier);
                        }
                        // Thrown when a conflicts relationship is violated
                        catch (InconsistentKraken)
                        {
                            log.WarnFormat("Skipping installation of {0} due to relationship error.", changedIdentifier);
                            user.RaiseMessage("Skipping installation of {0} due to relationship error.", changedIdentifier);
                        }
                    }
                }
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);
        }
Пример #21
0
        public static string Download(string url, string filename = null, IUser user = null)
        {
            user = user ?? new NullUser();
            user.RaiseMessage("Downloading {0}", url);

            // Generate a temporary file if none is provided.
            if (filename == null)
            {
                filename = FileTransaction.GetTempFileName();
            }

            Log.DebugFormat("Downloading {0} to {1}", url, filename);

            var agent = MakeDefaultHttpClient();

            try
            {
                agent.DownloadFile(url, filename);
            }
            catch (Exception ex)
            {
                Log.InfoFormat("Download failed, trying with curlsharp...");

                try
                {
                    Curl.Init();

                    using (FileStream stream = File.OpenWrite(filename))
                        using (var curl = Curl.CreateEasy(url, stream))
                        {
                            CurlCode result = curl.Perform();
                            if (result != CurlCode.Ok)
                            {
                                throw new Kraken("curl download of " + url + " failed with CurlCode " + result);
                            }
                            else
                            {
                                Log.Debug("curlsharp download successful");
                            }
                        }

                    Curl.CleanUp();
                    return(filename);
                }
                catch
                {
                    // D'oh, failed again. Fall through to clean-up handling.
                }

                // Clean up our file, it's unlikely to be complete.
                // We do this even though we're using transactional files, as we may not be in a transaction.
                // It's okay if this fails.
                try
                {
                    Log.DebugFormat("Removing {0} after web error failure", filename);
                    FileTransaction.Delete(filename);
                }
                catch
                {
                    // Apparently we need a catch, even if we do nothing.
                }

                // Look for an exception regarding the authentication.
                if (Regex.IsMatch(ex.ToString(), "The authentication or decryption has failed."))
                {
                    throw new MissingCertificateKraken("Failed downloading " + url, ex);
                }

                // Not the exception we were looking for! Throw it further upwards!
                throw;
            }

            return(filename);
        }
Пример #22
0
        /// <summary>
        /// Updates the supplied registry from the URL given.
        /// This does not *save* the registry. For that, you probably want Repo.Update
        /// </summary>
        internal static void UpdateRegistry(Uri repo, Registry registry, KSP ksp, IUser user, Boolean clear = true)
        {
            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo);
            }
            catch (System.Net.WebException)
            {
                user.RaiseMessage("Connection to {0} could not be established.", repo);
                return;
            }

            // Clear our list of known modules.
            var old_available = registry.available_modules;

            if (clear)
            {
                registry.ClearAvailable();
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            switch (type)
            {
            case FileType.TarGz:
                UpdateRegistryFromTarGz(repo_file, registry);
                break;

            case FileType.Zip:
                UpdateRegistryFromZip(repo_file, registry);
                break;

            default:
                break;
            }

            List <CkanModule> metadataChanges = new List <CkanModule>();

            foreach (var identifierModulePair in old_available)
            {
                var identifier = identifierModulePair.Key;

                if (registry.IsInstalled(identifier))
                {
                    var installedVersion = registry.InstalledVersion(identifier);
                    if (!(registry.available_modules.ContainsKey(identifier)))
                    {
                        log.InfoFormat("UpdateRegistry, module {0}, version {1} not in repository ({2})", identifier, installedVersion, repo);
                        continue;
                    }

                    if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion))
                    {
                        continue;
                    }

                    // if the mod is installed and the metadata is different we have to reinstall it
                    var metadata = registry.available_modules[identifier].module_version[installedVersion];

                    if (!old_available.ContainsKey(identifier) ||
                        !old_available[identifier].module_version.ContainsKey(installedVersion))
                    {
                        continue;
                    }

                    var oldMetadata = old_available[identifier].module_version[installedVersion];

                    bool same = true;
                    if ((metadata.install == null) != (oldMetadata.install == null) ||
                        (metadata.install != null && metadata.install.Length != oldMetadata.install.Length))
                    {
                        same = false;
                    }
                    else
                    {
                        if (metadata.install != null)
                        {
                            for (int i = 0; i < metadata.install.Length; i++)
                            {
                                if (metadata.install[i].file != oldMetadata.install[i].file)
                                {
                                    same = false;
                                    break;
                                }

                                if (metadata.install[i].install_to != oldMetadata.install[i].install_to)
                                {
                                    same = false;
                                    break;
                                }

                                if ((metadata.install[i].filter == null) != (oldMetadata.install[i].filter == null))
                                {
                                    same = false;
                                    break;
                                }

                                if (metadata.install[i].filter != null)
                                {
                                    if (!metadata.install[i].filter.SequenceEqual(oldMetadata.install[i].filter))
                                    {
                                        same = false;
                                        break;
                                    }
                                }

                                if ((metadata.install[i].filter_regexp == null) != (oldMetadata.install[i].filter_regexp == null))
                                {
                                    same = false;
                                    break;
                                }

                                if (metadata.install[i].filter_regexp != null)
                                {
                                    if (!metadata.install[i].filter_regexp.SequenceEqual(oldMetadata.install[i].filter_regexp))
                                    {
                                        same = false;
                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if (!same)
                    {
                        metadataChanges.Add(registry.available_modules[identifier].module_version[installedVersion]);
                    }
                }
            }

            if (metadataChanges.Any())
            {
                string mods = "";
                for (int i = 0; i < metadataChanges.Count; i++)
                {
                    mods += metadataChanges[i].identifier + " "
                            + metadataChanges[i].version.ToString() + ((i < metadataChanges.Count - 1) ? ", " : "");
                }

                if (user.RaiseYesNoDialog(String.Format(
                                              @"The following mods have had their metadata changed since last update - {0}.
It is advisable that you reinstall them in order to preserve consistency with the repository. Do you wish to reinstall now?", mods)))
                {
                    ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser());
                    installer.Upgrade(metadataChanges, new NetAsyncDownloader(new NullUser()));
                }
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);
        }
Пример #23
0
        /// <summary>
        /// Uninstall the module provided. For internal use only.
        /// Use UninstallList for user queries, it also does dependency handling.
        /// This does *NOT* save the registry.
        /// </summary>

        private void Uninstall(string modName)
        {
            using (var transaction = CkanTransaction.CreateTransactionScope())
            {
                InstalledModule mod = registry_manager.registry.InstalledModule(modName);

                if (mod == null)
                {
                    log.ErrorFormat("Trying to uninstall {0} but it's not installed", modName);
                    throw new ModNotInstalledKraken(modName);
                }

                // Walk our registry to find all files for this mod.
                IEnumerable <string> files = mod.Files;

                var directoriesToDelete = new HashSet <string>();

                foreach (string file in files)
                {
                    string path = ksp.ToAbsoluteGameDir(file);

                    try
                    {
                        FileAttributes attr = File.GetAttributes(path);

                        // [This is] bitwise math. Basically, attr is some binary value with one bit meaning
                        // "this is a directory". The bitwise and & operator will return a binary value where
                        // only the bits that are on (1) in both the operands are turned on. In this case
                        // doing a bitwise and operation against attr and the FileAttributes.Directory value
                        // will return the value of FileAttributes.Directory if the Directory file attribute
                        // bit is turned on. See en.wikipedia.org/wiki/Bitwise_operation for a better
                        // explanation. – Kyle Trauberman Aug 30 '12 at 21:28
                        // (https://stackoverflow.com/questions/1395205/better-way-to-check-if-path-is-a-file-or-a-directory)
                        // This is the fastest way to do this test.
                        if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
                        {
                            if (!directoriesToDelete.Contains(path))
                            {
                                directoriesToDelete.Add(path);
                            }
                        }
                        else
                        {
                            // Add this files' directory to the list for deletion if it isn't already there.
                            // Helps clean up directories when modules are uninstalled out of dependency order
                            // Since we check for directory contents when deleting, this should purge empty
                            // dirs, making less ModuleManager headaches for people.
                            var directoryName = Path.GetDirectoryName(path);
                            if (!(directoriesToDelete.Contains(directoryName)))
                            {
                                directoriesToDelete.Add(directoryName);
                            }

                            log.DebugFormat("Removing {0}", file);
                            file_transaction.Delete(path);
                        }
                    }
                    catch (Exception ex)
                    {
                        // XXX: This is terrible, we're catching all exceptions.
                        // We don't consider this problem serious enough to abort and revert,
                        // so treat it as a "--verbose" level log message.
                        log.InfoFormat("Failure in locating file {0} : {1}", path, ex.Message);
                    }
                }

                // Remove from registry.
                registry_manager.registry.DeregisterModule(ksp, modName);

                // Our collection of directories may leave empty parent directories.
                directoriesToDelete = AddParentDirectories(directoriesToDelete);

                // Sort our directories from longest to shortest, to make sure we remove child directories
                // before parents. GH #78.
                foreach (string directory in directoriesToDelete.OrderBy(dir => dir.Length).Reverse())
                {
                    if (!Directory.EnumerateFileSystemEntries(directory).Any())
                    {
                        // It is bad if any of this directories gets removed
                        // So we protect them
                        if (IsReservedDirectory(directory))
                        {
                            continue;
                        }

                        // We *don't* use our file_transaction to delete files here, because
                        // it fails if the system's temp directory is on a different device
                        // to KSP. However we *can* safely delete it now we know it's empty,
                        // because the TxFileMgr *will* put it back if there's a file inside that
                        // needs it.
                        //
                        // This works around GH #251.
                        // The filesystem boundry bug is described in https://transactionalfilemgr.codeplex.com/workitem/20

                        log.DebugFormat("Removing {0}", directory);
                        Directory.Delete(directory);
                    }
                    else
                    {
                        log.InfoFormat("Not removing directory {0}, it's not empty", directory);
                    }
                }
                log.InfoFormat("Removed {0}", modName);
                transaction.Complete();
            }
        }
Пример #24
0
        /// <summary>
        /// Uninstall the module provided. For internal use only.
        /// Use UninstallList for user queries, it also does dependency handling.
        /// This does *NOT* save the registry.
        /// </summary>

        private void Uninstall(string modName)
        {
            using (var transaction = new TransactionScope())
            {
                InstalledModule mod = registry_manager.registry.InstalledModule(modName);

                if (mod == null)
                {
                    log.ErrorFormat("Trying to uninstall {0} but it's not installed", modName);
                    throw new ModNotInstalledKraken(modName);
                }

                // Walk our registry to find all files for this mod.
                IEnumerable <string> files = mod.Files;

                var directoriesToDelete = new HashSet <string>();

                foreach (string file in files)
                {
                    string path = ksp.ToAbsoluteGameDir(file);

                    try
                    {
                        FileAttributes attr = File.GetAttributes(path);

                        if ((attr & FileAttributes.Directory) == FileAttributes.Directory)
                        {
                            directoriesToDelete.Add(path);
                        }
                        else
                        {
                            log.InfoFormat("Removing {0}", file);
                            file_transaction.Delete(path);
                        }
                    }
                    catch (Exception ex)
                    {
                        // XXX: This is terrible, we're catching all exceptions.
                        log.ErrorFormat("Failure in locating file {0} : {1}", path, ex.Message);
                    }
                }

                // Remove from registry.

                registry_manager.registry.DeregisterModule(ksp, modName);

                // Sort our directories from longest to shortest, to make sure we remove child directories
                // before parents. GH #78.
                foreach (string directory in directoriesToDelete.OrderBy(dir => dir.Length).Reverse())
                {
                    if (!Directory.EnumerateFileSystemEntries(directory).Any())
                    {
                        // We *don't* use our file_transaction to delete files here, because
                        // it fails if the system's temp directory is on a different device
                        // to KSP. However we *can* safely delete it now we know it's empty,
                        // because the TxFileMgr *will* put it back if there's a file inside that
                        // needs it.
                        //
                        // This works around GH #251.
                        // The filesystem boundry bug is described in https://transactionalfilemgr.codeplex.com/workitem/20

                        log.InfoFormat("Removing {0}", directory);
                        Directory.Delete(directory);
                    }
                    else
                    {
                        User.WriteLine("Not removing directory {0}, it's not empty", directory);
                    }
                }
                transaction.Complete();
            }

            return;
        }
Пример #25
0
        /// <summary>
        /// Updates the supplied registry from the URL given.
        /// This does not *save* the registry. For that, you probably want Repo.Update
        /// </summary>
        internal static void UpdateRegistry(Uri repo, Registry registry, KSP ksp, IUser user, Boolean clear = true)
        {
            log.InfoFormat("Downloading {0}", repo);

            string repo_file = String.Empty;

            try
            {
                repo_file = Net.Download(repo);
            }
            catch (System.Net.WebException e)
            {
                user.RaiseError($"Couldn't download {repo}.", e);
                return;
            }

            // Clear our list of known modules.
            var old_available = registry.available_modules;

            if (clear)
            {
                registry.ClearAvailable();
            }

            // Check the filetype.
            FileType type = FileIdentifier.IdentifyFile(repo_file);

            switch (type)
            {
            case FileType.TarGz:
                UpdateRegistryFromTarGz(repo_file, registry);
                break;

            case FileType.Zip:
                UpdateRegistryFromZip(repo_file, registry);
                break;

            default:
                break;
            }

            List <CfanModule> metadataChanges = new List <CfanModule>();

            foreach (var identifierModulePair in old_available)
            {
                var identifier = identifierModulePair.Key;

                if (registry.IsInstalled(identifier))
                {
                    AbstractVersion abstractVersion  = registry.InstalledVersion(identifier);
                    var             installedVersion = new ModVersion(abstractVersion.ToString());

                    if (!(registry.available_modules.ContainsKey(identifier)))
                    {
                        log.InfoFormat("UpdateRegistry, module {0}, version {1} not in repository ({2})", identifier, installedVersion, repo);
                        continue;
                    }

                    if (!registry.available_modules[identifier].module_version.ContainsKey(installedVersion))
                    {
                        continue;
                    }

                    // if the mod is installed and the metadata is different we have to reinstall it
                    CfanModule metadata = new CfanModule(registry.available_modules[identifier].module_version[installedVersion]);

                    if (!old_available.ContainsKey(identifier) ||
                        !old_available[identifier].module_version.ContainsKey(installedVersion))
                    {
                        continue;
                    }

                    CfanModule oldMetadata = new CfanModule(old_available[identifier].module_version[installedVersion]);

                    bool same = metadata.kind == oldMetadata.kind;

                    if (!same)
                    {
                        metadataChanges.Add(new CfanModule(registry.available_modules[identifier].module_version[installedVersion]));
                    }
                }
            }

            if (metadataChanges.Any())
            {
                string mods = "";
                for (int i = 0; i < metadataChanges.Count; i++)
                {
                    mods += metadataChanges[i].identifier + " "
                            + metadataChanges[i].modVersion.ToString() + ((i < metadataChanges.Count - 1) ? ", " : "");
                }

                if (user.RaiseYesNoDialog(String.Format(
                                              @"The following mods have had their metadata changed since last update - {0}.
It is advisable that you reinstall them in order to preserve consistency with the repository. Do you wish to reinstall now?", mods)))
                {
                    ModuleInstaller installer = ModuleInstaller.GetInstance(ksp, new NullUser());
                    installer.Upgrade(metadataChanges, new NetAsyncModulesDownloader(new NullUser(), ksp.tryGetFactorioAuthData()));
                }
            }

            // Remove our downloaded meta-data now we've processed it.
            // Seems weird to do this as part of a transaction, but Net.Download uses them, so let's be consistent.
            file_transaction.Delete(repo_file);
        }