- 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(); | |||
public static void RegAllHotkeys() | |||
{ | |||
var hotkeyConfig = Configuration.Load().hotkey; | |||
var hotkeyConfig = Program.MainController.GetCurrentConfiguration().hotkey; | |||
if (hotkeyConfig == null || !hotkeyConfig.RegHotkeysAtStartup) | |||
return; | |||
@@ -192,6 +192,13 @@ namespace Shadowsocks.Controller | |||
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) | |||
{ | |||
string abpContent; | |||
@@ -45,7 +45,7 @@ namespace Shadowsocks.Controller | |||
_config = config; | |||
string usedSecret = _config.secureLocalPac ? $"&secret={PacSecret}" : ""; | |||
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); | |||
} | |||
@@ -176,7 +176,6 @@ Connection: Close | |||
"; | |||
byte[] response = Encoding.UTF8.GetBytes(responseHead + pacContent); | |||
socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); | |||
Utils.ReleaseMemory(true); | |||
} | |||
catch (Exception e) | |||
{ | |||
@@ -172,7 +172,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
WebClient http = new WebClient(); | |||
http.Headers.Add("User-Agent", UserAgent); | |||
http.Proxy = new WebProxy(config.localHost, config.localPort); | |||
http.Proxy = new WebProxy(config.LocalHost, config.localPort); | |||
return http; | |||
} | |||
@@ -27,7 +27,6 @@ namespace Shadowsocks.Controller | |||
// manipulates UI | |||
// interacts with low level logic | |||
#region Members definition | |||
private Thread _ramThread; | |||
private Thread _trafficThread; | |||
private Listener _listener; | |||
@@ -93,10 +92,10 @@ namespace Shadowsocks.Controller | |||
public ShadowsocksController() | |||
{ | |||
_config = Configuration.Load(); | |||
Configuration.Process(ref _config); | |||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | |||
_strategyManager = new StrategyManager(this); | |||
_pluginsByServer = new ConcurrentDictionary<Server, Sip003Plugin>(); | |||
StartReleasingMemory(); | |||
StartTrafficStatistics(61); | |||
ProgramUpdated += (o, e) => | |||
@@ -107,23 +106,34 @@ namespace Shadowsocks.Controller | |||
#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() | |||
{ | |||
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 (regHotkeys) | |||
{ | |||
if (!systemWakeUp) | |||
HotkeyReg.RegAllHotkeys(); | |||
} | |||
} | |||
public void Stop() | |||
@@ -154,6 +164,7 @@ namespace Shadowsocks.Controller | |||
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(); | |||
@@ -226,7 +237,6 @@ namespace Shadowsocks.Controller | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
UpdateSystemProxy(); | |||
Utils.ReleaseMemory(true); | |||
} | |||
protected void SaveConfig(Configuration newConfig) | |||
@@ -394,6 +404,13 @@ namespace Shadowsocks.Controller | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
} | |||
public void ToggleRegeneratePacOnUpdate(bool enabled) | |||
{ | |||
_config.regeneratePacOnUpdate = enabled; | |||
SaveConfig(_config); | |||
ConfigChanged?.Invoke(this, new EventArgs()); | |||
} | |||
#endregion | |||
#region SIP002 | |||
@@ -622,28 +639,6 @@ namespace Shadowsocks.Controller | |||
#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 | |||
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 | |||
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 ... | |||
@@ -3,6 +3,7 @@ using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Windows; | |||
using Newtonsoft.Json; | |||
using NLog; | |||
using Shadowsocks.Controller; | |||
@@ -27,14 +28,15 @@ namespace Shadowsocks.Model | |||
public bool global; | |||
public bool enabled; | |||
public bool shareOverLan; | |||
public bool isDefault; | |||
public bool firstRun; | |||
public int localPort; | |||
public bool portableMode; | |||
public bool showPluginOutput; | |||
public string pacUrl; | |||
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 autoCheckUpdate; | |||
public bool checkPreRelease; | |||
@@ -54,7 +56,7 @@ namespace Shadowsocks.Model | |||
public HotkeyConfig hotkey; | |||
[JsonIgnore] | |||
public bool updated; | |||
public bool firstRunOnNewVersion; | |||
public Configuration() | |||
{ | |||
@@ -64,13 +66,14 @@ namespace Shadowsocks.Model | |||
global = false; | |||
enabled = false; | |||
shareOverLan = false; | |||
isDefault = true; | |||
firstRun = true; | |||
localPort = 1080; | |||
portableMode = true; | |||
showPluginOutput = false; | |||
pacUrl = ""; | |||
useOnlinePac = false; | |||
secureLocalPac = true; | |||
regeneratePacOnUpdate = true; | |||
availabilityStatistics = false; | |||
autoCheckUpdate = false; | |||
checkPreRelease = false; | |||
@@ -88,7 +91,7 @@ namespace Shadowsocks.Model | |||
proxy = new ProxyConfig(); | |||
hotkey = new HotkeyConfig(); | |||
updated = false; | |||
firstRunOnNewVersion = false; | |||
configs = new List<Server>(); | |||
onlineConfigSource = new List<string>(); | |||
@@ -108,11 +111,8 @@ namespace Shadowsocks.Model | |||
#endif | |||
[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() | |||
{ | |||
if (index >= 0 && index < configs.Count) | |||
@@ -129,6 +129,11 @@ namespace Shadowsocks.Model | |||
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); | |||
@@ -137,53 +142,71 @@ namespace Shadowsocks.Model | |||
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() | |||
{ | |||
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 | |||
{ | |||
config.nLogConfig = NLogConfig.LoadXML(); | |||
@@ -203,48 +226,42 @@ namespace Shadowsocks.Model | |||
} | |||
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) | |||
{ | |||
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 | |||
{ | |||
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); | |||
} | |||
finally | |||
{ | |||
if (configStreamWriter != null) | |||
configStreamWriter.Dispose(); | |||
if (configFileStream != null) | |||
configFileStream.Dispose(); | |||
} | |||
} | |||
public static List<Server> SortByOnlineConfig(IEnumerable<Server> servers) | |||
@@ -97,8 +97,6 @@ namespace Shadowsocks | |||
#endregion | |||
#region Event Handlers Setup | |||
Utils.ReleaseMemory(true); | |||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException); | |||
// handle UI exceptions | |||
Application.ThreadException += Application_ThreadException; | |||
@@ -185,7 +183,7 @@ namespace Shadowsocks | |||
Thread.Sleep(10 * 1000); | |||
try | |||
{ | |||
MainController.Start(false); | |||
MainController.Start(true); | |||
logger.Info("controller started"); | |||
} | |||
catch (Exception ex) | |||
@@ -95,42 +95,6 @@ namespace Shadowsocks.Util | |||
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) | |||
{ | |||
byte[] buffer = new byte[1024]; | |||
@@ -29,7 +29,6 @@ namespace Shadowsocks.View | |||
private NotifyIcon _notifyIcon; | |||
private Icon icon, icon_in, icon_out, icon_both, previousIcon; | |||
private bool _isFirstRun; | |||
private bool _isStartupChecking; | |||
private string _urlToOpen; | |||
@@ -50,6 +49,7 @@ namespace Shadowsocks.View | |||
private MenuItem editGFWUserRuleItem; | |||
private MenuItem editOnlinePACItem; | |||
private MenuItem secureLocalPacUrlToggleItem; | |||
private MenuItem regenerateLocalPacOnUpdateItem; | |||
private MenuItem autoCheckUpdatesToggleItem; | |||
private MenuItem checkPreReleaseToggleItem; | |||
private MenuItem proxyItem; | |||
@@ -108,9 +108,8 @@ namespace Shadowsocks.View | |||
Configuration config = controller.GetConfigurationCopy(); | |||
if (config.isDefault) | |||
if (config.firstRun) | |||
{ | |||
_isFirstRun = true; | |||
ShowConfigForm(); | |||
} | |||
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.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for Geosite...", new EventHandler(this.EditUserRuleFileForGeositeItem_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)), | |||
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)); | |||
} | |||
@@ -360,6 +360,7 @@ namespace Shadowsocks.View | |||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | |||
localPACItem.Checked = !onlinePACItem.Checked; | |||
secureLocalPacUrlToggleItem.Checked = config.secureLocalPac; | |||
regenerateLocalPacOnUpdateItem.Checked = config.regeneratePacOnUpdate; | |||
UpdatePACItemsEnabledStatus(); | |||
UpdateUpdateMenu(); | |||
} | |||
@@ -446,15 +447,14 @@ namespace Shadowsocks.View | |||
{ | |||
logForm.Dispose(); | |||
logForm = null; | |||
Utils.ReleaseMemory(true); | |||
} | |||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
configForm.Dispose(); | |||
configForm = null; | |||
Utils.ReleaseMemory(true); | |||
if (_isFirstRun) | |||
var config = controller.GetCurrentConfiguration(); | |||
if (config.firstRun) | |||
{ | |||
CheckUpdateForFirstRun(); | |||
ShowBalloonTip( | |||
@@ -463,7 +463,7 @@ namespace Shadowsocks.View | |||
ToolTipIcon.Info, | |||
0 | |||
); | |||
_isFirstRun = false; | |||
config.firstRun = false; | |||
} | |||
} | |||
@@ -471,21 +471,18 @@ namespace Shadowsocks.View | |||
{ | |||
proxyForm.Dispose(); | |||
proxyForm = null; | |||
Utils.ReleaseMemory(true); | |||
} | |||
void hotkeySettingsForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
hotkeySettingsForm.Dispose(); | |||
hotkeySettingsForm = null; | |||
Utils.ReleaseMemory(true); | |||
} | |||
void onlineConfigForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
onlineConfigForm.Dispose(); | |||
onlineConfigForm = null; | |||
Utils.ReleaseMemory(true); | |||
} | |||
#endregion | |||
@@ -541,7 +538,8 @@ namespace Shadowsocks.View | |||
private void CheckUpdateForFirstRun() | |||
{ | |||
Configuration config = controller.GetConfigurationCopy(); | |||
if (config.isDefault) return; | |||
if (config.firstRun) | |||
return; | |||
_isStartupChecking = true; | |||
updateChecker.CheckUpdate(config, 3000); | |||
} | |||
@@ -689,14 +687,18 @@ namespace Shadowsocks.View | |||
Configuration configuration = controller.GetConfigurationCopy(); | |||
foreach (var server in configuration.configs) | |||
{ | |||
if (Configuration.ChecksServer(server)) | |||
try | |||
{ | |||
Configuration.CheckServer(server); | |||
MenuItem item = new MenuItem(server.ToString()); | |||
item.Tag = configuration.configs.FindIndex(s => s == server); | |||
item.Click += AServerItem_Click; | |||
items.Add(strategyCount + serverCount, item); | |||
serverCount++; | |||
} | |||
catch | |||
{ | |||
} | |||
} | |||
foreach (MenuItem item in items) | |||
@@ -901,6 +903,12 @@ namespace Shadowsocks.View | |||
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) | |||
{ | |||
controller.CopyPacUrl(); | |||
@@ -16,7 +16,7 @@ namespace Shadowsocks.ViewModels | |||
/// </summary> | |||
public ServerSharingViewModel() | |||
{ | |||
_config = Configuration.Load(); | |||
_config = Program.MainController.GetCurrentConfiguration(); | |||
_servers = _config.configs; | |||
_selectedServer = _servers.First(); | |||
//_selectedServerUrlImage = new BitmapImage(); | |||