diff --git a/Tools/create_upg_chk_files.ps1 b/Tools/create_upg_chk_files.ps1 new file mode 100644 index 000000000..dfacba4e8 --- /dev/null +++ b/Tools/create_upg_chk_files.ps1 @@ -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 \ No newline at end of file diff --git a/mRemoteV1/App/Info/UpdateChannelInfo.cs b/mRemoteV1/App/Info/UpdateChannelInfo.cs index c03de729b..1dac711c6 100644 --- a/mRemoteV1/App/Info/UpdateChannelInfo.cs +++ b/mRemoteV1/App/Info/UpdateChannelInfo.cs @@ -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 } } } \ No newline at end of file diff --git a/mRemoteV1/App/Update/AppUpdater.cs b/mRemoteV1/App/Update/AppUpdater.cs index 5801fb02b..edcb1f3ea 100644 --- a/mRemoteV1/App/Update/AppUpdater.cs +++ b/mRemoteV1/App/Update/AppUpdater.cs @@ -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 } } \ No newline at end of file diff --git a/mRemoteV1/App/Update/UpdateFile.cs b/mRemoteV1/App/Update/UpdateFile.cs index cabb8742d..1fff1fdfb 100644 --- a/mRemoteV1/App/Update/UpdateFile.cs +++ b/mRemoteV1/App/Update/UpdateFile.cs @@ -7,11 +7,9 @@ namespace mRemoteNG.App.Update public class UpdateFile { #region Public Properties - private Dictionary _items = new Dictionary(StringComparer.InvariantCultureIgnoreCase); // ReSharper disable MemberCanBePrivate.Local // ReSharper disable once MemberCanBePrivate.Global - public Dictionary Items => _items; - + public Dictionary Items { get; } = new Dictionary(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 } } \ No newline at end of file diff --git a/mRemoteV1/App/Update/UpdateInfo.cs b/mRemoteV1/App/Update/UpdateInfo.cs index b9b70c8b4..8515d500e 100644 --- a/mRemoteV1/App/Update/UpdateInfo.cs +++ b/mRemoteV1/App/Update/UpdateInfo.cs @@ -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; diff --git a/mRemoteV1/Resources/Language/Language.Designer.cs b/mRemoteV1/Resources/Language/Language.Designer.cs index c68991ce5..899f6bc5e 100644 --- a/mRemoteV1/Resources/Language/Language.Designer.cs +++ b/mRemoteV1/Resources/Language/Language.Designer.cs @@ -1448,6 +1448,15 @@ namespace mRemoteNG { } } + /// + /// Looks up a localized string similar to Download. + /// + internal static string strDownloadPortable { + get { + return ResourceManager.GetString("strDownloadPortable", resourceCulture); + } + } + /// /// Looks up a localized string similar to Duplicate. /// @@ -6677,6 +6686,15 @@ namespace mRemoteNG { } } + /// + /// Looks up a localized string similar to Download Completed!. + /// + internal static string strUpdatePortableDownloadComplete { + get { + return ResourceManager.GetString("strUpdatePortableDownloadComplete", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use a different username and password. /// diff --git a/mRemoteV1/Resources/Language/Language.resx b/mRemoteV1/Resources/Language/Language.resx index 4901fc82f..34cff4195 100644 --- a/mRemoteV1/Resources/Language/Language.resx +++ b/mRemoteV1/Resources/Language/Language.resx @@ -2415,4 +2415,10 @@ mRemoteNG will now quit and begin with the installation. Sound Quality + + Download Completed! + + + Download + \ No newline at end of file diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs index a57e5d635..c91e16dc2 100644 --- a/mRemoteV1/UI/Forms/frmMain.cs +++ b/mRemoteV1/UI/Forms/frmMain.cs @@ -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) diff --git a/mRemoteV1/UI/Window/UpdateWindow.cs b/mRemoteV1/UI/Window/UpdateWindow.cs index 52d341290..55489483b 100644 --- a/mRemoteV1/UI/Window/UpdateWindow.cs +++ b/mRemoteV1/UI/Window/UpdateWindow.cs @@ -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 } } \ No newline at end of file