Example #1
0
        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;
        }
Example #2
0
        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;
        }
Example #3
0
        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;
        }
Example #4
0
        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;
        }
Example #5
0
        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;
        }
Example #6
0
        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);
                }
            }
        }
Example #7
0
        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);
        }
Example #8
0
        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);
        }