/// <summary> /// Constructor /// </summary> /// <param name="items">List of updates to show</param> /// <param name="applicationIcon">The icon</param> /// <param name="separatorTemplate">HTML template for every single note. Use {0} = Version. {1} = Date. {2} = Note Body</param> /// <param name="headAddition">Additional text they will inserted into HTML Head. For Stylesheets.</param> public NetSparkleForm(Sparkle sparkle, NetSparkleAppCastItem[] items, Icon applicationIcon = null, string separatorTemplate = "", string headAddition = "") { _sparkle = sparkle; _updates = items; SeparatorTemplate = !string.IsNullOrEmpty(separatorTemplate) ? separatorTemplate : "<div style=\"border: #ccc 1px solid;\"><div style=\"background: {3}; padding: 5px;\"><span style=\"float: right; display:float;\">{1}</span>{0}</div><div style=\"padding: 5px;\">{2}</div></div><br>"; InitializeComponent(); // init ui try { NetSparkleBrowser.AllowWebBrowserDrop = false; NetSparkleBrowser.AllowNavigation = false; } catch (Exception ex) { Debug.WriteLine("Error in browser init: " + ex.Message); } NetSparkleAppCastItem item = items[0]; lblHeader.Text = lblHeader.Text.Replace("APP", item.AppName); lblInfoText.Text = lblInfoText.Text.Replace("APP", item.AppName + " " + item.Version); lblInfoText.Text = lblInfoText.Text.Replace("OLDVERSION", getVersion(new Version(item.AppVersionInstalled))); if (items.Length == 0) { RemoveReleaseNotesControls(); } else { NetSparkleAppCastItem latestVersion = _updates.OrderByDescending(p => p.Version).FirstOrDefault(); StringBuilder sb = new StringBuilder("<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8'>" + headAddition + "</head><body>"); foreach (NetSparkleAppCastItem castItem in items) { sb.Append(string.Format(SeparatorTemplate, castItem.Version, castItem.PublicationDate.ToString("dd MMM yyyy"), GetReleaseNotes(castItem), latestVersion.Version.Equals(castItem.Version) ? "#ABFF82" : "#AFD7FF")); } sb.Append("</body>"); string releaseNotes = sb.ToString(); NetSparkleBrowser.DocumentText = releaseNotes; } if (applicationIcon != null) { imgAppIcon.Image = new Icon(applicationIcon, new Size(48, 48)).ToBitmap(); Icon = applicationIcon; } TopMost = false; }
/// <summary> /// Gets the latest version /// </summary> /// <returns>the AppCast item corresponding to the latest version</returns> public NetSparkleAppCastItem GetLatestVersion() { NetSparkleAppCastItem latestVersion = null; if (_castUrl.StartsWith("file://")) //handy for testing { var path = _castUrl.Replace("file://", ""); using (var reader = XmlReader.Create(path)) { latestVersion = ReadAppCast(reader, latestVersion, _config.InstalledVersion); } } else { // build a http web request stream WebRequest request = WebRequest.Create(_castUrl); request.UseDefaultCredentials = true; // request the cast and build the stream WebResponse response = request.GetResponse(); using (Stream inputstream = response.GetResponseStream()) { using (XmlTextReader reader = new XmlTextReader(inputstream)) { latestVersion = ReadAppCast(reader, latestVersion, _config.InstalledVersion); } } } latestVersion.AppName = _config.ApplicationName; latestVersion.AppVersionInstalled = _config.InstalledVersion; return(latestVersion); }
private string GetReleaseNotes(NetSparkleAppCastItem item) { if (string.IsNullOrEmpty(item.ReleaseNotesLink)) { return(null); } string notes = DownloadReleaseNotes(item.ReleaseNotesLink); if (string.IsNullOrEmpty(notes)) { return(null); } var extension = Path.GetExtension(item.ReleaseNotesLink); if (extension != null && MarkDownExtension.Contains(extension.ToLower())) { try { var md = new MarkdownSharp.Markdown(); notes = md.Transform(notes); } catch (Exception ex) { Debug.WriteLine("Error parsing MarkDown syntax: " + ex.Message); } } return(notes); }
private void Parse(XmlReader reader) { NetSparkleAppCastItem currentItem = null; while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { switch (reader.Name) { case ItemNode: currentItem = new NetSparkleAppCastItem(); break; case ReleaseNotesLinkNode: if (currentItem != null) { currentItem.ReleaseNotesLink = reader.ReadString().Trim(); } break; case EnclosureNode: if (currentItem != null) { currentItem.Version = reader.GetAttribute(VersionAttribute); currentItem.DownloadLink = reader.GetAttribute(UrlAttribute); currentItem.DSASignature = reader.GetAttribute(DasSignature); } break; case PubDateNode: if (currentItem != null) { string dt = reader.ReadString().Trim(); try { currentItem.PublicationDate = DateTime.ParseExact(dt, "ddd, dd MMM yyyy HH:mm:ss zzz", System.Globalization.CultureInfo.InvariantCulture); } catch (FormatException ex) { Debug.WriteLine("Cannot parse item datetime " + dt + " with message " + ex.Message); } } break; } } else if (reader.NodeType == XmlNodeType.EndElement) { switch (reader.Name) { case ItemNode: _items.Add(currentItem); break; } } } // sort versions reserve order _items.Sort((item1, item2) => - 1 * item1.CompareTo(item2)); }
private string GetReleaseNotes(NetSparkleAppCastItem item) { // at first try to use embedded description if (!string.IsNullOrEmpty(item.Description)) { // check for markdown Regex containsHtmlRegex = new Regex(@"<\s*([^ >]+)[^>]*>.*?<\s*/\s*\1\s*>"); if (containsHtmlRegex.IsMatch(item.Description)) { return(item.Description); } else { var md = new MarkdownSharp.Markdown(); var temp = md.Transform(item.Description); return(temp); } } // no embedded so try to get external if (string.IsNullOrEmpty(item.ReleaseNotesLink)) { return(null); } // download release note string notes = DownloadReleaseNotes(item.ReleaseNotesLink); if (string.IsNullOrEmpty(notes)) { return(null); } // check dsa of release notes if (!string.IsNullOrEmpty(item.ReleaseNotesDSASignature)) { if (_sparkle.DSAVerificator.VerifyDSASignatureOfString(item.ReleaseNotesDSASignature, notes) == ValidationResult.Invalid) { return(null); } } // process release notes var extension = Path.GetExtension(item.ReleaseNotesLink); if (extension != null && MarkDownExtension.Contains(extension.ToLower())) { try { var md = new MarkdownSharp.Markdown(); notes = md.Transform(notes); } catch (Exception ex) { Debug.WriteLine("Error parsing MarkDown syntax: " + ex.Message); } } return(notes); }
/// <summary> /// This method shows the update ui and allows to perform the /// update process /// </summary> /// <param name="currentItem">the item to show the UI for</param> /// <param name="useNotificationToast"> </param> public void ShowUpdateNeededUI(NetSparkleAppCastItem currentItem, bool useNotificationToast) { if (useNotificationToast) { UIFactory.ShowToast(currentItem, _applicationIcon, OnToastClick); } else { ShowUpdateNeededUIInner(currentItem); } }
/// <summary> /// Constructor /// </summary> /// <param name="item"></param> /// <param name="applicationIcon"></param> public NetSparkleForm(NetSparkleAppCastItem item, Icon applicationIcon) { InitializeComponent(); // init ui try { NetSparkleBrowser.AllowWebBrowserDrop = false; NetSparkleBrowser.AllowNavigation = false; } catch (Exception ex) { Debug.WriteLine("Error in browser init: " + ex.Message); } _currentItem = item; lblHeader.Text = lblHeader.Text.Replace("APP", item.AppName); lblInfoText.Text = lblInfoText.Text.Replace("APP", item.AppName + " " + item.Version); lblInfoText.Text = lblInfoText.Text.Replace("OLDVERSION", item.AppVersionInstalled); if (!string.IsNullOrEmpty(item.ReleaseNotesLink)) { if (new List <string>(new[] { ".md", ".mkdn", ".mkd", ".markdown" }).Contains(Path.GetExtension(item.ReleaseNotesLink).ToLower())) { try { ShowMarkdownReleaseNotes(item); } catch (Exception) { #if DEBUG throw; #else NetSparkleBrowser.Navigate(item.ReleaseNotesLink); //just show it raw #endif } } else { NetSparkleBrowser.Navigate(item.ReleaseNotesLink); } } else { RemoveReleaseNotesControls(); } imgAppIcon.Image = applicationIcon.ToBitmap(); Icon = applicationIcon; TopMost = true; }
/// <summary> /// This method checks if an update is required. During this process the appcast /// will be downloaded and checked against the reference assembly. Ensure that /// the calling process has access to the internet and read access to the /// reference assembly. This method is also called from the background loops. /// </summary> /// <param name="config">the configuration</param> /// <param name="latestVersion">returns the latest version</param> /// <returns><c>true</c> if an update is required</returns> public UpdateStatus GetUpdateStatus(NetSparkleConfiguration config, out NetSparkleAppCastItem latestVersion) { // report ReportDiagnosticMessage("Downloading and checking appcast"); // init the appcast NetSparkleAppCast cast = new NetSparkleAppCast(_appCastUrl, config); // check if any updates are available try { latestVersion = cast.GetLatestVersion(); } catch (Exception e) { // show the exception message ReportDiagnosticMessage("Error during app cast download: " + e.Message); // just null the version info latestVersion = null; } if (latestVersion == null) { ReportDiagnosticMessage("No version information in app cast found"); return(UpdateStatus.CouldNotDetermine); } ReportDiagnosticMessage("Latest version on the server is " + latestVersion.Version); // set the last check time ReportDiagnosticMessage("Touch the last check timestamp"); config.TouchCheckTime(); // check if the available update has to be skipped if (latestVersion.Version.Equals(config.SkipThisVersion)) { ReportDiagnosticMessage("Latest update has to be skipped (user decided to skip version " + config.SkipThisVersion + ")"); return(UpdateStatus.UserSkipped); } // check if the version will be the same then the installed version Version v1 = new Version(config.InstalledVersion); Version v2 = new Version(latestVersion.Version); if (v2 <= v1) { ReportDiagnosticMessage("Installed version is valid, no update needed (" + config.InstalledVersion + ")"); return(UpdateStatus.UpdateNotAvailable); } // ok we need an update return(UpdateStatus.UpdateAvailable); }
/// <summary> /// Constructor /// </summary> /// <param name="items">List of updates to show</param> /// <param name="applicationIcon"></param> public NetSparkleForm(NetSparkleAppCastItem[] items, Icon applicationIcon) { _updates = items; SeparatorTemplate = "<div style=\"border: 1px black dashed; padding: 5px; margin-bottom: 5px; margin-top: 5px;\"><span style=\"float: right; display:float;\">{1}</span>{0}</div>"; InitializeComponent(); // init ui //try //{ // NetSparkleBrowser.AllowWebBrowserDrop = false; // NetSparkleBrowser.AllowNavigation = false; //} //catch (Exception ex) //{ // Debug.WriteLine("Error in browser init: " + ex.Message); //} NetSparkleAppCastItem item = items[0]; lblHeader.Text = lblHeader.Text.Replace("APP", item.AppName); lblInfoText.Text = lblInfoText.Text.Replace("APP", item.AppName + " " + item.Version); //lblInfoText.Text = lblInfoText.Text.Replace("OLDVERSION", item.AppVersionInstalled); if (items.Length == 0) { //RemoveReleaseNotesControls(); } else { StringBuilder sb = new StringBuilder("<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8'></head><body>"); foreach (NetSparkleAppCastItem castItem in items) { sb.Append(string.Format(SeparatorTemplate, castItem.Version, castItem.PublicationDate.ToString("dd MMM yyyy"))); sb.Append(GetReleaseNotes(castItem)); } sb.Append("</body>"); string releaseNotes = sb.ToString(); Debug.WriteLine("Release Notes:"); Debug.WriteLine(releaseNotes); //NetSparkleBrowser.DocumentText = releaseNotes; } imgAppIcon.Image = applicationIcon.ToBitmap(); Icon = applicationIcon; TopMost = true; }
/// <summary> /// Show 'toast' window to notify new version is available /// </summary> /// <param name="item">Appcast item</param> /// <param name="applicationIcon">Icon to use in window</param> /// <param name="clickHandler">handler for click</param> public virtual void ShowToast(NetSparkleAppCastItem item, Icon applicationIcon, EventHandler clickHandler) { var toast = new ToastNotifier { Tag = item, Image = { Image = applicationIcon.ToBitmap() } }; toast.ToastClicked += clickHandler; toast.Show(Resources.DefaultNetSparkleUIFactory_ToastMessage, Resources.DefaultNetSparkleUIFactory_ToastCallToAction, 5); }
/// <summary> /// Updates from appcast /// </summary> /// <param name="currentItem">the current (top-most) item in the app-cast</param> private void Update(NetSparkleAppCastItem currentItem) { if (currentItem != null) { // show the update ui if (EnableSilentMode) { InitDownloadAndInstallProcess(currentItem); } else { ShowUpdateNeededUI(currentItem, true); } } }
/// <summary> /// Constructor /// </summary> /// <param name="item"></param> /// <param name="applicationIcon">Your application Icon</param> public NetSparkleDownloadProgress(NetSparkleAppCastItem item, Icon applicationIcon) { InitializeComponent(); imgAppIcon.Image = applicationIcon.ToBitmap(); Icon = applicationIcon; // init ui btnInstallAndReLaunch.Visible = false; lblHeader.Text = lblHeader.Text.Replace("APP", item.AppName + " " + item.Version); downloadProgressLbl.Text = ""; progressDownload.Maximum = 100; progressDownload.Minimum = 0; progressDownload.Step = 1; FormClosing += NetSparkleDownloadProgress_FormClosing; }
/// <summary> /// Constructor /// </summary> /// <param name="item"></param> /// <param name="applicationIcon">Your application Icon</param> public NetSparkleDownloadProgress(NetSparkleAppCastItem item, Icon applicationIcon) { InitializeComponent(); imgAppIcon.Image = applicationIcon.ToBitmap(); Icon = applicationIcon; // init ui btnInstallAndReLaunch.Visible = false; lblHeader.Text = lblHeader.Text.Replace("APP", item.AppName + " " + item.Version); progressDownload.Maximum = 100; progressDownload.Minimum = 0; progressDownload.Step = 1; // show the right Size = new Size(Size.Width, 107); lblSecurityHint.Visible = false; }
private void ShowUpdateNeededUIInner(NetSparkleAppCastItem currentItem) { if (UserWindow == null) { // create the form UserWindow = UIFactory.CreateSparkleForm(currentItem, _applicationIcon); } UserWindow.CurrentItem = currentItem; if (HideReleaseNotes) { UserWindow.HideReleaseNotes(); } // clear if already set. UserWindow.UserResponded -= OnUserWindowUserResponded; UserWindow.UserResponded += OnUserWindowUserResponded; UserWindow.Show(); }
/// <summary> /// Starts the download process /// </summary> /// <param name="item">the appcast item to download</param> private void InitDownloadAndInstallProcess(NetSparkleAppCastItem item) { // get the filename of the download lin string[] segments = item.DownloadLink.Split('/'); string fileName = segments[segments.Length - 1]; // get temp path _downloadTempFileName = Environment.ExpandEnvironmentVariables("%temp%\\" + fileName); if (ProgressWindow == null) { ProgressWindow = UIFactory.CreateProgressWindow(item, _applicationIcon); } else { ProgressWindow.InstallAndRelaunch -= OnProgressWindowInstallAndRelaunch; } ProgressWindow.InstallAndRelaunch += OnProgressWindowInstallAndRelaunch; // set up the download client // start async download if (_webDownloadClient != null) { _webDownloadClient.DownloadProgressChanged -= ProgressWindow.OnClientDownloadProgressChanged; _webDownloadClient.DownloadFileCompleted -= OnWebDownloadClientDownloadFileCompleted; _webDownloadClient = null; } _webDownloadClient = new WebClient { UseDefaultCredentials = true }; _webDownloadClient.DownloadProgressChanged += ProgressWindow.OnClientDownloadProgressChanged; _webDownloadClient.DownloadFileCompleted += OnWebDownloadClientDownloadFileCompleted; Uri url = new Uri(item.DownloadLink); _webDownloadClient.DownloadFileAsync(url, _downloadTempFileName); ProgressWindow.ShowDialog(); }
private void ShowMarkdownReleaseNotes(NetSparkleAppCastItem item) { string contents; if (item.ReleaseNotesLink.StartsWith("file://")) //handy for testing { contents = File.ReadAllText(item.ReleaseNotesLink.Replace("file://", "")); } else { using (var webClient = new WebClient()) { // This looks overly complex (vs. simply calling webClient.DownloadString), but it ensures that // the encoding is preserved so that special characters (e.g., curly quotes) do not get clobbered. contents = (new System.IO.StreamReader(new MemoryStream(webClient.DownloadData(item.ReleaseNotesLink)), true)).ReadToEnd(); } } var md = new MarkdownSharp.Markdown(); contents = md.Transform(contents); NetSparkleBrowser.DocumentText = contents; }
private static NetSparkleAppCastItem ReadAppCast(XmlReader reader, NetSparkleAppCastItem latestVersion, string installedVersion) { NetSparkleAppCastItem currentItem = null; // The fourth segment of the version number is ignored by Windows Installer: var installedVersionV = new Version(installedVersion); var installedVersionWithoutFourthSegment = new Version(installedVersionV.Major, installedVersionV.Minor, installedVersionV.Build); while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { switch (reader.Name) { case itemNode: { currentItem = new NetSparkleAppCastItem(); break; } case releaseNotesLinkNode: { currentItem.ReleaseNotesLink = reader.ReadString().Trim(); break; } case enclosureNode: { var deltaFrom = reader.GetAttribute(deltaFromAttribute); if (deltaFrom == null || deltaFrom == installedVersionWithoutFourthSegment.ToString()) { currentItem.Version = reader.GetAttribute(versionAttribute); currentItem.DownloadLink = reader.GetAttribute(urlAttribute); currentItem.DSASignature = reader.GetAttribute(dasSignature); } break; } } } else if (reader.NodeType == XmlNodeType.EndElement) { switch (reader.Name) { case itemNode: { if (latestVersion == null) { latestVersion = currentItem; } else if (currentItem.CompareTo(latestVersion) > 0) { latestVersion = currentItem; } break; } } } } return(latestVersion); }
/// <summary> /// Create download progress window /// </summary> /// <param name="item">Appcast item to download</param> /// <param name="applicationIcon">Application icon to use</param> /// <returns></returns> public virtual INetSparkleDownloadProgress CreateProgressWindow(NetSparkleAppCastItem item, Icon applicationIcon) { return(new NetSparkleDownloadProgress(item, applicationIcon)); }
/// <summary> /// Create sparkle form implementation /// </summary> /// <param name="currentItem">App cast item to show</param> /// <param name="applicationIcon">Icon</param> /// <returns></returns> public virtual INetSparkleForm CreateSparkleForm(NetSparkleAppCastItem currentItem, Icon applicationIcon) { return(new NetSparkleForm(currentItem, applicationIcon)); }
/// <summary> /// This method will be executed as worker thread /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnWorkerDoWork(object sender, DoWorkEventArgs e) { // store the did run once feature bool goIntoLoop = true; bool checkTSP = true; bool doInitialCheck = _doInitialCheck; bool isInitialCheck = true; // start our lifecycles do { // set state Boolean bUpdateRequired = false; // notify if (CheckLoopStarted != null) { CheckLoopStarted(this); } // report status if (doInitialCheck == false) { ReportDiagnosticMessage("Initial check prohibited, going to wait"); doInitialCheck = true; goto WaitSection; } // report status ReportDiagnosticMessage("Starting update loop..."); // read the config ReportDiagnosticMessage("Reading config..."); NetSparkleConfiguration config = GetApplicationConfig(); // calc CheckTasp Boolean checkTSPInternal = checkTSP; if (isInitialCheck && checkTSPInternal) { checkTSPInternal = !_forceInitialCheck; } // check if it's ok the recheck to software state if (checkTSPInternal) { TimeSpan csp = DateTime.Now - config.LastCheckTime; if (csp < _checkFrequency) { ReportDiagnosticMessage(String.Format("Update check performed within the last {0} minutes!", _checkFrequency.TotalMinutes)); goto WaitSection; } } else { checkTSP = true; } // when sparkle will be deactivated wait an other cycle if (config.CheckForUpdate == false) { ReportDiagnosticMessage("Check for updates disabled"); goto WaitSection; } // update the runonce feature goIntoLoop = !config.DidRunOnce; // update profile information is needed UpdateSystemProfileInformation(config); // check if update is required NetSparkleAppCastItem latestVersion = null; bUpdateRequired = UpdateStatus.UpdateAvailable == GetUpdateStatus(config, out latestVersion); if (!bUpdateRequired) { goto WaitSection; } // show the update window ReportDiagnosticMessage("Update needed from version " + config.InstalledVersion + " to version " + latestVersion.Version); // send notification if needed UpdateDetectedEventArgs ev = new UpdateDetectedEventArgs { NextAction = NextUpdateAction.ShowStandardUserInterface, ApplicationConfig = config, LatestVersion = latestVersion }; if (UpdateDetected != null) { UpdateDetected(this, ev); } // check results switch (ev.NextAction) { case NextUpdateAction.PerformUpdateUnattended: { ReportDiagnosticMessage("Unattended update whished from consumer"); EnableSilentMode = true; _worker.ReportProgress(1, latestVersion); break; } case NextUpdateAction.ProhibitUpdate: { ReportDiagnosticMessage("Update prohibited from consumer"); break; } default: { ReportDiagnosticMessage("Showing Standard Update UI"); _worker.ReportProgress(1, latestVersion); break; } } WaitSection: // reset initialcheck isInitialCheck = false; // notify if (CheckLoopFinished != null) { CheckLoopFinished(this, bUpdateRequired); } // report wait statement ReportDiagnosticMessage(String.Format("Sleeping for an other {0} minutes, exit event or force update check event", _checkFrequency.TotalMinutes)); // wait for if (!goIntoLoop) { break; } // build the event array WaitHandle[] handles = new WaitHandle[1]; handles[0] = _exitHandle; // wait for any int i = WaitHandle.WaitAny(handles, _checkFrequency); if (WaitHandle.WaitTimeout == i) { ReportDiagnosticMessage(String.Format("{0} minutes are over", _checkFrequency.TotalMinutes)); continue; } // check the exit hadnle if (i == 0) { ReportDiagnosticMessage("Got exit signal"); break; } // check an other check needed if (i == 1) { ReportDiagnosticMessage("Got force update check signal"); checkTSP = false; } } while (goIntoLoop); // reset the islooping handle _loopingHandle.Reset(); }