Compare commits

...
Author SHA1 Message Date
icylogic f76af14247 Merge pull request #401 from SuperHentai/with_online_config
Fix #399 for branch 'with_online_config'
2015-12-18 15:14:47 +08:00
Gang Zhuo 49e8ccc150 delay check for updates 2015-12-17 01:23:45 +08:00
icylogic 0a45895342 fix #389: crash on empty statistic data. 2015-12-17 01:21:51 +08:00
Kaguya 0090f18fcc Update README 2015-12-15 14:39:52 +08:00
Kaguya f4ca3e7729 https://github.com/shadowsocks/shadowsocks-windows/issues/399 2015-12-15 14:35:42 +08:00
Kaguya 2c1ac81700 使获取配置窗口的图标使用resource的数据 2015-12-13 18:25:52 +08:00
Kaguya 24ae5ef8a0 测试通过 2015-12-12 00:29:22 +08:00
Kaguya 1199f1fe1f Complete Online Config 2015-12-09 20:00:16 +08:00
21 changed files with 1294 additions and 110 deletions
+60 -71
View File
@@ -1,83 +1,72 @@
Shadowsocks for Windows
=======================
### 带在线获取并同步最新Server参数的功能的一个版本
* 现在由于干扰或一些别的原因,服务器配置会经常修改,而对于多人共享或者其他类似情境,发布和同步频繁修改的的配置就显得比较不便。我加了一个通过url较安全地获取配置的功能,即通过一个发布服务器,客户端可使用rsa加密通信从服务器获取最新的配置并与本地的配置同步,从而实现便捷获取最新配置.
* 其实在之前已经进行过约三个月的小范围测试,累计用户约1400人,效果还算不错,所以在考虑能否加入项目本体.
* 配置文件Server段结构如下
[![Build Status]][Appveyor]
{
"server" : "127.0.0.1",
"server_port" : 2333,
"password" : "kaguya",
"method" : "aes-128-cfb",
"remarks" : "新测试1",
"auth" : false,
"provider" : "新服务器",
"fingerprint" : "90a409a19ba0db0f7cd0cff0dffbbcde"
},
{
"server" : "127.0.0.1",
"server_port" : 8388,
"password" : "test",
"method" : "aes-256-cfb",
"remarks" : "comment",
"auth" : false,
"provider" : "local",
"fingerprint" : ""
}
与原来相比,增加了provider和fingerprint两项
provider记录了配置的来源,如果是自行手工添加的则为local,若为在线获取的则为发布服务器指定的名称.用于在系统托盘菜单中显示用于指示配置来源.
[中文说明]
fingerprint记录了配置来源服务器的唯一的指纹,若是自行手工添加的则为空.此段用于标记来自同一发布服务器的一组配置的集合,当从同一发布服务器更新配置时,拥有与服务器相同指纹的配置可以被更新/覆盖.
#### Features
Server模型和配置文件的其他部分其他均与先前一致.
* 工作原理
* 客户端随机生成一对RSA密钥,并用明文发送公钥至发布服务器.
* 服务器接收并记录客户端公钥,将服务器的公钥、指纹、随机生成的验证字符串用客户端公钥加密以后发回.
* 客户端收到服务器公钥和指纹,如果存在对应的本地记录,则将收到的数据与本地记录进行比对,若不相符,判定为无效服务器;若无本地记录,则默认信任服务器有效性并将其加入本地记录.客户端把服务器发来的验证字符串原样返回,同时计算出一个随机字符串,将两者用服务器公钥加密后发往服务器.
* 服务器收到客户端请求,检查客户端返回的验证字符串并与服务器本地保存的字符串进行对比,若符合则信任客户端身份;否则判定为欺诈请求返回404错误.服务器根据预定策略将Shadowsocks配置和用户生成的验证字符串用客户端公钥加密后返回.
* 客户端收到数据,校验服务器返回的验证字符串,如果不符,则判定服务器身份不可信;如果相符,信任服务器身份并接受服务器返回的配置,反之则中止该次通信.至此,服务器和客户端完成双向的身份认证和数据的传输.
* 客户端将本地拥有与服务器相同指纹的配置置用获取的最新位置覆盖,完成配置的更新.
1. System proxy configuration
2. PAC mode and global mode
3. [GFWList] and user rules
4. Supports HTTP proxy
5. Supports server auto switching
6. Supports UDP relay (see Usage)
* 操作示例
* 服务器配置的显示,如下图
![default](https://cloud.githubusercontent.com/assets/5331336/11752593/7788730c-a07b-11e5-9a45-b46757475679.png)
#### Download
* 在线获取/更新服务器的过程,如下图
![add](https://cloud.githubusercontent.com/assets/5331336/11752616/8b8a28aa-a07b-11e5-9160-1baab4e17826.png)
Download the [latest release].
1.点击“导入”打开导入配置窗口
#### Basic
2.输入获得的url,点击“获取”,若无异常,列表框显示获得的配置,若出现异常,弹窗显示异常信息.
1. Find Shadowsocks icon in the notification tray
2. You can add multiple servers in servers menu
3. Select `Enable System Proxy` menu to enable system proxy. Please disable other
proxy addons in your browser, or set them to use system proxy
4. You can also configure your browser proxy manually if you don't want to enable
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in `Servers -> Edit Servers`
3.选择不想添加的配置,点击“移除选定配置”,排除指定配置.
#### PAC
4.点击“添加以上配置”,将列表中剩余的配置加入本地配置列表并覆盖同一来源的配置.
1. You can change PAC rules by editing the PAC file. When you save the PAC file
with any editor, Shadowsocks will notify browsers about the change automatically
2. You can also update PAC file from [GFWList] (maintained by 3rd party)
3. You can also use online PAC URL
* 安全性考虑
* 这个功能采取“信任新服务器,在本地保存‘服务器-指纹’对作为服务器身份”的策略,当同一指纹在第一次通信安全可靠及服务器私钥未泄露的情况下,可以在后续的更新配置中排除中间人攻击的威胁.
#### Server Auto Switching
* 更多
* 配套的完整的发布服务器的实现在这里:(基于laravel,有部署脚本)
[https://github.com/SuperHentai/Shadowsocks-Config-Server](https://github.com/SuperHentai/Shadowsocks-Config-Server)
* 匆忙之中写完的说明,里面有比较详细的信息:[https://github.com/SuperHentai/Shadowsocks-Config-Server/blob/master/readme.md](https://github.com/SuperHentai/Shadowsocks-Config-Server/blob/master/readme.md)
[https://github.com/SuperHentai/Shadowsocks-Config-Server/wiki/%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3](https://github.com/SuperHentai/Shadowsocks-Config-Server/wiki/%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3)
1. Load balance: choosing server randomly
2. High availability: choosing the best server (low latency and packet loss)
3. Choose By Total Package Loss: ping and choose. Please also enable
`Availability Statistics` in the menu if you want to use this
4. Write your own strategy by implement IStrategy interface and send us a pull request!
#### UDP
For UDP, you need to use SocksCap or ProxyCap to force programs you want
to be proxied to tunnel over Shadowsocks
#### Multiple Instances
If you want to manage multiple servers using other tools like SwitchyOmega,
you can start multiple Shadowsocks instances. To avoid configuration conflicts,
copy Shadowsocks to a new directory and choose a different local port.
Also, make sure to use `SOCKS5` proxy in SwitchyOmega, since we have only
one HTTP proxy instance.
#### Server Configuration
Please visit [Servers] for more information.
#### Portable Mode
If you want to put all temporary files into shadowsocks/temp folder instead of
system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks folder.
#### Develop
Visual Studio 2015 is required.
#### License
GPLv3
[Appveyor]: https://ci.appveyor.com/project/icylogic/shadowsocks-windows-l9mwe
[Build Status]: https://ci.appveyor.com/api/projects/status/ytllr9yjkbpc2tu2/branch/master
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
* 放出的测试程序[https://github.com/SuperHentai/shadowsocks-windows/releases/tag/test](https://github.com/SuperHentai/shadowsocks-windows/releases/tag/test)
* 已部署的测试发布服务器URL:http://test.unwall.org/
* 发布服务器操作后台[http://test.unwall.org/panel](http://test.unwall.org/panel)
* 测试用户名:kaguya
* 测试密码:kaguya
@@ -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;
}
}
}
@@ -123,7 +123,7 @@ namespace Shadowsocks.Controller
{
Logging.Debug("Ping " + server.FriendlyName());
if (server.server == "") return null;
var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6));
var ping = new Ping();
var ret = new List<DataList>();
foreach (
@@ -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();
}
}
/// <summary>
/// 数据加密处理
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 数据解密处理
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
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;
}
/// <summary>
/// RSA加密操作 Encrypt方法
/// </summary>
/// <param name="DataToEncrypt"></param>
/// <param name="publicKey"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// RSA解密操作 Decrypt方法
/// </summary>
/// <param name="input"></param>
/// <param name="privateKey"></param>
/// <param name="password"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// 解决PHP Base64编码后回车问题
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
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;
}
}
/// <summary>
/// BouncyCastle 密钥类
/// </summary>
public class Password : IPasswordFinder
{
private char[] password;
public Password(char[] word)
{
this.password = (char[])word.Clone();
}
public char[] GetPassword()
{
return (char[])password.Clone();
}
}
}
@@ -28,12 +28,41 @@ namespace Shadowsocks.Controller
public const string Version = "2.5.8";
private class CheckUpdateTimer : System.Timers.Timer
{
public Configuration config;
public CheckUpdateTimer(int p) : base(p)
{
}
}
public void CheckUpdate(Configuration config, int delay)
{
CheckUpdateTimer timer = new CheckUpdateTimer(delay);
timer.AutoReset = false;
timer.Elapsed += Timer_Elapsed;
timer.config = config;
timer.Enabled = true;
}
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
CheckUpdateTimer timer = (CheckUpdateTimer)sender;
Configuration config = timer.config;
timer.Elapsed -= Timer_Elapsed;
timer.Enabled = false;
timer.Dispose();
CheckUpdate(config);
}
public void CheckUpdate(Configuration config)
{
this.config = config;
try
{
Logging.Debug("Checking updates...");
WebClient http = CreateWebClient();
http.DownloadStringCompleted += http_DownloadStringCompleted;
http.DownloadStringAsync(new Uri(UpdateURL));
@@ -81,9 +110,13 @@ namespace Shadowsocks.Controller
startDownload();
}
else if (CheckUpdateCompleted != null)
else
{
CheckUpdateCompleted(this, new EventArgs());
Logging.Debug("No update is available");
if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
}
}
}
catch (Exception ex)
@@ -117,6 +150,7 @@ namespace Shadowsocks.Controller
Logging.LogUsefulException(e.Error);
return;
}
Logging.Debug($"New version {LatestVersionNumber} found: {LatestVersionLocalName}");
if (CheckUpdateCompleted != null)
{
CheckUpdateCompleted(this, new EventArgs());
+11
View File
@@ -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
@@ -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;
}
}
+5
View File
@@ -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()
+4 -1
View File
@@ -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);
+86 -15
View File
@@ -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;
}
}
+34 -1
View File
@@ -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<Server> _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<Server> TempConfigs = new List<Server>();
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);
}
}
}
}
+179
View File
@@ -0,0 +1,179 @@
namespace Shadowsocks.View
{
partial class ImportFromWebForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
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.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;
}
}
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using Shadowsocks.Controller;
using System.Windows.Forms;
using System.Collections;
using Shadowsocks.Model;
using Shadowsocks.Properties;
namespace Shadowsocks.View
{
public partial class ImportFromWebForm : Form
{
private int _lastSelectedIndex;
public List<Server> Configs = new List<Server>();
public ImportFromWebForm()
{
this.Font = System.Drawing.SystemFonts.MessageBoxFont;
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
InitializeComponent();
UpdateTexts();
}
private void UpdateTexts()
{
this.Text = I18N.GetString("Import");
AddConfigButton.Text = I18N.GetString("&Add Config");
ProviderURLLabel.Text = I18N.GetString("URL");
GetConfigButton.Text = I18N.GetString("&Get");
RemoveButton.Text = I18N.GetString("&Remove");
}
private void ImportFromWebForm_Load(object sender, EventArgs e)
{
}
private void GetConfigButton_Click(object sender, EventArgs e)
{
OnlineConfigController Config = new OnlineConfigController(URLTextBox.Text);
try
{
Server[] RawServers = Config.GetOnlineConfig();
this.Configs.Clear();
ConfigListBox.Items.Clear();
foreach (Server TempServer in RawServers)
{
Configs.Add(TempServer);
ConfigListBox.Items.Add(TempServer.FriendlyName());
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void RemoveButton_Click(object sender, EventArgs e)
{
_lastSelectedIndex = ConfigListBox.SelectedIndex;
if(_lastSelectedIndex>=0 && _lastSelectedIndex < Configs.Count)
{
Configs.RemoveAt(_lastSelectedIndex);
}
if (_lastSelectedIndex >= Configs.Count)
{
// can be -1
_lastSelectedIndex = Configs.Count - 1;
}
ConfigListBox.Items.Clear();
foreach (Server TempServer in Configs)
{
ConfigListBox.Items.Add(TempServer.FriendlyName());
}
}
private void AddConfigButton_Click(object sender, EventArgs e)
{
if (this.Configs.Count < 1)
{
string Message = "No valid configs to import!";
Message = I18N.GetString("No valid configs to import!");
MessageBox.Show(Message);
}
else
{
ConfigForm config = (ConfigForm)this.Owner;
config._onlineConfig = this.Configs;
this.Close();
}
}
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
+30 -5
View File
@@ -66,7 +66,9 @@ namespace Shadowsocks.View
UpdateTrayIcon();
_notifyIcon.Visible = true;
_notifyIcon.ContextMenu = contextMenu1;
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick;
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed;
this.updateChecker = new UpdateChecker();
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted;
@@ -78,7 +80,7 @@ namespace Shadowsocks.View
if (config.autoCheckUpdate)
{
_isStartupChecking = true;
updateChecker.CheckUpdate(config);
updateChecker.CheckUpdate(config, 3000);
}
if (config.isDefault)
@@ -256,7 +258,6 @@ namespace Shadowsocks.View
if (updateChecker.NewVersionFound)
{
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000);
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked;
_isFirstRun = false;
}
else if (!_isStartupChecking)
@@ -269,9 +270,23 @@ namespace Shadowsocks.View
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e)
{
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked;
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
System.Diagnostics.Process.Start("explorer.exe", argument);
if (updateChecker.NewVersionFound)
{
updateChecker.NewVersionFound = false; /* Reset the flag */
if (System.IO.File.Exists(updateChecker.LatestVersionLocalName))
{
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\"";
System.Diagnostics.Process.Start("explorer.exe", argument);
}
}
}
private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e)
{
if (updateChecker.NewVersionFound)
{
updateChecker.NewVersionFound = false; /* Reset the flag */
}
}
@@ -311,12 +326,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)
{
@@ -85,6 +85,9 @@ namespace Shadowsocks.View
{
string serverName = _servers[serverSelector.SelectedIndex];
_dataTable.Rows.Clear();
//return directly when no data is usable
if (_controller.availabilityStatistics?.FilteredStatistics == null) return;
List<AvailabilityStatistics.RawStatisticsData> statistics;
if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return;
IEnumerable<IGrouping<int, AvailabilityStatistics.RawStatisticsData>> dataGroups;
+6 -2
View File
@@ -8,11 +8,15 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" />
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.2.29.0" newVersion="2.2.29.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
+3 -1
View File
@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net4-client" />
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" />
<package id="Fody" version="1.29.3" targetFramework="net4-client" developmentDependency="true" />
<package id="Microsoft.Bcl" version="1.1.8" targetFramework="net4-client" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net4-client" />
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net4-client" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net4-client" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net4-client" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net4-client" />
</packages>
+39 -11
View File
@@ -64,6 +64,10 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<HintPath>3rd\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath>
@@ -90,22 +94,34 @@
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.IO.dll</HintPath>
<Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath>
<Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System.Net" />
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="System.Runtime, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll</HintPath>
<Reference Include="System.Net.Http, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.dll</HintPath>
<Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System.Threading.Tasks, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Threading.Tasks.dll</HintPath>
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.Primitives, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.WebRequest.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath>
<Private>True</Private>
<EmbedInteropTypes>False</EmbedInteropTypes>
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Forms.DataVisualization" />
@@ -169,7 +185,9 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\OnlineConfigController.cs" />
<Compile Include="Controller\Service\AvailabilityStatistics.cs" />
<Compile Include="Controller\Service\RSAHandler.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" />
<Compile Include="Controller\System\AutoStartup.cs" />
@@ -184,7 +202,7 @@
<Compile Include="Encryption\EncryptorBase.cs" />
<Compile Include="Encryption\EncryptorFactory.cs" />
<Compile Include="Encryption\IVEncryptor.cs" />
<Compile Include="Encryption\MbedTLS.cs" />
<Compile Include="Encryption\MbedTLS.cs" />
<Compile Include="Encryption\PolarSSL.cs" />
<Compile Include="Encryption\PolarSSLEncryptor.cs" />
<Compile Include="Encryption\Sodium.cs" />
@@ -192,6 +210,7 @@
<Compile Include="Encryption\IEncryptor.cs" />
<Compile Include="Controller\Service\PACServer.cs" />
<Compile Include="Model\LogViewerConfig.cs" />
<Compile Include="Model\OnlineConfigDataPackage.cs" />
<Compile Include="Model\Server.cs" />
<Compile Include="Model\Configuration.cs" />
<Compile Include="Model\StatisticsStrategyConfiguration.cs" />
@@ -222,6 +241,12 @@
<Compile Include="View\CalculationControl.Designer.cs">
<DependentUpon>CalculationControl.cs</DependentUpon>
</Compile>
<Compile Include="View\ImportFromWebForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="View\ImportFromWebForm.Designer.cs">
<DependentUpon>ImportFromWebForm.cs</DependentUpon>
</Compile>
<Compile Include="View\LogForm.cs">
<SubType>Form</SubType>
</Compile>
@@ -256,6 +281,9 @@
<EmbeddedResource Include="View\CalculationControl.resx">
<DependentUpon>CalculationControl.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\ImportFromWebForm.resx">
<DependentUpon>ImportFromWebForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="View\LogForm.resx">
<DependentUpon>LogForm.cs</DependentUpon>
</EmbeddedResource>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BouncyCastle" version="1.7.0" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>
+26
View File
@@ -30,7 +30,25 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="BouncyCastle.Crypto, Version=1.7.4137.9688, Culture=neutral, PublicKeyToken=a4292a325f69b123, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\BouncyCastle.1.7.0\lib\Net40-Client\BouncyCastle.Crypto.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=7.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.WebRequest" />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
@@ -54,6 +72,9 @@
<Name>shadowsocks-csharp</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
@@ -74,6 +95,11 @@
</Choose>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">