Browse Source

Merge branch 'master' into feature/statistics_ui

# Conflicts:
#	shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
#	shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
#	shadowsocks-csharp/View/LogForm.Designer.cs
#	shadowsocks-csharp/View/MenuViewController.cs
tags/3.0
icylogic 10 years ago
parent
commit
aa2334c9fd
31 changed files with 614 additions and 231 deletions
  1. +9
    -0
      CHANGES
  2. +29
    -6
      README.md
  3. +1
    -1
      shadowsocks-csharp/Controller/I18N.cs
  4. +4
    -4
      shadowsocks-csharp/Controller/Logging.cs
  5. +22
    -11
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  6. +0
    -2
      shadowsocks-csharp/Controller/Service/Listener.cs
  7. +1
    -2
      shadowsocks-csharp/Controller/Service/PACServer.cs
  8. +7
    -5
      shadowsocks-csharp/Controller/Service/PolipoRunner.cs
  9. +0
    -1
      shadowsocks-csharp/Controller/Service/PortForwarder.cs
  10. +48
    -9
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  11. +6
    -1
      shadowsocks-csharp/Controller/Service/UDPRelay.cs
  12. +2
    -3
      shadowsocks-csharp/Controller/Service/UpdateChecker.cs
  13. +3
    -5
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  14. +3
    -3
      shadowsocks-csharp/Controller/Strategy/IStrategy.cs
  15. +18
    -11
      shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs
  16. +2
    -6
      shadowsocks-csharp/Controller/System/SystemProxy.cs
  17. +18
    -1
      shadowsocks-csharp/Data/cn.txt
  18. +0
    -1
      shadowsocks-csharp/Encryption/IVEncryptor.cs
  19. +2
    -2
      shadowsocks-csharp/Encryption/PolarSSL.cs
  20. +2
    -1
      shadowsocks-csharp/Encryption/Sodium.cs
  21. +44
    -42
      shadowsocks-csharp/Encryption/SodiumEncryptor.cs
  22. +0
    -2
      shadowsocks-csharp/Encryption/TableEncryptor.cs
  23. +5
    -3
      shadowsocks-csharp/Program.cs
  24. +43
    -3
      shadowsocks-csharp/Util/Util.cs
  25. +53
    -1
      shadowsocks-csharp/View/ConfigForm.Designer.cs
  26. +96
    -26
      shadowsocks-csharp/View/ConfigForm.cs
  27. +2
    -2
      shadowsocks-csharp/View/ConfigForm.resx
  28. +129
    -46
      shadowsocks-csharp/View/LogForm.Designer.cs
  29. +49
    -12
      shadowsocks-csharp/View/LogForm.cs
  30. +16
    -17
      shadowsocks-csharp/View/MenuViewController.cs
  31. +0
    -2
      shadowsocks-csharp/View/QRCodeSplashForm.cs

+ 9
- 0
CHANGES View File

@@ -1,3 +1,12 @@
2.5.6 2015-08-19
- Add portable mode. Create shadowsocks_portable_mode.txt to use it
- Support server reorder

2.5.5 2015-08-17
- Fix crash when enabling Availability Statistics and some servers can not be resolved
- Allow multiple instances
- Other fixes

2.5.4 2015-08-16 2.5.4 2015-08-16
- Hide Privoxy icon - Hide Privoxy icon




+ 29
- 6
README.md View File

@@ -3,34 +3,36 @@ Shadowsocks for Windows


[![Build Status]][Appveyor] [![Build Status]][Appveyor]


[中文说明]

#### Features #### Features


1. System proxy configuration 1. System proxy configuration
2. PAC mode and global mode 2. PAC mode and global mode
3. GFWList and user rules
3. [GFWList] and user rules
4. Supports HTTP proxy 4. Supports HTTP proxy
5. Supports server auto switching 5. Supports server auto switching
6. Supports UDP relay (see Usage) 6. Supports UDP relay (see Usage)


#### Download #### Download


Download a [latest release].
Download the [latest release].


#### Basic #### Basic


1. Find Shadowsocks icon in the notification tray 1. Find Shadowsocks icon in the notification tray
2. You can add multiple servers in servers menu 2. You can add multiple servers in servers menu
3. Select Enable System Proxy menu to enable system proxy. Please disable other
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 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 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 system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in Server -> Edit Servers
port in `Servers -> Edit Servers`


#### PAC #### PAC


1. You can change PAC rules by editing the PAC file. When you save the PAC file 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 with any editor, Shadowsocks will notify browsers about the change automatically
2. You can also update PAC file from GFWList (maintained by 3rd party)
2. You can also update PAC file from [GFWList] (maintained by 3rd party)
3. You can also use online PAC URL 3. You can also use online PAC URL


#### Server Auto Switching #### Server Auto Switching
@@ -44,7 +46,25 @@ with any editor, Shadowsocks will notify browsers about the change automatically
#### UDP #### UDP


For UDP, you need to use SocksCap or ProxyCap to force programs you want For UDP, you need to use SocksCap or ProxyCap to force programs you want
to proxy to tunnel over Shadowsocks
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 #### Develop


@@ -58,3 +78,6 @@ GPLv3
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp [Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master [Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases [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

+ 1
- 1
shadowsocks-csharp/Controller/I18N.cs View File

@@ -12,7 +12,7 @@ namespace Shadowsocks.Controller
static I18N() static I18N()
{ {
Strings = new Dictionary<string, string>(); Strings = new Dictionary<string, string>();
if (System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag.ToLowerInvariant().StartsWith("zh")) if (System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag.ToLowerInvariant().StartsWith("zh"))
{ {
string[] lines = Regex.Split(Resources.cn, "\r\n|\r|\n"); string[] lines = Regex.Split(Resources.cn, "\r\n|\r|\n");


+ 4
- 4
shadowsocks-csharp/Controller/Logging.cs View File

@@ -1,4 +1,5 @@
using System;
using Shadowsocks.Util;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
@@ -14,14 +15,14 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
string temppath = Path.GetTempPath();
string temppath = Utils.GetTempPath();
LogFile = Path.Combine(temppath, "shadowsocks.log"); LogFile = Path.Combine(temppath, "shadowsocks.log");
FileStream fs = new FileStream(LogFile, FileMode.Append); FileStream fs = new FileStream(LogFile, FileMode.Append);
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs);
sw.AutoFlush = true; sw.AutoFlush = true;
Console.SetOut(sw); Console.SetOut(sw);
Console.SetError(sw); Console.SetError(sw);
return true; return true;
} }
catch (IOException e) catch (IOException e)
@@ -71,7 +72,6 @@ namespace Shadowsocks.Controller
Console.WriteLine(e); Console.WriteLine(e);
} }
} }
} }
// Simply extended System.IO.StreamWriter for adding timestamp workaround // Simply extended System.IO.StreamWriter for adding timestamp workaround


+ 22
- 11
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -10,13 +10,15 @@ using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using Shadowsocks.Model; using Shadowsocks.Model;
using SimpleJson = SimpleJson.SimpleJson; using SimpleJson = SimpleJson.SimpleJson;
using Shadowsocks.Util;
using Timer = System.Threading.Timer; using Timer = System.Threading.Timer;


namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
using DataUnit = KeyValuePair<string, string>; using DataUnit = KeyValuePair<string, string>;
using DataList = List<KeyValuePair<string, string>>; using DataList = List<KeyValuePair<string, string>>;
class AvailabilityStatistics

