You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
11 KiB
356 lines
11 KiB
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
|
// software and associated documentation files (the "Software"), to deal in the Software
|
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all copies or
|
|
// substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Data;
|
|
using System.Windows.Input;
|
|
using System.Windows.Navigation;
|
|
using System.Xml.Linq;
|
|
|
|
using ICSharpCode.AvalonEdit.Rendering;
|
|
using ICSharpCode.Decompiler;
|
|
using ICSharpCode.ILSpy.Properties;
|
|
using ICSharpCode.ILSpy.TextView;
|
|
|
|
using OSVersionHelper;
|
|
|
|
namespace ICSharpCode.ILSpy
|
|
{
|
|
[ExportMainMenuCommand(Menu = nameof(Resources._Help), Header = nameof(Resources._About), MenuOrder = 99999)]
|
|
sealed class AboutPage : SimpleCommand
|
|
{
|
|
public override void Execute(object parameter)
|
|
{
|
|
MainWindow.Instance.NavigateTo(
|
|
new RequestNavigateEventArgs(new Uri("resource://aboutpage"), null),
|
|
inNewTabPage: true
|
|
);
|
|
}
|
|
|
|
static readonly Uri UpdateUrl = new Uri("https://ilspy.net/updates.xml");
|
|
const string band = "stable";
|
|
|
|
static AvailableVersionInfo latestAvailableVersion;
|
|
|
|
public static void Display(DecompilerTextView textView)
|
|
{
|
|
AvalonEditTextOutput output = new AvalonEditTextOutput() {
|
|
Title = Resources.About,
|
|
EnableHyperlinks = true
|
|
};
|
|
output.WriteLine(Resources.ILSpyVersion + RevisionClass.FullVersion);
|
|
if (WindowsVersionHelper.HasPackageIdentity)
|
|
{
|
|
output.WriteLine($"Package Name: {WindowsVersionHelper.GetPackageFamilyName()}");
|
|
}
|
|
else
|
|
{// if we're running in an MSIX, updates work differently
|
|
output.AddUIElement(
|
|
delegate {
|
|
StackPanel stackPanel = new StackPanel();
|
|
stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
|
|
stackPanel.Orientation = Orientation.Horizontal;
|
|
if (latestAvailableVersion == null)
|
|
{
|
|
AddUpdateCheckButton(stackPanel, textView);
|
|
}
|
|
else
|
|
{
|
|
// we already retrieved the latest version sometime earlier
|
|
ShowAvailableVersion(latestAvailableVersion, stackPanel);
|
|
}
|
|
CheckBox checkBox = new CheckBox();
|
|
checkBox.Margin = new Thickness(4);
|
|
checkBox.Content = Resources.AutomaticallyCheckUpdatesEveryWeek;
|
|
UpdateSettings settings = new UpdateSettings(ILSpySettings.Load());
|
|
checkBox.SetBinding(CheckBox.IsCheckedProperty, new Binding("AutomaticUpdateCheckEnabled") { Source = settings });
|
|
return new StackPanel {
|
|
Margin = new Thickness(0, 4, 0, 0),
|
|
Cursor = Cursors.Arrow,
|
|
Children = { stackPanel, checkBox }
|
|
};
|
|
});
|
|
output.WriteLine();
|
|
}
|
|
|
|
foreach (var plugin in App.ExportProvider.GetExportedValues<IAboutPageAddition>())
|
|
plugin.Write(output);
|
|
output.WriteLine();
|
|
output.Address = new Uri("resource://AboutPage");
|
|
using (Stream s = typeof(AboutPage).Assembly.GetManifestResourceStream(typeof(AboutPage), Resources.ILSpyAboutPageTxt))
|
|
{
|
|
using (StreamReader r = new StreamReader(s))
|
|
{
|
|
string line;
|
|
while ((line = r.ReadLine()) != null)
|
|
{
|
|
output.WriteLine(line);
|
|
}
|
|
}
|
|
}
|
|
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("MIT License", "resource:license.txt"));
|
|
output.AddVisualLineElementGenerator(new MyLinkElementGenerator("third-party notices", "resource:third-party-notices.txt"));
|
|
textView.ShowText(output);
|
|
}
|
|
|
|
sealed class MyLinkElementGenerator : LinkElementGenerator
|
|
{
|
|
readonly Uri uri;
|
|
|
|
public MyLinkElementGenerator(string matchText, string url) : base(new Regex(Regex.Escape(matchText)))
|
|
{
|
|
this.uri = new Uri(url);
|
|
this.RequireControlModifierForClick = false;
|
|
}
|
|
|
|
protected override Uri GetUriFromMatch(Match match)
|
|
{
|
|
return uri;
|
|
}
|
|
}
|
|
|
|
static void AddUpdateCheckButton(StackPanel stackPanel, DecompilerTextView textView)
|
|
{
|
|
Button button = ThemeManager.Current.CreateButton();
|
|
button.Content = Resources.CheckUpdates;
|
|
button.Cursor = Cursors.Arrow;
|
|
stackPanel.Children.Add(button);
|
|
|
|
button.Click += async delegate {
|
|
button.Content = Resources.Checking;
|
|
button.IsEnabled = false;
|
|
|
|
try
|
|
{
|
|
AvailableVersionInfo vInfo = await GetLatestVersionAsync();
|
|
stackPanel.Children.Clear();
|
|
ShowAvailableVersion(vInfo, stackPanel);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput();
|
|
exceptionOutput.WriteLine(ex.ToString());
|
|
textView.ShowText(exceptionOutput);
|
|
}
|
|
};
|
|
}
|
|
|
|
static readonly Version currentVersion = new Version(RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision);
|
|
|
|
static void ShowAvailableVersion(AvailableVersionInfo availableVersion, StackPanel stackPanel)
|
|
{
|
|
if (currentVersion == availableVersion.Version)
|
|
{
|
|
stackPanel.Children.Add(
|
|
new Image {
|
|
Width = 16, Height = 16,
|
|
Source = Images.OK,
|
|
Margin = new Thickness(4, 0, 4, 0)
|
|
});
|
|
stackPanel.Children.Add(
|
|
new TextBlock {
|
|
Text = Resources.UsingLatestRelease,
|
|
VerticalAlignment = VerticalAlignment.Bottom
|
|
});
|
|
}
|
|
else if (currentVersion < availableVersion.Version)
|
|
{
|
|
stackPanel.Children.Add(
|
|
new TextBlock {
|
|
Text = string.Format(Resources.VersionAvailable, availableVersion.Version),
|
|
Margin = new Thickness(0, 0, 8, 0),
|
|
VerticalAlignment = VerticalAlignment.Bottom
|
|
});
|
|
if (availableVersion.DownloadUrl != null)
|
|
{
|
|
Button button = ThemeManager.Current.CreateButton();
|
|
button.Content = Resources.Download;
|
|
button.Cursor = Cursors.Arrow;
|
|
button.Click += delegate {
|
|
MainWindow.OpenLink(availableVersion.DownloadUrl);
|
|
};
|
|
stackPanel.Children.Add(button);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stackPanel.Children.Add(new TextBlock { Text = Resources.UsingNightlyBuildNewerThanLatestRelease });
|
|
}
|
|
}
|
|
|
|
static async Task<AvailableVersionInfo> GetLatestVersionAsync()
|
|
{
|
|
WebClient wc = new WebClient();
|
|
IWebProxy systemWebProxy = WebRequest.GetSystemWebProxy();
|
|
systemWebProxy.Credentials = CredentialCache.DefaultCredentials;
|
|
wc.Proxy = systemWebProxy;
|
|
|
|
string data = await wc.DownloadStringTaskAsync(UpdateUrl);
|
|
|
|
XDocument doc = XDocument.Load(new StringReader(data));
|
|
var bands = doc.Root.Elements("band");
|
|
var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == band) ?? bands.First();
|
|
Version version = new Version((string)currentBand.Element("latestVersion"));
|
|
string url = (string)currentBand.Element("downloadUrl");
|
|
if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal)))
|
|
url = null; // don't accept non-urls
|
|
|
|
latestAvailableVersion = new AvailableVersionInfo { Version = version, DownloadUrl = url };
|
|
return latestAvailableVersion;
|
|
}
|
|
|
|
sealed class AvailableVersionInfo
|
|
{
|
|
public Version Version;
|
|
public string DownloadUrl;
|
|
}
|
|
|
|
sealed class UpdateSettings : INotifyPropertyChanged
|
|
{
|
|
public UpdateSettings(ILSpySettings spySettings)
|
|
{
|
|
XElement s = spySettings["UpdateSettings"];
|
|
this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true;
|
|
try
|
|
{
|
|
this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck");
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
// avoid crashing on settings files invalid due to
|
|
// https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2
|
|
}
|
|
}
|
|
|
|
bool automaticUpdateCheckEnabled;
|
|
|
|
public bool AutomaticUpdateCheckEnabled {
|
|
get { return automaticUpdateCheckEnabled; }
|
|
set {
|
|
if (automaticUpdateCheckEnabled != value)
|
|
{
|
|
automaticUpdateCheckEnabled = value;
|
|
Save();
|
|
OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled));
|
|
}
|
|
}
|
|
}
|
|
|
|
DateTime? lastSuccessfulUpdateCheck;
|
|
|
|
public DateTime? LastSuccessfulUpdateCheck {
|
|
get { return lastSuccessfulUpdateCheck; }
|
|
set {
|
|
if (lastSuccessfulUpdateCheck != value)
|
|
{
|
|
lastSuccessfulUpdateCheck = value;
|
|
Save();
|
|
OnPropertyChanged(nameof(LastSuccessfulUpdateCheck));
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Save()
|
|
{
|
|
XElement updateSettings = new XElement("UpdateSettings");
|
|
updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled));
|
|
if (lastSuccessfulUpdateCheck != null)
|
|
updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck));
|
|
ILSpySettings.SaveSettings(updateSettings);
|
|
}
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
void OnPropertyChanged(string propertyName)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// If automatic update checking is enabled, checks if there are any updates available.
|
|
/// Returns the download URL if an update is available.
|
|
/// Returns null if no update is available, or if no check was performed.
|
|
/// </summary>
|
|
public static async Task<string> CheckForUpdatesIfEnabledAsync(ILSpySettings spySettings)
|
|
{
|
|
UpdateSettings s = new UpdateSettings(spySettings);
|
|
|
|
// If we're in an MSIX package, updates work differently
|
|
if (s.AutomaticUpdateCheckEnabled && !WindowsVersionHelper.HasPackageIdentity)
|
|
{
|
|
// perform update check if we never did one before;
|
|
// or if the last check wasn't in the past 7 days
|
|
if (s.LastSuccessfulUpdateCheck == null
|
|
|| s.LastSuccessfulUpdateCheck < DateTime.UtcNow.AddDays(-7)
|
|
|| s.LastSuccessfulUpdateCheck > DateTime.UtcNow)
|
|
{
|
|
return await CheckForUpdateInternal(s);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static Task<string> CheckForUpdatesAsync(ILSpySettings spySettings)
|
|
{
|
|
UpdateSettings s = new UpdateSettings(spySettings);
|
|
return CheckForUpdateInternal(s);
|
|
}
|
|
|
|
static async Task<string> CheckForUpdateInternal(UpdateSettings s)
|
|
{
|
|
try
|
|
{
|
|
var v = await GetLatestVersionAsync();
|
|
s.LastSuccessfulUpdateCheck = DateTime.UtcNow;
|
|
if (v.Version > currentVersion)
|
|
return v.DownloadUrl;
|
|
else
|
|
return null;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// ignore errors getting the version info
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Interface that allows plugins to extend the about page.
|
|
/// </summary>
|
|
public interface IAboutPageAddition
|
|
{
|
|
void Write(ISmartTextOutput textOutput);
|
|
}
|
|
}
|