Merge pull request #326 from GangZhuo/issues-301
- Add an option for checking updates - download updates automatically
This commit is contained in:
@@ -1,94 +1,49 @@
|
||||
using Shadowsocks.Model;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
using SimpleJson;
|
||||
|
||||
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 LatestVersionName;
|
||||
public string LatestVersionURL;
|
||||
public event EventHandler NewVersionFound;
|
||||
public string LatestVersionLocalName;
|
||||
public event EventHandler CheckUpdateCompleted;
|
||||
|
||||
public const string Version = "2.5.8";
|
||||
|
||||
public void CheckUpdate(Configuration config)
|
||||
{
|
||||
// TODO test failures
|
||||
WebClient http = new WebClient();
|
||||
http.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36");
|
||||
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
|
||||
http.DownloadStringCompleted += http_DownloadStringCompleted;
|
||||
http.DownloadStringAsync(new Uri(UpdateURL));
|
||||
}
|
||||
this.config = config;
|
||||
|
||||
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++)
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
WebClient http = CreateWebClient();
|
||||
http.DownloadStringCompleted += http_DownloadStringCompleted;
|
||||
http.DownloadStringAsync(new Uri(UpdateURL));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public class VersionComparer : IComparer<string>
|
||||
{
|
||||
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
|
||||
public int Compare(string x, string y)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y));
|
||||
Logging.LogUsefulException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ParseVersionFromURL(string url)
|
||||
{
|
||||
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase);
|
||||
if (match.Success)
|
||||
{
|
||||
if (match.Groups.Count == 2)
|
||||
{
|
||||
return match.Groups[1].Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SortVersions(List<string> versions)
|
||||
{
|
||||
versions.Sort(new VersionComparer());
|
||||
}
|
||||
|
||||
private bool IsNewVersion(string url)
|
||||
{
|
||||
if (url.IndexOf("prerelease") >= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string version = ParseVersionFromURL(url);
|
||||
if (version == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string currentVersion = Version;
|
||||
|
||||
return CompareVersion(version, currentVersion) > 0;
|
||||
}
|
||||
|
||||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -97,7 +52,7 @@ namespace Shadowsocks.Controller
|
||||
|
||||
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
|
||||
|
||||
List<string> versions = new List<string>();
|
||||
List<Asset> asserts = new List<Asset>();
|
||||
foreach (JsonObject release in result)
|
||||
{
|
||||
if ((bool)release["prerelease"])
|
||||
@@ -106,32 +61,152 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
foreach (JsonObject asset in (JsonArray)release["assets"])
|
||||
{
|
||||
string url = (string)asset["browser_download_url"];
|
||||
if (IsNewVersion(url))
|
||||
Asset ass = new Asset();
|
||||
ass.Parse(asset);
|
||||
if (ass.IsNewVersion(Version))
|
||||
{
|
||||
versions.Add(url);
|
||||
asserts.Add(ass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (versions.Count == 0)
|
||||
if (asserts.Count != 0)
|
||||
{
|
||||
return;
|
||||
SortByVersions(asserts);
|
||||
Asset asset = asserts[asserts.Count - 1];
|
||||
NewVersionFound = true;
|
||||
LatestVersionURL = asset.browser_download_url;
|
||||
LatestVersionNumber = asset.version;
|
||||
LatestVersionName = asset.name;
|
||||
|
||||
startDownload();
|
||||
}
|
||||
// sort versions
|
||||
SortVersions(versions);
|
||||
LatestVersionURL = versions[versions.Count - 1];
|
||||
LatestVersionNumber = ParseVersionFromURL(LatestVersionURL);
|
||||
if (NewVersionFound != null)
|
||||
else if (CheckUpdateCompleted != null)
|
||||
{
|
||||
NewVersionFound(this, new EventArgs());
|
||||
CheckUpdateCompleted(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.Debug(ex.ToString());
|
||||
return;
|
||||
Logging.LogUsefulException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void startDownload()
|
||||
{
|
||||
try
|
||||
{
|
||||
string temppath = Utils.GetTempPath();
|
||||
LatestVersionLocalName = Path.Combine(temppath, 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;
|
||||
}
|
||||
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(new VersionComparer());
|
||||
}
|
||||
|
||||
public class Asset
|
||||
{
|
||||
public bool prerelease;
|
||||
public string name;
|
||||
public string version;
|
||||
public string browser_download_url;
|
||||
|
||||
public bool IsNewVersion(string currentVersion)
|
||||
{
|
||||
if (prerelease)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (version == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return CompareVersion(version, currentVersion) > 0;
|
||||
}
|
||||
|
||||
public void Parse(JsonObject asset)
|
||||
{
|
||||
name = (string)asset["name"];
|
||||
browser_download_url = (string)asset["browser_download_url"];
|
||||
version = ParseVersionFromURL(browser_download_url);
|
||||
prerelease = browser_download_url.IndexOf("prerelease") >= 0;
|
||||
}
|
||||
|
||||
private static string ParseVersionFromURL(string url)
|
||||
{
|
||||
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase);
|
||||
if (match.Success)
|
||||
{
|
||||
if (match.Groups.Count == 2)
|
||||
{
|
||||
return match.Groups[1].Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class VersionComparer : IComparer<Asset>
|
||||
{
|
||||
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
|
||||
public int Compare(Asset x, Asset y)
|
||||
{
|
||||
return Asset.CompareVersion(x.version, y.version);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,6 +280,12 @@ namespace Shadowsocks.Controller
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleCheckingUpdate(bool enabled)
|
||||
{
|
||||
_config.autoCheckUpdate = enabled;
|
||||
Configuration.Save(_config);
|
||||
}
|
||||
|
||||
protected void Reload()
|
||||
{
|
||||
// some logic in configuration updated the config when saving, we need to read it again
|
||||
|
||||
@@ -21,6 +21,9 @@ Show QRCode...=显示二维码...
|
||||
Scan QRCode from Screen...=扫描屏幕上的二维码...
|
||||
Availability Statistics=统计可用性
|
||||
Show Logs...=显示日志...
|
||||
Updates...=更新...
|
||||
Check Updates...=检查更新
|
||||
Automatically Check Updates=自动检查更新
|
||||
About...=关于...
|
||||
Quit=退出
|
||||
Edit Servers=编辑服务器
|
||||
@@ -78,7 +81,8 @@ Password can not be blank=密码不能为空
|
||||
Port out of range=端口超出范围
|
||||
Port can't be 8123=端口不能为 8123
|
||||
Shadowsocks {0} Update Found=Shadowsocks {0} 更新
|
||||
Click here to download=点击这里下载
|
||||
No update is available=没有可用的更新
|
||||
Click here to update=点击这里升级
|
||||
Shadowsocks is here=Shadowsocks 在这里
|
||||
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks
|
||||
System Proxy Enabled=系统代理已启用
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace Shadowsocks.Model
|
||||
public string pacUrl;
|
||||
public bool useOnlinePac;
|
||||
public bool availabilityStatistics;
|
||||
public bool autoCheckUpdate;
|
||||
|
||||
private static string CONFIG_FILE = "gui-config.json";
|
||||
|
||||
@@ -76,6 +77,7 @@ namespace Shadowsocks.Model
|
||||
index = 0,
|
||||
isDefault = true,
|
||||
localPort = 1080,
|
||||
autoCheckUpdate = true,
|
||||
configs = new List<Server>()
|
||||
{
|
||||
GetDefaultServer()
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Shadowsocks.View
|
||||
private ContextMenu contextMenu1;
|
||||
|
||||
private bool _isFirstRun;
|
||||
private bool _isStartupChecking;
|
||||
private MenuItem enableItem;
|
||||
private MenuItem modeItem;
|
||||
private MenuItem AutoStartupItem;
|
||||
@@ -42,6 +43,7 @@ namespace Shadowsocks.View
|
||||
private MenuItem updateFromGFWListItem;
|
||||
private MenuItem editGFWUserRuleItem;
|
||||
private MenuItem editOnlinePACItem;
|
||||
private MenuItem autoCheckUpdatesToggleItem;
|
||||
private ConfigForm configForm;
|
||||
private string _urlToOpen;
|
||||
|
||||
@@ -68,13 +70,19 @@ namespace Shadowsocks.View
|
||||
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
|
||||
|
||||
this.updateChecker = new UpdateChecker();
|
||||
updateChecker.NewVersionFound += updateChecker_NewVersionFound;
|
||||
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
|
||||
|
||||
LoadCurrentConfiguration();
|
||||
|
||||
updateChecker.CheckUpdate(controller.GetConfigurationCopy());
|
||||
Configuration config = controller.GetConfigurationCopy();
|
||||
|
||||
if (controller.GetConfigurationCopy().isDefault)
|
||||
if (config.autoCheckUpdate)
|
||||
{
|
||||
_isStartupChecking = true;
|
||||
updateChecker.CheckUpdate(config);
|
||||
}
|
||||
|
||||
if (config.isDefault)
|
||||
{
|
||||
_isFirstRun = true;
|
||||
ShowConfigForm();
|
||||
@@ -182,6 +190,11 @@ namespace Shadowsocks.View
|
||||
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
|
||||
new MenuItem("-"),
|
||||
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
|
||||
CreateMenuGroup("Updates...", new MenuItem[] {
|
||||
CreateMenuItem("Check Updates...", new EventHandler(this.checkUpdatesItem_Click)),
|
||||
new MenuItem("-"),
|
||||
this.autoCheckUpdatesToggleItem = CreateMenuItem("Automatically Check Updates", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
|
||||
}),
|
||||
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
|
||||
new MenuItem("-"),
|
||||
CreateMenuItem("Quit", new EventHandler(this.Quit_Click))
|
||||
@@ -238,17 +251,27 @@ namespace Shadowsocks.View
|
||||
ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000);
|
||||
}
|
||||
|
||||
void updateChecker_NewVersionFound(object sender, EventArgs e)
|
||||
void updateChecker_CheckUpdateCompleted(object sender, EventArgs e)
|
||||
{
|
||||
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to download"), ToolTipIcon.Info, 5000);
|
||||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
|
||||
_isFirstRun = false;
|
||||
if (updateChecker.NewVersionFound)
|
||||
{
|
||||
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000);
|
||||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
|
||||
_isFirstRun = false;
|
||||
}
|
||||
else if (!_isStartupChecking)
|
||||
{
|
||||
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000);
|
||||
_isFirstRun = false;
|
||||
}
|
||||
_isStartupChecking = false;
|
||||
}
|
||||
|
||||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
|
||||
{
|
||||
System.Diagnostics.Process.Start(updateChecker.LatestVersionURL);
|
||||
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked;
|
||||
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
|
||||
System.Diagnostics.Process.Start("explorer.exe", argument);
|
||||
}
|
||||
|
||||
|
||||
@@ -266,6 +289,7 @@ namespace Shadowsocks.View
|
||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
|
||||
localPACItem.Checked = !onlinePACItem.Checked;
|
||||
UpdatePACItemsEnabledStatus();
|
||||
UpdateUpdateMenu();
|
||||
}
|
||||
|
||||
private void UpdateServersMenu()
|
||||
@@ -343,7 +367,7 @@ namespace Shadowsocks.View
|
||||
if (_isFirstRun)
|
||||
{
|
||||
_notifyIcon.BalloonTipTitle = I18N.GetString("Shadowsocks is here");
|
||||
_notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu");
|
||||
_notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu");
|
||||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
|
||||
_notifyIcon.ShowBalloonTip(0);
|
||||
_isFirstRun = false;
|
||||
@@ -591,5 +615,23 @@ namespace Shadowsocks.View
|
||||
this.editOnlinePACItem.Enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUpdateMenu()
|
||||
{
|
||||
Configuration configuration = controller.GetConfigurationCopy();
|
||||
autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate;
|
||||
}
|
||||
|
||||
private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Configuration configuration = controller.GetConfigurationCopy();
|
||||
controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate);
|
||||
UpdateUpdateMenu();
|
||||
}
|
||||
|
||||
private void checkUpdatesItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
updateChecker.CheckUpdate(controller.GetConfigurationCopy());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -13,13 +13,13 @@ namespace test
|
||||
[TestMethod]
|
||||
public void TestCompareVersion()
|
||||
{
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1.0", "2.3.1") == 0);
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.2", "1.3") < 0);
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.2") > 0);
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.3") == 0);
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.2.1", "1.2") > 0);
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1", "2.4") < 0);
|
||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3.2", "1.3.1") > 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1.0", "2.3.1") == 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2", "1.3") < 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.2") > 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.3") == 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2.1", "1.2") > 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1", "2.4") < 0);
|
||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0);
|
||||
}
|
||||
|
||||
private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor)
|
||||
|
||||
Reference in New Issue
Block a user