private async void downloadAndDisplayAllReleaseNotes(AppCastItem[] items, AppCastItem latestVersion, string initialHTML)
        {
            _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);

                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>");

            string fullHTML = sb.ToString();

            ReleaseNotesBrowser.Invoke((MethodInvoker) delegate
            {
                // see https://stackoverflow.com/a/15209861/3938401
                ReleaseNotesBrowser.Navigate("about:blank");
                ReleaseNotesBrowser.Document.OpenNew(true);
                ReleaseNotesBrowser.Document.Write(fullHTML);
                ReleaseNotesBrowser.DocumentText = fullHTML;
            });
            _sparkle.LogWriter.PrintMessage("Done initializing release notes!");
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="item">The appcast item to use</param>
        /// <param name="applicationIcon">Your application Icon</param>
        public DownloadProgressWindow(AppCastItem 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 += DownloadProgressWindow_FormClosing;
        }
 /// <summary>
 /// Create download progress window
 /// </summary>
 /// <param name="item">Appcast item to download</param>
 /// <param name="applicationIcon">Application icon to use</param>
 public virtual IDownloadProgress CreateProgressWindow(AppCastItem item, Icon applicationIcon)
 {
     return(new DownloadProgressWindow(item, applicationIcon));
 }
        /// <summary>
        /// Form constructor for showing release notes.
        /// </summary>
        /// <param name="sparkle">The <see cref="Sparkle"/> instance to use</param>
        /// <param name="items">List of updates to show. Should contain at least one item.</param>
        /// <param name="applicationIcon">The icon to display</param>
        /// <param name="isUpdateAlreadyDownloaded">If true, make sure UI text shows that the user is about to install the file instead of download it.</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 UpdateAvailableWindow(Sparkle sparkle, AppCastItem[] items, Icon applicationIcon = null, bool isUpdateAlreadyDownloaded = false,
                                     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
            {
                ReleaseNotesBrowser.AllowWebBrowserDrop = false;
                ReleaseNotesBrowser.AllowNavigation     = false;
            }
            catch (Exception ex)
            {
                _sparkle.LogWriter.PrintMessage("Error in browser init: {0}", ex.Message);
            }

            AppCastItem item = items.FirstOrDefault();

            lblHeader.Text = lblHeader.Text.Replace("APP", item != null ? item.AppName : "the application");
            if (item != null)
            {
                lblInfoText.Text = lblInfoText.Text.Replace("APP", item.AppName + " " + item.Version);
                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 = "";
                }
                lblInfoText.Text = lblInfoText.Text.Replace("OLDVERSION", versionString);
            }
            else
            {
                // TODO: string translations (even though I guess this window should never be called with 0 app cast items...)
                lblInfoText.Text = "Would you like to [DOWNLOAD] it now?";
            }
            lblInfoText.Text = lblInfoText.Text.Replace("[DOWNLOAD]", isUpdateAlreadyDownloaded ? "install" : "download");

            AppCastItem latestVersion = items.OrderByDescending(p => p.Version).FirstOrDefault();
            string      initialHTML   = "<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8'>" + headAddition + "</head><body>";

            ReleaseNotesBrowser.DocumentText = initialHTML + "<p><em>Loading release notes...</em></p></body></html>";
            bool isUserMissingCriticalUpdate = false;

            foreach (AppCastItem castItem in items)
            {
                isUserMissingCriticalUpdate = isUserMissingCriticalUpdate | castItem.IsCriticalUpdate;
            }
            buttonRemind.Enabled = isUserMissingCriticalUpdate == false;
            skipButton.Enabled   = isUserMissingCriticalUpdate == false;
            //if (isUserMissingCriticalUpdate)
            //{
            //    FormClosing += UpdateAvailableWindow_FormClosing; // no closing a critical update!
            //}

            if (applicationIcon != null)
            {
                imgAppIcon.Image = new Icon(applicationIcon, new Size(48, 48)).ToBitmap();
                Icon             = applicationIcon;
            }
            EnsureDialogShown();
            _cancellationTokenSource = new CancellationTokenSource();
            _cancellationToken       = _cancellationTokenSource.Token;
            downloadAndDisplayAllReleaseNotes(items, latestVersion, initialHTML);
        }
        private async Task <string> GetReleaseNotes(AppCastItem item)
        {
            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 MarkdownSharp.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.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.ReleaseNotesDSASignature))
            {
                if (_sparkle.DSAChecker.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();
                    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);
        }
