diff --git a/XenAdmin/Alerts/Types/ClientUpdateAlert.cs b/XenAdmin/Alerts/Types/ClientUpdateAlert.cs index 233d193240..b6ee5ab921 100644 --- a/XenAdmin/Alerts/Types/ClientUpdateAlert.cs +++ b/XenAdmin/Alerts/Types/ClientUpdateAlert.cs @@ -31,13 +31,15 @@ using System; using System.Diagnostics; using System.IO; +using System.Linq; using System.Windows.Forms; using XenAdmin.Actions; using XenAdmin.Actions.GUIActions; using XenAdmin.Actions.Updates; using XenAdmin.Core; using XenAdmin.Dialogs; - +using XenAdmin.Dialogs.WarningDialogs; +using XenCenterLib; namespace XenAdmin.Alerts { @@ -123,10 +125,13 @@ public static void DownloadAndInstallNewClient(ClientUpdateAlert updateAlert, IW if (currentTasks) { - if (new Dialogs.WarningDialogs.CloseXenCenterWarningDialog(true).ShowDialog(parent) != DialogResult.OK) + using (var dlg = new CloseXenCenterWarningDialog(true)) { - downloadAndInstallClientAction.ReleaseInstaller(); - return; + if (dlg.ShowDialog(parent) != DialogResult.OK) + { + downloadAndInstallClientAction.ReleaseDownloadedContent(); + return; + } } } @@ -134,17 +139,49 @@ public static void DownloadAndInstallNewClient(ClientUpdateAlert updateAlert, IW { Process.Start(outputPathAndFileName); log.DebugFormat("Update {0} found and install started", updateAlert.Name); - downloadAndInstallClientAction.ReleaseInstaller(); + downloadAndInstallClientAction.ReleaseDownloadedContent(); Application.Exit(); } catch (Exception e) { log.Error("Exception occurred when starting the installation process.", e); - downloadAndInstallClientAction.ReleaseInstaller(true); + downloadAndInstallClientAction.ReleaseDownloadedContent(true); using (var dlg = new ErrorDialog(Messages.UPDATE_CLIENT_FAILED_INSTALLER_LAUNCH)) dlg.ShowDialog(parent); } } + + public static void DownloadSource(IWin32Window parent) + { + // If no update no need to show where to save dialog + var clientVersion = Updates.ClientVersions.FirstOrDefault(); + + if (clientVersion == null) + { + // There is no XCUpdates.xml so direct to website. + Program.OpenURL(InvisibleMessages.WEBSITE_DOWNLOADS); + } + else + { + string outputPathAndFileName; + using (var saveSourceDialog = new SaveFileDialog()) + { + saveSourceDialog.FileName = $"{BrandManager.BrandConsole}-v{clientVersion.Version}-source.zip"; + saveSourceDialog.DefaultExt = "zip"; + saveSourceDialog.Filter = "(*.zip)|*.zip|All files (*.*)|*.*"; + saveSourceDialog.InitialDirectory = Win32.GetKnownFolderPath(Win32.KnownFolders.Downloads); + + if (saveSourceDialog.ShowDialog(parent) != DialogResult.OK) + { + return; + } + outputPathAndFileName = saveSourceDialog.FileName; + } + + var downloadSourceAction = new DownloadSourceAction(clientVersion.Name, new Uri(clientVersion.SourceUrl), outputPathAndFileName); + downloadSourceAction.RunAsync(); + } + } } } diff --git a/XenAdmin/Alerts/Types/GuiOldAlert.cs b/XenAdmin/Alerts/Types/GuiOldAlert.cs index 62db440330..ad0cb53e1f 100644 --- a/XenAdmin/Alerts/Types/GuiOldAlert.cs +++ b/XenAdmin/Alerts/Types/GuiOldAlert.cs @@ -49,7 +49,7 @@ public GuiOldAlert() public override Action FixLinkAction { - get { return () => Program.OpenURL(InvisibleMessages.OUT_OF_DATE_WEBSITE); } + get { return () => Program.OpenURL(InvisibleMessages.WEBSITE_DOWNLOADS); } } public override string FixLinkText => Messages.ALERT_NEW_VERSION_DOWNLOAD; diff --git a/XenAdmin/Core/Updates.cs b/XenAdmin/Core/Updates.cs index 0ee6cd53dd..bebe4c2931 100644 --- a/XenAdmin/Core/Updates.cs +++ b/XenAdmin/Core/Updates.cs @@ -67,7 +67,7 @@ public class Updates private static List XenServerVersionsForAutoCheck = new List(); private static List XenServerPatches = new List(); - private static List ClientVersions = new List(); + public static List ClientVersions = new List(); public static List XenServerVersions = new List(); private static readonly List updateAlerts = new List(); diff --git a/XenAdmin/MainWindow.Designer.cs b/XenAdmin/MainWindow.Designer.cs index 2d22626118..f4e7382e2b 100644 --- a/XenAdmin/MainWindow.Designer.cs +++ b/XenAdmin/MainWindow.Designer.cs @@ -279,11 +279,13 @@ private void InitializeComponent() this.xenCenterPluginsOnlineToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator7 = new System.Windows.Forms.ToolStripSeparator(); this.pluginItemsPlaceHolderToolStripMenuItem8 = new System.Windows.Forms.ToolStripMenuItem(); + this.downloadLatestSourceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutXenSourceAdminToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainMenuBar = new XenAdmin.Controls.MenuStripEx(); this.updateClientToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.downloadInstallToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.relNotesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.downloadSourceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.securityGroupsToolStripMenuItem = new XenAdmin.Commands.CommandToolStripMenuItem(); this.MenuPanel = new System.Windows.Forms.Panel(); this.StatusStrip = new XenAdmin.Controls.StatusStripEx(); @@ -1867,6 +1869,7 @@ private void InitializeComponent() this.xenCenterPluginsOnlineToolStripMenuItem, this.toolStripSeparator7, this.pluginItemsPlaceHolderToolStripMenuItem8, + this.downloadLatestSourceToolStripMenuItem, this.aboutXenSourceAdminToolStripMenuItem}); this.helpToolStripMenuItem.Name = "helpToolStripMenuItem"; resources.ApplyResources(this.helpToolStripMenuItem, "helpToolStripMenuItem"); @@ -1928,6 +1931,12 @@ private void InitializeComponent() this.pluginItemsPlaceHolderToolStripMenuItem8.Name = "pluginItemsPlaceHolderToolStripMenuItem8"; resources.ApplyResources(this.pluginItemsPlaceHolderToolStripMenuItem8, "pluginItemsPlaceHolderToolStripMenuItem8"); // + // downloadLatestSourceToolStripMenuItem + // + this.downloadLatestSourceToolStripMenuItem.Name = "downloadLatestSourceToolStripMenuItem"; + resources.ApplyResources(this.downloadLatestSourceToolStripMenuItem, "downloadLatestSourceToolStripMenuItem"); + this.downloadLatestSourceToolStripMenuItem.Click += new System.EventHandler(this.downloadLatestSourceToolStripMenuItem_Click); + // // aboutXenSourceAdminToolStripMenuItem // this.aboutXenSourceAdminToolStripMenuItem.Name = "aboutXenSourceAdminToolStripMenuItem"; @@ -1960,7 +1969,8 @@ private void InitializeComponent() this.updateClientToolStripMenuItem.BackColor = System.Drawing.SystemColors.Control; this.updateClientToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.downloadInstallToolStripMenuItem, - this.relNotesToolStripMenuItem}); + this.relNotesToolStripMenuItem, + this.downloadSourceToolStripMenuItem}); this.updateClientToolStripMenuItem.Image = global::XenAdmin.Properties.Resources._015_Download_h32bit_16; this.updateClientToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 5, 0); this.updateClientToolStripMenuItem.Name = "updateClientToolStripMenuItem"; @@ -1978,6 +1988,12 @@ private void InitializeComponent() resources.ApplyResources(this.relNotesToolStripMenuItem, "relNotesToolStripMenuItem"); this.relNotesToolStripMenuItem.Click += new System.EventHandler(this.relNotesToolStripMenuItem_Click); // + // downloadSourceToolStripMenuItem + // + this.downloadSourceToolStripMenuItem.Name = "downloadSourceToolStripMenuItem"; + resources.ApplyResources(this.downloadSourceToolStripMenuItem, "downloadSourceToolStripMenuItem"); + this.downloadSourceToolStripMenuItem.Click += new System.EventHandler(this.downloadSourceToolStripMenuItem_Click); + // // securityGroupsToolStripMenuItem // this.securityGroupsToolStripMenuItem.Name = "securityGroupsToolStripMenuItem"; @@ -2344,6 +2360,8 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem toolStripMenuItemCfu; private System.Windows.Forms.ToolStripSeparator toolStripSeparator32; private XenAdmin.Commands.CommandToolStripMenuItem toolStripMenuItemVtpm; + private System.Windows.Forms.ToolStripMenuItem downloadSourceToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem downloadLatestSourceToolStripMenuItem; private System.Windows.Forms.Label labelFiltersOnOff; private System.Windows.Forms.ToolStripMenuItem configureUpdatesToolStripMenuItem; private TabPages.ManageCdnUpdatesPage cdnUpdatesPage; diff --git a/XenAdmin/MainWindow.cs b/XenAdmin/MainWindow.cs index 14e3f6c180..a9da97ca9c 100755 --- a/XenAdmin/MainWindow.cs +++ b/XenAdmin/MainWindow.cs @@ -62,6 +62,7 @@ using XenAdmin.Controls.GradientPanel; using XenAdmin.Dialogs.ServerUpdates; using XenAdmin.Help; +using XenAdmin.Actions.Updates; namespace XenAdmin { @@ -269,6 +270,7 @@ public MainWindow(string[] args) statusButtonAlerts.Visible = statusButtonUpdates.Visible = statusButtonCdnUpdates.Visible = statusButtonProgress.Visible = statusButtonErrors.Visible = false; statusButtonUpdates.ToolTipText = string.Format(statusButtonUpdates.ToolTipText, BrandManager.ProductVersion821); statusButtonCdnUpdates.ToolTipText = string.Format(statusButtonCdnUpdates.ToolTipText, BrandManager.ProductBrand, BrandManager.ProductVersionPost82); + downloadLatestSourceToolStripMenuItem.Text = Messages.DOWNLOAD_LATEST_SOURCE; } private void RegisterEvents() @@ -966,7 +968,7 @@ private void connection_CachePopulated(IXenConnection connection) Program.Invoke(Program.MainWindow, delegate { var msg = string.Format(Messages.GUI_OUT_OF_DATE, BrandManager.BrandConsole, Helpers.GetName(coordinator)); - var url = InvisibleMessages.OUT_OF_DATE_WEBSITE; + var url = InvisibleMessages.WEBSITE_DOWNLOADS; var title = string.Format(Messages.CONNECTION_REFUSED_TITLE, Helpers.GetName(coordinator).Ellipsise(80)); var error = $"{msg}\n{url}"; @@ -994,7 +996,7 @@ private void connection_CachePopulated(IXenConnection connection) { var msg = string.Format(Messages.GUI_NOT_COMPATIBLE, BrandManager.BrandConsole, BrandManager.ProductVersion712, BrandManager.ProductVersion80, Helpers.GetName(coordinator)); - var url = InvisibleMessages.OUT_OF_DATE_WEBSITE; + var url = InvisibleMessages.WEBSITE_DOWNLOADS; var title = string.Format(Messages.CONNECTION_REFUSED_TITLE, Helpers.GetName(coordinator).Ellipsise(80)); var error = $"{msg}\n{url}"; @@ -2715,7 +2717,14 @@ private void SetClientUpdateAlert() { updateAlert = Updates.ClientUpdateAlerts.FirstOrDefault(); if (updateAlert != null) + { relNotesToolStripMenuItem.Text = string.Format(Messages.MAINWINDOW_UPDATE_RELEASE, updateAlert.NewVersion.Version); + downloadSourceToolStripMenuItem.Text = string.Format(Messages.DOWNLOAD_SOURCE, BrandManager.BrandConsole, updateAlert.NewVersion.Version); + } + var clientVersion = Updates.ClientVersions.FirstOrDefault(); + downloadLatestSourceToolStripMenuItem.Text = clientVersion != null + ? string.Format(Messages.DOWNLOAD_SOURCE, BrandManager.BrandConsole, clientVersion.Version) + : string.Format(Messages.DOWNLOAD_LATEST_SOURCE, BrandManager.BrandConsole); updateClientToolStripMenuItem.Visible = updateAlert != null; } @@ -3391,5 +3400,15 @@ private void configureUpdatesToolStripMenuItem_Click(object sender, EventArgs e) using (var dialog = new ConfigUpdatesDialog()) dialog.ShowDialog(this); } + + private void downloadSourceToolStripMenuItem_Click(object sender, EventArgs e) + { + ClientUpdateAlert.DownloadSource(this); + } + + private void downloadLatestSourceToolStripMenuItem_Click(object sender, EventArgs e) + { + ClientUpdateAlert.DownloadSource(this); + } } } diff --git a/XenAdmin/MainWindow.resx b/XenAdmin/MainWindow.resx index e915993871..e9b37af946 100644 --- a/XenAdmin/MainWindow.resx +++ b/XenAdmin/MainWindow.resx @@ -2772,6 +2772,9 @@ PluginItemsPlaceHolder + + 186, 22 + 186, 22 @@ -2797,17 +2800,23 @@ Microsoft Sans Serif, 8.25pt - 173, 22 + 180, 22 Download and &Install - 173, 22 + 180, 22 v{0} &Release Notes + + 180, 22 + + + Download v{0} source + 87, 20 @@ -4143,6 +4152,12 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + downloadLatestSourceToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + aboutXenSourceAdminToolStripMenuItem @@ -4167,6 +4182,12 @@ System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + downloadSourceToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + securityGroupsToolStripMenuItem diff --git a/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs b/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs index 0d919a3c2d..146c23af4b 100644 --- a/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs +++ b/XenAdminTests/UnitTests/AlertTests/XenCenterUpdateAlertTests.cs @@ -43,8 +43,8 @@ public class XenCenterUpdateAlertTests public void VerifyStoredDataWithDefaultConstructor() { var version = new ClientVersion("6.0.2", "xc", true, false, "http://url", - new DateTime(2011, 12, 09).ToString(), "abcde"); - + new DateTime(2011, 12, 09).ToString(), "abcde", "http://sourceurl"); + ClassVerifiers.VerifyGetters(new ClientUpdateAlert(version), new UpdateAlertClassUnitTestData { diff --git a/XenModel/Actions/Updates/ClientVersion.cs b/XenModel/Actions/Updates/ClientVersion.cs index 2d3a15034f..831c00700e 100644 --- a/XenModel/Actions/Updates/ClientVersion.cs +++ b/XenModel/Actions/Updates/ClientVersion.cs @@ -43,8 +43,9 @@ public class ClientVersion public string Lang; public DateTime TimeStamp; public string Checksum; + public string SourceUrl; - public ClientVersion(string version_lang, string name, bool latest, bool latest_cr, string url, string timestamp, string checksum) + public ClientVersion(string version_lang, string name, bool latest, bool latest_cr, string url, string timestamp, string checksum, string sourceUrl) { ParseVersion(version_lang); Name = name; @@ -53,6 +54,7 @@ public ClientVersion(string version_lang, string name, bool latest, bool latest_ Url = url; DateTime.TryParse(timestamp, out TimeStamp); Checksum = checksum; + SourceUrl = sourceUrl; } private void ParseVersion(string version_lang) diff --git a/XenModel/Actions/Updates/DownloadAndUpdateClientAction.cs b/XenModel/Actions/Updates/DownloadFileAction.cs similarity index 64% rename from XenModel/Actions/Updates/DownloadAndUpdateClientAction.cs rename to XenModel/Actions/Updates/DownloadFileAction.cs index 9849350b84..e8271fca7d 100644 --- a/XenModel/Actions/Updates/DownloadAndUpdateClientAction.cs +++ b/XenModel/Actions/Updates/DownloadFileAction.cs @@ -42,38 +42,36 @@ namespace XenAdmin.Actions.Updates { - public class DownloadAndUpdateClientAction : AsyncAction, IByteProgressAction + public class DownloadFileAction : AsyncAction, IByteProgressAction { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private const int SLEEP_TIME_TO_CHECK_DOWNLOAD_STATUS_MS = 900; private const int SLEEP_TIME_BEFORE_RETRY_MS = 5000; - //If you consider increasing this for any reason (I think 5 is already more than enough), have a look at the usage of SLEEP_TIME_BEFORE_RETRY_MS in DownloadFile() as well. + //If you consider increasing this for any reason (I think 5 is already more than enough), + //have a look at the usage of SLEEP_TIME_BEFORE_RETRY_MS in DownloadFile() as well. private const int MAX_NUMBER_OF_TRIES = 5; private readonly Uri _address; private readonly string _outputPathAndFileName; - private readonly string _updateName; - private readonly bool _downloadUpdate; - private DownloadState _updateDownloadState; - private Exception _updateDownloadError; - private readonly string _checksum; + private readonly string _fileName; + private readonly bool _canDownloadFile; + private DownloadState _fileState; + private Exception _downloadError; private readonly string _authToken; private WebClient _client; - private FileStream _msiStream; + protected string OutputPathAndFileName => _outputPathAndFileName; public string ByteProgressDescription { get; set; } - public DownloadAndUpdateClientAction(string updateName, Uri uri, string outputFileName, string checksum) - : base(null, string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_TITLE, updateName), - string.Empty, true) + protected DownloadFileAction(string fileName, Uri uri, string outputFileName, string title, bool suppressHistory) + : base(null, title, fileName, suppressHistory) { - _updateName = updateName; + _fileName = fileName; _address = uri; - _downloadUpdate = _address != null; + _canDownloadFile = _address != null; _outputPathAndFileName = outputFileName; - _checksum = checksum; _authToken = XenAdminConfigManager.Provider.GetClientUpdatesQueryParam(); } @@ -105,7 +103,7 @@ private void DownloadFile() _client.Proxy = XenAdminConfigManager.Provider.GetProxyFromSettings(null, false); //start the download - _updateDownloadState = DownloadState.InProgress; + _fileState = DownloadState.InProgress; var uriBuilder = new UriBuilder(_address); @@ -122,7 +120,7 @@ private void DownloadFile() bool updateDownloadCancelling = false; //wait for the file to be downloaded - while (_updateDownloadState == DownloadState.InProgress) + while (_fileState == DownloadState.InProgress) { if (!updateDownloadCancelling && (Cancelling || Cancelled)) { @@ -134,10 +132,10 @@ private void DownloadFile() Thread.Sleep(SLEEP_TIME_TO_CHECK_DOWNLOAD_STATUS_MS); } - if (_updateDownloadState == DownloadState.Cancelled) + if (_fileState == DownloadState.Cancelled) throw new CancelledException(); - if (_updateDownloadState == DownloadState.Error) + if (_fileState == DownloadState.Error) { needToRetry = true; @@ -149,10 +147,10 @@ private void DownloadFile() "Error while downloading from '{0}'. Number of errors so far (including this): {1}. Trying maximum {2} times.", _address, errorCount, MAX_NUMBER_OF_TRIES); - if (_updateDownloadError == null) + if (_downloadError == null) log.Error("An unknown error occurred."); else - log.Error(_updateDownloadError); + log.Error(_downloadError); } } while (errorCount < MAX_NUMBER_OF_TRIES && needToRetry); } @@ -167,30 +165,29 @@ private void DownloadFile() } //if this is still the case after having retried MAX_NUMBER_OF_TRIES number of times. - if (_updateDownloadState == DownloadState.Error) + if (_fileState == DownloadState.Error) { log.ErrorFormat("Giving up - Maximum number of retries ({0}) has been reached.", MAX_NUMBER_OF_TRIES); - throw _updateDownloadError ?? new Exception(Messages.ERROR_UNKNOWN); + throw _downloadError ?? new Exception(Messages.ERROR_UNKNOWN); } } private void NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { - if (!e.IsAvailable && _client != null && _updateDownloadState == DownloadState.InProgress) + if (!e.IsAvailable && _client != null && _fileState == DownloadState.InProgress) { - _updateDownloadError = new WebException(Messages.NETWORK_CONNECTIVITY_ERROR); - _updateDownloadState = DownloadState.Error; + _downloadError = new WebException(Messages.NETWORK_CONNECTIVITY_ERROR); + _fileState = DownloadState.Error; _client.CancelAsync(); } } protected override void Run() { - if (!_downloadUpdate) + if (!_canDownloadFile) return; - log.InfoFormat("Downloading '{0}' installer (from '{1}') to '{2}'", _updateName, _address, _outputPathAndFileName); - Description = string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_DESCRIPTION, _updateName); + log.InfoFormat("Downloading '{0}' (from '{1}') to '{2}'", _fileName, _address, _outputPathAndFileName); LogDescriptionChanges = false; DownloadFile(); LogDescriptionChanges = true; @@ -202,23 +199,30 @@ protected override void Run() throw new CancelledException(); if (!File.Exists(_outputPathAndFileName)) - throw new Exception(Messages.DOWNLOAD_CLIENT_INSTALLER_MSI_NOT_FOUND); + throw new Exception(GetDownloadedFileNotFoundMessage()); - ValidateMsi(); + ValidateDownloadedFile(); Description = Messages.COMPLETED; } protected override void CleanOnError() { - ReleaseInstaller(true); + ReleaseDownloadedContent(true); + } + + protected virtual string GetDownloadedFileNotFoundMessage() + { + return Messages.DOWNLOAD_FILE_ACTION_NOT_FOUND; } - public void ReleaseInstaller(bool deleteMsi = false) + protected virtual void ValidateDownloadedFile() { - _msiStream?.Dispose(); + } - if (!deleteMsi) + public virtual void ReleaseDownloadedContent(bool deleteDownloadedContent = false) + { + if (!deleteDownloadedContent) return; try @@ -232,27 +236,89 @@ public void ReleaseInstaller(bool deleteMsi = false) } } - private void ValidateMsi() + private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + { + int pc = (int)(95.0 * e.BytesReceived / e.TotalBytesToReceive); + + var descr = string.Format(Messages.DOWNLOAD_FILE_ACTION_PROGRESS_DESCRIPTION, _fileName, + Util.DiskSizeString(e.BytesReceived, "F1"), + Util.DiskSizeString(e.TotalBytesToReceive)); + ByteProgressDescription = descr; + Tick(pc, descr); + } + + private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + if (e.Cancelled && _fileState == DownloadState.Error) // cancelled due to network connectivity issue (see NetworkAvailabilityChanged) + return; + + if (e.Cancelled) + { + _fileState = DownloadState.Cancelled; + log.DebugFormat("'{0}' download cancelled by the user", _fileName); + return; + } + + if (e.Error != null) + { + _downloadError = e.Error; + log.DebugFormat("'{0}' download failed", _fileName); + _fileState = DownloadState.Error; + return; + } + + _fileState = DownloadState.Completed; + log.DebugFormat("'{0}' download completed successfully", _fileName); + } + + public override void RecomputeCanCancel() + { + CanCancel = !Cancelling && !IsCompleted && _fileState == DownloadState.InProgress; + } + } + + public class DownloadAndUpdateClientAction : DownloadFileAction + { + private readonly string _checksum; + private FileStream _msiStream; + + public DownloadAndUpdateClientAction(string installerName, Uri uri, string outputFileName, string checksum) + : base(installerName, + uri, + outputFileName, + string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_TITLE, installerName), + true) + { + _checksum = checksum; + Description = string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_DESCRIPTION, installerName); + } + + protected override string GetDownloadedFileNotFoundMessage() + { + return Messages.DOWNLOAD_CLIENT_INSTALLER_MSI_NOT_FOUND; + } + + protected override void ValidateDownloadedFile() { Description = Messages.UPDATE_CLIENT_VALIDATING_INSTALLER; - _msiStream = new FileStream(_outputPathAndFileName, FileMode.Open, FileAccess.Read); + _msiStream = new FileStream(OutputPathAndFileName, FileMode.Open, FileAccess.Read); - var calculatedChecksum = string.Empty; + var calculatedChecksum = string.Empty; var hash = StreamUtilities.ComputeHash(_msiStream, out _); if (hash != null) calculatedChecksum = string.Join(string.Empty, hash.Select(b => $"{b:x2}")); - // Check if calculatedChecksum matches what is in chcupdates.xml + // Check if calculatedChecksum matches what is in xcupdates.xml if (!_checksum.Equals(calculatedChecksum, StringComparison.InvariantCultureIgnoreCase)) - throw new Exception(Messages.UPDATE_CLIENT_INVALID_CHECKSUM ); + throw new Exception(Messages.UPDATE_CLIENT_INVALID_CHECKSUM); bool valid; try { // Check digital signature of .msi - using (var basicSigner = X509Certificate.CreateFromSignedFile(_outputPathAndFileName)) + using (var basicSigner = X509Certificate.CreateFromSignedFile(OutputPathAndFileName)) { using (var cert = new X509Certificate2(basicSigner)) valid = cert.Verify(); @@ -267,43 +333,23 @@ private void ValidateMsi() throw new Exception(Messages.UPDATE_CLIENT_INVALID_DIGITAL_CERTIFICATE); } - private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + public override void ReleaseDownloadedContent(bool deleteDownloadedContent = false) { - int pc = (int)(95.0 * e.BytesReceived / e.TotalBytesToReceive); - var descr = string.Format(Messages.DOWNLOAD_CLIENT_INSTALLER_ACTION_PROGRESS_DESCRIPTION, _updateName, - Util.DiskSizeString(e.BytesReceived, "F1"), - Util.DiskSizeString(e.TotalBytesToReceive)); - ByteProgressDescription = descr; - Tick(pc, descr); - } - - private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) - { - if (e.Cancelled && _updateDownloadState == DownloadState.Error) // cancelled due to network connectivity issue (see NetworkAvailabilityChanged) - return; - - if (e.Cancelled) - { - _updateDownloadState = DownloadState.Cancelled; - log.DebugFormat("Client update '{0}' download cancelled by the user", _updateName); - return; - } - - if (e.Error != null) - { - _updateDownloadError = e.Error; - log.DebugFormat("Client update '{0}' download failed", _updateName); - _updateDownloadState = DownloadState.Error; - return; - } - - _updateDownloadState = DownloadState.Completed; - log.DebugFormat("Client update '{0}' download completed successfully", _updateName); + _msiStream.Dispose(); + base.ReleaseDownloadedContent(deleteDownloadedContent); } + } - public override void RecomputeCanCancel() + public class DownloadSourceAction : DownloadFileAction + { + public DownloadSourceAction(string sourceName, Uri uri, string outputFileName) + : base(sourceName, + uri, + outputFileName, + string.Format(Messages.DOWNLOAD_CLIENT_SOURCE_ACTION_TITLE, sourceName), + false) { - CanCancel = !Cancelling && !IsCompleted && (_updateDownloadState == DownloadState.InProgress); + Description = string.Format(Messages.DOWNLOAD_CLIENT_SOURCE_ACTION_DESCRIPTION, sourceName); } } } diff --git a/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs b/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs index 39c4fd3cca..39ea0db635 100644 --- a/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs +++ b/XenModel/Actions/Updates/DownloadUpdatesXmlAction.cs @@ -105,6 +105,7 @@ private void GetXenCenterVersions(XmlDocument xdoc) bool latest = false; bool latestCr = false; string url = string.Empty; + string sourceUrl = string.Empty; string timestamp = string.Empty; string checksum = string.Empty; @@ -120,13 +121,15 @@ private void GetXenCenterVersions(XmlDocument xdoc) latestCr = attrib.Value.ToUpperInvariant() == bool.TrueString.ToUpperInvariant(); else if (attrib.Name == "url") url = attrib.Value; + else if (attrib.Name == "sourceUrl") + sourceUrl = attrib.Value; else if (attrib.Name == "timestamp") timestamp = attrib.Value; else if (attrib.Name == "checksum") checksum = attrib.Value; } - ClientVersions.Add(new ClientVersion(versionLang, name, latest, latestCr, url, timestamp, checksum)); + ClientVersions.Add(new ClientVersion(versionLang, name, latest, latestCr, url, timestamp, checksum, sourceUrl)); } } } diff --git a/XenModel/InvisibleMessages.Designer.cs b/XenModel/InvisibleMessages.Designer.cs index 56caace06a..89188031cb 100644 --- a/XenModel/InvisibleMessages.Designer.cs +++ b/XenModel/InvisibleMessages.Designer.cs @@ -150,15 +150,6 @@ public static string LOCALE { } } - /// - /// Looks up a localized string similar to https://www.xenserver.com/downloads. - /// - public static string OUT_OF_DATE_WEBSITE { - get { - return ResourceManager.GetString("OUT_OF_DATE_WEBSITE", resourceCulture); - } - } - /// /// Looks up a localized string similar to https://github.com/xenserver/xencenter-samples. /// @@ -212,5 +203,14 @@ public static string UPSELL_LEARNMOREURL_TRIAL { return ResourceManager.GetString("UPSELL_LEARNMOREURL_TRIAL", resourceCulture); } } + + /// + /// Looks up a localized string similar to https://www.xenserver.com/downloads. + /// + public static string WEBSITE_DOWNLOADS { + get { + return ResourceManager.GetString("WEBSITE_DOWNLOADS", resourceCulture); + } + } } } diff --git a/XenModel/InvisibleMessages.resx b/XenModel/InvisibleMessages.resx index 3ff214dac4..7c1bdf9f2a 100644 --- a/XenModel/InvisibleMessages.resx +++ b/XenModel/InvisibleMessages.resx @@ -147,9 +147,6 @@ en-US - - https://www.xenserver.com/downloads - https://github.com/xenserver/xencenter-samples @@ -168,4 +165,7 @@ https://www.xenserver.com/ + + https://www.xenserver.com/downloads + diff --git a/XenModel/Messages.Designer.cs b/XenModel/Messages.Designer.cs index c9719a457a..bf9759448b 100755 --- a/XenModel/Messages.Designer.cs +++ b/XenModel/Messages.Designer.cs @@ -14058,15 +14058,6 @@ public static string DOWNLOAD_CLIENT_INSTALLER_ACTION_DESCRIPTION { } } - /// - /// Looks up a localized string similar to Downloading {0} installer ({1} of {2}).... - /// - public static string DOWNLOAD_CLIENT_INSTALLER_ACTION_PROGRESS_DESCRIPTION { - get { - return ResourceManager.GetString("DOWNLOAD_CLIENT_INSTALLER_ACTION_PROGRESS_DESCRIPTION", resourceCulture); - } - } - /// /// Looks up a localized string similar to Download {0} installer. /// @@ -14085,6 +14076,24 @@ public static string DOWNLOAD_CLIENT_INSTALLER_MSI_NOT_FOUND { } } + /// + /// Looks up a localized string similar to Downloading {0} source code. + /// + public static string DOWNLOAD_CLIENT_SOURCE_ACTION_DESCRIPTION { + get { + return ResourceManager.GetString("DOWNLOAD_CLIENT_SOURCE_ACTION_DESCRIPTION", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download {0} source code. + /// + public static string DOWNLOAD_CLIENT_SOURCE_ACTION_TITLE { + get { + return ResourceManager.GetString("DOWNLOAD_CLIENT_SOURCE_ACTION_TITLE", resourceCulture); + } + } + /// /// Looks up a localized string similar to Download.... /// @@ -14094,6 +14103,33 @@ public static string DOWNLOAD_ELLIPSES { } } + /// + /// Looks up a localized string similar to The downloaded file can no longer be found.. + /// + public static string DOWNLOAD_FILE_ACTION_NOT_FOUND { + get { + return ResourceManager.GetString("DOWNLOAD_FILE_ACTION_NOT_FOUND", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Downloading {0} ({1} of {2}).... + /// + public static string DOWNLOAD_FILE_ACTION_PROGRESS_DESCRIPTION { + get { + return ResourceManager.GetString("DOWNLOAD_FILE_ACTION_PROGRESS_DESCRIPTION", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Download latest {0} source code. + /// + public static string DOWNLOAD_LATEST_SOURCE { + get { + return ResourceManager.GetString("DOWNLOAD_LATEST_SOURCE", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} is now available. Download the latest at the {1} downloads website.. /// @@ -14103,6 +14139,15 @@ public static string DOWNLOAD_LATEST_XS_BODY { } } + /// + /// Looks up a localized string similar to Download {0} v{1} source code. + /// + public static string DOWNLOAD_SOURCE { + get { + return ResourceManager.GetString("DOWNLOAD_SOURCE", resourceCulture); + } + } + /// /// Looks up a localized string similar to Downloading update from '{0}' ({1} of {2}).... /// diff --git a/XenModel/Messages.resx b/XenModel/Messages.resx index 4a08d6a28e..c0ca9f5923 100755 --- a/XenModel/Messages.resx +++ b/XenModel/Messages.resx @@ -4965,21 +4965,36 @@ Plugging in untrustworthy USB devices to your computer may put your computer at Downloading {0} installer - - Downloading {0} installer ({1} of {2})... - Download {0} installer The downloaded installer can no longer be found. + + Downloading {0} source code + + + Download {0} source code + Download... + + The downloaded file can no longer be found. + + + Downloading {0} ({1} of {2})... + + + Download latest {0} source code + {0} is now available. Download the latest at the {1} downloads website. + + Download {0} v{1} source code + Downloading update from '{0}' ({1} of {2})... diff --git a/XenModel/XenModel.csproj b/XenModel/XenModel.csproj index 13349a2b82..b0b30a5cab 100755 --- a/XenModel/XenModel.csproj +++ b/XenModel/XenModel.csproj @@ -131,7 +131,7 @@ - + diff --git a/scripts/xenadmin-build.ps1 b/scripts/xenadmin-build.ps1 index 59ec9f3e50..489a06f0b5 100644 --- a/scripts/xenadmin-build.ps1 +++ b/scripts/xenadmin-build.ps1 @@ -310,18 +310,20 @@ $xmlFormat=@" url="{2}" checksum="{3}" value="{4}" + sourceUrl="{5}" /> "@ $msi_url = $XC_UPDATES_URL -replace "XCUpdates.xml","$appName.msi" +$source_url = $XC_UPDATES_URL -replace "XCUpdates.xml","$appName-source.zip" $date=(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") $productVersion = "$BRANDING_XC_PRODUCT_VERSION.$buildNumber" $productFullName = "$appName $productVersion" -$prodXml = [string]::Format($xmlFormat, $productFullName, $date, $msi_url, $msi_checksum, $productVersion) -$testXml = [string]::Format($xmlFormat, $productFullName, $date, "@DEV_MSI_URL_PLACEHOLDER@", $msi_checksum, $productVersion) +$prodXml = [string]::Format($xmlFormat, $productFullName, $date, $msi_url, $msi_checksum, $productVersion, $source_url) +$testXml = [string]::Format($xmlFormat, $productFullName, $date, "@DEV_MSI_URL_PLACEHOLDER@", $msi_checksum, $productVersion, "@DEV_SRC_URL_PLACEHOLDER@") Write-Host $prodXml Write-Host $testXml @@ -331,7 +333,9 @@ Write-Host $testXml $bomLessEncoding = New-Object System.Text.UTF8Encoding $false Write-Host "INFO: Generating XCUpdates.xml" + [System.IO.File]::WriteAllLines("$OUTPUT_DIR\XCUpdates.xml", $prodXml, $bomLessEncoding) Write-Host "INFO: Generating stage-test-XCUpdates.xml. URL is a placeholder value" + [System.IO.File]::WriteAllLines("$OUTPUT_DIR\stage-test-XCUpdates.xml", $testXml, $bomLessEncoding)