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 9 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
- Hide Privoxy icon



+ 29
- 6
README.md View File

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

[![Build Status]][Appveyor]

[中文说明]

#### Features

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

#### Download

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

#### Basic

1. Find Shadowsocks icon in the notification tray
2. You can add multiple servers in servers menu
3. Select Enable System Proxy menu to enable system proxy. Please disable other
3. Select `Enable System Proxy` menu to enable system proxy. Please disable other
proxy addons in your browser, or set them to use system proxy
4. You can also configure your browser proxy manually if you don't want to enable
system proxy. Set Socks5 or HTTP proxy to 127.0.0.1:1080. You can change this
port in Server -> Edit Servers
port in `Servers -> Edit Servers`

#### PAC

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

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

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

@@ -58,3 +78,6 @@ GPLv3
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E

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

@@ -12,7 +12,7 @@ namespace Shadowsocks.Controller
static I18N()
{
Strings = new Dictionary<string, string>();
if (System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag.ToLowerInvariant().StartsWith("zh"))
{
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.IO;
using System.Net.Sockets;
@@ -14,14 +15,14 @@ namespace Shadowsocks.Controller
{
try
{
string temppath = Path.GetTempPath();
string temppath = Utils.GetTempPath();
LogFile = Path.Combine(temppath, "shadowsocks.log");
FileStream fs = new FileStream(LogFile, FileMode.Append);
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs);
sw.AutoFlush = true;
Console.SetOut(sw);
Console.SetError(sw);
return true;
}
catch (IOException e)
@@ -71,7 +72,6 @@ namespace Shadowsocks.Controller
Console.WriteLine(e);
}
}
}
// 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 Shadowsocks.Model;
using SimpleJson = SimpleJson.SimpleJson;
using Shadowsocks.Util;
using Timer = System.Threading.Timer;

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

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

@@ -41,10 +43,18 @@ namespace Shadowsocks.Controller
{
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;
}
catch (Exception e)
@@ -80,7 +90,7 @@ namespace Shadowsocks.Controller
string country = obj["country"];
string city = obj["city"];
string isp = obj["isp"];
string regionName= obj["regionName"];
string regionName = obj["regionName"];
if (country == null || city == null || isp == null || regionName == null) return ret;
ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}");
ret[1] = new DataUnit(State.ISP, isp);
@@ -93,7 +103,8 @@ namespace Shadowsocks.Controller
if (server.server == "") return null;
var ping = new Ping();
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
try
@@ -108,7 +119,7 @@ namespace Shadowsocks.Controller
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply
});
}
catch (PingException e)
catch (Exception e)
{
Logging.LogUsefulException(e);
}
@@ -138,11 +149,11 @@ namespace Shadowsocks.Controller
if (!File.Exists(AvailabilityStatisticsFile))
{
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
lines = new[] { headerLine, dataLine };
lines = new[] {headerLine, dataLine};
}
else
{
lines = new[] { dataLine };
lines = new[] {dataLine};
}
File.AppendAllLines(AvailabilityStatisticsFile, lines);
}


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

