diff --git a/CHANGES b/CHANGES index a175380c..1ad329a5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,15 @@ +4.3.0.0 2020-10-19 +- Cleanup and update dependencies (#2983) +- Geosite group validation + PAC regeneration on version update (#2988) +- PAC: add options for direct and proxied groups (#2990) +- Transition to WPF: ForwardProxyView + HotkeysView + OnlineConfigView + VersionUpdatePromptView (#2991) + +4.2.1.0 2020-10-12 +- SIP008 support (#2942) +- Exclude @cn from PAC proxied list (#2982) +- Transition to WPF: ReactiveUI and ServerSharingView (#2959) +- User-Agent for OnlineConfigResolver and GeositeUpdater (#2978) + 4.2.0.1 2020-09-26 - Fix domain rule handling in PAC script (#2956) diff --git a/README.md b/README.md index 849f9cc5..07328391 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [中文说明] -#### Features +## Features 1. System proxy configuration 2. PAC mode and global mode @@ -15,15 +15,15 @@ 6. Supports UDP relay (see Usage) 7. Supports plugins -#### Download +## Downloads Download the latest release from [release page]. -#### Requirements +## Requirements Microsoft [.NET Framework 4.7.2] or higher, Microsoft [Visual C++ 2015 Redistributable] (x86) . -#### Basic +## Basics 1. Find Shadowsocks icon in the notification tray 2. You can add multiple servers in servers menu @@ -33,19 +33,32 @@ proxy addons in your browser, or set them to use system proxy system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this port in `Servers -> Edit Servers` -#### PAC +## PAC -1. You can change PAC rules by editing the PAC file. When you save the PAC file -with any editor, Shadowsocks will notify browsers about the change automatically -2. You can also update PAC file from [GeoSite] \(maintained by 3rd party) -3. You can also use online PAC URL +- The PAC rules are generated from the geosite database in [v2fly/domain-list-community](https://github.com/v2fly/domain-list-community). +- Generation modes: whitelist mode and blacklist mode. +- Domain groups: `geositeDirectGroups` and `geositeProxiedGroups`. + - `geositeDirectGroups` is initialized with `cn` and `geolocation-!cn@cn`. + - `geositeProxiedGroups` is initialized with `geolocation-!cn`. +- To switch between different modes, modify the `geositePreferDirect` property in `gui-config.json` + - When `geositePreferDirect` is false (default), PAC works in whitelist mode. Exception rules are generated from `geositeDirectGroups`. Unmatched domains goes through the proxy. + - When `geositePreferDirect` is true, PAC works in blacklist mode. Blocking rules are generated from `geositeProxiedGroups`. Exception rules are generated from `geositeDirectGroups`. Unmatched domains are connected to directly. +- Starting from 4.3.0.0, shadowsocks-windows defaults to whitelist mode with Chinese domains excluded from connecting via the proxy. +- The new default values make sure that: + - When in whitelist mode, Chinese domains, including non-Chinese companies' Chinese CDNs, are connected to directly. + - When in blacklist mode, only non-Chinese domains goes through the proxy. Chinese domains, as well as non-Chinese companies' Chinese CDNs, are connected to directly. + +### User-defined rules + +- To define your own PAC rules, it's recommended to use the `user-rule.txt` file. +- You can also modify `pac.txt` directly. But your modifications won't persist after updating geosite from the upstream. For Windows10 Store and related applications, please execute the following command under Admin privilege: ``` netsh winhttp import proxy source=ie ``` -#### Server Auto Switching +## Server Auto Switching 1. Load balance: choosing server randomly 2. High availability: choosing the best server (low latency and packet loss) @@ -53,18 +66,18 @@ netsh winhttp import proxy source=ie `Availability Statistics` in the menu if you want to use this 4. Write your own strategy by implement IStrategy interface and send us a pull request! -#### UDP +## UDP For UDP, you need to use SocksCap or ProxyCap to force programs you want to be proxied to tunnel over Shadowsocks -#### Multiple Instances +## Multiple Instances If you want to manage multiple servers using other tools like SwitchyOmega, you can start multiple Shadowsocks instances. To avoid configuration conflicts, copy Shadowsocks to a new directory and choose a different local port. -#### Plugins +## Plugins If you would like to connect to server via a plugin, please set the plugin's path (relative or absolute) on Edit Servers form. @@ -73,70 +86,72 @@ _Note_: Forward Proxy will not be used while a plugin is enabled. Details: [Working with non SIP003 standard Plugin]. -#### Global hotkeys +## Global hotkeys Hotkeys could be registered automatically on startup. If you are using multiple instances of Shadowsocks, you must set different key combination for each instance. -##### How to input? +### How to input? 1. Put focus in the corresponding textbox. 2. Press the key combination that you want to use. 3. Release all keys when you think it is ready. 4. Your input appears in the textbox. -##### How to change? +### How to change? 1. Put focus in the corresponding textbox. 2. Press BackSpace key to clear content. 3. Re-input new key combination. -##### How to deactivate? +### How to deactivate? 1. Clear content in the textbox that you want to deactivate, if you want to deactivate all, please clear all textboxes. 2. Press OK button to confirm. -##### Meaning of label color +### Meaning of label color - Green: This key combination is not occupied by other programs and register successfully. - Yellow: This key combination is occupied by other programs and you have to change to another one. - Transparent without color: The initial status. -#### Server Configuration +## Server Configuration Please visit [Servers] for more information. -#### Experimental +## Experimental [Experimental Features] -#### Development +## Development -1. [Visual Studio 2017] & [.NET Framework 4.7.2 Developer Pack] are required. +1. [Visual Studio 2019] & [.NET Framework 4.7.2 Developer Pack] are required. 2. It is recommended to share your idea on the Issue Board before you start to work, especially for feature development. -#### License +## License [GPLv3] -#### Open Source Components / Libraries +## Open Source Components / Libraries ``` -Caseless.Fody (MIT) https://github.com/Fody/Caseless -Costura.Fody (MIT) https://github.com/Fody/Costura -Fody (MIT) https://github.com/Fody/Fody -GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey -Newtonsoft.Json (MIT) https://www.newtonsoft.com/json -StringEx.CS () https://github.com/LazyMode/StringEx -ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net - -BouncyCastle.NetCore (MIT) https://github.com/chrishaly/bc-csharp -NaCl.Core (MIT) https://github.com/idaviddesmet/NaCl.Core -Privoxy (GPLv2) https://www.privoxy.org -Sysproxy () https://github.com/Noisyfox/sysproxy +BouncyCastle.NetCore (MIT) https://github.com/chrishaly/bc-csharp +Caseless.Fody (MIT) https://github.com/Fody/Caseless +Costura.Fody (MIT) https://github.com/Fody/Costura +Fody (MIT) https://github.com/Fody/Fody +GlobalHotKey (GPLv3) https://github.com/kirmir/GlobalHotKey +MdXaml (MIT) https://github.com/whistyun/MdXaml +Newtonsoft.Json (MIT) https://www.newtonsoft.com/json +Privoxy (GPLv2) https://www.privoxy.org +ReactiveUI.WPF (MIT) https://github.com/reactiveui/ReactiveUI +ReactiveUI.Events.WPF (MIT) https://github.com/reactiveui/ReactiveUI +ReactiveUI.Fody (MIT) https://github.com/reactiveui/ReactiveUI +ReactiveUI.Validation (MIT) https://github.com/reactiveui/ReactiveUI.Validation +WPFLocalizationExtension (MS-PL) https://github.com/XAMLMarkupExtensions/WPFLocalizationExtension/ +ZXing.Net (Apache 2.0) https://github.com/micjahn/ZXing.Net ``` @@ -153,4 +168,4 @@ Sysproxy () https://github.com/Noisyfox/sysproxy [Visual C++ 2015 Redistributable]: https://www.microsoft.com/en-us/download/details.aspx?id=53840 [GPLv3]: https://github.com/shadowsocks/shadowsocks-windows/blob/master/LICENSE.txt [Working with non SIP003 standard Plugin]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Working-with-non-SIP003-standard-Plugin -[Experimental Features]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Experimental \ No newline at end of file +[Experimental Features]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Experimental diff --git a/shadowsocks-csharp/Controller/HotkeyReg.cs b/shadowsocks-csharp/Controller/HotkeyReg.cs index 90a3dee9..25b1dd93 100644 --- a/shadowsocks-csharp/Controller/HotkeyReg.cs +++ b/shadowsocks-csharp/Controller/HotkeyReg.cs @@ -12,7 +12,7 @@ namespace Shadowsocks.Controller private static Logger logger = LogManager.GetCurrentClassLogger(); public static void RegAllHotkeys() { - var hotkeyConfig = Configuration.Load().hotkey; + var hotkeyConfig = Program.MainController.GetCurrentConfiguration().hotkey; if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) return; @@ -50,7 +50,7 @@ namespace Shadowsocks.Controller var callback = _callback as HotKeys.HotKeyCallBackHandler; - if (hotkeyStr.IsNullOrEmpty()) + if (string.IsNullOrEmpty(hotkeyStr)) { HotKeys.UnregExistingHotkey(callback); onComplete?.Invoke(RegResult.UnregSuccess); diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs deleted file mode 100644 index f6d9a3d6..00000000 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ /dev/null @@ -1,534 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using Newtonsoft.Json; -using NLog; -using Shadowsocks.Model; -using Shadowsocks.Util; - -namespace Shadowsocks.Controller -{ - using Statistics = Dictionary>; - - public sealed class AvailabilityStatistics : IDisposable - { - private static Logger logger = LogManager.GetCurrentClassLogger(); - - public const string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; - private const string StatisticsFilesName = "shadowsocks.availability.json"; - public static string AvailabilityStatisticsFile; - //static constructor to initialize every public static fields before refereced - static AvailabilityStatistics() - { - AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); - } - - //arguments for ICMP tests - private int Repeat => Config.RepeatTimesNum; - public const int TimeoutMilliseconds = 500; - - //records cache for current server in {_monitorInterval} minutes - private readonly ConcurrentDictionary> _latencyRecords = new ConcurrentDictionary>(); - //speed in KiB/s - private readonly ConcurrentDictionary> _inboundSpeedRecords = new ConcurrentDictionary>(); - private readonly ConcurrentDictionary> _outboundSpeedRecords = new ConcurrentDictionary>(); - private readonly ConcurrentDictionary _inOutBoundRecords = new ConcurrentDictionary(); - - private class InOutBoundRecord - { - private long _inbound; - private long _lastInbound; - private long _outbound; - private long _lastOutbound; - - public void UpdateInbound(long delta) - { - Interlocked.Add(ref _inbound, delta); - } - - public void UpdateOutbound(long delta) - { - Interlocked.Add(ref _outbound, delta); - } - - public void GetDelta(out long inboundDelta, out long outboundDelta) - { - var i = Interlocked.Read(ref _inbound); - var il = Interlocked.Exchange(ref _lastInbound, i); - inboundDelta = i - il; - - - var o = Interlocked.Read(ref _outbound); - var ol = Interlocked.Exchange(ref _lastOutbound, o); - outboundDelta = o - ol; - } - } - - //tasks - private readonly TimeSpan _delayBeforeStart = TimeSpan.FromSeconds(1); - private readonly TimeSpan _retryInterval = TimeSpan.FromMinutes(2); - private TimeSpan RecordingInterval => TimeSpan.FromMinutes(Config.DataCollectionMinutes); - private Timer _perSecondTimer; //analyze and save cached records to RawStatistics and filter records - private readonly TimeSpan _monitorInterval = TimeSpan.FromSeconds(1); - //private Timer _writer; //write RawStatistics to file - //private readonly TimeSpan _writingInterval = TimeSpan.FromMinutes(1); - - private ShadowsocksController _controller; - private StatisticsStrategyConfiguration Config => _controller.StatisticsConfiguration; - - // Static Singleton Initialization - public static AvailabilityStatistics Instance { get; } = new AvailabilityStatistics(); - public Statistics RawStatistics { get; private set; } - public Statistics FilteredStatistics { get; private set; } - - private AvailabilityStatistics() - { - RawStatistics = new Statistics(); - } - - internal void UpdateConfiguration(ShadowsocksController controller) - { - _controller = controller; - Reset(); - try - { - if (Config.StatisticsEnabled) - { - LoadRawStatistics(); - if (_perSecondTimer == null) - { - _perSecondTimer = new Timer(OperationsPerSecond, new Counter(), _delayBeforeStart, TimeSpan.FromSeconds(1)); - } - } - else - { - _perSecondTimer?.Dispose(); - } - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - } - - private void OperationsPerSecond(object state) - { - lock(state) - { - var counter = state as Counter; - if (counter.count % _monitorInterval.TotalSeconds == 0) - { - UpdateSpeed(); - } - - if (counter.count % RecordingInterval.TotalSeconds == 0) - { - Run(); - } - - counter.count++; - } - } - - private void UpdateSpeed() - { - foreach (var kv in _inOutBoundRecords) - { - var id = kv.Key; - var record = kv.Value; - - long inboundDelta, outboundDelta; - - record.GetDelta(out inboundDelta, out outboundDelta); - - var inboundSpeed = GetSpeedInKiBPerSecond(inboundDelta, _monitorInterval.TotalSeconds); - var outboundSpeed = GetSpeedInKiBPerSecond(outboundDelta, _monitorInterval.TotalSeconds); - - // not thread safe - var inR = _inboundSpeedRecords.GetOrAdd(id, (k) => new List()); - var outR = _outboundSpeedRecords.GetOrAdd(id, (k) => new List()); - - inR.Add(inboundSpeed); - outR.Add(outboundSpeed); - - logger.Debug( - $"{id}: current/max inbound {inboundSpeed}/{inR.Max()} KiB/s, current/max outbound {outboundSpeed}/{outR.Max()} KiB/s"); - } - } - - private void Reset() - { - _inboundSpeedRecords.Clear(); - _outboundSpeedRecords.Clear(); - _latencyRecords.Clear(); - } - - private void Run() - { - UpdateRecords(); - Reset(); - } - - private void UpdateRecords() - { - var records = new Dictionary(); - UpdateRecordsState state = new UpdateRecordsState(); - int serverCount = _controller.GetCurrentConfiguration().configs.Count; - state.counter = serverCount; - bool isPing = Config.Ping; - for (int i = 0; i < serverCount; i++) - { - try - { - var server = _controller.GetCurrentConfiguration().configs[i]; - var id = server.Identifier(); - List inboundSpeedRecords = null; - List outboundSpeedRecords = null; - List latencyRecords = null; - _inboundSpeedRecords.TryGetValue(id, out inboundSpeedRecords); - _outboundSpeedRecords.TryGetValue(id, out outboundSpeedRecords); - _latencyRecords.TryGetValue(id, out latencyRecords); - StatisticsRecord record = new StatisticsRecord(id, inboundSpeedRecords, outboundSpeedRecords, latencyRecords); - /* duplicate server identifier */ - if (records.ContainsKey(id)) - records[id] = record; - else - records.Add(id, record); - if (isPing) - { - // FIXME: on ping completed, every thing could be asynchrously changed. - // focus on: Config/ RawStatistics - MyPing ping = new MyPing(server, Repeat); - ping.Completed += ping_Completed; - ping.Start(new PingState { state = state, record = record }); - } - else if (!record.IsEmptyData()) - { - AppendRecord(id, record); - } - } - catch (Exception e) - { - logger.Debug("config changed asynchrously, just ignore this server"); - } - } - - if (!isPing) - { - Save(); - FilterRawStatistics(); - } - } - - private void ping_Completed(object sender, MyPing.CompletedEventArgs e) - { - PingState pingState = (PingState)e.UserState; - UpdateRecordsState state = pingState.state; - Server server = e.Server; - StatisticsRecord record = pingState.record; - record.SetResponse(e.RoundtripTime); - if (!record.IsEmptyData()) - { - AppendRecord(server.Identifier(), record); - } - logger.Debug($"Ping {server} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms"); - if (Interlocked.Decrement(ref state.counter) == 0) - { - Save(); - FilterRawStatistics(); - } - } - - private void AppendRecord(string serverIdentifier, StatisticsRecord record) - { - try - { - List records; - lock (RawStatistics) - { - if (!RawStatistics.TryGetValue(serverIdentifier, out records)) - { - records = new List(); - RawStatistics[serverIdentifier] = records; - } - } - records.Add(record); - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - } - - private void Save() - { - logger.Debug($"save statistics to {AvailabilityStatisticsFile}"); - if (RawStatistics.Count == 0) - { - return; - } - try - { - string content; -#if DEBUG - content = JsonConvert.SerializeObject(RawStatistics, Formatting.Indented); -#else - content = JsonConvert.SerializeObject(RawStatistics, Formatting.None); -#endif - File.WriteAllText(AvailabilityStatisticsFile, content); - } - catch (IOException e) - { - logger.LogUsefulException(e); - } - } - - private bool IsValidRecord(StatisticsRecord record) - { - if (Config.ByHourOfDay) - { - if (!record.Timestamp.Hour.Equals(DateTime.Now.Hour)) return false; - } - return true; - } - - private void FilterRawStatistics() - { - try - { - logger.Debug("filter raw statistics"); - if (RawStatistics == null) return; - var filteredStatistics = new Statistics(); - - foreach (var serverAndRecords in RawStatistics) - { - var server = serverAndRecords.Key; - var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord); - filteredStatistics[server] = filteredRecords; - } - - FilteredStatistics = filteredStatistics; - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - } - - private void LoadRawStatistics() - { - try - { - var path = AvailabilityStatisticsFile; - logger.Debug($"loading statistics from {path}"); - if (!File.Exists(path)) - { - using (File.Create(path)) - { - //do nothing - } - } - var content = File.ReadAllText(path); - RawStatistics = JsonConvert.DeserializeObject(content) ?? RawStatistics; - } - catch (Exception e) - { - logger.LogUsefulException(e); - Console.WriteLine($"failed to load statistics; use runtime statistics, some data may be lost"); - } - } - - private static int GetSpeedInKiBPerSecond(long bytes, double seconds) - { - var result = (int)(bytes / seconds) / 1024; - return result; - } - - public void Dispose() - { - _perSecondTimer.Dispose(); - } - - public void UpdateLatency(Server server, int latency) - { - _latencyRecords.GetOrAdd(server.Identifier(), (k) => - { - List records = new List(); - records.Add(latency); - return records; - }); - } - - public void UpdateInboundCounter(Server server, long n) - { - _inOutBoundRecords.AddOrUpdate(server.Identifier(), (k) => - { - var r = new InOutBoundRecord(); - r.UpdateInbound(n); - - return r; - }, (k, v) => - { - v.UpdateInbound(n); - return v; - }); - } - - public void UpdateOutboundCounter(Server server, long n) - { - _inOutBoundRecords.AddOrUpdate(server.Identifier(), (k) => - { - var r = new InOutBoundRecord(); - r.UpdateOutbound(n); - - return r; - }, (k, v) => - { - v.UpdateOutbound(n); - return v; - }); - } - - private class Counter - { - public int count = 0; - } - - class UpdateRecordsState - { - public int counter; - } - - class PingState - { - public UpdateRecordsState state; - public StatisticsRecord record; - } - - class MyPing - { - private static Logger logger = LogManager.GetCurrentClassLogger(); - - //arguments for ICMP tests - public const int TimeoutMilliseconds = 500; - - public EventHandler Completed; - private Server server; - - private int repeat; - private IPAddress ip; - private Ping ping; - private List RoundtripTime; - - public MyPing(Server server, int repeat) - { - this.server = server; - this.repeat = repeat; - RoundtripTime = new List(repeat); - ping = new Ping(); - ping.PingCompleted += Ping_PingCompleted; - } - - public void Start(object userstate) - { - if (server.server == "") - { - FireCompleted(new Exception("Invalid Server"), userstate); - return; - } - new Task(() => ICMPTest(0, userstate)).Start(); - } - - private void ICMPTest(int delay, object userstate) - { - try - { - logger.Debug($"Ping {server}"); - if (ip == null) - { - ip = Dns.GetHostAddresses(server.server) - .First( - ip => - ip.AddressFamily == AddressFamily.InterNetwork || - ip.AddressFamily == AddressFamily.InterNetworkV6); - } - repeat--; - if (delay > 0) - Thread.Sleep(delay); - ping.SendAsync(ip, TimeoutMilliseconds, userstate); - } - catch (Exception e) - { - logger.Error($"An exception occured while eveluating {server}"); - logger.LogUsefulException(e); - FireCompleted(e, userstate); - } - } - - private void Ping_PingCompleted(object sender, PingCompletedEventArgs e) - { - try - { - if (e.Reply.Status == IPStatus.Success) - { - logger.Debug($"Ping {server} {e.Reply.RoundtripTime} ms"); - RoundtripTime.Add((int?)e.Reply.RoundtripTime); - } - else - { - logger.Debug($"Ping {server} timeout"); - RoundtripTime.Add(null); - } - TestNext(e.UserState); - } - catch (Exception ex) - { - logger.Error($"An exception occured while eveluating {server}"); - logger.LogUsefulException(ex); - FireCompleted(ex, e.UserState); - } - } - - private void TestNext(object userstate) - { - if (repeat > 0) - { - //Do ICMPTest in a random frequency - int delay = TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds; - new Task(() => ICMPTest(delay, userstate)).Start(); - } - else - { - FireCompleted(null, userstate); - } - } - - private void FireCompleted(Exception error, object userstate) - { - Completed?.Invoke(this, new CompletedEventArgs - { - Error = error, - Server = server, - RoundtripTime = RoundtripTime, - UserState = userstate - }); - } - - public class CompletedEventArgs : EventArgs - { - public Exception Error; - public Server Server; - public List RoundtripTime; - public object UserState; - } - } - - } -} diff --git a/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs b/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs index 54c65764..40c64bdc 100644 --- a/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GeositeUpdater.cs @@ -35,8 +35,6 @@ namespace Shadowsocks.Controller private static readonly string DATABASE_PATH = Utils.GetTempPath("dlc.dat"); - private static SocketsHttpHandler socketsHttpHandler; - private static HttpClient httpClient; private static readonly string GEOSITE_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat"; private static readonly string GEOSITE_SHA256SUM_URL = "https://github.com/v2fly/domain-list-community/raw/release/dlc.dat.sha256sum"; private static byte[] geositeDB; @@ -45,9 +43,6 @@ namespace Shadowsocks.Controller static GeositeUpdater() { - //socketsHttpHandler = new SocketsHttpHandler(); - //httpClient = new HttpClient(socketsHttpHandler); - if (File.Exists(DATABASE_PATH) && new FileInfo(DATABASE_PATH).Length > 0) { geositeDB = File.ReadAllBytes(DATABASE_PATH); @@ -68,7 +63,7 @@ namespace Shadowsocks.Controller var list = GeositeList.Parser.ParseFrom(geositeDB); foreach (var item in list.Entries) { - Geosites[item.GroupName.ToLower()] = item.Domains; + Geosites[item.GroupName.ToLowerInvariant()] = item.Domains; } } @@ -84,9 +79,9 @@ namespace Shadowsocks.Controller string geositeSha256sumUrl = GEOSITE_SHA256SUM_URL; SHA256 mySHA256 = SHA256.Create(); var config = Program.MainController.GetCurrentConfiguration(); - string group = config.geositeGroup; - bool blacklist = config.geositeBlacklistMode; - + bool blacklist = config.geositePreferDirect; + var httpClient = Program.MainController.GetHttpClient(); + if (!string.IsNullOrWhiteSpace(config.geositeUrl)) { logger.Info("Found custom Geosite URL in config file"); @@ -94,21 +89,6 @@ namespace Shadowsocks.Controller } logger.Info($"Checking Geosite from {geositeUrl}"); - // use System.Net.Http.HttpClient to download GeoSite db. - // NASTY workaround: new HttpClient every update - // because we can't change proxy on existing socketsHttpHandler instance - socketsHttpHandler = new SocketsHttpHandler(); - httpClient = new HttpClient(socketsHttpHandler); - if (config.enabled) - { - socketsHttpHandler.UseProxy = true; - socketsHttpHandler.Proxy = new WebProxy( - config.isIPv6Enabled - ? $"[{IPAddress.IPv6Loopback}]" - : IPAddress.Loopback.ToString(), - config.localPort); - } - try { // download checksum first @@ -150,32 +130,26 @@ namespace Shadowsocks.Controller // update stuff geositeDB = downloadedBytes; LoadGeositeList(); - bool pacFileChanged = MergeAndWritePACFile(group, blacklist); + bool pacFileChanged = MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, blacklist); UpdateCompleted?.Invoke(null, new GeositeResultEventArgs(pacFileChanged)); } catch (Exception ex) { Error?.Invoke(null, new ErrorEventArgs(ex)); } - finally - { - if (socketsHttpHandler != null) - { - socketsHttpHandler.Dispose(); - socketsHttpHandler = null; - } - if (httpClient != null) - { - httpClient.Dispose(); - httpClient = null; - } - } } - public static bool MergeAndWritePACFile(string group, bool blacklist) + /// + /// Merge and write pac.txt from geosite. + /// Used at multiple places. + /// + /// A list of geosite groups configured for direct connection. + /// A list of geosite groups configured for proxied connection. + /// Whether to use blacklist mode. False for whitelist. + /// + public static bool MergeAndWritePACFile(List directGroups, List proxiedGroups, bool blacklist) { - IList domains = Geosites[group]; - string abpContent = MergePACFile(domains, blacklist); + string abpContent = MergePACFile(directGroups, proxiedGroups, blacklist); if (File.Exists(PACDaemon.PAC_FILE)) { string original = FileManager.NonExclusiveReadAllText(PACDaemon.PAC_FILE, Encoding.UTF8); @@ -188,7 +162,44 @@ namespace Shadowsocks.Controller return true; } - private static string MergePACFile(IList domains, bool blacklist) + /// + /// Checks if the specified group exists in GeoSite database. + /// + /// The group name to check for. + /// True if the group exists. False if the group doesn't exist. + public static bool CheckGeositeGroup(string group) => SeparateAttributeFromGroupName(group, out string groupName, out _) && Geosites.ContainsKey(groupName); + + /// + /// Separates the attribute (e.g. @cn) from a group name. + /// No checks are performed. + /// + /// A group name potentially with a trailing attribute. + /// The group name with the attribute stripped. + /// The attribute. + /// True for success. False for more than one '@'. + private static bool SeparateAttributeFromGroupName(string group, out string groupName, out string attribute) + { + var splitGroupAttributeList = group.Split('@'); + if (splitGroupAttributeList.Length == 1) // no attribute + { + groupName = splitGroupAttributeList[0]; + attribute = ""; + } + else if (splitGroupAttributeList.Length == 2) // has attribute + { + groupName = splitGroupAttributeList[0]; + attribute = splitGroupAttributeList[1]; + } + else + { + groupName = ""; + attribute = ""; + return false; + } + return true; + } + + private static string MergePACFile(List directGroups, List proxiedGroups, bool blacklist) { string abpContent; if (File.Exists(PACDaemon.USER_ABP_FILE)) @@ -204,27 +215,25 @@ namespace Shadowsocks.Controller if (File.Exists(PACDaemon.USER_RULE_FILE)) { string userrulesString = FileManager.NonExclusiveReadAllText(PACDaemon.USER_RULE_FILE, Encoding.UTF8); - userruleLines = PreProcessGFWList(userrulesString); + userruleLines = ProcessUserRules(userrulesString); } - List gfwLines = GeositeToGFWList(domains, blacklist); + List ruleLines = GenerateRules(directGroups, proxiedGroups, blacklist); abpContent = $@"var __USERRULES__ = {JsonConvert.SerializeObject(userruleLines, Formatting.Indented)}; -var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; +var __RULES__ = {JsonConvert.SerializeObject(ruleLines, Formatting.Indented)}; {abpContent}"; return abpContent; } - private static readonly IEnumerable IgnoredLineBegins = new[] { '!', '[' }; - - private static List PreProcessGFWList(string content) + private static List ProcessUserRules(string content) { List valid_lines = new List(); - using (var sr = new StringReader(content)) + using (var stringReader = new StringReader(content)) { - foreach (var line in sr.NonWhiteSpaceLines()) + for (string line = stringReader.ReadLine(); line != null; line = stringReader.ReadLine()) { - if (line.BeginWithAny(IgnoredLineBegins)) + if (string.IsNullOrWhiteSpace(line) || line.StartsWith("!") || line.StartsWith("[")) continue; valid_lines.Add(line); } @@ -232,44 +241,105 @@ var __RULES__ = {JsonConvert.SerializeObject(gfwLines, Formatting.Indented)}; return valid_lines; } - private static List GeositeToGFWList(IList domains, bool blacklist) + /// + /// Generates rule lines based on user preference. + /// + /// A list of geosite groups configured for direct connection. + /// A list of geosite groups configured for proxied connection. + /// Whether to use blacklist mode. False for whitelist. + /// A list of rule lines. + private static List GenerateRules(List directGroups, List proxiedGroups, bool blacklist) { - return blacklist ? GeositeToGFWListBlack(domains) : GeositeToGFWListWhite(domains); + List ruleLines; + if (blacklist) // blocking + exception rules + { + ruleLines = GenerateBlockingRules(proxiedGroups); + ruleLines.AddRange(GenerateExceptionRules(directGroups)); + } + else // proxy all + exception rules + { + ruleLines = new List() + { + "/.*/" // block/proxy all unmatched domains + }; + ruleLines.AddRange(GenerateExceptionRules(directGroups)); + } + return ruleLines; } - private static List GeositeToGFWListBlack(IList domains) + /// + /// Generates rules that match domains that should be proxied. + /// + /// A list of source groups. + /// A list of rule lines. + private static List GenerateBlockingRules(List groups) { - List ret = new List(domains.Count + 100);// 100 overhead - foreach (var d in domains) + List ruleLines = new List(); + foreach (var group in groups) { - string domain = d.Value; - - switch (d.Type) + // separate group name and attribute + SeparateAttributeFromGroupName(group, out string groupName, out string attribute); + var domainObjects = Geosites[groupName]; + if (!string.IsNullOrEmpty(attribute)) // has attribute { - case DomainObject.Types.Type.Plain: - ret.Add(domain); - break; - case DomainObject.Types.Type.Regex: - ret.Add($"/{domain}/"); - break; - case DomainObject.Types.Type.Domain: - ret.Add($"||{domain}"); - break; - case DomainObject.Types.Type.Full: - ret.Add($"|http://{domain}"); - ret.Add($"|https://{domain}"); - break; + var attributeObject = new DomainObject.Types.Attribute + { + Key = attribute, + BoolValue = true + }; + foreach (var domainObject in domainObjects) + { + if (domainObject.Attribute.Contains(attributeObject)) + switch (domainObject.Type) + { + case DomainObject.Types.Type.Plain: + ruleLines.Add(domainObject.Value); + break; + case DomainObject.Types.Type.Regex: + ruleLines.Add($"/{domainObject.Value}/"); + break; + case DomainObject.Types.Type.Domain: + ruleLines.Add($"||{domainObject.Value}"); + break; + case DomainObject.Types.Type.Full: + ruleLines.Add($"|http://{domainObject.Value}"); + ruleLines.Add($"|https://{domainObject.Value}"); + break; + } + } } + else // no attribute + foreach (var domainObject in domainObjects) + { + switch (domainObject.Type) + { + case DomainObject.Types.Type.Plain: + ruleLines.Add(domainObject.Value); + break; + case DomainObject.Types.Type.Regex: + ruleLines.Add($"/{domainObject.Value}/"); + break; + case DomainObject.Types.Type.Domain: + ruleLines.Add($"||{domainObject.Value}"); + break; + case DomainObject.Types.Type.Full: + ruleLines.Add($"|http://{domainObject.Value}"); + ruleLines.Add($"|https://{domainObject.Value}"); + break; + } + } } - return ret; + return ruleLines; } - private static List GeositeToGFWListWhite(IList domains) - { - return GeositeToGFWListBlack(domains) - .Select(r => $"@@{r}") // convert to whitelist - .Prepend("/.*/") // blacklist all other site + /// + /// Generates rules that match domains that should be connected directly without a proxy. + /// + /// A list of source groups. + /// A list of rule lines. + private static List GenerateExceptionRules(List groups) + => GenerateBlockingRules(groups) + .Select(r => $"@@{r}") // convert blocking rules to exception rules .ToList(); - } } } diff --git a/shadowsocks-csharp/Controller/Service/OnlineConfigResolver.cs b/shadowsocks-csharp/Controller/Service/OnlineConfigResolver.cs index 4c52d362..31059b73 100644 --- a/shadowsocks-csharp/Controller/Service/OnlineConfigResolver.cs +++ b/shadowsocks-csharp/Controller/Service/OnlineConfigResolver.cs @@ -11,27 +11,15 @@ namespace Shadowsocks.Controller.Service { public class OnlineConfigResolver { - public static async Task> GetOnline(string url, IWebProxy proxy = null) + public static async Task> GetOnline(string url) { - var socketsHttpHandler = new SocketsHttpHandler() - { - UseProxy = proxy != null, - Proxy = proxy - }; - var httpClient = new HttpClient(socketsHttpHandler) - { - Timeout = TimeSpan.FromSeconds(15) - }; - + var httpClient = Program.MainController.GetHttpClient(); string server_json = await httpClient.GetStringAsync(url); - var servers = server_json.GetServers(); - foreach (var server in servers) { server.group = url; } - return servers.ToList(); } } diff --git a/shadowsocks-csharp/Controller/Service/PACDaemon.cs b/shadowsocks-csharp/Controller/Service/PACDaemon.cs index 3b1dd5dd..217f0fda 100644 --- a/shadowsocks-csharp/Controller/Service/PACDaemon.cs +++ b/shadowsocks-csharp/Controller/Service/PACDaemon.cs @@ -45,7 +45,7 @@ namespace Shadowsocks.Controller { if (!File.Exists(PAC_FILE)) { - GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); + GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); } return PAC_FILE; } @@ -63,7 +63,7 @@ namespace Shadowsocks.Controller { if (!File.Exists(PAC_FILE)) { - GeositeUpdater.MergeAndWritePACFile(config.geositeGroup, config.geositeBlacklistMode); + GeositeUpdater.MergeAndWritePACFile(config.geositeDirectGroups, config.geositeProxiedGroups, config.geositePreferDirect); } return File.ReadAllText(PAC_FILE, Encoding.UTF8); } diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index cf31010a..49585816 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -43,7 +43,7 @@ namespace Shadowsocks.Controller _config = config; string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; string contentHash = GetHash(_pacDaemon.GetPACContent()); - PacUrl = $"http://{config.localHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; + PacUrl = $"http://{config.LocalHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; logger.Debug("Set PAC URL:" + PacUrl); } diff --git a/shadowsocks-csharp/Controller/Service/Sip003Plugin.cs b/shadowsocks-csharp/Controller/Service/Sip003Plugin.cs index a0048e14..7024c648 100644 --- a/shadowsocks-csharp/Controller/Service/Sip003Plugin.cs +++ b/shadowsocks-csharp/Controller/Service/Sip003Plugin.cs @@ -122,12 +122,13 @@ namespace Shadowsocks.Controller.Service public string ExpandEnvironmentVariables(string name, StringDictionary environmentVariables = null) { + name = name.ToLower(); // Expand the environment variables from the new process itself if (environmentVariables != null) { foreach(string key in environmentVariables.Keys) { - name = name.Replace($"%{key}%", environmentVariables[key], StringComparison.OrdinalIgnoreCase); + name = name.Replace($"%{key.ToLower()}%", environmentVariables[key]); } } // Also expand the environment variables from current main process (system) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 9eb00630..e6bd021c 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -239,7 +239,7 @@ namespace Shadowsocks.Controller public DateTime lastActivity; private readonly ShadowsocksController _controller; - private readonly ProxyConfig _config; + private readonly ForwardProxyConfig _config; private readonly Socket _connection; private IEncryptor encryptor; @@ -708,10 +708,10 @@ namespace Shadowsocks.Controller { switch (_config.proxyType) { - case ProxyConfig.PROXY_SOCKS5: + case ForwardProxyConfig.PROXY_SOCKS5: remote = new Socks5Proxy(); break; - case ProxyConfig.PROXY_HTTP: + case ForwardProxyConfig.PROXY_HTTP: remote = new HttpProxy(); break; default: diff --git a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs index 78f55f57..39d091b7 100644 --- a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs +++ b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs @@ -1,55 +1,36 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Net; +using System.Net.Http; using System.Reflection; using System.Text.RegularExpressions; - +using System.Threading.Tasks; +using System.Windows; using Newtonsoft.Json.Linq; using NLog; +using Shadowsocks.Localization; using Shadowsocks.Model; using Shadowsocks.Util; +using Shadowsocks.Views; namespace Shadowsocks.Controller { public class UpdateChecker { - private static Logger logger = LogManager.GetCurrentClassLogger(); + private readonly Logger logger; + private readonly HttpClient httpClient; + // https://developer.github.com/v3/repos/releases/ private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases"; - private const string UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36"; - - private Configuration config; - public bool NewVersionFound; - public string LatestVersionNumber; - public string LatestVersionSuffix; - public string LatestVersionName; - public string LatestVersionURL; - public string LatestVersionLocalName; - public event EventHandler CheckUpdateCompleted; - - public static readonly string Version = Assembly.GetEntryAssembly().GetName().Version.ToString(); - private class CheckUpdateTimer : System.Timers.Timer - { - public Configuration config; - - public CheckUpdateTimer(int p) : base(p) - { - } - } + private Configuration _config; + private Window versionUpdatePromptWindow; + private JToken _releaseObject; - public void CheckUpdate(Configuration config, int delay) - { -#if DEBUG - return; -#pragma warning disable CS0162 // 检测到无法访问的代码 -#endif - CheckUpdateTimer timer = new CheckUpdateTimer(delay); - timer.AutoReset = false; - timer.Elapsed += Timer_Elapsed; - timer.config = config; - timer.Enabled = true; - } + public string NewReleaseVersion { get; private set; } + public string NewReleaseZipFilename { get; private set; } private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { @@ -60,204 +41,146 @@ namespace Shadowsocks.Controller timer.Dispose(); CheckUpdate(config); } + public event EventHandler CheckUpdateCompleted; - public void CheckUpdate(Configuration config) - { -#if DEBUG - return; -#endif - - this.config = config; + public static readonly string Version = Assembly.GetEntryAssembly().GetName().Version.ToString(); + private readonly Version _version; - try - { - logger.Info("Checking updates..."); - WebClient http = CreateWebClient(); - http.DownloadStringCompleted += http_DownloadStringCompleted; - http.DownloadStringAsync(new Uri(UpdateURL)); - } - catch (Exception ex) - { - logger.LogUsefulException(ex); - } + public UpdateChecker() + { + logger = LogManager.GetCurrentClassLogger(); + httpClient = Program.MainController.GetHttpClient(); + _version = new Version(Version); + _config = Program.MainController.GetCurrentConfiguration(); } - private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + /// + /// Checks for updates and asks the user if updates are found. + /// + /// A delay in milliseconds before checking. + /// + public async Task CheckForVersionUpdate(int millisecondsDelay = 0) { + // delay + logger.Info($"Waiting for {millisecondsDelay}ms before checking for version update."); + await Task.Delay(millisecondsDelay); + // update _config so we would know if the user checked or unchecked pre-release checks + _config = Program.MainController.GetCurrentConfiguration(); + // start + logger.Info($"Checking for version update."); try { - string response = e.Result; - - JArray result = JArray.Parse(response); - - List asserts = new List(); - if (result != null) + // list releases via API + var releasesListJsonString = await httpClient.GetStringAsync(UpdateURL); + // parse + var releasesJArray = JArray.Parse(releasesListJsonString); + foreach (var releaseObject in releasesJArray) { - foreach (JObject release in result) + var releaseTagName = (string)releaseObject["tag_name"]; + var releaseVersion = new Version(releaseTagName); + if (releaseTagName == _config.skippedUpdateVersion) // finished checking + break; + if (releaseVersion.CompareTo(_version) > 0 && + (!(bool)releaseObject["prerelease"] || _config.checkPreRelease && (bool)releaseObject["prerelease"])) // selected { - var isPreRelease = (bool)release["prerelease"]; - if (isPreRelease && !config.checkPreRelease) - { - continue; - } - foreach (JObject asset in (JArray)release["assets"]) - { - Asset ass = Asset.ParseAsset(asset); - if (ass != null) - { - ass.prerelease = isPreRelease; - if (ass.IsNewVersion(Version, config.checkPreRelease)) - { - asserts.Add(ass); - } - } - } - } - } - if (asserts.Count != 0) - { - SortByVersions(asserts); - Asset asset = asserts[asserts.Count - 1]; - NewVersionFound = true; - LatestVersionURL = asset.browser_download_url; - LatestVersionNumber = asset.version; - LatestVersionName = asset.name; - LatestVersionSuffix = asset.suffix == null ? "" : $"-{asset.suffix}"; - - startDownload(); - } - else - { - logger.Info("No update is available"); - if (CheckUpdateCompleted != null) - { - CheckUpdateCompleted(this, new EventArgs()); + logger.Info($"Found new version {releaseTagName}."); + _releaseObject = releaseObject; + NewReleaseVersion = releaseTagName; + AskToUpdate(releaseObject); + return; } } + logger.Info($"No new versions found."); + CheckUpdateCompleted?.Invoke(this, new EventArgs()); } - catch (Exception ex) + catch (Exception e) { - logger.LogUsefulException(ex); + logger.LogUsefulException(e); } } - private void startDownload() + /// + /// Opens a window to show the update's information. + /// + /// The update release object. + private void AskToUpdate(JToken releaseObject) { - try - { - LatestVersionLocalName = Utils.GetTempPath(LatestVersionName); - WebClient http = CreateWebClient(); - http.DownloadFileCompleted += Http_DownloadFileCompleted; - http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName); - } - catch (Exception ex) + if (versionUpdatePromptWindow == null) { - logger.LogUsefulException(ex); + versionUpdatePromptWindow = new Window() + { + Title = LocalizationProvider.GetLocalizedValue("VersionUpdate"), + Height = 480, + Width = 640, + MinHeight = 480, + MinWidth = 640, + Content = new VersionUpdatePromptView(releaseObject) + }; + versionUpdatePromptWindow.Closed += VersionUpdatePromptWindow_Closed; + versionUpdatePromptWindow.Show(); } + versionUpdatePromptWindow.Activate(); } - private void Http_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) + private void VersionUpdatePromptWindow_Closed(object sender, EventArgs e) + { + versionUpdatePromptWindow = null; + } + + /// + /// Downloads the selected update and notifies the user. + /// + /// + public async Task DoUpdate() { try { - if (e.Error != null) - { - logger.LogUsefulException(e.Error); - return; - } - logger.Info($"New version {LatestVersionNumber}{LatestVersionSuffix} found: {LatestVersionLocalName}"); - if (CheckUpdateCompleted != null) + var assets = (JArray)_releaseObject["assets"]; + // download all assets + foreach (JObject asset in assets) { - CheckUpdateCompleted(this, new EventArgs()); + var filename = (string)asset["name"]; + var browser_download_url = (string)asset["browser_download_url"]; + var response = await httpClient.GetAsync(browser_download_url); + using (var downloadedFileStream = File.Create(Utils.GetTempPath(filename))) + await response.Content.CopyToAsync(downloadedFileStream); + logger.Info($"Downloaded {filename}."); + // store .zip filename + if (filename.EndsWith(".zip")) + NewReleaseZipFilename = filename; } + logger.Info("Finished downloading."); + // notify user + CloseVersionUpdatePromptWindow(); + Process.Start("explorer.exe", $"/select, \"{Utils.GetTempPath(NewReleaseZipFilename)}\""); } - catch (Exception ex) + catch (Exception e) { - logger.LogUsefulException(ex); + logger.LogUsefulException(e); } } - private WebClient CreateWebClient() + /// + /// Saves the skipped update version. + /// + public void SkipUpdate() { - WebClient http = new WebClient(); - http.Headers.Add("User-Agent", UserAgent); - http.Proxy = new WebProxy(config.localHost, config.localPort); - return http; + var version = (string)_releaseObject["tag_name"] ?? ""; + _config.skippedUpdateVersion = version; + Program.MainController.SaveSkippedUpdateVerion(version); + logger.Info($"The update {version} has been skipped and will be ignored next time."); + CloseVersionUpdatePromptWindow(); } - private void SortByVersions(List asserts) + /// + /// Closes the update prompt window. + /// + public void CloseVersionUpdatePromptWindow() { - asserts.Sort(); - } - - public class Asset : IComparable - { - public bool prerelease; - public string name; - public string version; - public string browser_download_url; - public string suffix; - - public static Asset ParseAsset(JObject assertJObject) - { - var name = (string)assertJObject["name"]; - Match match = Regex.Match(name, @"^Shadowsocks-(?\d+(?:\.\d+)*)(?:|-(?.+))\.\w+$", - RegexOptions.IgnoreCase); - if (match.Success) - { - string version = match.Groups["version"].Value; - - var asset = new Asset - { - browser_download_url = (string)assertJObject["browser_download_url"], - name = name, - version = version - }; - - if (match.Groups["suffix"].Success) - { - asset.suffix = match.Groups["suffix"].Value; - } - - return asset; - } - - return null; - } - - public bool IsNewVersion(string currentVersion, bool checkPreRelease) - { - if (prerelease && !checkPreRelease) - { - return false; - } - if (version == null) - { - return false; - } - var cmp = CompareVersion(version, currentVersion); - return cmp > 0; - } - - 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 int CompareTo(Asset other) + if (versionUpdatePromptWindow != null) { - return CompareVersion(version, other.version); + versionUpdatePromptWindow.Close(); + versionUpdatePromptWindow = null; } } } diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index f602ef7a..8f784606 100644 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Net.Sockets; using System.Text; using System.Threading; @@ -15,12 +16,14 @@ using Shadowsocks.Controller.Service; using Shadowsocks.Controller.Strategy; using Shadowsocks.Model; using Shadowsocks.Util; +using WPFLocalizeExtension.Engine; namespace Shadowsocks.Controller { public class ShadowsocksController { - private static Logger logger = LogManager.GetCurrentClassLogger(); + private readonly Logger logger; + private readonly HttpClient httpClient; // controller: // handle user actions @@ -38,9 +41,6 @@ namespace Shadowsocks.Controller private PrivoxyRunner privoxyRunner; private readonly ConcurrentDictionary _pluginsByServer; - public AvailabilityStatistics availabilityStatistics = AvailabilityStatistics.Instance; - public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } - private long _inboundCounter = 0; private long _outboundCounter = 0; public long InboundCounter => Interlocked.Read(ref _inboundCounter); @@ -92,8 +92,10 @@ namespace Shadowsocks.Controller public ShadowsocksController() { + logger = LogManager.GetCurrentClassLogger(); + httpClient = new HttpClient(); _config = Configuration.Load(); - StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); + Configuration.Process(ref _config); _strategyManager = new StrategyManager(this); _pluginsByServer = new ConcurrentDictionary(); StartTrafficStatistics(61); @@ -106,23 +108,34 @@ namespace Shadowsocks.Controller #region Basic - public void Start(bool regHotkeys = true) + public void Start(bool systemWakeUp = false) { - if (_config.updated && regHotkeys) + if (_config.firstRunOnNewVersion && !systemWakeUp) { - _config.updated = false; ProgramUpdated.Invoke(this, new UpdatedEventArgs() { OldVersion = _config.version, NewVersion = UpdateChecker.Version, }); + // delete pac.txt when regeneratePacOnUpdate is true + if (_config.regeneratePacOnUpdate) + try + { + File.Delete(PACDaemon.PAC_FILE); + logger.Info("Deleted pac.txt from previous version."); + } + catch (Exception e) + { + logger.LogUsefulException(e); + } + // finish up first run of new version + _config.firstRunOnNewVersion = false; + _config.version = UpdateChecker.Version; Configuration.Save(_config); } Reload(); - if (regHotkeys) - { + if (!systemWakeUp) HotkeyReg.RegAllHotkeys(); - } } public void Stop() @@ -150,10 +163,24 @@ namespace Shadowsocks.Controller Encryption.RNG.Reload(); // some logic in configuration updated the config when saving, we need to read it again _config = Configuration.Load(); + Configuration.Process(ref _config); NLogConfig.LoadConfiguration(); - StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); + logger.Info($"WPF Localization Extension|Current culture: {LocalizeDictionary.CurrentCulture}"); + + // set User-Agent for httpClient + try + { + if (!string.IsNullOrWhiteSpace(_config.userAgentString)) + httpClient.DefaultRequestHeaders.Add("User-Agent", _config.userAgentString); + } + catch + { + // reset userAgent to default and reapply + Configuration.ResetUserAgent(_config); + httpClient.DefaultRequestHeaders.Add("User-Agent", _config.userAgentString); + } privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); @@ -167,7 +194,6 @@ namespace Shadowsocks.Controller GeositeUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; GeositeUpdater.Error += PacServer_PACUpdateError; - availabilityStatistics.UpdateConfiguration(this); _tcpListener?.Stop(); _udpListener?.Stop(); StopPlugins(); @@ -186,7 +212,6 @@ namespace Shadowsocks.Controller privoxyRunner.Start(_config); TCPRelay tcpRelay = new TCPRelay(this, _config); - tcpRelay.OnConnected += UpdateLatency; tcpRelay.OnInbound += UpdateInboundCounter; tcpRelay.OnOutbound += UpdateOutboundCounter; tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); @@ -239,22 +264,9 @@ namespace Shadowsocks.Controller Errored?.Invoke(this, new ErrorEventArgs(e)); } - public Server GetCurrentServer() - { - return _config.GetCurrentServer(); - } - - // always return copy - public Configuration GetConfigurationCopy() - { - return Configuration.Load(); - } - - // always return current instance - public Configuration GetCurrentConfiguration() - { - return _config; - } + public HttpClient GetHttpClient() => httpClient; + public Server GetCurrentServer() => _config.GetCurrentServer(); + public Configuration GetCurrentConfiguration() => _config; public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint) { @@ -270,13 +282,6 @@ namespace Shadowsocks.Controller return GetCurrentServer(); } - public void SelectServerIndex(int index) - { - _config.index = index; - _config.strategy = null; - SaveConfig(_config); - } - public void SaveServers(List servers, int localPort, bool portableMode) { _config.configs = servers; @@ -285,6 +290,13 @@ namespace Shadowsocks.Controller Configuration.Save(_config); } + public void SelectServerIndex(int index) + { + _config.index = index; + _config.strategy = null; + SaveConfig(_config); + } + public void ToggleShareOverLAN(bool enabled) { _config.shareOverLan = enabled; @@ -313,7 +325,7 @@ namespace Shadowsocks.Controller EnableGlobalChanged?.Invoke(this, new EventArgs()); } - public void SaveProxy(ProxyConfig proxyConfig) + public void SaveProxy(ForwardProxyConfig proxyConfig) { _config.proxy = proxyConfig; SaveConfig(_config); @@ -346,7 +358,7 @@ namespace Shadowsocks.Controller private static readonly IEnumerable IgnoredLineBegins = new[] { '!', '[' }; private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) { - GeositeUpdater.MergeAndWritePACFile(_config.geositeGroup, _config.geositeBlacklistMode); + GeositeUpdater.MergeAndWritePACFile(_config.geositeDirectGroups, _config.geositeProxiedGroups, _config.geositePreferDirect); UpdateSystemProxy(); } @@ -393,6 +405,13 @@ namespace Shadowsocks.Controller ConfigChanged?.Invoke(this, new EventArgs()); } + public void ToggleRegeneratePacOnUpdate(bool enabled) + { + _config.regeneratePacOnUpdate = enabled; + SaveConfig(_config); + ConfigChanged?.Invoke(this, new EventArgs()); + } + #endregion #region SIP002 @@ -419,7 +438,7 @@ namespace Shadowsocks.Controller { try { - if (ssURL.IsNullOrEmpty() || ssURL.IsWhiteSpace()) + if (string.IsNullOrWhiteSpace(ssURL)) return false; var servers = Server.GetServers(ssURL); @@ -474,6 +493,12 @@ namespace Shadowsocks.Controller ConfigChanged?.Invoke(this, new EventArgs()); } + public void SaveSkippedUpdateVerion(string version) + { + _config.skippedUpdateVersion = version; + Configuration.Save(_config); + } + public void SaveLogViewerConfig(LogViewerConfig newConfig) { _config.logViewer = newConfig; @@ -493,7 +518,7 @@ namespace Shadowsocks.Controller #endregion - #region Statistic + #region Strategy public void SelectStrategy(string strategyID) { @@ -502,12 +527,6 @@ namespace Shadowsocks.Controller SaveConfig(_config); } - public void SaveStrategyConfigurations(StatisticsStrategyConfiguration configuration) - { - StatisticsConfiguration = configuration; - StatisticsStrategyConfiguration.Save(configuration); - } - public IList GetStrategies() { return _strategyManager.GetStrategies(); @@ -525,43 +544,16 @@ namespace Shadowsocks.Controller return null; } - public void UpdateStatisticsConfiguration(bool enabled) - { - if (availabilityStatistics != null) - { - availabilityStatistics.UpdateConfiguration(this); - _config.availabilityStatistics = enabled; - SaveConfig(_config); - } - } - - public void UpdateLatency(object sender, SSTCPConnectedEventArgs args) - { - GetCurrentStrategy()?.UpdateLatency(args.server, args.latency); - if (_config.availabilityStatistics) - { - availabilityStatistics.UpdateLatency(args.server, (int)args.latency.TotalMilliseconds); - } - } - public void UpdateInboundCounter(object sender, SSTransmitEventArgs args) { GetCurrentStrategy()?.UpdateLastRead(args.server); Interlocked.Add(ref _inboundCounter, args.length); - if (_config.availabilityStatistics) - { - availabilityStatistics.UpdateInboundCounter(args.server, args.length); - } } public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) { GetCurrentStrategy()?.UpdateLastWrite(args.server); Interlocked.Add(ref _outboundCounter, args.length); - if (_config.availabilityStatistics) - { - availabilityStatistics.UpdateOutboundCounter(args.server, args.length); - } } #endregion @@ -620,7 +612,7 @@ namespace Shadowsocks.Controller } #endregion - + #region Traffic Statistics private void StartTrafficStatistics(int queueMaxSize) @@ -668,7 +660,7 @@ namespace Shadowsocks.Controller public async Task UpdateOnlineConfigInternal(string url) { - var onlineServer = await OnlineConfigResolver.GetOnline(url, _config.WebProxy); + var onlineServer = await OnlineConfigResolver.GetOnline(url); _config.configs = Configuration.SortByOnlineConfig( _config.configs .Where(c => c.group != url) @@ -695,10 +687,10 @@ namespace Shadowsocks.Controller return true; } - public async Task UpdateAllOnlineConfig() + public async Task> UpdateAllOnlineConfig() { var selected = GetCurrentServer(); - int failCount = 0; + var failedUrls = new List(); foreach (var url in _config.onlineConfigSource) { try @@ -708,18 +700,18 @@ namespace Shadowsocks.Controller catch (Exception e) { logger.LogUsefulException(e); - failCount++; + failedUrls.Add(url); } } _config.index = _config.configs.IndexOf(selected); SaveConfig(_config); - return failCount; + return failedUrls; } - public void SaveOnlineConfigSource(IEnumerable vs) + public void SaveOnlineConfigSource(List sources) { - _config.onlineConfigSource = vs.ToList(); + _config.onlineConfigSource = sources; SaveConfig(_config); } diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs deleted file mode 100644 index a2d9bfc7..00000000 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; - -using Newtonsoft.Json; -using NLog; -using Shadowsocks.Model; - -namespace Shadowsocks.Controller.Strategy -{ - using Statistics = Dictionary>; - - internal class StatisticsStrategy : IStrategy, IDisposable - { - private static Logger logger = LogManager.GetCurrentClassLogger(); - - private readonly ShadowsocksController _controller; - private Server _currentServer; - private readonly Timer _timer; - private Statistics _filteredStatistics; - private AvailabilityStatistics Service => _controller.availabilityStatistics; - private int ChoiceKeptMilliseconds - => (int)TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds; - - public StatisticsStrategy(ShadowsocksController controller) - { - _controller = controller; - var servers = controller.GetCurrentConfiguration().configs; - var randomIndex = new Random().Next() % servers.Count; - _currentServer = servers[randomIndex]; //choose a server randomly at first - // FIXME: consider Statistics and Config changing asynchrously. - _timer = new Timer(ReloadStatisticsAndChooseAServer); - } - - private void ReloadStatisticsAndChooseAServer(object obj) - { - logger.Debug("Reloading statistics and choose a new server...."); - var servers = _controller.GetCurrentConfiguration().configs; - LoadStatistics(); - ChooseNewServer(servers); - } - - private void LoadStatistics() - { - _filteredStatistics = - Service.FilteredStatistics ?? - Service.RawStatistics ?? - _filteredStatistics; - } - - //return the score by data - //server with highest score will be choosen - private float? GetScore(string identifier, List records) - { - var config = _controller.StatisticsConfiguration; - float? score = null; - - var averageRecord = new StatisticsRecord(identifier, - records.Where(record => record.MaxInboundSpeed != null).Select(record => record.MaxInboundSpeed.Value).ToList(), - records.Where(record => record.MaxOutboundSpeed != null).Select(record => record.MaxOutboundSpeed.Value).ToList(), - records.Where(record => record.AverageLatency != null).Select(record => record.AverageLatency.Value).ToList()); - averageRecord.SetResponse(records.Select(record => record.AverageResponse).ToList()); - - foreach (var calculation in config.Calculations) - { - var name = calculation.Key; - var field = typeof (StatisticsRecord).GetField(name); - dynamic value = field?.GetValue(averageRecord); - var factor = calculation.Value; - if (value == null || factor.Equals(0)) continue; - score = score ?? 0; - score += value * factor; - } - - if (score != null) - { - logger.Debug($"Highest score: {score} {JsonConvert.SerializeObject(averageRecord, Formatting.Indented)}"); - } - return score; - } - - private void ChooseNewServer(List servers) - { - if (_filteredStatistics == null || servers.Count == 0) - { - return; - } - try - { - var serversWithStatistics = (from server in servers - let id = server.Identifier() - where _filteredStatistics.ContainsKey(id) - let score = GetScore(id, _filteredStatistics[id]) - where score != null - select new - { - server, - score - }).ToArray(); - - if (serversWithStatistics.Length < 2) - { - LogWhenEnabled("no enough statistics data or all factors in calculations are 0"); - return; - } - - var bestResult = serversWithStatistics - .Aggregate((server1, server2) => server1.score > server2.score ? server1 : server2); - - LogWhenEnabled($"Switch to server: {bestResult.server.ToString()} by statistics: score {bestResult.score}"); - _currentServer = bestResult.server; - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - } - - private void LogWhenEnabled(string log) - { - if (_controller.GetCurrentStrategy()?.ID == ID) //output when enabled - { - Console.WriteLine(log); - } - } - - public string ID => "com.shadowsocks.strategy.scbs"; - - public string Name => I18N.GetString("Choose by statistics"); - - public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint) - { - if (_currentServer == null) - { - ChooseNewServer(_controller.GetCurrentConfiguration().configs); - } - return _currentServer; //current server cached for CachedInterval - } - - public void ReloadServers() - { - ChooseNewServer(_controller.GetCurrentConfiguration().configs); - _timer?.Change(0, ChoiceKeptMilliseconds); - } - - public void SetFailure(Server server) - { - logger.Debug($"failure: {server.ToString()}"); - } - - public void UpdateLastRead(Server server) - { - } - - public void UpdateLastWrite(Server server) - { - } - - public void UpdateLatency(Server server, TimeSpan latency) - { - } - - public void Dispose() - { - _timer.Dispose(); - } - } -} diff --git a/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs b/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs index 81a00e4d..be8869da 100644 --- a/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs +++ b/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs @@ -13,7 +13,6 @@ namespace Shadowsocks.Controller.Strategy _strategies = new List(); _strategies.Add(new BalancingStrategy(controller)); _strategies.Add(new HighAvailabilityStrategy(controller)); - _strategies.Add(new StatisticsStrategy(controller)); // TODO: load DLL plugins } public IList GetStrategies() diff --git a/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs b/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs index cb60d739..08f15836 100644 --- a/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs +++ b/shadowsocks-csharp/Controller/System/Hotkeys/HotkeyCallbacks.cs @@ -23,7 +23,7 @@ namespace Shadowsocks.Controller.Hotkeys /// public static Delegate GetCallback(string methodname) { - if (methodname.IsNullOrEmpty()) throw new ArgumentException(nameof(methodname)); + if (string.IsNullOrEmpty(methodname)) throw new ArgumentException(nameof(methodname)); MethodInfo dynMethod = typeof(HotkeyCallbacks).GetMethod(methodname, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase); return dynMethod == null ? null : Delegate.CreateDelegate(typeof(HotKeys.HotKeyCallBackHandler), Instance, dynMethod); @@ -46,21 +46,20 @@ namespace Shadowsocks.Controller.Hotkeys private void SwitchSystemProxyCallback() { - bool enabled = _controller.GetConfigurationCopy().enabled; + bool enabled = _controller.GetCurrentConfiguration().enabled; _controller.ToggleEnable(!enabled); } private void SwitchSystemProxyModeCallback() { - var config = _controller.GetConfigurationCopy(); - if (config.enabled == false) return; - var currStatus = config.global; - _controller.ToggleGlobal(!currStatus); + var config = _controller.GetCurrentConfiguration(); + if (config.enabled) + _controller.ToggleGlobal(!config.global); } private void SwitchAllowLanCallback() { - var status = _controller.GetConfigurationCopy().shareOverLan; + var status = _controller.GetCurrentConfiguration().shareOverLan; _controller.ToggleShareOverLAN(!status); } diff --git a/shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs b/shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs index 45069321..a55591ef 100644 --- a/shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs +++ b/shadowsocks-csharp/Controller/System/Hotkeys/Hotkeys.cs @@ -110,7 +110,7 @@ namespace Shadowsocks.Controller.Hotkeys { try { - if (s.IsNullOrEmpty()) return null; + if (string.IsNullOrEmpty(s)) return null; int offset = s.LastIndexOf("+", StringComparison.OrdinalIgnoreCase); if (offset <= 0) return null; string modifierStr = s.Substring(0, offset).Trim(); diff --git a/shadowsocks-csharp/Controller/System/SystemProxy.cs b/shadowsocks-csharp/Controller/System/SystemProxy.cs index 25370818..826ee9c7 100644 --- a/shadowsocks-csharp/Controller/System/SystemProxy.cs +++ b/shadowsocks-csharp/Controller/System/SystemProxy.cs @@ -31,7 +31,7 @@ namespace Shadowsocks.Controller else { string pacUrl; - if (config.useOnlinePac && !config.pacUrl.IsNullOrEmpty()) + if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl)) { pacUrl = config.pacUrl; } diff --git a/shadowsocks-csharp/Data/i18n.csv b/shadowsocks-csharp/Data/i18n.csv index bc9c1f95..8a3a6689 100644 --- a/shadowsocks-csharp/Data/i18n.csv +++ b/shadowsocks-csharp/Data/i18n.csv @@ -16,7 +16,6 @@ PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PACモード Global,Для всей системы,全局模式,全局模式,グローバルプロキシ,전역,Global Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… -Statistics Config...,Настройки статистики…,统计配置...,統計設定檔...,統計情報の設定...,통계 설정,Configuration des statistiques… Online Config...,,在线配置...,線上配置...,,, Start on Boot,Автозагрузка,开机启动,開機啟動,システム起動時に実行,시스템 시작 시에 시작하기,Démarrage automatique Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,ss:// リンクの関連付け,ss:// 링크 연결, @@ -28,6 +27,7 @@ Edit Local PAC File...,Редактировать локальный PAC…,编 Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 프록시 자동 구성 파일 업데이트,Mettre à jour le PAC local à partir de Geosite Edit User Rule for Geosite...,Редактировать свои правила для Geosite,编辑 Geosite 的用户规则...,編輯 Geosite 的使用者規則...,ユーザールールの編集...,Geosite 사용자 수정,Modifier la règle utilisateur pour Geosite ... Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 프록시 자동 구성 파일 암호화,Sécuriser PAC local +Regenerate local PAC on version update,,版本更新后重新生成本地 PAC,版本更新後重新生成本地 PAC,,, Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 프록시 자동 구성 파일 URL 복사,Copier l'URL du PAC local Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... @@ -46,7 +46,6 @@ Quit,Выход,退出,結束,終了,종료,Quitter Edit Servers,Редактирование серверов,编辑服务器,編輯伺服器,サーバーの編集,서버 수정,Éditer serveurs Load Balance,Балансировка нагрузки,负载均衡,負載平衡,サーバーロードバランス,로드밸런싱,Répartition de charge High Availability,Высокая доступность,高可用,高可用性,高可用性,고가용성,Haute disponibilité -Choose by statistics,На основе статистики,根据统计,根據統計,統計で選ぶ,통계 기반,Choisissez par statistiques Show Plugin Output,События плагинов в журнале,显示插件输出,,プラグインの出力情報を表示,플러그인 출력 보이기,Afficher la sortie du plugin Write translation template,Создать шаблон для перевода,写入翻译模板,,翻訳テンプレートファイルを書き込む,번역 템플릿 쓰기,Écrire un modèle de traduction ,,,,,, @@ -80,55 +79,6 @@ Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Desc deprecated,Устаревшее,不推荐,不推薦,非推奨,더 이상 사용되지 않음,Obsolète "Encryption method {0} not exist, will replace with {1}",,加密方法{0}不存在,将使用{1}代替,,暗号化方式{0}が存在しません,{1}に置換します,{0} 암호화 방식이 존재하지 않으므로 {1}로 대체될 것입니다.,"Méthode de chiffrement {0} n'existe pas, sera remplacée par {1}" ,,,,,, -# Online Config Form,,,,,, -,,,,,, -Online config,,在线配置,線上配置,,, -Online config URL,,在线配置链接,線上配置鏈接,,, -&Update,,更新,更新,,, -U&pdate All,,全部更新,全部更新,,, -,,,,,, -#Statistics Config,,,,,, -,,,,,, -Enable Statistics,Включить сбор статистики,启用统计,,統計を有効にする,통계 활성화,Activer statistiques -Ping Test,Проверка связи (Ping),Ping测试,,Ping測定,Ping 테스트,Test ping -packages everytime,пакета на проверку,个包/次,,パケット/回,시간 당 패킷,Packages à chaque fois -By hour of day,Ежечасно,按照每天的小时数统计,,毎日の時間数で統計,매 시간,Par heure du jour -Collect data per,Собирать данные за,收集数据每,,ごとにデータを収集,데이터 수집 기간,Recueillir des données par -Keep choice for,Хранить отбор данных за,保持选择每,,,설정 유지하기,Gardez le choix pour -minutes,мин.,分钟,,分,분,Minutes -Final Score:,Финальная оценка:,总分:,,,최종 점수:,Score final: -AverageLatency,СредЗадержка,平均延迟,,平均遅延時間,평균 지연시간,LatenceMoyenne -MinLatency,МинЗадержка,最小延迟,,最小遅延時間,최소 지연시간,MinLatence -MaxLatency,МаксЗадержка,最大延迟,,最大遅延時間,최대 지연시간,MaxLatence -AverageInboundSpeed,СредВходСкорость,平均入站速度,,平均インバウンド速度,평균 인바운드 속도,VitesseEntranteMoyenne -MinInboundSpeed,МинВходСкорость,最小入站速度,,最小インバウンド速度,최소 인바운드 속도,MinVitesseEntrante -MaxInboundSpeed,СредВходСкорость,最大入站速度,,最大インバウンド速度,최대 인바운드 속도,MaxVitesseEntrante -AverageOutboundSpeed,СредИсхСкорость,平均出站速度,,平均アウトバウンド速度,평균 아웃바운드 속도,VitesseSortanteMoyenne -MinOutboundSpeed,МинИсхСкорость,最小出站速度,,最小アウトバウンド速度,최소 아웃바운드 속도,MinVitesseSortante -MaxOutboundSpeed,МаксИсхСкорость,最大出站速度,,最大アウトバウンド速度,최대 아웃바운드 속도,MaxVitesseSortante -AverageResponse,СредВремяОтвета,平均响应速度,,平均レスポンス速度,평균 응답,RéponseMoyenne -MinResponse,МинВремяОтвета,最小响应速度,,最小レスポンス速度,최소 응답,MinRéponse -MaxResponse,МаксВремяОтвета,最大响应速度,,最大レスポンス速度,최대 응답,MaxRéponse -PackageLoss,ПотериПакетов,丢包率,,パケットロス率,패킷 손실,Perte de paquets -Speed,Скорость,速度,,速度,속도,Vitesse -Package Loss,Потери пакетов,丢包率,,パケットロス率,패킷 손실,Perte de paquets -Ping,Ping,网络延迟,,Ping,Ping,Ping -Chart Mode,График,图表模式,,図表モード,차트 모드,Mode graphique -24h,24ч,24小时,,24時間,24시간,24h -all,За все время,全部,,すべて,전체,tout -,,,,,, -# Proxy Form,,,,,, -,,,,,, -Edit Proxy,Редактирование прокси,代理设置,編輯 Proxy,プロキシの編集,프록시 수정,Modifier le proxy -Use Proxy,Использовать прокси,使用代理,使用 Proxy,プロキシを使用する,프록시 사용,Utiliser un proxy -Proxy Type,Тип прокси,代理类型,Proxy 類型,プロキシタイプ,프록시 종류,Type de proxy -Proxy Addr,Адрес прокси,代理地址,Proxy 位址,プロキシアドレス,프록시 주소,Adresse de proxy -Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port de proxy -"If server has a plugin, proxy will not be used","Если сервер использует плагины, прокси НЕ будет использоваться",若服务器含有插件,代理将不被使用,若伺服器含有外掛程式,Proxy 將不被使用,サーバーにプラグインがある場合、プロキシは使用されません,서버에 플러그인이 설치 되어 있는 경우 프록시를 사용할 수 없습니다.,"Si le serveur a un plugin, le proxy ne sera pas utilisé" -Use Auth,Требуется авторизация,使用认证,使用認證,認証を使用する,서버 인증 사용,Utiliser l'authentification -User Name,Пользователь,用户名,認證用戶,ユーザ名,사용자 이름,Nom d'utilisateur -Auth Pwd,Пароль,认证密码,認證口令,パスワード,비밀번호,Mot de passe d'authentification -,,,,,, # Log Form,,,,,, ,,,,,, &File,Файл,文件(&F),檔案 (&F),ファイル (&F),파일 (&F),Fichier @@ -154,17 +104,6 @@ Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 프록시 자동 구성 URL 수정…,Modifier l'URL du PAC en ligne ... Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,프록시 자동 구성 URL을 입력하세요,Veuillez saisir l'URL PAC ,,,,,, -# HotkeySettings Form,,,,,, -,,,,,, -Switch system proxy,ВКЛ/ВЫКЛ системный прокси-сервер,切换系统代理状态,切換系統 Proxy 狀態,システム プロキシの状態を切り替える,시스템 프록시 전환,Changer l'état de proxy système -Switch system proxy mode,Переключение режима прокси-сервера,切换系统代理模式,切換系統 Proxy 模式,プロキシモードを切り替える,시스템 프록시 모드 전환,Changer le mode de proxy système -Allow Clients from LAN,Общий доступ к подключению,切换局域网共享,切換區域網路共用,LAN からのアクセスの許可を切り替える,LAN으로부터 클라이언트 허용,Autoriser les clients du LAN -Show Logs...,Просмотр журналов,显示日志,顯示記錄檔,ログの表示,로그 보기…,Afficher les journaux -Switch to previous server,Переключить на пред. сервер,切换上个服务器,切換上一個伺服器,前のサーバーに切り替える,이전 서버로 전환,Passer au serveur précédent -Switch to next server,Переключить на след. сервер,切换下个服务器,切換下一個伺服器,次のサーバーに切り替える,다음 서버로 전환,Passer au serveur suivant -Reg All,Применить все,注册全部快捷键,註冊所有快速鍵,全部登録する,모두 등록,Enregistrer tout -Reg Hotkeys At Startup,Применять при запуске программы,启动时注册快捷键,啟動時註冊快速鍵,起動時にホットキーを登録する,시스템 시작 시 단축키 등록,Enregistrer tout au démarrage -,,,,,, # Messages,,,,,, ,,,,,, Shadowsocks Error: {0},Ошибка Shadowsocks: {0},Shadowsocks 错误: {0},Shadowsocks 錯誤: {0},Shadowsocks エラー: {0},Shadowsocks 오류: {0},Erreur shadowsocks: {0} @@ -177,9 +116,7 @@ Server IP can not be blank,IP-адрес сервера не может быть Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée Port can't be 8123,Адрес порта 8123 не может быть использован,端口不能为 8123,連接埠號碼不能為 8123,8123 番以外のポート番号を指定して下さい。,8123번 포트는 사용할 수 없습니다.,Le port ne peut pas être 8123 -Shadowsocks {0} Update Found,Обнаружена новая версия Shadowsocks: {0},Shadowsocks {0} 更新,Shadowsocks {0} 更新,Shadowsocks バージョン {0} は利用できます。,Shadowsocks {0} 업데이트가 있습니다.,Shadowsocks {0} Mise à jour trouvée No update is available,Обновлений не обнаружено,没有可用的更新,沒有可用的更新,お使いのバージョンは最新です。,사용 가능한 업데이트가 없습니다.,Aucune mise à jour n'est disponible -Click here to update,Нажмите сюда для обновления,点击这里升级,點按此處升級,クリックしてアップデートします。,여기를 클릭하여 업데이트,Cliquez ici pour mettre à jour Shadowsocks is here,Shadowsocks находится здесь,Shadowsocks 在这里,Shadowsocks 在這裡,Shadowsocks はここです。,Shadowsocks는 여기에 있습니다,Veuillez trouver Shadowsocks ici You can turn on/off Shadowsocks in the context menu,Вы можете управлять Shadowsocks из контекстного меню,可以在右键菜单中开关 Shadowsocks,可以在右鍵選項單中開關 Shadowsocks,コンテキストメニューを使って、Shadowsocks を有効または無効にすることができます。,프로그램 메뉴에서 Shadowsocks를 끄고 켤 수 있습니다.,Vous pouvez activer / désactiver Shadowsocks dans le menu contextuel System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé @@ -191,7 +128,7 @@ No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. "If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire." -Failed to decode QRCode,Не удалось распознать QRCode,无法解析二维码,QR 碼解碼失敗,QR コードの読み取りに失敗しました。,QR코드를 해석하는데에 실패했습니다.,Impossible de décoder le QRCode +Invalid QR Code content: {0},,无效二维码内容: {0},無效二維碼內容: {0},,, Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,, @@ -218,5 +155,3 @@ Whether to discard unconfigured servers,Внесенные изменения б ,,,,タイムアウト値のフォーマットが無効なため、オートセーブできません。変更を破棄しますか,, "Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,プロキシ設定の処理にエラーが発生しました、現在のプロキシ設定をリセットし、再試行してもいいですか,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시 시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? "Unrecoverable proxy setting error occured, see log for detail","Произошла серьезная ошибка, подробности можно узнать в журналах",发生不可恢复的代理设置错误,查看日志以取得详情,,プロキシ設定に回復不能なエラーが発生しました、ログで詳細をご確認ください,복구 불가능한 프록시 설정 오류가 발생했습니다. 자세한 정보는 로그를 참조하세요.,"Une erreur de paramètre de proxy irrécupérable s'est produite, consultez le journal pour plus de détails" -Auth user can not be blank,Пользователь не может быть пустым,认证用户不能为空,認證用戶不能為空,認証ユーザが指定されていません。,인증 정보의 사용자 이름은 비어있을 수 없습니다.,L'utilisateur d'authentification ne peut pas être vide -Auth pwd can not be blank,Пароль не может быть пустым,认证密码不能为空,認證口令不能為空,認証パスワードが指定されていません。,인증 정보의 비밀번호는 비어있을 수 없습니다.,Le mot de passe d'authentification ne peut pas être vide diff --git a/shadowsocks-csharp/Encryption/EncryptorFactory.cs b/shadowsocks-csharp/Encryption/EncryptorFactory.cs index 6f68e23c..e3418257 100644 --- a/shadowsocks-csharp/Encryption/EncryptorFactory.cs +++ b/shadowsocks-csharp/Encryption/EncryptorFactory.cs @@ -71,7 +71,7 @@ namespace Shadowsocks.Encryption public static IEncryptor GetEncryptor(string method, string password) { - if (method.IsNullOrEmpty()) + if (string.IsNullOrEmpty(method)) { method = Model.Server.DefaultMethod; } diff --git a/shadowsocks-csharp/FodyWeavers.xml b/shadowsocks-csharp/FodyWeavers.xml index a5ec775c..39c0beec 100644 --- a/shadowsocks-csharp/FodyWeavers.xml +++ b/shadowsocks-csharp/FodyWeavers.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/shadowsocks-csharp/FodyWeavers.xsd b/shadowsocks-csharp/FodyWeavers.xsd index a96b3592..91102829 100644 --- a/shadowsocks-csharp/FodyWeavers.xsd +++ b/shadowsocks-csharp/FodyWeavers.xsd @@ -4,6 +4,7 @@ + diff --git a/shadowsocks-csharp/Localization/LocalizationProvider.cs b/shadowsocks-csharp/Localization/LocalizationProvider.cs new file mode 100644 index 00000000..0677bce1 --- /dev/null +++ b/shadowsocks-csharp/Localization/LocalizationProvider.cs @@ -0,0 +1,13 @@ +using System.Reflection; +using WPFLocalizeExtension.Extensions; + +namespace Shadowsocks.Localization +{ + public static class LocalizationProvider + { + public static T GetLocalizedValue(string key) + { + return LocExtension.GetLocalizedValue(Assembly.GetCallingAssembly().GetName().Name + ":Strings:" + key); + } + } +} diff --git a/shadowsocks-csharp/Localization/Strings.Designer.cs b/shadowsocks-csharp/Localization/Strings.Designer.cs new file mode 100644 index 00000000..6aa6738a --- /dev/null +++ b/shadowsocks-csharp/Localization/Strings.Designer.cs @@ -0,0 +1,432 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Shadowsocks.Localization { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Localization.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to _Add. + /// + internal static string addButton_Content { + get { + return ResourceManager.GetString("addButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Address. + /// + internal static string Address { + get { + return ResourceManager.GetString("Address", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allow clients from LAN. + /// + internal static string AllowClientsFromLAN { + get { + return ResourceManager.GetString("AllowClientsFromLAN", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Cancel. + /// + internal static string cancelButton_Content { + get { + return ResourceManager.GetString("cancelButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Copy. + /// + internal static string Copy { + get { + return ResourceManager.GetString("Copy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Copy link. + /// + internal static string copyLinkButton_Content { + get { + return ResourceManager.GetString("copyLinkButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Credentials (optional). + /// + internal static string CredentialsOptional { + get { + return ResourceManager.GetString("CredentialsOptional", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Details. + /// + internal static string Details { + get { + return ResourceManager.GetString("Details", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Forward Proxy. + /// + internal static string ForwardProxy { + get { + return ResourceManager.GetString("ForwardProxy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hotkeys. + /// + internal static string Hotkeys { + get { + return ResourceManager.GetString("Hotkeys", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to HTTP. + /// + internal static string HTTP { + get { + return ResourceManager.GetString("HTTP", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No proxy. + /// + internal static string NoProxy { + get { + return ResourceManager.GetString("NoProxy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Not now. + /// + internal static string notNowButton_Content { + get { + return ResourceManager.GetString("notNowButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _OK. + /// + internal static string okButton_Content { + get { + return ResourceManager.GetString("okButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Online Configuration Delivery. + /// + internal static string OnlineConfigDelivery { + get { + return ResourceManager.GetString("OnlineConfigDelivery", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open logs window. + /// + internal static string OpenLogsWindow { + get { + return ResourceManager.GetString("OpenLogsWindow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Password. + /// + internal static string Password { + get { + return ResourceManager.GetString("Password", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Port. + /// + internal static string Port { + get { + return ResourceManager.GetString("Port", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Register all. + /// + internal static string registerAllButton_Content { + get { + return ResourceManager.GetString("registerAllButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Register hotkeys at startup. + /// + internal static string RegisterHotkeysAtStartup { + get { + return ResourceManager.GetString("RegisterHotkeysAtStartup", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remove. + /// + internal static string removeButton_Content { + get { + return ResourceManager.GetString("removeButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Save. + /// + internal static string saveButton_Content { + get { + return ResourceManager.GetString("saveButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Server Sharing. + /// + internal static string ServerSharing { + get { + return ResourceManager.GetString("ServerSharing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The following sources failed to update:\n\n. + /// + internal static string sip008UpdateAllFailure { + get { + return ResourceManager.GetString("sip008UpdateAllFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully updated all sources!. + /// + internal static string sip008UpdateAllSuccess { + get { + return ResourceManager.GetString("sip008UpdateAllSuccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update failed. See the logs for more information.. + /// + internal static string sip008UpdateFailure { + get { + return ResourceManager.GetString("sip008UpdateFailure", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully updated the selected source!. + /// + internal static string sip008UpdateSuccess { + get { + return ResourceManager.GetString("sip008UpdateSuccess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Skip version. + /// + internal static string skipVersionButton_Content { + get { + return ResourceManager.GetString("skipVersionButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SOCKS5. + /// + internal static string SOCKS5 { + get { + return ResourceManager.GetString("SOCKS5", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to next server. + /// + internal static string SwitchToNextServer { + get { + return ResourceManager.GetString("SwitchToNextServer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Switch to previous server. + /// + internal static string SwitchToPreviousServer { + get { + return ResourceManager.GetString("SwitchToPreviousServer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Timeout (sec). + /// + internal static string Timeout { + get { + return ResourceManager.GetString("Timeout", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle proxy mode. + /// + internal static string ToggleProxyMode { + get { + return ResourceManager.GetString("ToggleProxyMode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggle system proxy. + /// + internal static string ToggleSystemProxy { + get { + return ResourceManager.GetString("ToggleSystemProxy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type. + /// + internal static string Type { + get { + return ResourceManager.GetString("Type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Update all. + /// + internal static string updateAllButton_Content { + get { + return ResourceManager.GetString("updateAllButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to _Update. + /// + internal static string updateButton_Content { + get { + return ResourceManager.GetString("updateButton_Content", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please read the release notes carefully. Then decide whether to update.. + /// + internal static string updatePromptBody { + get { + return ResourceManager.GetString("updatePromptBody", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An update is available.. + /// + internal static string updatePromptTitle { + get { + return ResourceManager.GetString("updatePromptTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Username. + /// + internal static string Username { + get { + return ResourceManager.GetString("Username", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to VersionUpdate. + /// + internal static string VersionUpdate { + get { + return ResourceManager.GetString("VersionUpdate", resourceCulture); + } + } + } +} diff --git a/shadowsocks-csharp/View/ProxyForm.resx b/shadowsocks-csharp/Localization/Strings.fr.resx similarity index 73% rename from shadowsocks-csharp/View/ProxyForm.resx rename to shadowsocks-csharp/Localization/Strings.fr.resx index 29dcb1b3..da708dd2 100644 --- a/shadowsocks-csharp/View/ProxyForm.resx +++ b/shadowsocks-csharp/Localization/Strings.fr.resx @@ -1,120 +1,183 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Mot de passe + + + Port + + + Type + + + adresse + + + Délai d'attente (sec) + + + Annuler + + + OK + + + Changer l'état de proxy système + + + Changer le mode de proxy système + + + Autoriser les clients du LAN + + + Afficher les journaux + + + Passer au serveur précédent + + + Passer au serveur suivant + + + Enregistrer tout + + + Enregistrer tout au démarrage + + + Ajouter + + + sauter la version + + + Copier le lien + + + retirer + + + mise à jour + + + copie + \ No newline at end of file diff --git a/shadowsocks-csharp/View/QRCodeForm.resx b/shadowsocks-csharp/Localization/Strings.ja.resx old mode 100755 new mode 100644 similarity index 72% rename from shadowsocks-csharp/View/QRCodeForm.resx rename to shadowsocks-csharp/Localization/Strings.ja.resx index 29dcb1b3..b47f5ba1 --- a/shadowsocks-csharp/View/QRCodeForm.resx +++ b/shadowsocks-csharp/Localization/Strings.ja.resx @@ -1,120 +1,180 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + パスワード + + + タイプ + + + アドレス + + + ポート + + + タイムアウト (秒) + + + キャンセル + + + OK + + + システム プロキシの状態を切り替える + + + プロキシモードを切り替える + + + LAN からのアクセスの許可を切り替える + + + ログの表示 + + + 前のサーバーに切り替える + + + 次のサーバーに切り替える + + + 全部登録する + + + 起動時にホットキーを登録する + + + 新規 + + + バージョンをスキップ + + + リンクをコピーする + + + 更新 + + + コピー + \ No newline at end of file diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx b/shadowsocks-csharp/Localization/Strings.ko.resx similarity index 73% rename from shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx rename to shadowsocks-csharp/Localization/Strings.ko.resx index 8360b4c2..65c899e4 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.resx +++ b/shadowsocks-csharp/Localization/Strings.ko.resx @@ -117,13 +117,64 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 4, 5 - - - 238, 6 - - - 191 - + + 비밀번호 + + + 유형 + + + 주소 + + + 포트 + + + 시간 초과 (초) + + + 취소 + + + 확인 + + + 시스템 프록시 전환 + + + 시스템 프록시 모드 전환 + + + LAN으로부터 클라이언트 허용 + + + 로그 보기… + + + 이전 서버로 전환 + + + 다음 서버로 전환 + + + 모두 등록 + + + 시스템 시작 시 단축키 등록 + + + 추가 + + + 버전 건너 뛰기 + + + 링크 복사 + + + 새롭게 함 + + + + \ No newline at end of file diff --git a/shadowsocks-csharp/Localization/Strings.resx b/shadowsocks-csharp/Localization/Strings.resx new file mode 100644 index 00000000..9348c488 --- /dev/null +++ b/shadowsocks-csharp/Localization/Strings.resx @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Type + + + No proxy + + + SOCKS5 + + + HTTP + + + Details + + + Address + + + Port + + + Timeout (sec) + + + Credentials (optional) + + + Username + + + Password + + + _Save + + + _Cancel + + + _OK + + + Toggle system proxy + + + Toggle proxy mode + + + Allow clients from LAN + + + Open logs window + + + Switch to previous server + + + Switch to next server + + + Register hotkeys at startup + + + _Register all + + + _Update + + + Update all + + + _Copy link + + + Remove + + + _Add + + + An update is available. + + + Please read the release notes carefully. Then decide whether to update. + + + _Skip version + + + _Not now + + + _Copy + + + Forward Proxy + + + Server Sharing + + + Hotkeys + + + Online Configuration Delivery + + + VersionUpdate + + + Successfully updated the selected source! + + + Update failed. See the logs for more information. + + + Successfully updated all sources! + + + The following sources failed to update:\n\n + + \ No newline at end of file diff --git a/shadowsocks-csharp/View/CalculationControl.resx b/shadowsocks-csharp/Localization/Strings.ru.resx similarity index 70% rename from shadowsocks-csharp/View/CalculationControl.resx rename to shadowsocks-csharp/Localization/Strings.ru.resx index 29dcb1b3..ce007448 100644 --- a/shadowsocks-csharp/View/CalculationControl.resx +++ b/shadowsocks-csharp/Localization/Strings.ru.resx @@ -1,120 +1,183 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Пароль + + + тип + + + адрес + + + порт + + + Таймаут (сек) + + + Отмена + + + ОК + + + ВКЛ/ВЫКЛ системный прокси-сервер + + + Переключение режима прокси-сервера + + + Общий доступ к подключению + + + Просмотр журналов + + + Переключить на пред. сервер + + + Переключить на след. сервер + + + Применить все + + + Применять при запуске программы + + + Добавить + + + пропустить версию + + + копировать ссылку + + + Удалить + + + Обновить + + + копировать + \ No newline at end of file diff --git a/shadowsocks-csharp/Localization/Strings.zh-Hans.resx b/shadowsocks-csharp/Localization/Strings.zh-Hans.resx new file mode 100644 index 00000000..f3ef9520 --- /dev/null +++ b/shadowsocks-csharp/Localization/Strings.zh-Hans.resx @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 不使用代理 + + + 类型 + + + 认证 (可选) + + + 详细设置 + + + 地址 + + + 保存 + + + 取消 + + + 用户名 + + + 密码 + + + 端口 + + + 超时 (秒) + + + 确定 + + + 全部注册 + + + 切换系统代理 + + + 切换代理模式 + + + 允许内网客户端连接 + + + 打开日志窗口 + + + 切换到上一个服务器 + + + 切换到下一个服务器 + + + 启动时注册快捷键 + + + 更新 + + + 全部更新 + + + 复制链接 + + + 移除 + + + 添加 + + + 跳过版本 + + + 暂不更新 + + + 发现可用更新 + + + 请仔细阅读发布信息,然后决定是否更新。 + + + 复制 + + + 前置代理 + + + 在线配置下发 + + + 热键 + + + 服务器分享 + + + 版本更新 + + + 成功更新选定来源! + + + 更新失败,请查看日志获取更多信息。 + + + 成功更新所有来源! + + + 下列来源更新失败:\n\n + + \ No newline at end of file diff --git a/shadowsocks-csharp/Localization/Strings.zh-Hant.resx b/shadowsocks-csharp/Localization/Strings.zh-Hant.resx new file mode 100644 index 00000000..668c2b25 --- /dev/null +++ b/shadowsocks-csharp/Localization/Strings.zh-Hant.resx @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 密碼 + + + Port + + + 類型 + + + 位址 + + + 逾時 (秒) + + + 取消 + + + 確定 + + + 切換系統 Proxy 狀態 + + + 切換系統 Proxy 模式 + + + 切換區域網路共用 + + + 顯示記錄檔 + + + 切換上一個伺服器 + + + 切換下一個伺服器 + + + 註冊所有快速鍵 + + + 啟動時註冊快速鍵 + + + 更新 + + + 全部更新 + + + 新增 + + + 跳過版本 + + + 暫不更新 + + + 發現可用更新 + + + 複製鏈接 + + + 複製 + + \ No newline at end of file diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 60df5410..b955e0f7 100644 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; +using System.Windows; using Newtonsoft.Json; using NLog; using Shadowsocks.Controller; @@ -27,33 +28,36 @@ namespace Shadowsocks.Model public bool global; public bool enabled; public bool shareOverLan; - public bool isDefault; + public bool firstRun; public int localPort; public bool portableMode; public bool showPluginOutput; public string pacUrl; public bool useOnlinePac; - public bool secureLocalPac; - public bool availabilityStatistics; + public bool secureLocalPac; // enable secret for PAC server + public bool regeneratePacOnUpdate; // regenerate pac.txt on version update public bool autoCheckUpdate; public bool checkPreRelease; + public string skippedUpdateVersion; // skip the update with this version number public bool isVerboseLogging; // hidden options public bool isIPv6Enabled; // for experimental ipv6 support - public bool generateLegacyUrl = false; // for pre-sip002 url compatibility + public bool generateLegacyUrl; // for pre-sip002 url compatibility public string geositeUrl; // for custom geosite source (and rule group) - public string geositeGroup; - public bool geositeBlacklistMode; + public List geositeDirectGroups; // groups of domains that we connect without the proxy + public List geositeProxiedGroups; // groups of domains that we connect via the proxy + public bool geositePreferDirect; // a.k.a blacklist mode + public string userAgent; //public NLogConfig.LogLevel logLevel; public LogViewerConfig logViewer; - public ProxyConfig proxy; + public ForwardProxyConfig proxy; public HotkeyConfig hotkey; [JsonIgnore] - public bool updated; + public bool firstRunOnNewVersion; public Configuration() { @@ -63,35 +67,48 @@ namespace Shadowsocks.Model global = false; enabled = false; shareOverLan = false; - isDefault = true; + firstRun = true; localPort = 1080; portableMode = true; showPluginOutput = false; pacUrl = ""; useOnlinePac = false; secureLocalPac = true; - availabilityStatistics = false; + regeneratePacOnUpdate = true; autoCheckUpdate = false; checkPreRelease = false; + skippedUpdateVersion = ""; isVerboseLogging = false; // hidden options isIPv6Enabled = false; generateLegacyUrl = false; geositeUrl = ""; - geositeGroup = "geolocation-!cn"; - geositeBlacklistMode = true; + geositeDirectGroups = new List() + { + "cn", + "geolocation-!cn@cn" + }; + geositeProxiedGroups = new List() + { + "geolocation-!cn" + }; + geositePreferDirect = false; + userAgent = "ShadowsocksWindows/$version"; logViewer = new LogViewerConfig(); - proxy = new ProxyConfig(); + proxy = new ForwardProxyConfig(); hotkey = new HotkeyConfig(); - updated = false; + firstRunOnNewVersion = false; configs = new List(); onlineConfigSource = new List(); } + [JsonIgnore] + public string userAgentString; // $version substituted with numeral version in it + [JsonIgnore] NLogConfig nLogConfig; @@ -103,11 +120,8 @@ namespace Shadowsocks.Model #endif [JsonIgnore] - public string localHost => GetLocalHost(); - private string GetLocalHost() - { - return isIPv6Enabled ? "[::1]" : "127.0.0.1"; - } + public string LocalHost => isIPv6Enabled ? "[::1]" : "127.0.0.1"; + public Server GetCurrentServer() { if (index >= 0 && index < configs.Count) @@ -124,6 +138,11 @@ namespace Shadowsocks.Model localPort) : null; + /// + /// Used by multiple forms to validate a server. + /// Communication is done by throwing exceptions. + /// + /// public static void CheckServer(Server server) { CheckServer(server.server); @@ -132,52 +151,70 @@ namespace Shadowsocks.Model CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); } - public static bool ChecksServer(Server server) - { - try - { - CheckServer(server); - return true; - } - catch (Exception) - { - return false; - } - } - + /// + /// Loads the configuration from file. + /// + /// An Configuration object. public static Configuration Load() { Configuration config; - try + if (File.Exists(CONFIG_FILE)) { - string configContent = File.ReadAllText(CONFIG_FILE); - config = JsonConvert.DeserializeObject(configContent); - config.isDefault = false; - if (UpdateChecker.Asset.CompareVersion(UpdateChecker.Version, config.version ?? "0") > 0) + try { - config.updated = true; + string configContent = File.ReadAllText(CONFIG_FILE); + config = JsonConvert.DeserializeObject(configContent, new JsonSerializerSettings() + { + ObjectCreationHandling = ObjectCreationHandling.Replace + }); + return config; } - - if (config.configs.Count == 0) - config.configs.Add(GetDefaultServer()); - if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) - config.index = 0; - if (!System.Net.Sockets.Socket.OSSupportsIPv6) + catch (Exception e) { - config.isIPv6Enabled = false; // disable IPv6 if os not support + if (!(e is FileNotFoundException)) + logger.LogUsefulException(e); } - //TODO if remote host(server) do not support IPv6 (or DNS resolve AAAA TYPE record) disable IPv6? - - config.proxy.CheckConfig(); } - catch (Exception e) + config = new Configuration(); + return config; + } + + /// + /// Process the loaded configurations and set up things. + /// + /// A reference of Configuration object. + public static void Process(ref Configuration config) + { + // Verify if the configured geosite groups exist. + // Reset to default if ANY one of the configured group doesn't exist. + if (!ValidateGeositeGroupList(config.geositeDirectGroups)) + ResetGeositeDirectGroup(ref config.geositeDirectGroups); + if (!ValidateGeositeGroupList(config.geositeProxiedGroups)) + ResetGeositeProxiedGroup(ref config.geositeProxiedGroups); + + // Mark the first run of a new version. + var appVersion = new Version(UpdateChecker.Version); + var configVersion = new Version(config.version); + if (appVersion.CompareTo(configVersion) > 0) { - if (!(e is FileNotFoundException)) - logger.LogUsefulException(e); - config = new Configuration(); - config.configs.Add(GetDefaultServer()); + config.firstRunOnNewVersion = true; } - + // Add an empty server configuration + if (config.configs.Count == 0) + config.configs.Add(GetDefaultServer()); + // Selected server + if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) + config.index = 0; + if (config.index >= config.configs.Count) + config.index = config.configs.Count - 1; + // Check OS IPv6 support + if (!System.Net.Sockets.Socket.OSSupportsIPv6) + config.isIPv6Enabled = false; + config.proxy.CheckConfig(); + // Replace $version with the version number. + config.userAgentString = config.userAgent.Replace("$version", config.version); + + // NLog log level try { config.nLogConfig = NLogConfig.LoadXML(); @@ -197,46 +234,42 @@ namespace Shadowsocks.Model } catch (Exception e) { - // todo: route the error to UI since there is no log file in this scenario - logger.Error(e, "Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); + MessageBox.Show($"Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes.\n{e.Message}"); } - - return config; } + /// + /// Saves the Configuration object to file. + /// + /// A Configuration object. public static void Save(Configuration config) { config.configs = SortByOnlineConfig(config.configs); - if (config.index >= config.configs.Count) - config.index = config.configs.Count - 1; - if (config.index < -1) - config.index = -1; - if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) - config.index = 0; - config.isDefault = false; + + FileStream configFileStream = null; + StreamWriter configStreamWriter = null; try { - using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) - { - string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); - sw.Write(jsonString); - sw.Flush(); - } - try - { - // apply changes to NLog.config - config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info); - NLogConfig.SaveXML(config.nLogConfig); - } - catch (Exception e) - { - logger.Error(e, "Cannot set the log level to NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); - } + configFileStream = File.Open(CONFIG_FILE, FileMode.Create); + configStreamWriter = new StreamWriter(configFileStream); + var jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); + configStreamWriter.Write(jsonString); + configStreamWriter.Flush(); + // NLog + config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info); + NLogConfig.SaveXML(config.nLogConfig); } - catch (IOException e) + catch (Exception e) { logger.LogUsefulException(e); } + finally + { + if (configStreamWriter != null) + configStreamWriter.Dispose(); + if (configFileStream != null) + configFileStream.Dispose(); + } } public static List SortByOnlineConfig(IEnumerable servers) @@ -248,6 +281,49 @@ namespace Shadowsocks.Model return ret; } + /// + /// Validates if the groups in the list are all valid. + /// + /// The list of groups to validate. + /// + /// True if all groups are valid. + /// False if any one of them is invalid. + /// + public static bool ValidateGeositeGroupList(List groups) + { + foreach (var geositeGroup in groups) + if (!GeositeUpdater.CheckGeositeGroup(geositeGroup)) // found invalid group + { +#if DEBUG + logger.Debug($"Available groups:"); + foreach (var group in GeositeUpdater.Geosites.Keys) + logger.Debug($"{group}"); +#endif + logger.Warn($"The Geosite group {geositeGroup} doesn't exist. Resetting to default groups."); + return false; + } + return true; + } + + public static void ResetGeositeDirectGroup(ref List geositeDirectGroups) + { + geositeDirectGroups.Clear(); + geositeDirectGroups.Add("cn"); + geositeDirectGroups.Add("geolocation-!cn@cn"); + } + + public static void ResetGeositeProxiedGroup(ref List geositeProxiedGroups) + { + geositeProxiedGroups.Clear(); + geositeProxiedGroups.Add("geolocation-!cn"); + } + + public static void ResetUserAgent(Configuration config) + { + config.userAgent = "ShadowsocksWindows/$version"; + config.userAgentString = config.userAgent.Replace("$version", config.version); + } + public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null) { if (config?.configs != null) @@ -284,13 +360,13 @@ namespace Shadowsocks.Model private static void CheckPassword(string password) { - if (password.IsNullOrEmpty()) + if (string.IsNullOrEmpty(password)) throw new ArgumentException(I18N.GetString("Password can not be blank")); } public static void CheckServer(string server) { - if (server.IsNullOrEmpty()) + if (string.IsNullOrEmpty(server)) throw new ArgumentException(I18N.GetString("Server IP can not be blank")); } @@ -300,17 +376,5 @@ namespace Shadowsocks.Model throw new ArgumentException( I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); } - - public static void CheckProxyAuthUser(string user) - { - if (user.IsNullOrEmpty()) - throw new ArgumentException(I18N.GetString("Auth user can not be blank")); - } - - public static void CheckProxyAuthPwd(string pwd) - { - if (pwd.IsNullOrEmpty()) - throw new ArgumentException(I18N.GetString("Auth pwd can not be blank")); - } } } diff --git a/shadowsocks-csharp/Model/ProxyConfig.cs b/shadowsocks-csharp/Model/ForwardProxyConfig.cs similarity index 90% rename from shadowsocks-csharp/Model/ProxyConfig.cs rename to shadowsocks-csharp/Model/ForwardProxyConfig.cs index da60b622..7a1d4ec5 100644 --- a/shadowsocks-csharp/Model/ProxyConfig.cs +++ b/shadowsocks-csharp/Model/ForwardProxyConfig.cs @@ -1,43 +1,43 @@ -using System; - -namespace Shadowsocks.Model -{ - [Serializable] - public class ProxyConfig - { - public const int PROXY_SOCKS5 = 0; - public const int PROXY_HTTP = 1; - - public const int MaxProxyTimeoutSec = 10; - private const int DefaultProxyTimeoutSec = 3; - - public bool useProxy; - public int proxyType; - public string proxyServer; - public int proxyPort; - public int proxyTimeout; - public bool useAuth; - public string authUser; - public string authPwd; - - public ProxyConfig() - { - useProxy = false; - proxyType = PROXY_SOCKS5; - proxyServer = ""; - proxyPort = 0; - proxyTimeout = DefaultProxyTimeoutSec; - useAuth = false; - authUser = ""; - authPwd = ""; - } - - public void CheckConfig() - { - if (proxyType < PROXY_SOCKS5 || proxyType > PROXY_HTTP) - { - proxyType = PROXY_SOCKS5; - } - } - } -} +using System; + +namespace Shadowsocks.Model +{ + [Serializable] + public class ForwardProxyConfig + { + public const int PROXY_SOCKS5 = 0; + public const int PROXY_HTTP = 1; + + public const int MaxProxyTimeoutSec = 10; + private const int DefaultProxyTimeoutSec = 3; + + public bool useProxy; + public int proxyType; + public string proxyServer; + public int proxyPort; + public int proxyTimeout; + public bool useAuth; + public string authUser; + public string authPwd; + + public ForwardProxyConfig() + { + useProxy = false; + proxyType = PROXY_SOCKS5; + proxyServer = ""; + proxyPort = 0; + proxyTimeout = DefaultProxyTimeoutSec; + useAuth = false; + authUser = ""; + authPwd = ""; + } + + public void CheckConfig() + { + if (proxyType < PROXY_SOCKS5 || proxyType > PROXY_HTTP) + { + proxyType = PROXY_SOCKS5; + } + } + } +} diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 32a98c36..d72f1c8b 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -55,21 +55,17 @@ namespace Shadowsocks.Model return server.GetHashCode() ^ server_port; } - public override bool Equals(object obj) - { - Server o2 = (Server)obj; - return server == o2.server && server_port == o2.server_port; - } + public override bool Equals(object obj) => obj is Server o2 && server == o2.server && server_port == o2.server_port; public override string ToString() { - if (server.IsNullOrEmpty()) + if (string.IsNullOrEmpty(server)) { return I18N.GetString("New server"); } string serverStr = $"{FormalHostName}:{server_port}"; - return remarks.IsNullOrEmpty() + return string.IsNullOrEmpty(remarks) ? serverStr : $"{remarks} ({serverStr})"; } @@ -93,7 +89,7 @@ namespace Shadowsocks.Model u.Port = server_port; u.Fragment = HttpUtility.UrlEncode(remarks, Encoding.UTF8); - if (!plugin.IsNullOrWhiteSpace()) + if (!string.IsNullOrWhiteSpace(plugin)) { NameValueCollection param = HttpUtility.ParseQueryString(""); @@ -147,7 +143,7 @@ namespace Shadowsocks.Model Server server = new Server(); var base64 = match.Groups["base64"].Value.TrimEnd('/'); var tag = match.Groups["tag"].Value; - if (!tag.IsNullOrEmpty()) + if (!string.IsNullOrEmpty(tag)) { server.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8); } @@ -173,7 +169,7 @@ namespace Shadowsocks.Model public static Server ParseURL(string serverUrl) { string _serverUrl = serverUrl.Trim(); - if (!_serverUrl.BeginWith("ss://", StringComparison.InvariantCultureIgnoreCase)) + if (!_serverUrl.StartsWith("ss://", StringComparison.InvariantCultureIgnoreCase)) { return null; } diff --git a/shadowsocks-csharp/Model/StatisticsRecord.cs b/shadowsocks-csharp/Model/StatisticsRecord.cs deleted file mode 100644 index 5c1051a4..00000000 --- a/shadowsocks-csharp/Model/StatisticsRecord.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Shadowsocks.Model -{ - // Simple processed records for a short period of time - public class StatisticsRecord - { - public DateTime Timestamp { get; set; } = DateTime.Now; - public string ServerIdentifier { get; set; } - - // in ping-only records, these fields would be null - public int? AverageLatency; - public int? MinLatency; - public int? MaxLatency; - - private bool EmptyLatencyData => (AverageLatency == null) && (MinLatency == null) && (MaxLatency == null); - - public int? AverageInboundSpeed; - public int? MinInboundSpeed; - public int? MaxInboundSpeed; - - private bool EmptyInboundSpeedData - => (AverageInboundSpeed == null) && (MinInboundSpeed == null) && (MaxInboundSpeed == null); - - public int? AverageOutboundSpeed; - public int? MinOutboundSpeed; - public int? MaxOutboundSpeed; - - private bool EmptyOutboundSpeedData - => (AverageOutboundSpeed == null) && (MinOutboundSpeed == null) && (MaxOutboundSpeed == null); - - // if user disabled ping test, response would be null - public int? AverageResponse; - public int? MinResponse; - public int? MaxResponse; - public float? PackageLoss; - - private bool EmptyResponseData - => (AverageResponse == null) && (MinResponse == null) && (MaxResponse == null) && (PackageLoss == null); - - public bool IsEmptyData() { - return EmptyInboundSpeedData && EmptyOutboundSpeedData && EmptyResponseData && EmptyLatencyData; - } - - public StatisticsRecord() - { - } - - public StatisticsRecord(string identifier, ICollection inboundSpeedRecords, ICollection outboundSpeedRecords, ICollection latencyRecords) - { - ServerIdentifier = identifier; - var inbound = inboundSpeedRecords?.Where(s => s > 0).ToList(); - if (inbound != null && inbound.Any()) - { - AverageInboundSpeed = (int) inbound.Average(); - MinInboundSpeed = inbound.Min(); - MaxInboundSpeed = inbound.Max(); - } - var outbound = outboundSpeedRecords?.Where(s => s > 0).ToList(); - if (outbound!= null && outbound.Any()) - { - AverageOutboundSpeed = (int) outbound.Average(); - MinOutboundSpeed = outbound.Min(); - MaxOutboundSpeed = outbound.Max(); - } - var latency = latencyRecords?.Where(s => s > 0).ToList(); - if (latency!= null && latency.Any()) - { - AverageLatency = (int) latency.Average(); - MinLatency = latency.Min(); - MaxLatency = latency.Max(); - } - } - - public StatisticsRecord(string identifier, ICollection responseRecords) - { - ServerIdentifier = identifier; - SetResponse(responseRecords); - } - - public void SetResponse(ICollection responseRecords) - { - if (responseRecords == null) return; - var records = responseRecords.Where(response => response != null).Select(response => response.Value).ToList(); - if (!records.Any()) return; - AverageResponse = (int?) records.Average(); - MinResponse = records.Min(); - MaxResponse = records.Max(); - PackageLoss = responseRecords.Count(response => response != null)/(float) responseRecords.Count; - } - } -} diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs deleted file mode 100644 index 18028856..00000000 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; - -using Newtonsoft.Json; -using NLog; - -namespace Shadowsocks.Model -{ - [Serializable] - public class StatisticsStrategyConfiguration - { - private static Logger logger = LogManager.GetCurrentClassLogger(); - - public static readonly string ID = "com.shadowsocks.strategy.statistics"; - public bool StatisticsEnabled { get; set; } = false; - public bool ByHourOfDay { get; set; } = true; - public bool Ping { get; set; } - public int ChoiceKeptMinutes { get; set; } = 10; - public int DataCollectionMinutes { get; set; } = 10; - public int RepeatTimesNum { get; set; } = 4; - - private const string ConfigFile = "statistics-config.json"; - - public static StatisticsStrategyConfiguration Load() - { - try - { - var content = File.ReadAllText(ConfigFile); - var configuration = JsonConvert.DeserializeObject(content); - return configuration; - } - catch (FileNotFoundException) - { - var configuration = new StatisticsStrategyConfiguration(); - Save(configuration); - return configuration; - } - catch (Exception e) - { - logger.LogUsefulException(e); - return new StatisticsStrategyConfiguration(); - } - } - - public static void Save(StatisticsStrategyConfiguration configuration) - { - try - { - var content = JsonConvert.SerializeObject(configuration, Formatting.Indented); - File.WriteAllText(ConfigFile, content); - } - catch (Exception e) - { - logger.LogUsefulException(e); - } - } - - public Dictionary Calculations; - - public StatisticsStrategyConfiguration() - { - var properties = typeof(StatisticsRecord).GetFields(BindingFlags.Instance | BindingFlags.Public); - Calculations = properties.ToDictionary(p => p.Name, _ => (float)0); - } - } -} diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index 52dac1cc..8b1803e4 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Net; +using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -10,10 +11,13 @@ using System.Windows.Forms; using CommandLine; using Microsoft.Win32; using NLog; +using ReactiveUI; using Shadowsocks.Controller; using Shadowsocks.Controller.Hotkeys; using Shadowsocks.Util; using Shadowsocks.View; +using Splat; +using WPFLocalizeExtension.Engine; namespace Shadowsocks { @@ -89,6 +93,16 @@ namespace Shadowsocks // we have to do this for self-contained executables Directory.SetCurrentDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); + // We would use this in v5. + // Parameters would have to be dropped from views' constructors (VersionUpdatePromptView) + //Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); + + // Workaround for hosting WPF controls in a WinForms app. + // We have to manually set the culture for the LocalizeDictionary instance. + // https://stackoverflow.com/questions/374518/localizing-a-winforms-application-with-embedded-wpf-user-controls + // https://stackoverflow.com/questions/14668640/wpf-localize-extension-translate-window-at-run-time + LocalizeDictionary.Instance.Culture = Thread.CurrentThread.CurrentCulture; + #if DEBUG // truncate privoxy log file while debugging string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); @@ -163,7 +177,7 @@ namespace Shadowsocks Thread.Sleep(10 * 1000); try { - MainController.Start(false); + MainController.Start(true); logger.Info("controller started"); } catch (Exception ex) diff --git a/shadowsocks-csharp/Properties/DataSources/Shadowsocks.Model.StatisticsStrategyConfiguration.datasource b/shadowsocks-csharp/Properties/DataSources/Shadowsocks.Model.StatisticsStrategyConfiguration.datasource deleted file mode 100644 index a1a52e80..00000000 --- a/shadowsocks-csharp/Properties/DataSources/Shadowsocks.Model.StatisticsStrategyConfiguration.datasource +++ /dev/null @@ -1,10 +0,0 @@ - - - - Shadowsocks.Model.StatisticsStrategyConfiguration, Shadowsocks, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null - \ No newline at end of file diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs index 257c9a3d..2c64d698 100644 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ b/shadowsocks-csharp/Properties/Resources.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -13,16 +13,16 @@ namespace Shadowsocks.Properties { /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // 此类是由 StronglyTypedResourceBuilder - // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 - // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen - // (以 /str 作为命令选项),或重新生成 VS 项目。 + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { + public class Resources { private static global::System.Resources.ResourceManager resourceMan; @@ -33,10 +33,10 @@ namespace Shadowsocks.Properties { } /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + public static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shadowsocks.Properties.Resources", typeof(Resources).Assembly); @@ -47,11 +47,11 @@ namespace Shadowsocks.Properties { } /// - /// 重写当前线程的 CurrentUICulture 属性 - /// 重写当前线程的 CurrentUICulture 属性。 + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + public static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -61,7 +61,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找类似 /* eslint-disable */ + /// Looks up a localized string similar to /* eslint-disable */ ///// Was generated by gfwlist2pac in precise mode ///// https://github.com/clowwindy/gfwlist2pac /// @@ -69,25 +69,28 @@ namespace Shadowsocks.Properties { ///// 2019-02-08: Updated to support shadowsocks-windows user rules. /// ///var proxy = __PROXY__; - ///var userrules = __USERRULES__; - ///var rules = __RULES__; + ///var userrules = []; + ///var rules = []; /// - ////* - ///* This file is part of Adblock Plus <http://adblockplus.org/>, - ///* Copyright (C) 2006-2014 Eyeo GmbH - ///* - ///* Adblock Plus is free software: you can redistribute it and/or [字符串的其余部分被截断]"; 的本地化字符串。 + ///// convert to abp grammar + ///for (var i = 0; i < __RULES__.length; i++) { + /// var s = __RULES__[i]; + /// if (s.substring(0, 2) == "||") s += "^"; + /// rules.push(s); + ///} + /// + ///for (var i = 0; i < [rest of string was truncated]";. /// - internal static string abp_js { + public static string abp_js { get { return ResourceManager.GetString("abp_js", resourceCulture); } } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] dlc_dat { + public static byte[] dlc_dat { get { object obj = ResourceManager.GetObject("dlc_dat", resourceCulture); return ((byte[])(obj)); @@ -95,7 +98,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找类似 en,ru-RU,zh-CN,zh-TW,ja,ko,fr + /// Looks up a localized string similar to en,ru-RU,zh-CN,zh-TW,ja,ko,fr ///#Restart program to apply translation,,,,,, ///#This is comment line,,,,,, ///#Always keep language name at head of file,,,,,, @@ -107,31 +110,31 @@ namespace Shadowsocks.Properties { ///,,,,,, ///#Menu,,,,,, ///,,,,,, - ///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [字符串的其余部分被截断]"; 的本地化字符串。 + ///System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,P [rest of string was truncated]";. /// - internal static string i18n_csv { + public static string i18n_csv { get { return ResourceManager.GetString("i18n_csv", resourceCulture); } } /// - /// 查找类似 <?xml version="1.0" encoding="utf-8" ?> + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> ///<!-- Warning: Configuration may reset after shadowsocks upgrade. --> ///<!-- If you messed it up, delete this file and Shadowsocks will create a new one. --> ///<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> /// <targets> /// <!-- This line is managed by Shadowsocks. Do not modify it unless you know what you are doing.--> - /// <target name="file" xsi:type="File" fileName="ss_win_temp\shadowsocks.log" writ [字符串的其余部分被截断]"; 的本地化字符串。 + /// <target name="file" xsi:type="File" fileName="ss_win_temp\shadowsocks.log" writ [rest of string was truncated]";. /// - internal static string NLog_config { + public static string NLog_config { get { return ResourceManager.GetString("NLog_config", resourceCulture); } } /// - /// 查找类似 listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ + /// Looks up a localized string similar to listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ ///toggle 0 ///logfile ss_privoxy.log ///show-on-task-bar 0 @@ -139,18 +142,18 @@ namespace Shadowsocks.Properties { ///forward-socks5 / __SOCKS_HOST__:__SOCKS_PORT__ . ///max-client-connections 2048 ///hide-console - /// 的本地化字符串。 + ///. /// - internal static string privoxy_conf { + public static string privoxy_conf { get { return ResourceManager.GetString("privoxy_conf", resourceCulture); } } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] privoxy_exe { + public static byte[] privoxy_exe { get { object obj = ResourceManager.GetObject("privoxy_exe", resourceCulture); return ((byte[])(obj)); @@ -158,9 +161,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap ss32Fill { + public static System.Drawing.Bitmap ss32Fill { get { object obj = ResourceManager.GetObject("ss32Fill", resourceCulture); return ((System.Drawing.Bitmap)(obj)); @@ -168,9 +171,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap ss32In { + public static System.Drawing.Bitmap ss32In { get { object obj = ResourceManager.GetObject("ss32In", resourceCulture); return ((System.Drawing.Bitmap)(obj)); @@ -178,9 +181,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap ss32Out { + public static System.Drawing.Bitmap ss32Out { get { object obj = ResourceManager.GetObject("ss32Out", resourceCulture); return ((System.Drawing.Bitmap)(obj)); @@ -188,9 +191,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap ss32Outline { + public static System.Drawing.Bitmap ss32Outline { get { object obj = ResourceManager.GetObject("ss32Outline", resourceCulture); return ((System.Drawing.Bitmap)(obj)); @@ -198,9 +201,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// - internal static System.Drawing.Bitmap ssw128 { + public static System.Drawing.Bitmap ssw128 { get { object obj = ResourceManager.GetObject("ssw128", resourceCulture); return ((System.Drawing.Bitmap)(obj)); @@ -208,9 +211,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] sysproxy_exe { + public static byte[] sysproxy_exe { get { object obj = ResourceManager.GetObject("sysproxy_exe", resourceCulture); return ((byte[])(obj)); @@ -218,9 +221,9 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] sysproxy64_exe { + public static byte[] sysproxy64_exe { get { object obj = ResourceManager.GetObject("sysproxy64_exe", resourceCulture); return ((byte[])(obj)); @@ -228,11 +231,11 @@ namespace Shadowsocks.Properties { } /// - /// 查找类似 ! Put user rules line by line in this file. + /// Looks up a localized string similar to ! Put user rules line by line in this file. ///! See https://adblockplus.org/en/filter-cheatsheet - /// 的本地化字符串。 + ///. /// - internal static string user_rule { + public static string user_rule { get { return ResourceManager.GetString("user_rule", resourceCulture); } diff --git a/shadowsocks-csharp/Proxy/HttpProxy.cs b/shadowsocks-csharp/Proxy/HttpProxy.cs index dd13b779..b7580f04 100644 --- a/shadowsocks-csharp/Proxy/HttpProxy.cs +++ b/shadowsocks-csharp/Proxy/HttpProxy.cs @@ -39,8 +39,6 @@ namespace Shadowsocks.Proxy public object AsyncState { get; set; } - public int BytesToRead; - public Exception ex { get; set; } } @@ -199,7 +197,7 @@ namespace Shadowsocks.Proxy } else { - if (line.IsNullOrEmpty()) + if (string.IsNullOrEmpty(line)) { return true; } diff --git a/shadowsocks-csharp/StringEx.cs b/shadowsocks-csharp/StringEx.cs deleted file mode 100644 index 96880e19..00000000 --- a/shadowsocks-csharp/StringEx.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; - -#if EXPOSE_EVERYTHING || EXPOSE_STRINGEX -public -#endif -static partial class StringEx -{ -#pragma warning disable 1591 - - public static StringComparison GlobalDefaultComparison { get; set; } = StringComparison.Ordinal; - - [ThreadStatic] - private static StringComparison? _DefaultComparison; - public static StringComparison DefaultComparison - { - get { return _DefaultComparison ?? GlobalDefaultComparison; } - set { _DefaultComparison = value; } - } - - #region basic String methods - - public static bool IsNullOrEmpty(this string value) - => string.IsNullOrEmpty(value); - - public static bool IsNullOrWhiteSpace(this string value) - => string.IsNullOrWhiteSpace(value); - - public static bool IsWhiteSpace(this string value) - { - foreach (var c in value) - { - if (char.IsWhiteSpace(c)) continue; - - return false; - } - return true; - } - -#if !PCL - public static string IsInterned(this string value) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return string.IsInterned(value); - } - - public static string Intern(this string value) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return string.Intern(value); - } -#endif - -#if UNSAFE - public static unsafe string ToLowerForASCII(this string value) - { - if (value.IsNullOrWhiteSpace()) - return value; - - value = string.Copy(value); - fixed (char* low = value) - { - var end = low + value.Length; - for (var p = low; p < end; p++) - { - var c = *p; - if (c < 'A' || c > 'Z') - continue; - *p = (char)(c + 0x20); - } - } - return value; - } - - public static unsafe string ToUpperForASCII(this string value) - { - if (value.IsNullOrWhiteSpace()) - return value; - - value = string.Copy(value); - fixed (char* low = value) - { - var end = low + value.Length; - for (var p = low; p < end; p++) - { - var c = *p; - if (c < 'a' || c > 'z') - continue; - *p = (char)(c - 0x20); - } - } - return value; - } -#else - public static string ToLowerForASCII(this string value) - { - if (value.IsNullOrWhiteSpace()) - return value; - - var sb = new StringBuilder(value.Length); - foreach (var c in value) - { - if (c < 'A' || c > 'Z') - sb.Append(c); - else - sb.Append((char)(c + 0x20)); - } - return sb.ToString(); - } - - public static string ToUpperForASCII(this string value) - { - if (value.IsNullOrWhiteSpace()) - return value; - - var sb = new StringBuilder(value.Length); - foreach (var c in value) - { - if (c < 'a' || c > 'z') - sb.Append(c); - else - sb.Append((char)(c - 0x20)); - } - return sb.ToString(); - } -#endif - - #endregion - - #region comparing - - #region Is - - public static bool Is(this string a, string b) - => string.Equals(a, b, DefaultComparison); - public static bool Is(this string a, string b, StringComparison comparisonType) - => string.Equals(a, b, comparisonType); - - #endregion - - #region BeginWith - - public static bool BeginWith(this string s, char c) - { - if (s.IsNullOrEmpty()) return false; - return s[0] == c; - } - public static bool BeginWithAny(this string s, IEnumerable chars) - { - if (s.IsNullOrEmpty()) return false; - return chars.Contains(s[0]); - } - public static bool BeginWithAny(this string s, params char[] chars) - => s.BeginWithAny(chars.AsEnumerable()); - - public static bool BeginWith(this string a, string b) - { - if (a == null || b == null) return false; - - return a.StartsWith(b, DefaultComparison); - } - public static bool BeginWith(this string a, string b, StringComparison comparisonType) - { - if (a == null || b == null) return false; - - return a.StartsWith(b, comparisonType); - } -#if !PCL - public static bool BeginWith(this string a, string b, bool ignoreCase, CultureInfo culture) - { - if (a == null || b == null) return false; - - return a.StartsWith(b, ignoreCase, culture); - } -#endif - - #endregion - - #region FinishWith - - public static bool FinishWith(this string s, char c) - { - if (s.IsNullOrEmpty()) return false; - return s.Last() == c; - } - public static bool FinishWithAny(this string s, IEnumerable chars) - { - if (s.IsNullOrEmpty()) return false; - return chars.Contains(s.Last()); - } - public static bool FinishWithAny(this string s, params char[] chars) - => s.FinishWithAny(chars.AsEnumerable()); - - public static bool FinishWith(this string a, string b) - { - if (a == null || b == null) return false; - - return a.EndsWith(b, DefaultComparison); - } - public static bool FinishWith(this string a, string b, StringComparison comparisonType) - { - if (a == null || b == null) return false; - - return a.EndsWith(b, comparisonType); - } -#if !PCL - public static bool FinishWith(this string a, string b, bool ignoreCase, CultureInfo culture) - { - if (a == null || b == null) return false; - - return a.EndsWith(b, ignoreCase, culture); - } -#endif - - #endregion - - #endregion - - #region ToLines - - public static IEnumerable ToLines(this TextReader reader) - { - string line; - while ((line = reader.ReadLine()) != null) - yield return line; - } - public static IEnumerable NonEmptyLines(this TextReader reader) - { - string line; - while ((line = reader.ReadLine()) != null) - { - if (line == "") continue; - yield return line; - } - } - public static IEnumerable NonWhiteSpaceLines(this TextReader reader) - { - string line; - while ((line = reader.ReadLine()) != null) - { - if (line.IsWhiteSpace()) continue; - yield return line; - } - } - - #endregion - - #region others - - private static readonly char[][] Quotes = new[] - { - "\"\"", - "''", - "“”", - "‘’", - "『』", - "「」", - "〖〗", - "【】", - }.Select(s => s.ToCharArray()).ToArray(); - public static string Enquote(this string value) - { - if (value == null) - return "(null)"; - - foreach (var pair in Quotes) - { - if (value.IndexOfAny(pair) < 0) - return pair[0] + value + pair[1]; - } - - return '"' + value.Replace("\\", @"\\").Replace("\"", @"\""") + '"'; - } - - public static string Replace(this string value, string find, string rep, StringComparison comparsionType) - { - if (find.IsNullOrEmpty()) - throw new ArgumentException(null, nameof(find)); - if (rep == null) - rep = ""; - if (value.IsNullOrEmpty()) - return value; - - var sb = new StringBuilder(value.Length); - - var last = 0; - var len = find.Length; - var idx = value.IndexOf(find, DefaultComparison); - while (idx != -1) - { - sb.Append(value.Substring(last, idx - last)); - sb.Append(rep); - idx += len; - - last = idx; - idx = value.IndexOf(find, idx, comparsionType); - } - sb.Append(value.Substring(last)); - - return sb.ToString(); - } - public static string ReplaceEx(this string value, string find, string rep) - => value.Replace(find, rep, DefaultComparison); - - #endregion -} diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index b0e04bdd..5c7560ba 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -8,6 +8,10 @@ using System.Windows.Forms; using Microsoft.Win32; using Shadowsocks.Controller; using Shadowsocks.Model; +using System.Drawing; +using ZXing; +using ZXing.QrCode; +using ZXing.Common; namespace Shadowsocks.Util { @@ -183,7 +187,7 @@ namespace Shadowsocks.Util // we are building x86 binary for both x86 and x64, which will // cause problem when opening registry key // detect operating system instead of CPU - if (name.IsNullOrEmpty()) throw new ArgumentException(nameof(name)); + if (string.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name)); try { RegistryKey userKey = RegistryKey.OpenBaseKey(hive, @@ -208,10 +212,47 @@ namespace Shadowsocks.Util return Environment.OSVersion.Version.Major > 5; } - [DllImport("kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool SetProcessWorkingSetSize(IntPtr process, - UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize); + public static string ScanQRCodeFromScreen() + { + foreach (Screen screen in Screen.AllScreens) + { + using (Bitmap fullImage = new Bitmap(screen.Bounds.Width, + screen.Bounds.Height)) + { + using (Graphics g = Graphics.FromImage(fullImage)) + { + g.CopyFromScreen(screen.Bounds.X, + screen.Bounds.Y, + 0, 0, + fullImage.Size, + CopyPixelOperation.SourceCopy); + } + int maxTry = 10; + for (int i = 0; i < maxTry; i++) + { + int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); + int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); + Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); + Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); + + double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width; + using (Graphics g = Graphics.FromImage(target)) + { + g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), + cropRect, + GraphicsUnit.Pixel); + } + var source = new BitmapLuminanceSource(target); + var bitmap = new BinaryBitmap(new HybridBinarizer(source)); + QRCodeReader reader = new QRCodeReader(); + var result = reader.decode(bitmap); + if (result != null) + return result.Text; + } + } + } + return null; + } public static void OpenInBrowser(string url) { diff --git a/shadowsocks-csharp/View/CalculationControl.Designer.cs b/shadowsocks-csharp/View/CalculationControl.Designer.cs deleted file mode 100644 index 9e10c675..00000000 --- a/shadowsocks-csharp/View/CalculationControl.Designer.cs +++ /dev/null @@ -1,114 +0,0 @@ -namespace Shadowsocks.View -{ - partial class CalculationControl - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.factorNum = new System.Windows.Forms.NumericUpDown(); - this.multiply = new System.Windows.Forms.Label(); - this.plus = new System.Windows.Forms.Label(); - this.valueLabel = new System.Windows.Forms.Label(); - ((System.ComponentModel.ISupportInitialize)(this.factorNum)).BeginInit(); - this.SuspendLayout(); - // - // factorNum - // - this.factorNum.DecimalPlaces = 2; - this.factorNum.Dock = System.Windows.Forms.DockStyle.Right; - this.factorNum.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.factorNum.Increment = new decimal(new int[] { - 1, - 0, - 0, - 131072}); - this.factorNum.Location = new System.Drawing.Point(210, 0); - this.factorNum.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2); - this.factorNum.Minimum = new decimal(new int[] { - 1000, - 0, - 0, - -2147418112}); - this.factorNum.Name = "factorNum"; - this.factorNum.Size = new System.Drawing.Size(76, 30); - this.factorNum.TabIndex = 6; - // - // multiply - // - this.multiply.AutoSize = true; - this.multiply.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.multiply.Location = new System.Drawing.Point(180, 2); - this.multiply.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.multiply.Name = "multiply"; - this.multiply.Size = new System.Drawing.Size(22, 23); - this.multiply.TabIndex = 2; - this.multiply.Text = "×"; - // - // plus - // - this.plus.AutoSize = true; - this.plus.Font = new System.Drawing.Font("Segoe UI", 10F); - this.plus.Location = new System.Drawing.Point(4, 2); - this.plus.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); - this.plus.Name = "plus"; - this.plus.Size = new System.Drawing.Size(22, 23); - this.plus.TabIndex = 3; - this.plus.Text = "+"; - // - // valueLabel - // - this.valueLabel.AutoSize = true; - this.valueLabel.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.valueLabel.Location = new System.Drawing.Point(35, 4); - this.valueLabel.Name = "valueLabel"; - this.valueLabel.Size = new System.Drawing.Size(101, 20); - this.valueLabel.TabIndex = 7; - this.valueLabel.Text = "PackageLoss"; - // - // CalculationControl - // - this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.valueLabel); - this.Controls.Add(this.factorNum); - this.Controls.Add(this.multiply); - this.Controls.Add(this.plus); - this.Margin = new System.Windows.Forms.Padding(0); - this.Name = "CalculationControl"; - this.Size = new System.Drawing.Size(286, 26); - ((System.ComponentModel.ISupportInitialize)(this.factorNum)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - private System.Windows.Forms.NumericUpDown factorNum; - private System.Windows.Forms.Label multiply; - private System.Windows.Forms.Label plus; - private System.Windows.Forms.Label valueLabel; - } -} diff --git a/shadowsocks-csharp/View/CalculationControl.cs b/shadowsocks-csharp/View/CalculationControl.cs deleted file mode 100644 index c7f8fdf2..00000000 --- a/shadowsocks-csharp/View/CalculationControl.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Data; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Windows.Forms; - -namespace Shadowsocks.View -{ - public partial class CalculationControl : UserControl - { - public CalculationControl(string text, float value) - { - InitializeComponent(); - valueLabel.Text = text; - factorNum.Value = (decimal) value; - } - - public string Value => valueLabel.Text; - public float Factor => (float) factorNum.Value; - } -} diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index f8b051c1..85d17d2d 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -229,7 +229,7 @@ namespace Shadowsocks.View { password = null; string outPassword; - if ((outPassword = PasswordTextBox.Text).IsNullOrWhiteSpace()) + if (string.IsNullOrWhiteSpace(outPassword = PasswordTextBox.Text)) { if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) { @@ -352,7 +352,7 @@ namespace Shadowsocks.View private void LoadCurrentConfiguration() { - _modifiedConfiguration = controller.GetConfigurationCopy(); + _modifiedConfiguration = controller.GetCurrentConfiguration(); LoadServerNameListToUI(_modifiedConfiguration); _lastSelectedIndex = _modifiedConfiguration.index; diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.cs b/shadowsocks-csharp/View/HotkeySettingsForm.cs deleted file mode 100644 index b55e9ef1..00000000 --- a/shadowsocks-csharp/View/HotkeySettingsForm.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Shadowsocks.Controller; -using Shadowsocks.Model; -using Shadowsocks.Properties; -using System; -using System.Drawing; -using System.Text; -using System.Windows.Forms; -using static Shadowsocks.Controller.HotkeyReg; - -namespace Shadowsocks.View -{ - public partial class HotkeySettingsForm : Form - { - private readonly ShadowsocksController _controller; - - // this is a copy of hotkey configuration that we are working on - private HotkeyConfig _modifiedHotkeyConfig; - - public HotkeySettingsForm(ShadowsocksController controller) - { - InitializeComponent(); - UpdateTexts(); - Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); - - _controller = controller; - _controller.ConfigChanged += controller_ConfigChanged; - - LoadCurrentConfiguration(); - } - - private void UpdateTexts() - { - I18N.TranslateForm(this); - } - - private void controller_ConfigChanged(object sender, EventArgs e) - { - LoadCurrentConfiguration(); - } - - private void LoadCurrentConfiguration() - { - _modifiedHotkeyConfig = _controller.GetConfigurationCopy().hotkey; - SetConfigToUI(_modifiedHotkeyConfig); - } - - private void SetConfigToUI(HotkeyConfig config) - { - SwitchSystemProxyTextBox.Text = config.SwitchSystemProxy; - SwitchProxyModeTextBox.Text = config.SwitchSystemProxyMode; - SwitchAllowLanTextBox.Text = config.SwitchAllowLan; - ShowLogsTextBox.Text = config.ShowLogs; - ServerMoveUpTextBox.Text = config.ServerMoveUp; - ServerMoveDownTextBox.Text = config.ServerMoveDown; - RegHotkeysAtStartupCheckBox.Checked = config.RegHotkeysAtStartup; - } - - private void SaveConfig() - { - _controller.SaveHotkeyConfig(_modifiedHotkeyConfig); - } - - private HotkeyConfig GetConfigFromUI() - { - return new HotkeyConfig - { - SwitchSystemProxy = SwitchSystemProxyTextBox.Text, - SwitchSystemProxyMode = SwitchProxyModeTextBox.Text, - SwitchAllowLan = SwitchAllowLanTextBox.Text, - ShowLogs = ShowLogsTextBox.Text, - ServerMoveUp = ServerMoveUpTextBox.Text, - ServerMoveDown = ServerMoveDownTextBox.Text, - RegHotkeysAtStartup = RegHotkeysAtStartupCheckBox.Checked - }; - } - - /// - /// Capture hotkey - Press key - /// - private void HotkeyDown(object sender, KeyEventArgs e) - { - StringBuilder sb = new StringBuilder(); - //Combination key only - if (e.Modifiers != 0) - { - // XXX: Hotkey parsing depends on the sequence, more specifically, ModifierKeysConverter. - // Windows key is reserved by operating system, we deny this key. - if (e.Control) - { - sb.Append("Ctrl+"); - } - if (e.Alt) - { - sb.Append("Alt+"); - } - if (e.Shift) - { - sb.Append("Shift+"); - } - - Keys keyvalue = (Keys)e.KeyValue; - if ((keyvalue >= Keys.PageUp && keyvalue <= Keys.Down) || - (keyvalue >= Keys.A && keyvalue <= Keys.Z) || - (keyvalue >= Keys.F1 && keyvalue <= Keys.F12)) - { - sb.Append(e.KeyCode); - } - else if (keyvalue >= Keys.D0 && keyvalue <= Keys.D9) - { - sb.Append('D').Append((char)e.KeyValue); - } - else if (keyvalue >= Keys.NumPad0 && keyvalue <= Keys.NumPad9) - { - sb.Append("NumPad").Append((char)(e.KeyValue - 48)); - } - } - ((TextBox)sender).Text = sb.ToString(); - } - - /// - /// Capture hotkey - Release key - /// - private void HotkeyUp(object sender, KeyEventArgs e) - { - var tb = (TextBox)sender; - var content = tb.Text.TrimEnd(); - if (content.Length >= 1 && content[content.Length - 1] == '+') - { - tb.Text = ""; - } - } - - private void CancelButton_Click(object sender, EventArgs e) - { - this.Close(); - } - - private void OKButton_Click(object sender, EventArgs e) - { - _modifiedHotkeyConfig = GetConfigFromUI(); - // try to register, notify to change settings if failed - if (!RegisterAllHotkeys(_modifiedHotkeyConfig)) - { - MessageBox.Show(I18N.GetString("Register hotkey failed")); - } - - // All check passed, saving - SaveConfig(); - this.Close(); - } - - private void RegisterAllButton_Click(object sender, EventArgs e) - { - _modifiedHotkeyConfig = GetConfigFromUI(); - RegisterAllHotkeys(_modifiedHotkeyConfig); - } - - private bool RegisterAllHotkeys(HotkeyConfig hotkeyConfig) - { - return - RegHotkeyFromString(hotkeyConfig.SwitchSystemProxy, "SwitchSystemProxyCallback", result => HandleRegResult(hotkeyConfig.SwitchSystemProxy, SwitchSystemProxyLabel, result)) - && RegHotkeyFromString(hotkeyConfig.SwitchSystemProxyMode, "SwitchSystemProxyModeCallback", result => HandleRegResult(hotkeyConfig.SwitchSystemProxyMode, SwitchProxyModeLabel, result)) - && RegHotkeyFromString(hotkeyConfig.SwitchAllowLan, "SwitchAllowLanCallback", result => HandleRegResult(hotkeyConfig.SwitchAllowLan, SwitchAllowLanLabel, result)) - && RegHotkeyFromString(hotkeyConfig.ShowLogs, "ShowLogsCallback", result => HandleRegResult(hotkeyConfig.ShowLogs, ShowLogsLabel, result)) - && RegHotkeyFromString(hotkeyConfig.ServerMoveUp, "ServerMoveUpCallback", result => HandleRegResult(hotkeyConfig.ServerMoveUp, ServerMoveUpLabel, result)) - && RegHotkeyFromString(hotkeyConfig.ServerMoveDown, "ServerMoveDownCallback", result => HandleRegResult(hotkeyConfig.ServerMoveDown, ServerMoveDownLabel, result)); - } - - private void HandleRegResult(string hotkeyStr, Label label, RegResult result) - { - switch (result) - { - case RegResult.ParseError: - MessageBox.Show(I18N.GetString("Cannot parse hotkey: {0}", hotkeyStr)); - break; - case RegResult.UnregSuccess: - label.ResetBackColor(); - break; - case RegResult.RegSuccess: - label.BackColor = Color.Green; - break; - case RegResult.RegFailure: - label.BackColor = Color.Red; - break; - default: - break; - } - } - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs b/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs deleted file mode 100644 index 47f89946..00000000 --- a/shadowsocks-csharp/View/HotkeySettingsForm.designer.cs +++ /dev/null @@ -1,346 +0,0 @@ -namespace Shadowsocks.View -{ - partial class HotkeySettingsForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; - this.btnOK = new System.Windows.Forms.Button(); - this.btnCancel = new System.Windows.Forms.Button(); - this.btnRegisterAll = new System.Windows.Forms.Button(); - this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.RegHotkeysAtStartupLabel = new System.Windows.Forms.Label(); - this.SwitchSystemProxyLabel = new System.Windows.Forms.Label(); - this.SwitchProxyModeLabel = new System.Windows.Forms.Label(); - this.SwitchAllowLanLabel = new System.Windows.Forms.Label(); - this.ShowLogsLabel = new System.Windows.Forms.Label(); - this.ServerMoveUpLabel = new System.Windows.Forms.Label(); - this.ServerMoveDownLabel = new System.Windows.Forms.Label(); - this.SwitchSystemProxyTextBox = new System.Windows.Forms.TextBox(); - this.SwitchProxyModeTextBox = new System.Windows.Forms.TextBox(); - this.SwitchAllowLanTextBox = new System.Windows.Forms.TextBox(); - this.ShowLogsTextBox = new System.Windows.Forms.TextBox(); - this.ServerMoveUpTextBox = new System.Windows.Forms.TextBox(); - this.ServerMoveDownTextBox = new System.Windows.Forms.TextBox(); - this.RegHotkeysAtStartupCheckBox = new System.Windows.Forms.CheckBox(); - flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); - flowLayoutPanel1.SuspendLayout(); - this.tableLayoutPanel1.SuspendLayout(); - this.SuspendLayout(); - // - // flowLayoutPanel1 - // - this.tableLayoutPanel1.SetColumnSpan(flowLayoutPanel1, 2); - flowLayoutPanel1.Controls.Add(this.btnOK); - flowLayoutPanel1.Controls.Add(this.btnCancel); - flowLayoutPanel1.Controls.Add(this.btnRegisterAll); - flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.BottomUp; - flowLayoutPanel1.Location = new System.Drawing.Point(0, 248); - flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); - flowLayoutPanel1.Name = "flowLayoutPanel1"; - flowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 0, 20, 0); - flowLayoutPanel1.RightToLeft = System.Windows.Forms.RightToLeft.Yes; - flowLayoutPanel1.Size = new System.Drawing.Size(594, 52); - flowLayoutPanel1.TabIndex = 6; - // - // btnOK - // - this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; - this.btnOK.Location = new System.Drawing.Point(416, 9); - this.btnOK.Margin = new System.Windows.Forms.Padding(4); - this.btnOK.Name = "btnOK"; - this.btnOK.Size = new System.Drawing.Size(154, 39); - this.btnOK.TabIndex = 0; - this.btnOK.Text = "OK"; - this.btnOK.UseVisualStyleBackColor = true; - this.btnOK.Click += new System.EventHandler(this.OKButton_Click); - // - // btnCancel - // - this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnCancel.Location = new System.Drawing.Point(254, 9); - this.btnCancel.Margin = new System.Windows.Forms.Padding(4); - this.btnCancel.Name = "btnCancel"; - this.btnCancel.Size = new System.Drawing.Size(154, 39); - this.btnCancel.TabIndex = 1; - this.btnCancel.Text = "Cancel"; - this.btnCancel.UseVisualStyleBackColor = true; - this.btnCancel.Click += new System.EventHandler(this.CancelButton_Click); - // - // btnRegisterAll - // - this.btnRegisterAll.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnRegisterAll.Location = new System.Drawing.Point(92, 9); - this.btnRegisterAll.Margin = new System.Windows.Forms.Padding(4); - this.btnRegisterAll.Name = "btnRegisterAll"; - this.btnRegisterAll.Size = new System.Drawing.Size(154, 39); - this.btnRegisterAll.TabIndex = 2; - this.btnRegisterAll.Text = "Reg All"; - this.btnRegisterAll.UseVisualStyleBackColor = true; - this.btnRegisterAll.Click += new System.EventHandler(this.RegisterAllButton_Click); - // - // tableLayoutPanel1 - // - this.tableLayoutPanel1.ColumnCount = 2; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.Controls.Add(this.RegHotkeysAtStartupLabel, 0, 6); - this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyLabel, 0, 0); - this.tableLayoutPanel1.Controls.Add(this.SwitchProxyModeLabel, 0, 1); - this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanLabel, 0, 2); - this.tableLayoutPanel1.Controls.Add(this.ShowLogsLabel, 0, 3); - this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpLabel, 0, 4); - this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownLabel, 0, 5); - this.tableLayoutPanel1.Controls.Add(flowLayoutPanel1, 0, 7); - this.tableLayoutPanel1.Controls.Add(this.SwitchSystemProxyTextBox, 1, 0); - this.tableLayoutPanel1.Controls.Add(this.SwitchProxyModeTextBox, 1, 1); - this.tableLayoutPanel1.Controls.Add(this.SwitchAllowLanTextBox, 1, 2); - this.tableLayoutPanel1.Controls.Add(this.ShowLogsTextBox, 1, 3); - this.tableLayoutPanel1.Controls.Add(this.ServerMoveUpTextBox, 1, 4); - this.tableLayoutPanel1.Controls.Add(this.ServerMoveDownTextBox, 1, 5); - this.tableLayoutPanel1.Controls.Add(this.RegHotkeysAtStartupCheckBox, 1, 6); - this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); - this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4); - this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 8; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16726F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.7784F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.38949F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 14.16309F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 50F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F)); - this.tableLayoutPanel1.Size = new System.Drawing.Size(582, 303); - this.tableLayoutPanel1.TabIndex = 0; - // - // RegHotkeysAtStartupLabel - // - this.RegHotkeysAtStartupLabel.AutoSize = true; - this.RegHotkeysAtStartupLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.RegHotkeysAtStartupLabel.Location = new System.Drawing.Point(40, 213); - this.RegHotkeysAtStartupLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.RegHotkeysAtStartupLabel.Name = "RegHotkeysAtStartupLabel"; - this.RegHotkeysAtStartupLabel.Size = new System.Drawing.Size(199, 35); - this.RegHotkeysAtStartupLabel.TabIndex = 16; - this.RegHotkeysAtStartupLabel.Text = "Reg Hotkeys At Startup"; - this.RegHotkeysAtStartupLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // SwitchSystemProxyLabel - // - this.SwitchSystemProxyLabel.AutoSize = true; - this.SwitchSystemProxyLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.SwitchSystemProxyLabel.Location = new System.Drawing.Point(63, 0); - this.SwitchSystemProxyLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.SwitchSystemProxyLabel.Name = "SwitchSystemProxyLabel"; - this.SwitchSystemProxyLabel.Size = new System.Drawing.Size(176, 35); - this.SwitchSystemProxyLabel.TabIndex = 0; - this.SwitchSystemProxyLabel.Text = "Switch system proxy"; - this.SwitchSystemProxyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // SwitchProxyModeLabel - // - this.SwitchProxyModeLabel.AutoSize = true; - this.SwitchProxyModeLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.SwitchProxyModeLabel.Location = new System.Drawing.Point(10, 35); - this.SwitchProxyModeLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.SwitchProxyModeLabel.Name = "SwitchProxyModeLabel"; - this.SwitchProxyModeLabel.Size = new System.Drawing.Size(229, 35); - this.SwitchProxyModeLabel.TabIndex = 1; - this.SwitchProxyModeLabel.Text = "Switch system proxy mode"; - this.SwitchProxyModeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // SwitchAllowLanLabel - // - this.SwitchAllowLanLabel.AutoSize = true; - this.SwitchAllowLanLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.SwitchAllowLanLabel.Location = new System.Drawing.Point(39, 70); - this.SwitchAllowLanLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.SwitchAllowLanLabel.Name = "SwitchAllowLanLabel"; - this.SwitchAllowLanLabel.Size = new System.Drawing.Size(200, 35); - this.SwitchAllowLanLabel.TabIndex = 3; - this.SwitchAllowLanLabel.Text = "Allow Clients from LAN"; - this.SwitchAllowLanLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ShowLogsLabel - // - this.ShowLogsLabel.AutoSize = true; - this.ShowLogsLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.ShowLogsLabel.Location = new System.Drawing.Point(129, 105); - this.ShowLogsLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.ShowLogsLabel.Name = "ShowLogsLabel"; - this.ShowLogsLabel.Size = new System.Drawing.Size(110, 35); - this.ShowLogsLabel.TabIndex = 4; - this.ShowLogsLabel.Text = "Show Logs..."; - this.ShowLogsLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ServerMoveUpLabel - // - this.ServerMoveUpLabel.AutoSize = true; - this.ServerMoveUpLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.ServerMoveUpLabel.Location = new System.Drawing.Point(25, 140); - this.ServerMoveUpLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.ServerMoveUpLabel.Name = "ServerMoveUpLabel"; - this.ServerMoveUpLabel.Size = new System.Drawing.Size(214, 37); - this.ServerMoveUpLabel.TabIndex = 4; - this.ServerMoveUpLabel.Text = "Switch to previous server"; - this.ServerMoveUpLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ServerMoveDownLabel - // - this.ServerMoveDownLabel.AutoSize = true; - this.ServerMoveDownLabel.Dock = System.Windows.Forms.DockStyle.Right; - this.ServerMoveDownLabel.Location = new System.Drawing.Point(60, 177); - this.ServerMoveDownLabel.Margin = new System.Windows.Forms.Padding(10, 0, 10, 0); - this.ServerMoveDownLabel.Name = "ServerMoveDownLabel"; - this.ServerMoveDownLabel.Size = new System.Drawing.Size(179, 36); - this.ServerMoveDownLabel.TabIndex = 4; - this.ServerMoveDownLabel.Text = "Switch to next server"; - this.ServerMoveDownLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // SwitchSystemProxyTextBox - // - this.SwitchSystemProxyTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.SwitchSystemProxyTextBox.Location = new System.Drawing.Point(252, 3); - this.SwitchSystemProxyTextBox.Name = "SwitchSystemProxyTextBox"; - this.SwitchSystemProxyTextBox.ReadOnly = true; - this.SwitchSystemProxyTextBox.Size = new System.Drawing.Size(339, 29); - this.SwitchSystemProxyTextBox.TabIndex = 7; - this.SwitchSystemProxyTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); - this.SwitchSystemProxyTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); - // - // SwitchProxyModeTextBox - // - this.SwitchProxyModeTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.SwitchProxyModeTextBox.Location = new System.Drawing.Point(252, 38); - this.SwitchProxyModeTextBox.Name = "SwitchProxyModeTextBox"; - this.SwitchProxyModeTextBox.ReadOnly = true; - this.SwitchProxyModeTextBox.Size = new System.Drawing.Size(339, 29); - this.SwitchProxyModeTextBox.TabIndex = 8; - this.SwitchProxyModeTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); - this.SwitchProxyModeTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); - // - // SwitchAllowLanTextBox - // - this.SwitchAllowLanTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.SwitchAllowLanTextBox.Location = new System.Drawing.Point(252, 73); - this.SwitchAllowLanTextBox.Name = "SwitchAllowLanTextBox"; - this.SwitchAllowLanTextBox.ReadOnly = true; - this.SwitchAllowLanTextBox.Size = new System.Drawing.Size(339, 29); - this.SwitchAllowLanTextBox.TabIndex = 10; - this.SwitchAllowLanTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); - this.SwitchAllowLanTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); - // - // ShowLogsTextBox - // - this.ShowLogsTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.ShowLogsTextBox.Location = new System.Drawing.Point(252, 108); - this.ShowLogsTextBox.Name = "ShowLogsTextBox"; - this.ShowLogsTextBox.ReadOnly = true; - this.ShowLogsTextBox.Size = new System.Drawing.Size(339, 29); - this.ShowLogsTextBox.TabIndex = 11; - this.ShowLogsTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); - this.ShowLogsTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); - // - // ServerMoveUpTextBox - // - this.ServerMoveUpTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.ServerMoveUpTextBox.Location = new System.Drawing.Point(252, 143); - this.ServerMoveUpTextBox.Name = "ServerMoveUpTextBox"; - this.ServerMoveUpTextBox.ReadOnly = true; - this.ServerMoveUpTextBox.Size = new System.Drawing.Size(339, 29); - this.ServerMoveUpTextBox.TabIndex = 12; - this.ServerMoveUpTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); - this.ServerMoveUpTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); - // - // ServerMoveDownTextBox - // - this.ServerMoveDownTextBox.Dock = System.Windows.Forms.DockStyle.Fill; - this.ServerMoveDownTextBox.Location = new System.Drawing.Point(252, 180); - this.ServerMoveDownTextBox.Name = "ServerMoveDownTextBox"; - this.ServerMoveDownTextBox.ReadOnly = true; - this.ServerMoveDownTextBox.Size = new System.Drawing.Size(339, 29); - this.ServerMoveDownTextBox.TabIndex = 13; - this.ServerMoveDownTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.HotkeyDown); - this.ServerMoveDownTextBox.KeyUp += new System.Windows.Forms.KeyEventHandler(this.HotkeyUp); - // - // RegHotkeysAtStartupCheckBox - // - this.RegHotkeysAtStartupCheckBox.AutoSize = true; - this.RegHotkeysAtStartupCheckBox.Dock = System.Windows.Forms.DockStyle.Left; - this.RegHotkeysAtStartupCheckBox.Location = new System.Drawing.Point(252, 216); - this.RegHotkeysAtStartupCheckBox.Name = "RegHotkeysAtStartupCheckBox"; - this.RegHotkeysAtStartupCheckBox.Size = new System.Drawing.Size(18, 29); - this.RegHotkeysAtStartupCheckBox.TabIndex = 17; - this.RegHotkeysAtStartupCheckBox.UseVisualStyleBackColor = true; - // - // HotkeySettingsForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(582, 303); - this.Controls.Add(this.tableLayoutPanel1); - this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134))); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Margin = new System.Windows.Forms.Padding(5, 6, 5, 6); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "HotkeySettingsForm"; - this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Edit Hotkeys..."; - flowLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); - this.ResumeLayout(false); - - } - - #endregion - private System.Windows.Forms.Label SwitchSystemProxyLabel; - private System.Windows.Forms.Label SwitchProxyModeLabel; - private System.Windows.Forms.Label SwitchAllowLanLabel; - private System.Windows.Forms.Label ShowLogsLabel; - private System.Windows.Forms.Label ServerMoveUpLabel; - private System.Windows.Forms.Label ServerMoveDownLabel; - private System.Windows.Forms.Button btnOK; - private System.Windows.Forms.Button btnCancel; - private System.Windows.Forms.TextBox ShowLogsTextBox; - private System.Windows.Forms.TextBox SwitchAllowLanTextBox; - private System.Windows.Forms.TextBox SwitchProxyModeTextBox; - private System.Windows.Forms.TextBox SwitchSystemProxyTextBox; - private System.Windows.Forms.TextBox ServerMoveUpTextBox; - private System.Windows.Forms.TextBox ServerMoveDownTextBox; - private System.Windows.Forms.Button btnRegisterAll; - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - private System.Windows.Forms.Label RegHotkeysAtStartupLabel; - private System.Windows.Forms.CheckBox RegHotkeysAtStartupCheckBox; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/View/HotkeySettingsForm.resx b/shadowsocks-csharp/View/HotkeySettingsForm.resx deleted file mode 100644 index 5813f883..00000000 --- a/shadowsocks-csharp/View/HotkeySettingsForm.resx +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - False - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - - True - - \ No newline at end of file diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index 7fac8a28..937553b7 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -62,7 +62,7 @@ namespace Shadowsocks.View LogMessageTextBox.AppendText("Cannot get the log file name from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); } - LogViewerConfig config = controller.GetConfigurationCopy().logViewer; + LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; topMostTrigger = config.topMost; wrapTextTrigger = config.wrapText; @@ -244,7 +244,7 @@ namespace Shadowsocks.View timer.Tick += Timer_Tick; timer.Start(); - LogViewerConfig config = controller.GetConfigurationCopy().logViewer; + LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; Height = config.Height; Width = config.Width; @@ -270,7 +270,7 @@ namespace Shadowsocks.View { timer.Stop(); controller.TrafficChanged -= controller_TrafficChanged; - LogViewerConfig config = controller.GetConfigurationCopy().logViewer; + LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; config.topMost = topMostTrigger; config.wrapText = wrapTextTrigger; diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index bea69330..7565db36 100644 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -1,9 +1,11 @@ using NLog; using Shadowsocks.Controller; +using Shadowsocks.Localization; using Shadowsocks.Model; using Shadowsocks.Properties; using Shadowsocks.Util; using Shadowsocks.Util.SystemProxy; +using Shadowsocks.Views; using System; using System.Diagnostics; using System.Drawing; @@ -11,7 +13,8 @@ using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; -using System.Windows.Media.Imaging; +using System.Windows.Forms.Integration; +using System.Windows.Threading; using ZXing; using ZXing.Common; using ZXing.QrCode; @@ -20,19 +23,15 @@ namespace Shadowsocks.View { public class MenuViewController { - private static Logger logger = LogManager.GetCurrentClassLogger(); - // yes this is just a menu view controller - // when config form is closed, it moves away from RAM - // and it should just do anything related to the config form + private readonly Logger logger = LogManager.GetCurrentClassLogger(); private ShadowsocksController controller; - private UpdateChecker updateChecker; + public UpdateChecker updateChecker; private NotifyIcon _notifyIcon; private Icon icon, icon_in, icon_out, icon_both, previousIcon; - private bool _isFirstRun; - private bool _isStartupChecking; + private bool _isStartupCheck; private string _urlToOpen; private ContextMenuStrip contextMenu1; @@ -52,6 +51,7 @@ namespace Shadowsocks.View private ToolStripMenuItem editGFWUserRuleItem; private ToolStripMenuItem editOnlinePACItem; private ToolStripMenuItem secureLocalPacUrlToggleItem; + private ToolStripMenuItem regenerateLocalPacOnUpdateItem; private ToolStripMenuItem autoCheckUpdatesToggleItem; private ToolStripMenuItem checkPreReleaseToggleItem; private ToolStripMenuItem proxyItem; @@ -62,12 +62,12 @@ namespace Shadowsocks.View private ToolStripMenuItem onlineConfigItem; private ConfigForm configForm; - private ProxyForm proxyForm; private LogForm logForm; - private HotkeySettingsForm hotkeySettingsForm; - private OnlineConfigForm onlineConfigForm; - + private System.Windows.Window serverSharingWindow; + private System.Windows.Window hotkeysWindow; + private System.Windows.Window forwardProxyWindow; + private System.Windows.Window onlineConfigWindow; // color definition for icon color transformation private readonly Color colorMaskBlue = Color.FromArgb(255, 25, 125, 191); @@ -103,22 +103,21 @@ namespace Shadowsocks.View _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; controller.TrafficChanged += controller_TrafficChanged; - this.updateChecker = new UpdateChecker(); + updateChecker = new UpdateChecker(); updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; LoadCurrentConfiguration(); - Configuration config = controller.GetConfigurationCopy(); + Configuration config = controller.GetCurrentConfiguration(); - if (config.isDefault) + if (config.firstRun) { - _isFirstRun = true; ShowConfigForm(); } else if (config.autoCheckUpdate) { - _isStartupChecking = true; - updateChecker.CheckUpdate(config, 3000); + _isStartupCheck = true; + Dispatcher.CurrentDispatcher.Invoke(() => updateChecker.CheckForVersionUpdate(3000)); } } @@ -126,7 +125,7 @@ namespace Shadowsocks.View private void UpdateTrayIconAndNotifyText() { - Configuration config = controller.GetConfigurationCopy(); + Configuration config = controller.GetCurrentConfiguration(); bool enabled = config.enabled; bool global = config.global; @@ -242,8 +241,6 @@ namespace Shadowsocks.View icon_both = Icon.FromHandle(ViewUtils.ResizeBitmap(ViewUtils.AddBitmapOverlay(iconBitmap, Resources.ss32In, Resources.ss32Out), size.Width, size.Height).GetHicon()); } - - #endregion #region ToolStripMenuItems and MenuGroups @@ -270,7 +267,6 @@ namespace Shadowsocks.View this.ServersItem = CreateMenuGroup("Servers", new ToolStripItem [] { this.SeperatorItem = new ToolStripSeparator(), this.ConfigItem = CreateToolStripMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), - CreateToolStripMenuItem("Statistics Config...", StatisticsConfigItem_Click), new ToolStripSeparator(), CreateToolStripMenuItem("Share Server Config...", new EventHandler(this.QRCodeItem_Click)), CreateToolStripMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)), @@ -284,6 +280,7 @@ namespace Shadowsocks.View this.updateFromGeositeItem = CreateToolStripMenuItem("Update Local PAC from Geosite", new EventHandler(this.UpdatePACFromGeositeItem_Click)), this.editGFWUserRuleItem = CreateToolStripMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)), this.secureLocalPacUrlToggleItem = CreateToolStripMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)), + this.regenerateLocalPacOnUpdateItem = CreateToolStripMenuItem("Regenerate local PAC on version update", new EventHandler(this.RegenerateLocalPacOnUpdateItem_Click)), CreateToolStripMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), this.editOnlinePACItem = CreateToolStripMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), }), @@ -341,7 +338,7 @@ namespace Shadowsocks.View } } - void controller_Errored(object sender, System.IO.ErrorEventArgs e) + void controller_Errored(object sender, ErrorEventArgs e) { MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message)); } @@ -354,7 +351,7 @@ namespace Shadowsocks.View private void LoadCurrentConfiguration() { - Configuration config = controller.GetConfigurationCopy(); + Configuration config = controller.GetCurrentConfiguration(); UpdateServersMenu(); UpdateSystemProxyItemsEnabledStatus(config); ShareOverLANItem.Checked = config.shareOverLan; @@ -365,6 +362,7 @@ namespace Shadowsocks.View onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; localPACItem.Checked = !onlinePACItem.Checked; secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; + regenerateLocalPacOnUpdateItem.Checked = config.regeneratePacOnUpdate; UpdatePACItemsEnabledStatus(); UpdateUpdateMenu(); } @@ -386,36 +384,6 @@ namespace Shadowsocks.View } } - private void ShowProxyForm() - { - if (proxyForm != null) - { - proxyForm.Activate(); - } - else - { - proxyForm = new ProxyForm(controller); - proxyForm.Show(); - proxyForm.Activate(); - proxyForm.FormClosed += proxyForm_FormClosed; - } - } - - private void ShowHotKeySettingsForm() - { - if (hotkeySettingsForm != null) - { - hotkeySettingsForm.Activate(); - } - else - { - hotkeySettingsForm = new HotkeySettingsForm(controller); - hotkeySettingsForm.Show(); - hotkeySettingsForm.Activate(); - hotkeySettingsForm.FormClosed += hotkeySettingsForm_FormClosed; - } - } - private void ShowLogForm() { if (logForm != null) @@ -431,22 +399,6 @@ namespace Shadowsocks.View } } - private void ShowOnlineConfigForm() - { - - if (onlineConfigForm != null) - { - onlineConfigForm.Activate(); - } - else - { - onlineConfigForm = new OnlineConfigForm(controller); - onlineConfigForm.Show(); - onlineConfigForm.Activate(); - onlineConfigForm.FormClosed += onlineConfigForm_FormClosed; - } - } - void logForm_FormClosed(object sender, FormClosedEventArgs e) { logForm.Dispose(); @@ -457,7 +409,8 @@ namespace Shadowsocks.View { configForm.Dispose(); configForm = null; - if (_isFirstRun) + var config = controller.GetCurrentConfiguration(); + if (config.firstRun) { CheckUpdateForFirstRun(); ShowBalloonTip( @@ -466,28 +419,10 @@ namespace Shadowsocks.View ToolTipIcon.Info, 0 ); - _isFirstRun = false; + config.firstRun = false; } } - void proxyForm_FormClosed(object sender, FormClosedEventArgs e) - { - proxyForm.Dispose(); - proxyForm = null; - } - - void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) - { - hotkeySettingsForm.Dispose(); - hotkeySettingsForm = null; - } - - void onlineConfigForm_FormClosed(object sender, FormClosedEventArgs e) - { - onlineConfigForm.Dispose(); - onlineConfigForm = null; - } - #endregion #region Misc @@ -502,23 +437,10 @@ namespace Shadowsocks.View void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) { - if (updateChecker.NewVersionFound) - { - updateChecker.NewVersionFound = false; /* Reset the flag */ - if (File.Exists(updateChecker.LatestVersionLocalName)) - { - string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; - Process.Start("explorer.exe", argument); - } - } } private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) { - if (updateChecker.NewVersionFound) - { - updateChecker.NewVersionFound = false; /* Reset the flag */ - } } private void notifyIcon1_Click(object sender, MouseEventArgs e) @@ -538,12 +460,13 @@ namespace Shadowsocks.View } } - private void CheckUpdateForFirstRun() + void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) { - Configuration config = controller.GetConfigurationCopy(); - if (config.isDefault) return; - _isStartupChecking = true; - updateChecker.CheckUpdate(config, 3000); + Configuration config = controller.GetCurrentConfiguration(); + if (config.firstRun) + return; + _isStartupCheck = true; + Dispatcher.CurrentDispatcher.Invoke(() => updateChecker.CheckForVersionUpdate(3000)); } public void ShowLogForm_HotKey() @@ -555,6 +478,144 @@ namespace Shadowsocks.View #region Main menu + void controller_ShareOverLANStatusChanged(object sender, EventArgs e) + { + ShareOverLANItem.Checked = controller.GetCurrentConfiguration().shareOverLan; + } + + private void proxyItem_Click(object sender, EventArgs e) + { + if (forwardProxyWindow == null) + { + forwardProxyWindow = new System.Windows.Window() + { + Title = LocalizationProvider.GetLocalizedValue("ForwardProxy"), + Height = 400, + Width = 280, + MinHeight = 400, + MinWidth = 280, + Content = new ForwardProxyView() + }; + forwardProxyWindow.Closed += ForwardProxyWindow_Closed; + ElementHost.EnableModelessKeyboardInterop(forwardProxyWindow); + forwardProxyWindow.Show(); + } + forwardProxyWindow.Activate(); + } + + private void ForwardProxyWindow_Closed(object sender, EventArgs e) + { + forwardProxyWindow = null; + } + + public void CloseForwardProxyWindow() => forwardProxyWindow.Close(); + + private void OnlineConfig_Click(object sender, EventArgs e) + { + if (onlineConfigWindow == null) + { + onlineConfigWindow = new System.Windows.Window() + { + Title = LocalizationProvider.GetLocalizedValue("OnlineConfigDelivery"), + Height = 510, + Width = 480, + MinHeight = 510, + MinWidth = 480, + Content = new OnlineConfigView() + }; + onlineConfigWindow.Closed += OnlineConfigWindow_Closed; + ElementHost.EnableModelessKeyboardInterop(onlineConfigWindow); + onlineConfigWindow.Show(); + } + onlineConfigWindow.Activate(); + } + + private void OnlineConfigWindow_Closed(object sender, EventArgs e) + { + onlineConfigWindow = null; + } + + private void hotKeyItem_Click(object sender, EventArgs e) + { + if (hotkeysWindow == null) + { + hotkeysWindow = new System.Windows.Window() + { + Title = LocalizationProvider.GetLocalizedValue("Hotkeys"), + Height = 260, + Width = 320, + MinHeight = 260, + MinWidth = 320, + Content = new HotkeysView() + }; + hotkeysWindow.Closed += HotkeysWindow_Closed; + ElementHost.EnableModelessKeyboardInterop(hotkeysWindow); + hotkeysWindow.Show(); + } + hotkeysWindow.Activate(); + } + + private void HotkeysWindow_Closed(object sender, EventArgs e) + { + hotkeysWindow = null; + } + + public void CloseHotkeysWindow() => hotkeysWindow.Close(); + + private void ShareOverLANItem_Click(object sender, EventArgs e) + { + ShareOverLANItem.Checked = !ShareOverLANItem.Checked; + controller.ToggleShareOverLAN(ShareOverLANItem.Checked); + } + + private void AutoStartupItem_Click(object sender, EventArgs e) + { + AutoStartupItem.Checked = !AutoStartupItem.Checked; + if (!AutoStartup.Set(AutoStartupItem.Checked)) + { + MessageBox.Show(I18N.GetString("Failed to update registry")); + } + LoadCurrentConfiguration(); + } + + private void ProtocolHandlerItem_Click(object sender, EventArgs e) + { + ProtocolHandlerItem.Checked = !ProtocolHandlerItem.Checked; + if (!ProtocolHandler.Set(ProtocolHandlerItem.Checked)) + { + MessageBox.Show(I18N.GetString("Failed to update registry")); + } + LoadCurrentConfiguration(); + } + + private void Quit_Click(object sender, EventArgs e) + { + controller.Stop(); + _notifyIcon.Visible = false; + Application.Exit(); + } + + #endregion + + #region System proxy + + private void controller_EnableStatusChanged(object sender, EventArgs e) + { + disableItem.Checked = !controller.GetCurrentConfiguration().enabled; + } + + private void CheckUpdateForFirstRun() + { + Configuration config = controller.GetConfigurationCopy(); + if (config.isDefault) return; + _isStartupChecking = true; + updateChecker.CheckUpdate(config, 3000); + } + + #endregion + + #region Main menu + void controller_ShareOverLANStatusChanged(object sender, EventArgs e) { ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; @@ -620,13 +681,13 @@ namespace Shadowsocks.View private void EnableItem_Click(object sender, EventArgs e) { controller.ToggleEnable(false); - Configuration config = controller.GetConfigurationCopy(); + Configuration config = controller.GetCurrentConfiguration(); UpdateSystemProxyItemsEnabledStatus(config); } void controller_EnableGlobalChanged(object sender, EventArgs e) { - globalModeItem.Checked = controller.GetConfigurationCopy().global; + globalModeItem.Checked = controller.GetCurrentConfiguration().global; PACModeItem.Checked = !globalModeItem.Checked; } @@ -652,7 +713,7 @@ namespace Shadowsocks.View { controller.ToggleEnable(true); controller.ToggleGlobal(true); - Configuration config = controller.GetConfigurationCopy(); + Configuration config = controller.GetCurrentConfiguration(); UpdateSystemProxyItemsEnabledStatus(config); } @@ -660,7 +721,7 @@ namespace Shadowsocks.View { controller.ToggleEnable(true); controller.ToggleGlobal(false); - Configuration config = controller.GetConfigurationCopy(); + Configuration config = controller.GetCurrentConfiguration(); UpdateSystemProxyItemsEnabledStatus(config); } @@ -693,7 +754,7 @@ namespace Shadowsocks.View items.Add(new ToolStripSeparator() { Tag = "-server-" }); } int serverCount = 0; - Configuration configuration = controller.GetConfigurationCopy(); + Configuration configuration = controller.GetCurrentConfiguration(); foreach (var server in configuration.configs) { if (Configuration.ChecksServer(server)) @@ -746,112 +807,58 @@ namespace Shadowsocks.View ShowConfigForm(); } - void splash_FormClosed(object sender, FormClosedEventArgs e) - { - ShowConfigForm(); - } - void openURLFromQRCode(object sender, FormClosedEventArgs e) { Utils.OpenInBrowser(_urlToOpen); } - private void StatisticsConfigItem_Click(object sender, EventArgs e) + private void QRCodeItem_Click(object sender, EventArgs e) { - StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); - form.Show(); + if (serverSharingWindow == null) + { + serverSharingWindow = new System.Windows.Window() + { + Title = LocalizationProvider.GetLocalizedValue("ServerSharing"), + Height = 400, + Width = 660, + MinHeight = 400, + MinWidth = 660, + Content = new ServerSharingView() + }; + serverSharingWindow.Closed += ServerSharingWindow_Closed; + ElementHost.EnableModelessKeyboardInterop(serverSharingWindow); + serverSharingWindow.Show(); + } + serverSharingWindow.Activate(); } - private void QRCodeItem_Click(object sender, EventArgs e) + private void ServerSharingWindow_Closed(object sender, EventArgs e) { - QRCodeForm qrCodeForm = new QRCodeForm(controller.GetServerURLForCurrentServer()); - //qrCodeForm.Icon = this.Icon; - // TODO - qrCodeForm.Show(); + serverSharingWindow = null; } private void ScanQRCodeItem_Click(object sender, EventArgs e) { - foreach (Screen screen in Screen.AllScreens) + var result = Utils.ScanQRCodeFromScreen(); + if (result != null) { - using (Bitmap fullImage = new Bitmap(screen.Bounds.Width, - screen.Bounds.Height)) + if (result.ToLowerInvariant().StartsWith("http://") || result.ToLowerInvariant().StartsWith("https://")) { - using (Graphics g = Graphics.FromImage(fullImage)) - { - g.CopyFromScreen(screen.Bounds.X, - screen.Bounds.Y, - 0, 0, - fullImage.Size, - CopyPixelOperation.SourceCopy); - } - int maxTry = 10; - for (int i = 0; i < maxTry; i++) - { - int marginLeft = (int)((double)fullImage.Width * i / 2.5 / maxTry); - int marginTop = (int)((double)fullImage.Height * i / 2.5 / maxTry); - Rectangle cropRect = new Rectangle(marginLeft, marginTop, fullImage.Width - marginLeft * 2, fullImage.Height - marginTop * 2); - Bitmap target = new Bitmap(screen.Bounds.Width, screen.Bounds.Height); - - double imageScale = (double)screen.Bounds.Width / (double)cropRect.Width; - using (Graphics g = Graphics.FromImage(target)) - { - g.DrawImage(fullImage, new Rectangle(0, 0, target.Width, target.Height), - cropRect, - GraphicsUnit.Pixel); - } - var source = new BitmapSourceLuminanceSource(null); - var bitmap = new BinaryBitmap(new HybridBinarizer(source)); - QRCodeReader reader = new QRCodeReader(); - var result = reader.decode(bitmap); - if (result != null) - { - var success = controller.AddServerBySSURL(result.Text); - QRCodeSplashForm splash = new QRCodeSplashForm(); - if (success) - { - splash.FormClosed += splash_FormClosed; - } - else if (result.Text.ToLower().StartsWith("http://") || result.Text.ToLower().StartsWith("https://")) - { - _urlToOpen = result.Text; - splash.FormClosed += openURLFromQRCode; - } - else - { - MessageBox.Show(I18N.GetString("Failed to decode QRCode")); - return; - } - double minX = Int32.MaxValue, minY = Int32.MaxValue, maxX = 0, maxY = 0; - foreach (ResultPoint point in result.ResultPoints) - { - minX = Math.Min(minX, point.X); - minY = Math.Min(minY, point.Y); - maxX = Math.Max(maxX, point.X); - maxY = Math.Max(maxY, point.Y); - } - minX /= imageScale; - minY /= imageScale; - maxX /= imageScale; - maxY /= imageScale; - // make it 20% larger - double margin = (maxX - minX) * 0.20f; - minX += -margin + marginLeft; - maxX += margin + marginLeft; - minY += -margin + marginTop; - maxY += margin + marginTop; - splash.Location = new Point(screen.Bounds.X, screen.Bounds.Y); - // we need a panel because a window has a minimal size - // TODO: test on high DPI - splash.TargetRect = new Rectangle((int)minX, (int)minY, (int)maxX - (int)minX, (int)maxY - (int)minY); - splash.Size = new Size(fullImage.Width, fullImage.Height); - splash.Show(); - return; - } - } + _urlToOpen = result; + openURLFromQRCode(); } + else if (controller.AddServerBySSURL(result)) + { + ShowConfigForm(); + } + else + { + MessageBox.Show(I18N.GetString("Invalid QR Code content: {0}", result)); + } + return; } - MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen.")); + else + MessageBox.Show(I18N.GetString("No QRCode found. Try to zoom in or move it to the center of the screen.")); } private void ImportURLItem_Click(object sender, EventArgs e) @@ -881,11 +888,11 @@ namespace Shadowsocks.View { if (!onlinePACItem.Checked) { - if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) + if (string.IsNullOrEmpty(controller.GetCurrentConfiguration().pacUrl)) { UpdateOnlinePACURLItem_Click(sender, e); } - if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) + if (!string.IsNullOrEmpty(controller.GetCurrentConfiguration().pacUrl)) { localPACItem.Checked = false; onlinePACItem.Checked = true; @@ -897,12 +904,12 @@ namespace Shadowsocks.View private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) { - string origPacUrl = controller.GetConfigurationCopy().pacUrl; + string origPacUrl = controller.GetCurrentConfiguration().pacUrl; string pacUrl = ViewUtils.InputBox( I18N.GetString("Please input PAC Url"), I18N.GetString("Edit Online PAC URL"), origPacUrl, -1, -1); - if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl) + if (!string.IsNullOrEmpty(pacUrl) && pacUrl != origPacUrl) { controller.SavePACUrl(pacUrl); } @@ -910,10 +917,16 @@ namespace Shadowsocks.View private void SecureLocalPacUrlToggleItem_Click(object sender, EventArgs e) { - Configuration configuration = controller.GetConfigurationCopy(); + Configuration configuration = controller.GetCurrentConfiguration(); controller.ToggleSecureLocalPac(!configuration.secureLocalPac); } + private void RegenerateLocalPacOnUpdateItem_Click(object sender, EventArgs e) + { + var config = controller.GetCurrentConfiguration(); + controller.ToggleRegeneratePacOnUpdate(!config.regeneratePacOnUpdate); + } + private void CopyLocalPacUrlItem_Click(object sender, EventArgs e) { controller.CopyPacUrl(); @@ -937,7 +950,6 @@ namespace Shadowsocks.View } } - private void EditPACFileItem_Click(object sender, EventArgs e) { controller.TouchPACFile(); @@ -980,12 +992,12 @@ namespace Shadowsocks.View void controller_VerboseLoggingStatusChanged(object sender, EventArgs e) { - VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging; + VerboseLoggingToggleItem.Checked = controller.GetCurrentConfiguration().isVerboseLogging; } void controller_ShowPluginOutputChanged(object sender, EventArgs e) { - ShowPluginOutputToggleItem.Checked = controller.GetConfigurationCopy().showPluginOutput; + ShowPluginOutputToggleItem.Checked = controller.GetCurrentConfiguration().showPluginOutput; } private void VerboseLoggingToggleItem_Click(object sender, EventArgs e) @@ -1016,41 +1028,37 @@ namespace Shadowsocks.View void updateChecker_CheckUpdateCompleted(object sender, EventArgs e) { - if (updateChecker.NewVersionFound) - { - ShowBalloonTip(I18N.GetString("Shadowsocks {0} Update Found", updateChecker.LatestVersionNumber + updateChecker.LatestVersionSuffix), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000); - } - else if (!_isStartupChecking) + if (!_isStartupCheck && updateChecker.NewReleaseZipFilename == null) { ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000); } - _isStartupChecking = false; + _isStartupCheck = false; } private void UpdateUpdateMenu() { - Configuration configuration = controller.GetConfigurationCopy(); + Configuration configuration = controller.GetCurrentConfiguration(); autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate; checkPreReleaseToggleItem.Checked = configuration.checkPreRelease; } private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e) { - Configuration configuration = controller.GetConfigurationCopy(); + Configuration configuration = controller.GetCurrentConfiguration(); controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate); UpdateUpdateMenu(); } private void checkPreReleaseToggleItem_Click(object sender, EventArgs e) { - Configuration configuration = controller.GetConfigurationCopy(); + Configuration configuration = controller.GetCurrentConfiguration(); controller.ToggleCheckingPreRelease(!configuration.checkPreRelease); UpdateUpdateMenu(); } - private void checkUpdatesItem_Click(object sender, EventArgs e) + private async void checkUpdatesItem_Click(object sender, EventArgs e) { - updateChecker.CheckUpdate(controller.GetConfigurationCopy()); + await updateChecker.CheckForVersionUpdate(); } private void AboutItem_Click(object sender, EventArgs e) diff --git a/shadowsocks-csharp/View/ProxyForm.Designer.cs b/shadowsocks-csharp/View/ProxyForm.Designer.cs deleted file mode 100644 index abbf6527..00000000 --- a/shadowsocks-csharp/View/ProxyForm.Designer.cs +++ /dev/null @@ -1,333 +0,0 @@ -namespace Shadowsocks.View -{ - partial class ProxyForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.MyCancelButton = new System.Windows.Forms.Button(); - this.OKButton = new System.Windows.Forms.Button(); - this.UseProxyCheckBox = new System.Windows.Forms.CheckBox(); - this.ProxyAddrLabel = new System.Windows.Forms.Label(); - this.ProxyServerTextBox = new System.Windows.Forms.TextBox(); - this.ProxyPortLabel = new System.Windows.Forms.Label(); - this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); - this.ProxyTypeLabel = new System.Windows.Forms.Label(); - this.ProxyTypeComboBox = new System.Windows.Forms.ComboBox(); - this.ProxyTimeoutTextBox = new System.Windows.Forms.TextBox(); - this.ProxyTimeoutLabel = new System.Windows.Forms.Label(); - this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.ProxyNotificationLabel = new System.Windows.Forms.Label(); - this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); - this.AuthUserLabel = new System.Windows.Forms.Label(); - this.AuthPwdLabel = new System.Windows.Forms.Label(); - this.UseAuthCheckBox = new System.Windows.Forms.CheckBox(); - this.AuthUserTextBox = new System.Windows.Forms.TextBox(); - this.AuthPwdTextBox = new System.Windows.Forms.TextBox(); - this.tableLayoutPanel1.SuspendLayout(); - this.flowLayoutPanel1.SuspendLayout(); - this.SuspendLayout(); - // - // MyCancelButton - // - this.MyCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.MyCancelButton.Location = new System.Drawing.Point(84, 3); - this.MyCancelButton.Margin = new System.Windows.Forms.Padding(3, 3, 0, 0); - this.MyCancelButton.Name = "MyCancelButton"; - this.MyCancelButton.Size = new System.Drawing.Size(75, 23); - this.MyCancelButton.TabIndex = 13; - this.MyCancelButton.Text = "Cancel"; - this.MyCancelButton.UseVisualStyleBackColor = true; - this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click); - // - // OKButton - // - this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK; - this.OKButton.Location = new System.Drawing.Point(3, 3); - this.OKButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); - this.OKButton.Name = "OKButton"; - this.OKButton.Size = new System.Drawing.Size(75, 23); - this.OKButton.TabIndex = 12; - this.OKButton.Text = "OK"; - this.OKButton.UseVisualStyleBackColor = true; - this.OKButton.Click += new System.EventHandler(this.OKButton_Click); - // - // UseProxyCheckBox - // - this.UseProxyCheckBox.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.UseProxyCheckBox.AutoSize = true; - this.tableLayoutPanel1.SetColumnSpan(this.UseProxyCheckBox, 2); - this.UseProxyCheckBox.Location = new System.Drawing.Point(3, 6); - this.UseProxyCheckBox.Name = "UseProxyCheckBox"; - this.UseProxyCheckBox.Size = new System.Drawing.Size(78, 16); - this.UseProxyCheckBox.TabIndex = 0; - this.UseProxyCheckBox.Text = "Use Proxy"; - this.UseProxyCheckBox.UseVisualStyleBackColor = true; - this.UseProxyCheckBox.CheckedChanged += new System.EventHandler(this.UseProxyCheckBox_CheckedChanged); - // - // ProxyAddrLabel - // - this.ProxyAddrLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyAddrLabel.AutoSize = true; - this.ProxyAddrLabel.Location = new System.Drawing.Point(3, 64); - this.ProxyAddrLabel.Name = "ProxyAddrLabel"; - this.ProxyAddrLabel.Size = new System.Drawing.Size(65, 12); - this.ProxyAddrLabel.TabIndex = 0; - this.ProxyAddrLabel.Text = "Proxy Addr"; - // - // ProxyServerTextBox - // - this.ProxyServerTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyServerTextBox.Location = new System.Drawing.Point(74, 59); - this.ProxyServerTextBox.MaxLength = 512; - this.ProxyServerTextBox.Name = "ProxyServerTextBox"; - this.ProxyServerTextBox.Size = new System.Drawing.Size(138, 21); - this.ProxyServerTextBox.TabIndex = 1; - this.ProxyServerTextBox.WordWrap = false; - // - // ProxyPortLabel - // - this.ProxyPortLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyPortLabel.AutoSize = true; - this.ProxyPortLabel.Location = new System.Drawing.Point(218, 64); - this.ProxyPortLabel.Name = "ProxyPortLabel"; - this.ProxyPortLabel.Size = new System.Drawing.Size(77, 12); - this.ProxyPortLabel.TabIndex = 2; - this.ProxyPortLabel.Text = "Proxy Port"; - // - // ProxyPortTextBox - // - this.ProxyPortTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyPortTextBox.Location = new System.Drawing.Point(301, 59); - this.ProxyPortTextBox.MaxLength = 10; - this.ProxyPortTextBox.Name = "ProxyPortTextBox"; - this.ProxyPortTextBox.Size = new System.Drawing.Size(91, 21); - this.ProxyPortTextBox.TabIndex = 3; - this.ProxyPortTextBox.WordWrap = false; - // - // ProxyTypeLabel - // - this.ProxyTypeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyTypeLabel.AutoSize = true; - this.ProxyTypeLabel.Location = new System.Drawing.Point(3, 36); - this.ProxyTypeLabel.Name = "ProxyTypeLabel"; - this.ProxyTypeLabel.Size = new System.Drawing.Size(65, 12); - this.ProxyTypeLabel.TabIndex = 1; - this.ProxyTypeLabel.Text = "Proxy Type"; - // - // ProxyTypeComboBox - // - this.ProxyTypeComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.ProxyTypeComboBox.FormattingEnabled = true; - this.ProxyTypeComboBox.Items.AddRange(new object[] { - "SOCKS5", - "HTTP"}); - this.ProxyTypeComboBox.Location = new System.Drawing.Point(74, 33); - this.ProxyTypeComboBox.Margin = new System.Windows.Forms.Padding(3, 5, 3, 5); - this.ProxyTypeComboBox.Name = "ProxyTypeComboBox"; - this.ProxyTypeComboBox.Size = new System.Drawing.Size(138, 20); - this.ProxyTypeComboBox.TabIndex = 2; - this.ProxyTypeComboBox.SelectedIndexChanged += new System.EventHandler(this.ProxyTypeComboBox_SelectedIndexChanged); - // - // ProxyTimeoutTextBox - // - this.ProxyTimeoutTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyTimeoutTextBox.Location = new System.Drawing.Point(301, 31); - this.ProxyTimeoutTextBox.Name = "ProxyTimeoutTextBox"; - this.ProxyTimeoutTextBox.Size = new System.Drawing.Size(91, 21); - this.ProxyTimeoutTextBox.TabIndex = 3; - // - // ProxyTimeoutLabel - // - this.ProxyTimeoutLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyTimeoutLabel.AutoSize = true; - this.ProxyTimeoutLabel.Location = new System.Drawing.Point(218, 36); - this.ProxyTimeoutLabel.Name = "ProxyTimeoutLabel"; - this.ProxyTimeoutLabel.Size = new System.Drawing.Size(77, 12); - this.ProxyTimeoutLabel.TabIndex = 4; - this.ProxyTimeoutLabel.Text = "Timeout(Sec)"; - // - // tableLayoutPanel1 - // - this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.tableLayoutPanel1.ColumnCount = 4; - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 60F)); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 40F)); - this.tableLayoutPanel1.Controls.Add(this.UseProxyCheckBox, 0, 0); - this.tableLayoutPanel1.Controls.Add(this.ProxyTypeLabel, 0, 1); - this.tableLayoutPanel1.Controls.Add(this.ProxyPortTextBox, 3, 2); - this.tableLayoutPanel1.Controls.Add(this.ProxyTypeComboBox, 1, 1); - this.tableLayoutPanel1.Controls.Add(this.ProxyTimeoutLabel, 2, 1); - this.tableLayoutPanel1.Controls.Add(this.ProxyPortLabel, 2, 2); - this.tableLayoutPanel1.Controls.Add(this.ProxyTimeoutTextBox, 3, 1); - this.tableLayoutPanel1.Controls.Add(this.ProxyServerTextBox, 1, 2); - this.tableLayoutPanel1.Controls.Add(this.ProxyAddrLabel, 0, 2); - this.tableLayoutPanel1.Controls.Add(this.ProxyNotificationLabel, 0, 3); - this.tableLayoutPanel1.Controls.Add(this.flowLayoutPanel1, 0, 6); - this.tableLayoutPanel1.Controls.Add(this.AuthUserLabel, 0, 5); - this.tableLayoutPanel1.Controls.Add(this.AuthPwdLabel, 2, 5); - this.tableLayoutPanel1.Controls.Add(this.UseAuthCheckBox, 0, 4); - this.tableLayoutPanel1.Controls.Add(this.AuthUserTextBox, 1, 5); - this.tableLayoutPanel1.Controls.Add(this.AuthPwdTextBox, 3, 5); - this.tableLayoutPanel1.Location = new System.Drawing.Point(15, 15); - this.tableLayoutPanel1.Name = "tableLayoutPanel1"; - this.tableLayoutPanel1.RowCount = 7; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66713F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66713F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.66713F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.6662F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.6662F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 16.6662F)); - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(395, 204); - this.tableLayoutPanel1.TabIndex = 14; - // - // ProxyNotificationLabel - // - this.ProxyNotificationLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.ProxyNotificationLabel.AutoSize = true; - this.tableLayoutPanel1.SetColumnSpan(this.ProxyNotificationLabel, 4); - this.ProxyNotificationLabel.ForeColor = System.Drawing.Color.Red; - this.ProxyNotificationLabel.Location = new System.Drawing.Point(3, 92); - this.ProxyNotificationLabel.Name = "ProxyNotificationLabel"; - this.ProxyNotificationLabel.Size = new System.Drawing.Size(389, 12); - this.ProxyNotificationLabel.TabIndex = 5; - this.ProxyNotificationLabel.Text = "If server has a plugin, proxy will not be used"; - // - // flowLayoutPanel1 - // - this.flowLayoutPanel1.AutoSize = true; - this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.tableLayoutPanel1.SetColumnSpan(this.flowLayoutPanel1, 4); - this.flowLayoutPanel1.Controls.Add(this.MyCancelButton); - this.flowLayoutPanel1.Controls.Add(this.OKButton); - this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Right; - this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; - this.flowLayoutPanel1.Location = new System.Drawing.Point(233, 171); - this.flowLayoutPanel1.Name = "flowLayoutPanel1"; - this.flowLayoutPanel1.Size = new System.Drawing.Size(159, 30); - this.flowLayoutPanel1.TabIndex = 6; - // - // AuthUserLabel - // - this.AuthUserLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.AuthUserLabel.AutoSize = true; - this.AuthUserLabel.Location = new System.Drawing.Point(3, 148); - this.AuthUserLabel.Name = "AuthUserLabel"; - this.AuthUserLabel.Size = new System.Drawing.Size(65, 12); - this.AuthUserLabel.TabIndex = 7; - this.AuthUserLabel.Text = "User Name"; - // - // AuthPwdLabel - // - this.AuthPwdLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.AuthPwdLabel.AutoSize = true; - this.AuthPwdLabel.Location = new System.Drawing.Point(218, 148); - this.AuthPwdLabel.Name = "AuthPwdLabel"; - this.AuthPwdLabel.Size = new System.Drawing.Size(77, 12); - this.AuthPwdLabel.TabIndex = 8; - this.AuthPwdLabel.Text = "Password"; - // - // UseAuthCheckBox - // - this.UseAuthCheckBox.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.UseAuthCheckBox.AutoSize = true; - this.tableLayoutPanel1.SetColumnSpan(this.UseAuthCheckBox, 2); - this.UseAuthCheckBox.Location = new System.Drawing.Point(3, 118); - this.UseAuthCheckBox.Name = "UseAuthCheckBox"; - this.UseAuthCheckBox.Size = new System.Drawing.Size(72, 16); - this.UseAuthCheckBox.TabIndex = 9; - this.UseAuthCheckBox.Text = "Use Auth"; - this.UseAuthCheckBox.UseVisualStyleBackColor = true; - this.UseAuthCheckBox.CheckedChanged += new System.EventHandler(this.UseAuthCheckBox_CheckedChanged); - // - // AuthUserTextBox - // - this.AuthUserTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.AuthUserTextBox.Location = new System.Drawing.Point(74, 143); - this.AuthUserTextBox.Name = "AuthUserTextBox"; - this.AuthUserTextBox.Size = new System.Drawing.Size(138, 21); - this.AuthUserTextBox.TabIndex = 10; - // - // AuthPwdTextBox - // - this.AuthPwdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); - this.AuthPwdTextBox.Location = new System.Drawing.Point(301, 143); - this.AuthPwdTextBox.Name = "AuthPwdTextBox"; - this.AuthPwdTextBox.PasswordChar = '*'; - this.AuthPwdTextBox.Size = new System.Drawing.Size(91, 21); - this.AuthPwdTextBox.TabIndex = 11; - // - // ProxyForm - // - this.AcceptButton = this.OKButton; - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.AutoSize = true; - this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.CancelButton = this.MyCancelButton; - this.ClientSize = new System.Drawing.Size(448, 231); - this.Controls.Add(this.tableLayoutPanel1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "ProxyForm"; - this.Padding = new System.Windows.Forms.Padding(12, 12, 12, 9); - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Edit Proxy"; - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ProxyForm_FormClosed); - this.tableLayoutPanel1.ResumeLayout(false); - this.tableLayoutPanel1.PerformLayout(); - this.flowLayoutPanel1.ResumeLayout(false); - this.ResumeLayout(false); - - } - - #endregion - private System.Windows.Forms.CheckBox UseProxyCheckBox; - private System.Windows.Forms.Label ProxyAddrLabel; - private System.Windows.Forms.TextBox ProxyServerTextBox; - private System.Windows.Forms.Label ProxyPortLabel; - private System.Windows.Forms.TextBox ProxyPortTextBox; - private System.Windows.Forms.Button MyCancelButton; - private System.Windows.Forms.Button OKButton; - private System.Windows.Forms.Label ProxyTypeLabel; - private System.Windows.Forms.ComboBox ProxyTypeComboBox; - private System.Windows.Forms.TextBox ProxyTimeoutTextBox; - private System.Windows.Forms.Label ProxyTimeoutLabel; - private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; - private System.Windows.Forms.Label ProxyNotificationLabel; - private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; - private System.Windows.Forms.Label AuthUserLabel; - private System.Windows.Forms.Label AuthPwdLabel; - private System.Windows.Forms.CheckBox UseAuthCheckBox; - private System.Windows.Forms.TextBox AuthUserTextBox; - private System.Windows.Forms.TextBox AuthPwdTextBox; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/View/ProxyForm.cs b/shadowsocks-csharp/View/ProxyForm.cs deleted file mode 100644 index 218f296f..00000000 --- a/shadowsocks-csharp/View/ProxyForm.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Drawing; -using System.Windows.Forms; -using Shadowsocks.Controller; -using Shadowsocks.Model; -using Shadowsocks.Properties; - -namespace Shadowsocks.View -{ - public partial class ProxyForm : Form - { - private ShadowsocksController controller; - - // this is a copy of configuration that we are working on - private ProxyConfig _modifiedProxyConfig; - - public ProxyForm(ShadowsocksController controller) - { - this.Font = System.Drawing.SystemFonts.MessageBoxFont; - InitializeComponent(); - - UpdateTexts(); - this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); - - this.controller = controller; - controller.ConfigChanged += controller_ConfigChanged; - - UpdateEnabled(); - LoadCurrentConfiguration(); - } - - private void UpdateTexts() - { - I18N.TranslateForm(this); - } - - private void controller_ConfigChanged(object sender, EventArgs e) - { - LoadCurrentConfiguration(); - } - - private void LoadCurrentConfiguration() - { - _modifiedProxyConfig = controller.GetConfigurationCopy().proxy; - UseProxyCheckBox.Checked = _modifiedProxyConfig.useProxy; - ProxyServerTextBox.Text = _modifiedProxyConfig.proxyServer; - ProxyPortTextBox.Text = _modifiedProxyConfig.proxyPort.ToString(); - ProxyTimeoutTextBox.Text = _modifiedProxyConfig.proxyTimeout.ToString(); - ProxyTypeComboBox.SelectedIndex = _modifiedProxyConfig.proxyType; - UseAuthCheckBox.Checked = _modifiedProxyConfig.useAuth; - AuthUserTextBox.Text = _modifiedProxyConfig.authUser; - AuthPwdTextBox.Text = _modifiedProxyConfig.authPwd; - } - - private void OKButton_Click(object sender, EventArgs e) - { - _modifiedProxyConfig.useProxy = UseProxyCheckBox.Checked; - if (_modifiedProxyConfig.useProxy) - { - if (!int.TryParse(ProxyPortTextBox.Text, out _modifiedProxyConfig.proxyPort)) - { - MessageBox.Show(I18N.GetString("Illegal port number format")); - return; - } - - if (!int.TryParse(ProxyTimeoutTextBox.Text, out _modifiedProxyConfig.proxyTimeout)) - { - MessageBox.Show(I18N.GetString("Illegal timeout format")); - return; - } - - _modifiedProxyConfig.proxyType = ProxyTypeComboBox.SelectedIndex; - - try - { - Configuration.CheckServer(_modifiedProxyConfig.proxyServer = ProxyServerTextBox.Text); - Configuration.CheckPort(_modifiedProxyConfig.proxyPort); - Configuration.CheckTimeout(_modifiedProxyConfig.proxyTimeout, ProxyConfig.MaxProxyTimeoutSec); - - _modifiedProxyConfig.useAuth = UseAuthCheckBox.Checked; - if (_modifiedProxyConfig.useAuth) - { - Configuration.CheckProxyAuthUser(_modifiedProxyConfig.authUser = AuthUserTextBox.Text); - Configuration.CheckProxyAuthPwd(_modifiedProxyConfig.authPwd = AuthPwdTextBox.Text); - } - } - catch (Exception ex) - { - MessageBox.Show(ex.Message); - return; - } - } - - controller.SaveProxy(_modifiedProxyConfig); - - this.Close(); - } - - private void CancelButton_Click(object sender, EventArgs e) - { - this.Close(); - } - - private void ProxyForm_FormClosed(object sender, FormClosedEventArgs e) - { - controller.ConfigChanged -= controller_ConfigChanged; - } - - private void UseProxyCheckBox_CheckedChanged(object sender, EventArgs e) - { - UpdateEnabled(); - } - - private void UpdateEnabled() - { - if (UseProxyCheckBox.Checked) - { - ProxyServerTextBox.Enabled = - ProxyPortTextBox.Enabled = - ProxyTimeoutTextBox.Enabled = - ProxyTypeComboBox.Enabled = true; - - if (ProxyTypeComboBox.SelectedIndex == ProxyConfig.PROXY_HTTP) - { - UseAuthCheckBox.Enabled = true; - - if (UseAuthCheckBox.Checked) - { - AuthUserTextBox.Enabled = - AuthPwdTextBox.Enabled = true; - } - else - { - AuthUserTextBox.Enabled = - AuthPwdTextBox.Enabled = false; - } - } - else - { - // TODO support for SOCK5 auth - UseAuthCheckBox.Enabled = - AuthUserTextBox.Enabled = - AuthPwdTextBox.Enabled = false; - } - } - else - { - ProxyServerTextBox.Enabled = - ProxyPortTextBox.Enabled = - ProxyTimeoutTextBox.Enabled = - ProxyTypeComboBox.Enabled = - UseAuthCheckBox.Enabled = - AuthUserTextBox.Enabled = - AuthPwdTextBox.Enabled = false; - } - } - - private void ProxyTypeComboBox_SelectedIndexChanged(object sender, EventArgs e) - { - // TODO support for SOCK5 auth - if (ProxyTypeComboBox.SelectedIndex != ProxyConfig.PROXY_HTTP) - { - UseAuthCheckBox.Checked = false; - AuthUserTextBox.Clear(); - AuthPwdTextBox.Clear(); - } - - UpdateEnabled(); - } - - private void UseAuthCheckBox_CheckedChanged(object sender, EventArgs e) - { - UpdateEnabled(); - } - } -} diff --git a/shadowsocks-csharp/View/QRCodeForm.Designer.cs b/shadowsocks-csharp/View/QRCodeForm.Designer.cs deleted file mode 100755 index 0dbc2da3..00000000 --- a/shadowsocks-csharp/View/QRCodeForm.Designer.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace Shadowsocks.View -{ - partial class QRCodeForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.pictureBox1 = new System.Windows.Forms.PictureBox(); - this.listBox1 = new System.Windows.Forms.ListBox(); - this.textBoxURL = new System.Windows.Forms.TextBox(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); - this.SuspendLayout(); - // - // pictureBox1 - // - this.pictureBox1.Location = new System.Drawing.Point(10, 10); - this.pictureBox1.Margin = new System.Windows.Forms.Padding(0); - this.pictureBox1.Name = "pictureBox1"; - this.pictureBox1.Size = new System.Drawing.Size(210, 210); - this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; - this.pictureBox1.TabIndex = 1; - this.pictureBox1.TabStop = false; - // - // listBox1 - // - this.listBox1.DisplayMember = "Value"; - this.listBox1.FormattingEnabled = true; - this.listBox1.ItemHeight = 12; - this.listBox1.Location = new System.Drawing.Point(224, 10); - this.listBox1.Name = "listBox1"; - this.listBox1.ScrollAlwaysVisible = true; - this.listBox1.Size = new System.Drawing.Size(227, 208); - this.listBox1.TabIndex = 2; - this.listBox1.ValueMember = "Key"; - this.listBox1.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged); - // - // textBoxURL - // - this.textBoxURL.Location = new System.Drawing.Point(10, 227); - this.textBoxURL.Name = "textBoxURL"; - this.textBoxURL.ReadOnly = true; - this.textBoxURL.Size = new System.Drawing.Size(441, 21); - this.textBoxURL.TabIndex = 3; - this.textBoxURL.Click += new System.EventHandler(this.textBoxURL_Click); - // - // QRCodeForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.AutoSize = true; - this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(457, 257); - this.Controls.Add(this.textBoxURL); - this.Controls.Add(this.listBox1); - this.Controls.Add(this.pictureBox1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "QRCodeForm"; - this.Padding = new System.Windows.Forms.Padding(10); - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "QRCode and URL"; - this.Load += new System.EventHandler(this.QRCodeForm_Load); - ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.PictureBox pictureBox1; - private System.Windows.Forms.ListBox listBox1; - private System.Windows.Forms.TextBox textBoxURL; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/View/QRCodeForm.cs b/shadowsocks-csharp/View/QRCodeForm.cs deleted file mode 100755 index d8fc8c60..00000000 --- a/shadowsocks-csharp/View/QRCodeForm.cs +++ /dev/null @@ -1,90 +0,0 @@ -using ZXing.QrCode.Internal; -using Shadowsocks.Controller; -using Shadowsocks.Properties; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Text; -using System.Windows.Forms; -using Shadowsocks.Model; - -namespace Shadowsocks.View -{ - public partial class QRCodeForm : Form - { - private string code; - - public QRCodeForm(string code) - { - this.code = code; - InitializeComponent(); - this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); - this.Text = I18N.GetString("QRCode and URL"); - } - - private void GenQR(string ssconfig) - { - string qrText = ssconfig; - QRCode code = ZXing.QrCode.Internal.Encoder.encode(qrText, ErrorCorrectionLevel.M); - ByteMatrix m = code.Matrix; - int blockSize = Math.Max(pictureBox1.Height/m.Height, 1); - - var qrWidth = m.Width*blockSize; - var qrHeight = m.Height*blockSize; - var dWidth = pictureBox1.Width - qrWidth; - var dHeight = pictureBox1.Height - qrHeight; - var maxD = Math.Max(dWidth, dHeight); - pictureBox1.SizeMode = maxD >= 7*blockSize ? PictureBoxSizeMode.Zoom : PictureBoxSizeMode.CenterImage; - - Bitmap drawArea = new Bitmap((m.Width*blockSize), (m.Height*blockSize)); - using (Graphics g = Graphics.FromImage(drawArea)) - { - g.Clear(Color.White); - using (Brush b = new SolidBrush(Color.Black)) - { - for (int row = 0; row < m.Width; row++) - { - for (int col = 0; col < m.Height; col++) - { - if (m[row, col] != 0) - { - g.FillRectangle(b, blockSize*row, blockSize*col, blockSize, blockSize); - } - } - } - } - } - pictureBox1.Image = drawArea; - } - - private void QRCodeForm_Load(object sender, EventArgs e) - { - Configuration config = Configuration.Load(); - List> serverDatas = config.configs.Select( - server => - new KeyValuePair(server.GetURL(config.generateLegacyUrl), server.ToString()) - ).ToList(); - listBox1.DataSource = serverDatas; - - int selectIndex = serverDatas.FindIndex(serverData => serverData.Key.StartsWith(code)); - if (selectIndex >= 0) listBox1.SetSelected(selectIndex, true); - } - - private void listBox1_SelectedIndexChanged(object sender, EventArgs e) - { - var url = (sender as ListBox)?.SelectedValue.ToString(); - GenQR(url); - textBoxURL.Text = url; - } - - private void textBoxURL_Click(object sender, EventArgs e) - { - textBoxURL.SelectAll(); - } - } -} diff --git a/shadowsocks-csharp/View/QRCodeSplashForm.cs b/shadowsocks-csharp/View/QRCodeSplashForm.cs deleted file mode 100755 index a2d2357b..00000000 --- a/shadowsocks-csharp/View/QRCodeSplashForm.cs +++ /dev/null @@ -1,281 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Text; -using System.Windows.Forms; -using System.Drawing.Imaging; -using System.Runtime.InteropServices; -using System.Diagnostics; - - -namespace Shadowsocks.View -{ - public class QRCodeSplashForm : Form - { - public Rectangle TargetRect; - - public QRCodeSplashForm() - { - FormBorderStyle = FormBorderStyle.None; - this.Load += QRCodeSplashForm_Load; - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; - this.BackColor = System.Drawing.Color.White; - this.ClientSize = new System.Drawing.Size(1, 1); - this.ControlBox = false; - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "QRCodeSplashForm"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; - this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; - this.TopMost = true; - } - - private Timer timer; - private int flashStep; - private static double FPS = 1.0 / 15 * 1000; // System.Windows.Forms.Timer resolution is 15ms - private static double ANIMATION_TIME = 0.5; - private static int ANIMATION_STEPS = (int)(ANIMATION_TIME * FPS); - Stopwatch sw; - int x; - int y; - int w; - int h; - Bitmap bitmap; - Graphics g; - Pen pen; - SolidBrush brush; - - private void QRCodeSplashForm_Load(object sender, EventArgs e) - { - SetStyle(ControlStyles.SupportsTransparentBackColor, true); - this.BackColor = Color.Transparent; - flashStep = 0; - x = 0; - y = 0; - w = Width; - h = Height; - sw = Stopwatch.StartNew(); - timer = new Timer(); - timer.Interval = (int)(ANIMATION_TIME * 1000 / ANIMATION_STEPS); - timer.Tick += timer_Tick; - timer.Start(); - bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); - g = Graphics.FromImage(bitmap); - pen = new Pen(Color.Red, 3); - brush = new SolidBrush(Color.FromArgb(30, Color.Red)); - } - - void timer_Tick(object sender, EventArgs e) - { - double percent = (double)sw.ElapsedMilliseconds / 1000.0 / (double)ANIMATION_TIME; - if (percent < 1) - { - // ease out - percent = 1 - Math.Pow((1 - percent), 4); - x = (int)(TargetRect.X * percent); - y = (int)(TargetRect.Y * percent); - w = (int)(TargetRect.Width * percent + this.Size.Width * (1 - percent)); - h = (int)(TargetRect.Height * percent + this.Size.Height * (1 - percent)); - //codeRectView.Location = new Point(x, y); - //codeRectView.Size = new Size(w, h); - pen.Color = Color.FromArgb((int)(255 * percent), Color.Red); - brush.Color = Color.FromArgb((int)(30 * percent), Color.Red); - g.Clear(Color.Transparent); - g.FillRectangle(brush, x, y, w, h); - g.DrawRectangle(pen, x, y, w, h); - SetBitmap(bitmap); - } - else - { - if (flashStep == 0) - { - timer.Interval = 100; - g.Clear(Color.Transparent); - SetBitmap(bitmap); - } - else if (flashStep == 1) - { - timer.Interval = 50; - g.FillRectangle(brush, x, y, w, h); - g.DrawRectangle(pen, x, y, w, h); - SetBitmap(bitmap); - } - else if (flashStep == 2) - { - g.Clear(Color.Transparent); - SetBitmap(bitmap); - } - else if (flashStep == 3) - { - g.FillRectangle(brush, x, y, w, h); - g.DrawRectangle(pen, x, y, w, h); - SetBitmap(bitmap); - } - else if (flashStep == 4) - { - g.Clear(Color.Transparent); - SetBitmap(bitmap); - } - else if (flashStep == 5) - { - g.FillRectangle(brush, x, y, w, h); - g.DrawRectangle(pen, x, y, w, h); - SetBitmap(bitmap); - } - else - { - sw.Stop(); - timer.Stop(); - pen.Dispose(); - brush.Dispose(); - bitmap.Dispose(); - this.Close(); - } - flashStep++; - } - } - - // PerPixelAlphaForm.cs - // http://www.codeproject.com/Articles/1822/Per-Pixel-Alpha-Blend-in-C - // Rui Lopes - protected override CreateParams CreateParams - { - get - { - CreateParams cp = base.CreateParams; - cp.ExStyle |= 0x00080000; // This form has to have the WS_EX_LAYERED extended style - return cp; - } - } - - public void SetBitmap(Bitmap bitmap) - { - SetBitmap(bitmap, 255); - } - - /// Changes the current bitmap with a custom opacity level. Here is where all happens! - public void SetBitmap(Bitmap bitmap, byte opacity) - { - if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) - throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); - - // The idea of this is very simple, - // 1. Create a compatible DC with screen; - // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC; - // 3. Call the UpdateLayeredWindow. - - IntPtr screenDc = Win32.GetDC(IntPtr.Zero); - IntPtr memDc = Win32.CreateCompatibleDC(screenDc); - IntPtr hBitmap = IntPtr.Zero; - IntPtr oldBitmap = IntPtr.Zero; - - try - { - hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap - oldBitmap = Win32.SelectObject(memDc, hBitmap); - - Win32.Size size = new Win32.Size(bitmap.Width, bitmap.Height); - Win32.Point pointSource = new Win32.Point(0, 0); - Win32.Point topPos = new Win32.Point(Left, Top); - Win32.BLENDFUNCTION blend = new Win32.BLENDFUNCTION(); - blend.BlendOp = Win32.AC_SRC_OVER; - blend.BlendFlags = 0; - blend.SourceConstantAlpha = opacity; - blend.AlphaFormat = Win32.AC_SRC_ALPHA; - - Win32.UpdateLayeredWindow(Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, Win32.ULW_ALPHA); - } - finally - { - Win32.ReleaseDC(IntPtr.Zero, screenDc); - if (hBitmap != IntPtr.Zero) - { - Win32.SelectObject(memDc, oldBitmap); - //Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from Win32 GDI and it's working fine without any resource leak. - Win32.DeleteObject(hBitmap); - } - Win32.DeleteDC(memDc); - } - } - } - - - // class that exposes needed win32 gdi functions. - class Win32 - { - - [StructLayout(LayoutKind.Sequential)] - public struct Point - { - public Int32 x; - public Int32 y; - - public Point(Int32 x, Int32 y) { this.x = x; this.y = y; } - } - - - [StructLayout(LayoutKind.Sequential)] - public struct Size - { - public Int32 cx; - public Int32 cy; - - public Size(Int32 cx, Int32 cy) { this.cx = cx; this.cy = cy; } - } - - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ARGB - { - public byte Blue; - public byte Green; - public byte Red; - public byte Alpha; - } - - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct BLENDFUNCTION - { - public byte BlendOp; - public byte BlendFlags; - public byte SourceConstantAlpha; - public byte AlphaFormat; - } - - - public const Int32 ULW_COLORKEY = 0x00000001; - public const Int32 ULW_ALPHA = 0x00000002; - public const Int32 ULW_OPAQUE = 0x00000004; - - public const byte AC_SRC_OVER = 0x00; - public const byte AC_SRC_ALPHA = 0x01; - - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - public static extern int UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); - - [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetDC(IntPtr hWnd); - - [DllImport("user32.dll", ExactSpelling = true)] - public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); - - [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] - public static extern IntPtr CreateCompatibleDC(IntPtr hDC); - - [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] - public static extern int DeleteDC(IntPtr hdc); - - [DllImport("gdi32.dll", ExactSpelling = true)] - public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); - - [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] - public static extern int DeleteObject(IntPtr hObject); - } -} diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs deleted file mode 100644 index 0e80c12e..00000000 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.Designer.cs +++ /dev/null @@ -1,537 +0,0 @@ -namespace Shadowsocks.View -{ - partial class StatisticsStrategyConfigurationForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); - System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); - System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); - System.Windows.Forms.DataVisualization.Charting.Series series3 = new System.Windows.Forms.DataVisualization.Charting.Series(); - this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); - this.PingCheckBox = new System.Windows.Forms.CheckBox(); - this.KeepChoiceForLabel = new System.Windows.Forms.Label(); - this.MinutesLabel2 = new System.Windows.Forms.Label(); - this.chartModeSelector = new System.Windows.Forms.GroupBox(); - this.allMode = new System.Windows.Forms.RadioButton(); - this.dayMode = new System.Windows.Forms.RadioButton(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); - this.splitContainer2 = new System.Windows.Forms.SplitContainer(); - this.CollectDataPerLabel = new System.Windows.Forms.Label(); - this.MinutesLabel1 = new System.Windows.Forms.Label(); - this.dataCollectionMinutesNum = new System.Windows.Forms.NumericUpDown(); - this.StatisticsEnabledCheckBox = new System.Windows.Forms.CheckBox(); - this.choiceKeptMinutesNum = new System.Windows.Forms.NumericUpDown(); - this.byHourOfDayCheckBox = new System.Windows.Forms.CheckBox(); - this.repeatTimesNum = new System.Windows.Forms.NumericUpDown(); - this.PackagePerPingLabel = new System.Windows.Forms.Label(); - this.splitContainer3 = new System.Windows.Forms.SplitContainer(); - this.FinalScoreLabel = new System.Windows.Forms.Label(); - this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); - this.serverSelector = new System.Windows.Forms.ComboBox(); - this.CancelButton = new System.Windows.Forms.Button(); - this.OKButton = new System.Windows.Forms.Button(); - this.CalculatinTip = new System.Windows.Forms.ToolTip(this.components); - this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); - ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); - this.chartModeSelector.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); - this.splitContainer1.Panel1.SuspendLayout(); - this.splitContainer1.Panel2.SuspendLayout(); - this.splitContainer1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); - this.splitContainer2.Panel1.SuspendLayout(); - this.splitContainer2.Panel2.SuspendLayout(); - this.splitContainer2.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit(); - this.splitContainer3.Panel1.SuspendLayout(); - this.splitContainer3.Panel2.SuspendLayout(); - this.splitContainer3.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); - this.SuspendLayout(); - // - // StatisticsChart - // - this.StatisticsChart.BackColor = System.Drawing.Color.Transparent; - chartArea1.AxisX.MajorGrid.Enabled = false; - chartArea1.AxisY.MajorGrid.Enabled = false; - chartArea1.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.False; - chartArea1.AxisY2.MajorGrid.Enabled = false; - chartArea1.BackColor = System.Drawing.Color.Transparent; - chartArea1.Name = "DataArea"; - this.StatisticsChart.ChartAreas.Add(chartArea1); - this.StatisticsChart.Dock = System.Windows.Forms.DockStyle.Fill; - legend1.BackColor = System.Drawing.Color.Transparent; - legend1.Name = "ChartLegend"; - this.StatisticsChart.Legends.Add(legend1); - this.StatisticsChart.Location = new System.Drawing.Point(0, 0); - this.StatisticsChart.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.StatisticsChart.Name = "StatisticsChart"; - this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; - series1.ChartArea = "DataArea"; - series1.Color = System.Drawing.Color.DarkGray; - series1.Legend = "ChartLegend"; - series1.Name = "Speed"; - series1.ToolTip = "#VALX\\nMax inbound speed\\n#VAL KiB/s"; - series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; - series2.ChartArea = "DataArea"; - series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; - series2.Color = System.Drawing.Color.Crimson; - series2.CustomProperties = "EmptyPointValue=Zero"; - series2.Legend = "ChartLegend"; - series2.Name = "Package Loss"; - series2.ToolTip = "#VALX\\n#VAL%"; - series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; - series2.YAxisType = System.Windows.Forms.DataVisualization.Charting.AxisType.Secondary; - series2.YValuesPerPoint = 2; - series3.BorderWidth = 5; - series3.ChartArea = "DataArea"; - series3.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; - series3.Color = System.Drawing.Color.DodgerBlue; - series3.Legend = "ChartLegend"; - series3.MarkerSize = 10; - series3.MarkerStyle = System.Windows.Forms.DataVisualization.Charting.MarkerStyle.Circle; - series3.Name = "Ping"; - series3.ToolTip = "#VALX\\n#VAL ms"; - series3.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; - this.StatisticsChart.Series.Add(series1); - this.StatisticsChart.Series.Add(series2); - this.StatisticsChart.Series.Add(series3); - this.StatisticsChart.Size = new System.Drawing.Size(982, 435); - this.StatisticsChart.TabIndex = 2; - // - // PingCheckBox - // - this.PingCheckBox.AutoSize = true; - this.PingCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "Ping", true)); - this.PingCheckBox.Location = new System.Drawing.Point(13, 54); - this.PingCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.PingCheckBox.Name = "PingCheckBox"; - this.PingCheckBox.Size = new System.Drawing.Size(107, 27); - this.PingCheckBox.TabIndex = 5; - this.PingCheckBox.Text = "Ping Test"; - this.PingCheckBox.UseVisualStyleBackColor = true; - this.PingCheckBox.CheckedChanged += new System.EventHandler(this.PingCheckBox_CheckedChanged); - // - // KeepChoiceForLabel - // - this.KeepChoiceForLabel.AutoSize = true; - this.KeepChoiceForLabel.Location = new System.Drawing.Point(9, 206); - this.KeepChoiceForLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); - this.KeepChoiceForLabel.Name = "KeepChoiceForLabel"; - this.KeepChoiceForLabel.Size = new System.Drawing.Size(139, 23); - this.KeepChoiceForLabel.TabIndex = 8; - this.KeepChoiceForLabel.Text = "Keep choice for"; - // - // MinutesLabel2 - // - this.MinutesLabel2.AutoSize = true; - this.MinutesLabel2.Location = new System.Drawing.Point(286, 206); - this.MinutesLabel2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); - this.MinutesLabel2.Name = "MinutesLabel2"; - this.MinutesLabel2.Size = new System.Drawing.Size(75, 23); - this.MinutesLabel2.TabIndex = 9; - this.MinutesLabel2.Text = "minutes"; - // - // chartModeSelector - // - this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.chartModeSelector.Controls.Add(this.allMode); - this.chartModeSelector.Controls.Add(this.dayMode); - this.chartModeSelector.Location = new System.Drawing.Point(733, 182); - this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.chartModeSelector.Name = "chartModeSelector"; - this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.chartModeSelector.Size = new System.Drawing.Size(234, 103); - this.chartModeSelector.TabIndex = 3; - this.chartModeSelector.TabStop = false; - this.chartModeSelector.Text = "Chart Mode"; - // - // allMode - // - this.allMode.AutoSize = true; - this.allMode.Location = new System.Drawing.Point(11, 61); - this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.allMode.Name = "allMode"; - this.allMode.Size = new System.Drawing.Size(50, 27); - this.allMode.TabIndex = 1; - this.allMode.Text = "all"; - this.allMode.UseVisualStyleBackColor = true; - this.allMode.CheckedChanged += new System.EventHandler(this.allMode_CheckedChanged); - // - // dayMode - // - this.dayMode.AutoSize = true; - this.dayMode.Checked = true; - this.dayMode.Location = new System.Drawing.Point(11, 29); - this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.dayMode.Name = "dayMode"; - this.dayMode.Size = new System.Drawing.Size(61, 27); - this.dayMode.TabIndex = 0; - this.dayMode.TabStop = true; - this.dayMode.Text = "24h"; - this.dayMode.UseVisualStyleBackColor = true; - this.dayMode.CheckedChanged += new System.EventHandler(this.dayMode_CheckedChanged); - // - // splitContainer1 - // - this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer1.IsSplitterFixed = true; - this.splitContainer1.Location = new System.Drawing.Point(0, 0); - this.splitContainer1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.splitContainer1.Name = "splitContainer1"; - this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer1.Panel1 - // - this.splitContainer1.Panel1.Controls.Add(this.splitContainer2); - // - // splitContainer1.Panel2 - // - this.splitContainer1.Panel2.Controls.Add(this.serverSelector); - this.splitContainer1.Panel2.Controls.Add(this.CancelButton); - this.splitContainer1.Panel2.Controls.Add(this.OKButton); - this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector); - this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); - this.splitContainer1.Size = new System.Drawing.Size(982, 753); - this.splitContainer1.SplitterDistance = 308; - this.splitContainer1.SplitterWidth = 10; - this.splitContainer1.TabIndex = 12; - // - // splitContainer2 - // - this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; - this.splitContainer2.IsSplitterFixed = true; - this.splitContainer2.Location = new System.Drawing.Point(0, 0); - this.splitContainer2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.splitContainer2.Name = "splitContainer2"; - // - // splitContainer2.Panel1 - // - this.splitContainer2.Panel1.Controls.Add(this.CollectDataPerLabel); - this.splitContainer2.Panel1.Controls.Add(this.MinutesLabel1); - this.splitContainer2.Panel1.Controls.Add(this.dataCollectionMinutesNum); - this.splitContainer2.Panel1.Controls.Add(this.StatisticsEnabledCheckBox); - this.splitContainer2.Panel1.Controls.Add(this.choiceKeptMinutesNum); - this.splitContainer2.Panel1.Controls.Add(this.byHourOfDayCheckBox); - this.splitContainer2.Panel1.Controls.Add(this.repeatTimesNum); - this.splitContainer2.Panel1.Controls.Add(this.PackagePerPingLabel); - this.splitContainer2.Panel1.Controls.Add(this.KeepChoiceForLabel); - this.splitContainer2.Panel1.Controls.Add(this.PingCheckBox); - this.splitContainer2.Panel1.Controls.Add(this.MinutesLabel2); - // - // splitContainer2.Panel2 - // - this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); - this.splitContainer2.Size = new System.Drawing.Size(982, 308); - this.splitContainer2.SplitterDistance = 384; - this.splitContainer2.SplitterWidth = 5; - this.splitContainer2.TabIndex = 7; - // - // CollectDataPerLabel - // - this.CollectDataPerLabel.AutoSize = true; - this.CollectDataPerLabel.Location = new System.Drawing.Point(9, 164); - this.CollectDataPerLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); - this.CollectDataPerLabel.Name = "CollectDataPerLabel"; - this.CollectDataPerLabel.Size = new System.Drawing.Size(139, 23); - this.CollectDataPerLabel.TabIndex = 20; - this.CollectDataPerLabel.Text = "Collect data per"; - // - // MinutesLabel1 - // - this.MinutesLabel1.AutoSize = true; - this.MinutesLabel1.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.MinutesLabel1.Location = new System.Drawing.Point(286, 165); - this.MinutesLabel1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); - this.MinutesLabel1.Name = "MinutesLabel1"; - this.MinutesLabel1.Size = new System.Drawing.Size(75, 23); - this.MinutesLabel1.TabIndex = 19; - this.MinutesLabel1.Text = "minutes"; - // - // dataCollectionMinutesNum - // - this.dataCollectionMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "DataCollectionMinutes", true)); - this.dataCollectionMinutesNum.Increment = new decimal(new int[] { - 10, - 0, - 0, - 0}); - this.dataCollectionMinutesNum.Location = new System.Drawing.Point(177, 162); - this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { - 120, - 0, - 0, - 0}); - this.dataCollectionMinutesNum.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.dataCollectionMinutesNum.Name = "dataCollectionMinutesNum"; - this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 29); - this.dataCollectionMinutesNum.TabIndex = 18; - this.dataCollectionMinutesNum.Value = new decimal(new int[] { - 10, - 0, - 0, - 0}); - // - // StatisticsEnabledCheckBox - // - this.StatisticsEnabledCheckBox.AutoSize = true; - this.StatisticsEnabledCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "StatisticsEnabled", true)); - this.StatisticsEnabledCheckBox.Location = new System.Drawing.Point(13, 12); - this.StatisticsEnabledCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.StatisticsEnabledCheckBox.Name = "StatisticsEnabledCheckBox"; - this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(163, 27); - this.StatisticsEnabledCheckBox.TabIndex = 17; - this.StatisticsEnabledCheckBox.Text = "Enable Statistics"; - this.StatisticsEnabledCheckBox.UseVisualStyleBackColor = true; - // - // choiceKeptMinutesNum - // - this.choiceKeptMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "ChoiceKeptMinutes", true)); - this.choiceKeptMinutesNum.Increment = new decimal(new int[] { - 10, - 0, - 0, - 0}); - this.choiceKeptMinutesNum.Location = new System.Drawing.Point(177, 204); - this.choiceKeptMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.choiceKeptMinutesNum.Maximum = new decimal(new int[] { - 120, - 0, - 0, - 0}); - this.choiceKeptMinutesNum.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.choiceKeptMinutesNum.Name = "choiceKeptMinutesNum"; - this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 29); - this.choiceKeptMinutesNum.TabIndex = 16; - this.choiceKeptMinutesNum.Value = new decimal(new int[] { - 10, - 0, - 0, - 0}); - // - // byHourOfDayCheckBox - // - this.byHourOfDayCheckBox.AutoSize = true; - this.byHourOfDayCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByHourOfDay", true)); - this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 127); - this.byHourOfDayCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.byHourOfDayCheckBox.Name = "byHourOfDayCheckBox"; - this.byHourOfDayCheckBox.Size = new System.Drawing.Size(150, 27); - this.byHourOfDayCheckBox.TabIndex = 15; - this.byHourOfDayCheckBox.Text = "By hour of day"; - this.byHourOfDayCheckBox.UseVisualStyleBackColor = true; - // - // repeatTimesNum - // - this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); - this.repeatTimesNum.Location = new System.Drawing.Point(34, 84); - this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); - this.repeatTimesNum.Maximum = new decimal(new int[] { - 10, - 0, - 0, - 0}); - this.repeatTimesNum.Name = "repeatTimesNum"; - this.repeatTimesNum.Size = new System.Drawing.Size(99, 29); - this.repeatTimesNum.TabIndex = 14; - this.repeatTimesNum.Value = new decimal(new int[] { - 4, - 0, - 0, - 0}); - // - // PackagePerPingLabel - // - this.PackagePerPingLabel.AutoSize = true; - this.PackagePerPingLabel.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.PackagePerPingLabel.Location = new System.Drawing.Point(139, 86); - this.PackagePerPingLabel.Name = "PackagePerPingLabel"; - this.PackagePerPingLabel.Size = new System.Drawing.Size(172, 23); - this.PackagePerPingLabel.TabIndex = 13; - this.PackagePerPingLabel.Text = "packages everytime"; - // - // splitContainer3 - // - this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; - this.splitContainer3.IsSplitterFixed = true; - this.splitContainer3.Location = new System.Drawing.Point(0, 0); - this.splitContainer3.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.splitContainer3.Name = "splitContainer3"; - this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer3.Panel1 - // - this.splitContainer3.Panel1.Controls.Add(this.FinalScoreLabel); - // - // splitContainer3.Panel2 - // - this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); - this.splitContainer3.Size = new System.Drawing.Size(593, 308); - this.splitContainer3.SplitterDistance = 42; - this.splitContainer3.SplitterWidth = 1; - this.splitContainer3.TabIndex = 6; - // - // FinalScoreLabel - // - this.FinalScoreLabel.AutoSize = true; - this.FinalScoreLabel.Location = new System.Drawing.Point(5, 9); - this.FinalScoreLabel.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); - this.FinalScoreLabel.Name = "FinalScoreLabel"; - this.FinalScoreLabel.Size = new System.Drawing.Size(103, 23); - this.FinalScoreLabel.TabIndex = 0; - this.FinalScoreLabel.Text = "Final Score:"; - this.CalculatinTip.SetToolTip(this.FinalScoreLabel, "(The server with the highest score would be choosen)"); - // - // calculationContainer - // - this.calculationContainer.AutoScroll = true; - this.calculationContainer.Dock = System.Windows.Forms.DockStyle.Fill; - this.calculationContainer.Location = new System.Drawing.Point(0, 0); - this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.calculationContainer.Name = "calculationContainer"; - this.calculationContainer.Size = new System.Drawing.Size(593, 265); - this.calculationContainer.TabIndex = 1; - // - // serverSelector - // - this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.serverSelector.FormattingEnabled = true; - this.serverSelector.Location = new System.Drawing.Point(733, 145); - this.serverSelector.Name = "serverSelector"; - this.serverSelector.Size = new System.Drawing.Size(233, 31); - this.serverSelector.TabIndex = 6; - this.serverSelector.SelectionChangeCommitted += new System.EventHandler(this.serverSelector_SelectionChangeCommitted); - // - // CancelButton - // - this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelButton.Location = new System.Drawing.Point(865, 364); - this.CancelButton.Name = "CancelButton"; - this.CancelButton.Size = new System.Drawing.Size(101, 41); - this.CancelButton.TabIndex = 5; - this.CancelButton.Text = "Cancel"; - this.CancelButton.UseVisualStyleBackColor = true; - this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); - // - // OKButton - // - this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OKButton.Location = new System.Drawing.Point(758, 364); - this.OKButton.Name = "OKButton"; - this.OKButton.Size = new System.Drawing.Size(101, 41); - this.OKButton.TabIndex = 4; - this.OKButton.Text = "OK"; - this.OKButton.UseVisualStyleBackColor = true; - this.OKButton.Click += new System.EventHandler(this.OKButton_Click); - // - // bindingConfiguration - // - this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); - // - // StatisticsStrategyConfigurationForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 23F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.AutoSize = true; - this.ClientSize = new System.Drawing.Size(982, 753); - this.Controls.Add(this.splitContainer1); - this.Font = new System.Drawing.Font("微软雅黑", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); - this.MinimumSize = new System.Drawing.Size(1000, 800); - this.Name = "StatisticsStrategyConfigurationForm"; - this.Text = "StatisticsStrategyConfigurationForm"; - this.Text = "Statistics configuration"; - ((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); - this.chartModeSelector.ResumeLayout(false); - this.chartModeSelector.PerformLayout(); - this.splitContainer1.Panel1.ResumeLayout(false); - this.splitContainer1.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); - this.splitContainer1.ResumeLayout(false); - this.splitContainer2.Panel1.ResumeLayout(false); - this.splitContainer2.Panel1.PerformLayout(); - this.splitContainer2.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); - this.splitContainer2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).EndInit(); - this.splitContainer3.Panel1.ResumeLayout(false); - this.splitContainer3.Panel1.PerformLayout(); - this.splitContainer3.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); - this.splitContainer3.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); - this.ResumeLayout(false); - - } - - #endregion - private System.Windows.Forms.DataVisualization.Charting.Chart StatisticsChart; - private System.Windows.Forms.CheckBox PingCheckBox; - private System.Windows.Forms.Label KeepChoiceForLabel; - private System.Windows.Forms.Label MinutesLabel2; - private System.Windows.Forms.GroupBox chartModeSelector; - private System.Windows.Forms.RadioButton allMode; - private System.Windows.Forms.RadioButton dayMode; - private System.Windows.Forms.SplitContainer splitContainer1; - private System.Windows.Forms.Label FinalScoreLabel; - private System.Windows.Forms.SplitContainer splitContainer2; - private System.Windows.Forms.FlowLayoutPanel calculationContainer; - private System.Windows.Forms.SplitContainer splitContainer3; - private System.Windows.Forms.NumericUpDown repeatTimesNum; - private System.Windows.Forms.Label PackagePerPingLabel; - private System.Windows.Forms.CheckBox byHourOfDayCheckBox; - private System.Windows.Forms.NumericUpDown choiceKeptMinutesNum; - private System.Windows.Forms.CheckBox StatisticsEnabledCheckBox; - private System.Windows.Forms.Label CollectDataPerLabel; - private System.Windows.Forms.Label MinutesLabel1; - private System.Windows.Forms.NumericUpDown dataCollectionMinutesNum; - private System.Windows.Forms.BindingSource bindingConfiguration; - private new System.Windows.Forms.Button CancelButton; - private System.Windows.Forms.Button OKButton; - private System.Windows.Forms.ComboBox serverSelector; - private System.Windows.Forms.ToolTip CalculatinTip; - } -} \ No newline at end of file diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs deleted file mode 100644 index dac87c3b..00000000 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Drawing; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using System.Windows.Forms; -using System.Windows.Forms.DataVisualization.Charting; -using Shadowsocks.Controller; -using Shadowsocks.Model; -using Shadowsocks.Properties; - -namespace Shadowsocks.View -{ - public partial class StatisticsStrategyConfigurationForm : Form - { - private readonly ShadowsocksController _controller; - private StatisticsStrategyConfiguration _configuration; - private readonly DataTable _dataTable = new DataTable(); - private List _servers; - private readonly Series _speedSeries; - private readonly Series _packageLossSeries; - private readonly Series _pingSeries; - - public StatisticsStrategyConfigurationForm(ShadowsocksController controller) - { - if (controller == null) return; - InitializeComponent(); - Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); - _speedSeries = StatisticsChart.Series["Speed"]; - _packageLossSeries = StatisticsChart.Series["Package Loss"]; - _pingSeries = StatisticsChart.Series["Ping"]; - _controller = controller; - _controller.ConfigChanged += (sender, args) => LoadConfiguration(); - UpdateTexts(); - LoadConfiguration(); - Load += (sender, args) => InitData(); - } - - private void UpdateTexts() - { - I18N.TranslateForm(this); - - foreach (var item in StatisticsChart.Series) - { - item.Name = I18N.GetString(item.Name); - } - - } - private void LoadConfiguration() - { - var configs = _controller.GetCurrentConfiguration().configs; - _servers = configs.Select(server => server.Identifier()).ToList(); - _configuration = _controller.StatisticsConfiguration - ?? new StatisticsStrategyConfiguration(); - if (_configuration.Calculations == null) - { - _configuration = new StatisticsStrategyConfiguration(); - } - } - - private void InitData() - { - bindingConfiguration.Add(_configuration); - foreach (var kv in _configuration.Calculations) - { - var calculation = new CalculationControl(I18N.GetString(kv.Key), kv.Value); - calculationContainer.Controls.Add(calculation); - } - - serverSelector.DataSource = _servers; - - _dataTable.Columns.Add("Timestamp", typeof(DateTime)); - _dataTable.Columns.Add("Speed", typeof(int)); - _speedSeries.XValueMember = "Timestamp"; - _speedSeries.YValueMembers = "Speed"; - - // might be empty - _dataTable.Columns.Add("Package Loss", typeof(int)); - _dataTable.Columns.Add("Ping", typeof(int)); - _packageLossSeries.XValueMember = "Timestamp"; - _packageLossSeries.YValueMembers = "Package Loss"; - _pingSeries.XValueMember = "Timestamp"; - _pingSeries.YValueMembers = "Ping"; - - StatisticsChart.DataSource = _dataTable; - LoadChartData(); - StatisticsChart.DataBind(); - } - - private void CancelButton_Click(object sender, EventArgs e) - { - Close(); - } - - private void OKButton_Click(object sender, EventArgs e) - { - foreach (CalculationControl calculation in calculationContainer.Controls) - { - _configuration.Calculations[calculation.Value] = calculation.Factor; - } - _controller?.SaveStrategyConfigurations(_configuration); - _controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked); - Close(); - } - - private void LoadChartData() - { - var serverName = _servers[serverSelector.SelectedIndex]; - _dataTable.Rows.Clear(); - - //return directly when no data is usable - if (_controller.availabilityStatistics?.FilteredStatistics == null) return; - List statistics; - if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return; - IEnumerable> dataGroups; - if (allMode.Checked) - { - _pingSeries.XValueType = ChartValueType.DateTime; - _packageLossSeries.XValueType = ChartValueType.DateTime; - _speedSeries.XValueType = ChartValueType.DateTime; - dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear); - StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "g"; - StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "g"; - } - else - { - _pingSeries.XValueType = ChartValueType.Time; - _packageLossSeries.XValueType = ChartValueType.Time; - _speedSeries.XValueType = ChartValueType.Time; - dataGroups = statistics.GroupBy(data => data.Timestamp.Hour); - StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "HH:00"; - StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "HH:00"; - } - var finalData = from dataGroup in dataGroups - orderby dataGroup.Key - select new - { - dataGroup.First().Timestamp, - Speed = dataGroup.Max(data => data.MaxInboundSpeed) ?? 0, - Ping = (int)(dataGroup.Average(data => data.AverageResponse) ?? 0), - PackageLossPercentage = (int)(dataGroup.Average(data => data.PackageLoss) ?? 0) * 100 - }; - foreach (var data in finalData.Where(data => data.Speed != 0 || data.PackageLossPercentage != 0 || data.Ping != 0)) - { - _dataTable.Rows.Add(data.Timestamp, data.Speed, data.PackageLossPercentage, data.Ping); - } - StatisticsChart.DataBind(); - } - - private void serverSelector_SelectionChangeCommitted(object sender, EventArgs e) - { - LoadChartData(); - } - - private void dayMode_CheckedChanged(object sender, EventArgs e) - { - LoadChartData(); - } - - private void allMode_CheckedChanged(object sender, EventArgs e) - { - LoadChartData(); - } - - private void PingCheckBox_CheckedChanged(object sender, EventArgs e) - { - repeatTimesNum.ReadOnly = !PingCheckBox.Checked; - } - } -} diff --git a/shadowsocks-csharp/ViewModels/ForwardProxyViewModel.cs b/shadowsocks-csharp/ViewModels/ForwardProxyViewModel.cs new file mode 100644 index 00000000..f901cac8 --- /dev/null +++ b/shadowsocks-csharp/ViewModels/ForwardProxyViewModel.cs @@ -0,0 +1,131 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using ReactiveUI.Validation.Extensions; +using ReactiveUI.Validation.Helpers; +using Shadowsocks.Controller; +using Shadowsocks.Model; +using Shadowsocks.View; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; + +namespace Shadowsocks.ViewModels +{ + public class ForwardProxyViewModel : ReactiveValidationObject + { + public ForwardProxyViewModel() + { + _config = Program.MainController.GetCurrentConfiguration(); + _controller = Program.MainController; + _menuViewController = Program.MenuController; + + if (!_config.proxy.useProxy) + NoProxy = true; + else if (_config.proxy.proxyType == 0) + UseSocks5Proxy = true; + else + UseHttpProxy = true; + + Address = _config.proxy.proxyServer; + Port = _config.proxy.proxyPort; + Timeout = _config.proxy.proxyTimeout; + + Username = _config.proxy.authUser; + Password = _config.proxy.authPwd; + + this.WhenAnyValue(x => x.NoProxy, x => !x) + .ToPropertyEx(this, x => x.CanModifyDetails); + + AddressRule = this.ValidationRule( + viewModel => viewModel.Address, + address => !string.IsNullOrWhiteSpace(address), + "Address can't be empty or whitespaces."); + PortRule = this.ValidationRule( + viewModel => viewModel.Port, + port => port > 0 && port <= 65535, + port => $"{port} is out of range (0, 65535]."); + TimeoutRule = this.ValidationRule( + viewModel => viewModel.Timeout, + timeout => timeout > 0 && timeout <= 10, + timeout => $"{timeout} is out of range (0, 10]."); + + var authValid = this + .WhenAnyValue(x => x.Username, x => x.Password, (username, password) => new { Username = username, Password = password }) + .Select(x => string.IsNullOrWhiteSpace(x.Username) == string.IsNullOrWhiteSpace(x.Password)); + AuthRule = this.ValidationRule(authValid, "You must provide both username and password."); + + var canSave = this.IsValid(); + + Save = ReactiveCommand.Create(() => + { + _controller.SaveProxy(GetForwardProxyConfig()); + _menuViewController.CloseForwardProxyWindow(); + }, canSave); + Cancel = ReactiveCommand.Create(_menuViewController.CloseForwardProxyWindow); + } + + private readonly Configuration _config; + private readonly ShadowsocksController _controller; + private readonly MenuViewController _menuViewController; + + public ValidationHelper AddressRule { get; } + public ValidationHelper PortRule { get; } + public ValidationHelper TimeoutRule { get; } + public ValidationHelper AuthRule { get; } + + public ReactiveCommand Save { get; } + public ReactiveCommand Cancel { get; } + + [ObservableAsProperty] + public bool CanModifyDetails { get; } + + [Reactive] + public bool NoProxy { get; set; } + + [Reactive] + public bool UseSocks5Proxy { get; set; } + + [Reactive] + public bool UseHttpProxy { get; set; } + + [Reactive] + public string Address { get; set; } + + [Reactive] + public int Port { get; set; } + + [Reactive] + public int Timeout { get; set; } + + [Reactive] + public string Username { get; set; } + + [Reactive] + public string Password { get; set; } + + private ForwardProxyConfig GetForwardProxyConfig() + { + var forwardProxyConfig = new ForwardProxyConfig() + { + proxyServer = Address, + proxyPort = Port, + proxyTimeout = Timeout, + authUser = Username, + authPwd = Password + }; + if (NoProxy) + forwardProxyConfig.useProxy = false; + else if (UseSocks5Proxy) + { + forwardProxyConfig.useProxy = true; + forwardProxyConfig.proxyType = 0; + } + else + { + forwardProxyConfig.useProxy = true; + forwardProxyConfig.proxyType = 1; + } + return forwardProxyConfig; + } + } +} diff --git a/shadowsocks-csharp/ViewModels/HotkeysViewModel.cs b/shadowsocks-csharp/ViewModels/HotkeysViewModel.cs new file mode 100644 index 00000000..141d2cf9 --- /dev/null +++ b/shadowsocks-csharp/ViewModels/HotkeysViewModel.cs @@ -0,0 +1,189 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Shadowsocks.Controller; +using Shadowsocks.Model; +using Shadowsocks.View; +using System.Reactive; +using System.Text; +using System.Windows.Input; + +namespace Shadowsocks.ViewModels +{ + public class HotkeysViewModel : ReactiveObject + { + public HotkeysViewModel() + { + _config = Program.MainController.GetCurrentConfiguration(); + _controller = Program.MainController; + _menuViewController = Program.MenuController; + + HotkeySystemProxy = _config.hotkey.SwitchSystemProxy; + HotkeyProxyMode = _config.hotkey.SwitchSystemProxyMode; + HotkeyAllowLan = _config.hotkey.SwitchAllowLan; + HotkeyOpenLogs = _config.hotkey.ShowLogs; + HotkeySwitchPrev = _config.hotkey.ServerMoveUp; + HotkeySwitchNext = _config.hotkey.ServerMoveDown; + RegisterAtStartup = _config.hotkey.RegHotkeysAtStartup; + + HotkeySystemProxyStatus = "✔"; + HotkeyProxyModeStatus = "✔"; + HotkeyAllowLanStatus = "✔"; + HotkeyOpenLogsStatus = "✔"; + HotkeySwitchPrevStatus = "✔"; + HotkeySwitchNextStatus = "✔"; + + RegisterAll = ReactiveCommand.Create(() => RegisterAllAndUpdateStatus()); + Save = ReactiveCommand.Create(() => RegisterAllAndUpdateStatus(true)); + Cancel = ReactiveCommand.Create(_menuViewController.CloseHotkeysWindow); + } + + private readonly Configuration _config; + private readonly ShadowsocksController _controller; + private readonly MenuViewController _menuViewController; + + public ReactiveCommand RegisterAll { get; } + public ReactiveCommand Save { get; } + public ReactiveCommand Cancel { get; } + + [Reactive] + public string HotkeySystemProxy { get; set; } + + [Reactive] + public string HotkeyProxyMode { get; set; } + + [Reactive] + public string HotkeyAllowLan { get; set; } + + [Reactive] + public string HotkeyOpenLogs { get; set; } + + [Reactive] + public string HotkeySwitchPrev { get; set; } + + [Reactive] + public string HotkeySwitchNext { get; set; } + + [Reactive] + public bool RegisterAtStartup { get; set; } + + [Reactive] + public string HotkeySystemProxyStatus { get; set; } + + [Reactive] + public string HotkeyProxyModeStatus { get; set; } + + [Reactive] + public string HotkeyAllowLanStatus { get; set; } + + [Reactive] + public string HotkeyOpenLogsStatus { get; set; } + + [Reactive] + public string HotkeySwitchPrevStatus { get; set; } + + [Reactive] + public string HotkeySwitchNextStatus { get; set; } + + public void RecordKeyDown(int hotkeyIndex, KeyEventArgs keyEventArgs) + { + var recordedKeyStringBuilder = new StringBuilder(); + + // record modifiers + if ((Keyboard.Modifiers & ModifierKeys.Control) > 0) + recordedKeyStringBuilder.Append("Ctrl+"); + if ((Keyboard.Modifiers & ModifierKeys.Alt) > 0) + recordedKeyStringBuilder.Append("Alt+"); + if ((Keyboard.Modifiers & ModifierKeys.Shift) > 0) + recordedKeyStringBuilder.Append("Shift+"); + + // record other keys when at least one modifier is pressed + if (recordedKeyStringBuilder.Length > 0 && (keyEventArgs.Key < Key.LeftShift || keyEventArgs.Key > Key.RightAlt)) + recordedKeyStringBuilder.Append(keyEventArgs.Key); + + switch (hotkeyIndex) + { + case 0: + HotkeySystemProxy = recordedKeyStringBuilder.ToString(); + break; + case 1: + HotkeyProxyMode = recordedKeyStringBuilder.ToString(); + break; + case 2: + HotkeyAllowLan = recordedKeyStringBuilder.ToString(); + break; + case 3: + HotkeyOpenLogs = recordedKeyStringBuilder.ToString(); + break; + case 4: + HotkeySwitchPrev = recordedKeyStringBuilder.ToString(); + break; + case 5: + HotkeySwitchNext = recordedKeyStringBuilder.ToString(); + break; + } + } + + public void FinishOnKeyUp(int hotkeyIndex, KeyEventArgs keyEventArgs) + { + switch (hotkeyIndex) + { + case 0: + if (HotkeySystemProxy.EndsWith("+")) + HotkeySystemProxy = ""; + break; + case 1: + if (HotkeyProxyMode.EndsWith("+")) + HotkeyProxyMode = ""; + break; + case 2: + if (HotkeyAllowLan.EndsWith("+")) + HotkeyAllowLan = ""; + break; + case 3: + if (HotkeyOpenLogs.EndsWith("+")) + HotkeyOpenLogs = ""; + break; + case 4: + if (HotkeySwitchPrev.EndsWith("+")) + HotkeySwitchPrev = ""; + break; + case 5: + if (HotkeySwitchNext.EndsWith("+")) + HotkeySwitchNext = ""; + break; + } + } + + private void RegisterAllAndUpdateStatus(bool save = false) + { + HotkeySystemProxyStatus = HotkeyReg.RegHotkeyFromString(HotkeySystemProxy, "SwitchSystemProxyCallback") ? "✔" : "❌"; + HotkeyProxyModeStatus = HotkeyReg.RegHotkeyFromString(HotkeyProxyMode, "SwitchSystemProxyModeCallback") ? "✔" : "❌"; + HotkeyAllowLanStatus = HotkeyReg.RegHotkeyFromString(HotkeyAllowLan, "SwitchAllowLanCallback") ? "✔" : "❌"; + HotkeyOpenLogsStatus = HotkeyReg.RegHotkeyFromString(HotkeyOpenLogs, "ShowLogsCallback") ? "✔" : "❌"; + HotkeySwitchPrevStatus = HotkeyReg.RegHotkeyFromString(HotkeySwitchPrev, "ServerMoveUpCallback") ? "✔" : "❌"; + HotkeySwitchNextStatus = HotkeyReg.RegHotkeyFromString(HotkeySwitchNext, "ServerMoveDownCallback") ? "✔" : "❌"; + + if (HotkeySystemProxyStatus == "✔" && + HotkeyProxyModeStatus == "✔" && + HotkeyAllowLanStatus == "✔" && + HotkeyOpenLogsStatus == "✔" && + HotkeySwitchPrevStatus == "✔" && + HotkeySwitchNextStatus == "✔" && save) + { + _controller.SaveHotkeyConfig(GetHotkeyConfig); + _menuViewController.CloseHotkeysWindow(); + } + } + + private HotkeyConfig GetHotkeyConfig => new HotkeyConfig() + { + SwitchSystemProxy = HotkeySystemProxy, + SwitchSystemProxyMode = HotkeyProxyMode, + SwitchAllowLan = HotkeyAllowLan, + ShowLogs = HotkeyOpenLogs, + ServerMoveUp = HotkeySwitchPrev, + ServerMoveDown = HotkeySwitchNext, + RegHotkeysAtStartup = RegisterAtStartup + }; + } +} diff --git a/shadowsocks-csharp/ViewModels/OnlineConfigViewModel.cs b/shadowsocks-csharp/ViewModels/OnlineConfigViewModel.cs new file mode 100644 index 00000000..f803b351 --- /dev/null +++ b/shadowsocks-csharp/ViewModels/OnlineConfigViewModel.cs @@ -0,0 +1,115 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using ReactiveUI.Validation.Extensions; +using ReactiveUI.Validation.Helpers; +using Shadowsocks.Controller; +using Shadowsocks.Localization; +using Shadowsocks.Model; +using Shadowsocks.View; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive; +using System.Reactive.Linq; +using System.Text; +using System.Windows; + +namespace Shadowsocks.ViewModels +{ + public class OnlineConfigViewModel : ReactiveValidationObject + { + public OnlineConfigViewModel() + { + _config = Program.MainController.GetCurrentConfiguration(); + _controller = Program.MainController; + _menuViewController = Program.MenuController; + + Sources = new ObservableCollection(_config.onlineConfigSource); + SelectedSource = ""; + Address = ""; + + // TODO in v5: if http:// show warning as materialDesign:HintAssist.HelperText + AddressRule = this.ValidationRule( + viewModel => viewModel.Address, + address => address.StartsWith("http://"), + "Warning: getting online configuration from plain HTTP sources is NOT secure!"); + + var canUpdateCopyRemove = this.WhenAnyValue( + x => x.SelectedSource, + selectedSource => !string.IsNullOrWhiteSpace(selectedSource)); + var canUpdateAll = this.WhenAnyValue( + x => x.Sources.Count, + count => count > 0); + var canAdd = this.WhenAnyValue( + x => x.Address, + address => Uri.IsWellFormedUriString(address, UriKind.Absolute) && + (address.StartsWith("https://") || address.StartsWith("http://"))); + + Update = ReactiveCommand.CreateFromTask(() => _controller.UpdateOnlineConfig(SelectedSource), canUpdateCopyRemove); + UpdateAll = ReactiveCommand.CreateFromTask(_controller.UpdateAllOnlineConfig, canUpdateAll); + CopyLink = ReactiveCommand.Create(() => Clipboard.SetText(SelectedSource), canUpdateCopyRemove); + Remove = ReactiveCommand.Create(() => + { + bool result; + var urlToRemove = SelectedSource; // save it here because SelectedSource is lost once we remove the selection + do + { + result = Sources.Remove(urlToRemove); + } while (result); + _controller.RemoveOnlineConfig(urlToRemove); + }, canUpdateCopyRemove); + Add = ReactiveCommand.Create(() => + { + Sources.Add(Address); + SelectedSource = Address; + _controller.SaveOnlineConfigSource(Sources.ToList()); + Address = ""; + }, canAdd); + + // TODO in v5: use MaterialDesignThemes snackbar messages + this.WhenAnyObservable(x => x.Update) + .Subscribe(x => + { + if (x) + MessageBox.Show(LocalizationProvider.GetLocalizedValue("sip008UpdateSuccess")); + else + MessageBox.Show(LocalizationProvider.GetLocalizedValue("sip008UpdateFailure")); + }); + this.WhenAnyObservable(x => x.UpdateAll) + .Subscribe(x => + { + if (x.Count == 0) + MessageBox.Show(LocalizationProvider.GetLocalizedValue("sip008UpdateAllSuccess")); + else + { + var stringBuilder = new StringBuilder(LocalizationProvider.GetLocalizedValue("sip008UpdateAllFailure")); + foreach (var url in x) + stringBuilder.AppendLine(url); + MessageBox.Show(stringBuilder.ToString()); + } + }); + } + + private readonly Configuration _config; + private readonly ShadowsocksController _controller; + private readonly MenuViewController _menuViewController; + + public ValidationHelper AddressRule { get; } + + public ReactiveCommand Update { get; } + public ReactiveCommand> UpdateAll { get; } + public ReactiveCommand CopyLink { get; } + public ReactiveCommand Remove { get; } + public ReactiveCommand Add { get; } + + [Reactive] + public ObservableCollection Sources { get; private set; } + + [Reactive] + public string SelectedSource { get; set; } + + [Reactive] + public string Address { get; set; } + } +} diff --git a/shadowsocks-csharp/ViewModels/ServerSharingViewModel.cs b/shadowsocks-csharp/ViewModels/ServerSharingViewModel.cs new file mode 100644 index 00000000..f1375690 --- /dev/null +++ b/shadowsocks-csharp/ViewModels/ServerSharingViewModel.cs @@ -0,0 +1,96 @@ +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Reactive; +using System.Windows; +using System.Windows.Media.Imaging; + +namespace Shadowsocks.ViewModels +{ + public class ServerSharingViewModel : ReactiveObject + { + /// + /// The view model class for the server sharing user control. + /// + public ServerSharingViewModel() + { + _config = Program.MainController.GetCurrentConfiguration(); + Servers = _config.configs; + SelectedServer = Servers[0]; + + this.WhenAnyValue(x => x.SelectedServer) + .Subscribe(_ => UpdateUrlAndImage()); + + CopyLink = ReactiveCommand.Create(() => Clipboard.SetText(SelectedServerUrl)); + } + + private readonly Configuration _config; + + public ReactiveCommand CopyLink { get; } + + [Reactive] + public List Servers { get; private set; } + + [Reactive] + public Server SelectedServer { get; set; } + + [Reactive] + public string SelectedServerUrl { get; private set; } + + [Reactive] + public BitmapImage SelectedServerUrlImage { get; private set; } + + /// + /// Called when SelectedServer changed + /// to update SelectedServerUrl and SelectedServerUrlImage + /// + private void UpdateUrlAndImage() + { + // update SelectedServerUrl + SelectedServerUrl = SelectedServer.GetURL(_config.generateLegacyUrl); + + // generate QR code + var qrCode = ZXing.QrCode.Internal.Encoder.encode(SelectedServerUrl, ZXing.QrCode.Internal.ErrorCorrectionLevel.L); + var byteMatrix = qrCode.Matrix; + + // paint bitmap + int blockSize = Math.Max(1024 / byteMatrix.Height, 1); + Bitmap drawArea = new Bitmap((byteMatrix.Width * blockSize), (byteMatrix.Height * blockSize)); + using (var graphics = Graphics.FromImage(drawArea)) + { + graphics.Clear(Color.White); + using (var solidBrush = new SolidBrush(Color.Black)) + { + for (int row = 0; row < byteMatrix.Width; row++) + { + for (int column = 0; column < byteMatrix.Height; column++) + { + if (byteMatrix[row, column] != 0) + { + graphics.FillRectangle(solidBrush, blockSize * row, blockSize * column, blockSize, blockSize); + } + } + } + } + } + + // transform to BitmapImage for binding + BitmapImage bitmapImage = new BitmapImage(); + using (MemoryStream memoryStream = new MemoryStream()) + { + drawArea.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp); + memoryStream.Position = 0; + bitmapImage.BeginInit(); + bitmapImage.StreamSource = memoryStream; + bitmapImage.CacheOption = BitmapCacheOption.OnLoad; + bitmapImage.EndInit(); + } + SelectedServerUrlImage = bitmapImage; + } + } +} diff --git a/shadowsocks-csharp/ViewModels/VersionUpdatePromptViewModel.cs b/shadowsocks-csharp/ViewModels/VersionUpdatePromptViewModel.cs new file mode 100644 index 00000000..0ae9f2d1 --- /dev/null +++ b/shadowsocks-csharp/ViewModels/VersionUpdatePromptViewModel.cs @@ -0,0 +1,34 @@ +using Newtonsoft.Json.Linq; +using ReactiveUI; +using Shadowsocks.Controller; +using System.Reactive; + +namespace Shadowsocks.ViewModels +{ + public class VersionUpdatePromptViewModel : ReactiveObject + { + public VersionUpdatePromptViewModel(JToken releaseObject) + { + _updateChecker = Program.MenuController.updateChecker; + _releaseObject = releaseObject; + ReleaseNotes = string.Concat( + $"# {((bool)_releaseObject["prerelease"] ? "⚠ Pre-release" : "ℹ Release")} {(string)_releaseObject["tag_name"] ?? "Failed to get tag name"}\r\n", + (string)_releaseObject["body"] ?? "Failed to get release notes"); + + Update = ReactiveCommand.CreateFromTask(_updateChecker.DoUpdate); + SkipVersion = ReactiveCommand.Create(_updateChecker.SkipUpdate); + NotNow = ReactiveCommand.Create(_updateChecker.CloseVersionUpdatePromptWindow); + } + + private readonly UpdateChecker _updateChecker; + private readonly JToken _releaseObject; + + public string ReleaseNotes { get; } + + public ReactiveCommand Update { get; } + + public ReactiveCommand SkipVersion { get; } + + public ReactiveCommand NotNow { get; } + } +} diff --git a/shadowsocks-csharp/Views/ForwardProxyView.xaml b/shadowsocks-csharp/Views/ForwardProxyView.xaml new file mode 100644 index 00000000..0c6a9319 --- /dev/null +++ b/shadowsocks-csharp/Views/ForwardProxyView.xaml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +