@@ -1,6 +1,7 @@ | |||
Backup/ | |||
shadowsocks-csharp/bin/ | |||
shadowsocks-csharp/obj/ | |||
bin/ | |||
obj/ | |||
shadowsocks-csharp/shadowsocks-csharp.csproj.user | |||
TestResults | |||
*.suo | |||
@@ -1,12 +1,17 @@ | |||
2.0.4 | |||
2.0.5 2014-11-09 | |||
- Fix QRCode size | |||
- Share over LAN option | |||
- Log to temp path instead | |||
2.0.4 2014-11-09 | |||
- Try to fix data corruption | |||
- Remove all configuration except x86 | |||
2.0.3 | |||
2.0.3 2014-11-08 | |||
- Support QRCode generation | |||
- Fix compatibility issues with some Chrome version | |||
2.0.2 | |||
2.0.2 2014-11-08 | |||
- Add remarks | |||
- Fix error when polipo is killed | |||
@@ -3,6 +3,8 @@ Shadowsocks for Windows | |||
[![Build Status]][Appveyor] | |||
Currently beta. Please file an issue if you find any bugs. | |||
### Features | |||
1. Native Windows UI | |||
@@ -3,6 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio Express 2012 for Windows Desktop | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}" | |||
ProjectSection(ProjectDependencies) = postProject | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} | |||
EndProjectSection | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|x86 = Debug|x86 | |||
@@ -14,6 +19,10 @@ Global | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Deploy.0 = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.ActiveCfg = Release|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.Build.0 = Release|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.ActiveCfg = Debug|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.Build.0 = Debug|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.ActiveCfg = Release|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.Build.0 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
@@ -11,12 +11,14 @@ namespace Shadowsocks.Controller | |||
class Local | |||
{ | |||
private Server config; | |||
private Server _server; | |||
private bool _shareOverLAN; | |||
//private Encryptor encryptor; | |||
Socket _listener; | |||
public Local(Server config) | |||
public Local(Configuration config) | |||
{ | |||
this.config = config; | |||
this._server = config.GetCurrentServer(); | |||
_shareOverLAN = config.shareOverLan; | |||
//this.encryptor = new Encryptor(config.method, config.password); | |||
} | |||
@@ -25,9 +27,17 @@ namespace Shadowsocks.Controller | |||
try | |||
{ | |||
// Create a TCP/IP socket. | |||
_listener = new Socket(AddressFamily.InterNetwork, | |||
SocketType.Stream, ProtocolType.Tcp); | |||
IPEndPoint localEndPoint = new IPEndPoint(0, config.local_port); | |||
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |||
_listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); | |||
IPEndPoint localEndPoint = null; | |||
if (_shareOverLAN) | |||
{ | |||
localEndPoint = new IPEndPoint(IPAddress.Any, _server.local_port); | |||
} | |||
else | |||
{ | |||
localEndPoint = new IPEndPoint(IPAddress.Loopback, _server.local_port); | |||
} | |||
// Bind the socket to the local endpoint and listen for incoming connections. | |||
_listener.Bind(localEndPoint); | |||
@@ -58,37 +68,20 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
// Get the socket that handles the client request. | |||
Socket listener = (Socket)ar.AsyncState; | |||
//if (!listener.Connected) | |||
//{ | |||
// return; | |||
//} | |||
Socket conn = listener.EndAccept(ar); | |||
conn.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); | |||
listener.BeginAccept( | |||
new AsyncCallback(AcceptCallback), | |||
listener); | |||
// Create the state object. | |||
Handler handler = new Handler(); | |||
handler.connection = conn; | |||
//if (encryptor.method == "table") | |||
//{ | |||
// handler.encryptor = encryptor; | |||
//} | |||
//else | |||
//{ | |||
// handler.encryptor = new Encryptor(config.method, config.password); | |||
//} | |||
handler.encryptor = EncryptorFactory.GetEncryptor(config.method, config.password); | |||
handler.config = config; | |||
handler.encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||
handler.config = _server; | |||
handler.Start(); | |||
//handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, | |||
// new AsyncCallback(ReadCallback), state); | |||
} | |||
catch | |||
{ | |||
@@ -119,6 +112,9 @@ namespace Shadowsocks.Controller | |||
public byte[] connetionSendBuffer = new byte[BufferSize]; | |||
// Received data string. | |||
public StringBuilder sb = new StringBuilder(); | |||
private bool connectionShutdown = false; | |||
private bool remoteShutdown = false; | |||
private bool closed = false; | |||
public void Start() | |||
@@ -138,6 +134,7 @@ namespace Shadowsocks.Controller | |||
remote = new Socket(ipAddress.AddressFamily, | |||
SocketType.Stream, ProtocolType.Tcp); | |||
remote.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true); | |||
// Connect to the remote endpoint. | |||
remote.BeginConnect(remoteEP, | |||
@@ -150,18 +147,30 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void CheckClose() | |||
{ | |||
if (connectionShutdown && remoteShutdown) | |||
{ | |||
this.Close(); | |||
} | |||
} | |||
public void Close() | |||
{ | |||
if (closed) | |||
lock (this) | |||
{ | |||
return; | |||
if (closed) | |||
{ | |||
return; | |||
} | |||
closed = true; | |||
} | |||
closed = true; | |||
if (connection != null) | |||
{ | |||
try | |||
{ | |||
connection.Shutdown(SocketShutdown.Send); | |||
connection.Shutdown(SocketShutdown.Both); | |||
connection.Close(); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -172,7 +181,8 @@ namespace Shadowsocks.Controller | |||
{ | |||
try | |||
{ | |||
remote.Shutdown(SocketShutdown.Send); | |||
remote.Shutdown(SocketShutdown.Both); | |||
remote.Close(); | |||
} | |||
catch (SocketException e) | |||
{ | |||
@@ -229,7 +239,7 @@ namespace Shadowsocks.Controller | |||
// reject socks 4 | |||
response = new byte[]{ 0, 91 }; | |||
} | |||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(handshakeSendCallback), null); | |||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); | |||
} | |||
else | |||
{ | |||
@@ -243,7 +253,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void handshakeSendCallback(IAsyncResult ar) | |||
private void HandshakeSendCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
@@ -275,7 +285,7 @@ namespace Shadowsocks.Controller | |||
if (bytesRead > 0) | |||
{ | |||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | |||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(startPipe), null); | |||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(StartPipe), null); | |||
} | |||
else | |||
{ | |||
@@ -290,15 +300,15 @@ namespace Shadowsocks.Controller | |||
} | |||
private void startPipe(IAsyncResult ar) | |||
private void StartPipe(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
connection.EndReceive(ar); | |||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, | |||
new AsyncCallback(pipeRemoteReceiveCallback), null); | |||
new AsyncCallback(PipeRemoteReceiveCallback), null); | |||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, | |||
new AsyncCallback(pipeConnectionReceiveCallback), null); | |||
new AsyncCallback(PipeConnectionReceiveCallback), null); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -307,7 +317,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void pipeRemoteReceiveCallback(IAsyncResult ar) | |||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | |||
{ | |||
try | |||
@@ -318,12 +328,14 @@ namespace Shadowsocks.Controller | |||
{ | |||
int bytesToSend; | |||
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); | |||
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(pipeConnectionSendCallback), null); | |||
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); | |||
} | |||
else | |||
{ | |||
Console.WriteLine("bytesRead: " + bytesRead.ToString()); | |||
this.Close(); | |||
//Console.WriteLine("bytesRead: " + bytesRead.ToString()); | |||
connection.Shutdown(SocketShutdown.Send); | |||
connectionShutdown = true; | |||
CheckClose(); | |||
} | |||
} | |||
catch (Exception e) | |||
@@ -333,7 +345,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void pipeConnectionReceiveCallback(IAsyncResult ar) | |||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | |||
{ | |||
try | |||
@@ -344,11 +356,13 @@ namespace Shadowsocks.Controller | |||
{ | |||
int bytesToSend; | |||
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); | |||
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(pipeRemoteSendCallback), null); | |||
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); | |||
} | |||
else | |||
{ | |||
this.Close(); | |||
remote.Shutdown(SocketShutdown.Send); | |||
remoteShutdown = true; | |||
CheckClose(); | |||
} | |||
} | |||
catch (Exception e) | |||
@@ -358,13 +372,13 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void pipeRemoteSendCallback(IAsyncResult ar) | |||
private void PipeRemoteSendCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
remote.EndSend(ar); | |||
connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, | |||
new AsyncCallback(pipeConnectionReceiveCallback), null); | |||
new AsyncCallback(PipeConnectionReceiveCallback), null); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -373,13 +387,13 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void pipeConnectionSendCallback(IAsyncResult ar) | |||
private void PipeConnectionSendCallback(IAsyncResult ar) | |||
{ | |||
try | |||
{ | |||
connection.EndSend(ar); | |||
remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, | |||
new AsyncCallback(pipeRemoteReceiveCallback), null); | |||
new AsyncCallback(PipeRemoteReceiveCallback), null); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -0,0 +1,34 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Text; | |||
namespace Shadowsocks.Controller | |||
{ | |||
public class Logging | |||
{ | |||
public static string LogFile; | |||
public static bool OpenLogFile() | |||
{ | |||
try | |||
{ | |||
string temppath = Path.GetTempPath(); | |||
LogFile = Path.Combine(temppath, "shadowsocks.log"); | |||
FileStream fs = new FileStream(LogFile, FileMode.Append); | |||
TextWriter tmp = Console.Out; | |||
StreamWriter sw = new StreamWriter(fs); | |||
sw.AutoFlush = true; | |||
Console.SetOut(sw); | |||
Console.SetError(sw); | |||
return true; | |||
} | |||
catch (IOException e) | |||
{ | |||
Console.WriteLine(e.ToString()); | |||
return false; | |||
} | |||
} | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
@@ -12,28 +13,51 @@ namespace Shadowsocks.Controller | |||
{ | |||
class PACServer | |||
{ | |||
private static int PORT = 8090; | |||
private static string PAC_FILE = "pac.txt"; | |||
Socket listener; | |||
Socket _listener; | |||
FileSystemWatcher watcher; | |||
public event EventHandler PACFileChanged; | |||
public void Start() | |||
public void Start(Configuration configuration) | |||
{ | |||
// Create a TCP/IP socket. | |||
listener = new Socket(AddressFamily.InterNetwork, | |||
SocketType.Stream, ProtocolType.Tcp); | |||
IPEndPoint localEndPoint = new IPEndPoint(0, 8090); | |||
// Bind the socket to the local endpoint and listen for incoming connections. | |||
listener.Bind(localEndPoint); | |||
listener.Listen(100); | |||
listener.BeginAccept( | |||
new AsyncCallback(AcceptCallback), | |||
listener); | |||
WatchPacFile(); | |||
try | |||
{ | |||
// Create a TCP/IP socket. | |||
_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | |||
_listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); | |||
IPEndPoint localEndPoint = null; | |||
if (configuration.shareOverLan) | |||
{ | |||
localEndPoint = new IPEndPoint(IPAddress.Any, PORT); | |||
} | |||
else | |||
{ | |||
localEndPoint = new IPEndPoint(IPAddress.Loopback, PORT); | |||
} | |||
// Bind the socket to the local endpoint and listen for incoming connections. | |||
_listener.Bind(localEndPoint); | |||
_listener.Listen(100); | |||
_listener.BeginAccept( | |||
new AsyncCallback(AcceptCallback), | |||
_listener); | |||
WatchPacFile(); | |||
} | |||
catch (SocketException) | |||
{ | |||
_listener.Close(); | |||
throw; | |||
} | |||
} | |||
public void Stop() | |||
{ | |||
_listener.Close(); | |||
_listener = null; | |||
} | |||
public string TouchPACFile() | |||
@@ -55,10 +79,10 @@ namespace Shadowsocks.Controller | |||
try | |||
{ | |||
Socket listener = (Socket)ar.AsyncState; | |||
Socket conn = listener.EndAccept(ar); | |||
listener.BeginAccept( | |||
new AsyncCallback(AcceptCallback), | |||
listener); | |||
Socket conn = listener.EndAccept(ar); | |||
conn.BeginReceive(new byte[1024], 0, 1024, 0, | |||
new AsyncCallback(ReceiveCallback), conn); | |||
@@ -92,7 +116,6 @@ namespace Shadowsocks.Controller | |||
return System.Text.Encoding.UTF8.GetString(buffer, 0, n); | |||
} | |||
} | |||
WatchPacFile(); | |||
} | |||
private void ReceiveCallback(IAsyncResult ar) | |||
@@ -104,7 +127,9 @@ namespace Shadowsocks.Controller | |||
string pac = GetPACContent(); | |||
string proxy = "PROXY 127.0.0.1:8123;"; | |||
IPEndPoint localEndPoint = (IPEndPoint)conn.LocalEndPoint; | |||
string proxy = "PROXY " + localEndPoint.Address + ":8123;"; | |||
pac = pac.Replace("__PROXY__", proxy); | |||
@@ -13,8 +13,9 @@ namespace Shadowsocks.Controller | |||
{ | |||
private Process _process; | |||
public void Start(Server config) | |||
public void Start(Configuration configuration) | |||
{ | |||
Server server = configuration.GetCurrentServer(); | |||
if (_process == null) | |||
{ | |||
Process[] existingPolipo = Process.GetProcessesByName("ss_polipo"); | |||
@@ -31,8 +32,9 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
string temppath = Path.GetTempPath(); | |||
string polipoConfig = Resources.polipo_config; | |||
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", config.local_port.ToString()); | |||
string polipoConfig = Resources.polipo_config; | |||
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", server.local_port.ToString()); | |||
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); | |||
FileManager.ByteArrayToFile(temppath + "/polipo.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); | |||
FileManager.UncompressFile(temppath + "/ss_polipo.exe", Resources.polipo_exe); | |||
@@ -41,11 +43,10 @@ namespace Shadowsocks.Controller | |||
_process.StartInfo.FileName = temppath + "/ss_polipo.exe"; | |||
_process.StartInfo.Arguments = "-c \"" + temppath + "/polipo.conf\""; | |||
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; | |||
_process.StartInfo.UseShellExecute = false; | |||
_process.StartInfo.UseShellExecute = true; | |||
_process.StartInfo.CreateNoWindow = true; | |||
_process.StartInfo.RedirectStandardOutput = true; | |||
_process.StartInfo.RedirectStandardError = true; | |||
//process.StandardOutput | |||
//_process.StartInfo.RedirectStandardOutput = true; | |||
//_process.StartInfo.RedirectStandardError = true; | |||
_process.Start(); | |||
} | |||
} | |||
@@ -1,7 +1,9 @@ | |||
using Shadowsocks.Model; | |||
using System.IO; | |||
using Shadowsocks.Model; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using System.Threading; | |||
namespace Shadowsocks.Controller | |||
{ | |||
@@ -25,6 +27,7 @@ namespace Shadowsocks.Controller | |||
public event EventHandler ConfigChanged; | |||
public event EventHandler EnableStatusChanged; | |||
public event EventHandler ShareOverLANStatusChanged; | |||
// when user clicked Edit PAC, and PAC file has already created | |||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | |||
@@ -33,14 +36,14 @@ namespace Shadowsocks.Controller | |||
{ | |||
_config = Configuration.Load(); | |||
polipoRunner = new PolipoRunner(); | |||
polipoRunner.Start(_config.GetCurrentServer()); | |||
local = new Local(_config.GetCurrentServer()); | |||
polipoRunner.Start(_config); | |||
local = new Local(_config); | |||
try | |||
{ | |||
local.Start(); | |||
pacServer = new PACServer(); | |||
pacServer.PACFileChanged += pacServer_PACFileChanged; | |||
pacServer.Start(); | |||
pacServer.Start(_config); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -50,25 +53,6 @@ namespace Shadowsocks.Controller | |||
UpdateSystemProxy(); | |||
} | |||
public void SaveConfig(Configuration newConfig) | |||
{ | |||
Configuration.Save(newConfig); | |||
// some logic in configuration updated the config when saving, we need to read it again | |||
_config = Configuration.Load(); | |||
local.Stop(); | |||
polipoRunner.Stop(); | |||
polipoRunner.Start(_config.GetCurrentServer()); | |||
local = new Local(_config.GetCurrentServer()); | |||
local.Start(); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
public Server GetCurrentServer() | |||
{ | |||
return _config.GetCurrentServer(); | |||
@@ -80,6 +64,11 @@ namespace Shadowsocks.Controller | |||
return Configuration.Load(); | |||
} | |||
public void SaveServers(List<Server> servers) | |||
{ | |||
_config.configs = servers; | |||
SaveConfig(_config); | |||
} | |||
public void ToggleEnable(bool enabled) | |||
{ | |||
@@ -92,6 +81,22 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
public void ToggleShareOverLAN(bool enabled) | |||
{ | |||
_config.shareOverLan = enabled; | |||
SaveConfig(_config); | |||
if (ShareOverLANStatusChanged != null) | |||
{ | |||
ShareOverLANStatusChanged(this, new EventArgs()); | |||
} | |||
} | |||
public void SelectServerIndex(int index) | |||
{ | |||
_config.index = index; | |||
SaveConfig(_config); | |||
} | |||
public void Stop() | |||
{ | |||
if (stopped) | |||
@@ -124,6 +129,34 @@ namespace Shadowsocks.Controller | |||
return "ss://" + base64; | |||
} | |||
protected void SaveConfig(Configuration newConfig) | |||
{ | |||
Configuration.Save(newConfig); | |||
// some logic in configuration updated the config when saving, we need to read it again | |||
_config = Configuration.Load(); | |||
pacServer.Stop(); | |||
local.Stop(); | |||
// don't put polipoRunner.Start() before pacServer.Stop() | |||
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 | |||
// though UseShellExecute is set to true now | |||
// http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open | |||
polipoRunner.Stop(); | |||
polipoRunner.Start(_config); | |||
local = new Local(_config); | |||
local.Start(); | |||
pacServer.Start(_config); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
private void UpdateSystemProxy() | |||
{ | |||
if (_config.enabled) | |||
@@ -140,6 +173,5 @@ namespace Shadowsocks.Controller | |||
{ | |||
UpdateSystemProxy(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,151 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Collections.Generic; | |||
using System.Net; | |||
using System.Reflection; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Xml; | |||
namespace Shadowsocks.Controller | |||
{ | |||
public class UpdateChecker | |||
{ | |||
private const string UpdateURL = "https://sourceforge.net/api/file/index/project-id/1817190/path/dist/mtime/desc/limit/10/rss"; | |||
public string LatestVersionNumber; | |||
public string LatestVersionURL; | |||
public event EventHandler NewVersionFound; | |||
public void CheckUpdate() | |||
{ | |||
// TODO test failures | |||
WebClient http = new WebClient(); | |||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||
http.DownloadStringAsync(new Uri(UpdateURL)); | |||
} | |||
public static int CompareVersion(string l, string r) | |||
{ | |||
var ls = l.Split('.'); | |||
var rs = r.Split('.'); | |||
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++) | |||
{ | |||
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0; | |||
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0; | |||
if (lp != rp) | |||
{ | |||
return lp - rp; | |||
} | |||
} | |||
return 0; | |||
} | |||
public class VersionComparer : IComparer<string> | |||
{ | |||
// Calls CaseInsensitiveComparer.Compare with the parameters reversed. | |||
public int Compare(string x, string y) | |||
{ | |||
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); | |||
} | |||
} | |||
private static string ParseVersionFromURL(string url) | |||
{ | |||
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase); | |||
if (match.Success) | |||
{ | |||
if (match.Groups.Count == 2) | |||
{ | |||
return match.Groups[1].Value; | |||
} | |||
} | |||
return null; | |||
} | |||
private void SortVersions(List<string> versions) | |||
{ | |||
versions.Sort(new VersionComparer()); | |||
} | |||
private bool IsNewVersion(string url) | |||
{ | |||
// check dotnet 4.0 | |||
AssemblyName[] references = Assembly.GetExecutingAssembly().GetReferencedAssemblies(); | |||
Version dotNetVersion = Environment.Version; | |||
foreach (AssemblyName reference in references) | |||
{ | |||
if (reference.Name == "mscorlib") | |||
{ | |||
dotNetVersion = reference.Version; | |||
} | |||
} | |||
if (dotNetVersion.Major >= 4) | |||
{ | |||
if (url.IndexOf("dotnet4.0") < 0) | |||
{ | |||
return false; | |||
} | |||
} | |||
else | |||
{ | |||
if (url.IndexOf("dotnet4.0") >= 0) | |||
{ | |||
return false; | |||
} | |||
} | |||
string version = ParseVersionFromURL(url); | |||
if (version == null) | |||
{ | |||
return false; | |||
} | |||
string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); | |||
return CompareVersion(version, currentVersion) > 0; | |||
} | |||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||
{ | |||
try | |||
{ | |||
string response = e.Result; | |||
XmlDocument xmlDoc = new XmlDocument(); | |||
xmlDoc.LoadXml(response); | |||
XmlNodeList elements = xmlDoc.GetElementsByTagName("media:content"); | |||
List<string> versions = new List<string>(); | |||
foreach (XmlNode el in elements) | |||
{ | |||
foreach (XmlAttribute attr in el.Attributes) | |||
{ | |||
if (attr.Name == "url") | |||
{ | |||
if (IsNewVersion(attr.Value)) | |||
{ | |||
versions.Add(attr.Value); | |||
} | |||
} | |||
} | |||
} | |||
if (versions.Count == 0) | |||
{ | |||
return; | |||
} | |||
// sort versions | |||
SortVersions(versions); | |||
LatestVersionURL = versions[versions.Count - 1]; | |||
LatestVersionNumber = ParseVersionFromURL(LatestVersionURL); | |||
if (NewVersionFound != null) | |||
{ | |||
NewVersionFound(this, new EventArgs()); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.Write(ex.ToString()); | |||
return; | |||
} | |||
} | |||
} | |||
} |
@@ -1,4 +1,4 @@ | |||
proxyAddress = "127.0.0.1" | |||
proxyAddress = "__POLIPO_BIND_IP__" | |||
socksParentProxy = "127.0.0.1:__SOCKS_PORT__" | |||
socksProxyType = socks5 | |||
@@ -103,56 +103,59 @@ namespace Shadowsocks.Encrypt | |||
private void InitCipher(ref byte[] ctx, byte[] iv, bool isCipher) | |||
{ | |||
ctx = new byte[_cipherInfo[3]]; | |||
byte[] realkey; | |||
if (_method == "rc4-md5") | |||
lock (ctx) | |||
{ | |||
byte[] temp = new byte[keyLen + ivLen]; | |||
realkey = new byte[keyLen]; | |||
Array.Copy(_key, 0, temp, 0, keyLen); | |||
Array.Copy(iv, 0, temp, keyLen, ivLen); | |||
realkey = MD5.Create().ComputeHash(temp); | |||
} | |||
else | |||
{ | |||
realkey = _key; | |||
} | |||
if (_cipher == CIPHER_AES) | |||
{ | |||
PolarSSL.aes_init(ctx); | |||
// PolarSSL takes key length by bit | |||
// since we'll use CFB mode, here we both do enc, not dec | |||
PolarSSL.aes_setkey_enc(ctx, realkey, keyLen * 8); | |||
if (isCipher) | |||
byte[] realkey; | |||
if (_method == "rc4-md5") | |||
{ | |||
_encryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _encryptIV, ivLen); | |||
byte[] temp = new byte[keyLen + ivLen]; | |||
realkey = new byte[keyLen]; | |||
Array.Copy(_key, 0, temp, 0, keyLen); | |||
Array.Copy(iv, 0, temp, keyLen, ivLen); | |||
realkey = MD5.Create().ComputeHash(temp); | |||
} | |||
else | |||
{ | |||
_decryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _decryptIV, ivLen); | |||
realkey = _key; | |||
} | |||
} | |||
else if (_cipher == CIPHER_BF) | |||
{ | |||
PolarSSL.blowfish_init(ctx); | |||
// PolarSSL takes key length by bit | |||
PolarSSL.blowfish_setkey(ctx, realkey, keyLen * 8); | |||
if (isCipher) | |||
if (_cipher == CIPHER_AES) | |||
{ | |||
_encryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _encryptIV, ivLen); | |||
PolarSSL.aes_init(ctx); | |||
// PolarSSL takes key length by bit | |||
// since we'll use CFB mode, here we both do enc, not dec | |||
PolarSSL.aes_setkey_enc(ctx, realkey, keyLen * 8); | |||
if (isCipher) | |||
{ | |||
_encryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _encryptIV, ivLen); | |||
} | |||
else | |||
{ | |||
_decryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _decryptIV, ivLen); | |||
} | |||
} | |||
else | |||
else if (_cipher == CIPHER_BF) | |||
{ | |||
_decryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _decryptIV, ivLen); | |||
PolarSSL.blowfish_init(ctx); | |||
// PolarSSL takes key length by bit | |||
PolarSSL.blowfish_setkey(ctx, realkey, keyLen * 8); | |||
if (isCipher) | |||
{ | |||
_encryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _encryptIV, ivLen); | |||
} | |||
else | |||
{ | |||
_decryptIV = new byte[ivLen]; | |||
Array.Copy(iv, _decryptIV, ivLen); | |||
} | |||
} | |||
else if (_cipher == CIPHER_RC4) | |||
{ | |||
PolarSSL.arc4_init(ctx); | |||
PolarSSL.arc4_setup(ctx, realkey, keyLen); | |||
} | |||
} | |||
else if (_cipher == CIPHER_RC4) | |||
{ | |||
PolarSSL.arc4_init(ctx); | |||
PolarSSL.arc4_setup(ctx, realkey, keyLen); | |||
} | |||
} | |||
@@ -170,36 +173,50 @@ namespace Shadowsocks.Encrypt | |||
lock (tempbuf) | |||
{ | |||
// C# could be multi-threaded | |||
lock (_encryptCtx) | |||
{ | |||
if (_disposed) | |||
{ | |||
throw new ObjectDisposedException(this.ToString()); | |||
} | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, tempbuf); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, tempbuf); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_crypt(_encryptCtx, length, buf, tempbuf); | |||
break; | |||
} | |||
outlength = length + ivLen; | |||
Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, outlength); | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
outlength = length; | |||
lock (_encryptCtx) | |||
{ | |||
if (_disposed) | |||
{ | |||
throw new ObjectDisposedException(this.ToString()); | |||
} | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, tempbuf); | |||
PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, tempbuf); | |||
PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_crypt(_encryptCtx, length, buf, tempbuf); | |||
PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf); | |||
break; | |||
} | |||
outlength = length + ivLen; | |||
Buffer.BlockCopy(tempbuf, 0, outbuf, ivLen, outlength); | |||
} | |||
} | |||
else | |||
{ | |||
outlength = length; | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_crypt_cfb128(_encryptCtx, PolarSSL.AES_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_crypt_cfb64(_encryptCtx, PolarSSL.BLOWFISH_ENCRYPT, length, ref _encryptIVOffset, _encryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_crypt(_encryptCtx, length, buf, outbuf); | |||
break; | |||
} | |||
} | |||
} | |||
@@ -214,36 +231,50 @@ namespace Shadowsocks.Encrypt | |||
{ | |||
// C# could be multi-threaded | |||
Buffer.BlockCopy(buf, ivLen, tempbuf, 0, length - ivLen); | |||
lock (_decryptCtx) | |||
{ | |||
if (_disposed) | |||
{ | |||
throw new ObjectDisposedException(this.ToString()); | |||
} | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_crypt(_decryptCtx, length - ivLen, tempbuf, outbuf); | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
outlength = length; | |||
lock (_decryptCtx) | |||
{ | |||
if (_disposed) | |||
{ | |||
throw new ObjectDisposedException(this.ToString()); | |||
} | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); | |||
PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length - ivLen, ref _decryptIVOffset, _decryptIV, tempbuf, outbuf); | |||
PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_crypt(_decryptCtx, length - ivLen, tempbuf, outbuf); | |||
PolarSSL.arc4_crypt(_decryptCtx, length, buf, outbuf); | |||
break; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
outlength = length; | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_crypt_cfb128(_decryptCtx, PolarSSL.AES_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_crypt_cfb64(_decryptCtx, PolarSSL.BLOWFISH_DECRYPT, length, ref _decryptIVOffset, _decryptIV, buf, outbuf); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_crypt(_decryptCtx, length, buf, outbuf); | |||
break; | |||
} | |||
} | |||
} | |||
#region IDisposable | |||
@@ -262,46 +293,53 @@ namespace Shadowsocks.Encrypt | |||
protected virtual void Dispose(bool disposing) | |||
{ | |||
if (!_disposed) | |||
lock (this) | |||
{ | |||
if (disposing) | |||
if (_disposed) | |||
{ | |||
return; | |||
} | |||
_disposed = true; | |||
} | |||
if (disposing) | |||
{ | |||
if (_encryptCtx != null) | |||
{ | |||
switch (_cipher) | |||
lock (_encryptCtx) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_free(_encryptCtx); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_free(_encryptCtx); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_free(_encryptCtx); | |||
break; | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_free(_encryptCtx); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_free(_encryptCtx); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_free(_encryptCtx); | |||
break; | |||
} | |||
} | |||
} | |||
if (_decryptCtx != null) | |||
{ | |||
switch (_cipher) | |||
lock (_decryptCtx) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_free(_decryptCtx); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_free(_decryptCtx); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_free(_decryptCtx); | |||
break; | |||
switch (_cipher) | |||
{ | |||
case CIPHER_AES: | |||
PolarSSL.aes_free(_decryptCtx); | |||
break; | |||
case CIPHER_BF: | |||
PolarSSL.blowfish_free(_decryptCtx); | |||
break; | |||
case CIPHER_RC4: | |||
PolarSSL.arc4_free(_decryptCtx); | |||
break; | |||
} | |||
} | |||
} | |||
_encryptCtx = null; | |||
_decryptCtx = null; | |||
_disposed = true; | |||
} | |||
} | |||
#endregion | |||
@@ -15,6 +15,7 @@ namespace Shadowsocks.Model | |||
public List<Server> configs; | |||
public int index; | |||
public bool enabled; | |||
public bool shareOverLan; | |||
public bool isDefault; | |||
private static string CONFIG_FILE = "gui-config.json"; | |||
@@ -23,7 +23,7 @@ namespace Shadowsocks | |||
[STAThread] | |||
static void Main() | |||
{ | |||
using (Mutex mutex = new Mutex(false, "Global\\" + Assembly.GetExecutingAssembly().GetType().GUID.ToString())) | |||
using (Mutex mutex = new Mutex(false, "Global\\" + "71981632-A427-497F-AB91-241CD227EC1F")) | |||
{ | |||
if (!mutex.WaitOne(0, false)) | |||
{ | |||
@@ -47,26 +47,13 @@ namespace Shadowsocks | |||
} | |||
LoadLibrary(dllPath); | |||
try | |||
{ | |||
FileStream fs = new FileStream("shadowsocks.log", FileMode.Append); | |||
TextWriter tmp = Console.Out; | |||
StreamWriter sw = new StreamWriter(fs); | |||
sw.AutoFlush = true; | |||
Console.SetOut(sw); | |||
Console.SetError(sw); | |||
} | |||
catch (IOException e) | |||
{ | |||
Console.WriteLine(e.ToString()); | |||
} | |||
Logging.OpenLogFile(); | |||
Application.EnableVisualStyles(); | |||
Application.SetCompatibleTextRenderingDefault(false); | |||
ShadowsocksController controller = new ShadowsocksController(); | |||
// TODO run without a main form to save RAM | |||
Application.Run(new ConfigForm(controller)); | |||
} | |||
} | |||
} | |||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices; | |||
// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值, | |||
// 方法是按如下所示使用“*”: | |||
// [assembly: AssemblyVersion("1.0.*")] | |||
[assembly: AssemblyVersion("2.0.4")] | |||
[assembly: AssemblyVersion("2.0.6")] | |||
// [assembly: AssemblyFileVersion("2.0.0")] |
@@ -71,7 +71,7 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to proxyAddress = "127.0.0.1" | |||
/// Looks up a localized string similar to proxyAddress = "__POLIPO_BIND_IP__" | |||
/// | |||
///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" | |||
///socksProxyType = socks5 | |||
@@ -50,12 +50,14 @@ | |||
this.panel1 = new System.Windows.Forms.Panel(); | |||
this.contextMenu1 = new System.Windows.Forms.ContextMenu(); | |||
this.enableItem = new System.Windows.Forms.MenuItem(); | |||
this.ShareOverLANItem = new System.Windows.Forms.MenuItem(); | |||
this.ServersItem = new System.Windows.Forms.MenuItem(); | |||
this.SeperatorItem = new System.Windows.Forms.MenuItem(); | |||
this.ConfigItem = new System.Windows.Forms.MenuItem(); | |||
this.menuItem4 = new System.Windows.Forms.MenuItem(); | |||
this.editPACFileItem = new System.Windows.Forms.MenuItem(); | |||
this.QRCodeItem = new System.Windows.Forms.MenuItem(); | |||
this.ShowLogItem = new System.Windows.Forms.MenuItem(); | |||
this.aboutItem = new System.Windows.Forms.MenuItem(); | |||
this.menuItem3 = new System.Windows.Forms.MenuItem(); | |||
this.quitItem = new System.Windows.Forms.MenuItem(); | |||
@@ -285,10 +287,12 @@ | |||
// | |||
this.contextMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||
this.enableItem, | |||
this.ShareOverLANItem, | |||
this.ServersItem, | |||
this.menuItem4, | |||
this.editPACFileItem, | |||
this.QRCodeItem, | |||
this.ShowLogItem, | |||
this.aboutItem, | |||
this.menuItem3, | |||
this.quitItem}); | |||
@@ -299,9 +303,15 @@ | |||
this.enableItem.Text = "&Enable"; | |||
this.enableItem.Click += new System.EventHandler(this.EnableItem_Click); | |||
// | |||
// ShareOverLANItem | |||
// | |||
this.ShareOverLANItem.Index = 1; | |||
this.ShareOverLANItem.Text = "Share over LAN"; | |||
this.ShareOverLANItem.Click += new System.EventHandler(this.ShareOverLANItem_Click); | |||
// | |||
// ServersItem | |||
// | |||
this.ServersItem.Index = 1; | |||
this.ServersItem.Index = 2; | |||
this.ServersItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||
this.SeperatorItem, | |||
this.ConfigItem}); | |||
@@ -320,35 +330,41 @@ | |||
// | |||
// menuItem4 | |||
// | |||
this.menuItem4.Index = 2; | |||
this.menuItem4.Index = 3; | |||
this.menuItem4.Text = "-"; | |||
// | |||
// editPACFileItem | |||
// | |||
this.editPACFileItem.Index = 3; | |||
this.editPACFileItem.Index = 4; | |||
this.editPACFileItem.Text = "Edit &PAC File..."; | |||
this.editPACFileItem.Click += new System.EventHandler(this.EditPACFileItem_Click); | |||
// | |||
// QRCodeItem | |||
// | |||
this.QRCodeItem.Index = 4; | |||
this.QRCodeItem.Index = 5; | |||
this.QRCodeItem.Text = "Show &QRCode..."; | |||
this.QRCodeItem.Click += new System.EventHandler(this.QRCodeItem_Click); | |||
// | |||
// ShowLogItem | |||
// | |||
this.ShowLogItem.Index = 6; | |||
this.ShowLogItem.Text = "Show Logs..."; | |||
this.ShowLogItem.Click += new System.EventHandler(this.ShowLogItem_Click); | |||
// | |||
// aboutItem | |||
// | |||
this.aboutItem.Index = 5; | |||
this.aboutItem.Index = 7; | |||
this.aboutItem.Text = "About..."; | |||
this.aboutItem.Click += new System.EventHandler(this.AboutItem_Click); | |||
// | |||
// menuItem3 | |||
// | |||
this.menuItem3.Index = 6; | |||
this.menuItem3.Index = 8; | |||
this.menuItem3.Text = "-"; | |||
// | |||
// quitItem | |||
// | |||
this.quitItem.Index = 7; | |||
this.quitItem.Index = 9; | |||
this.quitItem.Text = "&Quit"; | |||
this.quitItem.Click += new System.EventHandler(this.Quit_Click); | |||
// | |||
@@ -418,9 +434,10 @@ | |||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; | |||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); | |||
this.MaximizeBox = false; | |||
this.MinimizeBox = false; | |||
this.Name = "ConfigForm"; | |||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | |||
this.Text = "Shadowsocks"; | |||
this.Text = "Edit Servers"; | |||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ConfigForm_FormClosed); | |||
this.Load += new System.EventHandler(this.ConfigForm_Load); | |||
this.Shown += new System.EventHandler(this.ConfigForm_Shown); | |||
@@ -471,6 +488,8 @@ | |||
private System.Windows.Forms.TextBox RemarksTextBox; | |||
private System.Windows.Forms.Label label6; | |||
private System.Windows.Forms.MenuItem QRCodeItem; | |||
private System.Windows.Forms.MenuItem ShowLogItem; | |||
private System.Windows.Forms.MenuItem ShareOverLANItem; | |||
} | |||
} | |||
@@ -13,11 +13,12 @@ namespace Shadowsocks.View | |||
public partial class ConfigForm : Form | |||
{ | |||
private ShadowsocksController controller; | |||
private UpdateChecker updateChecker; | |||
// this is a copy of configuration that we are working on | |||
private Configuration modifiedConfiguration; | |||
private int oldSelectedIndex = -1; | |||
private bool isFirstRun; | |||
private Configuration _modifiedConfiguration; | |||
private int _oldSelectedIndex = -1; | |||
private bool _isFirstRun; | |||
public ConfigForm(ShadowsocksController controller) | |||
{ | |||
@@ -28,6 +29,10 @@ namespace Shadowsocks.View | |||
controller.EnableStatusChanged += controller_EnableStatusChanged; | |||
controller.ConfigChanged += controller_ConfigChanged; | |||
controller.PACFileReadyToOpen += controller_PACFileReadyToOpen; | |||
controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; | |||
this.updateChecker = new UpdateChecker(); | |||
updateChecker.NewVersionFound += updateChecker_NewVersionFound; | |||
LoadCurrentConfiguration(); | |||
} | |||
@@ -42,6 +47,11 @@ namespace Shadowsocks.View | |||
enableItem.Checked = controller.GetConfiguration().enabled; | |||
} | |||
void controller_ShareOverLANStatusChanged(object sender, EventArgs e) | |||
{ | |||
ShareOverLANItem.Checked = controller.GetConfiguration().shareOverLan; | |||
} | |||
void controller_PACFileReadyToOpen(object sender, ShadowsocksController.PathEventArgs e) | |||
{ | |||
string argument = @"/select, " + e.Path; | |||
@@ -49,6 +59,21 @@ namespace Shadowsocks.View | |||
System.Diagnostics.Process.Start("explorer.exe", argument); | |||
} | |||
void updateChecker_NewVersionFound(object sender, EventArgs e) | |||
{ | |||
notifyIcon1.BalloonTipTitle = "Shadowsocks " + updateChecker.LatestVersionNumber + " Update Found"; | |||
notifyIcon1.BalloonTipText = "Click here to download"; | |||
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info; | |||
notifyIcon1.BalloonTipClicked += notifyIcon1_BalloonTipClicked; | |||
notifyIcon1.ShowBalloonTip(5000); | |||
_isFirstRun = false; | |||
} | |||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | |||
{ | |||
Process.Start(updateChecker.LatestVersionURL); | |||
} | |||
private void ShowWindow() | |||
{ | |||
@@ -61,7 +86,7 @@ namespace Shadowsocks.View | |||
{ | |||
try | |||
{ | |||
if (oldSelectedIndex == -1 || oldSelectedIndex >= modifiedConfiguration.configs.Count) | |||
if (_oldSelectedIndex == -1 || _oldSelectedIndex >= _modifiedConfiguration.configs.Count) | |||
{ | |||
return true; | |||
} | |||
@@ -75,7 +100,8 @@ namespace Shadowsocks.View | |||
remarks = RemarksTextBox.Text | |||
}; | |||
Configuration.CheckServer(server); | |||
modifiedConfiguration.configs[oldSelectedIndex] = server; | |||
_modifiedConfiguration.configs[_oldSelectedIndex] = server; | |||
return true; | |||
} | |||
catch (FormatException) | |||
@@ -91,15 +117,15 @@ namespace Shadowsocks.View | |||
private void LoadSelectedServer() | |||
{ | |||
if (ServersListBox.SelectedIndex >= 0 && ServersListBox.SelectedIndex < modifiedConfiguration.configs.Count) | |||
if (ServersListBox.SelectedIndex >= 0 && ServersListBox.SelectedIndex < _modifiedConfiguration.configs.Count) | |||
{ | |||
Server server = modifiedConfiguration.configs[ServersListBox.SelectedIndex]; | |||
Server server = _modifiedConfiguration.configs[ServersListBox.SelectedIndex]; | |||
IPTextBox.Text = server.server; | |||
ServerPortTextBox.Text = server.server_port.ToString(); | |||
PasswordTextBox.Text = server.password; | |||
ProxyPortTextBox.Text = server.local_port.ToString(); | |||
EncryptionSelect.Text = server.method == null ? "aes-256-cfb" : server.method; | |||
EncryptionSelect.Text = server.method ?? "aes-256-cfb"; | |||
RemarksTextBox.Text = server.remarks; | |||
ServerGroupBox.Visible = true; | |||
//IPTextBox.Focus(); | |||
@@ -113,7 +139,7 @@ namespace Shadowsocks.View | |||
private void LoadConfiguration(Configuration configuration) | |||
{ | |||
ServersListBox.Items.Clear(); | |||
foreach (Server server in modifiedConfiguration.configs) | |||
foreach (Server server in _modifiedConfiguration.configs) | |||
{ | |||
ServersListBox.Items.Add(string.IsNullOrEmpty(server.server) ? "New server" : string.IsNullOrEmpty(server.remarks)? server.server + ":" + server.server_port : server.server + ":" + server.server_port + " (" + server.remarks + ")"); | |||
} | |||
@@ -121,14 +147,15 @@ namespace Shadowsocks.View | |||
private void LoadCurrentConfiguration() | |||
{ | |||
modifiedConfiguration = controller.GetConfiguration(); | |||
LoadConfiguration(modifiedConfiguration); | |||
oldSelectedIndex = modifiedConfiguration.index; | |||
ServersListBox.SelectedIndex = modifiedConfiguration.index; | |||
_modifiedConfiguration = controller.GetConfiguration(); | |||
LoadConfiguration(_modifiedConfiguration); | |||
_oldSelectedIndex = _modifiedConfiguration.index; | |||
ServersListBox.SelectedIndex = _modifiedConfiguration.index; | |||
LoadSelectedServer(); | |||
UpdateServersMenu(); | |||
enableItem.Checked = modifiedConfiguration.enabled; | |||
enableItem.Checked = _modifiedConfiguration.enabled; | |||
ShareOverLANItem.Checked = _modifiedConfiguration.shareOverLan; | |||
} | |||
private void UpdateServersMenu() | |||
@@ -167,13 +194,14 @@ namespace Shadowsocks.View | |||
} | |||
else | |||
{ | |||
isFirstRun = true; | |||
_isFirstRun = true; | |||
} | |||
updateChecker.CheckUpdate(); | |||
} | |||
private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) | |||
{ | |||
if (oldSelectedIndex == ServersListBox.SelectedIndex) | |||
if (_oldSelectedIndex == ServersListBox.SelectedIndex) | |||
{ | |||
// we are moving back to oldSelectedIndex or doing a force move | |||
return; | |||
@@ -181,11 +209,11 @@ namespace Shadowsocks.View | |||
if (!SaveOldSelectedServer()) | |||
{ | |||
// why this won't cause stack overflow? | |||
ServersListBox.SelectedIndex = oldSelectedIndex; | |||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||
return; | |||
} | |||
LoadSelectedServer(); | |||
oldSelectedIndex = ServersListBox.SelectedIndex; | |||
_oldSelectedIndex = ServersListBox.SelectedIndex; | |||
} | |||
private void AddButton_Click(object sender, EventArgs e) | |||
@@ -195,27 +223,27 @@ namespace Shadowsocks.View | |||
return; | |||
} | |||
Server server = Configuration.GetDefaultServer(); | |||
modifiedConfiguration.configs.Add(server); | |||
LoadConfiguration(modifiedConfiguration); | |||
ServersListBox.SelectedIndex = modifiedConfiguration.configs.Count - 1; | |||
oldSelectedIndex = ServersListBox.SelectedIndex; | |||
_modifiedConfiguration.configs.Add(server); | |||
LoadConfiguration(_modifiedConfiguration); | |||
ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1; | |||
_oldSelectedIndex = ServersListBox.SelectedIndex; | |||
} | |||
private void DeleteButton_Click(object sender, EventArgs e) | |||
{ | |||
oldSelectedIndex = ServersListBox.SelectedIndex; | |||
if (oldSelectedIndex >= 0 && oldSelectedIndex < modifiedConfiguration.configs.Count) | |||
_oldSelectedIndex = ServersListBox.SelectedIndex; | |||
if (_oldSelectedIndex >= 0 && _oldSelectedIndex < _modifiedConfiguration.configs.Count) | |||
{ | |||
modifiedConfiguration.configs.RemoveAt(oldSelectedIndex); | |||
_modifiedConfiguration.configs.RemoveAt(_oldSelectedIndex); | |||
} | |||
if (oldSelectedIndex >= modifiedConfiguration.configs.Count) | |||
if (_oldSelectedIndex >= _modifiedConfiguration.configs.Count) | |||
{ | |||
// can be -1 | |||
oldSelectedIndex = modifiedConfiguration.configs.Count - 1; | |||
_oldSelectedIndex = _modifiedConfiguration.configs.Count - 1; | |||
} | |||
ServersListBox.SelectedIndex = oldSelectedIndex; | |||
LoadConfiguration(modifiedConfiguration); | |||
ServersListBox.SelectedIndex = oldSelectedIndex; | |||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||
LoadConfiguration(_modifiedConfiguration); | |||
ServersListBox.SelectedIndex = _oldSelectedIndex; | |||
LoadSelectedServer(); | |||
} | |||
@@ -231,12 +259,13 @@ namespace Shadowsocks.View | |||
private void ShowFirstTimeBalloon() | |||
{ | |||
if (isFirstRun) | |||
if (_isFirstRun) | |||
{ | |||
notifyIcon1.BalloonTipTitle = "Shadowsocks is here"; | |||
notifyIcon1.BalloonTipText = "You can turn on/off Shadowsocks in the context menu"; | |||
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info; | |||
notifyIcon1.ShowBalloonTip(0); | |||
isFirstRun = false; | |||
_isFirstRun = false; | |||
} | |||
} | |||
@@ -246,12 +275,12 @@ namespace Shadowsocks.View | |||
{ | |||
return; | |||
} | |||
if (modifiedConfiguration.configs.Count == 0) | |||
if (_modifiedConfiguration.configs.Count == 0) | |||
{ | |||
MessageBox.Show("Please add at least one server"); | |||
return; | |||
} | |||
controller.SaveConfig(modifiedConfiguration); | |||
controller.SaveServers(_modifiedConfiguration.configs); | |||
this.Hide(); | |||
ShowFirstTimeBalloon(); | |||
} | |||
@@ -285,6 +314,12 @@ namespace Shadowsocks.View | |||
controller.ToggleEnable(enableItem.Checked); | |||
} | |||
private void ShareOverLANItem_Click(object sender, EventArgs e) | |||
{ | |||
ShareOverLANItem.Checked = !ShareOverLANItem.Checked; | |||
controller.ToggleShareOverLAN(ShareOverLANItem.Checked); | |||
} | |||
private void EditPACFileItem_Click(object sender, EventArgs e) | |||
{ | |||
controller.TouchPACFile(); | |||
@@ -293,9 +328,14 @@ namespace Shadowsocks.View | |||
private void AServerItem_Click(object sender, EventArgs e) | |||
{ | |||
MenuItem item = (MenuItem)sender; | |||
Configuration configuration = controller.GetConfiguration(); | |||
configuration.index = (int)item.Tag; | |||
controller.SaveConfig(configuration); | |||
controller.SelectServerIndex((int)item.Tag); | |||
} | |||
private void ShowLogItem_Click(object sender, EventArgs e) | |||
{ | |||
string argument = Logging.LogFile; | |||
System.Diagnostics.Process.Start("notepad.exe", argument); | |||
} | |||
private void ConfigForm_Shown(object sender, EventArgs e) | |||
@@ -25,35 +25,50 @@ namespace Shadowsocks.View | |||
private void GenQR(string ssconfig) | |||
{ | |||
string qrText = ssconfig; | |||
QRCode4CS.QRCode qrCoded = new QRCode4CS.QRCode(6, QRErrorCorrectLevel.H); | |||
qrCoded.AddData(qrText); | |||
qrCoded.Make(); | |||
int blockSize = 5; | |||
Bitmap drawArea = new Bitmap((qrCoded.GetModuleCount() * blockSize), (qrCoded.GetModuleCount() * blockSize)); | |||
for (int row = 0; row < qrCoded.GetModuleCount(); row++) | |||
QRCode4CS.Options options = new QRCode4CS.Options(); | |||
options.Text = qrText; | |||
QRCode4CS.QRCode qrCoded = null; | |||
bool success = false; | |||
foreach (var level in new QRErrorCorrectLevel[]{QRErrorCorrectLevel.H, QRErrorCorrectLevel.Q, QRErrorCorrectLevel.M, QRErrorCorrectLevel.L}) | |||
{ | |||
for (int col = 0; col < qrCoded.GetModuleCount(); col++) | |||
for (int i = 3; i < 10; i++) | |||
{ | |||
bool isDark = qrCoded.IsDark(row, col); | |||
if (isDark) | |||
try | |||
{ | |||
for (int y = 0; y < blockSize; y++) | |||
{ | |||
int myCol = (blockSize * (col - 1)) + (y + blockSize); | |||
for (int x = 0; x < blockSize; x++) | |||
{ | |||
drawArea.SetPixel((blockSize * (row - 1)) + (x + blockSize), myCol, Color.Black); | |||
} | |||
} | |||
options.TypeNumber = i; | |||
options.CorrectLevel = level; | |||
qrCoded = new QRCode4CS.QRCode(options); | |||
qrCoded.Make(); | |||
success = true; | |||
break; | |||
} | |||
catch | |||
{ | |||
qrCoded = null; | |||
continue; | |||
} | |||
else | |||
} | |||
if (success) | |||
break; | |||
} | |||
if (qrCoded == null) | |||
{ | |||
return; | |||
} | |||
int blockSize = Math.Max(200 / qrCoded.GetModuleCount(), 1); | |||
Bitmap drawArea = new Bitmap((qrCoded.GetModuleCount() * blockSize), (qrCoded.GetModuleCount() * blockSize)); | |||
using (Graphics g = Graphics.FromImage(drawArea)) | |||
{ | |||
g.Clear(Color.White); | |||
using (Brush b = new SolidBrush(Color.Black)) | |||
{ | |||
for (int row = 0; row < qrCoded.GetModuleCount(); row++) | |||
{ | |||
for (int y = 0; y < blockSize; y++) | |||
for (int col = 0; col < qrCoded.GetModuleCount(); col++) | |||
{ | |||
int myCol = (blockSize * (col - 1)) + (y + blockSize); | |||
for (int x = 0; x < blockSize; x++) | |||
if (qrCoded.IsDark(row, col)) | |||
{ | |||
drawArea.SetPixel((blockSize * (row - 1)) + (x + blockSize), myCol, Color.White); | |||
g.FillRectangle(b, blockSize * row, blockSize * col, blockSize, blockSize); | |||
} | |||
} | |||
} | |||
@@ -63,11 +63,14 @@ | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Drawing" /> | |||
<Reference Include="System.Windows.Forms" /> | |||
<Reference Include="System.XML" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="3rd\QRCodeCS.cs" /> | |||
<Compile Include="3rd\SimpleJson.cs" /> | |||
<Compile Include="Controller\FileManager.cs" /> | |||
<Compile Include="Controller\Logging.cs" /> | |||
<Compile Include="Controller\UpdateChecker.cs" /> | |||
<Compile Include="Encrypt\EncryptorBase.cs" /> | |||
<Compile Include="Encrypt\EncryptorFactory.cs" /> | |||
<Compile Include="Encrypt\PolarSSL.cs" /> | |||
@@ -0,0 +1,36 @@ | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
// General Information about an assembly is controlled through the following | |||
// set of attributes. Change these attribute values to modify the information | |||
// associated with an assembly. | |||
[assembly: AssemblyTitle("test")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("")] | |||
[assembly: AssemblyProduct("test")] | |||
[assembly: AssemblyCopyright("Copyright © 2014")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
// Setting ComVisible to false makes the types in this assembly not visible | |||
// to COM components. If you need to access a type in this assembly from | |||
// COM, set the ComVisible attribute to true on that type. | |||
[assembly: ComVisible(false)] | |||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||
[assembly: Guid("f74e87ac-7e3a-444b-a1d9-8b91a674c60f")] | |||
// Version information for an assembly consists of the following four values: | |||
// | |||
// Major Version | |||
// Minor Version | |||
// Build Number | |||
// Revision | |||
// | |||
// You can specify all the values or you can default the Build and Revision Numbers | |||
// by using the '*' as shown below: | |||
// [assembly: AssemblyVersion("1.0.*")] | |||
[assembly: AssemblyVersion("1.0.0.0")] | |||
[assembly: AssemblyFileVersion("1.0.0.0")] |
@@ -0,0 +1,22 @@ | |||
using System; | |||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
using Shadowsocks.Controller; | |||
namespace test | |||
{ | |||
[TestClass] | |||
public class UnitTest | |||
{ | |||
[TestMethod] | |||
public void TestCompareVersion() | |||
{ | |||
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1.0", "2.3.1") == 0); | |||
Assert.IsTrue(UpdateChecker.CompareVersion("1.2", "1.3") < 0); | |||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.2") > 0); | |||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.3") == 0); | |||
Assert.IsTrue(UpdateChecker.CompareVersion("1.2.1", "1.2") > 0); | |||
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1", "2.4") < 0); | |||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3.2", "1.3.1") > 0); | |||
} | |||
} | |||
} |
@@ -0,0 +1,81 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{45913187-0685-4903-B250-DCEF0479CD86}</ProjectGuid> | |||
<OutputType>Library</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>test</RootNamespace> | |||
<AssemblyName>test</AssemblyName> | |||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | |||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> | |||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | |||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath> | |||
<IsCodedUITest>False</IsCodedUITest> | |||
<TestProjectType>UnitTest</TestProjectType> | |||
<TargetFrameworkProfile /> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | |||
<OutputPath>bin\x86\Debug\</OutputPath> | |||
<PlatformTarget>x86</PlatformTarget> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> | |||
<OutputPath>bin\x86\Release\</OutputPath> | |||
<PlatformTarget>x86</PlatformTarget> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="System" /> | |||
</ItemGroup> | |||
<Choose> | |||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | |||
</ItemGroup> | |||
</When> | |||
<Otherwise> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" /> | |||
</ItemGroup> | |||
</Otherwise> | |||
</Choose> | |||
<ItemGroup> | |||
<Compile Include="UnitTest.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\shadowsocks-csharp\shadowsocks-csharp.csproj"> | |||
<Project>{8c02d2f7-7cdb-4d55-9f25-cd03ef4aa062}</Project> | |||
<Name>shadowsocks-csharp</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<Choose> | |||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<Private>False</Private> | |||
</Reference> | |||
</ItemGroup> | |||
</When> | |||
</Choose> | |||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<!-- 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"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |