mirror of
				https://github.com/bolucat/Archive.git
				synced 2025-11-01 04:13:16 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			385 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			385 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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 string geositeSha256sumUrl; // optional custom sha256sum url, leave empty to disable checksum verification for your custom geosite source
 | |
|         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 = "";
 | |
|             geositeSha256sumUrl = "";
 | |
|             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));
 | |
|         }
 | |
|     }
 | |
| }
 | 
![github-action[bot]](/assets/img/avatar_default.png)