- Validate geosite group configuration - Reset to default if specified group doesn't exist - Regenerate pac.txt on version update (can be turned off) - Cleanup of `Configuration` to separate loading logic and processing logictags/4.3.0.0
@@ -12,7 +12,7 @@ namespace Shadowsocks.Controller | |||||
private static Logger logger = LogManager.GetCurrentClassLogger(); | private static Logger logger = LogManager.GetCurrentClassLogger(); | ||||
public static void RegAllHotkeys() | public static void RegAllHotkeys() | ||||
{ | { | ||||
var hotkeyConfig = Configuration.Load().hotkey; | |||||
var hotkeyConfig = Program.MainController.GetCurrentConfiguration().hotkey; | |||||
if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) | if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) | ||||
return; | return; | ||||
@@ -192,6 +192,13 @@ namespace Shadowsocks.Controller | |||||
return true; | return true; | ||||
} | } | ||||
/// <summary> | |||||
/// Checks if the specified group exists in GeoSite database. | |||||
/// </summary> | |||||
/// <param name="group">The group name to check for.</param> | |||||
/// <returns>True if the group exists. False if the group doesn't exist.</returns> | |||||
public static bool CheckGeositeGroup(string group) => Geosites.ContainsKey(group); | |||||
private static string MergePACFile(IList<DomainObject> domains, bool blacklist) | private static string MergePACFile(IList<DomainObject> domains, bool blacklist) | ||||
{ | { | ||||
string abpContent; | string abpContent; | ||||
@@ -45,7 +45,7 @@ namespace Shadowsocks.Controller | |||||
_config = config; | _config = config; | ||||
string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | ||||
string contentHash = GetHash(_pacDaemon.GetPACContent()); | string contentHash = GetHash(_pacDaemon.GetPACContent()); | ||||
PacUrl = $"http://{config.localHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; | |||||
PacUrl = $"http://{config.LocalHost}:{config.localPort}/{RESOURCE_NAME}?hash={contentHash}{usedSecret}"; | |||||
logger.Debug("Set PAC URL:" + PacUrl); | logger.Debug("Set PAC URL:" + PacUrl); | ||||
} | } | ||||
@@ -176,7 +176,6 @@ Connection: Close | |||||
"; | "; | ||||
byte[] response = Encoding.UTF8.GetBytes(responseHead + pacContent); | byte[] response = Encoding.UTF8.GetBytes(responseHead + pacContent); | ||||
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); | socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); | ||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -172,7 +172,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
WebClient http = new WebClient(); | WebClient http = new WebClient(); | ||||
http.Headers.Add("User-Agent", UserAgent); | http.Headers.Add("User-Agent", UserAgent); | ||||
http.Proxy = new WebProxy(config.localHost, config.localPort); | |||||
http.Proxy = new WebProxy(config.LocalHost, config.localPort); | |||||
return http; | return http; | ||||
} | } | ||||
@@ -27,7 +27,6 @@ namespace Shadowsocks.Controller | |||||
// manipulates UI | // manipulates UI | ||||
// interacts with low level logic | // interacts with low level logic | ||||
#region Members definition | #region Members definition | ||||
private Thread _ramThread; | |||||
private Thread _trafficThread; | private Thread _trafficThread; | ||||
private Listener _listener; | private Listener _listener; | ||||
@@ -93,10 +92,10 @@ namespace Shadowsocks.Controller | |||||
public ShadowsocksController() | public ShadowsocksController() | ||||
{ | { | ||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
Configuration.Process(ref _config); | |||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | ||||
_strategyManager = new StrategyManager(this); | _strategyManager = new StrategyManager(this); | ||||
_pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | _pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | ||||
StartReleasingMemory(); | |||||
StartTrafficStatistics(61); | StartTrafficStatistics(61); | ||||
ProgramUpdated += (o, e) => | ProgramUpdated += (o, e) => | ||||
@@ -107,23 +106,34 @@ namespace Shadowsocks.Controller | |||||
#region Basic | #region Basic | ||||
public void Start(bool regHotkeys = true) | |||||
public void Start(bool systemWakeUp = false) | |||||
{ | { | ||||
if (_config.updated && regHotkeys) | |||||
if (_config.firstRunOnNewVersion && !systemWakeUp) | |||||
{ | { | ||||
_config.updated = false; | |||||
ProgramUpdated.Invoke(this, new UpdatedEventArgs() | ProgramUpdated.Invoke(this, new UpdatedEventArgs() | ||||
{ | { | ||||
OldVersion = _config.version, | OldVersion = _config.version, | ||||
NewVersion = UpdateChecker.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); | Configuration.Save(_config); | ||||
} | } | ||||
Reload(); | Reload(); | ||||
if (regHotkeys) | |||||
{ | |||||
if (!systemWakeUp) | |||||
HotkeyReg.RegAllHotkeys(); | HotkeyReg.RegAllHotkeys(); | ||||
} | |||||
} | } | ||||
public void Stop() | public void Stop() | ||||
@@ -154,6 +164,7 @@ namespace Shadowsocks.Controller | |||||
Encryption.RNG.Reload(); | Encryption.RNG.Reload(); | ||||
// some logic in configuration updated the config when saving, we need to read it again | // some logic in configuration updated the config when saving, we need to read it again | ||||
_config = Configuration.Load(); | _config = Configuration.Load(); | ||||
Configuration.Process(ref _config); | |||||
NLogConfig.LoadConfiguration(); | NLogConfig.LoadConfiguration(); | ||||
@@ -226,7 +237,6 @@ namespace Shadowsocks.Controller | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | ConfigChanged?.Invoke(this, new EventArgs()); | ||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
protected void SaveConfig(Configuration newConfig) | protected void SaveConfig(Configuration newConfig) | ||||
@@ -394,6 +404,13 @@ namespace Shadowsocks.Controller | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | ConfigChanged?.Invoke(this, new EventArgs()); | ||||
} | } | ||||
public void ToggleRegeneratePacOnUpdate(bool enabled) | |||||
{ | |||||
_config.regeneratePacOnUpdate = enabled; | |||||
SaveConfig(_config); | |||||
ConfigChanged?.Invoke(this, new EventArgs()); | |||||
} | |||||
#endregion | #endregion | ||||
#region SIP002 | #region SIP002 | ||||
@@ -622,28 +639,6 @@ namespace Shadowsocks.Controller | |||||
#endregion | #endregion | ||||
#region Memory Management | |||||
private void StartReleasingMemory() | |||||
{ | |||||
_ramThread = new Thread(new ThreadStart(ReleaseMemory)) | |||||
{ | |||||
IsBackground = true | |||||
}; | |||||
_ramThread.Start(); | |||||
} | |||||
private void ReleaseMemory() | |||||
{ | |||||
while (true) | |||||
{ | |||||
Utils.ReleaseMemory(false); | |||||
Thread.Sleep(30 * 1000); | |||||
} | |||||
} | |||||
#endregion | |||||
#region Traffic Statistics | #region Traffic Statistics | ||||
private void StartTrafficStatistics(int queueMaxSize) | private void StartTrafficStatistics(int queueMaxSize) | ||||
@@ -28,6 +28,7 @@ Edit Local PAC File...,Редактировать локальный PAC…,编 | |||||
Update Local PAC from Geosite,Обновить локальный PAC из Geosite,从 Geosite 更新本地 PAC,從 Geosite 更新本機 PAC,Geosite からローカル PAC を更新,Geosite에서 로컬 프록시 자동 구성 파일 업데이트,Mettre à jour le PAC local à partir de Geosite | 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 ... | 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 | 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 | 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 ... | Share Server Config...,Поделиться конфигурацией сервера…,分享服务器配置...,分享伺服器設定檔...,サーバーの設定を共有...,서버 설정 공유,Partager la configuration du serveur ... | ||||
Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | Scan QRCode from Screen...,Сканировать QRCode с экрана…,扫描屏幕上的二维码...,掃描螢幕上的 QR 碼...,画面から QR コードをスキャン...,화면에서 QR코드 스캔,Scanner le QRCode à partir de l'écran ... | ||||
@@ -3,6 +3,7 @@ using System.Collections.Generic; | |||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | using System.Net; | ||||
using System.Windows; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using NLog; | using NLog; | ||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
@@ -27,14 +28,15 @@ namespace Shadowsocks.Model | |||||
public bool global; | public bool global; | ||||
public bool enabled; | public bool enabled; | ||||
public bool shareOverLan; | public bool shareOverLan; | ||||
public bool isDefault; | |||||
public bool firstRun; | |||||
public int localPort; | public int localPort; | ||||
public bool portableMode; | public bool portableMode; | ||||
public bool showPluginOutput; | public bool showPluginOutput; | ||||
public string pacUrl; | public string pacUrl; | ||||
public bool useOnlinePac; | public bool useOnlinePac; | ||||
public bool secureLocalPac; | |||||
public bool secureLocalPac; // enable secret for PAC server | |||||
public bool regeneratePacOnUpdate; // regenerate pac.txt on version update | |||||
public bool availabilityStatistics; | public bool availabilityStatistics; | ||||
public bool autoCheckUpdate; | public bool autoCheckUpdate; | ||||
public bool checkPreRelease; | public bool checkPreRelease; | ||||
@@ -54,7 +56,7 @@ namespace Shadowsocks.Model | |||||
public HotkeyConfig hotkey; | public HotkeyConfig hotkey; | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public bool updated; | |||||
public bool firstRunOnNewVersion; | |||||
public Configuration() | public Configuration() | ||||
{ | { | ||||
@@ -64,13 +66,14 @@ namespace Shadowsocks.Model | |||||
global = false; | global = false; | ||||
enabled = false; | enabled = false; | ||||
shareOverLan = false; | shareOverLan = false; | ||||
isDefault = true; | |||||
firstRun = true; | |||||
localPort = 1080; | localPort = 1080; | ||||
portableMode = true; | portableMode = true; | ||||
showPluginOutput = false; | showPluginOutput = false; | ||||
pacUrl = ""; | pacUrl = ""; | ||||
useOnlinePac = false; | useOnlinePac = false; | ||||
secureLocalPac = true; | secureLocalPac = true; | ||||
regeneratePacOnUpdate = true; | |||||
availabilityStatistics = false; | availabilityStatistics = false; | ||||
autoCheckUpdate = false; | autoCheckUpdate = false; | ||||
checkPreRelease = false; | checkPreRelease = false; | ||||
@@ -88,7 +91,7 @@ namespace Shadowsocks.Model | |||||
proxy = new ProxyConfig(); | proxy = new ProxyConfig(); | ||||
hotkey = new HotkeyConfig(); | hotkey = new HotkeyConfig(); | ||||
updated = false; | |||||
firstRunOnNewVersion = false; | |||||
configs = new List<Server>(); | configs = new List<Server>(); | ||||
onlineConfigSource = new List<string>(); | onlineConfigSource = new List<string>(); | ||||
@@ -108,11 +111,8 @@ namespace Shadowsocks.Model | |||||
#endif | #endif | ||||
[JsonIgnore] | [JsonIgnore] | ||||
public string localHost => GetLocalHost(); | |||||
private string GetLocalHost() | |||||
{ | |||||
return isIPv6Enabled ? "[::1]" : "127.0.0.1"; | |||||
} | |||||
public string LocalHost => isIPv6Enabled ? "[::1]" : "127.0.0.1"; | |||||
public Server GetCurrentServer() | public Server GetCurrentServer() | ||||
{ | { | ||||
if (index >= 0 && index < configs.Count) | if (index >= 0 && index < configs.Count) | ||||
@@ -129,6 +129,11 @@ namespace Shadowsocks.Model | |||||
localPort) | localPort) | ||||
: null; | : 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) | public static void CheckServer(Server server) | ||||
{ | { | ||||
CheckServer(server.server); | CheckServer(server.server); | ||||
@@ -137,53 +142,71 @@ namespace Shadowsocks.Model | |||||
CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); | CheckTimeout(server.timeout, Server.MaxServerTimeoutSec); | ||||
} | } | ||||
public static bool ChecksServer(Server server) | |||||
{ | |||||
try | |||||
{ | |||||
CheckServer(server); | |||||
return true; | |||||
} | |||||
catch (Exception) | |||||
{ | |||||
return false; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Loads the configuration from file. | |||||
/// </summary> | |||||
/// <returns>An Configuration object.</returns> | |||||
public static Configuration Load() | public static Configuration Load() | ||||
{ | { | ||||
Configuration config; | Configuration config; | ||||
try | |||||
if (File.Exists(CONFIG_FILE)) | |||||
{ | { | ||||
string configContent = File.ReadAllText(CONFIG_FILE); | |||||
config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||||
config.isDefault = false; | |||||
config.version = UpdateChecker.Version; | |||||
if (UpdateChecker.Asset.CompareVersion(UpdateChecker.Version, config.version ?? "0") > 0) | |||||
try | |||||
{ | { | ||||
config.updated = true; | |||||
string configContent = File.ReadAllText(CONFIG_FILE); | |||||
config = JsonConvert.DeserializeObject<Configuration>(configContent); | |||||
return config; | |||||
} | } | ||||
if (config.configs.Count == 0) | |||||
config.configs.Add(GetDefaultServer()); | |||||
if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) | |||||
config.index = 0; | |||||
if (!System.Net.Sockets.Socket.OSSupportsIPv6) | |||||
catch (Exception e) | |||||
{ | { | ||||
config.isIPv6Enabled = false; // disable IPv6 if os not support | |||||
if (!(e is FileNotFoundException)) | |||||
logger.LogUsefulException(e); | |||||
} | } | ||||
//TODO if remote host(server) do not support IPv6 (or DNS resolve AAAA TYPE record) disable IPv6? | |||||
} | |||||
config = new Configuration(); | |||||
return config; | |||||
} | |||||
config.proxy.CheckConfig(); | |||||
/// <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 group exists. | |||||
// Reset to default if no such group. | |||||
if (!GeositeUpdater.CheckGeositeGroup(config.geositeGroup)) | |||||
{ | |||||
#if DEBUG | |||||
logger.Debug($"Current group: {config.geositeGroup}"); | |||||
foreach (var group in GeositeUpdater.Geosites.Keys) | |||||
logger.Debug($"{group}"); | |||||
#endif | |||||
config.geositeGroup = "geolocation-!cn"; | |||||
logger.Warn("The specified Geosite group doesn't exist. Using default group."); | |||||
} | } | ||||
catch (Exception e) | |||||
// Mark the first run of a new version. | |||||
if (UpdateChecker.Asset.CompareVersion(UpdateChecker.Version, config.version ?? "0") > 0) | |||||
{ | { | ||||
if (!(e is FileNotFoundException)) | |||||
logger.LogUsefulException(e); | |||||
config = new Configuration(); | |||||
config.configs.Add(GetDefaultServer()); | |||||
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 | try | ||||
{ | { | ||||
config.nLogConfig = NLogConfig.LoadXML(); | config.nLogConfig = NLogConfig.LoadXML(); | ||||
@@ -203,48 +226,42 @@ namespace Shadowsocks.Model | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
// todo: route the error to UI since there is no log file in this scenario | |||||
logger.Error(e, "Cannot get the log level from NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||||
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}"); | |||||
} | } | ||||
config.userAgentString = config.userAgent.Replace("$version", config.version); | |||||
return config; | |||||
} | } | ||||
/// <summary> | |||||
/// Saves the Configuration object to file. | |||||
/// </summary> | |||||
/// <param name="config">A Configuration object.</param> | |||||
public static void Save(Configuration config) | public static void Save(Configuration config) | ||||
{ | { | ||||
config.configs = SortByOnlineConfig(config.configs); | config.configs = SortByOnlineConfig(config.configs); | ||||
if (config.index >= config.configs.Count) | |||||
config.index = config.configs.Count - 1; | |||||
if (config.index < -1) | |||||
config.index = -1; | |||||
if (config.index == -1 && string.IsNullOrEmpty(config.strategy)) | |||||
config.index = 0; | |||||
config.isDefault = false; | |||||
FileStream configFileStream = null; | |||||
StreamWriter configStreamWriter = null; | |||||
try | try | ||||
{ | { | ||||
using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) | |||||
{ | |||||
string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); | |||||
sw.Write(jsonString); | |||||
sw.Flush(); | |||||
} | |||||
try | |||||
{ | |||||
// apply changes to NLog.config | |||||
config.nLogConfig.SetLogLevel(config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info); | |||||
NLogConfig.SaveXML(config.nLogConfig); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
logger.Error(e, "Cannot set the log level to NLog config file. Please check if the nlog config file exists with corresponding XML nodes."); | |||||
} | |||||
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 (IOException e) | |||||
catch (Exception e) | |||||
{ | { | ||||
logger.LogUsefulException(e); | logger.LogUsefulException(e); | ||||
} | } | ||||
finally | |||||
{ | |||||
if (configStreamWriter != null) | |||||
configStreamWriter.Dispose(); | |||||
if (configFileStream != null) | |||||
configFileStream.Dispose(); | |||||
} | |||||
} | } | ||||
public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | ||||
@@ -97,8 +97,6 @@ namespace Shadowsocks | |||||
#endregion | #endregion | ||||
#region Event Handlers Setup | #region Event Handlers Setup | ||||
Utils.ReleaseMemory(true); | |||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); | Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); | ||||
// handle UI exceptions | // handle UI exceptions | ||||
Application.ThreadException += Application_ThreadException; | Application.ThreadException += Application_ThreadException; | ||||
@@ -185,7 +183,7 @@ namespace Shadowsocks | |||||
Thread.Sleep(10 * 1000); | Thread.Sleep(10 * 1000); | ||||
try | try | ||||
{ | { | ||||
MainController.Start(false); | |||||
MainController.Start(true); | |||||
logger.Info("controller started"); | logger.Info("controller started"); | ||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
@@ -95,42 +95,6 @@ namespace Shadowsocks.Util | |||||
return Path.Combine(GetTempPath(), filename); | return Path.Combine(GetTempPath(), filename); | ||||
} | } | ||||
public static void ReleaseMemory(bool removePages) | |||||
{ | |||||
// release any unused pages | |||||
// making the numbers look good in task manager | |||||
// this is totally nonsense in programming | |||||
// but good for those users who care | |||||
// making them happier with their everyday life | |||||
// which is part of user experience | |||||
GC.Collect(GC.MaxGeneration); | |||||
GC.WaitForPendingFinalizers(); | |||||
if (removePages) | |||||
{ | |||||
// as some users have pointed out | |||||
// removing pages from working set will cause some IO | |||||
// which lowered user experience for another group of users | |||||
// | |||||
// so we do 2 more things here to satisfy them: | |||||
// 1. only remove pages once when configuration is changed | |||||
// 2. add more comments here to tell users that calling | |||||
// this function will not be more frequent than | |||||
// IM apps writing chat logs, or web browsers writing cache files | |||||
// if they're so concerned about their disk, they should | |||||
// uninstall all IM apps and web browsers | |||||
// | |||||
// please open an issue if you're worried about anything else in your computer | |||||
// no matter it's GPU performance, monitor contrast, audio fidelity | |||||
// or anything else in the task manager | |||||
// we'll do as much as we can to help you | |||||
// | |||||
// just kidding | |||||
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, | |||||
(UIntPtr)0xFFFFFFFF, | |||||
(UIntPtr)0xFFFFFFFF); | |||||
} | |||||
} | |||||
public static string UnGzip(byte[] buf) | public static string UnGzip(byte[] buf) | ||||
{ | { | ||||
byte[] buffer = new byte[1024]; | byte[] buffer = new byte[1024]; | ||||
@@ -29,7 +29,6 @@ namespace Shadowsocks.View | |||||
private NotifyIcon _notifyIcon; | private NotifyIcon _notifyIcon; | ||||
private Icon icon, icon_in, icon_out, icon_both, previousIcon; | private Icon icon, icon_in, icon_out, icon_both, previousIcon; | ||||
private bool _isFirstRun; | |||||
private bool _isStartupChecking; | private bool _isStartupChecking; | ||||
private string _urlToOpen; | private string _urlToOpen; | ||||
@@ -50,6 +49,7 @@ namespace Shadowsocks.View | |||||
private MenuItem editGFWUserRuleItem; | private MenuItem editGFWUserRuleItem; | ||||
private MenuItem editOnlinePACItem; | private MenuItem editOnlinePACItem; | ||||
private MenuItem secureLocalPacUrlToggleItem; | private MenuItem secureLocalPacUrlToggleItem; | ||||
private MenuItem regenerateLocalPacOnUpdateItem; | |||||
private MenuItem autoCheckUpdatesToggleItem; | private MenuItem autoCheckUpdatesToggleItem; | ||||
private MenuItem checkPreReleaseToggleItem; | private MenuItem checkPreReleaseToggleItem; | ||||
private MenuItem proxyItem; | private MenuItem proxyItem; | ||||
@@ -108,9 +108,8 @@ namespace Shadowsocks.View | |||||
Configuration config = controller.GetConfigurationCopy(); | Configuration config = controller.GetConfigurationCopy(); | ||||
if (config.isDefault) | |||||
if (config.firstRun) | |||||
{ | { | ||||
_isFirstRun = true; | |||||
ShowConfigForm(); | ShowConfigForm(); | ||||
} | } | ||||
else if (config.autoCheckUpdate) | else if (config.autoCheckUpdate) | ||||
@@ -279,6 +278,7 @@ namespace Shadowsocks.View | |||||
this.updateFromGeositeItem = CreateMenuItem("Update Local PAC from Geosite", new EventHandler(this.UpdatePACFromGeositeItem_Click)), | this.updateFromGeositeItem = CreateMenuItem("Update Local PAC from Geosite", new EventHandler(this.UpdatePACFromGeositeItem_Click)), | ||||
this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)), | this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_Click)), | ||||
this.secureLocalPacUrlToggleItem = CreateMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)), | this.secureLocalPacUrlToggleItem = CreateMenuItem("Secure Local PAC", new EventHandler(this.SecureLocalPacUrlToggleItem_Click)), | ||||
this.regenerateLocalPacOnUpdateItem = CreateMenuItem("Regenerate local PAC on version update", new EventHandler(this.RegenerateLocalPacOnUpdateItem_Click)), | |||||
CreateMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | CreateMenuItem("Copy Local PAC URL", new EventHandler(this.CopyLocalPacUrlItem_Click)), | ||||
this.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), | this.editOnlinePACItem = CreateMenuItem("Edit Online PAC URL...", new EventHandler(this.UpdateOnlinePACURLItem_Click)), | ||||
}), | }), | ||||
@@ -336,7 +336,7 @@ namespace Shadowsocks.View | |||||
} | } | ||||
} | } | ||||
void controller_Errored(object sender, System.IO.ErrorEventArgs e) | |||||
void controller_Errored(object sender, ErrorEventArgs e) | |||||
{ | { | ||||
MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message)); | MessageBox.Show(e.GetException().ToString(), I18N.GetString("Shadowsocks Error: {0}", e.GetException().Message)); | ||||
} | } | ||||
@@ -360,6 +360,7 @@ namespace Shadowsocks.View | |||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | ||||
localPACItem.Checked = !onlinePACItem.Checked; | localPACItem.Checked = !onlinePACItem.Checked; | ||||
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | ||||
regenerateLocalPacOnUpdateItem.Checked = config.regeneratePacOnUpdate; | |||||
UpdatePACItemsEnabledStatus(); | UpdatePACItemsEnabledStatus(); | ||||
UpdateUpdateMenu(); | UpdateUpdateMenu(); | ||||
} | } | ||||
@@ -446,15 +447,14 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
logForm.Dispose(); | logForm.Dispose(); | ||||
logForm = null; | logForm = null; | ||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | void configForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
configForm.Dispose(); | configForm.Dispose(); | ||||
configForm = null; | configForm = null; | ||||
Utils.ReleaseMemory(true); | |||||
if (_isFirstRun) | |||||
var config = controller.GetCurrentConfiguration(); | |||||
if (config.firstRun) | |||||
{ | { | ||||
CheckUpdateForFirstRun(); | CheckUpdateForFirstRun(); | ||||
ShowBalloonTip( | ShowBalloonTip( | ||||
@@ -463,7 +463,7 @@ namespace Shadowsocks.View | |||||
ToolTipIcon.Info, | ToolTipIcon.Info, | ||||
0 | 0 | ||||
); | ); | ||||
_isFirstRun = false; | |||||
config.firstRun = false; | |||||
} | } | ||||
} | } | ||||
@@ -471,21 +471,18 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
proxyForm.Dispose(); | proxyForm.Dispose(); | ||||
proxyForm = null; | proxyForm = null; | ||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) | void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
hotkeySettingsForm.Dispose(); | hotkeySettingsForm.Dispose(); | ||||
hotkeySettingsForm = null; | hotkeySettingsForm = null; | ||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
void onlineConfigForm_FormClosed(object sender, FormClosedEventArgs e) | void onlineConfigForm_FormClosed(object sender, FormClosedEventArgs e) | ||||
{ | { | ||||
onlineConfigForm.Dispose(); | onlineConfigForm.Dispose(); | ||||
onlineConfigForm = null; | onlineConfigForm = null; | ||||
Utils.ReleaseMemory(true); | |||||
} | } | ||||
#endregion | #endregion | ||||
@@ -541,7 +538,8 @@ namespace Shadowsocks.View | |||||
private void CheckUpdateForFirstRun() | private void CheckUpdateForFirstRun() | ||||
{ | { | ||||
Configuration config = controller.GetConfigurationCopy(); | Configuration config = controller.GetConfigurationCopy(); | ||||
if (config.isDefault) return; | |||||
if (config.firstRun) | |||||
return; | |||||
_isStartupChecking = true; | _isStartupChecking = true; | ||||
updateChecker.CheckUpdate(config, 3000); | updateChecker.CheckUpdate(config, 3000); | ||||
} | } | ||||
@@ -689,14 +687,18 @@ namespace Shadowsocks.View | |||||
Configuration configuration = controller.GetConfigurationCopy(); | Configuration configuration = controller.GetConfigurationCopy(); | ||||
foreach (var server in configuration.configs) | foreach (var server in configuration.configs) | ||||
{ | { | ||||
if (Configuration.ChecksServer(server)) | |||||
try | |||||
{ | { | ||||
Configuration.CheckServer(server); | |||||
MenuItem item = new MenuItem(server.ToString()); | MenuItem item = new MenuItem(server.ToString()); | ||||
item.Tag = configuration.configs.FindIndex(s => s == server); | item.Tag = configuration.configs.FindIndex(s => s == server); | ||||
item.Click += AServerItem_Click; | item.Click += AServerItem_Click; | ||||
items.Add(strategyCount + serverCount, item); | items.Add(strategyCount + serverCount, item); | ||||
serverCount++; | serverCount++; | ||||
} | } | ||||
catch | |||||
{ | |||||
} | |||||
} | } | ||||
foreach (MenuItem item in items) | foreach (MenuItem item in items) | ||||
@@ -901,6 +903,12 @@ namespace Shadowsocks.View | |||||
controller.ToggleSecureLocalPac(!configuration.secureLocalPac); | controller.ToggleSecureLocalPac(!configuration.secureLocalPac); | ||||
} | } | ||||
private void RegenerateLocalPacOnUpdateItem_Click(object sender, EventArgs e) | |||||
{ | |||||
var config = controller.GetConfigurationCopy(); | |||||
controller.ToggleRegeneratePacOnUpdate(!config.regeneratePacOnUpdate); | |||||
} | |||||
private void CopyLocalPacUrlItem_Click(object sender, EventArgs e) | private void CopyLocalPacUrlItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
controller.CopyPacUrl(); | controller.CopyPacUrl(); | ||||
@@ -16,7 +16,7 @@ namespace Shadowsocks.ViewModels | |||||
/// </summary> | /// </summary> | ||||
public ServerSharingViewModel() | public ServerSharingViewModel() | ||||
{ | { | ||||
_config = Configuration.Load(); | |||||
_config = Program.MainController.GetCurrentConfiguration(); | |||||
_servers = _config.configs; | _servers = _config.configs; | ||||
_selectedServer = _servers.First(); | _selectedServer = _servers.First(); | ||||
//_selectedServerUrlImage = new BitmapImage(); | //_selectedServerUrlImage = new BitmapImage(); | ||||