private static void OnAppcastDownloadFinished(Appcast appcast) { try { AppcastDownloadFinished?.Invoke(appcast); } catch (Exception exp) { OnLogMessage("Internal error: " + exp.Message); } }
private static void OnNewUpdateAvailable(Appcast appcast) { try { NewUpdateAvailable?.Invoke(appcast); } catch { // ignore all } }
/// <summary> /// Skip current version /// IMPORTANT: Should be used ONLY in case of own implementation GUI for user iteraction /// </summary> public static void SkipThisVersion() { Appcast ac = __CurrentAppcast; if (ac != null) { __VersionToSkip = ac.Version; WriteRegistry(); } Cancel(); }
static void _downloader_DownloadFinished(bool isCanceled, Exception ex) { try { lock (Locker) { ContinueEvent.Reset(); OnDownloadFinished(isCanceled, ex); if (isCanceled || ex != null) { LastCheckTime = DateTime.Now; WriteRegistry(); DeleteTempFile(); __Downloader = null; } // start installer if (__Downloader != null && !string.IsNullOrEmpty(__Downloader.DestinationFilePath) && !string.IsNullOrEmpty(__Downloader.SourceFilePath) ) { // waiting for call 'Continue()/Cancel()' function ContinueEvent.WaitOne(); if (__IsCancelled) { DeleteTempFile(); } else { __TmpFile = Path.Combine(Path.GetDirectoryName(__Downloader.DestinationFilePath), Path.GetFileName(__Downloader.SourceFilePath)); if (File.Exists(__TmpFile)) { File.Delete(__TmpFile); } // set correct extenfion File.Move(__Downloader.DestinationFilePath, __TmpFile); WriteRegistry(); // Check DSA signature if (string.IsNullOrEmpty(__OpenSslPath) == false && __DsaPublicKey != null) { Appcast appcast = __CurrentAppcast; if (appcast == null) { OnError(new UpdaterExceptionSignatureError()); return; } if (CheckSignature(__TmpFile, appcast.Signature) == false) { OnError(new UpdaterExceptionSignatureError()); return; } } // start installer! Process.Start(__TmpFile); } } LastCheckTime = DateTime.Now; WriteRegistry(); } } finally { __CurrentAppcast = null; __Downloader = null; IsUpdateInProgress = false; } }
/// <summary> /// Perform Update /// </summary> /// <param name="isManualCheck">TRUE - user iteraction (called by user manually) /// In this case, we should ignore "versionToSkip" property /// </param> /// <param name="checkInBackground"> Do not show "Checking for updates ..." dialog </param> private static void DoCheckUpdate(bool isManualCheck = false, bool checkInBackground = false) { lock (Locker) { bool isUpdateReady = false; try { IsUpdateInProgress = true; __CurrentAppcast = null; __IsCancelled = false; ContinueEvent.Reset(); // check if downloading not finished if (__Downloader != null) { return; } // delete rubbish from previous session DeleteTempFile(); if (checkInBackground == false) { OnCheckingForUpdate(); } // read latest appcast try { if (isManualCheck && !string.IsNullOrEmpty(AppcastFilePathForManualUpdate)) { // Appcast files for automatic and manual updates are divided. // // If appcast file for manual update is available AND version of manual update is newer - we should use appcast with manual update. // Othervise - we are using 'original' appcast file for automatic updates. try { Appcast manualAppcast = null; bool isManualAppcastDownloaded = false; Task.Run(() => { try { manualAppcast = Appcast.Read(AppcastFilePathForManualUpdate); } catch { manualAppcast = null; } finally { isManualAppcastDownloaded = true; } }); var defaultAppcast = Appcast.Read(AppcastFilePath); SpinWait.SpinUntil(() => isManualAppcastDownloaded == true, 3000); if (manualAppcast != null && defaultAppcast != null && CompareVersions(defaultAppcast.Version, manualAppcast.Version)) { __CurrentAppcast = manualAppcast; } else { __CurrentAppcast = defaultAppcast; } } catch { } // ignore all } if (__CurrentAppcast == null) { __CurrentAppcast = Appcast.Read(AppcastFilePath); } } catch (UpdaterExceptionAppcastDownload ex) { if (isManualCheck) { OnError(ex); } //OnNothingToUpdate(); return; } catch (Exception) { if (isManualCheck) { OnNothingToUpdate(); } return; } finally { LastCheckTime = DateTime.Now; WriteRegistry(); } // if called function Cancel() OnAppcastDownloadFinished(__CurrentAppcast); // if called function Cancel() if (__IsCancelled) { return; } // check if we should skip this version if (string.IsNullOrEmpty(__VersionToSkip) == false && __CurrentAppcast.Version.Equals(__VersionToSkip)) { return; } if (!CompareVersions(__CurrentAppcast.Version)) { if (isManualCheck) { OnNothingToUpdate(); } return; } // Notification about new update OnNewUpdateAvailable(__CurrentAppcast); // waiting for call 'Continue()/Cancel()/SkipThisVersion()' function ContinueEvent.WaitOne(); LastCheckTime = DateTime.Now; WriteRegistry(); // if called function Cancel() if (__IsCancelled) { return; } // check if we should skip this version // if was called function SkipThisVersion() if (string.IsNullOrEmpty(__VersionToSkip) == false && __CurrentAppcast.Version.Equals(__VersionToSkip)) { return; } isUpdateReady = true; } finally { if (isUpdateReady == false) { __CurrentAppcast = null; IsUpdateInProgress = false; } } // delete rubbish from previous session DeleteTempFile(); __TmpFile = Path.GetTempFileName(); WriteRegistry(); // save current appcast __Downloader = new Downloader(__CurrentAppcast.UpdateLink, __TmpFile); __Downloader.Downloaded += _downloader_Downloaded; __Downloader.DownloadFinished += _downloader_DownloadFinished; __Downloader.Download(); } }
/// <summary> /// Download and read appcast /// </summary> /// <param name="appcastPath"></param> /// <returns></returns> internal static Appcast Read(string appcastPath) { XmlDocument myXmlDocument = new XmlDocument(); try { myXmlDocument.Load(appcastPath); } catch (Exception ex) { throw new UpdaterExceptionAppcastDownload("Error loading appcast file.\n" + ex.Message + "\n\nPlease, check internet connection", ex); } try { XPathNavigator navigator = myXmlDocument.CreateNavigator(); XmlNamespaceManager xmlns = new XmlNamespaceManager(navigator.NameTable); const string sparkleNamespaceUrl = "http://www.andymatuschak.org/xml-namespaces/sparkle"; xmlns.AddNamespace("sparkle", sparkleNamespaceUrl); if (myXmlDocument.DocumentElement == null) { throw new UpdaterExceptionAppcastDownload("Error loading appcast file."); } Appcast appcast = new Appcast(); XmlNode releaseNotesNode = myXmlDocument.DocumentElement.SelectSingleNode("/rss/channel/item/sparkle:releaseNotesLink", xmlns); if (releaseNotesNode != null) { string rn = releaseNotesNode.InnerText.Trim(); if (!string.IsNullOrEmpty(rn)) { appcast.ReleaseNotesLink = releaseNotesNode.InnerText.Trim(); } } XmlNode enclosureNode = myXmlDocument.DocumentElement.SelectSingleNode("/rss/channel/item/enclosure"); if (enclosureNode == null || enclosureNode.Attributes == null) { throw new UpdaterExceptionAppcastParsing("Not found information about update"); } XmlAttribute urlAttr = enclosureNode.Attributes["url"]; XmlAttribute versionAttr = enclosureNode.Attributes["sparkle:version"]; XmlAttribute lengthAttr = enclosureNode.Attributes["length"]; XmlAttribute dsaSignatureAttr = enclosureNode.Attributes["sparkle:dsaSignature"]; if (urlAttr == null) { throw new UpdaterExceptionAppcastParsing("Update link not defined"); } if (versionAttr == null) { throw new UpdaterExceptionAppcastParsing("Version not defined"); } if (!Uri.IsWellFormedUriString(urlAttr.InnerText.Trim(), UriKind.RelativeOrAbsolute)) { throw new UpdaterExceptionAppcastParsing("Update link error"); } appcast.UpdateLink = urlAttr.InnerText.Trim(); appcast.Version = versionAttr.InnerText.Trim(); UInt32 length; if (lengthAttr != null && UInt32.TryParse(lengthAttr.InnerText.Trim(), out length)) { appcast.Length = length; } appcast.Signature = dsaSignatureAttr.InnerText.Trim(); return(appcast); } catch (Exception ex) { throw new UpdaterExceptionAppcastParsing("Error parsing appcast file", ex); } }