internal class AvailabilityStatistics
{ {
private const string StatisticsFilesName = "shadowsocks.availability.csv"; private const string StatisticsFilesName = "shadowsocks.availability.csv";
private const string Delimiter = ","; private const string Delimiter = ",";
@@ -33,7 +35,7 @@ namespace Shadowsocks.Controller
//static constructor to initialize every public static fields before refereced //static constructor to initialize every public static fields before refereced
static AvailabilityStatistics() static AvailabilityStatistics()
{ {
var temppath = Path.GetTempPath();
string temppath = Utils.GetTempPath();
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName);
} }


@@ -41,10 +43,18 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
_timer?.Dispose();
if (!enabled) return true;
_state = new State();
_timer = new Timer(Evaluate, _state, delayBeforeStart, Interval);
if (enabled)
{
if (_timer?.Change(0, Interval) == null)
{
_state = new State();
_timer = new Timer(Evaluate, _state, 0, Interval);
}
}
else
{
_timer?.Dispose();
}
return true; return true;
} }
catch (Exception e) catch (Exception e)
@@ -80,7 +90,7 @@ namespace Shadowsocks.Controller
string country = obj["country"]; string country = obj["country"];
string city = obj["city"]; string city = obj["city"];
string isp = obj["isp"]; string isp = obj["isp"];
string regionName= obj["regionName"];
string regionName = obj["regionName"];
if (country == null || city == null || isp == null || regionName == null) return ret; if (country == null || city == null || isp == null || regionName == null) return ret;
ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}"); ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}");
ret[1] = new DataUnit(State.ISP, isp); ret[1] = new DataUnit(State.ISP, isp);
@@ -93,7 +103,8 @@ namespace Shadowsocks.Controller
if (server.server == "") return null; if (server.server == "") return null;
var ping = new Ping(); var ping = new Ping();
var ret = new List<DataList>(); var ret = new List<DataList>();
foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")))
foreach (
var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")))
{ {
//ICMP echo. we can also set options and special bytes //ICMP echo. we can also set options and special bytes
try try
@@ -108,7 +119,7 @@ namespace Shadowsocks.Controller
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply //new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply
}); });
} }
catch (PingException e)
catch (Exception e)
{ {
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
} }
@@ -138,11 +149,11 @@ namespace Shadowsocks.Controller
if (!File.Exists(AvailabilityStatisticsFile)) if (!File.Exists(AvailabilityStatisticsFile))
{ {
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
lines = new[] { headerLine, dataLine };
lines = new[] {headerLine, dataLine};
} }
else else
{ {
lines = new[] { dataLine };
lines = new[] {dataLine};
} }
File.AppendAllLines(AvailabilityStatisticsFile, lines); File.AppendAllLines(AvailabilityStatisticsFile, lines);
} }


+ 0
- 2
shadowsocks-csharp/Controller/Service/Listener.cs View File

@@ -77,7 +77,6 @@ namespace Shadowsocks.Controller
_udpSocket.Bind(localEndPoint); _udpSocket.Bind(localEndPoint);
_tcpSocket.Listen(1024); _tcpSocket.Listen(1024);
// Start an asynchronous socket to listen for connections. // Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started"); Console.WriteLine("Shadowsocks started");
_tcpSocket.BeginAccept( _tcpSocket.BeginAccept(
@@ -185,7 +184,6 @@ namespace Shadowsocks.Controller
} }
} }
private void ReceiveCallback(IAsyncResult ar) private void ReceiveCallback(IAsyncResult ar)
{ {
object[] state = (object[])ar.AsyncState; object[] state = (object[])ar.AsyncState;


+ 1
- 2
shadowsocks-csharp/Controller/Service/PACServer.cs View File

@@ -86,7 +86,6 @@ namespace Shadowsocks.Controller
} }
} }
public string TouchPACFile() public string TouchPACFile()
{ {
if (File.Exists(PAC_FILE)) if (File.Exists(PAC_FILE))
@@ -146,7 +145,7 @@ Connection: Close
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; ", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = System.Text.Encoding.UTF8.GetBytes(text); byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
} }
catch (Exception e) catch (Exception e)
{ {


+ 7
- 5
shadowsocks-csharp/Controller/Service/PolipoRunner.cs View File

@@ -9,6 +9,7 @@ using System.Text;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Shadowsocks.Util;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
@@ -20,7 +21,7 @@ namespace Shadowsocks.Controller
static PolipoRunner() static PolipoRunner()
{ {
temppath = Path.GetTempPath();
temppath = Utils.GetTempPath();
try try
{ {
FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe); FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe);
@@ -65,10 +66,13 @@ namespace Shadowsocks.Controller
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig));
if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) {
temppath = temppath + "\\";
}
_process = new Process(); _process = new Process();
// Configure the process using the StartInfo properties. // Configure the process using the StartInfo properties.
_process.StartInfo.FileName = temppath + "/ss_privoxy.exe";
_process.StartInfo.Arguments = " \"" + temppath + "/privoxy.conf\"";
_process.StartInfo.FileName = temppath + "ss_privoxy.exe";
_process.StartInfo.Arguments = " \"" + temppath + "privoxy.conf\"";
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_process.StartInfo.UseShellExecute = true; _process.StartInfo.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true; _process.StartInfo.CreateNoWindow = true;
@@ -144,7 +148,6 @@ namespace Shadowsocks.Controller
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public void RefreshTrayArea() public void RefreshTrayArea()
{ {
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null); IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
@@ -161,7 +164,6 @@ namespace Shadowsocks.Controller
RefreshTrayArea(notificationAreaHandle); RefreshTrayArea(notificationAreaHandle);
} }
private static void RefreshTrayArea(IntPtr windowHandle) private static void RefreshTrayArea(IntPtr windowHandle)
{ {
const uint wmMousemove = 0x0200; const uint wmMousemove = 0x0200;


+ 0
- 1
shadowsocks-csharp/Controller/Service/PortForwarder.cs View File

@@ -103,7 +103,6 @@ namespace Shadowsocks.Controller
} }
} }
private void StartPipe(IAsyncResult ar) private void StartPipe(IAsyncResult ar)
{ {
if (_closed) if (_closed)


+ 48
- 9
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -14,9 +14,18 @@ namespace Shadowsocks.Controller
class TCPRelay : Listener.Service class TCPRelay : Listener.Service
{ {
private ShadowsocksController _controller; private ShadowsocksController _controller;
private DateTime _lastSweepTime;
public ISet<Handler> Handlers
{
get; set;
}
public TCPRelay(ShadowsocksController controller) public TCPRelay(ShadowsocksController controller)
{ {
this._controller = controller; this._controller = controller;
this.Handlers = new HashSet<Handler>();
this._lastSweepTime = DateTime.Now;
} }
public bool Handle(byte[] firstPacket, int length, Socket socket, object state) public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
@@ -33,9 +42,33 @@ namespace Shadowsocks.Controller
Handler handler = new Handler(); Handler handler = new Handler();
handler.connection = socket; handler.connection = socket;
handler.controller = _controller; handler.controller = _controller;
handler.relay = this;
handler.Start(firstPacket, length); handler.Start(firstPacket, length);
return true;
IList<Handler> handlersToClose = new List<Handler>();
lock (this.Handlers)
{
this.Handlers.Add(handler);
Logging.Debug($"connections: {Handlers.Count}");
DateTime now = DateTime.Now;
if (now - _lastSweepTime > TimeSpan.FromSeconds(1))
{
_lastSweepTime = now;
foreach (Handler handler1 in this.Handlers)
{
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900))
{
handlersToClose.Add(handler1);
}
}
}
}
foreach (Handler handler1 in handlersToClose)
{
Logging.Debug("Closing timed out connection");
handler1.Close();
}
return true;
} }
} }
@@ -48,6 +81,10 @@ namespace Shadowsocks.Controller
public Socket remote; public Socket remote;
public Socket connection; public Socket connection;
public ShadowsocksController controller; public ShadowsocksController controller;
public TCPRelay relay;
public DateTime lastActivity;
private int retryCount = 0; private int retryCount = 0;
private bool connected; private bool connected;
@@ -55,7 +92,7 @@ namespace Shadowsocks.Controller
private byte[] _firstPacket; private byte[] _firstPacket;
private int _firstPacketLength; private int _firstPacketLength;
// Size of receive buffer. // Size of receive buffer.
public const int RecvSize = 16384;
public const int RecvSize = 8192;
public const int BufferSize = RecvSize + 32; public const int BufferSize = RecvSize + 32;
private int totalRead = 0; private int totalRead = 0;
@@ -74,7 +111,7 @@ namespace Shadowsocks.Controller
private bool connectionShutdown = false; private bool connectionShutdown = false;
private bool remoteShutdown = false; private bool remoteShutdown = false;
private bool closed = false; private bool closed = false;
private object encryptionLock = new object(); private object encryptionLock = new object();
private object decryptionLock = new object(); private object decryptionLock = new object();
@@ -96,6 +133,7 @@ namespace Shadowsocks.Controller
this._firstPacket = firstPacket; this._firstPacket = firstPacket;
this._firstPacketLength = length; this._firstPacketLength = length;
this.HandshakeReceive(); this.HandshakeReceive();
this.lastActivity = DateTime.Now;
} }
private void CheckClose() private void CheckClose()
@@ -108,6 +146,11 @@ namespace Shadowsocks.Controller
public void Close() public void Close()
{ {
lock (relay.Handlers)
{
Logging.Debug($"connections: {relay.Handlers.Count}");
relay.Handlers.Remove(this);
}
lock (this) lock (this)
{ {
if (closed) if (closed)
@@ -152,7 +195,6 @@ namespace Shadowsocks.Controller
} }
} }
private void HandshakeReceive() private void HandshakeReceive()
{ {
if (closed) if (closed)
@@ -222,7 +264,7 @@ namespace Shadowsocks.Controller
try try
{ {
int bytesRead = connection.EndReceive(ar); int bytesRead = connection.EndReceive(ar);
if (bytesRead >= 3) if (bytesRead >= 3)
{ {
command = connetionRecvBuffer[1]; command = connetionRecvBuffer[1];
@@ -272,7 +314,6 @@ namespace Shadowsocks.Controller
private void ReadAll(IAsyncResult ar) private void ReadAll(IAsyncResult ar)
{ {
if (closed) if (closed)
{ {
return; return;
@@ -347,7 +388,6 @@ namespace Shadowsocks.Controller
} }
IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port);
remote = new Socket(ipAddress.AddressFamily, remote = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp); SocketType.Stream, ProtocolType.Tcp);
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
@@ -485,6 +525,7 @@ namespace Shadowsocks.Controller
if (bytesRead > 0) if (bytesRead > 0)
{ {
this.lastActivity = DateTime.Now;
int bytesToSend; int bytesToSend;
lock (decryptionLock) lock (decryptionLock)
{ {
@@ -548,7 +589,6 @@ namespace Shadowsocks.Controller
} }
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy(); IStrategy strategy = controller.GetCurrentStrategy();
if (strategy != null) if (strategy != null)
{ {
@@ -607,5 +647,4 @@ namespace Shadowsocks.Controller
} }
} }
} }
} }

+ 6
- 1
shadowsocks-csharp/Controller/Service/UDPRelay.cs View File

@@ -108,9 +108,11 @@ namespace Shadowsocks.Controller
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)
{ {
// TODO: handle the ObjectDisposedException
} }
catch (Exception) catch (Exception)
{ {
// TODO: need more think about handle other Exceptions, or should remove this catch().
} }
finally finally
{ {
@@ -124,9 +126,11 @@ namespace Shadowsocks.Controller
} }
catch (ObjectDisposedException) catch (ObjectDisposedException)
{ {
// TODO: handle the ObjectDisposedException
} }
catch (Exception) catch (Exception)
{ {
// TODO: need more think about handle other Exceptions, or should remove this catch().
} }
finally finally
{ {
@@ -134,6 +138,8 @@ namespace Shadowsocks.Controller
} }
} }
} }
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
class LRUCache<K, V> where V : UDPRelay.UDPHandler class LRUCache<K, V> where V : UDPRelay.UDPHandler
{ {
@@ -196,5 +202,4 @@ namespace Shadowsocks.Controller
public K key; public K key;
public V value; public V value;
} }
} }

