Files

254 lines
8.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using Shadowsocks.Model;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
public class UpdateChecker
{
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases";
private const string UserAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36";
private Configuration config;
public bool NewVersionFound;
public string LatestVersionNumber;
public string LatestVersionSuffix;
public string LatestVersionName;
public string LatestVersionURL;
public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted;
public const string Version = "4.0.10";
private class CheckUpdateTimer : System.Timers.Timer
{
public Configuration config;
public CheckUpdateTimer(int p) : base(p)
{
}
}
public void CheckUpdate(Configuration config, int delay)
{
CheckUpdateTimer timer = new CheckUpdateTimer(delay);
timer.AutoReset = false;
timer.Elapsed += Timer_Elapsed;
timer.config = config;
timer.Enabled = true;
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
CheckUpdateTimer timer = (CheckUpdateTimer)sender;
Configuration config = timer.config;
timer.Elapsed -= Timer_Elapsed;
timer.Enabled = false;
timer.Dispose();
CheckUpdate(config);
}
public void CheckUpdate(Configuration config)
{
this.config = config;
try
{
Logging.Debug("Checking updates...");
WebClient http = CreateWebClient();
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
try
{
string response = e.Result;
JArray result = JArray.Parse(response);
List<Asset> asserts = new List<Asset>();
if (result != null)
{
foreach (JObject release in result)
{
var isPreRelease = (bool) release["prerelease"];
if (isPreRelease && !config.checkPreRelease)
{
continue;
}
foreach (JObject asset in (JArray)release["assets"])
{
Asset ass = Asset.ParseAsset(asset);
if (ass != null)
{
ass.prerelease = isPreRelease;
if (ass.IsNewVersion(Version, config.checkPreRelease))
{
asserts.Add(ass);
}
}
}
}
}
if (asserts.Count != 0)
{
SortByVersions(asserts);
Asset asset = asserts[asserts.Count - 1];
NewVersionFound = true;
LatestVersionURL = asset.browser_download_url;
LatestVersionNumber = asset.version;
LatestVersionName = asset.name;
LatestVersionSuffix = asset.suffix == null ? "" : $"-{asset.suffix}";
startDownload();
}
else
{
Logging.Debug("No update is available");
if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
}
}
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private void startDownload()
{
try
{
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName);
WebClient http = CreateWebClient();
http.DownloadFileCompleted += Http_DownloadFileCompleted;
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName);
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private void Http_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
try
{
if (e.Error != null)
{
Logging.LogUsefulException(e.Error);
return;
}
Logging.Debug($"New version {LatestVersionNumber}{LatestVersionSuffix} found: {LatestVersionLocalName}");
if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
}
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private WebClient CreateWebClient()
{
WebClient http = new WebClient();
http.Headers.Add("User-Agent", UserAgent);
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
return http;
}
private void SortByVersions(List<Asset> asserts)
{
asserts.Sort();
}
public class Asset : IComparable<Asset>
{
public bool prerelease;
public string name;
public string version;
public string browser_download_url;
public string suffix;
public static Asset ParseAsset(JObject assertJObject)
{
var name = (string) assertJObject["name"];
Match match = Regex.Match(name, @"^Shadowsocks-(?<version>\d+(?:\.\d+)*)(?:|-(?<suffix>.+))\.\w+$",
RegexOptions.IgnoreCase);
if (match.Success)
{
string version = match.Groups["version"].Value;
var asset = new Asset
{
browser_download_url = (string) assertJObject["browser_download_url"],
name = name,
version = version
};
if (match.Groups["suffix"].Success)
{
asset.suffix = match.Groups["suffix"].Value;
}
return asset;
}
return null;
}
public bool IsNewVersion(string currentVersion, bool checkPreRelease)
{
if (prerelease && !checkPreRelease)
{
return false;
}
if (version == null)
{
return false;
}
var cmp = CompareVersion(version, currentVersion);
return cmp > 0;
}
public static int CompareVersion(string l, string r)
{
var ls = l.Split('.');
var rs = r.Split('.');
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++)
{
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0;
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0;
if (lp != rp)
{
return lp - rp;
}
}
return 0;
}
public int CompareTo(Asset other)
{
return CompareVersion(version, other.version);
}
}
}
}