@@ -77,7 +77,6 @@ namespace Shadowsocks.Controller
_udpSocket.Bind(localEndPoint);
_tcpSocket.Listen(1024);
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Shadowsocks started");
_tcpSocket.BeginAccept(
@@ -185,7 +184,6 @@ namespace Shadowsocks.Controller
}
}
private void ReceiveCallback(IAsyncResult ar)
{
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()
{
if (File.Exists(PAC_FILE))
@@ -146,7 +145,7 @@ Connection: Close
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac;
byte[] response = System.Text.Encoding.UTF8.GetBytes(text);
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket);
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
}
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;
using System.Runtime.InteropServices;
using Shadowsocks.Util;
namespace Shadowsocks.Controller
{
@@ -20,7 +21,7 @@ namespace Shadowsocks.Controller
static PolipoRunner()
{
temppath = Path.GetTempPath();
temppath = Utils.GetTempPath();
try
{
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");
FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig));
if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) {
temppath = temppath + "\\";
}
_process = new Process();
// 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.UseShellExecute = true;
_process.StartInfo.CreateNoWindow = true;
@@ -144,7 +148,6 @@ namespace Shadowsocks.Controller
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public void RefreshTrayArea()
{
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
@@ -161,7 +164,6 @@ namespace Shadowsocks.Controller
RefreshTrayArea(notificationAreaHandle);
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
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)
{
if (_closed)


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

@@ -14,9 +14,18 @@ namespace Shadowsocks.Controller
class TCPRelay : Listener.Service
{
private ShadowsocksController _controller;
private DateTime _lastSweepTime;
public ISet<Handler> Handlers
{
get; set;
}
public TCPRelay(ShadowsocksController controller)
{
this._controller = controller;
this.Handlers = new HashSet<Handler>();
this._lastSweepTime = DateTime.Now;
}
public bool Handle(byte[] firstPacket, int length, Socket socket, object state)
@@ -33,9 +42,33 @@ namespace Shadowsocks.Controller
Handler handler = new Handler();
handler.connection = socket;
handler.controller = _controller;
handler.relay = this;
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 connection;
public ShadowsocksController controller;
public TCPRelay relay;
public DateTime lastActivity;
private int retryCount = 0;
private bool connected;
@@ -55,7 +92,7 @@ namespace Shadowsocks.Controller
private byte[] _firstPacket;
private int _firstPacketLength;
// Size of receive buffer.
public const int RecvSize = 16384;
public const int RecvSize = 8192;
public const int BufferSize = RecvSize + 32;
private int totalRead = 0;
@@ -74,7 +111,7 @@ namespace Shadowsocks.Controller
private bool connectionShutdown = false;
private bool remoteShutdown = false;
private bool closed = false;
private object encryptionLock = new object();
private object decryptionLock = new object();
@@ -96,6 +133,7 @@ namespace Shadowsocks.Controller
this._firstPacket = firstPacket;
this._firstPacketLength = length;
this.HandshakeReceive();
this.lastActivity = DateTime.Now;
}
private void CheckClose()
@@ -108,6 +146,11 @@ namespace Shadowsocks.Controller
public void Close()
{
lock (relay.Handlers)
{
Logging.Debug($"connections: {relay.Handlers.Count}");
relay.Handlers.Remove(this);
}
lock (this)
{
if (closed)
@@ -152,7 +195,6 @@ namespace Shadowsocks.Controller
}
}
private void HandshakeReceive()
{
if (closed)
@@ -222,7 +264,7 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = connection.EndReceive(ar);
if (bytesRead >= 3)
{
command = connetionRecvBuffer[1];
@@ -272,7 +314,6 @@ namespace Shadowsocks.Controller
private void ReadAll(IAsyncResult ar)
{
if (closed)
{
return;
@@ -347,7 +388,6 @@ namespace Shadowsocks.Controller
}
IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port);
remote = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
@@ -485,6 +525,7 @@ namespace Shadowsocks.Controller
if (bytesRead > 0)
{
this.lastActivity = DateTime.Now;
int bytesToSend;
lock (decryptionLock)
{
@@ -548,7 +589,6 @@ namespace Shadowsocks.Controller
}
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null);
IStrategy strategy = controller.GetCurrentStrategy();
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)
{
// TODO: handle the ObjectDisposedException
}
catch (Exception)
{
// TODO: need more think about handle other Exceptions, or should remove this catch().
}
finally
{
@@ -124,9 +126,11 @@ namespace Shadowsocks.Controller
}
catch (ObjectDisposedException)
{
// TODO: handle the ObjectDisposedException
}
catch (Exception)
{
// TODO: need more think about handle other Exceptions, or should remove this catch().
}
finally
{
@@ -134,6 +138,8 @@ namespace Shadowsocks.Controller
}
}
}
// cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054
class LRUCache<K, V> where V : UDPRelay.UDPHandler
{
@@ -196,5 +202,4 @@ namespace Shadowsocks.Controller
public K key;
public V value;
}
}

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