+ 2
- 3
shadowsocks-csharp/Controller/Service/UpdateChecker.cs View File

@@ -12,13 +12,13 @@ namespace Shadowsocks.Controller
{ {
public class UpdateChecker public class UpdateChecker
{ {
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-csharp/releases";
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases";
public string LatestVersionNumber; public string LatestVersionNumber;
public string LatestVersionURL; public string LatestVersionURL;
public event EventHandler NewVersionFound; public event EventHandler NewVersionFound;
public const string Version = "2.5.4";
public const string Version = "2.5.6";
public void CheckUpdate(Configuration config) public void CheckUpdate(Configuration config)
{ {
@@ -53,7 +53,6 @@ namespace Shadowsocks.Controller
{ {
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y));
} }
} }
private static string ParseVersionFromURL(string url) private static string ParseVersionFromURL(string url)


+ 3
- 5
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -39,7 +39,7 @@ namespace Shadowsocks.Controller
public event EventHandler EnableStatusChanged; public event EventHandler EnableStatusChanged;
public event EventHandler EnableGlobalChanged; public event EventHandler EnableGlobalChanged;
public event EventHandler ShareOverLANStatusChanged; public event EventHandler ShareOverLANStatusChanged;
// when user clicked Edit PAC, and PAC file has already created // when user clicked Edit PAC, and PAC file has already created
public event EventHandler<PathEventArgs> PACFileReadyToOpen; public event EventHandler<PathEventArgs> PACFileReadyToOpen;
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen; public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen;
@@ -365,17 +365,15 @@ namespace Shadowsocks.Controller
} }
UpdateSystemProxy(); UpdateSystemProxy();
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
} }
protected void SaveConfig(Configuration newConfig) protected void SaveConfig(Configuration newConfig)
{ {
Configuration.Save(newConfig); Configuration.Save(newConfig);
Reload(); Reload();
} }
private void UpdateSystemProxy() private void UpdateSystemProxy()
{ {
if (_config.enabled) if (_config.enabled)
@@ -422,7 +420,7 @@ namespace Shadowsocks.Controller
{ {
while (true) while (true)
{ {
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(false);
Thread.Sleep(30 * 1000); Thread.Sleep(30 * 1000);
} }
} }


+ 3
- 3
shadowsocks-csharp/Controller/Strategy/IStrategy.cs View File

@@ -14,17 +14,17 @@ namespace Shadowsocks.Controller.Strategy
/* /*
* IStrategy * IStrategy
*
*
* Subclasses must be thread-safe * Subclasses must be thread-safe
*/ */
public interface IStrategy public interface IStrategy
{ {
string Name { get; } string Name { get; }
string ID { get; } string ID { get; }
/* /*
* Called when servers need to be reloaded, i.e. new configuration saved
* Called when servers need to be reloaded, i.e. new configuration saved
*/ */
void ReloadServers(); void ReloadServers();


+ 18
- 11
shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs View File

@@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text;
using Shadowsocks.Model;
using System.IO;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Threading; using System.Threading;
using Shadowsocks.Model; using Shadowsocks.Model;
@@ -18,15 +21,6 @@ namespace Shadowsocks.Controller.Strategy
private const int CachedInterval = 30*60*1000; //choose a new server every 30 minutes private const int CachedInterval = 30*60*1000; //choose a new server every 30 minutes
private const int RetryInterval = 2*60*1000; //choose a new server every 30 minutes private const int RetryInterval = 2*60*1000; //choose a new server every 30 minutes


public class StatisticsData
{
public int SuccessTimes;
public int TimedOutTimes;
public int AverageResponse;
public int MinResponse;
public int MaxResponse;
}

public StatisticsStrategy(ShadowsocksController controller) public StatisticsStrategy(ShadowsocksController controller)
{ {
_controller = controller; _controller = controller;
@@ -99,6 +93,15 @@ namespace Shadowsocks.Controller.Strategy
return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss return (double)data.SuccessTimes / (data.SuccessTimes + data.TimedOutTimes); //simply choose min package loss
} }


private class StatisticsData
{
public int SuccessTimes;
public int TimedOutTimes;
public int AverageResponse;
public int MinResponse;
public int MaxResponse;
}

private void ChooseNewServer(List<Server> servers) private void ChooseNewServer(List<Server> servers)
{ {
if (_statistics == null || servers.Count == 0) if (_statistics == null || servers.Count == 0)
@@ -118,7 +121,7 @@ namespace Shadowsocks.Controller.Strategy
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); ).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2);


if (!_currentServer.Equals(bestResult.server)) //output when enabled if (!_currentServer.Equals(bestResult.server)) //output when enabled
{
{
LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}"); LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}");
} }
_currentServer = bestResult.server; _currentServer = bestResult.server;
@@ -137,7 +140,7 @@ namespace Shadowsocks.Controller.Strategy
} }
} }


public string ID => "com.shadowsocks.strategy.statistics";
public string ID => "com.shadowsocks.strategy.scbs";


public string Name => I18N.GetString("Choose By Total Package Loss"); public string Name => I18N.GetString("Choose By Total Package Loss");


