static UpdaterManager() { string installdir = null; var attempts = new string[] { System.IO.Path.Combine(InstalledBaseDir, "updates"), System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), APPNAME, "updates"), System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APPNAME, "updates"), System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates"), }; foreach(var p in attempts) if (TestDirectoryIsWriteable(p)) { installdir = p; break; } INSTALLDIR = installdir; if (INSTALLDIR != null) { if (!System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, README_FILE))) System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, README_FILE), AutoUpdateSettings.UpdateFolderReadme); if (!System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE))) System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE), AutoUpdateSettings.UpdateInstallFileText); } UpdateInfo selfVersion = null; try { selfVersion = ReadInstalledManifest(System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location)); } catch { } if (selfVersion == null) selfVersion = new UpdateInfo() { Displayname = "Current", Version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), ReleaseTime = new DateTime(0), ReleaseType = #if DEBUG "Debug" #else "Nightly" #endif }; SelfVersion = selfVersion; }
public static bool DownloadAndUnpackUpdate(UpdateInfo version, Action<double> progress = null) { if (INSTALLDIR == null) return false; var updates = version.RemoteURLS.ToList(); // If alternate update URLs are specified, // we look for packages there as well if (AutoUpdateSettings.UsesAlternateURLs) { var packagepath = new Library.Utility.Uri(updates[0]).Path; var packagename = packagepath.Split('/').Last(); foreach(var alt_url in AutoUpdateSettings.URLs.Reverse()) { var alt_uri = new Library.Utility.Uri(alt_url); var path_components = alt_uri.Path.Split('/'); var path = string.Join("/", path_components.Take(path_components.Count() - 1).Union(new string[] { packagename})); var new_path = alt_uri.SetPath(path); updates.Insert(0, new_path.ToString()); } } using(var tempfile = new Library.Utility.TempFile()) { foreach(var url in updates) { try { Action<long> cb = null; if (progress != null) cb = (s) => { progress(Math.Min(1.0, Math.Max(0.0, (double)s / version.CompressedSize))); }; var wreq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url); wreq.UserAgent = string.Format("{0} v{1}", APPNAME, SelfVersion.Version); wreq.Headers.Add("X-Install-ID", InstallID); using(var resp = wreq.GetResponse()) using(var rss = resp.GetResponseStream()) using(var pgs = new Duplicati.Library.Utility.ProgressReportingStream(rss, version.CompressedSize, cb)) using(var fs = System.IO.File.Open(tempfile, System.IO.FileMode.Create)) Duplicati.Library.Utility.Utility.CopyStream(pgs, fs); var sha256 = System.Security.Cryptography.SHA256.Create(); var md5 = System.Security.Cryptography.MD5.Create(); using(var s = System.IO.File.OpenRead(tempfile)) { if (s.Length != version.CompressedSize) throw new Exception(string.Format("Invalid file size {0}, expected {1} for {2}", s.Length, version.CompressedSize, url)); var sha256hash = Convert.ToBase64String(sha256.ComputeHash(s)); if (sha256hash != version.SHA256) throw new Exception(string.Format("Damaged or corrupted file, sha256 mismatch for {0}", url)); } using(var s = System.IO.File.OpenRead(tempfile)) { var md5hash = Convert.ToBase64String(md5.ComputeHash(s)); if (md5hash != version.MD5) throw new Exception(string.Format("Damaged or corrupted file, md5 mismatch for {0}", url)); } using(var tempfolder = new Duplicati.Library.Utility.TempFolder()) using(var zip = new Duplicati.Library.Compression.FileArchiveZip(tempfile, new Dictionary<string, string>())) { foreach(var file in zip.ListFilesWithSize("")) { if (System.IO.Path.IsPathRooted(file.Key) || file.Key.Trim().StartsWith("..", StringComparison.InvariantCultureIgnoreCase)) throw new Exception(string.Format("Out-of-place file path detected: {0}", file.Key)); var targetpath = System.IO.Path.Combine(tempfolder, file.Key); var targetfolder = System.IO.Path.GetDirectoryName(targetpath); if (!System.IO.Directory.Exists(targetfolder)) System.IO.Directory.CreateDirectory(targetfolder); using(var zs = zip.OpenRead(file.Key)) using(var fs = System.IO.File.Create(targetpath)) zs.CopyTo(fs); } if (VerifyUnpackedFolder(tempfolder, version)) { var versionstring = TryParseVersion(version.Version).ToString(); var targetfolder = System.IO.Path.Combine(INSTALLDIR, versionstring); if (System.IO.Directory.Exists(targetfolder)) System.IO.Directory.Delete(targetfolder, true); System.IO.Directory.CreateDirectory(targetfolder); var tempfolderpath = Duplicati.Library.Utility.Utility.AppendDirSeparator(tempfolder); var tempfolderlength = tempfolderpath.Length; // Would be nice, but does not work :( //System.IO.Directory.Move(tempfolder, targetfolder); foreach(var e in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(tempfolder)) { var relpath = e.Substring(tempfolderlength); if (string.IsNullOrWhiteSpace(relpath)) continue; var fullpath = System.IO.Path.Combine(targetfolder, relpath); if (relpath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) System.IO.Directory.CreateDirectory(fullpath); else System.IO.File.Copy(e, fullpath); } // Verification will kick in when we list the installed updates //VerifyUnpackedFolder(targetfolder, version); System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, CURRENT_FILE), versionstring); m_hasUpdateInstalled = null; var obsolete = (from n in FindInstalledVersions() where n.Value.Version != version.Version && n.Value.Version != SelfVersion.Version let x = TryParseVersion(n.Value.Version) orderby x descending select n).Skip(1).ToArray(); foreach(var f in obsolete) try { System.IO.Directory.Delete(f.Key, true); } catch { } return true; } else { throw new Exception(string.Format("Unable to verify unpacked folder for url: {0}", url)); } } } catch(Exception ex) { if (OnError != null) OnError(ex); } } } return false; }
public static bool VerifyUnpackedFolder(string folder, UpdateInfo version = null) { try { UpdateInfo update; FileEntry manifest; var sha256 = System.Security.Cryptography.SHA256.Create(); var md5 = System.Security.Cryptography.MD5.Create(); using(var fs = System.IO.File.OpenRead(System.IO.Path.Combine(folder, UPDATE_MANIFEST_FILENAME))) { using(var ss = new SignatureReadingStream(fs, SIGN_KEY)) using(var tr = new System.IO.StreamReader(ss)) using(var jr = new Newtonsoft.Json.JsonTextReader(tr)) update = new Newtonsoft.Json.JsonSerializer().Deserialize<UpdateInfo>(jr); sha256.Initialize(); md5.Initialize(); fs.Position = 0; var h1 = Convert.ToBase64String(sha256.ComputeHash(fs)); fs.Position = 0; var h2 = Convert.ToBase64String(md5.ComputeHash(fs)); manifest = new FileEntry() { Path = UPDATE_MANIFEST_FILENAME, Ignore = false, LastWriteTime = update.ReleaseTime, SHA256 = h1, MD5 = h2 }; } if (version != null && (update.Displayname != version.Displayname || update.ReleaseTime != version.ReleaseTime)) throw new Exception("The found version was not the expected version"); var paths = update.Files.Where(x => !x.Ignore).ToDictionary(x => x.Path.Replace('/', System.IO.Path.DirectorySeparatorChar), Library.Utility.Utility.ClientFilenameStringComparer); paths.Add(manifest.Path, manifest); var ignores = (from x in update.Files where x.Ignore select Library.Utility.Utility.AppendDirSeparator(x.Path.Replace('/', System.IO.Path.DirectorySeparatorChar))).ToList(); folder = Library.Utility.Utility.AppendDirSeparator(folder); var baselen = folder.Length; foreach(var file in Library.Utility.Utility.EnumerateFileSystemEntries(folder)) { var relpath = file.Substring(baselen); if (string.IsNullOrWhiteSpace(relpath)) continue; FileEntry fe; if (!paths.TryGetValue(relpath, out fe)) { var ignore = false; foreach(var c in ignores) if (ignore = relpath.StartsWith(c)) break; if (ignore) continue; throw new Exception(string.Format("Found unexpected file: {0}", file)); } paths.Remove(relpath); if (fe.Path.EndsWith("/")) continue; sha256.Initialize(); md5.Initialize(); using(var fs = System.IO.File.OpenRead(file)) { if (Convert.ToBase64String(sha256.ComputeHash(fs)) != fe.SHA256) throw new Exception(string.Format("Invalid sha256 hash for file: {0}", file)); fs.Position = 0; if (Convert.ToBase64String(md5.ComputeHash(fs)) != fe.MD5) throw new Exception(string.Format("Invalid md5 hash for file: {0}", file)); } } var filteredpaths = (from p in paths where !string.IsNullOrWhiteSpace(p.Key) && !p.Key.EndsWith("/") select p.Key).ToList(); if (filteredpaths.Count == 1) throw new Exception(string.Format("Folder {0} is missing: {1}", folder, filteredpaths.First())); else if (filteredpaths.Count > 0) throw new Exception(string.Format("Folder {0} is missing {1} and {2} other file(s)", folder, filteredpaths.First(), filteredpaths.Count - 1)); return true; } catch (Exception ex) { if (OnError != null) OnError(ex); } return false; }
static UpdaterManager() { // Update folder strategy is a bit complicated, // because it depends on the actual system, // and because it tries to find a good spot // by probing for locations // The "overrides" paths are checked, // to see if they exist and are writeable. // The first existing and writeable path // for "overrides" is chosen // If override was not found, the "legacypaths" // are checked in the same way to see if // we have previously used such a folder // and if that folder has contents, // which indicates that it has been used. // Finally we check the "attempts", // which are suitable candidates // for storing the updates on each // operating system if (string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(string.Format(UPDATEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME)))) { string installdir = null; var programfiles = System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); // The user can override updates by having a local updates folder var overrides = new List<string>(new string [] { System.IO.Path.Combine(InstalledBaseDir, "updates"), }); if (Library.Utility.Utility.IsClientWindows) { overrides.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APPNAME, "updates")); overrides.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates")); } else { if (Library.Utility.Utility.IsClientOSX) overrides.Add(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support", APPNAME, "updates")); overrides.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates")); } // Previous locations that we don't want to use, // but we keep them active to avoid breaking the update syste var legacypaths = new List<string>(); if (!string.IsNullOrWhiteSpace(programfiles)) legacypaths.Add(System.IO.Path.Combine(programfiles, APPNAME, "updates")); if (Library.Utility.Utility.IsClientLinux) legacypaths.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APPNAME, "updates")); // The real attempts that we probe for var attempts = new List<string>(); // We do not want to install anything in the basedir, if the application is installed in "ProgramFiles" if (!string.IsNullOrWhiteSpace(programfiles) && !InstalledBaseDir.StartsWith(Library.Utility.Utility.AppendDirSeparator(programfiles))) attempts.Add(System.IO.Path.Combine(InstalledBaseDir, "updates")); if (Library.Utility.Utility.IsClientOSX) attempts.Add(System.IO.Path.Combine("/", "Library", "Application Support", APPNAME, "updates")); else attempts.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), APPNAME, "updates")); attempts.AddRange(overrides.Skip(1)); // Check if the override folder exists, and choose that foreach (var p in overrides) if (!string.IsNullOrWhiteSpace(p) && System.IO.Directory.Exists(p) && TestDirectoryIsWriteable(p)) { installdir = p; break; } if (string.IsNullOrWhiteSpace(installdir)) foreach (var p in legacypaths) if (!string.IsNullOrWhiteSpace(p) && System.IO.Directory.Exists(p) && System.IO.Directory.EnumerateFiles(p, "*", System.IO.SearchOption.TopDirectoryOnly).Count() > 0 && TestDirectoryIsWriteable(p)) { installdir = p; break; } if (string.IsNullOrWhiteSpace(installdir)) foreach (var p in attempts) if (!string.IsNullOrWhiteSpace(p) && TestDirectoryIsWriteable(p)) { installdir = p; break; } INSTALLDIR = installdir; } else { INSTALLDIR = Library.Utility.Utility.ExpandEnvironmentVariables(System.Environment.GetEnvironmentVariable(string.Format(UPDATEINSTALLDIR_ENVNAME_TEMPLATE, APPNAME))); } if (INSTALLDIR != null) { if (!System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, README_FILE))) System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, README_FILE), AutoUpdateSettings.UpdateFolderReadme); if (!System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE))) System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE), AutoUpdateSettings.UpdateInstallFileText); } UpdateInfo selfVersion = null; UpdateInfo baseVersion = null; try { selfVersion = ReadInstalledManifest(System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location)); } catch { } try { baseVersion = ReadInstalledManifest(InstalledBaseDir); } catch { } if (selfVersion == null) { selfVersion = new UpdateInfo() { Displayname = string.IsNullOrWhiteSpace(Duplicati.License.VersionNumbers.TAG) ? "Current" : Duplicati.License.VersionNumbers.TAG, Version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), ReleaseTime = new DateTime(0), ReleaseType = #if DEBUG "Debug" #else string.IsNullOrWhiteSpace(AutoUpdateSettings.BuildUpdateChannel) ? "Nightly" : AutoUpdateSettings.BuildUpdateChannel #endif }; } if (baseVersion == null) baseVersion = selfVersion; SelfVersion = selfVersion; BaseVersion = baseVersion; }
static UpdaterManager() { string installdir = null; var attempts = new string[] { System.IO.Path.Combine(InstalledBaseDir, "updates"), // Not defined on Non-Windows string.IsNullOrWhiteSpace(System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)) ? null : System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), APPNAME, "updates"), System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APPNAME, "updates"), System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates"), }; foreach (var p in attempts) { if (!string.IsNullOrWhiteSpace(p) && TestDirectoryIsWriteable(p)) { installdir = p; break; } } INSTALLDIR = installdir; if (INSTALLDIR != null) { if (!System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, README_FILE))) { System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, README_FILE), AutoUpdateSettings.UpdateFolderReadme); } if (!System.IO.File.Exists(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE))) { System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, INSTALL_FILE), AutoUpdateSettings.UpdateInstallFileText); } } UpdateInfo selfVersion = null; try { selfVersion = ReadInstalledManifest(System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location)); } catch { } if (selfVersion == null) { selfVersion = new UpdateInfo() { Displayname = "Current", Version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), ReleaseTime = new DateTime(0), ReleaseType = #if DEBUG "Debug" #else "Nightly" #endif } } ; SelfVersion = selfVersion; }
private static void WrapWithUpdater(AutoUpdateStrategy defaultstrategy, Action wrappedFunction) { string optstr = Environment.GetEnvironmentVariable(string.Format(UPDATE_STRATEGY_ENVNAME_TEMPLATE, APPNAME)); AutoUpdateStrategy strategy; if (string.IsNullOrWhiteSpace(optstr) || !Enum.TryParse(optstr, out strategy)) { strategy = defaultstrategy; } System.Threading.Thread backgroundChecker = null; UpdateInfo updateDetected = null; bool updateInstalled = false; bool checkForUpdate; bool downloadUpdate; bool runAfter; bool runDuring; bool runBefore; switch (strategy) { case AutoUpdateStrategy.CheckBefore: case AutoUpdateStrategy.CheckDuring: case AutoUpdateStrategy.CheckAfter: checkForUpdate = true; downloadUpdate = false; break; case AutoUpdateStrategy.InstallBefore: case AutoUpdateStrategy.InstallDuring: case AutoUpdateStrategy.InstallAfter: checkForUpdate = true; downloadUpdate = true; break; default: checkForUpdate = false; downloadUpdate = false; break; } switch (strategy) { case AutoUpdateStrategy.CheckBefore: case AutoUpdateStrategy.InstallBefore: runBefore = true; runDuring = false; runAfter = false; break; case AutoUpdateStrategy.CheckAfter: case AutoUpdateStrategy.InstallAfter: runBefore = false; runDuring = false; runAfter = true; break; case AutoUpdateStrategy.CheckDuring: case AutoUpdateStrategy.InstallDuring: runBefore = false; runDuring = true; runAfter = false; break; default: runBefore = false; runDuring = false; runAfter = false; break; } if (checkForUpdate) { backgroundChecker = new System.Threading.Thread(() => { // Don't run "during" if the task is short if (runDuring) { System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10)); } updateDetected = CheckForUpdate(); if (updateDetected != null && downloadUpdate) { if (!runDuring) { Console.WriteLine("Update to {0} detected, installing...", updateDetected.Displayname); } updateInstalled = DownloadAndUnpackUpdate(updateDetected); } }); backgroundChecker.IsBackground = true; backgroundChecker.Name = "BackgroundUpdateChecker"; if (!runAfter) { backgroundChecker.Start(); } if (runBefore) { Console.WriteLine("Checking for update ..."); backgroundChecker.Join(); if (downloadUpdate) { if (updateInstalled) { Console.WriteLine("Install succeeded, running updated version"); } else { Console.WriteLine("Install or download failed, using current version"); } } else if (updateDetected != null) { Console.WriteLine("Update \"{0}\" detected", updateDetected.Displayname); } backgroundChecker = null; } } wrappedFunction(); if (backgroundChecker != null && runAfter) { Console.WriteLine("Checking for update ..."); backgroundChecker.Start(); backgroundChecker.Join(); } if (backgroundChecker != null && updateDetected != null) { if (backgroundChecker.IsAlive) { Console.WriteLine("Waiting for update \"{0}\" to complete", updateDetected.Displayname); backgroundChecker.Join(); } if (downloadUpdate) { if (updateInstalled) { Console.WriteLine("Install succeeded, running updated version on next launch"); } else { Console.WriteLine("Install or download failed, using current version on next launch"); } } else { Console.WriteLine("Update \"{0}\" detected", updateDetected.Displayname); } } }
public static bool VerifyUnpackedFolder(string folder, UpdateInfo version = null) { try { UpdateInfo update; FileEntry manifest; var sha256 = System.Security.Cryptography.SHA256.Create(); var md5 = System.Security.Cryptography.MD5.Create(); using (var fs = System.IO.File.OpenRead(System.IO.Path.Combine(folder, UPDATE_MANIFEST_FILENAME))) { using (var ss = new SignatureReadingStream(fs, SIGN_KEY)) using (var tr = new System.IO.StreamReader(ss)) using (var jr = new Newtonsoft.Json.JsonTextReader(tr)) update = new Newtonsoft.Json.JsonSerializer().Deserialize <UpdateInfo>(jr); sha256.Initialize(); md5.Initialize(); fs.Position = 0; var h1 = Convert.ToBase64String(sha256.ComputeHash(fs)); fs.Position = 0; var h2 = Convert.ToBase64String(md5.ComputeHash(fs)); manifest = new FileEntry() { Path = UPDATE_MANIFEST_FILENAME, Ignore = false, LastWriteTime = update.ReleaseTime, SHA256 = h1, MD5 = h2 }; } if (version != null && (update.Displayname != version.Displayname || update.ReleaseTime != version.ReleaseTime)) { throw new Exception("The found version was not the expected version"); } var paths = update.Files.Where(x => !x.Ignore).ToDictionary(x => x.Path.Replace('/', System.IO.Path.DirectorySeparatorChar), Library.Utility.Utility.ClientFilenameStringComparer); paths.Add(manifest.Path, manifest); var ignores = (from x in update.Files where x.Ignore select Library.Utility.Utility.AppendDirSeparator(x.Path.Replace('/', System.IO.Path.DirectorySeparatorChar))).ToList(); folder = Library.Utility.Utility.AppendDirSeparator(folder); var baselen = folder.Length; foreach (var file in Library.Utility.Utility.EnumerateFileSystemEntries(folder)) { var relpath = file.Substring(baselen); if (string.IsNullOrWhiteSpace(relpath)) { continue; } FileEntry fe; if (!paths.TryGetValue(relpath, out fe)) { var ignore = false; foreach (var c in ignores) { if (ignore = relpath.StartsWith(c)) { break; } } if (ignore) { continue; } throw new Exception(string.Format("Found unexpected file: {0}", file)); } paths.Remove(relpath); if (fe.Path.EndsWith("/")) { continue; } sha256.Initialize(); md5.Initialize(); using (var fs = System.IO.File.OpenRead(file)) { if (Convert.ToBase64String(sha256.ComputeHash(fs)) != fe.SHA256) { throw new Exception(string.Format("Invalid sha256 hash for file: {0}", file)); } fs.Position = 0; if (Convert.ToBase64String(md5.ComputeHash(fs)) != fe.MD5) { throw new Exception(string.Format("Invalid md5 hash for file: {0}", file)); } } } var filteredpaths = (from p in paths where !string.IsNullOrWhiteSpace(p.Key) && !p.Key.EndsWith("/") select p.Key).ToList(); if (filteredpaths.Count == 1) { throw new Exception(string.Format("Folder {0} is missing: {1}", folder, filteredpaths.First())); } else if (filteredpaths.Count > 0) { throw new Exception(string.Format("Folder {0} is missing {1} and {2} other file(s)", folder, filteredpaths.First(), filteredpaths.Count - 1)); } return(true); } catch (Exception ex) { if (OnError != null) { OnError(ex); } } return(false); }
public static bool DownloadAndUnpackUpdate(UpdateInfo version, Action <double> progress = null) { if (INSTALLDIR == null) { return(false); } var updates = version.RemoteURLS.ToList(); // If alternate update URLs are specified, // we look for packages there as well if (AutoUpdateSettings.UsesAlternateURLs) { var packagepath = new Library.Utility.Uri(updates[0]).Path; var packagename = packagepath.Split('/').Last(); foreach (var alt_url in AutoUpdateSettings.URLs.Reverse()) { var alt_uri = new Library.Utility.Uri(alt_url); var path_components = alt_uri.Path.Split('/'); var path = string.Join("/", path_components.Take(path_components.Count() - 1).Union(new string[] { packagename })); var new_path = alt_uri.SetPath(path); updates.Insert(0, new_path.ToString()); } } using (var tempfile = new Library.Utility.TempFile()) { foreach (var url in updates) { try { Action <long> cb = null; if (progress != null) { cb = (s) => { progress(Math.Min(1.0, Math.Max(0.0, (double)s / version.CompressedSize))); } } ; var wreq = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url); wreq.UserAgent = string.Format("{0} v{1}", APPNAME, SelfVersion.Version); wreq.Headers.Add("X-Install-ID", InstallID); using (var resp = wreq.GetResponse()) using (var rss = resp.GetResponseStream()) using (var pgs = new Duplicati.Library.Utility.ProgressReportingStream(rss, version.CompressedSize, cb)) using (var fs = System.IO.File.Open(tempfile, System.IO.FileMode.Create)) Duplicati.Library.Utility.Utility.CopyStream(pgs, fs); var sha256 = System.Security.Cryptography.SHA256.Create(); var md5 = System.Security.Cryptography.MD5.Create(); using (var s = System.IO.File.OpenRead(tempfile)) { if (s.Length != version.CompressedSize) { throw new Exception(string.Format("Invalid file size {0}, expected {1} for {2}", s.Length, version.CompressedSize, url)); } var sha256hash = Convert.ToBase64String(sha256.ComputeHash(s)); if (sha256hash != version.SHA256) { throw new Exception(string.Format("Damaged or corrupted file, sha256 mismatch for {0}", url)); } } using (var s = System.IO.File.OpenRead(tempfile)) { var md5hash = Convert.ToBase64String(md5.ComputeHash(s)); if (md5hash != version.MD5) { throw new Exception(string.Format("Damaged or corrupted file, md5 mismatch for {0}", url)); } } using (var tempfolder = new Duplicati.Library.Utility.TempFolder()) using (var zip = new Duplicati.Library.Compression.FileArchiveZip(tempfile, new Dictionary <string, string>())) { foreach (var file in zip.ListFilesWithSize("")) { if (System.IO.Path.IsPathRooted(file.Key) || file.Key.Trim().StartsWith("..", StringComparison.InvariantCultureIgnoreCase)) { throw new Exception(string.Format("Out-of-place file path detected: {0}", file.Key)); } var targetpath = System.IO.Path.Combine(tempfolder, file.Key); var targetfolder = System.IO.Path.GetDirectoryName(targetpath); if (!System.IO.Directory.Exists(targetfolder)) { System.IO.Directory.CreateDirectory(targetfolder); } using (var zs = zip.OpenRead(file.Key)) using (var fs = System.IO.File.Create(targetpath)) zs.CopyTo(fs); } if (VerifyUnpackedFolder(tempfolder, version)) { var versionstring = TryParseVersion(version.Version).ToString(); var targetfolder = System.IO.Path.Combine(INSTALLDIR, versionstring); if (System.IO.Directory.Exists(targetfolder)) { System.IO.Directory.Delete(targetfolder, true); } System.IO.Directory.CreateDirectory(targetfolder); var tempfolderpath = Duplicati.Library.Utility.Utility.AppendDirSeparator(tempfolder); var tempfolderlength = tempfolderpath.Length; // Would be nice, but does not work :( //System.IO.Directory.Move(tempfolder, targetfolder); foreach (var e in Duplicati.Library.Utility.Utility.EnumerateFileSystemEntries(tempfolder)) { var relpath = e.Substring(tempfolderlength); if (string.IsNullOrWhiteSpace(relpath)) { continue; } var fullpath = System.IO.Path.Combine(targetfolder, relpath); if (relpath.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) { System.IO.Directory.CreateDirectory(fullpath); } else { System.IO.File.Copy(e, fullpath); } } // Verification will kick in when we list the installed updates //VerifyUnpackedFolder(targetfolder, version); System.IO.File.WriteAllText(System.IO.Path.Combine(INSTALLDIR, CURRENT_FILE), versionstring); m_hasUpdateInstalled = null; var obsolete = (from n in FindInstalledVersions() where n.Value.Version != version.Version && n.Value.Version != SelfVersion.Version let x = TryParseVersion(n.Value.Version) orderby x descending select n).Skip(1).ToArray(); foreach (var f in obsolete) { try { System.IO.Directory.Delete(f.Key, true); } catch { } } return(true); } else { throw new Exception(string.Format("Unable to verify unpacked folder for url: {0}", url)); } } } catch (Exception ex) { if (OnError != null) { OnError(ex); } } } } return(false); }