Example #1
0
        /// <summary>
        /// Parse the app cast XML string into a list of <see cref="AppCastItem"/> objects.
        /// When complete, the Items list should contain the parsed information
        /// as <see cref="AppCastItem"/> objects.
        /// </summary>
        /// <param name="appcast">the non-null string XML app cast</param>
        private void ParseAppCast(string appcast)
        {
            const string itemNode = "item";

            Items.Clear();

            XDocument doc     = XDocument.Parse(appcast);
            var       rss     = doc?.Element("rss");
            var       channel = rss?.Element("channel");

            Title    = channel?.Element("title")?.Value ?? string.Empty;
            Language = channel?.Element("language")?.Value ?? "en";

            var items = doc.Descendants(itemNode);

            foreach (var item in items)
            {
                var currentItem = AppCastItem.Parse(_config.InstalledVersion, _config.ApplicationName, _castUrl, item, _logWriter);
                _logWriter.PrintMessage("Found an item in the app cast: version {0} ({1}) -- os = {2}",
                                        currentItem?.Version, currentItem?.ShortVersion, currentItem.OperatingSystemString);
                Items.Add(currentItem);
            }

            // sort versions in reverse order
            Items.Sort((item1, item2) => - 1 * item1.CompareTo(item2));
        }
Example #2
0
        private async void LoadReleaseNotes(List <AppCastItem> items)
        {
            AppCastItem latestVersion = items.OrderByDescending(p => p.Version).FirstOrDefault();
            string      releaseNotes  = await _releaseNotesGrabber.DownloadAllReleaseNotes(items, latestVersion, _cancellationToken);

            wbChangeLog.Invoke((MethodInvoker) delegate
            {
                // see https://stackoverflow.com/a/15209861/3938401
                wbChangeLog.Navigate("about:blank");
                wbChangeLog.Document.OpenNew(true);
                wbChangeLog.Document.Write(releaseNotes);
                wbChangeLog.DocumentText = releaseNotes;
            });
        }
Example #3
0
        public DownloadProgressDialog(AppCastItem item, Icon applicationIcon)
        {
            InitializeComponent();

            pbxApplicationIcon.Image = applicationIcon.ToBitmap();

            Icon = applicationIcon;

            kbtnInstall.Visible = false;

            klblHeader.Text = $"Downloading { item.AppName } { item.Version }";

            klblDownloadProgress.Text = string.Empty;

            pbDownload.Maximum = 100;

            pbDownload.Minimum = 0;

            pbDownload.Step = 1;
        }
Example #4
0
        public UpdateAvailableDialog(SparkleUpdater sparkle, List <AppCastItem> items, Icon applicationIcon = null, bool isUpdateAlreadyDownloaded = false, string separatorTemplate = "", string headAddition = "")
        {
            _sparkle             = sparkle;
            _updates             = items;
            _releaseNotesGrabber = new ReleaseNotesGrabber(separatorTemplate, headAddition, sparkle);

            InitializeComponent();

            // init ui
            try
            {
                wbChangeLog.AllowWebBrowserDrop = false;
                wbChangeLog.AllowNavigation     = false;
            }
            catch (Exception ex)
            {
                _sparkle.LogWriter.PrintMessage("Error in browser init: {0}", ex.Message);
            }

            AppCastItem item = items.FirstOrDefault();

            var downloadInstallText = isUpdateAlreadyDownloaded ? "install" : "download";

            klblHeader.Text = $"A new version of { item.AppName } is available!";
            if (item != null)
            {
                var versionString = "";
                try
                {
                    // Use try/catch since Version constructor can throw an exception and we don't want to
                    // die just because the user has a malformed version string
                    Version versionObj = new Version(item.AppVersionInstalled);
                    versionString = Utilities.GetVersionString(versionObj);
                }
                catch
                {
                    versionString = "?";
                }
                klblInfo.Text = string.Format("{0} {3} is now available (you have {1}). Would you like to {2} it now?", item.AppName, versionString,
                                              downloadInstallText, item.Version);
            }
            else
            {
                // TODO: string translations (even though I guess this window should never be called with 0 app cast items...)
                klblInfo.Text = string.Format("Would you like to {0} it now?", downloadInstallText);
            }

            bool isUserMissingCriticalUpdate = items.Any(x => x.IsCriticalUpdate);

            kbtnRemindLater.Enabled = isUserMissingCriticalUpdate == false;
            kbtnSkip.Enabled        = isUserMissingCriticalUpdate == false;
            //if (isUserMissingCriticalUpdate)
            //{
            //    FormClosing += UpdateAvailableWindow_FormClosing; // no closing a critical update!
            //}

            if (applicationIcon != null)
            {
                using (Icon icon = new Icon(applicationIcon, new Size(48, 48)))
                {
                    pbxApplicationIcon.Image = icon.ToBitmap();
                }
                Icon = applicationIcon;
            }
            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationToken       = _cancellationTokenSource.Token;

            wbChangeLog.DocumentText = _releaseNotesGrabber.GetLoadingText();
            EnsureDialogShown();
            LoadReleaseNotes(items);
        }