@@ -148,6 +151,9 @@ namespace Shadowsocks.Controller.Strategy
{ {
ChooseNewServer(_controller.GetCurrentConfiguration().configs); ChooseNewServer(_controller.GetCurrentConfiguration().configs);
} }
if (oldServer != _currentServer)
{
}
return _currentServer; //current server cached for CachedInterval return _currentServer; //current server cached for CachedInterval
} }


@@ -176,5 +182,6 @@ namespace Shadowsocks.Controller.Strategy
{ {
//TODO: combine this part of data with ICMP statics //TODO: combine this part of data with ICMP statics
} }

} }
} }

+ 2
- 6
shadowsocks-csharp/Controller/System/SystemProxy.cs View File

@@ -56,18 +56,14 @@ namespace Shadowsocks.Controller
pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now); pacUrl = "http://127.0.0.1:" + config.localPort.ToString() + "/pac?t=" + GetTimestamp(DateTime.Now);
registry.SetValue("ProxyEnable", 0); registry.SetValue("ProxyEnable", 0);
var readProxyServer = registry.GetValue("ProxyServer"); var readProxyServer = registry.GetValue("ProxyServer");
if (readProxyServer != null && readProxyServer.Equals("127.0.0.1:" + config.localPort.ToString()))
registry.SetValue("ProxyServer", "");
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", pacUrl); registry.SetValue("AutoConfigURL", pacUrl);
} }
} }
else else
{ {
registry.SetValue("ProxyEnable", 0); registry.SetValue("ProxyEnable", 0);
if (global)
{
registry.SetValue("ProxyServer", "");
}
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", ""); registry.SetValue("AutoConfigURL", "");
} }
//Set AutoDetectProxy Off //Set AutoDetectProxy Off


+ 18
- 1
shadowsocks-csharp/Data/cn.txt View File

@@ -19,6 +19,7 @@ Update Local PAC from GFWList=从 GFWList 更新本地 PAC
Edit User Rule for GFWList...=编辑 GFWList 的用户规则... Edit User Rule for GFWList...=编辑 GFWList 的用户规则...
Show QRCode...=显示二维码... Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码... Scan QRCode from Screen...=扫描屏幕上的二维码...
Availability Statistics=统计可用性
Show Logs...=显示日志... Show Logs...=显示日志...
About...=关于... About...=关于...
Quit=退出 Quit=退出
@@ -41,6 +42,19 @@ Remarks=备注
OK=确定 OK=确定
Cancel=取消 Cancel=取消
New server=未配置的服务器 New server=未配置的服务器
Move &Up=上移(&U)
Move D&own=下移(&O)

# Log Form

&File=文件(&F)
&Open Location=在资源管理器中打开(&O)
E&xit=退出(&X)
&Clean logs=清空(&C)
&Font=字体(&F)
&Wrap text=自动换行(&W)
&Top most=置顶(&T)
Log Viewer=日志查看器


# QRCode Form # QRCode Form


@@ -72,7 +86,10 @@ Failed to update PAC file =更新 PAC 文件失败
PAC updated=更新 PAC 成功 PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。 No updates found. Please report to GFWList if you have problems with it.=未发现更新。如有问题请提交给 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置 No QRCode found. Try to zoom in or move it to the center of the screen.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置
Shadowsocks is already running.=Shadowsocks 已经在运行。
Find Shadowsocks icon in your notify tray.=请在任务栏里寻找 Shadowsocks 图标。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同时启动多个,可以另外复制一份到别的目录。
Failed to decode QRCode=无法解析二维码 Failed to decode QRCode=无法解析二维码
Failed to update registry=无法修改注册表 Failed to update registry=无法修改注册表
System Proxy On: =系统代理已启用: System Proxy On: =系统代理已启用:
Running: Port {0}=正在运行:端口 {0}
Running: Port {0}=正在运行:端口 {0}

+ 0
- 1
shadowsocks-csharp/Encryption/IVEncryptor.cs View File

@@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption
protected int keyLen; protected int keyLen;
protected int ivLen; protected int ivLen;
public IVEncryptor(string method, string password) public IVEncryptor(string method, string password)
: base(method, password) : base(method, password)
{ {


+ 2
- 2
shadowsocks-csharp/Encryption/PolarSSL.cs View File

@@ -1,5 +1,6 @@
using Shadowsocks.Controller; using Shadowsocks.Controller;
using Shadowsocks.Properties; using Shadowsocks.Properties;
using Shadowsocks.Util;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -18,7 +19,7 @@ namespace Shadowsocks.Encryption
static PolarSSL() static PolarSSL()
{ {
string tempPath = Path.GetTempPath();
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll"; string dllPath = tempPath + "/libsscrypto.dll";
try try
{ {
@@ -49,7 +50,6 @@ namespace Shadowsocks.Encryption
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]
public extern static int aes_crypt_cfb128(IntPtr ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output); public extern static int aes_crypt_cfb128(IntPtr ctx, int mode, int length, ref int iv_off, byte[] iv, byte[] input, byte[] output);
public const int ARC4_CTX_SIZE = 264; public const int ARC4_CTX_SIZE = 264;
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]


+ 2
- 1
shadowsocks-csharp/Encryption/Sodium.cs View File

@@ -1,5 +1,6 @@
using Shadowsocks.Controller; using Shadowsocks.Controller;
using Shadowsocks.Properties; using Shadowsocks.Properties;
using Shadowsocks.Util;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -14,7 +15,7 @@ namespace Shadowsocks.Encryption
static Sodium() static Sodium()
{ {
string tempPath = Path.GetTempPath();
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll"; string dllPath = tempPath + "/libsscrypto.dll";
try try
{ {


+ 44
- 42
shadowsocks-csharp/Encryption/SodiumEncryptor.cs View File

@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading;
namespace Shadowsocks.Encryption namespace Shadowsocks.Encryption
{ {
@@ -12,19 +13,17 @@ namespace Shadowsocks.Encryption
const int SODIUM_BLOCK_SIZE = 64; const int SODIUM_BLOCK_SIZE = 64;
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
protected int _encryptBytesRemaining; protected int _encryptBytesRemaining;
protected int _decryptBytesRemaining; protected int _decryptBytesRemaining;
protected ulong _encryptIC; protected ulong _encryptIC;
protected ulong _decryptIC; protected ulong _decryptIC;
protected byte[] _encryptBuf;
protected byte[] _decryptBuf;
public SodiumEncryptor(string method, string password) public SodiumEncryptor(string method, string password)
: base(method, password) : base(method, password)
{ {
InitKey(method, password); InitKey(method, password);
_encryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
_decryptBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
} }
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> {
@@ -47,48 +46,51 @@ namespace Shadowsocks.Encryption
// TODO write a unidirection cipher so we don't have to if if if // TODO write a unidirection cipher so we don't have to if if if
int bytesRemaining; int bytesRemaining;
ulong ic; ulong ic;
byte[] sodiumBuf;
byte[] iv; byte[] iv;
if (isCipher)
{
bytesRemaining = _encryptBytesRemaining;
ic = _encryptIC;
sodiumBuf = _encryptBuf;
iv = _encryptIV;
}
else
{
bytesRemaining = _decryptBytesRemaining;
ic = _decryptIC;
sodiumBuf = _decryptBuf;
iv = _decryptIV;
}
int padding = bytesRemaining;
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
switch (_cipher)
// I'm tired. just add a big lock
// let's optimize for RAM instead of CPU
lock(sodiumBuf)
{ {
case CIPHER_SALSA20:
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
}
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length;
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
if (isCipher)
{
bytesRemaining = _encryptBytesRemaining;
ic = _encryptIC;
iv = _encryptIV;
}
else
{
bytesRemaining = _decryptBytesRemaining;
ic = _decryptIC;
iv = _decryptIV;
}
int padding = bytesRemaining;
Buffer.BlockCopy(buf, 0, sodiumBuf, padding, length);
if (isCipher)
{
_encryptBytesRemaining = bytesRemaining;
_encryptIC = ic;
}
else
{
_decryptBytesRemaining = bytesRemaining;
_decryptIC = ic;
switch (_cipher)
{
case CIPHER_SALSA20:
Sodium.crypto_stream_salsa20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
case CIPHER_CHACHA20:
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key);
break;
}
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length);
padding += length;
ic += (ulong)padding / SODIUM_BLOCK_SIZE;
bytesRemaining = padding % SODIUM_BLOCK_SIZE;
if (isCipher)
{
_encryptBytesRemaining = bytesRemaining;
_encryptIC = ic;
}
else
{
_decryptBytesRemaining = bytesRemaining;
_decryptIC = ic;
}
} }
} }


+ 0
- 2
shadowsocks-csharp/Encryption/TableEncryptor.cs View File

@@ -41,7 +41,6 @@ namespace Shadowsocks.Encryption
outlength = length; outlength = length;
} }
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{ {
byte[] result = new byte[length]; byte[] result = new byte[length];
@@ -100,7 +99,6 @@ namespace Shadowsocks.Encryption
return sorted; return sorted;
} }
public override void Dispose() public override void Dispose()
{ {
} }


+ 5
- 3
shadowsocks-csharp/Program.cs View File

@@ -18,8 +18,8 @@ namespace Shadowsocks
[STAThread] [STAThread]
static void Main() static void Main()
{ {
Util.Utils.ReleaseMemory();
using (Mutex mutex = new Mutex(false, "Global\\" + "71981632-A427-497F-AB91-241CD227EC1F"))
Util.Utils.ReleaseMemory(true);
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode()))
{ {
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
@@ -31,7 +31,9 @@ namespace Shadowsocks
{ {
Process oldProcess = oldProcesses[0]; Process oldProcess = oldProcesses[0];
} }
MessageBox.Show("Shadowsocks is already running.\n\nFind Shadowsocks icon in your notify tray.");
MessageBox.Show(I18N.GetString("Find Shadowsocks icon in your notify tray.") + "\n" +
I18N.GetString("If you want to start multiple Shadowsocks, make a copy in another directory."),
I18N.GetString("Shadowsocks is already running."));
return; return;
} }
Directory.SetCurrentDirectory(Application.StartupPath); Directory.SetCurrentDirectory(Application.StartupPath);


+ 43
- 3
shadowsocks-csharp/Util/Util.cs View File

@@ -5,12 +5,31 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Windows.Forms;
namespace Shadowsocks.Util namespace Shadowsocks.Util
{ {
public class Utils public class Utils
{ {
public static void ReleaseMemory()
// return path to store temporary files
public static string GetTempPath()
{
if (File.Exists(Application.StartupPath + "\\shadowsocks_portable_mode.txt"))
{
try
{
Directory.CreateDirectory(Application.StartupPath + "\\temp");
} catch (Exception e)
{
Console.WriteLine(e);
}
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log
return Application.StartupPath + "\\temp";
}
return Path.GetTempPath();
}
public static void ReleaseMemory(bool removePages)
{ {
// release any unused pages // release any unused pages
// making the numbers look good in task manager // making the numbers look good in task manager
@@ -20,8 +39,29 @@ namespace Shadowsocks.Util
// which is part of user experience // which is part of user experience
GC.Collect(GC.MaxGeneration); GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
if (removePages)
{
// as some users have pointed out
// removing pages from working set will cause some IO
// which lowered user experience for another group of users
//
// so we do 2 more things here to satisfy them:
// 1. only remove pages once when configuration is changed
// 2. add more comments here to tell users that calling
// this function will not be more frequent than
// IM apps writing chat logs, or web browsers writing cache files
// if they're so concerned about their disk, they should
// uninstall all IM apps and web browsers
//
// please open an issue if you're worried about anything else in your computer
// no matter it's GPU performance, monitor contrast, audio fidelity
// or anything else in the task manager
// we'll do as much as we can to help you
//
// just kidding
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
}
} }
public static string UnGzip(byte[] buf) public static string UnGzip(byte[] buf)


+ 53
- 1
shadowsocks-csharp/View/ConfigForm.Designer.cs View File

@@ -47,6 +47,9 @@
this.ServerGroupBox = new System.Windows.Forms.GroupBox(); this.ServerGroupBox = new System.Windows.Forms.GroupBox();
this.ServersListBox = new System.Windows.Forms.ListBox(); this.ServersListBox = new System.Windows.Forms.ListBox();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel();
this.MoveDownButton = new System.Windows.Forms.Button();
this.MoveUpButton = new System.Windows.Forms.Button();
this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel(); this.tableLayoutPanel5 = new System.Windows.Forms.TableLayoutPanel();
this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); this.ProxyPortTextBox = new System.Windows.Forms.TextBox();
this.ProxyPortLabel = new System.Windows.Forms.Label(); this.ProxyPortLabel = new System.Windows.Forms.Label();
@@ -55,6 +58,7 @@
this.tableLayoutPanel1.SuspendLayout(); this.tableLayoutPanel1.SuspendLayout();
this.ServerGroupBox.SuspendLayout(); this.ServerGroupBox.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout(); this.tableLayoutPanel2.SuspendLayout();
this.tableLayoutPanel6.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout(); this.tableLayoutPanel5.SuspendLayout();
this.tableLayoutPanel3.SuspendLayout(); this.tableLayoutPanel3.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout(); this.tableLayoutPanel4.SuspendLayout();
@@ -184,7 +188,7 @@
// //
// EncryptionSelect // EncryptionSelect
// //
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
this.EncryptionSelect.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right))); | System.Windows.Forms.AnchorStyles.Right)));
this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.EncryptionSelect.FormattingEnabled = true; this.EncryptionSelect.FormattingEnabled = true;
@@ -281,6 +285,7 @@
// //
this.ServersListBox.FormattingEnabled = true; this.ServersListBox.FormattingEnabled = true;
this.ServersListBox.IntegralHeight = false; this.ServersListBox.IntegralHeight = false;
this.ServersListBox.ItemHeight = 12;
this.ServersListBox.Location = new System.Drawing.Point(0, 0); this.ServersListBox.Location = new System.Drawing.Point(0, 0);
this.ServersListBox.Margin = new System.Windows.Forms.Padding(0); this.ServersListBox.Margin = new System.Windows.Forms.Padding(0);
this.ServersListBox.Name = "ServersListBox"; this.ServersListBox.Name = "ServersListBox";
@@ -295,6 +300,7 @@
this.tableLayoutPanel2.ColumnCount = 2; this.tableLayoutPanel2.ColumnCount = 2;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
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.tableLayoutPanel5, 1, 1);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2); this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2);
this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0); this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0);
@@ -310,6 +316,48 @@
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238); this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238);
this.tableLayoutPanel2.TabIndex = 7; this.tableLayoutPanel2.TabIndex = 7;
// //
// tableLayoutPanel6
//
this.tableLayoutPanel6.AutoSize = true;
this.tableLayoutPanel6.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tableLayoutPanel6.ColumnCount = 2;
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
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, 211);
this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0);
this.tableLayoutPanel6.Name = "tableLayoutPanel6";
this.tableLayoutPanel6.RowCount = 1;
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.Size = new System.Drawing.Size(166, 32);
this.tableLayoutPanel6.TabIndex = 10;
//
// MoveDownButton
//
this.MoveDownButton.Dock = System.Windows.Forms.DockStyle.Right;
this.MoveDownButton.Location = new System.Drawing.Point(86, 6);
this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3);
this.MoveDownButton.Name = "MoveDownButton";
this.MoveDownButton.Size = new System.Drawing.Size(80, 23);
this.MoveDownButton.TabIndex = 7;
this.MoveDownButton.Text = "Move D&own";
this.MoveDownButton.UseVisualStyleBackColor = true;
this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click);
//
// MoveUpButton
//
this.MoveUpButton.Dock = System.Windows.Forms.DockStyle.Left;
this.MoveUpButton.Location = new System.Drawing.Point(0, 6);
this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3);
this.MoveUpButton.Name = "MoveUpButton";
this.MoveUpButton.Size = new System.Drawing.Size(80, 23);
this.MoveUpButton.TabIndex = 6;
this.MoveUpButton.Text = "Move &Up";
this.MoveUpButton.UseVisualStyleBackColor = true;
this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click);
//
// tableLayoutPanel5 // tableLayoutPanel5
// //
this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.tableLayoutPanel5.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
@@ -418,6 +466,7 @@
this.ServerGroupBox.PerformLayout(); this.ServerGroupBox.PerformLayout();
this.tableLayoutPanel2.ResumeLayout(false); this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout(); this.tableLayoutPanel2.PerformLayout();
this.tableLayoutPanel6.ResumeLayout(false);
this.tableLayoutPanel5.ResumeLayout(false); this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout(); this.tableLayoutPanel5.PerformLayout();
this.tableLayoutPanel3.ResumeLayout(false); this.tableLayoutPanel3.ResumeLayout(false);
@@ -453,6 +502,9 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private System.Windows.Forms.TextBox ProxyPortTextBox; private System.Windows.Forms.TextBox ProxyPortTextBox;
private System.Windows.Forms.Label ProxyPortLabel; private System.Windows.Forms.Label ProxyPortLabel;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.Button MoveDownButton;
private System.Windows.Forms.Button MoveUpButton;
} }
} }

+ 96
- 26
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -18,7 +18,7 @@ namespace Shadowsocks.View
// this is a copy of configuration that we are working on // this is a copy of configuration that we are working on
private Configuration _modifiedConfiguration; private Configuration _modifiedConfiguration;
private int _oldSelectedIndex = -1;
private int _lastSelectedIndex = -1;
public ConfigForm(ShadowsocksController controller) public ConfigForm(ShadowsocksController controller)
{ {
@@ -51,6 +51,8 @@ namespace Shadowsocks.View
ServerGroupBox.Text = I18N.GetString("Server"); ServerGroupBox.Text = I18N.GetString("Server");
OKButton.Text = I18N.GetString("OK"); OKButton.Text = I18N.GetString("OK");
MyCancelButton.Text = I18N.GetString("Cancel"); MyCancelButton.Text = I18N.GetString("Cancel");
MoveUpButton.Text = I18N.GetString("Move &Up");
MoveDownButton.Text = I18N.GetString("Move D&own");
this.Text = I18N.GetString("Edit Servers"); this.Text = I18N.GetString("Edit Servers");
} }
@@ -58,7 +60,7 @@ namespace Shadowsocks.View
{ {
LoadCurrentConfiguration(); LoadCurrentConfiguration();
} }
private void ShowWindow() private void ShowWindow()
{ {
this.Opacity = 1; this.Opacity = 1;
@@ -70,7 +72,7 @@ namespace Shadowsocks.View
{ {
try try
{ {
if (_oldSelectedIndex == -1 || _oldSelectedIndex >= _modifiedConfiguration.configs.Count)
if (_lastSelectedIndex == -1 || _lastSelectedIndex >= _modifiedConfiguration.configs.Count)
{ {
return true; return true;
} }
@@ -85,9 +87,9 @@ namespace Shadowsocks.View
int localPort = int.Parse(ProxyPortTextBox.Text); int localPort = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(server); Configuration.CheckServer(server);
Configuration.CheckLocalPort(localPort); Configuration.CheckLocalPort(localPort);
_modifiedConfiguration.configs[_oldSelectedIndex] = server;
_modifiedConfiguration.configs[_lastSelectedIndex] = server;
_modifiedConfiguration.localPort = localPort; _modifiedConfiguration.localPort = localPort;
return true; return true;
} }
catch (FormatException) catch (FormatException)
@@ -113,12 +115,6 @@ namespace Shadowsocks.View
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString();
EncryptionSelect.Text = server.method ?? "aes-256-cfb"; EncryptionSelect.Text = server.method ?? "aes-256-cfb";
RemarksTextBox.Text = server.remarks; RemarksTextBox.Text = server.remarks;
ServerGroupBox.Visible = true;
//IPTextBox.Focus();
}
else
{
ServerGroupBox.Visible = false;
} }
} }
@@ -135,12 +131,13 @@ namespace Shadowsocks.View
{ {
_modifiedConfiguration = controller.GetConfigurationCopy(); _modifiedConfiguration = controller.GetConfigurationCopy();
LoadConfiguration(_modifiedConfiguration); LoadConfiguration(_modifiedConfiguration);
_oldSelectedIndex = _modifiedConfiguration.index;
if (_oldSelectedIndex < 0)
_lastSelectedIndex = _modifiedConfiguration.index;
if (_lastSelectedIndex < 0)
{ {
_oldSelectedIndex = 0;
_lastSelectedIndex = 0;
} }
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
UpdateMoveUpAndDownButton();
LoadSelectedServer(); LoadSelectedServer();
} }
@@ -151,7 +148,11 @@ namespace Shadowsocks.View
private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e)
{ {
if (_oldSelectedIndex == ServersListBox.SelectedIndex)
if (!ServersListBox.CanSelect)
{
return;
}
if (_lastSelectedIndex == ServersListBox.SelectedIndex)
{ {
// we are moving back to oldSelectedIndex or doing a force move // we are moving back to oldSelectedIndex or doing a force move
return; return;
@@ -159,11 +160,13 @@ namespace Shadowsocks.View
if (!SaveOldSelectedServer()) if (!SaveOldSelectedServer())
{ {
// why this won't cause stack overflow? // why this won't cause stack overflow?
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
return; return;
} }
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
UpdateMoveUpAndDownButton();
LoadSelectedServer(); LoadSelectedServer();
_oldSelectedIndex = ServersListBox.SelectedIndex;
_lastSelectedIndex = ServersListBox.SelectedIndex;
} }
private void AddButton_Click(object sender, EventArgs e) private void AddButton_Click(object sender, EventArgs e)
@@ -176,29 +179,30 @@ namespace Shadowsocks.View
_modifiedConfiguration.configs.Add(server); _modifiedConfiguration.configs.Add(server);
LoadConfiguration(_modifiedConfiguration); LoadConfiguration(_modifiedConfiguration);
ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1; ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1;
_oldSelectedIndex = ServersListBox.SelectedIndex;
_lastSelectedIndex = ServersListBox.SelectedIndex;
} }
private void DeleteButton_Click(object sender, EventArgs e) private void DeleteButton_Click(object sender, EventArgs e)
{ {
_oldSelectedIndex = ServersListBox.SelectedIndex;
if (_oldSelectedIndex >= 0 && _oldSelectedIndex < _modifiedConfiguration.configs.Count)
_lastSelectedIndex = ServersListBox.SelectedIndex;
if (_lastSelectedIndex >= 0 && _lastSelectedIndex < _modifiedConfiguration.configs.Count)
{ {
_modifiedConfiguration.configs.RemoveAt(_oldSelectedIndex);
_modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex);
} }
if (_oldSelectedIndex >= _modifiedConfiguration.configs.Count)
if (_lastSelectedIndex >= _modifiedConfiguration.configs.Count)
{ {
// can be -1 // can be -1
_oldSelectedIndex = _modifiedConfiguration.configs.Count - 1;
_lastSelectedIndex = _modifiedConfiguration.configs.Count - 1;
} }
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
LoadConfiguration(_modifiedConfiguration); LoadConfiguration(_modifiedConfiguration);
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
LoadSelectedServer(); LoadSelectedServer();
} }
private void OKButton_Click(object sender, EventArgs e) private void OKButton_Click(object sender, EventArgs e)
{ {
Server server = controller.GetCurrentServer();
if (!SaveOldSelectedServer()) if (!SaveOldSelectedServer())
{ {
return; return;
@@ -209,6 +213,7 @@ namespace Shadowsocks.View
return; return;
} }
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort); controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server));
this.Close(); this.Close();
} }
@@ -227,5 +232,70 @@ namespace Shadowsocks.View
controller.ConfigChanged -= controller_ConfigChanged; controller.ConfigChanged -= controller_ConfigChanged;
} }
private void MoveConfigItem(int step)
{
int index = ServersListBox.SelectedIndex;
Server server = _modifiedConfiguration.configs[index];
object item = ServersListBox.SelectedItem;
_modifiedConfiguration.configs.Remove(server);
_modifiedConfiguration.configs.Insert(index + step, server);
_modifiedConfiguration.index += step;
ServersListBox.BeginUpdate();
ServersListBox.Enabled = false;
_lastSelectedIndex = index + step;
ServersListBox.Items.Remove(item);
ServersListBox.Items.Insert(index + step, item);
ServersListBox.Enabled = true;
ServersListBox.SelectedIndex = index + step;
ServersListBox.EndUpdate();
UpdateMoveUpAndDownButton();
}
private void UpdateMoveUpAndDownButton()
{
if (ServersListBox.SelectedIndex == 0)
{
MoveUpButton.Enabled = false;
}
else
{
MoveUpButton.Enabled = true;
}
if (ServersListBox.SelectedIndex == ServersListBox.Items.Count - 1)
{
MoveDownButton.Enabled = false;
}
else
{
MoveDownButton.Enabled = true;
}
}
private void MoveUpButton_Click(object sender, EventArgs e)
{
if (!SaveOldSelectedServer())
{
return;
}
if (ServersListBox.SelectedIndex > 0)
{
MoveConfigItem(-1); // -1 means move backward
}
}
private void MoveDownButton_Click(object sender, EventArgs e)
{
if (!SaveOldSelectedServer())
{
return;
}
if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1)
{
MoveConfigItem(+1); // +1 means move forward
}
}
} }
} }

