@@ -1,3 +1,4 @@ | |||||
/.vs/ | |||||
Backup/ | Backup/ | ||||
bin/ | bin/ | ||||
obj/ | obj/ | ||||
@@ -5,3 +6,9 @@ shadowsocks-csharp/shadowsocks-csharp.csproj.user | |||||
TestResults | TestResults | ||||
*.suo | *.suo | ||||
shadowsocks-csharp/3rd/* | |||||
!shadowsocks-csharp/3rd/zxing/ | |||||
!shadowsocks-csharp/3rd/SimpleJson.cs | |||||
packages/* | |||||
shadowsocks-csharp.sln.DotSettings.user |
@@ -1,3 +1,9 @@ | |||||
2.5.8 2015-09-20 | |||||
- Update GFWList url | |||||
2.5.7 2015-09-19 | |||||
- Fix repeated IV | |||||
2.5.6 2015-08-19 | 2.5.6 2015-08-19 | ||||
- Add portable mode. Create shadowsocks_portable_mode.txt to use it | - Add portable mode. Create shadowsocks_portable_mode.txt to use it | ||||
- Support server reorder | - Support server reorder | ||||
@@ -75,8 +75,8 @@ Visual Studio 2015 is required. | |||||
GPLv3 | GPLv3 | ||||
[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp | |||||
[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master | |||||
[Appveyor]: https://ci.appveyor.com/project/icylogic/shadowsocks-windows-l9mwe | |||||
[Build Status]: https://ci.appveyor.com/api/projects/status/ytllr9yjkbpc2tu2/branch/master | |||||
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases | [latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases | ||||
[GFWList]: https://github.com/gfwlist/gfwlist | [GFWList]: https://github.com/gfwlist/gfwlist | ||||
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side | [Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side | ||||
@@ -0,0 +1,6 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<configuration> | |||||
<config> | |||||
<add key="repositoryPath" value="shadowsocks-csharp\3rd" /> | |||||
</config> | |||||
</configuration> |
@@ -1,8 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.IO.Compression; | using System.IO.Compression; | ||||
using System.Text; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -12,9 +10,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
System.IO.FileStream _FileStream = | |||||
new System.IO.FileStream(fileName, System.IO.FileMode.Create, | |||||
System.IO.FileAccess.Write); | |||||
FileStream _FileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write); | |||||
_FileStream.Write(content, 0, content.Length); | _FileStream.Write(content, 0, content.Length); | ||||
_FileStream.Close(); | _FileStream.Close(); | ||||
return true; | return true; | ||||
@@ -31,7 +27,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
FileStream destinationFile = File.Create(fileName); | FileStream destinationFile = File.Create(fileName); | ||||
// Because the uncompressed size of the file is unknown, | |||||
// Because the uncompressed size of the file is unknown, | |||||
// we are using an arbitrary buffer size. | // we are using an arbitrary buffer size. | ||||
byte[] buffer = new byte[4096]; | byte[] buffer = new byte[4096]; | ||||
int n; | int n; | ||||
@@ -39,17 +35,13 @@ namespace Shadowsocks.Controller | |||||
using (GZipStream input = new GZipStream(new MemoryStream(content), | using (GZipStream input = new GZipStream(new MemoryStream(content), | ||||
CompressionMode.Decompress, false)) | CompressionMode.Decompress, false)) | ||||
{ | { | ||||
while (true) | |||||
while ((n = input.Read(buffer, 0, buffer.Length)) > 0) | |||||
{ | { | ||||
n = input.Read(buffer, 0, buffer.Length); | |||||
if (n == 0) | |||||
{ | |||||
break; | |||||
} | |||||
destinationFile.Write(buffer, 0, n); | destinationFile.Write(buffer, 0, n); | ||||
} | } | ||||
} | } | ||||
destinationFile.Close(); | destinationFile.Close(); | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,23 +1,23 @@ | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.IO; | using System.IO; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Text; | |||||
using System.Net; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
public class Logging | public class Logging | ||||
{ | { | ||||
public static string LogFile; | |||||
public static string LogFilePath; | |||||
public static bool OpenLogFile() | public static bool OpenLogFile() | ||||
{ | { | ||||
try | try | ||||
{ | { | ||||
string temppath = Utils.GetTempPath(); | |||||
LogFile = Path.Combine(temppath, "shadowsocks.log"); | |||||
FileStream fs = new FileStream(LogFile, FileMode.Append); | |||||
LogFilePath = Utils.GetTempPath("shadowsocks.log"); | |||||
FileStream fs = new FileStream(LogFilePath, FileMode.Append); | |||||
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); | StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); | ||||
sw.AutoFlush = true; | sw.AutoFlush = true; | ||||
Console.SetOut(sw); | Console.SetOut(sw); | ||||
@@ -32,11 +32,46 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private static void WriteToLogFile(object o) | |||||
{ | |||||
Console.WriteLine(o); | |||||
} | |||||
public static void Error(object o) | |||||
{ | |||||
WriteToLogFile("[E] " + o); | |||||
} | |||||
public static void Info(object o) | |||||
{ | |||||
WriteToLogFile(o); | |||||
} | |||||
public static void Debug(object o) | public static void Debug(object o) | ||||
{ | { | ||||
#if DEBUG | |||||
WriteToLogFile("[D] " + o); | |||||
#endif | |||||
} | |||||
public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null) | |||||
{ | |||||
#if DEBUG | #if DEBUG | ||||
Console.WriteLine(o); | |||||
if (header == null && tailer == null) | |||||
Debug($"{local} => {remote} (size={len})"); | |||||
else if (header == null && tailer != null) | |||||
Debug($"{local} => {remote} (size={len}), {tailer}"); | |||||
else if (header != null && tailer == null) | |||||
Debug($"{header}: {local} => {remote} (size={len})"); | |||||
else | |||||
Debug($"{header}: {local} => {remote} (size={len}), {tailer}"); | |||||
#endif | |||||
} | |||||
public static void Debug(Socket sock, int len, string header = null, string tailer = null) | |||||
{ | |||||
#if DEBUG | |||||
Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer); | |||||
#endif | #endif | ||||
} | } | ||||
@@ -57,11 +92,19 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
else if (se.SocketErrorCode == SocketError.NotConnected) | else if (se.SocketErrorCode == SocketError.NotConnected) | ||||
{ | { | ||||
// close when not connected | |||||
// The application tried to send or receive data, and the System.Net.Sockets.Socket is not connected. | |||||
} | |||||
else if (se.SocketErrorCode == SocketError.HostUnreachable) | |||||
{ | |||||
// There is no network route to the specified host. | |||||
} | |||||
else if (se.SocketErrorCode == SocketError.TimedOut) | |||||
{ | |||||
// The connection attempt timed out, or the connected host has failed to respond. | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
Console.WriteLine(e); | |||||
Info(e); | |||||
} | } | ||||
} | } | ||||
else if (e is ObjectDisposedException) | else if (e is ObjectDisposedException) | ||||
@@ -69,7 +112,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
Console.WriteLine(e); | |||||
Info(e); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,50 +1,75 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Globalization; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | |||||
using System.Net.Http; | |||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Net.Sockets; | |||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | |||||
using Newtonsoft.Json; | |||||
using Newtonsoft.Json.Linq; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using System.Reflection; | |||||
using Shadowsocks.Util; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
class AvailabilityStatistics | |||||
using DataUnit = KeyValuePair<string, string>; | |||||
using DataList = List<KeyValuePair<string, string>>; | |||||
using Statistics = Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>; | |||||
public class AvailabilityStatistics | |||||
{ | { | ||||
private static readonly string StatisticsFilesName = "shadowsocks.availability.csv"; | |||||
private static readonly string Delimiter = ","; | |||||
private static readonly int Timeout = 500; | |||||
private static readonly int Repeat = 4; //repeat times every evaluation | |||||
private static readonly int Interval = 10 * 60 * 1000; //evaluate proxies every 15 minutes | |||||
private Timer timer = null; | |||||
private State state = null; | |||||
private List<Server> servers; | |||||
public static readonly string DateTimePattern = "yyyy-MM-dd HH:mm:ss"; | |||||
private const string StatisticsFilesName = "shadowsocks.availability.csv"; | |||||
private const string Delimiter = ","; | |||||
private const int Timeout = 500; | |||||
private const int DelayBeforeStart = 1000; | |||||
public Statistics RawStatistics { get; private set; } | |||||
public Statistics FilteredStatistics { get; private set; } | |||||
public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); | |||||
private int Repeat => _config.RepeatTimesNum; | |||||
private const int RetryInterval = 2 * 60 * 1000; //retry 2 minutes after failed | |||||
private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; | |||||
private Timer _timer; | |||||
private State _state; | |||||
private List<Server> _servers; | |||||
private StatisticsStrategyConfiguration _config; | |||||
public static string AvailabilityStatisticsFile; | public static string AvailabilityStatisticsFile; | ||||
//static constructor to initialize every public static fields before refereced | //static constructor to initialize every public static fields before refereced | ||||
static AvailabilityStatistics() | static AvailabilityStatistics() | ||||
{ | { | ||||
string temppath = Utils.GetTempPath(); | |||||
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); | |||||
AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); | |||||
} | } | ||||
public bool Set(bool enabled) | |||||
public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig) | |||||
{ | { | ||||
UpdateConfiguration(config, statisticsConfig); | |||||
} | |||||
public bool Set(StatisticsStrategyConfiguration config) | |||||
{ | |||||
_config = config; | |||||
try | try | ||||
{ | { | ||||
if (enabled) | |||||
if (config.StatisticsEnabled) | |||||
{ | { | ||||
if (timer?.Change(0, Interval) == null) | |||||
if (_timer?.Change(DelayBeforeStart, Interval) == null) | |||||
{ | { | ||||
state = new State(); | |||||
timer = new Timer(Evaluate, state, 0, Interval); | |||||
_state = new State(); | |||||
_timer = new Timer(Run, _state, DelayBeforeStart, Interval); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
timer?.Dispose(); | |||||
_timer?.Dispose(); | |||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
@@ -55,63 +80,262 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void Evaluate(object obj) | |||||
//hardcode | |||||
//TODO: backup reliable isp&geolocation provider or a local database is required | |||||
public static async Task<DataList> GetGeolocationAndIsp() | |||||
{ | { | ||||
Ping ping = new Ping(); | |||||
State state = (State) obj; | |||||
foreach (var server in servers) | |||||
Logging.Debug("Retrive information of geolocation and isp"); | |||||
const string API = "http://ip-api.com/json"; | |||||
const string alternativeAPI = "http://www.telize.com/geoip"; //must be comptible with current API | |||||
var result = await GetInfoFromAPI(API); | |||||
if (result != null) return result; | |||||
result = await GetInfoFromAPI(alternativeAPI); | |||||
if (result != null) return result; | |||||
return new DataList | |||||
{ | |||||
new DataUnit(State.Geolocation, State.Unknown), | |||||
new DataUnit(State.ISP, State.Unknown) | |||||
}; | |||||
} | |||||
private static async Task<DataList> GetInfoFromAPI(string API) | |||||
{ | |||||
string jsonString; | |||||
try | |||||
{ | |||||
jsonString = await new HttpClient().GetStringAsync(API); | |||||
} | |||||
catch (HttpRequestException e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
return null; | |||||
} | |||||
JObject obj; | |||||
try | |||||
{ | |||||
obj = JObject.Parse(jsonString); | |||||
} | |||||
catch (JsonReaderException) | |||||
{ | { | ||||
Logging.Debug("eveluating " + server.FriendlyName()); | |||||
foreach (var _ in Enumerable.Range(0, Repeat)) | |||||
return null; | |||||
} | |||||
string country = (string)obj["country"]; | |||||
string city = (string)obj["city"]; | |||||
string isp = (string)obj["isp"]; | |||||
if (country == null || city == null || isp == null) return null; | |||||
return new DataList { | |||||
new DataUnit(State.Geolocation, $"\"{country} {city}\""), | |||||
new DataUnit(State.ISP, $"\"{isp}\"") | |||||
}; | |||||
} | |||||
private async Task<List<DataList>> ICMPTest(Server server) | |||||
{ | |||||
Logging.Debug("Ping " + server.FriendlyName()); | |||||
if (server.server == "") return null; | |||||
var ret = new List<DataList>(); | |||||
try { | |||||
var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6)); | |||||
var ping = new Ping(); | |||||
foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString(DateTimePattern))) | |||||
{ | { | ||||
//TODO: do simple analyze of data to provide friendly message, like package loss. | |||||
string timestamp = 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 | ||||
//seems no need to use SendPingAsync | |||||
try | try | ||||
{ | { | ||||
PingReply reply = ping.Send(server.server, Timeout); | |||||
state.data = new List<KeyValuePair<string, string>>(); | |||||
state.data.Add(new KeyValuePair<string, string>("Timestamp", timestamp)); | |||||
state.data.Add(new KeyValuePair<string, string>("Server", server.FriendlyName())); | |||||
state.data.Add(new KeyValuePair<string, string>("Status", reply.Status.ToString())); | |||||
state.data.Add(new KeyValuePair<string, string>("RoundtripTime", reply.RoundtripTime.ToString())); | |||||
//state.data.Add(new KeyValuePair<string, string>("data", reply.Buffer.ToString())); // The data of reply | |||||
Append(state.data); | |||||
var reply = await ping.SendTaskAsync(IP, Timeout); | |||||
ret.Add(new List<KeyValuePair<string, string>> | |||||
{ | |||||
new KeyValuePair<string, string>("Timestamp", timestamp), | |||||
new KeyValuePair<string, string>("Server", server.FriendlyName()), | |||||
new KeyValuePair<string, string>("Status", reply?.Status.ToString()), | |||||
new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString()) | |||||
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply | |||||
}); | |||||
Thread.Sleep(Timeout + new Random().Next() % Timeout); | |||||
//Do ICMPTest in a random frequency | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
} | } | ||||
} | } | ||||
}catch(Exception e) | |||||
{ | |||||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
return ret; | |||||
} | |||||
private void Run(object obj) | |||||
{ | |||||
LoadRawStatistics(); | |||||
FilterRawStatistics(); | |||||
evaluate(); | |||||
} | |||||
private async void evaluate() | |||||
{ | |||||
var geolocationAndIsp = GetGeolocationAndIsp(); | |||||
foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) | |||||
{ | |||||
if (dataLists == null) continue; | |||||
foreach (var dataList in dataLists.Where(dataList => dataList != null)) | |||||
{ | |||||
await geolocationAndIsp; | |||||
Append(dataList, geolocationAndIsp.Result); | |||||
} | |||||
} | } | ||||
} | } | ||||
private static void Append(List<KeyValuePair<string, string>> data) | |||||
private static void Append(DataList dataList, IEnumerable<DataUnit> extra) | |||||
{ | { | ||||
string dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray()); | |||||
var data = dataList.Concat(extra); | |||||
var dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray()); | |||||
string[] lines; | string[] lines; | ||||
if (!File.Exists(AvailabilityStatisticsFile)) | if (!File.Exists(AvailabilityStatisticsFile)) | ||||
{ | { | ||||
string headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); | |||||
lines = new string[] { headerLine, dataLine }; | |||||
var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); | |||||
lines = new[] { headerLine, dataLine }; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
lines = new string[] { dataLine }; | |||||
lines = new[] { dataLine }; | |||||
} | } | ||||
File.AppendAllLines(AvailabilityStatisticsFile, lines); | |||||
try | |||||
{ | |||||
File.AppendAllLines(AvailabilityStatisticsFile, lines); | |||||
} | |||||
catch (IOException e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
internal void UpdateConfiguration(Configuration config, StatisticsStrategyConfiguration statisticsConfig) | |||||
{ | |||||
Set(statisticsConfig); | |||||
_servers = config.configs; | |||||
} | |||||
private async void FilterRawStatistics() | |||||
{ | |||||
if (RawStatistics == null) return; | |||||
if (FilteredStatistics == null) | |||||
{ | |||||
FilteredStatistics = new Statistics(); | |||||
} | |||||
foreach (IEnumerable<RawStatisticsData> rawData in RawStatistics.Values) | |||||
{ | |||||
var filteredData = rawData; | |||||
if (_config.ByIsp) | |||||
{ | |||||
var current = await GetGeolocationAndIsp(); | |||||
filteredData = | |||||
filteredData.Where( | |||||
data => | |||||
data.Geolocation == current[0].Value || | |||||
data.Geolocation == State.Unknown); | |||||
filteredData = | |||||
filteredData.Where( | |||||
data => data.ISP == current[1].Value || data.ISP == State.Unknown); | |||||
if (filteredData.LongCount() == 0) return; | |||||
} | |||||
if (_config.ByHourOfDay) | |||||
{ | |||||
var currentHour = DateTime.Now.Hour; | |||||
filteredData = filteredData.Where(data => | |||||
data.Timestamp != UnknownDateTime && data.Timestamp.Hour.Equals(currentHour) | |||||
); | |||||
if (filteredData.LongCount() == 0) return; | |||||
} | |||||
var dataList = filteredData as List<RawStatisticsData> ?? filteredData.ToList(); | |||||
var serverName = dataList[0].ServerName; | |||||
FilteredStatistics[serverName] = dataList; | |||||
} | |||||
} | |||||
private void LoadRawStatistics() | |||||
{ | |||||
try | |||||
{ | |||||
var path = AvailabilityStatisticsFile; | |||||
Logging.Debug($"loading statistics from {path}"); | |||||
if (!File.Exists(path)) | |||||
{ | |||||
try { | |||||
using (FileStream fs = File.Create(path)) | |||||
{ | |||||
//do nothing | |||||
} | |||||
}catch(Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
if (!File.Exists(path)) { | |||||
Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later"); | |||||
_timer.Change(RetryInterval, Interval); | |||||
return; | |||||
} | |||||
} | |||||
RawStatistics = (from l in File.ReadAllLines(path).Skip(1) | |||||
let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) | |||||
let rawData = new RawStatisticsData | |||||
{ | |||||
Timestamp = ParseExactOrUnknown(strings[0]), | |||||
ServerName = strings[1], | |||||
ICMPStatus = strings[2], | |||||
RoundtripTime = int.Parse(strings[3]), | |||||
Geolocation = 5 > strings.Length ? | |||||
null | |||||
: strings[4], | |||||
ISP = 6 > strings.Length ? null : strings[5] | |||||
} | |||||
group rawData by rawData.ServerName into server | |||||
select new | |||||
{ | |||||
ServerName = server.Key, | |||||
data = server.ToList() | |||||
}).ToDictionary(server => server.ServerName, server => server.data); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
private DateTime ParseExactOrUnknown(string str) | |||||
{ | |||||
DateTime dateTime; | |||||
return !DateTime.TryParseExact(str, DateTimePattern, null, DateTimeStyles.None, out dateTime) ? UnknownDateTime : dateTime; | |||||
} | |||||
public class State | |||||
{ | |||||
public DataList dataList = new DataList(); | |||||
public const string Geolocation = "Geolocation"; | |||||
public const string ISP = "ISP"; | |||||
public const string Unknown = "Unknown"; | |||||
} | } | ||||
internal void UpdateConfiguration(Configuration _config) | |||||
public class RawStatisticsData | |||||
{ | { | ||||
Set(_config.availabilityStatistics); | |||||
servers = _config.configs; | |||||
public DateTime Timestamp; | |||||
public string ServerName; | |||||
public string ICMPStatus; | |||||
public int RoundtripTime; | |||||
public string Geolocation; | |||||
public string ISP; | |||||
} | } | ||||
private class State | |||||
public class StatisticsData | |||||
{ | { | ||||
public List<KeyValuePair<string, string>> data = new List<KeyValuePair<string, string>>(); | |||||
public float PackageLoss; | |||||
public int AverageResponse; | |||||
public int MinResponse; | |||||
public int MaxResponse; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,22 +1,20 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | |||||
using System.Net; | |||||
using System.IO; | using System.IO; | ||||
using System.Net; | |||||
using System.Text; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using SimpleJson; | |||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
using Shadowsocks.Model; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
public class GFWListUpdater | public class GFWListUpdater | ||||
{ | { | ||||
private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; | |||||
private static string PAC_FILE = PACServer.PAC_FILE; | |||||
private static string USER_RULE_FILE = PACServer.USER_RULE_FILE; | |||||
private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"; | |||||
public event EventHandler<ResultEventArgs> UpdateCompleted; | public event EventHandler<ResultEventArgs> UpdateCompleted; | ||||
@@ -36,30 +34,39 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8); | |||||
List<string> lines = ParseResult(e.Result); | List<string> lines = ParseResult(e.Result); | ||||
if (File.Exists(USER_RULE_FILE)) | |||||
if (File.Exists(PACServer.USER_RULE_FILE)) | |||||
{ | { | ||||
string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); | |||||
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); | |||||
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | ||||
foreach(string rule in rules) | |||||
foreach (string rule in rules) | |||||
{ | { | ||||
if (rule.StartsWith("!") || rule.StartsWith("[")) | if (rule.StartsWith("!") || rule.StartsWith("[")) | ||||
continue; | continue; | ||||
lines.Add(rule); | lines.Add(rule); | ||||
} | } | ||||
} | } | ||||
string abpContent = Utils.UnGzip(Resources.abp_js); | |||||
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); | |||||
if (File.Exists(PAC_FILE)) | |||||
string abpContent; | |||||
if (File.Exists(PACServer.USER_ABP_FILE)) | |||||
{ | |||||
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
{ | |||||
abpContent = Utils.UnGzip(Resources.abp_js); | |||||
} | |||||
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); | |||||
if (File.Exists(PACServer.PAC_FILE)) | |||||
{ | { | ||||
string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||||
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); | |||||
if (original == abpContent) | if (original == abpContent) | ||||
{ | { | ||||
UpdateCompleted(this, new ResultEventArgs(false)); | UpdateCompleted(this, new ResultEventArgs(false)); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); | |||||
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); | |||||
if (UpdateCompleted != null) | if (UpdateCompleted != null) | ||||
{ | { | ||||
UpdateCompleted(this, new ResultEventArgs(true)); | UpdateCompleted(this, new ResultEventArgs(true)); | ||||
@@ -82,7 +89,7 @@ namespace Shadowsocks.Controller | |||||
http.DownloadStringAsync(new Uri(GFWLIST_URL)); | http.DownloadStringAsync(new Uri(GFWLIST_URL)); | ||||
} | } | ||||
public List<string> ParseResult(string response) | |||||
public static List<string> ParseResult(string response) | |||||
{ | { | ||||
byte[] bytes = Convert.FromBase64String(response); | byte[] bytes = Convert.FromBase64String(response); | ||||
string content = Encoding.ASCII.GetString(bytes); | string content = Encoding.ASCII.GetString(bytes); | ||||
@@ -97,4 +104,4 @@ namespace Shadowsocks.Controller | |||||
return valid_lines; | return valid_lines; | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -1,10 +1,10 @@ | |||||
using Shadowsocks.Model; | |||||
using System; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Net; | using System.Net; | ||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Text; | |||||
using Shadowsocks.Model; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -78,10 +78,8 @@ namespace Shadowsocks.Controller | |||||
_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"); | |||||
_tcpSocket.BeginAccept( | |||||
new AsyncCallback(AcceptCallback), | |||||
_tcpSocket); | |||||
Logging.Info("Shadowsocks started"); | |||||
_tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket); | |||||
UDPState udpState = new UDPState(); | UDPState udpState = new UDPState(); | ||||
_udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); | _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); | ||||
} | } | ||||
@@ -163,7 +161,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
finally | finally | ||||
{ | { | ||||
@@ -208,7 +206,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e); | |||||
Logging.LogUsefulException(e); | |||||
conn.Close(); | conn.Close(); | ||||
} | } | ||||
} | } | ||||
@@ -1,31 +1,33 @@ | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System; | |||||
using System.Collections; | |||||
using System.IO; | using System.IO; | ||||
using System.IO.Compression; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Text; | using System.Text; | ||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
class PACServer : Listener.Service | class PACServer : Listener.Service | ||||
{ | { | ||||
public static string PAC_FILE = "pac.txt"; | |||||
public static string USER_RULE_FILE = "user-rule.txt"; | |||||
public static readonly string PAC_FILE = "pac.txt"; | |||||
public static readonly string USER_RULE_FILE = "user-rule.txt"; | |||||
public static readonly string USER_ABP_FILE = "abp.txt"; | |||||
FileSystemWatcher watcher; | |||||
FileSystemWatcher PACFileWatcher; | |||||
FileSystemWatcher UserRuleFileWatcher; | |||||
private Configuration _config; | private Configuration _config; | ||||
public event EventHandler PACFileChanged; | public event EventHandler PACFileChanged; | ||||
public event EventHandler UserRuleFileChanged; | |||||
public PACServer() | public PACServer() | ||||
{ | { | ||||
this.WatchPacFile(); | this.WatchPacFile(); | ||||
this.WatchUserRuleFile(); | |||||
} | } | ||||
public void UpdateConfiguration(Configuration config) | public void UpdateConfiguration(Configuration config) | ||||
@@ -46,7 +48,7 @@ namespace Shadowsocks.Controller | |||||
bool hostMatch = false, pathMatch = false, useSocks = false; | bool hostMatch = false, pathMatch = false, useSocks = false; | ||||
foreach (string line in lines) | foreach (string line in lines) | ||||
{ | { | ||||
string[] kv = line.Split(new char[]{':'}, 2); | |||||
string[] kv = line.Split(new char[] { ':' }, 2); | |||||
if (kv.Length == 2) | if (kv.Length == 2) | ||||
{ | { | ||||
if (kv[0] == "Host") | if (kv[0] == "Host") | ||||
@@ -56,14 +58,14 @@ namespace Shadowsocks.Controller | |||||
hostMatch = true; | hostMatch = true; | ||||
} | } | ||||
} | } | ||||
else if (kv[0] == "User-Agent") | |||||
{ | |||||
// we need to drop connections when changing servers | |||||
/* if (kv[1].IndexOf("Chrome") >= 0) | |||||
{ | |||||
useSocks = true; | |||||
} */ | |||||
} | |||||
//else if (kv[0] == "User-Agent") | |||||
//{ | |||||
// // we need to drop connections when changing servers | |||||
// if (kv[1].IndexOf("Chrome") >= 0) | |||||
// { | |||||
// useSocks = true; | |||||
// } | |||||
//} | |||||
} | } | ||||
else if (kv.Length == 1) | else if (kv.Length == 1) | ||||
{ | { | ||||
@@ -142,14 +144,14 @@ Content-Type: application/x-ns-proxy-autoconfig | |||||
Content-Length: {0} | Content-Length: {0} | ||||
Connection: Close | Connection: Close | ||||
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; | |||||
byte[] response = System.Text.Encoding.UTF8.GetBytes(text); | |||||
", Encoding.UTF8.GetBytes(pac).Length) + pac; | |||||
byte[] response = 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(true); | |||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e); | |||||
Logging.LogUsefulException(e); | |||||
socket.Close(); | socket.Close(); | ||||
} | } | ||||
} | } | ||||
@@ -167,27 +169,78 @@ Connection: Close | |||||
private void WatchPacFile() | private void WatchPacFile() | ||||
{ | { | ||||
if (watcher != null) | |||||
{ | |||||
watcher.Dispose(); | |||||
} | |||||
watcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
watcher.Filter = PAC_FILE; | |||||
watcher.Changed += Watcher_Changed; | |||||
watcher.Created += Watcher_Changed; | |||||
watcher.Deleted += Watcher_Changed; | |||||
watcher.Renamed += Watcher_Changed; | |||||
watcher.EnableRaisingEvents = true; | |||||
if (PACFileWatcher != null) | |||||
{ | |||||
PACFileWatcher.Dispose(); | |||||
} | |||||
PACFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
PACFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
PACFileWatcher.Filter = PAC_FILE; | |||||
PACFileWatcher.Changed += PACFileWatcher_Changed; | |||||
PACFileWatcher.Created += PACFileWatcher_Changed; | |||||
PACFileWatcher.Deleted += PACFileWatcher_Changed; | |||||
PACFileWatcher.Renamed += PACFileWatcher_Changed; | |||||
PACFileWatcher.EnableRaisingEvents = true; | |||||
} | } | ||||
private void Watcher_Changed(object sender, FileSystemEventArgs e) | |||||
private void WatchUserRuleFile() | |||||
{ | { | ||||
if (PACFileChanged != null) | |||||
if (UserRuleFileWatcher != null) | |||||
{ | { | ||||
PACFileChanged(this, new EventArgs()); | |||||
UserRuleFileWatcher.Dispose(); | |||||
} | |||||
UserRuleFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); | |||||
UserRuleFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; | |||||
UserRuleFileWatcher.Filter = USER_RULE_FILE; | |||||
UserRuleFileWatcher.Changed += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Created += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Deleted += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.Renamed += UserRuleFileWatcher_Changed; | |||||
UserRuleFileWatcher.EnableRaisingEvents = true; | |||||
} | |||||
#region FileSystemWatcher.OnChanged() | |||||
// FileSystemWatcher Changed event is raised twice | |||||
// http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice | |||||
private static Hashtable fileChangedTime = new Hashtable(); | |||||
private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e) | |||||
{ | |||||
string path = e.FullPath.ToString(); | |||||
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString(); | |||||
// if there is no path info stored yet or stored path has different time of write then the one now is inspected | |||||
if (!fileChangedTime.ContainsKey(path) || fileChangedTime[path].ToString() != currentLastWriteTime) | |||||
{ | |||||
if (PACFileChanged != null) | |||||
{ | |||||
Logging.Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||||
PACFileChanged(this, new EventArgs()); | |||||
} | |||||
// lastly we update the last write time in the hashtable | |||||
fileChangedTime[path] = currentLastWriteTime; | |||||
} | |||||
} | |||||
private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e) | |||||
{ | |||||
string path = e.FullPath.ToString(); | |||||
string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString(); | |||||
// if there is no path info stored yet or stored path has different time of write then the one now is inspected | |||||
if (!fileChangedTime.ContainsKey(path) || fileChangedTime[path].ToString() != currentLastWriteTime) | |||||
{ | |||||
if (UserRuleFileChanged != null) | |||||
{ | |||||
Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); | |||||
UserRuleFileChanged(this, new EventArgs()); | |||||
} | |||||
// lastly we update the last write time in the hashtable | |||||
fileChangedTime[path] = currentLastWriteTime; | |||||
} | } | ||||
} | } | ||||
#endregion | |||||
private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) | private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) | ||||
{ | { | ||||
@@ -202,7 +255,7 @@ Connection: Close | |||||
//} | //} | ||||
//catch (Exception e) | //catch (Exception e) | ||||
//{ | //{ | ||||
// Console.WriteLine(e); | |||||
// Logging.LogUsefulException(e); | |||||
//} | //} | ||||
return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";"; | return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";"; | ||||
} | } | ||||
@@ -1,14 +1,14 @@ | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using System; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.IO; | using System.IO; | ||||
using System.IO.Compression; | |||||
using System.Text; | |||||
using System.Net.NetworkInformation; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.NetworkInformation; | |||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | using Shadowsocks.Util; | ||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
@@ -16,16 +16,14 @@ namespace Shadowsocks.Controller | |||||
class PolipoRunner | class PolipoRunner | ||||
{ | { | ||||
private Process _process; | private Process _process; | ||||
private static string temppath; | |||||
private int _runningPort; | private int _runningPort; | ||||
static PolipoRunner() | static PolipoRunner() | ||||
{ | { | ||||
temppath = Utils.GetTempPath(); | |||||
try | try | ||||
{ | { | ||||
FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe); | |||||
FileManager.UncompressFile(temppath + "/mgwz.dll", Resources.mgwz_dll); | |||||
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe); | |||||
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll); | |||||
} | } | ||||
catch (IOException e) | catch (IOException e) | ||||
{ | { | ||||
@@ -56,7 +54,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e.ToString()); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
} | } | ||||
string polipoConfig = Resources.privoxy_conf; | string polipoConfig = Resources.privoxy_conf; | ||||
@@ -64,20 +62,16 @@ namespace Shadowsocks.Controller | |||||
polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); | polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); | ||||
polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString()); | polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString()); | ||||
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(Utils.GetTempPath("privoxy.conf"), 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 = "ss_privoxy.exe"; | |||||
_process.StartInfo.Arguments = "privoxy.conf"; | |||||
_process.StartInfo.WorkingDirectory = Utils.GetTempPath(); | |||||
_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; | ||||
//_process.StartInfo.RedirectStandardOutput = true; | |||||
//_process.StartInfo.RedirectStandardError = true; | |||||
_process.Start(); | _process.Start(); | ||||
} | } | ||||
RefreshTrayArea(); | RefreshTrayArea(); | ||||
@@ -94,7 +88,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e.ToString()); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
_process = null; | _process = null; | ||||
} | } | ||||
@@ -1,8 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Text; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -140,7 +138,6 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
//Console.WriteLine("bytesRead: " + bytesRead.ToString()); | |||||
_local.Shutdown(SocketShutdown.Send); | _local.Shutdown(SocketShutdown.Send); | ||||
_localShutdown = true; | _localShutdown = true; | ||||
CheckClose(); | CheckClose(); | ||||
@@ -1,31 +1,30 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | |||||
using System.Net.Sockets; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | |||||
using System.Timers; | |||||
using Shadowsocks.Controller.Strategy; | |||||
using Shadowsocks.Encryption; | using Shadowsocks.Encryption; | ||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Controller.Strategy; | |||||
using System.Timers; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
class TCPRelay : Listener.Service | class TCPRelay : Listener.Service | ||||
{ | { | ||||
private ShadowsocksController _controller; | private ShadowsocksController _controller; | ||||
private DateTime _lastSweepTime; | private DateTime _lastSweepTime; | ||||
public ISet<Handler> Handlers | |||||
public ISet<TCPHandler> Handlers | |||||
{ | { | ||||
get; set; | get; set; | ||||
} | } | ||||
public TCPRelay(ShadowsocksController controller) | public TCPRelay(ShadowsocksController controller) | ||||
{ | { | ||||
this._controller = controller; | |||||
this.Handlers = new HashSet<Handler>(); | |||||
this._lastSweepTime = DateTime.Now; | |||||
_controller = controller; | |||||
Handlers = new HashSet<TCPHandler>(); | |||||
_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) | ||||
@@ -39,22 +38,21 @@ namespace Shadowsocks.Controller | |||||
return false; | return false; | ||||
} | } | ||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
Handler handler = new Handler(); | |||||
TCPHandler handler = new TCPHandler(this); | |||||
handler.connection = socket; | handler.connection = socket; | ||||
handler.controller = _controller; | handler.controller = _controller; | ||||
handler.relay = this; | handler.relay = this; | ||||
handler.Start(firstPacket, length); | handler.Start(firstPacket, length); | ||||
IList<Handler> handlersToClose = new List<Handler>(); | |||||
lock (this.Handlers) | |||||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | |||||
lock (Handlers) | |||||
{ | { | ||||
this.Handlers.Add(handler); | |||||
Logging.Debug($"connections: {Handlers.Count}"); | |||||
Handlers.Add(handler); | |||||
DateTime now = DateTime.Now; | DateTime now = DateTime.Now; | ||||
if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) | if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) | ||||
{ | { | ||||
_lastSweepTime = now; | _lastSweepTime = now; | ||||
foreach (Handler handler1 in this.Handlers) | |||||
foreach (TCPHandler handler1 in Handlers) | |||||
{ | { | ||||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | ||||
{ | { | ||||
@@ -63,18 +61,28 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
} | } | ||||
foreach (Handler handler1 in handlersToClose) | |||||
foreach (TCPHandler handler1 in handlersToClose) | |||||
{ | { | ||||
Logging.Debug("Closing timed out connection"); | |||||
Logging.Debug("Closing timed out TCP connection."); | |||||
handler1.Close(); | handler1.Close(); | ||||
} | } | ||||
return true; | |||||
return true; | |||||
} | |||||
public void UpdateInboundCounter(long n) | |||||
{ | |||||
_controller.UpdateInboundCounter(n); | |||||
} | |||||
public void UpdateOutboundCounter(long n) | |||||
{ | |||||
_controller.UpdateOutboundCounter(n); | |||||
} | } | ||||
} | } | ||||
class Handler | |||||
class TCPHandler | |||||
{ | { | ||||
//public Encryptor encryptor; | |||||
// public Encryptor encryptor; | |||||
public IEncryptor encryptor; | public IEncryptor encryptor; | ||||
public Server server; | public Server server; | ||||
// Client socket. | // Client socket. | ||||
@@ -85,6 +93,7 @@ namespace Shadowsocks.Controller | |||||
public DateTime lastActivity; | public DateTime lastActivity; | ||||
private const int maxRetry = 4; | |||||
private int retryCount = 0; | private int retryCount = 0; | ||||
private bool connected; | private bool connected; | ||||
@@ -93,17 +102,18 @@ namespace Shadowsocks.Controller | |||||
private int _firstPacketLength; | private int _firstPacketLength; | ||||
// Size of receive buffer. | // Size of receive buffer. | ||||
public const int RecvSize = 8192; | public const int RecvSize = 8192; | ||||
public const int BufferSize = RecvSize + 32; | |||||
public const int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth | |||||
public const int BufferSize = RecvSize + RecvReserveSize + 32; | |||||
private int totalRead = 0; | private int totalRead = 0; | ||||
private int totalWrite = 0; | private int totalWrite = 0; | ||||
// remote receive buffer | // remote receive buffer | ||||
private byte[] remoteRecvBuffer = new byte[RecvSize]; | |||||
private byte[] remoteRecvBuffer = new byte[BufferSize]; | |||||
// remote send buffer | // remote send buffer | ||||
private byte[] remoteSendBuffer = new byte[BufferSize]; | private byte[] remoteSendBuffer = new byte[BufferSize]; | ||||
// connection receive buffer | // connection receive buffer | ||||
private byte[] connetionRecvBuffer = new byte[RecvSize]; | |||||
private byte[] connetionRecvBuffer = new byte[BufferSize]; | |||||
// connection send buffer | // connection send buffer | ||||
private byte[] connetionSendBuffer = new byte[BufferSize]; | private byte[] connetionSendBuffer = new byte[BufferSize]; | ||||
// Received data string. | // Received data string. | ||||
@@ -116,6 +126,12 @@ namespace Shadowsocks.Controller | |||||
private object decryptionLock = new object(); | private object decryptionLock = new object(); | ||||
private DateTime _startConnectTime; | private DateTime _startConnectTime; | ||||
private TCPRelay tcprelay; // TODO: tcprelay ?= relay | |||||
public TCPHandler(TCPRelay tcprelay) | |||||
{ | |||||
this.tcprelay = tcprelay; | |||||
} | |||||
public void CreateRemote() | public void CreateRemote() | ||||
{ | { | ||||
@@ -124,23 +140,23 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
throw new ArgumentException("No server configured"); | throw new ArgumentException("No server configured"); | ||||
} | } | ||||
this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); | |||||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); | |||||
this.server = server; | this.server = server; | ||||
} | } | ||||
public void Start(byte[] firstPacket, int length) | public void Start(byte[] firstPacket, int length) | ||||
{ | { | ||||
this._firstPacket = firstPacket; | |||||
this._firstPacketLength = length; | |||||
this.HandshakeReceive(); | |||||
this.lastActivity = DateTime.Now; | |||||
_firstPacket = firstPacket; | |||||
_firstPacketLength = length; | |||||
HandshakeReceive(); | |||||
lastActivity = DateTime.Now; | |||||
} | } | ||||
private void CheckClose() | private void CheckClose() | ||||
{ | { | ||||
if (connectionShutdown && remoteShutdown) | if (connectionShutdown && remoteShutdown) | ||||
{ | { | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -148,7 +164,6 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
lock (relay.Handlers) | lock (relay.Handlers) | ||||
{ | { | ||||
Logging.Debug($"connections: {relay.Handlers.Count}"); | |||||
relay.Handlers.Remove(this); | relay.Handlers.Remove(this); | ||||
} | } | ||||
lock (this) | lock (this) | ||||
@@ -212,19 +227,19 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
// reject socks 4 | // reject socks 4 | ||||
response = new byte[] { 0, 91 }; | response = new byte[] { 0, 91 }; | ||||
Console.WriteLine("socks 5 protocol error"); | |||||
Logging.Error("socks 5 protocol error"); | |||||
} | } | ||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); | connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -238,20 +253,19 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
// +----+-----+-------+------+----------+----------+ | |||||
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | |||||
// +----+-----+-------+------+----------+----------+ | |||||
// | 1 | 1 | X'00' | 1 | Variable | 2 | | |||||
// +----+-----+-------+------+----------+----------+ | |||||
// +-----+-----+-------+------+----------+----------+ | |||||
// | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | |||||
// +-----+-----+-------+------+----------+----------+ | |||||
// | 1 | 1 | X'00' | 1 | Variable | 2 | | |||||
// +-----+-----+-------+------+----------+----------+ | |||||
// Skip first 3 bytes | // Skip first 3 bytes | ||||
// TODO validate | // TODO validate | ||||
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, | |||||
new AsyncCallback(handshakeReceive2Callback), null); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -280,14 +294,14 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
Console.WriteLine("failed to recv data in handshakeReceive2Callback"); | |||||
this.Close(); | |||||
Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()"); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -323,27 +337,27 @@ namespace Shadowsocks.Controller | |||||
if (ar.AsyncState != null) | if (ar.AsyncState != null) | ||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(ReadAll), null); | |||||
Logging.Debug(remote, RecvSize, "TCP Relay"); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
int bytesRead = connection.EndReceive(ar); | int bytesRead = connection.EndReceive(ar); | ||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(ReadAll), null); | |||||
Logging.Debug(remote, RecvSize, "TCP Relay"); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -359,15 +373,16 @@ namespace Shadowsocks.Controller | |||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
// inner class | |||||
private class ServerTimer : Timer | private class ServerTimer : Timer | ||||
{ | { | ||||
public Server Server; | public Server Server; | ||||
public ServerTimer(int p) :base(p) | |||||
public ServerTimer(int p) : base(p) | |||||
{ | { | ||||
} | } | ||||
} | } | ||||
@@ -401,13 +416,12 @@ namespace Shadowsocks.Controller | |||||
connected = false; | connected = false; | ||||
// Connect to the remote endpoint. | // Connect to the remote endpoint. | ||||
remote.BeginConnect(remoteEP, | |||||
new AsyncCallback(ConnectCallback), connectTimer); | |||||
remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -423,22 +437,22 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
strategy.SetFailure(server); | strategy.SetFailure(server); | ||||
} | } | ||||
Console.WriteLine(String.Format("{0} timed out", server.FriendlyName())); | |||||
Logging.Info($"{server.FriendlyName()} timed out"); | |||||
remote.Close(); | remote.Close(); | ||||
RetryConnect(); | RetryConnect(); | ||||
} | } | ||||
private void RetryConnect() | private void RetryConnect() | ||||
{ | { | ||||
if (retryCount < 4) | |||||
if (retryCount < maxRetry) | |||||
{ | { | ||||
Logging.Debug("Connection failed, retrying"); | |||||
Logging.Debug($"Connection failed, retry ({retryCount})"); | |||||
StartConnect(); | StartConnect(); | ||||
retryCount++; | retryCount++; | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -462,8 +476,7 @@ namespace Shadowsocks.Controller | |||||
connected = true; | connected = true; | ||||
//Console.WriteLine("Socket connected to {0}", | |||||
// remote.RemoteEndPoint.ToString()); | |||||
Logging.Debug($"Socket connected to {remote.RemoteEndPoint}"); | |||||
var latency = DateTime.Now - _startConnectTime; | var latency = DateTime.Now - _startConnectTime; | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
@@ -500,15 +513,13 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
try | try | ||||
{ | { | ||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -522,10 +533,11 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
int bytesRead = remote.EndReceive(ar); | int bytesRead = remote.EndReceive(ar); | ||||
totalRead += bytesRead; | totalRead += bytesRead; | ||||
tcprelay.UpdateInboundCounter(bytesRead); | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
this.lastActivity = DateTime.Now; | |||||
lastActivity = DateTime.Now; | |||||
int bytesToSend; | int bytesToSend; | ||||
lock (decryptionLock) | lock (decryptionLock) | ||||
{ | { | ||||
@@ -535,33 +547,33 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); | encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); | ||||
} | } | ||||
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)"); | |||||
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); | connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
if (strategy != null) | if (strategy != null) | ||||
{ | { | ||||
strategy.UpdateLastRead(this.server); | |||||
strategy.UpdateLastRead(server); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
//Console.WriteLine("bytesRead: " + bytesRead.ToString()); | |||||
connection.Shutdown(SocketShutdown.Send); | connection.Shutdown(SocketShutdown.Send); | ||||
connectionShutdown = true; | connectionShutdown = true; | ||||
CheckClose(); | CheckClose(); | ||||
if (totalRead == 0) | |||||
{ | |||||
// closed before anything received, reports as failure | |||||
// disable this feature | |||||
// controller.GetCurrentStrategy().SetFailure(this.server); | |||||
} | |||||
//if (totalRead == 0) | |||||
//{ | |||||
// // closed before anything received, reports as failure | |||||
// // disable this feature | |||||
// controller.GetCurrentStrategy().SetFailure(this.server); | |||||
//} | |||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -587,12 +599,14 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); | encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); | ||||
} | } | ||||
Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); | |||||
tcprelay.UpdateOutboundCounter(bytesToSend); | |||||
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) | ||||
{ | { | ||||
strategy.UpdateLastWrite(this.server); | |||||
strategy.UpdateLastWrite(server); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
@@ -605,7 +619,7 @@ namespace Shadowsocks.Controller | |||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -618,13 +632,12 @@ namespace Shadowsocks.Controller | |||||
try | try | ||||
{ | { | ||||
remote.EndSend(ar); | remote.EndSend(ar); | ||||
connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
@@ -637,13 +650,12 @@ namespace Shadowsocks.Controller | |||||
try | try | ||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, | |||||
new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,12 +1,12 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Text; | |||||
using Shadowsocks.Encryption; | |||||
using Shadowsocks.Model; | |||||
using System.Net.Sockets; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | |||||
using System.Runtime.CompilerServices; | using System.Runtime.CompilerServices; | ||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
using Shadowsocks.Encryption; | |||||
using Shadowsocks.Model; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -14,6 +14,10 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
private ShadowsocksController _controller; | private ShadowsocksController _controller; | ||||
private LRUCache<IPEndPoint, UDPHandler> _cache; | private LRUCache<IPEndPoint, UDPHandler> _cache; | ||||
public long outbound = 0; | |||||
public long inbound = 0; | |||||
public UDPRelay(ShadowsocksController controller) | public UDPRelay(ShadowsocksController controller) | ||||
{ | { | ||||
this._controller = controller; | this._controller = controller; | ||||
@@ -70,23 +74,27 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
_remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); | _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); | ||||
_remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); | _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); | ||||
} | } | ||||
public void Send(byte[] data, int length) | public void Send(byte[] data, int length) | ||||
{ | { | ||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||||
byte[] dataIn = new byte[length - 3]; | |||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true); | |||||
byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES]; | |||||
Array.Copy(data, 3, dataIn, 0, length - 3); | Array.Copy(data, 3, dataIn, 0, length - 3); | ||||
byte[] dataOut = new byte[length - 3 + 16]; | |||||
byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; | |||||
int outlen; | int outlen; | ||||
encryptor.Encrypt(dataIn, dataIn.Length, dataOut, out outlen); | |||||
_remote.SendTo(dataOut, _remoteEndPoint); | |||||
encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen); | |||||
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); | |||||
_remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); | |||||
} | } | ||||
public void Receive() | public void Receive() | ||||
{ | { | ||||
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); | EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); | ||||
Logging.Debug($"++++++Receive Server Port, size:" + _buffer.Length); | |||||
_remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); | _remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); | ||||
} | } | ||||
public void RecvFromCallback(IAsyncResult ar) | public void RecvFromCallback(IAsyncResult ar) | ||||
{ | { | ||||
try | try | ||||
@@ -97,12 +105,13 @@ namespace Shadowsocks.Controller | |||||
byte[] dataOut = new byte[bytesRead]; | byte[] dataOut = new byte[bytesRead]; | ||||
int outlen; | int outlen; | ||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password); | |||||
IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true); | |||||
encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); | encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); | ||||
byte[] sendBuf = new byte[outlen + 3]; | byte[] sendBuf = new byte[outlen + 3]; | ||||
Array.Copy(dataOut, 0, sendBuf, 3, outlen); | Array.Copy(dataOut, 0, sendBuf, 3, outlen); | ||||
Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); | |||||
_local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); | _local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); | ||||
Receive(); | Receive(); | ||||
} | } | ||||
@@ -118,6 +127,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
} | } | ||||
} | } | ||||
public void Close() | public void Close() | ||||
{ | { | ||||
try | try | ||||
@@ -139,7 +149,6 @@ 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 | ||||
{ | { | ||||
@@ -1,92 +1,73 @@ | |||||
using Shadowsocks.Model; | |||||
using System; | |||||
using System.Collections; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Net; | using System.Net; | ||||
using System.Reflection; | |||||
using System.Text; | |||||
using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||
using SimpleJson; | |||||
using Newtonsoft.Json.Linq; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
public class UpdateChecker | public class UpdateChecker | ||||
{ | { | ||||
private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases"; | private const string UpdateURL = "https://api.github.com/repos/shadowsocks/shadowsocks-windows/releases"; | ||||
private const string UserAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36"; | |||||
private Configuration config; | |||||
public bool NewVersionFound; | |||||
public string LatestVersionNumber; | public string LatestVersionNumber; | ||||
public string LatestVersionName; | |||||
public string LatestVersionURL; | public string LatestVersionURL; | ||||
public event EventHandler NewVersionFound; | |||||
public string LatestVersionLocalName; | |||||
public event EventHandler CheckUpdateCompleted; | |||||
public const string Version = "2.5.6"; | |||||
public const string Version = "2.5.8.2"; | |||||
public void CheckUpdate(Configuration config) | |||||
private class CheckUpdateTimer : System.Timers.Timer | |||||
{ | { | ||||
// TODO test failures | |||||
WebClient http = new WebClient(); | |||||
http.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.3319.102 Safari/537.36"); | |||||
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); | |||||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||||
http.DownloadStringAsync(new Uri(UpdateURL)); | |||||
} | |||||
public Configuration config; | |||||
public static int CompareVersion(string l, string r) | |||||
{ | |||||
var ls = l.Split('.'); | |||||
var rs = r.Split('.'); | |||||
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++) | |||||
public CheckUpdateTimer(int p) : base(p) | |||||
{ | { | ||||
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0; | |||||
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0; | |||||
if (lp != rp) | |||||
{ | |||||
return lp - rp; | |||||
} | |||||
} | } | ||||
return 0; | |||||
} | } | ||||
public class VersionComparer : IComparer<string> | |||||
public void CheckUpdate(Configuration config, int delay) | |||||
{ | { | ||||
// Calls CaseInsensitiveComparer.Compare with the parameters reversed. | |||||
public int Compare(string x, string y) | |||||
{ | |||||
return CompareVersion(ParseVersionFromURL(x), ParseVersionFromURL(y)); | |||||
} | |||||
CheckUpdateTimer timer = new CheckUpdateTimer(delay); | |||||
timer.AutoReset = false; | |||||
timer.Elapsed += Timer_Elapsed; | |||||
timer.config = config; | |||||
timer.Enabled = true; | |||||
} | } | ||||
private static string ParseVersionFromURL(string url) | |||||
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) | |||||
{ | { | ||||
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase); | |||||
if (match.Success) | |||||
{ | |||||
if (match.Groups.Count == 2) | |||||
{ | |||||
return match.Groups[1].Value; | |||||
} | |||||
} | |||||
return null; | |||||
CheckUpdateTimer timer = (CheckUpdateTimer)sender; | |||||
Configuration config = timer.config; | |||||
timer.Elapsed -= Timer_Elapsed; | |||||
timer.Enabled = false; | |||||
timer.Dispose(); | |||||
CheckUpdate(config); | |||||
} | } | ||||
private void SortVersions(List<string> versions) | |||||
public void CheckUpdate(Configuration config) | |||||
{ | { | ||||
versions.Sort(new VersionComparer()); | |||||
} | |||||
this.config = config; | |||||
private bool IsNewVersion(string url) | |||||
{ | |||||
if (url.IndexOf("prerelease") >= 0) | |||||
try | |||||
{ | { | ||||
return false; | |||||
Logging.Debug("Checking updates..."); | |||||
WebClient http = CreateWebClient(); | |||||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||||
http.DownloadStringAsync(new Uri(UpdateURL)); | |||||
} | } | ||||
string version = ParseVersionFromURL(url); | |||||
if (version == null) | |||||
catch (Exception ex) | |||||
{ | { | ||||
return false; | |||||
Logging.LogUsefulException(ex); | |||||
} | } | ||||
string currentVersion = Version; | |||||
return CompareVersion(version, currentVersion) > 0; | |||||
} | } | ||||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | ||||
@@ -95,42 +76,167 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
string response = e.Result; | string response = e.Result; | ||||
JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result); | |||||
JArray result = JArray.Parse(response); | |||||
List<string> versions = new List<string>(); | |||||
foreach (JsonObject release in result) | |||||
List<Asset> asserts = new List<Asset>(); | |||||
if (result != null) | |||||
{ | { | ||||
if ((bool)release["prerelease"]) | |||||
{ | |||||
continue; | |||||
} | |||||
foreach (JsonObject asset in (JsonArray)release["assets"]) | |||||
foreach (JObject release in result) | |||||
{ | { | ||||
string url = (string)asset["browser_download_url"]; | |||||
if (IsNewVersion(url)) | |||||
if ((bool)release["prerelease"]) | |||||
{ | |||||
continue; | |||||
} | |||||
foreach (JObject asset in (JArray)release["assets"]) | |||||
{ | { | ||||
versions.Add(url); | |||||
Asset ass = new Asset(); | |||||
ass.Parse(asset); | |||||
if (ass.IsNewVersion(Version)) | |||||
{ | |||||
asserts.Add(ass); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } | ||||
if (asserts.Count != 0) | |||||
{ | |||||
SortByVersions(asserts); | |||||
Asset asset = asserts[asserts.Count - 1]; | |||||
NewVersionFound = true; | |||||
LatestVersionURL = asset.browser_download_url; | |||||
LatestVersionNumber = asset.version; | |||||
LatestVersionName = asset.name; | |||||
if (versions.Count == 0) | |||||
startDownload(); | |||||
} | |||||
else | |||||
{ | { | ||||
Logging.Debug("No update is available"); | |||||
if (CheckUpdateCompleted != null) | |||||
{ | |||||
CheckUpdateCompleted(this, new EventArgs()); | |||||
} | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Logging.LogUsefulException(ex); | |||||
} | |||||
} | |||||
private void startDownload() | |||||
{ | |||||
try | |||||
{ | |||||
LatestVersionLocalName = Utils.GetTempPath(LatestVersionName); | |||||
WebClient http = CreateWebClient(); | |||||
http.DownloadFileCompleted += Http_DownloadFileCompleted; | |||||
http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Logging.LogUsefulException(ex); | |||||
} | |||||
} | |||||
private void Http_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) | |||||
{ | |||||
try | |||||
{ | |||||
if (e.Error != null) | |||||
{ | |||||
Logging.LogUsefulException(e.Error); | |||||
return; | return; | ||||
} | } | ||||
// sort versions | |||||
SortVersions(versions); | |||||
LatestVersionURL = versions[versions.Count - 1]; | |||||
LatestVersionNumber = ParseVersionFromURL(LatestVersionURL); | |||||
if (NewVersionFound != null) | |||||
Logging.Debug($"New version {LatestVersionNumber} found: {LatestVersionLocalName}"); | |||||
if (CheckUpdateCompleted != null) | |||||
{ | { | ||||
NewVersionFound(this, new EventArgs()); | |||||
CheckUpdateCompleted(this, new EventArgs()); | |||||
} | } | ||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
{ | { | ||||
Logging.Debug(ex.ToString()); | |||||
return; | |||||
Logging.LogUsefulException(ex); | |||||
} | |||||
} | |||||
private WebClient CreateWebClient() | |||||
{ | |||||
WebClient http = new WebClient(); | |||||
http.Headers.Add("User-Agent", UserAgent); | |||||
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); | |||||
return http; | |||||
} | |||||
private void SortByVersions(List<Asset> asserts) | |||||
{ | |||||
asserts.Sort(new VersionComparer()); | |||||
} | |||||
public class Asset | |||||
{ | |||||
public bool prerelease; | |||||
public string name; | |||||
public string version; | |||||
public string browser_download_url; | |||||
public bool IsNewVersion(string currentVersion) | |||||
{ | |||||
if (prerelease) | |||||
{ | |||||
return false; | |||||
} | |||||
if (version == null) | |||||
{ | |||||
return false; | |||||
} | |||||
return CompareVersion(version, currentVersion) > 0; | |||||
} | |||||
public void Parse(JObject asset) | |||||
{ | |||||
name = (string)asset["name"]; | |||||
browser_download_url = (string)asset["browser_download_url"]; | |||||
version = ParseVersionFromURL(browser_download_url); | |||||
prerelease = browser_download_url.IndexOf("prerelease") >= 0; | |||||
} | |||||
private static string ParseVersionFromURL(string url) | |||||
{ | |||||
Match match = Regex.Match(url, @".*Shadowsocks-win.*?-([\d\.]+)\.\w+", RegexOptions.IgnoreCase); | |||||
if (match.Success) | |||||
{ | |||||
if (match.Groups.Count == 2) | |||||
{ | |||||
return match.Groups[1].Value; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public static int CompareVersion(string l, string r) | |||||
{ | |||||
var ls = l.Split('.'); | |||||
var rs = r.Split('.'); | |||||
for (int i = 0; i < Math.Max(ls.Length, rs.Length); i++) | |||||
{ | |||||
int lp = (i < ls.Length) ? int.Parse(ls[i]) : 0; | |||||
int rp = (i < rs.Length) ? int.Parse(rs[i]) : 0; | |||||
if (lp != rp) | |||||
{ | |||||
return lp - rp; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
} | |||||
class VersionComparer : IComparer<Asset> | |||||
{ | |||||
// Calls CaseInsensitiveComparer.Compare with the parameters reversed. | |||||
public int Compare(Asset x, Asset y) | |||||
{ | |||||
return Asset.CompareVersion(x.version, y.version); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,12 +1,17 @@ | |||||
using System.IO; | |||||
using Shadowsocks.Model; | |||||
using System; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | |||||
using System.Net; | |||||
using System.Net.Sockets; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Net.Sockets; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
using System.Net; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
@@ -25,7 +30,12 @@ namespace Shadowsocks.Controller | |||||
private StrategyManager _strategyManager; | private StrategyManager _strategyManager; | ||||
private PolipoRunner polipoRunner; | private PolipoRunner polipoRunner; | ||||
private GFWListUpdater gfwListUpdater; | private GFWListUpdater gfwListUpdater; | ||||
private AvailabilityStatistics _availabilityStatics; | |||||
public AvailabilityStatistics availabilityStatistics { get; private set; } | |||||
public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } | |||||
public long inboundCounter = 0; | |||||
public long outboundCounter = 0; | |||||
private bool stopped = false; | private bool stopped = false; | ||||
private bool _systemProxyIsDirty = false; | private bool _systemProxyIsDirty = false; | ||||
@@ -53,6 +63,7 @@ namespace Shadowsocks.Controller | |||||
public ShadowsocksController() | public ShadowsocksController() | ||||
{ | { | ||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | |||||
_strategyManager = new StrategyManager(this); | _strategyManager = new StrategyManager(this); | ||||
StartReleasingMemory(); | StartReleasingMemory(); | ||||
} | } | ||||
@@ -122,7 +133,13 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_config.configs = servers; | _config.configs = servers; | ||||
_config.localPort = localPort; | _config.localPort = localPort; | ||||
SaveConfig(_config); | |||||
Configuration.Save(_config); | |||||
} | |||||
public void SaveStrategyConfigurations(StatisticsStrategyConfiguration configuration) | |||||
{ | |||||
StatisticsConfiguration = configuration; | |||||
StatisticsStrategyConfiguration.Save(configuration); | |||||
} | } | ||||
public bool AddServerBySSURL(string ssURL) | public bool AddServerBySSURL(string ssURL) | ||||
@@ -236,7 +253,7 @@ namespace Shadowsocks.Controller | |||||
public static string GetQRCode(Server server) | public static string GetQRCode(Server server) | ||||
{ | { | ||||
string parts = server.method + ":" + server.password + "@" + server.server + ":" + server.server_port; | string parts = server.method + ":" + server.password + "@" + server.server + ":" + server.server_port; | ||||
string base64 = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(parts)); | |||||
string base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(parts)); | |||||
return "ss://" + base64; | return "ss://" + base64; | ||||
} | } | ||||
@@ -248,14 +265,12 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
public void ToggleAvailabilityStatistics(bool enabled) | |||||
public void UpdateStatisticsConfiguration(bool enabled) | |||||
{ | { | ||||
if (_availabilityStatics != null) | |||||
{ | |||||
_availabilityStatics.Set(enabled); | |||||
_config.availabilityStatistics = enabled; | |||||
SaveConfig(_config); | |||||
} | |||||
if (availabilityStatistics == null) return; | |||||
availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration); | |||||
_config.availabilityStatistics = enabled; | |||||
SaveConfig(_config); | |||||
} | } | ||||
public void SavePACUrl(string pacUrl) | public void SavePACUrl(string pacUrl) | ||||
@@ -280,10 +295,33 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
public void ToggleCheckingUpdate(bool enabled) | |||||
{ | |||||
_config.autoCheckUpdate = enabled; | |||||
Configuration.Save(_config); | |||||
} | |||||
public void SaveLogViewerConfig(LogViewerConfig newConfig) | |||||
{ | |||||
_config.logViewer = newConfig; | |||||
Configuration.Save(_config); | |||||
} | |||||
public void UpdateInboundCounter(long n) | |||||
{ | |||||
Interlocked.Add(ref inboundCounter, n); | |||||
} | |||||
public void UpdateOutboundCounter(long n) | |||||
{ | |||||
Interlocked.Add(ref outboundCounter, n); | |||||
} | |||||
protected void Reload() | protected void Reload() | ||||
{ | { | ||||
// some logic in configuration updated the config when saving, we need to read it again | // some logic in configuration updated the config when saving, we need to read it again | ||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | |||||
if (polipoRunner == null) | if (polipoRunner == null) | ||||
{ | { | ||||
@@ -293,6 +331,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_pacServer = new PACServer(); | _pacServer = new PACServer(); | ||||
_pacServer.PACFileChanged += pacServer_PACFileChanged; | _pacServer.PACFileChanged += pacServer_PACFileChanged; | ||||
_pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged; | |||||
} | } | ||||
_pacServer.UpdateConfiguration(_config); | _pacServer.UpdateConfiguration(_config); | ||||
if (gfwListUpdater == null) | if (gfwListUpdater == null) | ||||
@@ -302,17 +341,16 @@ namespace Shadowsocks.Controller | |||||
gfwListUpdater.Error += pacServer_PACUpdateError; | gfwListUpdater.Error += pacServer_PACUpdateError; | ||||
} | } | ||||
if (_listener != null) | |||||
if (availabilityStatistics == null) | |||||
{ | { | ||||
_listener.Stop(); | |||||
availabilityStatistics = new AvailabilityStatistics(_config, StatisticsConfiguration); | |||||
} | } | ||||
availabilityStatistics.UpdateConfiguration(_config, StatisticsConfiguration); | |||||
if (_availabilityStatics == null) | |||||
if (_listener != null) | |||||
{ | { | ||||
_availabilityStatics = new AvailabilityStatistics(); | |||||
_availabilityStatics.UpdateConfiguration(_config); | |||||
_listener.Stop(); | |||||
} | } | ||||
// don't put polipoRunner.Start() before pacServer.Stop() | // don't put polipoRunner.Start() before pacServer.Stop() | ||||
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 | // or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 | ||||
// though UseShellExecute is set to true now | // though UseShellExecute is set to true now | ||||
@@ -360,7 +398,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
Util.Utils.ReleaseMemory(true); | |||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
protected void SaveConfig(Configuration newConfig) | protected void SaveConfig(Configuration newConfig) | ||||
@@ -404,6 +442,47 @@ namespace Shadowsocks.Controller | |||||
UpdatePACFromGFWListError(this, e); | UpdatePACFromGFWListError(this, e); | ||||
} | } | ||||
private void pacServer_UserRuleFileChanged(object sender, EventArgs e) | |||||
{ | |||||
// TODO: this is a dirty hack. (from code GListUpdater.http_DownloadStringCompleted()) | |||||
if (!File.Exists(Utils.GetTempPath("gfwlist.txt"))) | |||||
{ | |||||
UpdatePACFromGFWList(); | |||||
return; | |||||
} | |||||
List<string> lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt"))); | |||||
if (File.Exists(PACServer.USER_RULE_FILE)) | |||||
{ | |||||
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); | |||||
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |||||
foreach (string rule in rules) | |||||
{ | |||||
if (rule.StartsWith("!") || rule.StartsWith("[")) | |||||
continue; | |||||
lines.Add(rule); | |||||
} | |||||
} | |||||
string abpContent; | |||||
if (File.Exists(PACServer.USER_ABP_FILE)) | |||||
{ | |||||
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); | |||||
} | |||||
else | |||||
{ | |||||
abpContent = Utils.UnGzip(Resources.abp_js); | |||||
} | |||||
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); | |||||
if (File.Exists(PACServer.PAC_FILE)) | |||||
{ | |||||
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); | |||||
if (original == abpContent) | |||||
{ | |||||
return; | |||||
} | |||||
} | |||||
File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); | |||||
} | |||||
private void StartReleasingMemory() | private void StartReleasingMemory() | ||||
{ | { | ||||
_ramThread = new Thread(new ThreadStart(ReleaseMemory)); | _ramThread = new Thread(new ThreadStart(ReleaseMemory)); | ||||
@@ -415,7 +494,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
while (true) | while (true) | ||||
{ | { | ||||
Util.Utils.ReleaseMemory(false); | |||||
Utils.ReleaseMemory(false); | |||||
Thread.Sleep(30 * 1000); | Thread.Sleep(30 * 1000); | ||||
} | } | ||||
} | } | ||||
@@ -132,14 +132,14 @@ namespace Shadowsocks.Controller.Strategy | |||||
if (_currentServer == null || max.score - _currentServer.score > 200) | if (_currentServer == null || max.score - _currentServer.score > 200) | ||||
{ | { | ||||
_currentServer = max; | _currentServer = max; | ||||
Console.WriteLine("HA switching to server: {0}", _currentServer.server.FriendlyName()); | |||||
Logging.Info($"HA switching to server: {_currentServer.server.FriendlyName()}"); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
public void UpdateLatency(Model.Server server, TimeSpan latency) | public void UpdateLatency(Model.Server server, TimeSpan latency) | ||||
{ | { | ||||
Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency)); | |||||
Logging.Debug($"latency: {server.FriendlyName()} {latency}"); | |||||
ServerStatus status; | ServerStatus status; | ||||
if (_serverStatus.TryGetValue(server, out status)) | if (_serverStatus.TryGetValue(server, out status)) | ||||
@@ -151,7 +151,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
public void UpdateLastRead(Model.Server server) | public void UpdateLastRead(Model.Server server) | ||||
{ | { | ||||
Logging.Debug(String.Format("last read: {0}", server.FriendlyName())); | |||||
Logging.Debug($"last read: {server.FriendlyName()}"); | |||||
ServerStatus status; | ServerStatus status; | ||||
if (_serverStatus.TryGetValue(server, out status)) | if (_serverStatus.TryGetValue(server, out status)) | ||||
@@ -162,7 +162,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
public void UpdateLastWrite(Model.Server server) | public void UpdateLastWrite(Model.Server server) | ||||
{ | { | ||||
Logging.Debug(String.Format("last write: {0}", server.FriendlyName())); | |||||
Logging.Debug($"last write: {server.FriendlyName()}"); | |||||
ServerStatus status; | ServerStatus status; | ||||
if (_serverStatus.TryGetValue(server, out status)) | if (_serverStatus.TryGetValue(server, out status)) | ||||
@@ -173,7 +173,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
public void SetFailure(Model.Server server) | public void SetFailure(Model.Server server) | ||||
{ | { | ||||
Logging.Debug(String.Format("failure: {0}", server.FriendlyName())); | |||||
Logging.Debug($"failure: {server.FriendlyName()}"); | |||||
ServerStatus status; | ServerStatus status; | ||||
if (_serverStatus.TryGetValue(server, out status)) | if (_serverStatus.TryGetValue(server, out status)) | ||||
@@ -1,176 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Text; | |||||
using Shadowsocks.Model; | |||||
using System.IO; | |||||
using System.Net.NetworkInformation; | |||||
using System.Threading; | |||||
namespace Shadowsocks.Controller.Strategy | |||||
{ | |||||
class SimplyChooseByStatisticsStrategy : IStrategy | |||||
{ | |||||
private ShadowsocksController _controller; | |||||
private Server _currentServer; | |||||
private Timer timer; | |||||
private Dictionary<string, StatisticsData> statistics; | |||||
private static readonly int CachedInterval = 30 * 60 * 1000; //choose a new server every 30 minutes | |||||
public SimplyChooseByStatisticsStrategy(ShadowsocksController controller) | |||||
{ | |||||
_controller = controller; | |||||
var servers = controller.GetCurrentConfiguration().configs; | |||||
int randomIndex = new Random().Next() % servers.Count(); | |||||
_currentServer = servers[randomIndex]; //choose a server randomly at first | |||||
timer = new Timer(ReloadStatisticsAndChooseAServer); | |||||
} | |||||
private void ReloadStatisticsAndChooseAServer(object obj) | |||||
{ | |||||
Logging.Debug("Reloading statistics and choose a new server...."); | |||||
List<Server> servers = _controller.GetCurrentConfiguration().configs; | |||||
LoadStatistics(); | |||||
ChooseNewServer(servers); | |||||
} | |||||
/* | |||||
return a dict: | |||||
{ | |||||
'ServerFriendlyName1':StatisticsData, | |||||
'ServerFriendlyName2':... | |||||
} | |||||
*/ | |||||
private void LoadStatistics() | |||||
{ | |||||
try | |||||
{ | |||||
var path = AvailabilityStatistics.AvailabilityStatisticsFile; | |||||
Logging.Debug(string.Format("loading statistics from{0}", path)); | |||||
statistics = (from l in File.ReadAllLines(path) | |||||
.Skip(1) | |||||
let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) | |||||
let rawData = new | |||||
{ | |||||
ServerName = strings[1], | |||||
IPStatus = strings[2], | |||||
RoundtripTime = int.Parse(strings[3]) | |||||
} | |||||
group rawData by rawData.ServerName into server | |||||
select new | |||||
{ | |||||
ServerName = server.Key, | |||||
data = new StatisticsData | |||||
{ | |||||
SuccessTimes = server.Count(data => IPStatus.Success.ToString().Equals(data.IPStatus)), | |||||
TimedOutTimes = server.Count(data => IPStatus.TimedOut.ToString().Equals(data.IPStatus)), | |||||
AverageResponse = Convert.ToInt32(server.Average(data => data.RoundtripTime)), | |||||
MinResponse = server.Min(data => data.RoundtripTime), | |||||
MaxResponse = server.Max(data => data.RoundtripTime) | |||||
} | |||||
}).ToDictionary(server => server.ServerName, server => server.data); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
//return the score by data | |||||
//server with highest score will be choosen | |||||
private static double GetScore(StatisticsData data) | |||||
{ | |||||
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) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
var bestResult = (from server in servers | |||||
let name = server.FriendlyName() | |||||
where statistics.ContainsKey(name) | |||||
select new | |||||
{ | |||||
server, | |||||
score = GetScore(statistics[name]) | |||||
} | |||||
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); | |||||
if (_controller.GetCurrentStrategy().ID == ID && _currentServer != bestResult.server) //output when enabled | |||||
{ | |||||
Console.WriteLine("Switch to server: {0} by package loss:{1}", bestResult.server.FriendlyName(), 1 - bestResult.score); | |||||
} | |||||
_currentServer = bestResult.server; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
public string ID | |||||
{ | |||||
get { return "com.shadowsocks.strategy.scbs"; } | |||||
} | |||||
public string Name | |||||
{ | |||||
get { return I18N.GetString("Choose By Total Package Loss"); } | |||||
} | |||||
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) | |||||
{ | |||||
var oldServer = _currentServer; | |||||
if (oldServer == null) | |||||
{ | |||||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | |||||
} | |||||
if (oldServer != _currentServer) | |||||
{ | |||||
} | |||||
return _currentServer; //current server cached for CachedInterval | |||||
} | |||||
public void ReloadServers() | |||||
{ | |||||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | |||||
timer?.Change(0, CachedInterval); | |||||
} | |||||
public void SetFailure(Server server) | |||||
{ | |||||
Logging.Debug(String.Format("failure: {0}", server.FriendlyName())); | |||||
} | |||||
public void UpdateLastRead(Server server) | |||||
{ | |||||
//TODO: combine this part of data with ICMP statics | |||||
} | |||||
public void UpdateLastWrite(Server server) | |||||
{ | |||||
//TODO: combine this part of data with ICMP statics | |||||
} | |||||
public void UpdateLatency(Server server, TimeSpan latency) | |||||
{ | |||||
//TODO: combine this part of data with ICMP statics | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,153 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Net.NetworkInformation; | |||||
using System.Threading; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Model; | |||||
namespace Shadowsocks.Controller.Strategy | |||||
{ | |||||
class StatisticsStrategy : IStrategy | |||||
{ | |||||
private readonly ShadowsocksController _controller; | |||||
private Server _currentServer; | |||||
private readonly Timer _timer; | |||||
private Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>> _filteredStatistics; | |||||
private int ChoiceKeptMilliseconds | |||||
=> (int) TimeSpan.FromMinutes(_controller.StatisticsConfiguration.ChoiceKeptMinutes).TotalMilliseconds; | |||||
public StatisticsStrategy(ShadowsocksController controller) | |||||
{ | |||||
_controller = controller; | |||||
var servers = controller.GetCurrentConfiguration().configs; | |||||
var randomIndex = new Random().Next() % servers.Count(); | |||||
_currentServer = servers[randomIndex]; //choose a server randomly at first | |||||
_timer = new Timer(ReloadStatisticsAndChooseAServer); | |||||
} | |||||
private void ReloadStatisticsAndChooseAServer(object obj) | |||||
{ | |||||
Logging.Debug("Reloading statistics and choose a new server...."); | |||||
var servers = _controller.GetCurrentConfiguration().configs; | |||||
LoadStatistics(); | |||||
ChooseNewServer(servers); | |||||
} | |||||
private void LoadStatistics() | |||||
{ | |||||
_filteredStatistics = _controller.availabilityStatistics.RawStatistics ?? _filteredStatistics ?? new Dictionary<string, List<AvailabilityStatistics.RawStatisticsData>>(); | |||||
} | |||||
//return the score by data | |||||
//server with highest score will be choosen | |||||
private float GetScore(string serverName) | |||||
{ | |||||
var config = _controller.StatisticsConfiguration; | |||||
List<AvailabilityStatistics.RawStatisticsData> dataList; | |||||
if (_filteredStatistics == null || !_filteredStatistics.TryGetValue(serverName, out dataList)) return 0; | |||||
var successTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.Success.ToString())); | |||||
var timedOutTimes = (float) dataList.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString())); | |||||
var statisticsData = new AvailabilityStatistics.StatisticsData | |||||
{ | |||||
PackageLoss = timedOutTimes/(successTimes + timedOutTimes)*100, | |||||
AverageResponse = Convert.ToInt32(dataList.Average(data => data.RoundtripTime)), | |||||
MinResponse = dataList.Min(data => data.RoundtripTime), | |||||
MaxResponse = dataList.Max(data => data.RoundtripTime) | |||||
}; | |||||
float factor; | |||||
float score = 0; | |||||
if (!config.Calculations.TryGetValue("PackageLoss", out factor)) factor = 0; | |||||
score += statisticsData.PackageLoss*factor; | |||||
if (!config.Calculations.TryGetValue("AverageResponse", out factor)) factor = 0; | |||||
score += statisticsData.AverageResponse*factor; | |||||
if (!config.Calculations.TryGetValue("MinResponse", out factor)) factor = 0; | |||||
score += statisticsData.MinResponse*factor; | |||||
if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; | |||||
score += statisticsData.MaxResponse*factor; | |||||
Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}"); | |||||
return score; | |||||
} | |||||
private void ChooseNewServer(List<Server> servers) | |||||
{ | |||||
if (_filteredStatistics == null || servers.Count == 0) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
var bestResult = (from server in servers | |||||
let name = server.FriendlyName() | |||||
where _filteredStatistics.ContainsKey(name) | |||||
select new | |||||
{ | |||||
server, | |||||
score = GetScore(name) | |||||
} | |||||
).Aggregate((result1, result2) => result1.score > result2.score ? result1 : result2); | |||||
LogWhenEnabled($"Switch to server: {bestResult.server.FriendlyName()} by statistics: score {bestResult.score}"); | |||||
_currentServer = bestResult.server; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
private void LogWhenEnabled(string log) | |||||
{ | |||||
if (_controller.GetCurrentStrategy()?.ID == ID) //output when enabled | |||||
{ | |||||
Console.WriteLine(log); | |||||
} | |||||
} | |||||
public string ID => "com.shadowsocks.strategy.scbs"; | |||||
public string Name => I18N.GetString("Choose By Total Package Loss"); | |||||
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) | |||||
{ | |||||
var oldServer = _currentServer; | |||||
if (oldServer == null) | |||||
{ | |||||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | |||||
} | |||||
if (oldServer != _currentServer) | |||||
{ | |||||
} | |||||
return _currentServer; //current server cached for CachedInterval | |||||
} | |||||
public void ReloadServers() | |||||
{ | |||||
ChooseNewServer(_controller.GetCurrentConfiguration().configs); | |||||
_timer?.Change(0, ChoiceKeptMilliseconds); | |||||
} | |||||
public void SetFailure(Server server) | |||||
{ | |||||
Logging.Debug($"failure: {server.FriendlyName()}"); | |||||
} | |||||
public void UpdateLastRead(Server server) | |||||
{ | |||||
//TODO: combine this part of data with ICMP statics | |||||
} | |||||
public void UpdateLastWrite(Server server) | |||||
{ | |||||
//TODO: combine this part of data with ICMP statics | |||||
} | |||||
public void UpdateLatency(Server server, TimeSpan latency) | |||||
{ | |||||
//TODO: combine this part of data with ICMP statics | |||||
} | |||||
} | |||||
} |
@@ -13,7 +13,7 @@ namespace Shadowsocks.Controller.Strategy | |||||
_strategies = new List<IStrategy>(); | _strategies = new List<IStrategy>(); | ||||
_strategies.Add(new BalancingStrategy(controller)); | _strategies.Add(new BalancingStrategy(controller)); | ||||
_strategies.Add(new HighAvailabilityStrategy(controller)); | _strategies.Add(new HighAvailabilityStrategy(controller)); | ||||
_strategies.Add(new SimplyChooseByStatisticsStrategy(controller)); | |||||
_strategies.Add(new StatisticsStrategy(controller)); | |||||
// TODO: load DLL plugins | // TODO: load DLL plugins | ||||
} | } | ||||
public IList<IStrategy> GetStrategies() | public IList<IStrategy> GetStrategies() | ||||
@@ -6,21 +6,23 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
class AutoStartup | class AutoStartup | ||||
{ | { | ||||
static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode(); | |||||
public static bool Set(bool enabled) | public static bool Set(bool enabled) | ||||
{ | { | ||||
RegistryKey runKey = null; | |||||
try | try | ||||
{ | { | ||||
string path = Application.ExecutablePath; | string path = Application.ExecutablePath; | ||||
RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); | |||||
runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); | |||||
if (enabled) | if (enabled) | ||||
{ | { | ||||
runKey.SetValue("Shadowsocks", path); | |||||
runKey.SetValue(Key, path); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
runKey.DeleteValue("Shadowsocks"); | |||||
runKey.DeleteValue(Key); | |||||
} | } | ||||
runKey.Close(); | |||||
return true; | return true; | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -28,20 +30,39 @@ namespace Shadowsocks.Controller | |||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
return false; | return false; | ||||
} | } | ||||
finally | |||||
{ | |||||
if (runKey != null) | |||||
{ | |||||
try { runKey.Close(); } | |||||
catch (Exception e) | |||||
{ Logging.LogUsefulException(e); } | |||||
} | |||||
} | |||||
} | } | ||||
public static bool Check() | public static bool Check() | ||||
{ | { | ||||
RegistryKey runKey = null; | |||||
try | try | ||||
{ | { | ||||
string path = Application.ExecutablePath; | string path = Application.ExecutablePath; | ||||
RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run"); | |||||
runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); | |||||
string[] runList = runKey.GetValueNames(); | string[] runList = runKey.GetValueNames(); | ||||
runKey.Close(); | |||||
foreach (string item in runList) | foreach (string item in runList) | ||||
{ | { | ||||
if (item.Equals("Shadowsocks")) | |||||
if (item.Equals(Key)) | |||||
return true; | return true; | ||||
else if (item.Equals("Shadowsocks")) // Compatibility with older versions | |||||
{ | |||||
string value = Convert.ToString(runKey.GetValue(item)); | |||||
if (path.Equals(value, StringComparison.InvariantCultureIgnoreCase)) | |||||
{ | |||||
runKey.DeleteValue(item); | |||||
runKey.SetValue(Key, path); | |||||
return true; | |||||
} | |||||
} | |||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
@@ -50,6 +71,15 @@ namespace Shadowsocks.Controller | |||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
return false; | return false; | ||||
} | } | ||||
finally | |||||
{ | |||||
if (runKey != null) | |||||
{ | |||||
try { runKey.Close(); } | |||||
catch(Exception e) | |||||
{ Logging.LogUsefulException(e); } | |||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -10,6 +10,7 @@ PAC=PAC 模式 | |||||
Global=全局模式 | Global=全局模式 | ||||
Servers=服务器 | Servers=服务器 | ||||
Edit Servers...=编辑服务器... | Edit Servers...=编辑服务器... | ||||
Statistics Config...=统计配置... | |||||
Start on Boot=开机启动 | Start on Boot=开机启动 | ||||
Allow Clients from LAN=允许来自局域网的连接 | Allow Clients from LAN=允许来自局域网的连接 | ||||
Local PAC=使用本地 PAC | Local PAC=使用本地 PAC | ||||
@@ -21,6 +22,9 @@ Show QRCode...=显示二维码... | |||||
Scan QRCode from Screen...=扫描屏幕上的二维码... | Scan QRCode from Screen...=扫描屏幕上的二维码... | ||||
Availability Statistics=统计可用性 | Availability Statistics=统计可用性 | ||||
Show Logs...=显示日志... | Show Logs...=显示日志... | ||||
Updates...=更新... | |||||
Check for Updates...=检查更新 | |||||
Check for Updates at Startup=启动时检查更新 | |||||
About...=关于... | About...=关于... | ||||
Quit=退出 | Quit=退出 | ||||
Edit Servers=编辑服务器 | Edit Servers=编辑服务器 | ||||
@@ -39,6 +43,7 @@ Password=密码 | |||||
Encryption=加密 | Encryption=加密 | ||||
Proxy Port=代理端口 | Proxy Port=代理端口 | ||||
Remarks=备注 | Remarks=备注 | ||||
Onetime Authentication (Experimental)=一次性认证(实验性) | |||||
OK=确定 | OK=确定 | ||||
Cancel=取消 | Cancel=取消 | ||||
New server=未配置的服务器 | New server=未配置的服务器 | ||||
@@ -50,10 +55,12 @@ Move D&own=下移(&O) | |||||
&File=文件(&F) | &File=文件(&F) | ||||
&Open Location=在资源管理器中打开(&O) | &Open Location=在资源管理器中打开(&O) | ||||
E&xit=退出(&X) | E&xit=退出(&X) | ||||
&Clean logs=清空(&C) | |||||
&Font=字体(&F) | |||||
&Wrap text=自动换行(&W) | |||||
&Top most=置顶(&T) | |||||
&View=视图(&V) | |||||
&Clean Logs=清空日志(&C) | |||||
Change &Font=设置字体(&F) | |||||
&Wrap Text=自动换行(&W) | |||||
&Top Most=置顶(&T) | |||||
&Show Toolbar=显示工具栏(&S) | |||||
Log Viewer=日志查看器 | Log Viewer=日志查看器 | ||||
# QRCode Form | # QRCode Form | ||||
@@ -77,7 +84,8 @@ Password can not be blank=密码不能为空 | |||||
Port out of range=端口超出范围 | Port out of range=端口超出范围 | ||||
Port can't be 8123=端口不能为 8123 | Port can't be 8123=端口不能为 8123 | ||||
Shadowsocks {0} Update Found=Shadowsocks {0} 更新 | Shadowsocks {0} Update Found=Shadowsocks {0} 更新 | ||||
Click here to download=点击这里下载 | |||||
No update is available=没有可用的更新 | |||||
Click here to update=点击这里升级 | |||||
Shadowsocks is here=Shadowsocks 在这里 | Shadowsocks is here=Shadowsocks 在这里 | ||||
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks | You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks | ||||
System Proxy Enabled=系统代理已启用 | System Proxy Enabled=系统代理已启用 | ||||
@@ -1,5 +1,5 @@ | |||||
listen-address __POLIPO_BIND_IP__:8123 | |||||
listen-address __POLIPO_BIND_IP__:__POLIPO_BIND_PORT__ | |||||
show-on-task-bar 0 | show-on-task-bar 0 | ||||
activity-animation 0 | activity-animation 0 | ||||
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . | |||||
hide-console | |||||
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . | |||||
hide-console |
@@ -8,19 +8,23 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
public const int MAX_INPUT_SIZE = 32768; | public const int MAX_INPUT_SIZE = 32768; | ||||
protected EncryptorBase(string method, string password) | |||||
protected EncryptorBase(string method, string password, bool onetimeauth, bool isudp) | |||||
{ | { | ||||
Method = method; | Method = method; | ||||
Password = password; | Password = password; | ||||
OnetimeAuth = onetimeauth; | |||||
IsUDP = isudp; | |||||
} | } | ||||
protected string Method; | protected string Method; | ||||
protected string Password; | protected string Password; | ||||
protected bool OnetimeAuth; | |||||
protected bool IsUDP; | |||||
protected byte[] GetPasswordHash() | protected byte[] GetPasswordHash() | ||||
{ | { | ||||
byte[] inputBytes = Encoding.UTF8.GetBytes(Password); | byte[] inputBytes = Encoding.UTF8.GetBytes(Password); | ||||
byte[] hash = MD5.Create().ComputeHash(inputBytes); | |||||
byte[] hash = MbedTLS.MD5(inputBytes); | |||||
return hash; | return hash; | ||||
} | } | ||||
@@ -8,15 +8,11 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
private static Dictionary<string, Type> _registeredEncryptors; | private static Dictionary<string, Type> _registeredEncryptors; | ||||
private static Type[] _constructorTypes = new Type[] { typeof(string), typeof(string) }; | |||||
private static Type[] _constructorTypes = new Type[] { typeof(string), typeof(string), typeof(bool), typeof(bool) }; | |||||
static EncryptorFactory() | static EncryptorFactory() | ||||
{ | { | ||||
_registeredEncryptors = new Dictionary<string, Type>(); | _registeredEncryptors = new Dictionary<string, Type>(); | ||||
foreach (string method in TableEncryptor.SupportedCiphers()) | |||||
{ | |||||
_registeredEncryptors.Add(method, typeof(TableEncryptor)); | |||||
} | |||||
foreach (string method in PolarSSLEncryptor.SupportedCiphers()) | foreach (string method in PolarSSLEncryptor.SupportedCiphers()) | ||||
{ | { | ||||
_registeredEncryptors.Add(method, typeof(PolarSSLEncryptor)); | _registeredEncryptors.Add(method, typeof(PolarSSLEncryptor)); | ||||
@@ -27,16 +23,16 @@ namespace Shadowsocks.Encryption | |||||
} | } | ||||
} | } | ||||
public static IEncryptor GetEncryptor(string method, string password) | |||||
public static IEncryptor GetEncryptor(string method, string password, bool onetimeauth, bool isudp) | |||||
{ | { | ||||
if (string.IsNullOrEmpty(method)) | if (string.IsNullOrEmpty(method)) | ||||
{ | { | ||||
method = "table"; | |||||
method = "aes-256-cfb"; | |||||
} | } | ||||
method = method.ToLowerInvariant(); | method = method.ToLowerInvariant(); | ||||
Type t = _registeredEncryptors[method]; | Type t = _registeredEncryptors[method]; | ||||
ConstructorInfo c = t.GetConstructor(_constructorTypes); | ConstructorInfo c = t.GetConstructor(_constructorTypes); | ||||
IEncryptor result = (IEncryptor)c.Invoke(new object[] { method, password }); | |||||
IEncryptor result = (IEncryptor)c.Invoke(new object[] { method, password, onetimeauth, isudp }); | |||||
return result; | return result; | ||||
} | } | ||||
} | } | ||||
@@ -2,12 +2,24 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Security.Cryptography; | using System.Security.Cryptography; | ||||
using System.Text; | using System.Text; | ||||
using System.Net; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
public abstract class IVEncryptor | public abstract class IVEncryptor | ||||
: EncryptorBase | : EncryptorBase | ||||
{ | { | ||||
public const int MAX_KEY_LENGTH = 64; | |||||
public const int MAX_IV_LENGTH = 16; | |||||
public const int ONETIMEAUTH_FLAG = 0x10; | |||||
public const int ADDRTYPE_MASK = 0xF; | |||||
public const int ONETIMEAUTH_BYTES = 10; | |||||
public const int CLEN_BYTES = 2; | |||||
public const int AUTH_BYTES = ONETIMEAUTH_BYTES + CLEN_BYTES; | |||||
protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; | protected static byte[] tempbuf = new byte[MAX_INPUT_SIZE]; | ||||
protected Dictionary<string, int[]> ciphers; | protected Dictionary<string, int[]> ciphers; | ||||
@@ -25,9 +37,11 @@ namespace Shadowsocks.Encryption | |||||
protected byte[] _key; | protected byte[] _key; | ||||
protected int keyLen; | protected int keyLen; | ||||
protected int ivLen; | protected int ivLen; | ||||
protected uint counter = 0; | |||||
protected byte[] _keyBuffer = null; | |||||
public IVEncryptor(string method, string password) | |||||
: base(method, password) | |||||
public IVEncryptor(string method, string password, bool onetimeauth, bool isudp) | |||||
: base(method, password, onetimeauth, isudp) | |||||
{ | { | ||||
InitKey(method, password); | InitKey(method, password); | ||||
} | } | ||||
@@ -48,18 +62,22 @@ namespace Shadowsocks.Encryption | |||||
} | } | ||||
keyLen = ciphers[_method][0]; | keyLen = ciphers[_method][0]; | ||||
ivLen = ciphers[_method][1]; | ivLen = ciphers[_method][1]; | ||||
if (CachedKeys.ContainsKey(k)) | |||||
{ | |||||
_key = CachedKeys[k]; | |||||
} | |||||
else | |||||
if (!CachedKeys.ContainsKey(k)) | |||||
{ | { | ||||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||||
_key = new byte[32]; | |||||
byte[] iv = new byte[16]; | |||||
bytesToKey(passbuf, _key); | |||||
CachedKeys[k] = _key; | |||||
lock (CachedKeys) | |||||
{ | |||||
if (!CachedKeys.ContainsKey(k)) | |||||
{ | |||||
byte[] passbuf = Encoding.UTF8.GetBytes(password); | |||||
_key = new byte[32]; | |||||
byte[] iv = new byte[16]; | |||||
bytesToKey(passbuf, _key); | |||||
CachedKeys[k] = _key; | |||||
} | |||||
} | |||||
} | } | ||||
if (_key == null) | |||||
_key = CachedKeys[k]; | |||||
} | } | ||||
protected void bytesToKey(byte[] password, byte[] key) | protected void bytesToKey(byte[] password, byte[] key) | ||||
@@ -69,16 +87,15 @@ namespace Shadowsocks.Encryption | |||||
byte[] md5sum = null; | byte[] md5sum = null; | ||||
while (i < key.Length) | while (i < key.Length) | ||||
{ | { | ||||
MD5 md5 = MD5.Create(); | |||||
if (i == 0) | if (i == 0) | ||||
{ | { | ||||
md5sum = md5.ComputeHash(password); | |||||
md5sum = MbedTLS.MD5(password); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
md5sum.CopyTo(result, 0); | md5sum.CopyTo(result, 0); | ||||
password.CopyTo(result, md5sum.Length); | password.CopyTo(result, md5sum.Length); | ||||
md5sum = md5.ComputeHash(result); | |||||
md5sum = MbedTLS.MD5(result); | |||||
} | } | ||||
md5sum.CopyTo(key, i); | md5sum.CopyTo(key, i); | ||||
i += md5sum.Length; | i += md5sum.Length; | ||||
@@ -88,7 +105,8 @@ namespace Shadowsocks.Encryption | |||||
protected static void randBytes(byte[] buf, int length) | protected static void randBytes(byte[] buf, int length) | ||||
{ | { | ||||
byte[] temp = new byte[length]; | byte[] temp = new byte[length]; | ||||
new Random().NextBytes(temp); | |||||
RNGCryptoServiceProvider rngServiceProvider = new RNGCryptoServiceProvider(); | |||||
rngServiceProvider.GetBytes(temp); | |||||
temp.CopyTo(buf, 0); | temp.CopyTo(buf, 0); | ||||
} | } | ||||
@@ -111,14 +129,122 @@ namespace Shadowsocks.Encryption | |||||
protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf); | protected abstract void cipherUpdate(bool isCipher, int length, byte[] buf, byte[] outbuf); | ||||
protected int getHeadLen(byte[] buf, int length) | |||||
{ | |||||
int len = 0; | |||||
int atyp = length > 0 ? (buf[0] & ADDRTYPE_MASK) : 0; | |||||
if (atyp == 1) | |||||
{ | |||||
len = 7; // atyp (1 bytes) + ipv4 (4 bytes) + port (2 bytes) | |||||
} | |||||
else if (atyp == 3 && length > 1) | |||||
{ | |||||
int nameLen = buf[1]; | |||||
len = 4 + nameLen; // atyp (1 bytes) + name length (1 bytes) + name (n bytes) + port (2 bytes) | |||||
} | |||||
else if (atyp == 4) | |||||
{ | |||||
len = 19; // atyp (1 bytes) + ipv6 (16 bytes) + port (2 bytes) | |||||
} | |||||
if (len == 0 || len > length) | |||||
throw new Exception($"invalid header with addr type {atyp}"); | |||||
return len; | |||||
} | |||||
protected byte[] genOnetimeAuthHash(byte[] msg, int msg_len) | |||||
{ | |||||
byte[] auth = new byte[ONETIMEAUTH_BYTES]; | |||||
byte[] hash = new byte[20]; | |||||
byte[] auth_key = new byte[MAX_IV_LENGTH + MAX_KEY_LENGTH]; | |||||
Buffer.BlockCopy(_encryptIV, 0, auth_key, 0, ivLen); | |||||
Buffer.BlockCopy(_key, 0, auth_key, ivLen, keyLen); | |||||
Sodium.ss_sha1_hmac_ex(auth_key, (uint)(ivLen + keyLen), | |||||
msg, 0, (uint)msg_len, hash); | |||||
Buffer.BlockCopy(hash, 0, auth, 0, ONETIMEAUTH_BYTES); | |||||
return auth; | |||||
} | |||||
protected void updateKeyBuffer() | |||||
{ | |||||
if (_keyBuffer == null) | |||||
{ | |||||
_keyBuffer = new byte[MAX_IV_LENGTH + 4]; | |||||
Buffer.BlockCopy(_encryptIV, 0, _keyBuffer, 0, ivLen); | |||||
} | |||||
byte[] counter_bytes = BitConverter.GetBytes((uint)IPAddress.HostToNetworkOrder((int)counter)); | |||||
Buffer.BlockCopy(counter_bytes, 0, _keyBuffer, ivLen, 4); | |||||
counter++; | |||||
} | |||||
protected byte[] genHash(byte[] buf, int offset, int len) | |||||
{ | |||||
byte[] hash = new byte[20]; | |||||
updateKeyBuffer(); | |||||
Sodium.ss_sha1_hmac_ex(_keyBuffer, (uint)_keyBuffer.Length, | |||||
buf, offset, (uint)len, hash); | |||||
return hash; | |||||
} | |||||
protected void reactBuffer4TCP(byte[] buf, ref int length) | |||||
{ | |||||
if (!_encryptIVSent) | |||||
{ | |||||
int headLen = getHeadLen(buf, length); | |||||
int dataLen = length - headLen; | |||||
buf[0] |= ONETIMEAUTH_FLAG; | |||||
byte[] hash = genOnetimeAuthHash(buf, headLen); | |||||
Buffer.BlockCopy(buf, headLen, buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen); | |||||
Buffer.BlockCopy(hash, 0, buf, headLen, ONETIMEAUTH_BYTES); | |||||
hash = genHash(buf, headLen + ONETIMEAUTH_BYTES + AUTH_BYTES, dataLen); | |||||
Buffer.BlockCopy(hash, 0, buf, headLen + ONETIMEAUTH_BYTES + CLEN_BYTES, ONETIMEAUTH_BYTES); | |||||
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)dataLen)); | |||||
Buffer.BlockCopy(lenBytes, 0, buf, headLen + ONETIMEAUTH_BYTES, CLEN_BYTES); | |||||
length = headLen + ONETIMEAUTH_BYTES + AUTH_BYTES + dataLen; | |||||
} | |||||
else | |||||
{ | |||||
byte[] hash = genHash(buf, 0, length); | |||||
Buffer.BlockCopy(buf, 0, buf, AUTH_BYTES, length); | |||||
byte[] lenBytes = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((short)length)); | |||||
Buffer.BlockCopy(lenBytes, 0, buf, 0, CLEN_BYTES); | |||||
Buffer.BlockCopy(hash, 0, buf, CLEN_BYTES, ONETIMEAUTH_BYTES); | |||||
length += AUTH_BYTES; | |||||
} | |||||
} | |||||
protected void reactBuffer4UDP(byte[] buf, ref int length) | |||||
{ | |||||
buf[0] |= ONETIMEAUTH_FLAG; | |||||
byte[] hash = genOnetimeAuthHash(buf, length); | |||||
Buffer.BlockCopy(hash, 0, buf, length, ONETIMEAUTH_BYTES); | |||||
length += ONETIMEAUTH_BYTES; | |||||
} | |||||
protected void reactBuffer(byte[] buf, ref int length) | |||||
{ | |||||
if (OnetimeAuth && ivLen > 0) | |||||
{ | |||||
if (!IsUDP) | |||||
{ | |||||
reactBuffer4TCP(buf, ref length); | |||||
} | |||||
else | |||||
{ | |||||
reactBuffer4UDP(buf, ref length); | |||||
} | |||||
} | |||||
} | |||||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | ||||
{ | { | ||||
if (!_encryptIVSent) | if (!_encryptIVSent) | ||||
{ | { | ||||
_encryptIVSent = true; | |||||
randBytes(outbuf, ivLen); | randBytes(outbuf, ivLen); | ||||
initCipher(outbuf, true); | initCipher(outbuf, true); | ||||
outlength = length + ivLen; | outlength = length + ivLen; | ||||
reactBuffer(buf, ref length); | |||||
_encryptIVSent = true; | |||||
lock (tempbuf) | lock (tempbuf) | ||||
{ | { | ||||
cipherUpdate(true, length, buf, tempbuf); | cipherUpdate(true, length, buf, tempbuf); | ||||
@@ -128,6 +254,7 @@ namespace Shadowsocks.Encryption | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
reactBuffer(buf, ref length); | |||||
outlength = length; | outlength = length; | ||||
cipherUpdate(true, length, buf, outbuf); | cipherUpdate(true, length, buf, outbuf); | ||||
} | } | ||||
@@ -153,5 +280,6 @@ namespace Shadowsocks.Encryption | |||||
cipherUpdate(false, length, buf, outbuf); | cipherUpdate(false, length, buf, outbuf); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,65 @@ | |||||
using System; | |||||
using System.IO; | |||||
using System.Runtime.InteropServices; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Encryption | |||||
{ | |||||
public class MbedTLS | |||||
{ | |||||
const string DLLNAME = "libsscrypto"; | |||||
static MbedTLS() | |||||
{ | |||||
string dllPath = Utils.GetTempPath("libsscrypto.dll"); | |||||
try | |||||
{ | |||||
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); | |||||
} | |||||
catch (IOException) | |||||
{ | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Console.WriteLine(e.ToString()); | |||||
} | |||||
LoadLibrary(dllPath); | |||||
} | |||||
[DllImport("Kernel32.dll")] | |||||
private static extern IntPtr LoadLibrary(string path); | |||||
public const int MD5_CTX_SIZE = 88; | |||||
public static byte[] MD5(byte[] input) | |||||
{ | |||||
IntPtr ctx = Marshal.AllocHGlobal(MD5_CTX_SIZE); | |||||
byte[] output = new byte[16]; | |||||
MbedTLS.md5_init(ctx); | |||||
MbedTLS.md5_starts(ctx); | |||||
MbedTLS.md5_update(ctx, input, (uint)input.Length); | |||||
MbedTLS.md5_finish(ctx, output); | |||||
MbedTLS.md5_free(ctx); | |||||
Marshal.FreeHGlobal(ctx); | |||||
return output; | |||||
} | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static void md5_init(IntPtr ctx); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static void md5_free(IntPtr ctx); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static void md5_starts(IntPtr ctx); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static void md5_update(IntPtr ctx, byte[] input, uint ilen); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static void md5_finish(IntPtr ctx, byte[] output); | |||||
} | |||||
} |
@@ -1,11 +1,10 @@ | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.IO; | using System.IO; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -19,8 +18,7 @@ namespace Shadowsocks.Encryption | |||||
static PolarSSL() | static PolarSSL() | ||||
{ | { | ||||
string tempPath = Utils.GetTempPath(); | |||||
string dllPath = tempPath + "/libsscrypto.dll"; | |||||
string dllPath = Utils.GetTempPath("libsscrypto.dll"); | |||||
try | try | ||||
{ | { | ||||
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); | FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); | ||||
@@ -30,7 +28,7 @@ namespace Shadowsocks.Encryption | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e.ToString()); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
LoadLibrary(dllPath); | LoadLibrary(dllPath); | ||||
} | } | ||||
@@ -63,6 +61,5 @@ namespace Shadowsocks.Encryption | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | ||||
public extern static int arc4_crypt(IntPtr ctx, int length, byte[] input, byte[] output); | public extern static int arc4_crypt(IntPtr ctx, int length, byte[] input, byte[] output); | ||||
} | } | ||||
} | } |
@@ -16,8 +16,8 @@ namespace Shadowsocks.Encryption | |||||
private IntPtr _encryptCtx = IntPtr.Zero; | private IntPtr _encryptCtx = IntPtr.Zero; | ||||
private IntPtr _decryptCtx = IntPtr.Zero; | private IntPtr _decryptCtx = IntPtr.Zero; | ||||
public PolarSSLEncryptor(string method, string password) | |||||
: base(method, password) | |||||
public PolarSSLEncryptor(string method, string password, bool onetimeauth, bool isudp) | |||||
: base(method, password, onetimeauth, isudp) | |||||
{ | { | ||||
InitKey(method, password); | InitKey(method, password); | ||||
} | } | ||||
@@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption | |||||
{"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, | {"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, | ||||
{"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, | {"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, | ||||
{"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, | {"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, | ||||
{"rc4", new int[]{16, 0, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, | |||||
{"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, | {"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, | ||||
}; | }; | ||||
@@ -61,7 +60,7 @@ namespace Shadowsocks.Encryption | |||||
realkey = new byte[keyLen]; | realkey = new byte[keyLen]; | ||||
Array.Copy(_key, 0, temp, 0, keyLen); | Array.Copy(_key, 0, temp, 0, keyLen); | ||||
Array.Copy(iv, 0, temp, keyLen, ivLen); | Array.Copy(iv, 0, temp, keyLen, ivLen); | ||||
realkey = MD5.Create().ComputeHash(temp); | |||||
realkey = MbedTLS.MD5(temp); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -1,11 +1,10 @@ | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.IO; | using System.IO; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.Encryption | namespace Shadowsocks.Encryption | ||||
{ | { | ||||
@@ -15,19 +14,17 @@ namespace Shadowsocks.Encryption | |||||
static Sodium() | static Sodium() | ||||
{ | { | ||||
string tempPath = Utils.GetTempPath(); | |||||
string dllPath = tempPath + "/libsscrypto.dll"; | |||||
string dllPath = Utils.GetTempPath("libsscrypto.dll"); | |||||
try | try | ||||
{ | { | ||||
FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); | FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); | ||||
LoadLibrary(dllPath); | |||||
} | } | ||||
catch (IOException) | catch (IOException) | ||||
{ | { | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Console.WriteLine(e.ToString()); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
LoadLibrary(dllPath); | LoadLibrary(dllPath); | ||||
} | } | ||||
@@ -36,9 +33,18 @@ namespace Shadowsocks.Encryption | |||||
private static extern IntPtr LoadLibrary(string path); | private static extern IntPtr LoadLibrary(string path); | ||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | ||||
public extern static void crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); | |||||
public extern static int crypto_stream_salsa20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | ||||
public extern static void crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); | |||||
public extern static int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static int crypto_stream_chacha20_ietf_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, uint ic, byte[] k); | |||||
[DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] | |||||
public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen, | |||||
byte[] input, int ioff, uint ilen, | |||||
byte[] output); | |||||
} | } | ||||
} | } | ||||
@@ -10,6 +10,7 @@ namespace Shadowsocks.Encryption | |||||
{ | { | ||||
const int CIPHER_SALSA20 = 1; | const int CIPHER_SALSA20 = 1; | ||||
const int CIPHER_CHACHA20 = 2; | const int CIPHER_CHACHA20 = 2; | ||||
const int CIPHER_CHACHA20_IETF = 3; | |||||
const int SODIUM_BLOCK_SIZE = 64; | const int SODIUM_BLOCK_SIZE = 64; | ||||
@@ -20,8 +21,8 @@ namespace Shadowsocks.Encryption | |||||
protected ulong _encryptIC; | protected ulong _encryptIC; | ||||
protected ulong _decryptIC; | protected ulong _decryptIC; | ||||
public SodiumEncryptor(string method, string password) | |||||
: base(method, password) | |||||
public SodiumEncryptor(string method, string password, bool onetimeauth, bool isudp) | |||||
: base(method, password, onetimeauth, isudp) | |||||
{ | { | ||||
InitKey(method, password); | InitKey(method, password); | ||||
} | } | ||||
@@ -29,6 +30,7 @@ namespace Shadowsocks.Encryption | |||||
private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { | private static Dictionary<string, int[]> _ciphers = new Dictionary<string, int[]> { | ||||
{"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}}, | {"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}}, | ||||
{"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}}, | {"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}}, | ||||
{"chacha20-ietf", new int[]{32, 12, CIPHER_CHACHA20_IETF, PolarSSL.AES_CTX_SIZE}}, | |||||
}; | }; | ||||
protected override Dictionary<string, int[]> getCiphers() | protected override Dictionary<string, int[]> getCiphers() | ||||
@@ -75,6 +77,9 @@ namespace Shadowsocks.Encryption | |||||
case CIPHER_CHACHA20: | case CIPHER_CHACHA20: | ||||
Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); | ||||
break; | break; | ||||
case CIPHER_CHACHA20_IETF: | |||||
Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key); | |||||
break; | |||||
} | } | ||||
Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); | Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); | ||||
padding += length; | padding += length; | ||||
@@ -1,106 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
namespace Shadowsocks.Encryption | |||||
{ | |||||
public class TableEncryptor | |||||
: EncryptorBase | |||||
{ | |||||
public TableEncryptor(string method, string password) | |||||
: base(method, password) | |||||
{ | |||||
byte[] hash = GetPasswordHash(); | |||||
// TODO endian | |||||
ulong a = BitConverter.ToUInt64(hash, 0); | |||||
for (int i = 0; i < 256; i++) | |||||
{ | |||||
_encryptTable[i] = (byte)i; | |||||
} | |||||
for (int i = 1; i < 1024; i++) | |||||
{ | |||||
_encryptTable = MergeSort(_encryptTable, a, i); | |||||
} | |||||
for (int i = 0; i < 256; i++) | |||||
{ | |||||
_decryptTable[_encryptTable[i]] = (byte)i; | |||||
} | |||||
} | |||||
public static List<string> SupportedCiphers() | |||||
{ | |||||
return new List<string>(new string[]{"table"}); | |||||
} | |||||
public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
byte[] result = new byte[length]; | |||||
for (int i = 0; i < length; i++) | |||||
{ | |||||
outbuf[i] = _encryptTable[buf[i]]; | |||||
} | |||||
outlength = length; | |||||
} | |||||
public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) | |||||
{ | |||||
byte[] result = new byte[length]; | |||||
for (int i = 0; i < length; i++) | |||||
{ | |||||
outbuf[i] = _decryptTable[buf[i]]; | |||||
} | |||||
outlength = length; | |||||
} | |||||
private readonly byte[] _encryptTable = new byte[256]; | |||||
private readonly byte[] _decryptTable = new byte[256]; | |||||
private static long Compare(byte x, byte y, ulong a, int i) | |||||
{ | |||||
return (long)(a % (ulong)(x + i)) - (long)(a % (ulong)(y + i)); | |||||
} | |||||
private byte[] MergeSort(byte[] array, ulong a, int j) | |||||
{ | |||||
if (array.Length == 1) | |||||
{ | |||||
return array; | |||||
} | |||||
int middle = array.Length / 2; | |||||
byte[] left = new byte[middle]; | |||||
for (int i = 0; i < middle; i++) | |||||
{ | |||||
left[i] = array[i]; | |||||
} | |||||
byte[] right = new byte[array.Length - middle]; | |||||
for (int i = 0; i < array.Length - middle; i++) | |||||
{ | |||||
right[i] = array[i + middle]; | |||||
} | |||||
left = MergeSort(left, a, j); | |||||
right = MergeSort(right, a, j); | |||||
int leftptr = 0; | |||||
int rightptr = 0; | |||||
byte[] sorted = new byte[array.Length]; | |||||
for (int k = 0; k < array.Length; k++) | |||||
{ | |||||
if (rightptr == right.Length || ((leftptr < left.Length) && (Compare(left[leftptr], right[rightptr], a, j) <= 0))) | |||||
{ | |||||
sorted[k] = left[leftptr]; | |||||
leftptr++; | |||||
} | |||||
else if (leftptr == left.Length || ((rightptr < right.Length) && (Compare(right[rightptr], left[leftptr], a, j)) <= 0)) | |||||
{ | |||||
sorted[k] = right[rightptr]; | |||||
rightptr++; | |||||
} | |||||
} | |||||
return sorted; | |||||
} | |||||
public override void Dispose() | |||||
{ | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,5 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<Weavers> | |||||
<Costura/> | |||||
</Weavers> |
@@ -1,9 +1,9 @@ | |||||
using Shadowsocks.Controller; | |||||
using System; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Text; | |||||
using System.Windows.Forms; | |||||
using Shadowsocks.Controller; | |||||
using Newtonsoft.Json; | |||||
namespace Shadowsocks.Model | namespace Shadowsocks.Model | ||||
{ | { | ||||
@@ -23,19 +23,17 @@ namespace Shadowsocks.Model | |||||
public string pacUrl; | public string pacUrl; | ||||
public bool useOnlinePac; | public bool useOnlinePac; | ||||
public bool availabilityStatistics; | public bool availabilityStatistics; | ||||
public bool autoCheckUpdate; | |||||
public LogViewerConfig logViewer; | |||||
private static string CONFIG_FILE = "gui-config.json"; | private static string CONFIG_FILE = "gui-config.json"; | ||||
public Server GetCurrentServer() | public Server GetCurrentServer() | ||||
{ | { | ||||
if (index >= 0 && index < configs.Count) | if (index >= 0 && index < configs.Count) | ||||
{ | |||||
return configs[index]; | return configs[index]; | ||||
} | |||||
else | else | ||||
{ | |||||
return GetDefaultServer(); | return GetDefaultServer(); | ||||
} | |||||
} | } | ||||
public static void CheckServer(Server server) | public static void CheckServer(Server server) | ||||
@@ -50,32 +48,24 @@ namespace Shadowsocks.Model | |||||
try | try | ||||
{ | { | ||||
string configContent = File.ReadAllText(CONFIG_FILE); | string configContent = File.ReadAllText(CONFIG_FILE); | ||||
Configuration config = SimpleJson.SimpleJson.DeserializeObject<Configuration>(configContent, new JsonSerializerStrategy()); | |||||
Configuration config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||||
config.isDefault = false; | config.isDefault = false; | ||||
if (config.localPort == 0) | if (config.localPort == 0) | ||||
{ | |||||
config.localPort = 1080; | config.localPort = 1080; | ||||
} | |||||
if (config.index == -1) | |||||
{ | |||||
if (config.strategy == null) | |||||
{ | |||||
config.index = 0; | |||||
} | |||||
} | |||||
if (config.index == -1 && config.strategy == null) | |||||
config.index = 0; | |||||
return config; | return config; | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
if (!(e is FileNotFoundException)) | if (!(e is FileNotFoundException)) | ||||
{ | |||||
Console.WriteLine(e); | |||||
} | |||||
Logging.LogUsefulException(e); | |||||
return new Configuration | return new Configuration | ||||
{ | { | ||||
index = 0, | index = 0, | ||||
isDefault = true, | isDefault = true, | ||||
localPort = 1080, | localPort = 1080, | ||||
autoCheckUpdate = true, | |||||
configs = new List<Server>() | configs = new List<Server>() | ||||
{ | { | ||||
GetDefaultServer() | GetDefaultServer() | ||||
@@ -87,26 +77,17 @@ namespace Shadowsocks.Model | |||||
public static void Save(Configuration config) | public static void Save(Configuration config) | ||||
{ | { | ||||
if (config.index >= config.configs.Count) | if (config.index >= config.configs.Count) | ||||
{ | |||||
config.index = config.configs.Count - 1; | config.index = config.configs.Count - 1; | ||||
} | |||||
if (config.index < -1) | if (config.index < -1) | ||||
{ | |||||
config.index = -1; | config.index = -1; | ||||
} | |||||
if (config.index == -1) | |||||
{ | |||||
if (config.strategy == null) | |||||
{ | |||||
config.index = 0; | |||||
} | |||||
} | |||||
if (config.index == -1 && config.strategy == null) | |||||
config.index = 0; | |||||
config.isDefault = false; | config.isDefault = false; | ||||
try | try | ||||
{ | { | ||||
using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) | using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) | ||||
{ | { | ||||
string jsonString = SimpleJson.SimpleJson.SerializeObject(config); | |||||
string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); | |||||
sw.Write(jsonString); | sw.Write(jsonString); | ||||
sw.Flush(); | sw.Flush(); | ||||
} | } | ||||
@@ -125,55 +106,32 @@ namespace Shadowsocks.Model | |||||
private static void Assert(bool condition) | private static void Assert(bool condition) | ||||
{ | { | ||||
if (!condition) | if (!condition) | ||||
{ | |||||
throw new Exception(I18N.GetString("assertion failure")); | throw new Exception(I18N.GetString("assertion failure")); | ||||
} | |||||
} | } | ||||
public static void CheckPort(int port) | public static void CheckPort(int port) | ||||
{ | { | ||||
if (port <= 0 || port > 65535) | if (port <= 0 || port > 65535) | ||||
{ | |||||
throw new ArgumentException(I18N.GetString("Port out of range")); | throw new ArgumentException(I18N.GetString("Port out of range")); | ||||
} | |||||
} | } | ||||
public static void CheckLocalPort(int port) | public static void CheckLocalPort(int port) | ||||
{ | { | ||||
CheckPort(port); | CheckPort(port); | ||||
if (port == 8123) | if (port == 8123) | ||||
{ | |||||
throw new ArgumentException(I18N.GetString("Port can't be 8123")); | throw new ArgumentException(I18N.GetString("Port can't be 8123")); | ||||
} | |||||
} | } | ||||
private static void CheckPassword(string password) | private static void CheckPassword(string password) | ||||
{ | { | ||||
if (string.IsNullOrEmpty(password)) | if (string.IsNullOrEmpty(password)) | ||||
{ | |||||
throw new ArgumentException(I18N.GetString("Password can not be blank")); | throw new ArgumentException(I18N.GetString("Password can not be blank")); | ||||
} | |||||
} | } | ||||
private static void CheckServer(string server) | private static void CheckServer(string server) | ||||
{ | { | ||||
if (string.IsNullOrEmpty(server)) | if (string.IsNullOrEmpty(server)) | ||||
{ | |||||
throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | ||||
} | |||||
} | |||||
private class JsonSerializerStrategy : SimpleJson.PocoJsonSerializerStrategy | |||||
{ | |||||
// convert string to int | |||||
public override object DeserializeObject(object value, Type type) | |||||
{ | |||||
if (type == typeof(Int32) && value.GetType() == typeof(string)) | |||||
{ | |||||
return Int32.Parse(value.ToString()); | |||||
} | |||||
return base.DeserializeObject(value, type); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,103 @@ | |||||
using Shadowsocks.View; | |||||
using System; | |||||
using System.Drawing; | |||||
using System.Windows.Forms; | |||||
namespace Shadowsocks.Model | |||||
{ | |||||
[Serializable] | |||||
public class LogViewerConfig | |||||
{ | |||||
public string fontName; | |||||
public float fontSize; | |||||
public string bgColor; | |||||
public string textColor; | |||||
public bool topMost; | |||||
public bool wrapText; | |||||
public bool toolbarShown; | |||||
public int width; | |||||
public int height; | |||||
public int top; | |||||
public int left; | |||||
public LogViewerConfig() | |||||
{ | |||||
fontName = "Consolas"; | |||||
fontSize = 8; | |||||
bgColor = "black"; | |||||
textColor = "white"; | |||||
topMost = false; | |||||
wrapText = false; | |||||
toolbarShown = false; | |||||
width = 600; | |||||
height = 400; | |||||
left = GetBestLeft(); | |||||
top = GetBestTop(); | |||||
} | |||||
// Use GetBestTop() and GetBestLeft() to ensure the log viwer form can be always display IN screen. | |||||
public int GetBestLeft() | |||||
{ | |||||
width = (width >= 400) ? width : 400; // set up the minimum size | |||||
return Screen.PrimaryScreen.WorkingArea.Width - width; | |||||
} | |||||
public int GetBestTop() | |||||
{ | |||||
height = (height >= 200) ? height : 200; // set up the minimum size | |||||
return Screen.PrimaryScreen.WorkingArea.Height - height; | |||||
} | |||||
public Font GetFont() | |||||
{ | |||||
try | |||||
{ | |||||
return new Font(fontName, fontSize, FontStyle.Regular); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
return new Font("Console", 8F); | |||||
} | |||||
} | |||||
public void SetFont(Font font) | |||||
{ | |||||
fontName = font.Name; | |||||
fontSize = font.Size; | |||||
} | |||||
public Color GetBackgroundColor() | |||||
{ | |||||
try | |||||
{ | |||||
return ColorTranslator.FromHtml(bgColor); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
return ColorTranslator.FromHtml("black"); | |||||
} | |||||
} | |||||
public void SetBackgroundColor(Color color) | |||||
{ | |||||
bgColor = ColorTranslator.ToHtml(color); | |||||
} | |||||
public Color GetTextColor() | |||||
{ | |||||
try | |||||
{ | |||||
return ColorTranslator.FromHtml(textColor); | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
return ColorTranslator.FromHtml("white"); | |||||
} | |||||
} | |||||
public void SetTextColor(Color color) | |||||
{ | |||||
textColor = ColorTranslator.ToHtml(color); | |||||
} | |||||
} | |||||
} |
@@ -1,12 +1,9 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Text; | using System.Text; | ||||
using System.IO; | |||||
using System.Diagnostics; | |||||
using SimpleJson; | |||||
using Shadowsocks.Controller; | |||||
using System.Text.RegularExpressions; | using System.Text.RegularExpressions; | ||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Model | namespace Shadowsocks.Model | ||||
{ | { | ||||
[Serializable] | [Serializable] | ||||
@@ -17,6 +14,7 @@ namespace Shadowsocks.Model | |||||
public string password; | public string password; | ||||
public string method; | public string method; | ||||
public string remarks; | public string remarks; | ||||
public bool auth; | |||||
public override int GetHashCode() | public override int GetHashCode() | ||||
{ | { | ||||
@@ -26,7 +24,7 @@ namespace Shadowsocks.Model | |||||
public override bool Equals(object obj) | public override bool Equals(object obj) | ||||
{ | { | ||||
Server o2 = (Server)obj; | Server o2 = (Server)obj; | ||||
return this.server == o2.server && this.server_port == o2.server_port; | |||||
return server == o2.server && server_port == o2.server_port; | |||||
} | } | ||||
public string FriendlyName() | public string FriendlyName() | ||||
@@ -47,11 +45,12 @@ namespace Shadowsocks.Model | |||||
public Server() | public Server() | ||||
{ | { | ||||
this.server = ""; | |||||
this.server_port = 8388; | |||||
this.method = "aes-256-cfb"; | |||||
this.password = ""; | |||||
this.remarks = ""; | |||||
server = ""; | |||||
server_port = 8388; | |||||
method = "aes-256-cfb"; | |||||
password = ""; | |||||
remarks = ""; | |||||
auth = false; | |||||
} | } | ||||
public Server(string ssURL) : this() | public Server(string ssURL) : this() | ||||
@@ -63,7 +62,7 @@ namespace Shadowsocks.Model | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
bytes = System.Convert.FromBase64String(base64); | |||||
bytes = Convert.FromBase64String(base64); | |||||
} | } | ||||
catch (FormatException) | catch (FormatException) | ||||
{ | { | ||||
@@ -81,13 +80,15 @@ namespace Shadowsocks.Model | |||||
string afterAt = data.Substring(indexLastAt + 1); | string afterAt = data.Substring(indexLastAt + 1); | ||||
int indexLastColon = afterAt.LastIndexOf(':'); | int indexLastColon = afterAt.LastIndexOf(':'); | ||||
this.server_port = int.Parse(afterAt.Substring(indexLastColon + 1)); | |||||
this.server = afterAt.Substring(0, indexLastColon); | |||||
server_port = int.Parse(afterAt.Substring(indexLastColon + 1)); | |||||
server = afterAt.Substring(0, indexLastColon); | |||||
string beforeAt = data.Substring(0, indexLastAt); | string beforeAt = data.Substring(0, indexLastAt); | ||||
string[] parts = beforeAt.Split(new[] { ':' }); | string[] parts = beforeAt.Split(new[] { ':' }); | ||||
this.method = parts[0]; | |||||
this.password = parts[1]; | |||||
method = parts[0]; | |||||
password = parts[1]; | |||||
//TODO: read one_time_auth | |||||
} | } | ||||
catch (IndexOutOfRangeException) | catch (IndexOutOfRangeException) | ||||
{ | { | ||||
@@ -0,0 +1,106 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using Newtonsoft.Json; | |||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Model | |||||
{ | |||||
[Serializable] | |||||
public class StatisticsStrategyConfiguration | |||||
{ | |||||
public static readonly string ID = "com.shadowsocks.strategy.statistics"; | |||||
private bool _statisticsEnabled = true; | |||||
private bool _byIsp = false; | |||||
private bool _byHourOfDay = false; | |||||
private int _choiceKeptMinutes = 10; | |||||
private int _dataCollectionMinutes = 10; | |||||
private int _repeatTimesNum = 4; | |||||
private const string ConfigFile = "statistics-config.json"; | |||||
public static StatisticsStrategyConfiguration Load() | |||||
{ | |||||
try | |||||
{ | |||||
var content = File.ReadAllText(ConfigFile); | |||||
var configuration = JsonConvert.DeserializeObject<StatisticsStrategyConfiguration>(content); | |||||
return configuration; | |||||
} | |||||
catch (FileNotFoundException) | |||||
{ | |||||
var configuration = new StatisticsStrategyConfiguration(); | |||||
Save(configuration); | |||||
return configuration; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
return new StatisticsStrategyConfiguration(); | |||||
} | |||||
} | |||||
public static void Save(StatisticsStrategyConfiguration configuration) | |||||
{ | |||||
try | |||||
{ | |||||
var content = JsonConvert.SerializeObject(configuration, Formatting.Indented); | |||||
File.WriteAllText(ConfigFile, content); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
public Dictionary<string, float> Calculations; | |||||
public StatisticsStrategyConfiguration() | |||||
{ | |||||
var availabilityStatisticsType = typeof(AvailabilityStatistics); | |||||
var statisticsData = availabilityStatisticsType.GetNestedType("StatisticsData"); | |||||
var properties = statisticsData.GetFields(BindingFlags.Instance | BindingFlags.Public); | |||||
Calculations = properties.ToDictionary(p => p.Name, _ => (float)0); | |||||
} | |||||
public bool StatisticsEnabled | |||||
{ | |||||
get { return _statisticsEnabled; } | |||||
set { _statisticsEnabled = value; } | |||||
} | |||||
public bool ByIsp | |||||
{ | |||||
get { return _byIsp; } | |||||
set { _byIsp = value; } | |||||
} | |||||
public bool ByHourOfDay | |||||
{ | |||||
get { return _byHourOfDay; } | |||||
set { _byHourOfDay = value; } | |||||
} | |||||
public int ChoiceKeptMinutes | |||||
{ | |||||
get { return _choiceKeptMinutes; } | |||||
set { _choiceKeptMinutes = value; } | |||||
} | |||||
public int DataCollectionMinutes | |||||
{ | |||||
get { return _dataCollectionMinutes; } | |||||
set { _dataCollectionMinutes = value; } | |||||
} | |||||
public int RepeatTimesNum | |||||
{ | |||||
get { return _repeatTimesNum; } | |||||
set { _repeatTimesNum = value; } | |||||
} | |||||
} | |||||
} |
@@ -1,13 +1,13 @@ | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.View; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.IO; | using System.IO; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Util; | |||||
using Shadowsocks.View; | |||||
namespace Shadowsocks | namespace Shadowsocks | ||||
{ | { | ||||
static class Program | static class Program | ||||
@@ -18,7 +18,7 @@ namespace Shadowsocks | |||||
[STAThread] | [STAThread] | ||||
static void Main() | static void Main() | ||||
{ | { | ||||
Util.Utils.ReleaseMemory(true); | |||||
Utils.ReleaseMemory(true); | |||||
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) | using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) | ||||
{ | { | ||||
Application.EnableVisualStyles(); | Application.EnableVisualStyles(); | ||||
@@ -37,15 +37,19 @@ namespace Shadowsocks | |||||
return; | return; | ||||
} | } | ||||
Directory.SetCurrentDirectory(Application.StartupPath); | Directory.SetCurrentDirectory(Application.StartupPath); | ||||
#if !DEBUG | |||||
#if DEBUG | |||||
Logging.OpenLogFile(); | |||||
// truncate privoxy log file while debugging | |||||
string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); | |||||
if (File.Exists(privoxyLogFilename)) | |||||
using (new FileStream(privoxyLogFilename, FileMode.Truncate)) { } | |||||
#else | |||||
Logging.OpenLogFile(); | Logging.OpenLogFile(); | ||||
#endif | #endif | ||||
ShadowsocksController controller = new ShadowsocksController(); | ShadowsocksController controller = new ShadowsocksController(); | ||||
MenuViewController viewController = new MenuViewController(controller); | MenuViewController viewController = new MenuViewController(controller); | ||||
controller.Start(); | controller.Start(); | ||||
Application.Run(); | Application.Run(); | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,10 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<!-- | |||||
This file is automatically generated by Visual Studio .Net. It is | |||||
used to store generic object data source configuration information. | |||||
Renaming the file extension or editing the content of this file may | |||||
cause the file to be unrecognizable by the program. | |||||
--> | |||||
<GenericObjectDataSource DisplayName="StatisticsStrategyConfiguration" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource"> | |||||
<TypeInfo>Shadowsocks.Model.StatisticsStrategyConfiguration, Shadowsocks, Version=2.5.2.0, Culture=neutral, PublicKeyToken=null</TypeInfo> | |||||
</GenericObjectDataSource> |
@@ -10,8 +10,8 @@ | |||||
namespace Shadowsocks.Properties { | namespace Shadowsocks.Properties { | ||||
using System; | using System; | ||||
/// <summary> | /// <summary> | ||||
/// A strongly-typed resource class, for looking up localized strings, etc. | /// A strongly-typed resource class, for looking up localized strings, etc. | ||||
/// </summary> | /// </summary> | ||||
@@ -31,7 +31,7 @@ namespace Shadowsocks.Properties { | |||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] | ||||
internal Resources() { | internal Resources() { | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Returns the cached ResourceManager instance used by this class. | /// Returns the cached ResourceManager instance used by this class. | ||||
/// </summary> | /// </summary> | ||||
@@ -45,7 +45,7 @@ namespace Shadowsocks.Properties { | |||||
return resourceMan; | return resourceMan; | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Overrides the current thread's CurrentUICulture property for all | /// Overrides the current thread's CurrentUICulture property for all | ||||
/// resource lookups using this strongly typed resource class. | /// resource lookups using this strongly typed resource class. | ||||
@@ -59,7 +59,7 @@ namespace Shadowsocks.Properties { | |||||
resourceCulture = value; | resourceCulture = value; | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | /// Looks up a localized resource of type System.Byte[]. | ||||
/// </summary> | /// </summary> | ||||
@@ -69,39 +69,37 @@ namespace Shadowsocks.Properties { | |||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized string similar to # translation for Simplified Chinese | |||||
/// | |||||
///Shadowsocks=Shadowsocks | |||||
/// | |||||
///# Menu items | |||||
/// | |||||
///Enable System Proxy=启用系统代理 | |||||
///Mode=系统代理模式 | |||||
///PAC=PAC 模式 | |||||
///Global=全局模式 | |||||
///Servers=服务器 | |||||
///Edit Servers...=编辑服务器... | |||||
///Start on Boot=开机启动 | |||||
///Allow Clients from LAN=允许来自局域网的连接 | |||||
///Local PAC=使用本地 PAC | |||||
///Online PAC=使用在线 PAC | |||||
///Edit Local PAC File...=编辑本地 PAC 文件... | |||||
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC | |||||
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | |||||
///Show QRCode...=显示二维码... | |||||
///Scan QRCode from Screen...=扫描屏幕上的二维码... | |||||
///Show Logs...=显示日志... | |||||
///About...=关于... | |||||
///Quit=退出 [rest of string was truncated]";. | |||||
/// Looks up a localized string similar to # translation for Simplified Chinese | |||||
/// | |||||
///Shadowsocks=Shadowsocks | |||||
/// | |||||
///# Menu items | |||||
/// | |||||
///Enable System Proxy=启用系统代理 | |||||
///Mode=系统代理模式 | |||||
///PAC=PAC 模式 | |||||
///Global=全局模式 | |||||
///Servers=服务器 | |||||
///Edit Servers...=编辑服务器... | |||||
///Start on Boot=开机启动 | |||||
///Allow Clients from LAN=允许来自局域网的连接 | |||||
///Local PAC=使用本地 PAC | |||||
///Online PAC=使用在线 PAC | |||||
///Edit Local PAC File...=编辑本地 PAC 文件... | |||||
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC | |||||
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | |||||
///Show QRCode...=显示二维码... | |||||
///Scan QRCode from Screen...=扫描屏幕上的二维码... | |||||
///Availability Statistic [rest of string was truncated]";. | |||||
/// </summary> | /// </summary> | ||||
internal static string cn { | internal static string cn { | ||||
get { | get { | ||||
return ResourceManager.GetString("cn", resourceCulture); | return ResourceManager.GetString("cn", resourceCulture); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | /// Looks up a localized resource of type System.Byte[]. | ||||
/// </summary> | /// </summary> | ||||
@@ -111,7 +109,7 @@ namespace Shadowsocks.Properties { | |||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | /// Looks up a localized resource of type System.Byte[]. | ||||
/// </summary> | /// </summary> | ||||
@@ -121,7 +119,7 @@ namespace Shadowsocks.Properties { | |||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized string similar to listen-address __POLIPO_BIND_IP__:8123 | /// Looks up a localized string similar to listen-address __POLIPO_BIND_IP__:8123 | ||||
///show-on-task-bar 0 | ///show-on-task-bar 0 | ||||
@@ -134,7 +132,7 @@ namespace Shadowsocks.Properties { | |||||
return ResourceManager.GetString("privoxy_conf", resourceCulture); | return ResourceManager.GetString("privoxy_conf", resourceCulture); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | /// Looks up a localized resource of type System.Byte[]. | ||||
/// </summary> | /// </summary> | ||||
@@ -144,7 +142,7 @@ namespace Shadowsocks.Properties { | |||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Byte[]. | /// Looks up a localized resource of type System.Byte[]. | ||||
/// </summary> | /// </summary> | ||||
@@ -154,7 +152,7 @@ namespace Shadowsocks.Properties { | |||||
return ((byte[])(obj)); | return ((byte[])(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | /// Looks up a localized resource of type System.Drawing.Bitmap. | ||||
/// </summary> | /// </summary> | ||||
@@ -164,7 +162,7 @@ namespace Shadowsocks.Properties { | |||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | /// Looks up a localized resource of type System.Drawing.Bitmap. | ||||
/// </summary> | /// </summary> | ||||
@@ -174,7 +172,7 @@ namespace Shadowsocks.Properties { | |||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | /// Looks up a localized resource of type System.Drawing.Bitmap. | ||||
/// </summary> | /// </summary> | ||||
@@ -184,7 +182,7 @@ namespace Shadowsocks.Properties { | |||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | /// Looks up a localized resource of type System.Drawing.Bitmap. | ||||
/// </summary> | /// </summary> | ||||
@@ -194,7 +192,7 @@ namespace Shadowsocks.Properties { | |||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized string similar to ! Put user rules line by line in this file. | /// Looks up a localized string similar to ! Put user rules line by line in this file. | ||||
///! See https://adblockplus.org/en/filter-cheatsheet | ///! See https://adblockplus.org/en/filter-cheatsheet | ||||
@@ -1,32 +1,48 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.IO; | using System.IO; | ||||
using System.IO.Compression; | using System.IO.Compression; | ||||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||||
using System.Text; | |||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Util | namespace Shadowsocks.Util | ||||
{ | { | ||||
public class Utils | public class Utils | ||||
{ | { | ||||
private static string TempPath = null; | |||||
// return path to store temporary files | // return path to store temporary files | ||||
public static string GetTempPath() | public static string GetTempPath() | ||||
{ | { | ||||
if (File.Exists(Application.StartupPath + "\\shadowsocks_portable_mode.txt")) | |||||
if (TempPath == null) | |||||
{ | { | ||||
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"; | |||||
if (File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt"))) | |||||
try | |||||
{ | |||||
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp")); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
TempPath = Path.GetTempPath(); | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
finally | |||||
{ | |||||
// don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log | |||||
TempPath = Path.Combine(Application.StartupPath, "temp"); | |||||
} | |||||
else | |||||
TempPath = Path.GetTempPath(); | |||||
} | } | ||||
return Path.GetTempPath(); | |||||
return TempPath; | |||||
} | |||||
// return a full path with filename combined which pointed to the temporary directory | |||||
public static string GetTempPath(string filename) | |||||
{ | |||||
return Path.Combine(GetTempPath(), filename); | |||||
} | } | ||||
public static void ReleaseMemory(bool removePages) | public static void ReleaseMemory(bool removePages) | ||||
@@ -82,6 +98,33 @@ namespace Shadowsocks.Util | |||||
} | } | ||||
} | } | ||||
public static string FormatBandwidth(long n) | |||||
{ | |||||
float f = n; | |||||
string unit = "B"; | |||||
if (f > 1024) | |||||
{ | |||||
f = f / 1024; | |||||
unit = "KiB"; | |||||
} | |||||
if (f > 1024) | |||||
{ | |||||
f = f / 1024; | |||||
unit = "MiB"; | |||||
} | |||||
if (f > 1024) | |||||
{ | |||||
f = f / 1024; | |||||
unit = "GiB"; | |||||
} | |||||
if (f > 1024) | |||||
{ | |||||
f = f / 1024; | |||||
unit = "TiB"; | |||||
} | |||||
return $"{f:0.##}{unit}"; | |||||
} | |||||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||||
[return: MarshalAs(UnmanagedType.Bool)] | [return: MarshalAs(UnmanagedType.Bool)] | ||||
private static extern bool SetProcessWorkingSetSize(IntPtr process, | private static extern bool SetProcessWorkingSetSize(IntPtr process, | ||||
@@ -0,0 +1,111 @@ | |||||
namespace Shadowsocks.View | |||||
{ | |||||
partial class CalculationControl | |||||
{ | |||||
/// <summary> | |||||
/// Required designer variable. | |||||
/// </summary> | |||||
private System.ComponentModel.IContainer components = null; | |||||
/// <summary> | |||||
/// Clean up any resources being used. | |||||
/// </summary> | |||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | |||||
protected override void Dispose(bool disposing) | |||||
{ | |||||
if (disposing && (components != null)) | |||||
{ | |||||
components.Dispose(); | |||||
} | |||||
base.Dispose(disposing); | |||||
} | |||||
#region Component Designer generated code | |||||
/// <summary> | |||||
/// Required method for Designer support - do not modify | |||||
/// the contents of this method with the code editor. | |||||
/// </summary> | |||||
private void InitializeComponent() | |||||
{ | |||||
this.factorNum = new System.Windows.Forms.NumericUpDown(); | |||||
this.multiply = new System.Windows.Forms.Label(); | |||||
this.plus = new System.Windows.Forms.Label(); | |||||
this.valueLabel = new System.Windows.Forms.Label(); | |||||
((System.ComponentModel.ISupportInitialize)(this.factorNum)).BeginInit(); | |||||
this.SuspendLayout(); | |||||
// | |||||
// factorNum | |||||
// | |||||
this.factorNum.DecimalPlaces = 2; | |||||
this.factorNum.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.factorNum.Increment = new decimal(new int[] { | |||||
1, | |||||
0, | |||||
0, | |||||
131072}); | |||||
this.factorNum.Location = new System.Drawing.Point(285, 5); | |||||
this.factorNum.Minimum = new decimal(new int[] { | |||||
1000, | |||||
0, | |||||
0, | |||||
-2147418112}); | |||||
this.factorNum.Name = "factorNum"; | |||||
this.factorNum.Size = new System.Drawing.Size(86, 34); | |||||
this.factorNum.TabIndex = 6; | |||||
// | |||||
// multiply | |||||
// | |||||
this.multiply.AutoSize = true; | |||||
this.multiply.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.multiply.Location = new System.Drawing.Point(251, 7); | |||||
this.multiply.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.multiply.Name = "multiply"; | |||||
this.multiply.Size = new System.Drawing.Size(26, 28); | |||||
this.multiply.TabIndex = 2; | |||||
this.multiply.Text = "×"; | |||||
// | |||||
// plus | |||||
// | |||||
this.plus.AutoSize = true; | |||||
this.plus.Font = new System.Drawing.Font("Segoe UI", 10F); | |||||
this.plus.Location = new System.Drawing.Point(5, 7); | |||||
this.plus.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.plus.Name = "plus"; | |||||
this.plus.Size = new System.Drawing.Size(26, 28); | |||||
this.plus.TabIndex = 3; | |||||
this.plus.Text = "+"; | |||||
// | |||||
// valueLabel | |||||
// | |||||
this.valueLabel.AutoSize = true; | |||||
this.valueLabel.Font = new System.Drawing.Font("Microsoft YaHei", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.valueLabel.Location = new System.Drawing.Point(39, 11); | |||||
this.valueLabel.Name = "valueLabel"; | |||||
this.valueLabel.Size = new System.Drawing.Size(118, 24); | |||||
this.valueLabel.TabIndex = 7; | |||||
this.valueLabel.Text = "PackageLoss"; | |||||
// | |||||
// CalculationControl | |||||
// | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 18F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |||||
this.Controls.Add(this.valueLabel); | |||||
this.Controls.Add(this.factorNum); | |||||
this.Controls.Add(this.multiply); | |||||
this.Controls.Add(this.plus); | |||||
this.Name = "CalculationControl"; | |||||
this.Size = new System.Drawing.Size(380, 46); | |||||
((System.ComponentModel.ISupportInitialize)(this.factorNum)).EndInit(); | |||||
this.ResumeLayout(false); | |||||
this.PerformLayout(); | |||||
} | |||||
#endregion | |||||
private System.Windows.Forms.NumericUpDown factorNum; | |||||
private System.Windows.Forms.Label multiply; | |||||
private System.Windows.Forms.Label plus; | |||||
private System.Windows.Forms.Label valueLabel; | |||||
} | |||||
} |
@@ -0,0 +1,25 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel; | |||||
using System.Drawing; | |||||
using System.Data; | |||||
using System.Globalization; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Windows.Forms; | |||||
namespace Shadowsocks.View | |||||
{ | |||||
public partial class CalculationControl : UserControl | |||||
{ | |||||
public CalculationControl(string text, float value) | |||||
{ | |||||
InitializeComponent(); | |||||
valueLabel.Text = text; | |||||
factorNum.Value = (decimal) value; | |||||
} | |||||
public string Value => valueLabel.Text; | |||||
public float Factor => (float) factorNum.Value; | |||||
} | |||||
} |
@@ -0,0 +1,120 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<root> | |||||
<!-- | |||||
Microsoft ResX Schema | |||||
Version 2.0 | |||||
The primary goals of this format is to allow a simple XML format | |||||
that is mostly human readable. The generation and parsing of the | |||||
various data types are done through the TypeConverter classes | |||||
associated with the data types. | |||||
Example: | |||||
... ado.net/XML headers & schema ... | |||||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||||
<resheader name="version">2.0</resheader> | |||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||||
</data> | |||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||||
<comment>This is a comment</comment> | |||||
</data> | |||||
There are any number of "resheader" rows that contain simple | |||||
name/value pairs. | |||||
Each data row contains a name, and value. The row also contains a | |||||
type or mimetype. Type corresponds to a .NET class that support | |||||
text/value conversion through the TypeConverter architecture. | |||||
Classes that don't support this are serialized and stored with the | |||||
mimetype set. | |||||
The mimetype is used for serialized objects, and tells the | |||||
ResXResourceReader how to depersist the object. This is currently not | |||||
extensible. For a given mimetype the value must be set accordingly: | |||||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||||
that the ResXResourceWriter will generate, however the reader can | |||||
read any of the formats listed below. | |||||
mimetype: application/x-microsoft.net.object.binary.base64 | |||||
value : The object must be serialized with | |||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |||||
: and then encoded with base64 encoding. | |||||
mimetype: application/x-microsoft.net.object.soap.base64 | |||||
value : The object must be serialized with | |||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||||
: and then encoded with base64 encoding. | |||||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||||
value : The object must be serialized into a byte array | |||||
: using a System.ComponentModel.TypeConverter | |||||
: and then encoded with base64 encoding. | |||||
--> | |||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |||||
<xsd:element name="root" msdata:IsDataSet="true"> | |||||
<xsd:complexType> | |||||
<xsd:choice maxOccurs="unbounded"> | |||||
<xsd:element name="metadata"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" use="required" type="xsd:string" /> | |||||
<xsd:attribute name="type" type="xsd:string" /> | |||||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||||
<xsd:attribute ref="xml:space" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="assembly"> | |||||
<xsd:complexType> | |||||
<xsd:attribute name="alias" type="xsd:string" /> | |||||
<xsd:attribute name="name" type="xsd:string" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="data"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||||
<xsd:attribute ref="xml:space" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="resheader"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
</xsd:choice> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
</xsd:schema> | |||||
<resheader name="resmimetype"> | |||||
<value>text/microsoft-resx</value> | |||||
</resheader> | |||||
<resheader name="version"> | |||||
<value>2.0</value> | |||||
</resheader> | |||||
<resheader name="reader"> | |||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | |||||
<resheader name="writer"> | |||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | |||||
</root> |
@@ -3,14 +3,14 @@ | |||||
partial class ConfigForm | partial class ConfigForm | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
/// 必需的设计器变量。 | |||||
/// Required designer variable. | |||||
/// </summary> | /// </summary> | ||||
private System.ComponentModel.IContainer components = null; | private System.ComponentModel.IContainer components = null; | ||||
/// <summary> | /// <summary> | ||||
/// 清理所有正在使用的资源。 | |||||
/// Clean up any resources being used. | |||||
/// </summary> | /// </summary> | ||||
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param> | |||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | |||||
protected override void Dispose(bool disposing) | protected override void Dispose(bool disposing) | ||||
{ | { | ||||
if (disposing && (components != null)) | if (disposing && (components != null)) | ||||
@@ -20,11 +20,11 @@ | |||||
base.Dispose(disposing); | base.Dispose(disposing); | ||||
} | } | ||||
#region Windows 窗体设计器生成的代码 | |||||
#region Windows Form Designer generated code | |||||
/// <summary> | /// <summary> | ||||
/// 设计器支持所需的方法 - 不要 | |||||
/// 使用代码编辑器修改此方法的内容。 | |||||
/// Required method for Designer support - do not modify | |||||
/// the contents of this method with the code editor. | |||||
/// </summary> | /// </summary> | ||||
private void InitializeComponent() | private void InitializeComponent() | ||||
{ | { | ||||
@@ -39,6 +39,7 @@ | |||||
this.PasswordTextBox = new System.Windows.Forms.TextBox(); | this.PasswordTextBox = new System.Windows.Forms.TextBox(); | ||||
this.EncryptionLabel = new System.Windows.Forms.Label(); | this.EncryptionLabel = new System.Windows.Forms.Label(); | ||||
this.EncryptionSelect = new System.Windows.Forms.ComboBox(); | this.EncryptionSelect = new System.Windows.Forms.ComboBox(); | ||||
this.OneTimeAuth = new System.Windows.Forms.CheckBox(); | |||||
this.panel2 = new System.Windows.Forms.Panel(); | this.panel2 = new System.Windows.Forms.Panel(); | ||||
this.OKButton = new System.Windows.Forms.Button(); | this.OKButton = new System.Windows.Forms.Button(); | ||||
this.MyCancelButton = new System.Windows.Forms.Button(); | this.MyCancelButton = new System.Windows.Forms.Button(); | ||||
@@ -81,37 +82,39 @@ | |||||
this.tableLayoutPanel1.Controls.Add(this.PasswordTextBox, 1, 2); | this.tableLayoutPanel1.Controls.Add(this.PasswordTextBox, 1, 2); | ||||
this.tableLayoutPanel1.Controls.Add(this.EncryptionLabel, 0, 3); | this.tableLayoutPanel1.Controls.Add(this.EncryptionLabel, 0, 3); | ||||
this.tableLayoutPanel1.Controls.Add(this.EncryptionSelect, 1, 3); | this.tableLayoutPanel1.Controls.Add(this.EncryptionSelect, 1, 3); | ||||
this.tableLayoutPanel1.Controls.Add(this.OneTimeAuth, 1, 6); | |||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(8, 21); | this.tableLayoutPanel1.Location = new System.Drawing.Point(8, 21); | ||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); | this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); | ||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | ||||
this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3); | this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(3); | ||||
this.tableLayoutPanel1.RowCount = 6; | |||||
this.tableLayoutPanel1.RowCount = 7; | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(238, 137); | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(249, 162); | |||||
this.tableLayoutPanel1.TabIndex = 0; | this.tableLayoutPanel1.TabIndex = 0; | ||||
// | // | ||||
// RemarksTextBox | // RemarksTextBox | ||||
// | // | ||||
this.RemarksTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | this.RemarksTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.RemarksTextBox.Location = new System.Drawing.Point(72, 111); | |||||
this.RemarksTextBox.Location = new System.Drawing.Point(83, 113); | |||||
this.RemarksTextBox.MaxLength = 32; | this.RemarksTextBox.MaxLength = 32; | ||||
this.RemarksTextBox.Name = "RemarksTextBox"; | this.RemarksTextBox.Name = "RemarksTextBox"; | ||||
this.RemarksTextBox.Size = new System.Drawing.Size(160, 20); | |||||
this.RemarksTextBox.TabIndex = 10; | |||||
this.RemarksTextBox.Size = new System.Drawing.Size(160, 21); | |||||
this.RemarksTextBox.TabIndex = 4; | |||||
this.RemarksTextBox.WordWrap = false; | this.RemarksTextBox.WordWrap = false; | ||||
// | // | ||||
// RemarksLabel | // RemarksLabel | ||||
// | // | ||||
this.RemarksLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | this.RemarksLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | ||||
this.RemarksLabel.AutoSize = true; | this.RemarksLabel.AutoSize = true; | ||||
this.RemarksLabel.Location = new System.Drawing.Point(17, 114); | |||||
this.RemarksLabel.Location = new System.Drawing.Point(30, 117); | |||||
this.RemarksLabel.Name = "RemarksLabel"; | this.RemarksLabel.Name = "RemarksLabel"; | ||||
this.RemarksLabel.Size = new System.Drawing.Size(49, 13); | |||||
this.RemarksLabel.Size = new System.Drawing.Size(47, 12); | |||||
this.RemarksLabel.TabIndex = 9; | this.RemarksLabel.TabIndex = 9; | ||||
this.RemarksLabel.Text = "Remarks"; | this.RemarksLabel.Text = "Remarks"; | ||||
// | // | ||||
@@ -119,9 +122,9 @@ | |||||
// | // | ||||
this.IPLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | this.IPLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | ||||
this.IPLabel.AutoSize = true; | this.IPLabel.AutoSize = true; | ||||
this.IPLabel.Location = new System.Drawing.Point(15, 9); | |||||
this.IPLabel.Location = new System.Drawing.Point(18, 10); | |||||
this.IPLabel.Name = "IPLabel"; | this.IPLabel.Name = "IPLabel"; | ||||
this.IPLabel.Size = new System.Drawing.Size(51, 13); | |||||
this.IPLabel.Size = new System.Drawing.Size(59, 12); | |||||
this.IPLabel.TabIndex = 0; | this.IPLabel.TabIndex = 0; | ||||
this.IPLabel.Text = "Server IP"; | this.IPLabel.Text = "Server IP"; | ||||
// | // | ||||
@@ -129,9 +132,9 @@ | |||||
// | // | ||||
this.ServerPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | this.ServerPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | ||||
this.ServerPortLabel.AutoSize = true; | this.ServerPortLabel.AutoSize = true; | ||||
this.ServerPortLabel.Location = new System.Drawing.Point(6, 35); | |||||
this.ServerPortLabel.Location = new System.Drawing.Point(6, 37); | |||||
this.ServerPortLabel.Name = "ServerPortLabel"; | this.ServerPortLabel.Name = "ServerPortLabel"; | ||||
this.ServerPortLabel.Size = new System.Drawing.Size(60, 13); | |||||
this.ServerPortLabel.Size = new System.Drawing.Size(71, 12); | |||||
this.ServerPortLabel.TabIndex = 1; | this.ServerPortLabel.TabIndex = 1; | ||||
this.ServerPortLabel.Text = "Server Port"; | this.ServerPortLabel.Text = "Server Port"; | ||||
// | // | ||||
@@ -139,40 +142,40 @@ | |||||
// | // | ||||
this.PasswordLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | this.PasswordLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | ||||
this.PasswordLabel.AutoSize = true; | this.PasswordLabel.AutoSize = true; | ||||
this.PasswordLabel.Location = new System.Drawing.Point(13, 61); | |||||
this.PasswordLabel.Location = new System.Drawing.Point(24, 64); | |||||
this.PasswordLabel.Name = "PasswordLabel"; | this.PasswordLabel.Name = "PasswordLabel"; | ||||
this.PasswordLabel.Size = new System.Drawing.Size(53, 13); | |||||
this.PasswordLabel.Size = new System.Drawing.Size(53, 12); | |||||
this.PasswordLabel.TabIndex = 2; | this.PasswordLabel.TabIndex = 2; | ||||
this.PasswordLabel.Text = "Password"; | this.PasswordLabel.Text = "Password"; | ||||
// | // | ||||
// IPTextBox | // IPTextBox | ||||
// | // | ||||
this.IPTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | this.IPTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.IPTextBox.Location = new System.Drawing.Point(72, 6); | |||||
this.IPTextBox.Location = new System.Drawing.Point(83, 6); | |||||
this.IPTextBox.MaxLength = 512; | this.IPTextBox.MaxLength = 512; | ||||
this.IPTextBox.Name = "IPTextBox"; | this.IPTextBox.Name = "IPTextBox"; | ||||
this.IPTextBox.Size = new System.Drawing.Size(160, 20); | |||||
this.IPTextBox.Size = new System.Drawing.Size(160, 21); | |||||
this.IPTextBox.TabIndex = 0; | this.IPTextBox.TabIndex = 0; | ||||
this.IPTextBox.WordWrap = false; | this.IPTextBox.WordWrap = false; | ||||
// | // | ||||
// ServerPortTextBox | // ServerPortTextBox | ||||
// | // | ||||
this.ServerPortTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | this.ServerPortTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.ServerPortTextBox.Location = new System.Drawing.Point(72, 32); | |||||
this.ServerPortTextBox.Location = new System.Drawing.Point(83, 33); | |||||
this.ServerPortTextBox.MaxLength = 10; | this.ServerPortTextBox.MaxLength = 10; | ||||
this.ServerPortTextBox.Name = "ServerPortTextBox"; | this.ServerPortTextBox.Name = "ServerPortTextBox"; | ||||
this.ServerPortTextBox.Size = new System.Drawing.Size(160, 20); | |||||
this.ServerPortTextBox.Size = new System.Drawing.Size(160, 21); | |||||
this.ServerPortTextBox.TabIndex = 1; | this.ServerPortTextBox.TabIndex = 1; | ||||
this.ServerPortTextBox.WordWrap = false; | this.ServerPortTextBox.WordWrap = false; | ||||
// | // | ||||
// PasswordTextBox | // PasswordTextBox | ||||
// | // | ||||
this.PasswordTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | this.PasswordTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right))); | ||||
this.PasswordTextBox.Location = new System.Drawing.Point(72, 58); | |||||
this.PasswordTextBox.Location = new System.Drawing.Point(83, 60); | |||||
this.PasswordTextBox.MaxLength = 256; | this.PasswordTextBox.MaxLength = 256; | ||||
this.PasswordTextBox.Name = "PasswordTextBox"; | this.PasswordTextBox.Name = "PasswordTextBox"; | ||||
this.PasswordTextBox.PasswordChar = '*'; | this.PasswordTextBox.PasswordChar = '*'; | ||||
this.PasswordTextBox.Size = new System.Drawing.Size(160, 20); | |||||
this.PasswordTextBox.Size = new System.Drawing.Size(160, 21); | |||||
this.PasswordTextBox.TabIndex = 2; | this.PasswordTextBox.TabIndex = 2; | ||||
this.PasswordTextBox.WordWrap = false; | this.PasswordTextBox.WordWrap = false; | ||||
// | // | ||||
@@ -180,33 +183,43 @@ | |||||
// | // | ||||
this.EncryptionLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | this.EncryptionLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | ||||
this.EncryptionLabel.AutoSize = true; | this.EncryptionLabel.AutoSize = true; | ||||
this.EncryptionLabel.Location = new System.Drawing.Point(9, 88); | |||||
this.EncryptionLabel.Location = new System.Drawing.Point(12, 91); | |||||
this.EncryptionLabel.Name = "EncryptionLabel"; | this.EncryptionLabel.Name = "EncryptionLabel"; | ||||
this.EncryptionLabel.Size = new System.Drawing.Size(57, 13); | |||||
this.EncryptionLabel.Size = new System.Drawing.Size(65, 12); | |||||
this.EncryptionLabel.TabIndex = 8; | this.EncryptionLabel.TabIndex = 8; | ||||
this.EncryptionLabel.Text = "Encryption"; | this.EncryptionLabel.Text = "Encryption"; | ||||
// | // | ||||
// 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; | ||||
this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl; | this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl; | ||||
this.EncryptionSelect.ItemHeight = 13; | |||||
this.EncryptionSelect.ItemHeight = 12; | |||||
this.EncryptionSelect.Items.AddRange(new object[] { | this.EncryptionSelect.Items.AddRange(new object[] { | ||||
"table", | |||||
"rc4-md5", | "rc4-md5", | ||||
"salsa20", | "salsa20", | ||||
"chacha20", | "chacha20", | ||||
"chacha20-ietf", | |||||
"aes-256-cfb", | "aes-256-cfb", | ||||
"aes-192-cfb", | "aes-192-cfb", | ||||
"aes-128-cfb", | |||||
"rc4"}); | |||||
this.EncryptionSelect.Location = new System.Drawing.Point(72, 84); | |||||
"aes-128-cfb"}); | |||||
this.EncryptionSelect.Location = new System.Drawing.Point(83, 87); | |||||
this.EncryptionSelect.Name = "EncryptionSelect"; | this.EncryptionSelect.Name = "EncryptionSelect"; | ||||
this.EncryptionSelect.Size = new System.Drawing.Size(160, 21); | |||||
this.EncryptionSelect.Size = new System.Drawing.Size(160, 20); | |||||
this.EncryptionSelect.TabIndex = 3; | this.EncryptionSelect.TabIndex = 3; | ||||
this.EncryptionSelect.SelectedIndexChanged += new System.EventHandler(this.EncryptionSelect_SelectedIndexChanged); | |||||
// | |||||
// OneTimeAuth | |||||
// | |||||
this.OneTimeAuth.AutoSize = true; | |||||
this.OneTimeAuth.Location = new System.Drawing.Point(83, 140); | |||||
this.OneTimeAuth.Name = "OneTimeAuth"; | |||||
this.OneTimeAuth.Size = new System.Drawing.Size(156, 16); | |||||
this.OneTimeAuth.TabIndex = 5; | |||||
this.OneTimeAuth.Text = "Onetime Authentication"; | |||||
this.OneTimeAuth.UseVisualStyleBackColor = true; | |||||
// | // | ||||
// panel2 | // panel2 | ||||
// | // | ||||
@@ -226,7 +239,7 @@ | |||||
this.OKButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); | this.OKButton.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); | ||||
this.OKButton.Name = "OKButton"; | this.OKButton.Name = "OKButton"; | ||||
this.OKButton.Size = new System.Drawing.Size(75, 23); | this.OKButton.Size = new System.Drawing.Size(75, 23); | ||||
this.OKButton.TabIndex = 8; | |||||
this.OKButton.TabIndex = 12; | |||||
this.OKButton.Text = "OK"; | this.OKButton.Text = "OK"; | ||||
this.OKButton.UseVisualStyleBackColor = true; | this.OKButton.UseVisualStyleBackColor = true; | ||||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | ||||
@@ -239,7 +252,7 @@ | |||||
this.MyCancelButton.Margin = new System.Windows.Forms.Padding(3, 3, 0, 0); | this.MyCancelButton.Margin = new System.Windows.Forms.Padding(3, 3, 0, 0); | ||||
this.MyCancelButton.Name = "MyCancelButton"; | this.MyCancelButton.Name = "MyCancelButton"; | ||||
this.MyCancelButton.Size = new System.Drawing.Size(75, 23); | this.MyCancelButton.Size = new System.Drawing.Size(75, 23); | ||||
this.MyCancelButton.TabIndex = 9; | |||||
this.MyCancelButton.TabIndex = 13; | |||||
this.MyCancelButton.Text = "Cancel"; | this.MyCancelButton.Text = "Cancel"; | ||||
this.MyCancelButton.UseVisualStyleBackColor = true; | this.MyCancelButton.UseVisualStyleBackColor = true; | ||||
this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click); | this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click); | ||||
@@ -251,7 +264,7 @@ | |||||
this.DeleteButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3); | this.DeleteButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3); | ||||
this.DeleteButton.Name = "DeleteButton"; | this.DeleteButton.Name = "DeleteButton"; | ||||
this.DeleteButton.Size = new System.Drawing.Size(80, 23); | this.DeleteButton.Size = new System.Drawing.Size(80, 23); | ||||
this.DeleteButton.TabIndex = 7; | |||||
this.DeleteButton.TabIndex = 9; | |||||
this.DeleteButton.Text = "&Delete"; | this.DeleteButton.Text = "&Delete"; | ||||
this.DeleteButton.UseVisualStyleBackColor = true; | this.DeleteButton.UseVisualStyleBackColor = true; | ||||
this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click); | this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click); | ||||
@@ -263,7 +276,7 @@ | |||||
this.AddButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); | this.AddButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); | ||||
this.AddButton.Name = "AddButton"; | this.AddButton.Name = "AddButton"; | ||||
this.AddButton.Size = new System.Drawing.Size(80, 23); | this.AddButton.Size = new System.Drawing.Size(80, 23); | ||||
this.AddButton.TabIndex = 6; | |||||
this.AddButton.TabIndex = 8; | |||||
this.AddButton.Text = "&Add"; | this.AddButton.Text = "&Add"; | ||||
this.AddButton.UseVisualStyleBackColor = true; | this.AddButton.UseVisualStyleBackColor = true; | ||||
this.AddButton.Click += new System.EventHandler(this.AddButton_Click); | this.AddButton.Click += new System.EventHandler(this.AddButton_Click); | ||||
@@ -276,8 +289,8 @@ | |||||
this.ServerGroupBox.Location = new System.Drawing.Point(178, 0); | this.ServerGroupBox.Location = new System.Drawing.Point(178, 0); | ||||
this.ServerGroupBox.Margin = new System.Windows.Forms.Padding(12, 0, 0, 0); | this.ServerGroupBox.Margin = new System.Windows.Forms.Padding(12, 0, 0, 0); | ||||
this.ServerGroupBox.Name = "ServerGroupBox"; | this.ServerGroupBox.Name = "ServerGroupBox"; | ||||
this.ServerGroupBox.Size = new System.Drawing.Size(249, 174); | |||||
this.ServerGroupBox.TabIndex = 6; | |||||
this.ServerGroupBox.Size = new System.Drawing.Size(260, 200); | |||||
this.ServerGroupBox.TabIndex = 0; | |||||
this.ServerGroupBox.TabStop = false; | this.ServerGroupBox.TabStop = false; | ||||
this.ServerGroupBox.Text = "Server"; | this.ServerGroupBox.Text = "Server"; | ||||
// | // | ||||
@@ -290,7 +303,7 @@ | |||||
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"; | ||||
this.ServersListBox.Size = new System.Drawing.Size(166, 148); | this.ServersListBox.Size = new System.Drawing.Size(166, 148); | ||||
this.ServersListBox.TabIndex = 5; | |||||
this.ServersListBox.TabIndex = 7; | |||||
this.ServersListBox.SelectedIndexChanged += new System.EventHandler(this.ServersListBox_SelectedIndexChanged); | this.ServersListBox.SelectedIndexChanged += new System.EventHandler(this.ServersListBox_SelectedIndexChanged); | ||||
// | // | ||||
// tableLayoutPanel2 | // tableLayoutPanel2 | ||||
@@ -313,7 +326,7 @@ | |||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(427, 238); | |||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(438, 265); | |||||
this.tableLayoutPanel2.TabIndex = 7; | this.tableLayoutPanel2.TabIndex = 7; | ||||
// | // | ||||
// tableLayoutPanel6 | // tableLayoutPanel6 | ||||
@@ -326,7 +339,7 @@ | |||||
this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0); | this.tableLayoutPanel6.Controls.Add(this.MoveDownButton, 1, 0); | ||||
this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0); | this.tableLayoutPanel6.Controls.Add(this.MoveUpButton, 0, 0); | ||||
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top; | this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Top; | ||||
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 211); | |||||
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 233); | |||||
this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); | this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0); | ||||
this.tableLayoutPanel6.Name = "tableLayoutPanel6"; | this.tableLayoutPanel6.Name = "tableLayoutPanel6"; | ||||
this.tableLayoutPanel6.RowCount = 1; | this.tableLayoutPanel6.RowCount = 1; | ||||
@@ -341,7 +354,7 @@ | |||||
this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3); | this.MoveDownButton.Margin = new System.Windows.Forms.Padding(3, 6, 0, 3); | ||||
this.MoveDownButton.Name = "MoveDownButton"; | this.MoveDownButton.Name = "MoveDownButton"; | ||||
this.MoveDownButton.Size = new System.Drawing.Size(80, 23); | this.MoveDownButton.Size = new System.Drawing.Size(80, 23); | ||||
this.MoveDownButton.TabIndex = 7; | |||||
this.MoveDownButton.TabIndex = 11; | |||||
this.MoveDownButton.Text = "Move D&own"; | this.MoveDownButton.Text = "Move D&own"; | ||||
this.MoveDownButton.UseVisualStyleBackColor = true; | this.MoveDownButton.UseVisualStyleBackColor = true; | ||||
this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click); | this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click); | ||||
@@ -353,7 +366,7 @@ | |||||
this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); | this.MoveUpButton.Margin = new System.Windows.Forms.Padding(0, 6, 3, 3); | ||||
this.MoveUpButton.Name = "MoveUpButton"; | this.MoveUpButton.Name = "MoveUpButton"; | ||||
this.MoveUpButton.Size = new System.Drawing.Size(80, 23); | this.MoveUpButton.Size = new System.Drawing.Size(80, 23); | ||||
this.MoveUpButton.TabIndex = 6; | |||||
this.MoveUpButton.TabIndex = 10; | |||||
this.MoveUpButton.Text = "Move &Up"; | this.MoveUpButton.Text = "Move &Up"; | ||||
this.MoveUpButton.UseVisualStyleBackColor = true; | this.MoveUpButton.UseVisualStyleBackColor = true; | ||||
this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click); | this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click); | ||||
@@ -369,36 +382,36 @@ | |||||
this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | this.tableLayoutPanel5.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | ||||
this.tableLayoutPanel5.Controls.Add(this.ProxyPortTextBox, 1, 0); | this.tableLayoutPanel5.Controls.Add(this.ProxyPortTextBox, 1, 0); | ||||
this.tableLayoutPanel5.Controls.Add(this.ProxyPortLabel, 0, 0); | this.tableLayoutPanel5.Controls.Add(this.ProxyPortLabel, 0, 0); | ||||
this.tableLayoutPanel5.Location = new System.Drawing.Point(241, 174); | |||||
this.tableLayoutPanel5.Location = new System.Drawing.Point(242, 200); | |||||
this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); | this.tableLayoutPanel5.Margin = new System.Windows.Forms.Padding(0); | ||||
this.tableLayoutPanel5.Name = "tableLayoutPanel5"; | this.tableLayoutPanel5.Name = "tableLayoutPanel5"; | ||||
this.tableLayoutPanel5.Padding = new System.Windows.Forms.Padding(3); | this.tableLayoutPanel5.Padding = new System.Windows.Forms.Padding(3); | ||||
this.tableLayoutPanel5.RowCount = 1; | this.tableLayoutPanel5.RowCount = 1; | ||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26F)); | |||||
this.tableLayoutPanel5.Size = new System.Drawing.Size(186, 32); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F)); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F)); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F)); | |||||
this.tableLayoutPanel5.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F)); | |||||
this.tableLayoutPanel5.Size = new System.Drawing.Size(196, 33); | |||||
this.tableLayoutPanel5.TabIndex = 9; | this.tableLayoutPanel5.TabIndex = 9; | ||||
// | // | ||||
// ProxyPortTextBox | // ProxyPortTextBox | ||||
// | // | ||||
this.ProxyPortTextBox.Anchor = System.Windows.Forms.AnchorStyles.Left; | this.ProxyPortTextBox.Anchor = System.Windows.Forms.AnchorStyles.Left; | ||||
this.ProxyPortTextBox.Location = new System.Drawing.Point(67, 6); | |||||
this.ProxyPortTextBox.Location = new System.Drawing.Point(77, 6); | |||||
this.ProxyPortTextBox.MaxLength = 10; | this.ProxyPortTextBox.MaxLength = 10; | ||||
this.ProxyPortTextBox.Name = "ProxyPortTextBox"; | this.ProxyPortTextBox.Name = "ProxyPortTextBox"; | ||||
this.ProxyPortTextBox.Size = new System.Drawing.Size(113, 20); | |||||
this.ProxyPortTextBox.TabIndex = 4; | |||||
this.ProxyPortTextBox.Size = new System.Drawing.Size(113, 21); | |||||
this.ProxyPortTextBox.TabIndex = 6; | |||||
this.ProxyPortTextBox.WordWrap = false; | this.ProxyPortTextBox.WordWrap = false; | ||||
// | // | ||||
// ProxyPortLabel | // ProxyPortLabel | ||||
// | // | ||||
this.ProxyPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | this.ProxyPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | ||||
this.ProxyPortLabel.AutoSize = true; | this.ProxyPortLabel.AutoSize = true; | ||||
this.ProxyPortLabel.Location = new System.Drawing.Point(6, 9); | |||||
this.ProxyPortLabel.Location = new System.Drawing.Point(6, 10); | |||||
this.ProxyPortLabel.Name = "ProxyPortLabel"; | this.ProxyPortLabel.Name = "ProxyPortLabel"; | ||||
this.ProxyPortLabel.Size = new System.Drawing.Size(55, 13); | |||||
this.ProxyPortLabel.Size = new System.Drawing.Size(65, 12); | |||||
this.ProxyPortLabel.TabIndex = 3; | this.ProxyPortLabel.TabIndex = 3; | ||||
this.ProxyPortLabel.Text = "Proxy Port"; | this.ProxyPortLabel.Text = "Proxy Port"; | ||||
// | // | ||||
@@ -413,7 +426,7 @@ | |||||
this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0); | this.tableLayoutPanel3.Controls.Add(this.MyCancelButton, 1, 0); | ||||
this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0); | this.tableLayoutPanel3.Controls.Add(this.OKButton, 0, 0); | ||||
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right; | this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Right; | ||||
this.tableLayoutPanel3.Location = new System.Drawing.Point(268, 209); | |||||
this.tableLayoutPanel3.Location = new System.Drawing.Point(279, 236); | |||||
this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); | this.tableLayoutPanel3.Margin = new System.Windows.Forms.Padding(3, 3, 0, 3); | ||||
this.tableLayoutPanel3.Name = "tableLayoutPanel3"; | this.tableLayoutPanel3.Name = "tableLayoutPanel3"; | ||||
this.tableLayoutPanel3.RowCount = 1; | this.tableLayoutPanel3.RowCount = 1; | ||||
@@ -431,7 +444,7 @@ | |||||
this.tableLayoutPanel4.Controls.Add(this.DeleteButton, 1, 0); | this.tableLayoutPanel4.Controls.Add(this.DeleteButton, 1, 0); | ||||
this.tableLayoutPanel4.Controls.Add(this.AddButton, 0, 0); | this.tableLayoutPanel4.Controls.Add(this.AddButton, 0, 0); | ||||
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Top; | this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Top; | ||||
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 174); | |||||
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 200); | |||||
this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); | this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(0); | ||||
this.tableLayoutPanel4.Name = "tableLayoutPanel4"; | this.tableLayoutPanel4.Name = "tableLayoutPanel4"; | ||||
this.tableLayoutPanel4.RowCount = 1; | this.tableLayoutPanel4.RowCount = 1; | ||||
@@ -505,6 +518,7 @@ | |||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6; | ||||
private System.Windows.Forms.Button MoveDownButton; | private System.Windows.Forms.Button MoveDownButton; | ||||
private System.Windows.Forms.Button MoveUpButton; | private System.Windows.Forms.Button MoveUpButton; | ||||
private System.Windows.Forms.CheckBox OneTimeAuth; | |||||
} | } | ||||
} | } | ||||
@@ -48,6 +48,7 @@ namespace Shadowsocks.View | |||||
EncryptionLabel.Text = I18N.GetString("Encryption"); | EncryptionLabel.Text = I18N.GetString("Encryption"); | ||||
ProxyPortLabel.Text = I18N.GetString("Proxy Port"); | ProxyPortLabel.Text = I18N.GetString("Proxy Port"); | ||||
RemarksLabel.Text = I18N.GetString("Remarks"); | RemarksLabel.Text = I18N.GetString("Remarks"); | ||||
OneTimeAuth.Text = I18N.GetString("Onetime Authentication (Experimental)"); | |||||
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"); | ||||
@@ -78,11 +79,12 @@ namespace Shadowsocks.View | |||||
} | } | ||||
Server server = new Server | Server server = new Server | ||||
{ | { | ||||
server = IPTextBox.Text, | |||||
server = IPTextBox.Text.Trim(), | |||||
server_port = int.Parse(ServerPortTextBox.Text), | server_port = int.Parse(ServerPortTextBox.Text), | ||||
password = PasswordTextBox.Text, | password = PasswordTextBox.Text, | ||||
method = EncryptionSelect.Text, | method = EncryptionSelect.Text, | ||||
remarks = RemarksTextBox.Text | |||||
remarks = RemarksTextBox.Text, | |||||
auth = OneTimeAuth.Checked | |||||
}; | }; | ||||
int localPort = int.Parse(ProxyPortTextBox.Text); | int localPort = int.Parse(ProxyPortTextBox.Text); | ||||
Configuration.CheckServer(server); | Configuration.CheckServer(server); | ||||
@@ -115,6 +117,7 @@ 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; | ||||
OneTimeAuth.Checked = server.auth; | |||||
} | } | ||||
} | } | ||||
@@ -146,6 +149,28 @@ namespace Shadowsocks.View | |||||
} | } | ||||
private void ConfigForm_KeyDown(object sender, KeyEventArgs e) | |||||
{ | |||||
// Sometimes the users may hit enter key by mistake, and the form will close without saving entries. | |||||
if (e.KeyCode == Keys.Enter) | |||||
{ | |||||
Server server = controller.GetCurrentServer(); | |||||
if (!SaveOldSelectedServer()) | |||||
{ | |||||
return; | |||||
} | |||||
if (_modifiedConfiguration.configs.Count == 0) | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Please add at least one server")); | |||||
return; | |||||
} | |||||
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort); | |||||
controller.SelectServerIndex(_modifiedConfiguration.configs.IndexOf(server)); | |||||
} | |||||
} | |||||
private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) | private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) | ||||
{ | { | ||||
if (!ServersListBox.CanSelect) | if (!ServersListBox.CanSelect) | ||||
@@ -163,7 +188,10 @@ namespace Shadowsocks.View | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | ServersListBox.SelectedIndex = _lastSelectedIndex; | ||||
return; | return; | ||||
} | } | ||||
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName(); | |||||
if (_lastSelectedIndex >= 0) | |||||
{ | |||||
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName(); | |||||
} | |||||
UpdateMoveUpAndDownButton(); | UpdateMoveUpAndDownButton(); | ||||
LoadSelectedServer(); | LoadSelectedServer(); | ||||
_lastSelectedIndex = ServersListBox.SelectedIndex; | _lastSelectedIndex = ServersListBox.SelectedIndex; | ||||
@@ -297,5 +325,18 @@ namespace Shadowsocks.View | |||||
MoveConfigItem(+1); // +1 means move forward | MoveConfigItem(+1); // +1 means move forward | ||||
} | } | ||||
} | } | ||||
private void EncryptionSelect_SelectedIndexChanged(object sender, EventArgs e) | |||||
{ | |||||
if (EncryptionSelect.Text == "rc4" || EncryptionSelect.Text == "table") | |||||
{ | |||||
OneTimeAuth.Enabled = false; | |||||
OneTimeAuth.Checked = false; | |||||
} | |||||
else | |||||
{ | |||||
OneTimeAuth.Enabled = true; | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -30,19 +30,25 @@ | |||||
{ | { | ||||
this.components = new System.ComponentModel.Container(); | this.components = new System.ComponentModel.Container(); | ||||
this.LogMessageTextBox = 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.MainMenu = new System.Windows.Forms.MainMenu(this.components); | |||||
this.FileMenuItem = new System.Windows.Forms.MenuItem(); | this.FileMenuItem = new System.Windows.Forms.MenuItem(); | ||||
this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem(); | this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem(); | ||||
this.ExitMenuItem = new System.Windows.Forms.MenuItem(); | this.ExitMenuItem = new System.Windows.Forms.MenuItem(); | ||||
this.panel1 = new System.Windows.Forms.Panel(); | |||||
this.ViewMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.CleanLogsMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.ChangeFontMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.WrapTextMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.TopMostMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.MenuItemSeparater = new System.Windows.Forms.MenuItem(); | |||||
this.ShowToolbarMenuItem = new System.Windows.Forms.MenuItem(); | |||||
this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.ChangeFontButton = new System.Windows.Forms.Button(); | this.ChangeFontButton = new System.Windows.Forms.Button(); | ||||
this.CleanLogsButton = new System.Windows.Forms.Button(); | this.CleanLogsButton = new System.Windows.Forms.Button(); | ||||
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); | this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); | ||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | ||||
this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.panel1.SuspendLayout(); | |||||
this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); | |||||
this.tableLayoutPanel1.SuspendLayout(); | this.tableLayoutPanel1.SuspendLayout(); | ||||
this.ToolbarFlowLayoutPanel.SuspendLayout(); | |||||
this.SuspendLayout(); | this.SuspendLayout(); | ||||
// | // | ||||
// LogMessageTextBox | // LogMessageTextBox | ||||
@@ -51,25 +57,20 @@ | |||||
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | 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.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.ForeColor = System.Drawing.Color.White; | ||||
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 43); | |||||
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 40); | |||||
this.LogMessageTextBox.MaxLength = 2147483647; | this.LogMessageTextBox.MaxLength = 2147483647; | ||||
this.LogMessageTextBox.Multiline = true; | this.LogMessageTextBox.Multiline = true; | ||||
this.LogMessageTextBox.Name = "LogMessageTextBox"; | this.LogMessageTextBox.Name = "LogMessageTextBox"; | ||||
this.LogMessageTextBox.ReadOnly = true; | this.LogMessageTextBox.ReadOnly = true; | ||||
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; | this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; | ||||
this.LogMessageTextBox.Size = new System.Drawing.Size(541, 307); | |||||
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131); | |||||
this.LogMessageTextBox.TabIndex = 0; | this.LogMessageTextBox.TabIndex = 0; | ||||
this.LogMessageTextBox.WordWrap = false; | |||||
// | |||||
// contextMenuStrip1 | |||||
// | |||||
this.contextMenuStrip1.Name = "contextMenuStrip1"; | |||||
this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4); | |||||
// | // | ||||
// mainMenu1 | |||||
// MainMenu | |||||
// | // | ||||
this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||||
this.FileMenuItem}); | |||||
this.MainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||||
this.FileMenuItem, | |||||
this.ViewMenuItem}); | |||||
// | // | ||||
// FileMenuItem | // FileMenuItem | ||||
// | // | ||||
@@ -91,23 +92,72 @@ | |||||
this.ExitMenuItem.Text = "E&xit"; | this.ExitMenuItem.Text = "E&xit"; | ||||
this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click); | this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click); | ||||
// | // | ||||
// panel1 | |||||
// ViewMenuItem | |||||
// | |||||
this.ViewMenuItem.Index = 1; | |||||
this.ViewMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { | |||||
this.CleanLogsMenuItem, | |||||
this.ChangeFontMenuItem, | |||||
this.WrapTextMenuItem, | |||||
this.TopMostMenuItem, | |||||
this.MenuItemSeparater, | |||||
this.ShowToolbarMenuItem}); | |||||
this.ViewMenuItem.Text = "&View"; | |||||
// | |||||
// CleanLogsMenuItem | |||||
// | |||||
this.CleanLogsMenuItem.Index = 0; | |||||
this.CleanLogsMenuItem.Text = "&Clean Logs"; | |||||
this.CleanLogsMenuItem.Click += new System.EventHandler(this.CleanLogsMenuItem_Click); | |||||
// | |||||
// ChangeFontMenuItem | |||||
// | |||||
this.ChangeFontMenuItem.Index = 1; | |||||
this.ChangeFontMenuItem.Text = "Change &Font"; | |||||
this.ChangeFontMenuItem.Click += new System.EventHandler(this.ChangeFontMenuItem_Click); | |||||
// | |||||
// WrapTextMenuItem | |||||
// | |||||
this.WrapTextMenuItem.Index = 2; | |||||
this.WrapTextMenuItem.Text = "&Wrap Text"; | |||||
this.WrapTextMenuItem.Click += new System.EventHandler(this.WrapTextMenuItem_Click); | |||||
// | |||||
// TopMostMenuItem | |||||
// | |||||
this.TopMostMenuItem.Index = 3; | |||||
this.TopMostMenuItem.Text = "&Top Most"; | |||||
this.TopMostMenuItem.Click += new System.EventHandler(this.TopMostMenuItem_Click); | |||||
// | |||||
// MenuItemSeparater | |||||
// | |||||
this.MenuItemSeparater.Index = 4; | |||||
this.MenuItemSeparater.Text = "-"; | |||||
// | |||||
// ShowToolbarMenuItem | |||||
// | // | ||||
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; | |||||
this.ShowToolbarMenuItem.Index = 5; | |||||
this.ShowToolbarMenuItem.Text = "&Show Toolbar"; | |||||
this.ShowToolbarMenuItem.Click += new System.EventHandler(this.ShowToolbarMenuItem_Click); | |||||
// | |||||
// TopMostCheckBox | |||||
// | |||||
this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | |||||
| System.Windows.Forms.AnchorStyles.Left))); | |||||
this.TopMostCheckBox.AutoSize = true; | |||||
this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3); | |||||
this.TopMostCheckBox.Name = "TopMostCheckBox"; | |||||
this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25); | |||||
this.TopMostCheckBox.TabIndex = 3; | |||||
this.TopMostCheckBox.Text = "&Top Most"; | |||||
this.TopMostCheckBox.UseVisualStyleBackColor = true; | |||||
this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged); | |||||
// | // | ||||
// ChangeFontButton | // ChangeFontButton | ||||
// | // | ||||
this.ChangeFontButton.Location = new System.Drawing.Point(107, 4); | |||||
this.ChangeFontButton.AutoSize = true; | |||||
this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); | |||||
this.ChangeFontButton.Name = "ChangeFontButton"; | this.ChangeFontButton.Name = "ChangeFontButton"; | ||||
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); | |||||
this.ChangeFontButton.Size = new System.Drawing.Size(75, 25); | |||||
this.ChangeFontButton.TabIndex = 2; | this.ChangeFontButton.TabIndex = 2; | ||||
this.ChangeFontButton.Text = "&Font"; | this.ChangeFontButton.Text = "&Font"; | ||||
this.ChangeFontButton.UseVisualStyleBackColor = true; | this.ChangeFontButton.UseVisualStyleBackColor = true; | ||||
@@ -115,22 +165,25 @@ | |||||
// | // | ||||
// CleanLogsButton | // CleanLogsButton | ||||
// | // | ||||
this.CleanLogsButton.Location = new System.Drawing.Point(9, 4); | |||||
this.CleanLogsButton.AutoSize = true; | |||||
this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); | |||||
this.CleanLogsButton.Name = "CleanLogsButton"; | this.CleanLogsButton.Name = "CleanLogsButton"; | ||||
this.CleanLogsButton.Size = new System.Drawing.Size(75, 23); | |||||
this.CleanLogsButton.Size = new System.Drawing.Size(75, 25); | |||||
this.CleanLogsButton.TabIndex = 1; | this.CleanLogsButton.TabIndex = 1; | ||||
this.CleanLogsButton.Text = "&Clean logs"; | |||||
this.CleanLogsButton.Text = "&Clean Logs"; | |||||
this.CleanLogsButton.UseVisualStyleBackColor = true; | this.CleanLogsButton.UseVisualStyleBackColor = true; | ||||
this.CleanLogsButton.Click += new System.EventHandler(this.CleanLogsButton_Click); | this.CleanLogsButton.Click += new System.EventHandler(this.CleanLogsButton_Click); | ||||
// | // | ||||
// WrapTextCheckBox | // WrapTextCheckBox | ||||
// | // | ||||
this.WrapTextCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | |||||
| System.Windows.Forms.AnchorStyles.Left))); | |||||
this.WrapTextCheckBox.AutoSize = true; | this.WrapTextCheckBox.AutoSize = true; | ||||
this.WrapTextCheckBox.Location = new System.Drawing.Point(209, 9); | |||||
this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); | |||||
this.WrapTextCheckBox.Name = "WrapTextCheckBox"; | this.WrapTextCheckBox.Name = "WrapTextCheckBox"; | ||||
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 16); | |||||
this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25); | |||||
this.WrapTextCheckBox.TabIndex = 0; | this.WrapTextCheckBox.TabIndex = 0; | ||||
this.WrapTextCheckBox.Text = "&Wrap text"; | |||||
this.WrapTextCheckBox.Text = "&Wrap Text"; | |||||
this.WrapTextCheckBox.UseVisualStyleBackColor = true; | this.WrapTextCheckBox.UseVisualStyleBackColor = true; | ||||
this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged); | this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged); | ||||
// | // | ||||
@@ -138,45 +191,48 @@ | |||||
// | // | ||||
this.tableLayoutPanel1.ColumnCount = 1; | this.tableLayoutPanel1.ColumnCount = 1; | ||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); | 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.Controls.Add(this.LogMessageTextBox, 0, 1); | ||||
this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0); | |||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | ||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | ||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | ||||
this.tableLayoutPanel1.RowCount = 2; | 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.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 353); | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174); | |||||
this.tableLayoutPanel1.TabIndex = 2; | this.tableLayoutPanel1.TabIndex = 2; | ||||
// | // | ||||
// TopMostCheckBox | |||||
// ToolbarFlowLayoutPanel | |||||
// | // | ||||
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); | |||||
this.ToolbarFlowLayoutPanel.AutoSize = true; | |||||
this.ToolbarFlowLayoutPanel.Controls.Add(this.CleanLogsButton); | |||||
this.ToolbarFlowLayoutPanel.Controls.Add(this.ChangeFontButton); | |||||
this.ToolbarFlowLayoutPanel.Controls.Add(this.WrapTextCheckBox); | |||||
this.ToolbarFlowLayoutPanel.Controls.Add(this.TopMostCheckBox); | |||||
this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); | |||||
this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; | |||||
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 31); | |||||
this.ToolbarFlowLayoutPanel.TabIndex = 2; | |||||
// | // | ||||
// LogForm | // LogForm | ||||
// | // | ||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||
this.ClientSize = new System.Drawing.Size(547, 353); | |||||
this.ClientSize = new System.Drawing.Size(384, 174); | |||||
this.Controls.Add(this.tableLayoutPanel1); | this.Controls.Add(this.tableLayoutPanel1); | ||||
this.Menu = this.mainMenu1; | |||||
this.Menu = this.MainMenu; | |||||
this.MinimumSize = new System.Drawing.Size(400, 213); | |||||
this.Name = "LogForm"; | this.Name = "LogForm"; | ||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | ||||
this.Text = "Log Viewer"; | this.Text = "Log Viewer"; | ||||
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.ResumeLayout(false); | ||||
this.tableLayoutPanel1.PerformLayout(); | this.tableLayoutPanel1.PerformLayout(); | ||||
this.ToolbarFlowLayoutPanel.ResumeLayout(false); | |||||
this.ToolbarFlowLayoutPanel.PerformLayout(); | |||||
this.ResumeLayout(false); | this.ResumeLayout(false); | ||||
} | } | ||||
@@ -184,16 +240,22 @@ | |||||
#endregion | #endregion | ||||
private System.Windows.Forms.TextBox LogMessageTextBox; | private System.Windows.Forms.TextBox LogMessageTextBox; | ||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; | |||||
private System.Windows.Forms.MainMenu mainMenu1; | |||||
private System.Windows.Forms.MainMenu MainMenu; | |||||
private System.Windows.Forms.MenuItem FileMenuItem; | private System.Windows.Forms.MenuItem FileMenuItem; | ||||
private System.Windows.Forms.MenuItem OpenLocationMenuItem; | private System.Windows.Forms.MenuItem OpenLocationMenuItem; | ||||
private System.Windows.Forms.MenuItem ExitMenuItem; | private System.Windows.Forms.MenuItem ExitMenuItem; | ||||
private System.Windows.Forms.Panel panel1; | |||||
private System.Windows.Forms.CheckBox WrapTextCheckBox; | private System.Windows.Forms.CheckBox WrapTextCheckBox; | ||||
private System.Windows.Forms.Button CleanLogsButton; | private System.Windows.Forms.Button CleanLogsButton; | ||||
private System.Windows.Forms.Button ChangeFontButton; | private System.Windows.Forms.Button ChangeFontButton; | ||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | ||||
private System.Windows.Forms.CheckBox TopMostCheckBox; | private System.Windows.Forms.CheckBox TopMostCheckBox; | ||||
private System.Windows.Forms.MenuItem ViewMenuItem; | |||||
private System.Windows.Forms.MenuItem CleanLogsMenuItem; | |||||
private System.Windows.Forms.MenuItem ChangeFontMenuItem; | |||||
private System.Windows.Forms.MenuItem WrapTextMenuItem; | |||||
private System.Windows.Forms.MenuItem TopMostMenuItem; | |||||
private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; | |||||
private System.Windows.Forms.MenuItem MenuItemSeparater; | |||||
private System.Windows.Forms.MenuItem ShowToolbarMenuItem; | |||||
} | } | ||||
} | } |
@@ -1,15 +1,13 @@ | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel; | |||||
using System.Data; | |||||
using System; | |||||
using System.Drawing; | using System.Drawing; | ||||
using System.IO; | using System.IO; | ||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.View | namespace Shadowsocks.View | ||||
{ | { | ||||
public partial class LogForm : Form | public partial class LogForm : Form | ||||
@@ -18,12 +16,28 @@ namespace Shadowsocks.View | |||||
string filename; | string filename; | ||||
Timer timer; | Timer timer; | ||||
const int BACK_OFFSET = 65536; | const int BACK_OFFSET = 65536; | ||||
ShadowsocksController controller; | |||||
public LogForm(string filename) | |||||
public LogForm(ShadowsocksController controller, string filename) | |||||
{ | { | ||||
this.controller = controller; | |||||
this.filename = filename; | this.filename = filename; | ||||
InitializeComponent(); | InitializeComponent(); | ||||
this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||||
if (config == null) | |||||
{ | |||||
config = new LogViewerConfig(); | |||||
} | |||||
else { | |||||
topMostTrigger = config.topMost; | |||||
wrapTextTrigger = config.wrapText; | |||||
toolbarTrigger = config.toolbarShown; | |||||
LogMessageTextBox.BackColor = config.GetBackgroundColor(); | |||||
LogMessageTextBox.ForeColor = config.GetTextColor(); | |||||
LogMessageTextBox.Font = config.GetFont(); | |||||
} | |||||
UpdateTexts(); | UpdateTexts(); | ||||
} | } | ||||
@@ -33,11 +47,17 @@ namespace Shadowsocks.View | |||||
FileMenuItem.Text = I18N.GetString("&File"); | FileMenuItem.Text = I18N.GetString("&File"); | ||||
OpenLocationMenuItem.Text = I18N.GetString("&Open Location"); | OpenLocationMenuItem.Text = I18N.GetString("&Open Location"); | ||||
ExitMenuItem.Text = I18N.GetString("E&xit"); | 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"); | |||||
CleanLogsButton.Text = I18N.GetString("&Clean Logs"); | |||||
ChangeFontButton.Text = I18N.GetString("Change &Font"); | |||||
WrapTextCheckBox.Text = I18N.GetString("&Wrap Text"); | |||||
TopMostCheckBox.Text = I18N.GetString("&Top Most"); | |||||
ViewMenuItem.Text = I18N.GetString("&View"); | |||||
CleanLogsMenuItem.Text = I18N.GetString("&Clean Logs"); | |||||
ChangeFontMenuItem.Text = I18N.GetString("Change &Font"); | |||||
WrapTextMenuItem.Text = I18N.GetString("&Wrap Text"); | |||||
TopMostMenuItem.Text = I18N.GetString("&Top Most"); | |||||
ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar"); | |||||
Text = I18N.GetString("Log Viewer"); | |||||
} | } | ||||
private void Timer_Tick(object sender, EventArgs e) | private void Timer_Tick(object sender, EventArgs e) | ||||
@@ -58,7 +78,7 @@ namespace Shadowsocks.View | |||||
string line = ""; | string line = ""; | ||||
while ((line = reader.ReadLine()) != null) | while ((line = reader.ReadLine()) != null) | ||||
LogMessageTextBox.AppendText(line + "\r\n"); | |||||
LogMessageTextBox.AppendText(line + Environment.NewLine); | |||||
LogMessageTextBox.ScrollToCaret(); | LogMessageTextBox.ScrollToCaret(); | ||||
@@ -78,7 +98,7 @@ namespace Shadowsocks.View | |||||
while ((line = reader.ReadLine()) != null) | while ((line = reader.ReadLine()) != null) | ||||
{ | { | ||||
changed = true; | changed = true; | ||||
LogMessageTextBox.AppendText(line + "\r\n"); | |||||
LogMessageTextBox.AppendText(line + Environment.NewLine); | |||||
} | } | ||||
if (changed) | if (changed) | ||||
@@ -88,32 +108,68 @@ namespace Shadowsocks.View | |||||
lastOffset = reader.BaseStream.Position; | lastOffset = reader.BaseStream.Position; | ||||
} | } | ||||
this.Text = I18N.GetString("Log Viewer") + | |||||
$" [in: {Utils.FormatBandwidth(controller.inboundCounter)}, out: {Utils.FormatBandwidth(controller.outboundCounter)}]"; | |||||
} | } | ||||
private void LogForm_Load(object sender, EventArgs e) | private void LogForm_Load(object sender, EventArgs e) | ||||
{ | { | ||||
InitContent(); | InitContent(); | ||||
timer = new Timer(); | timer = new Timer(); | ||||
timer.Interval = 300; | timer.Interval = 300; | ||||
timer.Tick += Timer_Tick; | timer.Tick += Timer_Tick; | ||||
timer.Start(); | timer.Start(); | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||||
if (config == null) | |||||
config = new LogViewerConfig(); | |||||
Height = config.height; | |||||
Width = config.width; | |||||
Top = config.GetBestTop(); | |||||
Left = config.GetBestLeft(); | |||||
topMostTriggerLock = true; | |||||
TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; | |||||
topMostTriggerLock = false; | |||||
wrapTextTriggerLock = true; | |||||
LogMessageTextBox.WordWrap = WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger; | |||||
wrapTextTriggerLock = false; | |||||
ToolbarFlowLayoutPanel.Visible = ShowToolbarMenuItem.Checked = toolbarTrigger; | |||||
} | } | ||||
private void LogForm_FormClosing(object sender, FormClosingEventArgs e) | private void LogForm_FormClosing(object sender, FormClosingEventArgs e) | ||||
{ | { | ||||
timer.Stop(); | timer.Stop(); | ||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | |||||
if (config == null) | |||||
config = new LogViewerConfig(); | |||||
config.topMost = topMostTrigger; | |||||
config.wrapText = wrapTextTrigger; | |||||
config.toolbarShown = toolbarTrigger; | |||||
config.SetFont(LogMessageTextBox.Font); | |||||
config.SetBackgroundColor(LogMessageTextBox.BackColor); | |||||
config.SetTextColor(LogMessageTextBox.ForeColor); | |||||
config.top = Top; | |||||
config.left = Left; | |||||
config.height = Height; | |||||
config.width = Width; | |||||
controller.SaveLogViewerConfig(config); | |||||
} | } | ||||
private void OpenLocationMenuItem_Click(object sender, EventArgs e) | private void OpenLocationMenuItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
string argument = "/select, \"" + filename + "\""; | string argument = "/select, \"" + filename + "\""; | ||||
Console.WriteLine(argument); | |||||
Logging.Debug(argument); | |||||
System.Diagnostics.Process.Start("explorer.exe", argument); | System.Diagnostics.Process.Start("explorer.exe", argument); | ||||
} | } | ||||
private void ExitMenuItem_Click(object sender, EventArgs e) | private void ExitMenuItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
this.Close(); | |||||
Close(); | |||||
} | } | ||||
private void LogForm_Shown(object sender, EventArgs e) | private void LogForm_Shown(object sender, EventArgs e) | ||||
@@ -121,30 +177,125 @@ namespace Shadowsocks.View | |||||
LogMessageTextBox.ScrollToCaret(); | LogMessageTextBox.ScrollToCaret(); | ||||
} | } | ||||
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
#region Clean up the content in LogMessageTextBox. | |||||
private void DoCleanLogs() | |||||
{ | { | ||||
LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked; | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
LogMessageTextBox.Clear(); | |||||
} | |||||
private void CleanLogsMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
DoCleanLogs(); | |||||
} | } | ||||
private void CleanLogsButton_Click(object sender, EventArgs e) | private void CleanLogsButton_Click(object sender, EventArgs e) | ||||
{ | { | ||||
LogMessageTextBox.Clear(); | |||||
DoCleanLogs(); | |||||
} | |||||
#endregion | |||||
#region Change the font settings applied in LogMessageTextBox. | |||||
private void DoChangeFont() | |||||
{ | |||||
try | |||||
{ | |||||
FontDialog fd = new FontDialog(); | |||||
fd.Font = LogMessageTextBox.Font; | |||||
if (fd.ShowDialog() == DialogResult.OK) | |||||
{ | |||||
LogMessageTextBox.Font = new Font(fd.Font.FontFamily, fd.Font.Size, fd.Font.Style); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Logging.LogUsefulException(ex); | |||||
MessageBox.Show(ex.Message); | |||||
} | |||||
} | |||||
private void ChangeFontMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
DoChangeFont(); | |||||
} | } | ||||
private void ChangeFontButton_Click(object sender, EventArgs e) | private void ChangeFontButton_Click(object sender, EventArgs e) | ||||
{ | { | ||||
FontDialog fd = new FontDialog(); | |||||
fd.Font = LogMessageTextBox.Font; | |||||
if (fd.ShowDialog() == DialogResult.OK) | |||||
DoChangeFont(); | |||||
} | |||||
#endregion | |||||
#region Trigger the log messages to wrapable, or not. | |||||
bool wrapTextTrigger = false; | |||||
bool wrapTextTriggerLock = false; | |||||
private void TriggerWrapText() | |||||
{ | |||||
wrapTextTriggerLock = true; | |||||
wrapTextTrigger = !wrapTextTrigger; | |||||
LogMessageTextBox.WordWrap = wrapTextTrigger; | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger; | |||||
wrapTextTriggerLock = false; | |||||
} | |||||
private void WrapTextMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
if (!wrapTextTriggerLock) | |||||
{ | |||||
TriggerWrapText(); | |||||
} | |||||
} | |||||
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
if (!wrapTextTriggerLock) | |||||
{ | { | ||||
LogMessageTextBox.Font = fd.Font; | |||||
TriggerWrapText(); | |||||
} | } | ||||
} | } | ||||
#endregion | |||||
#region Trigger the window to top most, or not. | |||||
bool topMostTrigger = false; | |||||
bool topMostTriggerLock = false; | |||||
private void TriggerTopMost() | |||||
{ | |||||
topMostTriggerLock = true; | |||||
topMostTrigger = !topMostTrigger; | |||||
TopMost = topMostTrigger; | |||||
TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; | |||||
topMostTriggerLock = false; | |||||
} | |||||
private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) | private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) | ||||
{ | { | ||||
this.TopMost = TopMostCheckBox.Checked; | |||||
if (!topMostTriggerLock) | |||||
{ | |||||
TriggerTopMost(); | |||||
} | |||||
} | |||||
private void TopMostMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
if (!topMostTriggerLock) | |||||
{ | |||||
TriggerTopMost(); | |||||
} | |||||
} | |||||
#endregion | |||||
private bool toolbarTrigger = false; | |||||
private void ShowToolbarMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
toolbarTrigger = !toolbarTrigger; | |||||
ToolbarFlowLayoutPanel.Visible = toolbarTrigger; | |||||
ShowToolbarMenuItem.Checked = toolbarTrigger; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -117,10 +117,31 @@ | |||||
<resheader name="writer"> | <resheader name="writer"> | ||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.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> | ||||
<metadata name="contextMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||||
<metadata name="LogMessageTextBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="MainMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||||
<value>17, 17</value> | <value>17, 17</value> | ||||
</metadata> | </metadata> | ||||
<metadata name="mainMenu1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||||
<value>172, 17</value> | |||||
<metadata name="TopMostCheckBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ChangeFontButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="CleanLogsButton.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="WrapTextCheckBox.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="tableLayoutPanel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="ToolbarFlowLayoutPanel.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | |||||
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>True</value> | |||||
</metadata> | </metadata> | ||||
</root> | </root> |
@@ -1,16 +1,18 @@ | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using System; | |||||
using System; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Drawing; | using System.Drawing; | ||||
using System.Text; | |||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using ZXing; | using ZXing; | ||||
using ZXing.Common; | using ZXing.Common; | ||||
using ZXing.QrCode; | using ZXing.QrCode; | ||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Util; | |||||
namespace Shadowsocks.View | namespace Shadowsocks.View | ||||
{ | { | ||||
public class MenuViewController | public class MenuViewController | ||||
@@ -26,10 +28,10 @@ namespace Shadowsocks.View | |||||
private ContextMenu contextMenu1; | private ContextMenu contextMenu1; | ||||
private bool _isFirstRun; | private bool _isFirstRun; | ||||
private bool _isStartupChecking; | |||||
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; | ||||
@@ -42,7 +44,10 @@ namespace Shadowsocks.View | |||||
private MenuItem updateFromGFWListItem; | private MenuItem updateFromGFWListItem; | ||||
private MenuItem editGFWUserRuleItem; | private MenuItem editGFWUserRuleItem; | ||||
private MenuItem editOnlinePACItem; | private MenuItem editOnlinePACItem; | ||||
private MenuItem autoCheckUpdatesToggleItem; | |||||
private ConfigForm configForm; | private ConfigForm configForm; | ||||
private List<LogForm> logForms = new List<LogForm>(); | |||||
private bool logFormsVisible = false; | |||||
private string _urlToOpen; | private string _urlToOpen; | ||||
public MenuViewController(ShadowsocksController controller) | public MenuViewController(ShadowsocksController controller) | ||||
@@ -65,16 +70,25 @@ namespace Shadowsocks.View | |||||
UpdateTrayIcon(); | UpdateTrayIcon(); | ||||
_notifyIcon.Visible = true; | _notifyIcon.Visible = true; | ||||
_notifyIcon.ContextMenu = contextMenu1; | _notifyIcon.ContextMenu = contextMenu1; | ||||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; | |||||
_notifyIcon.MouseClick += notifyIcon1_Click; | |||||
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; | _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; | ||||
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; | |||||
this.updateChecker = new UpdateChecker(); | this.updateChecker = new UpdateChecker(); | ||||
updateChecker.NewVersionFound += updateChecker_NewVersionFound; | |||||
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | |||||
LoadCurrentConfiguration(); | LoadCurrentConfiguration(); | ||||
updateChecker.CheckUpdate(controller.GetConfigurationCopy()); | |||||
Configuration config = controller.GetConfigurationCopy(); | |||||
if (controller.GetConfigurationCopy().isDefault) | |||||
if (config.autoCheckUpdate) | |||||
{ | |||||
_isStartupChecking = true; | |||||
updateChecker.CheckUpdate(config, 3000); | |||||
} | |||||
if (config.isDefault) | |||||
{ | { | ||||
_isFirstRun = true; | _isFirstRun = true; | ||||
ShowConfigForm(); | ShowConfigForm(); | ||||
@@ -164,6 +178,7 @@ namespace Shadowsocks.View | |||||
this.ServersItem = CreateMenuGroup("Servers", new MenuItem[] { | this.ServersItem = CreateMenuGroup("Servers", new MenuItem[] { | ||||
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("Statistics Config...", StatisticsConfigItem_Click), | |||||
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), | CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), | ||||
CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) | CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) | ||||
}), | }), | ||||
@@ -178,10 +193,14 @@ 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)), | ||||
CreateMenuGroup("Updates...", new MenuItem[] { | |||||
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), | |||||
new MenuItem("-"), | |||||
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)), | |||||
}), | |||||
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)), | CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
CreateMenuItem("Quit", new EventHandler(this.Quit_Click)) | CreateMenuItem("Quit", new EventHandler(this.Quit_Click)) | ||||
@@ -238,19 +257,41 @@ namespace Shadowsocks.View | |||||
ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000); | ShowBalloonTip(I18N.GetString("Shadowsocks"), result, ToolTipIcon.Info, 1000); | ||||
} | } | ||||
void updateChecker_NewVersionFound(object sender, EventArgs e) | |||||
void updateChecker_CheckUpdateCompleted(object sender, EventArgs e) | |||||
{ | { | ||||
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to download"), ToolTipIcon.Info, 5000); | |||||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; | |||||
_isFirstRun = false; | |||||
if (updateChecker.NewVersionFound) | |||||
{ | |||||
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000); | |||||
_isFirstRun = false; | |||||
} | |||||
else if (!_isStartupChecking) | |||||
{ | |||||
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("No update is available"), ToolTipIcon.Info, 5000); | |||||
_isFirstRun = false; | |||||
} | |||||
_isStartupChecking = false; | |||||
} | } | ||||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | ||||
{ | { | ||||
System.Diagnostics.Process.Start(updateChecker.LatestVersionURL); | |||||
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked; | |||||
if (updateChecker.NewVersionFound) | |||||
{ | |||||
updateChecker.NewVersionFound = false; /* Reset the flag */ | |||||
if (System.IO.File.Exists(updateChecker.LatestVersionLocalName)) | |||||
{ | |||||
string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; | |||||
System.Diagnostics.Process.Start("explorer.exe", argument); | |||||
} | |||||
} | |||||
} | } | ||||
private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) | |||||
{ | |||||
if (updateChecker.NewVersionFound) | |||||
{ | |||||
updateChecker.NewVersionFound = false; /* Reset the flag */ | |||||
} | |||||
} | |||||
private void LoadCurrentConfiguration() | private void LoadCurrentConfiguration() | ||||
{ | { | ||||
@@ -262,10 +303,10 @@ 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(); | ||||
UpdateUpdateMenu(); | |||||
} | } | ||||
private void UpdateServersMenu() | private void UpdateServersMenu() | ||||
@@ -301,7 +342,6 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
item.Checked = true; | item.Checked = true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -319,10 +359,36 @@ namespace Shadowsocks.View | |||||
} | } | ||||
} | } | ||||
private void ShowLogForms() | |||||
{ | |||||
if (logForms.Count == 0) | |||||
{ | |||||
LogForm f = new LogForm(controller, Logging.LogFilePath); | |||||
f.Show(); | |||||
f.FormClosed += logForm_FormClosed; | |||||
logForms.Add(f); | |||||
logFormsVisible = true; | |||||
} | |||||
else | |||||
{ | |||||
logFormsVisible = !logFormsVisible; | |||||
foreach (LogForm f in logForms) | |||||
{ | |||||
f.Visible = logFormsVisible; | |||||
} | |||||
} | |||||
} | |||||
void logForm_FormClosed(object sender, FormClosedEventArgs e) | |||||
{ | |||||
logForms.Remove((LogForm)sender); | |||||
} | |||||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | void configForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
configForm = null; | configForm = null; | ||||
Util.Utils.ReleaseMemory(true); | |||||
Utils.ReleaseMemory(true); | |||||
ShowFirstTimeBalloon(); | ShowFirstTimeBalloon(); | ||||
} | } | ||||
@@ -343,7 +409,7 @@ namespace Shadowsocks.View | |||||
if (_isFirstRun) | if (_isFirstRun) | ||||
{ | { | ||||
_notifyIcon.BalloonTipTitle = I18N.GetString("Shadowsocks is here"); | _notifyIcon.BalloonTipTitle = I18N.GetString("Shadowsocks is here"); | ||||
_notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu"); | |||||
_notifyIcon.BalloonTipText = I18N.GetString("You can turn on/off Shadowsocks in the context menu"); | |||||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info; | _notifyIcon.BalloonTipIcon = ToolTipIcon.Info; | ||||
_notifyIcon.ShowBalloonTip(0); | _notifyIcon.ShowBalloonTip(0); | ||||
_isFirstRun = false; | _isFirstRun = false; | ||||
@@ -355,6 +421,18 @@ namespace Shadowsocks.View | |||||
Process.Start("https://github.com/shadowsocks/shadowsocks-windows"); | Process.Start("https://github.com/shadowsocks/shadowsocks-windows"); | ||||
} | } | ||||
private void notifyIcon1_Click(object sender, MouseEventArgs e) | |||||
{ | |||||
if (e.Button == MouseButtons.Left) | |||||
{ | |||||
// TODO: show something interesting | |||||
} | |||||
else if (e.Button == MouseButtons.Middle) | |||||
{ | |||||
ShowLogForms(); | |||||
} | |||||
} | |||||
private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e) | private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e) | ||||
{ | { | ||||
if (e.Button == MouseButtons.Left) | if (e.Button == MouseButtons.Left) | ||||
@@ -413,9 +491,17 @@ namespace Shadowsocks.View | |||||
private void ShowLogItem_Click(object sender, EventArgs e) | private void ShowLogItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
string argument = Logging.LogFile; | |||||
LogForm f = new LogForm(controller, Logging.LogFilePath); | |||||
f.Show(); | |||||
f.FormClosed += logForm_FormClosed; | |||||
new LogForm(argument).Show(); | |||||
logForms.Add(f); | |||||
} | |||||
private void StatisticsConfigItem_Click(object sender, EventArgs e) | |||||
{ | |||||
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); | |||||
form.Show(); | |||||
} | } | ||||
private void QRCodeItem_Click(object sender, EventArgs e) | private void QRCodeItem_Click(object sender, EventArgs e) | ||||
@@ -520,18 +606,15 @@ namespace Shadowsocks.View | |||||
Process.Start(_urlToOpen); | Process.Start(_urlToOpen); | ||||
} | } | ||||
private void AutoStartupItem_Click(object sender, EventArgs e) { | |||||
private void AutoStartupItem_Click(object sender, EventArgs e) | |||||
{ | |||||
AutoStartupItem.Checked = !AutoStartupItem.Checked; | AutoStartupItem.Checked = !AutoStartupItem.Checked; | ||||
if (!AutoStartup.Set(AutoStartupItem.Checked)) { | |||||
if (!AutoStartup.Set(AutoStartupItem.Checked)) | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Failed to update registry")); | 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) | ||||
{ | { | ||||
if (!localPACItem.Checked) | if (!localPACItem.Checked) | ||||
@@ -591,5 +674,23 @@ namespace Shadowsocks.View | |||||
this.editOnlinePACItem.Enabled = true; | this.editOnlinePACItem.Enabled = true; | ||||
} | } | ||||
} | } | ||||
private void UpdateUpdateMenu() | |||||
{ | |||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
autoCheckUpdatesToggleItem.Checked = configuration.autoCheckUpdate; | |||||
} | |||||
private void autoCheckUpdatesToggleItem_Click(object sender, EventArgs e) | |||||
{ | |||||
Configuration configuration = controller.GetConfigurationCopy(); | |||||
controller.ToggleCheckingUpdate(!configuration.autoCheckUpdate); | |||||
UpdateUpdateMenu(); | |||||
} | |||||
private void checkUpdatesItem_Click(object sender, EventArgs e) | |||||
{ | |||||
updateChecker.CheckUpdate(controller.GetConfigurationCopy()); | |||||
} | |||||
} | } | ||||
} | } |
@@ -0,0 +1,531 @@ | |||||
namespace Shadowsocks.View | |||||
{ | |||||
partial class StatisticsStrategyConfigurationForm | |||||
{ | |||||
/// <summary> | |||||
/// Required designer variable. | |||||
/// </summary> | |||||
private System.ComponentModel.IContainer components = null; | |||||
/// <summary> | |||||
/// Clean up any resources being used. | |||||
/// </summary> | |||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> | |||||
protected override void Dispose(bool disposing) | |||||
{ | |||||
if (disposing && (components != null)) | |||||
{ | |||||
components.Dispose(); | |||||
} | |||||
base.Dispose(disposing); | |||||
} | |||||
#region Windows Form Designer generated code | |||||
/// <summary> | |||||
/// Required method for Designer support - do not modify | |||||
/// the contents of this method with the code editor. | |||||
/// </summary> | |||||
private void InitializeComponent() | |||||
{ | |||||
this.components = new System.ComponentModel.Container(); | |||||
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); | |||||
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); | |||||
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); | |||||
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); | |||||
this.StatisticsChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); | |||||
this.byISPCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.label2 = new System.Windows.Forms.Label(); | |||||
this.label3 = new System.Windows.Forms.Label(); | |||||
this.label4 = new System.Windows.Forms.Label(); | |||||
this.chartModeSelector = new System.Windows.Forms.GroupBox(); | |||||
this.allMode = new System.Windows.Forms.RadioButton(); | |||||
this.dayMode = new System.Windows.Forms.RadioButton(); | |||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer(); | |||||
this.splitContainer2 = new System.Windows.Forms.SplitContainer(); | |||||
this.label9 = new System.Windows.Forms.Label(); | |||||
this.label8 = new System.Windows.Forms.Label(); | |||||
this.dataCollectionMinutesNum = new System.Windows.Forms.NumericUpDown(); | |||||
this.StatisticsEnabledCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.choiceKeptMinutesNum = new System.Windows.Forms.NumericUpDown(); | |||||
this.byHourOfDayCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.repeatTimesNum = new System.Windows.Forms.NumericUpDown(); | |||||
this.label6 = new System.Windows.Forms.Label(); | |||||
this.splitContainer3 = new System.Windows.Forms.SplitContainer(); | |||||
this.label1 = new System.Windows.Forms.Label(); | |||||
this.calculationContainer = new System.Windows.Forms.FlowLayoutPanel(); | |||||
this.serverSelector = new System.Windows.Forms.ComboBox(); | |||||
this.CancelButton = new System.Windows.Forms.Button(); | |||||
this.OKButton = new System.Windows.Forms.Button(); | |||||
this.bindingConfiguration = new System.Windows.Forms.BindingSource(this.components); | |||||
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).BeginInit(); | |||||
this.chartModeSelector.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); | |||||
this.splitContainer1.Panel1.SuspendLayout(); | |||||
this.splitContainer1.Panel2.SuspendLayout(); | |||||
this.splitContainer1.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); | |||||
this.splitContainer2.Panel1.SuspendLayout(); | |||||
this.splitContainer2.Panel2.SuspendLayout(); | |||||
this.splitContainer2.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).BeginInit(); | |||||
((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).BeginInit(); | |||||
((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).BeginInit(); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).BeginInit(); | |||||
this.splitContainer3.Panel1.SuspendLayout(); | |||||
this.splitContainer3.Panel2.SuspendLayout(); | |||||
this.splitContainer3.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).BeginInit(); | |||||
this.SuspendLayout(); | |||||
// | |||||
// StatisticsChart | |||||
// | |||||
this.StatisticsChart.BackColor = System.Drawing.Color.Transparent; | |||||
chartArea1.AxisX.MajorGrid.Enabled = false; | |||||
chartArea1.AxisY.MajorGrid.Enabled = false; | |||||
chartArea1.AxisY2.MajorGrid.Enabled = false; | |||||
chartArea1.BackColor = System.Drawing.Color.Transparent; | |||||
chartArea1.Name = "DataArea"; | |||||
this.StatisticsChart.ChartAreas.Add(chartArea1); | |||||
this.StatisticsChart.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
legend1.BackColor = System.Drawing.Color.Transparent; | |||||
legend1.Name = "ChartLegend"; | |||||
this.StatisticsChart.Legends.Add(legend1); | |||||
this.StatisticsChart.Location = new System.Drawing.Point(0, 0); | |||||
this.StatisticsChart.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.StatisticsChart.Name = "StatisticsChart"; | |||||
this.StatisticsChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.Pastel; | |||||
series1.ChartArea = "DataArea"; | |||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Bubble; | |||||
series1.Color = System.Drawing.Color.FromArgb(((int)(((byte)(221)))), ((int)(((byte)(88)))), ((int)(((byte)(0))))); | |||||
series1.Legend = "ChartLegend"; | |||||
series1.Name = "Package Loss"; | |||||
series1.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; | |||||
series1.YValuesPerPoint = 2; | |||||
series2.BorderWidth = 4; | |||||
series2.ChartArea = "DataArea"; | |||||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; | |||||
series2.Color = System.Drawing.Color.FromArgb(((int)(((byte)(155)))), ((int)(((byte)(77)))), ((int)(((byte)(150))))); | |||||
series2.Legend = "ChartLegend"; | |||||
series2.Name = "Ping"; | |||||
series2.XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; | |||||
this.StatisticsChart.Series.Add(series1); | |||||
this.StatisticsChart.Series.Add(series2); | |||||
this.StatisticsChart.Size = new System.Drawing.Size(1077, 303); | |||||
this.StatisticsChart.TabIndex = 2; | |||||
// | |||||
// byISPCheckBox | |||||
// | |||||
this.byISPCheckBox.AutoSize = true; | |||||
this.byISPCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByIsp", true)); | |||||
this.byISPCheckBox.Location = new System.Drawing.Point(13, 54); | |||||
this.byISPCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.byISPCheckBox.Name = "byISPCheckBox"; | |||||
this.byISPCheckBox.Size = new System.Drawing.Size(220, 31); | |||||
this.byISPCheckBox.TabIndex = 5; | |||||
this.byISPCheckBox.Text = "By ISP/geolocation"; | |||||
this.byISPCheckBox.UseVisualStyleBackColor = true; | |||||
// | |||||
// label2 | |||||
// | |||||
this.label2.AutoSize = true; | |||||
this.label2.Location = new System.Drawing.Point(8, 136); | |||||
this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.label2.Name = "label2"; | |||||
this.label2.Size = new System.Drawing.Size(167, 27); | |||||
this.label2.TabIndex = 8; | |||||
this.label2.Text = "Keep choice for "; | |||||
// | |||||
// label3 | |||||
// | |||||
this.label3.AutoSize = true; | |||||
this.label3.Location = new System.Drawing.Point(285, 136); | |||||
this.label3.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.label3.Name = "label3"; | |||||
this.label3.Size = new System.Drawing.Size(87, 27); | |||||
this.label3.TabIndex = 9; | |||||
this.label3.Text = "minutes"; | |||||
// | |||||
// label4 | |||||
// | |||||
this.label4.AutoSize = true; | |||||
this.label4.Location = new System.Drawing.Point(8, 218); | |||||
this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.label4.Name = "label4"; | |||||
this.label4.Size = new System.Drawing.Size(54, 27); | |||||
this.label4.TabIndex = 10; | |||||
this.label4.Text = "Ping"; | |||||
// | |||||
// chartModeSelector | |||||
// | |||||
this.chartModeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||||
this.chartModeSelector.Controls.Add(this.allMode); | |||||
this.chartModeSelector.Controls.Add(this.dayMode); | |||||
this.chartModeSelector.Location = new System.Drawing.Point(801, 104); | |||||
this.chartModeSelector.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.chartModeSelector.Name = "chartModeSelector"; | |||||
this.chartModeSelector.Padding = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.chartModeSelector.Size = new System.Drawing.Size(261, 103); | |||||
this.chartModeSelector.TabIndex = 3; | |||||
this.chartModeSelector.TabStop = false; | |||||
this.chartModeSelector.Text = "Chart Mode"; | |||||
this.chartModeSelector.Enter += new System.EventHandler(this.chartModeSelector_Enter); | |||||
// | |||||
// allMode | |||||
// | |||||
this.allMode.AutoSize = true; | |||||
this.allMode.Checked = true; | |||||
this.allMode.Location = new System.Drawing.Point(11, 61); | |||||
this.allMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.allMode.Name = "allMode"; | |||||
this.allMode.Size = new System.Drawing.Size(58, 31); | |||||
this.allMode.TabIndex = 1; | |||||
this.allMode.TabStop = true; | |||||
this.allMode.Text = "all"; | |||||
this.allMode.UseVisualStyleBackColor = true; | |||||
this.allMode.CheckedChanged += new System.EventHandler(this.allMode_CheckedChanged); | |||||
// | |||||
// dayMode | |||||
// | |||||
this.dayMode.AutoSize = true; | |||||
this.dayMode.Location = new System.Drawing.Point(11, 29); | |||||
this.dayMode.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.dayMode.Name = "dayMode"; | |||||
this.dayMode.Size = new System.Drawing.Size(73, 31); | |||||
this.dayMode.TabIndex = 0; | |||||
this.dayMode.Text = "24h"; | |||||
this.dayMode.UseVisualStyleBackColor = true; | |||||
this.dayMode.CheckedChanged += new System.EventHandler(this.dayMode_CheckedChanged); | |||||
// | |||||
// splitContainer1 | |||||
// | |||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.splitContainer1.IsSplitterFixed = true; | |||||
this.splitContainer1.Location = new System.Drawing.Point(0, 0); | |||||
this.splitContainer1.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.splitContainer1.Name = "splitContainer1"; | |||||
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; | |||||
// | |||||
// splitContainer1.Panel1 | |||||
// | |||||
this.splitContainer1.Panel1.Controls.Add(this.splitContainer2); | |||||
// | |||||
// splitContainer1.Panel2 | |||||
// | |||||
this.splitContainer1.Panel2.Controls.Add(this.serverSelector); | |||||
this.splitContainer1.Panel2.Controls.Add(this.CancelButton); | |||||
this.splitContainer1.Panel2.Controls.Add(this.OKButton); | |||||
this.splitContainer1.Panel2.Controls.Add(this.chartModeSelector); | |||||
this.splitContainer1.Panel2.Controls.Add(this.StatisticsChart); | |||||
this.splitContainer1.Size = new System.Drawing.Size(1077, 614); | |||||
this.splitContainer1.SplitterDistance = 301; | |||||
this.splitContainer1.SplitterWidth = 10; | |||||
this.splitContainer1.TabIndex = 12; | |||||
// | |||||
// splitContainer2 | |||||
// | |||||
this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; | |||||
this.splitContainer2.IsSplitterFixed = true; | |||||
this.splitContainer2.Location = new System.Drawing.Point(0, 0); | |||||
this.splitContainer2.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.splitContainer2.Name = "splitContainer2"; | |||||
// | |||||
// splitContainer2.Panel1 | |||||
// | |||||
this.splitContainer2.Panel1.Controls.Add(this.label9); | |||||
this.splitContainer2.Panel1.Controls.Add(this.label8); | |||||
this.splitContainer2.Panel1.Controls.Add(this.dataCollectionMinutesNum); | |||||
this.splitContainer2.Panel1.Controls.Add(this.StatisticsEnabledCheckBox); | |||||
this.splitContainer2.Panel1.Controls.Add(this.choiceKeptMinutesNum); | |||||
this.splitContainer2.Panel1.Controls.Add(this.byHourOfDayCheckBox); | |||||
this.splitContainer2.Panel1.Controls.Add(this.repeatTimesNum); | |||||
this.splitContainer2.Panel1.Controls.Add(this.label6); | |||||
this.splitContainer2.Panel1.Controls.Add(this.label2); | |||||
this.splitContainer2.Panel1.Controls.Add(this.label4); | |||||
this.splitContainer2.Panel1.Controls.Add(this.byISPCheckBox); | |||||
this.splitContainer2.Panel1.Controls.Add(this.label3); | |||||
// | |||||
// splitContainer2.Panel2 | |||||
// | |||||
this.splitContainer2.Panel2.Controls.Add(this.splitContainer3); | |||||
this.splitContainer2.Size = new System.Drawing.Size(1077, 301); | |||||
this.splitContainer2.SplitterDistance = 384; | |||||
this.splitContainer2.SplitterWidth = 5; | |||||
this.splitContainer2.TabIndex = 7; | |||||
// | |||||
// label9 | |||||
// | |||||
this.label9.AutoSize = true; | |||||
this.label9.Location = new System.Drawing.Point(8, 175); | |||||
this.label9.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.label9.Name = "label9"; | |||||
this.label9.Size = new System.Drawing.Size(162, 27); | |||||
this.label9.TabIndex = 20; | |||||
this.label9.Text = "Collect data per"; | |||||
// | |||||
// label8 | |||||
// | |||||
this.label8.AutoSize = true; | |||||
this.label8.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.label8.Location = new System.Drawing.Point(285, 176); | |||||
this.label8.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.label8.Name = "label8"; | |||||
this.label8.Size = new System.Drawing.Size(87, 27); | |||||
this.label8.TabIndex = 19; | |||||
this.label8.Text = "minutes"; | |||||
// | |||||
// dataCollectionMinutesNum | |||||
// | |||||
this.dataCollectionMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "DataCollectionMinutes", true)); | |||||
this.dataCollectionMinutesNum.Increment = new decimal(new int[] { | |||||
10, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.dataCollectionMinutesNum.Location = new System.Drawing.Point(176, 173); | |||||
this.dataCollectionMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | |||||
this.dataCollectionMinutesNum.Maximum = new decimal(new int[] { | |||||
120, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.dataCollectionMinutesNum.Minimum = new decimal(new int[] { | |||||
5, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.dataCollectionMinutesNum.Name = "dataCollectionMinutesNum"; | |||||
this.dataCollectionMinutesNum.Size = new System.Drawing.Size(100, 34); | |||||
this.dataCollectionMinutesNum.TabIndex = 18; | |||||
this.dataCollectionMinutesNum.Value = new decimal(new int[] { | |||||
10, | |||||
0, | |||||
0, | |||||
0}); | |||||
// | |||||
// StatisticsEnabledCheckBox | |||||
// | |||||
this.StatisticsEnabledCheckBox.AutoSize = true; | |||||
this.StatisticsEnabledCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "StatisticsEnabled", true)); | |||||
this.StatisticsEnabledCheckBox.Location = new System.Drawing.Point(13, 12); | |||||
this.StatisticsEnabledCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.StatisticsEnabledCheckBox.Name = "StatisticsEnabledCheckBox"; | |||||
this.StatisticsEnabledCheckBox.Size = new System.Drawing.Size(189, 31); | |||||
this.StatisticsEnabledCheckBox.TabIndex = 17; | |||||
this.StatisticsEnabledCheckBox.Text = "Enable Statistics"; | |||||
this.StatisticsEnabledCheckBox.UseVisualStyleBackColor = true; | |||||
// | |||||
// choiceKeptMinutesNum | |||||
// | |||||
this.choiceKeptMinutesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "ChoiceKeptMinutes", true)); | |||||
this.choiceKeptMinutesNum.Increment = new decimal(new int[] { | |||||
10, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.choiceKeptMinutesNum.Location = new System.Drawing.Point(176, 134); | |||||
this.choiceKeptMinutesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | |||||
this.choiceKeptMinutesNum.Maximum = new decimal(new int[] { | |||||
120, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.choiceKeptMinutesNum.Minimum = new decimal(new int[] { | |||||
5, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.choiceKeptMinutesNum.Name = "choiceKeptMinutesNum"; | |||||
this.choiceKeptMinutesNum.Size = new System.Drawing.Size(100, 34); | |||||
this.choiceKeptMinutesNum.TabIndex = 16; | |||||
this.choiceKeptMinutesNum.Value = new decimal(new int[] { | |||||
10, | |||||
0, | |||||
0, | |||||
0}); | |||||
// | |||||
// byHourOfDayCheckBox | |||||
// | |||||
this.byHourOfDayCheckBox.AutoSize = true; | |||||
this.byHourOfDayCheckBox.DataBindings.Add(new System.Windows.Forms.Binding("Checked", this.bindingConfiguration, "ByHourOfDay", true)); | |||||
this.byHourOfDayCheckBox.Location = new System.Drawing.Point(13, 95); | |||||
this.byHourOfDayCheckBox.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.byHourOfDayCheckBox.Name = "byHourOfDayCheckBox"; | |||||
this.byHourOfDayCheckBox.Size = new System.Drawing.Size(180, 31); | |||||
this.byHourOfDayCheckBox.TabIndex = 15; | |||||
this.byHourOfDayCheckBox.Text = "By hour of day"; | |||||
this.byHourOfDayCheckBox.UseVisualStyleBackColor = true; | |||||
// | |||||
// repeatTimesNum | |||||
// | |||||
this.repeatTimesNum.DataBindings.Add(new System.Windows.Forms.Binding("Value", this.bindingConfiguration, "RepeatTimesNum", true)); | |||||
this.repeatTimesNum.Location = new System.Drawing.Point(72, 216); | |||||
this.repeatTimesNum.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); | |||||
this.repeatTimesNum.Maximum = new decimal(new int[] { | |||||
10, | |||||
0, | |||||
0, | |||||
0}); | |||||
this.repeatTimesNum.Name = "repeatTimesNum"; | |||||
this.repeatTimesNum.Size = new System.Drawing.Size(99, 34); | |||||
this.repeatTimesNum.TabIndex = 14; | |||||
this.repeatTimesNum.Value = new decimal(new int[] { | |||||
4, | |||||
0, | |||||
0, | |||||
0}); | |||||
// | |||||
// label6 | |||||
// | |||||
this.label6.AutoSize = true; | |||||
this.label6.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.label6.Location = new System.Drawing.Point(178, 218); | |||||
this.label6.Name = "label6"; | |||||
this.label6.Size = new System.Drawing.Size(201, 27); | |||||
this.label6.TabIndex = 13; | |||||
this.label6.Text = "packages everytime"; | |||||
// | |||||
// splitContainer3 | |||||
// | |||||
this.splitContainer3.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.splitContainer3.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; | |||||
this.splitContainer3.IsSplitterFixed = true; | |||||
this.splitContainer3.Location = new System.Drawing.Point(0, 0); | |||||
this.splitContainer3.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.splitContainer3.Name = "splitContainer3"; | |||||
this.splitContainer3.Orientation = System.Windows.Forms.Orientation.Horizontal; | |||||
// | |||||
// splitContainer3.Panel1 | |||||
// | |||||
this.splitContainer3.Panel1.Controls.Add(this.label1); | |||||
// | |||||
// splitContainer3.Panel2 | |||||
// | |||||
this.splitContainer3.Panel2.Controls.Add(this.calculationContainer); | |||||
this.splitContainer3.Size = new System.Drawing.Size(688, 301); | |||||
this.splitContainer3.SplitterDistance = 46; | |||||
this.splitContainer3.SplitterWidth = 10; | |||||
this.splitContainer3.TabIndex = 6; | |||||
// | |||||
// label1 | |||||
// | |||||
this.label1.AutoSize = true; | |||||
this.label1.Location = new System.Drawing.Point(5, 12); | |||||
this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0); | |||||
this.label1.Name = "label1"; | |||||
this.label1.Size = new System.Drawing.Size(262, 27); | |||||
this.label1.TabIndex = 0; | |||||
this.label1.Text = "Design evaluation method"; | |||||
// | |||||
// calculationContainer | |||||
// | |||||
this.calculationContainer.AutoScroll = true; | |||||
this.calculationContainer.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.calculationContainer.Location = new System.Drawing.Point(0, 0); | |||||
this.calculationContainer.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.calculationContainer.Name = "calculationContainer"; | |||||
this.calculationContainer.Size = new System.Drawing.Size(688, 245); | |||||
this.calculationContainer.TabIndex = 1; | |||||
// | |||||
// serverSelector | |||||
// | |||||
this.serverSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||||
this.serverSelector.FormattingEnabled = true; | |||||
this.serverSelector.Location = new System.Drawing.Point(801, 67); | |||||
this.serverSelector.Name = "serverSelector"; | |||||
this.serverSelector.Size = new System.Drawing.Size(260, 35); | |||||
this.serverSelector.TabIndex = 6; | |||||
this.serverSelector.SelectedIndexChanged += new System.EventHandler(this.serverSelector_SelectedIndexChanged); | |||||
// | |||||
// CancelButton | |||||
// | |||||
this.CancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||||
this.CancelButton.Location = new System.Drawing.Point(960, 220); | |||||
this.CancelButton.Name = "CancelButton"; | |||||
this.CancelButton.Size = new System.Drawing.Size(101, 41); | |||||
this.CancelButton.TabIndex = 5; | |||||
this.CancelButton.Text = "Cancel"; | |||||
this.CancelButton.UseVisualStyleBackColor = true; | |||||
this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); | |||||
// | |||||
// OKButton | |||||
// | |||||
this.OKButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); | |||||
this.OKButton.Location = new System.Drawing.Point(852, 220); | |||||
this.OKButton.Name = "OKButton"; | |||||
this.OKButton.Size = new System.Drawing.Size(101, 41); | |||||
this.OKButton.TabIndex = 4; | |||||
this.OKButton.Text = "OK"; | |||||
this.OKButton.UseVisualStyleBackColor = true; | |||||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | |||||
// | |||||
// bindingConfiguration | |||||
// | |||||
this.bindingConfiguration.DataSource = typeof(Shadowsocks.Model.StatisticsStrategyConfiguration); | |||||
// | |||||
// StatisticsStrategyConfigurationForm | |||||
// | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 27F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |||||
this.AutoSize = true; | |||||
this.ClientSize = new System.Drawing.Size(1077, 614); | |||||
this.Controls.Add(this.splitContainer1); | |||||
this.Font = new System.Drawing.Font("Microsoft YaHei", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.Margin = new System.Windows.Forms.Padding(5, 10, 5, 10); | |||||
this.MinimumSize = new System.Drawing.Size(1059, 498); | |||||
this.Name = "StatisticsStrategyConfigurationForm"; | |||||
this.Text = "StatisticsStrategyConfigurationForm"; | |||||
((System.ComponentModel.ISupportInitialize)(this.StatisticsChart)).EndInit(); | |||||
this.chartModeSelector.ResumeLayout(false); | |||||
this.chartModeSelector.PerformLayout(); | |||||
this.splitContainer1.Panel1.ResumeLayout(false); | |||||
this.splitContainer1.Panel2.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); | |||||
this.splitContainer1.ResumeLayout(false); | |||||
this.splitContainer2.Panel1.ResumeLayout(false); | |||||
this.splitContainer2.Panel1.PerformLayout(); | |||||
this.splitContainer2.Panel2.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); | |||||
this.splitContainer2.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.dataCollectionMinutesNum)).EndInit(); | |||||
((System.ComponentModel.ISupportInitialize)(this.choiceKeptMinutesNum)).EndInit(); | |||||
((System.ComponentModel.ISupportInitialize)(this.repeatTimesNum)).EndInit(); | |||||
this.splitContainer3.Panel1.ResumeLayout(false); | |||||
this.splitContainer3.Panel1.PerformLayout(); | |||||
this.splitContainer3.Panel2.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer3)).EndInit(); | |||||
this.splitContainer3.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.bindingConfiguration)).EndInit(); | |||||
this.ResumeLayout(false); | |||||
} | |||||
#endregion | |||||
private System.Windows.Forms.DataVisualization.Charting.Chart StatisticsChart; | |||||
private System.Windows.Forms.CheckBox byISPCheckBox; | |||||
private System.Windows.Forms.Label label2; | |||||
private System.Windows.Forms.Label label3; | |||||
private System.Windows.Forms.Label label4; | |||||
private System.Windows.Forms.GroupBox chartModeSelector; | |||||
private System.Windows.Forms.RadioButton allMode; | |||||
private System.Windows.Forms.RadioButton dayMode; | |||||
private System.Windows.Forms.SplitContainer splitContainer1; | |||||
private System.Windows.Forms.Label label1; | |||||
private System.Windows.Forms.SplitContainer splitContainer2; | |||||
private System.Windows.Forms.FlowLayoutPanel calculationContainer; | |||||
private System.Windows.Forms.SplitContainer splitContainer3; | |||||
private System.Windows.Forms.NumericUpDown repeatTimesNum; | |||||
private System.Windows.Forms.Label label6; | |||||
private System.Windows.Forms.CheckBox byHourOfDayCheckBox; | |||||
private System.Windows.Forms.NumericUpDown choiceKeptMinutesNum; | |||||
private System.Windows.Forms.CheckBox StatisticsEnabledCheckBox; | |||||
private System.Windows.Forms.Label label9; | |||||
private System.Windows.Forms.Label label8; | |||||
private System.Windows.Forms.NumericUpDown dataCollectionMinutesNum; | |||||
private System.Windows.Forms.BindingSource bindingConfiguration; | |||||
private new System.Windows.Forms.Button CancelButton; | |||||
private System.Windows.Forms.Button OKButton; | |||||
private System.Windows.Forms.ComboBox serverSelector; | |||||
} | |||||
} |
@@ -0,0 +1,141 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Data; | |||||
using System.Linq; | |||||
using System.Net.NetworkInformation; | |||||
using System.Windows.Forms; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Model; | |||||
namespace Shadowsocks.View | |||||
{ | |||||
public partial class StatisticsStrategyConfigurationForm : Form | |||||
{ | |||||
private readonly ShadowsocksController _controller; | |||||
private StatisticsStrategyConfiguration _configuration; | |||||
private DataTable _dataTable = new DataTable(); | |||||
private List<string> _servers; | |||||
public StatisticsStrategyConfigurationForm(ShadowsocksController controller) | |||||
{ | |||||
if (controller == null) return; | |||||
InitializeComponent(); | |||||
_controller = controller; | |||||
_controller.ConfigChanged += (sender, args) => LoadConfiguration(); | |||||
LoadConfiguration(); | |||||
Load += (sender, args) => InitData(); | |||||
} | |||||
private void LoadConfiguration() | |||||
{ | |||||
var configs = _controller.GetCurrentConfiguration().configs; | |||||
_servers = configs.Select(server => server.FriendlyName()).ToList(); | |||||
_configuration = _controller.StatisticsConfiguration | |||||
?? new StatisticsStrategyConfiguration(); | |||||
if (_configuration.Calculations == null) | |||||
{ | |||||
_configuration = new StatisticsStrategyConfiguration(); | |||||
} | |||||
} | |||||
private void InitData() | |||||
{ | |||||
bindingConfiguration.Add(_configuration); | |||||
foreach (var kv in _configuration.Calculations) | |||||
{ | |||||
var calculation = new CalculationControl(kv.Key, kv.Value); | |||||
calculationContainer.Controls.Add(calculation); | |||||
} | |||||
serverSelector.DataSource = _servers; | |||||
_dataTable.Columns.Add("Timestamp", typeof(DateTime)); | |||||
_dataTable.Columns.Add("Package Loss", typeof(int)); | |||||
_dataTable.Columns.Add("Ping", typeof(int)); | |||||
StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp"; | |||||
StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss"; | |||||
StatisticsChart.Series["Ping"].XValueMember = "Timestamp"; | |||||
StatisticsChart.Series["Ping"].YValueMembers = "Ping"; | |||||
StatisticsChart.DataSource = _dataTable; | |||||
loadChartData(); | |||||
StatisticsChart.DataBind(); | |||||
} | |||||
private void CancelButton_Click(object sender, EventArgs e) | |||||
{ | |||||
Close(); | |||||
} | |||||
private void OKButton_Click(object sender, EventArgs e) | |||||
{ | |||||
foreach (CalculationControl calculation in calculationContainer.Controls) | |||||
{ | |||||
_configuration.Calculations[calculation.Value] = calculation.Factor; | |||||
} | |||||
_controller?.SaveStrategyConfigurations(_configuration); | |||||
_controller?.UpdateStatisticsConfiguration(StatisticsEnabledCheckBox.Checked); | |||||
Close(); | |||||
} | |||||
private void loadChartData() | |||||
{ | |||||
string serverName = _servers[serverSelector.SelectedIndex]; | |||||
_dataTable.Rows.Clear(); | |||||
//return directly when no data is usable | |||||
if (_controller.availabilityStatistics?.FilteredStatistics == null) return; | |||||
List<AvailabilityStatistics.RawStatisticsData> statistics; | |||||
if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return; | |||||
IEnumerable<IGrouping<int, AvailabilityStatistics.RawStatisticsData>> dataGroups; | |||||
if (allMode.Checked) | |||||
{ | |||||
dataGroups = statistics.GroupBy(data => data.Timestamp.DayOfYear); | |||||
StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "MM/dd/yyyy"; | |||||
StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "MM/dd/yyyy"; | |||||
} | |||||
else | |||||
{ | |||||
dataGroups = statistics.GroupBy(data => data.Timestamp.Hour); | |||||
StatisticsChart.ChartAreas["DataArea"].AxisX.LabelStyle.Format = "HH:00"; | |||||
StatisticsChart.ChartAreas["DataArea"].AxisX2.LabelStyle.Format = "HH:00"; | |||||
} | |||||
var finalData = from dataGroup in dataGroups | |||||
orderby dataGroup.Key | |||||
select new | |||||
{ | |||||
Timestamp = dataGroup.First().Timestamp, | |||||
Ping = (int)dataGroup.Average(data => data.RoundtripTime), | |||||
PackageLoss = (int) | |||||
(dataGroup.Count(data => data.ICMPStatus.Equals(IPStatus.TimedOut.ToString())) | |||||
/ (float)dataGroup.Count() * 100) | |||||
}; | |||||
foreach (var data in finalData) | |||||
{ | |||||
_dataTable.Rows.Add(data.Timestamp, data.PackageLoss, data.Ping); | |||||
} | |||||
StatisticsChart.DataBind(); | |||||
} | |||||
private void serverSelector_SelectedIndexChanged(object sender, EventArgs e) | |||||
{ | |||||
loadChartData(); | |||||
} | |||||
private void chartModeSelector_Enter(object sender, EventArgs e) | |||||
{ | |||||
} | |||||
private void dayMode_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
loadChartData(); | |||||
} | |||||
private void allMode_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
loadChartData(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,126 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<root> | |||||
<!-- | |||||
Microsoft ResX Schema | |||||
Version 2.0 | |||||
The primary goals of this format is to allow a simple XML format | |||||
that is mostly human readable. The generation and parsing of the | |||||
various data types are done through the TypeConverter classes | |||||
associated with the data types. | |||||
Example: | |||||
... ado.net/XML headers & schema ... | |||||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||||
<resheader name="version">2.0</resheader> | |||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||||
</data> | |||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||||
<comment>This is a comment</comment> | |||||
</data> | |||||
There are any number of "resheader" rows that contain simple | |||||
name/value pairs. | |||||
Each data row contains a name, and value. The row also contains a | |||||
type or mimetype. Type corresponds to a .NET class that support | |||||
text/value conversion through the TypeConverter architecture. | |||||
Classes that don't support this are serialized and stored with the | |||||
mimetype set. | |||||
The mimetype is used for serialized objects, and tells the | |||||
ResXResourceReader how to depersist the object. This is currently not | |||||
extensible. For a given mimetype the value must be set accordingly: | |||||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||||
that the ResXResourceWriter will generate, however the reader can | |||||
read any of the formats listed below. | |||||
mimetype: application/x-microsoft.net.object.binary.base64 | |||||
value : The object must be serialized with | |||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |||||
: and then encoded with base64 encoding. | |||||
mimetype: application/x-microsoft.net.object.soap.base64 | |||||
value : The object must be serialized with | |||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||||
: and then encoded with base64 encoding. | |||||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||||
value : The object must be serialized into a byte array | |||||
: using a System.ComponentModel.TypeConverter | |||||
: and then encoded with base64 encoding. | |||||
--> | |||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |||||
<xsd:element name="root" msdata:IsDataSet="true"> | |||||
<xsd:complexType> | |||||
<xsd:choice maxOccurs="unbounded"> | |||||
<xsd:element name="metadata"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" use="required" type="xsd:string" /> | |||||
<xsd:attribute name="type" type="xsd:string" /> | |||||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||||
<xsd:attribute ref="xml:space" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="assembly"> | |||||
<xsd:complexType> | |||||
<xsd:attribute name="alias" type="xsd:string" /> | |||||
<xsd:attribute name="name" type="xsd:string" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="data"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||||
<xsd:attribute ref="xml:space" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
<xsd:element name="resheader"> | |||||
<xsd:complexType> | |||||
<xsd:sequence> | |||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||||
</xsd:sequence> | |||||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
</xsd:choice> | |||||
</xsd:complexType> | |||||
</xsd:element> | |||||
</xsd:schema> | |||||
<resheader name="resmimetype"> | |||||
<value>text/microsoft-resx</value> | |||||
</resheader> | |||||
<resheader name="version"> | |||||
<value>2.0</value> | |||||
</resheader> | |||||
<resheader name="reader"> | |||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | |||||
<resheader name="writer"> | |||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||||
</resheader> | |||||
<metadata name="bindingConfiguration.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||||
<value>1, 30</value> | |||||
</metadata> | |||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>63</value> | |||||
</metadata> | |||||
</root> |
@@ -1,6 +1,19 @@ | |||||
<?xml version="1.0"?> | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<configuration> | <configuration> | ||||
<startup> | <startup> | ||||
<supportedRuntime version="v4.0"/> | |||||
<supportedRuntime version="v2.0.50727"/> | |||||
</startup></configuration> | |||||
<supportedRuntime version="v4.0" /> | |||||
<supportedRuntime version="v2.0.50727" /> | |||||
</startup> | |||||
<runtime> | |||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> | |||||
<dependentAssembly> | |||||
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |||||
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" /> | |||||
</dependentAssembly> | |||||
<dependentAssembly> | |||||
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |||||
<bindingRedirect oldVersion="0.0.0.0-2.6.10.0" newVersion="2.6.10.0" /> | |||||
</dependentAssembly> | |||||
</assemblyBinding> | |||||
</runtime> | |||||
</configuration> |
@@ -10,7 +10,7 @@ | |||||
</trustInfo> | </trustInfo> | ||||
<asmv3:application> | <asmv3:application> | ||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> | <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> | ||||
<dpiAware>true</dpiAware> | |||||
<dpiAware>True/PM</dpiAware> | |||||
</asmv3:windowsSettings> | </asmv3:windowsSettings> | ||||
</asmv3:application> | </asmv3:application> | ||||
</assembly> | </assembly> |
@@ -0,0 +1,11 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<packages> | |||||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" /> | |||||
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | |||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net40-client" /> | |||||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40-client" /> | |||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net40-client" /> | |||||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net4-client" /> | |||||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net40-client" /> | |||||
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net40-client" /> | |||||
</packages> |
@@ -36,6 +36,8 @@ | |||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion> | <ApplicationVersion>1.0.0.%2a</ApplicationVersion> | ||||
<UseApplicationTrust>false</UseApplicationTrust> | <UseApplicationTrust>false</UseApplicationTrust> | ||||
<BootstrapperEnabled>true</BootstrapperEnabled> | <BootstrapperEnabled>true</BootstrapperEnabled> | ||||
<NuGetPackageImportStamp> | |||||
</NuGetPackageImportStamp> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | ||||
<DebugSymbols>true</DebugSymbols> | <DebugSymbols>true</DebugSymbols> | ||||
@@ -46,6 +48,7 @@ | |||||
<ErrorReport>prompt</ErrorReport> | <ErrorReport>prompt</ErrorReport> | ||||
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet> | <CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet> | ||||
<Prefer32Bit>false</Prefer32Bit> | <Prefer32Bit>false</Prefer32Bit> | ||||
<Optimize>false</Optimize> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> | <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> | ||||
<OutputPath>bin\x86\Release\</OutputPath> | <OutputPath>bin\x86\Release\</OutputPath> | ||||
@@ -62,12 +65,57 @@ | |||||
<ApplicationManifest>app.manifest</ApplicationManifest> | <ApplicationManifest>app.manifest</ApplicationManifest> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Reference Include="Microsoft.CSharp" /> | |||||
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.VisualBasic" /> | <Reference Include="Microsoft.VisualBasic" /> | ||||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="PresentationCore" /> | |||||
<Reference Include="PresentationFramework" /> | |||||
<Reference Include="System" /> | <Reference Include="System" /> | ||||
<Reference Include="System.Data" /> | <Reference Include="System.Data" /> | ||||
<Reference Include="System.Drawing" /> | <Reference Include="System.Drawing" /> | ||||
<Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Net" /> | |||||
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Net.Http.WebRequest, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Windows.Forms" /> | <Reference Include="System.Windows.Forms" /> | ||||
<Reference Include="System.Windows.Forms.DataVisualization" /> | |||||
<Reference Include="System.Xaml" /> | |||||
<Reference Include="System.XML" /> | <Reference Include="System.XML" /> | ||||
<Reference Include="UIAutomationProvider" /> | |||||
<Reference Include="WindowsBase" /> | |||||
<Reference Include="WindowsFormsIntegration" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Include="3rd\zxing\BarcodeFormat.cs" /> | <Compile Include="3rd\zxing\BarcodeFormat.cs" /> | ||||
@@ -116,7 +164,6 @@ | |||||
<Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" /> | <Compile Include="3rd\zxing\qrcode\encoder\MaskUtil.cs" /> | ||||
<Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.cs" /> | <Compile Include="3rd\zxing\qrcode\encoder\MatrixUtil.cs" /> | ||||
<Compile Include="3rd\zxing\qrcode\encoder\QRCode.cs" /> | <Compile Include="3rd\zxing\qrcode\encoder\QRCode.cs" /> | ||||
<Compile Include="3rd\SimpleJson.cs" /> | |||||
<Compile Include="3rd\zxing\qrcode\QRCodeReader.cs" /> | <Compile Include="3rd\zxing\qrcode\QRCodeReader.cs" /> | ||||
<Compile Include="3rd\zxing\Result.cs" /> | <Compile Include="3rd\zxing\Result.cs" /> | ||||
<Compile Include="3rd\zxing\ResultMetadataType.cs" /> | <Compile Include="3rd\zxing\ResultMetadataType.cs" /> | ||||
@@ -125,7 +172,7 @@ | |||||
<Compile Include="3rd\zxing\WriterException.cs" /> | <Compile Include="3rd\zxing\WriterException.cs" /> | ||||
<Compile Include="Controller\Service\AvailabilityStatistics.cs" /> | <Compile Include="Controller\Service\AvailabilityStatistics.cs" /> | ||||
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" /> | <Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" /> | ||||
<Compile Include="Controller\Strategy\SimplyChooseByStatisticsStrategy.cs" /> | |||||
<Compile Include="Controller\Strategy\StatisticsStrategy.cs" /> | |||||
<Compile Include="Controller\System\AutoStartup.cs" /> | <Compile Include="Controller\System\AutoStartup.cs" /> | ||||
<Compile Include="Controller\FileManager.cs" /> | <Compile Include="Controller\FileManager.cs" /> | ||||
<Compile Include="Controller\Service\GFWListUpdater.cs" /> | <Compile Include="Controller\Service\GFWListUpdater.cs" /> | ||||
@@ -138,15 +185,17 @@ | |||||
<Compile Include="Encryption\EncryptorBase.cs" /> | <Compile Include="Encryption\EncryptorBase.cs" /> | ||||
<Compile Include="Encryption\EncryptorFactory.cs" /> | <Compile Include="Encryption\EncryptorFactory.cs" /> | ||||
<Compile Include="Encryption\IVEncryptor.cs" /> | <Compile Include="Encryption\IVEncryptor.cs" /> | ||||
<Compile Include="Encryption\MbedTLS.cs" /> | |||||
<Compile Include="Encryption\PolarSSL.cs" /> | <Compile Include="Encryption\PolarSSL.cs" /> | ||||
<Compile Include="Encryption\PolarSSLEncryptor.cs" /> | <Compile Include="Encryption\PolarSSLEncryptor.cs" /> | ||||
<Compile Include="Encryption\Sodium.cs" /> | <Compile Include="Encryption\Sodium.cs" /> | ||||
<Compile Include="Encryption\SodiumEncryptor.cs" /> | <Compile Include="Encryption\SodiumEncryptor.cs" /> | ||||
<Compile Include="Encryption\TableEncryptor.cs" /> | |||||
<Compile Include="Encryption\IEncryptor.cs" /> | <Compile Include="Encryption\IEncryptor.cs" /> | ||||
<Compile Include="Controller\Service\PACServer.cs" /> | <Compile Include="Controller\Service\PACServer.cs" /> | ||||
<Compile Include="Model\LogViewerConfig.cs" /> | |||||
<Compile Include="Model\Server.cs" /> | <Compile Include="Model\Server.cs" /> | ||||
<Compile Include="Model\Configuration.cs" /> | <Compile Include="Model\Configuration.cs" /> | ||||
<Compile Include="Model\StatisticsStrategyConfiguration.cs" /> | |||||
<Compile Include="Properties\Resources.Designer.cs"> | <Compile Include="Properties\Resources.Designer.cs"> | ||||
<AutoGen>True</AutoGen> | <AutoGen>True</AutoGen> | ||||
<DesignTime>True</DesignTime> | <DesignTime>True</DesignTime> | ||||
@@ -168,6 +217,12 @@ | |||||
<Compile Include="Properties\AssemblyInfo.cs" /> | <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
<Compile Include="Controller\ShadowsocksController.cs" /> | <Compile Include="Controller\ShadowsocksController.cs" /> | ||||
<Compile Include="Controller\System\SystemProxy.cs" /> | <Compile Include="Controller\System\SystemProxy.cs" /> | ||||
<Compile Include="View\CalculationControl.cs"> | |||||
<SubType>UserControl</SubType> | |||||
</Compile> | |||||
<Compile Include="View\CalculationControl.Designer.cs"> | |||||
<DependentUpon>CalculationControl.cs</DependentUpon> | |||||
</Compile> | |||||
<Compile Include="View\LogForm.cs"> | <Compile Include="View\LogForm.cs"> | ||||
<SubType>Form</SubType> | <SubType>Form</SubType> | ||||
</Compile> | </Compile> | ||||
@@ -184,6 +239,12 @@ | |||||
<Compile Include="View\QRCodeSplashForm.cs"> | <Compile Include="View\QRCodeSplashForm.cs"> | ||||
<SubType>Form</SubType> | <SubType>Form</SubType> | ||||
</Compile> | </Compile> | ||||
<Compile Include="View\StatisticsStrategyConfigurationForm.cs"> | |||||
<SubType>Form</SubType> | |||||
</Compile> | |||||
<Compile Include="View\StatisticsStrategyConfigurationForm.Designer.cs"> | |||||
<DependentUpon>StatisticsStrategyConfigurationForm.cs</DependentUpon> | |||||
</Compile> | |||||
<EmbeddedResource Include="View\ConfigForm.resx"> | <EmbeddedResource Include="View\ConfigForm.resx"> | ||||
<DependentUpon>ConfigForm.cs</DependentUpon> | <DependentUpon>ConfigForm.cs</DependentUpon> | ||||
<SubType>Designer</SubType> | <SubType>Designer</SubType> | ||||
@@ -193,12 +254,18 @@ | |||||
<SubType>Designer</SubType> | <SubType>Designer</SubType> | ||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput> | <LastGenOutput>Resources.Designer.cs</LastGenOutput> | ||||
</EmbeddedResource> | </EmbeddedResource> | ||||
<EmbeddedResource Include="View\CalculationControl.resx"> | |||||
<DependentUpon>CalculationControl.cs</DependentUpon> | |||||
</EmbeddedResource> | |||||
<EmbeddedResource Include="View\LogForm.resx"> | <EmbeddedResource Include="View\LogForm.resx"> | ||||
<DependentUpon>LogForm.cs</DependentUpon> | <DependentUpon>LogForm.cs</DependentUpon> | ||||
</EmbeddedResource> | </EmbeddedResource> | ||||
<EmbeddedResource Include="View\QRCodeForm.resx"> | <EmbeddedResource Include="View\QRCodeForm.resx"> | ||||
<DependentUpon>QRCodeForm.cs</DependentUpon> | <DependentUpon>QRCodeForm.cs</DependentUpon> | ||||
</EmbeddedResource> | </EmbeddedResource> | ||||
<EmbeddedResource Include="View\StatisticsStrategyConfigurationForm.resx"> | |||||
<DependentUpon>StatisticsStrategyConfigurationForm.cs</DependentUpon> | |||||
</EmbeddedResource> | |||||
<None Include="app.config" /> | <None Include="app.config" /> | ||||
<None Include="app.manifest"> | <None Include="app.manifest"> | ||||
<SubType>Designer</SubType> | <SubType>Designer</SubType> | ||||
@@ -210,6 +277,8 @@ | |||||
<None Include="Data\proxy.pac.txt.gz" /> | <None Include="Data\proxy.pac.txt.gz" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="packages.config" /> | |||||
<None Include="Properties\DataSources\Shadowsocks.Model.StatisticsStrategyConfiguration.datasource" /> | |||||
<None Include="Resources\ss20.png" /> | <None Include="Resources\ss20.png" /> | ||||
<None Include="Resources\ss16.png" /> | <None Include="Resources\ss16.png" /> | ||||
<None Include="Resources\ss24.png" /> | <None Include="Resources\ss24.png" /> | ||||
@@ -217,6 +286,9 @@ | |||||
<Content Include="Data\cn.txt" /> | <Content Include="Data\cn.txt" /> | ||||
<Content Include="Data\privoxy_conf.txt" /> | <Content Include="Data\privoxy_conf.txt" /> | ||||
<Content Include="Data\user-rule.txt" /> | <Content Include="Data\user-rule.txt" /> | ||||
<Content Include="FodyWeavers.xml"> | |||||
<SubType>Designer</SubType> | |||||
</Content> | |||||
<Content Include="shadowsocks.ico" /> | <Content Include="shadowsocks.ico" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -252,6 +324,15 @@ | |||||
</BootstrapperPackage> | </BootstrapperPackage> | ||||
</ItemGroup> | </ItemGroup> | ||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | |||||
<PropertyGroup> | |||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | |||||
</PropertyGroup> | |||||
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> | |||||
<Error Condition="!Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets'))" /> | |||||
</Target> | |||||
<Import Project="3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> | |||||
<Import Project="3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" /> | |||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||||
Other similar extension points exist, see Microsoft.Common.targets. | Other similar extension points exist, see Microsoft.Common.targets. | ||||
<Target Name="BeforeBuild"> | <Target Name="BeforeBuild"> | ||||
@@ -13,19 +13,34 @@ namespace test | |||||
[TestMethod] | [TestMethod] | ||||
public void TestCompareVersion() | public void TestCompareVersion() | ||||
{ | { | ||||
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1.0", "2.3.1") == 0); | |||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.2", "1.3") < 0); | |||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.2") > 0); | |||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3", "1.3") == 0); | |||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.2.1", "1.2") > 0); | |||||
Assert.IsTrue(UpdateChecker.CompareVersion("2.3.1", "2.4") < 0); | |||||
Assert.IsTrue(UpdateChecker.CompareVersion("1.3.2", "1.3.1") > 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1.0", "2.3.1") == 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2", "1.3") < 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.2") > 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3", "1.3") == 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.2.1", "1.2") > 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("2.3.1", "2.4") < 0); | |||||
Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); | |||||
} | |||||
[TestMethod] | |||||
public void TestMD5() | |||||
{ | |||||
for (int len = 1; len < 64; len++) | |||||
{ | |||||
System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); | |||||
byte[] bytes = new byte[len]; | |||||
var random = new Random(); | |||||
random.NextBytes(bytes); | |||||
string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); | |||||
string md5str2 = Convert.ToBase64String(MbedTLS.MD5(bytes)); | |||||
Assert.IsTrue(md5str == md5str2); | |||||
} | |||||
} | } | ||||
private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) | private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) | ||||
{ | { | ||||
byte[] plain = new byte[16384]; | byte[] plain = new byte[16384]; | ||||
byte[] cipher = new byte[plain.Length + 16]; | |||||
byte[] cipher = new byte[plain.Length + 16 + IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES]; | |||||
byte[] plain2 = new byte[plain.Length + 16]; | byte[] plain2 = new byte[plain.Length + 16]; | ||||
int outLen = 0; | int outLen = 0; | ||||
int outLen2 = 0; | int outLen2 = 0; | ||||
@@ -84,8 +99,8 @@ namespace test | |||||
{ | { | ||||
IEncryptor encryptor; | IEncryptor encryptor; | ||||
IEncryptor decryptor; | IEncryptor decryptor; | ||||
encryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!"); | |||||
decryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!"); | |||||
encryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!", false, false); | |||||
decryptor = new PolarSSLEncryptor("aes-256-cfb", "barfoo!", false, false); | |||||
RunEncryptionRound(encryptor, decryptor); | RunEncryptionRound(encryptor, decryptor); | ||||
} | } | ||||
} | } | ||||
@@ -124,8 +139,8 @@ namespace test | |||||
var random = new Random(); | var random = new Random(); | ||||
IEncryptor encryptor; | IEncryptor encryptor; | ||||
IEncryptor decryptor; | IEncryptor decryptor; | ||||
encryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!"); | |||||
decryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!"); | |||||
encryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!", false, false); | |||||
decryptor = new PolarSSLEncryptor("rc4-md5", "barfoo!", false, false); | |||||
RunEncryptionRound(encryptor, decryptor); | RunEncryptionRound(encryptor, decryptor); | ||||
} | } | ||||
} | } | ||||
@@ -164,8 +179,8 @@ namespace test | |||||
var random = new Random(); | var random = new Random(); | ||||
IEncryptor encryptor; | IEncryptor encryptor; | ||||
IEncryptor decryptor; | IEncryptor decryptor; | ||||
encryptor = new SodiumEncryptor("salsa20", "barfoo!"); | |||||
decryptor = new SodiumEncryptor("salsa20", "barfoo!"); | |||||
encryptor = new SodiumEncryptor("salsa20", "barfoo!", false, false); | |||||
decryptor = new SodiumEncryptor("salsa20", "barfoo!", false, false); | |||||
RunEncryptionRound(encryptor, decryptor); | RunEncryptionRound(encryptor, decryptor); | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,8 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<packages> | |||||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> | |||||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> | |||||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> | |||||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> | |||||
<package id="System.Net.Http" version="4.0.0" targetFramework="net45" /> | |||||
</packages> |
@@ -17,6 +17,8 @@ | |||||
<IsCodedUITest>False</IsCodedUITest> | <IsCodedUITest>False</IsCodedUITest> | ||||
<TestProjectType>UnitTest</TestProjectType> | <TestProjectType>UnitTest</TestProjectType> | ||||
<TargetFrameworkProfile /> | <TargetFrameworkProfile /> | ||||
<NuGetPackageImportStamp> | |||||
</NuGetPackageImportStamp> | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | ||||
<OutputPath>bin\x86\Debug\</OutputPath> | <OutputPath>bin\x86\Debug\</OutputPath> | ||||
@@ -30,7 +32,30 @@ | |||||
<StartupObject /> | <StartupObject /> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System" /> | <Reference Include="System" /> | ||||
<Reference Include="System.Net" /> | |||||
<Reference Include="System.Net.Http" /> | |||||
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> | |||||
<Private>True</Private> | |||||
</Reference> | |||||
<Reference Include="System.Net.Http.WebRequest" /> | |||||
</ItemGroup> | </ItemGroup> | ||||
<Choose> | <Choose> | ||||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'"> | ||||
@@ -54,6 +79,9 @@ | |||||
<Name>shadowsocks-csharp</Name> | <Name>shadowsocks-csharp</Name> | ||||
</ProjectReference> | </ProjectReference> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<None Include="packages.config" /> | |||||
</ItemGroup> | |||||
<Choose> | <Choose> | ||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -74,6 +102,13 @@ | |||||
</Choose> | </Choose> | ||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | ||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
<Import Project="..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> | |||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | |||||
<PropertyGroup> | |||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | |||||
</PropertyGroup> | |||||
<Error Condition="!Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> | |||||
</Target> | |||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||||
Other similar extension points exist, see Microsoft.Common.targets. | Other similar extension points exist, see Microsoft.Common.targets. | ||||
<Target Name="BeforeBuild"> | <Target Name="BeforeBuild"> | ||||