diff --git a/appveyor.yml b/appveyor.yml index 00a792d3..91340e8a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ version: 1.0.{build} -image: Visual Studio 2015 +image: Visual Studio 2017 environment: matrix: - platform: x86 diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index b103c17a..70d5eb2f 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -2,21 +2,18 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Web; using System.Windows.Forms; -using Newtonsoft.Json; +using Shadowsocks.Controller.Service; using Shadowsocks.Controller.Strategy; using Shadowsocks.Model; -using Shadowsocks.Properties; using Shadowsocks.Util; -using System.Linq; -using Shadowsocks.Controller.Service; -using Shadowsocks.Proxy; namespace Shadowsocks.Controller { @@ -100,10 +97,7 @@ namespace Shadowsocks.Controller protected void ReportError(Exception e) { - if (Errored != null) - { - Errored(this, new ErrorEventArgs(e)); - } + Errored?.Invoke(this, new ErrorEventArgs(e)); } public Server GetCurrentServer() @@ -132,7 +126,7 @@ namespace Shadowsocks.Controller { foreach (var strategy in _strategyManager.GetStrategies()) { - if (strategy.ID == this._config.strategy) + if (strategy.ID == _config.strategy) { return strategy; } @@ -197,9 +191,13 @@ namespace Shadowsocks.Controller { try { - if (ssURL.IsNullOrEmpty() || ssURL.IsWhiteSpace()) return false; + if (ssURL.IsNullOrEmpty() || ssURL.IsWhiteSpace()) + return false; + var servers = Server.GetServers(ssURL); - if (servers == null || servers.Count == 0) return false; + if (servers == null || servers.Count == 0) + return false; + foreach (var server in servers) { _config.configs.Add(server); @@ -219,30 +217,24 @@ namespace Shadowsocks.Controller { _config.enabled = enabled; SaveConfig(_config); - if (EnableStatusChanged != null) - { - EnableStatusChanged(this, new EventArgs()); - } + + EnableStatusChanged?.Invoke(this, new EventArgs()); } public void ToggleGlobal(bool global) { _config.global = global; SaveConfig(_config); - if (EnableGlobalChanged != null) - { - EnableGlobalChanged(this, new EventArgs()); - } + + EnableGlobalChanged?.Invoke(this, new EventArgs()); } public void ToggleShareOverLAN(bool enabled) { _config.shareOverLan = enabled; SaveConfig(_config); - if (ShareOverLANStatusChanged != null) - { - ShareOverLANStatusChanged(this, new EventArgs()); - } + + ShareOverLANStatusChanged?.Invoke(this, new EventArgs()); } public void SaveProxy(ProxyConfig proxyConfig) @@ -255,10 +247,8 @@ namespace Shadowsocks.Controller { _config.isVerboseLogging = enabled; SaveConfig(_config); - if (VerboseLoggingStatusChanged != null) - { - VerboseLoggingStatusChanged(this, new EventArgs()); - } + + VerboseLoggingStatusChanged?.Invoke(this, new EventArgs()); } public void SelectServerIndex(int index) @@ -310,19 +300,15 @@ namespace Shadowsocks.Controller public void TouchPACFile() { string pacFilename = _pacServer.TouchPACFile(); - if (PACFileReadyToOpen != null) - { - PACFileReadyToOpen(this, new PathEventArgs() { Path = pacFilename }); - } + + PACFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = pacFilename }); } public void TouchUserRuleFile() { string userRuleFilename = _pacServer.TouchUserRuleFile(); - if (UserRuleFileReadyToOpen != null) - { - UserRuleFileReadyToOpen(this, new PathEventArgs() { Path = userRuleFilename }); - } + + UserRuleFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = userRuleFilename }); } public string GetServerURLForCurrentServer() @@ -381,60 +367,51 @@ namespace Shadowsocks.Controller public void UpdateStatisticsConfiguration(bool enabled) { - if (availabilityStatistics == null) return; - availabilityStatistics.UpdateConfiguration(this); - _config.availabilityStatistics = enabled; - SaveConfig(_config); + if (availabilityStatistics != null) + { + availabilityStatistics.UpdateConfiguration(this); + _config.availabilityStatistics = enabled; + SaveConfig(_config); + } } public void SavePACUrl(string pacUrl) { _config.pacUrl = pacUrl; SaveConfig(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + + ConfigChanged?.Invoke(this, new EventArgs()); } public void UseOnlinePAC(bool useOnlinePac) { _config.useOnlinePac = useOnlinePac; SaveConfig(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + + ConfigChanged?.Invoke(this, new EventArgs()); } public void ToggleSecureLocalPac(bool enabled) { _config.secureLocalPac = enabled; SaveConfig(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + + ConfigChanged?.Invoke(this, new EventArgs()); } public void ToggleCheckingUpdate(bool enabled) { _config.autoCheckUpdate = enabled; Configuration.Save(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + + ConfigChanged?.Invoke(this, new EventArgs()); } public void ToggleCheckingPreRelease(bool enabled) { _config.checkPreRelease = enabled; Configuration.Save(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + ConfigChanged?.Invoke(this, new EventArgs()); } public void SaveLogViewerConfig(LogViewerConfig newConfig) @@ -442,20 +419,16 @@ namespace Shadowsocks.Controller _config.logViewer = newConfig; newConfig.SaveSize(); Configuration.Save(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + + ConfigChanged?.Invoke(this, new EventArgs()); } public void SaveHotkeyConfig(HotkeyConfig newConfig) { _config.hotkey = newConfig; SaveConfig(_config); - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + + ConfigChanged?.Invoke(this, new EventArgs()); } public void UpdateLatency(Server server, TimeSpan latency) @@ -498,15 +471,15 @@ namespace Shadowsocks.Controller if (_pacServer == null) { _pacServer = new PACServer(); - _pacServer.PACFileChanged += pacServer_PACFileChanged; - _pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged; + _pacServer.PACFileChanged += PacServer_PACFileChanged; + _pacServer.UserRuleFileChanged += PacServer_UserRuleFileChanged; } _pacServer.UpdateConfiguration(_config); if (gfwListUpdater == null) { gfwListUpdater = new GFWListUpdater(); - gfwListUpdater.UpdateCompleted += pacServer_PACUpdateCompleted; - gfwListUpdater.Error += pacServer_PACUpdateError; + gfwListUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; + gfwListUpdater.Error += PacServer_PACUpdateError; } availabilityStatistics.UpdateConfiguration(this); @@ -536,11 +509,13 @@ namespace Shadowsocks.Controller TCPRelay tcpRelay = new TCPRelay(this, _config); UDPRelay udpRelay = new UDPRelay(this); - List services = new List(); - services.Add(tcpRelay); - services.Add(udpRelay); - services.Add(_pacServer); - services.Add(new PortForwarder(privoxyRunner.RunningPort)); + List services = new List + { + tcpRelay, + udpRelay, + _pacServer, + new PortForwarder(privoxyRunner.RunningPort) + }; _listener = new Listener(services); _listener.Start(_config); } @@ -548,9 +523,8 @@ namespace Shadowsocks.Controller { // translate Microsoft language into human language // i.e. An attempt was made to access a socket in a way forbidden by its access permissions => Port already in use - if (e is SocketException) + if (e is SocketException se) { - SocketException se = (SocketException)e; if (se.SocketErrorCode == SocketError.AddressAlreadyInUse) { e = new Exception(I18N.GetString("Port {0} already in use", _config.localPort), e); @@ -564,10 +538,7 @@ namespace Shadowsocks.Controller ReportError(e); } - if (ConfigChanged != null) - { - ConfigChanged(this, new EventArgs()); - } + ConfigChanged?.Invoke(this, new EventArgs()); UpdateSystemProxy(); Utils.ReleaseMemory(true); @@ -590,25 +561,23 @@ namespace Shadowsocks.Controller SystemProxy.Update(_config, false, _pacServer); } - private void pacServer_PACFileChanged(object sender, EventArgs e) + private void PacServer_PACFileChanged(object sender, EventArgs e) { UpdateSystemProxy(); } - private void pacServer_PACUpdateCompleted(object sender, GFWListUpdater.ResultEventArgs e) + private void PacServer_PACUpdateCompleted(object sender, GFWListUpdater.ResultEventArgs e) { - if (UpdatePACFromGFWListCompleted != null) - UpdatePACFromGFWListCompleted(this, e); + UpdatePACFromGFWListCompleted?.Invoke(this, e); } - private void pacServer_PACUpdateError(object sender, ErrorEventArgs e) + private void PacServer_PACUpdateError(object sender, ErrorEventArgs e) { - if (UpdatePACFromGFWListError != null) - UpdatePACFromGFWListError(this, e); + UpdatePACFromGFWListError?.Invoke(this, e); } private static readonly IEnumerable IgnoredLineBegins = new[] { '!', '[' }; - private void pacServer_UserRuleFileChanged(object sender, EventArgs e) + private void PacServer_UserRuleFileChanged(object sender, EventArgs e) { if (!File.Exists(Utils.GetTempPath("gfwlist.txt"))) { @@ -630,8 +599,10 @@ namespace Shadowsocks.Controller private void StartReleasingMemory() { - _ramThread = new Thread(new ThreadStart(ReleaseMemory)); - _ramThread.IsBackground = true; + _ramThread = new Thread(new ThreadStart(ReleaseMemory)) + { + IsBackground = true + }; _ramThread.Start(); } @@ -655,8 +626,10 @@ namespace Shadowsocks.Controller { trafficPerSecondQueue.Enqueue(new TrafficPerSecond()); } - _trafficThread = new Thread(new ThreadStart(() => TrafficStatistics(queueMaxSize))); - _trafficThread.IsBackground = true; + _trafficThread = new Thread(new ThreadStart(() => TrafficStatistics(queueMaxSize))) + { + IsBackground = true + }; _trafficThread.Start(); } @@ -666,10 +639,11 @@ namespace Shadowsocks.Controller while (true) { previous = trafficPerSecondQueue.Last(); - current = new TrafficPerSecond(); - - current.inboundCounter = InboundCounter; - current.outboundCounter = OutboundCounter; + current = new TrafficPerSecond + { + inboundCounter = InboundCounter, + outboundCounter = OutboundCounter + }; current.inboundIncreasement = current.inboundCounter - previous.inboundCounter; current.outboundIncreasement = current.outboundCounter - previous.outboundCounter; diff --git a/shadowsocks-csharp/Data/ja.txt b/shadowsocks-csharp/Data/ja.txt index 690e0d58..3b60941c 100644 --- a/shadowsocks-csharp/Data/ja.txt +++ b/shadowsocks-csharp/Data/ja.txt @@ -122,7 +122,6 @@ Port {0} is reserved by system= ポート番号 {0} はシステムによって Invalid server address=サーバーアドレスが無効です。 Illegal port number format=ポート番号のフォーマットが無効です。 Illegal timeout format=タイムアウト値のフォーマットが無効です。 -Please add at least one server=少なくとも一つのサーバーを指定して下さい。 Server IP can not be blank=サーバー IP が指定されていません。 Password can not be blank=パスワードが指定されていません。 Port out of range=ポート番号は範囲外です。 diff --git a/shadowsocks-csharp/Data/zh_CN.txt b/shadowsocks-csharp/Data/zh_CN.txt index b26593b0..d89385ea 100644 --- a/shadowsocks-csharp/Data/zh_CN.txt +++ b/shadowsocks-csharp/Data/zh_CN.txt @@ -123,7 +123,6 @@ Port {0} is reserved by system=端口 {0} 是系统保留端口 Invalid server address=非法服务器地址 Illegal port number format=非法端口格式 Illegal timeout format=非法超时格式 -Please add at least one server=请添加至少一个服务器 Server IP can not be blank=服务器 IP 不能为空 Password can not be blank=密码不能为空 Port out of range=端口超出范围 @@ -155,6 +154,14 @@ Register hotkey failed=注册快捷键失败 Cannot parse hotkey: {0}=解析快捷键失败: {0} Timeout is invalid, it should not exceed {0}=超时无效,不应超过 {0} +Operation failure=操作失败 +Auto save failed=自动保存失败 +Whether to discard unconfigured servers=是否丢弃未配置的服务器 +Invalid server address, Cannot automatically save or discard changes=非法服务器地址,无法自动保存是否丢弃修改 +Illegal port number format, Cannot automatically save or discard changes=非法端口格式,无法自动保存是否丢弃修改 +Password can not be blank, Cannot automatically save or discard changes=密码不能为空,无法自动保存是否丢弃修改 +Illegal timeout format, Cannot automatically save or discard changes=非法超时格式,无法自动保存是否丢弃修改 + Error occured when process proxy setting, do you want reset current setting and retry?=处理代理设置时发生错误,是否重置当前代理设置并重试? Unrecoverable proxy setting error occured, see log for detail=发生不可恢复的代理设置错误,查看日志以取得详情 Auth user can not be blank=认证用户不能为空 diff --git a/shadowsocks-csharp/Data/zh_TW.txt b/shadowsocks-csharp/Data/zh_TW.txt index d4b75ef8..5c2c19c5 100644 --- a/shadowsocks-csharp/Data/zh_TW.txt +++ b/shadowsocks-csharp/Data/zh_TW.txt @@ -122,7 +122,6 @@ Port {0} is reserved by system=連接埠號碼 {0} 由系統保留 Invalid server address=無效伺服器位址 Illegal port number format=無效連接埠號碼格式 Illegal timeout format=無效逾時格式 -Please add at least one server=請新增至少一個伺服器 Server IP can not be blank=伺服器 IP 不能為空 Password can not be blank=密碼不能為空 Port out of range=連接埠號碼超出範圍 diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 4d26b72f..3bb1af3b 100644 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using System.IO; - -using Shadowsocks.Controller; using Newtonsoft.Json; +using Shadowsocks.Controller; namespace Shadowsocks.Model { @@ -34,7 +33,7 @@ namespace Shadowsocks.Model public ProxyConfig proxy; public HotkeyConfig hotkey; - private static string CONFIG_FILE = "gui-config.json"; + private static readonly string CONFIG_FILE = "gui-config.json"; public Server GetCurrentServer() { @@ -46,12 +45,25 @@ namespace Shadowsocks.Model public static void CheckServer(Server server) { + CheckServer(server.server); CheckPort(server.server_port); CheckPassword(server.password); - CheckServer(server.server); CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); } + public static bool ChecksServer(Server server) + { + try + { + CheckServer(server); + return true; + } + catch (Exception) + { + return false; + } + } + public static Configuration Load() { try @@ -125,12 +137,18 @@ namespace Shadowsocks.Model } } - public static Server AddDefaultServerOrServer(Configuration config, Server server = null) + public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null) { if (config != null && config.configs != null) { server = (server ?? GetDefaultServer()); - config.configs.Add(server); + + config.configs.Insert(index.GetValueOrDefault(config.configs.Count), server); + + //if (index.HasValue) + // config.configs.Insert(index.Value, server); + //else + // config.configs.Add(server); } return server; } @@ -174,8 +192,7 @@ namespace Shadowsocks.Model public static void CheckTimeout(int timeout, int maxTimeout) { if (timeout <= 0 || timeout > maxTimeout) - throw new ArgumentException( - I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); + throw new ArgumentException(I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); } public static void CheckProxyAuthUser(string user) diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 2716e931..963a5d4a 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Runtime.CompilerServices; using System.Windows.Forms; using Shadowsocks.Controller; using Shadowsocks.Model; @@ -17,20 +18,20 @@ namespace Shadowsocks.View public ConfigForm(ShadowsocksController controller) { - this.Font = SystemFonts.MessageBoxFont; + Font = SystemFonts.MessageBoxFont; InitializeComponent(); // a dirty hack - this.ServersListBox.Dock = DockStyle.Fill; - this.tableLayoutPanel5.Dock = DockStyle.Fill; - this.PerformLayout(); + ServersListBox.Dock = DockStyle.Fill; + tableLayoutPanel5.Dock = DockStyle.Fill; + PerformLayout(); UpdateTexts(); SetupValueChangedListeners(); - this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); + Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); this.controller = controller; - controller.ConfigChanged += controller_ConfigChanged; + controller.ConfigChanged += Controller_ConfigChanged; LoadCurrentConfiguration(); } @@ -51,7 +52,7 @@ namespace Shadowsocks.View NeedPluginArgCheckBox.Text = I18N.GetString("Need Plugin Argument"); ProxyPortLabel.Text = I18N.GetString("Proxy Port"); PortableModeCheckBox.Text = I18N.GetString("Portable Mode"); - toolTip1.SetToolTip(this.PortableModeCheckBox, I18N.GetString("Restart required")); + toolTip1.SetToolTip(PortableModeCheckBox, I18N.GetString("Restart required")); RemarksLabel.Text = I18N.GetString("Remarks"); TimeoutLabel.Text = I18N.GetString("Timeout(Sec)"); ServerGroupBox.Text = I18N.GetString("Server"); @@ -60,7 +61,7 @@ namespace Shadowsocks.View ApplyButton.Text = I18N.GetString("Apply"); MoveUpButton.Text = I18N.GetString("Move &Up"); MoveDownButton.Text = I18N.GetString("Move D&own"); - this.Text = I18N.GetString("Edit Servers"); + Text = I18N.GetString("Edit Servers"); } private void SetupValueChangedListeners() @@ -78,7 +79,7 @@ namespace Shadowsocks.View ServerPortTextBox.TextChanged += ConfigValueChanged; } - private void controller_ConfigChanged(object sender, EventArgs e) + private void Controller_ConfigChanged(object sender, EventArgs e) { LoadCurrentConfiguration(); } @@ -88,7 +89,13 @@ namespace Shadowsocks.View ApplyButton.Enabled = true; } - private bool ValidateAndSaveSelectedServerDetails() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateIndexToEnd() + { + _lastSelectedIndex = (ServersListBox.SelectedIndex = (_modifiedConfiguration.configs.Count - 1)); + } + + private bool ValidateAndSaveSelectedServerDetails(bool isSave = false, bool isCopy = false) { try { @@ -96,15 +103,17 @@ namespace Shadowsocks.View { return true; } - Server server = GetServerDetailsFromUI(); - if (server == null) + + bool verify = GetServerDetailsFromUI(out Server server, isSave, isCopy); + + if (server != null) { - return false; - } - Configuration.CheckServer(server); - _modifiedConfiguration.configs[_lastSelectedIndex] = server; + if (isSave || isCopy) + Configuration.CheckServer(server); - return true; + _modifiedConfiguration.configs[_lastSelectedIndex] = server; + } + return verify; } catch (Exception ex) { @@ -113,36 +122,209 @@ namespace Shadowsocks.View return false; } - private Server GetServerDetailsFromUI() + private bool GetServerDetailsFromUI(out Server server, bool isSave = false, bool isCopy = false) + { + server = null; + + bool? checkIP = false; + bool? checkPort = false; + bool? checkPassword = false; + bool? checkTimeout = false; + + if ((checkIP = CheckIPTextBox(out string address, isSave, isCopy)).GetValueOrDefault(false) && address != null + && (checkPort = CheckServerPortTextBox(out int? addressPort, isSave, isCopy)).GetValueOrDefault(false) && addressPort.HasValue + && (checkPassword = CheckPasswordTextBox(out string serverPassword, isSave, isCopy)).GetValueOrDefault(false) && serverPassword != null + && (checkTimeout = CheckTimeoutTextBox(out int? timeout, isSave, isCopy)).GetValueOrDefault(false) && timeout.HasValue) + { + server = new Server() + { + server = address, + server_port = addressPort.Value, + password = serverPassword, + method = EncryptionSelect.Text, + plugin = PluginTextBox.Text, + plugin_opts = PluginOptionsTextBox.Text, + plugin_args = PluginArgumentsTextBox.Text, + remarks = RemarksTextBox.Text, + timeout = timeout.Value, + }; + + return true; + } + + if (checkIP == null || checkPort == null || checkTimeout == null) + { + _modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); + ServersListBox.SelectedIndexChanged -= ServersListBox_SelectedIndexChanged; + + LoadServerNameListToUI(_modifiedConfiguration); + UpdateIndexToEnd(); + + ServersListBox.SelectedIndexChanged += ServersListBox_SelectedIndexChanged; + return true; + } + else + return false; + } + + #region GetServerDetailsFromUI Check + + private bool? CheckIPTextBox(out string address, bool isSave, bool isCopy) + { + address = null; + string outAddress; + if (Uri.CheckHostName(outAddress = IPTextBox.Text.Trim()) == UriHostNameType.Unknown) + { + if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) + { + DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.OK) + return null; + } + else if (ApplyButton.Enabled && !isSave && !isCopy) + { + var result = MessageBox.Show(I18N.GetString("Invalid server address, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.Cancel) + return false; + else + { + address = _modifiedConfiguration.configs[_lastSelectedIndex].server; + return true; + } + } + else + { + MessageBox.Show(I18N.GetString("Invalid server address"), I18N.GetString("Operation failure")); + IPTextBox.Focus(); + } + return false; + } + else + { + address = outAddress; + } + return true; + } + + private bool? CheckServerPortTextBox(out int? addressPort, bool isSave, bool isCopy) + { + addressPort = null; + if (!int.TryParse(ServerPortTextBox.Text, out int outaddressPort)) + { + if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) + { + DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.OK) + return null; + } + else if (!isSave && !isCopy) + { + var result = MessageBox.Show(I18N.GetString("Illegal port number format, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.Cancel) + return false; + else + { + addressPort = _modifiedConfiguration.configs[_lastSelectedIndex].server_port; + return true; + } + } + else + { + MessageBox.Show(I18N.GetString("Illegal port number format"), I18N.GetString("Operation failure")); + ServerPortTextBox.Focus(); + } + return false; + } + else + { + addressPort = outaddressPort; + } + return true; + } + + private bool? CheckPasswordTextBox(out string password, bool isSave, bool isCopy) { - Server server = new Server(); - if (Uri.CheckHostName(server.server = IPTextBox.Text.Trim()) == UriHostNameType.Unknown) + password = null; + string outPassword; + if ((outPassword = PasswordTextBox.Text).IsNullOrWhiteSpace()) { - MessageBox.Show(I18N.GetString("Invalid server address")); - IPTextBox.Focus(); - return null; + if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) + { + DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.OK) + return null; + } + else if (ApplyButton.Enabled && !isSave && !isCopy) + { + var result = MessageBox.Show(I18N.GetString("Password can not be blank, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.Cancel) + return false; + else + { + password = _modifiedConfiguration.configs[_lastSelectedIndex].password; + return true; + } + } + else + { + MessageBox.Show(I18N.GetString("Password can not be blank"), I18N.GetString("Operation failure")); + PasswordTextBox.Focus(); + } + return false; } - if (!int.TryParse(ServerPortTextBox.Text, out server.server_port)) + else { - MessageBox.Show(I18N.GetString("Illegal port number format")); - ServerPortTextBox.Focus(); - return null; + password = outPassword; } - server.password = PasswordTextBox.Text; - server.method = EncryptionSelect.Text; - server.plugin = PluginTextBox.Text; - server.plugin_opts = PluginOptionsTextBox.Text; - server.plugin_args = PluginArgumentsTextBox.Text; - server.remarks = RemarksTextBox.Text; - if (!int.TryParse(TimeoutTextBox.Text, out server.timeout)) + return true; + } + + private bool? CheckTimeoutTextBox(out int? timeout, bool isSave, bool isCopy) + { + timeout = null; + if (!int.TryParse(TimeoutTextBox.Text, out int outTimeout)) + { + if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) + { + DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.OK) + return null; + } + else if (ApplyButton.Enabled && !isSave && !isCopy) + { + var result = MessageBox.Show(I18N.GetString("Illegal timeout format, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); + + if (result == DialogResult.Cancel) + return false; + else + { + timeout = _modifiedConfiguration.configs[_lastSelectedIndex].timeout; + return true; + } + } + else + { + MessageBox.Show(I18N.GetString("Illegal timeout format"), I18N.GetString("Operation failure")); + TimeoutTextBox.Focus(); + } + return false; + } + else { - MessageBox.Show(I18N.GetString("Illegal timeout format")); - TimeoutTextBox.Focus(); - return null; + timeout = outTimeout; } - return server; + return true; } + #endregion + private void LoadSelectedServerDetails() { if (ServersListBox.SelectedIndex >= 0 && ServersListBox.SelectedIndex < _modifiedConfiguration.configs.Count) @@ -168,6 +350,8 @@ namespace Shadowsocks.View RemarksTextBox.Text = server.remarks; TimeoutTextBox.Text = server.timeout.ToString(); + + ApplyButton.Enabled = false; } private void ShowHidePluginArgInput(bool show) @@ -176,7 +360,6 @@ namespace Shadowsocks.View PluginArgumentsLabel.Visible = show; } - private void LoadServerNameListToUI(Configuration configuration) { ServersListBox.Items.Clear(); @@ -200,20 +383,17 @@ namespace Shadowsocks.View ServersListBox.SelectedIndex = _lastSelectedIndex; UpdateButtons(); LoadSelectedServerDetails(); + ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); PortableModeCheckBox.Checked = _modifiedConfiguration.portableMode; + ApplyButton.Enabled = false; } private bool SaveValidConfiguration() { - if (!ValidateAndSaveSelectedServerDetails()) - { - return false; - } - if (_modifiedConfiguration.configs.Count == 0) + if (!ValidateAndSaveSelectedServerDetails(isSave: true)) { - MessageBox.Show(I18N.GetString("Please add at least one server")); return false; } @@ -269,81 +449,62 @@ namespace Shadowsocks.View private void AddButton_Click(object sender, EventArgs e) { - if (!ValidateAndSaveSelectedServerDetails()) + if (ValidateAndSaveSelectedServerDetails(isSave: true)) { - return; + Configuration.AddDefaultServerOrServer(_modifiedConfiguration); + LoadServerNameListToUI(_modifiedConfiguration); + UpdateIndexToEnd(); } - Configuration.AddDefaultServerOrServer(_modifiedConfiguration); - LoadServerNameListToUI(_modifiedConfiguration); - ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1; - _lastSelectedIndex = ServersListBox.SelectedIndex; } private void DuplicateButton_Click(object sender, EventArgs e) { - if (_lastSelectedIndex == -1 || _lastSelectedIndex > _modifiedConfiguration.configs.Count - || !ValidateAndSaveSelectedServerDetails()) + if (ValidateAndSaveSelectedServerDetails(isCopy: true)) { - return; + Server currServer = _modifiedConfiguration.configs[_lastSelectedIndex]; + Configuration.AddDefaultServerOrServer(_modifiedConfiguration, currServer); + LoadServerNameListToUI(_modifiedConfiguration); + UpdateIndexToEnd(); } - Server currServer = _modifiedConfiguration.configs[_lastSelectedIndex]; - var currIndex = _modifiedConfiguration.configs.IndexOf(currServer); - _modifiedConfiguration.configs.Insert(currIndex + 1, currServer); - LoadServerNameListToUI(_modifiedConfiguration); - ServersListBox.SelectedIndex = currIndex + 1; - _lastSelectedIndex = ServersListBox.SelectedIndex; } private void DeleteButton_Click(object sender, EventArgs e) { - _lastSelectedIndex = ServersListBox.SelectedIndex; - if (_lastSelectedIndex >= 0 && _lastSelectedIndex < _modifiedConfiguration.configs.Count) - { - _modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); - } + _modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); + if (_modifiedConfiguration.configs.Count == 0) { Configuration.AddDefaultServerOrServer(_modifiedConfiguration); } - if (_lastSelectedIndex >= _modifiedConfiguration.configs.Count) - { - // can be -1 - _lastSelectedIndex = _modifiedConfiguration.configs.Count - 1; - } - ServersListBox.SelectedIndex = _lastSelectedIndex; + LoadServerNameListToUI(_modifiedConfiguration); - ServersListBox.SelectedIndex = _lastSelectedIndex; + UpdateIndexToEnd(); LoadSelectedServerDetails(); UpdateButtons(); } - private void OKButton_Click(object sender, EventArgs e) - { - if (SaveValidConfiguration()) - { - this.Close(); - } - } - - private void CancelButton_Click(object sender, EventArgs e) - { - this.Close(); - } - - private void ApplyButton_Click(object sender, EventArgs e) + private void UpdateButtons() { - SaveValidConfiguration(); + DeleteButton.Enabled = (ServersListBox.Items.Count > 0); + MoveUpButton.Enabled = (ServersListBox.SelectedIndex > 0); + MoveDownButton.Enabled = (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1); } - private void ConfigForm_Shown(object sender, EventArgs e) + private void MoveUpButton_Click(object sender, EventArgs e) { - IPTextBox.Focus(); + if (ServersListBox.SelectedIndex > 0) + { + MoveConfigItem(-1); // -1 means move backward + } } - private void ConfigForm_FormClosed(object sender, FormClosedEventArgs e) + private void MoveDownButton_Click(object sender, EventArgs e) { - controller.ConfigChanged -= controller_ConfigChanged; + if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1) + { + MoveConfigItem(+1); // +1 means move forward + } } private void MoveConfigItem(int step) @@ -368,45 +529,42 @@ namespace Shadowsocks.View UpdateButtons(); } - private void UpdateButtons() + private void OKButton_Click(object sender, EventArgs e) { - DeleteButton.Enabled = (ServersListBox.Items.Count > 0); - MoveUpButton.Enabled = (ServersListBox.SelectedIndex > 0); - MoveDownButton.Enabled = (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1); + if (SaveValidConfiguration()) + { + Close(); + } } - private void MoveUpButton_Click(object sender, EventArgs e) + private void CancelButton_Click(object sender, EventArgs e) { - if (!ValidateAndSaveSelectedServerDetails()) - { - return; - } - if (ServersListBox.SelectedIndex > 0) - { - MoveConfigItem(-1); // -1 means move backward - } + Close(); } - private void MoveDownButton_Click(object sender, EventArgs e) + private void ApplyButton_Click(object sender, EventArgs e) { - if (!ValidateAndSaveSelectedServerDetails()) - { - return; - } - if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1) - { - MoveConfigItem(+1); // +1 means move forward - } + SaveValidConfiguration(); + } + + private void ConfigForm_Shown(object sender, EventArgs e) + { + IPTextBox.Focus(); + } + + private void ConfigForm_FormClosed(object sender, FormClosedEventArgs e) + { + controller.ConfigChanged -= Controller_ConfigChanged; } private void ShowPasswdCheckBox_CheckedChanged(object sender, EventArgs e) { - this.PasswordTextBox.UseSystemPasswordChar = !this.ShowPasswdCheckBox.Checked; + PasswordTextBox.UseSystemPasswordChar = !ShowPasswdCheckBox.Checked; } private void UsePluginArgCheckBox_CheckedChanged(object sender, EventArgs e) { - ShowHidePluginArgInput(this.NeedPluginArgCheckBox.Checked); + ShowHidePluginArgInput(NeedPluginArgCheckBox.Checked); } } } diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 9d44d02b..845f8319 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -438,11 +438,14 @@ namespace Shadowsocks.View Configuration configuration = controller.GetConfigurationCopy(); foreach (var server in configuration.configs) { - MenuItem item = new MenuItem(server.FriendlyName()); - item.Tag = i - strategyCount; - item.Click += AServerItem_Click; - items.Add(i, item); - i++; + if (Configuration.ChecksServer(server)) + { + MenuItem item = new MenuItem(server.FriendlyName()); + item.Tag = i - strategyCount; + item.Click += AServerItem_Click; + items.Add(i, item); + i++; + } } foreach (MenuItem item in items)