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/CHANGES b/CHANGES index b7838460..0a52a4a2 100644 --- a/CHANGES +++ b/CHANGES @@ -1,12 +1,17 @@ -2.0.4 +2.0.5 2014-11-09 +- Fix QRCode size +- Share over LAN option +- Log to temp path instead + +2.0.4 2014-11-09 - Try to fix data corruption - Remove all configuration except x86 -2.0.3 +2.0.3 2014-11-08 - Support QRCode generation - Fix compatibility issues with some Chrome version -2.0.2 +2.0.2 2014-11-08 - Add remarks - Fix error when polipo is killed diff --git a/README.md b/README.md index 4f52c919..6ea9efc5 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ For Windows 7 and older, please download Shadowsocks-win-x.x.x.zip. For Windows 8.1 and newer, please download Shadowsocks-win-dotnet4.0-x.x.x.zip. +Notice there's a bug on .Net 4.0 version. If you encounter crashes, try using +.Net 2.0 version. Please help us resolve this issue if you have any clue. + ### Usage 1. Find Shadowsocks icon in notification tray 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/Local.cs b/shadowsocks-csharp/Controller/Local.cs old mode 100755 new mode 100644 index b27e237f..a16276d0 --- a/shadowsocks-csharp/Controller/Local.cs +++ b/shadowsocks-csharp/Controller/Local.cs @@ -10,14 +10,15 @@ namespace Shadowsocks.Controller { class Local - { - private Server config; - public bool openOnLan; + { + private Server _server; + private bool _shareOverLAN; //private Encryptor encryptor; Socket _listener; - public Local(Server config) + public Local(Configuration config) { - this.config = config; + this._server = config.GetCurrentServer(); + _shareOverLAN = config.shareOverLan; //this.encryptor = new Encryptor(config.method, config.password); } @@ -25,17 +26,17 @@ namespace Shadowsocks.Controller { try { - // Create a TCP/IP socket. - _listener = new Socket(AddressFamily.InterNetwork, - SocketType.Stream, ProtocolType.Tcp); + // Create a TCP/IP socket. + _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); IPEndPoint localEndPoint = null; - if (openOnLan) + if (_shareOverLAN) { - localEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), config.local_port); + localEndPoint = new IPEndPoint(IPAddress.Any, _server.local_port); } else { - localEndPoint = new IPEndPoint(IPAddress.Loopback, config.local_port); + localEndPoint = new IPEndPoint(IPAddress.Loopback, _server.local_port); } // Bind the socket to the local endpoint and listen for incoming connections. @@ -67,37 +68,19 @@ namespace Shadowsocks.Controller { try { - - // Get the socket that handles the client request. Socket listener = (Socket)ar.AsyncState; - //if (!listener.Connected) - //{ - // return; - //} - Socket conn = listener.EndAccept(ar); listener.BeginAccept( new AsyncCallback(AcceptCallback), listener); - // Create the state object. Handler handler = new Handler(); handler.connection = conn; - //if (encryptor.method == "table") - //{ - // handler.encryptor = encryptor; - //} - //else - //{ - // handler.encryptor = new Encryptor(config.method, config.password); - //} - handler.encryptor = EncryptorFactory.GetEncryptor(config.method, config.password); - handler.config = config; + handler.encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); + handler.config = _server; handler.Start(); - //handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, - // new AsyncCallback(ReadCallback), state); } catch { @@ -331,7 +314,7 @@ namespace Shadowsocks.Controller } else { - Console.WriteLine("bytesRead: " + bytesRead.ToString()); + //Console.WriteLine("bytesRead: " + bytesRead.ToString()); this.Close(); } } diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs new file mode 100755 index 00000000..58a994f5 --- /dev/null +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Shadowsocks.Controller +{ + public class Logging + { + public static string LogFile; + + public static bool OpenLogFile() + { + try + { + string temppath = Path.GetTempPath(); + LogFile = Path.Combine(temppath, "shadowsocks.log"); + FileStream fs = new FileStream(LogFile, FileMode.Append); + TextWriter tmp = Console.Out; + StreamWriter sw = new StreamWriter(fs); + sw.AutoFlush = true; + Console.SetOut(sw); + Console.SetError(sw); + + return true; + } + catch (IOException e) + { + Console.WriteLine(e.ToString()); + return false; + } + } + } +} diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/PACServer.cs old mode 100755 new mode 100644 index a58aaf1c..422089dc --- a/shadowsocks-csharp/Controller/PACServer.cs +++ b/shadowsocks-csharp/Controller/PACServer.cs @@ -1,4 +1,5 @@ -using Shadowsocks.Properties; +using Shadowsocks.Model; +using Shadowsocks.Properties; using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,51 +13,51 @@ namespace Shadowsocks.Controller { class PACServer { + private static int PORT = 8090; private static string PAC_FILE = "pac.txt"; - public bool openOnLan; Socket _listener; FileSystemWatcher watcher; public event EventHandler PACFileChanged; - public void Start() + public void Start(Configuration configuration) { - // Create a TCP/IP socket. - _listener = new Socket(AddressFamily.InterNetwork, - SocketType.Stream, ProtocolType.Tcp); - _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); - IPEndPoint localEndPoint = null; - if (openOnLan) + try { - localEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 8090); + // Create a TCP/IP socket. + _listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + IPEndPoint localEndPoint = null; + if (configuration.shareOverLan) + { + localEndPoint = new IPEndPoint(IPAddress.Any, PORT); + } + else + { + localEndPoint = new IPEndPoint(IPAddress.Loopback, PORT); + } + + // Bind the socket to the local endpoint and listen for incoming connections. + _listener.Bind(localEndPoint); + _listener.Listen(100); + _listener.BeginAccept( + new AsyncCallback(AcceptCallback), + _listener); + + WatchPacFile(); } - else + catch (SocketException) { - localEndPoint = new IPEndPoint(IPAddress.Loopback, 8090); + _listener.Close(); + throw; } - - // Bind the socket to the local endpoint and listen for incoming connections. - _listener.Bind(localEndPoint); - _listener.Listen(100); - _listener.BeginAccept( - new AsyncCallback(AcceptCallback), - _listener); - - WatchPacFile(); } public void Stop() { - try - { - _listener.Close(); - } - catch (Exception) - { - - throw; - } + _listener.Close(); + _listener = null; } public string TouchPACFile() @@ -78,10 +79,10 @@ namespace Shadowsocks.Controller try { Socket listener = (Socket)ar.AsyncState; + Socket conn = listener.EndAccept(ar); listener.BeginAccept( new AsyncCallback(AcceptCallback), listener); - Socket conn = listener.EndAccept(ar); conn.BeginReceive(new byte[1024], 0, 1024, 0, new AsyncCallback(ReceiveCallback), conn); @@ -115,7 +116,6 @@ namespace Shadowsocks.Controller return System.Text.Encoding.UTF8.GetString(buffer, 0, n); } } - WatchPacFile(); } private void ReceiveCallback(IAsyncResult ar) @@ -127,7 +127,9 @@ namespace Shadowsocks.Controller string pac = GetPACContent(); - string proxy = "PROXY 127.0.0.1:8123;"; + IPEndPoint localEndPoint = (IPEndPoint)conn.LocalEndPoint; + + string proxy = "PROXY " + localEndPoint.Address + ":8123;"; pac = pac.Replace("__PROXY__", proxy); diff --git a/shadowsocks-csharp/Controller/PolipoRunner.cs b/shadowsocks-csharp/Controller/PolipoRunner.cs old mode 100755 new mode 100644 index e0fdf411..a82333f5 --- a/shadowsocks-csharp/Controller/PolipoRunner.cs +++ b/shadowsocks-csharp/Controller/PolipoRunner.cs @@ -14,8 +14,9 @@ namespace Shadowsocks.Controller private Process _process; public bool openOnLan; - public void Start(Server config) + public void Start(Configuration configuration) { + Server server = configuration.GetCurrentServer(); if (_process == null) { Process[] existingPolipo = Process.GetProcessesByName("ss_polipo"); @@ -31,17 +32,10 @@ namespace Shadowsocks.Controller Console.WriteLine(e.ToString()); } } - string temppath = Path.GetTempPath(); - string polipoConfig = Resources.polipo_config; - polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", config.local_port.ToString()); - if (openOnLan) - { - polipoConfig = polipoConfig.Replace("\"127.0.0.1\"", "\"0.0.0.0\""); - } - else - { - polipoConfig = polipoConfig.Replace("\"0.0.0.0\"", "\"127.0.0.1\""); - } + string temppath = Path.GetTempPath(); + string polipoConfig = Resources.polipo_config; + polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", server.local_port.ToString()); + polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); FileManager.ByteArrayToFile(temppath + "/polipo.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); FileManager.UncompressFile(temppath + "/ss_polipo.exe", Resources.polipo_exe); @@ -50,11 +44,10 @@ namespace Shadowsocks.Controller _process.StartInfo.FileName = temppath + "/ss_polipo.exe"; _process.StartInfo.Arguments = "-c \"" + temppath + "/polipo.conf\""; _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - _process.StartInfo.UseShellExecute = false; + _process.StartInfo.UseShellExecute = true; _process.StartInfo.CreateNoWindow = true; - _process.StartInfo.RedirectStandardOutput = true; - _process.StartInfo.RedirectStandardError = true; - //process.StandardOutput + //_process.StartInfo.RedirectStandardOutput = true; + //_process.StartInfo.RedirectStandardError = true; _process.Start(); } } diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs old mode 100755 new mode 100644 index 82b4557c..bd6e74ed --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -3,6 +3,7 @@ using Shadowsocks.Model; using System; using System.Collections.Generic; using System.Text; +using System.Threading; namespace Shadowsocks.Controller { @@ -27,6 +28,7 @@ namespace Shadowsocks.Controller public event EventHandler ConfigChanged; public event EventHandler EnableStatusChanged; + public event EventHandler ShareOverLANStatusChanged; // when user clicked Edit PAC, and PAC file has already created public event EventHandler PACFileReadyToOpen; @@ -40,19 +42,15 @@ namespace Shadowsocks.Controller } openOnLan = _config.openOnLan; - polipoRunner = new PolipoRunner(); - polipoRunner.openOnLan = openOnLan; - polipoRunner.Start(_config.GetCurrentServer()); - local = new Local(_config.GetCurrentServer()); - local.openOnLan = openOnLan; - + polipoRunner = new PolipoRunner(); + polipoRunner.Start(_config); + local = new Local(_config); try { local.Start(); pacServer = new PACServer(); - pacServer.PACFileChanged += pacServer_PACFileChanged; - pacServer.openOnLan = openOnLan; - pacServer.Start(); + pacServer.PACFileChanged += pacServer_PACFileChanged; + pacServer.Start(_config); } catch (Exception e) { @@ -61,37 +59,7 @@ namespace Shadowsocks.Controller UpdateSystemProxy(); } - - public void SaveConfig(Configuration newConfig) - { - Configuration.Save(newConfig); - if (newConfig.noChange && newConfig.openOnLan == openOnLan) - { - return; - } - // some logic in configuration updated the config when saving, we need to read it again - _config = Configuration.Load(); - openOnLan = _config.openOnLan; - - local.Stop(); - polipoRunner.Stop(); - polipoRunner.openOnLan = openOnLan; - polipoRunner.Start(_config.GetCurrentServer()); - - local = new Local(_config.GetCurrentServer()); - local.openOnLan = openOnLan; - local.Start(); - - pacServer.Stop(); - pacServer.openOnLan = openOnLan; - pacServer.Start(); - - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } - } - + public Server GetCurrentServer() { return _config.GetCurrentServer(); @@ -103,6 +71,11 @@ namespace Shadowsocks.Controller return Configuration.Load(); } + public void SaveServers(List servers) + { + _config.configs = servers; + SaveConfig(_config); + } public void ToggleEnable(bool enabled) { @@ -115,6 +88,22 @@ namespace Shadowsocks.Controller } } + public void ToggleShareOverLAN(bool enabled) + { + _config.shareOverLan = enabled; + SaveConfig(_config); + if (ShareOverLANStatusChanged != null) + { + ShareOverLANStatusChanged(this, new EventArgs()); + } + } + + public void SelectServerIndex(int index) + { + _config.index = index; + SaveConfig(_config); + } + public void Stop() { if (stopped) @@ -147,6 +136,34 @@ namespace Shadowsocks.Controller return "ss://" + base64; } + + protected void SaveConfig(Configuration newConfig) + { + Configuration.Save(newConfig); + // some logic in configuration updated the config when saving, we need to read it again + _config = Configuration.Load(); + + pacServer.Stop(); + local.Stop(); + + // don't put polipoRunner.Start() before pacServer.Stop() + // or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 + // though UseShellExecute is set to true now + // http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open + polipoRunner.Stop(); + polipoRunner.Start(_config); + + local = new Local(_config); + local.Start(); + pacServer.Start(_config); + + if (ConfigChanged != null) + { + ConfigChanged(this, new EventArgs()); + } + } + + private void UpdateSystemProxy() { if (_config.enabled) 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/Data/polipo_config.txt b/shadowsocks-csharp/Data/polipo_config.txt index 7f75652b..fc500543 100755 --- a/shadowsocks-csharp/Data/polipo_config.txt +++ b/shadowsocks-csharp/Data/polipo_config.txt @@ -1,4 +1,4 @@ -proxyAddress = "127.0.0.1" +proxyAddress = "__POLIPO_BIND_IP__" socksParentProxy = "127.0.0.1:__SOCKS_PORT__" socksProxyType = socks5 diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index a501f117..4869e2c8 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -15,6 +15,7 @@ namespace Shadowsocks.Model public List configs; public int index; public bool enabled; + public bool shareOverLan; public bool isDefault; public bool openOnLan; public bool enableLog; diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs old mode 100755 new mode 100644 index 1e0bf71a..251ba424 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -46,7 +46,8 @@ namespace Shadowsocks Console.WriteLine(e.ToString()); } LoadLibrary(dllPath); - + + Logging.OpenLogFile(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ShadowsocksController controller = new ShadowsocksController(); diff --git a/shadowsocks-csharp/Properties/AssemblyInfo.cs b/shadowsocks-csharp/Properties/AssemblyInfo.cs index 02c3eba7..7351ffd1 100755 --- a/shadowsocks-csharp/Properties/AssemblyInfo.cs +++ b/shadowsocks-csharp/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.4")] +[assembly: AssemblyVersion("2.0.5")] // [assembly: AssemblyFileVersion("2.0.0")] 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.Designer.cs b/shadowsocks-csharp/View/ConfigForm.Designer.cs old mode 100755 new mode 100644 index 4ac5052b..c1187c04 --- a/shadowsocks-csharp/View/ConfigForm.Designer.cs +++ b/shadowsocks-csharp/View/ConfigForm.Designer.cs @@ -50,12 +50,14 @@ this.panel1 = new System.Windows.Forms.Panel(); this.contextMenu1 = new System.Windows.Forms.ContextMenu(); this.enableItem = new System.Windows.Forms.MenuItem(); + this.ShareOverLANItem = new System.Windows.Forms.MenuItem(); this.ServersItem = new System.Windows.Forms.MenuItem(); this.SeperatorItem = new System.Windows.Forms.MenuItem(); this.ConfigItem = new System.Windows.Forms.MenuItem(); this.menuItem4 = new System.Windows.Forms.MenuItem(); this.editPACFileItem = new System.Windows.Forms.MenuItem(); this.QRCodeItem = new System.Windows.Forms.MenuItem(); + this.ShowLogItem = new System.Windows.Forms.MenuItem(); this.aboutItem = new System.Windows.Forms.MenuItem(); this.menuItem3 = new System.Windows.Forms.MenuItem(); this.quitItem = new System.Windows.Forms.MenuItem(); @@ -64,8 +66,6 @@ this.AddButton = new System.Windows.Forms.Button(); this.ServerGroupBox = new System.Windows.Forms.GroupBox(); this.ServersListBox = new System.Windows.Forms.ListBox(); - this.openOnLanBox = new System.Windows.Forms.CheckBox(); - this.enableLogBox = new System.Windows.Forms.CheckBox(); this.tableLayoutPanel1.SuspendLayout(); this.panel1.SuspendLayout(); this.panel3.SuspendLayout(); @@ -287,10 +287,12 @@ // this.contextMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.enableItem, + this.ShareOverLANItem, this.ServersItem, this.menuItem4, this.editPACFileItem, this.QRCodeItem, + this.ShowLogItem, this.aboutItem, this.menuItem3, this.quitItem}); @@ -301,9 +303,15 @@ this.enableItem.Text = "&Enable"; this.enableItem.Click += new System.EventHandler(this.EnableItem_Click); // + // ShareOverLANItem + // + this.ShareOverLANItem.Index = 1; + this.ShareOverLANItem.Text = "Share over LAN"; + this.ShareOverLANItem.Click += new System.EventHandler(this.ShareOverLANItem_Click); + // // ServersItem // - this.ServersItem.Index = 1; + this.ServersItem.Index = 2; this.ServersItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.SeperatorItem, this.ConfigItem}); @@ -322,35 +330,41 @@ // // menuItem4 // - this.menuItem4.Index = 2; + this.menuItem4.Index = 3; this.menuItem4.Text = "-"; // // editPACFileItem // - this.editPACFileItem.Index = 3; + this.editPACFileItem.Index = 4; this.editPACFileItem.Text = "Edit &PAC File..."; this.editPACFileItem.Click += new System.EventHandler(this.EditPACFileItem_Click); // // QRCodeItem // - this.QRCodeItem.Index = 4; + this.QRCodeItem.Index = 5; this.QRCodeItem.Text = "Show &QRCode..."; this.QRCodeItem.Click += new System.EventHandler(this.QRCodeItem_Click); // + // ShowLogItem + // + this.ShowLogItem.Index = 6; + this.ShowLogItem.Text = "Show Logs..."; + this.ShowLogItem.Click += new System.EventHandler(this.ShowLogItem_Click); + // // aboutItem // - this.aboutItem.Index = 5; + this.aboutItem.Index = 7; this.aboutItem.Text = "About..."; this.aboutItem.Click += new System.EventHandler(this.AboutItem_Click); // // menuItem3 // - this.menuItem3.Index = 6; + this.menuItem3.Index = 8; this.menuItem3.Text = "-"; // // quitItem // - this.quitItem.Index = 7; + this.quitItem.Index = 9; this.quitItem.Text = "&Quit"; this.quitItem.Click += new System.EventHandler(this.Quit_Click); // @@ -404,27 +418,6 @@ this.ServersListBox.TabIndex = 5; this.ServersListBox.SelectedIndexChanged += new System.EventHandler(this.ServersListBox_SelectedIndexChanged); // - // openOnLanBox - // - this.openOnLanBox.AutoSize = true; - this.openOnLanBox.Location = new System.Drawing.Point(16, 260); - this.openOnLanBox.Name = "openOnLanBox"; - this.openOnLanBox.Size = new System.Drawing.Size(90, 17); - this.openOnLanBox.TabIndex = 7; - this.openOnLanBox.Text = "Open On Lan"; - this.openOnLanBox.UseVisualStyleBackColor = true; - // - // enableLogBox - // - this.enableLogBox.AutoSize = true; - this.enableLogBox.Location = new System.Drawing.Point(112, 260); - this.enableLogBox.Name = "enableLogBox"; - this.enableLogBox.Size = new System.Drawing.Size(80, 17); - this.enableLogBox.TabIndex = 7; - this.enableLogBox.Text = "Enable Log"; - this.enableLogBox.UseVisualStyleBackColor = true; - this.enableLogBox.CheckedChanged += new System.EventHandler(this.enableLogBox_CheckedChanged); - // // ConfigForm // this.AcceptButton = this.OKButton; @@ -433,8 +426,6 @@ this.AutoSize = true; this.CancelButton = this.MyCancelButton; this.ClientSize = new System.Drawing.Size(489, 286); - this.Controls.Add(this.enableLogBox); - this.Controls.Add(this.openOnLanBox); this.Controls.Add(this.ServersListBox); this.Controls.Add(this.ServerGroupBox); this.Controls.Add(this.panel1); @@ -446,7 +437,7 @@ this.MinimizeBox = false; this.Name = "ConfigForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Shadowsocks"; + this.Text = "Edit Servers"; this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ConfigForm_FormClosed); this.Load += new System.EventHandler(this.ConfigForm_Load); this.Shown += new System.EventHandler(this.ConfigForm_Shown); @@ -497,8 +488,8 @@ private System.Windows.Forms.TextBox RemarksTextBox; private System.Windows.Forms.Label label6; private System.Windows.Forms.MenuItem QRCodeItem; - private System.Windows.Forms.CheckBox openOnLanBox; - private System.Windows.Forms.CheckBox enableLogBox; + private System.Windows.Forms.MenuItem ShowLogItem; + private System.Windows.Forms.MenuItem ShareOverLANItem; } } diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs old mode 100755 new mode 100644 index 65aa4b39..9d7c1fe2 --- 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; @@ -29,6 +30,10 @@ namespace Shadowsocks.View controller.EnableStatusChanged += controller_EnableStatusChanged; controller.ConfigChanged += controller_ConfigChanged; controller.PACFileReadyToOpen += controller_PACFileReadyToOpen; + controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; + + this.updateChecker = new UpdateChecker(); + updateChecker.NewVersionFound += updateChecker_NewVersionFound; LoadCurrentConfiguration(); } @@ -43,6 +48,11 @@ namespace Shadowsocks.View enableItem.Checked = controller.GetConfiguration().enabled; } + void controller_ShareOverLANStatusChanged(object sender, EventArgs e) + { + ShareOverLANItem.Checked = controller.GetConfiguration().shareOverLan; + } + void controller_PACFileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e) { string argument = @"/select, " + e.Path; @@ -50,6 +60,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() { @@ -151,7 +176,7 @@ namespace Shadowsocks.View UpdateServersMenu(); enableItem.Checked = _modifiedConfiguration.enabled; - openOnLanBox.Checked = _modifiedConfiguration.openOnLan; + ShareOverLANItem.Checked = _modifiedConfiguration.shareOverLan; } private void UpdateServersMenu() @@ -192,6 +217,7 @@ namespace Shadowsocks.View { _isFirstRun = true; } + updateChecker.CheckUpdate(); } private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) @@ -258,6 +284,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; } @@ -274,8 +301,7 @@ namespace Shadowsocks.View MessageBox.Show("Please add at least one server"); return; } - _modifiedConfiguration.openOnLan = openOnLanBox.Checked; - controller.SaveConfig(_modifiedConfiguration); + controller.SaveServers(_modifiedConfiguration.configs); this.Hide(); ShowFirstTimeBalloon(); } @@ -309,6 +335,12 @@ namespace Shadowsocks.View controller.ToggleEnable(enableItem.Checked); } + private void ShareOverLANItem_Click(object sender, EventArgs e) + { + ShareOverLANItem.Checked = !ShareOverLANItem.Checked; + controller.ToggleShareOverLAN(ShareOverLANItem.Checked); + } + private void EditPACFileItem_Click(object sender, EventArgs e) { controller.TouchPACFile(); @@ -317,9 +349,14 @@ namespace Shadowsocks.View private void AServerItem_Click(object sender, EventArgs e) { MenuItem item = (MenuItem)sender; - Configuration configuration = controller.GetConfiguration(); - configuration.index = (int)item.Tag; - controller.SaveConfig(configuration); + controller.SelectServerIndex((int)item.Tag); + } + + private void ShowLogItem_Click(object sender, EventArgs e) + { + string argument = Logging.LogFile; + + System.Diagnostics.Process.Start("notepad.exe", argument); } private void ConfigForm_Shown(object sender, EventArgs e) diff --git a/shadowsocks-csharp/View/QRCodeForm.cs b/shadowsocks-csharp/View/QRCodeForm.cs index 0a0ec840..12c5b6fd 100755 --- a/shadowsocks-csharp/View/QRCodeForm.cs +++ b/shadowsocks-csharp/View/QRCodeForm.cs @@ -25,35 +25,50 @@ namespace Shadowsocks.View private void GenQR(string ssconfig) { string qrText = ssconfig; - QRCode4CS.QRCode qrCoded = new QRCode4CS.QRCode(6, QRErrorCorrectLevel.H); - qrCoded.AddData(qrText); - qrCoded.Make(); - int blockSize = 5; - Bitmap drawArea = new Bitmap((qrCoded.GetModuleCount() * blockSize), (qrCoded.GetModuleCount() * blockSize)); - for (int row = 0; row < qrCoded.GetModuleCount(); row++) + QRCode4CS.Options options = new QRCode4CS.Options(); + options.Text = qrText; + QRCode4CS.QRCode qrCoded = null; + bool success = false; + foreach (var level in new QRErrorCorrectLevel[]{QRErrorCorrectLevel.H, QRErrorCorrectLevel.Q, QRErrorCorrectLevel.M, QRErrorCorrectLevel.L}) { - for (int col = 0; col < qrCoded.GetModuleCount(); col++) + for (int i = 3; i < 10; i++) { - bool isDark = qrCoded.IsDark(row, col); - if (isDark) + try { - for (int y = 0; y < blockSize; y++) - { - int myCol = (blockSize * (col - 1)) + (y + blockSize); - for (int x = 0; x < blockSize; x++) - { - drawArea.SetPixel((blockSize * (row - 1)) + (x + blockSize), myCol, Color.Black); - } - } + options.TypeNumber = i; + options.CorrectLevel = level; + qrCoded = new QRCode4CS.QRCode(options); + qrCoded.Make(); + success = true; + break; + } + catch + { + qrCoded = null; + continue; } - else + } + if (success) + break; + } + if (qrCoded == null) + { + return; + } + int blockSize = Math.Max(200 / qrCoded.GetModuleCount(), 1); + Bitmap drawArea = new Bitmap((qrCoded.GetModuleCount() * blockSize), (qrCoded.GetModuleCount() * blockSize)); + using (Graphics g = Graphics.FromImage(drawArea)) + { + g.Clear(Color.White); + using (Brush b = new SolidBrush(Color.Black)) + { + for (int row = 0; row < qrCoded.GetModuleCount(); row++) { - for (int y = 0; y < blockSize; y++) + for (int col = 0; col < qrCoded.GetModuleCount(); col++) { - int myCol = (blockSize * (col - 1)) + (y + blockSize); - for (int x = 0; x < blockSize; x++) + if (qrCoded.IsDark(row, col)) { - drawArea.SetPixel((blockSize * (row - 1)) + (x + blockSize), myCol, Color.White); + g.FillRectangle(b, blockSize * row, blockSize * col, blockSize, blockSize); } } } diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index c1c08d22..b648b831 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -63,11 +63,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