@@ -12,13 +12,13 @@ namespace Shadowsocks.Controller
{
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 LatestVersionURL;
public event EventHandler NewVersionFound;
public const string Version = "2.5.4";
public const string Version = "2.5.6";
public void CheckUpdate(Configuration config)
{
@@ -53,7 +53,6 @@ namespace Shadowsocks.Controller
{
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y));
}
}
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 EnableGlobalChanged;
public event EventHandler ShareOverLANStatusChanged;
// when user clicked Edit PAC, and PAC file has already created
public event EventHandler<PathEventArgs> PACFileReadyToOpen;
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen;
@@ -365,17 +365,15 @@ namespace Shadowsocks.Controller
}
UpdateSystemProxy();
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
}
protected void SaveConfig(Configuration newConfig)
{
Configuration.Save(newConfig);
Reload();
}
private void UpdateSystemProxy()
{
if (_config.enabled)
@@ -422,7 +420,7 @@ namespace Shadowsocks.Controller
{
while (true)
{
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(false);
Thread.Sleep(30 * 1000);
}
}


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

@@ -14,17 +14,17 @@ namespace Shadowsocks.Controller.Strategy
/*
* IStrategy
*
*
* Subclasses must be thread-safe
*/
public interface IStrategy
{
string Name { 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();


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

@@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Shadowsocks.Model;
using System.IO;
using System.Net.NetworkInformation;
using System.Threading;
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 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)
{
_controller = controller;
@@ -99,6 +93,15 @@ namespace Shadowsocks.Controller.Strategy
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)
{
if (_statistics == null || servers.Count == 0)
@@ -118,7 +121,7 @@ namespace Shadowsocks.Controller.Strategy
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2);

if (!_currentServer.Equals(bestResult.server)) //output when enabled
{
{
LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}");
}
_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");

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

@@ -176,5 +182,6 @@ namespace Shadowsocks.Controller.Strategy
{
//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);
registry.SetValue("ProxyEnable", 0);
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);
}
}
else
{
registry.SetValue("ProxyEnable", 0);
if (global)
{
registry.SetValue("ProxyServer", "");
}
registry.SetValue("ProxyServer", "");
registry.SetValue("AutoConfigURL", "");
}
//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 的用户规则...
Show QRCode...=显示二维码...
Scan QRCode from Screen...=扫描屏幕上的二维码...
Availability Statistics=统计可用性
Show Logs...=显示日志...
About...=关于...
Quit=退出
@@ -41,6 +42,19 @@ Remarks=备注
OK=确定
Cancel=取消
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