+ 2
- 2
shadowsocks-csharp/View/ConfigForm.resx View File

@@ -112,9 +112,9 @@
<value>2.0</value> <value>2.0</value>
</resheader> </resheader>
<resheader name="reader"> <resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
</root> </root>

+ 129
- 46
shadowsocks-csharp/View/LogForm.Designer.cs View File

@@ -29,69 +29,143 @@
private void InitializeComponent() private void InitializeComponent()
{ {
this.components = new System.ComponentModel.Container(); this.components = new System.ComponentModel.Container();
this.textBox1 = new System.Windows.Forms.TextBox();
this.LogMessageTextBox = new System.Windows.Forms.TextBox();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components);
this.menuItem1 = new System.Windows.Forms.MenuItem();
this.menuItem2 = new System.Windows.Forms.MenuItem();
this.menuItem4 = new System.Windows.Forms.MenuItem();
this.FileMenuItem = new System.Windows.Forms.MenuItem();
this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem();
this.ExitMenuItem = new System.Windows.Forms.MenuItem();
this.panel1 = new System.Windows.Forms.Panel();
this.ChangeFontButton = new System.Windows.Forms.Button();
this.CleanLogsButton = new System.Windows.Forms.Button();
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.TopMostCheckBox = new System.Windows.Forms.CheckBox();
this.panel1.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout(); this.SuspendLayout();
// //
// textBox1
//
this.textBox1.BackColor = System.Drawing.Color.Black;
this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.textBox1.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBox1.ForeColor = System.Drawing.Color.White;
this.textBox1.Location = new System.Drawing.Point(0, 0);
this.textBox1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.textBox1.MaxLength = 2147483647;
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.ReadOnly = true;
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.textBox1.Size = new System.Drawing.Size(820, 529);
this.textBox1.TabIndex = 0;
this.textBox1.WordWrap = false;
// LogMessageTextBox
//
this.LogMessageTextBox.BackColor = System.Drawing.Color.Black;
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.LogMessageTextBox.ForeColor = System.Drawing.Color.White;
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 43);
this.LogMessageTextBox.MaxLength = 2147483647;
this.LogMessageTextBox.Multiline = true;
this.LogMessageTextBox.Name = "LogMessageTextBox";
this.LogMessageTextBox.ReadOnly = true;
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both;
this.LogMessageTextBox.Size = new System.Drawing.Size(541, 307);
this.LogMessageTextBox.TabIndex = 0;
this.LogMessageTextBox.WordWrap = false;
// //
// contextMenuStrip1 // contextMenuStrip1
// //
this.contextMenuStrip1.ImageScalingSize = new System.Drawing.Size(24, 24);
this.contextMenuStrip1.Name = "contextMenuStrip1"; this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(74, 4);
this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4);
// //
// mainMenu1 // mainMenu1
// //
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem1});
this.FileMenuItem});
//
// FileMenuItem
//
this.FileMenuItem.Index = 0;
this.FileMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.OpenLocationMenuItem,
this.ExitMenuItem});
this.FileMenuItem.Text = "&File";
//
// OpenLocationMenuItem
//
this.OpenLocationMenuItem.Index = 0;
this.OpenLocationMenuItem.Text = "&Open Location";
this.OpenLocationMenuItem.Click += new System.EventHandler(this.OpenLocationMenuItem_Click);
//
// ExitMenuItem
//
this.ExitMenuItem.Index = 1;
this.ExitMenuItem.Text = "E&xit";
this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click);
//
// panel1
//
this.panel1.Controls.Add(this.TopMostCheckBox);
this.panel1.Controls.Add(this.ChangeFontButton);
this.panel1.Controls.Add(this.CleanLogsButton);
this.panel1.Controls.Add(this.WrapTextCheckBox);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(3, 3);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(541, 34);
this.panel1.TabIndex = 1;
//
// ChangeFontButton
//
this.ChangeFontButton.Location = new System.Drawing.Point(107, 4);
this.ChangeFontButton.Name = "ChangeFontButton";
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23);
this.ChangeFontButton.TabIndex = 2;
this.ChangeFontButton.Text = "&Font";
this.ChangeFontButton.UseVisualStyleBackColor = true;
this.ChangeFontButton.Click += new System.EventHandler(this.ChangeFontButton_Click);
//
// CleanLogsButton
//
this.CleanLogsButton.Location = new System.Drawing.Point(9, 4);
this.CleanLogsButton.Name = "CleanLogsButton";
this.CleanLogsButton.Size = new System.Drawing.Size(75, 23);
this.CleanLogsButton.TabIndex = 1;
this.CleanLogsButton.Text = "&Clean logs";
this.CleanLogsButton.UseVisualStyleBackColor = true;
this.CleanLogsButton.Click += new System.EventHandler(this.CleanLogsButton_Click);
// //
// menuItem1
// WrapTextCheckBox
// //
this.menuItem1.Index = 0;
this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.menuItem2,
this.menuItem4});
this.menuItem1.Text = "&File";
this.WrapTextCheckBox.AutoSize = true;
this.WrapTextCheckBox.Location = new System.Drawing.Point(209, 9);
this.WrapTextCheckBox.Name = "WrapTextCheckBox";
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 16);
this.WrapTextCheckBox.TabIndex = 0;
this.WrapTextCheckBox.Text = "&Wrap text";
this.WrapTextCheckBox.UseVisualStyleBackColor = true;
this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged);
// //
// menuItem2
// tableLayoutPanel1
// //
this.menuItem2.Index = 0;
this.menuItem2.Text = "&Open Location";
this.menuItem2.Click += new System.EventHandler(this.menuItem2_Click);
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 353);
this.tableLayoutPanel1.TabIndex = 2;
// //
// menuItem4
// TopMostCheckBox
// //
this.menuItem4.Index = 1;
this.menuItem4.Text = "E&xit";
this.menuItem4.Click += new System.EventHandler(this.menuItem4_Click);
this.TopMostCheckBox.AutoSize = true;
this.TopMostCheckBox.Location = new System.Drawing.Point(311, 9);
this.TopMostCheckBox.Name = "TopMostCheckBox";
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 16);
this.TopMostCheckBox.TabIndex = 3;
this.TopMostCheckBox.Text = "&Top most";
this.TopMostCheckBox.UseVisualStyleBackColor = true;
this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged);
// //
// LogForm // LogForm
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(820, 529);
this.Controls.Add(this.textBox1);
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.ClientSize = new System.Drawing.Size(547, 353);
this.Controls.Add(this.tableLayoutPanel1);
this.Menu = this.mainMenu1; this.Menu = this.mainMenu1;
this.Name = "LogForm"; this.Name = "LogForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
@@ -99,18 +173,27 @@
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing); this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing);
this.Load += new System.EventHandler(this.LogForm_Load); this.Load += new System.EventHandler(this.LogForm_Load);
this.Shown += new System.EventHandler(this.LogForm_Shown); this.Shown += new System.EventHandler(this.LogForm_Shown);
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout();
} }
#endregion #endregion
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox LogMessageTextBox;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
private System.Windows.Forms.MainMenu mainMenu1; private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuItem1;
private System.Windows.Forms.MenuItem menuItem2;
private System.Windows.Forms.MenuItem menuItem4;
private System.Windows.Forms.MenuItem FileMenuItem;
private System.Windows.Forms.MenuItem OpenLocationMenuItem;
private System.Windows.Forms.MenuItem ExitMenuItem;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.CheckBox WrapTextCheckBox;
private System.Windows.Forms.Button CleanLogsButton;
private System.Windows.Forms.Button ChangeFontButton;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.CheckBox TopMostCheckBox;
} }
} }

+ 49
- 12
shadowsocks-csharp/View/LogForm.cs View File

