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 @@
+
+
+
+
+