From 1199f1fe1fc8cbfad040416af7bf297ca1be7389 Mon Sep 17 00:00:00 2001 From: Kaguya Date: Wed, 9 Dec 2015 20:00:16 +0800 Subject: [PATCH] Complete Online Config --- .../Controller/OnlineConfigController.cs | 239 ++++++++++ .../Controller/Service/RSAHandler.cs | 277 +++++++++++ shadowsocks-csharp/Data/cn.txt | 11 + .../Model/OnlineConfigDataPackage.cs | 25 + shadowsocks-csharp/Model/Server.cs | 5 + shadowsocks-csharp/Program.cs | 5 +- .../View/ConfigForm.Designer.cs | 101 +++- shadowsocks-csharp/View/ConfigForm.cs | 35 +- .../View/ImportFromWebForm.Designer.cs | 181 +++++++ .../View/ImportFromWebForm.resx | 449 ++++++++++++++++++ shadowsocks-csharp/View/MenuViewController.cs | 10 + shadowsocks-csharp/app.config | 8 +- shadowsocks-csharp/packages.config | 4 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 50 +- test/packages.config | 8 + test/test.csproj | 26 + 16 files changed, 1403 insertions(+), 31 deletions(-) create mode 100644 shadowsocks-csharp/Controller/OnlineConfigController.cs create mode 100644 shadowsocks-csharp/Controller/Service/RSAHandler.cs create mode 100644 shadowsocks-csharp/Model/OnlineConfigDataPackage.cs create mode 100644 shadowsocks-csharp/View/ImportFromWebForm.Designer.cs create mode 100644 shadowsocks-csharp/View/ImportFromWebForm.resx create mode 100644 test/packages.config diff --git a/shadowsocks-csharp/Controller/OnlineConfigController.cs b/shadowsocks-csharp/Controller/OnlineConfigController.cs new file mode 100644 index 0000000..2b18500 --- /dev/null +++ b/shadowsocks-csharp/Controller/OnlineConfigController.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.IO; +using System.IO.Compression; +using Shadowsocks.Controller.Service; +using Shadowsocks.Model; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Converters; + + +namespace Shadowsocks.Controller +{ + class OnlineConfigController + { + private string _cookie = null; + public string Url = ""; + public string Type = "unknown"; + public string LocalPublickey = ""; + public string LocalPrivateKey = ""; + public string RemotePubkey = ""; + private KeyExchangeResponse KeyExchangeRes = null; + //public RSAHandler _rsa; + + public OnlineConfigController(string Url) + { + this.Url = Url; + string[] Prefix = Url.Split(new char[3] { ':', '/', '/' }); + switch (Prefix[0].ToLower()) + { + case "http": + Type = "http"; + break; + case "https": + Type = "https"; + break; + default: + this.Url = ""; + this.Type = "unknown"; + break; + } + RSAHandler.GenerateKeyPair(); + this.LocalPrivateKey = System.Environment.CurrentDirectory + "\\keys\\local.private.pem"; + this.LocalPublickey = System.Environment.CurrentDirectory + "\\keys\\local.public.pem"; + } + public Server[] GetOnlineConfig() + { + try + { + KeyExchangeResponse KeyExchangeResponse = this.KeyExchange(this.Url); + GetConfigResponse ConfigResponse = this.GetConfig(); + return ConfigResponse.Config; + } + catch + { + throw; + } + + } + private KeyExchangeResponse KeyExchange(string Url) + { + if(this.Url=="" || this.Type == "unknown") + { + throw new ArgumentException("Invalid Url"); + } + StreamReader LocalPublicKeyStream = new StreamReader(this.LocalPublickey); + string LocalPublicKey = LocalPublicKeyStream.ReadToEnd(); + LocalPublicKeyStream.Dispose(); + HttpWebResponse res = CreatePostHttpResponse(this.Url, LocalPublicKey, _cookie); + if (res.StatusCode != HttpStatusCode.OK) + { + throw new ArgumentException("Invalid Status Code"); + } + + //string[] x = res.Headers.AllKeys; + + string ResponseContent = ""; + using (StreamReader sr = new StreamReader(res.GetResponseStream())) + { + ResponseContent = sr.ReadToEnd(); + } + this._cookie = res.Headers.Get("Set-Cookie"); + try + { + ResponseContent = RSAHandler.DecryptRSA(ResponseContent, this.LocalPrivateKey); + } + catch + { + throw; + } + StringReader reader = new StringReader(ResponseContent); + JsonSerializer serializer = new JsonSerializer(); + KeyExchangeResponse response = (KeyExchangeResponse)serializer.Deserialize(new JsonTextReader(reader), typeof(KeyExchangeResponse)); + this.KeyExchangeRes = response; + if(File.Exists(System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem")) + { + StreamReader stream = new StreamReader(System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem"); + string LocalVersion = stream.ReadToEnd(); + + if (LocalVersion != response.ServerPublicKey) + { + throw new ArgumentException("Server Public Key Mismatch"); + } + + } + else + { + FileStream LocalVersion = File.Create(System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem"); + LocalVersion.Write(System.Text.Encoding.Default.GetBytes(response.ServerPublicKey), 0, response.ServerPublicKey.Length); + LocalVersion.Close(); + LocalVersion.Dispose(); + } + this.RemotePubkey = System.Environment.CurrentDirectory + "\\keys\\" + response.FingerPrint + ".pem"; + return response; + } + + private GetConfigResponse GetConfig() + { + GetConfigRequest Request = new GetConfigRequest(); + Request.decrypted_string = this.KeyExchangeRes.VerifyString; + char[] tempChr = new char[32]; + Random rand = new Random(); + for(int i = 0; i < 32; i++) + { + tempChr[i] = (char)rand.Next(61, 122); + } + Request.verify_string = new string(tempChr); + string RequestContent = JsonConvert.SerializeObject(Request); + string PostData = RSAHandler.EncryptRSA(RequestContent, this.RemotePubkey); + HttpWebResponse res; + string ResponseContent = ""; + try + { + res = CreatePostHttpResponse(this.Url, PostData, this._cookie); + if (res.StatusCode != HttpStatusCode.OK) + { + throw new ArgumentException("Invalid Status Code"); + } + + using (StreamReader sr = new StreamReader(res.GetResponseStream())) + { + ResponseContent = sr.ReadToEnd(); + } + } + catch + { + throw; + } + ResponseContent = RSAHandler.DecryptRSA(ResponseContent, this.LocalPrivateKey); + StringReader reader = new StringReader(ResponseContent); + JsonSerializer serializer = new JsonSerializer(); + GetConfigResponse response = (GetConfigResponse)serializer.Deserialize(new JsonTextReader(reader), typeof(GetConfigResponse)); + if (response.VerifyString != Request.verify_string) + { + throw new ArgumentException("Server Verify Fail"); + } + for(int i = 0; i < response.Config.Length;i++) + { + response.Config[i].provider = response.Provider; + response.Config[i].fingerprint = this.KeyExchangeRes.FingerPrint; + } + return response; + } + + public static HttpWebResponse CreatePostHttpResponse(string url, string postData,string cookies) + { + Encoding requestEncoding = Encoding.UTF8; + string UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36"; + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException("url"); + } + if (requestEncoding == null) + { + throw new ArgumentNullException("requestEncoding"); + } + HttpWebRequest request = null; + if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) + { + ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); + request = WebRequest.Create(url) as HttpWebRequest; + request.ProtocolVersion = HttpVersion.Version10; + } + else + { + request = WebRequest.Create(url) as HttpWebRequest; + } + + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + + request.UserAgent = UserAgent; + if (cookies != null) + { + request.Headers.Add(HttpRequestHeader.Cookie, cookies); + } + if (postData!="") + { + + byte[] data = requestEncoding.GetBytes(postData); + try + { + using (Stream stream = request.GetRequestStream()) + { + + stream.Write(data, 0, data.Length); + } + + }catch(Exception e) + { + throw new ArgumentException(e.Message); + } + + } + HttpWebResponse res = null; + try + { + res = request.GetResponse() as HttpWebResponse; + } + catch (Exception e) + { + throw new ArgumentException(e.Message); + } + return res; + } + + private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) + { + return true; + } + } +} + diff --git a/shadowsocks-csharp/Controller/Service/RSAHandler.cs b/shadowsocks-csharp/Controller/Service/RSAHandler.cs new file mode 100644 index 0000000..42d180c --- /dev/null +++ b/shadowsocks-csharp/Controller/Service/RSAHandler.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Org.BouncyCastle; +using System.IO; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Shadowsocks.Controller.Service +{ + class RSAHandler + { + public static void GenerateKeyPair() + { + RsaKeyPairGenerator keyGenerator = new RsaKeyPairGenerator(); + RsaKeyGenerationParameters param = new RsaKeyGenerationParameters( + Org.BouncyCastle.Math.BigInteger.ValueOf(3), + new Org.BouncyCastle.Security.SecureRandom(), + 1024, + 25); + keyGenerator.Init(param); + AsymmetricCipherKeyPair keyPair = keyGenerator.GenerateKeyPair(); + AsymmetricKeyParameter publicKey = keyPair.Public; + AsymmetricKeyParameter privateKey = keyPair.Private; + if (!Directory.Exists(System.Environment.CurrentDirectory + "\\keys")) + { + Directory.CreateDirectory(System.Environment.CurrentDirectory + "\\keys"); + } + if (File.Exists(System.Environment.CurrentDirectory + "\\keys\\local.private.pem")) + { + File.Delete(System.Environment.CurrentDirectory + "\\keys\\local.private.pem"); + } + if (File.Exists(System.Environment.CurrentDirectory + "\\keys\\local.public.pem")) + { + File.Delete(System.Environment.CurrentDirectory + "\\keys\\local.public.pem"); + } + FileStream fsPrivate = new FileStream(System.Environment.CurrentDirectory + "\\keys\\local.private.pem", FileMode.Create); + FileStream fsPublic = new FileStream(System.Environment.CurrentDirectory + "\\keys\\local.public.pem", FileMode.Create); + StreamWriter swPrivate = new StreamWriter(fsPrivate); + StreamWriter swPublic = new StreamWriter(fsPublic); + using (TextWriter textWriter = new StringWriter()) + { + var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter); + pemWriter.WriteObject(keyPair.Private); + swPrivate.Write(textWriter.ToString()); + swPrivate.Flush(); + swPrivate.Close(); + fsPrivate.Close(); + } + using (TextWriter textWriter = new StringWriter()) + { + var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(textWriter); + pemWriter.WriteObject(keyPair.Public); + swPublic.Write(textWriter.ToString()); + swPublic.Flush(); + swPublic.Close(); + fsPublic.Close(); + } + } + /// + /// 数据加密处理 + /// + /// + /// + public static string EncryptRSA(string text, string publicKey) + { + string value = ""; + try + { + if (!string.IsNullOrEmpty(text)) + { + #region 分段加密 解决加密明文过长问题 + + text = Convert.ToBase64String(Encoding.GetEncoding("utf-8").GetBytes(text)); + text = text.Replace("\r", "").Replace("\n", ""); + int len = 117; + int m = text.Length / len; + if (m * len != text.Length) + { + m = m + 1; + } + for (int i = 0; i < m; i++) + { + string temp = ""; + if (i < m - 1) + { + temp = text.Substring(i * len, len);//(i + 1) * len + } + else + { + temp = text.Substring(i * len); + } + temp = Convert.ToBase64String(Encrypt(Encoding.GetEncoding("utf-8").GetBytes(temp), publicKey)); + temp = temp.Replace("\r", "").Replace("\n", ""); + value += temp; + } + + #endregion + } + } + catch (Exception e) + { + return e.Message; + } + + + return value; + } + /// + /// 数据解密处理 + /// + /// + /// + public static string DecryptRSA(string text, string privateKey) + { + string value = ""; + try + { + if (!string.IsNullOrEmpty(text)) + { + #region 分段解密 解决加密密文过长问题 + int len = 172; + int m = text.Length / len; + if (m * len != text.Length) + { + m = m + 1; + } + + for (int i = 0; i < m; i++) + { + string temp = ""; + + if (i < m - 1) + { + temp = text.Substring(i * len, len); + } + else + { + temp = text.Substring(i * len); + } + temp = decode64(temp); + + temp = Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(temp), privateKey)); + value += temp; + + } + #endregion + value = decode64(value); + value = Encoding.UTF8.GetString(Convert.FromBase64String(value)); + } + } + catch (Exception e) + { + throw new ArgumentException(e.Message); + } + return value; + } + + /// + /// RSA加密操作 Encrypt方法 + /// + /// + /// + /// + private static byte[] Encrypt(byte[] DataToEncrypt, string publicKey) + { + try + { + PemReader r = new PemReader(new StreamReader(publicKey)); + Object readObject = r.ReadObject(); + AsymmetricKeyParameter pubKey = (AsymmetricKeyParameter)readObject; + IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); + c.Init(true, pubKey); + byte[] outBytes = c.DoFinal(DataToEncrypt); + return outBytes; + } + catch + { + throw; + } + } + + /// + /// RSA解密操作 Decrypt方法 + /// + /// + /// + /// + /// + private static byte[] Decrypt(byte[] input, string privateKey) + { + try + { + AsymmetricCipherKeyPair keyPair; + using (var reader = File.OpenText(privateKey)) + keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject(); + AsymmetricKeyParameter priKey = keyPair.Private; + IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding"); + c.Init(false, priKey); + byte[] outBytes = c.DoFinal(input); + return outBytes; + } + catch + { + throw; + } + } + + /// + /// 解决PHP Base64编码后回车问题 + /// + /// + /// + public static String decode64(String text) + { + String value = ""; + + try + { + text = string.IsNullOrEmpty(text) ? "" : text; + + int len = 64; + int m = text.Length / len; + if (m * len != text.Length) + { + m = m + 1; + } + + for (int i = 0; i < m; i++) + { + String temp = ""; + + if (i < m - 1) + { + temp = text.Substring(i * len, len); + value += temp + "\r\n"; + } + else + { + temp = text.Substring(i * len); + value += temp; + } + } + } + catch (Exception e) + { + return e.Message; + } + + return value; + } + } + + /// + /// BouncyCastle 密钥类 + /// + public class Password : IPasswordFinder + { + private char[] password; + + public Password(char[] word) + { + this.password = (char[])word.Clone(); + } + + public char[] GetPassword() + { + return (char[])password.Clone(); + } + } +} diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index b71a280..9ebf7fd 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -48,6 +48,17 @@ Cancel=取消 New server=未配置的服务器 Move &Up=上移(&U) Move D&own=下移(&O) +Provider=来源 +&Import=导入(&I) + +# Import From Web Form + +URL=发布者地址 +&Remove=移除选定配置(&R) +&Add Config=添加以上配置(&A) +&Get=获取(&G) +Import=从发布者获得配置 +No valid configs to import!=无可用配置! # Log Form diff --git a/shadowsocks-csharp/Model/OnlineConfigDataPackage.cs b/shadowsocks-csharp/Model/OnlineConfigDataPackage.cs new file mode 100644 index 0000000..d6fea7e --- /dev/null +++ b/shadowsocks-csharp/Model/OnlineConfigDataPackage.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Shadowsocks.Model +{ + class KeyExchangeResponse + { + public string FingerPrint; + public string ServerPublicKey; + public string VerifyString; + } + class GetConfigRequest + { + public string decrypted_string; + public string verify_string; + } + class GetConfigResponse + { + public Server[] Config; + public string VerifyString; + public string Provider; + } +} diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 24f4f2d..9d3d1a2 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -18,6 +18,9 @@ namespace Shadowsocks.Model public string method; public string remarks; public bool auth; + public string provider; + public string fingerprint; + public override int GetHashCode() { @@ -54,6 +57,8 @@ namespace Shadowsocks.Model this.password = ""; this.remarks = ""; this.auth = false; + this.fingerprint = ""; + this.provider = "local"; } public Server(string ssURL) : this() diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index efa241e..3c44598 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -39,7 +39,10 @@ namespace Shadowsocks Directory.SetCurrentDirectory(Application.StartupPath); Logging.OpenLogFile(); - + if (!Directory.Exists(System.Environment.CurrentDirectory + "\\keys")) + { + Directory.CreateDirectory(System.Environment.CurrentDirectory + "\\keys"); + } ShadowsocksController controller = new ShadowsocksController(); MenuViewController viewController = new MenuViewController(controller); diff --git a/shadowsocks-csharp/View/ConfigForm.Designer.cs b/shadowsocks-csharp/View/ConfigForm.Designer.cs index 306a92e..a541c76 100755 --- a/shadowsocks-csharp/View/ConfigForm.Designer.cs +++ b/shadowsocks-csharp/View/ConfigForm.Designer.cs @@ -56,6 +56,11 @@ this.ProxyPortLabel = new System.Windows.Forms.Label(); this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel(); + this.ProviderLabel = new System.Windows.Forms.Label(); + this.ProviderTextBox = new System.Windows.Forms.TextBox(); + this.FingerprintTextBox = new System.Windows.Forms.TextBox(); + this.tableLayoutPanel7 = new System.Windows.Forms.TableLayoutPanel(); + this.ProviderButton = new System.Windows.Forms.Button(); this.tableLayoutPanel1.SuspendLayout(); this.ServerGroupBox.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout(); @@ -63,6 +68,7 @@ this.tableLayoutPanel5.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout(); + this.tableLayoutPanel7.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 @@ -82,12 +88,15 @@ this.tableLayoutPanel1.Controls.Add(this.PasswordTextBox, 1, 2); this.tableLayoutPanel1.Controls.Add(this.EncryptionLabel, 0, 3); this.tableLayoutPanel1.Controls.Add(this.EncryptionSelect, 1, 3); - this.tableLayoutPanel1.Controls.Add(this.OneTimeAuth, 1, 6); + this.tableLayoutPanel1.Controls.Add(this.OneTimeAuth, 1, 8); + this.tableLayoutPanel1.Controls.Add(this.ProviderLabel, 0, 6); + this.tableLayoutPanel1.Controls.Add(this.ProviderTextBox, 1, 6); + this.tableLayoutPanel1.Controls.Add(this.FingerprintTextBox, 1, 7); this.tableLayoutPanel1.Location = new System.Drawing.Point(8, 21); this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3); - this.tableLayoutPanel1.RowCount = 7; + this.tableLayoutPanel1.RowCount = 9; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); @@ -95,7 +104,9 @@ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(249, 162); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(249, 216); this.tableLayoutPanel1.TabIndex = 0; // // RemarksTextBox @@ -213,7 +224,7 @@ // OneTimeAuth // this.OneTimeAuth.AutoSize = true; - this.OneTimeAuth.Location = new System.Drawing.Point(83, 140); + this.OneTimeAuth.Location = new System.Drawing.Point(83, 194); this.OneTimeAuth.Name = "OneTimeAuth"; this.OneTimeAuth.Size = new System.Drawing.Size(156, 16); this.OneTimeAuth.TabIndex = 5; @@ -288,7 +299,7 @@ this.ServerGroupBox.Location = new System.Drawing.Point(178, 0); this.ServerGroupBox.Margin = new System.Windows.Forms.Padding(12, 0, 0, 0); this.ServerGroupBox.Name = "ServerGroupBox"; - this.ServerGroupBox.Size = new System.Drawing.Size(260, 200); + this.ServerGroupBox.Size = new System.Drawing.Size(260, 254); this.ServerGroupBox.TabIndex = 0; this.ServerGroupBox.TabStop = false; this.ServerGroupBox.Text = "Server"; @@ -312,20 +323,22 @@ this.tableLayoutPanel2.ColumnCount = 2; this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); - this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel6, 0, 2); - this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1); - this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel6, 0, 3); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 3); this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0); this.tableLayoutPanel2.Controls.Add(this.ServerGroupBox, 1, 0); - this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel4, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel4, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel7, 0, 1); this.tableLayoutPanel2.Location = new System.Drawing.Point(12, 12); this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel2.Name = "tableLayoutPanel2"; - this.tableLayoutPanel2.RowCount = 3; + this.tableLayoutPanel2.RowCount = 4; this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel2.Size = new System.Drawing.Size(438, 265); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.Size = new System.Drawing.Size(438, 351); this.tableLayoutPanel2.TabIndex = 7; // // tableLayoutPanel6 @@ -338,7 +351,7 @@ this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0); this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0); this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top; - this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 233); + this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 319); this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel6.Name = "tableLayoutPanel6"; this.tableLayoutPanel6.RowCount = 1; @@ -381,7 +394,7 @@ this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel5.Controls.Add(this.ProxyPortTextBox, 1, 0); this.tableLayoutPanel5.Controls.Add(this.ProxyPortLabel, 0, 0); - this.tableLayoutPanel5.Location = new System.Drawing.Point(242, 200); + this.tableLayoutPanel5.Location = new System.Drawing.Point(242, 286); this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel5.Name = "tableLayoutPanel5"; this.tableLayoutPanel5.Padding = new System.Windows.Forms.Padding(3); @@ -425,7 +438,7 @@ this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0); this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0); this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right; - this.tableLayoutPanel3.Location = new System.Drawing.Point(279, 236); + this.tableLayoutPanel3.Location = new System.Drawing.Point(279, 322); this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); this.tableLayoutPanel3.Name = "tableLayoutPanel3"; this.tableLayoutPanel3.RowCount = 1; @@ -443,7 +456,7 @@ this.tableLayoutPanel4.Controls.Add(this.DeleteButton, 1, 0); this.tableLayoutPanel4.Controls.Add(this.AddButton, 0, 0); this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Top; - this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 200); + this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 286); this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); this.tableLayoutPanel4.Name = "tableLayoutPanel4"; this.tableLayoutPanel4.RowCount = 1; @@ -451,6 +464,58 @@ this.tableLayoutPanel4.Size = new System.Drawing.Size(166, 32); this.tableLayoutPanel4.TabIndex = 8; // + // ProviderLabel + // + this.ProviderLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.ProviderLabel.AutoSize = true; + this.ProviderLabel.Location = new System.Drawing.Point(24, 144); + this.ProviderLabel.Name = "ProviderLabel"; + this.ProviderLabel.Size = new System.Drawing.Size(53, 12); + this.ProviderLabel.TabIndex = 10; + this.ProviderLabel.Text = "Provider"; + // + // ProviderTextBox + // + this.ProviderTextBox.Location = new System.Drawing.Point(83, 140); + this.ProviderTextBox.Name = "ProviderTextBox"; + this.ProviderTextBox.ReadOnly = true; + this.ProviderTextBox.Size = new System.Drawing.Size(160, 21); + this.ProviderTextBox.TabIndex = 11; + // + // FingerprintTextBox + // + this.FingerprintTextBox.Location = new System.Drawing.Point(83, 167); + this.FingerprintTextBox.Name = "FingerprintTextBox"; + this.FingerprintTextBox.Size = new System.Drawing.Size(160, 21); + this.FingerprintTextBox.TabIndex = 12; + this.FingerprintTextBox.Visible = false; + // + // tableLayoutPanel7 + // + this.tableLayoutPanel7.ColumnCount = 2; + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel7.Controls.Add(this.ProviderButton, 0, 0); + this.tableLayoutPanel7.Location = new System.Drawing.Point(0, 254); + this.tableLayoutPanel7.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel7.Name = "tableLayoutPanel7"; + this.tableLayoutPanel7.RowCount = 1; + this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel7.Size = new System.Drawing.Size(166, 32); + this.tableLayoutPanel7.TabIndex = 11; + // + // ProviderButton + // + this.ProviderButton.Dock = System.Windows.Forms.DockStyle.Left; + this.ProviderButton.Location = new System.Drawing.Point(0, 6); + this.ProviderButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); + this.ProviderButton.Name = "ProviderButton"; + this.ProviderButton.Size = new System.Drawing.Size(80, 23); + this.ProviderButton.TabIndex = 0; + this.ProviderButton.Text = "&Import"; + this.ProviderButton.UseVisualStyleBackColor = true; + this.ProviderButton.Click += new System.EventHandler(this.ProviderButton_Click); + // // ConfigForm // this.AcceptButton = this.OKButton; @@ -483,6 +548,7 @@ this.tableLayoutPanel5.PerformLayout(); this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel4.ResumeLayout(false); + this.tableLayoutPanel7.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -518,6 +584,11 @@ private System.Windows.Forms.Button MoveDownButton; private System.Windows.Forms.Button MoveUpButton; private System.Windows.Forms.CheckBox OneTimeAuth; + private System.Windows.Forms.Label ProviderLabel; + private System.Windows.Forms.TextBox ProviderTextBox; + private System.Windows.Forms.TextBox FingerprintTextBox; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7; + private System.Windows.Forms.Button ProviderButton; } } diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 0135f48..591f105 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -19,7 +19,7 @@ namespace Shadowsocks.View // this is a copy of configuration that we are working on private Configuration _modifiedConfiguration; private int _lastSelectedIndex = -1; - + public List _onlineConfig; public ConfigForm(ShadowsocksController controller) { this.Font = System.Drawing.SystemFonts.MessageBoxFont; @@ -42,6 +42,7 @@ namespace Shadowsocks.View { AddButton.Text = I18N.GetString("&Add"); DeleteButton.Text = I18N.GetString("&Delete"); + ProviderButton.Text = I18N.GetString("&Import"); IPLabel.Text = I18N.GetString("Server IP"); ServerPortLabel.Text = I18N.GetString("Server Port"); PasswordLabel.Text = I18N.GetString("Password"); @@ -54,6 +55,7 @@ namespace Shadowsocks.View MyCancelButton.Text = I18N.GetString("Cancel"); MoveUpButton.Text = I18N.GetString("Move &Up"); MoveDownButton.Text = I18N.GetString("Move D&own"); + ProviderLabel.Text = I18N.GetString("Provider"); this.Text = I18N.GetString("Edit Servers"); } @@ -84,6 +86,8 @@ namespace Shadowsocks.View password = PasswordTextBox.Text, method = EncryptionSelect.Text, remarks = RemarksTextBox.Text, + fingerprint = FingerprintTextBox.Text, + provider = ProviderTextBox.Text, auth = OneTimeAuth.Checked }; int localPort = int.Parse(ProxyPortTextBox.Text); @@ -117,6 +121,8 @@ namespace Shadowsocks.View ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); EncryptionSelect.Text = server.method ?? "aes-256-cfb"; RemarksTextBox.Text = server.remarks; + ProviderTextBox.Text = server.provider; + FingerprintTextBox.Text = server.fingerprint; OneTimeAuth.Checked = server.auth; } } @@ -338,5 +344,32 @@ namespace Shadowsocks.View OneTimeAuth.Enabled = true; } } + + private void ProviderButton_Click(object sender, EventArgs e) + { + ImportFromWebForm formx = new ImportFromWebForm(); + formx.Owner = this; + formx.ShowDialog(); + if (this._onlineConfig != null) + { + string fp = this._onlineConfig[0].fingerprint; + List TempConfigs = new List(); + foreach(Server server in _modifiedConfiguration.configs) + { + if (server.fingerprint != fp) + { + TempConfigs.Add(server); + } + } + _modifiedConfiguration.configs = TempConfigs; + foreach (Server server in this._onlineConfig) + { + _modifiedConfiguration.configs.Add(server); + } + LoadConfiguration(_modifiedConfiguration); + controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort); + } + + } } } diff --git a/shadowsocks-csharp/View/ImportFromWebForm.Designer.cs b/shadowsocks-csharp/View/ImportFromWebForm.Designer.cs new file mode 100644 index 0000000..c83e0d4 --- /dev/null +++ b/shadowsocks-csharp/View/ImportFromWebForm.Designer.cs @@ -0,0 +1,181 @@ +namespace Shadowsocks.View +{ + partial class ImportFromWebForm + { + /// + /// 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.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ImportFromWebForm)); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.URLTextBox = new System.Windows.Forms.TextBox(); + this.ConfigListBox = new System.Windows.Forms.ListBox(); + this.GetConfigButton = new System.Windows.Forms.Button(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.RemoveButton = new System.Windows.Forms.Button(); + this.AddConfigButton = new System.Windows.Forms.Button(); + this.ProviderURLLabel = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 4; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 46F)); + this.tableLayoutPanel1.Controls.Add(this.URLTextBox, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.ConfigListBox, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.GetConfigButton, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.tableLayoutPanel2, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.ProviderURLLabel, 0, 0); + this.tableLayoutPanel1.Location = new System.Drawing.Point(8, 1); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 3; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(383, 259); + this.tableLayoutPanel1.TabIndex = 0; + // + // URLTextBox + // + this.URLTextBox.Location = new System.Drawing.Point(32, 3); + this.URLTextBox.Name = "URLTextBox"; + this.URLTextBox.Size = new System.Drawing.Size(219, 21); + this.URLTextBox.TabIndex = 1; + // + // ConfigListBox + // + this.tableLayoutPanel1.SetColumnSpan(this.ConfigListBox, 2); + this.ConfigListBox.FormattingEnabled = true; + this.ConfigListBox.ItemHeight = 12; + this.ConfigListBox.Location = new System.Drawing.Point(32, 32); + this.ConfigListBox.Name = "ConfigListBox"; + this.ConfigListBox.Size = new System.Drawing.Size(302, 196); + this.ConfigListBox.TabIndex = 2; + // + // GetConfigButton + // + this.GetConfigButton.Location = new System.Drawing.Point(254, 3); + this.GetConfigButton.Margin = new System.Windows.Forms.Padding(0, 3, 3, 3); + this.GetConfigButton.Name = "GetConfigButton"; + this.GetConfigButton.Size = new System.Drawing.Size(80, 23); + this.GetConfigButton.TabIndex = 3; + this.GetConfigButton.Text = "&Get"; + this.GetConfigButton.UseVisualStyleBackColor = true; + this.GetConfigButton.Click += new System.EventHandler(this.GetConfigButton_Click); + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tableLayoutPanel2.ColumnCount = 3; + this.tableLayoutPanel1.SetColumnSpan(this.tableLayoutPanel2, 2); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.Controls.Add(this.RemoveButton, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.AddConfigButton, 1, 0); + this.tableLayoutPanel2.Location = new System.Drawing.Point(29, 231); + this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(0); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 1; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.Size = new System.Drawing.Size(308, 33); + this.tableLayoutPanel2.TabIndex = 4; + // + // RemoveButton + // + this.RemoveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.RemoveButton.AutoEllipsis = true; + this.RemoveButton.Location = new System.Drawing.Point(195, 3); + this.RemoveButton.Name = "RemoveButton"; + this.RemoveButton.Size = new System.Drawing.Size(110, 23); + this.RemoveButton.TabIndex = 0; + this.RemoveButton.Text = "&Remove"; + this.RemoveButton.UseVisualStyleBackColor = true; + this.RemoveButton.Click += new System.EventHandler(this.RemoveButton_Click); + // + // AddConfigButton + // + this.AddConfigButton.Location = new System.Drawing.Point(79, 3); + this.AddConfigButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 1); + this.AddConfigButton.Name = "AddConfigButton"; + this.AddConfigButton.Size = new System.Drawing.Size(110, 23); + this.AddConfigButton.TabIndex = 1; + this.AddConfigButton.Text = "&Add Config"; + this.AddConfigButton.UseVisualStyleBackColor = true; + this.AddConfigButton.Click += new System.EventHandler(this.AddConfigButton_Click); + // + // ProviderURLLabel + // + this.ProviderURLLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; + this.ProviderURLLabel.AutoSize = true; + this.ProviderURLLabel.Location = new System.Drawing.Point(3, 8); + this.ProviderURLLabel.Name = "ProviderURLLabel"; + this.ProviderURLLabel.Size = new System.Drawing.Size(23, 12); + this.ProviderURLLabel.TabIndex = 0; + this.ProviderURLLabel.Text = "URL"; + // + // ImportFromWebForm + // + 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.ClientSize = new System.Drawing.Size(432, 339); + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ImportFromWebForm"; + this.Padding = new System.Windows.Forms.Padding(12, 12, 12, 9); + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Import"; + this.Load += new System.EventHandler(this.ImportFromWebForm_Load); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label ProviderURLLabel; + private System.Windows.Forms.TextBox URLTextBox; + private System.Windows.Forms.ListBox ConfigListBox; + private System.Windows.Forms.Button GetConfigButton; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Button RemoveButton; + private System.Windows.Forms.Button AddConfigButton; + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/View/ImportFromWebForm.resx b/shadowsocks-csharp/View/ImportFromWebForm.resx new file mode 100644 index 0000000..90c7220 --- /dev/null +++ b/shadowsocks-csharp/View/ImportFromWebForm.resx @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + AAABAAQAEBAAAAEAIABoBAAARgAAACAgAAABACAAqBAAAK4EAAAwMAAAAQAgAKglAABWFQAAAAAAAAEA + IADNEAAA/joAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA1qtqDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKDJIvXrm8EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAfxy/w4YpewAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwoMjuMOFJ6Po0a4DAAAAAM+dUxPFijBf3bmEBQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODBkQbMl0ckw4UocsGCIsDAfxz8v30Z/8eN + NTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADTpmEHzJdHKwAAAADFiS+Fv30a/799Gf+/fRn/v30Z/799 + Gf/Dhil3AAAAAAAAAAAAAAAA4cKUAseNNUPChCWdwIAe8MB+G/zFiS5v0KBWC8GCIc6/fRn/v30Z/799 + Gf+/fRn/woIjswAAAAAAAAAAzZlLGcKEJcy/fRn/v30Z/799Gf+/fRn/v30Z/8SHK5fOm04iwYAf5799 + Gf+/fRn/v30Z/8CAHu7r17cCAAAAAAAAAADXrW4MxYoweMGBIOu/fRn/v30Z/799Gf+/fRn/woQlusqT + QUjAfxz2v30Z/799Gf+/fRn/ypNAMAAAAAAAAAAAAAAAAAAAAADXrW4MxYkueMGAH+u/fRn/v30Z/799 + Gf/BgSHWx4w0e79+Gv6/fRn/v30Z/8OFJ2wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADTpmEMxIgseMGA + H+u/fRn/v30Z/8CAHuvFii++v30Z/799Gf/CgySqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADPnlMMw4YoeMCAHuu/fRn/wH8d+sKDI/y/fRn/wYEg5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADPnlMMw4UneMB/Heu/fRn/v30Z/799Gf/Ml0YmAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLlUQMwoMkeMB/Heu/fRn/xIcrZAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSpF4PxYkvf8WJL5AAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmzKQD/v8AAP5/ + AAD+fwAA/iMAAP4DAADyAwAAgAMAAAABAACAAQAA4AEAAPgBAAD+AQAA/4AAAP/gAAD/+AAA//4AACgA + AAAgAAAAQAAAAAEAIAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1qtqMAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAfx1/zJdHTAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL99 + GX/BgiLj165vEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + Gf+/fRn/v30Z/799Gf/BgSH7y5ZFYM+eUyjCgyTzv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/w4UotwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANOmYTDGjDOvwIAe/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/Afhv/yZE9h82ZS0jAfx37v30Z/799Gf+/fRn/v30Z/799 + Gf/CgyPz9+/iBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTpmEwxowzr8CAHv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/x401r82aTHy/fRr/v30Z/799 + Gf+/fRn/v30Z/799Gf/So104AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA06ZhMMaMM6/Afx3/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/xIYqy8qT + QLO/fRn/v30Z/799Gf+/fRn/v30Z/8eON3QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM+eUzDFiS6vwH8d/799Gf+/fRn/v30Z/799 + Gf+/fRn/xIcq68iPOvO/fRn/v30Z/799Gf+/fRn/xIcqrwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADPnlMwxYkur8B/ + Hf+/fRn/v30Z/799Gf+/fRn/wYEh/799Gf+/fRn/v30Z/799Gf/DhSjvAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAz55TMMWJLq/Afx3/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/XrW4wAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAMuVRDDDhimvwH8c/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8eM + NGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADLlUQww4Ypr8B/HP+/fRn/v30Z/799 + Gf+/fRn/xYgtpwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy5VEMMOG + Ka/Afxz/v30Z/799Gf/EhirnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAANKkXjzHjTa/wH4b/799Gv/ZsXYkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVqWdAx402v8mSPmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5sykDAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////9 + /////P////x////8f////D////wf///8PB///eAf//8AH//8AB//jAAP/AYAD+ADAA+AAIAPgABAB+AA + AAf4AAAH/gAAB/+AAAP/4AAD//gAA//+AAP//4AD///gAf//+AH///4B////gf///+D////4/////v// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1qxtSNSoZiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv30ZQMeMNMPp07AEAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAv30ZQL99 + Gv/MmElwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAv30ZQL99Gf/BgiHz165vJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAv30ZQL99Gf+/fRn/x4w0w9++jAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyJA7QL99Gf+/fRn/v34a/82ZSnEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy5ZFQL99Gf+/fRn/v30Z/8KD + I/TXrnAnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnzqgDzJdHT8SHK8+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/GizLb2LBzJAAA + AAAAAAAA2K9yQ8KDI/m/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf/BgiL/69e3EgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPv38QbVqWZmyI844b9+ + Gv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/xIcr8NWqaEQAAAAAAAAAANOlYWbAgB7+v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/z55TTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA9OnYCNSpZmjIjzjfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8KDJPvPnVJiAAAAAAAAAADPnVKPv30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/ypNAjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt278G1atqasiOOOG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/AgB7/zZpNifv38Qbz59QDyZA7rr99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/yI850QAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPv38QbVqWZmyI844b9+ + Gv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30a/8qU + QrHcuIEK6NCsD8WILcq/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/woIj9uTH + nA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA9OnYCNSnY2jGizLfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf/IjzjS2rR7G969iiXChCXiv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/8+dUkEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnz6kG0qRdasaLMuG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/xoox6dCgVy3ZsnY/wYIh9r99Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8qUQoUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPfv4gbRoVlmxowz4b99 + Gv+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8OGKPnQoFdS0qReYMB/ + Hf2/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8eNNsQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA79/GCNGiWmjGizLfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf/BgiH/zpxQeM6cUIi/fhr/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/8OGKfXlyqILAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADnz6kG0qRdasaLMuG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/v34b/syYSKPLlkW0v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf/WrGw9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO/exQbNmkxmxYku4b99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/LlUTSyZI95L99Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf/LlUN8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA586pCM2aTWjFiC3fv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/yZI+/8SH + K/+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/GjDO3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADfvowGzpxQasWILeG/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/79+G/+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf/EiCzzAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO/exQbNmkxmxYku4b99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/376NPAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA586pCM2ZS2jDhSjfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/ypRBbwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADXrm8Gy5VDasOFJ+G/fRn/v30Z/799Gf+/fRn/v30Z/799 + Gf+/fRn/v30Z/799Gf+/fRn/xosxqgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOfOqAbJkj5mw4Yp4b99 + Gf+/fRn/v30Z/799Gf+/fRn/v30Z/799Gf+/fRn/xIcq5/Pn1AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA376MCMqTQGjDhSjfv30Z/799Gf+/fRn/v30Z/799Gf+/fRn/wIAd/9y5gy8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADXrm8Gy5VDasOFJ+G/fRn/v30Z/799Gf+/fRn/v30Z/86b + T2sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN++jAbSo1yAxYov7b99 + Gf+/fRn/v30Z/8iPOaYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA69W2EdGhWX/GijHuv30Z/8SHKt3v3sUCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt278S0KBXf8eON/DduoUiAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDgyQ/ZsncMAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////AAD///////8AAP//+f///wAA///4////AAD///j///8AAP// + +H///wAA///4P///AAD///g///8AAP//+B///wAA///4D///AAD///gP+f8AAP//+D/A/wAA///5/AD/ + AAD////gAP8AAP///wAA/wAA///4AAB/AAD///gAAH8AAP/8PAAAfwAA/8AeAAA/AAD/AA8AAD8AAPgA + BwAAPwAAwAABgAA/AACAAADAAD8AAMAAAGAAHwAA8AAAMAAfAAD8AAAYAB8AAP8AAAAAHwAA/8AAAAAP + AAD/8AAAAA8AAP/8AAAADwAA//8AAAAPAAD//8AAAAcAAP//8AAABwAA///8AAAHAAD///8AAAcAAP// + /8AABwAA////8AADAAD////8AAMAAP////8AAwAA/////8ABAAD/////8AEAAP/////8AQAA//////8B + AAD//////8AAAP//////8AAA///////8AAD///////8AAP///////wAAiVBORw0KGgoAAAANSUhEUgAA + AQAAAAEACAYAAABccqhmAAAQlElEQVR42u3dP0xcZ7rH8R8riikQoqCYlWzJlijMhcJIWwQrxbqIFkrH + 2Vh2t1bcbBXWXRqL7Z11lcYR6TbC+4cSoi24FdyVIkERBIUlkEBXUyDdEaKYAuncwn69h+M5M+fM+ff+ + +X66WEQ54/D8zvM+553zjkVRJABh+lXTFwCgOQQAEDACAAgYAQAEjAAAAkYAAAEjAICAEQBAwAgAIGAE + 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 + sP8HnX+c8U4DeOMAAAAASUVORK5CYII= + + + \ No newline at end of file diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index b19a244..4dfa1eb 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -311,12 +311,22 @@ namespace Shadowsocks.View Configuration configuration = controller.GetConfigurationCopy(); foreach (var server in configuration.configs) { + if (server.provider != "local" && server.provider != "") { continue; } MenuItem item = new MenuItem(server.FriendlyName()); item.Tag = i - strategyCount; item.Click += AServerItem_Click; items.Add(i, item); i++; } + foreach (var server in configuration.configs) + { + if (server.provider == "local") { continue; } + MenuItem item = new MenuItem(server.provider+" | "+server.FriendlyName()); + item.Tag = i - strategyCount; + item.Click += AServerItem_Click; + items.Add(i, item); + i++; + } foreach (MenuItem item in items) { diff --git a/shadowsocks-csharp/app.config b/shadowsocks-csharp/app.config index a7ba106..303edf5 100755 --- a/shadowsocks-csharp/app.config +++ b/shadowsocks-csharp/app.config @@ -8,11 +8,15 @@ - + - + + + + + diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index b309fb9..230cbc8 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -1,9 +1,11 @@  + - + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index d58ca50..8b3bace 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -64,6 +64,10 @@ app.manifest + + 3rd\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll + True + 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll @@ -90,22 +94,34 @@ - - 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.IO.dll + + 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll True - False - - - 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll + + 3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.dll True - False - - 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Threading.Tasks.dll + + 3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Extensions.dll + True + + + 3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Primitives.dll + True + + + 3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.WebRequest.dll + True + + + 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll + True + + + 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll True - False @@ -169,7 +185,9 @@ + + @@ -184,7 +202,7 @@ - + @@ -192,6 +210,7 @@ + @@ -222,6 +241,12 @@ CalculationControl.cs + + Form + + + ImportFromWebForm.cs + Form @@ -256,6 +281,9 @@ CalculationControl.cs + + ImportFromWebForm.cs + LogForm.cs diff --git a/test/packages.config b/test/packages.config new file mode 100644 index 0000000..b7ca94e --- /dev/null +++ b/test/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/test/test.csproj b/test/test.csproj index d742014..fbf50fc 100755 --- a/test/test.csproj +++ b/test/test.csproj @@ -30,7 +30,25 @@ + + ..\shadowsocks-csharp\3rd\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll + True + + + ..\shadowsocks-csharp\3rd\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll + True + + + + ..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll + True + + + ..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll + True + + @@ -54,6 +72,9 @@ shadowsocks-csharp + + + @@ -74,6 +95,11 @@ + + + + +