@@ -72,7 +86,10 @@ Failed to update PAC file =更新 PAC 文件失败
PAC updated=更新 PAC 成功
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.=未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置
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 update registry=无法修改注册表
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 ivLen;
public IVEncryptor(string method, string password)
: base(method, password)
{


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

@@ -1,5 +1,6 @@
using Shadowsocks.Controller;
using Shadowsocks.Properties;
using Shadowsocks.Util;
using System;
using System.Collections.Generic;
using System.IO;
@@ -18,7 +19,7 @@ namespace Shadowsocks.Encryption
static PolarSSL()
{
string tempPath = Path.GetTempPath();
string tempPath = Utils.GetTempPath();
string dllPath = tempPath + "/libsscrypto.dll";
try
{
@@ -49,7 +50,6 @@ namespace Shadowsocks.Encryption
[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 const int ARC4_CTX_SIZE = 264;
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)]


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

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


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

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Shadowsocks.Encryption
{
@@ -12,19 +13,17 @@ namespace Shadowsocks.Encryption
const int SODIUM_BLOCK_SIZE = 64;
static byte[] sodiumBuf = new byte[MAX_INPUT_SIZE + SODIUM_BLOCK_SIZE];
protected int _encryptBytesRemaining;
protected int _decryptBytesRemaining;
protected ulong _encryptIC;
protected ulong _decryptIC;
protected byte[] _encryptBuf;
protected byte[] _decryptBuf;
public SodiumEncryptor(string method, string password)
: base(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[]> {
@@ -47,48 +46,51 @@ namespace Shadowsocks.Encryption
// TODO write a unidirection cipher so we don't have to if if if
int bytesRemaining;
ulong ic;
byte[] sodiumBuf;
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;
}
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength)
{
byte[] result = new byte[length];
@@ -100,7 +99,6 @@ namespace Shadowsocks.Encryption
return sorted;
}
public override void Dispose()
{
}


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

@@ -18,8 +18,8 @@ namespace Shadowsocks
[STAThread]
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.SetCompatibleTextRenderingDefault(false);
@@ -31,7 +31,9 @@ namespace Shadowsocks
{
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;
}
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.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace Shadowsocks.Util
{
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
// making the numbers look good in task manager
@@ -20,8 +39,29 @@ namespace Shadowsocks.Util
// which is part of user experience
GC.Collect(GC.MaxGeneration);
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)


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

@@ -47,6 +47,9 @@
this.ServerGroupBox = new System.Windows.Forms.GroupBox();
this.ServersListBox = new System.Windows.Forms.ListBox();
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.ProxyPortTextBox = new System.Windows.Forms.TextBox();
this.ProxyPortLabel = new System.Windows.Forms.Label();
@@ -55,6 +58,7 @@
this.tableLayoutPanel1.SuspendLayout();
this.ServerGroupBox.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.tableLayoutPanel6.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.tableLayoutPanel3.SuspendLayout();
this.tableLayoutPanel4.SuspendLayout();
@@ -184,7 +188,7 @@
//
// 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)));
this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.EncryptionSelect.FormattingEnabled = true;
@@ -281,6 +285,7 @@
//
this.ServersListBox.FormattingEnabled = true;
this.ServersListBox.IntegralHeight = false;
this.ServersListBox.ItemHeight = 12;
this.ServersListBox.Location = new System.Drawing.Point(0, 0);
this.ServersListBox.Margin = new System.Windows.Forms.Padding(0);
this.ServersListBox.Name = "ServersListBox";
@@ -295,6 +300,7 @@
this.tableLayoutPanel2.ColumnCount = 2;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel6, 0, 2);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel5, 1, 1);
this.tableLayoutPanel2.Controls.Add(this.tableLayoutPanel3, 1, 2);
this.tableLayoutPanel2.Controls.Add(this.ServersListBox, 0, 0);
@@ -310,6 +316,48 @@
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238);
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
//
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.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.tableLayoutPanel6.ResumeLayout(false);
this.tableLayoutPanel5.ResumeLayout(false);
this.tableLayoutPanel5.PerformLayout();
this.tableLayoutPanel3.ResumeLayout(false);
@@ -453,6 +502,9 @@
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel5;
private System.Windows.Forms.TextBox ProxyPortTextBox;
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
private Configuration _modifiedConfiguration;
private int _oldSelectedIndex = -1;
private int _lastSelectedIndex = -1;
public ConfigForm(ShadowsocksController controller)
{
@@ -51,6 +51,8 @@ namespace Shadowsocks.View
ServerGroupBox.Text = I18N.GetString("Server");
OKButton.Text = I18N.GetString("OK");
MyCancelButton.Text = I18N.GetString("Cancel");
MoveUpButton.Text = I18N.GetString("Move &Up");
MoveDownButton.Text = I18N.GetString("Move D&own");
this.Text = I18N.GetString("Edit Servers");
}
@@ -58,7 +60,7 @@ namespace Shadowsocks.View
{
LoadCurrentConfiguration();
}
private void ShowWindow()
{
this.Opacity = 1;
@@ -70,7 +72,7 @@ namespace Shadowsocks.View
{
try
{
if (_oldSelectedIndex == -1 || _oldSelectedIndex >= _modifiedConfiguration.configs.Count)
if (_lastSelectedIndex == -1 || _lastSelectedIndex >= _modifiedConfiguration.configs.Count)
{
return true;
}
@@ -85,9 +87,9 @@ namespace Shadowsocks.View
int localPort = int.Parse(ProxyPortTextBox.Text);
Configuration.CheckServer(server);
Configuration.CheckLocalPort(localPort);
_modifiedConfiguration.configs[_oldSelectedIndex] = server;
_modifiedConfiguration.configs[_lastSelectedIndex] = server;
_modifiedConfiguration.localPort = localPort;
return true;
}
catch (FormatException)
@@ -113,12 +115,6 @@ namespace Shadowsocks.View
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString();
EncryptionSelect.Text = server.method ?? "aes-256-cfb";
RemarksTextBox.Text = server.remarks;
ServerGroupBox.Visible = true;
//IPTextBox.Focus();
}
else
{
ServerGroupBox.Visible = false;
}
}
@@ -135,12 +131,13 @@ namespace Shadowsocks.View
{
_modifiedConfiguration = controller.GetConfigurationCopy();
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();
}
@@ -151,7 +148,11 @@ namespace Shadowsocks.View
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
return;
@@ -159,11 +160,13 @@ namespace Shadowsocks.View
if (!SaveOldSelectedServer())
{
// why this won't cause stack overflow?
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
return;
}
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName();
UpdateMoveUpAndDownButton();
LoadSelectedServer();
_oldSelectedIndex = ServersListBox.SelectedIndex;
_lastSelectedIndex = ServersListBox.SelectedIndex;
}
private void AddButton_Click(object sender, EventArgs e)
@@ -176,29 +179,30 @@ namespace Shadowsocks.View
_modifiedConfiguration.configs.Add(server);
LoadConfiguration(_modifiedConfiguration);
ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1;
_oldSelectedIndex = ServersListBox.SelectedIndex;
_lastSelectedIndex = ServersListBox.SelectedIndex;
}
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
_oldSelectedIndex = _modifiedConfiguration.configs.Count - 1;
_lastSelectedIndex = _modifiedConfiguration.configs.Count - 1;
}
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
LoadConfiguration(_modifiedConfiguration);
ServersListBox.SelectedIndex = _oldSelectedIndex;
ServersListBox.SelectedIndex = _lastSelectedIndex;
LoadSelectedServer();
}
private void OKButton_Click(object sender, EventArgs e)
{
Server server = controller.GetCurrentServer();
if (!SaveOldSelectedServer())
{
return;
@@ -209,6 +213,7 @@ namespace Shadowsocks.View
return;
}
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort);
controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server));
this.Close();
}
@@ -227,5 +232,70 @@ namespace Shadowsocks.View
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>
</resheader>
<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 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>
</root>

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

