public void Run() { try { DirectoryEx.EnsureExists(this.PackagesBackupPath); IList <PackageEdit> edits; using (var connection = this.PackageDatabase.ConnectTo()) { edits = connection.Query <PackageEdit>(GetEditsBaseSql).ToList(); } Log.Info("Fetched {2} queued edits from {0}/{1}", PackageDatabase.DataSource, PackageDatabase.InitialCatalog, edits.Count); // Group by package and take just the most recent edit for each package edits = edits.GroupBy(e => e.PackageKey) .Select(g => g.OrderByDescending(e => e.Timestamp).FirstOrDefault()) .Where(e => e != null) .ToList(); foreach (var edit in edits) { Exception thrown = null; try { this.ApplyEdit(edit); } catch (Exception ex) { thrown = ex; } if (thrown != null) { using (var connection = this.PackageDatabase.ConnectTo()) { connection.Query <int>(@" UPDATE PackageEdits SET TriedCount = TriedCount + 1, LastError = @error WHERE [Key] = @key", new { error = thrown.ToString(), key = edit.Key }); } } } } finally { DirectoryEx.TryDelete(this.PackagesTempPath); } }
private void MainForm_Load(object sender, EventArgs e) { // Check internet connection if (!Network.InternetIsAvailable) { Environment.ExitCode = 1; Application.Exit(); return; } // Get update infos from GitHub if enabled if (Settings.UpdateChannel == Settings.UpdateChannelOptions.Beta) { if (!NetEx.IPv4IsAvalaible && NetEx.IPv6IsAvalaible) { Environment.ExitCode = 1; Application.Exit(); return; } SetUpdateInfo(false, CorePaths.RepoSnapshotsUrl); } // Get update infos if not already set if (string.IsNullOrWhiteSpace(HashInfo)) { DownloadMirrors.AddRange(new[] { "https://port-a.de", "https://p-able.de", // Backup "https://dl.si13n7.de/Port-Able", "https://dl.si13n7.com/Port-Able", // Reserved "http://dl-0.de/Port-Able", "http://dl-1.de/Port-Able", "http://dl-2.de/Port-Able", "http://dl-3.de/Port-Able", "http://dl-4.de/Port-Able", "http://dl-5.de/Port-Able" }); if (!DownloadMirrors.Any()) { Environment.ExitCode = 1; Application.Exit(); return; } SetUpdateInfo(true, DownloadMirrors.ToArray()); } if (string.IsNullOrWhiteSpace(HashInfo)) { Environment.ExitCode = 1; Application.Exit(); return; } // Compare hashes var updateAvailable = false; try { foreach (var key in Ini.GetKeys("SHA256", HashInfo)) { var file = PathEx.Combine(PathEx.LocalDir, $"{key}.exe"); if (!File.Exists(file)) { file = Path.Combine(CorePaths.HomeDir, $"{key}.exe"); } if (Ini.Read("SHA256", key, HashInfo).EqualsEx(file.EncryptFile(ChecksumAlgorithm.Sha256))) { continue; } updateAvailable = true; break; } } catch (Exception ex) when(ex.IsCaught()) { Log.Write(ex); Environment.ExitCode = 1; Application.Exit(); return; } // Install updates if (updateAvailable) { if (string.IsNullOrEmpty(CorePaths.FileArchiver)) { if (MessageBox.Show(Language.GetText(nameof(en_US.RequirementsErrorMsg)), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes) { Process.Start(CorePaths.RepoReleasesUrl); } Environment.ExitCode = 1; Environment.Exit(Environment.ExitCode); } if (MessageBox.Show(Language.GetText(nameof(en_US.UpdateAvailableMsg)), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes) { SetChangeLog(DownloadMirrors.ToArray()); ShowInTaskbar = true; FormEx.Dockable(this); return; } DirectoryEx.TryDelete(CachePaths.UpdateDir); } // Exit the application if no updates were found Environment.ExitCode = 2; Application.Exit(); }
private static void UpdateAppInfoFile() { ResetAppInfoFile(); if (_appInfo?.Count > 430) { goto Shareware; } foreach (var mirror in AppSupply.GetMirrors(AppSuppliers.Internal)) { var link = PathEx.AltCombine(mirror, ".free", "AppInfo.ini"); if (Log.DebugMode > 0) { Log.Write($"Cache: Looking for '{link}'."); } if (NetEx.FileIsAvailable(link, 30000, UserAgents.Internal)) { NetEx.Transfer.DownloadFile(link, CachePaths.AppInfo, 60000, UserAgents.Internal, false); } if (!File.Exists(CachePaths.AppInfo)) { continue; } break; } var blacklist = Array.Empty <string>(); if (File.Exists(CachePaths.AppInfo)) { blacklist = Ini.GetSections(CachePaths.AppInfo).Where(x => Ini.Read(x, "Disabled", false, CachePaths.AppInfo)).ToArray(); UpdateAppInfoData(CachePaths.AppInfo, blacklist); } var tmpDir = Path.Combine(CorePaths.TempDir, PathEx.GetTempDirName()); if (!DirectoryEx.Create(tmpDir)) { return; } var tmpZip = Path.Combine(tmpDir, "AppInfo.7z"); foreach (var mirror in AppSupply.GetMirrors(AppSuppliers.Internal)) { var link = PathEx.AltCombine(mirror, ".free", "AppInfo.7z"); if (Log.DebugMode > 0) { Log.Write($"Cache: Looking for '{link}'."); } if (NetEx.FileIsAvailable(link, 30000, UserAgents.Internal)) { NetEx.Transfer.DownloadFile(link, tmpZip, 60000, UserAgents.Internal, false); } if (!File.Exists(tmpZip)) { continue; } break; } if (!File.Exists(tmpZip)) { var link = PathEx.AltCombine(AppSupplierHosts.PortableApps, "updater", "update.7z"); if (Log.DebugMode > 0) { Log.Write($"Cache: Looking for '{link}'."); } if (NetEx.FileIsAvailable(link, 60000, UserAgents.Empty)) { NetEx.Transfer.DownloadFile(link, tmpZip, 60000, UserAgents.Empty, false); } } if (File.Exists(tmpZip)) { using (var process = Compaction.SevenZipHelper.Unzip(tmpZip, tmpDir)) if (process?.HasExited == false) { process.WaitForExit(); } FileEx.TryDelete(tmpZip); } var tmpIni = DirectoryEx.GetFiles(tmpDir, "*.ini").FirstOrDefault(); if (!File.Exists(tmpIni)) { DirectoryEx.TryDelete(tmpDir); return; } UpdateAppInfoData(tmpIni, blacklist); FileEx.Serialize(CachePaths.AppInfo, AppInfo, true); DirectoryEx.TryDelete(tmpDir); Shareware: if (!Shareware.Enabled) { return; } foreach (var srv in Shareware.GetAddresses()) { var key = Shareware.FindAddressKey(srv); var usr = Shareware.GetUser(srv); var pwd = Shareware.GetPassword(srv); var url = PathEx.AltCombine(srv, "AppInfo.ini"); if (Log.DebugMode > 0) { Log.Write($"Shareware: Looking for '{{{key.Encode()}}}/AppInfo.ini'."); } if (!NetEx.FileIsAvailable(url, usr, pwd, 60000, UserAgents.Default)) { continue; } var appInfo = NetEx.Transfer.DownloadString(url, usr, pwd, 60000, UserAgents.Default); if (string.IsNullOrWhiteSpace(appInfo)) { continue; } UpdateAppInfoData(appInfo, null, key.Decode(BinaryToTextEncodings.Base85)); } }
public static void Find(out string javaPath) { // Read saved path from config. javaPath = null; if (File.Exists(Attributes.ConfigPath)) { javaPath = Ini.ReadDirect("Java", "Path", Attributes.ConfigPath); if (!string.IsNullOrWhiteSpace(javaPath)) { javaPath = PathEx.Combine(javaPath); } if (File.Exists(javaPath)) { goto Found; } } // Try getting the default path from Portable Apps Suite JRE installation. try { var dir = EnvironmentEx.GetVariableValue("AppsSuiteDir"); if (Directory.Exists(dir)) { #if x86 dir = Path.Combine(dir, "Apps", "CommonFiles", "Java"); #else dir = Path.Combine(dir, "Apps", "CommonFiles", "Java64"); if (!Directory.Exists(dir)) { Path.Combine(dir, "Apps", "CommonFiles", "Java"); } #endif if (Directory.Exists(dir)) { javaPath = GetPath(dir); if (File.Exists(javaPath)) { goto Found; } } } } catch (Exception ex) { Log.Write(ex); } // Start searching for the portable JRE, starting in the current directory. try { var dir = PathEx.LocalDir; var drive = new DriveInfo(dir).RootDirectory.Root.Name; int count = 0, length = dir.Split(Path.DirectorySeparatorChar).Length; var subs = new[] { #if x64 "_CommonFiles\\Java64", "CommonFiles\\Java64", #endif "_CommonFiles\\Java", "CommonFiles\\Java" }; while (!drive.ContainsEx(dir) && ++count < length) { foreach (var sub in subs) { var tmp = Path.Combine(dir, sub); if (!Directory.Exists(tmp)) { continue; } javaPath = GetPath(tmp); if (File.Exists(javaPath)) { goto Found; } } dir = PathEx.Combine(dir, "..").TrimEnd(Path.DirectorySeparatorChar); } } catch (Exception ex) { Log.Write(ex); } // Try getting the default path from the global JRE installation. try { var dirs = new[] { "%ProgramFiles%\\Java", #if x64 "%ProgramFiles(x86)%\\Java", #endif "%ProgramData%\\Oracle\\Java\\javapath" }; foreach (var dir in dirs.Select(PathEx.Combine)) { if (!Directory.Exists(dir)) { continue; } javaPath = GetPath(dir); if (File.Exists(javaPath)) { goto Found; } } } catch (Exception ex) { Log.Write(ex); } // If JRE was not found. MessageBox.Show(Strings.JavaWarnMessage, AssemblyInfo.Title, MessageBoxButtons.OK, MessageBoxIcon.Error); Environment.ExitCode = 1; Environment.Exit(Environment.ExitCode); // Else Found: var envPath = EnvironmentEx.GetVariablePathFull(javaPath); Ini.WriteDirect(nameof(JavaHandler), "Path", envPath, Attributes.ConfigPath); var usageDir = PathEx.Combine("%UserProfile%\\.oracle_jre_usage"); try { if (!Directory.Exists(usageDir)) { Directory.CreateDirectory(usageDir); } DirectoryEx.SetAttributes(usageDir, FileAttributes.Hidden); } catch (Exception ex) { Log.Write(ex); } AppDomain.CurrentDomain.ProcessExit += (s, e) => DirectoryEx.TryDelete(usageDir); }
private void ApplyEdit(PackageEdit edit) { // copy the original file string packageName = $"{edit.Id}.{edit.Version}.nupkg".ToLower(CultureInfo.InvariantCulture); string originalPath = Path.Combine(this.PackagesPath, packageName); string backupPath = Path.Combine(this.PackagesBackupPath, packageName); var tempDir = Path.Combine(this.PackagesTempPath, "HandlePackageEdits"); string directory = Path.Combine(tempDir, edit.Id, edit.Version); string tempPath = Path.Combine(directory, packageName); try { if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } Log.Info("Downloaded original copy of {0} {1}", edit.Id, edit.Version); File.Copy(originalPath, tempPath, true); // Load the zip file and find the manifest using (var originalStream = File.Open(tempPath, FileMode.Open, FileAccess.ReadWrite)) using (var archive = new ZipArchive(originalStream, ZipArchiveMode.Update)) { // Find the nuspec var nuspecEntries = archive.Entries.Where(e => ManifestSelector.IsMatch(e.FullName)).ToArray(); if (nuspecEntries.Length == 0) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, "Package has no manifest: {0} {1} (URL: {2})", edit.Id, edit.Version, tempPath)); } if (nuspecEntries.Length > 1) { throw new InvalidDataException( string.Format( CultureInfo.CurrentCulture, "Package has multiple manifests: {0} {1} (URL: {2})", edit.Id, edit.Version, tempPath)); } // We now have the nuspec var manifestEntry = nuspecEntries.Single(); // Load the manifest with a constrained stream Log.Info("Rewriting package file for {0} {1}", edit.Id, edit.Version); using (var manifestStream = manifestEntry.Open()) { var manifest = Manifest.ReadFrom(manifestStream, validateSchema: false); // Modify the manifest as per the edit edit.ApplyTo(manifest.Metadata); // Save the manifest back manifestStream.Seek(0, SeekOrigin.Begin); manifestStream.SetLength(0); manifest.Save(manifestStream); } Log.Info("Rewrote package file for {0} {1}", edit.Id, edit.Version); } // replace original file and back it up Log.Info("Replacing original package file for {0} {1} ({2}, backup location {3}).", edit.Id, edit.Version, originalPath, backupPath); File.Replace(tempPath, originalPath, backupPath); // Calculate new size and hash string hash; long size; using (var originalStream = File.OpenRead(originalPath)) { size = originalStream.Length; var hashAlgorithm = HashAlgorithm.Create(HashAlgorithmName); if (hashAlgorithm == null) { throw new InvalidOperationException($"Failed to create instance of hash algorithm {HashAlgorithmName}."); } hash = Convert.ToBase64String(hashAlgorithm.ComputeHash(originalStream)); } // Update the database Log.Info("Updating package record for {0} {1}", edit.Id, edit.Version); using (var connection = this.PackageDatabase.ConnectTo()) { var parameters = new DynamicParameters( new { edit.Authors, edit.Copyright, edit.Description, edit.IconUrl, edit.LicenseUrl, edit.ProjectUrl, edit.ReleaseNotes, edit.RequiresLicenseAcceptance, edit.Summary, edit.Title, edit.Tags, edit.Key, edit.PackageKey, edit.UserKey, PackageFileSize = size, Hash = hash, HashAlgorithm = HashAlgorithmName }); // Prep SQL for merging in authors StringBuilder loadAuthorsSql = new StringBuilder(); var authors = edit.Authors.Split(','); for (int i = 0; i < authors.Length; i++) { loadAuthorsSql.Append("INSERT INTO [PackageAuthors]([PackageKey],[Name]) VALUES(@PackageKey, @Author" + i + ")"); parameters.Add("Author" + i, authors[i]); } connection.Query <int>(@" BEGIN TRANSACTION -- Form a comma-separated list of authors DECLARE @existingAuthors nvarchar(MAX) SELECT @existingAuthors = COALESCE(@existingAuthors + ',', '') + Name FROM PackageAuthors WHERE PackageKey = @PackageKey -- Copy packages data to package history table INSERT INTO [PackageHistories] SELECT [Key] AS PackageKey, @UserKey AS UserKey, GETUTCDATE() AS Timestamp, Title, @existingAuthors AS Authors, Copyright, Description, IconUrl, LicenseUrl, ProjectUrl, ReleaseNotes, RequiresLicenseAcceptance, Summary, Tags, Hash, HashAlgorithm, PackageFileSize, LastUpdated, Published FROM [Packages] WHERE [Key] = @PackageKey -- Update the packages table UPDATE [Packages] SET Copyright = @Copyright, Description = @Description, IconUrl = @IconUrl, LicenseUrl = @LicenseUrl, ProjectUrl = @ProjectUrl, ReleaseNotes = @ReleaseNotes, RequiresLicenseAcceptance = @RequiresLicenseAcceptance, Summary = @Summary, Title = @Title, Tags = @Tags, LastEdited = GETUTCDATE(), LastUpdated = GETUTCDATE(), UserKey = @UserKey, Hash = @Hash, HashAlgorithm = @HashAlgorithm, PackageFileSize = @PackageFileSize, FlattenedAuthors = @Authors WHERE [Key] = @PackageKey -- Update Authors DELETE FROM [PackageAuthors] WHERE PackageKey = @PackageKey " + loadAuthorsSql + @" -- Clean this edit and all previous edits. DELETE FROM [PackageEdits] WHERE [PackageKey] = @PackageKey AND [Key] <= @Key COMMIT TRANSACTION", parameters); } Log.Info("Updated package record for {0} {1}", edit.Id, edit.Version); } catch (Exception ex) { Log.Error("Failed to update package information. Package {0} {1}. Exception {2}", edit.Id, edit.Version, ex.Message); throw; } finally { DirectoryEx.TryDelete(directory); } }