Merge pull request #224 from mRemoteNG/160_portable_update_check

160 portable update check
This commit is contained in:
Sean Kaim
2016-11-06 17:12:55 -05:00
committed by GitHub
9 changed files with 159 additions and 71 deletions

View File

@@ -0,0 +1,12 @@
#Requires -Version 4.0
$file = gci ..\Release | sort LastWriteTime | select -last 1 | % { $_.FullName }
$version = $file.tostring().Split("-")[2].trim(".zip")
Write-Host Version: $version
Write-Host dURL:
Write-Host clURL:
$hash = Get-FileHash -Algorithm MD5 $file | % { $_.Hash }
Write-Host CertificateThumbprint: $hash

View File

@@ -4,14 +4,31 @@
{
public static string FileName
{
#if PORTABLE
get
{
/* */
/* return PORTABLE update files here */
/* */
#if DEBUG
return "update-portable-debug.txt";
#else
return Settings.Default.UpdateChannel.ToLowerInvariant() == "debug" ? "update-portable-debug.txt" : "update-portable.txt";
#endif
}
#else //NOT portable
get
{
/* */
/* return INSTALLER update files here */
/* */
#if DEBUG
return "update-debug.txt";
#else
return Settings.Default.UpdateChannel.ToLowerInvariant() == "debug" ? "update-debug.txt" : "update.txt";
#endif
}
#endif //endif for PORTABLE
}
}
}

View File

@@ -5,6 +5,9 @@ using System.ComponentModel;
using System.Threading;
using mRemoteNG.Tools;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Forms;
using mRemoteNG.App.Info;
using mRemoteNG.Security.SymmetricEncryption;
@@ -28,7 +31,7 @@ namespace mRemoteNG.App.Update
private bool IsGetChangeLogRunning => _getChangeLogThread != null && _getChangeLogThread.IsAlive;
public bool IsDownloadUpdateRunning => (_downloadUpdateWebClient != null);
public bool IsDownloadUpdateRunning => _downloadUpdateWebClient != null;
#endregion
@@ -92,7 +95,7 @@ namespace mRemoteNG.App.Update
{
if (_currentUpdateInfo == null || !_currentUpdateInfo.IsValid)
{
throw (new InvalidOperationException("CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling GetChangeLogAsync()."));
throw new InvalidOperationException("CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling GetChangeLogAsync().");
}
if (IsGetChangeLogRunning)
@@ -110,20 +113,36 @@ namespace mRemoteNG.App.Update
{
if (_downloadUpdateWebClient != null)
{
throw (new InvalidOperationException("A previous call to DownloadUpdateAsync() is still in progress."));
throw new InvalidOperationException("A previous call to DownloadUpdateAsync() is still in progress.");
}
if (_currentUpdateInfo == null || !_currentUpdateInfo.IsValid)
{
throw (new InvalidOperationException("CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync()."));
throw new InvalidOperationException("CurrentUpdateInfo is not valid. GetUpdateInfoAsync() must be called before calling DownloadUpdateAsync().");
}
_currentUpdateInfo.UpdateFilePath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "exe"));
DownloadUpdateWebClient.DownloadFileAsync(CurrentUpdateInfo.DownloadAddress, _currentUpdateInfo.UpdateFilePath);
#if !PORTABLE
_currentUpdateInfo.UpdateFilePath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Path.GetRandomFileName(), "exe"));
#else
var sfd = new SaveFileDialog
{
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory),
FileName = _currentUpdateInfo.FileName,
RestoreDirectory = true
};
if (sfd.ShowDialog() == DialogResult.OK)
{
_currentUpdateInfo.UpdateFilePath = sfd.FileName;
}
else
{
return;
}
#endif
DownloadUpdateWebClient.DownloadFileAsync(CurrentUpdateInfo.DownloadAddress, _currentUpdateInfo.UpdateFilePath);
}
#endregion
#endregion
#region Private Properties
#region Private Properties
private WebClient _downloadUpdateWebClient;
private WebClient DownloadUpdateWebClient
{
@@ -142,9 +161,9 @@ namespace mRemoteNG.App.Update
return _downloadUpdateWebClient;
}
}
#endregion
#endregion
#region Private Methods
#region Private Methods
private WebClient CreateWebClient()
{
var webClient = new WebClient();
@@ -231,7 +250,8 @@ namespace mRemoteNG.App.Update
{
try
{
var updateAuthenticode = new Authenticode(_currentUpdateInfo.UpdateFilePath)
#if !PORTABLE
var updateAuthenticode = new Authenticode(_currentUpdateInfo.UpdateFilePath)
{
RequireThumbprintMatch = true,
ThumbprintToMatch = _currentUpdateInfo.CertificateThumbprint
@@ -246,7 +266,19 @@ namespace mRemoteNG.App.Update
throw (new Exception(updateAuthenticode.StatusMessage));
}
}
#else
using (var md5 = MD5.Create())
{
using (var stream = File.OpenRead(_currentUpdateInfo.UpdateFilePath))
{
var hash = md5.ComputeHash(stream);
var hashString = BitConverter.ToString(hash).Replace("-", "");
if (!hashString.Equals(_currentUpdateInfo.CertificateThumbprint))
throw new Exception("MD5 Hashes didn't match!");
}
}
#endif
}
catch (Exception ex)
{
raiseEventArgs = new AsyncCompletedEventArgs(ex, false, null);
@@ -263,9 +295,9 @@ namespace mRemoteNG.App.Update
_downloadUpdateWebClient.Dispose();
_downloadUpdateWebClient = null;
}
#endregion
#endregion
#region Events
#region Events
private AsyncCompletedEventHandler GetUpdateInfoCompletedEventEvent;
public event AsyncCompletedEventHandler GetUpdateInfoCompletedEvent
{
@@ -317,6 +349,6 @@ namespace mRemoteNG.App.Update
DownloadUpdateCompletedEventEvent = (AsyncCompletedEventHandler)Delegate.Remove(DownloadUpdateCompletedEventEvent, value);
}
}
#endregion
#endregion
}
}

