diff --git a/.gitignore b/.gitignore index dba5eabd..82c89bb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ Backup/ -shadowsocks-csharp/bin/ -shadowsocks-csharp/obj/ +bin/ +obj/ shadowsocks-csharp/shadowsocks-csharp.csproj.user +TestResults *.suo diff --git a/shadowsocks-csharp.sln b/shadowsocks-csharp.sln index 46bde1a8..83869ae2 100755 --- a/shadowsocks-csharp.sln +++ b/shadowsocks-csharp.sln @@ -3,6 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2012 for Windows Desktop Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}" + ProjectSection(ProjectDependencies) = postProject + {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -14,6 +19,10 @@ Global {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Deploy.0 = Debug|x86 {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.ActiveCfg = Release|x86 {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.Build.0 = Release|x86 + {45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.ActiveCfg = Debug|x86 + {45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.Build.0 = Debug|x86 + {45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.ActiveCfg = Release|x86 + {45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/shadowsocks-csharp/Controller/UpdateChecker.cs b/shadowsocks-csharp/Controller/UpdateChecker.cs new file mode 100755 index 00000000..b731013d --- /dev/null +++ b/shadowsocks-csharp/Controller/UpdateChecker.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; + +namespace Shadowsocks.Controller +{ + public class UpdateChecker + { + private const string UpdateURL = "https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/10/rss"; + + public string LatestVersionNumber; + public string LatestVersionURL; + public event EventHandler NewVersionFound; + + public void CheckUpdate() + { + // TODO test failures + WebClient http = new WebClient(); + http.DownloadStringCompleted += http_DownloadStringCompleted; + http.DownloadStringAsync(new Uri(UpdateURL)); + } + + 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 class VersionComparer : IComparer + { + // Calls CaseInsensitiveComparer.Compare with the parameters reversed. + public int Compare(string x, string y) + { + return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); + } + + } + + 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 versions) + { + versions.Sort(new VersionComparer()); + } + + private bool IsNewVersion(string url) + { + // check dotnet 4.0 + AssemblyName[] references = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); + Version dotNetVersion = Environment.Version; + foreach (AssemblyName reference in references) + { + if (reference.Name == "mscorlib") + { + dotNetVersion = reference.Version; + } + } + if (dotNetVersion.Major >= 4) + { + if (url.IndexOf("dotnet4.0") < 0) + { + return false; + } + } + else + { + if (url.IndexOf("dotnet4.0") >= 0) + { + return false; + } + } + string version = ParseVersionFromURL(url); + if (version == null) + { + return false; + } + string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + return CompareVersion(version, currentVersion) > 0; + } + + private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + { + try + { + string response = e.Result; + + XmlDocument xmlDoc = new XmlDocument(); + xmlDoc.LoadXml(response); + XmlNodeList elements = xmlDoc.GetElementsByTagName("media:content"); + List versions = new List(); + foreach (XmlNode el in elements) + { + foreach (XmlAttribute attr in el.Attributes) + { + if (attr.Name == "url") + { + Console.WriteLine(attr.Value); + if (IsNewVersion(attr.Value)) + { + versions.Add(attr.Value); + } + } + } + } + if (versions.Count == 0) + { + return; + } + // sort versions + SortVersions(versions); + LatestVersionURL = versions[versions.Count - 1]; + LatestVersionNumber = ParseVersionFromURL(LatestVersionURL); + if (NewVersionFound != null) + { + NewVersionFound(this, new EventArgs()); + } + } + catch (Exception ex) + { + Console.Write(ex.ToString()); + return; + } + } + } +} diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs index 8c4f781e..d292578d 100755 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ b/shadowsocks-csharp/Properties/Resources.Designer.cs @@ -71,7 +71,7 @@ namespace Shadowsocks.Properties { } /// - /// Looks up a localized string similar to proxyAddress = "127.0.0.1" + /// Looks up a localized string similar to proxyAddress = "__POLIPO_BIND_IP__" /// ///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" ///socksProxyType = socks5 diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 2a7e5db2..8bd980b8 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -13,6 +13,7 @@ namespace Shadowsocks.View public partial class ConfigForm : Form { private ShadowsocksController controller; + private UpdateChecker updateChecker; // this is a copy of configuration that we are working on private Configuration modifiedConfiguration; @@ -30,6 +31,9 @@ namespace Shadowsocks.View controller.PACFileReadyToOpen += controller_PACFileReadyToOpen; controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; + this.updateChecker = new UpdateChecker(); + updateChecker.NewVersionFound += updateChecker_NewVersionFound; + LoadCurrentConfiguration(); } @@ -55,6 +59,21 @@ namespace Shadowsocks.View System.Diagnostics.Process.Start("explorer.exe", argument); } + void updateChecker_NewVersionFound(object sender, EventArgs e) + { + notifyIcon1.BalloonTipTitle = "Shadowsocks " + updateChecker.LatestVersionNumber + " Update Found"; + notifyIcon1.BalloonTipText = "You can click here to download"; + notifyIcon1.BalloonTipIcon = ToolTipIcon.Info; + notifyIcon1.BalloonTipClicked += notifyIcon1_BalloonTipClicked; + notifyIcon1.ShowBalloonTip(5000); + isFirstRun = false; + } + + void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) + { + Process.Start(updateChecker.LatestVersionURL); + } + private void ShowWindow() { @@ -176,6 +195,7 @@ namespace Shadowsocks.View { isFirstRun = true; } + updateChecker.CheckUpdate(); } private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) @@ -242,6 +262,7 @@ namespace Shadowsocks.View { notifyIcon1.BalloonTipTitle = "Shadowsocks is here"; notifyIcon1.BalloonTipText = "You can turn on/off Shadowsocks in the context menu"; + notifyIcon1.BalloonTipIcon = ToolTipIcon.Info; notifyIcon1.ShowBalloonTip(0); isFirstRun = false; } diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index ab31982a..08ebf02c 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -10,7 +10,7 @@ Properties Shadowsocks Shadowsocks - v2.0 + v4.0 512 @@ -21,8 +21,7 @@ 3.5 - - + Client publish\ true Disk @@ -63,12 +62,14 @@ + + diff --git a/test/Properties/AssemblyInfo.cs b/test/Properties/AssemblyInfo.cs new file mode 100755 index 00000000..2918bf7a --- /dev/null +++ b/test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("test")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f74e87ac-7e3a-444b-a1d9-8b91a674c60f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/UnitTest.cs b/test/UnitTest.cs new file mode 100755 index 00000000..c23f4c75 --- /dev/null +++ b/test/UnitTest.cs @@ -0,0 +1,22 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Shadowsocks.Controller; + +namespace test +{ + [TestClass] + public class UnitTest + { + [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); + } + } +} diff --git a/test/test.csproj b/test/test.csproj new file mode 100755 index 00000000..235846d0 --- /dev/null +++ b/test/test.csproj @@ -0,0 +1,81 @@ + + + + Debug + AnyCPU + {45913187-0685-4903-B250-DCEF0479CD86} + Library + Properties + test + test + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + bin\x86\Debug\ + x86 + + + bin\x86\Release\ + x86 + + + + + + + + + + + + + + + + + + + + + + + {8c02d2f7-7cdb-4d55-9f25-cd03ef4aa062} + shadowsocks-csharp + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file