/// <summary> /// Installs the given pending update. /// </summary> /// <param name="pendingUpdate">The update to install.</param> /// <param name="downloadProgress">The action to callback with download progress updates.</param> /// <param name="installerStarted">The action called when the installer has started.</param> public void InstallPendingUpdate(PendingUpdate pendingUpdate, Action<double> downloadProgress, Action installerStarted) { log.Info("Installing update: " + pendingUpdate); if (pendingUpdate == null) { throw new ArgumentNullException("pendingUpdate", "No update pending"); } var currentProcess = Process.GetCurrentProcess(); var tempDirectory = string.Format(CultureInfo.InvariantCulture, "{0}-{1}-update", currentProcess.ProcessName, currentProcess.Id); var workingDirectory = Path.GetTempPath().PathCombine(tempDirectory); var installFile = pendingUpdate.InstallFileName == null ? "AutoUpdate.exe" : pendingUpdate.InstallFileName; var installPath = workingDirectory.PathCombine(installFile); var signatureUri = new Uri(pendingUpdate.UpdateFileUri.ToString() + ".signature"); log.Debug(string.Format(CultureInfo.InvariantCulture, "Working directory:{0} install path:{1}", workingDirectory, installPath)); Directory.CreateDirectory(workingDirectory); // Allow other processes to read the temp file but not write it using (var fileStream = new FileStream(installPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var buffer = new byte[0x10000]; var webClient = new WebClient(); // TODO: progress updates // Download the install to the file log.Info("Downloading install file: " + pendingUpdate.UpdateFileUri); using (var inputStream = webClient.OpenRead(pendingUpdate.UpdateFileUri)) { long length; bool hasLength = webClient.TryGetContentLength(out length); long totalRead = 0; var bytesRead = inputStream.Read(buffer, 0, buffer.Length); while (bytesRead > 0) { totalRead += bytesRead; UpdateProgress(downloadProgress, hasLength, totalRead, length); fileStream.Write(buffer, 0, bytesRead); bytesRead = inputStream.Read(buffer, 0, buffer.Length); } } fileStream.Flush(); // Verify the download log.Info("Verifying install file: " + installPath); var signature = webClient.DownloadData(signatureUri); if (!UpdateSettings.UpdateKeys.IsValidSignature(installPath, signature)) { log.Warn("Invalid signature detected on install file."); InvalidSignatureDetected(this, new InvalidSignatureEventArgs()); return; } } var psi = new ProcessStartInfo() { CreateNoWindow = false, FileName = installPath, UseShellExecute = false }; // Run the installer // NOTE: There is a possible race condition here if another user somehow manages to write to the update // file before the new process starts. It would make sense to adjust the install file ACLs before releasing // the lock on the file. log.Info("Running install file: " + installPath); var process = Process.Start(psi); installerStarted(); process.WaitForExit(); }