Beispiel #6
0
        private void Parse(XmlReader reader)
        {
            const string itemNode                 = "item";
            const string enclosureNode            = "enclosure";
            const string sparkleEnclosureNode     = "sparkle:enclosure";
            const string releaseNotesLinkNode     = "sparkle:releaseNotesLink";
            const string descriptionNode          = "description";
            const string versionAttribute         = "sparkle:version";
            const string dsaSignature             = "sparkle:dsaSignature";
            const string criticalAttribute        = "sparkle:criticalUpdate";
            const string operatingSystemAttribute = "sparkle:os";
            const string lengthAttribute          = "length";
            const string typeAttribute            = "type";
            const string urlAttribute             = "url";
            const string pubDateNode              = "pubDate";

            AppCastItem currentItem = null;

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                    case itemNode:
                        currentItem = new AppCastItem()
                        {
                            AppVersionInstalled   = _config.InstalledVersion,
                            AppName               = _config.ApplicationName,
                            UpdateSize            = 0,
                            IsCriticalUpdate      = false,
                            OperatingSystemString = "windows",
                            MIMEType              = "application/octet-stream"
                        };
                        break;

                    case releaseNotesLinkNode:
                        if (currentItem != null)
                        {
                            currentItem.ReleaseNotesDSASignature = reader.GetAttribute(dsaSignature);
                            currentItem.ReleaseNotesLink         = reader.ReadString().Trim();
                        }
                        break;

                    case descriptionNode:
                        if (currentItem != null)
                        {
                            currentItem.Description = reader.ReadString().Trim();
                        }
                        break;

                    case enclosureNode:
                    case sparkleEnclosureNode:
                        if (currentItem != null)
                        {
                            currentItem.Version              = reader.GetAttribute(versionAttribute);
                            currentItem.DownloadLink         = reader.GetAttribute(urlAttribute);
                            currentItem.DownloadDSASignature = reader.GetAttribute(dsaSignature);
                            string length = reader.GetAttribute(lengthAttribute);
                            if (length != null)
                            {
                                int size = 0;
                                if (int.TryParse(length, out size))
                                {
                                    currentItem.UpdateSize = size;
                                }
                                else
                                {
                                    currentItem.UpdateSize = 0;
                                }
                            }
                            bool   isCritical = false;
                            string critical   = reader.GetAttribute(criticalAttribute);
                            if (critical != null && critical == "true" || critical == "1")
                            {
                                isCritical = true;
                            }
                            currentItem.IsCriticalUpdate = isCritical;

                            string operatingSystem = reader.GetAttribute(operatingSystemAttribute);
                            if (operatingSystem != null && operatingSystem != "")
                            {
                                currentItem.OperatingSystemString = operatingSystem;
                            }

                            string mimeType = reader.GetAttribute(typeAttribute);
                            if (mimeType != null && mimeType != "")
                            {
                                currentItem.MIMEType = mimeType;
                            }
                        }
                        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)
                            {
                                _logWriter.PrintMessage("Cannot parse item datetime {0} with message {1}", dt, ex.Message);
                            }
                        }
                        break;
                    }
                }
                else if (reader.NodeType == XmlNodeType.EndElement)
                {
                    switch (reader.Name)
                    {
                    case itemNode:
                        _items.Add(currentItem);
                        break;
                    }
                }
            }

            // sort versions in reverse order
            _items.Sort((item1, item2) => - 1 * item1.CompareTo(item2));
        }
