Browse Source

Merge pull request #326 from GangZhuo/issues-301

- Add an option for checking updates
- download updates automatically
tags/3.0
Gang Zhuo 10 years ago
parent
commit
41c0c803cd
6 changed files with 218 additions and 89 deletions
  1. +147
    -72
      shadowsocks-csharp/Controller/Service/UpdateChecker.cs
  2. +6
    -0
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  3. +5
    -1
      shadowsocks-csharp/Data/cn.txt
  4. +2
    -0
      shadowsocks-csharp/Model/Configuration.cs
  5. +51
    -9
      shadowsocks-csharp/View/MenuViewController.cs
  6. +7
    -7
      test/UnitTest.cs

+ 147
- 72
shadowsocks-csharp/Controller/Service/UpdateChecker.cs View File

@@ -1,137 +1,212 @@
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;
try
{
WebClient http = CreateWebClient();
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
public static int CompareVersion(string l, string r)
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
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)
string response = e.Result;
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
List<Asset> asserts = new List<Asset>();
foreach (JsonObject release in result)
{
if ((bool)release["prerelease"])
{
continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
{
Asset ass = new Asset();
ass.Parse(asset);
if (ass.IsNewVersion(Version))
{
asserts.Add(ass);
}
}
}
if (asserts.Count != 0)
{
return lp - rp;
SortByVersions(asserts);
Asset asset = asserts[asserts.Count - 1];
NewVersionFound = true;
LatestVersionURL = asset.browser_download_url;
LatestVersionNumber = asset.version;
LatestVersionName = asset.name;
startDownload();
}
else if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
}
}
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
return 0;
}
public class VersionComparer : IComparer<string>
private void startDownload()
{
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
public int Compare(string x, string y)
try
{
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y));
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 static string ParseVersionFromURL(string url)
private void Http_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase);
if (match.Success)
try
{
if (match.Groups.Count == 2)
if(e.Error != null)
{
Logging.LogUsefulException(e.Error);
return;
}
if (CheckUpdateCompleted != null)
{
return match.Groups[1].Value;
CheckUpdateCompleted(this, new EventArgs());
}
}
return null;
catch (Exception ex)
{
Logging.LogUsefulException(ex);
}
}
private void SortVersions(List<string> versions)
private WebClient CreateWebClient()
{
versions.Sort(new VersionComparer());
WebClient http = new WebClient();
http.Headers.Add("User-Agent", UserAgent);
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort);
return http;
}
private bool IsNewVersion(string url)
private void SortByVersions(List<Asset> asserts)
{
if (url.IndexOf("prerelease") >= 0)
{
return false;
}
string version = ParseVersionFromURL(url);
if (version == null)
{
return false;
}
string currentVersion = Version;
return CompareVersion(version, currentVersion) > 0;
asserts.Sort(new VersionComparer());
}
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
public class Asset
{
try
public bool prerelease;
public string name;
public string version;
public string browser_download_url;
public bool IsNewVersion(string currentVersion)
{
string response = e.Result;
if (prerelease)
{
return false;
}
if (version == null)
{
return false;
}
return CompareVersion(version, currentVersion) > 0;
}
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result);
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;
}
List<string> versions = new List<string>();
foreach (JsonObject release in result)
private static string ParseVersionFromURL(string url)
{
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase);
if (match.Success)
{
if ((bool)release["prerelease"])
if (match.Groups.Count == 2)
{
continue;
}
foreach (JsonObject asset in (JsonArray)release["assets"])
{
string url = (string)asset["browser_download_url"];
if (IsNewVersion(url))
{
versions.Add(url);
}
return match.Groups[1].Value;
}
}
return null;
}
if (versions.Count == 0)
{
return;
}
// sort versions
SortVersions(versions);
LatestVersionURL = versions[versions.Count - 1];
LatestVersionNumber = ParseVersionFromURL(LatestVersionURL);
if (NewVersionFound != 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++)
{
NewVersionFound(this, new EventArgs());
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;
}
catch (Exception ex)
}
class VersionComparer : IComparer<Asset>
{
// Calls CaseInsensitiveComparer.Compare with the parameters reversed.
public int Compare(Asset x, Asset y)
{
Logging.Debug(ex.ToString());
return;
return Asset.CompareVersion(x.version, y.version);
}
}
}
}

+ 6
- 0
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -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


+ 5
- 1
shadowsocks-csharp/Data/cn.txt View File

@@ -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=系统代理已启用


+ 2
- 0
shadowsocks-csharp/Model/Configuration.cs View File

@@ -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()


+ 51
- 9
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -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
test/UnitTest.cs View File

@@ -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)


Loading…
Cancel
Save