@@ -1,10 +0,0 @@ | |||||
using CommandLine; | |||||
namespace Shadowsocks | |||||
{ | |||||
public class CommandLineOption | |||||
{ | |||||
[Option("open-url",Required = false,HelpText = "Add an ss:// URL")] | |||||
public string OpenUrl { get; set; } | |||||
} | |||||
} |
@@ -1,733 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Net.Http; | |||||
using System.Net.Sockets; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using System.Web; | |||||
using System.Windows.Forms; | |||||
using NLog; | |||||
using Shadowsocks.Controller.Service; | |||||
using Shadowsocks.Controller.Strategy; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Util; | |||||
using WPFLocalizeExtension.Engine; | |||||
namespace Shadowsocks.Controller | |||||
{ | |||||
public class ShadowsocksController | |||||
{ | |||||
private readonly Logger logger; | |||||
private readonly HttpClient httpClient; | |||||
// controller: | |||||
// handle user actions | |||||
// manipulates UI | |||||
// interacts with low level logic | |||||
#region Member definition | |||||
private Thread _trafficThread; | |||||
private TCPListener _tcpListener; | |||||
private UDPListener _udpListener; | |||||
private PACDaemon _pacDaemon; | |||||
private PACServer _pacServer; | |||||
private Configuration _config; | |||||
private StrategyManager _strategyManager; | |||||
private PrivoxyRunner privoxyRunner; | |||||
private readonly ConcurrentDictionary<Server, Sip003Plugin> _pluginsByServer; | |||||
private long _inboundCounter = 0; | |||||
private long _outboundCounter = 0; | |||||
public long InboundCounter => Interlocked.Read(ref _inboundCounter); | |||||
public long OutboundCounter => Interlocked.Read(ref _outboundCounter); | |||||
public Queue<TrafficPerSecond> trafficPerSecondQueue; | |||||
private bool stopped = false; | |||||
public class PathEventArgs : EventArgs | |||||
{ | |||||
public string Path; | |||||
} | |||||
public class UpdatedEventArgs : EventArgs | |||||
{ | |||||
public string OldVersion; | |||||
public string NewVersion; | |||||
} | |||||
public class TrafficPerSecond | |||||
{ | |||||
public long inboundCounter; | |||||
public long outboundCounter; | |||||
public long inboundIncreasement; | |||||
public long outboundIncreasement; | |||||
} | |||||
public event EventHandler ConfigChanged; | |||||
public event EventHandler EnableStatusChanged; | |||||
public event EventHandler EnableGlobalChanged; | |||||
public event EventHandler ShareOverLANStatusChanged; | |||||
public event EventHandler VerboseLoggingStatusChanged; | |||||
public event EventHandler ShowPluginOutputChanged; | |||||
public event EventHandler TrafficChanged; | |||||
// when user clicked Edit PAC, and PAC file has already created | |||||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | |||||
public event EventHandler<PathEventArgs> UserRuleFileReadyToOpen; | |||||
public event EventHandler<GeositeResultEventArgs> UpdatePACFromGeositeCompleted; | |||||
public event ErrorEventHandler UpdatePACFromGeositeError; | |||||
public event ErrorEventHandler Errored; | |||||
// Invoked when controller.Start(); | |||||
public event EventHandler<UpdatedEventArgs> ProgramUpdated; | |||||
#endregion | |||||
public ShadowsocksController() | |||||
{ | |||||
logger = LogManager.GetCurrentClassLogger(); | |||||
httpClient = new HttpClient(); | |||||
_config = Configuration.Load(); | |||||
Configuration.Process(ref _config); | |||||
_strategyManager = new StrategyManager(this); | |||||
_pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | |||||
StartTrafficStatistics(61); | |||||
ProgramUpdated += (o, e) => | |||||
{ | |||||
// version update precedures | |||||
if (e.OldVersion == "4.3.0.0" || e.OldVersion == "4.3.1.0") | |||||
_config.geositeDirectGroups.Add("private"); | |||||
logger.Info($"Updated from {e.OldVersion} to {e.NewVersion}"); | |||||
}; | |||||
} | |||||
#region Basic | |||||
public void Start(bool systemWakeUp = false) | |||||
{ | |||||
if (_config.firstRunOnNewVersion && !systemWakeUp) | |||||
{ | |||||
ProgramUpdated.Invoke(this, new UpdatedEventArgs() | |||||
{ | |||||
OldVersion = _config.version, | |||||
NewVersion = UpdateChecker.Version, | |||||
}); | |||||
// delete pac.txt when regeneratePacOnUpdate is true | |||||
if (_config.regeneratePacOnUpdate) | |||||
try | |||||
{ | |||||
File.Delete(PACDaemon.PAC_FILE); | |||||
logger.Info("Deleted pac.txt from previous version."); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
logger.LogUsefulException(e); | |||||
} | |||||
// finish up first run of new version | |||||
_config.firstRunOnNewVersion = false; | |||||
_config.version = UpdateChecker.Version; | |||||
Configuration.Save(_config); | |||||
} | |||||
Reload(); | |||||
if (!systemWakeUp) | |||||
HotkeyReg.RegAllHotkeys(); | |||||
} | |||||
public void Stop() | |||||
{ | |||||
if (stopped) | |||||
{ | |||||
return; | |||||
} | |||||
stopped = true; | |||||
_tcpListener?.Stop(); | |||||
_udpListener?.Stop(); | |||||
StopPlugins(); | |||||
if (privoxyRunner != null) | |||||
{ | |||||
privoxyRunner.Stop(); | |||||
} | |||||
if (_config.enabled) | |||||
{ | |||||
SystemProxy.Update(_config, true, null); | |||||
} | |||||
} | |||||
protected void Reload() | |||||
{ | |||||
Encryption.RNG.Reload(); | |||||
// some logic in configuration updated the config when saving, we need to read it again | |||||
_config = Configuration.Load(); | |||||
Configuration.Process(ref _config); | |||||
NLogConfig.LoadConfiguration(); | |||||
logger.Info($"WPF Localization Extension|Current culture: {LocalizeDictionary.CurrentCulture}"); | |||||
// set User-Agent for httpClient | |||||
try | |||||
{ | |||||
if (!string.IsNullOrWhiteSpace(_config.userAgentString)) | |||||
httpClient.DefaultRequestHeaders.Add("User-Agent", _config.userAgentString); | |||||
} | |||||
catch | |||||
{ | |||||
// reset userAgent to default and reapply | |||||
Configuration.ResetUserAgent(_config); | |||||
httpClient.DefaultRequestHeaders.Add("User-Agent", _config.userAgentString); | |||||
} | |||||
privoxyRunner = privoxyRunner ?? new PrivoxyRunner(); | |||||
_pacDaemon = _pacDaemon ?? new PACDaemon(_config); | |||||
_pacDaemon.PACFileChanged += PacDaemon_PACFileChanged; | |||||
_pacDaemon.UserRuleFileChanged += PacDaemon_UserRuleFileChanged; | |||||
_pacServer = _pacServer ?? new PACServer(_pacDaemon); | |||||
_pacServer.UpdatePACURL(_config); // So PACServer works when system proxy disabled. | |||||
GeositeUpdater.ResetEvent(); | |||||
GeositeUpdater.UpdateCompleted += PacServer_PACUpdateCompleted; | |||||
GeositeUpdater.Error += PacServer_PACUpdateError; | |||||
_tcpListener?.Stop(); | |||||
_udpListener?.Stop(); | |||||
StopPlugins(); | |||||
// don't put PrivoxyRunner.Start() before pacServer.Stop() | |||||
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1 | |||||
// though UseShellExecute is set to true now | |||||
// http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open | |||||
privoxyRunner.Stop(); | |||||
try | |||||
{ | |||||
var strategy = GetCurrentStrategy(); | |||||
strategy?.ReloadServers(); | |||||
StartPlugin(); | |||||
privoxyRunner.Start(_config); | |||||
TCPRelay tcpRelay = new TCPRelay(this, _config); | |||||
tcpRelay.OnInbound += UpdateInboundCounter; | |||||
tcpRelay.OnOutbound += UpdateOutboundCounter; | |||||
tcpRelay.OnFailed += (o, e) => GetCurrentStrategy()?.SetFailure(e.server); | |||||
UDPRelay udpRelay = new UDPRelay(this); | |||||
_tcpListener = new TCPListener(_config, new List<IStreamService> | |||||
{ | |||||
tcpRelay, | |||||
_pacServer, | |||||
new PortForwarder(privoxyRunner.RunningPort), | |||||
}); | |||||
_tcpListener.Start(); | |||||
_udpListener = new UDPListener(_config, new List<IDatagramService> | |||||
{ | |||||
udpRelay, | |||||
}); | |||||
_udpListener.Start(); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
// translate Microsoft language into human language | |||||
// i.e. An attempt was made to access a socket in a way forbidden by its access permissions => Port already in use | |||||
if (e is SocketException se) | |||||
{ | |||||
if (se.SocketErrorCode == SocketError.AddressAlreadyInUse) | |||||
{ | |||||
e = new Exception(I18N.GetString("Port {0} already in use", _config.localPort), e); | |||||
} | |||||
else if (se.SocketErrorCode == SocketError.AccessDenied) | |||||
{ | |||||
e = new Exception(I18N.GetString("Port {0} is reserved by system", _config.localPort), e); | |||||
} | |||||
} | |||||
logger.LogUsefulException(e); | |||||
ReportError(e); | |||||
} | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
UpdateSystemProxy(); | |||||
} | |||||
protected void SaveConfig(Configuration newConfig) | |||||
{ | |||||
Configuration.Save(newConfig); | |||||
Reload(); | |||||
} | |||||
protected void ReportError(Exception e) | |||||
{ | |||||
Errored?.Invoke(this, new ErrorEventArgs(e)); | |||||
} | |||||
public HttpClient GetHttpClient() => httpClient; | |||||
public Server GetCurrentServer() => _config.GetCurrentServer(); | |||||
public Configuration GetCurrentConfiguration() => _config; | |||||
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint, EndPoint destEndPoint) | |||||
{ | |||||
IStrategy strategy = GetCurrentStrategy(); | |||||
if (strategy != null) | |||||
{ | |||||
return strategy.GetAServer(type, localIPEndPoint, destEndPoint); | |||||
} | |||||
if (_config.index < 0) | |||||
{ | |||||
_config.index = 0; | |||||
} | |||||
return GetCurrentServer(); | |||||
} | |||||
public void SaveServers(List<Server> servers, int localPort, bool portableMode) | |||||
{ | |||||
_config.configs = servers; | |||||
_config.localPort = localPort; | |||||
_config.portableMode = portableMode; | |||||
Configuration.Save(_config); | |||||
} | |||||
public void SelectServerIndex(int index) | |||||
{ | |||||
_config.index = index; | |||||
_config.strategy = null; | |||||
SaveConfig(_config); | |||||
} | |||||
public void ToggleShareOverLAN(bool enabled) | |||||
{ | |||||
_config.shareOverLan = enabled; | |||||
SaveConfig(_config); | |||||
ShareOverLANStatusChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
#endregion | |||||
#region OS Proxy | |||||
public void ToggleEnable(bool enabled) | |||||
{ | |||||
_config.enabled = enabled; | |||||
SaveConfig(_config); | |||||
EnableStatusChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void ToggleGlobal(bool global) | |||||
{ | |||||
_config.global = global; | |||||
SaveConfig(_config); | |||||
EnableGlobalChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void SaveProxy(ForwardProxyConfig proxyConfig) | |||||
{ | |||||
_config.proxy = proxyConfig; | |||||
SaveConfig(_config); | |||||
} | |||||
private void UpdateSystemProxy() | |||||
{ | |||||
SystemProxy.Update(_config, false, _pacServer); | |||||
} | |||||
#endregion | |||||
#region PAC | |||||
private void PacDaemon_PACFileChanged(object sender, EventArgs e) | |||||
{ | |||||
UpdateSystemProxy(); | |||||
} | |||||
private void PacServer_PACUpdateCompleted(object sender, GeositeResultEventArgs e) | |||||
{ | |||||
UpdatePACFromGeositeCompleted?.Invoke(this, e); | |||||
} | |||||
private void PacServer_PACUpdateError(object sender, ErrorEventArgs e) | |||||
{ | |||||
UpdatePACFromGeositeError?.Invoke(this, e); | |||||
} | |||||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||||
private void PacDaemon_UserRuleFileChanged(object sender, EventArgs e) | |||||
{ | |||||
GeositeUpdater.MergeAndWritePACFile(_config.geositeDirectGroups, _config.geositeProxiedGroups, _config.geositePreferDirect); | |||||
UpdateSystemProxy(); | |||||
} | |||||
public void CopyPacUrl() | |||||
{ | |||||
Clipboard.SetDataObject(_pacServer.PacUrl); | |||||
} | |||||
public void SavePACUrl(string pacUrl) | |||||
{ | |||||
_config.pacUrl = pacUrl; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void UseOnlinePAC(bool useOnlinePac) | |||||
{ | |||||
_config.useOnlinePac = useOnlinePac; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void TouchPACFile() | |||||
{ | |||||
string pacFilename = _pacDaemon.TouchPACFile(); | |||||
PACFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = pacFilename }); | |||||
} | |||||
public void TouchUserRuleFile() | |||||
{ | |||||
string userRuleFilename = _pacDaemon.TouchUserRuleFile(); | |||||
UserRuleFileReadyToOpen?.Invoke(this, new PathEventArgs() { Path = userRuleFilename }); | |||||
} | |||||
public void ToggleSecureLocalPac(bool enabled) | |||||
{ | |||||
_config.secureLocalPac = enabled; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void ToggleRegeneratePacOnUpdate(bool enabled) | |||||
{ | |||||
_config.regeneratePacOnUpdate = enabled; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
#endregion | |||||
#region SIP002 | |||||
public bool AskAddServerBySSURL(string ssURL) | |||||
{ | |||||
var dr = MessageBox.Show(I18N.GetString("Import from URL: {0} ?", ssURL), I18N.GetString("Shadowsocks"), MessageBoxButtons.YesNo); | |||||
if (dr == DialogResult.Yes) | |||||
{ | |||||
if (AddServerBySSURL(ssURL)) | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Successfully imported from {0}", ssURL)); | |||||
return true; | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Failed to import. Please check if the link is valid.")); | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
public bool AddServerBySSURL(string ssURL) | |||||
{ | |||||
try | |||||
{ | |||||
if (string.IsNullOrWhiteSpace(ssURL)) | |||||
return false; | |||||
var servers = Server.GetServers(ssURL); | |||||
if (servers == null || servers.Count == 0) | |||||
return false; | |||||
foreach (var server in servers) | |||||
{ | |||||
_config.configs.Add(server); | |||||
} | |||||
_config.index = _config.configs.Count - 1; | |||||
SaveConfig(_config); | |||||
return true; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
logger.LogUsefulException(e); | |||||
return false; | |||||
} | |||||
} | |||||
public string GetServerURLForCurrentServer() | |||||
{ | |||||
return GetCurrentServer().GetURL(_config.generateLegacyUrl); | |||||
} | |||||
#endregion | |||||
#region Misc | |||||
public void ToggleVerboseLogging(bool enabled) | |||||
{ | |||||
_config.isVerboseLogging = enabled; | |||||
SaveConfig(_config); | |||||
NLogConfig.LoadConfiguration(); // reload nlog | |||||
VerboseLoggingStatusChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void ToggleCheckingUpdate(bool enabled) | |||||
{ | |||||
_config.autoCheckUpdate = enabled; | |||||
Configuration.Save(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void ToggleCheckingPreRelease(bool enabled) | |||||
{ | |||||
_config.checkPreRelease = enabled; | |||||
Configuration.Save(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void SaveSkippedUpdateVerion(string version) | |||||
{ | |||||
_config.skippedUpdateVersion = version; | |||||
Configuration.Save(_config); | |||||
} | |||||
public void SaveLogViewerConfig(LogViewerConfig newConfig) | |||||
{ | |||||
_config.logViewer = newConfig; | |||||
newConfig.SaveSize(); | |||||
Configuration.Save(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
public void SaveHotkeyConfig(HotkeyConfig newConfig) | |||||
{ | |||||
_config.hotkey = newConfig; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
#endregion | |||||
#region Strategy | |||||
public void SelectStrategy(string strategyID) | |||||
{ | |||||
_config.index = -1; | |||||
_config.strategy = strategyID; | |||||
SaveConfig(_config); | |||||
} | |||||
public IList<IStrategy> GetStrategies() | |||||
{ | |||||
return _strategyManager.GetStrategies(); | |||||
} | |||||
public IStrategy GetCurrentStrategy() | |||||
{ | |||||
foreach (var strategy in _strategyManager.GetStrategies()) | |||||
{ | |||||
if (strategy.ID == _config.strategy) | |||||
{ | |||||
return strategy; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public void UpdateInboundCounter(object sender, SSTransmitEventArgs args) | |||||
{ | |||||
GetCurrentStrategy()?.UpdateLastRead(args.server); | |||||
Interlocked.Add(ref _inboundCounter, args.length); | |||||
} | |||||
public void UpdateOutboundCounter(object sender, SSTransmitEventArgs args) | |||||
{ | |||||
GetCurrentStrategy()?.UpdateLastWrite(args.server); | |||||
Interlocked.Add(ref _outboundCounter, args.length); | |||||
} | |||||
#endregion | |||||
#region SIP003 | |||||
private void StartPlugin() | |||||
{ | |||||
var server = _config.GetCurrentServer(); | |||||
GetPluginLocalEndPointIfConfigured(server); | |||||
} | |||||
private void StopPlugins() | |||||
{ | |||||
foreach (var serverAndPlugin in _pluginsByServer) | |||||
{ | |||||
serverAndPlugin.Value?.Dispose(); | |||||
} | |||||
_pluginsByServer.Clear(); | |||||
} | |||||
public EndPoint GetPluginLocalEndPointIfConfigured(Server server) | |||||
{ | |||||
var plugin = _pluginsByServer.GetOrAdd( | |||||
server, | |||||
x => Sip003Plugin.CreateIfConfigured(x, _config.showPluginOutput)); | |||||
if (plugin == null) | |||||
{ | |||||
return null; | |||||
} | |||||
try | |||||
{ | |||||
if (plugin.StartIfNeeded()) | |||||
{ | |||||
logger.Info( | |||||
$"Started SIP003 plugin for {server.Identifier()} on {plugin.LocalEndPoint} - PID: {plugin.ProcessId}"); | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.Error("Failed to start SIP003 plugin: " + ex.Message); | |||||
throw; | |||||
} | |||||
return plugin.LocalEndPoint; | |||||
} | |||||
public void ToggleShowPluginOutput(bool enabled) | |||||
{ | |||||
_config.showPluginOutput = enabled; | |||||
SaveConfig(_config); | |||||
ShowPluginOutputChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
#endregion | |||||
#region Traffic Statistics | |||||
private void StartTrafficStatistics(int queueMaxSize) | |||||
{ | |||||
trafficPerSecondQueue = new Queue<TrafficPerSecond>(); | |||||
for (int i = 0; i < queueMaxSize; i++) | |||||
{ | |||||
trafficPerSecondQueue.Enqueue(new TrafficPerSecond()); | |||||
} | |||||
_trafficThread = new Thread(new ThreadStart(() => TrafficStatistics(queueMaxSize))) | |||||
{ | |||||
IsBackground = true | |||||
}; | |||||
_trafficThread.Start(); | |||||
} | |||||
private void TrafficStatistics(int queueMaxSize) | |||||
{ | |||||
TrafficPerSecond previous, current; | |||||
while (true) | |||||
{ | |||||
previous = trafficPerSecondQueue.Last(); | |||||
current = new TrafficPerSecond | |||||
{ | |||||
inboundCounter = InboundCounter, | |||||
outboundCounter = OutboundCounter | |||||
}; | |||||
current.inboundIncreasement = current.inboundCounter - previous.inboundCounter; | |||||
current.outboundIncreasement = current.outboundCounter - previous.outboundCounter; | |||||
trafficPerSecondQueue.Enqueue(current); | |||||
if (trafficPerSecondQueue.Count > queueMaxSize) | |||||
trafficPerSecondQueue.Dequeue(); | |||||
TrafficChanged?.Invoke(this, new EventArgs()); | |||||
Thread.Sleep(1000); | |||||
} | |||||
} | |||||
#endregion | |||||
#region SIP008 | |||||
public async Task<int> UpdateOnlineConfigInternal(string url) | |||||
{ | |||||
var onlineServer = await OnlineConfigResolver.GetOnline(url); | |||||
_config.configs = Configuration.SortByOnlineConfig( | |||||
_config.configs | |||||
.Where(c => c.group != url) | |||||
.Concat(onlineServer) | |||||
); | |||||
logger.Info($"updated {onlineServer.Count} server from {url}"); | |||||
return onlineServer.Count; | |||||
} | |||||
public async Task<bool> UpdateOnlineConfig(string url) | |||||
{ | |||||
var selected = GetCurrentServer(); | |||||
try | |||||
{ | |||||
int count = await UpdateOnlineConfigInternal(url); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
logger.LogUsefulException(e); | |||||
return false; | |||||
} | |||||
_config.index = _config.configs.IndexOf(selected); | |||||
SaveConfig(_config); | |||||
return true; | |||||
} | |||||
public async Task<List<string>> UpdateAllOnlineConfig() | |||||
{ | |||||
var selected = GetCurrentServer(); | |||||
var failedUrls = new List<string>(); | |||||
foreach (var url in _config.onlineConfigSource) | |||||
{ | |||||
try | |||||
{ | |||||
await UpdateOnlineConfigInternal(url); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
logger.LogUsefulException(e); | |||||
failedUrls.Add(url); | |||||
} | |||||
} | |||||
_config.index = _config.configs.IndexOf(selected); | |||||
SaveConfig(_config); | |||||
return failedUrls; | |||||
} | |||||
public void SaveOnlineConfigSource(List<string> sources) | |||||
{ | |||||
_config.onlineConfigSource = sources; | |||||
SaveConfig(_config); | |||||
} | |||||
public void RemoveOnlineConfig(string url) | |||||
{ | |||||
_config.onlineConfigSource.RemoveAll(v => v == url); | |||||
_config.configs = Configuration.SortByOnlineConfig( | |||||
_config.configs.Where(c => c.group != url) | |||||
); | |||||
SaveConfig(_config); | |||||
} | |||||
#endregion | |||||
} | |||||
} |
@@ -1,157 +0,0 @@ | |||||
en,ru-RU,zh-CN,zh-TW,ja,ko,fr | |||||
#Restart program to apply translation,,,,,, | |||||
#This is comment line,,,,,, | |||||
#Always keep language name at head of file,,,,,, | |||||
#Language name is output in log,,,,,, | |||||
"#You can find it by search ""Current language is:""",,,,,, | |||||
#Please use UTF-8 with BOM encoding so we can edit it in Excel,,,,,, | |||||
,,,,,, | |||||
Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks,Shadowsocks | |||||
,,,,,, | |||||
#Menu,,,,,, | |||||
,,,,,, | |||||
System Proxy,Системный прокси-сервер,系统代理,系統代理,システムプロキシ,시스템 프록시,Proxy système | |||||
Disable,Отключен,禁用,禁用,無効,비활성화,Désactiver | |||||
PAC,Сценарий настройки (PAC),PAC 模式,PAC 模式,PACモード,프록시 자동 구성 (PAC),PAC | |||||
Global,Для всей системы,全局模式,全局模式,グローバルプロキシ,전역,Global | |||||
Servers,Серверы,服务器,伺服器,サーバー,서버,Serveurs | |||||
Edit Servers...,Редактировать серверы…,编辑服务器...,編輯伺服器...,サーバーの編集...,서버 수정…,Éditer serveurs… | |||||
Online Config...,,在线配置...,線上配置...,,, | |||||
Start on Boot,Автозагрузка,开机启动,開機啟動,システム起動時に実行,시스템 시작 시에 시작하기,Démarrage automatique | |||||
Associate ss:// Links,Ассоциированный ss:// Ссылки,关联 ss:// 链接,關聯 ss:// 鏈接,ss:// リンクの関連付け,ss:// 링크 연결, | |||||
Forward Proxy...,Прямой прокси…,正向代理设置...,正向 Proxy 設定...,フォワードプロキシの設定...,포워드 프록시,Forward-proxy… | |||||
Allow other Devices to connect,Общий доступ к подключению,允许其他设备连入,允許其他裝置連入,他のデバイスからの接続を許可する,다른 기기에서 연결 허용,Autoriser d'autres appareils à se connecter | |||||
Local PAC,Локальный PAC,使用本地 PAC,使用本機 PAC,ローカル PAC,로컬 프록시 자동 구성,PAC local | |||||
Online PAC,Удаленный PAC,使用在线 PAC,使用線上 PAC,オンライン PAC,온라인 프록시 자동 구성,PAC en ligne | |||||
Edit Local PAC File...,Редактировать локальный PAC…,编辑本地 PAC 文件...,編輯本機 PAC 檔案...,ローカル PAC ファイルの編集...,로컬 프록시 자동 구성 파일 수정,Modifier le fichier PAC local ... | |||||
Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 프록시 자동 구성 파일 업데이트,Mettre à jour le PAC local à partir de Geosite | |||||
Edit User Rule for Geosite...,Редактировать свои правила для Geosite,编辑 Geosite 的用户规则...,編輯 Geosite 的使用者規則...,ユーザールールの編集...,Geosite 사용자 수정,Modifier la règle utilisateur pour Geosite ... | |||||
Secure Local PAC,Безопасный URL локального PAC,保护本地 PAC,安全本機 PAC,ローカル PAC を保護,로컬 프록시 자동 구성 파일 암호화,Sécuriser PAC local | |||||
Regenerate local PAC on version update,,版本更新后重新生成本地 PAC,版本更新後重新生成本地 PAC,,, | |||||
Copy Local PAC URL,Копировать URL локального PAC,复制本地 PAC 网址,複製本機 PAC 網址,ローカル PAC URL をコピー,로컬 프록시 자동 구성 파일 URL 복사,Copier l'URL du PAC local | |||||
Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | |||||
Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | |||||
Import URL from Clipboard...,Импорт адреса из буфера обмена…,从剪贴板导入URL...,從剪貼簿匯入 URL...,クリップボードから URL をインポート...,클립보드에서 URL 가져오기…,Importer l'URL du presse-papiers ... | |||||
Availability Statistics,Статистика доступности,统计可用性,統計可用性,可用性の統計,가용성 통계,Statistiques de disponibilité | |||||
Show Logs...,Показать журнал…,显示日志...,顯示記錄檔...,ログの表示...,로그 보기…,Afficher les journaux ... | |||||
Verbose Logging,Подробный журнал,详细记录日志,詳細資訊記錄,詳細なログを記録,자세한 로깅 사용,Journalisation détaillée | |||||
Updates...,Обновления…,更新...,更新...,アップデート...,업데이트…,Mettre à jour… | |||||
Check for Updates...,Проверить обновления…,检查更新,檢查更新,アップデートを確認...,업데이트 확인하기…,Vérifier les mises à jour ... | |||||
Check for Updates at Startup,Проверять при запуске,启动时检查更新,啟動時檢查更新,起動時にアップデートを確認,시스템 시작 시 업데이트 확인하기,Vérifier les mises à jour au démarrage | |||||
Check Pre-release Version,Проверять предрелизные версии,检查测试版更新,檢查發行前版本更新,ベータ版のアップデートも確認,개발 버전 업데이트 확인하기,Vérifier la version préliminaire | |||||
Edit Hotkeys...,Горячие клавиши…,编辑快捷键...,編輯快速鍵...,ホットキーの編集...,단축키 수정…,Modifier les raccourcis clavier ... | |||||
About...,О программе…,关于...,關於...,Shadowsocks について...,정보…,A propos | |||||
Help,Помощь,帮助,說明,ヘルプ,도움말,Aide | |||||
Quit,Выход,退出,結束,終了,종료,Quitter | |||||
Edit Servers,Редактирование серверов,编辑服务器,編輯伺服器,サーバーの編集,서버 수정,Éditer serveurs | |||||
Load Balance,Балансировка нагрузки,负载均衡,負載平衡,サーバーロードバランス,로드밸런싱,Répartition de charge | |||||
High Availability,Высокая доступность,高可用,高可用性,高可用性,고가용성,Haute disponibilité | |||||
Show Plugin Output,События плагинов в журнале,显示插件输出,,プラグインの出力情報を表示,플러그인 출력 보이기,Afficher la sortie du plugin | |||||
Write translation template,Создать шаблон для перевода,写入翻译模板,,翻訳テンプレートファイルを書き込む,번역 템플릿 쓰기,Écrire un modèle de traduction | |||||
,,,,,, | |||||
# Config Form,,,,,, | |||||
,,,,,, | |||||
Statistics configuration,Настройки статистики,统计配置,,統計の設定,통계 설정,Configuration des statistiques | |||||
&Add,Добавить,添加(&A),新增 (&A),新規 (&A),추가 (&A),Ajouter | |||||
&Delete,Удалить,删除(&D),移除 (&D),削除 (&D),삭제 (&D),Supprimer | |||||
Dupli&cate,Дублир-ть,复制(&C),複製 (&C),コピー (&C),복제 (&C),Dupliquer | |||||
Server,Сервер,服务器,伺服器,サーバー,서버,Serveur | |||||
Server IP,IP-адрес,服务器地址,伺服器位址,サーバーアドレス,서버 IP,Serveur IP | |||||
Server Port,Порт,服务器端口,伺服器連接埠,サーバーポート,서버 포트,Port de serveur | |||||
Password,Пароль,密码,密碼,パスワード,비밀번호,Mot de passe | |||||
Show Password,Показать пароль,显示密码,顯示密碼,パスワードを表示する,비밀번호 보이기,Montrer le mot de passe | |||||
Encryption,Шифрование,加密,加密,暗号化,암호화,Chiffrement | |||||
Plugin Program,Плагин,插件程序,外掛程式,プラグインプログラム,플러그인 프로그램,Programme de plugin | |||||
Plugin Options,Опции плагина,插件选项,外掛程式選項,プラグインのオプション,플러그인 설정,Options de plugin | |||||
Need Plugin Argument,Требуются аргументы,需要命令行参数,,コマンドライン引数を有効にする,플러그인 인자가 필요함,Besoin d'un argument de plugin | |||||
Plugin Arguments,Аргументы,插件参数,外掛程式參數,プラグインの引数,플러그인 인자,Arguments du plugin | |||||
Proxy Port,Порт прокси,代理端口,Proxy 連接埠,プロキシポート,프록시 포트,Port proxy | |||||
Portable Mode,Переносимый режим,便携模式,便攜模式,ポータブルモード,포터블 모드,Mode portable | |||||
Restart required,Требуется перезапуск программы,需要重新启动SS,需要重新啟動SS,再起動が必要,재시작이 필요합니다,Redémarrage nécessaire | |||||
Remarks,Примечания,备注,註解,付記,알림,Remarques | |||||
Timeout(Sec),Таймаут(сек),超时(秒),逾時 (秒),タイムアウト (秒),시간 초과 (초),Délai d'attente(sec) | |||||
OK,ОК,确定,確定,OK,확인,OK | |||||
Cancel,Отмена,取消,取消,キャンセル,취소,Annuler | |||||
Apply,Применить,应用,應用,適用,적용,Appliquer | |||||
New server,Новый сервер,未配置的服务器,新伺服器,新規サーバー,새 서버,Nouveau serveur | |||||
Move &Up,Выше,上移(&U),上移 (&U),上に移動 (&U),위로 (&U),Monter | |||||
Move D&own,Ниже,下移(&O),下移 (&O),下に移動 (&O),아래로 (&O),Descendre | |||||
deprecated,Устаревшее,不推荐,不推薦,非推奨,더 이상 사용되지 않음,Obsolète | |||||
"Encryption method {0} not exist, will replace with {1}",,加密方法{0}不存在,将使用{1}代替,,暗号化方式{0}が存在しません,{1}に置換します,{0} 암호화 방식이 존재하지 않으므로 {1}로 대체될 것입니다.,"Méthode de chiffrement {0} n'existe pas, sera remplacée par {1}" | |||||
,,,,,, | |||||
# Log Form,,,,,, | |||||
,,,,,, | |||||
&File,Файл,文件(&F),檔案 (&F),ファイル (&F),파일 (&F),Fichier | |||||
&Open Location,Расположение файла,在资源管理器中打开(&O),在檔案總管中開啟 (&O),ファイルの場所を開く (&O),위치 열기 (&O),Ouvrier | |||||
E&xit,Выход,退出(&X),結束 (&X),終了 (&X),종료 (&X),Quitter | |||||
&View,Вид,视图(&V),檢視 (&V),表示 (&V),보기 (&V),Afficher | |||||
&Clear Logs,Очистить журнал,清空日志(&C),清除記錄檔 (&C),ログの削除 (&C),로그 초기화 (&C),Effacer les journaux | |||||
Change &Font,Шрифт…,设置字体(&F),變更字型 (&F),フォント (&F),글꼴 변경 (&F),Définir la police | |||||
&Wrap Text,Перенос строк,自动换行(&W),自動換行 (&W),右端で折り返す (&W),텍스트 감싸기 (&W),Retour à la ligne | |||||
&Top Most,Поверх всех окон,置顶(&T),置頂 (&T),常に最前面に表示 (&T),상단 우선 (Top Most) (&T),Metter en haut | |||||
&Show Toolbar,Панель инструментов,显示工具栏(&S),顯示工具列 (&S),ツールバーの表示 (&S),툴바 보여주기 (&S),Afficher la barre d'outils | |||||
Log Viewer,Просмотр журнала,日志查看器,記錄檔檢視器,ログビューア,로그 뷰어,Visionneuse de journaux | |||||
Inbound,Входящая,入站,輸入,受信,"인바운드 (Rx, 다운로드)",Entrant | |||||
Outbound,Исходящая,出站,輸出,送信,"아웃바운드 (Tx, 업로드)",Sortant | |||||
,,,,,, | |||||
# QRCode Form,,,,,, | |||||
,,,,,, | |||||
QRCode and URL,QRCode и URL,二维码与 URL,QR 碼與 URL,QR コードと URL,QR코드와 URL,QRCode et URL | |||||
,,,,,, | |||||
# PAC Url Form,,,,,, | |||||
,,,,,, | |||||
Edit Online PAC URL,Изменение URL удаленного PAC,编辑在线 PAC 网址,編輯線上 PAC 網址,オンライン PAC URL の編集,온라인 프록시 자동 구성 URL 수정,Modifier l'URL du PAC en ligne | |||||
Edit Online PAC URL...,Редактировать URL удаленного PAC…,编辑在线 PAC 网址...,編輯線上 PAC 網址...,オンライン PAC URL の編集...,온라인 프록시 자동 구성 URL 수정…,Modifier l'URL du PAC en ligne ... | |||||
Please input PAC Url,Введите URL адрес для PAC-файла,请输入 PAC 网址,請輸入 PAC 網址,PAC URLを入力して下さい,프록시 자동 구성 URL을 입력하세요,Veuillez saisir l'URL PAC | |||||
,,,,,, | |||||
# Messages,,,,,, | |||||
,,,,,, | |||||
Shadowsocks Error: {0},Ошибка Shadowsocks: {0},Shadowsocks 错误: {0},Shadowsocks 錯誤: {0},Shadowsocks エラー: {0},Shadowsocks 오류: {0},Erreur shadowsocks: {0} | |||||
Port {0} already in use,Порт {0} уже используется,端口 {0} 已被占用,連接埠號碼 {0} 已被使用,ポート番号 {0} は既に使用されています。,{0}번 포트는 이미 사용 중입니다.,Port {0} déjà utilisé | |||||
Port {0} is reserved by system,Порт {0} зарезервирован системой,端口 {0} 是系统保留端口,連接埠號碼 {0} 由系統保留, ポート番号 {0} はシステムによって予約されています,{0}번 포트는 시스템에서 사용 중입니다.,Port {0} réservé par le système | |||||
Invalid server address,Неверный адрес сервера,非法服务器地址,無效伺服器位址,サーバーアドレスが無効です。,올바르지 않은 서버 주소입니다.,Adresse de serveur non valide | |||||
Illegal port number format,Неверный числовой формат порта,非法端口格式,無效連接埠號碼格式,ポート番号のフォーマットが無効です。,올바르지 않은 포트 번호 형식입니다.,Format de numéro de port illégal | |||||
Illegal timeout format,Неверный формат таймаута,非法超时格式,無效逾時格式,タイムアウト値のフォーマットが無効です。,올바르지 않은 시간 초과 형식입니다.,Format de délai d'attente illégal | |||||
Server IP can not be blank,IP-адрес сервера не может быть пустым,服务器 IP 不能为空,伺服器 IP 不能為空,サーバー IP が指定されていません。,서버 IP는 비어있으면 안됩니다.,L'adresse IP du serveur ne peut pas être vide | |||||
Password can not be blank,Пароль не может быть пустым,密码不能为空,密碼不能為空,パスワードが指定されていません。,비밀번호는 비어있으면 안됩니다.,Le mot de passe ne peut pas être vide | |||||
Port out of range,Порт выходит за допустимый диапазон,端口超出范围,連接埠號碼超出範圍,ポート番号は範囲外です。,올바른 포트 범위가 아닙니다.,Port hors de portée | |||||
Port can't be 8123,Адрес порта 8123 не может быть использован,端口不能为 8123,連接埠號碼不能為 8123,8123 番以外のポート番号を指定して下さい。,8123번 포트는 사용할 수 없습니다.,Le port ne peut pas être 8123 | |||||
No update is available,Обновлений не обнаружено,没有可用的更新,沒有可用的更新,お使いのバージョンは最新です。,사용 가능한 업데이트가 없습니다.,Aucune mise à jour n'est disponible | |||||
Shadowsocks is here,Shadowsocks находится здесь,Shadowsocks 在这里,Shadowsocks 在這裡,Shadowsocks はここです。,Shadowsocks는 여기에 있습니다,Veuillez trouver Shadowsocks ici | |||||
You can turn on/off Shadowsocks in the context menu,Вы можете управлять Shadowsocks из контекстного меню,可以在右键菜单中开关 Shadowsocks,可以在右鍵選項單中開關 Shadowsocks,コンテキストメニューを使って、Shadowsocks を有効または無効にすることができます。,프로그램 메뉴에서 Shadowsocks를 끄고 켤 수 있습니다.,Vous pouvez activer / désactiver Shadowsocks dans le menu contextuel | |||||
System Proxy Enabled,Системный прокси включен,系统代理已启用,系統 Proxy 已啟用,システム プロキシが有効です。,시스템 프록시가 활성화되었습니다.,Proxy système activé | |||||
System Proxy Disabled,Системный прокси отключен,系统代理未启用,系統 Proxy 未啟用,システム プロキシが無効です。,시스템 프록시가 비활성화되었습니다.,Proxy système désactivé | |||||
Failed to update PAC file ,Не удалось обновить PAC файл,更新 PAC 文件失败,更新 PAC 檔案失敗,PAC の更新に失敗しました。,프록시 자동 구성 파일을 업데이트하는데 실패했습니다.,Impossible de mettre à jour le fichier PAC | |||||
PAC updated,PAC файл обновлен,更新 PAC 成功,更新 PAC 成功,PAC を更新しました。,프록시 자동 구성 파일이 업데이트되었습니다.,PAC mis à jour | |||||
No updates found. Please report to Geosite if you have problems with it.,Обновлений не найдено. Сообщите авторам Geosite если у вас возникли проблемы.,未发现更新。如有问题请提交给 Geosite。,未發現更新。如有問題請報告至 Geosite。,お使いのバージョンは最新です。問題がある場合は、GFWList に報告して下さい。,사용 가능한 업데이트를 찾지 못했습니다. 문제가 있다면 Geosite로 전송해주세요.,Aucune mise à jour trouvée. Veuillez signaler à Geosite si vous avez des problèmes concernant. | |||||
No QRCode found. Try to zoom in or move it to the center of the screen.,QRCode не обнаружен. Попробуйте увеличить изображение или переместить его в центр экрана.,未发现二维码,尝试把它放大或移动到靠近屏幕中间的位置,未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置,QR コードが見つかりませんでした。コードを大きくするか、画面の中央に移動して下さい。,QR코드를 찾을 수 없습니다. 가운데로 화면을 이동시키거나 확대해보세요.,Aucun QRCode trouvé. Essayez de zoomer ou de le déplacer vers le centre de l'écran. | |||||
Shadowsocks is already running.,Shadowsocks уже запущен.,Shadowsocks 已经在运行。,Shadowsocks 已經在執行。,Shadowsocks 実行中,Shadowsocks가 이미 실행 중입니다.,Shadowsocks est déjà en cours d'exécution. | |||||
Find Shadowsocks icon in your notify tray.,Значок Shadowsocks можно найти в области уведомлений.,请在任务栏里寻找 Shadowsocks 图标。,請在工作列裡尋找 Shadowsocks 圖示。,通知領域には Shadowsocks のアイコンがあります。,트레이에서 Shadowsocks를 찾아주세요.,Trouvez l'icône Shadowsocks dans votre barre de notification. | |||||
"If you want to start multiple Shadowsocks, make a copy in another directory.","Если вы хотите запустить несколько копий Shadowsocks одновременно, создайте отдельную папку на каждую копию.",如果想同时启动多个,可以另外复制一份到别的目录。,如果想同時啟動多個,可以另外複製一份至別的目錄。,複数起動したい場合は、プログラムファイルを別のフォルダーにコピーしてから、もう一度実行して下さい。,"만약 여러 개의 Shadowsocks를 사용하고 싶으시다면, 파일을 다른 곳에 복사해주세요.","Si vous souhaitez démarrer plusieurs Shadowsocks, faites une copie dans un autre répertoire." | |||||
Invalid QR Code content: {0},,无效二维码内容: {0},無效二維碼內容: {0},,, | |||||
Failed to update registry,Не удалось обновить запись в реестре,无法修改注册表,無法修改登錄檔,レジストリの更新に失敗しました。,레지스트리를 업데이트하는데에 실패했습니다.,Impossible de mettre à jour de la base de registre | |||||
Import from URL: {0} ?,импортировать из адреса: {0} ?,从URL导入: {0} ?,從URL匯入: {0} ?,{0}:このURLからインポートしますか?,, | |||||
Successfully imported from {0},Успешно импортировано из {0},导入成功:{0},導入成功:{0},{0}:インポートしました。,, | |||||
Failed to import. Please check if the link is valid.,,导入失败,请检查链接是否有效。,導入失敗,請檢查鏈接是否有效。,インポートに失敗しました。リンクの有効性を確認してください。,, | |||||
System Proxy On: ,Системный прокси:,系统代理已启用:,系統 Proxy 已啟用:,システム プロキシが有効:,시스템 프록시 활성화됨: ,Proxy système activé: | |||||
Running: Port {0},Запущен на порту {0},正在运行:端口 {0},正在執行:連接埠號碼 {0},実行中:ポート {0},실행 중: 포트 {0}번,En cours d'exécution: port {0} | |||||
"Unexpected error, shadowsocks will exit. Please report to","Непредвиденная ошибка, пожалуйста сообщите на",非预期错误,Shadowsocks将退出。请提交此错误到,非預期錯誤,Shadowsocks 將結束。請報告此錯誤至,予想外のエラーが発生したため、Shadowsocks を終了します。詳しくは下記までお問い合わせ下さい:,알 수 없는 오류로 Shadowsocks가 종료될 것입니다. 오류를 여기로 제보해주세요:,Shadowsocks va quitter en présence d/érreur inattendue. Veuillez signaler à | |||||
"Unsupported operating system, use Windows Vista at least.","Операционная система не поддерживается, минимальные системные требования: Windows Vista или выше.",不支持的操作系统版本,最低需求为Windows Vista。,不支援的作業系統版本,最低需求為 Windows Vista。,お使いの OS はサポートされていません。Windows Vista 以降の OS で実行して下さい。,지원하지 않는 운영체제입니다. 최소 Windows Vista가 필요합니다.,"Système d'exploitation incompatible, veuillez utiliser Windows Vista ou ultérieure." | |||||
"Unsupported .NET Framework, please update to {0} or later.","Версия .NET Framework не поддерживается, минимальные системные требования: {0} или выше.",当前 .NET Framework 版本过低,请升级至{0}或更新版本。,目前 .NET Framework 版本過低,最低需求為{0}。,お使いの .NET Framework はサポートされていません。{0} 以降のバンジョーをインストールして下さい。,지원하지 않는 .NET 프레임워크입니다. {0} 또는 상위 버전으로 업데이트해주세요.,".NET Framework incompatible, veuillez mettre à jour vers {0} ou ultérieure." | |||||
Proxy request failed,Не удалось выполнить запрос,代理请求失败,Proxy 要求失敗,プロキシリクエストが失敗しました。,프록시 요청에 실패했습니다.,Échec de la demande de proxy | |||||
Proxy handshake failed,Не удалось выполнить хэндшейк,代理握手失败,Proxy 交握失敗,プロキシ ハンドシェイクに失敗しました。,프록시 핸드쉐이크에 실패했습니다.,Échec de la prise de contact par proxy | |||||
Register hotkey failed,Не удалось применить настройки горячих клавиш,注册快捷键失败,註冊快速鍵失敗,ホットキーの登錄に失敗しました。,단축키 등록에 실패했습니다.,Échec de l'enregistrement du raccourci clavier | |||||
Cannot parse hotkey: {0},Не удалось распознать следующие горячие клавиши: {0},解析快捷键失败: {0},剖析快速鍵失敗: {0},ホットキーを解析できません: {0},단축키를 해석할 수 없습니다: {0},Impossible d'analyser le raccourci clavier: {0} | |||||
"Timeout is invalid, it should not exceed {0}",Таймаут не может превышать значение {0},超时无效,不应超过 {0},逾時無效,不應超過 {0},タイムアウト値が無効です。{0} 以下の値を指定して下さい。,올바르지 않은 시간 초과 설정입니다. {0}을 초과하지 않아야 합니다.,"Le délai d'attente invalide, il ne doit pas dépasser {0}" | |||||
Cannot find the plugin program file,Файл плагина не найден,找不到插件程序文件,找不到外掛程式文件,プラグインプログラムが見つかりません,플러그인 프로그램 파일을 찾을 수 없습니다.,Impossible de trouver le fichier du programme du plugin | |||||
,,,,,, | |||||
Operation failure,Операция завершилась неудачей,操作失败,,操作に失敗しました,작업에 실패했습니다.,Operation failure | |||||
Auto save failed,Автоматическое сохранение не удалось,自动保存失败,,オートセーブに失敗しました,자동 저장에 실패했습니다.,Échec de l'enregistrement automatique | |||||
Whether to discard unconfigured servers,Внесенные изменения будут утеряны,是否丢弃未配置的服务器,,未設定のサーバーを破棄しますか,구성되지 않은 서버 설정 변경 사항을 폐기하시겠습니까?,Eliminer les serveurs non configurés ou non | |||||
"Invalid server address, Cannot automatically save or discard changes",Неверный адрес сервера. Невозможно сохранить или отменить изменения,非法服务器地址,无法自动保存,是否丢弃修改,,サーバーアドレスが無効なため、オートセーブできません。変更を破棄しますか,"서버 주소가 올바르지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Adresse de serveur invalide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Illegal port number format, Cannot automatically save or discard changes",Неверный числовой адрес порта. Невозможно сохранить или отменить изменения,非法端口格式,无法自动保存,是否丢弃修改,,ポート番号のフォーマットが無効なため、オートセーブできません。変更を破棄しますか,"포트 번호가 올바르지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Format de numéro de port illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Password can not be blank, Cannot automatically save or discard changes",Пароль не может быть пустым. Невозможно сохранить или отменить изменения,密码不能为空,无法自动保存,是否丢弃修改,,サーバーアドレスが無効なため、オートセーブできません。変更を破棄しますか,"비밀번호를 입력하지 않아 자동 저장 할 수 없습니다, 변경 사항을 폐기하시겠습니까?","Le mot de passe ne peut pas être vide, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
"Illegal timeout format, Cannot automatically save or discard changes",Неверный формат таймаута. Невозможно сохранить или отменить изменения,非法超时格式,无法自动保存,是否丢弃修改,,パスワードが指定されていないため、オートセーブできません。変更を破棄しますか,시간 초과 형식이 올바르지 않아 자동 저장 할 수 없습니다. 변경 사항을 폐기하시겠습니까?,"Format de délai d'attente illégal, impossible d'enregistrer ou d'annuler automatiquement les modifications" | |||||
,,,,タイムアウト値のフォーマットが無効なため、オートセーブできません。変更を破棄しますか,, | |||||
"Error occured when process proxy setting, do you want reset current setting and retry?",Произошла ошибка при обработке настроек. Хотите сбросить текущие настройки и попробовать снова?,处理代理设置时发生错误,是否重置当前代理设置并重试?,,プロキシ設定の処理にエラーが発生しました、現在のプロキシ設定をリセットし、再試行してもいいですか,프록시 설정을 처리하는데에 오류가 발생했습니다. 현재 설정을 폐기하고 다시 시도하시겠습니까?,Une erreur s'est produite lors du processus de configuration du proxy. Voulez-vous réinitialiser le paramètre actuel et réessayer? | |||||
"Unrecoverable proxy setting error occured, see log for detail","Произошла серьезная ошибка, подробности можно узнать в журналах",发生不可恢复的代理设置错误,查看日志以取得详情,,プロキシ設定に回復不能なエラーが発生しました、ログで詳細をご確認ください,복구 불가능한 프록시 설정 오류가 발생했습니다. 자세한 정보는 로그를 참조하세요.,"Une erreur de paramètre de proxy irrécupérable s'est produite, consultez le journal pour plus de détails" |
@@ -1,382 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Net; | |||||
using System.Windows; | |||||
using Newtonsoft.Json; | |||||
using NLog; | |||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Model | |||||
{ | |||||
[Serializable] | |||||
public class Configuration | |||||
{ | |||||
[JsonIgnore] | |||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||||
public string version; | |||||
public List<Server> configs; | |||||
public List<string> onlineConfigSource; | |||||
// when strategy is set, index is ignored | |||||
public string strategy; | |||||
public int index; | |||||
public bool global; | |||||
public bool enabled; | |||||
public bool shareOverLan; | |||||
public bool firstRun; | |||||
public int localPort; | |||||
public bool portableMode; | |||||
public bool showPluginOutput; | |||||
public string pacUrl; | |||||
public bool useOnlinePac; | |||||
public bool secureLocalPac; // enable secret for PAC server | |||||
public bool regeneratePacOnUpdate; // regenerate pac.txt on version update | |||||
public bool autoCheckUpdate; | |||||
public bool checkPreRelease; | |||||
public string skippedUpdateVersion; // skip the update with this version number | |||||
public bool isVerboseLogging; | |||||
// hidden options | |||||
public bool isIPv6Enabled; // for experimental ipv6 support | |||||
public bool generateLegacyUrl; // for pre-sip002 url compatibility | |||||
public string geositeUrl; // for custom geosite source (and rule group) | |||||
public List<string> geositeDirectGroups; // groups of domains that we connect without the proxy | |||||
public List<string> geositeProxiedGroups; // groups of domains that we connect via the proxy | |||||
public bool geositePreferDirect; // a.k.a blacklist mode | |||||
public string userAgent; | |||||
//public NLogConfig.LogLevel logLevel; | |||||
public LogViewerConfig logViewer; | |||||
public ForwardProxyConfig proxy; | |||||
public HotkeyConfig hotkey; | |||||
[JsonIgnore] | |||||
public bool firstRunOnNewVersion; | |||||
public Configuration() | |||||
{ | |||||
version = UpdateChecker.Version; | |||||
strategy = ""; | |||||
index = 0; | |||||
global = false; | |||||
enabled = false; | |||||
shareOverLan = false; | |||||
firstRun = true; | |||||
localPort = 1080; | |||||
portableMode = true; | |||||
showPluginOutput = false; | |||||
pacUrl = ""; | |||||
useOnlinePac = false; | |||||
secureLocalPac = true; | |||||
regeneratePacOnUpdate = true; | |||||
autoCheckUpdate = false; | |||||
checkPreRelease = false; | |||||
skippedUpdateVersion = ""; | |||||
isVerboseLogging = false; | |||||
// hidden options | |||||
isIPv6Enabled = false; | |||||
generateLegacyUrl = false; | |||||
geositeUrl = ""; | |||||
geositeDirectGroups = new List<string>() | |||||
{ | |||||
"private", | |||||
"cn", | |||||
"geolocation-!cn@cn", | |||||
}; | |||||
geositeProxiedGroups = new List<string>() | |||||
{ | |||||
"geolocation-!cn", | |||||
}; | |||||
geositePreferDirect = false; | |||||
userAgent = "ShadowsocksWindows/$version"; | |||||
logViewer = new LogViewerConfig(); | |||||
proxy = new ForwardProxyConfig(); | |||||
hotkey = new HotkeyConfig(); | |||||
firstRunOnNewVersion = false; | |||||
configs = new List<Server>(); | |||||
onlineConfigSource = new List<string>(); | |||||
} | |||||
[JsonIgnore] | |||||
public string userAgentString; // $version substituted with numeral version in it | |||||
[JsonIgnore] | |||||
NLogConfig nLogConfig; | |||||
private static readonly string CONFIG_FILE = "gui-config.json"; | |||||
#if DEBUG | |||||
private static readonly NLogConfig.LogLevel verboseLogLevel = NLogConfig.LogLevel.Trace; | |||||
#else | |||||
private static readonly NLogConfig.LogLevel verboseLogLevel = NLogConfig.LogLevel.Debug; | |||||
#endif | |||||
[JsonIgnore] | |||||
public string LocalHost => isIPv6Enabled ? "[::1]" : "127.0.0.1"; | |||||
public Server GetCurrentServer() | |||||
{ | |||||
if (index >= 0 && index < configs.Count) | |||||
return configs[index]; | |||||
else | |||||
return GetDefaultServer(); | |||||
} | |||||
public WebProxy WebProxy => enabled | |||||
? new WebProxy( | |||||
isIPv6Enabled | |||||
? $"[{IPAddress.IPv6Loopback}]" | |||||
: IPAddress.Loopback.ToString(), | |||||
localPort) | |||||
: null; | |||||
/// <summary> | |||||
/// Used by multiple forms to validate a server. | |||||
/// Communication is done by throwing exceptions. | |||||
/// </summary> | |||||
/// <param name="server"></param> | |||||
public static void CheckServer(Server server) | |||||
{ | |||||
CheckServer(server.server); | |||||
CheckPort(server.server_port); | |||||
CheckPassword(server.password); | |||||
CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); | |||||
} | |||||
/// <summary> | |||||
/// Loads the configuration from file. | |||||
/// </summary> | |||||
/// <returns>An Configuration object.</returns> | |||||
public static Configuration Load() | |||||
{ | |||||
Configuration config; | |||||
if (File.Exists(CONFIG_FILE)) | |||||
{ | |||||
try | |||||
{ | |||||
string configContent = File.ReadAllText(CONFIG_FILE); | |||||
config = JsonConvert.DeserializeObject<Configuration>(configContent, new JsonSerializerSettings() | |||||
{ | |||||
ObjectCreationHandling = ObjectCreationHandling.Replace | |||||
}); | |||||
return config; | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
if (!(e is FileNotFoundException)) | |||||
logger.LogUsefulException(e); | |||||
} | |||||
} | |||||
config = new Configuration(); | |||||
return config; | |||||
} | |||||
/// <summary> | |||||
/// Process the loaded configurations and set up things. | |||||
/// </summary> | |||||
/// <param name="config">A reference of Configuration object.</param> | |||||
public static void Process(ref Configuration config) | |||||
{ | |||||
// Verify if the configured geosite groups exist. | |||||
// Reset to default if ANY one of the configured group doesn't exist. | |||||
if (!ValidateGeositeGroupList(config.geositeDirectGroups)) | |||||
ResetGeositeDirectGroup(ref config.geositeDirectGroups); | |||||
if (!ValidateGeositeGroupList(config.geositeProxiedGroups)) | |||||
ResetGeositeProxiedGroup(ref config.geositeProxiedGroups); | |||||
// Mark the first run of a new version. | |||||
var appVersion = new Version(UpdateChecker.Version); | |||||
var configVersion = new Version(config.version); | |||||
if (appVersion.CompareTo(configVersion) > 0) | |||||
{ | |||||
config.firstRunOnNewVersion = true; | |||||
} | |||||
// Add an empty server configuration | |||||
if (config.configs.Count == 0) | |||||
config.configs.Add(GetDefaultServer()); | |||||
// Selected server | |||||
if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) | |||||
config.index = 0; | |||||
if (config.index >= config.configs.Count) | |||||
config.index = config.configs.Count - 1; | |||||
// Check OS IPv6 support | |||||
if (!System.Net.Sockets.Socket.OSSupportsIPv6) | |||||
config.isIPv6Enabled = false; | |||||
config.proxy.CheckConfig(); | |||||
// Replace $version with the version number. | |||||
config.userAgentString = config.userAgent.Replace("$version", config.version); | |||||
// NLog log level | |||||
try | |||||
{ | |||||
config.nLogConfig = NLogConfig.LoadXML(); | |||||
switch (config.nLogConfig.GetLogLevel()) | |||||
{ | |||||
case NLogConfig.LogLevel.Fatal: | |||||
case NLogConfig.LogLevel.Error: | |||||
case NLogConfig.LogLevel.Warn: | |||||
case NLogConfig.LogLevel.Info: | |||||
config.isVerboseLogging = false; | |||||
break; | |||||
case NLogConfig.LogLevel.Debug: | |||||
case NLogConfig.LogLevel.Trace: | |||||
config.isVerboseLogging = true; | |||||
break; | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
MessageBox.Show($"Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes.\n{e.Message}"); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Saves the Configuration object to file. | |||||
/// </summary> | |||||
/// <param name="config">A Configuration object.</param> | |||||
public static void Save(Configuration config) | |||||
{ | |||||
config.configs = SortByOnlineConfig(config.configs); | |||||
FileStream configFileStream = null; | |||||
StreamWriter configStreamWriter = null; | |||||
try | |||||
{ | |||||
configFileStream = File.Open(CONFIG_FILE, FileMode.Create); | |||||
configStreamWriter = new StreamWriter(configFileStream); | |||||
var jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); | |||||
configStreamWriter.Write(jsonString); | |||||
configStreamWriter.Flush(); | |||||
// NLog | |||||
config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info); | |||||
NLogConfig.SaveXML(config.nLogConfig); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
logger.LogUsefulException(e); | |||||
} | |||||
finally | |||||
{ | |||||
if (configStreamWriter != null) | |||||
configStreamWriter.Dispose(); | |||||
if (configFileStream != null) | |||||
configFileStream.Dispose(); | |||||
} | |||||
} | |||||
public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | |||||
{ | |||||
var groups = servers.GroupBy(s => s.group); | |||||
List<Server> ret = new List<Server>(); | |||||
ret.AddRange(groups.Where(g => string.IsNullOrEmpty(g.Key)).SelectMany(g => g)); | |||||
ret.AddRange(groups.Where(g => !string.IsNullOrEmpty(g.Key)).SelectMany(g => g)); | |||||
return ret; | |||||
} | |||||
/// <summary> | |||||
/// Validates if the groups in the list are all valid. | |||||
/// </summary> | |||||
/// <param name="groups">The list of groups to validate.</param> | |||||
/// <returns> | |||||
/// True if all groups are valid. | |||||
/// False if any one of them is invalid. | |||||
/// </returns> | |||||
public static bool ValidateGeositeGroupList(List<string> groups) | |||||
{ | |||||
foreach (var geositeGroup in groups) | |||||
if (!GeositeUpdater.CheckGeositeGroup(geositeGroup)) // found invalid group | |||||
{ | |||||
#if DEBUG | |||||
logger.Debug($"Available groups:"); | |||||
foreach (var group in GeositeUpdater.Geosites.Keys) | |||||
logger.Debug($"{group}"); | |||||
#endif | |||||
logger.Warn($"The Geosite group {geositeGroup} doesn't exist. Resetting to default groups."); | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
public static void ResetGeositeDirectGroup(ref List<string> geositeDirectGroups) | |||||
{ | |||||
geositeDirectGroups.Clear(); | |||||
geositeDirectGroups.Add("private"); | |||||
geositeDirectGroups.Add("cn"); | |||||
geositeDirectGroups.Add("geolocation-!cn@cn"); | |||||
} | |||||
public static void ResetGeositeProxiedGroup(ref List<string> geositeProxiedGroups) | |||||
{ | |||||
geositeProxiedGroups.Clear(); | |||||
geositeProxiedGroups.Add("geolocation-!cn"); | |||||
} | |||||
public static void ResetUserAgent(Configuration config) | |||||
{ | |||||
config.userAgent = "ShadowsocksWindows/$version"; | |||||
config.userAgentString = config.userAgent.Replace("$version", config.version); | |||||
} | |||||
public static Server AddDefaultServerOrServer(Configuration config, Server server = null, int? index = null) | |||||
{ | |||||
if (config?.configs != null) | |||||
{ | |||||
server = (server ?? GetDefaultServer()); | |||||
config.configs.Insert(index.GetValueOrDefault(config.configs.Count), server); | |||||
//if (index.HasValue) | |||||
// config.configs.Insert(index.Value, server); | |||||
//else | |||||
// config.configs.Add(server); | |||||
} | |||||
return server; | |||||
} | |||||
public static Server GetDefaultServer() | |||||
{ | |||||
return new Server(); | |||||
} | |||||
public static void CheckPort(int port) | |||||
{ | |||||
if (port <= 0 || port > 65535) | |||||
throw new ArgumentException(I18N.GetString("Port out of range")); | |||||
} | |||||
public static void CheckLocalPort(int port) | |||||
{ | |||||
CheckPort(port); | |||||
if (port == 8123) | |||||
throw new ArgumentException(I18N.GetString("Port can't be 8123")); | |||||
} | |||||
private static void CheckPassword(string password) | |||||
{ | |||||
if (string.IsNullOrEmpty(password)) | |||||
throw new ArgumentException(I18N.GetString("Password can not be blank")); | |||||
} | |||||
public static void CheckServer(string server) | |||||
{ | |||||
if (string.IsNullOrEmpty(server)) | |||||
throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | |||||
} | |||||
public static void CheckTimeout(int timeout, int maxTimeout) | |||||
{ | |||||
if (timeout <= 0 || timeout > maxTimeout) | |||||
throw new ArgumentException( | |||||
I18N.GetString("Timeout is invalid, it should not exceed {0}", maxTimeout)); | |||||
} | |||||
} | |||||
} |
@@ -1,215 +0,0 @@ | |||||
using System; | |||||
using System.Diagnostics; | |||||
using System.IO; | |||||
using System.IO.Pipes; | |||||
using System.Net; | |||||
using System.Reflection; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using System.Windows.Forms; | |||||
using CommandLine; | |||||
using Microsoft.Win32; | |||||
using NLog; | |||||
using ReactiveUI; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Controller.Hotkeys; | |||||
using Shadowsocks.Util; | |||||
using Shadowsocks.View; | |||||
using Splat; | |||||
using WPFLocalizeExtension.Engine; | |||||
namespace Shadowsocks | |||||
{ | |||||
internal static class Program | |||||
{ | |||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger(); | |||||
public static ShadowsocksController MainController { get; private set; } | |||||
public static MenuViewController MenuController { get; private set; } | |||||
public static CommandLineOption Options { get; private set; } | |||||
public static string[] Args { get; private set; } | |||||
// https://github.com/dotnet/runtime/issues/13051#issuecomment-510267727 | |||||
public static readonly string ExecutablePath = Process.GetCurrentProcess().MainModule?.FileName; | |||||
public static readonly string WorkingDirectory = Path.GetDirectoryName(ExecutablePath); | |||||
private static readonly Mutex mutex = new Mutex(true, $"Shadowsocks_{ExecutablePath.GetHashCode()}"); | |||||
/// <summary> | |||||
/// 应用程序的主入口点。 | |||||
/// </summary> | |||||
[STAThread] | |||||
static void Main(string[] args) | |||||
{ | |||||
#region Single Instance and IPC | |||||
bool hasAnotherInstance = !mutex.WaitOne(TimeSpan.Zero, true); | |||||
// store args for further use | |||||
Args = args; | |||||
Parser.Default.ParseArguments<CommandLineOption>(args) | |||||
.WithParsed(opt => Options = opt) | |||||
.WithNotParsed(e => e.Output()); | |||||
if (hasAnotherInstance) | |||||
{ | |||||
if (!string.IsNullOrWhiteSpace(Options.OpenUrl)) | |||||
{ | |||||
IPCService.RequestOpenUrl(Options.OpenUrl); | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Find Shadowsocks icon in your notify tray.") | |||||
+ Environment.NewLine | |||||
+ I18N.GetString("If you want to start multiple Shadowsocks, make a copy in another directory."), | |||||
I18N.GetString("Shadowsocks is already running.")); | |||||
} | |||||
return; | |||||
} | |||||
#endregion | |||||
#region Enviroment Setup | |||||
Directory.SetCurrentDirectory(WorkingDirectory); | |||||
// todo: initialize the NLog configuartion | |||||
Model.NLogConfig.TouchAndApplyNLogConfig(); | |||||
#endregion | |||||
#region Event Handlers Setup | |||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); | |||||
// handle UI exceptions | |||||
Application.ThreadException += Application_ThreadException; | |||||
// handle non-UI exceptions | |||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; | |||||
Application.ApplicationExit += Application_ApplicationExit; | |||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; | |||||
Application.EnableVisualStyles(); | |||||
Application.SetCompatibleTextRenderingDefault(false); | |||||
AutoStartup.RegisterForRestart(true); | |||||
#endregion | |||||
// See https://github.com/dotnet/runtime/issues/13051 | |||||
// we have to do this for self-contained executables | |||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName)); | |||||
// We would use this in v5. | |||||
// Parameters would have to be dropped from views' constructors (VersionUpdatePromptView) | |||||
//Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly()); | |||||
// Workaround for hosting WPF controls in a WinForms app. | |||||
// We have to manually set the culture for the LocalizeDictionary instance. | |||||
// https://stackoverflow.com/questions/374518/localizing-a-winforms-application-with-embedded-wpf-user-controls | |||||
// https://stackoverflow.com/questions/14668640/wpf-localize-extension-translate-window-at-run-time | |||||
LocalizeDictionary.Instance.Culture = Thread.CurrentThread.CurrentCulture; | |||||
#if DEBUG | |||||
// truncate privoxy log file while debugging | |||||
string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); | |||||
if (File.Exists(privoxyLogFilename)) | |||||
using (new FileStream(privoxyLogFilename, FileMode.Truncate)) { } | |||||
#endif | |||||
MainController = new ShadowsocksController(); | |||||
MenuController = new MenuViewController(MainController); | |||||
HotKeys.Init(MainController); | |||||
MainController.Start(); | |||||
// Update online config | |||||
Task.Run(async () => | |||||
{ | |||||
await Task.Delay(10 * 1000); | |||||
await MainController.UpdateAllOnlineConfig(); | |||||
}); | |||||
#region IPC Handler and Arguement Process | |||||
IPCService ipcService = new IPCService(); | |||||
Task.Run(() => ipcService.RunServer()); | |||||
ipcService.OpenUrlRequested += (_1, e) => MainController.AskAddServerBySSURL(e.Url); | |||||
if (!string.IsNullOrWhiteSpace(Options.OpenUrl)) | |||||
{ | |||||
MainController.AskAddServerBySSURL(Options.OpenUrl); | |||||
} | |||||
#endregion | |||||
Application.Run(); | |||||
} | |||||
private static int exited = 0; | |||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) | |||||
{ | |||||
if (Interlocked.Increment(ref exited) == 1) | |||||
{ | |||||
string errMsg = e.ExceptionObject.ToString(); | |||||
logger.Error(errMsg); | |||||
MessageBox.Show( | |||||
$"{I18N.GetString("Unexpected error, shadowsocks will exit. Please report to")} https://github.com/shadowsocks/shadowsocks-windows/issues {Environment.NewLine}{errMsg}", | |||||
"Shadowsocks non-UI Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||||
Application.Exit(); | |||||
} | |||||
} | |||||
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e) | |||||
{ | |||||
if (Interlocked.Increment(ref exited) == 1) | |||||
{ | |||||
string errorMsg = $"Exception Detail: {Environment.NewLine}{e.Exception}"; | |||||
logger.Error(errorMsg); | |||||
MessageBox.Show( | |||||
$"{I18N.GetString("Unexpected error, shadowsocks will exit. Please report to")} https://github.com/shadowsocks/shadowsocks-windows/issues {Environment.NewLine}{errorMsg}", | |||||
"Shadowsocks UI Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||||
Application.Exit(); | |||||
} | |||||
} | |||||
private static void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) | |||||
{ | |||||
switch (e.Mode) | |||||
{ | |||||
case PowerModes.Resume: | |||||
logger.Info("os wake up"); | |||||
if (MainController != null) | |||||
{ | |||||
Task.Factory.StartNew(() => | |||||
{ | |||||
Thread.Sleep(10 * 1000); | |||||
try | |||||
{ | |||||
MainController.Start(true); | |||||
logger.Info("controller started"); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
logger.LogUsefulException(ex); | |||||
} | |||||
}); | |||||
} | |||||
break; | |||||
case PowerModes.Suspend: | |||||
if (MainController != null) | |||||
{ | |||||
MainController.Stop(); | |||||
logger.Info("controller stopped"); | |||||
} | |||||
logger.Info("os suspend"); | |||||
break; | |||||
} | |||||
} | |||||
private static void Application_ApplicationExit(object sender, EventArgs e) | |||||
{ | |||||
// detach static event handlers | |||||
Application.ApplicationExit -= Application_ApplicationExit; | |||||
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; | |||||
Application.ThreadException -= Application_ThreadException; | |||||
HotKeys.Destroy(); | |||||
if (MainController != null) | |||||
{ | |||||
MainController.Stop(); | |||||
MainController = null; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,652 +0,0 @@ | |||||
namespace Shadowsocks.View | |||||
{ | |||||
partial class ConfigForm | |||||
{ | |||||
/// <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(); | |||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | |||||
this.PluginOptionsLabel = new System.Windows.Forms.Label(); | |||||
this.PluginTextBox = new System.Windows.Forms.TextBox(); | |||||
this.RemarksTextBox = new System.Windows.Forms.TextBox(); | |||||
this.IPLabel = new System.Windows.Forms.Label(); | |||||
this.ServerPortLabel = new System.Windows.Forms.Label(); | |||||
this.PasswordLabel = new System.Windows.Forms.Label(); | |||||
this.IPTextBox = new System.Windows.Forms.TextBox(); | |||||
this.ServerPortTextBox = new System.Windows.Forms.TextBox(); | |||||
this.PasswordTextBox = new System.Windows.Forms.TextBox(); | |||||
this.EncryptionLabel = new System.Windows.Forms.Label(); | |||||
this.EncryptionSelect = new System.Windows.Forms.ComboBox(); | |||||
this.TimeoutLabel = new System.Windows.Forms.Label(); | |||||
this.TimeoutTextBox = new System.Windows.Forms.TextBox(); | |||||
this.PluginLabel = new System.Windows.Forms.Label(); | |||||
this.PluginOptionsTextBox = new System.Windows.Forms.TextBox(); | |||||
this.ShowPasswdCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.PluginArgumentsTextBox = new System.Windows.Forms.TextBox(); | |||||
this.PluginArgumentsLabel = new System.Windows.Forms.Label(); | |||||
this.RemarksLabel = new System.Windows.Forms.Label(); | |||||
this.NeedPluginArgCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.panel2 = new System.Windows.Forms.Panel(); | |||||
this.OKButton = new System.Windows.Forms.Button(); | |||||
this.MyCancelButton = new System.Windows.Forms.Button(); | |||||
this.ApplyButton = new System.Windows.Forms.Button(); | |||||
this.DeleteButton = new System.Windows.Forms.Button(); | |||||
this.AddButton = new System.Windows.Forms.Button(); | |||||
this.ServerGroupBox = new System.Windows.Forms.GroupBox(); | |||||
this.ServersListBox = new System.Windows.Forms.ListBox(); | |||||
this.MoveDownButton = new System.Windows.Forms.Button(); | |||||
this.MoveUpButton = new System.Windows.Forms.Button(); | |||||
this.ProxyPortTextBox = new System.Windows.Forms.TextBox(); | |||||
this.ProxyPortLabel = new System.Windows.Forms.Label(); | |||||
this.PortableModeCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.DuplicateButton = new System.Windows.Forms.Button(); | |||||
this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); | |||||
this.tableLayoutPanel7 = new System.Windows.Forms.TableLayoutPanel(); | |||||
this.GroupLabel = new System.Windows.Forms.Label(); | |||||
this.GroupTextBox = new System.Windows.Forms.TextBox(); | |||||
this.tableLayoutPanel1.SuspendLayout(); | |||||
this.ServerGroupBox.SuspendLayout(); | |||||
this.tableLayoutPanel7.SuspendLayout(); | |||||
this.SuspendLayout(); | |||||
// | |||||
// tableLayoutPanel1 | |||||
// | |||||
this.tableLayoutPanel1.AutoSize = true; | |||||
this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||||
this.tableLayoutPanel1.ColumnCount = 2; | |||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); | |||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel1.Controls.Add(this.PluginOptionsLabel, 0, 6); | |||||
this.tableLayoutPanel1.Controls.Add(this.PluginTextBox, 1, 5); | |||||
this.tableLayoutPanel1.Controls.Add(this.RemarksTextBox, 1, 10); | |||||
this.tableLayoutPanel1.Controls.Add(this.IPLabel, 0, 0); | |||||
this.tableLayoutPanel1.Controls.Add(this.ServerPortLabel, 0, 1); | |||||
this.tableLayoutPanel1.Controls.Add(this.PasswordLabel, 0, 2); | |||||
this.tableLayoutPanel1.Controls.Add(this.IPTextBox, 1, 0); | |||||
this.tableLayoutPanel1.Controls.Add(this.ServerPortTextBox, 1, 1); | |||||
this.tableLayoutPanel1.Controls.Add(this.PasswordTextBox, 1, 2); | |||||
this.tableLayoutPanel1.Controls.Add(this.EncryptionLabel, 0, 4); | |||||
this.tableLayoutPanel1.Controls.Add(this.EncryptionSelect, 1, 4); | |||||
this.tableLayoutPanel1.Controls.Add(this.TimeoutLabel, 0, 11); | |||||
this.tableLayoutPanel1.Controls.Add(this.TimeoutTextBox, 1, 11); | |||||
this.tableLayoutPanel1.Controls.Add(this.PluginLabel, 0, 5); | |||||
this.tableLayoutPanel1.Controls.Add(this.PluginOptionsTextBox, 1, 6); | |||||
this.tableLayoutPanel1.Controls.Add(this.ShowPasswdCheckBox, 1, 3); | |||||
this.tableLayoutPanel1.Controls.Add(this.PluginArgumentsTextBox, 1, 8); | |||||
this.tableLayoutPanel1.Controls.Add(this.PluginArgumentsLabel, 0, 8); | |||||
this.tableLayoutPanel1.Controls.Add(this.NeedPluginArgCheckBox, 1, 7); | |||||
this.tableLayoutPanel1.Controls.Add(this.GroupTextBox, 1, 12); | |||||
this.tableLayoutPanel1.Controls.Add(this.GroupLabel, 0, 12); | |||||
this.tableLayoutPanel1.Controls.Add(this.RemarksLabel, 0, 10); | |||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(4, 22); | |||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0); | |||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | |||||
this.tableLayoutPanel1.Padding = new System.Windows.Forms.Padding(4); | |||||
this.tableLayoutPanel1.RowCount = 14; | |||||
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.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(362, 399); | |||||
this.tableLayoutPanel1.TabIndex = 0; | |||||
// | |||||
// PluginOptionsLabel | |||||
// | |||||
this.PluginOptionsLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.PluginOptionsLabel.AutoSize = true; | |||||
this.PluginOptionsLabel.Location = new System.Drawing.Point(24, 199); | |||||
this.PluginOptionsLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.PluginOptionsLabel.Name = "PluginOptionsLabel"; | |||||
this.PluginOptionsLabel.Size = new System.Drawing.Size(119, 15); | |||||
this.PluginOptionsLabel.TabIndex = 6; | |||||
this.PluginOptionsLabel.Text = "Plugin Options"; | |||||
this.toolTip1.SetToolTip(this.PluginOptionsLabel, "Environment variables for plugin program"); | |||||
// | |||||
// PluginTextBox | |||||
// | |||||
this.PluginTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.PluginTextBox.Location = new System.Drawing.Point(150, 163); | |||||
this.PluginTextBox.MaxLength = 256; | |||||
this.PluginTextBox.Name = "PluginTextBox"; | |||||
this.PluginTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.PluginTextBox.TabIndex = 5; | |||||
this.PluginTextBox.WordWrap = false; | |||||
// | |||||
// RemarksTextBox | |||||
// | |||||
this.RemarksTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.RemarksTextBox.Location = new System.Drawing.Point(150, 283); | |||||
this.RemarksTextBox.MaxLength = 32; | |||||
this.RemarksTextBox.Name = "RemarksTextBox"; | |||||
this.RemarksTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.RemarksTextBox.TabIndex = 8; | |||||
this.RemarksTextBox.WordWrap = false; | |||||
// | |||||
// IPLabel | |||||
// | |||||
this.IPLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.IPLabel.AutoSize = true; | |||||
this.IPLabel.Location = new System.Drawing.Point(64, 12); | |||||
this.IPLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.IPLabel.Name = "IPLabel"; | |||||
this.IPLabel.Size = new System.Drawing.Size(79, 15); | |||||
this.IPLabel.TabIndex = 0; | |||||
this.IPLabel.Text = "Server IP"; | |||||
// | |||||
// ServerPortLabel | |||||
// | |||||
this.ServerPortLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.ServerPortLabel.AutoSize = true; | |||||
this.ServerPortLabel.Location = new System.Drawing.Point(48, 43); | |||||
this.ServerPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.ServerPortLabel.Name = "ServerPortLabel"; | |||||
this.ServerPortLabel.Size = new System.Drawing.Size(95, 15); | |||||
this.ServerPortLabel.TabIndex = 1; | |||||
this.ServerPortLabel.Text = "Server Port"; | |||||
// | |||||
// PasswordLabel | |||||
// | |||||
this.PasswordLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.PasswordLabel.AutoSize = true; | |||||
this.PasswordLabel.Location = new System.Drawing.Point(72, 74); | |||||
this.PasswordLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.PasswordLabel.Name = "PasswordLabel"; | |||||
this.PasswordLabel.Size = new System.Drawing.Size(71, 15); | |||||
this.PasswordLabel.TabIndex = 2; | |||||
this.PasswordLabel.Text = "Password"; | |||||
this.PasswordLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; | |||||
// | |||||
// IPTextBox | |||||
// | |||||
this.IPTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.IPTextBox.Location = new System.Drawing.Point(150, 7); | |||||
this.IPTextBox.MaxLength = 512; | |||||
this.IPTextBox.Name = "IPTextBox"; | |||||
this.IPTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.IPTextBox.TabIndex = 0; | |||||
this.IPTextBox.WordWrap = false; | |||||
// | |||||
// ServerPortTextBox | |||||
// | |||||
this.ServerPortTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ServerPortTextBox.Location = new System.Drawing.Point(150, 38); | |||||
this.ServerPortTextBox.MaxLength = 10; | |||||
this.ServerPortTextBox.Name = "ServerPortTextBox"; | |||||
this.ServerPortTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.ServerPortTextBox.TabIndex = 1; | |||||
this.ServerPortTextBox.WordWrap = false; | |||||
// | |||||
// PasswordTextBox | |||||
// | |||||
this.PasswordTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.PasswordTextBox.Font = new System.Drawing.Font("Consolas", 9F); | |||||
this.PasswordTextBox.Location = new System.Drawing.Point(150, 69); | |||||
this.PasswordTextBox.MaxLength = 256; | |||||
this.PasswordTextBox.Name = "PasswordTextBox"; | |||||
this.PasswordTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.PasswordTextBox.TabIndex = 2; | |||||
this.PasswordTextBox.UseSystemPasswordChar = true; | |||||
this.PasswordTextBox.WordWrap = false; | |||||
// | |||||
// EncryptionLabel | |||||
// | |||||
this.EncryptionLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.EncryptionLabel.AutoSize = true; | |||||
this.EncryptionLabel.Location = new System.Drawing.Point(56, 134); | |||||
this.EncryptionLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.EncryptionLabel.Name = "EncryptionLabel"; | |||||
this.EncryptionLabel.Size = new System.Drawing.Size(87, 15); | |||||
this.EncryptionLabel.TabIndex = 4; | |||||
this.EncryptionLabel.Text = "Encryption"; | |||||
// | |||||
// EncryptionSelect | |||||
// | |||||
this.EncryptionSelect.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.EncryptionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; | |||||
this.EncryptionSelect.FormattingEnabled = true; | |||||
this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl; | |||||
this.EncryptionSelect.ItemHeight = 15; | |||||
this.EncryptionSelect.Location = new System.Drawing.Point(150, 129); | |||||
this.EncryptionSelect.Margin = new System.Windows.Forms.Padding(3, 5, 3, 8); | |||||
this.EncryptionSelect.Name = "EncryptionSelect"; | |||||
this.EncryptionSelect.Size = new System.Drawing.Size(205, 23); | |||||
this.EncryptionSelect.TabIndex = 4; | |||||
// | |||||
// TimeoutLabel | |||||
// | |||||
this.TimeoutLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.TimeoutLabel.AutoSize = true; | |||||
this.TimeoutLabel.Location = new System.Drawing.Point(40, 319); | |||||
this.TimeoutLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.TimeoutLabel.Name = "TimeoutLabel"; | |||||
this.TimeoutLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; | |||||
this.TimeoutLabel.Size = new System.Drawing.Size(103, 15); | |||||
this.TimeoutLabel.TabIndex = 9; | |||||
this.TimeoutLabel.Text = "Timeout(Sec)"; | |||||
// | |||||
// TimeoutTextBox | |||||
// | |||||
this.TimeoutTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.TimeoutTextBox.Location = new System.Drawing.Point(150, 314); | |||||
this.TimeoutTextBox.MaxLength = 5; | |||||
this.TimeoutTextBox.Name = "TimeoutTextBox"; | |||||
this.TimeoutTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.TimeoutTextBox.TabIndex = 9; | |||||
// | |||||
// PluginLabel | |||||
// | |||||
this.PluginLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.PluginLabel.AutoSize = true; | |||||
this.PluginLabel.Location = new System.Drawing.Point(24, 168); | |||||
this.PluginLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.PluginLabel.Name = "PluginLabel"; | |||||
this.PluginLabel.Size = new System.Drawing.Size(119, 15); | |||||
this.PluginLabel.TabIndex = 5; | |||||
this.PluginLabel.Text = "Plugin Program"; | |||||
// | |||||
// PluginOptionsTextBox | |||||
// | |||||
this.PluginOptionsTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.PluginOptionsTextBox.Location = new System.Drawing.Point(150, 194); | |||||
this.PluginOptionsTextBox.MaxLength = 256; | |||||
this.PluginOptionsTextBox.Name = "PluginOptionsTextBox"; | |||||
this.PluginOptionsTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.PluginOptionsTextBox.TabIndex = 6; | |||||
this.PluginOptionsTextBox.WordWrap = false; | |||||
// | |||||
// ShowPasswdCheckBox | |||||
// | |||||
this.ShowPasswdCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | |||||
| System.Windows.Forms.AnchorStyles.Left))); | |||||
this.ShowPasswdCheckBox.AutoSize = true; | |||||
this.ShowPasswdCheckBox.Location = new System.Drawing.Point(151, 101); | |||||
this.ShowPasswdCheckBox.Margin = new System.Windows.Forms.Padding(4); | |||||
this.ShowPasswdCheckBox.Name = "ShowPasswdCheckBox"; | |||||
this.ShowPasswdCheckBox.Size = new System.Drawing.Size(133, 19); | |||||
this.ShowPasswdCheckBox.TabIndex = 3; | |||||
this.ShowPasswdCheckBox.Text = "Show Password"; | |||||
this.ShowPasswdCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; | |||||
this.ShowPasswdCheckBox.UseVisualStyleBackColor = true; | |||||
this.ShowPasswdCheckBox.CheckedChanged += new System.EventHandler(this.ShowPasswdCheckBox_CheckedChanged); | |||||
// | |||||
// PluginArgumentsTextBox | |||||
// | |||||
this.PluginArgumentsTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.PluginArgumentsTextBox.Location = new System.Drawing.Point(150, 252); | |||||
this.PluginArgumentsTextBox.MaxLength = 512; | |||||
this.PluginArgumentsTextBox.Name = "PluginArgumentsTextBox"; | |||||
this.PluginArgumentsTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.PluginArgumentsTextBox.TabIndex = 7; | |||||
this.PluginArgumentsTextBox.WordWrap = false; | |||||
// | |||||
// PluginArgumentsLabel | |||||
// | |||||
this.PluginArgumentsLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.PluginArgumentsLabel.AutoSize = true; | |||||
this.PluginArgumentsLabel.Location = new System.Drawing.Point(8, 257); | |||||
this.PluginArgumentsLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.PluginArgumentsLabel.Name = "PluginArgumentsLabel"; | |||||
this.PluginArgumentsLabel.Size = new System.Drawing.Size(135, 15); | |||||
this.PluginArgumentsLabel.TabIndex = 7; | |||||
this.PluginArgumentsLabel.Text = "Plugin Arguments"; | |||||
this.toolTip1.SetToolTip(this.PluginArgumentsLabel, "Not a SIP003 standard. Used as CLI arguments.\r\nMandatory:\r\n%SS_LOCAL_HOST%, %SS_L" + | |||||
"OCAL_PORT%, %SS_REMOTE_HOST%, %SS_REMOTE_PORT%\r\nOptional:\r\n%SS_PLUGIN_OPTIONS%"); | |||||
// | |||||
// RemarksLabel | |||||
// | |||||
this.RemarksLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.RemarksLabel.AutoSize = true; | |||||
this.RemarksLabel.Location = new System.Drawing.Point(80, 288); | |||||
this.RemarksLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.RemarksLabel.Name = "RemarksLabel"; | |||||
this.RemarksLabel.Size = new System.Drawing.Size(63, 15); | |||||
this.RemarksLabel.TabIndex = 8; | |||||
this.RemarksLabel.Text = "Remarks"; | |||||
// | |||||
// NeedPluginArgCheckBox | |||||
// | |||||
this.NeedPluginArgCheckBox.AutoSize = true; | |||||
this.NeedPluginArgCheckBox.Location = new System.Drawing.Point(151, 226); | |||||
this.NeedPluginArgCheckBox.Margin = new System.Windows.Forms.Padding(4); | |||||
this.NeedPluginArgCheckBox.Name = "NeedPluginArgCheckBox"; | |||||
this.NeedPluginArgCheckBox.Size = new System.Drawing.Size(189, 19); | |||||
this.NeedPluginArgCheckBox.TabIndex = 10; | |||||
this.NeedPluginArgCheckBox.Text = "Need Plugin Argument"; | |||||
this.NeedPluginArgCheckBox.UseVisualStyleBackColor = true; | |||||
this.NeedPluginArgCheckBox.CheckedChanged += new System.EventHandler(this.UsePluginArgCheckBox_CheckedChanged); | |||||
// | |||||
// panel2 | |||||
// | |||||
this.panel2.Anchor = System.Windows.Forms.AnchorStyles.Top; | |||||
this.panel2.AutoSize = true; | |||||
this.panel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||||
this.panel2.Location = new System.Drawing.Point(190, 229); | |||||
this.panel2.Margin = new System.Windows.Forms.Padding(4); | |||||
this.panel2.Name = "panel2"; | |||||
this.panel2.Size = new System.Drawing.Size(0, 0); | |||||
this.panel2.TabIndex = 1; | |||||
// | |||||
// OKButton | |||||
// | |||||
this.OKButton.DialogResult = System.Windows.Forms.DialogResult.OK; | |||||
this.OKButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.OKButton.Location = new System.Drawing.Point(282, 501); | |||||
this.OKButton.Name = "OKButton"; | |||||
this.OKButton.Size = new System.Drawing.Size(87, 29); | |||||
this.OKButton.TabIndex = 17; | |||||
this.OKButton.Text = "OK"; | |||||
this.OKButton.UseVisualStyleBackColor = true; | |||||
this.OKButton.Click += new System.EventHandler(this.OKButton_Click); | |||||
// | |||||
// MyCancelButton | |||||
// | |||||
this.MyCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; | |||||
this.MyCancelButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.MyCancelButton.Location = new System.Drawing.Point(375, 501); | |||||
this.MyCancelButton.Name = "MyCancelButton"; | |||||
this.MyCancelButton.Size = new System.Drawing.Size(87, 29); | |||||
this.MyCancelButton.TabIndex = 18; | |||||
this.MyCancelButton.Text = "Cancel"; | |||||
this.MyCancelButton.UseVisualStyleBackColor = true; | |||||
this.MyCancelButton.Click += new System.EventHandler(this.CancelButton_Click); | |||||
// | |||||
// ApplyButton | |||||
// | |||||
this.ApplyButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ApplyButton.Enabled = false; | |||||
this.ApplyButton.Location = new System.Drawing.Point(468, 501); | |||||
this.ApplyButton.Name = "ApplyButton"; | |||||
this.ApplyButton.Size = new System.Drawing.Size(91, 29); | |||||
this.ApplyButton.TabIndex = 19; | |||||
this.ApplyButton.Text = "Apply"; | |||||
this.ApplyButton.UseVisualStyleBackColor = true; | |||||
this.ApplyButton.Click += new System.EventHandler(this.ApplyButton_Click); | |||||
// | |||||
// DeleteButton | |||||
// | |||||
this.DeleteButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.DeleteButton.Location = new System.Drawing.Point(96, 431); | |||||
this.DeleteButton.Name = "DeleteButton"; | |||||
this.DeleteButton.Size = new System.Drawing.Size(87, 29); | |||||
this.DeleteButton.TabIndex = 13; | |||||
this.DeleteButton.Text = "&Delete"; | |||||
this.DeleteButton.UseVisualStyleBackColor = true; | |||||
this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click); | |||||
// | |||||
// AddButton | |||||
// | |||||
this.AddButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.AddButton.Location = new System.Drawing.Point(3, 431); | |||||
this.AddButton.Name = "AddButton"; | |||||
this.AddButton.Size = new System.Drawing.Size(87, 29); | |||||
this.AddButton.TabIndex = 12; | |||||
this.AddButton.Text = "&Add"; | |||||
this.AddButton.UseVisualStyleBackColor = true; | |||||
this.AddButton.Click += new System.EventHandler(this.AddButton_Click); | |||||
// | |||||
// ServerGroupBox | |||||
// | |||||
this.ServerGroupBox.AutoSize = true; | |||||
this.ServerGroupBox.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; | |||||
this.tableLayoutPanel7.SetColumnSpan(this.ServerGroupBox, 4); | |||||
this.ServerGroupBox.Controls.Add(this.tableLayoutPanel1); | |||||
this.ServerGroupBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ServerGroupBox.Location = new System.Drawing.Point(189, 0); | |||||
this.ServerGroupBox.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); | |||||
this.ServerGroupBox.Name = "ServerGroupBox"; | |||||
this.ServerGroupBox.Padding = new System.Windows.Forms.Padding(4); | |||||
this.ServerGroupBox.Size = new System.Drawing.Size(370, 425); | |||||
this.ServerGroupBox.TabIndex = 0; | |||||
this.ServerGroupBox.TabStop = false; | |||||
this.ServerGroupBox.Text = "Server"; | |||||
// | |||||
// ServersListBox | |||||
// | |||||
this.tableLayoutPanel7.SetColumnSpan(this.ServersListBox, 2); | |||||
this.ServersListBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ServersListBox.FormattingEnabled = true; | |||||
this.ServersListBox.IntegralHeight = false; | |||||
this.ServersListBox.ItemHeight = 15; | |||||
this.ServersListBox.Location = new System.Drawing.Point(3, 3); | |||||
this.ServersListBox.Name = "ServersListBox"; | |||||
this.ServersListBox.Size = new System.Drawing.Size(180, 422); | |||||
this.ServersListBox.TabIndex = 11; | |||||
this.ServersListBox.SelectedIndexChanged += new System.EventHandler(this.ServersListBox_SelectedIndexChanged); | |||||
// | |||||
// MoveDownButton | |||||
// | |||||
this.MoveDownButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.MoveDownButton.Location = new System.Drawing.Point(96, 501); | |||||
this.MoveDownButton.Name = "MoveDownButton"; | |||||
this.MoveDownButton.Size = new System.Drawing.Size(87, 29); | |||||
this.MoveDownButton.TabIndex = 16; | |||||
this.MoveDownButton.Text = "Move D&own"; | |||||
this.MoveDownButton.UseVisualStyleBackColor = true; | |||||
this.MoveDownButton.Click += new System.EventHandler(this.MoveDownButton_Click); | |||||
// | |||||
// MoveUpButton | |||||
// | |||||
this.MoveUpButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.MoveUpButton.Location = new System.Drawing.Point(3, 501); | |||||
this.MoveUpButton.Name = "MoveUpButton"; | |||||
this.MoveUpButton.Size = new System.Drawing.Size(87, 29); | |||||
this.MoveUpButton.TabIndex = 15; | |||||
this.MoveUpButton.Text = "Move &Up"; | |||||
this.MoveUpButton.UseVisualStyleBackColor = true; | |||||
this.MoveUpButton.Click += new System.EventHandler(this.MoveUpButton_Click); | |||||
// | |||||
// ProxyPortTextBox | |||||
// | |||||
this.tableLayoutPanel7.SetColumnSpan(this.ProxyPortTextBox, 2); | |||||
this.ProxyPortTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ProxyPortTextBox.Location = new System.Drawing.Point(283, 432); | |||||
this.ProxyPortTextBox.Margin = new System.Windows.Forms.Padding(4); | |||||
this.ProxyPortTextBox.MaxLength = 10; | |||||
this.ProxyPortTextBox.Name = "ProxyPortTextBox"; | |||||
this.ProxyPortTextBox.Size = new System.Drawing.Size(178, 25); | |||||
this.ProxyPortTextBox.TabIndex = 10; | |||||
this.ProxyPortTextBox.WordWrap = false; | |||||
// | |||||
// ProxyPortLabel | |||||
// | |||||
this.ProxyPortLabel.AutoSize = true; | |||||
this.ProxyPortLabel.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.ProxyPortLabel.Location = new System.Drawing.Point(190, 428); | |||||
this.ProxyPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.ProxyPortLabel.Name = "ProxyPortLabel"; | |||||
this.ProxyPortLabel.Size = new System.Drawing.Size(85, 35); | |||||
this.ProxyPortLabel.TabIndex = 10; | |||||
this.ProxyPortLabel.Text = "Proxy Port"; | |||||
this.ProxyPortLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight; | |||||
// | |||||
// PortableModeCheckBox | |||||
// | |||||
this.PortableModeCheckBox.AutoSize = true; | |||||
this.tableLayoutPanel7.SetColumnSpan(this.PortableModeCheckBox, 2); | |||||
this.PortableModeCheckBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.PortableModeCheckBox.Location = new System.Drawing.Point(283, 467); | |||||
this.PortableModeCheckBox.Margin = new System.Windows.Forms.Padding(4); | |||||
this.PortableModeCheckBox.Name = "PortableModeCheckBox"; | |||||
this.PortableModeCheckBox.Size = new System.Drawing.Size(178, 27); | |||||
this.PortableModeCheckBox.TabIndex = 11; | |||||
this.PortableModeCheckBox.Text = "Portable Mode"; | |||||
this.toolTip1.SetToolTip(this.PortableModeCheckBox, "restart required"); | |||||
this.PortableModeCheckBox.UseVisualStyleBackColor = true; | |||||
// | |||||
// DuplicateButton | |||||
// | |||||
this.DuplicateButton.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.DuplicateButton.Location = new System.Drawing.Point(3, 466); | |||||
this.DuplicateButton.Name = "DuplicateButton"; | |||||
this.DuplicateButton.Size = new System.Drawing.Size(87, 29); | |||||
this.DuplicateButton.TabIndex = 14; | |||||
this.DuplicateButton.Text = "Dupli&cate"; | |||||
this.DuplicateButton.UseVisualStyleBackColor = true; | |||||
this.DuplicateButton.Click += new System.EventHandler(this.DuplicateButton_Click); | |||||
// | |||||
// tableLayoutPanel7 | |||||
// | |||||
this.tableLayoutPanel7.AutoSize = true; | |||||
this.tableLayoutPanel7.ColumnCount = 6; | |||||
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); | |||||
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); | |||||
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); | |||||
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); | |||||
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); | |||||
this.tableLayoutPanel7.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F)); | |||||
this.tableLayoutPanel7.Controls.Add(this.ApplyButton, 5, 3); | |||||
this.tableLayoutPanel7.Controls.Add(this.MyCancelButton, 4, 3); | |||||
this.tableLayoutPanel7.Controls.Add(this.OKButton, 3, 3); | |||||
this.tableLayoutPanel7.Controls.Add(this.ProxyPortTextBox, 3, 1); | |||||
this.tableLayoutPanel7.Controls.Add(this.MoveDownButton, 1, 3); | |||||
this.tableLayoutPanel7.Controls.Add(this.ProxyPortLabel, 2, 1); | |||||
this.tableLayoutPanel7.Controls.Add(this.MoveUpButton, 0, 3); | |||||
this.tableLayoutPanel7.Controls.Add(this.AddButton, 0, 1); | |||||
this.tableLayoutPanel7.Controls.Add(this.DeleteButton, 1, 1); | |||||
this.tableLayoutPanel7.Controls.Add(this.DuplicateButton, 0, 2); | |||||
this.tableLayoutPanel7.Controls.Add(this.ServersListBox, 0, 0); | |||||
this.tableLayoutPanel7.Controls.Add(this.ServerGroupBox, 2, 0); | |||||
this.tableLayoutPanel7.Controls.Add(this.PortableModeCheckBox, 3, 2); | |||||
this.tableLayoutPanel7.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.tableLayoutPanel7.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize; | |||||
this.tableLayoutPanel7.Location = new System.Drawing.Point(10, 10); | |||||
this.tableLayoutPanel7.Name = "tableLayoutPanel7"; | |||||
this.tableLayoutPanel7.RowCount = 4; | |||||
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel7.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel7.Size = new System.Drawing.Size(562, 533); | |||||
this.tableLayoutPanel7.TabIndex = 8; | |||||
// | |||||
// GroupLabel | |||||
// | |||||
this.GroupLabel.Anchor = System.Windows.Forms.AnchorStyles.Right; | |||||
this.GroupLabel.AutoSize = true; | |||||
this.GroupLabel.Location = new System.Drawing.Point(96, 350); | |||||
this.GroupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); | |||||
this.GroupLabel.Name = "GroupLabel"; | |||||
this.GroupLabel.RightToLeft = System.Windows.Forms.RightToLeft.No; | |||||
this.GroupLabel.Size = new System.Drawing.Size(47, 15); | |||||
this.GroupLabel.TabIndex = 11; | |||||
this.GroupLabel.Text = "Group"; | |||||
// | |||||
// GroupTextBox | |||||
// | |||||
this.GroupTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.GroupTextBox.Location = new System.Drawing.Point(150, 345); | |||||
this.GroupTextBox.MaxLength = 5; | |||||
this.GroupTextBox.Name = "GroupTextBox"; | |||||
this.GroupTextBox.ReadOnly = true; | |||||
this.GroupTextBox.Size = new System.Drawing.Size(205, 25); | |||||
this.GroupTextBox.TabIndex = 12; | |||||
// | |||||
// ConfigForm | |||||
// | |||||
this.AcceptButton = this.OKButton; | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; | |||||
this.CancelButton = this.MyCancelButton; | |||||
this.ClientSize = new System.Drawing.Size(582, 553); | |||||
this.Controls.Add(this.tableLayoutPanel7); | |||||
this.Controls.Add(this.panel2); | |||||
this.Margin = new System.Windows.Forms.Padding(4); | |||||
this.MinimizeBox = false; | |||||
this.MinimumSize = new System.Drawing.Size(400, 575); | |||||
this.Name = "ConfigForm"; | |||||
this.Padding = new System.Windows.Forms.Padding(10); | |||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | |||||
this.Text = "Edit Servers"; | |||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.ConfigForm_FormClosed); | |||||
this.Shown += new System.EventHandler(this.ConfigForm_Shown); | |||||
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.ConfigForm_KeyDown); | |||||
this.tableLayoutPanel1.ResumeLayout(false); | |||||
this.tableLayoutPanel1.PerformLayout(); | |||||
this.ServerGroupBox.ResumeLayout(false); | |||||
this.ServerGroupBox.PerformLayout(); | |||||
this.tableLayoutPanel7.ResumeLayout(false); | |||||
this.tableLayoutPanel7.PerformLayout(); | |||||
this.ResumeLayout(false); | |||||
this.PerformLayout(); | |||||
} | |||||
#endregion | |||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | |||||
private System.Windows.Forms.Label IPLabel; | |||||
private System.Windows.Forms.Label ServerPortLabel; | |||||
private System.Windows.Forms.Label PasswordLabel; | |||||
private System.Windows.Forms.TextBox IPTextBox; | |||||
private System.Windows.Forms.TextBox ServerPortTextBox; | |||||
private System.Windows.Forms.Label EncryptionLabel; | |||||
private System.Windows.Forms.ComboBox EncryptionSelect; | |||||
private System.Windows.Forms.Panel panel2; | |||||
private System.Windows.Forms.Button OKButton; | |||||
private System.Windows.Forms.Button MyCancelButton; | |||||
private System.Windows.Forms.Button ApplyButton; | |||||
private System.Windows.Forms.Button DeleteButton; | |||||
private System.Windows.Forms.Button AddButton; | |||||
private System.Windows.Forms.GroupBox ServerGroupBox; | |||||
private System.Windows.Forms.ListBox ServersListBox; | |||||
private System.Windows.Forms.TextBox RemarksTextBox; | |||||
private System.Windows.Forms.Label RemarksLabel; | |||||
private System.Windows.Forms.TextBox ProxyPortTextBox; | |||||
private System.Windows.Forms.Label ProxyPortLabel; | |||||
private System.Windows.Forms.Button MoveDownButton; | |||||
private System.Windows.Forms.Button MoveUpButton; | |||||
private System.Windows.Forms.Button DuplicateButton; | |||||
private System.Windows.Forms.Label TimeoutLabel; | |||||
private System.Windows.Forms.TextBox TimeoutTextBox; | |||||
private System.Windows.Forms.Label PluginOptionsLabel; | |||||
private System.Windows.Forms.TextBox PluginTextBox; | |||||
private System.Windows.Forms.Label PluginLabel; | |||||
private System.Windows.Forms.TextBox PluginOptionsTextBox; | |||||
private System.Windows.Forms.CheckBox ShowPasswdCheckBox; | |||||
private System.Windows.Forms.TextBox PasswordTextBox; | |||||
private System.Windows.Forms.TextBox PluginArgumentsTextBox; | |||||
private System.Windows.Forms.Label PluginArgumentsLabel; | |||||
private System.Windows.Forms.ToolTip toolTip1; | |||||
private System.Windows.Forms.CheckBox PortableModeCheckBox; | |||||
private System.Windows.Forms.CheckBox NeedPluginArgCheckBox; | |||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel7; | |||||
private System.Windows.Forms.Label GroupLabel; | |||||
private System.Windows.Forms.TextBox GroupTextBox; | |||||
} | |||||
} | |||||
@@ -1,552 +0,0 @@ | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Encryption; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Properties; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Drawing; | |||||
using System.Linq; | |||||
using System.Windows.Forms; | |||||
namespace Shadowsocks.View | |||||
{ | |||||
public partial class ConfigForm : Form | |||||
{ | |||||
private ShadowsocksController controller; | |||||
// this is a copy of configuration that we are working on | |||||
private Configuration _modifiedConfiguration; | |||||
private int _lastSelectedIndex = -1; | |||||
private bool isChange = false; | |||||
public ConfigForm(ShadowsocksController controller) | |||||
{ | |||||
Font = SystemFonts.MessageBoxFont; | |||||
InitializeComponent(); | |||||
EncryptionSelect.Items.AddRange(EncryptorFactory.ListAvaliableCiphers().ToArray()); | |||||
PerformLayout(); | |||||
UpdateTexts(); | |||||
SetupValueChangedListeners(); | |||||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||||
this.controller = controller; | |||||
controller.ConfigChanged += Controller_ConfigChanged; | |||||
LoadCurrentConfiguration(); | |||||
} | |||||
private void UpdateTexts() | |||||
{ | |||||
I18N.TranslateForm(this); | |||||
toolTip1.SetToolTip(PortableModeCheckBox, I18N.GetString("Restart required")); | |||||
} | |||||
private void SetupValueChangedListeners() | |||||
{ | |||||
IPTextBox.TextChanged += ConfigValueChanged; | |||||
ProxyPortTextBox.TextChanged += ConfigValueChanged; | |||||
PasswordTextBox.TextChanged += ConfigValueChanged; | |||||
EncryptionSelect.SelectedIndexChanged += ConfigValueChanged; | |||||
PluginTextBox.TextChanged += ConfigValueChanged; | |||||
PluginArgumentsTextBox.TextChanged += ConfigValueChanged; | |||||
PluginOptionsTextBox.TextChanged += ConfigValueChanged; | |||||
RemarksTextBox.TextChanged += ConfigValueChanged; | |||||
TimeoutTextBox.TextChanged += ConfigValueChanged; | |||||
PortableModeCheckBox.CheckedChanged += ConfigValueChanged; | |||||
ServerPortTextBox.TextChanged += ConfigValueChanged; | |||||
} | |||||
private void Controller_ConfigChanged(object sender, EventArgs e) | |||||
{ | |||||
LoadCurrentConfiguration(); | |||||
} | |||||
private void ConfigValueChanged(object sender, EventArgs e) | |||||
{ | |||||
isChange = true; | |||||
ApplyButton.Enabled = true; | |||||
} | |||||
private bool ValidateAndSaveSelectedServerDetails(bool isSave = false, bool isCopy = false) | |||||
{ | |||||
try | |||||
{ | |||||
if (_lastSelectedIndex == -1 || _lastSelectedIndex >= _modifiedConfiguration.configs.Count) | |||||
{ | |||||
return true; | |||||
} | |||||
bool verify = GetServerDetailsFromUI(out Server server, isSave, isCopy); | |||||
if (server != null) | |||||
{ | |||||
if (isSave || isCopy) | |||||
Configuration.CheckServer(server); | |||||
_modifiedConfiguration.configs[_lastSelectedIndex] = server; | |||||
} | |||||
return verify; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
MessageBox.Show(ex.Message); | |||||
} | |||||
return false; | |||||
} | |||||
private bool GetServerDetailsFromUI(out Server server, bool isSave = false, bool isCopy = false) | |||||
{ | |||||
server = null; | |||||
bool? checkIP = false; | |||||
bool? checkPort = false; | |||||
bool? checkPassword = false; | |||||
bool? checkTimeout = false; | |||||
if ((checkIP = CheckIPTextBox(out string address, isSave, isCopy)).GetValueOrDefault(false) && address != null | |||||
&& (checkPort = CheckServerPortTextBox(out int? addressPort, isSave, isCopy)).GetValueOrDefault(false) && addressPort.HasValue | |||||
&& (checkPassword = CheckPasswordTextBox(out string serverPassword, isSave, isCopy)).GetValueOrDefault(false) && serverPassword != null | |||||
&& (checkTimeout = CheckTimeoutTextBox(out int? timeout, isSave, isCopy)).GetValueOrDefault(false) && timeout.HasValue) | |||||
{ | |||||
server = new Server() | |||||
{ | |||||
server = address, | |||||
server_port = addressPort.Value, | |||||
password = serverPassword, | |||||
method = ((CipherInfo)EncryptionSelect.SelectedItem).Name, | |||||
plugin = PluginTextBox.Text, | |||||
plugin_opts = PluginOptionsTextBox.Text, | |||||
plugin_args = PluginArgumentsTextBox.Text, | |||||
remarks = RemarksTextBox.Text, | |||||
timeout = timeout.Value, | |||||
group = GroupTextBox.Text | |||||
}; | |||||
return true; | |||||
} | |||||
if (checkIP == null || checkPort == null || checkTimeout == null) | |||||
{ | |||||
_modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); | |||||
ServersListBox.SelectedIndexChanged -= ServersListBox_SelectedIndexChanged; | |||||
int lastIndex = ServersListBox.SelectedIndex; | |||||
LoadServerNameListToUI(_modifiedConfiguration); | |||||
_lastSelectedIndex = (ServersListBox.SelectedIndex = (_lastSelectedIndex == ServersListBox.Items.Count ? lastIndex : lastIndex - 1)); | |||||
ServersListBox.SelectedIndexChanged += ServersListBox_SelectedIndexChanged; | |||||
return true; | |||||
} | |||||
else | |||||
return false; | |||||
} | |||||
#region GetServerDetailsFromUI Check | |||||
private bool? CheckIPTextBox(out string address, bool isSave, bool isCopy) | |||||
{ | |||||
address = null; | |||||
string outAddress; | |||||
if (Uri.CheckHostName(outAddress = IPTextBox.Text.Trim()) == UriHostNameType.Unknown) | |||||
{ | |||||
if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) | |||||
{ | |||||
DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.OK) | |||||
return null; | |||||
} | |||||
else if (isChange && !isSave && !isCopy) | |||||
{ | |||||
var result = MessageBox.Show(I18N.GetString("Invalid server address, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.Cancel) | |||||
return false; | |||||
else | |||||
{ | |||||
address = _modifiedConfiguration.configs[_lastSelectedIndex].server; | |||||
return true; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Invalid server address"), I18N.GetString("Operation failure")); | |||||
IPTextBox.Focus(); | |||||
} | |||||
return false; | |||||
} | |||||
else | |||||
{ | |||||
address = outAddress; | |||||
} | |||||
return true; | |||||
} | |||||
private bool? CheckServerPortTextBox(out int? addressPort, bool isSave, bool isCopy) | |||||
{ | |||||
addressPort = null; | |||||
if (!int.TryParse(ServerPortTextBox.Text, out int outaddressPort)) | |||||
{ | |||||
if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) | |||||
{ | |||||
DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.OK) | |||||
return null; | |||||
} | |||||
else if (isChange && !isSave && !isCopy) | |||||
{ | |||||
var result = MessageBox.Show(I18N.GetString("Illegal port number format, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.Cancel) | |||||
return false; | |||||
else | |||||
{ | |||||
addressPort = _modifiedConfiguration.configs[_lastSelectedIndex].server_port; | |||||
return true; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Illegal port number format"), I18N.GetString("Operation failure")); | |||||
ServerPortTextBox.Focus(); | |||||
} | |||||
return false; | |||||
} | |||||
else | |||||
{ | |||||
addressPort = outaddressPort; | |||||
} | |||||
return true; | |||||
} | |||||
private bool? CheckPasswordTextBox(out string password, bool isSave, bool isCopy) | |||||
{ | |||||
password = null; | |||||
string outPassword; | |||||
if (string.IsNullOrWhiteSpace(outPassword = PasswordTextBox.Text)) | |||||
{ | |||||
if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) | |||||
{ | |||||
DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.OK) | |||||
return null; | |||||
} | |||||
else if (isChange && !isSave && !isCopy) | |||||
{ | |||||
var result = MessageBox.Show(I18N.GetString("Password can not be blank, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.Cancel) | |||||
return false; | |||||
else | |||||
{ | |||||
password = _modifiedConfiguration.configs[_lastSelectedIndex].password; | |||||
return true; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Password can not be blank"), I18N.GetString("Operation failure")); | |||||
PasswordTextBox.Focus(); | |||||
} | |||||
return false; | |||||
} | |||||
else | |||||
{ | |||||
password = outPassword; | |||||
} | |||||
return true; | |||||
} | |||||
private bool? CheckTimeoutTextBox(out int? timeout, bool isSave, bool isCopy) | |||||
{ | |||||
timeout = null; | |||||
if (!int.TryParse(TimeoutTextBox.Text, out int outTimeout)) | |||||
{ | |||||
if (!isSave && !isCopy && ServersListBox.Items.Count > 1 && I18N.GetString("New server").Equals(ServersListBox.Items[_lastSelectedIndex].ToString())) | |||||
{ | |||||
DialogResult result = MessageBox.Show(I18N.GetString("Whether to discard unconfigured servers"), I18N.GetString("Operation failure"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.OK) | |||||
return null; | |||||
} | |||||
else if (isChange && !isSave && !isCopy) | |||||
{ | |||||
var result = MessageBox.Show(I18N.GetString("Illegal timeout format, Cannot automatically save or discard changes"), I18N.GetString("Auto save failed"), MessageBoxButtons.OKCancel); | |||||
if (result == DialogResult.Cancel) | |||||
return false; | |||||
else | |||||
{ | |||||
timeout = _modifiedConfiguration.configs[_lastSelectedIndex].timeout; | |||||
return true; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
MessageBox.Show(I18N.GetString("Illegal timeout format"), I18N.GetString("Operation failure")); | |||||
TimeoutTextBox.Focus(); | |||||
} | |||||
return false; | |||||
} | |||||
else | |||||
{ | |||||
timeout = outTimeout; | |||||
} | |||||
return true; | |||||
} | |||||
#endregion | |||||
private void LoadSelectedServerDetails() | |||||
{ | |||||
if (ServersListBox.SelectedIndex >= 0 && ServersListBox.SelectedIndex < _modifiedConfiguration.configs.Count) | |||||
{ | |||||
Server server = _modifiedConfiguration.configs[ServersListBox.SelectedIndex]; | |||||
SetServerDetailsToUI(server); | |||||
} | |||||
} | |||||
private void SetServerDetailsToUI(Server server) | |||||
{ | |||||
IPTextBox.Text = server.server; | |||||
ServerPortTextBox.Text = server.server_port.ToString(); | |||||
PasswordTextBox.Text = server.password; | |||||
EncryptionSelect.SelectedItem = EncryptorFactory.GetCipherInfo(server.method ?? Server.DefaultMethod); | |||||
PluginTextBox.Text = server.plugin; | |||||
PluginOptionsTextBox.Text = server.plugin_opts; | |||||
PluginArgumentsTextBox.Text = server.plugin_args; | |||||
bool showPluginArgInput = !string.IsNullOrEmpty(server.plugin_args); | |||||
NeedPluginArgCheckBox.Checked = showPluginArgInput; | |||||
ShowHidePluginArgInput(showPluginArgInput); | |||||
RemarksTextBox.Text = server.remarks; | |||||
TimeoutTextBox.Text = server.timeout.ToString(); | |||||
GroupTextBox.Text = server.group; | |||||
isChange = false; | |||||
} | |||||
private void ShowHidePluginArgInput(bool show) | |||||
{ | |||||
PluginArgumentsTextBox.Visible = show; | |||||
PluginArgumentsLabel.Visible = show; | |||||
} | |||||
private void LoadServerNameListToUI(Configuration configuration) | |||||
{ | |||||
ServersListBox.Items.Clear(); | |||||
foreach (Server server in configuration.configs) | |||||
{ | |||||
ServersListBox.Items.Add(server.ToString()); | |||||
} | |||||
} | |||||
private void LoadCurrentConfiguration() | |||||
{ | |||||
_modifiedConfiguration = controller.GetCurrentConfiguration(); | |||||
LoadServerNameListToUI(_modifiedConfiguration); | |||||
_lastSelectedIndex = _modifiedConfiguration.index; | |||||
if (_lastSelectedIndex < 0 || _lastSelectedIndex >= ServersListBox.Items.Count) | |||||
{ | |||||
_lastSelectedIndex = 0; | |||||
} | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | |||||
UpdateButtons(); | |||||
LoadSelectedServerDetails(); | |||||
ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); | |||||
PortableModeCheckBox.Checked = _modifiedConfiguration.portableMode; | |||||
ApplyButton.Enabled = false; | |||||
} | |||||
private bool SaveValidConfiguration() | |||||
{ | |||||
if (!ValidateAndSaveSelectedServerDetails(isSave: true)) | |||||
{ | |||||
return false; | |||||
} | |||||
int localPort = int.Parse(ProxyPortTextBox.Text); | |||||
Configuration.CheckLocalPort(localPort); | |||||
_modifiedConfiguration.localPort = localPort; | |||||
_modifiedConfiguration.portableMode = PortableModeCheckBox.Checked; | |||||
controller.SaveServers(_modifiedConfiguration.configs, _modifiedConfiguration.localPort, _modifiedConfiguration.portableMode); | |||||
// SelectedIndex remains valid | |||||
// We handled this in event handlers, e.g. Add/DeleteButton, SelectedIndexChanged | |||||
// and move operations | |||||
controller.SelectServerIndex(ServersListBox.SelectedIndex); | |||||
return true; | |||||
} | |||||
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) | |||||
{ | |||||
SaveValidConfiguration(); | |||||
} | |||||
} | |||||
private void ServersListBox_SelectedIndexChanged(object sender, EventArgs e) | |||||
{ | |||||
if (!ServersListBox.CanSelect) | |||||
{ | |||||
return; | |||||
} | |||||
if (_lastSelectedIndex == ServersListBox.SelectedIndex) | |||||
{ | |||||
// we are moving back to oldSelectedIndex or doing a force move | |||||
return; | |||||
} | |||||
if (!ValidateAndSaveSelectedServerDetails()) | |||||
{ | |||||
// why this won't cause stack overflow? | |||||
ServersListBox.SelectedIndex = _lastSelectedIndex; | |||||
return; | |||||
} | |||||
if (_lastSelectedIndex >= 0 && _lastSelectedIndex < _modifiedConfiguration.configs.Count) | |||||
{ | |||||
ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].ToString(); | |||||
} | |||||
UpdateButtons(); | |||||
LoadSelectedServerDetails(); | |||||
_lastSelectedIndex = ServersListBox.SelectedIndex; | |||||
} | |||||
private void AddButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (ValidateAndSaveSelectedServerDetails(isSave: true)) | |||||
{ | |||||
Configuration.AddDefaultServerOrServer(_modifiedConfiguration); | |||||
LoadServerNameListToUI(_modifiedConfiguration); | |||||
_lastSelectedIndex = (ServersListBox.SelectedIndex = _modifiedConfiguration.configs.Count - 1); | |||||
} | |||||
} | |||||
private void DuplicateButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (ValidateAndSaveSelectedServerDetails(isCopy: true)) | |||||
{ | |||||
Server currServer = _modifiedConfiguration.configs[_lastSelectedIndex]; | |||||
Configuration.AddDefaultServerOrServer(_modifiedConfiguration, currServer, _lastSelectedIndex + 1); | |||||
LoadServerNameListToUI(_modifiedConfiguration); | |||||
_lastSelectedIndex = (ServersListBox.SelectedIndex = (_lastSelectedIndex + 1)); | |||||
} | |||||
} | |||||
private void DeleteButton_Click(object sender, EventArgs e) | |||||
{ | |||||
_modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); | |||||
if (_modifiedConfiguration.configs.Count == 0) | |||||
{ | |||||
Configuration.AddDefaultServerOrServer(_modifiedConfiguration); | |||||
} | |||||
LoadServerNameListToUI(_modifiedConfiguration); | |||||
ServersListBox.SelectedIndexChanged -= ServersListBox_SelectedIndexChanged; | |||||
_lastSelectedIndex = (ServersListBox.SelectedIndex = (_lastSelectedIndex >= _modifiedConfiguration.configs.Count ? (_modifiedConfiguration.configs.Count - 1) : _lastSelectedIndex)); | |||||
ServersListBox.SelectedIndexChanged += ServersListBox_SelectedIndexChanged; | |||||
LoadSelectedServerDetails(); | |||||
UpdateButtons(); | |||||
} | |||||
private void UpdateButtons() | |||||
{ | |||||
DeleteButton.Enabled = (ServersListBox.Items.Count > 0); | |||||
MoveUpButton.Enabled = (ServersListBox.SelectedIndex > 0); | |||||
MoveDownButton.Enabled = (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1); | |||||
} | |||||
private void MoveUpButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (ServersListBox.SelectedIndex > 0) | |||||
{ | |||||
MoveConfigItem(-1); // -1 means move backward | |||||
} | |||||
} | |||||
private void MoveDownButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (ServersListBox.SelectedIndex < ServersListBox.Items.Count - 1) | |||||
{ | |||||
MoveConfigItem(+1); // +1 means move forward | |||||
} | |||||
} | |||||
private void MoveConfigItem(int step) | |||||
{ | |||||
var server = _modifiedConfiguration.configs[_lastSelectedIndex]; | |||||
var newIndex = _lastSelectedIndex + step; | |||||
_modifiedConfiguration.configs.RemoveAt(_lastSelectedIndex); | |||||
_modifiedConfiguration.configs.Insert(newIndex, server); | |||||
ServersListBox.BeginUpdate(); | |||||
LoadServerNameListToUI(_modifiedConfiguration); | |||||
_lastSelectedIndex = newIndex; | |||||
ServersListBox.SelectedIndex = newIndex; | |||||
ServersListBox.EndUpdate(); | |||||
UpdateButtons(); | |||||
} | |||||
private void OKButton_Click(object sender, EventArgs e) | |||||
{ | |||||
if (SaveValidConfiguration()) | |||||
{ | |||||
Close(); | |||||
} | |||||
} | |||||
private void CancelButton_Click(object sender, EventArgs e) | |||||
{ | |||||
Close(); | |||||
} | |||||
private void ApplyButton_Click(object sender, EventArgs e) | |||||
{ | |||||
SaveValidConfiguration(); | |||||
} | |||||
private void ConfigForm_Shown(object sender, EventArgs e) | |||||
{ | |||||
IPTextBox.Focus(); | |||||
} | |||||
private void ConfigForm_FormClosed(object sender, FormClosedEventArgs e) | |||||
{ | |||||
controller.ConfigChanged -= Controller_ConfigChanged; | |||||
} | |||||
private void ShowPasswdCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
PasswordTextBox.UseSystemPasswordChar = !ShowPasswdCheckBox.Checked; | |||||
} | |||||
private void UsePluginArgCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
ShowHidePluginArgInput(NeedPluginArgCheckBox.Checked); | |||||
} | |||||
} | |||||
} |
@@ -1,126 +0,0 @@ | |||||
<?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="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||||
<value>17, 17</value> | |||||
</metadata> | |||||
<metadata name="toolTip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> | |||||
<value>17, 17</value> | |||||
</metadata> | |||||
</root> |
@@ -1,100 +0,0 @@ | |||||
namespace Shadowsocks.View | |||||
{ | |||||
partial class InputBox | |||||
{ | |||||
/// <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.label1 = new System.Windows.Forms.Label(); | |||||
this.textBox1 = new System.Windows.Forms.TextBox(); | |||||
this.btnOK = new System.Windows.Forms.Button(); | |||||
this.button2 = new System.Windows.Forms.Button(); | |||||
this.SuspendLayout(); | |||||
// | |||||
// label1 | |||||
// | |||||
this.label1.AutoSize = true; | |||||
this.label1.Location = new System.Drawing.Point(12, 9); | |||||
this.label1.Name = "label1"; | |||||
this.label1.Size = new System.Drawing.Size(55, 15); | |||||
this.label1.TabIndex = 0; | |||||
this.label1.Text = "label1"; | |||||
// | |||||
// textBox1 | |||||
// | |||||
this.textBox1.Location = new System.Drawing.Point(13, 51); | |||||
this.textBox1.Name = "textBox1"; | |||||
this.textBox1.Size = new System.Drawing.Size(484, 25); | |||||
this.textBox1.TabIndex = 1; | |||||
// | |||||
// btnOK | |||||
// | |||||
this.btnOK.Location = new System.Drawing.Point(321, 91); | |||||
this.btnOK.Name = "btnOK"; | |||||
this.btnOK.Size = new System.Drawing.Size(75, 31); | |||||
this.btnOK.TabIndex = 2; | |||||
this.btnOK.Text = "OK"; | |||||
this.btnOK.UseVisualStyleBackColor = true; | |||||
// | |||||
// button2 | |||||
// | |||||
this.button2.Location = new System.Drawing.Point(422, 91); | |||||
this.button2.Name = "button2"; | |||||
this.button2.Size = new System.Drawing.Size(75, 31); | |||||
this.button2.TabIndex = 3; | |||||
this.button2.Text = "Cancel"; | |||||
this.button2.UseVisualStyleBackColor = true; | |||||
// | |||||
// InputBox | |||||
// | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | |||||
this.ClientSize = new System.Drawing.Size(554, 134); | |||||
this.Controls.Add(this.button2); | |||||
this.Controls.Add(this.btnOK); | |||||
this.Controls.Add(this.textBox1); | |||||
this.Controls.Add(this.label1); | |||||
this.DoubleBuffered = true; | |||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; | |||||
this.MaximizeBox = false; | |||||
this.MinimizeBox = false; | |||||
this.Name = "InputBox"; | |||||
this.ShowInTaskbar = false; | |||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; | |||||
this.Text = "InputBox"; | |||||
this.ResumeLayout(false); | |||||
this.PerformLayout(); | |||||
} | |||||
#endregion | |||||
private System.Windows.Forms.Label label1; | |||||
private System.Windows.Forms.TextBox textBox1; | |||||
private System.Windows.Forms.Button btnOK; | |||||
private System.Windows.Forms.Button button2; | |||||
} | |||||
} |
@@ -1,24 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.ComponentModel; | |||||
using System.Data; | |||||
using System.Drawing; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using System.Windows.Forms; | |||||
namespace Shadowsocks.View | |||||
{ | |||||
internal partial class InputBox : Form | |||||
{ | |||||
public InputBox() | |||||
{ | |||||
InitializeComponent(); | |||||
} | |||||
internal string Prompt { get { return label1.Text; } set { label1.Text = value; } } | |||||
internal string Response { get { return textBox1.Text; } set { textBox1.Text = value; } } | |||||
} | |||||
} |
@@ -1,120 +0,0 @@ | |||||
<?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> |
@@ -1,332 +0,0 @@ | |||||
namespace Shadowsocks.View | |||||
{ | |||||
partial class LogForm | |||||
{ | |||||
/// <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.LogMessageTextBox = new System.Windows.Forms.TextBox(); | |||||
this.MainMenu = new System.Windows.Forms.MenuStrip(); | |||||
this.FileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.OpenLocationToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.ExitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.ViewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.ClearLogsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.ChangeFontToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.WrapTextToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.TopMostToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.ToolStripMenuItemSeparater = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.ShowToolbarToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); | |||||
this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.ChangeFontButton = new System.Windows.Forms.Button(); | |||||
this.ClearLogsButton = new System.Windows.Forms.Button(); | |||||
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); | |||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | |||||
this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); | |||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer(); | |||||
this.trafficChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); | |||||
this.tableLayoutPanel1.SuspendLayout(); | |||||
this.ToolbarFlowLayoutPanel.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); | |||||
this.splitContainer1.Panel1.SuspendLayout(); | |||||
this.splitContainer1.Panel2.SuspendLayout(); | |||||
this.splitContainer1.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.trafficChart)).BeginInit(); | |||||
this.SuspendLayout(); | |||||
// | |||||
// LogMessageTextBox | |||||
// | |||||
this.LogMessageTextBox.BackColor = System.Drawing.Color.Black; | |||||
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | |||||
this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; | |||||
this.LogMessageTextBox.Location = new System.Drawing.Point(0, 0); | |||||
this.LogMessageTextBox.MaxLength = 2147483647; | |||||
this.LogMessageTextBox.Multiline = true; | |||||
this.LogMessageTextBox.Name = "LogMessageTextBox"; | |||||
this.LogMessageTextBox.ReadOnly = true; | |||||
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; | |||||
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 74); | |||||
this.LogMessageTextBox.TabIndex = 0; | |||||
// | |||||
// MainMenu | |||||
// | |||||
this.MainMenu.Items.AddRange(new System.Windows.Forms.ToolStripMenuItem[] { | |||||
this.FileToolStripMenuItem, | |||||
this.ViewToolStripMenuItem}); | |||||
// | |||||
// FileToolStripMenuItem | |||||
// | |||||
this.FileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripMenuItem[] { | |||||
this.OpenLocationToolStripMenuItem, | |||||
this.ExitToolStripMenuItem}); | |||||
this.FileToolStripMenuItem.Text = "&File"; | |||||
// | |||||
// OpenLocationToolStripMenuItem | |||||
// | |||||
this.OpenLocationToolStripMenuItem.Text = "&Open Location"; | |||||
this.OpenLocationToolStripMenuItem.Click += new System.EventHandler(this.OpenLocationToolStripMenuItem_Click); | |||||
// | |||||
// ExitToolStripMenuItem | |||||
// | |||||
this.ExitToolStripMenuItem.Text = "E&xit"; | |||||
this.ExitToolStripMenuItem.Click += new System.EventHandler(this.ExitToolStripMenuItem_Click); | |||||
// | |||||
// ViewToolStripMenuItem | |||||
// | |||||
this.ViewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripMenuItem[] { | |||||
this.ClearLogsToolStripMenuItem, | |||||
this.ChangeFontToolStripMenuItem, | |||||
this.WrapTextToolStripMenuItem, | |||||
this.TopMostToolStripMenuItem, | |||||
this.ToolStripMenuItemSeparater, | |||||
this.ShowToolbarToolStripMenuItem}); | |||||
this.ViewToolStripMenuItem.Text = "&View"; | |||||
// | |||||
// ClearLogsToolStripMenuItem | |||||
// | |||||
this.ClearLogsToolStripMenuItem.Text = "&Clear Logs"; | |||||
this.ClearLogsToolStripMenuItem.Click += new System.EventHandler(this.ClearLogsToolStripMenuItem_Click); | |||||
// | |||||
// ChangeFontToolStripMenuItem | |||||
// | |||||
this.ChangeFontToolStripMenuItem.Text = "Change &Font"; | |||||
this.ChangeFontToolStripMenuItem.Click += new System.EventHandler(this.ChangeFontToolStripMenuItem_Click); | |||||
// | |||||
// WrapTextToolStripMenuItem | |||||
// | |||||
this.WrapTextToolStripMenuItem.Text = "&Wrap Text"; | |||||
this.WrapTextToolStripMenuItem.Click += new System.EventHandler(this.WrapTextToolStripMenuItem_Click); | |||||
// | |||||
// TopMostToolStripMenuItem | |||||
// | |||||
this.TopMostToolStripMenuItem.Text = "&Top Most"; | |||||
this.TopMostToolStripMenuItem.Click += new System.EventHandler(this.TopMostToolStripMenuItem_Click); | |||||
// | |||||
// ToolStripMenuItemSeparater | |||||
// | |||||
this.ToolStripMenuItemSeparater.Text = "-"; | |||||
// | |||||
// ShowToolbarToolStripMenuItem | |||||
// | |||||
this.ShowToolbarToolStripMenuItem.Text = "&Show Toolbar"; | |||||
this.ShowToolbarToolStripMenuItem.Click += new System.EventHandler(this.ShowToolbarToolStripMenuItem_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(249, 3); | |||||
this.TopMostCheckBox.Name = "TopMostCheckBox"; | |||||
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23); | |||||
this.TopMostCheckBox.TabIndex = 3; | |||||
this.TopMostCheckBox.Text = "&Top Most"; | |||||
this.TopMostCheckBox.UseVisualStyleBackColor = true; | |||||
this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged); | |||||
// | |||||
// ChangeFontButton | |||||
// | |||||
this.ChangeFontButton.AutoSize = true; | |||||
this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); | |||||
this.ChangeFontButton.Name = "ChangeFontButton"; | |||||
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); | |||||
this.ChangeFontButton.TabIndex = 2; | |||||
this.ChangeFontButton.Text = "&Font"; | |||||
this.ChangeFontButton.UseVisualStyleBackColor = true; | |||||
this.ChangeFontButton.Click += new System.EventHandler(this.ChangeFontButton_Click); | |||||
// | |||||
// ClearLogsButton | |||||
// | |||||
this.ClearLogsButton.AutoSize = true; | |||||
this.ClearLogsButton.Location = new System.Drawing.Point(3, 3); | |||||
this.ClearLogsButton.Name = "ClearLogsButton"; | |||||
this.ClearLogsButton.Size = new System.Drawing.Size(75, 23); | |||||
this.ClearLogsButton.TabIndex = 1; | |||||
this.ClearLogsButton.Text = "&Clear Logs"; | |||||
this.ClearLogsButton.UseVisualStyleBackColor = true; | |||||
this.ClearLogsButton.Click += new System.EventHandler(this.ClearLogsButton_Click); | |||||
// | |||||
// 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.Location = new System.Drawing.Point(165, 3); | |||||
this.WrapTextCheckBox.Name = "WrapTextCheckBox"; | |||||
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23); | |||||
this.WrapTextCheckBox.TabIndex = 0; | |||||
this.WrapTextCheckBox.Text = "&Wrap Text"; | |||||
this.WrapTextCheckBox.UseVisualStyleBackColor = true; | |||||
this.WrapTextCheckBox.CheckedChanged += new System.EventHandler(this.WrapTextCheckBox_CheckedChanged); | |||||
// | |||||
// tableLayoutPanel1 | |||||
// | |||||
this.tableLayoutPanel1.ColumnCount = 1; | |||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0); | |||||
this.tableLayoutPanel1.Controls.Add(this.splitContainer1, 0, 1); | |||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | |||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | |||||
this.tableLayoutPanel1.RowCount = 2; | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 161); | |||||
this.tableLayoutPanel1.TabIndex = 2; | |||||
// | |||||
// ToolbarFlowLayoutPanel | |||||
// | |||||
this.ToolbarFlowLayoutPanel.AutoSize = true; | |||||
this.ToolbarFlowLayoutPanel.Controls.Add(this.ClearLogsButton); | |||||
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, 29); | |||||
this.ToolbarFlowLayoutPanel.TabIndex = 2; | |||||
// | |||||
// splitContainer1 | |||||
// | |||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.splitContainer1.Location = new System.Drawing.Point(3, 38); | |||||
this.splitContainer1.Name = "splitContainer1"; | |||||
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; | |||||
// | |||||
// splitContainer1.Panel1 | |||||
// | |||||
this.splitContainer1.Panel1.Controls.Add(this.LogMessageTextBox); | |||||
// | |||||
// splitContainer1.Panel2 | |||||
// | |||||
this.splitContainer1.Panel2.Controls.Add(this.trafficChart); | |||||
this.splitContainer1.Size = new System.Drawing.Size(378, 120); | |||||
this.splitContainer1.SplitterDistance = 74; | |||||
this.splitContainer1.TabIndex = 3; | |||||
// | |||||
// trafficChart | |||||
// | |||||
chartArea1.AxisX.LabelStyle.Enabled = false; | |||||
chartArea1.AxisX.MajorGrid.Interval = 5D; | |||||
chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray; | |||||
chartArea1.AxisX.MajorTickMark.Enabled = false; | |||||
chartArea1.AxisX.Maximum = 61D; | |||||
chartArea1.AxisX.Minimum = 1D; | |||||
chartArea1.AxisY.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount; | |||||
chartArea1.AxisY.LabelAutoFitMaxFontSize = 8; | |||||
chartArea1.AxisY.LabelStyle.Interval = 0D; | |||||
chartArea1.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray; | |||||
chartArea1.AxisY.MajorTickMark.Enabled = false; | |||||
chartArea1.AxisY2.MajorGrid.LineColor = System.Drawing.Color.LightGray; | |||||
chartArea1.AxisY2.Minimum = 0D; | |||||
chartArea1.Name = "ChartArea1"; | |||||
this.trafficChart.ChartAreas.Add(chartArea1); | |||||
this.trafficChart.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
legend1.MaximumAutoSize = 25F; | |||||
legend1.Name = "Legend1"; | |||||
this.trafficChart.Legends.Add(legend1); | |||||
this.trafficChart.Location = new System.Drawing.Point(0, 0); | |||||
this.trafficChart.Name = "trafficChart"; | |||||
this.trafficChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None; | |||||
series1.BorderWidth = 2; | |||||
series1.ChartArea = "ChartArea1"; | |||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline; | |||||
series1.Color = System.Drawing.Color.FromArgb(255, 128, 0); | |||||
series1.IsXValueIndexed = true; | |||||
series1.Legend = "Legend1"; | |||||
series1.Name = "Inbound"; | |||||
series2.BorderWidth = 2; | |||||
series2.ChartArea = "ChartArea1"; | |||||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Spline; | |||||
series2.Color = System.Drawing.Color.FromArgb(128, 128, 255); | |||||
series2.IsXValueIndexed = true; | |||||
series2.Legend = "Legend1"; | |||||
series2.Name = "Outbound"; | |||||
this.trafficChart.Series.Add(series1); | |||||
this.trafficChart.Series.Add(series2); | |||||
this.trafficChart.Size = new System.Drawing.Size(378, 42); | |||||
this.trafficChart.TabIndex = 0; | |||||
this.trafficChart.Text = "chart1"; | |||||
// | |||||
// LogForm | |||||
// | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; | |||||
this.ClientSize = new System.Drawing.Size(384, 161); | |||||
this.Controls.Add(this.tableLayoutPanel1); | |||||
this.MainMenuStrip = this.MainMenu; | |||||
this.MinimumSize = new System.Drawing.Size(400, 200); | |||||
this.Name = "LogForm"; | |||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | |||||
this.Text = "Log Viewer"; | |||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing); | |||||
this.Load += new System.EventHandler(this.LogForm_Load); | |||||
this.Shown += new System.EventHandler(this.LogForm_Shown); | |||||
this.tableLayoutPanel1.ResumeLayout(false); | |||||
this.tableLayoutPanel1.PerformLayout(); | |||||
this.ToolbarFlowLayoutPanel.ResumeLayout(false); | |||||
this.ToolbarFlowLayoutPanel.PerformLayout(); | |||||
this.splitContainer1.Panel1.ResumeLayout(false); | |||||
this.splitContainer1.Panel1.PerformLayout(); | |||||
this.splitContainer1.Panel2.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); | |||||
this.splitContainer1.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.trafficChart)).EndInit(); | |||||
this.ResumeLayout(false); | |||||
} | |||||
#endregion | |||||
private System.Windows.Forms.TextBox LogMessageTextBox; | |||||
private System.Windows.Forms.MenuStrip MainMenu; | |||||
private System.Windows.Forms.ToolStripMenuItem FileToolStripMenuItem; | |||||
private System.Windows.Forms.ToolStripMenuItem OpenLocationToolStripMenuItem; | |||||
private System.Windows.Forms.ToolStripMenuItem ExitToolStripMenuItem; | |||||
private System.Windows.Forms.CheckBox WrapTextCheckBox; | |||||
private System.Windows.Forms.Button ClearLogsButton; | |||||
private System.Windows.Forms.Button ChangeFontButton; | |||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; | |||||
private System.Windows.Forms.CheckBox TopMostCheckBox; | |||||
private System.Windows.Forms.ToolStripMenuItem ViewToolStripMenuItem; | |||||
private System.Windows.Forms.ToolStripMenuItem ClearLogsToolStripMenuItem; | |||||
private System.Windows.Forms.ToolStripMenuItem ChangeFontToolStripMenuItem; | |||||
private System.Windows.Forms.ToolStripMenuItem WrapTextToolStripMenuItem; | |||||
private System.Windows.Forms.ToolStripMenuItem TopMostToolStripMenuItem; | |||||
private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; | |||||
private System.Windows.Forms.ToolStripMenuItem ToolStripMenuItemSeparater; | |||||
private System.Windows.Forms.ToolStripMenuItem ShowToolbarToolStripMenuItem; | |||||
private System.Windows.Forms.SplitContainer splitContainer1; | |||||
private System.Windows.Forms.DataVisualization.Charting.Chart trafficChart; | |||||
} | |||||
} |
@@ -1,447 +0,0 @@ | |||||
using System; | |||||
using System.Drawing; | |||||
using System.IO; | |||||
using System.Windows.Forms; | |||||
using System.Windows.Forms.DataVisualization.Charting; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using Shadowsocks.Controller; | |||||
using Shadowsocks.Properties; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Util; | |||||
using System.Text; | |||||
using NLog; | |||||
namespace Shadowsocks.View | |||||
{ | |||||
public partial class LogForm : Form | |||||
{ | |||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | |||||
long lastOffset; | |||||
string filename; | |||||
Timer timer; | |||||
const int BACK_OFFSET = 65536; | |||||
ShadowsocksController controller; | |||||
// global traffic update lock, make it static | |||||
private static readonly object _lock = new object(); | |||||
#region Traffic Chart | |||||
Queue<TrafficInfo> trafficInfoQueue = new Queue<TrafficInfo>(); | |||||
const int queueMaxLength = 60; | |||||
long lastInbound, lastOutbound; | |||||
long maxSpeed = 0, lastMaxSpeed = 0; | |||||
const long minScale = 50; | |||||
BandwidthScaleInfo bandwidthScale; | |||||
List<float> inboundPoints = new List<float>(); | |||||
List<float> outboundPoints = new List<float>(); | |||||
TextAnnotation inboundAnnotation = new TextAnnotation(); | |||||
TextAnnotation outboundAnnotation = new TextAnnotation(); | |||||
#endregion | |||||
public LogForm(ShadowsocksController controller) | |||||
{ | |||||
this.controller = controller; | |||||
InitializeComponent(); | |||||
Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); | |||||
var nLogConfig = NLogConfig.LoadXML(); | |||||
try | |||||
{ | |||||
this.filename = nLogConfig.GetLogFileName(); | |||||
} | |||||
catch(Exception) | |||||
{ | |||||
// failed to get the file name | |||||
} | |||||
if (string.IsNullOrEmpty(this.filename)) | |||||
{ | |||||
LogMessageTextBox.AppendText("Cannot get the log file name from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||||
} | |||||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||||
topMostTrigger = config.topMost; | |||||
wrapTextTrigger = config.wrapText; | |||||
toolbarTrigger = config.toolbarShown; | |||||
LogMessageTextBox.BackColor = config.BackgroundColor; | |||||
LogMessageTextBox.ForeColor = config.TextColor; | |||||
LogMessageTextBox.Font = config.Font; | |||||
controller.TrafficChanged += controller_TrafficChanged; | |||||
UpdateTexts(); | |||||
} | |||||
private void UpdateTrafficChart() | |||||
{ | |||||
lock (_lock) | |||||
{ | |||||
if (trafficInfoQueue.Count == 0) | |||||
return; | |||||
inboundPoints.Clear(); | |||||
outboundPoints.Clear(); | |||||
maxSpeed = 0; | |||||
foreach (var trafficInfo in trafficInfoQueue) | |||||
{ | |||||
inboundPoints.Add(trafficInfo.inbound); | |||||
outboundPoints.Add(trafficInfo.outbound); | |||||
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficInfo.inbound, trafficInfo.outbound)); | |||||
} | |||||
lastInbound = trafficInfoQueue.Last().inbound; | |||||
lastOutbound = trafficInfoQueue.Last().outbound; | |||||
} | |||||
if (maxSpeed > 0) | |||||
{ | |||||
lastMaxSpeed -= lastMaxSpeed / 32; | |||||
maxSpeed = Math.Max(minScale, Math.Max(maxSpeed, lastMaxSpeed)); | |||||
lastMaxSpeed = maxSpeed; | |||||
} | |||||
else | |||||
{ | |||||
maxSpeed = lastMaxSpeed = minScale; | |||||
} | |||||
bandwidthScale = Utils.GetBandwidthScale(maxSpeed); | |||||
// re-scale the original data points, since it is List<float>, .ForEach does not work | |||||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.unit).ToList(); | |||||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.unit).ToList(); | |||||
if (trafficChart.IsHandleCreated) | |||||
{ | |||||
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints); | |||||
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints); | |||||
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.unitName; | |||||
trafficChart.ChartAreas[0].AxisY.Maximum = bandwidthScale.value; | |||||
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last(); | |||||
inboundAnnotation.Text = Utils.FormatBandwidth(lastInbound); | |||||
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last(); | |||||
outboundAnnotation.Text = Utils.FormatBandwidth(lastOutbound); | |||||
trafficChart.Annotations.Clear(); | |||||
trafficChart.Annotations.Add(inboundAnnotation); | |||||
trafficChart.Annotations.Add(outboundAnnotation); | |||||
} | |||||
} | |||||
private void controller_TrafficChanged(object sender, EventArgs e) | |||||
{ | |||||
lock (_lock) | |||||
{ | |||||
if (trafficInfoQueue.Count == 0) | |||||
{ | |||||
// Init an empty queue | |||||
for (int i = 0; i < queueMaxLength; i++) | |||||
{ | |||||
trafficInfoQueue.Enqueue(new TrafficInfo(0, 0)); | |||||
} | |||||
foreach (var trafficPerSecond in controller.trafficPerSecondQueue) | |||||
{ | |||||
trafficInfoQueue.Enqueue(new TrafficInfo(trafficPerSecond.inboundIncreasement, | |||||
trafficPerSecond.outboundIncreasement)); | |||||
if (trafficInfoQueue.Count > queueMaxLength) | |||||
trafficInfoQueue.Dequeue(); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
var lastTraffic = controller.trafficPerSecondQueue.Last(); | |||||
trafficInfoQueue.Enqueue(new TrafficInfo(lastTraffic.inboundIncreasement, | |||||
lastTraffic.outboundIncreasement)); | |||||
if (trafficInfoQueue.Count > queueMaxLength) | |||||
trafficInfoQueue.Dequeue(); | |||||
} | |||||
} | |||||
} | |||||
private void UpdateTexts() | |||||
{ | |||||
I18N.TranslateForm(this); | |||||
trafficChart.Series["Inbound"].LegendText = I18N.GetString("Inbound"); | |||||
trafficChart.Series["Outbound"].LegendText = I18N.GetString("Outbound"); | |||||
} | |||||
private void Timer_Tick(object sender, EventArgs e) | |||||
{ | |||||
UpdateContent(); | |||||
UpdateTrafficChart(); | |||||
} | |||||
private void InitContent() | |||||
{ | |||||
if (string.IsNullOrEmpty(filename) || !File.Exists(filename)) | |||||
return; | |||||
using (StreamReader reader = new StreamReader(new FileStream(filename, | |||||
FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) | |||||
{ | |||||
if (reader.BaseStream.Length > BACK_OFFSET) | |||||
{ | |||||
reader.BaseStream.Seek(-BACK_OFFSET, SeekOrigin.End); | |||||
reader.ReadLine(); | |||||
} | |||||
string line = ""; | |||||
StringBuilder appendText = new StringBuilder(1024); | |||||
while ((line = reader.ReadLine()) != null) | |||||
appendText.AppendLine(line); | |||||
LogMessageTextBox.AppendText(appendText.ToString()); | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
lastOffset = reader.BaseStream.Position; | |||||
} | |||||
} | |||||
private void UpdateContent() | |||||
{ | |||||
this.Text = I18N.GetString("Log Viewer") + | |||||
$" [in: {Utils.FormatBytes(controller.InboundCounter)}, out: {Utils.FormatBytes(controller.OutboundCounter)}]"; | |||||
if (string.IsNullOrEmpty(filename) || !File.Exists(filename)) | |||||
return; | |||||
try | |||||
{ | |||||
using (StreamReader reader = new StreamReader(new FileStream(filename, | |||||
FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) | |||||
{ | |||||
reader.BaseStream.Seek(lastOffset, SeekOrigin.Begin); | |||||
string line = ""; | |||||
bool changed = false; | |||||
StringBuilder appendText = new StringBuilder(128); | |||||
while ((line = reader.ReadLine()) != null) | |||||
{ | |||||
changed = true; | |||||
appendText.AppendLine(line); | |||||
} | |||||
if (changed) | |||||
{ | |||||
LogMessageTextBox.AppendText(appendText.ToString()); | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
} | |||||
lastOffset = reader.BaseStream.Position; | |||||
} | |||||
} | |||||
catch (FileNotFoundException) | |||||
{ | |||||
} | |||||
} | |||||
private void LogForm_Load(object sender, EventArgs e) | |||||
{ | |||||
InitContent(); | |||||
timer = new Timer(); | |||||
timer.Interval = 100; | |||||
timer.Tick += Timer_Tick; | |||||
timer.Start(); | |||||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||||
Height = config.Height; | |||||
Width = config.Width; | |||||
Top = config.BestTop; | |||||
Left = config.BestLeft; | |||||
if (config.Maximized) | |||||
{ | |||||
WindowState = FormWindowState.Maximized; | |||||
} | |||||
topMostTriggerLock = true; | |||||
TopMost = TopMostToolStripMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; | |||||
topMostTriggerLock = false; | |||||
wrapTextTriggerLock = true; | |||||
LogMessageTextBox.WordWrap = WrapTextToolStripMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger; | |||||
wrapTextTriggerLock = false; | |||||
ToolbarFlowLayoutPanel.Visible = ShowToolbarToolStripMenuItem.Checked = toolbarTrigger; | |||||
} | |||||
private void LogForm_FormClosing(object sender, FormClosingEventArgs e) | |||||
{ | |||||
timer.Stop(); | |||||
controller.TrafficChanged -= controller_TrafficChanged; | |||||
LogViewerConfig config = controller.GetCurrentConfiguration().logViewer; | |||||
config.topMost = topMostTrigger; | |||||
config.wrapText = wrapTextTrigger; | |||||
config.toolbarShown = toolbarTrigger; | |||||
config.Font = LogMessageTextBox.Font; | |||||
config.BackgroundColor = LogMessageTextBox.BackColor; | |||||
config.TextColor = LogMessageTextBox.ForeColor; | |||||
if (WindowState != FormWindowState.Minimized && !(config.Maximized = WindowState == FormWindowState.Maximized)) | |||||
{ | |||||
config.Top = Top; | |||||
config.Left = Left; | |||||
config.Height = Height; | |||||
config.Width = Width; | |||||
} | |||||
controller.SaveLogViewerConfig(config); | |||||
} | |||||
private void OpenLocationToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
string argument = "/select, \"" + filename + "\""; | |||||
logger.Debug(argument); | |||||
System.Diagnostics.Process.Start("explorer.exe", argument); | |||||
} | |||||
private void ExitToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
Close(); | |||||
} | |||||
private void LogForm_Shown(object sender, EventArgs e) | |||||
{ | |||||
LogMessageTextBox.ScrollToCaret(); | |||||
} | |||||
#region Clean up the content in LogMessageTextBox. | |||||
private void DoClearLogs() | |||||
{ | |||||
try | |||||
{ | |||||
File.Delete(filename); | |||||
} | |||||
catch { } | |||||
lastOffset = 0; | |||||
LogMessageTextBox.Clear(); | |||||
} | |||||
private void ClearLogsToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
DoClearLogs(); | |||||
} | |||||
private void ClearLogsButton_Click(object sender, EventArgs e) | |||||
{ | |||||
DoClearLogs(); | |||||
} | |||||
#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) | |||||
{ | |||||
logger.LogUsefulException(ex); | |||||
MessageBox.Show(ex.Message); | |||||
} | |||||
} | |||||
private void ChangeFontToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
DoChangeFont(); | |||||
} | |||||
private void ChangeFontButton_Click(object sender, EventArgs e) | |||||
{ | |||||
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(); | |||||
WrapTextToolStripMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger; | |||||
wrapTextTriggerLock = false; | |||||
} | |||||
private void WrapTextToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
if (!wrapTextTriggerLock) | |||||
{ | |||||
TriggerWrapText(); | |||||
} | |||||
} | |||||
private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
if (!wrapTextTriggerLock) | |||||
{ | |||||
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; | |||||
TopMostToolStripMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; | |||||
topMostTriggerLock = false; | |||||
} | |||||
private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) | |||||
{ | |||||
if (!topMostTriggerLock) | |||||
{ | |||||
TriggerTopMost(); | |||||
} | |||||
} | |||||
private void TopMostToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
if (!topMostTriggerLock) | |||||
{ | |||||
TriggerTopMost(); | |||||
} | |||||
} | |||||
#endregion | |||||
private bool toolbarTrigger = false; | |||||
private void ShowToolbarToolStripMenuItem_Click(object sender, EventArgs e) | |||||
{ | |||||
toolbarTrigger = !toolbarTrigger; | |||||
ToolbarFlowLayoutPanel.Visible = toolbarTrigger; | |||||
ShowToolbarToolStripMenuItem.Checked = toolbarTrigger; | |||||
} | |||||
private class TrafficInfo | |||||
{ | |||||
public long inbound; | |||||
public long outbound; | |||||
public TrafficInfo(long inbound, long outbound) | |||||
{ | |||||
this.inbound = inbound; | |||||
this.outbound = outbound; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,150 +0,0 @@ | |||||
<?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="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> | |||||
</metadata> | |||||
<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="ClearLogsButton.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 name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>39</value> | |||||
</metadata> | |||||
</root> |
@@ -1,73 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<configuration> | |||||
<configSections> | |||||
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<section name="Shadowsocks.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/> | |||||
</sectionGroup> | |||||
</configSections> | |||||
<userSettings> | |||||
<Shadowsocks.Properties.Settings> | |||||
<setting name="LogViewerWidth" serializeAs="String"> | |||||
<value>600</value> | |||||
</setting> | |||||
<setting name="LogViewerHeight" serializeAs="String"> | |||||
<value>400</value> | |||||
</setting> | |||||
<setting name="LogViewerMaximized" serializeAs="String"> | |||||
<value>True</value> | |||||
</setting> | |||||
<setting name="LogViewerTop" serializeAs="String"> | |||||
<value>0</value> | |||||
</setting> | |||||
<setting name="LogViewerLeft" serializeAs="String"> | |||||
<value>0</value> | |||||
</setting> | |||||
</Shadowsocks.Properties.Settings> | |||||
</userSettings> | |||||
<!-- Uncomment to dump network tracing info | |||||
<system.diagnostics> | |||||
<sources> | |||||
<source name="System.Net" tracemode="includehex" maxdatasize="1024"> | |||||
<listeners> | |||||
<add name="System.Net"/> | |||||
</listeners> | |||||
</source> | |||||
<source name="System.Net.Cache"> | |||||
<listeners> | |||||
<add name="System.Net"/> | |||||
</listeners> | |||||
</source> | |||||
<source name="System.Net.Http"> | |||||
<listeners> | |||||
<add name="System.Net"/> | |||||
</listeners> | |||||
</source> | |||||
<source name="System.Net.Sockets"> | |||||
<listeners> | |||||
<add name="System.Net"/> | |||||
</listeners> | |||||
</source> | |||||
<source name="System.Net.WebSockets"> | |||||
<listeners> | |||||
<add name="System.Net"/> | |||||
</listeners> | |||||
</source> | |||||
</sources> | |||||
<switches> | |||||
<add name="System.Net" value="Verbose"/> | |||||
<add name="System.Net.Cache" value="Verbose"/> | |||||
<add name="System.Net.Http" value="Verbose"/> | |||||
<add name="System.Net.Sockets" value="Verbose"/> | |||||
<add name="System.Net.WebSockets" value="Verbose"/> | |||||
</switches> | |||||
<sharedListeners> | |||||
<add name="System.Net" | |||||
type="System.Diagnostics.TextWriterTraceListener" | |||||
initializeData="network-tracing.log" | |||||
/> | |||||
</sharedListeners> | |||||
<trace autoflush="true"/> | |||||
</system.diagnostics> | |||||
--> | |||||
</configuration> |
@@ -1,76 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1"> | |||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> | |||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> | |||||
<security> | |||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> | |||||
<!-- UAC Manifest Options | |||||
If you want to change the Windows User Account Control level replace the | |||||
requestedExecutionLevel node with one of the following. | |||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" /> | |||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> | |||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" /> | |||||
Specifying requestedExecutionLevel element will disable file and registry virtualization. | |||||
Remove this element if your application requires this virtualization for backwards | |||||
compatibility. | |||||
--> | |||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" /> | |||||
</requestedPrivileges> | |||||
</security> | |||||
</trustInfo> | |||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | |||||
<application> | |||||
<!-- A list of the Windows versions that this application has been tested on | |||||
and is designed to work with. Uncomment the appropriate elements | |||||
and Windows will automatically select the most compatible environment. --> | |||||
<!-- Windows Vista --> | |||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />--> | |||||
<!-- Windows 7 --> | |||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /> | |||||
<!-- Windows 8 --> | |||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /> | |||||
<!-- Windows 8.1 --> | |||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /> | |||||
<!-- Windows 10 --> | |||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> | |||||
</application> | |||||
</compatibility> | |||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher | |||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need | |||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should | |||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. --> | |||||
<application xmlns="urn:schemas-microsoft-com:asm.v3"> | |||||
<windowsSettings> | |||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor, System</dpiAwareness> | |||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> | |||||
</windowsSettings> | |||||
</application> | |||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) --> | |||||
<!-- | |||||
<dependency> | |||||
<dependentAssembly> | |||||
<assemblyIdentity | |||||
type="win32" | |||||
name="Microsoft.Windows.Common-Controls" | |||||
version="6.0.0.0" | |||||
processorArchitecture="*" | |||||
publicKeyToken="6595b64144ccf1df" | |||||
language="*" | |||||
/> | |||||
</dependentAssembly> | |||||
</dependency> | |||||
--> | |||||
</assembly> |
@@ -1,19 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8"?> | |||||
<packages> | |||||
<package id="BouncyCastle" version="1.8.5" targetFramework="net472" /> | |||||
<package id="Caseless.Fody" version="1.8.3" targetFramework="net472" /> | |||||
<package id="CommandLineParser" version="2.8.0" targetFramework="net472" /> | |||||
<package id="Costura.Fody" version="3.3.3" targetFramework="net472" /> | |||||
<package id="Fody" version="4.2.1" targetFramework="net472" developmentDependency="true" /> | |||||
<package id="GlobalHotKey" version="1.1.0" targetFramework="net472" /> | |||||
<package id="Google.Protobuf" version="3.15.2" targetFramework="net472" /> | |||||
<package id="NaCl.Core" version="1.2.0" targetFramework="net472" /> | |||||
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net472" /> | |||||
<package id="NLog" version="4.6.8" targetFramework="net472" /> | |||||
<package id="StringEx.CS" version="0.3.1" targetFramework="net472" developmentDependency="true" /> | |||||
<package id="System.Buffers" version="4.4.0" targetFramework="net472" /> | |||||
<package id="System.Memory" version="4.5.2" targetFramework="net472" /> | |||||
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net472" /> | |||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net472" /> | |||||
<package id="ZXing.Net" version="0.16.5" targetFramework="net472" /> | |||||
</packages> |
@@ -1,75 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop"> | |||||
<PropertyGroup> | |||||
<OutputType>WinExe</OutputType> | |||||
<TargetFramework>netcoreapp3.1</TargetFramework> | |||||
<UseWPF>true</UseWPF> | |||||
<UseWindowsForms>true</UseWindowsForms> | |||||
<Authors>clowwindy & community 2020</Authors> | |||||
<PackageId>Shadowsocks.Legacy</PackageId> | |||||
<Product>Shadowsocks Legacy Project</Product> | |||||
<Version>4.3.0.0</Version> | |||||
<ApplicationIcon>shadowsocks.ico</ApplicationIcon> | |||||
<StartupObject>Shadowsocks.Program</StartupObject> | |||||
<ApplicationManifest>app.manifest</ApplicationManifest> | |||||
<RootNamespace>Shadowsocks.Legacy</RootNamespace> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<None Remove="app.config" /> | |||||
<None Remove="app.manifest" /> | |||||
<None Remove="Data\i18n.csv" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="BouncyCastle.NetCore" Version="1.8.8" /> | |||||
<!--TODO: Remove Fody related stuff, as they're either not actually used or has NET Core built-in alternate--> | |||||
<PackageReference Include="Caseless.Fody" Version="1.9.0"> | |||||
<PrivateAssets>all</PrivateAssets> | |||||
</PackageReference> | |||||
<PackageReference Include="CommandLineParser" Version="2.8.0" /> | |||||
<PackageReference Include="Costura.Fody" Version="4.1.0"> | |||||
<PrivateAssets>all</PrivateAssets> | |||||
</PackageReference> | |||||
<PackageReference Include="Fody" Version="6.3.0"> | |||||
<PrivateAssets>all</PrivateAssets> | |||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |||||
</PackageReference> | |||||
<PackageReference Include="GlobalHotKeyCore" Version="1.2.0" /> | |||||
<PackageReference Include="Google.Protobuf" Version="3.15.2" /> | |||||
<PackageReference Include="MdXaml" Version="1.9.0" /> | |||||
<PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" /> | |||||
<PackageReference Include="NaCl.Core" Version="1.2.0" /> | |||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> | |||||
<PackageReference Include="NLog" Version="4.7.5" /> | |||||
<PackageReference Include="ReactiveUI.Events.WPF" Version="13.1.1" /> | |||||
<PackageReference Include="ReactiveUI.Fody" Version="13.0.38" /> | |||||
<PackageReference Include="ReactiveUI.Validation" Version="2.1.1" /> | |||||
<PackageReference Include="ReactiveUI.WPF" Version="13.0.38" /> | |||||
<PackageReference Include="System.Data.SqlClient" Version="4.8.2" /> | |||||
<PackageReference Include="System.Management" Version="4.7.0" /> | |||||
<PackageReference Include="System.Windows.Forms.DataVisualization" Version="1.0.0-prerelease.20110.1" /> | |||||
<PackageReference Include="WPFLocalizeExtension" Version="3.8.0" /> | |||||
<PackageReference Include="ZXing.Net" Version="0.16.6" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Resource Include="Data\i18n.csv" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Compile Update="Properties\Settings.Designer.cs"> | |||||
<DesignTimeSharedInput>True</DesignTimeSharedInput> | |||||
<AutoGen>True</AutoGen> | |||||
<DependentUpon>Settings.settings</DependentUpon> | |||||
</Compile> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<None Update="Properties\Settings.settings"> | |||||
<Generator>SettingsSingleFileGenerator</Generator> | |||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput> | |||||
</None> | |||||
</ItemGroup> | |||||
</Project> |
@@ -3,13 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 | |||||
# Visual Studio Version 16 | # Visual Studio Version 16 | ||||
VisualStudioVersion = 16.0.29709.97 | VisualStudioVersion = 16.0.29709.97 | ||||
MinimumVisualStudioVersion = 10.0.40219.1 | MinimumVisualStudioVersion = 10.0.40219.1 | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}" | |||||
EndProject | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShadowsocksTest", "test\ShadowsocksTest.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}" | |||||
ProjectSection(ProjectDependencies) = postProject | |||||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} | |||||
EndProjectSection | |||||
EndProject | |||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7BD54B24-BBD7-4DD8-99EF-DAEE6684B673}" | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7BD54B24-BBD7-4DD8-99EF-DAEE6684B673}" | ||||
ProjectSection(SolutionItems) = preProject | ProjectSection(SolutionItems) = preProject | ||||
.editorconfig = .editorconfig | .editorconfig = .editorconfig | ||||
@@ -39,7 +32,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.WPF.Tests", "Sh | |||||
EndProject | EndProject | ||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.CLI", "Shadowsocks.CLI\Shadowsocks.CLI.csproj", "{78EB3006-81B0-4C13-9B80-E91766874A57}" | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.CLI", "Shadowsocks.CLI\Shadowsocks.CLI.csproj", "{78EB3006-81B0-4C13-9B80-E91766874A57}" | ||||
EndProject | EndProject | ||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shadowsocks.Protocol", "Shadowsocks.Protocol\Shadowsocks.Protocol.csproj", "{94DE5045-4D09-437B-BDE3-679FCAF07A2D}" | |||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shadowsocks.Protocol", "Shadowsocks.Protocol\Shadowsocks.Protocol.csproj", "{94DE5045-4D09-437B-BDE3-679FCAF07A2D}" | |||||
EndProject | EndProject | ||||
Global | Global | ||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
@@ -47,10 +40,6 @@ Global | |||||
Release|Any CPU = Release|Any CPU | Release|Any CPU = Release|Any CPU | ||||
EndGlobalSection | EndGlobalSection | ||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||||
{DFE11C77-62FA-4011-8398-38626C02E382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | {DFE11C77-62FA-4011-8398-38626C02E382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
{DFE11C77-62FA-4011-8398-38626C02E382}.Debug|Any CPU.Build.0 = Debug|Any CPU | {DFE11C77-62FA-4011-8398-38626C02E382}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
{DFE11C77-62FA-4011-8398-38626C02E382}.Release|Any CPU.ActiveCfg = Release|Any CPU | {DFE11C77-62FA-4011-8398-38626C02E382}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
@@ -1,14 +0,0 @@ | |||||
<?xml version="1.0" encoding="utf-8" ?> | |||||
<configuration> | |||||
<configSections> | |||||
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/> | |||||
</configSections> | |||||
<nlog> | |||||
<targets> | |||||
<target name="logconsole" type="Debugger" /> | |||||
</targets> | |||||
<rules> | |||||
<logger name="*" minlevel="Trace" writeTo="logconsole" /> | |||||
</rules> | |||||
</nlog> | |||||
</configuration> |
@@ -1,84 +0,0 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.IO; | |||||
using System.Text; | |||||
using Shadowsocks.Controller; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class CachedNetworkStreamTest | |||||
{ | |||||
byte[] b0 = new byte[256]; | |||||
byte[] b1 = new byte[256]; | |||||
byte[] b2 = new byte[1024]; | |||||
// [TestInitialize] | |||||
[TestInitialize] | |||||
public void init() | |||||
{ | |||||
for (int i = 0; i < 256; i++) | |||||
{ | |||||
b0[i] = (byte)i; | |||||
b1[i] = (byte)(255 - i); | |||||
} | |||||
b0.CopyTo(b2, 0); | |||||
b1.CopyTo(b2, 256); | |||||
b0.CopyTo(b2, 512); | |||||
} | |||||
[TestMethod] | |||||
public void StreamTest() | |||||
{ | |||||
using MemoryStream ms = new MemoryStream(b2); | |||||
using CachedNetworkStream s = new CachedNetworkStream(ms); | |||||
byte[] o = new byte[128]; | |||||
Assert.AreEqual(128, s.Read(o, 0, 128)); | |||||
TestUtils.ArrayEqual(b0[0..128], o); | |||||
Assert.AreEqual(64, s.Read(o, 0, 64)); | |||||
TestUtils.ArrayEqual(b0[128..192], o[0..64]); | |||||
s.Seek(0, SeekOrigin.Begin); | |||||
Assert.AreEqual(64, s.Read(o, 0, 64)); | |||||
TestUtils.ArrayEqual(b0[0..64], o[0..64]); | |||||
// refuse to go out of cached range | |||||
Assert.ThrowsException<NotSupportedException>(() => | |||||
{ | |||||
s.Seek(193, SeekOrigin.Begin); | |||||
}); | |||||
Assert.AreEqual(128, s.Read(o, 0, 128)); | |||||
TestUtils.ArrayEqual(b0[64..192], o); | |||||
Assert.IsTrue(s.CanSeek); | |||||
Assert.AreEqual(128, s.Read(o, 0, 128)); | |||||
TestUtils.ArrayEqual(b0[192..256], o[0..64]); | |||||
TestUtils.ArrayEqual(b1[0..64], o[64..128]); | |||||
Assert.IsFalse(s.CanSeek); | |||||
// refuse to go back when non-cache data has been read | |||||
Assert.ThrowsException<NotSupportedException>(() => | |||||
{ | |||||
s.Seek(0, SeekOrigin.Begin); | |||||
}); | |||||
// read in non-cache range | |||||
Assert.AreEqual(64, s.Read(o, 0, 64)); | |||||
s.Read(o, 0, 128); | |||||
Assert.AreEqual(512, s.Position); | |||||
Assert.AreEqual(128, s.Read(o, 0, 128)); | |||||
TestUtils.ArrayEqual(b0[0..128], o); | |||||
s.Read(o, 0, 128); | |||||
s.Read(o, 0, 128); | |||||
s.Read(o, 0, 128); | |||||
// read at eos | |||||
Assert.AreEqual(0, s.Read(o, 0, 128)); | |||||
} | |||||
} | |||||
} |
@@ -1,141 +0,0 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using Shadowsocks.Encryption; | |||||
using Shadowsocks.Encryption.Stream; | |||||
using Shadowsocks.Encryption.AEAD; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Threading; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class CryptographyTest | |||||
{ | |||||
readonly Random random = new Random(); | |||||
[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]; | |||||
random.NextBytes(bytes); | |||||
string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); | |||||
string md5str2 = Convert.ToBase64String(CryptoUtils.MD5(bytes)); | |||||
Assert.IsTrue(md5str == md5str2); | |||||
} | |||||
} | |||||
#region Encryptor test tools | |||||
private void SingleEncryptionTestCase(IEncryptor encryptor, IEncryptor decryptor, int length) | |||||
{ | |||||
RNG.Reload(); | |||||
byte[] plain = new byte[length]; | |||||
byte[] cipher = new byte[plain.Length + 100];// AEAD with IPv4 address type needs +100 | |||||
byte[] plain2 = new byte[plain.Length + 16]; | |||||
random.NextBytes(plain); | |||||
int outLen = encryptor.Encrypt(plain, cipher); | |||||
int outLen2 = decryptor.Decrypt(plain2, cipher.AsSpan(0, outLen)); | |||||
//encryptor.Encrypt(plain, length, cipher, out int outLen); | |||||
//decryptor.Decrypt(cipher, outLen, plain2, out int outLen2); | |||||
Assert.AreEqual(length, outLen2); | |||||
TestUtils.ArrayEqual<byte>(plain.AsSpan(0, length).ToArray(), plain2.AsSpan(0, length).ToArray()); | |||||
} | |||||
const string password = "barfoo!"; | |||||
private void RunSingleEncryptionThread(Type enc, Type dec, string method) | |||||
{ | |||||
var ector = enc.GetConstructor(new Type[] { typeof(string), typeof(string) }); | |||||
var dctor = dec.GetConstructor(new Type[] { typeof(string), typeof(string) }); | |||||
try | |||||
{ | |||||
IEncryptor encryptor = (IEncryptor)ector.Invoke(new object[] { method, password }); | |||||
IEncryptor decryptor = (IEncryptor)dctor.Invoke(new object[] { method, password }); | |||||
for (int i = 0; i < 16; i++) | |||||
{ | |||||
RunEncryptionRound(encryptor, decryptor); | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
encryptionFailed = true; | |||||
throw; | |||||
} | |||||
} | |||||
private static bool encryptionFailed = false; | |||||
private void TestEncryptionMethod(Type enc, string method) | |||||
{ | |||||
TestEncryptionMethod(enc, enc, method); | |||||
} | |||||
private void TestEncryptionMethod(Type enc, Type dec, string method) | |||||
{ | |||||
encryptionFailed = false; | |||||
// run it once before the multi-threading test to initialize global tables | |||||
RunSingleEncryptionThread(enc, dec, method); | |||||
List<Thread> threads = new List<Thread>(); | |||||
for (int i = 0; i < 8; i++) | |||||
{ | |||||
Thread t = new Thread(new ThreadStart(() => RunSingleEncryptionThread(enc, dec, method))); threads.Add(t); | |||||
t.Start(); | |||||
} | |||||
foreach (Thread t in threads) | |||||
{ | |||||
t.Join(); | |||||
} | |||||
Assert.IsFalse(encryptionFailed); | |||||
} | |||||
#endregion | |||||
// encryption test cases | |||||
private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) | |||||
{ | |||||
SingleEncryptionTestCase(encryptor, decryptor, 7); // for not aligned data | |||||
SingleEncryptionTestCase(encryptor, decryptor, 1000); | |||||
SingleEncryptionTestCase(encryptor, decryptor, 12333); | |||||
SingleEncryptionTestCase(encryptor, decryptor, 16384); | |||||
} | |||||
[TestMethod] | |||||
public void TestAEADAesGcmNativeEncryption() | |||||
{ | |||||
TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-128-gcm"); | |||||
TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-192-gcm"); | |||||
TestEncryptionMethod(typeof(AEADAesGcmNativeEncryptor), "aes-256-gcm"); | |||||
} | |||||
[TestMethod] | |||||
public void TestAEADNaClEncryption() | |||||
{ | |||||
TestEncryptionMethod(typeof(AEADNaClEncryptor), "chacha20-ietf-poly1305"); | |||||
TestEncryptionMethod(typeof(AEADNaClEncryptor), "xchacha20-ietf-poly1305"); | |||||
} | |||||
[TestMethod] | |||||
public void TestStreamNativeEncryption() | |||||
{ | |||||
TestEncryptionMethod(typeof(StreamPlainNativeEncryptor), "plain"); | |||||
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4"); | |||||
TestEncryptionMethod(typeof(StreamRc4NativeEncryptor), "rc4-md5"); | |||||
} | |||||
[TestMethod] | |||||
public void TestStreamAesCfbBouncyCastleEncryption() | |||||
{ | |||||
TestEncryptionMethod(typeof(StreamAesCfbBouncyCastleEncryptor), "aes-128-cfb"); | |||||
TestEncryptionMethod(typeof(StreamAesCfbBouncyCastleEncryptor), "aes-192-cfb"); | |||||
TestEncryptionMethod(typeof(StreamAesCfbBouncyCastleEncryptor), "aes-256-cfb"); | |||||
} | |||||
[TestMethod] | |||||
public void TestStreamChachaNaClEncryption() | |||||
{ | |||||
TestEncryptionMethod(typeof(StreamChachaNaClEncryptor), "chacha20-ietf"); | |||||
} | |||||
} | |||||
} |
@@ -1,713 +0,0 @@ | |||||
/* *************************************************************************** | |||||
The component allows to read the environment variables of another process | |||||
running in a Windows system. | |||||
History: | |||||
- v1.2.ss Add GetCommandLine for convenience. | |||||
- v1.2: Added support for inspection of 64 bit processes from 32 bit host | |||||
- v1.1: Fixed issue with environment block size detection | |||||
- v1.0: Initial | |||||
****************************************************************************** | |||||
The MIT License (MIT) | |||||
Copyright (c) 2011-2014 Oleksiy Gapotchenko | |||||
Copyright (c) 2018 Shadowsocks Project | |||||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
of this software and associated documentation files (the "Software"), to deal | |||||
in the Software without restriction, including without limitation the rights | |||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
copies of the Software, and to permit persons to whom the Software is | |||||
furnished to do so, subject to the following conditions: | |||||
The above copyright notice and this permission notice shall be included in | |||||
all copies or substantial portions of the Software. | |||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
THE SOFTWARE. | |||||
*************************************************************************** */ | |||||
using System; | |||||
using System.Collections.Specialized; | |||||
using System.Diagnostics; | |||||
using System.Linq; | |||||
using System.Management; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Runtime.InteropServices; | |||||
using System.Text; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
static class ProcessEnvironment | |||||
{ | |||||
public static StringDictionary ReadEnvironmentVariables(this Process process) | |||||
{ | |||||
return _GetEnvironmentVariablesCore(process.Handle); | |||||
} | |||||
public static StringDictionary TryReadEnvironmentVariables(this Process process) | |||||
{ | |||||
try | |||||
{ | |||||
return ReadEnvironmentVariables(process); | |||||
} | |||||
catch | |||||
{ | |||||
return null; | |||||
} | |||||
} | |||||
public static string GetCommandLine(this Process process) | |||||
{ | |||||
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id)) | |||||
using (ManagementObjectCollection objects = searcher.Get()) | |||||
{ | |||||
return objects.Cast<ManagementBaseObject>().SingleOrDefault()?["CommandLine"]?.ToString(); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Universal pointer. | |||||
/// </summary> | |||||
struct UniPtr | |||||
{ | |||||
public UniPtr(IntPtr p) | |||||
{ | |||||
Value = p.ToInt64(); | |||||
Size = IntPtr.Size; | |||||
} | |||||
public UniPtr(long p) | |||||
{ | |||||
Value = p; | |||||
Size = sizeof(long); | |||||
} | |||||
public long Value; | |||||
public int Size; | |||||
public static implicit operator IntPtr(UniPtr p) | |||||
{ | |||||
return new IntPtr(p.Value); | |||||
} | |||||
public static implicit operator UniPtr(IntPtr p) | |||||
{ | |||||
return new UniPtr(p); | |||||
} | |||||
public override string ToString() | |||||
{ | |||||
return Value.ToString(); | |||||
} | |||||
public bool FitsInNativePointer | |||||
{ | |||||
get | |||||
{ | |||||
return Size <= IntPtr.Size; | |||||
} | |||||
} | |||||
public bool CanBeRepresentedByNativePointer | |||||
{ | |||||
get | |||||
{ | |||||
int actualSize = Size; | |||||
if (actualSize == 8) | |||||
{ | |||||
if (Value >> 32 == 0) | |||||
actualSize = 4; | |||||
} | |||||
return actualSize <= IntPtr.Size; | |||||
} | |||||
} | |||||
public long ToInt64() | |||||
{ | |||||
return Value; | |||||
} | |||||
} | |||||
static StringDictionary _GetEnvironmentVariablesCore(IntPtr hProcess) | |||||
{ | |||||
var penv = _GetPenv(hProcess); | |||||
const int maxEnvSize = 32767; | |||||
byte[] envData; | |||||
if (penv.CanBeRepresentedByNativePointer) | |||||
{ | |||||
int dataSize; | |||||
if (!_HasReadAccess(hProcess, penv, out dataSize)) | |||||
throw new Exception("Unable to read environment block."); | |||||
if (dataSize > maxEnvSize) | |||||
dataSize = maxEnvSize; | |||||
envData = new byte[dataSize]; | |||||
var res_len = IntPtr.Zero; | |||||
bool b = WindowsApi.ReadProcessMemory( | |||||
hProcess, | |||||
penv, | |||||
envData, | |||||
new IntPtr(dataSize), | |||||
ref res_len); | |||||
if (!b || (int)res_len != dataSize) | |||||
throw new Exception("Unable to read environment block data."); | |||||
} | |||||
else if (penv.Size == 8 && IntPtr.Size == 4) | |||||
{ | |||||
// Accessing 64 bit process under 32 bit host. | |||||
int dataSize; | |||||
if (!_HasReadAccessWow64(hProcess, penv.ToInt64(), out dataSize)) | |||||
throw new Exception("Unable to read environment block with WOW64 API."); | |||||
if (dataSize > maxEnvSize) | |||||
dataSize = maxEnvSize; | |||||
envData = new byte[dataSize]; | |||||
long res_len = 0; | |||||
int result = WindowsApi.NtWow64ReadVirtualMemory64( | |||||
hProcess, | |||||
penv.ToInt64(), | |||||
envData, | |||||
dataSize, | |||||
ref res_len); | |||||
if (result != WindowsApi.STATUS_SUCCESS || res_len != dataSize) | |||||
throw new Exception("Unable to read environment block data with WOW64 API."); | |||||
} | |||||
else | |||||
{ | |||||
throw new Exception("Unable to access process memory due to unsupported bitness cardinality."); | |||||
} | |||||
return _EnvToDictionary(envData); | |||||
} | |||||
static StringDictionary _EnvToDictionary(byte[] env) | |||||
{ | |||||
var result = new StringDictionary(); | |||||
int len = env.Length; | |||||
if (len < 4) | |||||
return result; | |||||
int n = len - 3; | |||||
for (int i = 0; i < n; ++i) | |||||
{ | |||||
byte c1 = env[i]; | |||||
byte c2 = env[i + 1]; | |||||
byte c3 = env[i + 2]; | |||||
byte c4 = env[i + 3]; | |||||
if (c1 == 0 && c2 == 0 && c3 == 0 && c4 == 0) | |||||
{ | |||||
len = i + 3; | |||||
break; | |||||
} | |||||
} | |||||
char[] environmentCharArray = Encoding.Unicode.GetChars(env, 0, len); | |||||
for (int i = 0; i < environmentCharArray.Length; i++) | |||||
{ | |||||
int startIndex = i; | |||||
while ((environmentCharArray[i] != '=') && (environmentCharArray[i] != '\0')) | |||||
{ | |||||
i++; | |||||
} | |||||
if (environmentCharArray[i] != '\0') | |||||
{ | |||||
if ((i - startIndex) == 0) | |||||
{ | |||||
while (environmentCharArray[i] != '\0') | |||||
{ | |||||
i++; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
string str = new string(environmentCharArray, startIndex, i - startIndex); | |||||
i++; | |||||
int num3 = i; | |||||
while (environmentCharArray[i] != '\0') | |||||
{ | |||||
i++; | |||||
} | |||||
string str2 = new string(environmentCharArray, num3, i - num3); | |||||
result[str] = str2; | |||||
} | |||||
} | |||||
} | |||||
return result; | |||||
} | |||||
static bool _TryReadIntPtr32(IntPtr hProcess, IntPtr ptr, out IntPtr readPtr) | |||||
{ | |||||
bool result; | |||||
RuntimeHelpers.PrepareConstrainedRegions(); | |||||
try | |||||
{ | |||||
} | |||||
finally | |||||
{ | |||||
int dataSize = sizeof(Int32); | |||||
var data = Marshal.AllocHGlobal(dataSize); | |||||
IntPtr res_len = IntPtr.Zero; | |||||
bool b = WindowsApi.ReadProcessMemory( | |||||
hProcess, | |||||
ptr, | |||||
data, | |||||
new IntPtr(dataSize), | |||||
ref res_len); | |||||
readPtr = new IntPtr(Marshal.ReadInt32(data)); | |||||
Marshal.FreeHGlobal(data); | |||||
if (!b || (int)res_len != dataSize) | |||||
result = false; | |||||
else | |||||
result = true; | |||||
} | |||||
return result; | |||||
} | |||||
static bool _TryReadIntPtr(IntPtr hProcess, IntPtr ptr, out IntPtr readPtr) | |||||
{ | |||||
bool result; | |||||
RuntimeHelpers.PrepareConstrainedRegions(); | |||||
try | |||||
{ | |||||
} | |||||
finally | |||||
{ | |||||
int dataSize = IntPtr.Size; | |||||
var data = Marshal.AllocHGlobal(dataSize); | |||||
IntPtr res_len = IntPtr.Zero; | |||||
bool b = WindowsApi.ReadProcessMemory( | |||||
hProcess, | |||||
ptr, | |||||
data, | |||||
new IntPtr(dataSize), | |||||
ref res_len); | |||||
readPtr = Marshal.ReadIntPtr(data); | |||||
Marshal.FreeHGlobal(data); | |||||
if (!b || (int)res_len != dataSize) | |||||
result = false; | |||||
else | |||||
result = true; | |||||
} | |||||
return result; | |||||
} | |||||
static bool _TryReadIntPtrWow64(IntPtr hProcess, long ptr, out long readPtr) | |||||
{ | |||||
bool result; | |||||
RuntimeHelpers.PrepareConstrainedRegions(); | |||||
try | |||||
{ | |||||
} | |||||
finally | |||||
{ | |||||
int dataSize = sizeof(long); | |||||
var data = Marshal.AllocHGlobal(dataSize); | |||||
long res_len = 0; | |||||
int status = WindowsApi.NtWow64ReadVirtualMemory64( | |||||
hProcess, | |||||
ptr, | |||||
data, | |||||
dataSize, | |||||
ref res_len); | |||||
readPtr = Marshal.ReadInt64(data); | |||||
Marshal.FreeHGlobal(data); | |||||
if (status != WindowsApi.STATUS_SUCCESS || res_len != dataSize) | |||||
result = false; | |||||
else | |||||
result = true; | |||||
} | |||||
return result; | |||||
} | |||||
static UniPtr _GetPenv(IntPtr hProcess) | |||||
{ | |||||
int processBitness = _GetProcessBitness(hProcess); | |||||
if (processBitness == 64) | |||||
{ | |||||
if (Environment.Is64BitProcess) | |||||
{ | |||||
// Accessing 64 bit process under 64 bit host. | |||||
IntPtr pPeb = _GetPeb64(hProcess); | |||||
IntPtr ptr; | |||||
if (!_TryReadIntPtr(hProcess, pPeb + 0x20, out ptr)) | |||||
throw new Exception("Unable to read PEB."); | |||||
IntPtr penv; | |||||
if (!_TryReadIntPtr(hProcess, ptr + 0x80, out penv)) | |||||
throw new Exception("Unable to read RTL_USER_PROCESS_PARAMETERS."); | |||||
return penv; | |||||
} | |||||
else | |||||
{ | |||||
// Accessing 64 bit process under 32 bit host. | |||||
var pPeb = _GetPeb64(hProcess); | |||||
long ptr; | |||||
if (!_TryReadIntPtrWow64(hProcess, pPeb.ToInt64() + 0x20, out ptr)) | |||||
throw new Exception("Unable to read PEB."); | |||||
long penv; | |||||
if (!_TryReadIntPtrWow64(hProcess, ptr + 0x80, out penv)) | |||||
throw new Exception("Unable to read RTL_USER_PROCESS_PARAMETERS."); | |||||
return new UniPtr(penv); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
// Accessing 32 bit process under 32 bit host. | |||||
IntPtr pPeb = _GetPeb32(hProcess); | |||||
IntPtr ptr; | |||||
if (!_TryReadIntPtr32(hProcess, pPeb + 0x10, out ptr)) | |||||
throw new Exception("Unable to read PEB."); | |||||
IntPtr penv; | |||||
if (!_TryReadIntPtr32(hProcess, ptr + 0x48, out penv)) | |||||
throw new Exception("Unable to read RTL_USER_PROCESS_PARAMETERS."); | |||||
return penv; | |||||
} | |||||
} | |||||
static int _GetProcessBitness(IntPtr hProcess) | |||||
{ | |||||
if (Environment.Is64BitOperatingSystem) | |||||
{ | |||||
bool wow64; | |||||
if (!WindowsApi.IsWow64Process(hProcess, out wow64)) | |||||
return 32; | |||||
if (wow64) | |||||
return 32; | |||||
return 64; | |||||
} | |||||
else | |||||
{ | |||||
return 32; | |||||
} | |||||
} | |||||
static IntPtr _GetPeb32(IntPtr hProcess) | |||||
{ | |||||
if (Environment.Is64BitProcess) | |||||
{ | |||||
var ptr = IntPtr.Zero; | |||||
int res_len = 0; | |||||
int pbiSize = IntPtr.Size; | |||||
int status = WindowsApi.NtQueryInformationProcess( | |||||
hProcess, | |||||
WindowsApi.ProcessWow64Information, | |||||
ref ptr, | |||||
pbiSize, | |||||
ref res_len); | |||||
if (res_len != pbiSize) | |||||
throw new Exception("Unable to query process information."); | |||||
return ptr; | |||||
} | |||||
else | |||||
{ | |||||
return _GetPebNative(hProcess); | |||||
} | |||||
} | |||||
static IntPtr _GetPebNative(IntPtr hProcess) | |||||
{ | |||||
var pbi = new WindowsApi.PROCESS_BASIC_INFORMATION(); | |||||
int res_len = 0; | |||||
int pbiSize = Marshal.SizeOf(pbi); | |||||
int status = WindowsApi.NtQueryInformationProcess( | |||||
hProcess, | |||||
WindowsApi.ProcessBasicInformation, | |||||
ref pbi, | |||||
pbiSize, | |||||
ref res_len); | |||||
if (res_len != pbiSize) | |||||
throw new Exception("Unable to query process information."); | |||||
return pbi.PebBaseAddress; | |||||
} | |||||
static UniPtr _GetPeb64(IntPtr hProcess) | |||||
{ | |||||
if (Environment.Is64BitProcess) | |||||
{ | |||||
return _GetPebNative(hProcess); | |||||
} | |||||
else | |||||
{ | |||||
// Get PEB via WOW64 API. | |||||
var pbi = new WindowsApi.PROCESS_BASIC_INFORMATION_WOW64(); | |||||
int res_len = 0; | |||||
int pbiSize = Marshal.SizeOf(pbi); | |||||
int status = WindowsApi.NtWow64QueryInformationProcess64( | |||||
hProcess, | |||||
WindowsApi.ProcessBasicInformation, | |||||
ref pbi, | |||||
pbiSize, | |||||
ref res_len); | |||||
if (res_len != pbiSize) | |||||
throw new Exception("Unable to query process information."); | |||||
return new UniPtr(pbi.PebBaseAddress); | |||||
} | |||||
} | |||||
static bool _HasReadAccess(IntPtr hProcess, IntPtr address, out int size) | |||||
{ | |||||
size = 0; | |||||
var memInfo = new WindowsApi.MEMORY_BASIC_INFORMATION(); | |||||
int result = WindowsApi.VirtualQueryEx( | |||||
hProcess, | |||||
address, | |||||
ref memInfo, | |||||
Marshal.SizeOf(memInfo)); | |||||
if (result == 0) | |||||
return false; | |||||
if (memInfo.Protect == WindowsApi.PAGE_NOACCESS || memInfo.Protect == WindowsApi.PAGE_EXECUTE) | |||||
return false; | |||||
try | |||||
{ | |||||
size = Convert.ToInt32(memInfo.RegionSize.ToInt64() - (address.ToInt64() - memInfo.BaseAddress.ToInt64())); | |||||
} | |||||
catch (OverflowException) | |||||
{ | |||||
return false; | |||||
} | |||||
if (size <= 0) | |||||
return false; | |||||
return true; | |||||
} | |||||
static bool _HasReadAccessWow64(IntPtr hProcess, long address, out int size) | |||||
{ | |||||
size = 0; | |||||
WindowsApi.MEMORY_BASIC_INFORMATION_WOW64 memInfo; | |||||
var memInfoType = typeof(WindowsApi.MEMORY_BASIC_INFORMATION_WOW64); | |||||
int memInfoLength = Marshal.SizeOf(memInfoType); | |||||
const int memInfoAlign = 8; | |||||
long resultLength = 0; | |||||
int result; | |||||
IntPtr hMemInfo = Marshal.AllocHGlobal(memInfoLength + memInfoAlign * 2); | |||||
try | |||||
{ | |||||
// Align to 64 bits. | |||||
IntPtr hMemInfoAligned = new IntPtr(hMemInfo.ToInt64() & ~(memInfoAlign - 1L)); | |||||
result = WindowsApi.NtWow64QueryVirtualMemory64( | |||||
hProcess, | |||||
address, | |||||
WindowsApi.MEMORY_INFORMATION_CLASS.MemoryBasicInformation, | |||||
hMemInfoAligned, | |||||
memInfoLength, | |||||
ref resultLength); | |||||
memInfo = (WindowsApi.MEMORY_BASIC_INFORMATION_WOW64)Marshal.PtrToStructure(hMemInfoAligned, memInfoType); | |||||
} | |||||
finally | |||||
{ | |||||
Marshal.FreeHGlobal(hMemInfo); | |||||
} | |||||
if (result != WindowsApi.STATUS_SUCCESS) | |||||
return false; | |||||
if (memInfo.Protect == WindowsApi.PAGE_NOACCESS || memInfo.Protect == WindowsApi.PAGE_EXECUTE) | |||||
return false; | |||||
try | |||||
{ | |||||
size = Convert.ToInt32(memInfo.RegionSize - (address - memInfo.BaseAddress)); | |||||
} | |||||
catch (OverflowException) | |||||
{ | |||||
return false; | |||||
} | |||||
if (size <= 0) | |||||
return false; | |||||
return true; | |||||
} | |||||
static class WindowsApi | |||||
{ | |||||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |||||
public struct PROCESS_BASIC_INFORMATION | |||||
{ | |||||
public IntPtr Reserved1; | |||||
public IntPtr PebBaseAddress; | |||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] | |||||
public IntPtr[] Reserved2; | |||||
public IntPtr UniqueProcessId; | |||||
public IntPtr Reserved3; | |||||
} | |||||
public const int ProcessBasicInformation = 0; | |||||
public const int ProcessWow64Information = 26; | |||||
[DllImport("ntdll.dll", SetLastError = true)] | |||||
public static extern int NtQueryInformationProcess( | |||||
IntPtr hProcess, | |||||
int pic, | |||||
ref PROCESS_BASIC_INFORMATION pbi, | |||||
int cb, | |||||
ref int pSize); | |||||
[DllImport("ntdll.dll", SetLastError = true)] | |||||
public static extern int NtQueryInformationProcess( | |||||
IntPtr hProcess, | |||||
int pic, | |||||
ref IntPtr pi, | |||||
int cb, | |||||
ref int pSize); | |||||
[DllImport("ntdll.dll", SetLastError = true)] | |||||
public static extern int NtQueryInformationProcess( | |||||
IntPtr hProcess, | |||||
int pic, | |||||
ref long pi, | |||||
int cb, | |||||
ref int pSize); | |||||
[StructLayout(LayoutKind.Sequential, Pack = 1)] | |||||
public struct PROCESS_BASIC_INFORMATION_WOW64 | |||||
{ | |||||
public long Reserved1; | |||||
public long PebBaseAddress; | |||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] | |||||
public long[] Reserved2; | |||||
public long UniqueProcessId; | |||||
public long Reserved3; | |||||
} | |||||
[DllImport("ntdll.dll", SetLastError = true)] | |||||
public static extern int NtWow64QueryInformationProcess64( | |||||
IntPtr hProcess, | |||||
int pic, | |||||
ref PROCESS_BASIC_INFORMATION_WOW64 pbi, | |||||
int cb, | |||||
ref int pSize); | |||||
[DllImport("kernel32.dll", SetLastError = true)] | |||||
public static extern bool ReadProcessMemory( | |||||
IntPtr hProcess, | |||||
IntPtr lpBaseAddress, | |||||
[Out] byte[] lpBuffer, | |||||
IntPtr dwSize, | |||||
ref IntPtr lpNumberOfBytesRead); | |||||
[DllImport("kernel32.dll", SetLastError = true)] | |||||
public static extern bool ReadProcessMemory( | |||||
IntPtr hProcess, | |||||
IntPtr lpBaseAddress, | |||||
IntPtr lpBuffer, | |||||
IntPtr dwSize, | |||||
ref IntPtr lpNumberOfBytesRead); | |||||
[DllImport("ntdll.dll", SetLastError = true)] | |||||
public static extern int NtWow64ReadVirtualMemory64( | |||||
IntPtr hProcess, | |||||
long lpBaseAddress, | |||||
IntPtr lpBuffer, | |||||
long dwSize, | |||||
ref long lpNumberOfBytesRead); | |||||
[DllImport("ntdll.dll", SetLastError = true)] | |||||
public static extern int NtWow64ReadVirtualMemory64( | |||||
IntPtr hProcess, | |||||
long lpBaseAddress, | |||||
[Out] byte[] lpBuffer, | |||||
long dwSize, | |||||
ref long lpNumberOfBytesRead); | |||||
public const int STATUS_SUCCESS = 0; | |||||
public const int PAGE_NOACCESS = 0x01; | |||||
public const int PAGE_EXECUTE = 0x10; | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
public struct MEMORY_BASIC_INFORMATION | |||||
{ | |||||
public IntPtr BaseAddress; | |||||
public IntPtr AllocationBase; | |||||
public int AllocationProtect; | |||||
public IntPtr RegionSize; | |||||
public int State; | |||||
public int Protect; | |||||
public int Type; | |||||
} | |||||
[DllImport("kernel32.dll")] | |||||
public static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength); | |||||
[StructLayout(LayoutKind.Sequential)] | |||||
public struct MEMORY_BASIC_INFORMATION_WOW64 | |||||
{ | |||||
public long BaseAddress; | |||||
public long AllocationBase; | |||||
public int AllocationProtect; | |||||
public long RegionSize; | |||||
public int State; | |||||
public int Protect; | |||||
public int Type; | |||||
} | |||||
public enum MEMORY_INFORMATION_CLASS | |||||
{ | |||||
MemoryBasicInformation | |||||
} | |||||
[DllImport("ntdll.dll")] | |||||
public static extern int NtWow64QueryVirtualMemory64( | |||||
IntPtr hProcess, | |||||
long lpAddress, | |||||
MEMORY_INFORMATION_CLASS memoryInformationClass, | |||||
IntPtr lpBuffer, // MEMORY_BASIC_INFORMATION_WOW64, pointer must be 64-bit aligned | |||||
long memoryInformationLength, | |||||
ref long returnLength); | |||||
[DllImport("kernel32.dll")] | |||||
public static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process); | |||||
} | |||||
} | |||||
} |
@@ -1,34 +0,0 @@ | |||||
<Project Sdk="Microsoft.NET.Sdk"> | |||||
<PropertyGroup> | |||||
<TargetFramework>netcoreapp3.1</TargetFramework> | |||||
<IsPackable>false</IsPackable> | |||||
<RootNamespace>Shadowsocks.Legacy.Test</RootNamespace> | |||||
</PropertyGroup> | |||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |||||
<WarningLevel>0</WarningLevel> | |||||
</PropertyGroup> | |||||
<ItemGroup> | |||||
<PackageReference Include="GlobalHotKeyCore" Version="1.2.0" /> | |||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> | |||||
<PackageReference Include="MSTest.TestAdapter" Version="2.1.0" /> | |||||
<PackageReference Include="MSTest.TestFramework" Version="2.1.0" /> | |||||
<PackageReference Include="coverlet.collector" Version="3.0.3"> | |||||
<PrivateAssets>all</PrivateAssets> | |||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |||||
</PackageReference> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<ProjectReference Include="..\shadowsocks-csharp\shadowsocks-csharp.csproj" /> | |||||
</ItemGroup> | |||||
<ItemGroup> | |||||
<Folder Include="Properties\" /> | |||||
</ItemGroup> | |||||
</Project> |
@@ -1,221 +0,0 @@ | |||||
using System; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using System.Threading; | |||||
using System.Collections.Generic; | |||||
using Shadowsocks.Model; | |||||
using Shadowsocks.Controller.Service; | |||||
using System.Diagnostics; | |||||
using System.Net; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class Sip003PluginTest | |||||
{ | |||||
string fake_plugin = "ftp"; | |||||
[TestMethod] | |||||
public void TestSip003Plugin_NoPlugin() | |||||
{ | |||||
var NoPlugin = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
NoPlugin, | |||||
"", | |||||
"", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
[TestMethod] | |||||
public void TestSip003Plugin_Plugin() | |||||
{ | |||||
var Plugin = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
Plugin, | |||||
fake_plugin, | |||||
"", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
[TestMethod] | |||||
public void TestSip003Plugin_PluginWithOpts() | |||||
{ | |||||
var PluginWithOpts = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option" | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
PluginWithOpts, | |||||
fake_plugin, | |||||
"_option", | |||||
"", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
[TestMethod] | |||||
public void TestSip003Plugin_PluginWithArgs() | |||||
{ | |||||
var PluginWithArgs = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_args = "_test" | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
PluginWithArgs, | |||||
fake_plugin, | |||||
"", | |||||
"_test", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
[TestMethod] | |||||
public void TestSip003Plugin_PluginWithOptsAndArgs() | |||||
{ | |||||
var PluginWithOptsAndArgs = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option", | |||||
plugin_args = "_test" | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
PluginWithOptsAndArgs, | |||||
fake_plugin, | |||||
"_option", | |||||
"_test", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
[TestMethod] | |||||
public void TestSip003Plugin_PluginWithArgsReplaced() | |||||
{ | |||||
var PluginWithArgsReplaced = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_args = "_test,%SS_REMOTE_HOST%" | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
PluginWithArgsReplaced, | |||||
fake_plugin, | |||||
"", | |||||
"_test,192.168.100.1", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
[TestMethod] | |||||
public void TestSip003Plugin_PluginWithOptsAndArgsReplaced() | |||||
{ | |||||
var PluginWithOptsAndArgsReplaced = Sip003Plugin.CreateIfConfigured( | |||||
new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb", | |||||
plugin = fake_plugin, | |||||
plugin_opts = "_option", | |||||
plugin_args = "_test,%SS_REMOTE_HOST%,%SS_PLUGIN_OPTIONS%" | |||||
}, | |||||
false); | |||||
RunPluginSupportTest( | |||||
PluginWithOptsAndArgsReplaced, | |||||
fake_plugin, | |||||
"_option", | |||||
"_test,192.168.100.1,_option", | |||||
"192.168.100.1", | |||||
8888); | |||||
} | |||||
private static void RunPluginSupportTest(Sip003Plugin plugin, string pluginName, string pluginOpts, string pluginArgs, string serverAddress, int serverPort) | |||||
{ | |||||
if (string.IsNullOrWhiteSpace(pluginName)) return; | |||||
plugin.StartIfNeeded(); | |||||
Process[] processes = Process.GetProcessesByName(pluginName); | |||||
Assert.AreEqual(processes.Length, 1); | |||||
Process p = processes[0]; | |||||
var penv = ProcessEnvironment.ReadEnvironmentVariables(p); | |||||
var pcmd = ProcessEnvironment.GetCommandLine(p).Trim(); | |||||
pcmd = pcmd.IndexOf(' ') >= 0 ? pcmd.Substring(pcmd.IndexOf(' ') + 1) : ""; | |||||
Assert.AreEqual(penv["SS_REMOTE_HOST"], serverAddress); | |||||
Assert.AreEqual(penv["SS_REMOTE_PORT"], serverPort.ToString()); | |||||
Assert.AreEqual(penv["SS_LOCAL_HOST"], IPAddress.Loopback.ToString()); | |||||
int _ignored; | |||||
Assert.IsTrue(int.TryParse(penv["SS_LOCAL_PORT"], out _ignored)); | |||||
Assert.AreEqual(penv["SS_PLUGIN_OPTIONS"], pluginOpts); | |||||
Assert.AreEqual(pcmd, pluginArgs); | |||||
plugin.Dispose(); | |||||
for (int i = 0; i < 50; i++) | |||||
{ | |||||
if (Process.GetProcessesByName(pluginName).Length == 0) return; | |||||
Thread.Sleep(50); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,41 +0,0 @@ | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
class TestUtils | |||||
{ | |||||
public static void ArrayEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual, string msg = "") | |||||
{ | |||||
var e1 = expected.GetEnumerator(); | |||||
var e2 = actual.GetEnumerator(); | |||||
int ctr = 0; | |||||
while (true) | |||||
{ | |||||
var e1next = e1.MoveNext(); | |||||
var e2next = e2.MoveNext(); | |||||
if (e1next && e2next) | |||||
{ | |||||
Assert.AreEqual(e1.Current, e2.Current, "at " + ctr); | |||||
} | |||||
else if (!e1next && !e2next) | |||||
{ | |||||
return; | |||||
} | |||||
else if (!e1next) | |||||
{ | |||||
Assert.Fail($"actual longer than expected ({ctr}) {msg}"); | |||||
} | |||||
else | |||||
{ | |||||
Assert.Fail($"actual shorter than expected ({ctr}) {msg}"); | |||||
} | |||||
ctr++; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -1,45 +0,0 @@ | |||||
using System; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using Shadowsocks.Controller; | |||||
using GlobalHotKey; | |||||
using System.Windows.Input; | |||||
using System.Threading; | |||||
using System.Collections.Generic; | |||||
using Shadowsocks.Controller.Hotkeys; | |||||
using System.Diagnostics; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class UnitTest | |||||
{ | |||||
[TestMethod] | |||||
public void TestHotKey2Str() | |||||
{ | |||||
Assert.AreEqual("Ctrl+A", HotKeys.HotKey2Str(Key.A, ModifierKeys.Control)); | |||||
Assert.AreEqual("Ctrl+Alt+D2", HotKeys.HotKey2Str(Key.D2, (ModifierKeys.Alt | ModifierKeys.Control))); | |||||
Assert.AreEqual("Ctrl+Alt+Shift+NumPad7", HotKeys.HotKey2Str(Key.NumPad7, (ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift))); | |||||
Assert.AreEqual("Ctrl+Alt+Shift+F6", HotKeys.HotKey2Str(Key.F6, (ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift))); | |||||
Assert.AreNotEqual("Ctrl+Shift+Alt+F6", HotKeys.HotKey2Str(Key.F6, (ModifierKeys.Alt | ModifierKeys.Control | ModifierKeys.Shift))); | |||||
} | |||||
[TestMethod] | |||||
public void TestStr2HotKey() | |||||
{ | |||||
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+A").Equals(new HotKey(Key.A, ModifierKeys.Control))); | |||||
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt)))); | |||||
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Shift)))); | |||||
Assert.IsTrue(HotKeys.Str2HotKey("Ctrl+Alt+Shift+A").Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | |||||
HotKey testKey0 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+A"); | |||||
Assert.IsTrue(testKey0 != null && testKey0.Equals(new HotKey(Key.A, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | |||||
HotKey testKey1 = HotKeys.Str2HotKey("Ctrl+Alt+Shift+F2"); | |||||
Assert.IsTrue(testKey1 != null && testKey1.Equals(new HotKey(Key.F2, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | |||||
HotKey testKey2 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+D7"); | |||||
Assert.IsTrue(testKey2 != null && testKey2.Equals(new HotKey(Key.D7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | |||||
HotKey testKey3 = HotKeys.Str2HotKey("Ctrl+Shift+Alt+NumPad7"); | |||||
Assert.IsTrue(testKey3 != null && testKey3.Equals(new HotKey(Key.NumPad7, (ModifierKeys.Control | ModifierKeys.Alt | ModifierKeys.Shift)))); | |||||
} | |||||
} | |||||
} |
@@ -1,252 +0,0 @@ | |||||
using System; | |||||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||||
using Shadowsocks.Controller; | |||||
using System.Threading; | |||||
using System.Collections.Generic; | |||||
using Shadowsocks.Model; | |||||
using System.Diagnostics; | |||||
namespace Shadowsocks.Test | |||||
{ | |||||
[TestClass] | |||||
public class UrlTest | |||||
{ | |||||
Server server1, server1WithRemark, server1WithPlugin, server1WithPluginAndRemark; | |||||
string server1CanonUrl, server1WithRemarkCanonUrl, server1WithPluginCanonUrl, server1WithPluginAndRemarkCanonUrl; | |||||
Server server2, server2WithRemark, server2WithPlugin, server2WithPluginAndRemark; | |||||
string server2CanonUrl, server2WithRemarkCanonUrl, server2WithPluginCanonUrl, server2WithPluginAndRemarkCanonUrl; | |||||
[TestInitialize] | |||||
public void PrepareTestData() | |||||
{ | |||||
server1 = new Server | |||||
{ | |||||
server = "192.168.100.1", | |||||
server_port = 8888, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}; | |||||
server1CanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4"; | |||||
// server2 has base64 padding | |||||
server2 = new Server | |||||
{ | |||||
server = "192.168.1.1", | |||||
server_port = 8388, | |||||
password = "test", | |||||
method = "bf-cfb" | |||||
}; | |||||
server2CanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA=="; | |||||
server1WithRemark = new Server | |||||
{ | |||||
server = server1.server, | |||||
server_port = server1.server_port, | |||||
password = server1.password, | |||||
method = server1.method, | |||||
remarks = "example-server 1" | |||||
}; | |||||
server1WithRemarkCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server+1"; | |||||
server2WithRemark = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
remarks = "example-server 2" | |||||
}; | |||||
server2WithRemarkCanonUrl = "ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==#example-server+2"; | |||||
server1WithPlugin = new Server | |||||
{ | |||||
server = server1.server, | |||||
server_port = server1.server_port, | |||||
password = server1.password, | |||||
method = server1.method, | |||||
plugin = "obfs-local", | |||||
plugin_opts = "obfs=http;obfs-host=google.com" | |||||
}; | |||||
server1WithPluginCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"; | |||||
server2WithPlugin = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
plugin = "obfs-local", | |||||
plugin_opts = "obfs=http;obfs-host=google.com" | |||||
}; | |||||
server2WithPluginCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"; | |||||
server1WithPluginAndRemark = new Server | |||||
{ | |||||
server = server1.server, | |||||
server_port = server1.server_port, | |||||
password = server1.password, | |||||
method = server1.method, | |||||
plugin = server1WithPlugin.plugin, | |||||
plugin_opts = server1WithPlugin.plugin_opts, | |||||
remarks = server1WithRemark.remarks | |||||
}; | |||||
server1WithPluginAndRemarkCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com#example-server+1"; | |||||
server2WithPluginAndRemark = new Server | |||||
{ | |||||
server = server2.server, | |||||
server_port = server2.server_port, | |||||
password = server2.password, | |||||
method = server2.method, | |||||
plugin = server2WithPlugin.plugin, | |||||
plugin_opts = server2WithPlugin.plugin_opts, | |||||
remarks = server2WithRemark.remarks | |||||
}; | |||||
server2WithPluginAndRemarkCanonUrl = | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com#example-server+2"; | |||||
} | |||||
[TestMethod] | |||||
public void TestParseUrl_Server1() | |||||
{ | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
server1CanonUrl, | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4/", | |||||
server1WithRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4/#example-server+1"), | |||||
new[] | |||||
{ | |||||
server1, | |||||
server1, | |||||
server1WithRemark, | |||||
server1WithRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888", | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server+1", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/#example-server+1", | |||||
server1WithPluginCanonUrl, | |||||
server1WithPluginAndRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com&unsupported=1#example-server+1"), | |||||
new[] | |||||
{ | |||||
server1, | |||||
server1, | |||||
server1WithRemark, | |||||
server1WithRemark, | |||||
server1WithPlugin, | |||||
server1WithPluginAndRemark, | |||||
server1WithPluginAndRemark | |||||
}); | |||||
} | |||||
[TestMethod] | |||||
public void TestParseUrl_Server2() | |||||
{ | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
server2CanonUrl, | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==/", | |||||
server2WithRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xLjE6ODM4OA==/#example-server+2"), | |||||
new[] | |||||
{ | |||||
server2, | |||||
server2, | |||||
server2WithRemark, | |||||
server2WithRemark | |||||
}); | |||||
RunParseShadowsocksUrlTest( | |||||
string.Join( | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388", | |||||
"\r\n", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388#example-server+2", | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/#example-server+2", | |||||
server2WithPluginCanonUrl, | |||||
server2WithPluginAndRemarkCanonUrl, | |||||
"ss://YmYtY2ZiOnRlc3Q@192.168.1.1:8388/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com&unsupported=1#example-server+2"), | |||||
new[] | |||||
{ | |||||
server2, | |||||
server2, | |||||
server2WithRemark, | |||||
server2WithRemark, | |||||
server2WithPlugin, | |||||
server2WithPluginAndRemark, | |||||
server2WithPluginAndRemark | |||||
}); | |||||
} | |||||
[TestMethod] | |||||
public void TestUrlGenerate() | |||||
{ | |||||
var generateUrlCases = new Dictionary<string, Server> | |||||
{ | |||||
[server1CanonUrl] = server1, | |||||
[server1WithRemarkCanonUrl] = server1WithRemark, | |||||
[server1WithPluginCanonUrl] = server1WithPlugin, | |||||
[server1WithPluginAndRemarkCanonUrl] = server1WithPluginAndRemark | |||||
}; | |||||
RunGenerateShadowsocksUrlTest(generateUrlCases); | |||||
} | |||||
private static void RunParseShadowsocksUrlTest(string testCase, IReadOnlyList<Server> expected) | |||||
{ | |||||
var actual = Server.GetServers(testCase); | |||||
if (actual.Count != expected.Count) | |||||
{ | |||||
Assert.Fail("Wrong number of configs. Expected: {0}. Actual: {1}", expected.Count, actual.Count); | |||||
} | |||||
for (int i = 0; i < expected.Count; i++) | |||||
{ | |||||
var expectedServer = expected[i]; | |||||
var actualServer = actual[i]; | |||||
Assert.AreEqual(expectedServer.server, actualServer.server); | |||||
Assert.AreEqual(expectedServer.server_port, actualServer.server_port); | |||||
Assert.AreEqual(expectedServer.password, actualServer.password); | |||||
Assert.AreEqual(expectedServer.method, actualServer.method); | |||||
Assert.AreEqual(expectedServer.plugin, actualServer.plugin); | |||||
Assert.AreEqual(expectedServer.plugin_opts, actualServer.plugin_opts); | |||||
Assert.AreEqual(expectedServer.remarks, actualServer.remarks); | |||||
Assert.AreEqual(expectedServer.timeout, actualServer.timeout); | |||||
} | |||||
} | |||||
private static void RunGenerateShadowsocksUrlTest(IReadOnlyDictionary<string, Server> testCases) | |||||
{ | |||||
foreach (var testCase in testCases) | |||||
{ | |||||
string expected = testCase.Key; | |||||
Server config = testCase.Value; | |||||
var actual = config.GetURL(true); | |||||
Assert.AreEqual(expected, actual); | |||||
} | |||||
} | |||||
} | |||||
} |