Beispiel #7
0
        private void Parse(XmlReader reader)
        {
            const string itemNode                 = "item";
            const string enclosureNode            = "enclosure";
            const string sparkleEnclosureNode     = "sparkle:enclosure";
            const string releaseNotesLinkNode     = "sparkle:releaseNotesLink";
            const string descriptionNode          = "description";
            const string versionAttribute         = "sparkle:version";
            const string dsaSignature             = "sparkle:dsaSignature";
            const string criticalAttribute        = "sparkle:criticalUpdate";
            const string operatingSystemAttribute = "sparkle:os";
            const string lengthAttribute          = "length";
            const string typeAttribute            = "type";
            const string urlAttribute             = "url";
            const string pubDateNode              = "pubDate";

            AppCastItem currentItem = null;

            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                    case itemNode:
                        currentItem = new AppCastItem()
                        {
                            AppVersionInstalled   = _config.InstalledVersion,
                            AppName               = _config.ApplicationName,
                            UpdateSize            = 0,
                            IsCriticalUpdate      = false,
                            OperatingSystemString = "windows",
                            MIMEType              = "application/octet-stream"
                        };
                        break;

                    case releaseNotesLinkNode:
                        if (currentItem != null)
                        {
                            currentItem.ReleaseNotesDSASignature = reader.GetAttribute(dsaSignature);
                            currentItem.ReleaseNotesLink         = reader.ReadString().Trim();
                        }
                        break;

                    case descriptionNode:
                        if (currentItem != null)
                        {
                            currentItem.Description = reader.ReadString().Trim();
                        }
                        break;

                    case enclosureNode:
                    case sparkleEnclosureNode:
                        if (currentItem != null)
                        {
                            currentItem.Version      = reader.GetAttribute(versionAttribute);
                            currentItem.DownloadLink = reader.GetAttribute(urlAttribute);
                            if (!string.IsNullOrEmpty(currentItem.DownloadLink) && !currentItem.DownloadLink.Contains("/"))
                            {
                                // Download link contains only the filename -> complete with _castUrl
                                currentItem.DownloadLink = _castUrl.Substring(0, _castUrl.LastIndexOf('/') + 1) + currentItem.DownloadLink;
                            }

                            currentItem.DownloadDSASignature = reader.GetAttribute(dsaSignature);
                            string length = reader.GetAttribute(lengthAttribute);
                            if (length != null)
                            {
                                int size = 0;
                                if (int.TryParse(length, out size))
                                {
                                    currentItem.UpdateSize = size;
                                }
                                else
                                {
                                    currentItem.UpdateSize = 0;
                                }
                            }
                            bool   isCritical = false;
                            string critical   = reader.GetAttribute(criticalAttribute);
                            if (critical != null && critical == "true" || critical == "1")
                            {
                                isCritical = true;
                            }
                            currentItem.IsCriticalUpdate = isCritical;

                            string operatingSystem = reader.GetAttribute(operatingSystemAttribute);
                            if (operatingSystem != null && operatingSystem != "")
                            {
                                currentItem.OperatingSystemString = operatingSystem;
                            }

                            string mimeType = reader.GetAttribute(typeAttribute);
                            if (mimeType != null && mimeType != "")
                            {
                                currentItem.MIMEType = mimeType;
                            }
                        }
                        break;

                    case pubDateNode:
                        if (currentItem != null)
                        {
                            // "ddd, dd MMM yyyy HH:mm:ss zzz" => Standard date format
                            //      e.g. "Sat, 26 Oct 2019 22:05:11 -05:00"
                            // "ddd, dd MMM yyyy HH:mm:ss Z" => Check for MS AppCenter Sparkle date format which ends with GMT
                            //      e.g. "Sat, 26 Oct 2019 22:05:11 GMT"
                            // "ddd, dd MMM yyyy HH:mm:ss" => Standard date format with no timezone (fallback)
                            //      e.g. "Sat, 26 Oct 2019 22:05:11"
                            string[] formats = { "ddd, dd MMM yyyy HH:mm:ss zzz", "ddd, dd MMM yyyy HH:mm:ss Z", "ddd, dd MMM yyyy HH:mm:ss" };
                            string   dt      = reader.ReadString().Trim();
                            if (DateTime.TryParseExact(dt, formats, System.Globalization.CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateValue))
                            {
                                _logWriter.PrintMessage("Converted '{0}' to {1}.", dt, dateValue);
                                currentItem.PublicationDate = dateValue;
                            }
                            else
                            {
                                _logWriter.PrintMessage("Cannot parse item datetime {0}", dt);
                            }
                        }
                        break;
                    }
                }
                else if (reader.NodeType == XmlNodeType.EndElement)
                {
                    switch (reader.Name)
                    {
                    case itemNode:
                        _items.Add(currentItem);
                        break;
                    }
                }
            }

            // sort versions in reverse order
            _items.Sort((item1, item2) => - 1 * item1.CompareTo(item2));
        }
        /// <summary>
        /// Form constructor
        /// </summary>
        /// <param name="sparkle">The <see cref="Sparkle"/> instance to use</param>
        /// <param name="items">List of updates to show</param>
        /// <param name="applicationIcon">The icon to display</param>
        /// <param name="isUpdateAlreadyDownloaded">If true, make sure UI text shows that the user is about to install the file instead of download it.</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 UpdateAvailableWindow(Sparkle sparkle, AppCastItem[] items, Icon applicationIcon = null, bool isUpdateAlreadyDownloaded = false,
                                     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
            {
                ReleaseNotesBrowser.AllowWebBrowserDrop = false;
                ReleaseNotesBrowser.AllowNavigation     = false;
            }
            catch (Exception ex)
            {
                _sparkle.LogWriter.PrintMessage("Error in browser init: {0}", ex.Message);
            }

            AppCastItem 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)));
            lblInfoText.Text = lblInfoText.Text.Replace("[DOWNLOAD]", isUpdateAlreadyDownloaded ? "install" : "download");

            if (items.Length == 0)
            {
                RemoveReleaseNotesControls();
            }
            else
            {
                AppCastItem 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>");
                bool          isUserMissingCriticalUpdate = false;
                foreach (AppCastItem castItem in items)
                {
                    isUserMissingCriticalUpdate = isUserMissingCriticalUpdate | castItem.IsCriticalUpdate;
                    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();
                ReleaseNotesBrowser.DocumentText = releaseNotes;

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

            if (applicationIcon != null)
            {
                imgAppIcon.Image = new Icon(applicationIcon, new Size(48, 48)).ToBitmap();
                Icon             = applicationIcon;
            }
            EnsureDialogShown();
        }