205 lines
7.7 KiB
C#
205 lines
7.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Reflection;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Katteker
|
|
{
|
|
/// <summary>
|
|
/// Update manager, which handles the updates.
|
|
/// </summary>
|
|
public class UpdateManager
|
|
{
|
|
private static readonly string BaseDir = AppDomain.CurrentDomain.BaseDirectory;
|
|
private static KattekerConfig _config;
|
|
private readonly string _applicationName;
|
|
private readonly string _packageDir;
|
|
private readonly string _rootAppDirectory;
|
|
private readonly string _urlOrPath;
|
|
private Releases _releases;
|
|
|
|
private UpdateManager(string urlOrPath, string applicationName, string rootDirectory)
|
|
{
|
|
_urlOrPath = urlOrPath;
|
|
_applicationName = applicationName;
|
|
_rootAppDirectory = rootDirectory;
|
|
var packageDir = Path.Combine(rootDirectory, Constants.PackageFolder);
|
|
if (!Directory.Exists(packageDir)) Directory.CreateDirectory(packageDir);
|
|
_packageDir = packageDir;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filename of the changelog.
|
|
/// </summary>
|
|
public string ChangelogFilename => _config.Changelog;
|
|
|
|
/// <summary>
|
|
/// Url or path where the update files are located.
|
|
/// </summary>
|
|
public string UrlOrPath => _urlOrPath;
|
|
|
|
/// <summary>
|
|
/// Create the update manager.
|
|
/// </summary>
|
|
/// <param name="urlOrPath">path to the publishing directory</param>
|
|
/// <param name="applicationName">name of the application to update.</param>
|
|
/// <param name="rootDirectory">root directory.</param>
|
|
/// <returns>the update manager.</returns>
|
|
public static UpdateManager Create(string urlOrPath = null, string applicationName = null, string rootDirectory = null)
|
|
{
|
|
_config = ReadConfigFile();
|
|
urlOrPath = urlOrPath ?? _config.Publish;
|
|
var appName = applicationName ?? Utility.GetApplicationName();
|
|
var rootAppDirectory = Path.Combine(rootDirectory ?? Utility.GetLocalAppDataDirectory(), appName);
|
|
return new UpdateManager(urlOrPath, appName, rootAppDirectory);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to create the update manager.
|
|
/// </summary>
|
|
/// <param name="manager">update manager</param>
|
|
/// <param name="urlOrPath">path to the publishing directory</param>
|
|
/// <param name="applicationName">name of the application to update.</param>
|
|
/// <param name="rootDirectory">root directory.</param>
|
|
/// <returns>true if the creation success, false otherwise.</returns>
|
|
public static bool TryCreate(out UpdateManager manager, string urlOrPath = null, string applicationName = null, string rootDirectory = null)
|
|
{
|
|
try
|
|
{
|
|
manager = Create(urlOrPath, applicationName, rootDirectory);
|
|
return true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
manager = default(UpdateManager);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<IEnumerable<ReleaseEntry>> CheckForUpdateAsync()
|
|
{
|
|
_releases = Utility.IsWebUrl(_urlOrPath) ? await DownloadIndexAsync(_urlOrPath).ConfigureAwait(false) : GetFromFilesystem(_urlOrPath);
|
|
var updateInfo = new UpdateInfo(_applicationName, _releases);
|
|
return updateInfo.ReleasesToApply;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restart the application.
|
|
/// </summary>
|
|
/// <param name="exeToStart"></param>
|
|
/// <param name="arguments"></param>
|
|
public void RestartApp(string exeToStart = null, string arguments = null)
|
|
{
|
|
exeToStart = exeToStart ?? Path.GetFileName(Assembly.GetEntryAssembly().Location);
|
|
var program = Path.Combine(_rootAppDirectory, exeToStart);
|
|
if (File.Exists(program))
|
|
{
|
|
Process.Start(program, arguments);
|
|
Thread.Sleep(500);
|
|
Environment.Exit(0);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update application.
|
|
/// </summary>
|
|
/// <param name="progress">The updating process.</param>
|
|
/// <returns></returns>
|
|
public async Task<bool> UpdateAppAsync(IProgress<int> progress = null)
|
|
{
|
|
progress?.Report(0);
|
|
var updateEntries = (await CheckForUpdateAsync().ConfigureAwait(false)).ToArray();
|
|
var update = updateEntries.LastOrDefault();
|
|
if (update == null) return false;
|
|
progress?.Report(30);
|
|
return await UpdateAppImplAsync(update, progress).ConfigureAwait(false);
|
|
}
|
|
|
|
private static async Task<Releases> DownloadIndexAsync(string urlOrPath)
|
|
{
|
|
var url = urlOrPath.TrimEnd('/');
|
|
url += "/" + Constants.Release;
|
|
var content = await new WebClient().DownloadStringTaskAsync(url).ConfigureAwait(false);
|
|
var lines = content.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
|
|
return new Releases(lines);
|
|
}
|
|
|
|
private static Releases GetFromFilesystem(string urlOrPath) => new Releases(urlOrPath);
|
|
|
|
private static KattekerConfig ReadConfigFile()
|
|
{
|
|
var configPath = Path.Combine(BaseDir, Constants.KattekerConfig);
|
|
if (!File.Exists(configPath)) throw new FileNotFoundException("Configuration file not found.", configPath);
|
|
return KattekerConfig.ReadFromFile(configPath);
|
|
}
|
|
|
|
private static bool VerifyFileChecksum(string targetFile, string lastEntrySha1)
|
|
{
|
|
var hash = Utility.ComputeFileHash(targetFile);
|
|
return lastEntrySha1.Equals(hash);
|
|
}
|
|
|
|
private Task KillAppStubAsync()
|
|
{
|
|
foreach (var process in Process.GetProcessesByName(_applicationName))
|
|
{
|
|
var path = Path.GetDirectoryName(process.MainModule.FileName);
|
|
if (_rootAppDirectory.Equals(path))
|
|
{
|
|
process.Kill();
|
|
return Task.Delay(500);
|
|
}
|
|
}
|
|
|
|
return Task.FromResult(true);
|
|
}
|
|
|
|
private async Task PutFileInPackageFolderAsync(string filename)
|
|
{
|
|
var targetFile = Path.Combine(_packageDir, filename);
|
|
File.Delete(targetFile);
|
|
if (Utility.IsWebUrl(_urlOrPath))
|
|
{
|
|
var url = _urlOrPath.TrimEnd('/');
|
|
url += "/" + filename;
|
|
await new WebClient().DownloadFileTaskAsync(new Uri(url), targetFile).ConfigureAwait(false);
|
|
}
|
|
else
|
|
{
|
|
File.Copy(Path.Combine(_urlOrPath, filename), targetFile);
|
|
}
|
|
}
|
|
|
|
private async Task<bool> UpdateAppImplAsync(ReleaseEntry lastEntry, IProgress<int> progress)
|
|
{
|
|
|
|
var targetFile = Path.Combine(_packageDir, lastEntry.Filename);
|
|
//download file.
|
|
await PutFileInPackageFolderAsync(lastEntry.Filename).ConfigureAwait(false);
|
|
progress?.Report(60);
|
|
|
|
if (!VerifyFileChecksum(targetFile, lastEntry.SHA1)) throw new FileLoadException();
|
|
progress?.Report(70);
|
|
|
|
await KillAppStubAsync().ConfigureAwait(false);
|
|
progress?.Report(80);
|
|
|
|
using (var updater = new Process())
|
|
{
|
|
updater.StartInfo = new ProcessStartInfo(targetFile, "/S");
|
|
updater.Start();
|
|
updater.WaitForExit();
|
|
}
|
|
|
|
progress?.Report(100);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|