View File

@@ -7,11 +7,9 @@ namespace mRemoteNG.App.Update
public class UpdateFile
{
#region Public Properties
private Dictionary<string, string> _items = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
// ReSharper disable MemberCanBePrivate.Local
// ReSharper disable once MemberCanBePrivate.Global
public Dictionary<string, string> Items => _items;
public Dictionary<string, string> Items { get; } = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
#endregion
#region Public Methods
@@ -52,7 +50,7 @@ namespace mRemoteNG.App.Update
var key = parts[0].Trim();
var value = parts[1].Trim();
_items.Add(key, value);
Items.Add(key, value);
}
}
}
@@ -61,7 +59,7 @@ namespace mRemoteNG.App.Update
private string GetString(string key)
{
// ReSharper restore MemberCanBePrivate.Local
return !Items.ContainsKey(key) ? string.Empty : this._items[key];
return !Items.ContainsKey(key) ? string.Empty : this.Items[key];
}
public Version GetVersion(string key)
@@ -80,6 +78,13 @@ namespace mRemoteNG.App.Update
{
return GetString(key).Replace(" ", "").ToUpperInvariant();
}
public string GetFileName()
{
var value = GetString("dURL");
var sv = value.Split('/');
return sv[sv.Length-1];
}
#endregion
}
}

View File

@@ -12,6 +12,7 @@ namespace mRemoteNG.App.Update
public Uri ImageAddress { get; private set; }
public Uri ImageLinkAddress { get; private set; }
public string CertificateThumbprint { get; private set; }
public string FileName { get; private set; }
public static UpdateInfo FromString(string input)
{
@@ -29,6 +30,7 @@ namespace mRemoteNG.App.Update
newInfo.ImageAddress = updateFile.GetUri("imgURL");
newInfo.ImageLinkAddress = updateFile.GetUri("imgURLLink");
newInfo.CertificateThumbprint = updateFile.GetThumbprint("CertificateThumbprint");
newInfo.FileName = updateFile.GetFileName();
newInfo.IsValid = true;
}
return newInfo;

View File