@@ -1,4 +1,5 @@
using Shadowsocks.Properties;
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
@@ -23,6 +24,20 @@ namespace Shadowsocks.View
this.filename = filename; this.filename = filename;
InitializeComponent(); InitializeComponent();
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon());
UpdateTexts();
}
private void UpdateTexts()
{
FileMenuItem.Text = I18N.GetString("&File");
OpenLocationMenuItem.Text = I18N.GetString("&Open Location");
ExitMenuItem.Text = I18N.GetString("E&xit");
CleanLogsButton.Text = I18N.GetString("&Clean logs");
ChangeFontButton.Text = I18N.GetString("&Font");
WrapTextCheckBox.Text = I18N.GetString("&Wrap text");
TopMostCheckBox.Text = I18N.GetString("&Top most");
this.Text = I18N.GetString("Log Viewer");
} }
private void Timer_Tick(object sender, EventArgs e) private void Timer_Tick(object sender, EventArgs e)
@@ -43,9 +58,9 @@ namespace Shadowsocks.View
string line = ""; string line = "";
while ((line = reader.ReadLine()) != null) while ((line = reader.ReadLine()) != null)
textBox1.AppendText(line + "\r\n");
LogMessageTextBox.AppendText(line + "\r\n");
textBox1.ScrollToCaret();
LogMessageTextBox.ScrollToCaret();
lastOffset = reader.BaseStream.Position; lastOffset = reader.BaseStream.Position;
} }
@@ -63,12 +78,12 @@ namespace Shadowsocks.View
while ((line = reader.ReadLine()) != null) while ((line = reader.ReadLine()) != null)
{ {
changed = true; changed = true;
textBox1.AppendText(line + "\r\n");
LogMessageTextBox.AppendText(line + "\r\n");
} }
if (changed) if (changed)
{ {
textBox1.ScrollToCaret();
LogMessageTextBox.ScrollToCaret();
} }
lastOffset = reader.BaseStream.Position; lastOffset = reader.BaseStream.Position;
@@ -89,25 +104,47 @@ namespace Shadowsocks.View
timer.Stop(); timer.Stop();
} }
private void menuItem2_Click(object sender, EventArgs e)
private void OpenLocationMenuItem_Click(object sender, EventArgs e)
{ {
string argument = @"/select, " + filename;
string argument = "/select, \"" + filename + "\"";
Console.WriteLine(argument);
System.Diagnostics.Process.Start("explorer.exe", argument); System.Diagnostics.Process.Start("explorer.exe", argument);
} }
private void menuItem3_Click(object sender, EventArgs e)
private void ExitMenuItem_Click(object sender, EventArgs e)
{ {
this.Close();
}
private void LogForm_Shown(object sender, EventArgs e)
{
LogMessageTextBox.ScrollToCaret();
} }
private void menuItem4_Click(object sender, EventArgs e)
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e)
{ {
this.Close();
LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked;
LogMessageTextBox.ScrollToCaret();
} }
private void LogForm_Shown(object sender, EventArgs e)
private void CleanLogsButton_Click(object sender, EventArgs e)
{
LogMessageTextBox.Clear();
}
private void ChangeFontButton_Click(object sender, EventArgs e)
{
FontDialog fd = new FontDialog();
fd.Font = LogMessageTextBox.Font;
if (fd.ShowDialog() == DialogResult.OK)
{
LogMessageTextBox.Font = fd.Font;
}
}
private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e)
{ {
textBox1.ScrollToCaret();
this.TopMost = TopMostCheckBox.Checked;
} }
} }
} }

+ 16
- 17
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -18,7 +18,7 @@ namespace Shadowsocks.View
// yes this is just a menu view controller // yes this is just a menu view controller
// when config form is closed, it moves away from RAM // when config form is closed, it moves away from RAM
// and it should just do anything related to the config form // and it should just do anything related to the config form
private ShadowsocksController controller; private ShadowsocksController controller;
private UpdateChecker updateChecker; private UpdateChecker updateChecker;
@@ -29,6 +29,7 @@ namespace Shadowsocks.View
private MenuItem enableItem; private MenuItem enableItem;
private MenuItem modeItem; private MenuItem modeItem;
private MenuItem AutoStartupItem; private MenuItem AutoStartupItem;
private MenuItem AvailabilityStatistics;
private MenuItem ShareOverLANItem; private MenuItem ShareOverLANItem;
private MenuItem SeperatorItem; private MenuItem SeperatorItem;
private MenuItem ConfigItem; private MenuItem ConfigItem;
@@ -164,7 +165,6 @@ namespace Shadowsocks.View
this.SeperatorItem = new MenuItem("-"), this.SeperatorItem = new MenuItem("-"),
this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)), this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_Click)),
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)),
CreateMenuItem("Statistics Stategy Options", new EventHandler(StatisticsStrategyOptionsItem_Click)),
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click))
}), }),
CreateMenuGroup("PAC ", new MenuItem[] { CreateMenuGroup("PAC ", new MenuItem[] {
@@ -178,6 +178,7 @@ namespace Shadowsocks.View
}), }),
new MenuItem("-"), new MenuItem("-"),
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.AvailabilityStatistics = CreateMenuItem("Availability Statistics", new EventHandler(this.AvailabilityStatisticsItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"), new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
@@ -261,6 +262,7 @@ namespace Shadowsocks.View
PACModeItem.Checked = !config.global; PACModeItem.Checked = !config.global;
ShareOverLANItem.Checked = config.shareOverLan; ShareOverLANItem.Checked = config.shareOverLan;
AutoStartupItem.Checked = AutoStartup.Check(); AutoStartupItem.Checked = AutoStartup.Check();
AvailabilityStatistics.Checked = config.availabilityStatistics;
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked; localPACItem.Checked = !onlinePACItem.Checked;
UpdatePACItemsEnabledStatus(); UpdatePACItemsEnabledStatus();
@@ -320,7 +322,7 @@ namespace Shadowsocks.View
void configForm_FormClosed(object sender, FormClosedEventArgs e) void configForm_FormClosed(object sender, FormClosedEventArgs e)
{ {
configForm = null; configForm = null;
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
ShowFirstTimeBalloon(); ShowFirstTimeBalloon();
} }
@@ -424,14 +426,6 @@ namespace Shadowsocks.View
qrCodeForm.Show(); qrCodeForm.Show();
} }
private void StatisticsStrategyOptionsItem_Click(object sender, EventArgs e)
{
//TODO: Load options
var statisticsStrategyOptionsForm = new StatisticsStrategyConfigurationForm(controller);
statisticsStrategyOptionsForm.Show();
//TODO: Save options
}
private void ScanQRCodeItem_Click(object sender, EventArgs e) private void ScanQRCodeItem_Click(object sender, EventArgs e)
{ {
foreach (Screen screen in Screen.AllScreens) foreach (Screen screen in Screen.AllScreens)
@@ -526,12 +520,17 @@ namespace Shadowsocks.View
Process.Start(_urlToOpen); Process.Start(_urlToOpen);
} }
private void AutoStartupItem_Click(object sender, EventArgs e) {
AutoStartupItem.Checked = !AutoStartupItem.Checked;
if (!AutoStartup.Set(AutoStartupItem.Checked)) {
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
}
private void AutoStartupItem_Click(object sender, EventArgs e) {
AutoStartupItem.Checked = !AutoStartupItem.Checked;
if (!AutoStartup.Set(AutoStartupItem.Checked)) {
MessageBox.Show(I18N.GetString("Failed to update registry"));
}
}
private void AvailabilityStatisticsItem_Click(object sender, EventArgs e) {
AvailabilityStatistics.Checked = !AvailabilityStatistics.Checked;
controller.ToggleAvailabilityStatistics(AvailabilityStatistics.Checked);
}
private void LocalPACItem_Click(object sender, EventArgs e) private void LocalPACItem_Click(object sender, EventArgs e)
{ {


+ 0
- 2
shadowsocks-csharp/View/QRCodeSplashForm.cs View File

@@ -242,7 +242,6 @@ namespace Shadowsocks.View
SetBitmap(bitmap, 255); SetBitmap(bitmap, 255);
} }
/// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para> /// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para>
public void SetBitmap(Bitmap bitmap, byte opacity) public void SetBitmap(Bitmap bitmap, byte opacity)
{ {
@@ -288,7 +287,6 @@ namespace Shadowsocks.View
} }
} }
protected override CreateParams CreateParams protected override CreateParams CreateParams
{ {
get get


Loading…
Cancel
Save