Example #5
0
        /// <summary>
        /// Download all of the release notes provided to this function and convert them to HTML
        /// </summary>
        /// <param name="items">List of items that you want to display in the release notes</param>
        /// <param name="latestVersion">The latest version (most current version) of your releases</param>
        /// <param name="cancellationToken">Token to cancel the async download requests</param>
        /// <returns></returns>
        public virtual async Task <string> DownloadAllReleaseNotes(List <AppCastItem> items, AppCastItem latestVersion, CancellationToken cancellationToken)
        {
            _sparkle.LogWriter.PrintMessage("Preparing to initialize release notes...");
            StringBuilder sb = new StringBuilder(_initialHTML);

            foreach (AppCastItem castItem in items)
            {
                _sparkle.LogWriter.PrintMessage("Initializing release notes for {0}", castItem.Version);
                // TODO: could we optimize this by doing multiple downloads at once?
                var releaseNotes = await GetReleaseNotes(castItem, _sparkle, cancellationToken);

                sb.Append(string.Format(_separatorTemplate,
                                        castItem.Version,
                                        castItem.PublicationDate.ToString("D"), // was dd MMM yyyy
                                        releaseNotes,
                                        latestVersion.Version.Equals(castItem.Version) ? "#ABFF82" : "#AFD7FF"));
            }
            sb.Append("</body>");

            _sparkle.LogWriter.PrintMessage("Done initializing release notes!");
            return(sb.ToString());
        }
Example #6
0
        /// <summary>
        /// Grab the release notes for the given item and return their release notes
        /// in HTML format so that they can be displayed to the user.
        /// </summary>
        /// <param name="item"><see cref="AppCastItem"/>item to download the release notes for</param>
        /// <param name="sparkle"><see cref="SparkleUpdater"/> that can be used for logging information
        /// about the release notes grabbing process (or its failures)</param>
        /// <param name="cancellationToken">token that can be used to cancel a release notes
        /// grabbing operation</param>
        /// <returns></returns>
        protected virtual async Task <string> GetReleaseNotes(AppCastItem item, SparkleUpdater sparkle, CancellationToken cancellationToken)
        {
            string criticalUpdate = item.IsCriticalUpdate ? "Critical Update" : "";

            // 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))
                {
                    if (item.IsCriticalUpdate)
                    {
                        item.Description = "<p><em>" + criticalUpdate + "</em></p>" + "<br>" + item.Description;
                    }
                    return(item.Description);
                }
                else
                {
                    var md = new Markdown();
                    if (item.IsCriticalUpdate)
                    {
                        item.Description = "*" + criticalUpdate + "*" + "\n\n" + item.Description;
                    }
                    var temp = md.Transform(item.Description);
                    return(temp);
                }
            }

            // not embedded so try to release notes from the link
            if (string.IsNullOrEmpty(item.ReleaseNotesLink))
            {
                return(null);
            }

            // download release notes
            sparkle.LogWriter.PrintMessage("Downloading release notes for {0} at {1}", item.Version, item.ReleaseNotesLink);
            string notes = await DownloadReleaseNotes(item.ReleaseNotesLink, cancellationToken, sparkle);

            sparkle.LogWriter.PrintMessage("Done downloading release notes for {0}", item.Version);
            if (string.IsNullOrEmpty(notes))
            {
                return(null);
            }

            // check dsa of release notes
            if (!string.IsNullOrEmpty(item.ReleaseNotesSignature))
            {
                if (ChecksReleaseNotesSignature &&
                    _sparkle.SignatureVerifier != null &&
                    Utilities.IsSignatureNeeded(_sparkle.SignatureVerifier.SecurityMode, _sparkle.SignatureVerifier.HasValidKeyInformation()) &&
                    sparkle.SignatureVerifier.VerifySignatureOfString(item.ReleaseNotesSignature, notes) == ValidationResult.Invalid)
                {
                    return(null);
                }
            }

            // process release notes
            var extension = Path.GetExtension(item.ReleaseNotesLink);

            if (extension != null && MarkdownExtensions.Contains(extension.ToLower()))
            {
                try
                {
                    var md = new Markdown();
                    if (item.IsCriticalUpdate)
                    {
                        notes = "*" + criticalUpdate + "*" + "\n\n" + notes;
                    }
                    notes = md.Transform(notes);
                }
                catch (Exception ex)
                {
                    sparkle.LogWriter.PrintMessage("Error parsing Markdown syntax: {0}", ex.Message);
                }
            }
            return(notes);
        }
Example #7
0
 /// <summary>
 /// Constructor for UpdateResponseArgs that allows for easy setting
 /// of the result
 /// </summary>
 /// <param name="result">User's response of type UpdateAvailableResult</param>
 /// <param name="item">Item that the user is responding to an update message for</param>
 public UpdateResponseEventArgs(UpdateAvailableResult result, AppCastItem item) : base()
 {
     Result     = result;
     UpdateItem = item;
 }
Example #8
0
 /// <summary>
 /// Create download progress window
 /// </summary>
 /// <param name="item">Appcast item to download</param>
 public virtual IDownloadProgress CreateProgressWindow(AppCastItem item)
 {
     return(new DownloadProgressDialog(item, _applicationIcon));
 }