@@ -1448,6 +1448,15 @@ namespace mRemoteNG {
}
}
/// <summary>
/// Looks up a localized string similar to Download.
/// </summary>
internal static string strDownloadPortable {
get {
return ResourceManager.GetString("strDownloadPortable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Duplicate.
/// </summary>
@@ -6677,6 +6686,15 @@ namespace mRemoteNG {
}
}
/// <summary>
/// Looks up a localized string similar to Download Completed!.
/// </summary>
internal static string strUpdatePortableDownloadComplete {
get {
return ResourceManager.GetString("strUpdatePortableDownloadComplete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use a different username and password.
/// </summary>

View File

@@ -2415,4 +2415,10 @@ mRemoteNG will now quit and begin with the installation.</value>
<data name="strPropertyNameSoundQuality" xml:space="preserve">
<value>Sound Quality</value>
</data>
<data name="strUpdatePortableDownloadComplete" xml:space="preserve">
<value>Download Completed!</value>
</data>
<data name="strDownloadPortable" xml:space="preserve">
<value>Download</value>
</data>
</root>

View File

@@ -42,7 +42,6 @@ namespace mRemoteNG.UI.Forms
private bool _showFullPathInTitle;
private ConnectionInfo _selectedConnection;
private SystemMenu _systemMenu;
private MiscTools.Fullscreen _fullscreen;
private ConnectionTreeWindow ConnectionTreeWindow { get; set; }
@@ -51,7 +50,7 @@ namespace mRemoteNG.UI.Forms
{
_showFullPathInTitle = Settings.Default.ShowCompleteConsPathInTitle;
InitializeComponent();
_fullscreen = new MiscTools.Fullscreen(this);
Fullscreen = new MiscTools.Fullscreen(this);
pnlDock.Theme = new VS2012LightTheme();
}
@@ -120,11 +119,8 @@ namespace mRemoteNG.UI.Forms
}
}
public MiscTools.Fullscreen Fullscreen
{
get { return _fullscreen; }
set { _fullscreen = value; }
}
public MiscTools.Fullscreen Fullscreen { get; set; }
#endregion
#region Startup & Shutdown
@@ -164,8 +160,6 @@ namespace mRemoteNG.UI.Forms
Windows.Show(WindowType.ComponentsCheck);
}
ApplySpecialSettingsForPortableVersion();
Startup.Instance.CreateConnectionsProvider();
AddSysMenuItems();
Microsoft.Win32.SystemEvents.DisplaySettingsChanged += DisplayChanged;
@@ -174,14 +168,6 @@ namespace mRemoteNG.UI.Forms
ConnectionTreeWindow = Windows.TreeForm;
}
private void ApplySpecialSettingsForPortableVersion()
{
#if PORTABLE
mMenToolsUpdate.Visible = false;
mMenInfoSep2.Visible = false;
#endif
}
private void ApplyLanguage()
{
mMenFile.Text = Language.strMenuFile;
@@ -285,7 +271,6 @@ namespace mRemoteNG.UI.Forms
private void frmMain_Shown(object sender, EventArgs e)
{
#if !PORTABLE
if (!Settings.Default.CheckForUpdatesAsked)
{
string[] commandButtons =
@@ -314,7 +299,7 @@ namespace mRemoteNG.UI.Forms
if (!Settings.Default.CheckForUpdatesOnStartup) return;
DateTime nextUpdateCheck = Convert.ToDateTime(
var nextUpdateCheck = Convert.ToDateTime(
Settings.Default.CheckForUpdatesLastCheck.Add(
TimeSpan.FromDays(Convert.ToDouble(Settings.Default.CheckForUpdatesFrequencyDays))));
@@ -322,7 +307,6 @@ namespace mRemoteNG.UI.Forms
if (!IsHandleCreated) CreateHandle(); // Make sure the handle is created so that InvokeRequired returns the correct result
Startup.Instance.CheckForUpdate();
#endif
}
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
@@ -458,7 +442,7 @@ namespace mRemoteNG.UI.Forms
#region Menu
#region File
private void mMenFile_DropDownOpening(Object sender, EventArgs e)
private void mMenFile_DropDownOpening(object sender, EventArgs e)
{
var selectedNodeType = ConnectionTreeWindow.SelectedNode?.GetTreeNodeType();
if (selectedNodeType == TreeNodeType.Root)

View File

@@ -1,19 +1,23 @@
using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;
using WeifenLuo.WinFormsUI.Docking;
using System.IO;
using System.Diagnostics;
using System.Drawing;
using System.Net;
using System.Windows.Forms;
using mRemoteNG.App;
using mRemoteNG.App.Update;
using WeifenLuo.WinFormsUI.Docking;
#if !PORTABLE
using System.IO;
#endif
namespace mRemoteNG.UI.Window
{
public partial class UpdateWindow : BaseWindow
{
#region Public Methods
#region Public Methods
public UpdateWindow(DockContent panel)
{
WindowType = WindowType.Update;
@@ -21,10 +25,11 @@ namespace mRemoteNG.UI.Window
InitializeComponent();
Runtime.FontOverride(this);
}
#endregion
#endregion
#region Form Stuff
public void Update_Load(object sender, EventArgs e)
#region Form Stuff
private void Update_Load(object sender, EventArgs e)
{
ApplyLanguage();
CheckForUpdate();
@@ -35,8 +40,12 @@ namespace mRemoteNG.UI.Window
Text = Language.strMenuCheckForUpdates;
TabText = Language.strMenuCheckForUpdates;
btnCheckForUpdate.Text = Language.strCheckForUpdate;
btnDownload.Text = Language.strDownloadAndInstall;
lblChangeLogLabel.Text = Language.strLabelChangeLog;
#if !PORTABLE
btnDownload.Text = Language.strDownloadAndInstall;
#else
btnDownload.Text = Language.strDownloadPortable;
#endif
lblChangeLogLabel.Text = Language.strLabelChangeLog;
lblInstalledVersion.Text = Language.strVersion;
lblInstalledVersionLabel.Text = $"{Language.strCurrentVersion}:";
lblLatestVersion.Text = Language.strVersion;
@@ -62,14 +71,14 @@ namespace mRemoteNG.UI.Window
}
Process.Start(linkUri.ToString());
}
#endregion
#endregion
#region Private Fields
#region Private Fields
private AppUpdater _appUpdate;
private bool _isUpdateDownloadHandlerDeclared;
#endregion
#endregion
#region Private Methods
#region Private Methods
private void CheckForUpdate()
{
if (_appUpdate == null)
@@ -120,7 +129,7 @@ namespace mRemoteNG.UI.Window
}
if (e.Error != null)
{
throw (e.Error);
throw e.Error;
}
if (_appUpdate.IsUpdateAvailable())
@@ -181,7 +190,7 @@ namespace mRemoteNG.UI.Window
if (InvokeRequired)
{
var myDelegate = new AsyncCompletedEventHandler(GetChangeLogCompleted);
Invoke(myDelegate, new object[] {sender, e});
Invoke(myDelegate, sender, e);
return;
}
@@ -192,10 +201,10 @@ namespace mRemoteNG.UI.Window
if (e.Cancelled)
return;
if (e.Error != null)
throw (e.Error);
txtChangeLog.Text = _appUpdate.ChangeLog;
}
throw e.Error;
txtChangeLog.Text = _appUpdate.ChangeLog.Replace("\n", Environment.NewLine);
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionMessage(Language.strUpdateGetChangeLogFailed, ex);
@@ -224,10 +233,10 @@ namespace mRemoteNG.UI.Window
Runtime.MessageCollector.AddExceptionMessage(Language.strUpdateDownloadFailed, ex);
}
}
#endregion
#endregion
#region Events
private void DownloadUpdateProgressChanged(object sender, System.Net.DownloadProgressChangedEventArgs e)
#region Events
private void DownloadUpdateProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
prgbDownload.Value = e.ProgressPercentage;
}
@@ -242,22 +251,25 @@ namespace mRemoteNG.UI.Window
if (e.Cancelled)
return;
if (e.Error != null)
throw (e.Error);
if (MessageBox.Show(Language.strUpdateDownloadComplete, Language.strMenuCheckForUpdates, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
throw e.Error;
#if !PORTABLE
if (MessageBox.Show(Language.strUpdateDownloadComplete, Language.strMenuCheckForUpdates, MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
Shutdown.Quit(_appUpdate.CurrentUpdateInfo.UpdateFilePath);
}
else
else
{
File.Delete(_appUpdate.CurrentUpdateInfo.UpdateFilePath);
}
#else
MessageBox.Show(Language.strUpdatePortableDownloadComplete, Language.strMenuCheckForUpdates, MessageBoxButtons.OK, MessageBoxIcon.Information);
#endif
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionMessage(Language.strUpdateDownloadCompleteFailed, ex);
}
}
#endregion
#endregion
}
}