@@ -29,69 +29,143 @@
private void InitializeComponent()
{
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.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();
//
// 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
//
this.contextMenuStrip1.ImageScalingSize = new System.Drawing.Size(24, 24);
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(74, 4);
this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4);
//
// mainMenu1
//
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
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F);
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
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.Name = "LogForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
@@ -99,18 +173,27 @@
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing);
this.Load += new System.EventHandler(this.LogForm_Load);
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.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.TextBox LogMessageTextBox;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
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.Collections.Generic;
using System.ComponentModel;
@@ -23,6 +24,20 @@ namespace Shadowsocks.View
this.filename = filename;
InitializeComponent();
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)
@@ -43,9 +58,9 @@ namespace Shadowsocks.View
string line = "";
while ((line = reader.ReadLine()) != null)
textBox1.AppendText(line + "\r\n");
LogMessageTextBox.AppendText(line + "\r\n");
textBox1.ScrollToCaret();
LogMessageTextBox.ScrollToCaret();
lastOffset = reader.BaseStream.Position;
}
@@ -63,12 +78,12 @@ namespace Shadowsocks.View
while ((line = reader.ReadLine()) != null)
{
changed = true;
textBox1.AppendText(line + "\r\n");
LogMessageTextBox.AppendText(line + "\r\n");
}
if (changed)
{
textBox1.ScrollToCaret();
LogMessageTextBox.ScrollToCaret();
}
lastOffset = reader.BaseStream.Position;
@@ -89,25 +104,47 @@ namespace Shadowsocks.View
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);
}
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
// when config form is closed, it moves away from RAM
// and it should just do anything related to the config form
private ShadowsocksController controller;
private UpdateChecker updateChecker;
@@ -29,6 +29,7 @@ namespace Shadowsocks.View
private MenuItem enableItem;
private MenuItem modeItem;
private MenuItem AutoStartupItem;
private MenuItem AvailabilityStatistics;
private MenuItem ShareOverLANItem;
private MenuItem SeperatorItem;
private MenuItem ConfigItem;
@@ -164,7 +165,6 @@ namespace Shadowsocks.View
this.SeperatorItem = new MenuItem("-"),
this.ConfigItem = CreateMenuItem("Edit Servers...", new EventHandler(this.Config_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))
}),
CreateMenuGroup("PAC ", new MenuItem[] {
@@ -178,6 +178,7 @@ namespace Shadowsocks.View
}),
new MenuItem("-"),
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)),
new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
@@ -261,6 +262,7 @@ namespace Shadowsocks.View
PACModeItem.Checked = !config.global;
ShareOverLANItem.Checked = config.shareOverLan;
AutoStartupItem.Checked = AutoStartup.Check();
AvailabilityStatistics.Checked = config.availabilityStatistics;
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac;
localPACItem.Checked = !onlinePACItem.Checked;
UpdatePACItemsEnabledStatus();
@@ -320,7 +322,7 @@ namespace Shadowsocks.View
void configForm_FormClosed(object sender, FormClosedEventArgs e)
{
configForm = null;
Util.Utils.ReleaseMemory();
Util.Utils.ReleaseMemory(true);
ShowFirstTimeBalloon();
}
@@ -424,14 +426,6 @@ namespace Shadowsocks.View
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)
{
foreach (Screen screen in Screen.AllScreens)
@@ -526,12 +520,17 @@ namespace Shadowsocks.View
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)
{


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

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


Loading…
Cancel
Save